String 地址引用问题

记住一点:栈区存引用和基本类型,不能存对象,而堆区存对象。==是比较地址,equals()比较对象内容。

Stirng = “实现原理”

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池中找是不是存在相同内容的字符串,而是直接创建一个新的字符串,对应新的地址。

new 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) {
        List list = 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,List list){
        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) {
        List list = 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,List list){
        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 {
    
    List result = new ArrayList<>();
    public List binaryTreePaths(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)

你可能感兴趣的:(java,开发语言)