深入解析final关键字的用法

夫陶公清风千古,余又何人,敢称庶几
在这里插入图片描述
个人博客地址:http://blog.breez.work


介绍

final用于声明属性【属性不可变】、方法【方法不可覆盖】、(除了抽象类)【类不可被继承】

解析

final属性

被final修饰的变量不可变。【引用不可变

public class FinalTest {
    public static void main(String[] args) {
        final StringBuffer s = new StringBuffer("Hello");
        s.append(" World");
        System.out.println(s); //输出Hello World
    }
}

public class FinalTest {
   public static void main(String[] args) {
       final StringBuffer s = new StringBuffer("Hello World");
       s = new StringBuffer("Hello world"); //编译报错
   }
}

从上面可以看出final指的是引用不可变,即它只能指向初始化时指向的那个对象,不关心指向对象内容的变化。所以被final修饰的变量必须被初始化。

如何初始化:

  1. 在定义的时候初始化
final String name="BreezAm";
name="change"; //编译错误
  1. final成员变量可以在初始块中初始化,但不可在静态块中初始化。
  • 在初始化块中初始化:【编译通过
public class FinalTest {
    private final String name;//定义一个成员变量
  
    {
        name = "BreezAm";//初始化块中初始化成员变量name
    }

    public static void main(String[] args) {
        FinalTest test = new FinalTest();
        String name = test.name;
        System.out.println(name);//BreezAm
        
        test.name="change";//编译报错
    }
}

试图在静态初始化块中初始化final变量【编译报错

public class FinalTest {
    private final String name;// 编译报错

    static{
        name = "BreezAm"; //编译报错,就算name定义时已经被初始化此处编译期也会报错
    }
    
    public static void main(String[] args) {
        FinalTest test = new FinalTest();
        String name = test.name;
        System.out.println(name);//BreezAm
    }
}
  1. 静态final成员变量可以在静态初始化块中初始化,但不可以在初始化块中初始化。
  • 静态初始化块中初始化成员变量【编译通过
public class FinalTest {
    private static final String name;//  定义一个静态、不可变的变量

    static {
        name = "BreezAm";//在静态初始化块中初始化
    }

    public static void main(String[] args) {
        String name = FinalTest.name;
        System.out.println(name);//BreezAm
    }
}
  • 试图在静态初始化块中初始化成员变量【编译报错
public class FinalTest {
    private static final String name;//  编译报错

    {
        name = "BreezAm";//在初始化块中初始化
    }

    public static void main(String[] args) {
        String name = FinalTest.name;
        System.out.println(name);//BreezAm
    }
}
  1. 在类的构造器中初始化,但静态final成员变量不可以在构造函数中初始化。
  • 构造器中初始化非静态final成员变量【编译通过
public class FinalTest {
    private final String name;//   定义一个成员变量【未初始化】

    public FinalTest() {
        name = "BreezAm";//在构造器中初始化final类型的成员变量
    }

    public static void main(String[] args) {
        FinalTest test = new FinalTest();
        String name = test.name;
        System.out.println(name);//BreezAm
    }
}

final方法

当一个方法被声明为final时,该方法不允许任何子类重写这个方法,但子类仍然可以使用这个方法。[注意final方法可以被重载]


另外,还有一种被称为inline(内联)的机制,当调用一个被声明为final的方法时,直接将方法主体插入到调用处,而不是进行方法调用(类似于C++中的inline),这样做可以提高程序的效率

案例:

/**
 * 父类
 */
public class Father {
    /**
     * 编写一个final方法
     */
    public final void doAction(){
        System.out.println("我是final方法、我不可以被重写,但是你可以用噢!渣女!!");
    }
}

子类试图重写final方法【编译报错

/**
 * 子类
 */
public class Children extends Father {

    public final void doAction(){ //编译报错、因为父类doAction方法是final类型的,所以不可以被重写

    }
	
    public static void main(String[] args) {

    }
}

使用父类中的final方法【编译通过

/**
 * 子类
 */
public class Children extends Father {
    public static void main(String[] args) {
        Children children = new Children();
        children.doAction();
    }
}

输出:

我是final方法、我不可以被重写,但是你可以用噢!渣女!!

子类重载父类中的final方法

/**
 * 子类
 */
public class Children extends Father {

    /**
     * 重写父类中被声明为final的方法
     * @param a
     * @param b
     */
    public final void doAction(int a,int b){ // 可以被重载,哈哈,被我发现了
        System.out.println("a:"+a);
        System.out.println("b:"+b);
    }
    public static void main(String[] args) {
        Children children = new Children();
        children.doAction(20,30);
    }
}

输出:

a:20
b:30

final参数

用来表示这个参数在这个函数内部不允许被修改

案例:

public class FinalTest {
    /**
     * 编写一个带参数为final的方法
     *
     * @param name
     */
    /*public void doActionFinal(final String name) {
        name = "我想修改name,但是编译报错了,难过!!"; //编译不通过
    }*/

    /**
     * 编写一个带非final参数的方法
     *
     * @param name
     */
    public void doAction(String name) {
        System.out.println("修改前:" + name);

        name = "name参数不是final的,我修改成功了,一个字:巴适!"; //编译通过
        System.out.println("修改后:  " + name);
    }

    public static void main(String[] args) {
        FinalTest test = new FinalTest();
        //test.doActionFinal("BreezAm");

        test.doAction("BreezAm");

    }
}

输出:

修改前:BreezAm
修改后:  name参数不是final的,我修改成功了,一个字:巴适!

final类

当一个类被声明为final时,此类不能被继承,所有的方法都不能被重写。但这并不表示final类的成员变量也是不可变的,要想做到final类的成员变量不可改变,必须给成员变量增加final修饰。注意:一个类不能即被声明为abstract,又被声明为final【抽象类不能声明为final】。

试图继承被声明为final的类【编译失败

/**
 * 父类
 */
public final class Father {
}

/**
 * 子类
 */
public class Children extends Father {  //编译报错

}

试图将一个abstract类声明为final【编译失败

/**
 * 父类
 */
public final abstract class Father { //编译失败,抽象类不能声明为final

}

修改final类的成员变量

/**
 * 父类
 */
public final class Father {
    private String name = "BreezAm";
    private final int num = 2018010110;

    public void doAction() {
        name = "清风";//修改final类的非final成员变量 编译通过

        num = 2019010110;//修改final类的final成员变量 编译失败
    }
}

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗨老同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值