• JAVA并发(五):关键词final


    JAVA并发(五):关键词final

    整理自https://www.pdai.tech/md/java/thread/java-thread-x-key-final.html

    1 本文介绍以下问题

    • 所有的final修饰的字段都是编译期常量吗?
    • 如何理解private所修饰的方法是隐式的final?
    • 说说final类型的类如何拓展? 比如String是final类型,我们想写个MyString复用所有String中方法,同时增加一个新的toMyString()的方法,应该如何做?
    • final方法可以被重载吗? 可以
    • 父类的final方法能不能够被子类重写? 不可以
    • 说说final域重排序规则?
    • 说说final的原理?
    • 使用 final 的限制条件和局限性?
    • 看本文最后的一个思考题

    2 final基础使用

    2.1 修饰类

    当某个类的整体定义为final时,就表明了你不能打算继承该类,而且也不允许别人这么做。即这个类是不能有子类的。

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

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

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

    /**
    * @pdai
    */
    class MyString{
    
        private String innerString;
    
        // ...init & other methods
    
        // 支持老的方法
        public int length(){
            return innerString.length(); // 通过innerString调用老的方法
        }
    
        // 添加新方法
        public String toMyString(){
            //...
        }
    }
    

    2.2 修饰方法

    主要介绍

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

    2.2.1 private与final

    类中所有private方法都隐式地指定为final的,由于无法取用private方法,所以也就不能覆盖它。可以对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的,无法被访问到

    2.2.2 final方法可以重载

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

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

    2.3 修饰参数

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

    2.4 修饰变量

    1. 不是所有的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的值在被初始化后无法被更改

    2. 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所修饰的字段仅占据内存的一个一份空间,一旦被初始化之后便不会被更改

    3. 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;
          }
      

      注意,如果字段由static和final修饰,仅能在定义处赋值,因为该字段不属于对象,属于这个类

    3 final域重排序规则

    这部分太绕了,等以后理解了再整理吧

    https://www.pdai.tech/md/java/thread/java-thread-x-key-final.html

  • 相关阅读:
    (轻松一刻)神秘而又强大的蓝翔学院!而且还是当中最可怕的挖掘机系!
    IIS 反向代理二级目录及泛目录
    正则表达式匹配外链和内链
    关于 preg_replace_callbank 的学习记录
    Linux 笔记
    Linux 笔记
    Linux 笔记
    使用 Docker 安装 showdoc
    Docker 入门
    Linux 笔记
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/14305898.html
Copyright © 2020-2023  润新知