String 理解与使用
1.String基础
1.1 String介绍
String是一个对象,不是基本数据类型,String类是final类,意味着String类不能被继承,它的成员方法都默认为final方法,String类其实是通过char[]数组来保存字符串的。
String对象一旦被创建就是固定不变的,对string对象的任何串操作都不影响到原对象,相关的任何change操作都会生成新的对象。
1.2 字符串常量池
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们用到非常多,JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一系列的优化:使用字符串常量池,每当我们创建字符串常量时,jvm会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
静态常量池:,即.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类,方法的信息,占用class文件绝大部分空间。
运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池
String a=”zhanshen”;
String b=”zhanshen”;
a,b和字面上的zhanshen都是指向JVM字符串常量池中的“zhanshen”对象,他们指向同一个对象。
String c =new String(“zhanshen”);
New关键字一定会产生一个对象zhanshen(这个zhanshen和上面的zhanshen不同)同时这个对象存储在堆中,所以上面应该产生两个对象,保存在栈中的c 和保存在堆中zhanshen。在java中不存在两个完全一模一样的字符串对象,故堆中的zhanshen应该是引用字符串常量池中zhanshen。所以c,zhanshen池中zhanshen的关系应该是càzhanshenà池中zhanshen。
a,b,c,zhanshen是不同的对象,但是从String的内部结构理解,String c =new String(“zhanshen”);虽然c的内容是创建在堆中,但是它的内部value还是只想jvm常量池中的value,构造zhanshen时所用的参数依然是zhanshen字符串常量。
所以可以以下结论:
解释:当执行String a=”zhanshen”时,jvm首先会去字符串池中查找是否存在”zhanshen”这个对象,如果不存在则在字符串池中创建“zhanshen”这个对象,然后将池中“zhanshen”这个对象的引用地址返回给字符串常量a,这样a会指向池中“zhanshen”这个字符串对象,如果存在,则不创建任何对象,直接将池中“zhanshen”这个对象的地址返回,赋给字符串常量,当创建字符串对象b时,字符串池中已经存在“zhanshen”这个对象,直接把对象“zhanshen”的引用地址返回给b,这样b指向了池中“zhanshen”这个对象,即a和b指向了同一个对象,因此a==b 正确:
采用new关键字新建一个字符串对象,JVM首先在字符串池中查找有没有“zhanshen”这个字符串对象,如果有,则不再池中再去创建“zhanshen”这个对象,直接在堆中创建一个“zhanshen”字符串对象,然后将堆中的这个“zhanshen”对象的地址返回赋给引c,这样 c就指向堆中创建的这个“zhanshen”字符串对象,当执行String d=new String(“zhanshen”)是因为采用new关键字创建对象时,每次new出来的都是一个新的对象,即引用c和d是两个不同的对象。
解释:s1,s2的“helloworld”是字符串常量,在编译器确定了,s0==s1为true:“hello”和“world”也都是字符串常量,当一个字符串有多个字符串常量连接而成,他也为字符串常量。
解释:用new String 创建的字符串不是常量,不能再编译期就确定,所以new String创建的字符串不放入常量池,有自己的地址空间。s0还是常量池中"helloworld”的引用,s1因为无法在编译期确定,所以是运行时创建的新对象"helloworld”的引用,s2因为有后半部分new String(”world”)所以也无法在编译期确定,所以也是一个新创建对象"helloworld”的引用。
解释:栈中开辟一块空间存放引用str1,str1指向池中String常量"abc",栈中开辟一块空间存放引用str2,str2指向池中String常量"def"。 栈中开辟一块空间存放引用str3。str1 + str2通过StringBuilder的最后一步toString()方法还原一个新的String对象"abcdef",因此堆中开辟一块空间存放此对象。引用str3指向堆中(str1 + str2)所还原的新String对象。 str3指向的对象在堆中,而常量"abcdef"在池中,输出为false。
字面量+拼接是在编译期间进行的,拼接后字符串存在字符串常量池中,而字符串引用的+拼接预算是在运行时进行的,新创建的字符串存放在堆中。
解释:s2中加上final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储在自己的常量池中或嵌入到它字节码流中,所有此时的“a”+”b”效果一致
解释:虽然s2用final修饰,但是赋值通过方法调用返回的,是在运行期确定,所以s1和s3不是同一个对象
1.3 String的理解
1.创建字符串有两种方式:
*1:使用“”创建字符串
使用“”引号创建的字符串都是常量,编译期就已经确定存储到String Pool中
*2:使用new关键创建字符串
使用new String(“”)创建的对象会存储到heap中,是运行期新创建
New创建字符串时首先查看池中是否有相同值得字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址,如果池中没有,则在堆中创建一份,然后返回堆中地址
使用只包含常量的字符串连接符“aa”+”aa”,创建的是常量,编译确定,存储到StringPool
使用包含变量的字符串连接符“aa”+s1创建的对象时运行期才创建,在heap中
2.String类初始化后不可变(immutable)
String使用private final char value[]来实现字符串的存储,String对象创建后无法修改。然而,String类对象确实有编辑字符串的功能,比如replace()。但这是通过重新创建一个新的对象来实现的,而不是对原有对象进行修改。
3.String,StringBuffer,StringBuilder比较
可变与不可变:String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象
是否线程安全:String中的对象时不可变的,也就是可以理解为常量,所以线程安全,StringBuffer与 StringBuilder中的方法和功能完全相同,只是StringBuffer中的方法大都采用了synchronized关键字进行修饰,因此是线程安全的,而StringBuilder没有这个修饰,被认为是非线程安全的。
执行效率:StringBuilder》StringBuffer》String:当然这个是相对的,不一定在所有情况下都是这样。比如String str = "hello"+ "world"的效率就比 StringBuilder st = new StringBuilder().append("hello").append("world")要高。因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
2.String常见方法
String类常用方法,记住方法的名称,返回值类型,参数类型个数,方法的作用
2.1 字符串与字符
字符串由字符数组所组成。
@1:public String(char[] value)(构造方法) :将字符数组内容变为字符串 @2:public String(char[] value,int offset,int count)(构造方法) :将部分字符数组变为字符串,offset表示开始点,count表示要操作的长度 @3:public char charAt(int index)(普通方法) :取得指定索引上的字符串 @4:public char[] toCharArray(普通方法) :将字符串转换为字符数组 |
实例:
2.2 字符串与字节
字符串与字节相互转换
@1:public String(byte[] bytes)(构造方法) :将全部的字节数组变为字符串 @2:public String(byte[] bytes,int offset,int length)(构造方法) :将部分的字节数组变为字符串 @3:public byte[] getBytes()(普通方法) :将字符串变为字节数组 @4:public byte[] getBytes(String charsetName)throw UnsupportedEncodingException) :字符串转码操作 |
实例:完成一个小写字母变为大写操作:
一般情况下,在程序之中如果要想操作字节数组只有两种情况:
-情况一:需要进行编码的转换时
-情况二:数据要进行传输的时候
2.3 字符串比较
比较两个字符串方法:
@1:public boolean equals(String anObject) :区分大小写的相等判断 @2:public boolean equalsIgnoreCase(String anotherString) :不区分大小写比较相等 @3:public int compareTo(String anotherString) :比较两个字符串的大小 |
比较两个字符串的大小关系,必须使用compareTo()方法,方法返回int类型数据,有三种结果:大于(返回结果大于0)小于(返回结果小于0)等于(返回结果为0)
2.4 字符串查找
在String中查找字符串
@1:public boolean contains(String s) :查找指定的字符串是否存在 @2:public int indexOf(String str) :从头查找指定字符串的位置,找不到返回-1 @3:public int indexOf(String str,int fromIndex) :由指定位置向后查找字符串的位置,找不到返回-1 @4:public int lastIndexOf(String str) :由后向前查找字符串的位置,找不到返回-1 @5:public int lastIndexOf(String str,int fromIndex) :从指定位置由后向前查找 @6:public boolean startsWith(String prefix) :判断是否以指定的字符串开头 @7:public boolean startsWith(String prefix,int toffset) :从指定位置判断是否以指定字符串开头 @8:public boolean endsWith(String suffix) :判断是否以指定的字符串结尾 |
2.5 字符串替换
String中替换操作的方法
@1:public String replaceAll(String regex,String replacement) :全部替换 @2:public String replaceFirst(String regex,String replacement) :替换首个 |
2.6 字符串拆分
字符串拆分指的是按照一个指定的字符串标记,对一个完整的字符串进行分割。方法如下
@1:public String [] split(String regex) :按照指定的字符串拆分 @2:public String[] split(String regex,int limit) :拆分为指定长度 |
注:拆分为指定个数,后面的内容将不再拆分而是一个整体
2.7 字符串截取
字符串截取从0开始
@1:public String substring(int beginIndex) :从指定位置截取到结尾 @2:public String substring(int beginIndex,int endIndex) :截取指定范围的内容 |
2.8 字符串拼接
对字符串进行拼接
@1:使用+号 @2:public String concat(String str) |
2.9 字符串其他方法
@1:public boolean isEmpty() :判断是否为空字符串("”) @2:public int length() @3:public String trim() :去掉左右空格 @4:public String toLowerCase() :将字符串转小写 @5:public String toUpperCase() :将字符串转大写 @6:public String intern() :入池 |