---
url: /doc/Java/vux436fp/index.md
---
## 9.1 类变量和类方法

### 9.1.1 类变量

> 类变量：也叫 静态变量/静态属性。是该类所有对象共享的变量。任何一个该类对象访问时都是相同的值，任何一个该类对象修改时也是同一个变量。
>
> 语法（推荐）：`访问修饰符 static 数据类型 变量名;`
>
> 或者也可以：`static 访问修饰符 数据类型 变量名;`
>
> \==根据 JDK 版本的不同，类变量存放在 堆 中或 方法区 中。==

1. 什么时候需要用类变量：

   当我们需要让某个类的所有对象都共享一个变量时，就可以考虑使用类变量（静态变量）

2. 类变量 与 实例变量（普通属性）的区别：

   类变量 是该类所有对象共享的，而 实例变量 是每个对象独享的

3. 加上 `static` 称为 类变量 或 静态变量。否则称为 实例变量/普通变量/非静态变量

4. 静态变量 可以通过 `类名.类变量名;` 或 `对象名.类变量名;` 来访问。但 Java 设计者推荐我们用 `类名.类变量名;` 来访问。（需满足访问权限和范围）

5. \==类变量 是在加载类时就初始化了。==所以，没有创建对象实例也能访问。

6. \==类变量 的生命周期是随着 类的加载 开始，随着 类的消亡 而销毁。==

7. 特别地：**一个 null 对象也可以访问静态变量 / 静态方法**

   ```java
   public class Test{
       static int n = 0;
       static void met() {
           System.out.println(++n);
       }

       public static void main(String[] args){
           Test t = null;
           System.out.println(t.n);			//这样不会报错
           t.met();							//这样也不会报错
       }
   }
   ```

### 9.1.2 类方法

> 当方法使用 `static` 修饰后，就是 静态方法。静态方法就能访问静态属性。如果我们不希望创建实例，也能调用方法，这个场合把方法做成静态方法是合适的。开发工具类时就可以如此做。

1. 类方法和普通方法都是随着类的加载而加载，将结构信息存储在 方法区。
2. 类方法中不允许使用和对象有关的关键字。所以，类方法没有 `this` 或 `super`
3. 类方法可以通过类名调用，也能通过对象名调用。普通方法不能通过类名调用。
4. \==类方法 中只能访问 类变量 或 类方法==
5. \==普通方法既可以访问普通方法也可以访问类方法==

## 9.2 理解 `main` 方法语法

> ```java
> public static void main(String[] args){...}
> ```

1. `main` 方法 **是 JVM 调用的方法**。所以该方法的 访问权限 必须为 `public`

2. **JVM 在执行 `main` 方法时不必创建对象，所以 `main`方法 必须为 `static`**

