Java中字符串String的研究

先看一个例子:

String str1 = new String("abc");
      String str2 
= new String("abc");
      
if(str1 == str2)
      {
        System.out.println(
"str1 == str2");
      }
      
if(str1.equals(str2))
      {
        System.out.println(
"str1 equals str2");
      }
      
      String str3 
= "abc";
      String str4 
= "abc";
      
if(str3 == str4)
    {
      System.out.println(
"str3 == str4");
    }
    
if(str3.equals(str4))
    {
      System.out.println(
"str3 equals str4");
    }
    
if(str1 == str4)
    {
      System.out.println(
"str1 equals str4");
    }
    
if(str1.equals(str4))
    {
      System.out.println(
"str1 equals str4");
    }

运行结果:

str1 equals str2
str3 == str4
str3 equals str4
str1 equals str4

为什么会出现这样的结果。。。这需要引入一些概念,其实很类似C++ 或C#之类,差别只是小小的。。。

首先了解一个对象在内存中创建的区域:

1.heap(即堆区):它是负责创建对象的,所有的创建出来的对象都是放在堆区的。所有new操作的创建的实例都存储在这里,包括用new String 创建的String。 
2.stack(即栈区):它是负责存放局部变量,和成员变量的。所有的成员变量和局部变  量都放在这个区内,然后他通过一个引用指向栈区的对象或data segment(静态代码)区的静态数据。
3.data segment(静态代码区):在这个区主要存放的是静态常量,和字符串常量(字符串池)。在类一开始被加载的时候此常量就被初始化放在这个区内,而且被全局所共享,所有的访问直接指向他即可。
4.code segment(代码区):它是存放代码的区,所有的执行代码都放在此区内。通过对象的调用指向此区。

如果没有重载equals方法,则equals是比较地址是否相等, == 比较值是否相等。 来一段demo

public class CC 
  {
    
int no = 0;
    
public CC(int _no)
    {
     no 
= _no; 
    }
  }

CC c1 
= new CC(1);
     CC c2 
= new CC(2);
     CC c3 
= c1;
     
if(c1 == c2)
     {
       System.out.println(
"c1 == c2");
     }
     
if(c1.equals(c2))
     {
       System.out.println(
"c1 equals c2");
     }
     
if(c1 == c3)
     {
       System.out.println(
"c1 == c3");
     }
     
if(c1.equals(c3))
   {
     System.out.println(
"c1 equals c3");
   }

运行结果:

c1 == c3
c1 equals c3

解释:equals判断两个内存地址是否相等,因为c1、c2都是用new实例化的,因此肯定是不同内存地址,因此equals肯定为false,因为c3是存的c1的引用,也就是c1在堆里的地址,所以为true

==判断两个内存中的内容是否相当,因为c1,c2都是new出来的2个不同对象,即使对象内的成员等一模一样,因此也为false,c3地址跟c1一样,因此内容也一样。

在java api中对equal的解释是:

equals 方法在非空对象引用上实现相等关系:

自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
对于任何非空引用值 x,x.equals(null) 都应返回 false。

equals是可以被类重写的,因此判断的标准也跟具体类不一样了。

像String,date等系统类已经将该方法重写。在String类中equals就被重写为判断字符串内容是否相当,而不是字符串地址。

再看开篇的demo程序。

结果是

str1 equals str2
str3 == str4
str3 equals str4
str1 equals str4

 解释一下,

在String中, String str1 = new String("abc")方式是在堆里分配一块内存;String str3 = "abc"方式,首先会到静态存储区的字符串池中搜索是否有该值,有的话,这直接给str3返回该指针,否则在字符串池中分配内存创建该字符串。

因此

String str1 = new String("abc");
String str2 = new String("abc");

是在堆分配了两块独立内存

String str3 = "abc";
String str4 = "abc";

执行第一句时,str3 = "abc"时候,先到静态存储区的字符串池中找,发现没有这个字符串,则创建之,第二句str4 = "abc" 逻辑跟第一句一样,这时字符串池中已有该串,则直接给str4返回了str3的地址。

因此有开篇的运行结果就容易理解了。

了解了这些特性,我们可以把一些常用字符串写到公共类里,设为static, 以后所有用到这些串的时候就不用再分配内存了

原文地址:https://www.cnblogs.com/jacktu/p/1333774.html