String类的常见问题

String类的常见问题

1.判定定义为String类型的两个对象是否相等(1)

public class StringDemo01 {
    public static void main(String[] args) {
        String string01 = "zrt";
        String string02 = "zrt";
        System.out.println(string01 == string02);		//true
        System.out.println(string01.equals(string02));	//true
    }
}

分析:

  1. 在Java中“==”这个符号比较运算符,可以用来比较基本数据类型引用数据类型是否相等

    • 基本数据类型:比较的是值是否相等

    • 引用数据类型:比较的是两个对象的内存地址是否相等

    • 注意的是,String类型不属于八种基本数据类型,换句话说,String对象属于引用数据类型。将"zrt"同时赋值给了两个String类型的对象,而且两个String类的引用都是指向同一个地址,故第一个语句为“true”。

  2. Object类提供的equals()方法,在String类(继承了Object类)中重写了equals()方法,通过JDK文档可知,此方法的解释为“将此字符串与指定的对象比较。当且仅当该参数不为null,并且是与此对象表示相同字符序列String对象时,结果才为true。”因为string01和strin02的值都为"zrt",具有相同字符序列,故第二个语句为“true”。

  3. 上述例子的内存图如下:

String类的常见问题_第1张图片

此过程大致如下:

  • 运行先编译,将当前类StringDemo01.class文件加载进入内存的方法区。
  • main方法压入栈内存。
  • 常量池创建一个“zrt”对象,产生一个内存地址。
  • 然后把“zrt”内存地址赋值给main方法里的成员变量string01,这个时候string01根据内存地址,指向了常量池中的“zrt”。
  • 常量池有个特点,发现字符串对象“zrt”已经存在,就不在创建重复的对象。
  • 运行到代码 String string02 =”zrt”, 由于常量池存在“zrt”,直接把“zrt”内存地址赋值给了string02。
  • 所以,string01和string02 都指向了内存中同一个地址,所以两者是完全相同的。

2.内存中创建对象的个数

public class StringDemo02 {
    public static void main(String[] args) {
        String string01 = "zrt";
    }
}

上述例子在内存中创建了多少个对象呢?

答案便是:两个,一个堆内存,一个在常量池,而且堆内存对象是常量池对象的一个拷贝副本

分析:我们都知道new这个关键词,new出来的对象都是存储在堆内存。“zrt”属于字符串,字符串属于常量,所以应该在常量池中创建,所以第一个创建的对象就是在常量池中的”zrt“。那为什么说堆内存对象是常量池的一个拷贝副本呢?从JDK1.6的文档中,可以看到构造方法String(String original) 的注释:”初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列“。也就是说,新创建的字符串就是该参数字符串的副本。换句话说,便是堆内存对象是常量池对象的一个拷贝副本

上述例子的内存图如下:

String类的常见问题_第2张图片


3.判定定义为String类型的两个对象是否相等(2)

public class StringDemo03 {
   public static void main(String[] args) {
     String string01 = new String("zrt");
     String string02 = "zrt";
     System.out.println(string01 == string02);		//false
     System.out.println(string01.equals(string02));	//true
   }
}

分析:”==“比较的string01对象和string02对象的内存地址,由于string01指向的是堆内存的地址,string02看到“zrt”已经在常量池存在,就直接指向了常量池的内存地址,所以,第一个语句的输出结果false。第二个equals()放法比较,比较是两个字符串序列是否相等,由于都是“zrt”,故第二个语句的输出结果为true。

上述例子的内存图如下:

String类的常见问题_第3张图片


4.判定定义为String类型的两个对象是否相等(3)

public class StringDemo04 {
   public static void main(String[] args) {
     String string01 = "z" + "r" + "t";
     String string02 = "zrt";
     System.out.println(string01 == string02);		//true
     System.out.println(string01.equals(string02));	//true
   }
}

分析:“z”,”r”,”t”本来就是三个字符串常量,经过”+“符号进行拼接之后变成了字符串“zrt”,而“zrt”本身就是字符串常量(Java中有常量优化机制),所以会在常量池创建一个“zrt”的字符串常量对象。因此,在进行到string02=”zrt”的时候,因为常量池存在“zrt”,所以不会再创建“zrt”字符串对象。故无论是比较内存地址还是比较字符串序列,都相等,所以两个语句的输出结果都是true。


5.判定定义为String类型的两个对象是否相等(4)

public class StringDemo05 { 
   public static void main(String[] args) {
     String string01 = "zrt";
     String string02 = "zrt";
     String string03 = string01 + "t";
     System.out.println(string02 == string03);		//false
     System.out.println(string02.equals(string03));	//true
   }
}

其中,第二个语句的输出结果为true,此处不再赘述。主要是为什么第一个语句的输出结果为什么是false呢?首先我们可以在JDK API1.6的文档中看到这样一句话:

Java 语言提供对字符串串联符号("+")以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。

分析:由上我们可知:任何数据和字符串进行加号(+)运算,最终得到是一个拼接而成新的字符串。上面注释说明了这个拼接的原理是由StringBuilder或者StringBuffer类和里面的append方法实现拼接,然后调用toString()把拼接的对象转换成字符串对象,最后把得到字符串对象的内存地址赋值给变量。

上述例子的内存图如下:

String类的常见问题_第4张图片

过程大致如下:

  • 常量池创建“zr”对象,并将其内存地址值赋值给string01,所以string01指向了“zr”。
  • 常量池创建“zrt”对象,并将其内存地址值赋值给string02,所以string02指向了“zrt”。
  • 由于语句用的是“+”进行拼接string01和字符串对象“t”,所以是使用StringBuffer类的append()方法,得到了StringBuffel对象“zrt”(注意:现在还不是String对象),用0x01表示内存地址。
  • 调用Object类的toString()方法,将StringBuffer对象转换成String对象,用0x01表示内存地址。
  • 将String对象的内存地址(0x02)赋值给string03。所以进行“==”判断的输出结果为false,因为内存地址不同。

6.总结

这篇文章,是我在学习String类的时候遇到一个小疑惑,做成笔记加深印象,重点就是理解JDK API一些注解和原理、内存图。通过画内存图也可以帮助我们理解其中的原理。


你可能感兴趣的:(个人笔记,java,java,字符串,jdk)