java中String s="abc"及String s=new String("abc")的区别

《String的特性》java

一、String类是final的,不可被继承。
二、String类是的本质是字符数组char[], 而且其值不可改变。
三、Java运行时会维护一个String Pool(String池),JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放运行时中产生的各类字符串,而且池中的字符串的内容不重复。而通常对象不存在这个缓冲池,而且建立的对象仅仅存在于方法的堆栈区。编程

String str=new String("abc"); 紧接着这段代码以后的每每是这个问题,那就是这行代码究竟建立了几个String对象呢?数组

相信你们对这道题并不陌生,答案也是众所周知的,1个或2个。优化

1 首先在堆中(不是常量池)建立一个指定的对象"abc",并让str引用指向该对象
2 在字符串常量池中查看,是否存在内容为"abc"字符串对象
3 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来
4 若不存在,则在字符串常量池中建立一个内容为"abc"的字符串对象,并将堆中的对象与之联系起来翻译

接下来咱们就从这道题展开,一块儿回顾一下与建立String对象相关的一些JAVA知识。 code

咱们能够把上面这行代码分红String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名为str的String类型的变量,所以它并无建立对象;=是对变量str进行初始化,将某个对象的引用赋值给它,显然也没有建立对象;如今只剩下new String("abc")了。那么,new String("abc")为何又能被当作"abc"和new String()呢?对象

咱们来看一下被咱们调用了的String的构造器: 
public String(String original) { //other code ... } 你们都知道,咱们经常使用的建立一个类的实例(对象)的方法有如下两种:
1、使用new建立对象。 
2、调用Class类的newInstance方法,利用反射机制建立对象。
咱们正是使用new调用了String类的上面那个构造器方法建立了一个对象,并将它的引用赋值给了str变量。同时咱们注意到,被调用的构造器方法接受的参数也是一个String对象,这个对象正是"abc"。由此咱们又要引入另一种建立String对象的方式的讨论——引号内包含文本。这种方式是String特有的,而且它与new的方式存在很大区别。blog

String str="abc"; 
毫无疑问,这行代码建立了一个String对象。 继承

String a="abc"; String b="abc"; 那这里呢?
答案仍是一个。 内存

String a="ab"+"cd"; 再看看这里呢?
经过编译器优化后,获得的效果是String a="abcd"
此时,若是字符串常量池中存在abcd,则该语句并不会建立对象,只是讲字符串常量池中的引用返回而已。
若是字符串常量池中不存在abcd,则会建立并放入字符串常量池,并返回引用,此时会有一个对象进行建立。

说到这里,咱们就须要引入对字符串池相关知识的回顾了。 
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着不少String对象,而且能够被共享使用,所以它提升了效率。因为String类是final的,它的值一经建立就不可改变,所以咱们不用担忧String对象共享而带来程序的混乱。字符串池由String类维护,咱们能够调用intern()方法来访问字符串池。 

咱们再回头看看String a="abc",这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。若是有,则再也不建立新的对象,将使用串池里原来的那个内存,直接返回已存在对象的引用,而不会从新分配内存;若是没有,则先建立这个对象,而后把它加入到字符串池中,再将它的引用返回。

而若是用String s=new String("abc"),无论串池里有没有"abc",它都会在堆中从新分配一块内存,定义一个新的对象。
所以咱们提倡你们用引号包含文本的方式来建立String对象以提升效率,实际上这也是咱们在编程中常采用的。

再看下个例子:

String s="java"+"blog";//直接将javablog对象放入字符串池中。
System.out.println(s=="javablog");//结果是true;

String str1="java";//指向字符串池
String str2="blog";//指向字符串池
String s = str1+str2; // +运算符会在堆中创建起两个String对象,这两个对象的值分别是“java”,"blog",也就是说从字符串常量池中复制这两个值,而后再堆中建立两个对象。而后再创建对象s, 而后将“javablog”的堆地址赋给s. 这句话共建立了3个String对象。
System.out.println(s=="javablog");//由于内存地址不一样,结果是false;

String s=str1+"blog";//不放在字符串池中,而是在堆中分分配。
System.out.println(s=="javablog");//结果是false;

JVM对形如String str="java"+"blog";根据编译器合并已知量的优化功能,在池中开辟一块空间,存放合并后的String常量"javablog"。而String s=str1+str2;是在运行时候才能知道的,也就是说str1+str2是在堆里建立的因此结果为false了。

总之,建立字符串有两种方式:两种内存区域(pool,heap) 1.""建立的字符串在字符串池中。 2.new 建立字符串时,首先查看池中是否有相同的字符串,若是有则拷贝一份放到堆中,而后返回堆中的地址;若是池中没有则在堆中建立一份,而后返回堆中的地址, 3.在对字符串赋值时,若是右操做数含有一个或一个以上的字符串引用时,则在堆中再创建一个字符串对象,返回引用。如:String s= str1+"blog";

相关文章
相关标签/搜索