文章目录
- 2. Java的类、包和接口
- 2.1 类、字段、方法
- 2.1.1 类(class)
- 2.1.2 字段(field)和方法(method)
- 2.1.3 构造方法
- 2.1.3 默认构造方法
- 2.1.4 使用对象
- 2.1.5 方法重载(overload)
- 2.1.6 this的使用
- 2.2 类的继承
- 2.2.1 继承
- 2.2.2 继承的好处
- 2.2.3 派生子类
- 2.2.4 继承关系
- 2.2.5 字段
- 2.2.6 方法
- 2.3 super的使用
- 2.3.1 使用super访问父类的域和方法
- 2.3.2 使用父类的构造方法
- 2.4 父类对象与子类对象的转换
- 2.5 包
- 2.5.1 package
- 2.5.2 package语句
- 2.5.3 import语句
- 2.5.4 编译和运行包的类
- 2.5.5 CLASSPATH
- 2.6 访问控制符
- 2.6.1 修饰符
- 2.6.2 成员的访问控制符
- 2.6.3 类的访问控制符
- 2.6.3 setter与getter
- 2.7 其它修饰符
- 2.7.1 非访问控制符
- 2.7.2 static字段
- 2.7.3 final
- 2.7.4 abstract
- 2.8 接口
- 2.8.1 接口(interface)
- 2.8.2 接口的作用
- 2.8.3 接口的实现
- 2.8.4 接口类型
- 2.8.5 接口中的常量
- 2.8.6 枚举
- 2.8.7 Java8中的接口
2. Java的类、包和接口
2.1 类、字段、方法
2.1.1 类(class)
- 类是组成Java程序的基本要素
- 是一类对象的原型
- 它 封装 了一类对象的状态和方法
它将 变量与函数 封装到一个类中
2.1.2 字段(field)和方法(method)
class Person {
String name;
int age;
void sayHello(){
System.out.println("Hello! My name is" + name );
}
}
- 字段(field)是类的属性,是用 变量 来表示的。
字段又称为域、域变量、属性、成员变量等
- 方法(method)是类的功能和操作, 是用 函数 来表示的
2.1.3 构造方法
- 构造方法(constructor)是一种特殊的方法。
- 用来初始化(new)该类的一个新的对象。
- 构造方法和类名同名,而且不写返回数据类型。
Person( String n, int a ){
name = n;
age = a;
}
2.1.3 默认构造方法
- 一般情况下,类都有一个至多个构造方法。
- 如果没有定义任何构造方法 ,系统会自动产生一个构造方法,称为默认构造方法(default constructor)。
- 默认构造方法不带参数,并且方法体为空。
2.1.4 使用对象
访问对象的字段或方法,需要用算符“.” :
Person p = new Person();
System.out.println( p.name );
p.sayHello();
这种使用方式的好处
2.1.5 方法重载(overload)
- 方法重载(overloading):多个方法有相同的名字,编译时能识别出来。
- 这些方法的 签名(signature) 不同,或者是参数个数不同,或者是参数类型不同。
- 通过方法重载可以实现多态(polymorphism) 。
2.1.6 this的使用
-
在方法及构造方法中,使用this来访问字段及方法
例如,方法sayHello中使用name和使用this.name是相同的。即:
void sayHello(){
System.out.println("Hello! My name is " + name );
}
与
void sayHello(){
System.out.println("Hello! My name is " + this.name );
}
-
使用this解决局部变量与域同名的问题
使用this还可以解决局部变量(方法中的变量)或参数变量与域变量同名的问题。如,在构造方法中,经常这样用:
Person( int age, String name ) {
this.age = age;
this.name = name;
}
这里,this.age表示域变量,而age表示的是参数变量。
-
构造方法中,用this调用另一构造方法
构造方法中,还可以用this来调用另一构造方法。如:
Person( )
{
this( 0, "" );
……
}
在构造方法中调用另一构造方法,则这条调用语句必须放在第一句。
2.2 类的继承
2.2.1 继承
- 继承(inheritance)是面向对象的程序设计中最为重要的特征之一
- 子类(subclass),父类或超类(superclass)
父类包括所有直接或间接被继承的类
- Java支持单继承:一个类只能有一个直接父类。
2.2.2 继承的好处
- 子类继承父类的状态和行为
- 可以修改父类的状态或重载父类的行为
- 可以添加新的状态和行为。
- 好处
- 可以提高程序的抽象程度
- 实现代码重用,提高开发效率和可维护性
2.2.3 派生子类
Java中的继承是通过extends关键字来实现的
class Student extends Person {
……
}
如果没有extends子句,则该类默认为java.lang.Object的子类。所有的类都是通过直接或间接地继承java.lang.Object得到的。
2.2.4 继承关系
继承关系在UML图中,是用一个箭头来表示子类与父类的关系的。
相当于 is a
类Student从类Person继承,定义如下:
class Student extends Person {
String school;
int score;
boolean isGood(){ return score>80; }

2.2.5 字段
- 字段的继承
- 子类可以继承父类的所有字段
- Student自动具有Person的属性(name,age)
- 字段的隐藏
子类重新定义一个与从父类那里继承来的域变量完全相同的变量,称为域的隐藏。域的隐藏在实际编程中用得较少。
- 字段的添加
在定义子类时,加上新的域变量,就可以使子类比父类多一些属性。如:class Student extends Person
{
String school;
int score;
}
2.2.6 方法
- 方法的继承
父类的非私有方法也可以被子类自动继承。如,Student自动继承Person的方
法。
- 方法的覆盖(Override)(修改)
子类也可以重新定义与父类同名的方法,实现对父类方法的覆盖(Override)。
- 方法的添加
子类可以新加一些方法,以针对子类实现相应的功能。如,在类Student中,加入一个方法,对分数进行判断boolean isGoodStudent(){
return score>=90;
}
- 方法的重载
一个类中可以有几个同名的方法,这称为方法的重载(Overload)。
同时,还可以重载父类的同名方法。与方法覆盖不同的是,重载不要求参数
类型列表相同。重载的方法实际是新加的方法。
如,在类Student中,重载一个名为sayHello的方法:void sayHello( Student another ){
System.out.println("Hi!");
if( school .equals( another.school ))
System.out.println(" Shoolmates ");
}
2.3 super的使用
2.3.1 使用super访问父类的域和方法
注意:正是由于继承,使用this可以访问父类的域和方法。但有时为了明确地指明父类的域和方法,就要用关键字super。
例如:父类Student有一个域age,在子类Student中用age, this.age, super.age来访问age是完全一样的:
void testThisSuper(){
int a;
a = age;
a = this.age;
a = super.age;
}
当然,使用super不能访问在子类中添加的域和方法。
有时需要使用super以区别同名的域与方法
- 使用super可以访问被子类所隐藏了的同名变量。
- 又如,当覆盖父类的同名方法的同时,又要调用父类的方法,就必须使用super。如:
void sayHello(){
super.sayHello();
System.out.println( "My school is " + school );
}
• 在覆盖父类的方法的同时,又利用已定义好的父类的方法。
2.3.2 使用父类的构造方法
构造方法是不能继承的
比如,父类Person有一个构造方法Person(String, int),不能说子类Student也自动有一个构造方法Student(String, int)。
但是,子类在构造方法中,可以用super来调用父类的构造方法。
Student(String name, int age, String school ){
super( name, age );
this.school = school;
}
使用时,super()必须放在第一句
2.4 父类对象与子类对象的转换
类似于基本数据类型数据之间的强制类型转换,存在继承关系的父类对象和
子类对象之间也可以在一定条件下相互转换。
- 子类对象可以被视为其父类的一个对象
如一个Student对象也是一个Person对象。
- 父类对象不能被当做其某一个子类的对象。
- 如果一个方法的形式参数定义的是父类对象,那么调用这个方法时,可以使用子类对象作为实际参数。
- 如果父类对象引用指向的实际是一个子类对象,那么这个父类对象的引用可以用强制类型转换(casting)成子类对象的引用。
2.5 包
2.5.1 package
- package pkg1[.pkg2[.pkg3…]];
- 包及子包的定义,实际上是为了解决名字空间、名字冲突
它与类的继承没有关系。事实上,一个子类与其父类可以位于不同的包中。
- 包有两方面的含义
一是名字空间、存储路径(文件夹)、
一是可访问性(同一包中的各个类,默认情况下可互相访问)
2.5.2 package语句
- 包层次的根目录是由环境变量CLASSPATH来确定的。
- 在简单情况下,没有package语句,这时称为无名包(unnamed package),在Eclipse中,也叫(default package)。
- Java的JDK提供了很多包
java.applet,java.awt,java.awt.image,java.awt.peer,java.io,
java.lang,java.net,java.util,javax.swing,等。
2.5.3 import语句
2.5.4 编译和运行包的类
- 使用javac可以将.class文件放入到相应的目录,只需要使用一个命令选项-d来指明包的根目录即可。
- javac -d d:\tang\ch04 d:\tang\ch04\pk\TestPkg.java
- javac -d . pk*.java
- 其中,“.”表示当前目录
- 运行该程序,需要指明含有main的类名:
java pk.TestPkg
2.5.5 CLASSPATH
- 在编译和运行程序中,经常要用到多个包,怎样指明这些包的根目录呢?
简单地说,包层次的根目录是由环境变量CLASSPATH来确定的。具体操作
有两种方法。
- 一是在java及javac命令行中,用-classpath(或-cp)选项来指明,如:
- java –classpath d:\tang\ch04;c:\java\classes;.
pk.TestPkg
- 二是设定classpath环境变量,用命令行设定环境变量,如:
set classpath= d:\tang\ch04;c:\java\classes;.
- 在Windows中还可以按第2章中的办法设定环境变量。
2.6 访问控制符
2.6.1 修饰符
- 修饰符(modifiers)分为两类
- 访问修饰符(access modifiers)
如public/private等
- 其他修饰符
如abstract等
- 可以修饰类、也可以修饰类的成员(字段、方法)
2.6.2 成员的访问控制符

2.6.3 类的访问控制符
在定义类时,也可以用访问控制符。
类的访问控制符或者为public,或者默认。
2.6.3 setter与getter
将字段用private修饰,从而更好地将信息进行封装和隐藏。
setXXXX和getXXXX方法对类的属性进行存取,分别称为setter与getter。
这种方法有以下优点
- 属性用private更好地封装和隐藏,外部类不能随意存取和修改。
- 提供方法来存取对象的属性,在方法中可以对给定的参数的合法性进行检验。
- 方法可以用来给出计算后的值。
- 方法可以完成其他必要的工作(如清理资源、设定状态,等等)。
- 只提供getXXXX方法,而不提供setXXXX方法,可以保证属性是只读的
以下为示例程序
class Person2
{
private int age;
public void setAge( int age ){
if (age>0 && age<100) this.age = age;
}
public int getAge(){
return age;
}
}
2.7 其它修饰符
2.7.1 非访问控制符

2.7.2 static字段
- 静态字段最本质的特点是:它们是类的字段,不属于任何一个对象实例。
- 它不保存在某个对象实例的内存区间中,而是保存在类的内存区域的公共存储单元。
- 类变量可以通过类名直接访问,也可以通过实例对象来访问,两种方法的结果是相同的。
- 如System类的in和out对象,就是属于类的域,直接用类名来访问,即System.in和System.out。
以下为示例程序:
在类Person中可以定义一个类域为totalNum:
class Person {
static long totalNum;
int age;
String Name;
}
totalNum代表人类的总人数,它与具体对象实例无关。可以有两种方法来
访问:Person.totalNum和p.totalNum (假定p是Person对象)。
在一定意义上,可以用来表示全局变量
- 用static修饰符修饰的方法仅属于类的静态方法,又称为类方法。与此相对,不用static修饰的方法,则为实例方法。
- 类方法的本质是该方法是属于整个类的,不是属于某个实例的。
声明一个方法为static有以下几重含义:
- 非static的方法是属于某个对象的方法,在这个对象创建时,对象的方法在内存中拥有自己专用的代码段。而static的方法是属于整个类的,它在内存中的代码段将随着类的定义而进行分配和装载,不被任何一个对象专有。
- 由于static方法是属于整个类的,所以它不能操纵和处理属于某个对象的成员变量,而只能处理属于整个类的成员变量,即static方法只能处理本类中的static域或调用static方法。
- static方法中,不能访问实例变量,不能使用this 或super。
- 调用这个方法时,应该使用类名直接调用,也可以用某一个具体的对象名。
例如:Math.random(),Integer.parseInt()等就是类方法,直接用类名进行访问。
2.7.2.1 import static
如果在程序开头,有如下语句:
import static java.lang.System.*;
那么在程序中,我们可以直接使用:
out.println();
2.7.3 final
- final类
如果一个类被final修饰符所修饰和限定,说明这个类不能被继承,即不可能有子类。
- final方法
final修饰符所修饰的方法,是不能被子类所覆盖的方法。
- final字段及final局部变量
- final字段、final局部变量(方法中的变量)
它们的值一旦给定,就不能更改。
是只读量,它们能且只能被赋值一次,而不能被赋值多次。
- 一个字段被static final两个修饰符所限定时,它可以表示常量,
如Integer. MAX_VALUE(表示最大整数)、Math.PI(表示圆周率)就是这种常量。
- 关于赋值
- 在定义static final域时,若不给定初始值,则按默认值进行初始化(数值为0,boolean型为false,引用型为null)。
- 在定义final字段时,若不是static的域,则必须且只能赋值一次,不能缺省。
这种域的赋值的方式有两种:一是在定义变量时赋初始值,二是在每一个构造函数中进行赋值。
- 在定义final局部变量时,也必须且只能赋值一次。它的值可能不是常量,但它的取值在变量存在期间不会改变。
2.7.4 abstract
- abstract类
凡是用abstract修饰符修饰的类被称为抽象类。
抽象类不能被实例化
- abstract方法
- 被abstract所修饰的方法叫抽象方法,抽象方法的作用在为所有子类定义一个统一的
接口。对抽象方法只需声明,而不需实现,即用分号(;)而不是用{},格式如下:
abstract returnType abstractMethod( [paramlist] );
- 抽象类中可以包含抽象方法,也可以不包含abstract方法。但是,一旦某个类中包含
了abstract方法,则这个类必须声明为abstract类。
- 抽象方法在子类中必须被实现,否则子类仍然是abstract的。
2.8 接口
2.8.1 接口(interface)
接口,某种特征的约定
- 定义接口 interface
所有方法都自动是public abstract的
- 实现接口 implements
可以实现多继承
与类的继承关系无关
2.8.2 接口的作用
-
通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。从而在一定意义上实现了多重继承。
-
通过接口可以指明多个类需要实现的方法。
-
通过接口可以了解对象的交互界面,而不需了解对象所对应的类。
-
通常接口以able或ible结尾,表明接口能完成一定的行为。
接口声明中还可以包括对接口的访问权限以及它的父接口列表。完整的接口声明如下:
[public] interface interfaceName [extends listOfSuperInterface]{
……
}
- 其中public指明任意类均可以使用这个接口,缺省情况下,只有与该接口定义在同一个包
中的类才可以访问这个接口。
- extends 子句与类声明中的extends子句基本相同,不同的是一个接口可以有多个父接口,
用逗号隔开,而一个类只能有一个父类。子接口继承父接口中所有的常量和方法。
-
方法定义的格式为:
returnType methodName ( [paramlist] );
例如:
在java中 (double) Math.pow(double x,double y) 方法的返回值类型 (returnType) 是double,方法名称 (methodName) 是Math.pow,参数 (paramlist) 是x和y
接口中只进行方法的声明,而不提供方法的实现,所以,方法定义没有方法体,且用分号(;)结尾。在接口中声明的方法具有public 和abstract属性。所以定义的时候这两个关键词是可以省略的。另外,如果在子接口中定义了和父接口同名的常量或相同的方法,则父接口中的常量被隐藏,方法被重载。
2.8.3 接口的实现
在类声明中用implements子句来表示一个类使用某个接口,在类体中可以使用接口中定义的常量,而且必须实现接口中定义的所有方法。一个类可以实现多个接口。
下面我们在类FIFOQueue中实现上面所定义的接口collection:
class FIFOQueue implements collection{
public void add ( Object obj ){
……
}
public void delete( Object obj ){
……
}
public Object find( Object obj ){
……
}
public int currentCount{
……
}
}
在类中实现接口所定义的方法时,方法的声明必须与接口中所定义的完全一致。
2.8.4 接口类型
- 接口可以作为一种引用类型来使用。任何实现该接口的类的实例都可以存储在该接口类型的变量中,通过这些变量可以访问类所实现的接口中的方法。Java运行时系统动态地确定该使用哪个类中的方法。
- 把接口作为一种数据类型可以不需要了解对象所对应的具体的类,以前面所定义的接口Collection和实现该接口的类FIFOQueue为例,下例中,我们以Collection作为引用类型来使用。
publc static void main( String args[] ){
Collection c = new FIFOQueue();
c.add( obj );
……
}
2.8.5 接口中的常量
接口体中可以包含常量定义的格式为:
type NAME = value;
其中type可以是任意类型,NAME是常量名,通常用大写,value是常量值。
在接口中定义的常量可以被实现该接口的多个类共享,它与 C中用#define以及C++中用const定义的常量是相同的。
在接口中定义的常量具有public, static, final的属性。
2.8.6 枚举
从JDK1.5起,可以使用枚举
enum Light { Red, Yellow, Green }
使用
Ligth light = Light.Red;
switch( light ) { case Red: ….. Break; }
2.8.7 Java8中的接口
Java8以上,接口成员还可以是:
- static方法
- 具有实现体的方法 (default方法)
默认方法的好处是:提供了一个默认实现,子类在implements可以不用再重新写了