NipGeihou's blog NipGeihou's blog
  • Java

    • 开发规范
    • 进阶笔记
    • 微服务
    • 快速开始
    • 设计模式
  • 其他

    • Golang
    • Python
    • Drat
  • Redis
  • MongoDB
  • 数据结构与算法
  • 计算机网络
  • 应用

    • Grafana
    • Prometheus
  • 容器与编排

    • KubeSphere
    • Kubernetes
    • Docker Compose
    • Docker
  • 组网

    • TailScale
    • WireGuard
  • 密码生成器
  • 英文单词生成器
🍳烹饪
🧑‍💻关于
  • 分类
  • 标签
  • 归档

NipGeihou

我见青山多妩媚,料青山见我应如是
  • Java

    • 开发规范
    • 进阶笔记
    • 微服务
    • 快速开始
    • 设计模式
  • 其他

    • Golang
    • Python
    • Drat
  • Redis
  • MongoDB
  • 数据结构与算法
  • 计算机网络
  • 应用

    • Grafana
    • Prometheus
  • 容器与编排

    • KubeSphere
    • Kubernetes
    • Docker Compose
    • Docker
  • 组网

    • TailScale
    • WireGuard
  • 密码生成器
  • 英文单词生成器
🍳烹饪
🧑‍💻关于
  • 分类
  • 标签
  • 归档
  • 设计模式

  • 开发规范

  • 经验分享

  • 记录

  • 快速开始

  • 笔记

    • 多线程与并发

      • 前言
      • 理论基础
      • Java中各种锁的概念
      • 关键字:synchronized
      • 关键字:volatile
      • 关键字:final
        • 基础使用
          • 修饰类
          • 修饰方法
          • private final
          • final方法是可以被重载的
          • 修饰参数
          • 修饰变量
          • static final
          • blank final
        • 常见面试题
          • 类
          • 变量
      • Java多线程 - 线程池
      • Java多线程 - Block Queue阻塞队列
      • Java多线程 - 辅助类
      • Java多线程 - CompletableFuture
      • Java多线程 - 线程变量传递ThreadLocal
    • JDK

    • Java集合

    • Spring

    • JVM

    • Other

  • 面试题

  • 微服务

  • 踩过的坑

  • Java
  • 笔记
  • 多线程与并发
NipGeihou
2022-11-23
目录

关键字:final

# 基础使用

# 修饰类

当某个类的整体定义为 final 时,就表明了不能继承该类,即这个类是不能有子类的。

注意

final 类中的所有方法都隐式为 final,因为无法覆盖他们,所以在 final 类中给任何方法添加 final 关键字是没有任何意义的。

# 修饰方法

  • private 方法是隐式的 final
  • final 方法是可以被重载的

# private final

public class Base {
    private void test() {
    }
}

public class Son extends Base{
    public void test() {
    }
    public static void main(String[] args) {
        Son son = new Son();
        Base father = son;
        //father.test();
    }
}

Base 和 Son 都有方法 test (),但是这并不是一种覆盖,因为 private 所修饰的方法是隐式的 final,也就是无法被继承,所以更不用说是覆盖了,在 Son 中的 test () 方法不过是属于 Son 的新成员罢了,Son 进行向上转型得到 father,但是 father.test () 是不可执行的,因为 Base 中的 test 方法是 private 的,无法被访问到。

# final 方法是可以被重载的

我们知道父类的 final 方法是不能够被子类重写的,那么 final 方法可以被重载吗?答案是可以的,下面代码是正确的。

public class FinalExampleParent {
    public final void test() {
    }

    public final void test(String str) {
    }
}

# 修饰参数

Java 允许在参数列表中以声明的方式将参数指明为 final,这意味这你无法在方法中更改参数引用所指向的对象。这个特性主要用来向匿名内部类传递数据。

# 修饰变量

# static final

一个既是 static 又是 final 的字段只占据一段不能改变的存储空间,它必须在定义的时候进行赋值,否则编译器将不予通过。

import java.util.Random;
public class Test {
    static Random r = new Random();
    final int k = r.nextInt(10);
    static final int k2 = r.nextInt(10); 
    public static void main(String[] args) {
        Test t1 = new Test();
        System.out.println("k="+t1.k+" k2="+t1.k2);
        Test t2 = new Test();
        System.out.println("k="+t2.k+" k2="+t2.k2);
    }
}

上面代码某次输出结果:

k=2 k2=7
k=8 k2=7

我们可以发现对于不同的对象 k 的值是不同的,但是 k2 的值却是相同的,这是为什么呢?因为 static 关键字所修饰的字段并不属于一个对象,而是属于这个类的。也可简单的理解为 static final 所修饰的字段仅占据内存的一个一份空间,一旦被初始化之后便不会被更改。

# blank final

Java 允许生成空白 final,也就是说被声明为 final 但又没有给出定值的字段,但是必须在该字段被使用之前被赋值,这给予我们两种选择:

  • 在定义处进行赋值 (这不叫空白 final)
  • 在构造器中进行赋值,保证了该值在被使用前赋值。

这增强了 final 的灵活性。

看下面代码:

public class Test {
    final int i1 = 1;
    final int i2;//空白final
    public Test() {
        i2 = 1;
    }
    public Test(int x) {
        this.i2 = x;
    }
}

可以看到 i2 的赋值更为灵活。但是请注意,如果字段由 static 和 final 修饰,仅能在声明时赋值或声明后在静态代码块中赋值,因为该字段不属于对象,属于这个类。

# 常见面试题

# 类

String 是 final 类型,我们想写个 MyString 复用所有 String 中方法,同时增加一个新的 toMyString () 的方法,应该如何做?

设计模式中最重要的两种关系,一种是继承 / 实现;另外一种是组合关系。所以当遇到不能用继承的 (final 修饰的类), 应该考虑用组合,如下代码大概写个组合实现的意思:

class MyString{

    private String innerString;

    // ...init & other methods

    // 支持老的方法
    public int length(){
        return innerString.length(); // 通过innerString调用老的方法
    }

    // 添加新方法
    public String toMyString(){
        //...
    }
}

# 变量

所有的 final 修饰的字段都是编译期常量吗?

现在来看编译期常量和非编译期常量,如:

public class Test {
    //编译期常量
    final int i = 1;
    final static int J = 1;
    final int[] a = {1,2,3,4};
    //非编译期常量
    Random r = new Random();
    final int k = r.nextInt();

    public static void main(String[] args) {

    }
}

k 的值由随机数对象决定,所以不是所有的 final 修饰的字段都是编译期常量,只是 k 的值在被初始化后无法被更改。

上次更新: 2022/12/31, 03:04:26
关键字:volatile
Java多线程 - 线程池

← 关键字:volatile Java多线程 - 线程池→

最近更新
01
Docker Swarm
04-18
02
安全隧道 - gost
04-17
03
Solana最佳实践
04-16
更多文章>
Theme by Vdoing | Copyright © 2018-2025 NipGeihou | 友情链接
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式