首页 > 试题广场 >

观察下列代码,分析结果() String s1 = "cod

[单选题]

观察下列代码,分析结果()

String s1 = "coder";     
String s2 = "coder";     
String s3 = "coder" + s2;     
String s4 = "coder" + "coder";     
String s5 = s1 + s2;            
System.out.println(s3 == s4); 
System.out.println(s3 == s5);    
System.out.println(s4 == "codercoder");
  • false;false; true;

  • false;true; true;

  • false;false; false;

  • true;false; true;

这道题官方的解析有错, s5并不是指向常量池, 而是指向堆
定义字符串的时候看定义时等号右边拼接字符串时有没有使用到变量, 如果有, 则重新创建一个新的对象

发表于 2022-10-23 10:45:26 回复(12)
引用类型判断时,==判断的是地址是否相等。如果用常量字符串来定义的(如s1,s2,s4)会存在常量池里面;用变量定义的就不会(如s3,s5)。所以上面的状态就是s1==s2;s4==“codercoder”;s3!=s4;s3!=s5。
发表于 2022-01-17 16:48:44 回复(14)

Java内存结构包含以下部分:

1、栈区:由编译器自动分配释放,具体方法执行结束后,系统自动释放JVM内存资源。

其作用有保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用。也可以用来保存加载方法时的帧。

2、堆区:一般由程序员分配释放,JVM不定时查看这个对象,如果没有引用指向这个对象就回收。

其作用为用来存放动态产生的数据,包括new出来的实例,字符数组等。

同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法。

3、数据区:用来存放static定义的静态成员。

4、常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于堆中
public static void main(String args[]) {
       // 字符串常量,分配在常量池中,编译器会对其进行优化,  Interned table
       // 即当一个字符串已经存在时,不再重复创建一个相同的对象,而是直接将s2也指向"hello".
       String s1 = "hello";   
       String s2 = "hello";
       // new出来的对象,分配在heap中.s3与s4虽然它们指向的字符串内容是相同的,但是是两个不同的对象.
       // 因此==进行比较时,其所存的引用是不同的,故不会相等
       String s3 = new String("world");      
       String s4 = new String("world");
       System.out.println(s1 == s2);   // true
       System.out.println(s3 == s4);   // false
       System.out.println(s3.equals(s4));   // true   
       // String中equals方法已经被重写过,比较的是内容是否相等.
   }
编辑于 2022-02-16 21:29:10 回复(9)
s=“a”+“b” 是字符串拼接, s = "a"+b 则是重新创建一个对象
发表于 2022-10-14 16:42:31 回复(0)
发表于 2022-08-20 09:20:25 回复(2)
一共有四种情况的字符串对象放在常量池: 1.直接“.…”得到的字符串 2.通过“…”+“…”直接相加的字符串 3.两个指向“...”的final常量拼接的结果 4.所有字符串对象.intern()得到的结果
发表于 2022-08-19 09:38:28 回复(2)
我想说的是S5应该不是放入常量池的吧,运行结果如下;


发表于 2022-05-09 22:51:24 回复(5)
String s1 = "coder";     
String s2 = "coder";     
String s3 = "coder" + s2;     
String s4 = "coder" + "coder";     
String s5 = s1 + s2;            
System.out.println(s3 == s4); 
System.out.println(s3 == s5);    
System.out.println(s4 == "codercoder");
这是一段JAVA代码,在JAVA语言中内存分配是除了堆(heap——程序员手动分配)、栈(自动分配)外,还有一个区域叫常量区,其中常量区的例子如下:
// 字符串常量,分配在常量池中,编译器会对其进行优化,  Interned table
// 即当一个字符串已经存在时,不再重复创建一个相同的对象,而是直接将s2也指向"hello".
String s1 = "hello";   
String s2 = "hello";
但是若是变量定义赋值,或者new,就会在内存中新找一段地址。
而题目中的 == 判断的是地址是否相同
所以结果如答案所示
System.out.println(s3 == s4);   //false
System.out.println(s3 == s5);   //false
System.out.println(s4 == "codercoder");  //true
本题需要判断变量的地址是否相同,同时JAVA内存分配示意图如下:

发表于 2022-03-01 12:13:32 回复(1)
在Java中,字符串的比较有两种方式,一种是使用`equals()`方法,这种方式是比较字符串的内容是否相同,另一种是使用`==`操作符,这种方式是比较两个字符串对象是否是同一个对象(即比较他们在内存中的地址)。

