第3 章Java语言面向对象特性 
本章将介绍三方面内容:①Java语言中类和对象的定义;②Java语言
对OOP(ObjectOrientedProgramming)的3个主要特性———封装、继承
和多态的支持机制;③数组对象这种数据结构。面向对象是Java语言的
基本特性之一,深刻理解这个特性是学好Java语言程序设计的关键。
3.1 类与对象
类描述了同一类对象共同拥有的数据和行为,包含被创建对象的属性
和方法的定义。学习Java语言编程就是学习怎样编写类,也就是怎样利用
Java语言的语法描述一类事物的公共属性和行为。在Java语言中,对象
的属性通过变量描述,对象的行为通过方法实现。方法可以操作属性以形
成一个算法实现一个具体的功能,把属性和方法封装成一个整体就形成了
一个类。
3.1.1 类与对象的定义
Java程序是由一个或若干个类组成的,类是Java程序的基本组成单
位。编写Java程序就是定义类,然后再根据定义的类创建对象。类由成员
变量和成员方法两部分组成,成员变量的类型可以是基本数据类型、数组
类型、自定义类型,成员方法用于处理类的数据。一个Java类从结构上可
以分为类的声明和类体两部分,如图3.1所示。
1.类的声明
类的声明用于描述类的名称以及类的属性(如访问权限、与其他类的
关系等)。声明类的语法如下: 
[public ] [abstract | final ] class < className > [extends 
superClassName][implements interfaceNameList]{...} 
● “[]”:表示可选项,“< >”表示必选项,“|”表示多选一。

50 Java 程序设计———基于JDK 9 和NetBeans 实现
图3.1 类定义的结构示意图 
● public、abstract或final:指定类的访问权限及其属性,用于说明所定义类的相关
特性(后续章节介绍)。
● class:Java语言的关键字,表明这是一个类的定义。
● className:指定类名称的标识符。
● extendssuperClassName:指定所定义的类继承自哪个父类。若使用extends关
键字,则父类名称为必选参数。
● ImplementsinterfaceNameList:指定该类实现哪些接口。当使用implement关键
字时,接口列表为必选参数。
2.类体
类体指的是出现在类声明后面的花括号中的内容。类体提供了类的对象在生命周期
中需要的所有代码———①构造和初始化新对象的构造方法;②表示类及其对象状态的变
量;③实现类及其对象的方法;④进行对象清除的finalize()方法。
3.1.2 成员变量与局部变量
当一个变量的声明出现在类体中,并且不属于任何一个方法时,该变量称为类的成员
变量。在方法体中声明的变量以及方法的参数统称为方法的局部变量。
1.成员变量
成员变量表示类的状态和属性,其声明的语法如下: 
[public | protected | private ][static][final][transient][volative]<type> 
<variableName>; 
● public、protected或private:指定变量的访问权限。

第3 章 Java 语言面向对象特性  51 
● static:指定变量为静态变量(也称为类变量),其特点是可以通过类名直接访问。
如果省略,则表示为实例变量。
● final:指定变量为常量。
● transient:声明变量为暂时性变量,告知JVM 该变量不属于对象的持久状态,从
而不能被持久存储。如果省略,则类中的所有变量都是对象持久状态的一部分,当
对象被保存到外存时,这些变量必须同时被保存。
● volatire:指定变量在被多个并发线程共享时,JVM 将采取优化的控制方法提高线
程的并发执行效率。该修饰符是Java语言的一种高级编程技术,一般程序员很少
使用。
● type:指定变量的数据类型。
● variableName:指定变量的名称。
【例3.1】 在类Apple的定义中,声明3个类的成员变量,并在main()方法中通过输
出它们的值说明其状态特征。 
1 public class Apple { 
2 public String color; //公共变量color 
3 public static int num; //静态变量num 
4 public final boolean MATURE=true; //定义常量MATURE 并赋值
5 public static void main(String[]args) { 
6 System.out.println("苹果的数量: "+Apple.num); 
7 Apple apple=new Apple(); 
8 System.out.println("苹果的颜色: "+apple.color); 
9 System.out.println("苹果是否成熟: "+apple.MATURE); 
10 } 
11 } 
【运行结果】 
E:\Java\JNBExamples>java Apple 
苹果的数量: 0 
苹果的颜色: null 
苹果是否成熟: true 
【分析讨论】
● num 是静态变量(类变量),在运行时JVM 只为类变量分配一次内存,并在加载类
过程中完成其内存分配,所以可以通过类名直接访问(第6行语句)。
● color与MATURE都是实例变量,必须通过创建对象的名称apple访问(第7、8、9 
行语句)。
2.局部变量
局部变量作为方法或语句块的成员,存在于方法的参数列表和方法体的定义中。其
定义的语法如下: 
[final] <type> <变量名字>

