遇到了下面这样的问题,通过代码验证。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class jjj {
public static void main(String[] args) {
List<String> l1 = new ArrayList<>();
List<String> l2 = new LinkedList<>();
l1.add("c");
l2.add(new String( "c"));
System.out.println(l1.equals(l2));
List<String> l1_ = new ArrayList<>();
List<String> l2_ = new ArrayList<>();
l1_.add("a");
l2_.add("a");
System.out.println(l1_.equals(l2_));
StringBuilder a = new StringBuilder("a");
StringBuilder b = new StringBuilder("a");
System.out.println(a.equals(b));
//System.out.println(a==b);
String a1 = new String("a");
String a2 = new String("a");
System.out.println(a1.equals(a2));
}
}
验证结果如下
假设上面库函数中的类全部都重写了equals函数,那么得到的结果应该是全部true。
但是并非如此,下面有关说明。
1>
List接口,无论是以何种方式实现,都是会根据其内部对象及顺序判断是否相等,对于这个可变类,实现了观察等价性。
下面代码验证:
List<String> l1_ = new ArrayList<>();
List<String> l2_ = new ArrayList<>();
l1_.add("a");
l1_.add("b");
l2_.add("b");
l2_.add("a");
System.out.println(l1_.equals(l2_));
2>
关于StringBuilder类,往往关注其可变性,但忽略了其equals函数。
StringBuilder作为可变类,实现的是行为等价性。
还有关于String类。
首先,是存在常量池的,其用来存储字符串常量。
String s_t1 = "a";
String s_t2 = "a";
System.out.println(s_t1.equals(s_t2));
System.out.println(s_t1 == s_t2);
结果:
上面创建String对象没有使用 new String。
在创建字符串对象时,首先编译期,JVM会去常量池来检验该字符串是否应存在。
如果该字符串没有出现在常量区中,那么会在常量池中开辟一个空间来存贮字符串比如“a",接下来会在栈上开辟一个名为s_t1(遵循上面的举例)的空间,保存着常量池中的地址值。
如果该对象已经出现在常量池中,那么不会开辟新的空间,而是会在栈上开辟一个名为s_t1的空间来存储常量池中的地址。
String类的equals类是已经重写了的。 同时“==”也会判断出两个字符串相等,因为此时查看的是字符串存储的地址(在常量池中)。
接下来,如果是以new String来创建对象。在编译时期,会去常量池检验是否该字符串 比如“a"已经出现过,如果没有,会在常量池中创建这样的一个新的对象;在运行时期,构造器在堆中new一个空间,将“a"字符串复制过来,在栈中开辟名为ss1或者ss2的空间,存放new出来的对象的地址。
这样等价于当new一个对象时,可能创建了一个对象,也可能是创建了两个对象。
String类的库函数equals已经实现了重写。
StringBuilder类没有实现方法equals的重写,依旧等价于==,会去根据地址是否相等来判断。
String ss1 = new String("a");
String ss2 = new String("a");
System.out.println(ss1.equals(ss2));
System.out.println(ss1==ss2);
3>
import java.util.ArrayList;
import java.util.List;
public class test2 {
public static void main(String[] args) {
List<String> s = new ArrayList<>();
s.add("s");
for(String s_temp:s) {
s.remove(s_temp);
}
}
}
即使去掉的元素是最后一个新添加的元素,依然会动态编译异常。
当remove操作过后,List中0对应为空,但此时还是会以String类型对List中的对象进行访问,此时产生了异常,类似于访问空指针的问题。
4>
Integer i = 1;
String s__ = true+"a"+i;
System.out.println(s__);
输出结果为:
可见,上述会发生动态类型转换,那么此时会进行一个其他类型向String类型的转换。