`
java183
  • 浏览: 13373 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

【今天读了篇关于Java字符的文章,值得收藏】到底创建了几个String对象?

阅读更多

我们首先来看一段代码:

Java代码
String str=new String("abc"); 

紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些JAVA知识。

我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么又能被看成"abc"和new String()呢?我们来看一下被我们调用了的String的构造器:
Java代码
public String(String original) {  
    //other code ...  


大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
使用new创建对象。
调用Class类的newInstance方法,利用反射机制创建对象。

我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同时我们注意到,被调用的构造器方法接受的参数也是一个String对象,这个对象正是"abc"。由此我们又要引入另外一种创建String对象的方式的讨论——引号内包含文本。

这种方式是String特有的,并且它与new的方式存在很大区别。
Java代码
String str="abc"; 

毫无疑问,这行代码创建了一个String对象。
Java代码
String a="abc";  
String b="abc"; 

那这里呢?答案还是一个。
Java代码
String a="ab"+"cd"; 

再看看这里呢?答案仍是一个。有点奇怪吗?说到这里,我们就需要引入对字符串池相关知识的回顾了。

在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。

我们再回头看看String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。因此,我们不难理解前面三个例子中头两个例子为什么是这个答案了。

对于第三个例子:
Java代码
String a="ab"+"cd"; 

由于常量的值在编译的时候就被确定了。在这里,"ab"和"cd"都是常量,因此变量a的值在编译时就可以确定。这行代码编译后的效果等同于:
Java代码
String a="abcd"; 

因此这里只创建了一个对象"abcd",并且它被保存在字符串池里了。

现在问题又来了,是不是所有经过“+”连接后得到的字符串都会被添加到字符串池中呢?我们都知道“==”可以用来比较两个变量,它有以下两种情况:
如果比较的是两个基本类型(char,byte,short,int,long,float,double,boolean),则是判断它们的值是否相等。
如果表较的是两个对象变量,则是判断它们的引用是否指向同一个对象。

下面我们就用“==”来做几个测试。为了便于说明,我们把指向字符串池中已经存在的对象也视为该对象被加入了字符串池:
Java代码
public class StringTest {  
    public static void main(String[] args) {  
        String a = "ab";// 创建了一个对象,并加入字符串池中  
        System.out.println("String a = \"ab\";");  
        String b = "cd";// 创建了一个对象,并加入字符串池中  
        System.out.println("String b = \"cd\";");  
        String c = "abcd";// 创建了一个对象,并加入字符串池中  
 
        String d = "ab" + "cd";  
        // 如果d和c指向了同一个对象,则说明d也被加入了字符串池  
        if (d == c) {  
            System.out.println("\"ab\"+\"cd\" 创建的对象 \"加入了\" 字符串池中");  
        }  
        // 如果d和c没有指向了同一个对象,则说明d没有被加入字符串池  
        else {  
            System.out.println("\"ab\"+\"cd\" 创建的对象 \"没加入\" 字符串池中");  
        }  
 
        String e = a + "cd";  
        // 如果e和c指向了同一个对象,则说明e也被加入了字符串池  
        if (e == c) {  
            System.out.println(" a  +\"cd\" 创建的对象 \"加入了\" 字符串池中");  
        }  
        // 如果e和c没有指向了同一个对象,则说明e没有被加入字符串池  
        else {  
            System.out.println(" a  +\"cd\" 创建的对象 \"没加入\" 字符串池中");  
        }  
 
        String f = "ab" + b;  
        // 如果f和c指向了同一个对象,则说明f也被加入了字符串池  
        if (f == c) {  
            System.out.println("\"ab\"+ b   创建的对象 \"加入了\" 字符串池中");  
        }  
        // 如果f和c没有指向了同一个对象,则说明f没有被加入字符串池  
        else {  
            System.out.println("\"ab\"+ b   创建的对象 \"没加入\" 字符串池中");  
        }  
 
        String g = a + b;  
        // 如果g和c指向了同一个对象,则说明g也被加入了字符串池  
        if (g == c) {  
            System.out.println(" a  + b   创建的对象 \"加入了\" 字符串池中");  
        }  
        // 如果g和c没有指向了同一个对象,则说明g没有被加入字符串池  
        else {  
            System.out.println(" a  + b   创建的对象 \"没加入\" 字符串池中");  
        }  
    }  


运行结果如下:
String a = "ab";
String b = "cd";
"ab"+"cd" 创建的对象 "加入了" 字符串池中
a  +"cd" 创建的对象 "没加入" 字符串池中
"ab"+ b   创建的对象 "没加入" 字符串池中
a  + b   创建的对象 "没加入" 字符串池中

从上面的结果中我们不难看出,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,对此我们不再赘述。

但是有一种情况需要引起我们的注意。请看下面的代码:
Java代码
public class StringStaticTest {  
    // 常量A  
    public static final String A = "ab";  
 
    // 常量B  
    public static final String B = "cd";  
 
    public static void main(String[] args) {  
        // 将两个常量用+连接对s进行初始化  
        String s = A + B;  
        String t = "abcd";  
        if (s == t) {  
            System.out.println("s等于t,它们是同一个对象");  
        } else {  
            System.out.println("s不等于t,它们不是同一个对象");  
        }  
    }  


这段代码的运行结果如下:
s等于t,它们是同一个对象

这又是为什么呢?原因是这样的,对于常量来讲,它的值是固定的,因此在编译期就能被确定了,而变量的值只有到运行时才能被确定,因为这个变量可以被不同的方法调用,从而可能引起值的改变。在上面的例子中,A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了。也就是说:
Java代码
String s=A+B; 

等同于:
Java代码
String s="ab"+"cd"; 

我对上面的例子稍加改变看看会出现什么情况:
Java代码
public class StringStaticTest {  
    // 常量A  
    public static final String A;  
 
    // 常量B  
    public static final String B;  
 
    static {  
        A = "ab";  
        B = "cd";  
    }  
 
    public static void main(String[] args) {  
        // 将两个常量用+连接对s进行初始化  
        String s = A + B;  
        String t = "abcd";  
        if (s == t) {  
            System.out.println("s等于t,它们是同一个对象");  
        } else {  
            System.out.println("s不等于t,它们不是同一个对象");  
        }  
    }  


它的运行结果是这样:
s不等于t,它们不是同一个对象

只是做了一点改动,结果就和刚刚的例子恰好相反。我们再来分析一下。A和B虽然被定义为常量(只能被赋值一次),但是它们都没有马上被赋值。在运算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s就不能在编译期被确定,而只能在运行时被创建了。

由于字符串池中对象的共享能够带来效率的提高,因此我们提倡大家用引号包含文本的方式来创建String对象,实际上这也是我们在编程中常采用的。

接下来我们再来看看intern()方法,它的定义如下:
Java代码
public native String intern(); 

这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。

我们来看这段代码:
Java代码
public class StringInternTest {  
    public static void main(String[] args) {  
        // 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象  
        String a = new String(new char[] { 'a', 'b', 'c', 'd' });  
        String b = a.intern();  
        if (b == a) {  
            System.out.println("b被加入了字符串池中,没有新建对象");  
        } else {  
            System.out.println("b没被加入字符串池中,新建了对象");  
        }  
    }  


运行结果:
b没被加入字符串池中,新建了对象

如果String类的intern()方法在没有找到相同值的对象时,是把当前对象加入字符串池中,然后返回它的引用的话,那么b和a指向的就是同一个对象;否则b指向的对象就是JAVA虚拟机在字符串池中新建的,只是它的值与a相同罢了。上面这段代码的运行结果恰恰印证了这一点。

最后我们再来说说String对象在JAVA虚拟机(JVM)中的存储,以及字符串池与堆(heap)和栈(stack)的关系。我们首先回顾一下堆和栈的区别:
栈(stack):主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆。
堆(heap):用于存储对象。

我们查看String类的源码就会发现,它有一个value属性,保存着String对象的值,类型是char[],这也正说明了字符串就是字符的序列。

当执行String a="abc";时,JAVA虚拟机会在栈中创建三个char型的值'a'、'b'和'c',然后在堆中创建一个String对象,它的值(value)是刚才在栈中创建的三个char型值组成的数组{'a','b','c'},最后这个新创建的String对象会被添加到字符串池中。如果我们接着执行String b=new String("abc");代码,由于"abc"已经被创建并保存于字符串池中,因此JAVA虚拟机只会在堆中新创建一个String对象,但是它的值(value)是共享前一行代码执行时在栈中创建的三个char型值值'a'、'b'和'c'。

说到这里,我们对于篇首提出的String str=new String("abc")为什么是创建了两个对象这个问题就已经相当明了了。
转自:
作者:臧圩人(zangweiren)
网址:http://zangweiren.iteye.com
分享到:
评论

相关推荐

    String s = new String(” a “) 到底产生几个对象?

    上图红色的这3个箭头,对于通过new产生一个字符串(”宜春”)时,会先去常量池中查找是否已经有了”宜春”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”宜春”对象的拷贝对象。...

    练习使用Java基本数据类型。使用Java的String类操作字符串和子串

    toffset 指从被调用方法的字符串的第几个字符开始测试,如果是0,表明从字符串的首字符开始。other是指使用的另一个字符串。ooffset指从另一个字符串的第几个字符开始。len指要比较几个字符。该方法返回boolean值。 ...

    java 面对对象编程.pdf.zip

    面向对象基础 面向对象和面向过程的区别 成员变量与局部变量的区别 创建一个对象用什么运算符?对象实体与对象引用有何不同? 对象的相等和引用相等的区别 类的构造方法的作用是什么?...这句话创建了几个

    Java问题宝典2012版

    创建了几个String Object? 二者之间有什么区别? 25 34、String 和StringBuffer的区别 25 35、如何把一段逗号分割的字符串转换成一个数组? 26 36、数组有没有length()这个方法? String有没有length()这个方法? 26 ...

    关于Java的几个经典问题

    到底创建了几个String对象(三)——变量(属性)的覆盖 (四)——final、finally和finalize的区别 (五)——传了值还是传了引用(六)——字符串(String)杂谈 (七)——日期和时间的处理 (八)——聊聊基本...

    JAVA面试题解惑系列合集

    1.2 JAVA面试题解惑系列(二)——到底创建了几个String对象? 1.3 JAVA面试题解惑系列(三)——变量(属性)的覆盖 1.4 JAVA面试题解惑系列(四)——final、finally和finalize的区别 1.5 JAVA面试题解惑系列(五...

    JAVA面试题解惑系列114页.pdf

    (二)到底创建了几个String 对象? (三)变量(属性)的覆盖 (四)final、finally 和finalize 的区别 (五)传了值还是传了引用? (六)字符串(String)杂谈 (七)日期和时间的处理 (八)聊聊基本类型(内置...

    java 字符工具类

    自己写的java字符工具类。主要有以下几个函数: public static String escape(String str,String charset):可以将以下格式的字符串

    java-string-similarity:一个Java库,实现了几种计算字符串之间相似度的算法

    ,用于计算两个字符串之间的归一化距离或相似度分数。 0.0 分表示两个字符串绝对不相似,1.0 表示绝对相似(或相等)。 介于两者之间的任何内容都表示两个字符串的相似程度。例子在这个简单的例子中,我们想要计算...

    java面试宝典

    创建了几个String Object? 12 40、接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 12 41、Java 的接口和C++的虚类的相同和不同处。 12 42、一个“.java”源文件中...

    StringUtil.java(字符串工具类)

    字符串工具类,获得一个UUID,user_name to userName,user-name to userName,user-name to UserName,user_name to UserName,userName to user_name,userName to USER_NAME,userName to UserName,UserName to ...

    别再问我 new 字符串创建了几个对象了!我来证明给你看!

    有人说创建了 1 个对象,也有人说创建了 2 个对象,还有人说可能创建了 1 个或 2 个对象,但谁都没有拿出干掉对方的证据,这就让我们这帮吃瓜群众们陷入了两难之中,不知道到底该信谁得。 但是今天就斗胆和大家聊聊...

    java字符串转数组.docx

    在Java中,将字符串转换为数组可以通过以下几种方式实现: 使用String类的toCharArray()方法: 这个方法将字符串转换为字符数组。 示例:char[] charArray = str.toCharArray(); 使用String类的split()方法: 这...

    java中计算两个日期相差几天

    java中计算两个日期相差几天 1.public class Test { 2.public void dateDiff(String startTime, String endTime, String format) { 3.//按照传入的格式生成一个simpledateformate对象 4.SimpleDateFormat sd = ...

    java 面试常问的问题 如何回答

    创建了几个String Object? 二者之间有什么区别? 23 34、String 和StringBuffer的区别 23 35、如何把一段逗号分割的字符串转换成一个数组? 24 36、数组有没有length()这个方法? String有没有length()这个方法? 24 ...

    JAVA上百实例源码以及开源项目

     Java波浪文字,一个利用Java处理字符的实例,可以设置运动方向参数,显示文本的字符数组,高速文本颜色,显示字体的 FontMetrics对象,得到Graphics实例,得到Image实例,填充颜色数组数据,初始化颜色数组。...

    java联系题

    //执行到这一行时,创建了几个对象? String s1 = "abc";//执行到这一行时,创建了几个对象? String s2 = new String("abc");//执行到这一行时,创建了几个对象? System.out.println(s == s1);//输出结果是什么...

    Java面试宝典-经典

    创建了几个String Object? 二者之间有什么区别? 23 34、String 和StringBuffer的区别 23 35、如何把一段逗号分割的字符串转换成一个数组? 24 36、数组有没有length()这个方法? String有没有length()这个方法? 24 ...

    Java面试宝典2010版

    创建了几个String Object? 二者之间有什么区别? 23 34、String 和StringBuffer的区别 23 35、如何把一段逗号分割的字符串转换成一个数组? 24 36、数组有没有length()这个方法? String有没有length()这个方法? 24 ...

    java基础面试题目,常见的几个java面试题目:说一下java类集;JDK 和 JRE 有什么区别

    java基础面试题目,常见的几个java面试题目:说一下java类集;JDK 和 JRE 有什么区别;Spring的工作原理;框架的源码有没有看过;动态代理是怎么实现的;final 在 Java 中有什么作用; Java 中的 Math. round(-1. 5)...

Global site tag (gtag.js) - Google Analytics