对于这段代码,首先需要理解Java中的字符串常量池(String Constant Pool)。字符串常量池是Java为了优化字符串的创建而设立的。当我们创建一个字符串常量,如`String s = "coder";`,Java会首先在字符串常量池中查找是否有相同的字符串,如果有,就返回该字符串的引用,如果没有,就在池中创建一个新的字符串。

然后我们来分析这段代码:
String s1 = "coder";     // 在字符串常量池中创建一个字符串"coder",s1指向它
String s2 = "coder";     // 在字符串常量池中找到已经存在的"coder",s2指向它,所以s1和s2指向同一个对象
String s3 = "coder" + s2;     // 这里涉及到字符串的连接,会创建新的字符串对象,s3指向这个新的对象
String s4 = "coder" + "coder";     // 这里是常量字符串的连接,编译器会在编译期间就完成连接,所以s4指向字符串常量池中的"codercoder"
String s5 = s1 + s2;            // 这里也是字符串的连接,会创建新的字符串对象,s5指向这个新的对象
System.out.println(s3 == s4); // s3和s4指向的不是同一个对象,所以结果为false
System.out.println(s3 == s5);    // s3和s5指向的不是同一个对象,所以结果为false
System.out.println(s4 == "codercoder"); // s4和"codercoder"都指向字符串常量池中的同一个对象,所以结果为true
所以,这段代码的输出应该是:
false
false
true
发表于 2023-11-07 12:07:05 回复(0)
这道题官方的解析有错, s5并不是指向常量池, 而是指向堆 定义字符串的时候看定义时等号右边拼接字符串时有没有使用到变量, 如果有, 则重新创建一个新的对象
发表于 2022-10-25 00:41:59 回复(0)
为什么字符串变量的加法a+b不在常量池,归根到底还是jvm在字节码层面上对字符串变量的加操作进行了修改,改成了使用stringbuilder,append(a)再append+(b),再返回toString方法,点开jdk的stringbuilder的toString方法就知道了,它是new出来的string对象所以存于堆区。而字面量'a'+'b'在编译阶段生成的字节码就已经是'ab'了,是放在常量池的。这类问题关键若能理解字节码的指令操作便可理解其原理
发表于 2024-06-05 20:56:45 回复(0)
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";
//对于String str3 = "str" + "ing";编译器会给你优化成String str3 = "string";
String str4 = str1 + str2;
//对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的。拼接完成之后调用 toString() 得到一个 String 对象 。
//String str4 = new StringBuilder().append(str1).append(str2).toString();
String str5 = "string";
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false
对于编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。
在编译过程中,Javac 编译器(下文中统称为编译器)会进行一个叫做 常量折叠(Constant Folding) 的代码优化。常量折叠会把常量表达式的值求出来作为常量嵌在最终生成的代码中,这是 Javac 编译器会对源代码做的极少量优化措施之一(代码优化几乎都在即时编译器中进行)。

对于String str3 = "str" + "ing";编译器会给你优化成String str3 = "string";

