1.String str=“aaa“与 String str=new String(“aaa”)一样吗?


String x = "张三";
String y = "张三";
String z = new String("张三");
System.out.println(x == y); // true
System.out.println(x == z); // false

String x = "张三" 的方式,Java 虚拟机会将其分配到常量池中,而常量池中没有重复的元素,比如当执行“张三”时,java虚拟机会先在常量池中检索是否已经有“张三”,如果有那么就将“张三”的地址赋给变量,如果没有就创建一个,然后在赋给变量;

String z = new String(“张三”) 则会被分到堆内存中,即使内容一样还是会创建新的对象。

public class StringTest {
    public static void main(String[] args){
        String s1="Hello";
        String s2="Hello";
        String s3=new String("Hello");
        System.out.println("s1和s2 引用地址是否相同:"+(s1 == s2));//true
        System.out.println("s1和s2 值是否相同:"+s1.equals(s2));//true
        System.out.println("s1和s3 引用地址是否相同:"+(s1 == s3));//false
        System.out.println("s1和s3 值是否相同:"+s1.equals(s3));//false
    }
}

"==“是判断两个对象引用的地址是否相同,也就是判断是否为同一个对象.

s1与s2 返回为true,s1与s3返回则是false。说明s1与s2 引用的同一个对象的地址,s3则与其它两个引用不是同一个对象地址。

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

那为什么s3与s1、s2引用的不是同一个字符串地址呢?

String s3=new String(“Hello”); JVM首先是在字符串常量池中找"Hello” 字符串,如果没有就创建字符串常量,然后放到常量池中,若已存在,则不需要创建;当遇到 new 时,还会在内存(不是字符串常量池中,而是在堆里面)上创建一个新的String对象,存储"Hello"地址,并将内存上的String对象引用地址返回,所以s3与s1、s2引用的不是同一个字符串地址。

s1与s2指向的都是常量池中的字符串常量,所以它们比较的是同一块内存地址,而s3指向的是堆里面的一块地址,说的具体点应该是堆里面的Eden区域,s1跟s3,s2跟s3比较都是不相等的,都不是同一块地址。

了解了String类的工作原理,回归问题本身:
在String的工作原理中,已经提到了,new一个String对象,是需要先在字符串常量中查找相同值或创建一个字符串常量(有则用没有则创建),然后再在内存中创建一个String对象,所以String str = new String(“xyz”); 会创建一个或两个对象。

相关延申:

String s=new String("abc")创建了几个对象?

我们可以把上面这行代码分成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 ...  }   大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
一、使用new创建对象。 
二、调用Class类的newInstance方法,利用反射机制创建对象。
我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同时我们注意到,被调用的构造器方法接受的参数也是一个String对象,这个对象正是"abc"。由此我们又要引入另外一种创建String对象的方式的讨论——引号内包含文本。
这种方式是String特有的,并且它与new的方式存在很大区别。  
String str="abc";  
毫无疑问,这行代码创建了一个String对象。  
String a="abc";  String b="abc";   那这里呢?
答案还是一个。  
String a="ab"+"cd";   再看看这里呢?
答案是三个。
说到这里,我们就需要引入对字符串池相关知识的回顾了。  
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。  
我们再回头看看String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。因此,我们不难理解前面三个例子中头两个例子为什么是这个答案了。
只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,对此我们不再赘述。因此我们提倡大家用引号包含文本的方式来创建String对象以提高效率,实际上这也是我们在编程中常采用的。
栈(stack):主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆。 
堆(heap):用于存储
PS:对于String s5 = new String("Hello World"); 创建了几个对象,我先前理解有误。
总结各种看法,因为有new,所以堆中必然有一个对象。另外,如果常量池中已有"Hello World",则不创建,没有,则在常量池中创建。所以这句代码,究竟在内存中创建一个还是两个对象,视情况而定。 

原文参考:https://blog.csdn.net/u013003837/article/details/28862657


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM