`

Java中堆栈问题,对象比较问题 文章搜集

    博客分类:
  • Java
 
阅读更多

1常量池:

 

在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。

 

2 字符串对象的创建:

由于字符串对象的大量使用[一般在heap(堆)分配内存],Java中为了节省内存空间和运行时间 [如比较字符串时,==比equals()快], 在编译阶段就把所有的字符串文字放到一个字符串池[pool of literal strings]中,而运行时字符串池成为常量池的一部分。字符串池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。我们知道,对两个引用变量,使用==判断它们的值[引用]是否相等,即指向同一个对象。

 

  现在看String s = new String("abc");语句,这里"abc"本身就是pool中的一个对象,而在运行时执行new String()时,将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。ok,这条语句就创建了2个String对象。

 

  String s1 = new String("abc") ;String s2 = new String("abc") ;if( s1 == s2 ){ }//false

 

  //创建了几个String Object? [三个,pool中一个"abc",heap中2个。]

 

只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中。

 

 

 

另外

 

1.==表示引用自同一对象,equals()表示值相等。

 

String str1 = "abc";引用的对象在栈(或者叫String池)中。

String str1 =new String ("abc"); 引用的对象在内存/堆中。

 

2.String str1 =  "string";在栈中

 

  String str3 =  "str";在栈中

 

  String str4 = "ing";在栈中

 

  String str2 = str3+str4; 在堆中,因为+号的作用是返回另外一个新建的String对象,而不是在栈中找string这个值。

 

String str2 = "str"+"ing";在栈中。str1==str2为true。

 

 

 

但是有一种情况需要引起我们的注意。请看下面的代码:

 

public class StringStaticTest {      

 

    public static final String A = "ab"; // 常量A

 

    public static final String B = "cd"; // 常量B

 

    public static void main(String[] args) {

 

         String s = A + B;  // 将两个常量用+连接对s进行初始化 

 

         String t = "abcd";   

 

        if (s == t) {   

 

             System.out.println("s等于t,它们是同一个对象");   

 

         } else {   

 

             System.out.println("s不等于t,它们不是同一个对象");   

 

         }   

 

     }   

 

}  

 

这段代码的运行结果如下:

 

s等于t,它们是同一个对象

 

原因是在上面的例子中,A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了。也就是说:String s=A+B;  等同于:String s="ab"+"cd";

 

我对上面的例子稍加改变看看会出现什么情况:

 

public class StringStaticTest {       

 

    public static final String A; // 常量A

 

    public static final String B;    // 常量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对象在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 s0=”kvill”; 

 

 

  String s1=”kvill”; 

 

  String s2=”kv” + “ill”; 

 

  System.out.println( s0==s1 ); 

 

  System.out.println( s0==s2 ); 

 

 

String s1 = "a"; String s2 = "b"; String s3 = s1+s2 +"c";

四个对象

 

 

另外:

 

字符串的+运算和字符串转换 

 

  字符串转换和串接是很基础的内容,因此我以为这个问题简直就是送分题。事实上,我自己就答错了。 

 

  String str = new String("jf"); // jf是接分 

 

  str = 1+2+str+3+4; 

 

  一共创建了多少String的对象?[我开始的答案:5个。jf、new、3jf、3jf3、3jf34] 

 

  首先看JLS的有关论述: 

 

  一、字符串转换的环境[JLS 5.4 String Conversion] 

 

  字符串转换环境仅仅指使用双元的+运算符的情况,其中一个操作数是一个String对象。在这一特定情形下,另一操作数转换成String,表达式的结果是这两个String的串接。 

 

  二、串接运算符[JLS 15.18.1 String Concatenation Operator + ] 

 

  如果一个操作数/表达式是String类型,则另一个操作数在运行时转换成一个String对象,并两者串接。此时,任何类型都可以转换成String。[这里,我漏掉了"3"和"4"] 

 

  如果是基本数据类型,则如同首先转换成其包装类对象,如int x视为转换成Integer(x)。 

 

  现在就全部统一到引用类型向String的转换了。这种转换如同[as if]调用该对象的无参数toString方法。[如果是null则转换成"null"]。因为toString方法在Object中定义,故所有的类都有该方法,而且Boolean, Character, Integer, Long, Float, Double, and String改写了该方法。 

 

  关于+是串接还是加法,由操作数决定。1+2+str+3+4 就很容易知道是"3jf34"。[BTW :在JLS的15.18.1.3中举的一个jocular little example,真的很无趣。] 

 

  下面的例子测试了改写toString方法的情况.。 

 

  class A 

 

  { int i = 10; 

 

  public static void main(String []args) 

 

  { String str = new String("jf"); 

 

  str += new A(); 

 

  System.out.print(str); 

 

  } 

 

  public String toString(){ return " a.i ="+i+"\n"; } 

 

  } 

 

  三、字符串转换的优化 

 

  按照上述说法,str = 1+2+str+3+4;语句似乎应该就应该生成5个String对象: 

 

  1+2 =3,then 3→Integer(3)→"3" in pool? [假设如此] 

 

  "3"+str(in heap) = "3jf" (in heap) 

 

  "3jf" +3 ,first 3→Integer(3)→"3" in pool? [则不创建] then "3jf3" 

 

  "3jf3"+4 create "4" in pool 

 

  then "3jf34" 

 

  这里我并不清楚3、4转换成字符串后是否在池中,所以上述结果仍然是猜测。 

 

  为了减少创建中间过渡性的字符串对象,提高反复进行串接运算时的性能,a Java compiler可以使用StringBuffer或者类似的技术,或者把转换与串接合并成一步。例如:对于 a + b + c ,Java编译器就可以将它视为[as if] 

 

  new StringBuffer().append(a).append(b).append(c).toString(); 

 

  注意,对于基本类型和引用类型,在append(a)过程中仍然要先将参数转换,从这个观点看,str = 1+2+str+3+4;创建的字符串可能是"3"、"4"和"3jf34"[以及一个StringBuffer对象]。 

 

  现在我仍然不知道怎么回答str = 1+2+str+3+4;创建了多少String的对象,。或许,这个问题不需要过于研究,至少SCJP不会考它。 

 

  3、这又不同:str = "3"+"jf"+"3"+"4"; 

 

  如果是一个完全由字符串文字组成的表达式,则在编译时,已经被优化而不会在运行时创建中间字符串。测试代码如下: 

 

  String str1 ="3jf34"; 

 

  String str2 ="3"+"jf"+"3"+"4"; 

 

  if(str1 == str2) { System.out.println("str1 == str2"); } 

 

  else { System.out.println("think again"); } 

 

  if(str2.equals(str1)) System.out.println("yet str2.equals(str1)"); 

 

  可见,str1与str2指向同一个对象,这个对象在pool中。所有遵循Java Language Spec的编译器都必须在编译时对constant expressions 进行简化。JLS规定:Strings computed by constant expressions (?15.28) are computed at compile time and then treated as if they were literals. 

 

  对于String str2 ="3"+"jf"+"3"+"4";我们说仅仅创建一个对象。注意,“创建多少对象”的讨论是说运行时创建多少对象。 

 

  BTW:编译时优化 

 

  String x = "aaa " + "bbb "; 

 

  if (false) { x = x + "ccc "; } 

 

  x += "ddd "; 

 

  等价于: String x = "aaa bbb "; x = x + "ddd "; 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics