1、final变量
final关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由final定义的变量为常量。
例如,在类中定义PI值,可以使用如下语句:
final double PI=3.14;
当在程序中使用到PI这个常量时,它的值就是3.14,如果在程序中再次对定义为final的常量赋值,编译器将不会接受。
final关键字定义的常量必须在声明时对其进行赋值操作。final除了可以修饰基本数据类型的常量,还可以修饰对象引用。由于数组也可以被看作一个对象来引用,所以final可以修饰数组。一旦一个对象引用被修饰为final后,它只能恒定指向一个对象,无法将其改变以指向另一个对象。一个既是static又是final的字段只占据一段不能改变的存储空间。
例,在项目的com.lzw包中创建FinalData类,在该类中创建Test内部类,并定义各种类型的final变量。
package com.lzw;
import java.util.Random;
public class FinalData {
static Random rand=new Random();
private final int VALUE_1=9;
private static final int VALUE_2=10;
private final Test test=new Test();
private Test test2=new Test();
private final int[]a= {1,2,3,4,5};
private final int i4=rand.nextInt(20);
private static final int i5=rand.nextInt(20);
public String toString() {
return i4+""+i5+"";
}
public static void main(String[] args) {
FinalData data=new FinalData();
/* data.test=new Test();
data.VALUE_1++;
data.VALUE_2++;*/
data.test2=new Test();
/*for(int i=0;i<data.a.length;i++) {
a[i]=9;
}*/
System.out.println(data.i4);
System.out.println(data.i5);
System.out.println(data);
}
}
class Test{
int i=0;
}
被定义为final的常量定义时需要使用大写字母命名,并且中间使用下划线进行连接,这是Java中的编码规则。同时,定义为final的数据无论是常量、对象引用还是数组,在主函数中都不可以被改变。
一个被定义为final对象的引用只能指向唯一一个对象,不可以将它再指向其他对象,但是一个对象本身的值却是可以改变的,那么为了使一个常量真正做到不可被修改,可以将常量声明为static final。
package com.lzw;
import java.util.Random;
public class FinalStaticData {
private static Random rand=new Random();//实例化一个Random对象
private final int a1=rand.nextInt(10);
private static final int a2=rand.nextInt(10);
public static void main(String[] args) {
FinalStaticData fdata=new FinalStaticData();
System.out.println("重新实例化对象调用a1的值"+fdata.a1);
System.out.println("重新实例化对象调用a2的值"+fdata.a2);
FinalStaticData fData=new FinalStaticData();
System.out.println("重新实例化对象调用a1的值"+fData.a1);
System.out.println("重新实例化对象调用a2的值"+fData.a2);
}
}
运行结果如下:
从运行结果来看,定义为final的常量不是恒定不变的,将随机数赋予定义为final的常量,可以做到每次运行程序时改变a1的值。但是a2与a1不同,由于它被声明为static final形式,所以在程序中为a2开辟了一个恒定不变的区域,当再次实例化一个FinalStaticData对象时,仍然指向a2这块内存区域,所以a2的值保持不变。a2是在装载时被初始化。而不是每次创建新对象时都被初始化;而a1会在重新实例化对象时被修改。
在Java 中定义全局常量,通常使用public static final来修饰,这样的常量只能在定义时被赋值。
可以将方法的参数定义为final类型,这预示着无法在方法中更改参数引用所指向的对象。
2、final方法
定义为final的方法不能被重写。
将方法定义为final类型可以防止子类修改该类的定义与实现方式,同时定义为final的方法的执行效率要高于非final方法。在修饰权限中曾经提到过private修饰符,如果一个父类的某个方法被设置为private修饰符,子类将无法访问该方法,自然无法覆盖该方法,所以一个定义为private的方法隐式被指定为final类型,这样无需将一个定义为private 的方法再定义为final类型。例如下面的语句:
private final void test(){
...//程序代码
}
但是在父类中被定义为private final的方法似乎可以被子类覆盖??????
package com.lzw;
class Parents{
private final void doit() {
System.out.println("父类.doit()");
}
final void doit2() {
System.out.println("父类.doit2()");
}
public void doit3() {
System.out.println("父类.doit3()");
}
}
class Sub extends Parents{
//在子类中定义一个doit()方法
public final void doit() {
System.out.println("子类.doit()");
}
//final方法不能被覆盖
/* final void doit2() {
System.out.println("子类.doit2()");
}*/
public void doit3() {
System.out.println("子类.doit3()");
}
}
public class FinalMethod {
public static void main(String[] args) {
Sub s=new Sub();//实例化
s.doit();//调用doit()方法
Parents p=s;//执行向上转型操作
// p.doit();//不能调用private操作
p.doit2();
p.doit3();
}
}
运行结果如下:
可以看出,final方法不能被覆盖。例如,doit2()方法不能在子类中被重写,但是在父类中定义了一个private final的doit()方法,同时在子类中也定义了一个doit()方法。从表面上来看,子类中的doit()方法覆盖了父类的doit()方法,但是覆盖必须满足一个对象向上转型为它的基本类型并调用相同方法这样一个条件
3、final类
定义为final的类不能被继承。
如果希望一个类不允许任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final形式。
final类的语法如下:
final 类名{}
如果将某个类设置为final形式,则类中的所有方法都被隐式设置为final形式,但是final类中的成员变量可以被定义为final或非final形式。
package com.lzw;
public final class FinalClass {
int a=3;
void doit() {
}
public static void main(String[] args) {
FinalClass f=new FinalClass();
f.a++;
System.out.println(f.a);
}
}
运行结果如下: