java篇 类的进阶0x09:final 修饰符

final 修饰符

final 修饰符可以用在不同的地方,但本质上作用都很相似,就是不让你变化。

  • final 修饰类:不可被继承;(比如说 java 的 String 类就是 final 修饰的)
  • final 修饰方法:不可被子类覆盖;
  • final 修饰变量:不可被赋值。

final 修饰类

final 修饰的类不可被继承。

// final 修饰类:不可被继承
public final class A {
    
}
public class B extends A{						// 报错:无法从最终 A 进行继承
    
}
// 调用类
public class TestUse {
    public static void main(String[] args) {
        B b = new B();
    }
}

final 修饰方法

final 修饰构造方法

final 不能用于修饰构造方法,会报错。

实际上,构造方法和别的方法不一样,别的方法还可以覆盖,但构造方法根本无法覆盖,所以对构造方法使用 final 这种行为本身也没有意义。

final 修饰成员方法

final 修饰的成员方法不能被覆盖。

public class A{
    public final void sayHi(){
        System.out.println("调用的是 A 的 sayHi");
    }
}
public class B extends A{
    public void sayHi() {								// 报错:被覆盖的方法为 final
        System.out.println("调用的是 B 的 sayHi");
    }
}

// 调用类
public class TestUse{
    public static void main(String[] args){
        A a = new B();
        a.sayHi();
    }
}
final 修饰形参

final 修饰的形参,只能通过调用该方法时传入的实参给该形参赋值,不能在方法体内再给该形参赋值(即便没有调用该方法,没有实参传入,定义该方法的时候在方法体里对该 final 形参写赋值语句就会编译运行报错)。

public class A{
    public void sayHi(final int num){
        // num = 3;										// 不能在方法体内对形参赋值,否则报错。
        System.out.println("调用的是 A 的 sayHi" + num);
    }
}

// 调用类
public class TestUse{
    public static void main(String[] args){
        A a = new A();
        a.sayHi(3);										// 只能在调用时,用实参给形参赋值。
    }
}

如果该成员方法没有使用 final 来修饰,仅仅是某个形参使用了 final 来修饰,那么这个成员方法仍旧可以被子类覆盖。

final 修饰局部变量

final 修饰局部变量,只能在声明该局部变量时直接赋值,或声明时不赋值但之后赋一次值,但不能赋第二次值,否则报错。

public class A{
    public void sayHi(int num){
        System.out.println("调用的是 A 的 sayHi" + num);
        final int count = 10;						// 可以声明时直接赋值
        final int age;
        age = 2;
        // age = 3;								// 若再次赋值会报错	
        System.out.println("age: " + age);
    }
}

final 修饰变量

final 修饰成员变量

如果 final 用于修饰成员变量,则要求这个成员变量:

  • 必须在声明的同时时初始化赋值,
  • 或声明时不赋值但必须在构造方法中赋值。

即赋值可以发生在构造方法或成员变量声明时,且必须二选一赋值,只能赋值一次,不能两处都赋值或两处都不赋值,否则报错。

除声明时以及在构造方法中赋值外,在其他地方不能对该成员变量赋值(比如成员方法、静态方法中),否则报错。

之所以 final 成员变量声明时赋值或在构造方法中赋值都可以,是因为二者实际上都是在 <init> 方法中完成的赋值。

public class A {
    private final int count;				// 声明时不赋值,则必须在构造方法中赋值,两处必须有一处要赋值(但不能两处都赋值,即便赋相同值)
    private final String name = "Tom";		// 声明的同时赋值
    private int age;
    public A(){
        this.count = 10;					// 构造方法中赋值
    }
    // 以下成员方法中对 count 赋值会报错。
    // public void setcount(){
    //     this.count = 13;
    // }
}
final 修饰引用类型的成员变量

和修饰非引用的成员变量一样,final 修饰的引用类型的成员变量,必须要赋值,且只能赋值一次,赋值后不能再修改。

除声明时以及在构造方法中赋值外,在其他地方不能对该成员变量赋值(比如成员方法、静态方法中),否则报错。

final 修饰引用类型的和非引用类型的成员变量的差异

与非引用类型的成员变量有些差别,用 final 修饰的引用类型的成员变量虽然也是赋值后无法修改,但这里的无法修改是指这个引用指向的地址无法修改(这个地址就是引用所指向的对象的地址),但所指向的这个对象本身里面的数据是可以修改的。

public class A {
    private final String[] namelist = new String[4];				// 其实也是声明时直接赋值,只是赋值了一个对象地址
    private final double[] scorelist = {100, 97.5, 87.5, 98};		// 声明时直接赋值

    public A() {
        String[] names = {"Tom", "Jerry", "Mark", "Marry"};
        for (int i = 0; i < namelist.length; i++) {
            this.namelist[i] = names[i];
        }
    }

    public void setNamelist() {
        namelist[0] = "John";
        // namelist = new String[3];		// 若不注释这句会报错:java: 无法为最终变量namelist分配值。也就是说不能让 namelist 指向一个新的对象。
    }

    public void getNamelist() {
        for (int i = 0; i < namelist.length; i++) {
            System.out.print(namelist[i] + "\t");
        }
        System.out.println();
    }
}

// 调用类
public class TestUse {
    public static void main(String[] args) {
        A a = new B();
        a.getNamelist();		// Tom	Jerry	Mark	Marry	
        a.setNamelist();
        a.getNamelist();		// John	Jerry	Mark	Marry	
    }
}
// 即实现了对象数据的修改

final 修饰静态变量

如果 final 用于修饰静态变量,则要求这个静态变量:

  • 必须在声明的同时初始化赋值,
  • 或声明时不赋值但必须在静态代码块中赋值。

即赋值可以发生在静态代码块或静态变量声明时,且必须二选一赋值,只能赋值一次,不能两处都赋值或两处都不赋值,否则报错。

除声明时以及在静态代码块中赋值外,在其他地方不能对该静态变量赋值(比如静态方法中),否则报错。

之所以 final 静态变量声明时赋值或在静态代码块中赋值都可以,是因为二者实际上都是在 <clinit> 方法中完成的赋值。

public class A {
    private final static int COUNT;					// 声明时不赋值,则必须在静态代码块中赋值,两处必须有一处要赋值(但不能两处都赋值,即便赋相同值)
    private final static String name = "Tom";		// 声明的同时赋值
    private int age;

    static{
        COUNT = 10;									// 静态代码块中赋值
    }
    
    // 若不在静态代码块中设置,而在静态方法中设置初始值,会报错
    // public static void setvalue(){
    //     COUNT = 3;
    // }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值