String的==和equals()

public class Main{
  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
false

原因:

1,字符串在java中存储在字符串常量区中
2,==判断的是对象引用是否是同一个引用,判断字符串相等要用equals方法
3、首先判断a==MESSAGE 同一份字符串常量在内存中只有一份,因此是同一地址,返回true(a =”tao”+”bao”,在虚拟机中就是a=”taobao”,而b+c这是新定义一个对象。)
再次比较(b+c)==MESSAGE 这相当于 new String(b+c)==MESSAGE 这里new了一个String对象,所以返回false

另一解释:
对于这道题,考察的是对String类型的认识以及编译器优化。Java中String不是基本类型,但是有些时候和基本类型差不多,如String b = “tao” ; 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new)。所以String这个类型值得好好研究下。
Java中的变量和基本类型的值存放于栈内存,而new出来的对象本身存放于堆内存,指向对象的引用还是存放在栈内存。例如如下的代码:
int i=1;
String s = new String( “Hello World” );
变量i和s以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。

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

如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义i和j是都赋值1,则i==j结果为true。==用于判断两个变量指向的地址是否一样。i==j就是判断i指向的1和j指向的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?当然不等,s和w指向堆内存中不同的String对象。如果判断两个String对象相等呢?用equals方法。

这里写图片描述

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

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

现在b和c不可能再次赋值了,所以编译器将b+c编译成了”taobao”。因此,这时的结果是true。
在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量b的final去掉,结果又变成了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

### Java 中 `String` 的 `==` `equals()` 方法区别 在 Java 编程语言中,当涉及到字符串比较时,`==` 运算符 `equals()` 方法有着显著的区别。 #### 使用 `==` 比较字符串 `==` 是用于基本类型的比较运算符,在处理对象(如 `String` 对象)时,它实际上是在比较两个变量所引用的对象是否位于相同的内存位置。这意味着即使两个不同的 `String` 实例包含了相同的内容,如果它们不是同一个对象,则 `==` 将返回 `false`[^2]。 ```java public class Test { public static void main(String[] args) { String s1 = new String("HELLO"); String s2 = new String("HELLO"); System.out.println(s1 == s2); // 输出 false } } ``` 这段代码创建了两个新的 `String` 对象并分别赋给 `s1` `s2` 变量;尽管这两个字符串具有完全一样的字符序列,但由于它们是通过 `new` 关键字创建的不同实例,因此 `==` 返回的是 `false`。 #### 使用 `equals()` 方法比较字符串 相比之下,`equals()` 方法会逐个字符地对比两个字符串的实际内容而不是其存储的位置。对于 `String` 类来说,默认实现了该方法以便能够正确判断两个字符串是否有相同的字符组成。只要两者的长度以及每一个对应位置上的字符都一致,那么无论这些字符串是由哪种方式构建出来的——无论是直接声明的常量池中的字符串还是经由 `new` 创建的新对象——都会被认为是相等的,并且 `equals()` 调用将返回 `true`。 ```java public class Test { public static void main(String[] args) { String s1 = new String("HELLO"); String s2 = new String("HELLO"); System.out.println(s1.equals(s2)); // 输出 true } } ``` 在这个例子中,虽然 `s1` `s2` 并不指向堆上同一处地址,但因为两者内部保存着同样的字母组合 “HELLO”,所以调用 `equals()` 后得到的结果就是 `true`。 另外值得注意的一点是关于字符串驻留机制(interning),即 JVM 自动维护的一个特殊区域用来存放那些被频繁使用的不可变字符串副本。当程序里多次出现相同的字符串文字串时,JVM 通常只会保留一份这样的拷贝于栈顶或静态区,并让后续遇到同样内容的文字串共享这份资源。这使得某些情况下即使是看起来像是独立定义的字符串也可能最终指向同一个地方从而令 `==` 成立[^4]。 然而,除非特别指明要利用这一特性优化性能或者节省空间外,一般建议总是采用 `equals()` 来做逻辑意义上的字符串匹配操作而非依赖不确定性的 `==` 行为。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值