java-final.md 6.8 KB
Newer Older
沉默王二's avatar
优化  
沉默王二 已提交
1
## final 关键字
沉默王二's avatar
更新  
沉默王二 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

“哥,今天学什么呢?”

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

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

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

### 01、final 变量

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

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

“来看这行代码。”

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

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

沉默王二's avatar
沉默王二 已提交
25
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-01.png)
沉默王二's avatar
更新  
沉默王二 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

“再来看这段代码。”

```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 重新赋值的话,编译器同样会生气。”

沉默王二's avatar
沉默王二 已提交
53
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-02.png)
沉默王二's avatar
更新  
沉默王二 已提交
54 55 56 57 58 59 60 61 62 63 64

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

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

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

沉默王二's avatar
沉默王二 已提交
65
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-03.png)
沉默王二's avatar
更新  
沉默王二 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

“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) {
    }
}
```

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

沉默王二's avatar
沉默王二 已提交
92
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-04.png)
沉默王二's avatar
更新  
沉默王二 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

### 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() {

    }
}
```

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

沉默王二's avatar
沉默王二 已提交
119
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-05.png)
沉默王二's avatar
更新  
沉默王二 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

“如果一个类中的某些方法要被其他方法调用,则应考虑事被调用的方法称为 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 的,无法继承。”

沉默王二's avatar
沉默王二 已提交
181
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/keywords/23-06.png)
沉默王二's avatar
更新  
沉默王二 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

“不过,类是 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 有没有这个机会。”

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