3. \==该方法接收 `String` 类型的数组参数。该数组中保存执行 Java 命令 时传递给所运行的类的参数。==

   > 工作台中：`javac 执行的程序.java`
   >
   > `java 执行的程序 参数1(arg[0]) 参数2(arg[1]) 参数3(arg[2]) ..`
   >
   > [IDEA 中怎么做？](https://www.bilibili.com/video/BV1fh411y7R8?p=385\&t=129.5)

4. 在 `main` 方法 中，我们可以直接调用 `main` 方法 所在类的静态方法或静态属性。

   但是，不能直接访问该类中的非静态成员，必须创建该类的一个实例后才能通过该实例访问非静态成员。

   ```java
   例如：
   public class Hello{
   	private String str = "hello";
   	public static void main(String args[]){
   		//System.out.println(str);//这样做是错的，静态方法不能访问静态域
   		//1.创建此属性的一个实例
           Hello hello =  new Hello();
   		System.out.println(hello.str);//这样间接访问了str
   }
   }
   ```

## ==9.3 代码块==

> 代码块：又称为 初始化块。属于类中的成员。类似于方法，将逻辑语句封装在方法体中，通过 `{ }` 包围起来。
>
> 和方法不同，没有方法名，没有返回，没有参数，只有方法体，而且不用通过对象或类 显式调用，而是==加载类时，或创建对象时 隐式调用。==
>
> 语法：`[修饰符]{代码};`

1. 修饰符 是可选项，可不写。要写的话，只能写 `static`
2. 代码块分为两类：
   * 静态代码块：有 `static`
   * 普通代码块：无 `static`
3. 逻辑语句可以为任意的逻辑语句。
4. `;` 可以写，也可以省略。建议写上。
5. \==代码块相当于另一种形式的构造器（构造器的补充机制），可以做初始化操作==
6. \==如果多个构造器中都有重复语句，就可以抽取到初始化块中，\*\*提高代码复用率。\*\*这样，不管用哪个构造器，都会执行代码块。==

### 9.3.1 使用细节

1. `static` 代码块：作用是对类进行初始化。**随着 类的加载 会且只会执行一次**。相对的：**普通代码块每创建一个对象就执行一次。**

* \*\*类什么时候被加载？ \*\*

  * 创建对象实例时（new）
  * 创建子类对象实例，父类也会加载
  * 使用类的静态成员时（父类也会加载）

  以上情况下类会被加载。加载后不需要再次加载，所以，静态代码块也只会执行一次。

1. 创建一个对象时，在 **一个类里** 调用顺序是：

   * 调用静态代码块 和 静态属性初始化。这两者优先级相同，多个存在时按照定义的顺序依次执行。
   * 调用普通代码块 和 普通属性初始化。这两者优先级也相同。
   * 调用构造器。

2. \==`构造器` 的最前面其实隐含了 `super();` 和 `调用普通代码块`。==而静态相关的代码块，属性初始化，在类加载时就执行完毕了。

   这样，创建一个对象时，在 **有继承关系的多个类里** 调用顺序是：

   * 父类 静态代码块 和 静态初始化
   * 子类 静态代码块 和 静态初始化
   * 父类 普通代码块 和 普通初始化
   * 父类 构造器
   * 子类 普通代码块 和 普通初始化
   * 子类 构造器

3. 静态代码块 只能调用 静态成员。普通代码块 能调用 任意成员。

## ==9.4 单例设计模式==

> 什么是设计模式：设计模式是在大量的实践中总结和理论化后优选的代码结构、编程风格、解决问题的思考方式。设计模式就像是经典的棋谱，免去我们自己再思考和摸索。

单例设计模式：采取一定的方法，保证再整个的软件系统中，对某个类只能存在一个对象实例，并且该类只提供一个取得其对象实例的方法

### 9.4.1 应用实例

> 后面会学更多，这里先展示两种：饿汉式、懒汉式

#### #9.4.1.1 饿汉式

步骤如下：

1. 构造器私有化（防止用户直接 new）

2. 类的内部创建对象

3. 向外暴露一个静态的公共方法

4. 代码实现

   > ```java
   > class GF{
   >        private String name;
   >     private static GF gf = new GF("萝茵");
   >        private GF(String name){
   >            this.name = name;
   >        }
   >        public static GF getGF(){
   >            return gf;
   >        }
   > }
   > ```
   >
   > 对象，通常都是重量级的对象
   >
   > 有时，我们用不到这个创建的对象，那个场合，会造成资源浪费。

#### #9.4.1.2 懒汉式

步骤如下：

1. 构造器私有化

2. 定义一个静态属性对象

3. 提供一个静态的公共方法，可以返回对象。如果静态对象为空，则创建对象

4. 代码实现

   > ```java
   >   >class GF{
   >    private String name;
   > private static GF gf;
   >    private GF(String name){
   >        this.name = name;
   >    }
   >    public static GF getGF(){
   >        if(gf == null){
   >            gf = new GF("萝茵");
   >        }
   >        return gf;
   >    }
   >   >}
   > ```

#### #9.4.1.3 两种方法对比

1. 二者创建对象的时机不同。饿汉式在加载类信息时创建，懒汉式在使用时才创建
2. \==饿汉式可能造成资源浪费，懒汉式可能存在线程安全问题（学习\[线程]后会进行完善）。==
3. Java SE 标准类中 java.lang.Runtime 就是一个单例模式。

## ==9.5 `final` 关键字==

> `final` 可以修饰 类、属性、方法、局部变量
>
> 以下情况下，可能用到 `final`
>
> 1. `final` 修饰类：该类不能被继承
> 2. `final` 修饰方法：该方法不能被重写
> 3. `final` 修饰值：该值不能被修改

### 9.5.1 使用细节

1. `final` 修饰的属性又叫常量，一般用 XX\_XX\_XX 来命名（全大写字母+下划线）

2. \==`final` 修饰的属性在定义时，**必须赋初始值**，且之后不能再修改。==赋值可以在下列位置之一：

   * 定义时
   * 构造器中
   * 代码块中

   注意：如果 `final` 修饰的属性是静态的，则只能在以下位置赋值。

   * 定义时
   * 静态代码块中

3. `final` 类不能继承，但能实例化对象。对的，是可以的。

4. \==如果不是 `final` 类，但含有 `final` 方法，虽然该方法不能重写，但能被继承。==

5. \==`final` 类可以有 `final` 方法。可以，但没必要==

6. \==`final` 不能修饰构造方法。==

7. \==`final` 和 `static` 搭配使用，效率更高（那个场合，虽然顺序不限，还是推荐 `static` 在前）。底层编译器做了优化处理。这样做，调用 **属性（定义时赋值）** 时居然 **不会造成类的加载！**==

8. \==包装类（Integer、Double、Float、Boolean、String 等）都是 `final` 类，都不能被继承。==

## ==9.6 抽象类==

> 当父类的某些方法需要声明，却不知道如何实现时，可以将其声明为抽象方法。那个场合，要将该类声明为 `abstract` 类。
>
> \==抽象类的价值更多是用于设计。设计者设计好后，让子类继承并实现。也是考官爱问的考点。==

> 定义抽象类：`访问修饰符 abstract 类名{...}`
>
> 定义抽象方法（注意：无方法体`{}`）：`访问修饰符 abstract 返回值 方法名(形参列表);`

### 9.6.1 使用细节

1. 抽象类不能被实例化
2. \*\*抽象类不一定包含抽象方法。\*\*也就是说，抽象类可以没有 `abstract`方法
3. **一旦包含 `abstract` 方法，则该类一定要声明为 `abstract`**,==一个抽象类包含一个或多个抽象方法，并不是全部都是抽象方法==
4. **`abstract` 只能修饰 类 和 方法**，不能修饰其他。
5. \==抽象类可以有任意成员（非抽象方法、构造器、静态属性等）==。即，抽象类本质还是类。
6. 抽象方法不能有主体。即，抽象方法不能实现。
7. \==如果一个类继承了 `abstract` 类，则其必须实现所有 `abstract` 方法，除非其自己也是 `abstract` 类。==
8. \*\*抽象方法不能用 `private` `final` `static` 来修饰。\*\*因为，这些关键词都和 重写 相违背。

### ==9.6.2 抽象类最佳实践-模板设计模式==

> 抽象类体现的就是一种模板模式的设计，抽象类作为多个子类的通用模板，子类在抽象类的基础上进行扩展、改造，但子类总体上会保留抽象类的行为方式。

模板设计模式能解决的问题：

1. 当功能内部一部分实现是确定，一部分实现是不确定的。这时可以把不确定的部分暴露出去，让子类去实现。
2. 编写一个抽象父类，父类提供了多个子类的通用方法，并把一个或多个方法留给其子类实现，就是一种模板模式。

**最佳实践**

1. 有多个类，完成不同的任务 job
2. 要求统计得到各自完成任务的时间
3. 实现 TestTemple.java

```java
package com.hspedu.abstract_;

abstract public class Template { //抽象类-模板设计模式

    public abstract void job();//抽象方法

    public void calculateTime() {//实现方法，调用job方法
        //得到开始的时间
        long start = System.currentTimeMillis();
        job(); //动态绑定机制，调用的是运行类型的job方法(是谁就用谁的)
        //得的结束的时间
        long end = System.currentTimeMillis();
        System.out.println("任务执行时间 " + (end - start));
    }
}
```

```java
package com.hspedu.abstract_;

public class AA extends Template {

    //计算任务
    //1+....+ 800000
    @Override
    public void job() { //实现Template的抽象方法job

        long num = 0;
        for (long i = 1; i <= 800000; i++) {
            num += i;
        }
    }

//    public void job2() {
//        //得到开始的时间
//        long start = System.currentTimeMillis();
//        long num = 0;
//        for (long i = 1; i <= 200000; i++) {
//            num += i;
//        }
//        //得的结束的时间
//        long end = System.currentTimeMillis();
//        System.out.println("AA 执行时间 " + (end - start));
//    }
}

```

```java
package com.hspedu.abstract_;

public class BB extends Template{

    public void job() {//这里也去，重写了Template的job方法

        long num = 0;
        for (long i = 1; i <= 80000; i++) {
            num *= i;
        }

    }
}

```

```java
package com.hspedu.abstract_;

public class TestTemplate {
    public static void main(String[] args) {

        AA aa = new AA();
        aa.calculateTime(); //这里还是需要有良好的OOP基础，对多态

        BB bb = new BB();
        bb.calculateTime();
    }
}
```

## 9.7 接口

> 接口就是给出一些没有实现的方法，封装到一起，到某个类要用的时候，再根据具体情况把这些方法写出来。
>
> 语法：`interface 接口名{...}`
>
> ```
> class 类名 implements 接口名{...必须实现接口的抽象方法...}
> ```
>
> 注意：JDK 7.0 以前，接口中只能是抽象方法。而 JDK 8.0 后，接口可以有静态（`static`）方法、默认（`default`）方法。
>
> 在接口中，抽象方法可以省略 `abstract`

接口中可以存在：

* 属性（只有静态 `static` 属性，可以不加 `static` 关键字）
* 方法（抽象 `abstract` 方法、默认 `default` 实现方法、静态 `static` 方法）

### 9.7.1 使用细节

1. 接口 不能被实例化。
2. \*\*接口中所有方法都是 `public` 方法。\*\*接口中的 抽象方法 可以不用 `abstract` 修饰。
3. 一个普通类实现接口，就必须把该接口所有方法都实现。（快捷键`alt + enter`）
4. **抽象类实现接口，可以不用实现接口的方法。**
5. 一个类可以同时实现多个接口。`class Name implements In1,In2{...}`
6. \==接口中的属性只能是 `final` 的，并且是 `public static final` 修饰符。修饰符就算不写，还是这样。==
7. 接口中属性的访问形式：`接口名.属性名`
8. 接口不能 **继承** 其他的类，但可以 **继承** 多个别的接口。（不是也不能 实现 别的接口）
9. \==接口的修饰符**只能是 `public` 和 默认**。这点和类的修饰符相同。==

### 9.7.2 实现接口 vs 继承类

1. 当子类继承父类，就自动拥有父类的所有功能。如果需要扩展功能，可以通过接口方式扩展。
2. 可以认为，接口 是对于 Java 单继承机制的补充。
3. 继承的价值主要在于：解决代码的复用性和可维护性。
4. 接口的价值主要在于：设计。设计好各种规范，让其他类去实现这些方法。
5. 接口比继承更加灵活。继承需要满足 is - a 的关系，而接口只需要满足 like - a 关系。
6. \==接口在一定程度上实现代码解耦。（即：接口规范性 + 动态绑定机制）==

### 9.7.3 接口的多态特性

1. 多态参数（接口的引用可以指向实现了接口的类的对象）

   > `viod work(Inerface01 i1){...}` 参数可以传入任意实现该接口的类。

2. 多态数组

3. 接口存在多态传递现象

   > \==如果有 A、B 两个接口，B 继承了 A 接口，而 C 类实现了 B 接口，那么等同于间接也实现了 A 接口。==
   >
   > 这样 c 类的实例也可以赋给 A 类的接口。

## 9.8 内部类

> 一个类的内部又完整的嵌套了另一个类结构。被嵌套的类被称为 内部类。
>
> ```java
> class Outer{		//外部类
> 	class Inner{	//内部类
> 	}
> }
> class Other{		//外部其他类
> }
> ```
>
> 内部类的最大特点是可以直接访问私有属性，并且可以体现类与类之间的包含关系。

### 9.8.1 四种内部类

分别是：

* 定义在外部类的局部位置上
  * 局部内部类：有 类名
  * **匿名内部类：无 类名**
* 定义在外部类的成员位置上
  * 成员内部类：无 `static` 修饰
  * 静态内部类： `static` 修饰的类

### 9.8.2 局部内部类

> 局部内部类：定义在外部类的局部位置上，并且有类名。（局部位置？比如：方法/代码块里）
>
> ```java
> class Outer {				//外部类
>     public void tools01() {
>         class Inner {		//局部内部类
>         }
>     }
> }
> ```

#### #9.8.2.1 使用细节

1. 定义在外部类的局部位置上，并且有类名。

2. \==可以访问外部类的所有成员，**包含私有成员**==

3. 局部内部类可以 直接访问 外部类的成员。

4. 不能添加 访问修饰符，因为其地位相当于局部变量。但，可以使用 `final`，因为局部变量也能用 `final`

5. \==作用域 仅仅在定义它的方法或代码块中==

6. \==外部类 在方法中，可以创建 局部内部类 的对象实例，然后调用方法。==

7. \==外部其他类 不能访问 局部内部类==

8. 如果外部类和局部内部类的成员重名时，默认遵循就近原则。那个场合，访问外部类成员使用 `外部类名.this.变量名`

   `外部类名.this` 本质就是 外部类的对象。即，调用了该方法（上例的 `tools01` ）的对象

   还不懂的话，看一下 [这个视频](https://www.bilibili.com/video/BV1fh411y7R8?p=415\&t=289.9) 悟一悟

### ==9.8.3 匿名内部类==

> 匿名内部类：定义在外部类的局部位置，且没有类名
>
> ```java
> >new 类/接口 (参数列表) {
>    类体
> >}
> ```
>
> 匿名内部类本质是没有名字的类，而且是内部类。同时，还是一个对象。
>
> 可以用匿名内部类简化开发

> 一个例子
>
> ```java
> >class Outer {							//外部类
>    public void tools01() {
>        Inter whatEver = new Inter(){	//匿名内部类
>        };
>    }
> >}
> >interface Inter{
> >}
> ```
>
> 其实，这个匿名内部类 `new Inter(){}` 的运行类型就是 `class XXXX implements Inter`。系统自动分配的名字是 `Outer$1`（`whatEver.getClass = "Outer$1"`）
>
> JDK 在创建匿名内部类 `Outer$1` 时，立即创建了一个对象实例，并将地址返回给了 `whatEver`
>
> 匿名内部类使用一次后就不能再次使用（`Outer$1` 就这一个了）

#### #9.8.3.1 使用细节

1. 匿名内部类语法比较独特。其既是一个类的定义，也是一个对象。因此，从语法上看，其既有 定义类的特征，也有 创建对象的特征。
2. 可以访问外部类的所有成员，包括私有的。
3. 局部内部类可以 直接访问 外部类的成员。
4. 不能添加 访问修饰符，因为其地位相当于局部变量。但，可以使用 `final`，因为局部变量也能用 `final`
5. 作用域：仅仅在定义它的方法或方法快中
6. 外部其他类 不能访问 匿名内部类
7. 如果外部类和匿名内部类的成员重名时，默认遵循就近原则。那个场合，访问外部类成员使用 `外部类名.this.变量名`

#### #9.8.3.2 使用场景

1. 当作实参直接传递，简洁高效

   > ```java
   > public class Homework1 {
   >     public static void main(String[] args) {
   >         new Cellphone().clock(new Bell() {				//看这里看这里
   >             @Override
   >             public void belling() {
   >                 System.out.println("小懒猪起床了！");
   >             }
   >         });
   >     }
   > }
   >
   > interface Bell {
   >     void ringing();
   > }
   >
   > class Cellphone{
   >     public void clock(Bell bell){
   >         bell.ringing();
   >     }
   > }
   > ```

### 9.8.4 成员内部类

> 成员内部类：定义在外部类的成员位置，并且没有 `static` 修饰。
>
> ```java
> class Outer{
> 	class Inner{
> 	}
> }
> ```

#### #9.8.4.1 使用细节

1. 可以直接访问外部类的所有成员，包括私有的
2. 可以添加任意访问修饰符。因为，成员内部类的地位就是一个成员。
3. 作用域 和外部类其他成员相同，为整个类体。
4. 局部内部类可以 直接访问 外部类的成员。
5. 外部类可以通过创建对象的方式访问成员内部类
6. 外部其他类访问成员内部类
   * `Outer.Inner name = Outer.new Inner(); `下个方法的缩写
   * `Outer.Inner name = new Outer().new Inner();`
   * 在外部类中编写一个方法，返回一个 `Inner` 的对象实例（就是对象的 getter）
7. 如果外部类和匿名内部类的成员重名时，默认遵循就近原则。那个场合，访问外部类成员使用 `外部类名.this.变量名`

### 9.8.5 静态内部类

> 静态内部类：定义在外部类的成员位置，经由 `static` 修饰。
>
> ```java
> class Outer{
> 	static class Inner{
> 	}
> }
> ```

#### #9.8.5.1 使用细节

1. 可以直接访问外部类的所有 **静态** 成员，包括私有的。但不能访问非静态成员
2. 可以添加访问修饰符。因为，静态内部类的地位就是一个成员。
3. 作用域 和其他成员相同，为整个类体。
4. 静态内部类可以 直接访问 外部类的成员。
5. 外部类可以通过创建对象的方式访问静态内部类
6. 外部其他类访问静态内部类
   * `Outer.Inner name = new Outer.Inner();` 即通过类名直接访问
   * 在外部类中编写一个方法，返回一个 `Inner` 的对象实例
   * 如果外部类和匿名内部类的成员重名时，默认遵循就近原则。那个场合，访问外部类成员使用 `外部类名.变量名`。*（怎么不一样了呢？因为静态内部类访问的都是静态成员）*
