JAVA-String详解

一、面试经常会碰到一个问题,就是String不可变

大部分答的时候会讲因为String的源码里面,它是这样的

/** The value is used for character storage. */
    private final char value[];

它是被final修饰的,被final修饰的真正含义是什么呢?一定能讲出是不可变,那么到底是什么不可变啊?我们可以来试一试

 final int[] value = new int[]{1,2,3};
 value = new int[]{1,2,4};

会报错

Error:(33, 9) java: 无法为最终变量value分配值

这说明引用不可变,value不能再指向另一个变量,但是这能说明value的值不可变吗?我们再来试试看

final int[] value = new int[]{1,2,3};
value[1]=4;
System.out.println(value[0]+" "+value[1]+" "+value[2]);

输出就是

1 4 3

因此,final不可变指的是引用对象不可变,而不是对象的值不可变,那么到底是什么让String对象不可变呢?再去源码看看,是不是还有个private,这个让value的值在外部是不可变的。再来看一个是如何表现线程安全的不可变的呢:

//现有一个get方法,在方法内改变string
public static String get(String str){
        str += "aaa";
        System.out.println("get方法中str的hashcode"+str.hashCode());
        return str;
    }
//测试
String str = "123";
System.out.println("str的hashcode"+str.hashCode());
System.out.println(get(str));
System.out.println(str);
System.out.println("最后的str的hashcode"+str.hashCode());

//输出的结果
str的hashcode48690
get方法中str的hashcode1450620111
123aaa
123
最后的str的hashcode48690

可以发现get方法其实形成了一个新的str,而真正的str并没有被改变
*这里有个小tip:String只能通过hashcode的方式获得相对的jvm中的地址,但并不是真实的地址。

二、java中还有个字符串常量值的概念

先来看看神奇的地方:

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = s3.intern();
System.out.println(s1==s2);
System.out.println( s2==s3);
System.out.println(s3==s4);
System.out.println(s4==s2);

//输出
true
false
false
true

s1和s2竟然是一样的,这就是常量池的作用,你创建一个常量,它会先去常量池中寻找是否已经存在,如果存在那就引用同一个,如果不存在那就放进去,通过new操作符创建的字符串对象不指向字符串池中的任何对象,但是可以通过使用字符串的intern()方法来指向其中的某一个。java.lang.String.intern()返回一个保留池字符串,就是一个在全局字符串池中有了一个入口。如果以前没有在全局字符串池中,那么它就会被添加到里面。
因此,使用new创建对象的时候是会创建两个对象或者一个对象,它的过程是先去常量池中寻找是否有,如果有,则再堆中(常量池在方法区中)创建一个新的对象并指向它,如果没有,则在常量池中先创建一个,再在堆中创建一个,还是指向堆中的,不信咱们来看看

String s1 = new String("hello");
String s2 = "hello";
System.out.println(s1==s2);

输出是false,因此使用new创建出来的必定是指向堆里面的。

三、字符串拼接

最经典就是+号问题了,那么String s = s1 + s2到底是个啥,和String s = "hello"+"world"有啥区别吗?其实他们打印输出的结果是一样的,只不过底层的方式是不一样的

String s = s1 + s2;

这种方式,是在底层先创建一个StringBuilder对象,然后进行两次append操作,最后再toString一下输出,而new一个StringBuilder对象是在堆中,所以操作都是在堆中完成的

String s = "hello" + "world";

这种方式,编译的时候会认为+号是没用的,所以实际上等同于

String s ="helloworld";

因此当问到String,StringBuilder,StringBuffer的问题时,为啥String做字符串拼接的效率是最低的就有答案了,它会创建新的对象放在堆里面,如果循环太多而gc来不及收回,那么堆中会被占用大量的空间。

想起来还有String的内容再接着补充吧

你可能感兴趣的:(JAVA-String详解)