记住一点:栈区存引用和基本类型,不能存对象,而堆区存对象。==是比较地址,equals()比较对象内容。
String str1 = "abcd"的实现过程:首先栈区创建str引用,然后在String池(独立于栈和堆而存在,存储不可变量)中寻找其指向的内容为"abcd"的对象,如果String池中没有,则创建一个,然后str指向String池中的对象,如果有,则直接将str1指向"abcd"";如果后来又定义了字符串变量 str2 = "abcd",则直接将str2引用指向String池中已经存在的“abcd”,不再重新创建对象;当str1进行了赋值(str1=“abc”),则str1将不再指向"abcd",而是重新指String池中的"abc",此时如果定义String str3 = "abc",进行str1 == str3操作,返回值为true,因为他们的值一样,地址一样,但是如果内容为"abc"的str1进行了字符串的+连接str1 = str1+"d";此时str1指向的是在堆中新建的内容为"abcd"的对象,即此时进行str1==str2,返回值false,因为地址不一样。
也就是说如果我们进行str +"fjsaf"变量加上字符串的操作的时候会直接在堆中创建对象,在堆中不会像在String池中找是不是存在相同内容的字符串,而是直接创建一个新的字符串,对应新的地址。
String str3 = new String("abcd")的实现过程:直接在堆中创建对象。如果后来又有String str4 = new String("abcd"),str4不会指向之前的对象,而是重新创建一个对象并指向它,所以如果此时进行str3==str4返回值是false,因为两个对象的地址不一样,如果是str3.equals(str4),返回true,因为内容相同。
String b = "Wow"; 和String c = new String("Wow");的区别主要在于它们在内存中的存储方式和对比方式。
存储方式:
String b = "Wow"; 是使用字符串字面量的方式创建字符串对象,它会将字符串"Wow"存储在字符串常量池中。如果字符串常量池中已经存在相同的字符串,则直接返回该字符串的引用。
String c = new String("Wow"); 是使用new关键字显式地创建一个新的字符串对象。它会在堆内存中创建一个新的字符串对象,并将字符串"Wow"存储在该对象中。
对比方式:
String b = "Wow"; 使用==运算符对字符串进行比较时,会比较字符串的引用地址。因为字符串常量池中的字符串是唯一的,所以如果存在相同的字符串字面量,它们的引用地址也是相同的。
String c = new String("Wow"); 使用==运算符对字符串进行比较时,同样会比较字符串的引用地址。由于使用new关键字创建的字符串对象在堆内存中,所以它的引用地址与字符串常量池中的字符串是不同的。
代码示例:
String b = "Wow";
String c = new String("Wow");
System.out.println(b == c); // false,比较引用地址
System.out.println(b.equals(c)); // true,比较字符串内容
String str1 = "abcd";
String str2 = "abcd";
String str3 = new String("abcd");
String str4 = new String("abcd");
System.out.println(str1==str2);//true地址一样
System.out.println(str3==str4);//false,但地址不一样
System.out.println(str3.equals(str3));//true,值一样
System.out.println(str2.equals(str3));//true,值一样
System.out.println((str1+"a")==(str2+"a"));//false;进行了+连接地址不一样
当我们定义一个String s = "hello" 然后传递到函数test(String temp) 那么比较s==temp 得到的值是true 因为我们两个指针相当于都指向了常量池中的一个地址,当然如果我们在test函数中定义String a = "hello" string b = "hell" String c = b+"o" 比较a==temp 那结果应该是true 因为都指向常量池一个地址 ,比较c==temp 为false因为一个指向堆一个指向常量池
public String temp = "hu"; th.checkS(th.temp); public void checkS(String str){ //str 指向常量池中地址 String ss = "hujiyuan"; 同样指向常量池中地址 String sr ="hu"; String su = "h"; su = su+"u"; System.out.println(str == temp); //true System.out.println(sr==str); //true System.out.println(su==str); //false 一个堆中一个常量池中 System.out.println(ss==str); //false }
传递堆中字符串
public String temp = "hu"; th.checkS(th.temp+"jiyuan"); public void checkS(String str){ 指向堆中“hujiyuan”地址 String ss = "hujiyuan"; String sr = new String("hujiyuan"); String su = "huji"; su = su+"yuan"; System.out.println(str == temp); //地址不一样 因为串值都不一样 System.out.println(sr==str); // 堆中单独创建 地址不同 System.out.println(su==str); System.out.println(ss==str); //一个常量池一个堆 }
String pp = new String("hujiyuan"); th.checkS(th.pp); public void checkS(String str){ //指向堆中引用地址 String ss = "hujiyuan"; String sr = new String("hujiyuan"); String su = "huji"; su = su+"yuan"; System.out.println(str == pp); //true 传递过来的一样 System.out.println(sr==str);//false System.out.println(su==str);//false System.out.println(ss==str);//false }
String s ="h"+"u"; 编译器优化会直接当成Stirng s ="hu" 仍然存储在常量池中。
使用StirngBuffer s传递参数 可以在新的函数中更改外部s指向地址的内容,相当于我下面的list
先看我的代码
//值得注意的是list是引用地址,如果我们直接在满足条件的时候进行添加,然后后续对list还进行了操作实际上是操作的最初的list
class Solution {
List> result = new ArrayList<>();
public List> pathSum(TreeNode root, int targetSum) {
Listlist = new ArrayList<>();
if(root==null) return null;
list.add(root.val);
travelsal(root,targetSum-root.val,list);
return result;
}
public void travelsal(TreeNode node,int sum,Listlist){
if(node.right==null&&node.left==null&&sum==0){
System.out.println("------------");
result.add(list);
//System.out.println(result);
System.out.println(list);
System.out.println("------------");
return;
}
if(node.right==null&&node.left==null){
System.out.println(list);
return;
}
if(node.left!=null){
list.add(node.left.val);
travelsal(node.left,sum-node.left.val,list);
list.remove(list.size()-1);
}
if(node.right!=null){
list.add(node.right.val);
travelsal(node.right,sum-node.right.val,list);
list.remove(list.size()-1);
}
}
出现错误 result结果集中list不正确,这是因为我们在代码中虽然将满足状态的list添加到result中了但是我们后续还会对list指向内容进行修改,因为我们传递的是堆中list的地址,因此递归中list始终都是堆中的一个地址没有变
解决方法就是我们每次满足条件的时候我们直接创建一个新的list 这样我们后续不可能对这个地址空间内容有影响了
class Solution {
List> result = new ArrayList<>();
public List> pathSum(TreeNode root, int targetSum) {
Listlist = new ArrayList<>();
if(root==null) return null;
list.add(root.val);
travelsal(root,targetSum-root.val,list);
return result;
}
public void travelsal(TreeNode node,int sum,Listlist){
if(node.right==null&&node.left==null&&sum==0){
// result.add(list);
result.add(new ArrayList<>(list));
return;
}
if(node.right==null&&node.left==null){
System.out.println(list);
return;
}
if(node.left!=null){
list.add(node.left.val);
travelsal(node.left,sum-node.left.val,list);
list.remove(list.size()-1);
}
if(node.right!=null){
list.add(node.right.val);
travelsal(node.right,sum-node.right.val,list);
list.remove(list.size()-1);
}
}
class Solution {
Listresult = new ArrayList<>();
public ListbinaryTreePaths(TreeNode root) {
//采用先序遍历方式
travelsal(root,"");
return result;
}
public void travelsal(TreeNode node,String s){
if(node.left == null&&node.right==null){
s+=node.val;
result.add(s);
return;
}
if(node.left!=null) travelsal(node.left,s+node.val+"->");
if(node.right!=null) travelsal(node.right,s+node.val+"->");
return;
}
这是一个递归解决路径问题,可以看到我们使用了s+node.val作为函数的参数传递,实际上我们传递过去是一个新字符串的地址,在堆中,所以我们其它递归不会对我们结果集中的对象有影响。
实际上就算我们传递的是字符串s本身,然后再函数中对s进行操作,我们外部的s指向的仍然是原来的地址。因为做操作就会在堆中创建一个新的对象。
更加深入地练习请看关于JAVA中String类以形参传递到函数里面,修改后外面引用不能获取到更改后的值 - 知乎 (zhihu.com)