String s1 = "123"; String s2 = "123"; System.out.println(s1 == s2); // true System.out.println(s1.equals(s2)); // true
首先需要知道的是第一种方式是常量池中的对象的地址的比较
第二种是利用了String类的equals方法比较,看下源码:
public boolean equals(Object anObject) { // 判断当前的string对象是否和传入进来的对象的地址值相同 if (this == anObject) { return true; } // 传入进来的对象是否是字符串类型的 if (anObject instanceof String) { // 向下转型,利用多态性质 String anotherString = (String)anObject; // 判断两个字符串的长度是否是相等的 int n = value.length; if (n == anotherString.value.length) { // 拿到当前对象的数组和比较进来的数组 // 居然还有这种操作,对于同种数据类型的来说,可以直接来进行操作 char v1[] = value; char v2[] = anotherString.value; int i = 0; // 遍历每一个字符数组中的每一个元素 while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } // 如果对象既不是和当前string对象地址相同;也不是同一个类型的;长度也不相等 return false; }
可以看到返回true的只有两个地方:一、是同一个对象;第二个、字符串中的每个字符都是相同的。
但是对于字符串来说,无论是哪一种都是可以的,只不过第一种的话更加实用而已。
String s = new String("abc");
首先第一步:确定常量池中是否有"abc"这个对象,如果没有,那么先在常量池中创建"abc"对象;如果有,那么就不需要在创建;
然后再从堆中的其他空间来创建一个对象,"abc"
所以可能创建了一个对象,也可能是创建了两个对象。这取决于常量池中是否有"abc"
String s1 = new String("abc"); String s2 = "abc"; System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // true
第一行代码先去常量池中查找是否"abc"对象,然后再去堆中创建一个对象,返回s1指向堆中的内存;
第二行中直接返回常量池中的地址;
第三行中==比较的是两个字符串的地址不同,所以返回为false;
第四行中利用的equals方法比较的是两个字符串,上面已经分析过了。
这里需要注意的是:堆内存中的和常量池中的都是用数组来进行存储的,比较的是数组中的字符
String类上就是用final来进行修饰的,所以这个类是无法继承的;
String类中用来存放成员的字符串使用char数组,只不过使用final来修饰的变量,如果一旦确定了值,那么对应的地址将无法来进行修改。但是我们可以修改其中的数组中的值,但是String类并没有来给我们提供。
int i = 0; while(true){ if(i>obj.length) break; obj[i] = i; i++; }
可以赋值,也可以来修改值。但是数组的引用是固定的。
String类但是我们去做修改,并没有提供对应的方法来进行操作。
看看下面这段代码:
String s = "Java"; s = "HTML";
值是不可变的,说的是存储的字符串的地址是没有办法改变的,但是引用可以随便指向。
对于状态来说,已经是固定的,无法来对其进行修改。
String s1 = "a"+"b"+"c"; String s2 = "abc"; System.out.println(s1 == s2); // true
思考问题:对于"a"、"b"、"c","abc"常量池中是否存在。
如果来进行验证,通过intern()方法来进行验证:
String a = new String("a"); String intern = a.intern(); System.out.println(a==intern); // false
这说明了常量池中已经有了这个"a",其实一开始就是不需要验证的。
String s1 = "ab"; String s2 = "abc"; String s3 = s1 + "c"; System.out.println(s3 == s2); // false System.out.println(s3.equals(s2)); // true
需要注意的:第三行中的使用方式
因为s1是变量,所以在编译的时候无法确定,只有在运行的时候才会确定下来值。
底层实现是会先创建一个StringBuilder/StringBuffer的对象来进行append()方法来进行追加,然后将追加后的字符串数据利用toString的方式创建出来一个新的String对象。所以s3中其实返回的是堆中的地址
与下面的类似:
String s3 = new String("abc");
s3和s2相比不是同一块内存地址,所以不可能相等
这里我再衍生一下:
String s1 = "ab"; String s2 = "abc"; String s3 = "ab" + "c"; System.out.println(s3 == s2); // true System.out.println(s3.equals(s2)); // true
这里为什么又是不相等的了?因为这里是在编译阶段做的事情,相当于执行完了第三行代码,已经返回了常量池中的"abc"的地址了。