最近遇到一个线上问题,原因是忽略的引用的一些语法,导致出错,现在记录一下:
@Test
public void testList(){
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
List<String> list2 = new ArrayList<String>();
list2.add("1");
list2.add("2");
//我现在想获得一个新list,内容是list中有,但list2中没有的
//于是我用了下面的代码实现
List<String> list1 = list;
list1.removeAll(list2);
System.out.println(list);//[3]---看这个,原始的list也变了
System.out.println(list1);//[3]
System.out.println(list2);//[1, 2]
//很明显,这里原始list的结果,并不是我想要的,我并不想让list发生变化
//但由于我是直接将list的引用给了list1,其实这个时候list1和list是指向同一个对象的,所以对list1做操作,list也会发生变化,因为这两个压根就是一个东西
//做个比喻,list就是一个人,他的名字叫list,他有三颗糖,分别1号,2号,3号
//那么同时list2就是另外一个人,他的名字叫list2,他有两颗糖,分别1号,2号
//我想要的,是获得list有,list2却没有的糖,我采用了排除法
//但我做的操作却是:List<String> list1 = list;
//这个相当于给list另外起了个外号,叫list1,但还是同一个人
//所以我拿走list1的糖,其实也就是拿走了list的糖,因为就是一个人
}
这个问题,对于自定义对象一样,只给引用,再操作那个新的引用,其实操作的都是一个对象,但是,对于String字符串,却不是这样的
@Test
public void testString(){
String a = "1";
String b = a;
b += "2";
System.out.println(a);//1
System.out.println(b);//12
//看这个操作,照样是引用,但a却没有因为b的改变
}
扩展总结:
基本类型对象存放在栈中,
引用类型对象数据存放在堆中,
String类型数据存在堆中,但可能存两份,看下面代码
String a = new String("good");
String b = "good";
String c = new String("good");
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(b == c);
第一行:首先在String常量池中查找有没有字符常量“good”.因为没有所以创建“good”对象,当执行new String(“good”)时,则在Java的堆中创建一个”good”对象,而a是该对象的引用,因此共计创建2个对象,都在堆中,一个在常量池内,一个在常量池外,两个good虽然是两个对象,但却占一块内存,都在堆中,只不过是常量池外的good指向常量池中的goog,a引用指的是常量池外的good
第二行:首先在String常量池中查找有没有字符串常量“good”,有则直接将b作为String常量池中“good”的一个引用,当你重新声明一个String型变量为“good”时,将使用串池里原来的那个“good”,而不用重新分配内存,也就是说,str与str1将会指向同一块内存,因此,此时没有创建任何对象。
第三行:依次在String常量池中查找有没有字符串常量“good”,有则不进行再次创建,由于这里用了new关键字(有new就有对象),所以在Java堆中又创建了一个“good”对象(地址与第一句在堆中创建的地址不同),而c则是这个对象的引用,因此执行此句时,创建了1个对象。
下图是a,b,c,good,list,list1,list2的存储位置:
本文通过具体示例探讨了Java中对象与引用的区别,解释了为何直接赋值引用会导致对象变化,并对比了String类型的特殊行为。此外,还深入解析了String常量池与堆内存中的对象存储机制。
139

被折叠的 条评论
为什么被折叠?



