文章目录
包装类
包装类的作用
- 某些方法需要对象类型的参数,比如需要接收Object的方法
- 方便实现基本数据类型到String的相互转换
- 包装类本身有一些包含一些方法
- 包装类可以实现序列化与反序列化
包装类的继承体系
Character和Boolean不是数值类型,直接继承自Object。
而Integer、Float、Double、Short、Long、Byte都是数值类型,继承自Number,Number再继承Object。
基本数据类型和包装类型相互转换
装箱:基本数据类型 -> 包装类
拆箱:包装类 -> 基本数据类型
jdk5之前
只能手动实现装箱与拆箱,如下
//手动装箱
int n1 = 5;
Integer n2 = Integer.valueOf(n1);//方式一
Integer n3 = new Integer(n1);//方式二
//手动拆箱
Integer m1 = new Integer(5);
int j = m1.intValue();
jdk5开始之后
可以支持自动装箱和自动拆箱,即基本数据类型和包装类可以直接相互赋值,如下
//自动装箱
Integer k1 = 5;//自动装箱底层调用了Integer.valueOf()
//自动拆箱
int k2 = k1;//自动拆箱底层调用了k1.intValue()
与String的相互转换
//包装类与String相互转换
Integer a = 355;
String b = String.valueOf(a);//Integer转String,用到了自动拆箱,因为参数a需要的是int,
Integer d = Integer.parseInt("123");//String转Integer,用到了自动装箱,因为原本结果是int
注意:调用哪个包装类的valueof()方法,就返回哪种数据类型,换句话说,你要得到什么数据类型,你就调用这个数据类型的valueof方法
包装类常用方法
转大小写、判断是否是数字、字母等等
Ingeter对象的创建
Ingeter的创建方式有两种,特别要注意直接使用赋值的方法创建的对象有什么特点。
方式一,直接new
//方式一,直接new
Integer integer = new Integer(5);
Integer integer1 = new Integer(5);
System.out.println(integer==integer1);//结果为false
结论:通过new方式创建的Integer,引用必然互不相同
方式二,通过int类型直接赋值(自动装箱)
//方式二,通过int类型直接赋值(自动装箱)
Integer integer2 = 5;
Integer integer3 = 5;
System.out.println(integer2==integer3);//结果为true
Integer integer4 = 150;
Integer integer5 = 150;
System.out.println(integer4==integer5);//结果为false
自动装箱底层调用的是valueof()方法,而valueof()源码如下
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
结论:可以得出,通过直接赋值的方法创建Ingeter,如果该值在[-128,127]之间,会返回的同一个引用(通过缓存数组实现);在此范围之外才会new新的对象,引用才会不一样
String类
String用于保存字符串常量,采用Unicode编码,汉字或字母都占用两个字节。
部分源码
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
}
由源码可知:
- String类本身是一个final修饰的类!
- String底层使用的是final修饰的char数组,不可修改。(这里的不可修改指的是数组地址不可修改,而不是数组元素内容不可修改,所以String可以通过反射获取char数组,从而改变内容。)
- String可序列化、可比较
- String一般获取不到(除反射外)其char数组内容,所以String是内容不可变的。
String创建方式
方法一:通过new关键字创建
方法二:直接赋值字符串的方式创建,也叫做字面量方式创建
区别:
- 字面量创建字符串会在字符串常量池中找,看是否有内容相同的对象,有的话则不创建(避免重复创建对象),没有的话就创建一个,最后返回池对象中的引用。(注:这里说的池中对象实体还是存储在堆中,只是引用放在在池中)
- new关键字创建时,前面的操作和字面量创建一样,只不过最后在运行时会在堆中创建一个新的对象,然后返回的也是堆中对象的引用。
总结:直接赋值创建的String直接返回常量池中的引用,而new创建的String则返回堆中的引用。可以直接理解,用双引号括起来表达的字符串"",返回的是常量池中的引用地址。
字符串常量池
java内存区划分为堆(存对象)、栈(存局部变量)、和方法区,其中方法区用于存储静态变量、常量等,所以各种常量池都在方法区之中,也包括了String常量池。(JDK7.0之后,常量池被移到了堆中)。
字符串工作原理很简单,核心就是当第一次创建某个字符串时,会在常量池中创建一个标记,如果下次创建的字符串内容当池中的标记内容相等的话,就不再重复创建了,当然这个机制的触发要看具体的创建方法,参考上一节。
String的不可变性
String如何保证不可变?
一方面是String底层使用了final修饰的char数组,保证了数组的引用地址不可变;另一方面是String没有暴露这个char数组(private),保证了内容不可变。最后一方面是,String类本身是final修饰的,不可被继承,从而方法不能被重写。
为什么String字符串要设置为不可变?
- 为了支持字符串常量池的正常工作,避免同时指向常量池中的对象之间相互影响。
- 提高并发安全性,不可能同时修改同一个String对象,因为修改最后都会返回新的String对象
- 提高hashcode()方法的性能,因为String不可变,所以hashcode()可以缓存起来复用
拓展:如何设计一个不可变类?
- 类本身设置final修饰,保证不可被继承
- 成员变量用final修饰,保证不可修改,如果是数组,则要再用privite修饰,保证不被外部访问