首页 > 试题广场 >

有如下一段代码,请选择其运行结果() public clas

[单选题]
有如下一段代码,请选择其运行结果()
public class StringDemo{
  private static final String MESSAGE="taobao";
  public static void main(String [] args) {
    String a ="tao"+"bao";
    String b="tao";
    String c="bao";
    System.out.println(a==MESSAGE);
    System.out.println((b+c)==MESSAGE);
  }
}  
  • true true
  • false false
  • true false
  • false true
推荐
答案:C
要注意两个问题:
1,字符串在java中存储在字符串常量区中
2,==判断的是对象引用是否是同一个引用,判断字符串相等要用equals方法
首先判断a==MESSAGE 同一份字符串常量在内存中只有一份,因此是同一地址,返回true
再次比较(b+c)==MESSAGE 这相当于 new String(b+c)==MESSAGE 这里new了一个String对象,所以返回false
验证如下:

编辑于 2015-01-26 20:50:02 回复(20)

对于这道题,考察的是对String类型的认识以及编译器优化。JavaString不是基本类型,但是有些时候和基本类型差不多,如String b =  "tao" 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new)。所以String这个类型值得好好研究下。

Java中的变量和基本类型的值存放于栈内存,而new出来的对象本身存放于堆内存,指向对象的引用还是存放在栈内存。例如如下的代码:

int  i=1;

    String s =  new  String( "Hello World" );

变量is以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。

 

 

 

 

栈内存的一个特点是数据共享,这样设计是为了减小内存消耗,前面定义了i=1i1都在栈内存内,如果再定义一个j=1,此时将j放入栈内存,然后查找栈内存中是否有1,如果有则j指向1。如果再给j赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后j指向2。也就是如果常量在栈内存中,就将变量指向该常量,如果没有就在该栈内存增加一个该常量,并将变量指向该常量。

 

 

如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义ij是都赋值1,则i==j结果为true==用于判断两个变量指向的地址是否一样。i==j就是判断i指向的1j指向的1是同一个吗?当然是了。对于直接赋值的字符串常量(如String s=Hello World”;中的Hello World)也是存放在栈内存中,而new出来的字符串对象(即String对象)是存放在堆内存中。如果定义String s=Hello World”和String w=Hello World”,s==w吗?肯定是true,因为他们指向的是同一个Hello World

 

 

堆内存没有数据共享的特点,前面定义的String s =  new  String( "Hello World" );后,变量s在栈内存内,Hello World 这个String对象在堆内存内。如果定义String w = new  String( "Hello World" );,则会在堆内存创建一个新的String对象,变量w存放在栈内存,w指向这个新的String对象。堆内存中不同对象(指同一类型的不同对象)的比较如果用==则结果肯定都是false,比如s==w?当然不等,sw指向堆内存中不同的String对象。如果判断两个String对象相等呢?用equals方法。

 

 

 

说了这么多只是说了这道题的铺垫知识,还没进入主题,下面分析这道题。 MESSAGE 成员变量及其指向的字符串常量肯定都是在栈内存里的,变量 a 运算完也是指向一个字符串“ taobao ”啊?是不是同一个呢?这涉及到编译器优化问题。对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。也就是说

String a =  "tao" + "bao" ;String a =  "taobao" ;编译出的字节码是一样的。所以等到运行时,根据上面说的栈内存是数据共享原则,aMESSAGE指向的是同一个字符串。而对于后面的(b+c)又是什么情况呢?b+c只能等到运行时才能判定是什么字符串,编译器不会优化,想想这也是有道理的,编译器怕你对b的值改变,所以编译器不会优化。运行时b+c计算出来的"taobao"和栈内存里已经有的"taobao"是一个吗?不是。b+c计算出来的"taobao"应该是放在堆内存中的String对象。这可以通过System. out .println( (b+c)== MESSAGE );的结果为false来证明这一点。如果计算出来的b+c也是在栈内存,那结果应该是trueJavaString的相加是通过StringBuffer实现的,先构造一个StringBuffer里面存放”tao”,然后调用append()方法追加”bao”,然后将值为”taobao”StringBuffer转化成String对象。StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中。下面改造一下这个语句System. out .println( (b+c).intern()== MESSAGE );结果是true intern() 方***先检查 String ( 或者说成栈内存 ) 中是否存在相同的字符串常量,如果有就返回。所以 intern()返回的就是MESSAGE指向的"taobao"。再把变量bc的定义改一下,

