当前位置: 代码迷 >> 综合 >> 【JAVASE(6)】JAVASE学习--字符串篇
  详细解决方案

【JAVASE(6)】JAVASE学习--字符串篇

热度:25   发布时间:2023-11-30 15:07:03.0

一、java.long.String类代表字符串。

String类:字符串是个常量,他们的值创建后不能改变,字符串的底层是一个被final修饰的数组,不能改变是个常量。其底层实现为:

private final byte[] value这是java1.8(不含1.8)以后的定义,在java1.8及其之前String的底层是一个char[]数组

程序地址所有的双引号字符串,均为String类对象(就算没有new,照样是)
    String的使用
        String:字符串,使用一对""引起来表示
            1.String声明为final的,不可被继承
            2.String实现了
                java.io.Serializable接口:表示字符创是支持序列化的。
                Constable接口:表示String可以比较大小
            3.String内部定义了final char[] value用于存储字符串数据
            4.String:代表不可变的字符序列。简称:不可变性。
                体现:
                    1.当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值
                    2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
                    3.当调用String的replace()方法修改指定的字符或字符串也需要重新指定内存区域赋值,不能使用原有的value进行赋值
                    4.字符串的内容永不可变,正应为字符串的内容不可变,所以字符串可以共享
实例一

{String s1="abc";//字面量的定义方式在方法区的字符串常量池中开辟了一个存储空间用于存储字符串"abc"并且将此存储空间的首地址赋值给了栈空间中的变量s1.即栈空间变量s1指向了方法区中字符串常量池中存放字符串"abc"的空间。String s2="abc";在栈空间中将s1保存的地址值同样赋值给了s2,即使得栈空间变量s1栈空间变量s2均指向了方法区字符串常量池中存放字符串"abc"的空间(这是因为常量池中不会存放两个相同的内容)s1="hello";在方法区字符串常量池中新开辟了一个存储空间专门用于存放字符串"hello"并且将此存储空间的首地址赋值给了栈空间变量s1,即栈空间变量s1从原来指向字符串"abc"改变成了指向字符串"hello"String s3="abc";在栈空间中将s1保存的地址值同样赋值给了s3,即使得栈空间变量s1栈空间变量s3均指向了方法区字符串常量池中存放字符串"abc"的空间(这是因为常量池中不会存放两个相同的内容)s3+="def";对s3指向的字符串进行连接操作,而String类型底层是char类型数组的关系(数组一旦产生其长度就是固定的)使得在方法区字符串常量池中必须重新开辟一个存储空间专门用于存储进行连接操作以后的字符串,再将其首地址赋值给栈空间变量s3String s4="abc";在栈空间中将s1保存的地址值同样赋值给了s4,即使得栈空间变量s1栈空间变量s4均指向了方法区字符串常量池中存放字符串"abc"的空间(这是因为常量池中不会存放两个相同的内容)String s5 = s4.replace('a', 'm');将栈空间中s4指向的字符串复制一份,存储于方法区常量池中新开的存储空间中,同时将字符串中的'a'字符改变为'm'字符,将此空间的首地址赋值给栈空间中新声明的s5变量}

          5.通过字面量的方式(区别于new方式)给一个字符串赋值,此时的字符串值声明在字符串常量池中;字符串常量池:程序中直接写上双引号的字符,就在字符串常量池中,而new的不在池子中
          6.常量池中是不会存放两个相同的内容。即在常量池中,若两个变量的内容相同,那么就表示这两个引用变量会指向常量池中同一个数据
          7.在jdk1.8(含jdk1.8)之前,字符串底层是用char[]字符数组实现,在jdk1.8之后字符串效果上相当于char[]字符数组,但底层原理是byte[]字节组;改变的原因是节省内存空间,避免不必要的浪费。


        String的实例化:
            方式一:通过字面量方式
                String str=" ";//右边直接用双引号
                注意:直接写上双引号,就是字符串对象

            方式二:new+构造器
                三种构造方法:
                public String():创建一个空白字符串,不含任何内容;
                public String(char[] array):由字符数组内容,来创建对应字符串
                public String(byte[] array):由字节数组内容,来创建对应字符串
实例一

{String s1="javaee";在方法区的字符串常量池中开辟了一个存储空间用于存储字符串"javaee"并且将此存储空间的首地址赋值给了栈空间中的变量s1.即栈空间变量s1指向了方法区中字符串常量池中存放字符串"javaee"的空间。String s2="javaee";在栈空间中将s1保存的地址值同样赋值给了s2,即使得栈空间变量s1栈空间变量s2均指向了方法区字符串常量池中存放字符串"javaee"的空间(这是因为常量池中不会存放两个相同的内容)System.out.println(s1==s2);//true由于s1和s2保存的地址值相同所以返回trueString s3=new String("javaee");在堆空间中new来一个新的String对象,将此对象的首地址值赋值给了栈空间中的s3变量,即栈空间中的变量s3指向了堆空间中新new出来的String对象,而堆空间中的String则保存了方法区常量池中保存字符串"javaee"的首地址值String s4=new String("javaee");在堆空间中又一次new来一个新的String对象,将此对象的首地址值赋值给了栈空间中的s4变量,即栈空间中的变量s4指向了堆空间中又一次新new出来的String对象,而堆空间中的String则保存了方法区常量池中保存字符串"javaee"的首地址值System.out.println(s1 == s3);//false:一个是方法区字符串常量池地址,一个是堆空间新new出来的String对象地址System.out.println(s1 == s4);//false:一个是方法区字符串常量池地址,一个是堆空间新new出来的String对象地址System.out.println(s3 == s4);//false:由于new了两个对象,所以每一个对象都有一个单独的存储空间和存储空间首地址}

实例二

{String s1="hello";在方法区的字符串常量池中,开辟存储空间用于存储“hello”字符串,将此存储空间的首地址值赋值给栈空间中的变量s1,即栈空间中的变量s1指向方法区字符串常量池中的“hello”。String s2="hello";由于在方法区字符串常量池中存储的字符串是唯一的,故s2中存储的地址值和s1中存储的地址值一样,故s1和s2共同指向方法区字符串常量池中的“hello”。String s3=new String("hello");在对空间中新开辟了一个存储空间,由于存放String对象,将此空间首地址值赋值给栈空间变量s3,即栈空间变量s3指向了堆空间中新造的String对象,然而堆空间中的String对象存储的是方法区字符串常量池中的“hello”的存储空间首地址值,即s3指向堆空间中的String对象,而String对象和s1,s2一同指向方法区字符串常量池中的“hello”s1+="world";此句等价于“s1=s1+"world",即在堆空间中开辟了一个新的String对象,将此对象的存储首地址值赋值给了s1,使得s1从原来指向指向方法区字符串常量池中的“hello”,改变为现在指向堆空间中新建出来的String对象,而此String对象中存储的内容在方法区字符串常量池中没有,故又开辟一个新的存储空间用于存储新的“helloworld”,将其首地址值赋值给堆空间中新new出来的String对象String s4=new String("abc");在内存中创建了两个对象,一个是在堆中new的对象,另外一个是在方法区字符串常量池中对应的字符串对象}


实例三

{public class Main {public static void main(String[] args) {String str1="abc";String str2="abc";在方法区字符串常量池中开辟了新的存储空间,用于存放字符串“abc”并且将存储空间首地址值赋值给了栈空间变量str1和str2,即栈空间变量str1和str2一同指向方法区字符串常量池中的“abc”char[] charArray={'a','b','c'};在堆空间中开辟了一个存储空间用于存储char型数组对象,而char型数组对象保存存储的是方法区字符串常量池中字符串“abc”中每一个字符的存储地址,而char型数组对象在堆空间中存储的首地址值则是赋值给了栈空间中的变量charArrayString str3=new String(charArray);在堆空间中开辟了一个新的存储空间用于存放String对象,而String对象中存储的是同为堆空间中的char型数组存储区域的首地址值,同时String对象本身的首地址值又赋值给了栈空间中的变量str3,即栈空间中的变量str3指向堆空间中的String对象,而string对象又指向了同在堆空间中的char[]型数组,而char型数组又指向了方法区字符串常量池中的"abc"System.out.println(str1==str2);//trueSystem.out.println(str1==str3);//falseSystem.out.println(str2==str3);//false对栈空间中的变量保存的地址值进行比较System.out.println(str1.equals(charArray));//false栈空间变量str1中存储的是方法区字符串常量池中的“abc”的首地址值,栈空间变量charArray中保存的是堆空间中char型数组对象的首地址值。}}}

        典型问题:String s=new String("abc");方式创建对象,在内存中创建了几个对象:创建了两个,第一个是堆空间中new的,另外一个是char[]类型数组对应的常量池中的数据:"abc"

        在String中:
            1.字面量(常量)与字面量(常量)的拼接结果在常量池。且常量池中不会存在相同内容的常量
            2.字面量与变量,或者变量与变量的拼接,其结果会在堆空间中产生
            3.若拼接的结果调用intern()方法,则返回值就在常量池中

        tip1:对象也可以是一个类

二、3种JVM:
        sun公司的hotSpot------一般默认这是虚拟机的底层结构
        BEA公司的JRockit
        IBM公司的J9 VM

    JVM的实际底层实现中包含方法区和堆
堆内存包含

{伊甸区新生0区新生1区以上三个区统称新生区---常见的异常抛出为:java heap space养老区永久存储区----其实不包含在堆内存中可以看做是方法区的一部分,常见的抛出异常是:PermGen space
}


        字符串常量池:程序中直接写上双引号的字符,就在字符串常量池中,而new的不在池子中;在jdk1.6中将字符串常量池归到了方法区中具体实现在永久代,在jdk1.7将字符串常量池归到了堆中,在jdk1.8中将字符串常量池归到了方法区中,具体实现是元空间

String和其他结构间的转换:

基本类型与字符串类型之间的相互转换:
基本类型->字符串(String)1.基本类型的值+""最简单的方法(工作中常用)2.包装类的静态方法toString(参数),不是Object类的toString()重载static String toString(int i)返回一个表示指定整数的String对象3.String类的静态方法valueOf(参数)static String valueOf(int i)返回int参数的字符串表示形式字符串(String)->基本类型使用包装类的静态方法parseXXX(“字符串”)Integer类:static int parseInt(String s)Double类:static double parseDouble(String s)String ---->基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)String<----包装类、包装类:调用String重载的valueOf(xxx)String ---->char[]:调用String的toCharArray()方法String <----char[]:调用String的构造器编码:String ---->byte[]:调用String的getBates()解码:byte[] ---->String:调用String的构造器String ----> StringBuffer/StringBuilder:调用StringBuffer/StringBuilder构造器{StringBuffer/StringBuilder(String str):构造一个字符串生成器,并初始化为指定字符串内容}StringBuffer/StringBuilder---->String:调用String的构造器;或StringBuffer/StringBuilder的toString()方法

编码:字符串--->字节 (看得懂---->看不懂的二进制数据)
解码:编码你过程:字节--->字符串(看不懂的二进制数据-->看得懂)
说明:解码时,要求解码使用的字符集和编码时使用的字符集一致,否则出现乱码

String的常用方法:

 int length():返回字符串长度,即底层char型数组的长度char charAt(int index):返回某索引处的字符(索引从0开始)boolean isEmpty():判断是否是空字符,以以char型数组长度为判断标准String toLowerCase():使用默认语言环境将String中所有字符转换为小写,并没有修改原字符串本身,而是将其复制后的副本进行修改String toUpperCase():使用默认语言环境将String中所有字符转换为大写,并没有修改原字符串本身,而是将其复制后的副本进行修改String trim():返回字符串的副本,忽略签到空白和尾部空白;删除一个字符串的前后空格,字符串内的空格不动boolean equals(Object obj):比较字符串的内容是否相同boolean equalsIgnoreCase(String anotherString):与equals方法相同忽略大小写String concat(String str):将指定字符串连接到此字符串的末尾,等价于“+”int compareTo(String anotherString):比较两个字符串大小,字符串比较大小先看字符是否相同;若不相同,那么就按照ASCII码值进行相减运算并返回运算结果,并且只把第一个不同的字符相减结果返回,涉及到字符串排序截取String substring(int beginIndex):返回一个新字符串,它是此字符串的从beginIndex开始截取的String substring(int beginIndex,int endIndex):返回一个新字符串,它是此字符串的从beginIndex开始截取到endIndex为止;左闭右开boolean endWith(String suffix):测试此字符串是否是以指定的后缀结束boolean startWith(String prefix):测试此字符串是否是以指定的前缀开始boolean startWith(String prefix,int toffset):测试此字符串从指定的索引开始的子字符串是否以指定前缀开始boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回trueint indexOf(String str):反回指定字符串在此字符串中第一次出现处的索引int indexOf(String str,int formIndex):反回指定字符串在此字符串中第一次出现处的索引int lastIndexOf(String str):返回指定字符串在此字符串中最右边出现处的索引int lastIndexOf(String str,int formIndex):返回指定字符串在此字符串中最后一次出现出的索引,从指定的索引开始反向搜索注:1、indexOf和lastIndexOf方法如果未找到都是返回-12、当存在一个唯一的字符在字符串中或者不存在字符串时indexOf和lastIndexOf方法返回的结果相同替换String replace(char oldChar,char newChar):返回一个新的字符串,它是通过newChar替换此字符串中出现的所有oldChar得到的String replace(CharSequence target,CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串String replaceAll(String regex,String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串String replaceFirst(String regex,String replacement):使用给定的replacement替换此字符串匹配给定的正则表达式的第一个子字符串匹配boolean matches(String regex):告知此字符串是否匹配给定的正则表达式切片String[] split(String regex):根据给定正则表达式的匹配拆分此字符串String[] split(String regex,int limit):根据给定正则表达式的匹配拆分此字符串,最多不超过limit个,若超过了,剩下的全部放到最后一个元素中。split方法的参数其实是个“正则表达式”,若对“.”进行切分,要写"\\."

三、关于StringBuffer和StringBuilder的使用
        String、StringBuffer、StringBuilder异同:
            String:不可变的字符序列;在jdk8之前(含jdk8)底层使用char[]存储;在jdk9以后,底层使用byte[]存储
            StringBuffer:字符串缓冲区,可以提高字符串的操作效率(看成一个长度可以变化的字符串)可变的字符序列;线程安全的,效率偏低;在jdk8之前(含jkd8)底层使用没有被final修饰的char[]存储;在jdk9以后,底层使用没有被final修饰的byte[]存储
            StringBuilder:字符串缓冲区,可以提高字符串的操作效率(看成一个长度可以变化的字符串)可变的字符序列;线程不安全,效率高,是jdk5.0开始;在jdk8之前(含jdk8)底层使用没有被final修饰的char[]存储;在jdk9以后,底层使用没有被final修饰的byte[]存储

注:
            1.在开发中建议常用:StringBuffer(int capacity)或StringBuilder(int capacity)       
            2.StringBuilder/StringBuffer在内存中始终是一个数组,占用空间少效率高,如果超出StringBuilder容量,会自动扩容

            StringBuilder/StringBuffer构造方法:

StringBuilder/StringBuffer():构造一个不带任何字符的字符串生成器,其初始容量为16个字符StringBuilder/StringBuffer(String str):构造一个字符串生成器,并初始化为指定字符串内容

代码解析(以java1.8为例):

{String str=new String();即相当于char[] value=new char[0];String str1=new String("abc");即相当于char[] value=new char[]{'a','b','c'};StringBuffer str3 = new StringBuffer();即相当于char[] value=new char[16];(java1.8(含))或相当于byte[] value = new byte[16];(java1.8(不含)以后)底层创建了一个长度是16的char型数组System.out.println(str3.length());即结果是0str3.append('a');//value[0]='a';str3.append('b');//value[1]='b';即扩容问题:若添加的数据底层数组无法容纳,就需要将底层数组进行扩容,默认情况下,扩容为原来容量的2倍再加2,同时将原有数组中的元素复制到新数组中StringBuffer str4 = new StringBuffer("abc");即char[] value=new char["abc".length+16];底层创建了一个长度是传入字符串长度加16的char型数组System.out.println(str4.length());即结果是3
}

        StringBuffer/StringBuilder常用方法:

StringBuffer/StringBuilder append(xxx):提供了很多的append方法,用于进行字符串的拼接,并返回当前对象自身StringBuffer/StringBuilder delete(int star,int end):删除指定位置的内容StringBuffer/StringBuilder replace(int star,int end,String str):把[star,end)左闭右开的位置替换为strStringBuffer/StringBuilder insert(int offset,xxx):在指定位置插入xxxStringBuffer/StringBuilder reverse():把当前字符串序列逆转StringBuffer/StringBuilder public int indexOf(String str)返回指定字符串在当前字符串中首次出现的位置StringBuffer/StringBuilder public String substring(int star,ing end)返回一个从start开始到end结束左闭右开的子字符串StringBuffer/StringBuilder public int length()StringBuffer/StringBuilder public char charAt()StringBuffer/StringBuilder public void setCharAt(int n,char ch)

       StringBuffer/StringBuilder常用方法小节:
       增:append(xxx)
       删:delete(int star,int end)
       改:setCharAt(int n,char ch)/replace(int star,int end,String str)
       查:char charAt()
       插:insert(int offset,xxx)
       长度:int length()
       遍历:直接显示/者{for+charAt()}/toString()

       对比String、StringBuffer、StringBuilder三者效率(从高到低排列)Builder > Buffer > String

 tip1:一般方法中只要涉及开始结束的都是左闭右开

以上是今日小结,不喜勿喷,感谢理解

相关链接:

【JAVASE(7)】JAVASE学习--注解及小众常用类篇_lixxkv的博客-CSDN博客
【JAVASE(5)】JAVASE学习--时间篇_lixxkv的博客-CSDN博客

  相关解决方案