消失了一段时间,也不知道在远方的电脑面前是否有人惦记着我。–pluto
在Java里面String肯定是最常用的数据类型没有之一,但是你真的了解String这个类吗?
String类是一个final修饰的,这表明String是一个不能被继承的类。我当初还傻乎乎想过要继承String,编译器报错,网上查了一查才发现String是final修饰,如果当时小手一点看一看源码,是不是就可以节省一下上网百度的时间了。
在Java8里面String类有8种构造方法,其中两种被标记过时。这里重点讲讲这个构造方法String(String original),源码实现如下:
public String(String original) {
//把原对象的value和hash赋值给新对象
this.value = original.value;
this.hash = original.hash;
}
可能你们不是很理解为什么要单独说这么一个构造方法,这一点后续会有讲解,看下面这段代码
public static void main(String[] args) {
String s1 = "123";
String s2 = "123";
String s3 = new String("123");
if (s1 == s2)
System.out.println("1");
if (s2 == s3)
System.out.println("2");
}
运行结果如图
有没有一点小惊讶?我第一次看见的时候感觉很不可思议的,感觉书上说法坑我,因为书上说字符串的比较要用equals()这个方法,不能使用==进行比较。
真的理解这段代码其实也不是很难,至少我看了《深入理解Java虚拟机》(周志明著)这本书过后,我是觉得这个问题理解起来没有任何难度的。如图(图有点丑,请不要介意)
在用引号创建字符串的时候,JVM会先在字符串常量池去寻找有没有对应的字符串,如果有就返回引用,没有就新建一个字符串并且放入字符串常量池。所以这里的s1==s2是成立,但是s2==s3就不成立,因为这里已经不是同一个对象了。为了验证我的说法,我修改了一下代码
public static void main(String[] args) {
String s1 = "123";
String s2 = "123";
String s3 = new String("123");
String s4 = getString("123");
if (s4 == s2) {
System.out.println("3");
}
}
public static String getString(String s) {
return s;
}
运行结果就是3,没有任何悬念。是不是觉得自己棒棒?感觉又懂了好多东西?我们来一组代码(这是Java7开始支持的)
public static void main(String[] args) {
String s1 = "123";
String s3 = new String("123");
switch (s3) {
case "123":
System.out.println("123");
break;
default:
System.out.println("321");
break;
}
}
猜猜结果是123还是321呢?我猜你是不知道,我上面已经提示过了。结果是123,我猜你可能还是不懂为什么。看一段编译结果代码
public static void main(String[] var0) {
String var1 = "123";
String var2 = new String("123");
byte var4 = -1;
//选择的是hash值
switch(var2.hashCode()) {
case 48690:
//进行内容比较
if (var2.equals("123")) {
var4 = 0;
}
default:
switch(var4) {
case 0:
System.out.println("123");
break;
default:
System.out.println("321");
}
}
}
看到上面的编译结果,结合我说的String(String original)这个构造方法,你是不是明白了什么?首先编译器会自动定义一个byte var4=-1,然后对我输入的字符串的hashCode()进行比较,接着对case中的hash值进行内容比较,重新给var4变量赋值,最后执行default里面的语句。这个判断方式和上面说的String(String original)的构造方式不谋而合。是不是觉得打开眼界?我们继续最后一波科普。
public static void main(String[] args) {
String s1 = "123";
String s2 = "1";
String s3 = "23";
String s4 = "1" + "23";
String s5 = s2 + s3;
if (s1 == s4)
System.out.println("1");
if (s1 == s5) {
System.out.println("2");
}
}
直接上结果截图吧
看到现在,你晕了吗?为什么只打印输出了一个结果?来一波编译结果源码分析
public static void main(String[] var0) {
String var1 = "123";
String var2 = "1";
String var3 = "23";
//这里是重点,和java源码已经不一样了
String var4 = "123";
//这里用的是StirngBuilder,具体看下图
String var5 = var2 + var3;
if (var1 == var4) {
System.out.println("1");
}
if (var1 == var5) {
System.out.println("2");
}
}
再来一张更直观的
看看实现细节,String s4 =”1”+”23” 被直接解释成String s4=”123”,而String s5=s2+s3 这个被转换成StringBuilder调用了两次append()方法,最后调用的toString()方法来返回的。讲到这里,你对上面的结果能理解了吗?
看完这篇文章,你有没有恍然大悟的感觉?