Java String知识点

1. 这段程序的结果是什么?

private static void test1() {
  String a = "a" + "b" + 1;
  String b = "ab1";
  System.out.println(a == b);
}

 

运行结果:

true

为什么呢?

a 引用是直接赋值的,b 引用是通过“+”赋值的,a 和b 两个引用为什么会指向同一

个内存单元?这就是JVM 的“编译时优化”

当编译器在编译代码:String a = "a" + "b" + 1;时,会将其编译为:String a = "ab1";。

为何?因为都是“常量”,编译器认为这3 个常量叠加会得到固定的值,无须运行时再

进行计算,所以就会这样优化。

 

关于“==”

首先要知道“==”用于匹配内存单元上的内容,其实就是一个数字,计算机内部也只

有数字,而在Java 语言中,当“==”匹配的时候,其实就是对比两个内存单元的内容是否一样。

如果是原始类型 byte、boolean、short、char、int、long、float、double,就是直接比较

它们的值。这个大家应该都清楚,这里不再详谈。

如果是引用(Reference),比较的就是引用的值,“引用的值”可以被认为是对象的逻

辑地址。如果两个引用发生“==”操作,就是比较相应的两个对象的地址值是否一样。换

一句话说,如果两个引用所保存的对象是同一个对象,则返回true,否则返回false(如果

引用指向的是null,其实这也是一个JVM 赋予给它的某个指定的值)。

理解寓意:大家各自拿到了一个公司的offer,现在我们看看哪些人拿到的offer 是同一

个公司的。

 

关于“equals()”

equals()方法,首先是在Object 类中被定义的,它的定义中就是使用“==”方式来匹配

的(这一点大家可以参看Object 类的源码)。也就是说,如果不去重写equals()方法,并且

对应的类其父类列表中都没有重写过equals()方法,那么默认的equals()操作就是对比对象的地址。

equals()方法之所以存在,是希望子类去重写这个方法,实现对比值的功能,类似的,

String 就自己实现了equals()方法。为什么要自己去实现呢?因为两个对象只要根据具体业

务的关键属性值来对比,确定它们是否是“一致的或相似的”,返回true|false 即可。

 

2. 这段程序的结果是什么?

private static String getA() {return"a";}
public static void test2() {
String a = "a";
final String c = "a";
String b = a + "b";
String d = c + "b";
String e = getA() + "b";
String compare = "ab";
System.out.println(b == compare);
System.out.println(d == compare);
System.out.println(e == compare);
}

 

结果:

false

true

false

 

第 1 个输出false。

“b”与“compare”对比,根据代码清单1-2 中的解释,compare 是一个常量,那么b

为什么不是呢?因为b = a + "b",a 并不是一个常量,虽然a 作为一个局部变量,它也指向

一个常量,但是其引用上并未“强制约束”是不可以被改变的。虽然知道它在这段代码中

是不会改变的,但运行时任何事情都会发生,尤其是在“字节码增强”技术面前,当代码

发生切入后,就可能发生改变。所以编译器是不会做这样优化的,所以此时在进行“+”运

算时会被编译为下面类似的结果:

StringBuilder temp = new StringBuilder();

temp.append(a).append("b");

String b = temp.toString();

注:这个编译结果以及编译时合并的优化,并非胖哥凭空捏造的,在后文中探讨javap

命令时,这些内容将得到实际的印证。

第 2 个输出true。

与第 1 个输出false 做了一个鲜明对比,区别在于对叠加的变量c 有一个final 修饰符。

从定义上强制约束了c 是不允许被改变的,由于final 不可变,所以编译器自然认为结果是

不可变的。

final 还有更多的特性用于并发编程中,我们将在第5 章中再次与它亲密接触。

第 3 个输出false。

它的叠加内容来源于一个方法,虽然方法内返回一个常量的引用,但是编译器并不会

去看方法内部做了什么,因为这样的优化会使编译器困惑,编译器可能需要递归才能知道

到底返回什么,而递归的深度是不可预测的,递归过后它也并不确保一定返回某一个指定

的常量。另外,即使返回的是一个常量,但是它是对常量的引用实现一份拷贝返回的,这

份拷贝并不是final 的。

 

3. 这段程序的结果是什么?

public static void test3() {
String a = "a";
String b = a + "b";
String c = "ab";
String d = new String(b);
println(b == c);
println(c == d);
println(c == d.intern());
println(b.intern() == d.intern());
}

 结果:

false

false

true

true

当调用 intern()方法时,JVM 会在这个常量池中通过equals()方

法查找是否存在等值的String,如果存在,则直接返回常量池中这个String 对象的地址;若

没有找到,则会创建等值的字符串(即等值的char[]数组字符串,但是char[]是新开辟的一

份拷贝空间),然后再返回这个新创建空间的地址。只要是同样的字符串,当调用intern()

方法时,都会得到常量池中对应String 的引用,所以两个字符串通过intern()操作后用等号

是可以匹配的。

 

4.  String“+”

原始代码为:

String a = "a";
String b = "b";
String c = a + b + "f";

 

编译器会将它编译为:

String a = "a";
String b = "b";
StringBuilder temp = new StringBuilder();
temp.append(a).append(b).append("f");
String c = temp.toString();

 

5. String、StringBuffer与StringBuilder之间区别

关于这三个类在字符串处理中的位置不言而喻,那么他们到底有什么优缺点,到底什么时候该用谁呢?下面我们从以下几点说明一下

 

  5.1.三者在执行速度方面的比较:StringBuilder >  StringBuffer  >  String

 

  5.2.String <(StringBuffer,StringBuilder)的原因

 

    String:字符串常量

 

    StringBuffer:字符创变量

 

    StringBuilder:字符创变量

 

    从上面的名字可以看到,String是“字符创常量”,也就是不可改变的对象。对于这句话的理解你可能会产生这样一个疑问  ,比如这段代码:

 String s = "abcd";
 s = s+1;
 System.out.print(s);// result : abcd1

 

 我们明明就是改变了String型的变量s的,为什么说是没有改变呢? 其实这是一种欺骗,JVM是这样解析这段代码的:首先创建对象s,赋予一个abcd,然后再创建一个新的对象s用来    执行第二行代码,也就是说我们之前对象s并没有变化,所以我们说String类型是不可改变的对象了,由于这种机制,每当用String操作字符串时,实际上是在不断的创建新的对象,而原来的对象就会变为垃圾被GC回收掉,可想而知这样执行效率会有多底。

 

     而StringBuffer与StringBuilder就不一样了,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,这样就不会像String一样创建一些而外的对象进行操作了,当然速度就快了。

 

5.3.一个特殊的例子:

String str = “This is only a” + “ simple” + “ test”;
StringBuffer builder = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

 

你会很惊讶的发现,生成str对象的速度简直太快了,而这个时候StringBuffer居然速度上根本一点都不占优势。其实这是JVM的一个把戏,实际上:

 

    String str = “This is only a” + “ simple” + “test”;

 

    其实就是:

 

    String str = “This is only a simple test”;

 

    所以不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的String对象的话,速度就没那么快了,譬如:

 

    String str2 = “This is only a”;

 

    String str3 = “ simple”;

 

    String str4 = “ test”;

 

    String str1 = str2 +str3 + str4;

 

    这时候JVM会规规矩矩的按照原来的方式去做。

 

5.4.StringBuilder与 StringBuffer

 

    StringBuilder:线程非安全的

 

    StringBuffer:线程安全的

 

    当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。

 

 

 

           对于三者使用的总结: 1.如果要操作少量的数据用 = String

 

                        2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

 

                        3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值