被final修饰的类表示不可被继承,并且此时该类中的所有成员方法都会被隐式地修饰为final。
一般不把类定义为final类。
说明这种方法提供的功能已经满足当前要求,不需要进行扩展,并且也不允许任何从此类继承的类来重写这种方法。
但是继承仍然可以继承这个方法,也就是说可以直接使用被final修饰的方法。在声明类中,一个 final 方法只被实现一次。
也就是只有明确不想被覆盖的方法会被修饰为final。
类的private方法会隐式地被指定为final方法。
final修饰成员变量时,必须要保证该成员变量在被声明时或者构造器中就被赋值了,且一旦初始化完成,值就不能被改变了。
有如下代码
public class Test { public static void main(String[] args) { String a = "hello2"; final String b = "hello"; String d = "hello"; String c = b + 2; String e = d + 2; System.out.println((a == c)); System.out.println((a == e)); } } /* true false */
此处的结果蕴含着final的特点:当final修饰基本数据类型和String时,如果在编译器就能知道这个变量的值,则编译器会把它当做编译期常量使用,类似C中的宏。于是此时的a和c实际指向了同一个引用,而e指向的是一块新的引用。
但是这个特性实现的前提是在编译期间就能知道具体的值,而如下所示
public class Test { public static void main(String[] args) { String a = "hello2"; final String b = getHello(); String c = b + 2; System.out.println((a == c)); } public static String getHello() { return "hello"; } } // false
上述代码在程序实际运行时,b才能通过调用getHello函数从而被初始化,所以无法在编译期间就获取到值,因此结果为false。
注意Java中采用的是值传递,所以在调用时要注意。例如
public class Test { public static void main(String[] args) { MyClass myClass = new MyClass(); int i = 0; myClass.changeValue(i); } } class MyClass { void changeValue(int i) { i++; } } //Error
上述代码会报错,因为在changeValue函数体中,i被识别为局部变量。而该变量为final,无法改变i的值。
修饰成员变量时,final保证不可变,static表示每个对象都有一份副本。
public class Test { public static void main(String[] args) { MyClass myClass1 = new MyClass(); MyClass myClass2 = new MyClass(); System.out.println(myClass1.i); System.out.println(myClass1.j); System.out.println(myClass2.i); System.out.println(myClass2.j); } } class MyClass { public final double i = Math.random(); public static double j = Math.random(); } // myClass1.i != myClass2.i // myClass1.j == myClass2.j
i被修饰为final,表示不可变,而j表示所有对象共享同一个j,所以i不同,j相同。