作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦
千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者
前言
壹哥之前跟大家说过,在面向对象中,有abstract、static和final 这3个核心修饰符。截止到现在,我们已经把abstract与static修饰符学习完毕,接下来就让我们再来学习final修饰符的用法与特性吧。
-----------------------------------------------前戏已做完,精彩即开始---------------------------------------------
全文大约【3500】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......
配套开源项目资料
Github:
GitHub - SunLtd/LearnJava
Gitee:
一一哥/从零开始学Java
一. final修饰符
1. 简介
在Java中,final表示“最终的、不可改变的、完结的”,它也是一种修饰符,可以修饰变量、方法和类。final修饰变量、方法和类时的意义是不同的,但本质是一样的,都表示不可改变,类似C#里的sealed关键字。final修饰的变量叫做最终变量,也就是常量,修饰的方法叫做最终方法,修饰的类叫做最终类。
2. 配套视频
与本节内容配套的视频链接如下:
Bilibili External Player
二. 常量
1. 概念
被final修饰的变量一旦被赋值初始化后,就不能再被重新赋值。即变量值只能被赋值一次,不可被反复修改,所以叫做最终变量,也叫做常量。
并且我们在定义final变量时,必须显式地进行初始化,指定初始值,否则会出现“The blank final field xxx may not have been initialized”的异常提示。变量值的初始化,可以在两个地方:一是在变量定义处,即在final变量定义时直接给其赋值;二是在构造方法或静态代码块中。这些地方只能选其一,不能同时既在定义时赋值,又在构造方法或静态代码块中另外赋值。
我们在开发时,通常会把final修饰符和static修饰符一起使用,来创建类的常量。
2. 特性
根据修饰变量的作用范围,比如在修饰局部变量和成员变量时,final会有不同的特性:
- final修饰局部变量时,在使用之前必须被赋值一次才能使用;
- final修饰成员变量时,如果在声明时没有赋值,则叫做“空白final变量”,空白final变量必须在构造方法或静态代码块中进行初始化。
根据修饰变量的数据类型,比如在修饰基本类型和引用类型的变量时,final也有不同的特性:
- final修饰基本类型的变量时,不能把基本类型的值重新赋值,因此基本类型的变量值不能被改变。
- final修饰引用类型的变量时,final只会保证引用类型的变量所引用的地址不会改变,即保证该变量会一直引用同一个对象。因为引用类型的变量保存的仅仅是一个引用地址,所以final修饰引用类型的变量时,该变量会一直引用同一个对象,但这个对象本身的成员和数据是完全可以发生改变的。
3. 语法
final修饰变量时,常用的语法格式如下:
final String 变量名=变量值;
我们在使用final声明变量时,一般会要求变量名的单词全部大写,且变量名由多个单词组成时,多个单词之间用下划线“_”分隔开,比如“SCHOOL_NAME”。
4. 案例
4.1 修饰局部变量
以下案例是final修饰局部变量时的用法和特性。
/**
* @author 一一哥Sun
*/
public class FinalDemo {
// final修饰局部变量,该变量使用之前要赋初值
public void declareFinal() {
// 先声明变量
final int x;
// 再赋初值,该值只能赋一次,否则会报错。
x = 200;
//The final local variable x may already have been assigned
//x = 400;
System.out.println("x=" + x);
// 声明的同时赋值
final int y = 300;
System.out.println("y=" + y);
}
}
从上述案例中可知,局部常量最好是在声明时就进行初始化。而且常量只能被赋值一次,否则会出现编译错误:“The final local variable xxx may already have been assigned”。
4.2 修饰成员变量
以下案例是final修饰成员变量时的用法和特性。
/**
* @author 一一哥Sun
*/
public class FinalDemo {
//final修饰成员变量
// 实例常量
final int a = 10; // 直接赋值
final int b; // 空白final变量,需要在构造方法中进行初始化
// 静态常量
final static int c = 20;// 直接赋值
final static int d; // 空白final变量,需要在静态代码块中进行初始化
static {
// 初始化静态变量
d = 40;
}
FinalDemo() {
// 初始化成员变量
b = 20;
// 不能第二次赋值,否则会发生编译错误
// The final local variable b may already have been assigned
//b = 30;
}
}
从上述代码中可知,空白final变量,可以在构造方法或静态代码块中初始化。
4.3 修饰基本类型的变量
以下案例是final修饰基本类型变量时的用法和特性。final修饰基本类型的变量时,不能把基本类型的变量重新赋值,因此基本类型的变量值不能被改变,否则会出现“The final local variable x cannot be assigned. It must be blank and not using a compound assignment”的异常。
/**
* @author 一一哥Sun
*/
public class FinalTypeDemo {
public static void main(String[] args) {
//final修饰基本类型的变量,变量值不可变
final int x=10;
//The final local variable x cannot be assigned. It must be blank and not using a compound assignment
//x=20;
System.out.println("x="+x);
}
}
4.4 修饰引用类型的变量
以下案例是final修饰引用类型变量时的用法和特性。final修饰引用类型的变量时,final只会保证引用类型的变量所引用的地址不会改变,即保证该变量会一直引用同一个对象,否则会出现“Array constants can only be used in initializers”或者“The final local variable user cannot be assigned. It must be blank and not using a compound assignment”的异常。
import java.util.Arrays;
/**
* @author 一一哥Sun
*/
public class FinalTypeDemo {
public static void main(String[] args) {
// final修饰数组变量,nums是一个引用变量
final int[] nums = { 1, 9, 7, 3 };
System.out.println(Arrays.toString(nums));
//final修饰引用类型时,引用的地址不可变,但引用对象本身的数据内容是可变的
//Array constants can only be used in initializers
//nums= {2,0,8,1};
// 对数组里的元素赋值修改没问题
nums[2] = 10;
System.out.println(Arrays.toString(nums));
// final修饰Person变量,p是一个引用变量
final User user = new User();
// 改变Person对象的age实例变量,合法
user.setAge(18);
System.out.println(user.getAge());
//对user变量的引用地址重新赋值,非法
//The final local variable user cannot be assigned. It must be blank and not using a compound assignment
//user = new User(30);
}
}
从上面的两个案例中,我们可以得知,使用 final修饰引用类型的变量时,变量不能被重新赋值,但我们可以改变该变量所引用对象里的内容。
三. 常量方法
1. 概念
被final修饰的方法称为常量方法,该方法可以被重载,也可以被子类继承,但却不能被重写。当一个方法的功能已经可以满足当前要求,不需要进行扩展,我们就不用任何子类来重写该方法,防止该方法的内容被修改。比如Object类中,就有一个final修饰的getClass()方法,Object的任何子类都不能重写这个方法。
2. 语法
final修饰方法时,常用的语法格式如下:
修饰符 final 返回值类型 方法名(){
//方法体
}
3. 案例
3.1 子类不能重写父类的final方法
如果我们在父类中定义一个final方法,子类继承父类后,子类不能重写父类中的这个final方法,否则会出现“Cannot override the final method from Father”异常。但是我们要注意,final方法是可以被重载的!
/**
* @author 一一哥Sun
*/
public class Father {
private String name;
public final void setName(String name) {
this.name=name;
}
//重载final方法
public final void setName(String firstName,String lastName) {
this.name=firstName+lastName;
}
}
//子类继承父类
public class Son extends Father{
private String name;
//子类不能重写父类中的final方法!
//Cannot override the final method from Father
//public void setName(String name) {
// this.name=name;
//}
}
3.2 父类中私有的final方法
如果我们在父类中定义了一个私有的private方法,因为它只能在当前类中可见,其子类无法访问该方法,所以子类也就无法重写该方法。如果子类中也定义了一个与父类中完全相同的private方法,这也不算方法重写,这只是重新定义了一个新方法。因此,即使我们使用final修饰private方法,我们也可以在其子类中定义与该方法相同的方法。
/**
* @author 一一哥Sun
*/
public class Father {
private double salary;
//父类中私有的方法
private final void setSalary(double salary) {
this.salary=salary;
}
}
//子类继承父类
public class Son extends Father{
private double salary;
//子类中定义与父类同名通参的方法,这不属于方法重写!
private void setSalary(double salary) {
this.salary=salary;
}
}
4. 配套视频
与本节内容配套的视频链接如下:
Bilibili External Player
四. 常量类
1. 概念
我们知道,通常子类继承父类时,子类可以访问父类的内部数据,并可通过重写父类的方法来改变父类方法的实现细节。但这样做可能会导致一些不安全的因素,所以为了保证某个类不可被继承,我们就可以使用final来修饰这个类。被final修饰的类称为常量类,这种类不能被继承,也就不能被修改或扩展。
final类无法被任何其他类继承,这意味着该类在Java的继承树体系中是一个叶子类,比如我们经常使用的String类,就是典型的final类。如下图所示:
而final类中的成员,我们可以用final修饰,也可以不用final修饰。比如final类中的方法,因为这些方法本身就属于final类,该类都不能被子类继承,里面的所有方法自然也就不能被子类重写,那么这些方法自然也就成了final型。所以final中的变量和方法,我们都没必要再单独添加final修饰符了。
2. 语法
final修饰类时,常用的语法格式如下:
访问修饰符 final class 类名{
//....
}
3. 实现案例
/**
* @author 一一哥Sun
* 父类是final类
*/
public final class Father {
......
}
//此时子类不能继承父类,否则会出错!
public class Son extends Father{
......
}
从上面的代码中我们可以验证得知,final类不能被继承,final类中的方法更不可能被重写,否则会出现“The type Son cannot subclass the final class Father”异常,如下图所示:
4. 配套视频
与本节内容配套的视频链接如下:
Bilibili External Player
-------------------------------------------------正片已结束,来根事后烟-----------------------------------------------
五. 结语
至此,壹哥就把final与常量、常量方法、常量类等相关内容讲解完毕了,现在你知道final的特性都有哪些吗?最后壹哥给大家总结如下:
- final修饰的变量,其变量值不可被改变,此时的变量被称为常量;
- final修饰引用类型的变量时,引用地址不可变,但对象中的数据可变;
- final修饰的方法不可以被重写;
- final修饰的类不可以被继承,即不能有子类。
另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。
六. 今日作业
class MyClass{
final int value;
public MyClass(){
}
public MyClass(int value){
this.value=value;
}
}
public class TestMain{
public static void main(String args[]){
MyClass mc=new MyClass(10);
System.out.println(mc.value);
}
}
根据上述代码,选择正确答案:
A. 编译通过,输出10;
B. 编译不通过,把第2行挨骂改为
final int value=10;
C. 编译不通过,把第4行代码改为
public MyClass(){value=10;}