52 Java 程序设计———基于JDK 9 和NetBeans 实现
● final:可选项,指定局部变量为常量。
● type:指定局部变量的数据类型,它可以是任意一种Java语言的数据类型。
● 变量名:变量名必须是合法的Java语言标识符。
● 对于类中定义的成员变量,如果没有初始化,那么Java语言将自动给它们赋予一
个初值,即默认初始值。而对于局部变量,在使用之前必须进行初始化,然后才能
使用。
【例3.2】 在类Apple的定义中,解释使用局部变量要注意的问题。 
1 public class Apple { 
2 String color="Red"; //成员变量color,赋初值"Red" 
3 float price; //成员变量price,默认初始值0.0f 
4 public String getColor() { 
5 return color; } 
6 public float count() { 
7 int num; //局部变量num 
8 if(num<0) //错误语句,因为局部变量num 还没有被赋值就使用
9 return 0; 
10 else 
11 return price*num; 
12 } 
13 public static void main(String[]args) { 
14 Apple apple=new Apple(); 
15 System.out.println("苹果总价格: "+apple.count()); 
16 } 
17 } 
【编译结果】 
E:\Java\JNBExamples>javac Apple.java 
Apple.java:8: 错误: 可能尚未初始化变量num 
if(num<0) //错误语句,因为局部变量num 还没有被赋值就使用 
^ 
1个错误。
【分析讨论】
● 在程序的第15行,通过对象apple调用了方法count(),而此时在count()方法中
定义的局部变量num 在使用之前没有进行初始化,所以造成程序编译错误(第8 
行语句)。
3.变量的有效范围
变量的有效范围是指变量在程序中的作用区域,在区域外不能直接访问变量。有效
范围决定了变量的生存周期———指从声明一个变量并分配内存空间、使用变量开始,然后
释放变量并清除所占用内存空间的过程。变量声明的位置,决定了变量的有效范围。根
据变量的有效范围的不同,可以将变量分为成员变量和局部变量两种。

第3 章 Java 语言面向对象特性  53 
● 成员变量:类体中声明的成员变量在整个类的范围内有效。
● 局部变量:在方法内或方法内的语句块(方法内部,“{”与“}”之间的代码块)中声
明的变量称为局部变量。在语句块以外,方法体内声明的变量在整个方法内有效。
【例3.3】 在类Olympics1的定义中,说明成员变量与局部变量的有效范围。 
1 public class Olympics1 { 
2 private int medal_All=800; //成员变量
3 public void China() { 
4 int medal_CN=100; //代码块外、方法体内的局部变量
5 if(medal_CN<1000) { //代码块
6 int gold=50; //代码块的局部变量
7 medal_CN+=30; //允许访问本方法的局部变量
8 medal_All-=130; //允许访问本类的成员变量
9 } //代码块结束
10 } 
11 } 
【分析讨论】
● 在第5行语句中,允许访问类的成员变量medal_All和在方法中定义的局部变量
medal_CN。
3.1.3 成员方法
类的成员方法由方法声明和方法体两部分组成,其语法如下: 
[accessLevel][static][final][abstract][synchronized]< return_type> < name> 
([<argument_list>]) [throws<exception_list>]{ [block] } 
● accessLevel:方法的访问权限,可选值为public、protected与private。
● static:指定成员方法为静态方法。
● final:指定成员方法为最终方法。
● abstract:指定方法为抽象方法。
● synchronized:控制多个并发线程对共享数据的访问。
● return_type:确定方法的返回类型,可以是任意的Java语言数据类型。如果方法
没有返回值,则可以指定为void标识。
● name:成员方法的名称。
● argument_list:形式参数列表。方法可分为有参数和无参数两种。参数类型可以
是Java语言的数据类型。
● throws<exception_list>:列出方法将要抛出的异常。
● block:方法体包括局部变量的声明和所有合法的Java语句。方法体可以省略,但
是外面的一对花括号不能省略。
● 方法体中的局部变量作用域只在方法内部,当方法调用返回时,局部变量也不再
存在。

