写了那么久的String字符串,你可能根本不懂它!

461 阅读4分钟

本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看

(一)引言

String str和new String()有什么区别?这个问题之前在面试中有被问到过一次,当时面试结束后也写了自己关于这个题目的理解,不过最近在看Effect Java的时候发现书中也提到了关于String的用法,刚好就放到一起聊一下。

(二)关于String字符串

String应该是Java中最常用的一个对象,他不是八种基本数据类型的其中之一,但是我随便翻了一下项目代码,用String定义的变量超过百分之八十。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}

关于String的几个基本知识点在String类的源码定义中就能看到:

1、在JDK8中,String实例的值是通过char数据存储的。(JDK9时,String实例的存储由char变成了byte数组,原因是使用byte数组可以减少一半的内存)

2、String类被final修饰,因此String不能被继承,value变量被final修饰,因此String实例不能被修改

3、String类实现了Serializable, Comparable, CharSequence接口

(三)关于字符串常量池

字符串常量池是虚拟机中的内容,但是接下来的几个问题需要用到,就简单了解下。为了让String字符串可以复用,Java虚拟机中设置了一种叫做字符串常量池的东西。

Java为了避免产生大量的String对象,设计了一个字符串常量池。工作原理是这样的,创建一个字符串时,JVM首先为检查字符串常量池中是否有值相等的字符串,如果有,则不再创建,直接返回该字符串的引用地址,若没有,则创建,然后放到字符串常量池中,并返回新创建的字符串的引用地址。因此看下面这段代码:

String s1="abc";
String s2="abc";
System.out.println(s1==s2);  //返回true

返回的结果就是true,因为指向了同一个对象。

关于字符串常量池的位置,在不同版本的JDK中有所不同:

JDK1.7之前字符串常量池属于运行时常量池的一部分,存放在方法区

JDK1.7之后字符串常量池被从方法区拿到了堆中

(四)String str和new String()有什么区别

回到开始的问题,String str和new String()有什么区别?区别在于,String str创建的字符串保存在字符串常量池中,并且可复用。new String()创建的字符串按照最标准的对象创建方式,生成在堆中,并且一个new String会在堆中创建一个对象。下面这张图应该可以把这个流程讲清楚。

s1==s2返回true,s1==s3返回false。

(五)new String()方法创建了几个对象

又是个好问题,这道题的答案是一个或者两个。

写一段代码:

public class test {
    public static void main(String[] args) {
        String s1=new String("ab");
    }
}

编译后通过javap -v test.class查看字节码:

1、在常量池中创建了ab对象,位置在#3

2、在堆中创建了ab对象

这样的情况下创建了两个对象,一个在常量池中,一个在堆中。

再写一段代码:

public class test {
    public static void main(String[] args) {
        String s1="ab";
        String s2=new String("ab");
    }
}

同样的方式查看字节码:

首先s1="ab"在常量池中创建了ab对象,存放在常量池的#2位置里,这个时候new String就只在堆中创建了一个String对象,常量池中因为已经有了,就不会再创建了。

这样的情况下,只创建了一个对象。

(六)总结

虽说怎么写代码都能实现一定的功能,但是有时候换种写法可以省下很多资源。比如将:

String str=new String("abc")

写成:

String str="abc"

虽然只是小小的改动,但是在频繁调用的方法中,就有可能避免创建了百万个String实例。

对于有一定开发经验的程序员,建议可以读一下《Effect-Java》,里面有许多写代码的更优方式。

我是鱼仔,我们下期再见!