并不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以:
  • 基本数据类型(byte、boolean、short、char、int、float、long、double)以及字符串常量。
  • final修饰的基本数据类型和字符串变量
  • 字符串通过 “+”拼接得到的字符串、基本数据类型之间算数运算(加减乘除)、基本数据类型的位运算(<<、>>、>>> )

    引用的值在程序编译期是无法确定的,编译器无法对其进行优化。

    对象引用和“+”的字符串拼接方式,实际上是通过StringBuilder调用append()方法实现的,拼接完成之后调用toString()得到一个String对象 。

    String str4 = new StringBuilder().append(str1).append(str2).toString(); 

    我们在平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用StringBuilder或者StringBuffer。

    不过,字符串使用final关键字声明之后,可以让编译器当做常量来处理。


    著作权归JavaGuide(javaguide.cn)所有 基于MIT协议 原文链接:https://javaguide.cn/java/basis/java-basic-questions-02.html
  • 发表于 2024-05-25 10:12:20 回复(0)
    1. 字符串常量池:Java在运行时维护一个字符串常量池,其中存储了所有的字符串字面量。当创建一个字符串字面量时,JVM会首先检查池中是否已存在该字符串。如果存在,就返回该字符串的引用;如果不存在,就在池中创建一个新的字符串对象。

    2. 字符串拼接:使用+操作符拼接字符串时,如果至少有一个操作数是字符串字面量或常量池中的字符串,并且另一个操作数是字符串变量(不是字面量或池中的直接引用),则结果是一个新的字符串对象,而不是常量池中的字符串。

    现在,让我们分析每个字符串变量:

    • String s1 = "coder";:s1引用常量池中的字符串"coder"。
    • String s2 = "coder";:s2同样引用常量池中的字符串"coder"。
    • String s3 = "coder" + s2;:这里"coder"是字面量,但s2是变量。因此,s3是新的字符串对象,不在常量池中。
    • String s4 = "coder" + "coder";:这里两个操作数都是字面量,所以结果字符串"codercoder"在编译时就被确定,并且存储在常量池中。因此,s4引用常量池中的"codercoder"。
    • String s5 = s1 + s2;:尽管s1和s2都引用常量池中的"coder",但+操作符用于变量时,结果是一个新的字符串对象,不在常量池中。

    接下来,我们分析System.out.println语句:

    • System.out.println(s3 == s4);:s3是新的字符串对象,而s4引用常量池中的字符串。因此,s3和s4不是同一个对象,输出false。
    • System.out.println(s3 == s5);:s3和s5都是通过变量拼接得到的字符串,因此它们都是新的字符串对象,不是同一个对象,输出false。
    • System.out.println(s4 == "codercoder");:s4引用常量池中的"codercoder",而"codercoder"也是字面量,会引用常量池中的同一个字符串。因此,s4和"codercoder"是同一个对象,输出true。
    发表于 2024-10-12 10:53:31 回复(0)

    在两个String变量相加的时候,变量不是显式已知的(编译器不知),底层实际调用了StringBuffer的append方法,s3指向堆区而不是常量池。

    在两个String字面量相加的时候,由于字面量是显式已知的,编译器将其优化为相加的结果并存入常量池。

    发表于 2024-03-31 21:50:16 回复(0)

    1常量+常量:结果是常量池地址。且常量池中不会存在相同内容的常量。

    2常量与变量变量与变量:结果在堆中,返回堆空间中地址

    3拼接后调用intern方法:返回值常量池中地址,和(1)结果一样。

    发表于 2023-10-09 10:05:27 回复(0)
    /**对class文件反编译
      *在JVM中对String的优化
      *采用字面量赋值时 如果+号两边不是变量,JVM会自动进行字符的拼接。
      *如果+号两边有变量,JVM不会对这一行进行任何操作
      **/
    String a="codercoder";
    String b="coder";
    String c="coder"+"coder";//==>String c="codercoder";
    String d="coder"+b;//==>String d=new String("coder"+b);
    //a==c  a!=d

    发表于 2022-12-09 17:45:58 回复(0)
    深入理解可以看看String的itern方法
    发表于 2022-07-21 18:40:48 回复(0)
    • ==:是判断地址值是否相同

    • 由于String的特点,其值是不会改变的,所有的String操作,并且是在JVM的常量池中不存在的字符串值,都会新创建一个String对象。操作是针对在空间中的值进行操作,而不是引用。

    • 并且,操作后的String新对象都是存放在JVM中的堆中。随着JVM的优化,永久代被元空间替代,所以字符串常量池也是存在堆中的。参考:请问下常量池到底是存储在元空间还是堆中呢?-慕课网 (imooc.com)

    • 那么,以上的所有变量,都是单独的String对象。所有的对象比较,都是不同的地址,所以false false

    • 但是由于String的对象是存储在堆中的字符流常量池。所以S1,S2,S4的值都是存储在常量值中。S3和S5都是存储在堆中。

    • 只有存在常量池中的对象才可以复用,比如JDBC连接池,线程池等等。

    • 所以S1和S2以及“coder”指向的引用都是同一个地址。那么“codercoder”和S4也是指向的同一个地址

    编辑于 2022-06-14 18:16:04 回复(0)
    对于"coder" + s2使用到了StringBuilder的拼接操作,会在堆中创建一个StringBuilder对象;对于"coder" + "coder"是在常量池开辟一个空间存放了"codercoder"字符串,如果已经存在了"codercoder",则直接使用这个字符串




    发表于 2024-04-25 18:37:40 回复(0)
    s3和s5貌似都是创建了新的对象,尽管内容一样,但是内存地址不一样,所以二者不相等!
    编辑于 2024-04-09 23:05:29 回复(0)