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 修饰的成员方法不能被覆盖。
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 形参写赋值语句就会编译运行报错)。
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 修饰局部变量,只能在声明该局部变量时直接赋值,或声明时不赋值但之后赋一次值,但不能赋第二次值,否则报错。
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 成员变量声明时赋值或在构造方法中赋值都可以,是因为二者实际上都是在
方法中完成的赋值。
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 修饰的引用类型的成员变量虽然也是赋值后无法修改,但这里的无法修改是指这个引用指向的地址无法修改(这个地址就是引用所指向的对象的地址),但所指向的这个对象本身里面的数据是可以修改的。
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 静态变量声明时赋值或在静态代码块中赋值都可以,是因为二者实际上都是在
方法中完成的赋值。
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;
// }
}