final  String b =  "tao" ;

         final  String c =  "bao" ;

            

       System. out .println( (b+c)== MESSAGE );

现在bc不可能再次赋值了,所以编译器将b+c编译成了”taobao”。因此,这时的结果是true

在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量bfinal去掉,结果又变成了false。这也就意味着会用到StringBuffer对象,计算的结果在堆内存中。

    如果对指向堆内存中的对象的String变量调用intern()会怎么样呢?实际上这个问题已经说过了,(b+c).intern()b+c的结果就是在堆内存中。对于指向栈内存中字符串常量的变量调用intern()返回的还是它自己,没有多大意义。它会根据堆内存中对象的值,去查找String池中是否有相同的字符串,如果有就将变量指向这个string池中的变量。

String a = "tao"+"bao";

       String b = new String("taobao");

     

      System.out.println(a==MESSAGE); //true

      System.out.println(b==MESSAGE);  //false

     

      b = b.intern();

      System.out.println(b==MESSAGE); //true

System. out .println(a==a.intern());  //true

发表于 2015-10-27 16:19:31 回复(171)
这题是在考编译器的优化,hotspot中 编译时"tao"+"bao"将直接变成"taobao",b+c则不会优化,因为不知道在之前的步骤中bc会不会发生改变,而针对b+c则是用语法糖,新建一个StringBuilder来处理
发表于 2015-09-08 17:40:27 回复(34)
C
java中内存机制,==判断的是对象引用是否是同一个引用
发表于 2015-01-26 20:00:15 回复(1)
理解==和equals的区别。
发表于 2014-11-04 19:19:33 回复(1)
前面的解释的很清楚,我再做一些补充。jvm识别final,无法改变的字符串常量直接相加,不需要调用方法进行构造,因此b+c的引用和message不一样
发表于 2018-06-12 23:25:22 回复(0)
public class StringDemo {
	private static final String MESSAGE = "taobao";

	public static void main(String[] args) {
		String a = "tao" + "bao";
		final String b = "tao";
		final String c = "bao";
		System.out.println(a == MESSAGE);
		System.out.println((b + c) == MESSAGE);
	}
}

如果在a,b前面加上final,对于常量,编译器在生成字节码文件时候会优化。

(a+b)通过编译器优化会等价于“taobao”。

