阿里云【名师课堂】Java面向对象开发21 ~ 25:String类的基本特点
21:String类两种实例化方式
直接赋值
之前的讲解过程中说到并使用过,String
可以采用直接赋值的方式进行处理,这一点类似于基本数据类型。这种方式较为常用。
范例:直接赋值实例化对象
public class StringDemo {
public static void main(String args[]) {
// str是一个对象,那么“hello”就应该保存在堆内存之中
String str = "Hello" ;
System.out.println(str) ;
}
}
new实例化新对象
String是一个类,类中必定提供有构造方法。而在String类中恰好提供了如下构造方法:
public String (String str) ;
- 注意这里的两个String,第一个是构造方法名,第二个是数据类型。
范例:观察程序
public class StringDemo {
public static void main(String args[]) {
// 既然String是类,可以通过关键字new进行对象实例化
String str = new String("Hello") ;
System.out.println(str) ;
}
}
22:字符串相等比较
众所周知,基本数据类型变量通过==
进行是否相等的比较,那么String呢?
范例:观察String直接使用==的情况
public class StringDemo {
public static void main(String args[]) {
// 通过关键字new进行对象实例化
String str1 = "Hello" ;
String str2 = new String("Hello") ;
System.out.println(str1 == str2) ;
}
}
两个字符串内容是相同的,但是输出却是false,为什么?需要内存分析:
要知道,==
进行的是数值的比较(char
比较的是字符的编码。也是数值)。如果==
用在String之中,所比较的就是两个对象(str1、str2)所保存的内存地址的数值,而并没有比较对象的内容。
String类提供的方法
这里暂时先学习这个方法的变形:public boolean equals(String str) ;
范例:进行String对象内容比较
public class StringDemo {
public static void main(String args[]) {
// 通过关键字new进行对象实例化
String str1 = "Hello" ;
String str2 = new String("Hello") ;
System.out.println(str1.equals(str2)) ;
}
}
总结:==
和equals()
的区别
==
进行的是数值比较,比较的是两个字符串对象的内存地址数值;equals()
可以进行字符串内容的比较
23:字符串为匿名对象
在任何语言的底层上面都不会提供有直接的字符串类型,现在所谓的高级语言只是为了方便用户开发所提供的一个支持。所以在Java中本身也没有直接提供字符串常量的概念,所有使用""
定义的内容本质上来讲都是String类的匿名对象。
范例:观察字符串操作
public class StringDemo {
public static void main(String args[]) {
String str = "Hello" ;
System.out.println(str.equals("Hello")) ; // 通过str对象调用String类
System.out.println("Hello".equals(str)) ; // "Hello"是匿名对象,当然也可以调用String类中的任意方法
}
}
可以看出String str = "Hello" ;
就是给一个匿名对象"Hello"
设置名字str
,并且匿名对象一定保存在堆内存之中。
提醒:在日后的开发过程中,如果要判断用户的输入是否等于指定的字符串,一定要把指定字符串写在前面。
- 范例:观察指定字符串写在后面的情况
public class StringDemo {
public static void main(String args[]) {
// 用户输入操作暂时没讲解,这里暂时用input代替
String input = null ; // 用户输入为空
System.out.println(input.equals("Hello")) ;
}
}
在进行用户数据接收的时候必须要考虑用户没有输入数据的问题,这时会出现如图所示异常。
- 范例:观察指定字符串写在后面的情况
因此要把指定字符串写在前面。因为任何的字符串常量都是String类的匿名对象,所以该对象永远不可能为null。
public class StringDemo {
public static void main(String args[]) {
// 用户输入操作暂时没讲解,这里暂时用input代替
String input = null ; // 用户输入为空
System.out.println("Hello".equals(input)) ;
}
}
24:String两种实例化区别
两种实例化模式中使用哪一种更好?二者的区别?
使用直接赋值
public class StringDemo {
public static void main(String args[]) {
// str是一个对象,那么“hello”就应该保存在堆内存之中
String str = "Hello" ;
}
}
- 小小的问题:使用直接赋值继续创建新的字符串对象:
public class StringDemo {
public static void main(String args[]) {
// str是一个对象,那么“hello”就应该保存在堆内存之中
String str1 = "Hello" ;
String str2 = "Hello" ;
String str3 = "Hello" ;
System.out.println(str1 == str2) ;
System.out.println(str1 == str3) ;
System.out.println(str2 == str3) ;
}
}
可以看出,创建新对象并没有开辟新的内存空间,而是三个栈内存指向同一个堆内存。
- 为什么会这样?
Java中有个对象池概念,字符串共享,懒得打这么多字,摸了。
new实例化新对象
使用new实例化对象的方法会开辟两块内存空间,其中有一块堆内存空间成为垃圾内存空间。除此之外,也会对字符串共享产生问题。
通过22、字符串相等比较我们知道,通过直接赋值和new实例化的同一个字符串无法比较用==
比较是否相同,因为new出来的对象没有入池。
但是并不意味着new出来的字符串不能进入到对象池保存,关键:可以手工处理——使用String类中的public String intern() ;
方法进行手工入池操作。
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello" ;
String str2 = new String("Hello").intern() ;
System.out.println(str1 == str2) ;
}
}
总结:String类中两种实例化方式的区别
- 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以保存在对象池中以供下次使用。
- 构造方法:会开辟两块堆内存空间,并且其中一块会成为垃圾空间,而且实例化的对象不会自动保存在对象池之中,而是使用
intern()
方法手工入池。
25:字符串常量不可变更
字符串一旦定义不可改变。
再次强调:在任何语言的底层实现的都是字符数组,数组的最大缺陷:长度固定,所以在定义字符串常量的时候本身它的内容是不可改变的。
范例:观察程序
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello" ;
str1 += "World" ;
System.out.println(str1) ;
}
}
- 为什么改变了?
- 以上字符串的变更是字符串对象的变更,而并不是常量的改变,观察内存图:
因此,在代码开发过程中不能出现这类代码(对字符串进行+-
之类操作),因为会产生大量垃圾内存。
总结
- 字符串的使用就采用直接赋值的模式完成;
- 字符串的比较就使用
equals()
方法实现; - 字符串没事别改变,迫不得已时也别改变太多。