54 Java 程序设计———基于JDK 9 和NetBeans 实现
● 如果局部变量的名字和所在类的成员变量的名字相同,则类的成员变量被隐藏;如
果要将成员变量显式地表现出来,则需要在成员变量的前面加上关键字this。
【例3.4】 在类Olympics2的定义中,说明在成员变量与局部变量同名的情形下,用
this标识成员变量的方法。 
1 public class Olympics2 { 
2 private int gold=0; 
3 private int silver=0; 
4 private int copper=0; 
5 public void changeModel(int a,int b,int c) { 
6 gold=a; 
7 int silver=b; //silver 使同名类成员变量隐藏
8 int copper=50; //copper 使同名类成员变量隐藏
9 System.out.println("In changeModel: "+"金牌="+ gold+" 银牌="+ silver+" 
铜牌="+copper); 
10 this.copper=c; //给类成员变量copper 赋值
11 } 
12 String getModel() { 
13 return "金牌="+gold+" 银牌="+silver+" 铜牌="+copper; 
14 } 
15 public static void main(String args[]) { 
16 Olympics2 o2=new Olympics2(); 
17 System.out.println("Before changeModel: "+o2.getModel()); 
18 o2.changeModel(100,100,100); 
19 System.out.println("After changeModel: "+o2.getModel()); 
20 } 
21 } 
【运行结果】 
E:\Java\JNBExamples>java Olympics2 
Before changeModel: 金牌=0 银牌=0 铜牌=0 
In changeModel: 金牌=100 银牌=100 铜牌=50 
After changeModel: 金牌=100 银牌=0 铜牌=100 
【分析讨论】
● 在main()方法中,创建了类Olympics2的对象o2。第17行语句通过o2调用了
getModel()方法。getModel()方法中操作的全部是成员变量,而且第2~4行语句
的成员变量进行的是显式初始化,所以得到第一行的输出结果。
● 成员变量silver和copper与方法changeModel()中定义的局部变量同名,如果不
加特殊标识this,则在方法changeModel()中操作的是局部变量silver和copper。
因此,在第18行语句调用changeModel()方法时,得到第三行的输出结果。
● changeModel()方法更新了成员变量gold和copper的值,所以第19行语句在调
用getModel()方法时,能得到第四行的输出结果。

第3 章 Java 语言面向对象特性  55 
注意:return通常放在方法的最后,用于退出当前方法并返回一个值,使程序把控制
权交给调用它的语句。return语句中的返回值必须与方法声明中的返回值类型相匹配。
3.1.4 对象的创建
在Java语言中,对象是通过类创建的,是类的动态实例。一个对象在程序运行期间
的生存周期包括创建、使用和销毁3个阶段。Java语言的对象创建、使用和销毁有一套
完善的机制。在Java语言中,创建一个对象的语法如下: 
<className> <objectName> 
● className:指定一个已经定义的类。
● objectName:指定一个对象的名称。
例如,声明Apple类的一个对象redApple的语句如下: 
Apple redApple; 
声明对象时,只是在内存中为其分配一个引用空间,并设置为null,表示不指向任何
存储空间,然后为对象分配存储空间。这个过程称为对象的实例化。实例化对象使用关
键字new实现,它的语法如下: 
<objectName>=new <SomeClass>([argument_list]); 
● objectName:指定已经声明的对象名称。
● SomeClass:指定需要调用的构造方法名称。
● srgument_list:指定构造方法的入口参数。如果无参数,则可省略。
在声明Apple类的一个对象greenApple后,通过下面的语句可为对象greenApple 
分配存储空间,执行new运算符后的构造方法将完成对象的初始化,并返回对象的引用。
当对象创建不成功时,new运算符将返回null给变量redApple。 
greenApple=new Apple(); 
声明对象时,也可以直接实例化对象,即把上述步骤合二为一。 
Apple greenApple=new Apple(); 
【例3.5】 在类Point中定义两个成员变量,在构造方法中定义具有两个整数的参数
列表。 
1 public class Point { 
2 int x=1; 
3 int y=1; 
4 public Point(int x,int y) { 
5 this.x=x; 
6 this.y=y; 
7 } 
8 }

56 Java 程序设计———基于JDK 9 和NetBeans 实现
如果执行如下语句,则可以创建Point类的对象。 
Point pt=new Point(2,3); 
下面是上述语句的对象创建与初始化过程。
(1)声明一个Point类的对象pt,并为其分配一个引用空间,初始值为null。此时,引
用没有指向任何存储空间,即没有分配存储地址。
(2)为对象分配存储空间,并将成员变量进行默认初始化,数值型变量的初值为0,逻
辑型变量的初值为false,引用型变量的初值为null。
(3)执行显式初始化,即执行在类成员变量声明时带有的简单赋值语句。
(4)执行构造方法,进行对象的初始化。
(5)最后,执行语句中的赋值操作,将新创建对象的存储空间的首地址赋给pt的引
用空间。
图3.2所示为执行上述过程中5个步骤时的对象状态。
图3.2 对象创建与初始化的示意图
3.1.5 对象的使用
创建对象以后,可以通过“.”操作符对成员变量进行访问。访问对象的成员变量的语
法如下: 
objectReference.variableName; 
objectReference:指定调用成员变量的对象名称。
variableName:指定要调用的成员变量的名称。
一般地,不提倡通过对象对成员变量进行直接访问。规范的对象变量访问方式是通
过对象提供的统一接口setter和getter(即成员方法)对变量进行读写操作,其优点是可
以实现变量的正确性、完整性的约束检查。当需要对对象变量进行直接访问时,可以使用
Java语言的访问控制机制,以控制哪些类能够直接对变量进行访问。
调用对象的成员方法的语法如下: 
objectReference.methodName([argument_list]); 
objectReference:指定调用成员方法的对象名称。
methodName:指定要调用的成员方法的名称。

第3 章 Java 语言面向对象特性  57 
argument_list:指定被调用的成员方法的参数列表。
对象的方法可以通过设置访问权限允许或禁止其他对象访问。
【例3.6】 创建Point类的对象pt,访问其成员方法和成员变量。 
1 public class Point { 
2 int x=1; 
3 int y=1; 
4 public void setXY(int x,int y) { 
5 this.x=x; 
6 this.y=y; 
7 } 
8 public int getXY() { 
9 return x*y; 
10 } 
11 public static void main(String[]args) { 
12 Point pt=new Point(); //声明并创建Point 类的对象pt 
13 pt.x=2; //访问对象pt 的成员变量x,并改变其值
14 System.out.println("x 与y 的乘积为: "+pt.getXY()); 
15 pt.setXY(3,2); //调用对象pt 带参数的成员方法setXY() 
16 System.out.println("x 与y 的乘积为: "+pt.getXY()); //调用成员方法getXY() 
17 } 
18 } 
【运行结果】 
E:\Java\JNBExamples>java Point 
x 与y 的乘积为: 2 
x 与y 的乘积为: 6 
【分析讨论】
● 在main()方法中,创建了类Point的对象pt(行12行语句),通过pt修改了x的值
(第13行语句)。
● 第14行语句通过调用方法getXX()输出更新前的执行结果。第15行语句通过访
问setXY()方法,传递参数3和2到变量x和y,即将成员变量更新。最后,再次调
用getXY()输出更新后的执行结果(第16行语句)。
3.1.6 对象的销毁
在Java语言中,程序员可以创建所需要的对象,但是不必关心对象的删除,因为Java 
语言提供了垃圾回收机制,可以自动地判断对象是否还在使用,并能够自动销毁不再使用
的对象,回收对象所占的系统资源。Object类提供了finalize()方法,自定义的Java类可
以覆盖这个方法,并在这个方法中释放对象所占的资源。JVM(Java虚拟机)的垃圾回收
操作的生命周期如图3.3所示。
在JVM 垃圾回收器看来,存储空间中的每个对象都可能处于以下3种状态之一。

58 Java 程序设计———基于JDK 9 和NetBeans 实现
图3.3 JVM 的垃圾回收操作的生命周期
● 可触及状态:当一个对象被创建之后,只要程序中还有引用变量在引用它,那么它
就始终处于可触及状态。
● 可复活状态:当程序不再有任何引用变量对象时,就进入可复活状态。在这个状
态中,垃圾回收器将会释放它占用的存储空间。在释放之前,将会调用它及其处于
可复活状态的对象的finalize()方法。这些finalize()方法有可能使对象重新转到
可触及状态。
● 不可触及状态:JVM 执行完所有可复活对象的finalize()方法之后,假如这些方法
都没有使该对象转到可触及状态,那么该对象将进入不可触及状态。只有当对象
处于不可触及状态时,垃圾回收器才真正回收它占用的存储空间。
3.1.7 方法重载
当在同一个类中定义了多个同名而内容不同的成员方法时,则称这些方法为重载方
法(overloadingmethod)。重载方法通过形式参数列表中参数个数、参数类型和参数顺序
的不同加以区分。在编译阶段,Java语言的编译器要检查每个方法所用的参数数量和类
型,然后调用正确的方法,即实现Java语言的编译时多态。Java语言规定重载方法必须
遵循以下原则。
● 方法的参数列表必须不同,包括参数的个数或类型,以此区分不同的方法体。
● 方法的返回值类型、修饰符可以相同,也可以不同。
● 在实现方法重载时,方法返回值的类型不能作为区分方法重载的标志。
【例3.7】 在类Calculate的定义中,定义两个名称为getArea()的方法(参数个数不
同)和两个名称为draw()的方法(参数类型不同),用以输出不同图形的面积。 
1 public class Calculate { 
2 final float PI=3.14159f;

第3 章 Java 语言面向对象特性  59 
3 public float getArea(float r) { //计算面积的方法
4 return PI*r*r; 
5 } 
6 public float getArea(float l,float w) { //重载方法getArea() 
7 return l*w; 
8 } 
9 public void draw(int num) { //画任意形状的图形
10 System.out.println("画"+num+"个任意形状的图形"); 
11 } 
12 public void draw(String shape) { //画指定形状的图形
13 System.out.println("画一个"+shape); 
14 } 
15 public static void main(String[]args) { 
16 Calculate c=new Calculate(); //创建Calculate 类的对象
17 float l=20; 
18 float w=40; 
19 System.out.println("长为"+l+"宽为"+w+"的矩形面积是: "+c.getArea(l,w)); 
20 float r=6; 
21 System.out.println("半径为"+r+"的圆形面积是: "+c.getArea(r)); 
22 int num=8; 
23 c.draw(num); 
24 c.draw("矩形"); 
25 } 
26 } 
【运行结果】 
E:\Java\JNBExamples>java Calculate 
长为20.0 宽为40.0 的矩形面积是: 800.0 
半径为6.0 的圆形面积是: 113.097244 
画8 个任意形状的图形
画一个矩形
【分析讨论】
● 在第19行语句中调用了getArea()方法,由于传递的实际参数是两个float类型变
量,所以此时对象c调用的是第6~8行语句定义的getArea()方法。
● 在第21行语句中调用的getArea()方法,由于传递的实际参数是一个float类型的
变量r,所以此时对象c调用的是第3~5行语句定义的getArea()方法。
● 在第23行语句中调用的draw()方法,由于传递的实际参数是一个int型变量
num,所以此时对象c调用的是第9~11行语句定义的draw()方法。
● 第24行语句中调用的draw()方法,由于传递的是一个String类型的参数,所以此
时对象c调用的是第12~24行语句中定义的draw()方法。
3.1.8 关键字this 
关键字this表示对象本身,常用于一些容易混淆的情形。例如,当成员方法的形式

60 Java 程序设计———基于JDK 9 和NetBeans 实现
参数名称与数据所在类的成员变量名称相同时,或者当成员方法的局部变量名称与类的
成员变量名称相同时,在方法内部可以借助关键字this指明引用的是类的成员变量,而
不是形式参数或局部变量,从而提高程序的可读性。
this代表了当前对象的一个引用,可以将其理解为对象的另外一个名字,通过这个名
字可以顺利地访问对象,修改对象的数据成员,调用对象的方法。归纳起来,this的使用
情形有如下3种。
● 用来访问当前对象的一个引用,使用格式为:this.数据成员。
● 用来访问当前对象的成员方法,使用格式为:this.成员方法(参数列表)。
● 重载构造方法时,用来引用同类的其他构造方法,使用格式为:this.(参数列表)。
【例3.8】 通过关键字this区别成员变量color和局部变量color,并通过this访问当
前对象的成员方法count()。 
1 public class Fruit { 
2 String color="绿色"; 
3 double price; 
4 int num; 
5 public void harvest() { 
6 String color="红色"; 
7 //此时输出的是成员变量color 
8 System.out.println("水果原来是: "+this.color+"的!"); 
9 System.out.println("水果已经收获!"); 
10 System.out.println("水果现在是: "+color+"的!"); //此时输出的是局部变量
11 //使用this 调用成员方法count() 
12 System.out.println("水果的总价格是: "+this.count(2.14,50)+"元。"); 
13 } 
14 public double count(double price, int num) { 
15 this.price=price; //将形参price 赋值给成员变量price 
16 this.num=num; //将形参num 赋值给成员变量num 
17 return price*num; 
18 } 
19 public static void main(String[]args) { 
20 Fruit obj=new Fruit(); 
21 obj.harvest(); 
22 } 
23 } 
【运行结果】 
E:\Java\JNBExamples>java Fruit 
水果原来是: 绿色的! 
水果已经收获! 
水果现在是: 红色的! 
水果的总价格是: 107.0 元

第3 章 Java 语言面向对象特性  61 
【分析讨论】
● 在方法count(doubleprice,intnum)中,如果不使用this,则作为类的成员变量的
price和num 将被隐藏,将不会得到预期的对象初始化结果。
● 在方法harvest()中,使用this调用方法count()的语句(第12行语句),这个this 
的使用是不必要的。当一个对象的方法被调用时,Java语言会自动给对象的变量
和方法都加上this引用,指向内存中的对象,所以有些情形下不需要使用this关
键字。
3.1.9 构造方法
Java语言中所有的类都有构造方法,用于对象的初始化。构造方法也有名称、参数、
方法体以及访问权限的限制。
1.构造方法的声明
定义构造方法的语法如下所示: 
[accessLevel]<className>([argument_list]) { 
[block] 
}
● accessLevel:指定构造方法的访问权限。
● className:指定构造方法的名称,这个名称必须与所属类的名称相同。
● argument_list:指定构造方法中所需要的参数。
● block:方法体是构造方法的实现部分,包括局部变量的声明和所有合法的Java语
句。当方法体省略时,其外面的一对花括号不能省略。
构造方法与一般方法在声明上的区别如下。
● 构造方法的名字必须与类名相同,并且构造方法不能有返回值。
● 用户不能直接调用构造方法,必须通过关键字new自动调用。
【例3.9】 在类Apple的定义中,声明两个构造方法,并通过这两个构造方法分别创
建两个Apple类的对象。 
1 public class Apple { 
2 private int num; 
3 private double price; 
4 public Apple() { 
5 num=10; 
6 price=2.34; } 
7 public Apple(int num,double price) { 
8 this.num=num; 
9 this.price=price; 
10 } 
11 public void display() { 
12 System.out.println("苹果的数量: "+num); 
13 System.out.println("苹果的单价: "+price);

62 Java 程序设计———基于JDK 9 和NetBeans 实现
14 } 
15 public static void main(String args[]){ 
16 Apple a1=new Apple(); 
17 Apple a2=new Apple(50,3.15) 
18 a1.display(); 
19 a2.display(); 
20 } 
21 } 
【运行结果】 
E:\Java\JNBExamples>java Apple 
苹果的数量: 50 
苹果的单价: 3.15 
【分析讨论】
● 在类Apple的定义中,第4~6行语句定义了1个没有参数的构造方法;第7~10 
行语句定义了含有2个参数的构造方法。
● 在main()方法中,通过这两个构造方法分别创建了两个对象a1、a2。其中,a1的
成员变量的值为10和2;a2的成员变量的值为传递的实际参数50和3.15。然后, 
通过对象a1和a2调用display()方法输出各自成员变量的值,得到上述输出结果。
2.缺省的构造方法
在Java语言中,类可以不定义构造方法,而其他的类仍然可以通过调用无参数的构
造方法实现该类的实例化。这是因为Java语言为每个类都自动提供一个特殊的、不带参
数且方法体为空的缺省构造方法。其语法形式如下。 
public <className>{ } 
● 当用缺省的构造方法初始化对象时,系统将用默认值初始化对象的成员变量。
● 一旦在类中定义了显式的构造方法,无论一个亦或多个,系统都将不再提供缺省的
无参数构造方法。此时如果在程序中使用缺省的构造方法,则会出现编译错误。
3.构造方法的重载
构造方法也可以重载,重载的目的是使类的对象具有不同的初始值,从而为对象的初
始化提供便利。一个类中的若干个构造方法之间还可以相互调用。当一个构造方法需要
调用另一个构造方法时,可以使用关键字this。同时,这条调用语句应该是整个构造方法
的第一条可执行语句,这样可以最大限度地提高已有代码的利用率,减少维护程序的工
作量。
【例3.10】 对类Apple的构造方法进行重载,使用关键字this引用同类的其他构造
方法。 
1 class Apple { 
2 private String color; 
3 private int num; 
4 public Apple(String c,int n) {

第3 章 Java 语言面向对象特性  63 
5 color=c; 
6 num=n; } 
7 public Apple(String c) { 
8 this(c,0); } 
9 public Apple() { 
10 this("Unknown"); } 
11 public String getColor() { 
12 return color; } 
13 public int getNum() { 
14 return num; } 
15 } 
16 public class AppleDemo { 
17 public static void main(String args[]) { 
18 Apple apple=new Apple(); 
19 System.out.println("苹果颜色: "+apple.getColor()); 
20 System.out.println("苹果数量: "+apple.getNum()); } 
21 } 
【运行结果】 
E:\Java\JNBExamples>java AppleDemo 
苹果颜色: Unknown 
苹果数量: 0 
【分析讨论】
● 关键字this用来调用同类的其他构造方法。
● 在main()方法中,第18行语句调用了无参数的构造方法Apple(),它的执行导致
第10行语句调用了含有1个参数的构造方法,而它的执行同样导致第8行语句调
用了含有2个参数的构造方法(第4行语句)。
3.2 封装与数据隐藏
封装是OOP的一个重要特性。一般地,封装是将客户端不应看到的信息包裹起来, 
使内部的执行对外部来看是一种不透明的黑箱,客户端不需要了解内部资源就能够达到
目的。为数据提供良好的封装是保证类设计的基本方法之一。
3.2.1 封装
封装也称数据隐藏,是指将对象的数据与操作数据的方法相结合,通过方法将对象的
数据与实现细节保护起来,只保留一些对外接口,以便与外界发生联系。系统的其他部分
只有通过包裹在数据外面的被授权的操作访问对象,因此封装同时也实现了对象的隐藏。
也就是说,用户无须知道对象的内部方法的实现细节,但可以根据对象提供的外部接口
(对象名和参数)访问对象。封装具有如下特征:

64
Java 
程序设计———基于JDK 9 
和NetBeans 
实现

● 在类的定义中,通过设置访问对象的属性及方法的权限,限制该类的对象及其他类
的对象的使用范围。
● 提供一个接口来描述其他对象的使用方法。
● 其他对象不能直接修改对象所拥有的属性和方法。
封装反映了事物的相对独立性。封装在编程上的作用是使对象以外的部分不能随意
存取对象的内部数据,从而有效地避免了外部错误对它的“交叉感染”。通过封装和数据
隐藏机制,将一个对象相关的变量和方法封装为一个独立的软件体,单独进行实现与维
护,并使对象能够在系统内部方便地进行传递,另外也保证了对象数据的一致性,并使程
序易于维护。OOP 的封装单位是对象。类的概念本身也具有封装的含义,因为对象的特
性是由类描述的。

3.2.2 
访问控制
访问控制是通过在类的定义中使用权限修饰符实现的,以达到保护类的变量和方法
的目的。Java语言支持4种不同的访问权限。

● 私有的:用private修饰符指定。
● 保护的:用protected修饰符指定。
● 共有的:用public修饰符指定。
● 默认的,也称为default或package:不适用于修饰符指定。
访问控制的对象有包、类、接口、类成员和构造方法。除了包的访问控制由系统决定
外,其他的均通过访问控制符实现。访问控制符是一组限定类、接口、类成员是否可以被
其他类访问的修饰符。其中,类和接口只有public和default两种。类成员和构造方法可
以是pulc、piae、poetd和dfut4 种, 1。

birvtrtceeal见表3.

表3.aa语言的类成员的4种访问控制权限及其可见性

1 
Jv

访问空字符
可否直接访问
同一个类中同一个包中不同包中的子类任何场合
private √ 
default √ √ 
protected √ √ √ 
public √ √ √ √ 

1.private 

类中带有private修饰符的成员只能在类的内部使用,在其他类中不允许直接访问。
一般地,把不想让外界访问的数据和方法声明为private,这有利于数据的安全并保证数
据的一致性,也符合编程中隐藏内部信息处理细节的基本原则。

构造方法也可以定义为private。如果一个类的构造方法声明为private,则其他类不
能生成该类的实例对象。一个类不能访问其他类的对象的private成员,但是同一个类两
个对象之间可以相互访问对方的private成员,这是因为访问控制是在类层次上(不同类


第3 章 Java 语言面向对象特性  65 
的所有实例对象),而不是在对象级别上(同一个类的特定实例)。
【例3.11】 在类Parent中,通过成员方法isEqualTo(),验证同一个类的对象之间可
以访问其私有的成员。 
1 class Parent { 
2 private int privateVar; 
3 public Parent(int p) { 
4 privateVar=p; 
5 } 
6 boolean isEqualTo(Parent anotherParent) { 
7 if(this.privateVar==anotherParent.privateVar) 
8 return true; 
9 else 
10 return false; 
11 } 
12 } 
13 public class PrivateDemo { 
14 public static void main(String[]args) { 
15 Parent p1=new Parent(20); 
16 Parent p2=new Parent(40); 
17 System.out.println(p1.isEqualTo(p2)); 
18 } 
19 } 
【运行结果】 
E:\Java\JNBExamples>java PrivateDemo 
false 
【分析讨论】
● 在Parent类中定义一个方法isEqualTo(),用于比较Parent类的当前对象的私有
变量privateVar与另一个对象anotherParent的私有变量privateVar是否相等。
如果相等,则返回true,否则返回false。
● 在测试类PrivateDemo中,虽然p1和p2同为Parent类的对象,但是它们的私有
成员变量p1.privateVar和p2.privateVar的值却不同,分别为20和40,因此,最后
的输出结果为false。
● 该程序说明访问控制是应用于类(class)层次或者类型(type)层次,而不是应用于
对象层次。
2.default 
如果一个类没有显式地设置成员的访问控制级别,则说明它使用的是默认的访问权
限,称为default或package。default权限允许被这个类本身,或者相同包中的类访问其
成员,这个访问级别假设在相同包中的类是相互信任的。对于构造方法,如果不加任何访
问权限,也是default权限,则除这个类本身和同一个包中的类以外,其他类均不能生成该

66 Java 程序设计———基于JDK 9 和NetBeans 实现
类的对象实例。
【例3.12】 在类Parent中定义具有default访问权限的变量和方法。变量和方法属
于p1包,Child类也属于p1包,所以p1包中的其他类也可以访问Parent类的default的
成员变量和方法。 
1 package p1; 
2 class Parent { 
3 int packageVar; 
4 void packageMethod() { 
5 System.out.println("I am packageMethod!"); 
6 } 
7 } 
下面是Child类的定义。 
1 package p2; 
2 class Child { 
3 void accessMethod() { 
4 Alpha a=new Alpha(); 
5 a.packageVar=10; //合法的
6 a.packageMethod(); //合法的
7 } 
8 } 
3.protected 
protected类型的类成员可以被同一个类、同一个包中的其他类以及它的子类访问。
因此,在允许类的子类和相同包中的类访问而禁止其他不相关的类访问时,可以使用
protected修饰符。protected修饰符将子类和相同包中的类看作一个家族,只允许家族成
员之间相互访问,而禁止这个家族之外的类和对象涉足其中。
假设定义了3个类:Parent、Person和Child,Child类是Parent的子类。Parent类和
Person类在包p1中,而Child类在包p2中。Parent类的定义如下: 
1 package p1; 
2 class Parent { 
3 protected int protectedVar; 
4 protected void protectMethod() { 
5 System.out.println("I am protectedMethod!"); 
6 } 
7 } 
因为Person 类与Parent类属于同一个包中,所以其可以访问Parent对象的
protected成员变量和方法。下面是Person类的定义。 
1 package p1; 
2 class Person {

第3 章 Java 语言面向对象特性  67 
3 void accessMethod() { 
4 Parent p=new Parent(); 
5 p.protectedVar=100; 
6 p.protectedMethod(); 
7 } 
8 } 
Child类继承了Parent类,但是却在包p2中。Child类的对象虽然可以访问Parent 
类的protected成员变量和方法,但是只能通过Child类的对象或者它的子类对象访问, 
不能通过Parent类的对象直接对这两个类的protected成员进行访问。因此,Child类的
方法accessMethod()试图通过Parent类的对象p 访问其变量protectedVar和方法
protectedMethod()是非法的,而通过Child类的对象c访问该变量和方法则是合法的。
下面是Child类的定义。 
1 package p2; 
2 import p1.*; 
3 class Child extends Parent { 
4 void accessMethod(Parent p, Child c) { 
5 p.protectedVar=10; //非法的
6 c.protectedVar=10; //合法的
7 p.protectedMethod(); //非法的
8 c.protectedMethod(); //合法的
9 } 
10 } 
【分析讨论】
如果Child类与Parent类属于同一个包,则上述非法语句就是合法的了。
4.public 
带有public修饰符的成员可以被所有的类访问。如果构造方法的访问权限为
public,则所有的类都可以生成该类的实例对象。一般地,一个成员只有在被外部对象使
用后不会产生不良结果时,才会被声明为public。在类中的方法被定义为public,表示该
方法是这个类对外的接口,程序的其他部分可以通过调用它们达到与当前类交换信息、传
递消息甚至影响当前类的目的,从而避免程序的其他部分直接操作这个类的数据。
3.2.3 package 与import 
用面向对象技术开发软件系统时,程序员需要定义许多类并使之共同工作,有些类可
能需要在多处反复被使用。为了使这些类易于查找和使用,避免命名冲突和限定类的访
问权限,程序员可以将一组相关的类与接口包裹在一起形成一个包。包是接口和类的集
合(或称为容器),它将一组类集中到一起。Java语言通过包就可以方便地管理程序中的
类。包(package)的优点主要体现在以下3方面。
● 编程人员可以很容易地确定包中的类是相关的,并且根据所需的功能找到相应
的类。

68 Java 程序设计———基于JDK 9 和NetBeans 实现
● 防止类命名混乱。每个包都创建一个新的命名空间,因此不同包中的相同的类名
不会冲突。
● 控制包中的类、接口、成员变量和方法的可见性。在包中,除声明为private的私有
成员外,类中所有的成员都可以被同一个包中的其他类和方法访问。
1.package语句
包的创建就是将Java程序文件中的接口与类纳入指定的包中。创建包可以通过在
类和接口的Java程序中用package语句实现。package语句的语法如下: 
package pk1[.pk2[.pk3...]]; 
其中,“.”符号代表目录分隔符,pk1是最外层的包,pk2、pk3依次是内层的包。
创建一个包就是在当前文件夹下创建一个子文件夹,存放这个包中包含的所有类的
class文件。Java编译器把包对应于文件系统的目录进行管理,因此包可以嵌套使用,即
一个包中可以含有类的定义,也可以含有子包,其嵌套层数没有限制。
【例3.13】 用关键字package将类Circle打包到com 下的graphics包中。 
1 package com.graphics; 
2 public class Circle { 
3 final float PI=3.14159f; //定义一个用于表示圆周率的常量PI 
4 public static void main(String[]args) { 
5 System.out.println("画一个圆形!"); 
6 } 
7 } 
类Circle 属于com.graphics包,所以该类的名字为com.graphics.Circle。假设
Circle.java保存在E:\JNBExamples\ch03中,而类com.graphics.Circle位于C:\mypkg 
中,那么编译和运行该类的步骤如下: 
(1)将C:\mypkg添加到CLASSPATH 中。
(2)在命令行窗口中将E:\JNBExamples\ch03作为当前目录,输入编译命令javac-d 
c:\mypkgCircle.java,则在当前目录下将产生Circle.class类文件(javac命令中的-d选项
用于指定所产生的类文件的路径)。
(3)在命令行窗口中输入javacom.graphics.Circle,则会得到运行结果“画一个圆”。
【分析讨论】
● package语句必须在Java程序的第一行,该行之前只能有空格和注释。每个Java 
程序中只能有一条package语句,一个类只能属于一个包。
● 如果没有package语句,则Java程序为无名包,此时Java程序保存在当前目录下。
2.import语句
通常,一个类只能引用与它在同一个包中的类。如果要使用其他包中的public类, 
则可以用以下两种方式。
● 导入包中的类。在每个要导入的类的名称前加上完整的包名。
● 使用import语句导入包中的类。其语法如下: