《Java深入解析-透析Java本质的36个话题》笔记_第五章

## 第5 章 类与接口220

### 话题30 相辅相成——基本数据类型与包装类220

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(1)自动装箱拆箱

Java的一些类库(集合类)不支持基本数据类型,在1.5之前需要手动将基本数据类型转包装类型。取出数据还要调用包装类的xValue方法。

list.add(new Integer(value));

Integer i = list.get(index);

i.intValue();

 

1.5之后增加自动装箱拆箱,不需要显式转换

list.add(2);

int i = list.get(index);

 

注意有些情况不能把基本数据类型当包装类使用

如:

int i = 10;

Integer integer = i.valueOf(2);//报错

 

Short s1 = 1;

Short s2 = 2;

s1 +=s2; // 报错,相当于s1 = (Short)(s1 + s2);

 

(2)包装类的缓存

包装类也是不可变的,跟String类相似。

包装类实现了缓存,基本数据类型转包装类型,如果基本数据类型存在包装类型的缓存范围内,则返回该缓存对象。

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(3)Interger类特殊性

Interger类可以设置系统属性修改缓存上限。

“==” 或“!=” 运算符,如果一个操作数是基本数据类型,另一个操作数是包装类型,包装类型会转成基本数据类型再比较值。

 

### 话题31 分门别类——数组的阐述232

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(1)数组声明

int[] i1;

int i2[];

 

(2)多维数组

Java中多维数组不是真正的多维,采用的是数组的数组实现的

《Java深入解析-透析Java本质的36个话题》笔记_第五章

区别C/C++,Java中的多维数组可以不是矩阵数组。

int [][] a = new int[2][];

a[0] = new int[4];

a[1] = new int[5];

 

a[0] 跟 a[1] 元素个数不一样。

(3)数组clone

数组的clone方法为浅复制,仅保证复制后数组的地址不一样,元素没有复制。

《Java深入解析-透析Java本质的36个话题》笔记_第五章

 

如果数组的对象是引用类型,有可能修改了数组的某个元素内部数据会导致别的地方使用的错误。

多维数组(维数在2~255之间)经过复制,只有最高维指向的对象不同,其它低维度的是相同的。

String[][][] a = new String[1][1][1];

a[0][0][0] = ""Java;

String[][][] b = a.clone;

 

a == b:false

a[0] == b[0] :true;

a[0][0] == b[0][0] :true;

a[0][0][0] == b[0][0][0] :true;

 

(4)数组输出

char类型的数组print输出的格式跟其他类型的不一样,将数组中所有字符依次输出。其它类型数组输出都是toString返回的结果。

 

### 话题32 规矩方圆——定义规范的接口类型242

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(1)如果定义的接口为空(没有任何成员),还是存在9个方法成员,来自于Object中声明的9个public方法

接口中所有变量都是public static final ,所有方法都是public abstract,所有内部类和内部接口都是public static

(2)接口是不能实例化的,使用new创建的实例是实现了接口的匿名内部类

(3)接口允许多继承,继承规则跟类的相似

《Java深入解析-透析Java本质的36个话题》笔记_第五章

 

两个接口有相同名称变量x,当同时实现两个接口,调用x必须使用限定名称,否则会发生编译错误。

两个接口有相同名称方法,如果重载了,要全部实现,如果不是,只需实现一个。参数要是两个重名方法参数的子签名(List是List<String>和List<Object>的子签名),返回类型是两个重名方法返回类型的可替换类型。

interface Dog{

    String kind = "Dog";

    void run(List<String> list);

    Colection<String> jump();

}

interface Cat{

    String kind = "Cat";

    void run(List<Object> list);

    List jump();

}

class Animal implements Dog,Cat{

    String s1 = Dog.kind ;

    String s2 = Cat.kind ;

    @Override

    public void run(List list){}

    @Override

    public List jump(){}

}

 

### 话题33 彻里至外——嵌套类型248

(1)静态成员类

static 修饰的类,不需依赖外围类

(2)内部类(非静态嵌套类)

内部类需要持有外围类的引用才能创建,创建内部类对象时,系统会将外围类对象作为参数传入内部类的构造器中

内部类不能声明静态成员或静态初始化块,定义final修饰的值是编译期常量的静态变量是可以的。

继承内部类,在构造方法中需要绑定一个外围类对象

 

```java

// 继承内部类,在构造方法中需要绑定一个外围类对象

public class InnerClassTest extends Outer.Inner {

 // 注释了这段会报错

 public InnerClassTest(Outer outer) {

  outer.super();

 }

}

 

class Outer {

 // 创建内部类对象时,系统会将外围类对象作为参数传入内部类的构造器中进行绑定

 class Inner {}

}

 

/**

 * 子类继承父类

 * 子类内部类继承父类内部类

 */

class InnerClassTest2 extends Outer {

 class Inner extends Outer.Inner {}

 

 public void go() {

  InnerClassTest2 innerClassTest2 = new InnerClassTest2();

  // Inner 持有InnerClassTest2对象引用,Inner是Outer.Inner子类,Inner初

  // 始化的时候调用Outer.Inner的构造方法,Outer.Inner需要跟外围类Outer对象绑定,

  // InnerClassTest2对象也是Outer 的子类,跟Outer.Inner绑定是可以的

  InnerClassTest2.Inner inner = innerClassTest2.new Inner();

 }

}

```

