本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看
(一)引言
String str和new String()有什么区别?这个问题之前在面试中有被问到过一次,当时面试结束后也写了自己关于这个题目的理解,不过最近在看Effect Java的时候发现书中也提到了关于String的用法,刚好就放到一起聊一下。
(二)关于String字符串
String应该是Java中最常用的一个对象,他不是八种基本数据类型的其中之一,但是我随便翻了一下项目代码,用String定义的变量超过百分之八十。
publicfinalclassStringimplementsjava.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */privatefinalcharvalue[]; /** Cache the hash code for the string */privateinthash; // 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首先为检查字符串常量池中是否有值相等的字符串,如果有,则不再创建,直接返回该字符串的引用地址,若没有,则创建,然后放到字符串常量池中,并返回新创建的字符串的引用地址。因此看下面这段代码:
Strings1="abc"; Strings2="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()方法创建了几个对象
又是个好问题,这道题的答案是一个或者两个。
写一段代码:
publicclasstest { publicstaticvoidmain(String[] args) { Strings1=newString("ab"); } }
编译后通过javap -v test.class查看字节码:
1、在常量池中创建了ab对象,位置在#3
2、在堆中创建了ab对象
这样的情况下创建了两个对象,一个在常量池中,一个在堆中。
再写一段代码:
publicclasstest { publicstaticvoidmain(String[] args) { Strings1="ab"; Strings2=newString("ab"); } }
同样的方式查看字节码:
首先s1="ab"在常量池中创建了ab对象,存放在常量池的#2位置里,这个时候new
String就只在堆中创建了一个String对象,常量池中因为已经有了,就不会再创建了。
这样的情况下,只创建了一个对象。
(六)总结
虽说怎么写代码都能实现一定的功能,但是有时候换种写法可以省下很多资源。比如将:
Stringstr=newString("abc")
写成:
Stringstr="abc"
虽然只是小小的改动,但是在频繁调用的方法中,就有可能避免创建了百万个String实例。
对于有一定开发经验的程序员,建议可以读一下《Effect-Java》,里面有许多写代码的更优方式。
我是鱼仔,我们下期再见!