final的意思是“这是不能被改变的”,基于设计和效率两大理由你可能希望阻止改变。final可能使用在data(数据)、methods(方法)、classes(类)三个地方。
Final data(类的属性或叫成员变量、方法的参数)
许多程序语言都提供某种机制,用来告诉编译器某块数据是“固定不变的”,不变的数据可能很有用,因为它:
1)可以是永不改变的“编译期常量(compile-time contents)”
2)可以在执行期(run-time)被初始化,而你却不想再改变它
在Java中此类常量由关键字final修饰,定义时必须赋值,当final用于基本类型和对象引用(object references)时意义有很大不同;用于基本类型时,final让属性的值保持不变;当用于对象引用(object references)时,final让引用(reference)保持不变,即某个reference被初始化用以代表某个对象之后,便再也不能改变指向另一个对象,但对象本身的内容却是可以改变的。
Java允许产生所谓“留白的finals”(Blank final),也就是允许我们将数据成员声明为final,却不给予初始值,但要求blank final必须在使用之前进行初始化,这一点是由编译器保证的。blank final对final关键字的使用提供了更多弹性,使class内的final数据成员可以在每个对象中有所不同,但依旧保持不变。
Java允许将方法的参数声明为final,只要在参数前加final关键字即可,意味着你无法改变此参数(基本类型无法改变参数的值,对象类型无法指向另一个对象)。
分别用FinalData、BlankFinal和FinalArguments来说明final关键字在这三个方面的用法:
1)可以是永不改变的“编译期常量(compile-time contents)”
2)可以在执行期(run-time)被初始化,而你却不想再改变它
在Java中此类常量由关键字final修饰,定义时必须赋值,当final用于基本类型和对象引用(object references)时意义有很大不同;用于基本类型时,final让属性的值保持不变;当用于对象引用(object references)时,final让引用(reference)保持不变,即某个reference被初始化用以代表某个对象之后,便再也不能改变指向另一个对象,但对象本身的内容却是可以改变的。
Java允许产生所谓“留白的finals”(Blank final),也就是允许我们将数据成员声明为final,却不给予初始值,但要求blank final必须在使用之前进行初始化,这一点是由编译器保证的。blank final对final关键字的使用提供了更多弹性,使class内的final数据成员可以在每个对象中有所不同,但依旧保持不变。
Java允许将方法的参数声明为final,只要在参数前加final关键字即可,意味着你无法改变此参数(基本类型无法改变参数的值,对象类型无法指向另一个对象)。
分别用FinalData、BlankFinal和FinalArguments来说明final关键字在这三个方面的用法:
FinalData:
/**
* Created by houyefeng on 2014/11/30.
*/
package com.hyf.studying.java.keyword;
import java.util.ArrayList;
/**
* 演示Java语言final关键字的作用和用法。
* @author houyefeng
* @version 0.0.1
* @since 0.0.1
*/
public class FinalData {
/*
i1、VAL_TWO、VAL_THREE都是带有编译期数值的final基本类型,没有重大区别;
VAL_THREE与i1和VAL_TWO在可见性上有区别,是更为典型的常量定义方式。
定义为static强调的是类的属性的值只有一个;定义为final强调是一个常量。
*/
final int i1 = 9; //i1的值在编译期就能确定下来
static final int VAL_TWO = 99;
public static final int VAL_THREE = 39;
/*
i4、i5演示并非所有被生命为final的常量的值都是在编译期(compile-time)可知的。
i4、i5的值是在执行期生成的随机数,从这里也能看出final成员被声明为static和non-static的区别,
这种区别只有在final成员的值是在执行期生成时才能得到体现。
*/
final int i4 = (int)(Math.random()*20);
static final int i5 = (int)(Math.random()*20);
/*
list1、list2、list3、array演示final用于非基本类型(object reference)时的使用和意义。
*/
ArrayList
list1 = new ArrayList
();
final ArrayList
list2 = new ArrayList
();
static final ArrayList
list3 = new ArrayList
(); final int [] array = {1,2,3,4}; /** * 打印i4和i5的值,以演示不是所有的fianl属性的值都是编译期可知的。 * @param tag 对象标识 */ public void print(String tag) { System.out.println(tag + ": i4=" + i4 + ", i5=" + i5); } public static void main(String [] args) { FinalData fd = new FinalData(); /* 以下2个语句都是不能编译能过的,因为i1和list2都是被final关键字修饰的,不能改变。 */ //fd.i1++; //fd.list2 = new ArrayList
(); fd.list1 = new ArrayList
(); //这个语句是正确的,因为list1不是final的 /* 下面的语句也是正确的,因为虽然list2是final的,但list2是对象类型,而不是基本类型, 这个语句改变的是list2指向对象的值,而不是改变的list2本身。 */ fd.list2.add("change the value"); fd.print("fd"); FinalData fd1 = new FinalData(); fd1.print("fd1"); } }
BlankFinal:
/**
* Created by houyefeng on 2014/12/1.
*/
package com.hyf.studying.java.keyword;
/**
* @author houyefeng
* @version 0.0.1
* @since 0.0.1
*/
public class BlankFinal {
private final int i=9;
/*
* 属性j和intE在声明时未进行初始化,必须在所有的构造方法中对其进行赋值,
* 这也是final属性在使用前被初始化的原因所在。如果使用eclipse开发时,构造方法
* 未对final属性进行初始化,会提示“The blank final field intE may not have been initialized”
* 这样的错误信息,从而不能编译类。
*/
private final int j;
private final Integer intE;
public BlankFinal() {
this.j = 9;
this.intE = new Integer(7);
}
public BlankFinal(int x) {
this.j = x;
this.intE = new Integer(x);
}
public void print() {
System.out.println("j=" + j + ", intE=" + intE);
}
public static void main(String[] args) {
new BlankFinal().print();
new BlankFinal(20).print();
}
}
FinalArguments:
/**
* Created by houyefeng on 2014/12/1.
*/
package com.hyf.studying.java.keyword;
import java.util.HashMap;
import java.util.Map;
/**
* @author houyefeng
* @version 0.0.1
* @since 0.0.1
*/
public class FinalArguments {
/*
方法with和f中注释的语句试图改变map和i,
编译器会提示“cannot assign a value to final variable 'map'”,
不能正常编译。
*/
public void with(final Map map) {
//map = new HashMap();
}
public void f(final int i) {
//i++;
int j = i+1;
System.out.println(j);
}
public void without(Map map) {
map = new HashMap();
}
}
Final methods(Final方法)
使用final方法的原因有二:一是基于设计的考量,你希望某个函数的行为在继承过程中保持不变,而且无法被覆写;二是基于效率,声明为final的方法在被调用时允许编译器进行调用优化,编译器可以根据自己的判断来决定是进行正常的方法调用还是将方法体嵌入到方法调用的位置。
class中的所有private方法都是final的,你无法在方法外调用private方法,也不能覆写它们。
下面3个类用来演示final方法的用法:
class中的所有private方法都是final的,你无法在方法外调用private方法,也不能覆写它们。
下面3个类用来演示final方法的用法:
/**
* Created by houyefeng on 2014/12/1.
*/
package com.hyf.studying.java.keyword;
/**
* @author houyefeng
* @version 0.0.1
* @since 0.0.1
*/
public class FinalMethods {
public static void main(String [] args) {
OverridingPrivateMethod opm = new OverridingPrivateMethod();
/*
输出为:PrivateMethods.finalMethod2()
*/
opm.finalMethod2();
//opm.finalMethod1(); 此方法由于声明为private在类外无法调用
/*
输出为:OverridingPrivateMethod.method()
*/
opm.method();
/*
输出为:OverridingPrivateMethod.method2()
*/
opm.method2();
PrivateMethod pm = opm;
pm.finalMethod2();
/*
此句在编译时提示找不到方法,在PrivateMethod类中虽然声明了method()方法,
但由于是private的,所以在OverridingPrivateMethod的对象向上转型为PrivateMethod类型时
method()方法不可见,说明OverridingPrivateMethod并没有成功覆写PrivateMethod的method方法。
pm.method();
*/
/*
输出为:OverridingPrivateMethod.method2(),和预料的一样
*/
pm.method2();
}
}
class PrivateMethod {
private final void finalMethod1() {
System.out.println("PrivateMethods.finalMethod1()");
}
protected final void finalMethod2() {
System.out.println("PrivateMethods.finalMethod2()");
}
private void method() {
System.out.println("PrivateMethods.method()");
}
public void method2() {
System.out.println("PrivateMethods.method2()");
}
}
class OverridingPrivateMethod extends PrivateMethod {
private final void finalMethod1() {
System.out.println("OverridingPrivateMethod.finalMethod1()");
}
/*
由于方法finalMethod2()从PrivateMethod继承而来,编译器将会提示不能覆写父类的final方法
protected final void finalMethod2() {
System.out.println("PrivateMethods.finalMethod2()");
}*/
public void method() {
System.out.println("OverridingPrivateMethod.method()");
}
public void method2() {
System.out.println("OverridingPrivateMethod.method2()");
}
}
Final classes(Final类)
基于设计上或保密的考量,你可以将类声明为final,这样类的行为将不会被改变,也不能被继承。
不论一个类是否被定义为final,其成员仍然遵循final data的规则,将class定义为final只不过是要杜绝继承的发生;由于final class中的方法无法继承,也不能够覆写,所以所有的方法都是final的。
类FinalClasses和FinalClass来演示final class类成员变量与方法与final的关系:
不论一个类是否被定义为final,其成员仍然遵循final data的规则,将class定义为final只不过是要杜绝继承的发生;由于final class中的方法无法继承,也不能够覆写,所以所有的方法都是final的。
类FinalClasses和FinalClass来演示final class类成员变量与方法与final的关系:
/**
* Created by houyefeng on 2014/12/1.
*/
package com.hyf.studying.java.keyword;
/**
* @author houyefeng
* @version 0.0.1
* @since 0.0.1
*/
public class FinalClasses {
public static void main(String [] args) {
FinalClass fc = new FinalClass();
/*
final class的方法被调用时,将改变成员变量i的值,
此方法得以顺利编译,运行时得到预期结果,但试图继承final class时
确不能编译成功;这个简单实例说明了final class的成员变量和方法与final
的关系
*/
fc.f();
}
}
final class FinalClass {
private int i = 1;
public void f() {
i++;
System.out.println(i);
}
}
告诫:不要轻易的将类或类的方法声明为final,因为我们很难预料类可能被重复运用的方式。