一、final
根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类的成员方法和变量(包括成员变量和局部变量)。它有以下特点:
- final类不能被继承,没有子类,final类中的方法默认是final的。
- final方法不能被子类的方法覆盖,但可以被继承。
- final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
- final不能用于修饰构造方法。
- 父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
1.1 final类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
1.2 final方法
final类不能被继承,因此final类的成员方法没有机会被覆盖。使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。因此,如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。
1.3 final变量
修饰变量是final用得最多的地方,对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。举个例子:
1
2
3
4
5
6
7
8
class Man {
private final int i = 0;
public Man() {
// i = 1; // 报错
final Object obj = new Object();
// obj = new Object(); // 报错
}
}
另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。举个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package org.test;
public class Test3 {
private final String S = "final实例变量S";
private final int A = 100;
public final int B = 90;
public static final int C = 80;
private static final int D = 70;
public final int E; // final空白,必须在初始化对象的时候赋初值
public Test3(int x) {
E = x;
}
public static void main(String[] args) {
Test3 t = new Test3(2);
// t.A=11; // 出错,final变量的值一旦给定就无法改变
// t.B=91; // 出错,final变量的值一旦给定就无法改变
// t.C=81; // 出错,final变量的值一旦给定就无法改变
// t.D=71; // 出错,final变量的值一旦给定就无法改变
System.out.println(t.A);
System.out.println(t.B);
System.out.println(t.C); // 不推荐用对象方式访问静态字段
System.out.println(t.D); // 不推荐用对象方式访问静态字段
System.out.println(Test3.C); // 推荐使用类名直接调用
System.out.println(Test3.D); // 推荐使用类名直接调用
// System.out.println(Test3.E); // 出错,因为E为final空白,依据不同对象值有所不同.
System.out.println(t.E);
Test3 t1 = new Test3(3);
System.out.println(t1.E); // final空白变量E依据对象的不同而不同
}
private void test() {
System.out.println(new Test3(1).A);
System.out.println(Test3.C);
System.out.println(Test3.D);
}
public void test2() {
final int a; // final空白,在需要的时候才赋值
final int b = 4; // 局部常量,final用于局部变量的情形
a = 3;
// a=4; // 出错,已经给赋过值了
// b=2; // 出错,已经给赋过值了
}
}
二、static
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块。它有以下特点:
- 被static修饰的成员变量和成员方法独立于该类的任何对象。
- 类的所有实例共享同一个static变量。
- static是在内存中分配一块区域,供整个类通用,所有的类的对象都享有它的共同的值。
2.1 static方法(静态方法)
静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。即使没有创建对象,也需要一个能调用的方法,为满足这两方面的要求,可使用static(静态)关键字。
2.2 static变量(静态变量)
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行初始化。
2.3 static代码块(静态块)
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。利用静态代码块可以对一些static变量进行赋值。举个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Test5 {
private static int a;
private int b;
static {
Test5.a = 3;
System.out.println(a);
Test5 t = new Test5();
t.f();
t.b = 1000;
System.out.println(t.b);
}
static {
Test5.a = 4;
System.out.println(a);
}
public static void main(String[] args) {
new Test5();
}
static {
Test5.a = 5;
System.out.println(a);
}
public void f() {
System.out.println("hhahhahah");
}
}
运行结果:
1
2
3
4
5
3
hhahhahah
1000
4
5
很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。