String创建对象两种方式的深入理解

引入:我们都知道,创建字符串对象的两种方式:
String name1 = "jack";
String name2 = new String("jack");

1. String name1 = “jack”;的解读
  • "jack"是字符串常量,也叫字符串字面量,位于字符串常量池中
  • name1是一个栈变量,指向这个常量池中的String对象
  • 若常量池中已存在该字面量,则返回常量池中的该字面量地址给name1
  • 若池中没有该字面量,则在池中创建该字面量,再把地址返回给name1
  • jdk1.7及以后,常量池从方法区移入堆中
2. String name2 = new String(“jack”);的解读
  • 首先是创建一个字面量"jack",这也是一次创建对象的过程,会按照1中的步骤来,也就是说,会从常量池中查找是否有"jack"这个字面量,结果找到了。
  • 把"jack"这个字面量作为String参数,调用对应的的构造器,第二次来创建String对象
 public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
  • 这就是说,新建的这个String对象,它虽然在常量池之外,但是它维护的域value[]却与常量池中的"jack"对象的value[]是同一个字符数组,这进一步节省了空间。
  • 也就是说,这第二种创建String对象的方式其实会创建两次String对象,只不过作为过渡的字面量,如果没有引用指向它,很快就会被GC回收。此例中,由于name1指向池中的这个字面量,它并不会被GC回收
  • 如何证明两者指向了同一字符数组?(代码中输出内容都会备注在后面)
	String s1 = "你是大笨蛋!";
	String s2 = new String("你是大笨蛋!");
	//此时只要获得两个String的value[],再比较一下唯一标识,就知道是不是同一个字符数组了!
	//问题是,value是private的,只能通过反射了
	Field valueStringField = String.class.getDeclaredField("value");
	valueStringField.setAccessible(true);//开通权限
	char[] value1 = (char[])valueStringField.get(s1);
	char[] value2 = (char[])valueStringField.get(s2);
	System.out.println(value1 == value2);//true
	System.out.println(System.identityHashCode(value1));//2133927002
	System.out.println(System.identityHashCode(value2));//2133927002
	//两者完全一致,说明是一个字符数组
  • 什么是System.identityHashCode(obj)?为什么不用obj.hashCode()?下面解答:
3. hashCode()
  • hashCode的总合同是:

    • 同一个对象多次调用hashCode,必须保证值一致,但不必从一次执行到另一次执行保持一致
    • 两个对象equals为真 <=> 两个对象的hashCode值相等,该规则表明,当重写hashCode和equals方法时,必须使此规则得到贯彻
    • 两个对象不同,不必保证其hashCode值不同
    • 用数学充分不必要,充要条件来表示就是:
      同一对象 => hashCode值相等
      obj1.equals(obj2)为true <=> hashCode值相等(需要该类重写了equals()方法,没有重写的equals()与 ==等价)
      不同对象,但equals为true => hashCode值相等
  • String重写了hashCode()、equals()方法:

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }    
  • 因此不能用hahCode值来判断两个对象是否为同一对象,由此引出System.identityHashCode(obj)
4. System.identityHashCode(obj)
  • 这是对象的唯一标识,不管该对象是否重写了hashCode方法,只要两个对象地址不同,那么该identityHashCode(obj)就不同。
5. String.intern()方法
  • 如何将一个在堆中新建的String对象放入常量池中?
String string = new String("傻子");
String poolString = string.intern();
  • 该方法会在常量池中新建一个新的String对象,但是value[]和堆中对象是同一个
  • 先证明两者是不同对象:
System.out.println(string == string.intern());//false
System.out.println(System.identityHashCode(string));//21685669
System.out.println(System.identityHashCode(string.intern()));//2133927002
  • 再证明两者的value仍然是同一个:(private域只能通过反射来获得了)
	Field valueStringField = String.class.getDeclaredField("value");
	valueStringField.setAccessible(true);//打卡通道
	char[] value1 = (char[]) valueStringField.get(string);
	char[] value2 = (char[]) valueStringField.get(string.intern());
	System.out.println(System.identityHashCode(value1));//325040804
	System.out.println(System.identityHashCode(value2));//325040804
  • 这证明:两个不同String对象的value[]域指向同一个字符数组
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值