发表于 2016-06-06 20:52:53 回复(0)
个人理解:"Hello World"这样的字符串存储在方法区的常量池中。无论是堆(通过String s = new String("Hello 
World"))还是栈( String w="Hello World" ),都存储的是对它的引用。即变量s和w在栈,new的对象在堆,而
"Hello World"本身在常量池。
发表于 2016-04-18 14:44:50 回复(1)
C
编辑于 2015-01-29 16:59:22 回复(0)
本题主要考察的是String声明变量在jvm中的存储方法
String a="tao";
String b="bao";
String c="taobao";
a,b,c,都是存在字符串常量池中的
String d="tao" +"bao";也是存在常量池中,d的构造过程是现在常量池中先找是否有“taobao”这个字符长 若有则直接引用改字符串 若没有则在字符长常量池中构造一个“taobao”
类似 String e="tao"+"ba"+"o"; 现在字符串常量池中查找“taoba” 若有则直接引用 若没有则构造一个放在该池中,然后在判断是否有“taobao”过程和前面一样
至于String f=a+b;实际等效于 String f=new String("taobao");存在在堆内存中 所以不相等

编辑于 2015-09-22 14:50:56 回复(2)
@smartleon 有一些错误,总体上是对的
public static void main(String[] args) {
	String a = "tao" + "bao";
	String b = "tao";
	String c = "bao";
	//true, taobao在字符串常量池已经存在,因此String a = "tao" + "bao";执行后,a也指向常量池中该字符串,因此引用相同
	System.out.println(a == MESSAGE);
	/*
	 * false
	 * 1. Java对String的相加是通过StringBuffer实现的,先构造一个StringBuffer里面存放"tao",然后调用append()方法追加"bao",然后将值为"taobao"的StringBuffer转化成String对象。
	 * 2. 很明显新返回的对象和MESSAGE不是指向同一个地方,返回的对象指向堆中String对象,MESSAGE指向常量区中字符串,即两者引用不同
	 */
	System.out.println((b + c) == MESSAGE);
}

发表于 2016-11-25 16:19:59 回复(10)
java对于String类型的相加是通过StringBuffer实现的,先构造一个StringBuffer对象来存放tao,
然后通过append方法追加上bao。StringBuffer对象的分配是在堆上的。
发表于 2015-11-22 20:43:43 回复(7)

a这种形式的String是存在栈中的,new出来的String是存在堆里面的,==判断的是引用地址是否相同,而equals方法,判断的是String的内容是否相同,区分开这个就明白了。

还有就是一种情况,在编译期确定变量的值,跟在运行期确定变量的值的区别
发表于 2015-08-18 16:29:35 回复(2)
发表于 2022-03-31 16:28:28 回复(1)
简单的说java不认识名字,只认识内容,要是将b+c改为,‘‘tao’’+"bao"结果就是true了。
发表于 2015-10-07 19:33:17 回复(1)
http://my.oschina.net/u/551903/blog/134000
这个网址对这个问题解释的很好很好
发表于 2015-04-17 18:06:03 回复(3)
涉及到编译器优化问题,对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。
发表于 2015-08-30 21:27:22 回复(0)

StringDemo类中有一个静态变量MESSAGE和一个字符串常量"taobao",接下来是主函数,当代码执行的时候,类加载器会加载StringDemo类,这个时候在方法区会开辟一个运行时常量池,并为Main函数开辟一个线程,与之对应的有一个。此时

public class StringDemo{
    private static final String MESSAGE = "taobao"; 
//运行到这儿的时候,MESSAGE变量放入主函数线程的栈中,"taobao"则放入StringDemo类的运行时常量池中
    public static void main(String []args){
         String a = "tao"+"bao";
//运行到这儿的时候,a放入main函数线程栈中,"tao"+"bao"先预编译成"taobao",然后放入StringDemo类的运行时常量池中,但是发现运行时//常量池中已经有"taobao"了,所以就直接把a指向运行时常量池中的"taobao"字符串。
        String b = "tao";
//运行至此,b放入main函数的主线程中,"tao"放到StringDemo类的运行时常量池中
        String c = "bao";
//运行至此,c放入main函数的主线程中,"bao"放到StringDemo类的运行时常量池中
        System.out.pirntln(a==MESSAGE);
//由于a和MESSAGE指向的是同一个运行时常量池中的同一个字符串,所以返回结果是true
        System.out.println((b+c)==MESSAGE);
//由于(b+c)在编译期间不会把两个字符串进行合并,因此最终(b+c)指向的字符串放入了堆内存,返回结果为false。
    }
} 

发表于 2016-12-23 15:43:15 回复(1)
intern() 遍历目标字符串在栈内存中是否存在 存在则返回栈内存中的字符串 在本题中要明确一个基础知识, 第一:Java中对字符串的加减是通过stringbuffer实现的。 第二:string x=“xxx” x是储存在栈内存中,xxx常量字符串是储存在栈内存中 第三:string x= new string(“xxx”) x是储存在栈内存中,new出来的对象是储存在堆内存中 第四:Java编译器优化,当(x+y)且x,y都是普通常量字符串时,编译器因为这两个字符串是可变的,不会将x和y代表的字符串遍历出来,所以会new一个stringbuffer对象储存x值,然后再加上y字符串,因为这个对象是new出来的,所以是储存在堆内存中,所以(x+y)引用的值是在堆内存中。 而如果x和y字符串都是final类型的,编译器会在编译时期将x和y转化为他们代表的字符串常量,而这些字符串常量是在栈内存中,编译器会自动先查找有没有x和y相加得出来的字符串,如果有,那么引用已有的字符串,如果没有,那么创建一个字符串。这里(x+y)得出的值就储存在栈内存中
编辑于 2018-09-11 19:27:02 回复(1)
编译器在编译的时候,会自动把“tao” + "bao"进行相加处理,得到“taobao”;
但是对于b+c在编译的时候不会相加。
发表于 2019-09-10 19:37:41 回复(0)
编译器优化问题。 对于字符串相加。如果是字符串内容(均为final类型,String类就是final类)直接相加,编译器优化导致在编译时直接得到相加之后的字符串常量。如果字符串常量池里已经有了内容相同的字符串,则比较结果为TRUE。 如果是指向字符串的引用相加,则不会发生编译器优化,因为在程序中指向字符串的引用可能会发生变化,如果优化会产生错误的结果。这样的话,就通过stringbuffer来相加,最后转换为一个存在堆内存中的string对象。
发表于 2019-04-20 15:45:29 回复(0)