Java 关键字 之 final

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/yichen97/article/details/100060183

概述

final关键字算是与static都比较常用的一个关键字,它可以作用于属性、方法、参数和类。

声明后,

属性 -> 不可变;

方法 -> 不可覆盖;

形参 -> 不可变;

类 -> 不可继承。

Final属性(常量)

在概述中已经提到,被final关键字修饰的属性是不可变的。

在这个例子中我重新对str赋值,然后报了个语法错误,他说str不能被分配,如果想改变它的值的话,让我移除掉fianl关键字。

这时候,str在编译的时候值就已经被确定了,值也不能被修改。

Final属性(构造器、代码块、方法初始化)

上面说的只能确定一个值,怎么样的使用才能让这个final的属性的赋值更加灵活呢?

构造器

这种不在定义的时候初始化,而在构造函数中初始化的,也叫做空白Final变量。

public class FinalNoun {

    private final String str;

    public FinalNoun(String str) {
        this.str = str;
    }

    public void print() {
        System.out.println(str);
    }

}

在这个例子中,我先把被final修饰的str不让它赋值,值在这个类的构造方法中进行赋值。需要了解构造方法可以戳这里

public class Test {

    public static void main(String[] args) {
        new FinalNoun("TEST").print();;
    }

}

去new这个类的时候,构造方法被调用,值也确实被赋上了。

TEST

代码块

既然前面提到构造方法是可以的,那么代码块肯定是也可以的,只不过构造代码块要比构造方法要执行的早一点。

public class FinalNoun {

    private final String str;

    {
       this.str = "TEST";
    }

    public void print() {
        System.out.println(str);
    }

}

输出同样也是

TEST

但是代码块只能使用普通的,而不能使用静态的,因为静态代码块执行要早。要想了解静态代码块的话,戳这里

方法

比如有一个方法是生成任意整数,它只有在执行的时候才可以知道这个方法返回的最终的值。

public class FinalNoun {

    private final int str = new Random().nextInt();

    public void print() {
        System.out.println(str);
    }

}

首先要拿到这个任意整数之后才可以赋值给str,还算比较好理解。

Final属性(引用数据类型)

先来看一个例子:

public class Monkey {

    private String name = "";

    public Monkey(String value){
       this.name = value;
    }

    public String getName() {
       return name;
    }

    public void setName(String value) {
       this.name = value;
    }
}

这只是一个普普通通的类,对象里有一个name属性,还有普普通通的get、set方法。

而就在我修改一个被final修饰的引用的时候,编辑器却提示该操作与final关键字是冲突的。

原来,final关键字修饰的是引用,而不是对象。所以,被final修饰过后,只有引用指向堆中的地址不变,而地址上的对象怎么变都可以。

为了验证对象的属性是可以被改变的,我把对象的name给它重新赋值。

public class Test {

    public final Monkey mMonkey = new Monkey("Huang");

    public void change() {
     mMonkey.setName("Zhang");
     System.out.println(mMonkey.getName());
    }

    public static void main(String[] args) {
        new Test().change();
    }
}

看到打印,对象的name属性确实是被改变了。

Zhang

Final形参

先来一个基本数据类型,int吧。

当一个方法的形参被final修饰的时候,这个参数在该方法内不可以被修改。

那么,我们来试试引用数据类型的。

public class Test {

    public void change(final Monkey monkey) {
        //不可修改引用
        //monkey = new Monkey("Zhang");
        monkey.setName("Zhang");
        System.out.println(monkey.getName());
    }

    public static void main(String[] args) {
        new Test().change(new Monkey("Huang"));
    }
}

当写到注释的地方,没让我们失望,还是报错了,可见,与上面一样,也是无法修改引用所指向的地址。

但这里的对象依然是可以被改变的。

Zhang

Final方法

假如我有一个类中有一个被fianl修饰了的方法:

public class Father {

    public final void face() {
        System.out.println("face");
    }

    public void face(String str) {
         System.out.println(str);
    }

    public static void main(String[] args) {
        new Father().face();
    }
}

好像没什么不一样的,该调用调用,该重载重载。

那我再写一个子类去继承它:

public class Son extends Father {

   @Override
    public void face(String str) {
      super.face(str);
    }
}

发现我根本无法重写face()方法,只能重写face(String str)方法。

public class Test {

    public static void main(String[] args) {
        new Son().face();
    }
}

在调用子类的face()方法的时候,也可以正常调用。

face

Final类

被final修饰的类不能被继承,但可以改变类内部的属性。

假如我把父类给Final掉,再用子类去继承它:

好像这也没什么用嘛!只是不能被继承而已。

其实不然呀,因为它是只读的,所以它在多线程的情况下是绝对安全的,也不需要用锁之类的去做线程同步,像String.java类就是final类。

补充

在匿名类中所有变量都必须是final变量;

接口中声明的所有变量本身是final的;

被 final static 共同修饰的,称为全局变量;

所有的private方法都隐式的指定为final的,为private方法添加final无任何意义;

final方法在编译阶段绑定,称为静态绑定;

没有在声明时初始化final变量的称为空白final变量。

参考文献

https://blog.csdn.net/qq_31655965/article/details/54800523

https://www.cnblogs.com/xiaoxiaoyihan/p/4974273.html#autoid-2-0-0

https://www.cnblogs.com/xiaoxi/p/6392154.html

猜你喜欢

转载自blog.csdn.net/yichen97/article/details/100060183