## final 关键字

“哥，今天学什么呢？”

“今天学一个重要的关键字——final。 ”我面带着朴实无华的微笑回答着她，“对了，三妹，你打算考研吗？”

“还没想过，我今年才大一呢，到时候再说吧，你决定。”

“好吧。”我摊摊手，表示很无辜，真的是所有的决定都交给我这个哥哥了，如果决定错了，锅得背上。

### 01、final 变量

“好了，我们先来看 final 修饰的变量吧！”

“被 final 修饰的变量无法重新赋值。换句话说，final 变量一旦初始化，就无法更改。”

“来看这行代码。”

```java
final int age = 18;
```

“当尝试将 age 的值修改为 30 的时候，编译器就生气了。”

![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-01.png)

“再来看这段代码。”

```java
public class Pig {
   private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
```

“这是一个很普通的 Java 类，它有一个字段 name。”

“然后，我们创建一个测试类，并声明一个 final 修饰的 Pig 对象。”

```java
final Pig pig = new Pig();
```

“如果尝试将 pig 重新赋值的话，编译器同样会生气。”

![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-02.png)

“但我们仍然可以去修改 pig 对象的 name。”

```java
final Pig pig = new Pig();
pig.setName("特立独行");
System.out.println(pig.getName()); // 特立独行
```

“另外，final 修饰的成员变量必须有一个默认值，否则编译器将会提醒没有初始化。”

![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-03.png)

“final 和 static 一起修饰的成员变量叫做常量，常量名必须全部大写。”

```java
public class Pig {
   private final int age = 1;
   public static final double PRICE = 36.5;
}
```

“有时候，我们还会用 final 关键字来修饰参数，它意味着参数在方法体内不能被再修改。”

“来看下面这段代码。”

```java
public class ArgFinalTest {
    public void arg(final int age) {
    }

    public void arg1(final String name) {
    }
}
```

“如果尝试去修改它的话，编译器会提示以下错误。”

![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-04.png)

### 02、final 方法

“被 final 修饰的方法不能被重写。如果我们在设计一个类的时候，认为某些方法不应该被重写，就应该把它设计成 final 的。”

“Thread 类就是一个例子，它本身不是 final 的，这意味着我们可以扩展它，但它的 `isAlive()` 方法是 final 的。”

```java
public class Thread implements Runnable {
    public final native boolean isAlive();
}
```
“需要注意的是，该方法是一个本地（native）方法，用于确认线程是否处于活跃状态。而本地方法是由操作系统决定的，因此重写该方法并不容易实现。”

“来看这段代码。”

```java
public class Actor {
    public final void show() {

    }
}
```

“当我们想要重写该方法的话，就会出现编译错误。”

![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-05.png)

“如果一个类中的某些方法要被其他方法调用，则应考虑事被调用的方法称为 final 方法，否则，重写该方法会影响到调用方法的使用。”

“三妹，来问你一个问题吧。”正想趁三妹回答问题的时候喝口水。

“你说吧，哥。”

“一个类是 final 的，和一个类不是 final，但它所有的方法都是 final 的，考虑一下，它们之间有什么区别？”

“我能想到的一点，就是前者不能被继承，也就是说方法无法被重写；后者呢，可以被继承，然后追加一些非 final 的方法。”还没等我把水咽下去，三妹就回答好了，着实惊呆了我。

“嗯嗯嗯，没毛病没毛病，进步很大啊！”

“那必须啊，谁叫我是你妹呢。”

### 03、final 类

“如果一个类使用了 final 关键字修饰，那么它就无法被继承.....”

“等等，哥，我知道，String 类就是一个 final 类。”还没等我说完，三妹就抢着说到。

“说得没毛病。”

```java
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {}
```

“那三妹你知道为什么 String 类要设计成 final 吗？”

“这个还真不知道。”三妹的表情透露出这种无奈。

“原因大致有 3 个。”

- 为了实现字符串常量池
- 为了线程安全
- 为了 HashCode 的不可变性

“想了解更详细的原因，可以一会看看我之前写的这篇文章。”

[为什么 Java 字符串是不可变的？](https://mp.weixin.qq.com/s/CRQrm5zGpqWxYL_ztk-b2Q)

“任何尝试从 final 类继承的行为将会引发编译错误。来看这段代码。”

```java
public final class Writer {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
```

“尝试去继承它，编译器会提示以下错误，Writer 类是 final 的，无法继承。”

![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-06.png)

“不过，类是 final 的，并不意味着该类的对象是不可变的。”

“来看这段代码。”

```java
Writer writer = new Writer();
writer.setName("沉默王二");
System.out.println(writer.getName()); // 沉默王二
```

“Writer 的 name 字段的默认值是 null，但可以通过 settter 方法将其更改为沉默王二。也就是说，如果一个类只是 final 的，那么它并不是不可变的全部条件。”

“关于不可变类，我之前也单独讲过一篇，你一会去看看。”

[不可变类](https://mp.weixin.qq.com/s/wbdV9rV60AwWiiTEBYPP7g)

“把一个类设计成 final 的，有其安全方面的考虑，但不应该故意为之，因为把一个类定义成 final 的，意味着它没办法继承，假如这个类的一些方法存在一些问题的话，我们就无法通过重写的方式去修复它。”


------

“三妹，final 关键字我们就学到这里吧，你一会再学习一下 Java 字符串为什么是不可变的和不可变类。”我揉一揉犯困的双眼，疲惫地给三妹说，“学完这两个知识点，你会对 final 的认知更清晰一些。”

“好的，二哥，我这就去学习去。你去休息会。”

我起身站到阳台上，看着窗外的车水马龙，不一会儿，就发起来呆。

“好想去再看一场周杰伦的演唱会，不知道 2021 有没有这个机会。”

我心里这样想着，天渐渐地暗了下来。