关于Java中String你可能不知道的那些事

来源:互联网 发布:菜鸟java并发编程书籍 编辑:程序博客网 时间:2024/05/22 11:56
一.String的“==”和“equals”
private static void test1() {          Stringa = "a"+"b"+1;          Stringb = "ab1";          System.out.println(a== b );     }

上面这段代码的输出结果是true,可能有很多人知道结果,下面来解释下原因。要解释这个问题,我们需要知道这几点。
1.关于"=="和equals是做什么的?
2.a和b在内存中是什么样的?
3.编译时优化方案。
"=="用于匹配内存单元上的内容,在Java语言中,"=="其实就是对比两个内存单元的内容是否一样,简单说就是两个对象的地址是否一样。如果是基本类型,就是直接比较它们的值。如果是引用对象,比较的就是引用的值。引用的值可以被认为是对象的逻辑地址。如果两个引用保存的对象是同一个对象,就返回true。
equals方法,在Object类中被定义的,默认就是"=="的实现,也就是说如果不覆写equals方法,那么默认的equals就是对比对象的地址。
equals方法之所以存在,是希望子类去重写这个方法,实现对比值的功能。类似的String就实现了equals方法。String的equals方法比较的是字符串的值是否相同。
重写equals方法之后一定要重写hashCode方法吗?其实两者之间没有明确规定,但是一般都建议重写hashCode方法,如果不重写会在hashMap,HashSet中添加对象时出现意外的情况。
a和b的内存情况是什么样的?上例中,a和b指的都是同一个对象"ab1"。这是在Java编译器优化后产生的结果,当编译器在编译代码Stringa="a"+"b"+1时会将其编译为String a="ab1"为何?因为都是常量,编译器认为这三个常量叠加会得到固定的值,无须运行时在计算,所以这样优化,这样做能一定的提升效率。所以上述a和b其实指的是同一个对象,这也说明了String的"+"操作不一定比StringBuilder.append方法慢。
    
 private static String getA(){          return "a" ;     }     publicstatic void test2(){          Stringa = "a";          final String c = "a" ;          Stringb = a+"b";          Stringd = c+"b";          Stringe = getA()+"b";          Stringcompare = "ab";          System.out.println(b== compare );          System.out.println(d== compare );          System.out.println(e== compare );     }

上面说完之后继续猜这段代码,答案是false,true,false。这段代码对于稍微有点复杂,结果不那么好猜,下面解释下具体原因。
1.第一个输出false,b与compare相比,compare是一个常量,那么b为什么不是呢?因为b=a+"b";a并不是一个常量,虽然a作为局部变量也指向一个常量,但是其引用并未约束是不可以改变的,虽然知道它在这段代码不会改变,但运行时任何事情都会发生,在字节码增强技术面前,当代码发生切入后,就可能发生改变,所以编译器不会做这样的优化。
2.第二个输出true,与第一个的区别在于对叠加的变量c有一个final修饰符,final修饰变量表示这个变量的引用不会改变,所以编译器进行了优化。
3.第三个输出false,这点和第一个相像,编译器无法对方法进行有啊。
接下来讨论下String的"=="和equals的区别,对于"=="只有两个变量的引用指向相同的时候才返回true,即两个String是一个对象,new String()产生的对象是不会相同的,而equals只要两个String的值相同就会返回true,一般我们比较两个字符串是否相等多会使用equals来比较。
附上String重写的equals方法:
 
public boolean equals(Object anObject ) {       if (this== anObject ) {           return true ;        }       if (anObject instanceof String) {            StringanotherString = (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 ;    }

二.String的"+"和StringBuilder.append方法
很多书上说StringBuilder.append方法比String的"+"性能快很多,其实也不是绝对的,前面说过String的"+"操作时在拼接常量的时候是可以进行编译器优化的,然而如果字符串不能被编译器优化时,确实应该使用StringBuilder.append方法,String的"+"操作放在循环中,会创建出来无穷多的StringBuilder对象,并且执行append()后再调用toString()来生成一个新的String对象。这些临时对象会占用大量的内存空间,导致频繁的GC,这是String的"+"耗时的主要原因。
附上StringBuilder.append的实现
 
public Abstract StringBuilder append(Stringstr) {       if (str == null)str = "null";       int len = str .length();        ensureCapacityInternal(count + len);       str.getChars(0,len,value,count);       count += len;       return this ;    } void expandCapacity( intminimumCapacity ) {       int newCapacity = value .length* 2 + 2;       if (newCapacity - minimumCapacity< 0)           newCapacity = minimumCapacity;       if (newCapacity < 0) {           if (minimumCapacity < 0) // overflow               throw new OutOfMemoryError();           newCapacity = Integer.MAX_VALUE;        }       value = Arrays. copyOf(value,newCapacity);    }



文章内容参考胖哥的Java特种兵,之前很幸运有机会和胖哥聊过一次,最近打算读读胖哥的书,长长见识。

0 0
原创粉丝点击