String类型是java中一种很重要的数据类型,但实际上,String是一个类,位于java.lang包下,定义如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
java.lang包存放着系统常用的基础类,例如String,Object等,JDK1.1之后自动导入,所以我们在创建字符串的时候不需要import依赖包。
String类实现了Serializable接口,可以进行序列化和反序列化。而且也实现了Comparable接口,并且覆写了compareTo方法。CharSequence接口定义了字符序列相关的信息。
1. 实例化方式
String有两种实例化方式,一种是直接赋值,另一种是使用String类的构造方法进行实例化。
直接赋值
String str = "Hello World" ;
在任何的语言的底层,都不会提供有直接的字符串类型。字符串类型只是高级语言提供给用户方便开发的支持而已。
在java之中,本身也没有直接提供字符串类型的概念,java本身提供的只有八大基本数据类型,而所有使用双引号 ” “定义的字符串本质上来讲都是String的匿名对象。
所以,String通过直接赋值所创建的字符串就是在堆上的一个String匿名对象。
构造方法构造:
String str = new String("Hello World");
虽然使用构造方法创建对象是最标准的创建方式,但是字符串创建一般推荐使用第一种方式。
2. 两种实例化的区别
String类的设计使用了共享设计模式。在JVM底层实际上会自动维护一个对象池(字符串对象池),如果现在采用了直接赋值的方式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用;如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。所谓的对象池就是一个对象数组(目的就是减少开销)。
== 操作符两端为数字时,比较的是它们的值,如果是对象,比较的是它们的地址。那么用其进行String直接赋值的验证,如下:
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
流程图如下:
但是,如果使用构造方法进行String的创建,那么每次new一次,就会直接在堆上创建新的字符串对象,而不会从池中获取,看下面的语句:
String str = new String("hello") ;
上面的过程实际上开辟了两个字符串空间,首先是括号内的”hello”字符串,因为所有双引号引起的字符串都是String的匿名对象,然后是通过其new出来的新字符串,这两者内容一样,但是不是同一片空间,而且,str指向第二块空间,第一块字符串空间没有指向,称为垃圾空间,如下图所示:
另外,new出来的字符串是不会入池的,但是我们可以采用String提供的实例方法intern()将字符串进行入池操作。
综上,可以看出两种方式的区别:
方式 | 特点 |
---|---|
直接赋值 | 只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。 |
构造方法 | 会开辟两块堆内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,可以使用intern()方法手工入池。 |
推荐使用第一种,效率高而且语法简单。