重拾Java--功底篇之String

今天看书看到这样一段代码,感觉十分有趣,在这放一下

private static void test(){
    String a = "a" + "b";
    String b = "ab";
    System.out.println(a == b);
}

呐尼,这边怎么用了”==”,不是应该用equals()么?
那么其实,这段代码运行的最终结果是true,如果你了解原理,这篇文章完全可以跳过,如果是猜的小伙伴,有兴趣可以继续往下看一看

想了解为什么会是true,需要了解以下几个知识点:
1.“==”是做什么的?
2.equals()呢?
3.a和b在内存中是什么样存在的?
4.编译时的优化方案

==

首先,”==”在Java中,原始类型如byte、short、int、long、float、double、boolean、char都是直接比较它们的值是否相等,这个不用多说。
但如果是引用类型的对象,那么比较的其实是两个对象的地址。

equals()

equals()方法在jdk源码的Object类中被定义,它可以被重写。
来看下equals()在Object类中的实现:

public boolean equals(Object obj) {
        return (this == obj);
    }

原来源码的原始实现用的就是==比较两个对象的地址是否一致,so easy。那么再来看下String类中的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;
    }

大致看一下代码,分为如下几个步骤:
1. 先判断两个对象地址是否相同,如果地址相同,则返回true
2. 再判断传入对象是否为String,若不是则返回false
3. 判断传入的String长度与当前String是否一致,若不一致则返回false
4. 循坏匹配两个String的每个字符是否相等,若不相等,返回false
5. 直到所有字符比较完成,返回true
好了,现在理解了String类中的equals如何工作了

hashCode()

讲到equals()不得不提一下hashCode(),这是一个Object类提供的native(本地)方法,作用是获取对象的hashCode,它的返回值与System.identityHashCode(Obj)一致。是一组由对象头部一部分二进制位组成的数字,用于标识该对象,但绝不等价于地址。这个hashCode主要在HashMap、HashSet等集合类中的对象写入和查找等算法中用于算法快速定位数据之用。
Ps:在JDK1.7中,如果Hash相关的集合的key是String类型,不再使用hashCode,而是用一种hash32属性。

编译时优化

JVM有时会在编译时对代码进行自动优化,如针对代码String a = "a" + "b",因为”a”和”b”都是常量,他们都储存在常量池中,在编译时其实已经转化成了String a = "ab",无须运行时再计算,同样的道理还有int i = 1 + 3,编译时自动优化为int i = 4
当然编译器做的优化远远不止这些,这边只是稍微提及一下。

String的”+”操作

这边稍微瞄一眼String的”+”操作的工作原理,例如代码String b = a + "b",其中a是一个String类型的字符串,因为a是一个变量,因此这段代码编译好之后是这个样子滴:

StringBuilder temp = new StringBuilder();
temp.append(a).append("b");
String b = temp.toString();

关于StringBuilder类的append源码在这就不分析了。(源码什么的 还是很有趣的!)
这边提一个intern()方法,这个方法的源码注释是这个样子的

* @return  a string that has the same contents as this string, but is
  *          guaranteed to be from a pool of unique strings.

大致意思就是,返回一个与当前字符串内容相同,且在Perm Gen(永久代,即内存的永久保存区域)中保证全局唯一的字符串。
wtf,这是什么意思呢?
首先,Perm Gen是常量池中的一块,它对于同一个值的字符串保证全局唯一。如果一个字符串调用intern(),JVM首先会去常量池中通过equals()选择等值的String,若存在,返回常量池中这个String的地址,如果未找到,会创建等值的字符串,再返回新创建空间的地址。

到这里,有点小累,来个例题放松放松吧

public class test {
    public static String getA(){return "a";}
    public static void main(String[] args) {
        String a = "a";
        final String _a = "a";
        String b = a + "b";
        String c = _a + "b";
        String d = getA() + "b";
        String e = new String(b);

        String compare = "ab";
        System.out.println(b == compare);
        System.out.println(c == compare);
        System.out.println(d == compare);
        System.out.println(e == compare);
        System.out.println(d.intern() == compare);
        System.out.println(e.intern() == compare);
        System.out.println(e.intern() == d.intern());
    }
}

答案是:false true false false true true true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值