常用类之String
我们使用Java语言写的第一个程序,就是向世界发出呐喊,在控制台输出了一个“Hello World!”。今天就来聊聊这个用""包裹的字符串。
1.String是什么?
String是Java中内置的一个类(java.lang.String),用“”包裹内容,表示字符串。
例:String name = "Tom";
name就是一个字符串对象,其值为“Tom”。
2.堆、栈、方法区
在这里要先简单的扩展一下JVM内存中的其中三个:堆、栈和方法区所存储的东西都是什么。
- 栈:存放的是基本数据类型的值、引用数据类型的引用值。
- 堆:引用数据类型的真实值。
- 方法区:存放的是所有的calss、static变量。(方法区中包含的都是在程序中唯一的元素)
3.String的使用
首先我们需要知道的是,String对象的值(字符串)存储在方法区中的字符串常量池。因为在实际的开发中,可能会频繁的使用字符串,所以把它放在常量池中提高程序的执行效率。
我们通过查看String的源码可以发现:字符串的底层是通过private final byte[] value;
来实现的,final修饰的变量是常量,不能二次赋值,所以,字符串是不可变的。
这是什么意思呢?我们举个例子,并且通过简单的图来理解一下:
- 我们知道字符串是可以直接赋值的:
package com.dh.string;
public class StringDemo01 {
public static void main(String[] args) {
String s1 = "abc";
//直接使用+进行字符串的拼接
String s2 = s1 + "de";
}
}
分析:执行第一句时,会将"abc"放在方法区的字符串常量中。然后执行第二句,字符串常量池中存在s1,即"abc"字符串,所以直接从字符串常量池中拿;因为字符串常量中不存在"de",所以需要在常量池中新开辟一个内存用于存放"de",但是没有引用指向它。此时执行字符串的拼接,但是因为字符串是不可变的,所以不能修改原来的"abc",而是需要在字符串常量中存放一个新的字符串"abcde",s2引用指向该字符串。
面试题:
package com.dh.string;
public class StringDemo2 {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
//结果为true
System.out.println(s1 == s2);
/*
分析:因为执行第一句代码时,常量池创建了一个字符串"abc",执行第2句代码时,因为存在了"abc",故不会新建,只是将s2的
引用指向s1,所以s1和s2的引用是相同的,==比较引用数据类型时比较的就是引用值,故结果为true。
*/
}
}
- 我们说String是个类,那么是类就会有构造方法,我们通过查看api文档就可以看到String类有哪些构造方法可以用来创建String对象
创建一个字符串对象,值为null:String s = new String();//s的值为""
创建一个字符串对象,值为hello:String s = new String("abc");
我们再来分析一下通过new创建的字符串对象又是如何存储的呢?
package com.dh.string;
public class StringDemo01 {
public static void main(String[] args) {
String s1 = "abc";
//直接使用+进行字符串的拼接
String s2 = s1 + "de";
//通过new实例化字符串对象
String s3 = new String("de");
}
}
分析:new的话是一定会在堆中多一个对象的,即在栈中有一个引用,指向堆中的对象,由于字符串是放在常量池中的,所以堆中的对象存放的就是字符串常量池中字符串的引用。
面试题:
package com.dh.string;
public class StringDemo3 {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
//结果为false
System.out.println(s1 == s2);
}
/*
分析:如下图
*/
}
面试题:此时共创建了几个对象?
注意:此时共创建了3个对象,堆中两个,常量池中一个。
扩展:垃圾回收器是不会释放常量的。
结论:通过上述例子,可以知道,使用==来比较字符串对象是不保险的,我们可以使用equals()方法来比较。(Object类中的equals()方法里还是使用 == ,所以String类重写了Object类的equals()。
package com.dh.string;
public class StringDemo3 {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
//结果为false
System.out.println(s1 == s2);
//使用equals(),结果为true
System.out.println(s1.equals(s2));
}
}
可自行查看源码~
本人水平有限,若有错误和不足,欢迎指出纠正~