final-关键字
一、final修饰的类不能被继承
当final修饰一个类时,表明这个类不能被其他类继承。例如,在 Java 中,String类就是被final修饰的,这保证了String类的不可变性和安全性,防止其他类通过继承来改变String类的行为。
final class MyFinalClass {
// 类的成员和方法
}
// 以下代码会报错,因为不能继承final类
class Subclass extends MyFinalClass {
}
二、final修饰的方法不能被覆盖(重写)
用final修饰的方法不能在子类中被覆盖(重写)。这可以防止子类意外地改变父类中关键方法的行为,保证了方法的功能和语义在继承体系中的稳定性。
class Parent {
public final void finalMethod() {
System.out.println("这是父类的final方法");
}
}
class Child extends Parent {
// 以下代码会报错,不能覆盖final方法
@Override
public void finalMethod() {
System.out.println("尝试覆盖父类的final方法");
}
}
三、final修饰的变量,一旦赋值不能重新赋值
- 基本数据类型:
final
修饰的基本数据类型变量,一旦赋值就不能再重新赋值。例如:final int num = 10; num = 20; // 这行代码会报错,不能重新赋值给final变量
final int k;
// 首次初始化时可以的。
k = 200;
// 再次重新赋值是不允许的,因为final的。
//k = 300;
- 引用数据类型
final
修饰的引用变量,一旦指向某个对象后,就不能再指向其他对象,但指向的对象内部的数据是可以修改的。例如:
示例1:
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 合法,修改对象内部的数据
sb = new StringBuilder("New"); // 这行代码会报错,不能重新赋值给final引用变量
示例2:
Product类:
public class Product {
private String name;
private double price;
public Product() {
}
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public void display(){
System.out.println("商品名称:" + this.name + ",商品价格:" + this.price);
}
}
ProductTest类:
public class ProductTest {
public static void main(String[] args) {
// 创建商品对象
final Product pro = new Product("BMW535li", 10.0);
pro.display();
// 报错
//pro = new Product("BenzE300L", 20.0);
// 指向的对象的内部内存可以修改。没问题。
pro.setName("BenzE300L");
pro.setPrice(20.0);
pro.display();
}
}
运行结果:
四、final修饰的实例变量必须在对象初始化时手动赋值
final
修饰的实例变量确实必须在对象初始化时手动赋值,这是 Java 语言的规定,主要有以下几种实现方式及相关要点:
1.在定义时直接赋值
可以在声明final实例变量的同时直接为其赋初始值,这是一种较为常见和直观的方式。
class MyClass {
// 在定义时直接给final实例变量赋值
final int instanceVar = 10;
// 其他方法和成员
}
2.在构造函数中赋值
final修饰的实例变量。必须在构造方法执行完之前手动赋上值。(不允许采用系统默认值) 一般不存在这种情况。
示例1:
class MyClass {
final int instanceVar;
public MyClass() {
// 在构造函数中给final实例变量赋值
instanceVar = 10;
}
// 其他方法和成员
}
示例2:
public class User {
final String name;
final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
- 如果一个类有多个构造函数,那么每个构造函数都必须为
final
实例变量赋值,以保证无论通过哪个构造函数创建对象,final
实例变量都能得到正确的初始化。
class MyClass {
final int instanceVar;
public MyClass() {
instanceVar = 10;
}
public MyClass(int value) {
// 在另一个构造函数中也给final实例变量赋值
instanceVar = value;
}
// 其他方法和成员
}
3.在实例初始化块中赋值
还可以使用实例初始化块来为final实例变量赋值,实例初始化块会在构造函数执行之前执行,也能确保final实例变量在对象初始化时被赋值。
class MyClass {
final int instanceVar;
{
// 在实例初始化块中给final实例变量赋值
instanceVar = 10;
}
// 其他方法和成员
}
如果final修饰的实例变量没有在上述这些时机进行手动赋值,编译器就会报错,提示变量未初始化。这一规则保证了final实例变量在使用前总是有一个确定的值,体现了final关键字所代表的 “不可变” 语义,有助于提高程序的稳定性和可维护性,避免出现因变量未初始化或意外修改而导致的错误。
五、final修饰的实例变量一般和static联合使用:称为常量
当final修饰的实例变量和static联合使用时就形成了常量,以下是关于它的详细介绍:
定义与特点
用final和static修饰的变量被称为类常量,它属于类本身,而不是类的某个具体实例。在内存中,类常量只有一份存储,被所有该类的实例共享。并且由于final的作用,一旦被赋值,其值在程序运行期间就不能再被修改。
命名规范
按照 Java 的命名规范,常量通常使用大写字母命名,多个单词之间用下划线分隔,这样可以清楚地表明它是一个常量,与其他变量区分开来。例如:
public class MathConstants {
public static final double PI = 3.141592653589793;
public static final int MAX_VALUE = 100;
}
应用场景
数学和物理常量:在科学计算或数学相关的程序中,经常会用到一些固定的数学或物理常量,如圆周率PI、重力加速度G等,将它们定义为常量可以方便在代码中使用。
配置参数:在应用程序中,可能会有一些配置参数,如数据库连接字符串、系统默认设置等,将这些参数定义为常量可以在整个应用程序中统一使用,并且方便修改和维护。
状态码和枚举值:在网络通信或系统交互中,常常会用到一些固定的状态码或枚举值来表示不同的状态或类型,将它们定义为常量可以使代码更加清晰和易于管理。例如
public class HttpStatusCodes {
public static final int OK = 200;
public static final int BAD_REQUEST = 400;
public static final int INTERNAL_SERVER_ERROR = 500;
}