1. 自动装箱和自动开箱
定义:自动装箱就是可以把一个基本类型变量直接赋值给对应的包装类变量;自动开箱则与之相反,允许直接把包装类对象赋给一个对应的基本类型变量。
- 包装类可以实现基本类型变量和字符串之间的转换。有如下两种方式:
① 包装类.parseXxx(String s) ,但是 Character 除外
② 包装类.valueOf(String s)
String str1 = "123";
int it1 = Integer.parseInt(str1);
int it2 = Integer.valueOf(str1);
2. == 和 equals 的区别:
String a = "哈喽";
String b = "哈喽";
//true。 == 对于基本数据类型来说 比较的是值
System.out.println(a == b);
//true。 == 对于引用数据类型来说 比较的是引用。 -128 ~ 127
System.out.println(Integer.valueOf(2) == Integer.valueOf(2));
//false。 系统把-128~127之间的整数自动装箱成 Integer 实例缓存了起来
//超过此范围时需要重新创建一个 Integer 实例,所以128新创建了两个不同的引用
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));
//true。 equals 默认情况下比较的是引用,但很多类都重写了 equals 方法
//比如 String、Integer等 把它变成了值比较,所以一般情况下 equals 比较的是值
System.out.println(Integer.valueOf(128).equals(Integer.valueOf(128)));
Cat cat1 = new Cat("安静");
Cat cat2 = new Cat("安静");
// false。 == 对引用数据类型来说 比较的是引用
System.out.println(cat1 == cat2);
// false。Cat 方法是自定义的,没有像 String、Integer 那样重写 equals方法
// 所以默认比较的是引用
System.out.println(cat1.equals(cat2));
String s1 = new String("安静");
String s2 = new String("安静");
System.out.println(s1 == s2); //false。 比较的是引用
System.out.println(s1.equals(s2)); //true。String 重写了 equals 方法,使它比较的是值
System.out.println(65 == 'A'); //true
System.out.println(65 == 65.0f); //true
“= =” 与 equals 区别的总结: = = 对于基本类型来说是值比较,对于引用类型来说比较的是引用变量;而 Object 默认提供的 equals 方法只是比较对象的地址,即 Object 类的 equals 方法比较的结果与 = = 运算符比较的结果完全相同。但是很多类重写了 equals 方法,比如String、Integer 等 把它变成了值比较,所以一般情况下 equals 比较的是值了。
- new String(“hello”) 与 “hello” 的区别:
new String(“hello”) 时,JVM会先使用常量池来管理“hello”直接量,再调用String类的构造器创建一个新的String对象,新创建的String对象被保存在堆内存中。也就是一共产生了两个字符串对象。
3. 类成员
- 如果一个 null 对象访问实例成员(实例变量和实例方法),将会引发NullPointerException异常,因为 null 表明该实例根本不存在,那它的实例成员自然也不存在。但如果 null 对象访问的是类成员,则可以正常运行,因为使用实例来访问类成员时,实际上是委托给该类来访问类成员的。
- 但类成员不能访问实例成员
4. 单例类
- 定义:一个类始终只能创建一个实例,则这个类称为单例类
- 步骤:① 构造器使用private修饰,不允许其它类自由创建该类的对象。 private Singleton(){ }
② 提供一个public方法作为该类的访问点,由于调用该方法之前还不存在对象,因此只能用类来调用,所以该方法还需用static修饰。 public static Singleton getInstance()
③ 为了保证只创建一个对象,需要缓存已经创建的对象来提醒该类对象已创,因此需要添加一个成员变量,由于要被②中的静态方法访问,所以该成员变量要使用static修饰。 private static Singleton instance
class Singleton {
//使用类变量缓存已创建的实例
private static Singleton instance;
//对构造器使用private修饰,隐藏该构造器
private Singleton() {
}
//提供静态方法作为访问点,用于返回Singleton实例
//加入自定义控制,保证只产生一个Singleton对象
public static Singleton getInstance() {
if (instance == null)
//创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
return instance;
}
}
public class java11 {
public static void main(String[] args) {
//创建Singleton对象不能通过构造器,只能通过类名.getInstance来得到实例
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); //true
}
}
5. final
- 修饰基本类型变量和引用类型变量的区别:
① final 修饰基本类型变量后,不能对基本类型变量重新赋值
② final 修饰的引用类型变量不能被重新赋值,但可以改变该引用类型变量所引用对象的内容。
final int[] iArr = {5, 6, 12, 9};
iArr[2] = -8;
System.out.println(Arrays.toString(iArr)); // [5, 6, -8, 9]
// iArr = {1, 2, 34, 5}; 错误
- final 可定义“宏变量”
String s1 = "疯狂java";
String s2 = "疯狂" + "java";
System.out.println(s1 == s2); //true
String a1 = "疯狂";
String b1 = "java";
String s3 = a1 + b1;
System.out.println(s1 == s3); //false
final String a2 = "疯狂";
final String b2 = "java";
String s4 = a2 + b2;
System.out.println(s1 == s4); //true
上面代码中,将 a1 和 b1 通过final修饰后,编译器就可以对a1、b1两个变量执行“宏替换”,这样编译器即可在编译阶段就确定 s3 的值(即 s4),就会让 s3 指向字符串池中缓存的“疯狂java”。