Java - final关键字

  • final可以加在成员变量、方法、类上,在java中的final,通常他指的是 "这是无法改变的" 的意思

  • final加在成员变量上

    • 当final加在成员变量上时,意义就是指这个变量的值一旦被初始化之后,就不允许再被改变

      • 如果final是加在基本类型(int、boolean、Integer...)上,因为这些变量中存的是实际的值,所以实际上是不允许改变他的值

      • 但是如果final是加在对象类型上,因为这些变量中存的是 "对象的引用",所以实际上是不允许这个对象的引用改变,但是对象自己内部的成员变量还是能够被改变的

        • 这一限制同样适用数组,因为他也是指向对象的引用

        • 不允许这个对象的引用改变的意思是指,当宣告final Tank tank = new Tank()之后,可以任意的执行tank.setXXX()去改变tank内部的变量值,但是不能够执行tank = new Tank(),将tank引用指向另一个new Tank()对象

      • 简单的说,当final加在基本类型上时,就是不允许赋予新值,而final加在对象类型或是数组上时,就是不允许换新

      • 事实上,在Java中也没有任何手段可以避免这件事,除非程序员自己对成员变量做了访问限制,直接不允许外部的人调用改变成员变量

      class Tank {
          int level = 2;
      }
      ​
      public class Main {
          public static void main(String[] args) {
              final int i = 1;
              final Tank tank = new Tank();
      ​
              System.out.println("i: " + i + ", Tank.level: " + tank.level);
              //i = 10; //过不了编译,因为final的基本类型的值是存的实际的值
              tank.level = 20; //虽然tank被加上了final关键字,但是因为引用没改变,所以可以改变其内部的值
              System.out.println("i: " + i + ", Tank.level: " + tank.level);
              
              //过不了编译,因为tank是一个对象类型
              //所以加上final关键字时,表示不允许他去更换引用
              //也就是说,tank只能永远指向当初new出来的那个Tank(),而不能再去指向另一个新new出来的Tank()
              //tank = new Tank(); 
              
              final int[] a = new int[10];
              //编译失败,理由和tank一样,因为a也是一个引用(指向数组对象们)
              //所以只能改这10个数组内部的值,但不能将a指向另一个new int[20]
              //a = new int[20]; 
              a[0] = 10;
              System.out.println("a[0]: " + a[0]);
              a[0] = 20; //因为数组和对象一样都是存放引用,所以和对象一样,也可以改变其内部的值
              System.out.println("a[0]: " + a[0]); 
          }
      }
      i: 1, Tank.level: 2
      i: 1, Tank.level: 20
      a[0]: 10
      a[0]: 20
    • 被设置为final的成员变量,不一定要在设置变量时就初始化,也可以选择在构造器裡进行初始化,如此给了我们写代码弹性的空间

      • final的用意是确保一个变量 "被初始化后",其值不会被改变,并没有限制初始化一定得在设置变量时初始化,所以也可以选择在构造器初始化

      • 但要注意,因为只有这两种地方 (设置变量时,构造器) 会初始化变量,所以必须要在这裡的其中一个地方初始化final变量,否则编译器会报错

      class Tank {
          final int x;  //x在构造器裡才初始化
          final int y = 2;  //y在设置变量时就先初始化
          //final int z; //编译失败,因为z没有在宣告变量时或构造器中的其中一个地方初始化
      ​
          Tank(){
              x = 1;
              //y = 3;  //编译过不了,因为y已经被初始化过了,不允许再次初始化
          }
      }
      ​
      public class Main {
          public static void main(String[] args) {
              final Tank tank = new Tank();
              System.out.println(tank.x + tank.y); //输出1, 2
      ​
          }
      }
  • final加在方法参数上

    • 和成员变量一样,如果方法参数是基本类型,其值就不允许修改,如果方法参数是对象类型,其对象引用就不允许修改 (但对象裡的变量能够被修改)

      class Tank {
          public int y = 2;
      }
      ​
      public class Main {
          public static void main(String[] args) {
              Tank tank = new Tank();
      ​
              printInt(1);
              printTank(tank);
          }
      ​
          public static void printTank(final Tank tank) { //final加在方法参数上
              System.out.println("tank.y: " + tank.y);
              tank.y = 20;
              System.out.println("tank.y: " + tank.y);
          }
      ​
          public static void printInt(final int i) { //final加在方法参数上
              System.out.println("i: " + i);
      //        i = 10;  //编译过不了
              System.out.println("i: " + i);
          }
      }
      i: 1
      i: 1
      tank.y: 2
      tank.y: 20
  • final加在方法上

    • final加在方法上的意义是把此方法锁定,即是禁止任何继承的子类@Override他,修改此方法的实现逻辑

      • 但是如果此final方法是public的话,子类仍然可以去使用他

        • 说穿了,final只是去限制子类不能去@Override改写此方法的具体逻辑而已,至于能不能调用此方法,那就看父类是设置为public/private,不关final的事情

      • 其实,类中所有的private方法都隐式的被加上了final关键字,因为继承的子类虽然能获得这些private方法,但是因为private的关系,所以子类不能去取用他们,而因为无法取用,所以自然也就无法改写他们的实现逻辑,也达到了final的意义 (禁止子类改写此方法)

        • 因此虽然说可以对所有的private方法自行手动加上final,不过不会增加任何意义,所以还是别加的好

      class Parent {
          public void publicPrint() {
              System.out.println("public Parent");
          }
      ​
          private void privatePrint() {
              System.out.println("private Parent");
          }
          
          public final void publicFinalPrint() {
              System.out.println("public final Parent");
          }
      }
      ​
      class Child extends Parent {
          //成功Override publicPrint()方法
          @Override
          public void publicPrint() {
              System.out.println("public Child");
          }
          
          //无法Override privatePrint()方法
      ​
          //无法Override publicFinalPrint()方法,但是可以调用他
          public void test() {
              super.publicFinalPrint();
          }
      }
      ​
      public class Main {
          public static void main (String[] args) {
              Child child = new Child();
              //仍然可以在子类中调用被宣告为final的父类方法
              //final只是限制子类不能去更改这个方法的实现逻辑而已,没有限制能不能调用
              //限制能不能调用,那个是public/private负责的
              child.publicFinalPrint();
          }
      }
  • final加在类上

    • 当final加在类上,表示不允许任何人来继承此类,也就是不允许改写此类的意思

      • 也就是说出于某种考虑,你对该类的设计永不需要任何变动,或者出于安全的考虑,你不希望他有子类

        final class Parent { //Parent类设为final类
        }
        
        //编译不通过,即不允许Child类继承Parent类
        //class Child extends Parent {}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值