第5 章类的高级特性
5.1 static关键字
static关键字用来声明静态变量和静态方法。例如: 
class MyClass { 
static int i; 
static void increase(){ 
i++; 
} 
}
静态变量和静态方法为类中所有对象所公有,可以不创建对象,直接
引用,也称为类变量和类方法。
引用方式:类名.静态变量/静态方法,如: 
MyClass.i; 
MyClass.increase(); 
如果在声明时不用static关键字修饰,则为实例变量和实例方法。
一个类通过使用new运算符可以创建多个不同的对象,这些对象将被
分配不同的内存空间,准确地说,就是不同的对象的实例变量将被分配不
同的内存空间,如果类中的成员变量有类变量,所有的对象的这个类变量
都分配给相同的一处内存。也就是说,对象共享类变量,改变其中一个对
象的这个类变量会影响其他对象的这个类变量。
静态变量可以通过类名直接访问,也可以通过对象来调用。采用这两
种方法取得的结果是相同的。如果是public静态变量,则其他类可以不通
过实例化访问它们。
类方法不能访问实例变量,只能访问类变量。类方法可以由类名直接
调用,也可由实例对象进行调用。类方法中不能使用this或super关
键字。

1 20  JavaEE 零基础入门 
对于实例变量必须先生成实例对象,通过该对象访问实例变量。实例方法可以对当
前对象的实例变量进行操作,也可以对类变量进行操作,实例方法由实例对象调用。下面
图5-1 实例变量与静态变量关系
的代码及图5-1 说明了实例变量与静态变量的
关系。 
class ABCD { 
char data; 
static int st_data; 
}c
lass Demo { 
ABCD a,b,c,d 
}
例5-1 关于实例成员和类成员的例子。
程序清单:ch05\MemberTest.java 
package ch05; 
class Member { 
static int classVar; 
int instanceVar; 
static void setClassVar(int i) { 
classVar=i; 
//instanceVar=i; //类方法不能访问实例变量 
} 
static int getClassVar(){ 
return classVar; 
} 
void setInstanceVar(int i){ 
classVar=i; //实例方法不但可以访问类变量,也可以访问实例变量 
instanceVar=i; 
} 
int getInstanceVar() { 
return instanceVar; 
} 
} 
public class MemberTest{ 
public static void main(String[] args) { 
Member m1=new Member(); 
Member m2=new Member(); 
m1.setClassVar(1); 
m2.setClassVar(2); 
System.out.println("m1.classVar="+ m1.getClassVar()+" m2.ClassVar="+ m2. 
getClassVar()); 
m1.setInstanceVar(11); 
m2.setInstanceVar(22);

第5 章 类的高级特性 1 21 
System.out.println("m1.InstanceVar="+ m1.getInstanceVar()+" m2.InstanceVar= 
"+m2.getInstanceVar()); 
} 
}
程序运行结果如下: 
m1.classVar=2 m2.ClassVar=2 
m1.InstanceVar=11 m2.InstanceVar=22 
分析一个不正确的变量引用实例: 
class StaticError{ 
String mystring="hello"; //实例变量 
public static void main(String[] args) { 
System.out.println(mystring); //静态方法访问实例变量出错 
} 
}
错误信息:can’tmakeastaticreferencetononstaticvariable。因为只有对象的方法
可以访问对象的变量。
解决的办法如下。
(1)将实例变量mystring改为类变量: 
class StaticError{ 
static String mystring="hello"; 
public static void main(String[] args) { 
System.out.println(mystring); 
} 
}
(2)将实例变量mystring改为局部变量: 
class NoStaticError{ 
public static void main(String[] args) { 
String mystring="hello"; 
System.out.println(mystring); 
} 
}
例5-2 下面例子中的梯形对象共享一个static的下底。
程序清单:ch05\CommonLader.java 
package ch05; 
class 梯形{ 
float 上底,高; //类的变量 
static float 下底; //类的变量 
梯形(float x,float y,float h) { //构造方法

1 22  JavaEE 零基础入门 
上底=x; 下底=y; 高=h; 
} 
float 获取下底() { 
return 下底; 
} 
void 修改下底(float b) { 
下底=b; 
} 
}p
ublic class CommonLader{ 
public static void main(String[] args){ 
梯形laderOne=new 梯形(3.0f,10.0f,20); 
梯形laderTwo=new 梯形(2.0f,3.0f,10); 
System.out.println("laderOne 的下底:"+laderOne.获取下底()); 
System.out.println("laderTwo 的下底:"+laderTwo.获取下底()); 
laderTwo.修改下底(60); 
System.out.println("laderOne 的下底:"+laderOne.获取下底()); 
System.out.println("laderTwo 的下底:"+laderTwo.获取下底()); 
} 
}
程序运行结果如下: 
laderOne 的下底: 3.0 
laderTwo 的下底: 3.0 
laderOne 的下底: 60.0 
laderTwo 的下底: 60.0 
当Java程序执行时,类的字节码文件被加载到内存,如果该类没有创建对象,类的实
例成员变量不会被分配内存。但是,类中的类变量,在该类被加载到内存时,就分配了相
应的内存空间。如果该类创建对象,那么不同对象的实例变量互不相同,即分配不同的内
存空间,而类变量不再重新分配内存,所有的对象共享类变量,即所有的对象的类变量是
相同的一处内存空间,类变量的内存空间直到程序退出运行,才释放所占有的内存。
实例方法和类方法的区别如下。
对于类中的类方法,在该类被加载到内存时,就分配了相应的入口地址。从而类方法
不仅可以被类创建的任何对象调用执行,也可以直接通过类名调用。类方法的入口地址
直到程序退出才被取消。
当类的字节码文件被加载到内存时,类的实例方法不会被分配入口地址。当该类创
建对象后,类中的实例方法才分配入口地址,从而实例方法可以被类创建的任何对象调用
执行。需要注意的是,当创建第一个对象时,类中的类方法就分配了入口地址,当再创建
对象时,不再分配入口地址,也就是说,方法的入口地址被所有的对象共享,当所有的对象
都不存在时,方法的入口地址才被取消。
无论是类方法还是实例方法,当被调用执行时,方法中的局部变量才被分配内存空

第5 章 类的高级特性 1 23 
间,方法调用完毕,局部变量即刻释放所占的内存。在一个方法被调用执行完毕之前,如
果该方法又被调用,那么,方法的局部变量会再次被分配新的内存空间,例如,方法在递归
调用时,方法中的局部变量会再次被分配新的内存空间。
5.2 this关键字
this关键字可以出现在类的实例方法中,代表使用该方法的当前对象。
为了说明this的用法,下面例子中的“三角形”的构造方法中,有意使用了this。当使
用构造方法来创建对象时,构造方法中的this就代表当前对象。
例5-3 “三角形”的构造方法中的this就代表当前对象。
程序清单:ch05\Triangle.java 
package ch05; 
class 三角形{ 
double a,b,c; 
三角形(double a,double b,double c) { //构造方法 
setABC(this,a,b,c); //调用实例方法 
} 
void setABC(三角形trangle,double a,double b,double c) { 
//实例方法,供构造方法调用初始化实例对象 
trangle.a=a; 
trangle.b=b; 
trangle.c=c; 
} 
} 
class Triangle{ 
public static void main(String[] args) { 
三角形tra=new 三角形(3,4,5); 
System.out.print("三角形的三边是:"+tra.a+","+tra.b+","+tra.c+","); 
} 
} 
运行结果如下: 
三角形的三边是: 3.0,4.0,5.0, 
实例方法可以操作类的成员变量,实际上,当成员变量在实例方法中出现时,默认的
格式是“this.成员变量”。如: 
class A{ 
int x,y; 
void fun(int x){ 
this.x=x; 
y=x;

1 24  JavaEE 零基础入门 
} 
}
在上述A 类中的实例方法fun()中出现了this,this就代表使用fun()的当前对象。
所以,“this.x”就表示当前对象的变量x,当对象调用方法fun()时,将函数的局部变量x 
赋给该对象的变量x。当一个对象调用方法时,方法中的成员变量就是指分配给该对象
的成员变量。
通常情况下,可以省略成员变量名字前面的“this.”,如成员变量y。但是,当成员变
量的名字和局部变量的名字相同时,成员变量前面的“this.”就不可以省略。
同样,类的实例方法可以调用类的其他方法,调用的默认格式是“this.方法”,如: 
class B{ 
void fun() { 
this.get(); //可省略this. 
} 
void get() { 
System.out.println("ok"); 
} 
}
在上述B类中的方法fun()中出现了this,this代表使用方法fun()的当前对象。所
以,方法fun()的方法体中this.get()就是调用当前对象的方法get()。也就是说,当某个
对象调用方法fun()的过程中,又调用了方法get()。由于这种逻辑关系非常明确,一个
方法调用另一个方法时可以省略方法名字前面的“this.”。
但是,this不能出现在类方法中,因为类方法可以通过类名直接调用,这时可能还没
有任何对象诞生。
例5-4 创建一个有两个方法的类,其中第一个方法使用this,第二个方法不使
用this。
程序清单:ch05\Rectangle.java 
package ch05; 
class Rectangle{ //矩形类 
int width; //矩形的宽 
int usethis(int width){ //返回宽度的函数 
this. width=width; //this 指自己这个对象 
return width; 
} 
int unusethis(int width){ 
int w=width; 
return w; 
} 
public static void main(String[] args){ 
Rectangle r=new Rectangle(); //类对象的实例化 
System.out.println("r.width="+ r.width+" r.usethis(1)="+ r.usethis(1)+

第5 章 类的高级特性 1 25 
"r.width="+r.width); 
System.out.println("r.width="+r.width+" r.unusethis(2)="+ r.unusethis(2) 
+" r.width="+r.width); 
} 
}
运行结果如下: 
r.width=0 r.usethis(1)=1 r.width=1 
r.width=1 r.unusethis(2)=2 r.width=1 
例5-5 编译并运行下面的程序,分析运行结果,进一步了解super和this的作用。
程序清单:ch05\SuperDemo.java 
package ch05; 
mport java.io.*; 
class SuperClass{ //定义父类 
int x; 
SuperClass(){ //父类的构造方法 
x=10; 
} 
void doClass(){ 
System.out.println("SuperClass.doClass()"); 
} 
} 
class SubClass extends SuperClass{ //定义子类 
int x; 
SubClass(){ //子类的构造方法 
super(); //调用父类的构造方法 
x=100; 
} 
void doClass(){ //重写父类的doClass 方法 
System.out.println("SubClass.doClass()"); 
} 
void doDemo(){ //演示super 和this 的方法 
int x; 
x=1000; 
super.doClass(); //调用父类的doClass 方法 
doClass(); //调用本类的doClass 方法 
System.out.println("super.x="+super.x); //父类的x 
System.out.println("this.x="+this.x); //本类的x 
System.out.println("x="+x); //本方法的x 
} 
}public class SuperDemo{ 
public static void main(String[] args){ //主方法

1 26  JavaEE 零基础入门 
SubClass s=new SubClass(); 
s.doDemo(); 
} 
}
运行结果如下: 
SuperClass.doClass() 
SubClass.doClass() 
super.x=10 
this.x=100 
x=1000 
分析:此程序中定义了一个父类,子类SubClass继承了父类SuperClass,在主函数
中定义SubClass类对象s时,自动调用类SubClass的构造函数SubClass(),在此构造函
数中先执行“super();”语句,这样就调用类SuperClass的构造方法SuperClass(),因为
super来指明超类中的方法。同样在子类方法doDemo()中,执行语句“super.doClass();”时, 
则调用父类的方法doClass()。如不用super来指定,则调用的是子类的方法doClass(), 
这里子类SubClass()的成员方法doClass()重写了父类SuperClass()的成员方法
doClass()。
语句“System.out.println("super.x="+super.x);”中super 调用的是父类
SuperClass的变量x,而语句“System.out.println("this.x="+this.x);”中this调用子类
SubClass的变量x,关键字this和super分别用来指明子类和父类中同名的成员变量。
这里父类SuperClass的成员变量x、子类SubClass的成员变量x和类方法doDemo()中
使用的局部变量x三者同名,则要使用关键字this和super来指定所要使用的变量。如
不用则输出类方法的局部变量,如语句“System.out.println("x="+x);”输出的就是类
方法doDemo()的局部变量。这里子类SubClass()的成员变量x隐藏了父类SuperClass()的
成员变量x。
5.3 静态导入
在JDK1.5后新加了导入静态方法和静态域的功能。在类中使用静态导入,可以使
用其他类中定义的类方法和类变量,而且这些类方法和类变量就像在本地定义的一样。
换句话说,静态导入允许在调用其他类中定义的静态成员时,可以不通过类名直接使用类
中的静态方法。
导入一个类一般都用importxxx.ClassName;而静态导入语句是importstaticxxx. 
ClassName.*;这里多了static和.*,意思是导入xxx.ClassName这个类里的所有静态
方法和静态域。当然,也可以只导入某个静态方法,只要把.* 换成静态方法名就行了。
以后在这个类中,就可以直接用方法名调用静态方法,而不必用“ClassName.方法名”的
方式来调用。
静态导入语句看起来和普通的import语句非常相似。但是,普通import语句从某

第5 章 类的高级特性 1 27 
个包中导入的是一个或所有的类,而静态import语句从某个类中导入的却是一个或所有
的类方法以及类变量。需要注意的是,针对一个给定的包,不可能用一行语句静态地导入
所有类的所有类方法和类变量。也就是说,不能这样编写代码: 
import static java.lang.*; //编译出错! 
如果一个本地方法和一个静态导入的方法有着相同的名字,那么本地方法将被
优先调用。
例如,源文件顶部添加:importstaticjava.lang.System.*;那么就可以使用System 类
的静态方法和静态域,而不必加类前缀。如: 
out.println("hello world"); //相当于System.out.println("hello world"); 
exit(0); //相当于System.exit(0); 
导入静态方法和导入静态域有以下两个实际的应用。
(1)算术函数,对Math类使用静态导入,就能更自然地使用算术函数。如: 
sqrt(pow(x,2)+pow(y,2)); 
(2)笨重的常量,如果需要使用大量带有冗长名字的常量,就应该使用静态导入。如: 
Date d=new Date(); 
if (d.get(DAY_OF_WEEK)==MONDAY) 
看起来比if(d.get(Calendar.DAY_OF_WEEK)==Calendar.MONDAY)清晰。
例5-6 导入静态方法举例,计算直角三角形的斜边。
程序清单:ch05\StaticImportTest.java 
package ch05; 
import static java.lang.Math.sqrt; //导入静态方法sqrt() 
import static java.lang.Math.pow; //导入静态方法pow() 
class Hypot { 
public static void main(String args[]) { 
double side1, side2; 
double hypot; 
side1=3.0; 
side2=4.0; 
//静态导入后,不再需要通过类名Math 来调用方法sqrt()和pow() 
hypot=sqrt(pow(side1, 2)+pow(side2, 2)); 
System.out.println("给定RT△的两边边长为:" + side1+"和"+ side2+", 其斜
边边长为:"+hypot); 
} 
} 
程序运行结果如下: 
给定RT△的两边边长为: 3.0 和4.0, 其斜边边长为: 5.0

1 28  JavaEE 零基础入门
5.4 final关键字
final关键字可以修饰类、类的成员变量和成员方法,但final的作用不同。
(1)final修饰成员变量,则成员变量成为常量。为了声明一个final变量,可以在类
型之前的变量声明使用final关键字,例如: 
final type piVar=3.14159; 
可以在任何作用域声明一个final变量。修饰成员变量时,定义时同时给出初始
值,而修饰局部变量时不做要求。这个语句声明了一个final变量并对它进行了初始
化。如果在后面还想给piVar赋其他的值,就会导致编译错误,因为final变量的值不能
再改变。
(2)final修饰成员方法,则该方法不能被子类重写。有些方法希望始终保持它在父
类中的定义而不会被子类修改以保证安全,此时可以使用final关键字来阻止方法重写。
例如: 
public final returnType methodName(paramList){ 
… 
}
(3)final修饰类,则类不能被继承。由于安全性的原因或者是面向对象的设计上的
考虑,有时希望一些类不能被继承,例如,Java中的String类,它对编译器和解释器的正
常运行有很重要的作用,不能对它轻易改变,因此把它修饰为final类,使它不能被继承, 
这就保证String类型的唯一性。同时,如果认为一个类的定义已经很完美,不需要再生
成它的子类,这时也应把它修饰为final类,定义一个final类的格式如下: 
final class finalClassName{ 
… 
}
如果一个类被声明为final的,则这个类中的所有方法也自动成为final的。
(4)final修饰引用类型变量,针对引用类型变量的final修饰符也是容易混淆的地
方。实际上final只是修饰引用变量自身的值,如限定类变量保存的实例地址不能变。至
于该变量所引用的对象,内容是否能变,那就管不着了。所以,对于如下语句: 
final StringBuffer strConst=new StringBuffer(); 
可以修改它指向的对象的内容,如: 
strConst.append(" "); 
但是不能修改它的值,如: 
strConst=null;