Lesson 9,Extending Classes
类的继承
LiFan(李凡)
Chengdu University of Information Technology
The Department Of Computer Science
2005
Class Relationships (类的关系 )
? 继承关系 (Inheritance Relationships)
如何让一个类继承另一个类的成员
? 接口关系 (Interfaces)
如何让一个类支持特定的行为
? 包级关系 (Packaging)
如何将类的对象构成逻辑分组
? 嵌套关系 (Inner Classes)
如何将一个类的声明嵌入另一个类的声明中
Why need extending classes?
? 对真实世界中对象继承关系的支持,满
足面向对象建模技术的需要
? 对面向对象程序设计语言中对象多态性
(Polymorphism)的实现
? 代码复用的一种形式
How to extend a class?
? 如果要声明类 B继承类 A,则必须满足两个条件,
(1) 类 A非 final (2) 类 A是 public或类 B与类 A同包,
如果满足条件则可按照以下的语法声明类 B,
<修饰符 > class B extends A { …… }
? 类 B称为类 A的子类 (subclass),类 A称为类 B的父类
或基类 (superclass),称类 A的父类或父类的父类为
类 B的祖先 (ancestor)
? 子类将拥有在其父类和祖先类中声明的所有 public
或 protected的成员
class Animal{
public float weight;
public void eat(){…}
}
class Mammal extends Animal{
public int heartRate;
public void breath(){…}
}
class Cat extends Mammal{
boolean longHair;
public void purr(){…}
}
Cat mycat=new Cat();
mycat.weight=5.5;
mycat.heartRate=80;
mycat.longHair=true;
mycat.eat();
mycat.breath();
mycat.purr();
Classes and Objects in JVM
? 在 JVM中类被表示为一块内存区域,分别存放 类
名 和 类的静态成员,可以用以下的图形表示,
SomeClass
static field1
static method1
static field2
static field3
static method2
类名
域
区
方
法
区
class A{
static int sfa=100;
static int sfb=1000;
static void sma(){…}
int fa=10;
int fb=100;
void ma(){…}
}
A
sfb
sma
1000
……
……
sfa 100
Classes and Objects in JVM
? 在 JVM中对象也被表示为一块内存区域,分别存
放 到对象所属类的引用 和 对象的成员,可以用以
下的图形表示,
:classRef
field1
method1
field2
field3
method2
类的
引用
域
区
方
法
区
A ra1=new A();
A ra2=ra1;
ra1
ra2
A
:A
fb
ma
100
……
……
fa 10
class B extends A{
static int sfb=100;
int fb=10;
void ma(){…}
}
A ra1=new A();
B rb1=new B();
ra1=rb1;
B
A/sfb
sma
1000
……
……
A/sfa 100
B/sfb 100
A
A/sfb
sma
1000
……
……
A/sfa 100
:B
A/fb
ma
100
……
……
A/fa 10
B/fb 10
:A
fb
ma
100
……
……
fa 10
ra1
rb1
Constructors in Subclasses
? 子类对象的成员初始化之前必须完成父类或祖
先类对象的成员的初始化,初始化的方式有两
种,隐式初始化 和 显式初始化
? 隐式初始化,当 父类提供了默认的构造函数,
且子类的构造函数中没有显式调用父类的其它
构造函数,则在执行子类的构造函数之前会自
动执行父类的构造函数,直到执行完 Object的
构造函数后才执行子类的构造函数
class A{
int a,b;
A(){
a=100;
}
}
class B extends A{
int c,d;
B(){
c=-100;
}
}
B rb=new B();
new B() A Object
a=100;
……
c=-100;
c=0;
d=0;
a=0;
b=0;
调用 A()
调用 Object()
返回
返回
Constructors in Subclasses
? 显式初始化,可以在子类构造函数的 第一条
语句 通过 super()或 super(…) 调用父类的默认
构造函数或特定签名的构造函数
? 当父类没有提供默认构造函数时,必须在子
类构造函数第一条语句通过 super(…) 完成父
类对象成员的初始化工作
class A{
int a,b;
A(int c,int d){
a=c;
b=d;
}
}
class B extends A{
int c;
B(int i){
c=i;
}
}
错误
B(int i,int j,int k){
super(i,j);
c=k;
}
Reference Types Compatibility
? 赋值兼容性规则,
(1) 对引用类型的变量进行赋值操作时,赋
值号右边的表达式的类型必须与赋值号左边
的对象引用类型相同或是其子类型
(2) 方法的 return语句返回的结果的类型必须
与方法声明中的返回类型相同或是其子类型
? Null对象引用与所有的引用类型兼容,所有
引用类型的变量都可被赋值为 null
B rb=new B();
A ra1=rb;
A ra2=new B();
Object aobj=ra1;
Object strobj=new String(“good”);
Object intobj=new Integer(100);
Reference Types Casting
? 引用类型间的类型转换 (Conversion)又称为造型
(Casting),可以分为 向上造型 (Upcasting)和 向下造
型 (Downcasting)两种情况
? 向上造型 又称为,Widening Conversion”,是指将
子类类型的引用赋值给其父类或祖先类类型的引
用,赋值操作中包含了隐式的类型转换
? 向下造型 又称为,Narrowing Conversion”,是指
将某类类型的引用赋值给其子类类型的引用,赋
值操作必须进行显式的类型转换
注意:向下造型可能存在一定的类型安全风险
Object obj1=new String(“good”);
Object obj2=new Integer(100);
String str1=(String)obj1;
Integer int1=(Integer)obj2;
Integer int2=(Integer)obj1;
向上造型
向下造型
造型错误
Testing for Type
? 运算符 instanceof 用于判断对象所属的类型
? Null对象引用虽然与所有引用类型兼容,但
null不是任何类型的实例,因此表达式,
null instanceof <类名 > == false
Object obj1=new String(“good”);
Integer int2=null;
if(obj1 instanceof Intger)
int2=(Integer)obj1;
Overriding Methods (方法覆盖 )
? 方法覆盖 是指在子类中替换父类或祖先类对某个
方法的实现
? 方法覆盖通过子类重新定义与父类或祖先类中具
有 同样签名 和 返回类型 的方法实现
? 覆盖的方法可以保持和父类的方法相同的可访问
范围,也可以修改访问控制修饰符增大可访问范
围,但是不能减小可访问范围
private package protected public
Overriding Methods (方法覆盖 )
? 被 final修饰的方法不允许在子类中覆盖
? 其它修饰符,如 synchronized,strictfp,native可以
在覆盖的方法声明中自由使用
? 父类被覆盖的方法的参数列表中被声明为 final的参
数在子类的覆盖方法的中可以不必指定为 final
? 子类覆盖方法声明的异常列表中的异常类必须与
父类被覆盖方法声明的异常列表中的异常类兼容
(相同或是其子类 )
? 只有在子类类体中可以访问的父类或祖先类的方
法才能被覆盖
Hiding Fields (隐藏域 )
? 域不能被覆盖,只能被隐藏,子类可以通过
声明与父类或祖先类中的域同名的域来实现
对父类或祖先类中的域的隐藏
? 要访问父类或祖先类中被隐藏的域必须通过
super引用,即 super.hiddenField
? 当通过对象引用访问对象的域的时,域的值
由对象引用所属的类型决定,并不由被引用
的对象的实际类型决定
class ShowTest{
public static void main(String[] args){
ExtendShow ext=new ExtendShow();
SuperShow sup=ext;
sup.show();
ext.show();
System.out.println(“sup.str=,+sup.str);
System.out.println(“ext.str=,+ext.str);
}
}
class SuperShow{
public String str=“SuperStr”;
public void show(){
System.out.println(“Super.show:,+str);
}
} class ExtendShow extends SuperShow{
public String str=“ExtendStr”;
public void show(){
System.out.println(“Extend.show:,+str);
}
}
java ShowTest
Extend.show= ExtendStr
Extend.show= ExtendStr
sup.str= SuperStr
ext.str= ExtendStr
Hiding Fields (隐藏域 )
? 类的静态成员 (域或方法 )不能被覆盖,但能
被子类声明的静态成员隐藏
? 访问类的静态成员,一般按照以下形式,
类名,静态域
类名,静态方法
super 引用
? 保留字 super可以在任意的非静态方法中使用
? super可以看作当前对象实例所对应的父类型
的实例引用,通过 super引用可以访问属于父
类实例的域和方法,通常这些域和方法被当
前类隐藏或被覆盖了
class That{
protected String nm(){
return,That”;
}
}
class More extends That{
protected String nm(){
return,More”;
}
protected void printNM(){
That sref=(That)this;
System.out.println(“this.nm() =,+this.nm());
System.out.println(“sref.nm() =,+sref.nm());
System.out.println(“super.nm() =,+super.nm());
}
public static void main(String[] args){
More more=new More();
more.printNM();
}
}
java More
this.nm() = More
sref.nm() = More
super.nm() = That
final 的用途
? 被 final修饰的类不能被继承,被 final修饰的
方法不能被覆盖
? 使用 final修饰类和方法可以提高安全性
? 使用 final修饰类和方法可以简化优化过程
? 使用 final修饰类和方法可以加快某些类型检
查过程
Abstract Classes and Methods
? 被 abstract修饰的类称为抽象类,被 abstract修饰的
方法称为抽象方法
? 抽象类不能被实例化,即不能使用抽象类的构造
函数创建对象,抽象类中可以声明抽象方法,除
此以外抽象类与一般的类没有区别
? 抽象类只定义了类的部分行为,这些行为是子类
共有的,其它行为由子类实现的抽象方法提供,
因此抽象类通常作为一个框架,把子类将实现的
抽象方法组织起来,简化或限制子类的设计
abstract class Benchmark{
abstract void benchmark();
public final long repeat(int count){
long start=System.currentTimeMillis();
for(int i=0;i<count;i++)
benchmark();
return (System.currentTimeMillis()-start);
}
}
class Method extends Benchmark{
void benchmark(){
……
}
public static void main(String[] args){
int count=Integer.parseInt(args[0]);
long time=new Method().repeat(count);
System.out.println(count+, methods in,+
time+“milliseconds”);
}
}
java.lang.Object 类
? Object类作为 Java整个类层次结构树的根节
点,是所有类的父类或祖先类
? Object类型的变量可以引用任何类的对象
? Object类定义了一些被所有类的对象继承的
方法,这些方法可以分为两大类,
(1) 通用工具方法
(2) 支持多线程的方法
java.lang.Object 类
? Object类定义的通用方法,
public boolean equals(Object obj)
public int hashCode()
protected Object clone()
public final Class getClass()
protected void finalize()
public String toString()
equals方法和 hashCode方法
? equals方法 用于比较两个对象,默认的实现是比较
两个对象引用是否引用了同一个对象
? hashCode方法 用于返回一个特定对象的散列码值,
不同的对象应该有不同的散列码值,便于将对象
存放到 java.util.HashMap或 java.util.Hashtable等依赖
散列码值存放对象的集合中
? 如果要在子类中覆盖 equals方法,为子类实现特定
的比较机制,则应同时覆盖 hashCode方法,保证
不同的对象具有不同的散列码值
class Student{
private String sno,name;
public Student(String no,String nm){
sno=no;
name=nm;
}
public boolean equals(Object obj){
if(!(obj instanceof Student))
return false;
Student other=(Student)obj;
return sno.equals(other.sno) && name.equals(other.name)
}
public int hashCode(){
int hc=sno.hashCode();
return hc*19+name.hashCode();
}
}
class StudentComparision{
public static void main(String[] args){
Student stu1=new Student(“1001”,“Zhangsan”);
Student stu2=new Student(“1001”,“Zhangsan”);
System.out.println(stu1==stu2);
System.out.println(stu1.equals(stu2));
}
}
java StudentComparision
false
true
Packaging Classes
? 在 Java源程序文件的第一行使用 package声明可以
使文件中定义的类成为指定包的成员
? package声明的语法如下,
package <包名 >;
? 包名通常由多个名字字符串构成,中间用句点,.”
分隔,每个名字表示的包称为其前面的名字表示
的包的子包,通常以组织机构的域名反转形式作
为其所有包的通用前缀,如
com.somecompany.apps
Packaging Classes
? 包有以下用途,
(1) 将相关的类和接口分组,包名通常表示
包中各个类和接口的用途
(2) 包创建了不同的命名空间 (Namespace),
从而有效地解决了类型命名冲突问题
(3) 包提供了对应用程序内部实现机制的保
护域
编译指定了包的类
? 如果待编译的类的源程序中使用了 package
声明指定了类所属的包,则必须使用 javac
命令的 -d选项来进行编译
javac –d <指定路径 > <类文件名列表 >
当指定路径为,.”时表示当前路径
package test;
class StudentComparision{
public static void main(String[] args){
Student stu1=new Student(“1001”,“Zhangsan”);
Student stu2=new Student(“1001”,“Zhangsan”);
System.out.println(stu1==stu2);
System.out.println(stu1.equals(stu2));
}
}
javac –d, StudentComparision.java
./test/SudentComparision.class
Type Imports
? 在类名前面加上类所属的包名,中间用句点,.”分
隔,称为 类的完全限定名 (Full Qualified Name),
简称类的限定名
? 当在类体中使用了与当前类不同包的类名时,编
译器编译时因为无法找到该类的定义而失败,有
两个解决办法,
(1) 使用不同包类的完全限定名
(2) 使用 import声明,为编译器提供该类的定义信
息
Type Imports
? import声明 一般紧跟在 package声明之后,必须在类
声明之前,其基本语法如下,
import <类的完全限定名 >;
? Java语言核心包 java.lang包中的类将被隐式导入,可
以直接使用其中的类
? import声明提供了一种包的智能导入方式,
import <包名 >.*
包中的类将根据需要导入,避免使用多条 import声明
package lecture.java.lesson9;
class Student{
……
}
package lecture.java.lesson10;
class StudentComparision{
public static void main(String[] args){
Student stu1=new Student(“1001”,“Zhangsan”);
Student stu2=new Student(“1001”,“Zhangsan”);
System.out.println(stu1==stu2);
System.out.println(stu1.equals(stu2));
}
}
import lecture.java.lesson9.Student;
Assignments (作业 )
? 通过阅读课本第 3章第 6小节,谈谈你对
内联 (inlining)机制的理解
? 通过阅读课本第 13章第 2小节,说明编
译器通过类名搜索类的定义信息的过程
? 编程,P69 练习 3.1
P85 练习 3.4
P89 练习 3.7
类的继承
LiFan(李凡)
Chengdu University of Information Technology
The Department Of Computer Science
2005
Class Relationships (类的关系 )
? 继承关系 (Inheritance Relationships)
如何让一个类继承另一个类的成员
? 接口关系 (Interfaces)
如何让一个类支持特定的行为
? 包级关系 (Packaging)
如何将类的对象构成逻辑分组
? 嵌套关系 (Inner Classes)
如何将一个类的声明嵌入另一个类的声明中
Why need extending classes?
? 对真实世界中对象继承关系的支持,满
足面向对象建模技术的需要
? 对面向对象程序设计语言中对象多态性
(Polymorphism)的实现
? 代码复用的一种形式
How to extend a class?
? 如果要声明类 B继承类 A,则必须满足两个条件,
(1) 类 A非 final (2) 类 A是 public或类 B与类 A同包,
如果满足条件则可按照以下的语法声明类 B,
<修饰符 > class B extends A { …… }
? 类 B称为类 A的子类 (subclass),类 A称为类 B的父类
或基类 (superclass),称类 A的父类或父类的父类为
类 B的祖先 (ancestor)
? 子类将拥有在其父类和祖先类中声明的所有 public
或 protected的成员
class Animal{
public float weight;
public void eat(){…}
}
class Mammal extends Animal{
public int heartRate;
public void breath(){…}
}
class Cat extends Mammal{
boolean longHair;
public void purr(){…}
}
Cat mycat=new Cat();
mycat.weight=5.5;
mycat.heartRate=80;
mycat.longHair=true;
mycat.eat();
mycat.breath();
mycat.purr();
Classes and Objects in JVM
? 在 JVM中类被表示为一块内存区域,分别存放 类
名 和 类的静态成员,可以用以下的图形表示,
SomeClass
static field1
static method1
static field2
static field3
static method2
类名
域
区
方
法
区
class A{
static int sfa=100;
static int sfb=1000;
static void sma(){…}
int fa=10;
int fb=100;
void ma(){…}
}
A
sfb
sma
1000
……
……
sfa 100
Classes and Objects in JVM
? 在 JVM中对象也被表示为一块内存区域,分别存
放 到对象所属类的引用 和 对象的成员,可以用以
下的图形表示,
:classRef
field1
method1
field2
field3
method2
类的
引用
域
区
方
法
区
A ra1=new A();
A ra2=ra1;
ra1
ra2
A
:A
fb
ma
100
……
……
fa 10
class B extends A{
static int sfb=100;
int fb=10;
void ma(){…}
}
A ra1=new A();
B rb1=new B();
ra1=rb1;
B
A/sfb
sma
1000
……
……
A/sfa 100
B/sfb 100
A
A/sfb
sma
1000
……
……
A/sfa 100
:B
A/fb
ma
100
……
……
A/fa 10
B/fb 10
:A
fb
ma
100
……
……
fa 10
ra1
rb1
Constructors in Subclasses
? 子类对象的成员初始化之前必须完成父类或祖
先类对象的成员的初始化,初始化的方式有两
种,隐式初始化 和 显式初始化
? 隐式初始化,当 父类提供了默认的构造函数,
且子类的构造函数中没有显式调用父类的其它
构造函数,则在执行子类的构造函数之前会自
动执行父类的构造函数,直到执行完 Object的
构造函数后才执行子类的构造函数
class A{
int a,b;
A(){
a=100;
}
}
class B extends A{
int c,d;
B(){
c=-100;
}
}
B rb=new B();
new B() A Object
a=100;
……
c=-100;
c=0;
d=0;
a=0;
b=0;
调用 A()
调用 Object()
返回
返回
Constructors in Subclasses
? 显式初始化,可以在子类构造函数的 第一条
语句 通过 super()或 super(…) 调用父类的默认
构造函数或特定签名的构造函数
? 当父类没有提供默认构造函数时,必须在子
类构造函数第一条语句通过 super(…) 完成父
类对象成员的初始化工作
class A{
int a,b;
A(int c,int d){
a=c;
b=d;
}
}
class B extends A{
int c;
B(int i){
c=i;
}
}
错误
B(int i,int j,int k){
super(i,j);
c=k;
}
Reference Types Compatibility
? 赋值兼容性规则,
(1) 对引用类型的变量进行赋值操作时,赋
值号右边的表达式的类型必须与赋值号左边
的对象引用类型相同或是其子类型
(2) 方法的 return语句返回的结果的类型必须
与方法声明中的返回类型相同或是其子类型
? Null对象引用与所有的引用类型兼容,所有
引用类型的变量都可被赋值为 null
B rb=new B();
A ra1=rb;
A ra2=new B();
Object aobj=ra1;
Object strobj=new String(“good”);
Object intobj=new Integer(100);
Reference Types Casting
? 引用类型间的类型转换 (Conversion)又称为造型
(Casting),可以分为 向上造型 (Upcasting)和 向下造
型 (Downcasting)两种情况
? 向上造型 又称为,Widening Conversion”,是指将
子类类型的引用赋值给其父类或祖先类类型的引
用,赋值操作中包含了隐式的类型转换
? 向下造型 又称为,Narrowing Conversion”,是指
将某类类型的引用赋值给其子类类型的引用,赋
值操作必须进行显式的类型转换
注意:向下造型可能存在一定的类型安全风险
Object obj1=new String(“good”);
Object obj2=new Integer(100);
String str1=(String)obj1;
Integer int1=(Integer)obj2;
Integer int2=(Integer)obj1;
向上造型
向下造型
造型错误
Testing for Type
? 运算符 instanceof 用于判断对象所属的类型
? Null对象引用虽然与所有引用类型兼容,但
null不是任何类型的实例,因此表达式,
null instanceof <类名 > == false
Object obj1=new String(“good”);
Integer int2=null;
if(obj1 instanceof Intger)
int2=(Integer)obj1;
Overriding Methods (方法覆盖 )
? 方法覆盖 是指在子类中替换父类或祖先类对某个
方法的实现
? 方法覆盖通过子类重新定义与父类或祖先类中具
有 同样签名 和 返回类型 的方法实现
? 覆盖的方法可以保持和父类的方法相同的可访问
范围,也可以修改访问控制修饰符增大可访问范
围,但是不能减小可访问范围
private package protected public
Overriding Methods (方法覆盖 )
? 被 final修饰的方法不允许在子类中覆盖
? 其它修饰符,如 synchronized,strictfp,native可以
在覆盖的方法声明中自由使用
? 父类被覆盖的方法的参数列表中被声明为 final的参
数在子类的覆盖方法的中可以不必指定为 final
? 子类覆盖方法声明的异常列表中的异常类必须与
父类被覆盖方法声明的异常列表中的异常类兼容
(相同或是其子类 )
? 只有在子类类体中可以访问的父类或祖先类的方
法才能被覆盖
Hiding Fields (隐藏域 )
? 域不能被覆盖,只能被隐藏,子类可以通过
声明与父类或祖先类中的域同名的域来实现
对父类或祖先类中的域的隐藏
? 要访问父类或祖先类中被隐藏的域必须通过
super引用,即 super.hiddenField
? 当通过对象引用访问对象的域的时,域的值
由对象引用所属的类型决定,并不由被引用
的对象的实际类型决定
class ShowTest{
public static void main(String[] args){
ExtendShow ext=new ExtendShow();
SuperShow sup=ext;
sup.show();
ext.show();
System.out.println(“sup.str=,+sup.str);
System.out.println(“ext.str=,+ext.str);
}
}
class SuperShow{
public String str=“SuperStr”;
public void show(){
System.out.println(“Super.show:,+str);
}
} class ExtendShow extends SuperShow{
public String str=“ExtendStr”;
public void show(){
System.out.println(“Extend.show:,+str);
}
}
java ShowTest
Extend.show= ExtendStr
Extend.show= ExtendStr
sup.str= SuperStr
ext.str= ExtendStr
Hiding Fields (隐藏域 )
? 类的静态成员 (域或方法 )不能被覆盖,但能
被子类声明的静态成员隐藏
? 访问类的静态成员,一般按照以下形式,
类名,静态域
类名,静态方法
super 引用
? 保留字 super可以在任意的非静态方法中使用
? super可以看作当前对象实例所对应的父类型
的实例引用,通过 super引用可以访问属于父
类实例的域和方法,通常这些域和方法被当
前类隐藏或被覆盖了
class That{
protected String nm(){
return,That”;
}
}
class More extends That{
protected String nm(){
return,More”;
}
protected void printNM(){
That sref=(That)this;
System.out.println(“this.nm() =,+this.nm());
System.out.println(“sref.nm() =,+sref.nm());
System.out.println(“super.nm() =,+super.nm());
}
public static void main(String[] args){
More more=new More();
more.printNM();
}
}
java More
this.nm() = More
sref.nm() = More
super.nm() = That
final 的用途
? 被 final修饰的类不能被继承,被 final修饰的
方法不能被覆盖
? 使用 final修饰类和方法可以提高安全性
? 使用 final修饰类和方法可以简化优化过程
? 使用 final修饰类和方法可以加快某些类型检
查过程
Abstract Classes and Methods
? 被 abstract修饰的类称为抽象类,被 abstract修饰的
方法称为抽象方法
? 抽象类不能被实例化,即不能使用抽象类的构造
函数创建对象,抽象类中可以声明抽象方法,除
此以外抽象类与一般的类没有区别
? 抽象类只定义了类的部分行为,这些行为是子类
共有的,其它行为由子类实现的抽象方法提供,
因此抽象类通常作为一个框架,把子类将实现的
抽象方法组织起来,简化或限制子类的设计
abstract class Benchmark{
abstract void benchmark();
public final long repeat(int count){
long start=System.currentTimeMillis();
for(int i=0;i<count;i++)
benchmark();
return (System.currentTimeMillis()-start);
}
}
class Method extends Benchmark{
void benchmark(){
……
}
public static void main(String[] args){
int count=Integer.parseInt(args[0]);
long time=new Method().repeat(count);
System.out.println(count+, methods in,+
time+“milliseconds”);
}
}
java.lang.Object 类
? Object类作为 Java整个类层次结构树的根节
点,是所有类的父类或祖先类
? Object类型的变量可以引用任何类的对象
? Object类定义了一些被所有类的对象继承的
方法,这些方法可以分为两大类,
(1) 通用工具方法
(2) 支持多线程的方法
java.lang.Object 类
? Object类定义的通用方法,
public boolean equals(Object obj)
public int hashCode()
protected Object clone()
public final Class getClass()
protected void finalize()
public String toString()
equals方法和 hashCode方法
? equals方法 用于比较两个对象,默认的实现是比较
两个对象引用是否引用了同一个对象
? hashCode方法 用于返回一个特定对象的散列码值,
不同的对象应该有不同的散列码值,便于将对象
存放到 java.util.HashMap或 java.util.Hashtable等依赖
散列码值存放对象的集合中
? 如果要在子类中覆盖 equals方法,为子类实现特定
的比较机制,则应同时覆盖 hashCode方法,保证
不同的对象具有不同的散列码值
class Student{
private String sno,name;
public Student(String no,String nm){
sno=no;
name=nm;
}
public boolean equals(Object obj){
if(!(obj instanceof Student))
return false;
Student other=(Student)obj;
return sno.equals(other.sno) && name.equals(other.name)
}
public int hashCode(){
int hc=sno.hashCode();
return hc*19+name.hashCode();
}
}
class StudentComparision{
public static void main(String[] args){
Student stu1=new Student(“1001”,“Zhangsan”);
Student stu2=new Student(“1001”,“Zhangsan”);
System.out.println(stu1==stu2);
System.out.println(stu1.equals(stu2));
}
}
java StudentComparision
false
true
Packaging Classes
? 在 Java源程序文件的第一行使用 package声明可以
使文件中定义的类成为指定包的成员
? package声明的语法如下,
package <包名 >;
? 包名通常由多个名字字符串构成,中间用句点,.”
分隔,每个名字表示的包称为其前面的名字表示
的包的子包,通常以组织机构的域名反转形式作
为其所有包的通用前缀,如
com.somecompany.apps
Packaging Classes
? 包有以下用途,
(1) 将相关的类和接口分组,包名通常表示
包中各个类和接口的用途
(2) 包创建了不同的命名空间 (Namespace),
从而有效地解决了类型命名冲突问题
(3) 包提供了对应用程序内部实现机制的保
护域
编译指定了包的类
? 如果待编译的类的源程序中使用了 package
声明指定了类所属的包,则必须使用 javac
命令的 -d选项来进行编译
javac –d <指定路径 > <类文件名列表 >
当指定路径为,.”时表示当前路径
package test;
class StudentComparision{
public static void main(String[] args){
Student stu1=new Student(“1001”,“Zhangsan”);
Student stu2=new Student(“1001”,“Zhangsan”);
System.out.println(stu1==stu2);
System.out.println(stu1.equals(stu2));
}
}
javac –d, StudentComparision.java
./test/SudentComparision.class
Type Imports
? 在类名前面加上类所属的包名,中间用句点,.”分
隔,称为 类的完全限定名 (Full Qualified Name),
简称类的限定名
? 当在类体中使用了与当前类不同包的类名时,编
译器编译时因为无法找到该类的定义而失败,有
两个解决办法,
(1) 使用不同包类的完全限定名
(2) 使用 import声明,为编译器提供该类的定义信
息
Type Imports
? import声明 一般紧跟在 package声明之后,必须在类
声明之前,其基本语法如下,
import <类的完全限定名 >;
? Java语言核心包 java.lang包中的类将被隐式导入,可
以直接使用其中的类
? import声明提供了一种包的智能导入方式,
import <包名 >.*
包中的类将根据需要导入,避免使用多条 import声明
package lecture.java.lesson9;
class Student{
……
}
package lecture.java.lesson10;
class StudentComparision{
public static void main(String[] args){
Student stu1=new Student(“1001”,“Zhangsan”);
Student stu2=new Student(“1001”,“Zhangsan”);
System.out.println(stu1==stu2);
System.out.println(stu1.equals(stu2));
}
}
import lecture.java.lesson9.Student;
Assignments (作业 )
? 通过阅读课本第 3章第 6小节,谈谈你对
内联 (inlining)机制的理解
? 通过阅读课本第 13章第 2小节,说明编
译器通过类名搜索类的定义信息的过程
? 编程,P69 练习 3.1
P85 练习 3.4
P89 练习 3.7