字符串操作是计算机程序设计中最常见的行为。以下是String的源码:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { }
我们可以看出String是final类型,不能被继承。它继承了3个接口,Serializable(可序列化的)、Comparable(可比较的)、CharSequence(字符序列)。
- 继承Serializable接口,可以被JVM序列化成文件
- 继承Comparable接口,主要实现它的compareTo(T o)方法,此方法用来比较两个字符串的长度,返回一个int值。
- 继承CharSequence接口,实现的方法常用的有length()、toString()等。
顺便提醒,Java是单继承、多实现语言。
隐式实例化:直接赋值
public class Demo { public static void main(String[] args) { String s = "hello"; String s2 = "hello"; System.out.println(s == s2); } }
以上直接赋值,是我们常用的实例化方式。通过输出结果为true可知,对于s2引用来说,没有重新新建一个匿名对象,而是将s2引用指向”hello”对象。“==”判断两个对象引用是否指向同一个对象。
从JVM角度来看。JVM将在堆中给”hello”分配一块内存,在虚拟机栈(局部变量表)中给s、s2分配一块内存,均指向”hello”。如下图,其中内存地址0x1001只是一个假设,知道代表一个内存地址就行。
image
那如果隐式实例化如下,引用与匿名对象是什么关系?
String s = "hello"; s = "world";
因为String是final类型,即一个String对象创建后就不能被修改。所以”world”是一个新匿名对象。s引用指向”world”,”hello”对象会被回收。如下图所示:
image
显式实例化:使用构造函数
public class Demo { public static void main(String[] args) { String s = "hello"; String s2 = new String("hello"); String s3 = new String("hello"); System.out.println(s==s2); System.out.println(s==s3); System.out.println(s2==s3); } }
从输出三个false可以看出,三个对象均不相同,从JVM分析,有如下图示:
image
显式实例化常用于将字符数组转换为字符串,使用方式如下:
String string = "hello"; char[] array = string.toCharArray(); return new String(array);
总结
直接赋值是一种共享设计,所有匿名对象放在方法区(逻辑上的堆)的字符串常量池中,新实例化的对象如果已经在字符串常量池存在,就不再新建对象,而是直接使用。所以推荐使用直接赋值方式。