若你困于无风之地,我将为你奏响高空之歌
1. 字符型常量和字符串常量
字符型常量相当于一个整数值,即ASSIC值,可以参与表达式运算。而字符串常量代表一个地址,即该字符串在内存中存放的位置。在占用内存空间上,字符型常量只占两个字节,而字符串常量占若干个字节。并且char是Java中的基本数据类型,而String不是。
2. 字符串常量池
JVM 为了提升性能和减少内存开销,避免同一内容字符串的重复创建,维护了一块特殊的内存空间,即全局字符串常量池。当需要使用某一字符串时,先去字符串常量池中查看该字符串是否存在,若存在,则直接使用。若不存在,则初始化,将该字符串放入字符串常量池中。
JDK1.6及以前,常量池在方法区,这时的方法区也叫做永久代;
JDK1.7的时候,方法区合并到了堆内存中,这时的常量池也可以说是在堆内存中;
JDK1.8及以后,方法区又从堆内存中剥离出来了,但实现方式与之前的永久代不同,这时的方法区被叫做元空间,常量池就存储在元空间。
3. String str="aaa"与 String str=new String(“aaa”) 的区别
String a = “aaa”
:程序运行时会在常量池中查找”aaa”字符串,若没找到,会将”aaa”字符串放进常量池,再将其地址赋给a;若找到,将找到的”aaa”字符串的地址赋给a。
String b = new String("aaa")
:程序会在堆内存中开辟一片新空间存放新对象,同时会将”aaa”字符串放入常量池,相当于创建了两个对象,无论常量池中有没有”aaa”字符串,程序都会在堆内存中开辟一片新空间存放新对象。
验证代码:
public class testString {
public static void main(String[] args) {
String str1 = "aaa";
String str2 = "aaa";
System.out.println("str1 == str2 " + (str1 == str2));
String str3 = new String("aaa");
String str4 = new String("aaa");
System.out.println("str3 == str4 " + (str3 == str4));
}
}
执行结果:
4. Java 中的基本数据类型
Java 中基本数据类型只有 8 个 :byte
、short
、int
、long
、float
、double
、char
、boolean
,其他的如 String 等都是引用类型(reference type)
5. String 的不可变性
1. 不可变性定义
不可变:
类的实例一旦创建后,其内容(状态)就不可改变。即一个对象一旦被创建后,整个对象就是不可变的。包括属任何性和状态。
理解 String 的不可变性,我们先要明确,字符串对象的概念。请看如下代码:
String string = "LiuWanQing";
string = "come on!";
等号左侧的 string 是存放在栈上的临时变量,而等号右面的 "LiuWanQing"
"come on!"
才是字符串对象,其存放在堆上。字符串对象是不可变的!!!
2. String 不可变的原因
我们可以观察一下,String 的实现源码
final修饰变量:
- 基础数据类型:一旦初始化,便不能改变其值。
- 引用类型(如 String):一旦初始化,便不能改变其引用,也就是不能指向一个新的对象,但是仍然可以修改引用指向的对象内容。
通过观察源码,我们可以得到不可变的原因为:
value使用final修饰;没有暴露成员变量;内部方法不会改动 value;类使用final修饰,不可继承
3. 不可变的好处
可用来做 HashMap 的 key: HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。
6. 包装类型
Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),两者之间可以互相转换。
装箱: 把基本类型转换成包装类型的过程叫做装箱(boxing)
自动装箱:
jdk1.5 开始引入了自动装箱 / 拆箱机制,自动将基本数据类型转化为对应的封装类型,成为一个对象以后就可以调用对象所声明的所有的方法
public static void main(String[] args) {
// 声明一个Integer对象,用到了自动的装箱:解析为:Integer num = Integer.valueOf(9);
Integer num = 9;
}
拆箱: 把包装类型转换成基本类型的过程叫做拆箱(unboxing)
自动拆箱:
public static void main(String[] args) {
// 声明一个Integer对象
Integer num = 9;
// 进行计算时隐含的有自动拆箱
System.out.print(num--);
}
原始类型:
boolean,char,byte,short,int,long,float,double
包装类型:
Boolean,Character,Byte,Short,Integer,Long,Float,Double