03. final关键字

88 阅读4分钟

[toc]

image.png

01. final的简介

final可以修饰变量,方法和类,用于表示所修饰的内容一旦赋值之后就不会再被改变.

02. final的具体使用场景

final能够修饰变量,方法和类,也就是final使用范围基本涵盖了java每个地方,下面就分别以修饰的位置:变量,方法和类分别来说一说。

2.1 变量

在java中变量,可以分为成员变量以及方法局部变量。因此也是按照这种方式依次来说,以避免漏掉任何一个死角。

1. final成员变量

通常每个类中的成员变量可以分为类变量(static修饰的变量)以及实例变量

针对这两种类型的变量赋初值的时机是不同的,类变量可以在声明变量的时候直接赋初值或者在静态代码块中给类变量赋初值。

而实例变量可以在声明变量的时候给实例变量赋初值,在非静态初始化块中以及构造器中赋初值。类变量有两个时机赋初值,而实例变量则可以有三个时机赋初值

**当final变量未初始化时系统不会进行隐式初始化,会出现报错。**也就是说:

  1. 类变量:必须要在静态初始化块中指定初始值或者声明该类变量时指定初始值,而且只能在这两个地方之一进行指定;
  2. 实例变量:必要要在非静态初始化块声明该实例变量或者在构造器中指定初始值,而且只能在这三个地方进行指定。

2. final局部变量

final局部变量由程序员进行显式初始化,如果final局部变量已经进行了初始化则后面就不能再次进行更改,如果final变量未进行初始化,可以进行赋值,当且仅有一次赋值,一旦赋值之后再次赋值就会出错。下面用具体的代码演示final局部变量的情况:

image.png

3. final修饰基本类型和引用类型的区别?

final修饰引用类型时表示其地址不可变,因为引用的本质是地址。

现在我们来换一个角度进行考虑,final修饰的是基本数据类型和引用类型有区别吗?

final基本数据类型 VS final引用数据类型

通过上面的例子我们已经看出来,如果final修饰的是一个基本数据类型的数据,一旦赋值后就不能再次更改,那么,如果final是引用数据类型了?这个引用的对象能够改变吗?我们同样来看一段代码。

public class FinalExample {
    //在声明final实例成员变量时进行赋值
    private final static Person person = new Person(24, 170);
    public static void main(String[] args) {
        //对final引用数据类型person进行更改
        person.age = 22;
        System.out.println(person.toString());
    }
    static class Person {
        private int age;
        private int height;

        public Person(int age, int height) {
            this.age = age;
            this.height = height;
        }
        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", height=" + height +
                    '}';
        }
    }
}

当我们对final修饰的引用数据类型变量person的属性改成22,是可以成功操作的。通过这个实验我们就可以看出来当final修饰基本数据类型变量时,不能对基本数据类型变量重新赋值,因此基本数据类型变量不能被改变。而对于引用类型变量而言,它仅仅保存的是一个引用,final只保证这个引用类型变量所引用的地址不会发生改变,即一直引用这个对象,但这个对象属性是可以改变的

2.2 方法

重写?

当父类的方法被final修饰的时候,子类不能重写父类的该方法,比如在Object中,getClass()方法就是final的,我们就不能重写该方法,但是hashCode()方法就不是被final所修饰的,我们就可以重写hashCode()方法。我们还是来写一个例子来加深一下理解: 先定义一个父类,里面有final修饰的方法test();

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

然后FinalExample继承该父类,当重写test()方法时出现报错,如下图:

image.png

通过这个现象我们就可以看出来被final修饰的方法不能够被子类所重写

重载?

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

    public final void test(String str) {
    }
}

可以看出被final修饰的方法是可以重载的。经过我们的分析可以得出如下结论:

1. 父类的final方法是不能够被子类重写的

2. final方法是可以被重载的

2.3 类

当一个类被final修饰时,表名该类是不能被子类继承的。子类继承往往可以重写父类的方法和改变父类属性,会带来一定的安全隐患,因此,当一个类不希望被继承时就可以使用final修饰。还是来写一个小例子:

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

父类会被final修饰,当子类继承该父类的时候,就会报错,如下图:

image.png