(3)本地内部类

在方法,构造器,初始化块中声明的类

不能使用访问修饰符和static修饰

本地内部类可以访问其声明的方法,构造器,初始化块所属类的左右成员,如果是在静态方法或静态初始化块中声明,需要通过对象引用才能访问实例成员。

本地类只能访问final修饰的局部变量或参数

public class LocalClass{

    LocalClass(){

        int x = 2;

        final int y = 3;

        class local01{

            // int z = x;//报错

            int w = y;

        }

    }

}

 

(4)匿名内部类

跟本地内部类类似,只能访问final局部变量和方法(构造器)的参数

匿名类会隐式继承某个类或实现某个接口,所以不能继承和实现接口(尽管接口可以多继承)

《Java深入解析-透析Java本质的36个话题》笔记_第五章

 

(5)嵌套接口

在类中或接口中声明的接口,嵌套接口是静态的

类实现接口,并不需要实现接口里面的嵌套接口

 

### 话题34 不胜枚举——枚举的神秘258

《Java深入解析-透析Java本质的36个话题》笔记_第五章

C/C++ 的枚举类型是int类型常量值,不安全。

1.5 加入枚举

枚举是类,枚举常量是类的对象,在枚举类外无法创建枚举对象

枚举类实例初始化中不能访问静态变量(枚举类特殊初始化方式:构造器先于静态初始化),但值是编译时常量的静态变量可以

 

### 话题35 按部就班——加载、链接与初始化265

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(1)虚拟机启动后,会创建一个启动类加载器,启动类加载器会加载扩展类加载器和系统类加载器。

(2)双亲委派

《Java深入解析-透析Java本质的36个话题》笔记_第五章

《Java深入解析-透析Java本质的36个话题》笔记_第五章

调用ClassLoader类的loadClass方法加载一个类或接口,类或接口在加载后不会初始化。

Java库中的类是由启动类加载器加载的,自定义的类通常由系统类加载器加载的。

(3)深入初始化

如果构造器没有调用本类的其它构造器,可看做所有的实例变量声明处初始化和实例初始化块都会复制到所有构造函数执行语句的最前面。如果调用本类其它构造器,这种复制在其它构造器中执行,调用this的构造器不执行这种复制。所有的静态变量声明处初始化可看做复制到静态初始化块中。

```java

private int x;

private int y = 20;

private static int xStatic;

private static int yStatic = 20;

 

{

 x=20;

 if(y>20) {

  throw new Exception();

 }

 //return 不能使用

}

 

static {

 xStatic = 30;

 if(yStatic>20) {}

 //return 不能使用

}

 

public InitQuestion() throws Exception{

 this(0,0);

}

 

public InitQuestion(int x) throws Exception{}

 

public InitQuestion(int x,int y) throws Exception{}

// 可以看做下面的

private int x;

private int y;

private static int xStatic;

private static int yStatic;

 

static {

 yStatic = 20;

 xStatic = 30;

 if(yStatic<=20) {}

}

 

public InitQuestion() throws Exception{

 this(0,0);

}

 

public InitQuestion(int x) throws Exception{

 y= 20;

 x=20;

 if(y>20) {

  throw new Exception();

 }

}

 

public InitQuestion(int x,int y) throws Exception{

 y= 20;

 x=20;

 if(y>20) {

  throw new Exception();

 }

}

```

 

实例初始化块和静态初始化块不能使用return。

静态初始化块不能抛出任何受检异常。

实例初始化块能抛出任何受检异常,前提是类中所有构造器必须同时抛出相应的异常或异常的父类。

 

<init> 与 <clinit>

编译器为每一个构造函数创建<init>方法。这些构造函数集合了实例变量声明处初始化和实例初始化块。

也为所有静态初始化语句集中到一起,生成 <clinit>方法,如果没有静态语句就不生成。

 

(4)类初始化

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(5)接口初始化

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(6)主动使用与被动使用

主动使用:访问的成员是类(接口)中声明的成员。

被动使用:访问的成员是父类继承的。

被动使用时不会初始化被动使用关联的类(接口)

 

### 话题36 择优录取——类型及其成员的选择283

《Java深入解析-透析Java本质的36个话题》笔记_第五章

遮蔽:在某作用域中,相同名字的类型或类型成员,其中一个遮蔽另外一个。

例如:在构造函数的参数value作用域内遮蔽了相同名字的成员变量value。

class A{

private int value;

public A(int value){

this.value = value;

}

}

(1)类型选择

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(2)变量选择

《Java深入解析-透析Java本质的36个话题》笔记_第五章

《Java深入解析-透析Java本质的36个话题》笔记_第五章

 

(3)方法选择

《Java深入解析-透析Java本质的36个话题》笔记_第五章

(4)模糊

当名称可能解析为变量名,类型名或包名时,变量名优先于类型名,类型名优先于包名。