菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
91
0

final关键字

原创
05/13 14:22
阅读数 75068

一.final数据

1-1 编译期常量

  • 定义:带有①编译时数值(区别于运行时数值)的②final基本数据类型的量。
  • 注意
    1. 既是static又是final的量不一定是编译期常量;
      public class NotCompileTimeConstant {
          static Random random = new Random(47);
          //Can't be a compile-time constant
          static final int number = random.nextInt(20);
      }
      如上代码中,number是用static final修饰的,但其值random.nextInt(20)是在运行时才会生成,所以其值不是编译时数值。因此number不是编译期常量。
    2. 既是static又是final的量用大写字母表示,并使用下划线分割各个单词;
    3. 一个既是static又是final的量只占据一段不能改变的存储空间;
  • 作用:对于编译期常量,编译器可以将该常量带入任何可能用到它的计算式中,也就是说可以在编译期执行计算式,减轻了一些运行时负担。

1-2 常量

  • 用final修饰的数据称为常量,常量包含编译期常量;
  • 一般在定义未被static修饰的常量时就对其进行初始化(也可先声明其为空白final,在构造函数中再对其进行初始化),初始化后就不能再进行修改。而对于static修饰的常量来说,需要在定义时就对其初始化。
  • 当常量的类型是基本数据类型时,final使值恒定不变;但当常量的类型是引用时,final使引用自身恒定不变(指向不变),但是引用指向的值是可以变的;

1-3 空白final

  • 定义:空白final是指被声明为final但又未给定初始值的域(这里的域需要是非静态的);
  • 作用:空白final在关键字final的使用上提供了更大的灵活性,为此,一个类中的final域就可以做到根据对象的特性而有所不同,却又保持恒定不变的特性。
  • 注意:
  1. 编译器会确保空白final在使用前被初始化;
  2. 必须在域的定义处或者每个构造器中用表达式对空白final进行赋值,否则编译器会报错。
    public class BlankFinal {
        private final int i;//Error:Variable 'i' might not have been initialized
    }

  • 探究:编译器是否会对类的空白final域进行默认初始化?

    public class BlankFinal {
        private int i;
    
        public static void main(String[] args) {
            BlankFinal blankFinal = new BlankFinal();
            System.out.println(blankFinal.i);//i=0
        }
    }
    当i不为final时,在创建此类的对象时,会将其默认初始化为0

     

    public class BlankFinal {
        private final int i;//Error:Variable 'i' might not have been initialized
    
        public static void main(String[] args) {
            BlankFinal blankFinal = new BlankFinal();
            System.out.println(blankFinal.i);
        }
    }
    
    //编译结果:java: 变量 i 未在默认构造器中初始化
    而当i为空白final时,编译器检测到i为被初始化就会报错,此时可能还未进行创建对象。
  • 所以不确定编译器是否会对空白final进行默认初始化,因为编译器会确保空白final在使用前必须被初始化,否则编译错误。但按理来说,如果能通过编译,那么final int i也会被默认初始化为0,因为默认初始化实际上是为对象分配一块内存空间,然后将该内存设置为二进制0值。
  • 1-4 final参数

    Java允许在参数列表中以声明的方式将参数指定为final,这意味着:如果参数类型是基本数据类型,那么你不能修改它的值;如果参数类型是引用类型,那么你不能修改引用的指向,但是可以修改引用指向的对象。

    1-5 final方法

    在方法的返回类型前加final关键字,可以防止任何继承类修改它的含义,即不能被覆盖(Override)。

    final和private关键字:

      类中所有的private方法都隐式地被指定为是final的。但由于在子类中无法取用private方法,所以其实无法覆盖private方法,所以对private方法添加final关键字没有什么额外的意义。

     

    这里又引出了一个容易造成混淆的问题:如果试图去覆盖一个private方法(隐含是final的),似乎是可以的,而且编译器也不会给出错误信息:

    class Father{
        private final void f(){
            System.out.println("Father.f()");
        }
    }
    
    class Son extends Father{
        private void f(){
            System.out.println("Son.f()");
        }
    }

    如上代码编译器未报错。

     

    而对于使用了final修饰的非private方法,如果在子类中覆盖了这些方法,编译器将会报错:

    class Father{
        final void g(){
            System.out.println("Father.g()");
        }
    
        protected final void h(){
            System.out.println("Father.h()");
        }
    
        public final void k(){
            System.out.println("Father.k()");
        }
    }
    
    class Son extends Father{
        void g(){ //Error 'g()' cannot override 'g()' in 'page143.Father'; overridden method is final
            System.out.println("Son.g()");
        }
    
        protected void h() { //Error 'h()' cannot override 'h()' in 'page143.Father'; overridden method is final
            System.out.println("Son.h()");
        }
    
        public void k(){ //Error 'k()' cannot override 'k()' in 'page143.Father'; overridden method is final
            System.out.println("Son.k()");
        }
    }

    这是因为 “覆盖” 只会发生在 是基类接口一部分 的方法上。private方法不是基类接口的一部分(因为private方法只能在类内部访问),所以在子类中写一个与父类同样的方法时未发生“覆盖”,新写的这个方法被认为是在子类中生成了一个新的方法,所以编译器不会报错。而public,protected和具有包访问权限的方法都被认为是类接口的一部分,所以可以发生“覆盖”,在发生覆盖时发现被覆盖的方法是final的,按理来说是不能覆盖的,所以编译器报错。

    1-6 final类

    • 当将某个类的整体定义为final时(通过将关键字final置于它的定义前),就表明了这个类不允许被继承。
    • 注意:
      • final类的域可根据个人意愿选择是否是final的,然后,由于final类禁止被继承,final类中的所有方法都隐式地被指定为final的,所以在final类中给方法添加final,并没有什么意义。

    发表评论

    0/200
    91 点赞
    0 评论
    收藏