static
和final
是Java
中最常用的关键字,本文介绍它们的所有用法以及含义。
static
通常用来修饰类的成员变量和类的方法,它可以用在以下场合:
1.修饰类的成员变量:意味着这个成员变量作为整个类而不是类的某个特定对象而存在,可以通过类名直接引用它们。一个static成员变量对每个类来说都只有一份存储空间,而非static成员变量则是对每个对象有一份存储空间。
2.修饰方法:static方法可以通过类名直接访问。
3.静态导入:静态导入static成员变量和static方法。
4.静态程序块:通常用来做一些初始化,static{}。
5.修饰成员类型:如修饰嵌套类(nested class),嵌套类和普通的内部类的区别就在于static的修饰。普通的内部类不能包含static数据和static属性,也不能包含嵌套类,但是嵌套类可以包含所有东西。注意static不能用来修饰最顶层的类。
下面的代码显示了static
的可用之处和不可用之处:
//
导入静态成员,不常用
import
static
java.util.Calendar.
AM
;
import
java.util.HashMap;
public
class
StaticTest {
//
静态成员变量,它只存有一份
private
static
int
i
= 123;
private
int
j
= 0;
private
static
HashMap
map
=
new
HashMap();
//
静态程序块,用来做一些初始化动作
static
{
map
.put(
"userid"
,
"admin"
);
}
public
void
printI() {
System.
out
.println(
"i="
+
i
);
}
//
静态方法,
public
static
void
s() {
int
m = 0;
//
可以定义非静态变量
static
int
n = 0;
//
错误代码,不能定义静态变量
System.
out
.println(
i
);
//
可以使用静态的成员变量
System.
out
.println(
j
);
//
错误代码,不能使用非静态的成员变量
}
public
static
void
main(String[] args) {
//
直接通过类引用静态成员变量和方法
StaticTest.s();
StaticTest.
i
++;
//test1
和
test2
打印出来的
i
值一样,因为它只有一份
StaticTest test1 =
new
StaticTest();
StaticTest test2 =
new
StaticTest();
test1.printI();
//i=124
test2.printI();
//i=124
}
}
|
下面的代码显示了static
修饰嵌套类:
public
class
StaticTest2 {
//
普通内部类
class
InnerClass {
static
int
i
= 0;
//
错误代码,普通内部类不能使用静态成员变量
static
final
int
j
= 0;
//
正确代码,常量
}
//
嵌套类,与普通内部类的区别在于多了
static
的修饰
static
class
NestedClass {
static
int
i
= 0;
//
正确代码
}
}
|
final关键字通常表示一个东西是不能改变的,它可以修饰的元素有:
1.修饰数据:通常用来定义常量。对于基本类型,final使数值恒定不变,而对于对象引用,final使引用恒定不变,一旦引用被初始化指向一个对象,就无法把它改为指向另一个对象,然而,对象其本身是可以被修改的。
2.修饰参数:和修饰数据一样。
3.修饰方法:通常用来防止方法被子类覆盖(overriden),一个类中的所有private方法都隐含是final的,因为它无法被子类读取,也就不会被覆盖。另外,编译器针对final方法的调用都采用内嵌(inline)调用,可提高效率,但不是final的主要用途。
4.修饰类:表明该类(class)不能被继承。在final类里,所有的方法都被隐含的指定为final的,因为永远不存在子类,也就不存在子类覆盖一说。final类的字段可以根据个人的意愿选择是或不是final,不论类是否定义为final,相同的规则都适用于final字段。
平日在编程中用的最多的应该是使用final修饰数据,尤其是定义常量。
下面的代码显示了final
对各种元素的修饰:
//
修饰类,类不能被继承
public
final
class
FinalTest {
//
修饰数据,
i
不能被再次赋值
private
final
int
i
= 0;
//
修饰方法,该方法不能被子类覆盖
public
final
void
f(String s) {
System.
out
.println(
"FinalTest.f"
);
}
//
修饰参数,
j
不能被重新赋值
public
void
g(
final
int
j) {
j = 1;
//
这条语句是错误的
}
}
|
下面的代码显示了父类不能覆盖子类的final
方法:
public
class
FinalChild
extends
Final {
//
这个方法是错误的,它不能覆盖父类的
final
方法
public
void
f(){};
//
这个方法正确,重载
public
void
f(String s){}
}
class
Final {
public
final
void
f() {}
}
|
对于对象引用的修饰容易让人迷惑,看下面的程序:
final
int
i = 0;
i = 1;
//
错误的代码,不能重新赋值
final
StringBuffer s =
new
StringBuffer(
"Java"
);
s.append(
"World!"
);
//
正确代码,可以改变对象内容
s =
new
StringBuffer(
"Hello"
);
//
错误的代码,不能给引用重新赋值,它不能指向别的对象
|
注意代码注释,对整型i
的修饰很好理解,给了i
初值后,不能再给它其它值。而对于对象引用s
,虽然用了final
修饰并赋了初值,但依然可以使用s
来操纵对象,只不过,不能再将s
指向其它对象。
不要以为在定义final
数据成员时必须指定初值,也不要以为它只能给一个常数值,它还可以是一个表达式,如下面的代码是成立的:
final
int
j;
j = 2 * 3;
像上面这种未指定初值的声明final数据成员通常称为空白final。
对于类的成员变量,有无final修饰存在一点区别,对于没有final修饰的成员变量,它们都有一个默认的初值(如整型数据为0),而对于final修饰的成员变量,必须确保它被初始化。看下面的程序:
public
class
TestFinal {
private
int
i
;
//
默认值为
0
。如果是
private final int i;
程序就会报错
public
void
print() {
System.
out
.println(
i
);
}
}
|
如果使用final修饰i,程序就会出错,因为它未被初始化。如果想用final修饰的同时又不想在声明时初始化,则
可以在构造函数中赋值。如下面的程序是正确的:
public
class
TestFinal {
private
final
int
i
;
public
TestFinal () {
i
= 0;
}
public
void
print() {
System.
out
.println(
i
);
}
}
|
一个既是static又是final的字段只占据一段不能改变的存储空间。 带有恒定初始值(即,编译时常量)的static final基本类型全用大写字母命名,并且字与字之间用下划线隔开。如java.lang.byte类里对byte类型的最小值定义:
public static final byte MIN_VALUE = -128;