一、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方法@Overridepublic 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;
}