深度解析String s=new String(“abc“)创建了几个对象?

解析String s=new String(“abc”)

我们知道想知道两个对象是不是同一个对象,直接用等号比较就可以

    public static void main(String[] args) {
        String strABC = new String("abc");
        String abc = "abc";
        System.out.println(strABC == abc); //结果flase,就表示不是一个对象,内存地址不同
        System.out.println(strABC.equals(abc)); //结果为true
        System.out.println("abc" == "abc"); //结果为true
        System.out.println("abc".getClass().getName()); //java.lang.String
    }

而且一旦你这样写,ide就会有提示,翻译过来是这样的
初始化一个新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本。 除非需要显式的原始副本,否则不需要使用此构造函数,因为字符串是不可变的
在这里插入图片描述
这个问题的根本原因就在于String的特殊性,而且初学编程的时候,有时候也会记错String是8种基本数据类型之一;也是因为他的特殊性,我们正常想得到一个类型的对象的时候都是需要new + class类型,但是8中数据类型和String确不需要用new的方式,直接可以写出来;像这样:
在这里插入图片描述
写个"abc"这就是一个对象,就可以调用java.lang.String类的各种方法了,所以证明"abc",就已经是一个对象了,而且我们知道 java.lang.String一般都是存储在常量池中的,也就是我们写的"abc"是存储在常量池中的;
当我们写 new String(“abc”); 这个的时候,里面包含了"abc",就已经在常量池创建好了一个"abc"对象,new String()又在堆里面创建了一个String类型的对象,所以就是创建了两个对象;而且新创建的字符串是参数字符串的副本,副本的意思就是内容一模一样,但是又不是同一个;

如何证明新创建的字符串是参数字符串的副本?

我们来看这样的一段代码

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String c = "abc";
        String cc = new String(c);
        // 利用反射获取String类型里的char数组
        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);//设置权限 可以对非public修饰的变量操作
        Object o = field.get(c);
        System.out.println(o);

        Object o2 = field.get(cc);
        System.out.println(o2);

        System.out.println("=========================");

        char[] abc = {'a', 'b', 'c'};
        Object abcc = abc;

        char[] abc2 = {'a', 'b', 'c'};
        Object abcc2 = abc2;
        System.out.println(abcc);
        System.out.println(abcc2);
    }

从结果我们可以看到o和o2的内容是一样的,然后我们分别打印他们的内存地址:
在这里插入图片描述
结果如下
在这里插入图片描述
也就是我们的c对象和new出来的cc对象里面实际存储的是同一个char[]数组,我们自己后创建的abc和abc2 他们的内容相同,但是hashcode不同,所以打印的结果不同;
我们在做一波骚操作:

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String c = "abc";
        String cc = new String(c);

        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);//设置权限 可以对非public修饰的变量操作
        Object o = field.get(c);

        Object o2 = field.get(cc);
        System.out.println(o);//打印结果 [C@330bedb4
        System.out.println(o2);//打印结果 [C@330bedb4
        System.out.println(o2 == o); // 打印结果true

        char[] ewfowjeif = (char[]) o2;
        ewfowjeif[0] = 'g';

        System.out.println(c);// 打印结果 gbc
        System.out.println(cc);// 打印结果 gbc

    }

在这里插入图片描述
就这样把 c和cc两个String对象内部存储的char数组的值改了,结果就是打印他们的时候,他们的值也改了

拓展:运行时常量和编译时常量

1、先来看String
其实就是看合在一起几个对象是否有变量或者需要运行,如果是确定值那结果就是确定的,如果有变量或需要运行程序,那就是运行时常量;运行时常量的内存地址是实时运行的到的,不是确定的

    public static void main(String[] args) {
        String a = "a";

        String c = a + "c";//运行时常量
        String cc = "a" + "c";//编译时常量
        String c2 = a + "c";//运行时常量
        String cc2 = "a" + "c";//编译时常量

        System.out.println(c == c2); // 结果为false
        System.out.println(cc == cc2); // 结果为true
        System.out.println(c == cc); // 结果为false

    }

还有一种特殊情况

public class StringABC {

    public static void main(String[] args) {
        System.out.println(Two.a);
    }
}

class Two {

    public static final char a = 'a';//这里除了char还可以是其他8种数据类型,另外String类型也可以

    static {
        System.out.println("Two.class加载了");
    }
}

运行结果
在这里插入图片描述
只打印了a,表示改类没有被加载,我们把代码做一个小小的改动

public class StringABC {

    public static void main(String[] args) {
        System.out.println(Two.a);
    }
}

class Two {

    public static final Object a = 'a';

    static {
        System.out.println("Two.class加载了");
    }
}

打印结果
在这里插入图片描述
类被加载了,只是声明类型不同

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值