第 4 章 Java对象的生命周期
2012-3-7 Java面向对象程序设计教程 2
主要内容
? 4.1 对象的声明与创建
? 4.1.1 基本术语
? 4.1.2 对象的声明
? 4.1.3 对象的创建
? 4.1.4 数组对象的声明和创建
? 4.2 对象的初始化
? 4.2.1 成员变量的缺省初始化过程
? 4.2.2 类成员变量的初始化与静态代码块
? 4.2.3 实例成员变量的初始化与构造方法
? 4.2.4 程序健壮性的代价和编码启示
? 4.2.5 类成员变量与实例成员变量初始化的区别
? 4.2.6 继承链上相关类的加载顺序以及构造方法链的调用
? 4.3 对象的使用
? 4.3.1 多态的使用
? 4.3.2 对象的比较
? 4.3.3 对象的传递
? 4.3.4 内部类的使用
? 4.4 对象的清除
4.1 对象的声明与创建
2012-3-7 Java面向对象程序设计教程 4
基本术语:类
? 类( Class)
可以由用户自己定义的引用类型,它是相
似对象的软件抽象、创建对象的模板。
? 每一个类都组合了一些能够反映某种类型
属性的字段和反映某种类型动作的方法。
2012-3-7 Java面向对象程序设计教程 5
基本术语:对象
? 对象( Object)
是一个人、地址、物品、事件、概念、屏
幕显示或者报表,它属于某一个类,对象
拥有自己的属性值和方法调用。
? 作为类设计的初衷,每一个类都将存在许
多相互独立的对象。
? 通常我们说一个对象是类的一个实例,一
个对象的生命周期包括声明、创建、使用
和清除四个状态。
2012-3-7 Java面向对象程序设计教程 6
基本术语:对象引用
? 在 Java语言中只能引用对象,无法声明一
个直接包含对象的变量。
? 类类型变量称为引用变量,它们并不直接
保留对象,只是保留对象的地址,而不是
拥有对象本身,通过引用变量即可以得到
一个对象。
? 所有对象总是且只是通过内存引用访问的,
这种方式称为间接寻址。
? 对象引用可以在需要的时候自动撤销。
2012-3-7 Java面向对象程序设计教程 7
类、对象和对象引用的关系示意图
2012-3-7 Java面向对象程序设计教程 8
基本术语:类变量
? 类变量必须在类体中声明,并且使用关键字
,static‖,因此也称为静态变量。
? 类变量在类被加载时完成相应的初始化工作。
? 类变量在一个运行系统中只有一份供整个类和实
例对象共享的值,该值有可能被类(及其子类)
和它们所创建的实例改变,每一次的改变都将影
响到该类(及其子类)和其它实例的调用。
? 类变量的作用域是整个类。
2012-3-7 Java面向对象程序设计教程 9
基本术语:实例变量
? 实例变量在类体中声明但不使用关键字
,static‖,也称为非静态变量。
? 它在对象初始化时完成相应的初始化工作,
并由某一个对象独自拥有。
? 实例变量的作用域是某一个类具体创建的
实例对象。
2012-3-7 Java面向对象程序设计教程 10
基本术语:局部变量
? 局部变量在某一个语句(如 for语句)或代码块
(如方法体)中声明。
? 当该语句或代码块被调用时,每次都将立即创建
相关的局部变量。
? 在一个局部变量的声明语句中可能包含有用于初
始化的表达式,但是,该表达式一般要等到这条
声明语句被执行的时候才会进行相应的初始化。
当该语句或代码块被执行完毕退出,相应的局部
变量也将失效。
? 局部变量的作用域是声明该变量的语句或代码块。
2012-3-7 Java面向对象程序设计教程 11
基本术语:参数变量
? 方法和构造方法用于传递信息的参数
( Argument)变量和作用于方法体的局
部变量相类似,当方法被调用的时候进行
相应的初始化,当方法体执行结束后失效。
2012-3-7 Java面向对象程序设计教程 12
变量举例
? 类变量和实例变量都拥有一个缺省初值,
如果某个变量在初始化时没有显式赋值,
那么将被赋予缺省初值。
? 局部变量则没有缺省初值,如果某个变量
在初始化时没有显式赋值,并且在其后的
语句中试图直接调用,Java编译器将会报
错提示,以保证每个变量都是按照编程人
员的意图进行赋值。
? 举例,VariableInit.java
2012-3-7 Java面向对象程序设计教程 13
讨论以下程序的问题所在
public class InitializationDemo{
int score = 100;
public String isExcellent(){
String result;
if(score>=90)
result = "Very very good!";
return result;
}
public static void main(String[] args){
InitializationDemo obj = new InitializationDemo();
System.out.print(obj.isExcellent());
}
}
2012-3-7 Java面向对象程序设计教程 14
基本术语:关键字 this
? this只能用于与实例有关的代码块中,如实例方
法、构造方法、实例初始化代码块或实例变量的
初始化代码块等,this就代表当前或者正在创建
的实例对象的引用,通常可以利用这一关键字实
现与局部变量同名的实例变量的调用。
? 在构造方法中还可以用 this来代表要显式调用的
其它构造方法。
? 除此以外,使用 this关键字都将引发编译时错误。
2012-3-7 Java面向对象程序设计教程 15
基本术语:关键字 super
? super只能用于与实例有关的代码块中,如实例
方法、构造方法、实例初始化代码块或实例变量
的初始化代码块等,super代表当前或者正在创
建的实例对象的父类,通常可以利用这一关键字
实现对父类同名属性或方法的调用。
? 在构造方法中还可以用 super来代表要调用的父
类构造方法,以实现构造方法链的初始化。
? 由于 Object类为 Java语言的根类,已经没有父类,
因此,如果在 Object类中使用了关键字 super,
将引发编译时错误。
2012-3-7 Java面向对象程序设计教程 16
this和 super举例
? this和 super是在设计类的时候、而不是使
用类及其实例的时候引用。
? 举例,ThisSuper.java
2012-3-7 Java面向对象程序设计教程 17
基本术语:访问权限修饰符
? 访问权限控制由强到弱的顺序是 public,protected、
( default),private。
? public访问权限意味着在任何地方,只要能访问到该类,
即可访问到该成员。
? protected访问权限意味着类中的成员允许同一个包或该
类的扩展子类访问。
? 缺省的访问权限不需要任何关键字,但通常称为
,Friendly‖。它意味着类中的成员只能由同一个包中的
其它类访问。
? private访问权限意味着该类中的成员只能由自己的成员
调用,而无法被该类以外的其它类访问。
? 声明局部变量不能包含访问权限修饰符,否则编译时将检
查出错。
2012-3-7 Java面向对象程序设计教程 18
存储对象状态的地方
? 寄存器( Register) JVM会根据本身需求适当地分配 。
? 堆( Heap) 堆是个自由内存区域,常用于动态或临时内存分配,对
类和数组对象提供内存。
? 栈( Stack) 栈存储方法调用的状态,如方法调用使用的任何局部
变量和方法的操作数。
? 方法区( Method area) 所有 JVM线程共享的公用存储区,存储运
行时常量池、方法数据、字段数据、方法与构造方法字节码等信息。
? 运行时常量池( Runtime constant pool) 类似于其它编程语言中
的符号表,它包含数值文字和字段常量之类的常量。
? 非运行时存储空间
2012-3-7 Java面向对象程序设计教程 19
对象的声明
? Java作为一种强类型语言,声明基本类型或引用
类型都要先指定类型,然后给出相应的变量标识
符,
Type Identifier;
? fianl型变量只能赋值一次,而且必须被显式赋值,
否则编译时将有检查错误提示。
? fianl型基本类型变量相当于常量。
? 对于引用型变量,当声明为, final‖时,如果该变量引
用了一个对象,则意味着该变量只能引用该对象,而
该对象的状态仍然可以被改变;如果该变量引用了一
个数组,则意味着该变量只能引用该数组,而该数组
的元素仍然可以被改变。
? 举例,FinalVariable.java
2012-3-7 Java面向对象程序设计教程 20
对象的创建
? 利用关键字 new可以创建一个对象,new运算符
为对象分配内存空间,调用该类的一个构造方法,
实例化一个对象,返回该对象所在内存地址的一
个引用。
? 该对象引用可以赋给相应类型的对象变量,以后
可以通过该变量操作所引用的对象。
? 如果创建该对象时没有把引用赋给某个对象变量,
则该对象将无法再被引用。
? 举例,Employee.java
2012-3-7 Java面向对象程序设计教程 21
数组对象的声明
? 数组是一种用来存储相同类型数据项的数
据结构,它由一组具有相同类型和相同变
量名,放在相邻内存位置的元素构成。
? 数组声明语句,
Type[] Identifier;
? 在任何数组变量的类型声明中,数组维数
都被忽略了,数组元素的个数是利用 new
运算符创建时、而不是在声明时决定的。
2012-3-7 Java面向对象程序设计教程 22
数组对象的创建
? 要创建 Java的数组对象,通常使用 new运算符进
行实例化。当第 1次生成数组时,应指定这个数组
拥有的元素是多少,也就是数组的大小。
? 数组对象的长度在创建时限定后,就不能再被改
变。
? 但可以在任何时候把一个不同长度的新数组赋值
给数组变量 Identifier。
? 数组第一个元素的下标为 0,最后一个元素的下标
是 (length-1),其中 length为数组长度,即数组
元素的个数。
? 创建数组的大小可以是 0,一个长度为 0的数组被
称为 empty(空)数组。
讨论:, int[] i=new int[0];‖和, int[] i=null;‖
2012-3-7 Java面向对象程序设计教程 23
数组举例
? 数组初始化,ArrayInit.java
?, int[] i=new int[0];‖和, int[] i=null;‖区
别
? 数组操作,ArrayManipulation.java
?Arrays类及其方法
? 数组复制,ArraycopyExample.java
?单层复制与多层复制的实现
4.2 对象的初始化
2012-3-7 Java面向对象程序设计教程 25
成员变量的缺省初始化过程
? 创建对象时需要对属于对象的成员变量进行初始
化。
? 无论成员变量是声明在其它方法的前面或者后面,
其初始化操作总会在方法调用之前合适的时机执
行,并按顺序逐步完成。
? 与方法中声明的局部变量不一样,为了保证数据
成员变量在使用前被有效初始化,JVM会在类加
载时对所有数据成员变量赋予缺省的初值。
? 这一步骤总是首先被执行的,当没有显式对某个
变量赋值时,缺省值的使用可以避免引发异常而
导致崩溃。
2012-3-7 Java面向对象程序设计教程 26
类型的缺省值
类型 缺 省 值
boolean false
char ‘ \u0000’ (null)
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d
引用类型 null
2012-3-7 Java面向对象程序设计教程 27
成员变量的显式初始化过程
? 显式对每个变量赋值进行初始化,这时
JVM会把按照类成员变量和实例成员变量
的不同将它们分别移入静态代码块( Static
block)或构造方法体( Constructor
body)中执行,将相应的值赋予该变量。
? 这是一种良好的编程习惯!
2012-3-7 Java面向对象程序设计教程 28
类成员变量的初始化与静态代码块
? 类成员变量的标志是变量声明时使用关键
字 static,它在类加载时完成初始化,并且
保持到该类被清除为止,此期间类及其实
例共享着同样一份数据;同时静态代码块
也在类加载时被执行。
2012-3-7 Java面向对象程序设计教程 29
例 子, java InitializationDemo ?
public class InitializationDemo{
static String str="aaa ";
static{
str="bbb ";
}
InitializationDemo(){
str="ccc ";
}
public static void main(String[] args){
System.out.print(str);
}
}
静态变量
静态代码块
输出 str的值,
"aaa "? ―bbb "? ―ccc "?
构造方法
2012-3-7 Java面向对象程序设计教程 30
分 析
? 观看类加载顺序,
java -verbose InitializationDemo ?
? 代码
static String str="aaa ";
static{
str="bbb ";
}
等价于
static String str;
static{
str="aaa ";
str="bbb ";
}
? 执行 javap –c InitializationDemo验证
? 类加载时,以上语句已被执行,因此进入 main方法后输出结果为
―bbb ‖。注意:构造方法 InitializationDemo()并没有被执行。
? 进一步的例子,AStatic.java
顺序相同
2012-3-7 Java面向对象程序设计教程 31
实例成员变量的初始化与构造方法
? 实例成员变量,相应的显式赋值语句和实
例代码块将按顺序被移入构造方法体中。
? 与静态语句不同的是,这些语句无论在构
造方法体的前面或者后面,移入后总是首
先被执行,然后执行构造方法体中原有的
语句。
2012-3-7 Java面向对象程序设计教程 32
例 子, java InitializationDemo ?
public class InitializationDemo{
String str="aaa ";
InitializationDemo(){
str="ccc ";
}
{
str="bbb ";
}
public static void main(String[] args){
InitializationDemo obj=new InitializationDemo();
System.out.print(obj.str);
}
}
实例变量
非静态代码块
输出 str的值,
"aaa "? ―bbb "? ―ccc "?
构造方法
2012-3-7 Java面向对象程序设计教程 33
分 析
? 代码
String str="aaa ";
InitializationDemo(){
str="ccc ";
}
{
str="bbb ";
}
等价于
static String str;
InitializationDemo(){
str="aaa ";
str="bbb ";
str="ccc ";
}
? 执行 javap –c InitializationDemo验证
? 进入 main方法后,实例化时,以上语句已被执行,因此输出结果为 ―ccc ‖。
? 进一步的例子,ANonStatic.java
顺序相同
2012-3-7 Java面向对象程序设计教程 34
讨论
? 程序健壮性的代价和编码启示
? 类成员变量与实例成员变量初始化的区别,
? 加载时机是不同的:前者在类加载时完成初始化,后
者则是在创建实例对象时完成初始化。
? 变量初始化后的保持时间也是不同的:类成员变量的
数据一直保持到类对象被终止(退出 JVM),在此期
间无论创建多少个实例对象,它都不再进行初始化,
而只是为实例对象提供共享数据;实例成员变量则只
保持到该实例对象被清除之时,无论创建多少个实例
对象,它每次都要进行相应的初始化而彼此之间互不
影响。
? 进一步的例子,
MemberInit.java ; EggOrChicken.java
2012-3-7 Java面向对象程序设计教程 35
继承链上相关类的加载顺序
? 类模块的层次结构将在子类及其直接或间
接超类间形成一条继承链。
? 在 Java语言中,每一条继承链都从其根类
Object开始。
? 所有直接或间接超类总是在当前子类之前
被加载,该类实现的接口也在当前子类之
前被加载。
? 举例,InheritanceChain.java
2012-3-7 Java面向对象程序设计教程 36
构造方法链的调用
? 构造方法链是指当创建某个类的实例时,它的构
造方法及其超类的构造方法都将被调用。
? 此过程将从当前类的构造方法开始,一直追溯到
Java语言的根类 Object;然后从 Object类的构
造方法开始依次执行,直到欲创建实例的当前类
为止,完成相应的初始化工作。
? 这一过程将保证当前创建的对象中从继承链中获
得的实例变量都将有效地进行初始化,使得它们
能够被正确地使用。
? 举例,ConstructorChain.java
2012-3-7 Java面向对象程序设计教程 37
讨论
? 如何使得当类的实例对象形成时总能完成
相应构造方法链的调用?
缺省机制举例,MyClass.java
? 构造方法的迷惑举例,
PuzzledConstructor.java
4.3 对象的使用
2012-3-7 Java面向对象程序设计教程 39
多态的使用
? 多态机制允许继承链上存在重名的变量或
方法。
? 多态性实现用继承链上的超类来管理其扩
展类的实例对象。
? 转型所带来的重名成员的调用问题。
? 向上自动转型与向下手工转型。
2012-3-7 Java面向对象程序设计教程 40
重载
? 重载代表着实现某个类内
部一种行为的多种手段,
设计类时最常用的是定义
多个构造方法,以满足在
不同的初始化条件下创建
该类的实例对象。
? 举例,Circle.java
? java.io.PrintStream类
中重载的 print方法,
print(boolean b)
print(char c)
print(char[] s)
print(double d)
print (float f)
print (int i)
print(long l)
print(Object obj)
print(String s)
2012-3-7 Java面向对象程序设计教程 41
覆盖
? 覆盖则代表着相关的类之间某一种特征的
多种行为。
? 当设计子类的方法时遇到和父类相似的特
征,而最恰当的方法名已经被父类使用了,
这时可能的解决方法,
?一种办法是把将要设计的子类方法名用一个还
没有被继承链上超类使用过的名字来命名;
?另一种办法是同样采用已经被超类使用过的最
恰当的名字 ——但必须由多态性得以保证。
2012-3-7 Java面向对象程序设计教程 42
使用不同名字的方式
2012-3-7 Java面向对象程序设计教程 43
利用多态机制的重名方式
? 举例,OverrideTest.java
2012-3-7 Java面向对象程序设计教程 44
处理与类型有关的对象,
使用时进行,if‖判断
//创建一个 Shape类的数组对象 shapes,用来存放所有扩展类对象
for(int i=0; i<shapes.length; i++){
if(shapes[i] instanceof Oval)
drawOval();
else if(shapes[i] instanceof Quatrangle)
drawQuatrangle();
else if(shapes[i] instanceof Triangle)
drawTriangle();
else if(shapes[i] instanceof Square)
drawSquare();
else if(shapes[i] instanceof Rectangle)
drawRectangle();
else if …
}
2012-3-7 Java面向对象程序设计教程 45
处理与类型有关的对象,
设计时进行,if‖判断
public void draw(){
//创建一个 Shape类的数组对象 shapes,用来存放所有扩展类对象
for(int i=0; i<shapes.length; i++){
if(shapes[i] instanceof Oval)
drawOval();
else if(shapes[i] instanceof Quatrangle)
drawQuatrangle();
else if(shapes[i] instanceof Triangle)
drawTriangle();
else if(shapes[i] instanceof Square)
drawSquare();
else if(shapes[i] instanceof Rectangle)
drawRectangle();
else if …
}
}
2012-3-7 Java面向对象程序设计教程 46
处理与类型有关的对象,
多态方式
//创建一个 Shape类的数组对象 shapes,用
来存放所有扩展类对象
for(int i=0; i<shapes.length; i++)
shapes[i].draw();
2012-3-7 Java面向对象程序设计教程 47
转型时重名成员的调用问题
? 举例,ConversionTest.java
? 对于重名的成员变量而言,无论是静态的还是非静态的,
当以某个超类声明而以扩展类实例化时,该变量将反映该
超类的性质,而用来实例化的扩展类变量性质将被隐藏掉。
这或许不是我们所希望的。因此,把变量设为 private,
然后转化为相应的成员方法除了方便控制访问权限外,也
才有可能利用对象的多态机制。
? 对于重名的静态成员方法,实际上也是反映该超类的性质,
并不具有我们所理解的多态性质。
? 我们所谓的扩展类方法覆盖超类方法的多态机制只是对非
静态(实例)方法而言的,其它情况则不能实现。
? 而且,这些非静态方法的可访问作用域还必须包含其子类
方可被覆盖,否则即便在子类中有相同构型的方法,依然
无法实现。
2012-3-7 Java面向对象程序设计教程 48
讨论
? 转型时在扩展类的实例中能否访问到超类
中被覆盖的实例方法?
?举例,ConversionTest.java中 Super类的
showNonStatic方法
? 转型时子类中细节内容的调用 ——向下手
工转型
?举例,This类的 showDetail方法
2012-3-7 Java面向对象程序设计教程 49
引用类型变量和对象本身之间的
松绑定机制
? 我们通过引用类型变量获得某个对象的地址,从
而可以访问该对象的数据;也就是说,当前引用
类型变量所指向的对象就是我们正在使用的对象。
? 当引用类型变量与当前的对象分离开并被重新赋
值后,该对象将不再被使用。
? 当一个对象同时被多个引用类型变量所引用,则
我们可以通过这些引用类型变量共享同一个对象。
? 当某个对象不被任何一个引用类型变量所引用时,
该对象就可以被清除了。
? 举例,WhoIsUse.java
2012-3-7 Java面向对象程序设计教程 50
关系运算符,==‖ 何时返回 true?
? 在比较两个同类型的基本类型变量时,如
果二者的值相同则返回 true,否则返回
false;
? 在比较两个同类型的引用类型变量时,如
果二者指向 同一个对象 则返回 true,否则
返回 false。
2012-3-7 Java面向对象程序设计教程 51
讨论,PuzzledString.java
String a1="test";
String b1="test";
? ―a1==b1‖返回 true还是 false?
String a=new String("test");
String b=new String("test");
? ―a==b‖返回 true还是 false?
2012-3-7 Java面向对象程序设计教程 52
equals方法的讨论(一)
? Object类的 equals方法比较对象本身,
public boolean equals(Object obj){
return(this==obj);
}
? Integer类的 equals方法设计成比较数值,
public boolean equals(Object obj){
if(obj instanceof Integer){
return value==((Integer)obj).intValue();
}
return false;
}
? String类的 equals方法设计成比较字符串内容,
参见 src.zip 中 String.java
2012-3-7 Java面向对象程序设计教程 53
equals方法的讨论(二)
? 对于自定义的类,则要根据自己的需要重
写 equals方法:返回 true还是 false将根据
设计意图而定,切不可妄下结论!
? 举例,
ObjectEqual.java
ObjectEqualUpdate.java
2012-3-7 Java面向对象程序设计教程 54
例证:引用类型变量和对象本身之间的关系
new StringBuffer("Contents");
StringBuffer str=new StringBuffer("Contents");
StringBuffer strTest=str;
strTest.append(" add Something.");
strTest=null;
StringBuffer strLure=new StringBuffer("Give you an apple,then leave him!");
strTest=strLure;
strTest=new StringBuffer("It is my business!");
? 例子,AboutReference.java
创建一个对象
strTest通过 str间接得到对象的引用
通过 strTest改变对象内容
变量改变指向:, 改弦易辙, 型
变量改变指向:, 分道扬镳, 型
变量改变指向:, 自立门户, 型
str得到所创建对象的引用
创建了几个对象?
str改变了吗?
str改变了吗?
2012-3-7 Java面向对象程序设计教程 55
方法参数传递的方式
? 基本类型作为方法参数传递均按值传递,
在方法体中对形参的改变并不影响原来的
变量;
? 所有的对象作为方法参数传递均按引用传
递,在方法体中对形参的改变将影响原来
的变量,因为它们都指向同一个对象。
? 举例,MethodArgument.java
2012-3-7 Java面向对象程序设计教程 56
引用类型变量的按值传递
? 当我们把引用类型变量作为实参传给方法
时,仅仅是把实参变量的引用值(表示逻
辑内存地址数据)赋给形参变量,而形参
变量却无法逆向地通过改变自身以达到改
变实参引用值的目的。
? 由于在处理引用变量与对象本身关系时,
C++采用紧绑定机制,而 Java采用松绑定
机制,因此有些例子结论有所不同。
2012-3-7 Java面向对象程序设计教程 57
C++语言按引用传递的例子
// C++语言利用指针变量操控参考的演示
#include <iostream>
using namespace std;
// 定义一个反映坐标点的类
class Coord {
public,
int x;
int y;
};
// Swap函数可以用来交换两个 Coord对象的内容
void swap(Coord &a,Coord &b){
Coord temp;
// 交换两个 Coord对象的内容的操作
temp=a;
a=b;
b=temp;
}
// 定义主函数
int main(){
// 声明两个 Coord对象
Coord ob1,ob2;
// 并赋值
ob1.x=10;
ob1.y=20;
ob2.x=88;
ob2.y=99;
// 输出交换前两个对象的状态
cout << ″交换前两个对象的状态,\n″;
cout << ″ob1,″ << ob1.x << ″,″ << ob1.y << ″\n″;
cout << ″ob2,″ << ob2.x << ″,″ << ob2.y << ″\n″;
swap(ob1,ob2);
// 输出交换后两个对象的状态
cout << ″交换后两个对象的状态,\n″;
cout << ″ob1,″ << ob1.x << ″,″ << ob1.y << ″\n″;
cout << ″ob2,″ << ob2.x << ″,″ << ob2.y << ″\n″;
return 0;
}
交换前两个对象的状态,
ob1,10,20
ob2,88,99
交换后两个对象的状态,
ob1,88,99
ob2,10,20
结论:实参变量所指向的对象
状态已被改变。
2012-3-7 Java面向对象程序设计教程 58
Java语言按引用传递的例子
//,SwapDemo.java
//package g3ds.joop.ch4;
// 定义一个反映坐标点的类
class Coord {
int x;
int y;
}
public class SwapDemo{
// 定义一个和 C++版本程序段类似的 Swap方法
static void swap(Coord a,Coord b){
Coord temp=new Coord();
temp=a;
a=b;
b=temp;
}
// 测试用的主方法
public static void main(String[] args){
Coord ob1=new Coord();
Coord ob2=new Coord();
ob1.x=10;
ob1.y=20;
ob2.x=88;
ob2.y=99;
// 输出运行 Swap方法前两个对象的状态
System.out.println(″运行 Swap方法前两个对象的状态, ″);
System.out.println(″ob1,″+ob1.x+″,″+ob1.y);
System.out.println(″ob2,″+ob2.x+″,″+ob2.y);
swap(ob1,ob2);
// 输出运行 Swap方法后两个对象的状态
System.out.println(″运行 Swap方法后两个对象的状态, ″);
System.out.println(″ob1,″+ob1.x+″,″+ob1.y);
System.out.println(″ob2,″+ob2.x+″,″+ob2.y);
}
}
运行 Swap方法前两个对象的状态,
ob1,10,20
ob2,88,99
运行 Swap方法后两个对象的状态,
ob1,10,20
ob2,88,99
结论:实参变量所指向的对象
状态未被改变。
2012-3-7 Java面向对象程序设计教程 59
讨论,PassByRefValue的执行过程
public class PassByRefValue{
public static void main(String[] agrs){
StringBuffer str1=new StringBuffer(″abc″);
StringBuffer str2=new StringBuffer(″123″);
PassByRefValue byRef=new PassByRefValue();
System.out.println(″main->str1 before swap method,″+str1);
System.out.println(″main->str2 before swap method,″+str2);
byRef.swap(str1,str2);
System.out.println(″main->str1 after swap method,″+str1);
System.out.println(″main->str2 after swap method,″+str2);
}
void swap(StringBuffer str1,StringBuffer str2){
StringBuffer temp=str1;
str1=str2;
str2=temp;
System.out.println(″swap->str1 in swap method,″+str1);
System.out.println(″swap->str2 in swap method,″+str2);
}
}
记作
main->str1
main->str2
记作
swap->str1
swap->str2
main->str1指向 abc
main->str2指向 123
通过 byRef调用 swap方法
main->str1赋予 swap->str1,此时 main->str1,swap->str1指向 abc
main->str2赋予 swap->str2,此时 main->str2,swap->str2指向 123
swap->temp,swap->str1,main->str1指向 abc
swap->temp,main->str1指向 abc
swap->str1,swap->str2,main->str2指向 123
swap->str2,sw p->temp,main->str1指向 abc
swap->str1,main->str2指向 123
输出
main->str1
main->str2
输出
main->str1
main->str2
输出
swap->str1
swap->str2
main-> str1 before swap method,abc
main-> str2 before swap method,123
swap-> str1 in swap method,123
swap-> str2 in swap method,abc
main-> str1 after swap method,abc
main-> str2 after swap method,123
2012-3-7 Java面向对象程序设计教程 60
进一步讨论
? 通过 clone方法实现对象的复制
? 举例,ObjectClone.java
2012-3-7 Java面向对象程序设计教程 61
内部类
? 在一个顶层类( Top level class)中声明另一个
类作为它的成员,这一些类称为内嵌类,非静态
的成员类有时也直接称为内部类。
? 内嵌类和它的顶层类可以分别有不同的继承关系
和实现不同的接口类型。
? 内部类也可以设计成抽象类或者接口。内部类的
声明方式跟一般的类没什么不同,不过就是外部
类的成员。
? 内部类可以使用任意一个访问限定词,不像一般
类只能使用 public和缺省( default)两个。
2012-3-7 Java面向对象程序设计教程 62
内部类的作用
? 程序设计时,有些逻辑上相关的类必须是
要伴随另一个类的存在才有意义,如果两
者分开,可能在类的管理上比较麻烦,所
以我们可以把这样的类写成一个内部类。
? 内部类从结构上看是它所在外部类(顶层
类)的组成部分,理所当然可以访问该外
部类的任何成员,包含声明为 private的成
员。
2012-3-7 Java面向对象程序设计教程 63
内部类的形式
? 作用域范围在整个类中的内部类可分为静
态成员类和实例成员类 ;
? 作用域范围仅属于一个方法甚至某个语句
的内部类称为局部成员类 ;
? 有些局部成员类甚至没有直接给出类名,
称为匿名成员类。
2012-3-7 Java面向对象程序设计教程 64
内部类的命名规则
? 一般是外部类名和相应的内部类名通过, $‖
符号连接起来,如
InnerClass$InstanceInner.class
? 内部类嵌套层数越深,相应的内部类名就
越长,
? 匿名类则用相应的数字符号表示。
2012-3-7 Java面向对象程序设计教程 65
内部类的使用
? 静态成员类加上了 static限定词,让别的类在访问时可以
通过外部类的名称来直接访问。
? 实例成员类就必须先生成外部类的对象后,才能通过这个
对象来访问。
? 在内部类中,除了静态成员类以外,所有非静态成员类体
内不能再声明带有 static关键字的成员。
? 关键字 this指的是内部类的实例,如果要访问外部类的同
名变量则是在 this之前加上外部类的类名。
? 局部成员类在访问包含它的方法体中的其它变量时,该变
量必须是个常量,也就是说声明时要加上 final关键字。
? 局部成员类只能在包含它的方法体中使用,其它的方法或
类基本上是访问不到它的(除非通过方法返回对象)。
2012-3-7 Java面向对象程序设计教程 66
匿名类
? 匿名类也属于局部成员类,但作用域范围最小。
? 匿名类最常用于设计事件处理类,因为这些类既多处理的
事情又单一,我们专门为其命名相当费劲,因此就把这个
命名工作交给 Java的编译器,让它在编译时根据一定的规
则进行命名。
? 不能在其它的场合中访问匿名类了。
? 由于定义匿名类总是类似于
new AnonymousInner(){};
因此没有办法在其中加入任何限定词。
? AnonymousInner只能是一个接口、抽象类或者一般类
(但不能是 final型的)。
? 举例,InnerClass.java
4.4 对象的清除
2012-3-7 Java面向对象程序设计教程 68
垃圾自动回收机制
? Java虚拟机采用一种称为垃圾回收的技术,以确
保仍被引用着的对象仍会驻留在内存里,而当对
象在程序中不再被引用时,它所占用的内存空间
就会被释放。
? 垃圾自动回收机制意味着不必担心所谓的悬挂引
用问题的发生,因为在程序的某处尚被引用着的
对象是不会被当作垃圾回收的。
? 垃圾回收并不能保证总有可用的内存给新生对象,
而且,垃圾回收也确实是个需要内存开销的过程。
2012-3-7 Java面向对象程序设计教程 69
垃圾回收的逻辑过程
? 首先要区分开, 活, 对象和, 死, 对象,
活对象是指那些在执行代码中仍被使用着
的对象,死对象就是要被回收的垃圾;然
后,回收死对象的内存空间。
2012-3-7 Java面向对象程序设计教程 70
垃圾回收模型的引用计数模型
? 当对象 X引用对象 Y时,系统会增加 Y上的引用计
数,而当 X不再引用对象 Y时,系统会减少它的引
用计数。当 Y上的引用计数器减为零之后,Y对象
即不再存活,并可以被回收了,这也同时会使 Y引
用的所有对象的引用计数分别减 1。
? 引用计数模型对于出现循环引用问题时是无效的:
如果 X和 Y彼此引用对方,那么它们的引用计数器
都不会减到零,所以也不会被垃圾回收。同样,
它们直接和间接引用的其它对象也不会因为被判
为不再存活而被回收。
2012-3-7 Java面向对象程序设计教程 71
垃圾回收模型的“标记 —清除”模
型
? 为了找到哪些对象还活跃着,首先要确定一个根对象的集
合,这包括了那些直接可达的对象:例如,在栈里的局部
变量中的对象引用是可达的,因为我们可以用这些变量来
操作对象,所以被局部变量所引用的对象当然是存活的。
一旦确定了根对象的集合,垃圾收集器就可以把那些被根
对象所引用的对象标记为可达对象。
? 然后,它会继续检查这些对象中所包含的对象引用:若被
这些引用所指的对象在第一步中已标为可达的,那么就把
它忽略过去,否则被引用对象会被标记为可达的;接下来
再检查这个刚被打过标记的对象中对其它对象的引用;这
个过程一直进行到没有可达对象尚未标记为止。
? 当整个打标记过程结束之后,死对象就可以从内存中被清
除了。
2012-3-7 Java面向对象程序设计教程 72
Runtime类以及 System类中
与垃圾回收有关的一些常用方法
? public void gc( )
该方法请求虚拟机执行无用对象的回收任务,以
便重用内存资源,
? public void runFinalization( )
该方法请求虚拟机在那些不可达的对象上,调用
从前还没有执行过的 finalize方法。
? public long freeMemory( )
该方法返回系统中可用内存字节数的估计值。
? public long totalMemory( )
该方法返回系统中全部内存资源字节值。
? 举例,FinalizeTest.java
期中复习小测 ……
2012-3-7 Java面向对象程序设计教程 2
主要内容
? 4.1 对象的声明与创建
? 4.1.1 基本术语
? 4.1.2 对象的声明
? 4.1.3 对象的创建
? 4.1.4 数组对象的声明和创建
? 4.2 对象的初始化
? 4.2.1 成员变量的缺省初始化过程
? 4.2.2 类成员变量的初始化与静态代码块
? 4.2.3 实例成员变量的初始化与构造方法
? 4.2.4 程序健壮性的代价和编码启示
? 4.2.5 类成员变量与实例成员变量初始化的区别
? 4.2.6 继承链上相关类的加载顺序以及构造方法链的调用
? 4.3 对象的使用
? 4.3.1 多态的使用
? 4.3.2 对象的比较
? 4.3.3 对象的传递
? 4.3.4 内部类的使用
? 4.4 对象的清除
4.1 对象的声明与创建
2012-3-7 Java面向对象程序设计教程 4
基本术语:类
? 类( Class)
可以由用户自己定义的引用类型,它是相
似对象的软件抽象、创建对象的模板。
? 每一个类都组合了一些能够反映某种类型
属性的字段和反映某种类型动作的方法。
2012-3-7 Java面向对象程序设计教程 5
基本术语:对象
? 对象( Object)
是一个人、地址、物品、事件、概念、屏
幕显示或者报表,它属于某一个类,对象
拥有自己的属性值和方法调用。
? 作为类设计的初衷,每一个类都将存在许
多相互独立的对象。
? 通常我们说一个对象是类的一个实例,一
个对象的生命周期包括声明、创建、使用
和清除四个状态。
2012-3-7 Java面向对象程序设计教程 6
基本术语:对象引用
? 在 Java语言中只能引用对象,无法声明一
个直接包含对象的变量。
? 类类型变量称为引用变量,它们并不直接
保留对象,只是保留对象的地址,而不是
拥有对象本身,通过引用变量即可以得到
一个对象。
? 所有对象总是且只是通过内存引用访问的,
这种方式称为间接寻址。
? 对象引用可以在需要的时候自动撤销。
2012-3-7 Java面向对象程序设计教程 7
类、对象和对象引用的关系示意图
2012-3-7 Java面向对象程序设计教程 8
基本术语:类变量
? 类变量必须在类体中声明,并且使用关键字
,static‖,因此也称为静态变量。
? 类变量在类被加载时完成相应的初始化工作。
? 类变量在一个运行系统中只有一份供整个类和实
例对象共享的值,该值有可能被类(及其子类)
和它们所创建的实例改变,每一次的改变都将影
响到该类(及其子类)和其它实例的调用。
? 类变量的作用域是整个类。
2012-3-7 Java面向对象程序设计教程 9
基本术语:实例变量
? 实例变量在类体中声明但不使用关键字
,static‖,也称为非静态变量。
? 它在对象初始化时完成相应的初始化工作,
并由某一个对象独自拥有。
? 实例变量的作用域是某一个类具体创建的
实例对象。
2012-3-7 Java面向对象程序设计教程 10
基本术语:局部变量
? 局部变量在某一个语句(如 for语句)或代码块
(如方法体)中声明。
? 当该语句或代码块被调用时,每次都将立即创建
相关的局部变量。
? 在一个局部变量的声明语句中可能包含有用于初
始化的表达式,但是,该表达式一般要等到这条
声明语句被执行的时候才会进行相应的初始化。
当该语句或代码块被执行完毕退出,相应的局部
变量也将失效。
? 局部变量的作用域是声明该变量的语句或代码块。
2012-3-7 Java面向对象程序设计教程 11
基本术语:参数变量
? 方法和构造方法用于传递信息的参数
( Argument)变量和作用于方法体的局
部变量相类似,当方法被调用的时候进行
相应的初始化,当方法体执行结束后失效。
2012-3-7 Java面向对象程序设计教程 12
变量举例
? 类变量和实例变量都拥有一个缺省初值,
如果某个变量在初始化时没有显式赋值,
那么将被赋予缺省初值。
? 局部变量则没有缺省初值,如果某个变量
在初始化时没有显式赋值,并且在其后的
语句中试图直接调用,Java编译器将会报
错提示,以保证每个变量都是按照编程人
员的意图进行赋值。
? 举例,VariableInit.java
2012-3-7 Java面向对象程序设计教程 13
讨论以下程序的问题所在
public class InitializationDemo{
int score = 100;
public String isExcellent(){
String result;
if(score>=90)
result = "Very very good!";
return result;
}
public static void main(String[] args){
InitializationDemo obj = new InitializationDemo();
System.out.print(obj.isExcellent());
}
}
2012-3-7 Java面向对象程序设计教程 14
基本术语:关键字 this
? this只能用于与实例有关的代码块中,如实例方
法、构造方法、实例初始化代码块或实例变量的
初始化代码块等,this就代表当前或者正在创建
的实例对象的引用,通常可以利用这一关键字实
现与局部变量同名的实例变量的调用。
? 在构造方法中还可以用 this来代表要显式调用的
其它构造方法。
? 除此以外,使用 this关键字都将引发编译时错误。
2012-3-7 Java面向对象程序设计教程 15
基本术语:关键字 super
? super只能用于与实例有关的代码块中,如实例
方法、构造方法、实例初始化代码块或实例变量
的初始化代码块等,super代表当前或者正在创
建的实例对象的父类,通常可以利用这一关键字
实现对父类同名属性或方法的调用。
? 在构造方法中还可以用 super来代表要调用的父
类构造方法,以实现构造方法链的初始化。
? 由于 Object类为 Java语言的根类,已经没有父类,
因此,如果在 Object类中使用了关键字 super,
将引发编译时错误。
2012-3-7 Java面向对象程序设计教程 16
this和 super举例
? this和 super是在设计类的时候、而不是使
用类及其实例的时候引用。
? 举例,ThisSuper.java
2012-3-7 Java面向对象程序设计教程 17
基本术语:访问权限修饰符
? 访问权限控制由强到弱的顺序是 public,protected、
( default),private。
? public访问权限意味着在任何地方,只要能访问到该类,
即可访问到该成员。
? protected访问权限意味着类中的成员允许同一个包或该
类的扩展子类访问。
? 缺省的访问权限不需要任何关键字,但通常称为
,Friendly‖。它意味着类中的成员只能由同一个包中的
其它类访问。
? private访问权限意味着该类中的成员只能由自己的成员
调用,而无法被该类以外的其它类访问。
? 声明局部变量不能包含访问权限修饰符,否则编译时将检
查出错。
2012-3-7 Java面向对象程序设计教程 18
存储对象状态的地方
? 寄存器( Register) JVM会根据本身需求适当地分配 。
? 堆( Heap) 堆是个自由内存区域,常用于动态或临时内存分配,对
类和数组对象提供内存。
? 栈( Stack) 栈存储方法调用的状态,如方法调用使用的任何局部
变量和方法的操作数。
? 方法区( Method area) 所有 JVM线程共享的公用存储区,存储运
行时常量池、方法数据、字段数据、方法与构造方法字节码等信息。
? 运行时常量池( Runtime constant pool) 类似于其它编程语言中
的符号表,它包含数值文字和字段常量之类的常量。
? 非运行时存储空间
2012-3-7 Java面向对象程序设计教程 19
对象的声明
? Java作为一种强类型语言,声明基本类型或引用
类型都要先指定类型,然后给出相应的变量标识
符,
Type Identifier;
? fianl型变量只能赋值一次,而且必须被显式赋值,
否则编译时将有检查错误提示。
? fianl型基本类型变量相当于常量。
? 对于引用型变量,当声明为, final‖时,如果该变量引
用了一个对象,则意味着该变量只能引用该对象,而
该对象的状态仍然可以被改变;如果该变量引用了一
个数组,则意味着该变量只能引用该数组,而该数组
的元素仍然可以被改变。
? 举例,FinalVariable.java
2012-3-7 Java面向对象程序设计教程 20
对象的创建
? 利用关键字 new可以创建一个对象,new运算符
为对象分配内存空间,调用该类的一个构造方法,
实例化一个对象,返回该对象所在内存地址的一
个引用。
? 该对象引用可以赋给相应类型的对象变量,以后
可以通过该变量操作所引用的对象。
? 如果创建该对象时没有把引用赋给某个对象变量,
则该对象将无法再被引用。
? 举例,Employee.java
2012-3-7 Java面向对象程序设计教程 21
数组对象的声明
? 数组是一种用来存储相同类型数据项的数
据结构,它由一组具有相同类型和相同变
量名,放在相邻内存位置的元素构成。
? 数组声明语句,
Type[] Identifier;
? 在任何数组变量的类型声明中,数组维数
都被忽略了,数组元素的个数是利用 new
运算符创建时、而不是在声明时决定的。
2012-3-7 Java面向对象程序设计教程 22
数组对象的创建
? 要创建 Java的数组对象,通常使用 new运算符进
行实例化。当第 1次生成数组时,应指定这个数组
拥有的元素是多少,也就是数组的大小。
? 数组对象的长度在创建时限定后,就不能再被改
变。
? 但可以在任何时候把一个不同长度的新数组赋值
给数组变量 Identifier。
? 数组第一个元素的下标为 0,最后一个元素的下标
是 (length-1),其中 length为数组长度,即数组
元素的个数。
? 创建数组的大小可以是 0,一个长度为 0的数组被
称为 empty(空)数组。
讨论:, int[] i=new int[0];‖和, int[] i=null;‖
2012-3-7 Java面向对象程序设计教程 23
数组举例
? 数组初始化,ArrayInit.java
?, int[] i=new int[0];‖和, int[] i=null;‖区
别
? 数组操作,ArrayManipulation.java
?Arrays类及其方法
? 数组复制,ArraycopyExample.java
?单层复制与多层复制的实现
4.2 对象的初始化
2012-3-7 Java面向对象程序设计教程 25
成员变量的缺省初始化过程
? 创建对象时需要对属于对象的成员变量进行初始
化。
? 无论成员变量是声明在其它方法的前面或者后面,
其初始化操作总会在方法调用之前合适的时机执
行,并按顺序逐步完成。
? 与方法中声明的局部变量不一样,为了保证数据
成员变量在使用前被有效初始化,JVM会在类加
载时对所有数据成员变量赋予缺省的初值。
? 这一步骤总是首先被执行的,当没有显式对某个
变量赋值时,缺省值的使用可以避免引发异常而
导致崩溃。
2012-3-7 Java面向对象程序设计教程 26
类型的缺省值
类型 缺 省 值
boolean false
char ‘ \u0000’ (null)
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d
引用类型 null
2012-3-7 Java面向对象程序设计教程 27
成员变量的显式初始化过程
? 显式对每个变量赋值进行初始化,这时
JVM会把按照类成员变量和实例成员变量
的不同将它们分别移入静态代码块( Static
block)或构造方法体( Constructor
body)中执行,将相应的值赋予该变量。
? 这是一种良好的编程习惯!
2012-3-7 Java面向对象程序设计教程 28
类成员变量的初始化与静态代码块
? 类成员变量的标志是变量声明时使用关键
字 static,它在类加载时完成初始化,并且
保持到该类被清除为止,此期间类及其实
例共享着同样一份数据;同时静态代码块
也在类加载时被执行。
2012-3-7 Java面向对象程序设计教程 29
例 子, java InitializationDemo ?
public class InitializationDemo{
static String str="aaa ";
static{
str="bbb ";
}
InitializationDemo(){
str="ccc ";
}
public static void main(String[] args){
System.out.print(str);
}
}
静态变量
静态代码块
输出 str的值,
"aaa "? ―bbb "? ―ccc "?
构造方法
2012-3-7 Java面向对象程序设计教程 30
分 析
? 观看类加载顺序,
java -verbose InitializationDemo ?
? 代码
static String str="aaa ";
static{
str="bbb ";
}
等价于
static String str;
static{
str="aaa ";
str="bbb ";
}
? 执行 javap –c InitializationDemo验证
? 类加载时,以上语句已被执行,因此进入 main方法后输出结果为
―bbb ‖。注意:构造方法 InitializationDemo()并没有被执行。
? 进一步的例子,AStatic.java
顺序相同
2012-3-7 Java面向对象程序设计教程 31
实例成员变量的初始化与构造方法
? 实例成员变量,相应的显式赋值语句和实
例代码块将按顺序被移入构造方法体中。
? 与静态语句不同的是,这些语句无论在构
造方法体的前面或者后面,移入后总是首
先被执行,然后执行构造方法体中原有的
语句。
2012-3-7 Java面向对象程序设计教程 32
例 子, java InitializationDemo ?
public class InitializationDemo{
String str="aaa ";
InitializationDemo(){
str="ccc ";
}
{
str="bbb ";
}
public static void main(String[] args){
InitializationDemo obj=new InitializationDemo();
System.out.print(obj.str);
}
}
实例变量
非静态代码块
输出 str的值,
"aaa "? ―bbb "? ―ccc "?
构造方法
2012-3-7 Java面向对象程序设计教程 33
分 析
? 代码
String str="aaa ";
InitializationDemo(){
str="ccc ";
}
{
str="bbb ";
}
等价于
static String str;
InitializationDemo(){
str="aaa ";
str="bbb ";
str="ccc ";
}
? 执行 javap –c InitializationDemo验证
? 进入 main方法后,实例化时,以上语句已被执行,因此输出结果为 ―ccc ‖。
? 进一步的例子,ANonStatic.java
顺序相同
2012-3-7 Java面向对象程序设计教程 34
讨论
? 程序健壮性的代价和编码启示
? 类成员变量与实例成员变量初始化的区别,
? 加载时机是不同的:前者在类加载时完成初始化,后
者则是在创建实例对象时完成初始化。
? 变量初始化后的保持时间也是不同的:类成员变量的
数据一直保持到类对象被终止(退出 JVM),在此期
间无论创建多少个实例对象,它都不再进行初始化,
而只是为实例对象提供共享数据;实例成员变量则只
保持到该实例对象被清除之时,无论创建多少个实例
对象,它每次都要进行相应的初始化而彼此之间互不
影响。
? 进一步的例子,
MemberInit.java ; EggOrChicken.java
2012-3-7 Java面向对象程序设计教程 35
继承链上相关类的加载顺序
? 类模块的层次结构将在子类及其直接或间
接超类间形成一条继承链。
? 在 Java语言中,每一条继承链都从其根类
Object开始。
? 所有直接或间接超类总是在当前子类之前
被加载,该类实现的接口也在当前子类之
前被加载。
? 举例,InheritanceChain.java
2012-3-7 Java面向对象程序设计教程 36
构造方法链的调用
? 构造方法链是指当创建某个类的实例时,它的构
造方法及其超类的构造方法都将被调用。
? 此过程将从当前类的构造方法开始,一直追溯到
Java语言的根类 Object;然后从 Object类的构
造方法开始依次执行,直到欲创建实例的当前类
为止,完成相应的初始化工作。
? 这一过程将保证当前创建的对象中从继承链中获
得的实例变量都将有效地进行初始化,使得它们
能够被正确地使用。
? 举例,ConstructorChain.java
2012-3-7 Java面向对象程序设计教程 37
讨论
? 如何使得当类的实例对象形成时总能完成
相应构造方法链的调用?
缺省机制举例,MyClass.java
? 构造方法的迷惑举例,
PuzzledConstructor.java
4.3 对象的使用
2012-3-7 Java面向对象程序设计教程 39
多态的使用
? 多态机制允许继承链上存在重名的变量或
方法。
? 多态性实现用继承链上的超类来管理其扩
展类的实例对象。
? 转型所带来的重名成员的调用问题。
? 向上自动转型与向下手工转型。
2012-3-7 Java面向对象程序设计教程 40
重载
? 重载代表着实现某个类内
部一种行为的多种手段,
设计类时最常用的是定义
多个构造方法,以满足在
不同的初始化条件下创建
该类的实例对象。
? 举例,Circle.java
? java.io.PrintStream类
中重载的 print方法,
print(boolean b)
print(char c)
print(char[] s)
print(double d)
print (float f)
print (int i)
print(long l)
print(Object obj)
print(String s)
2012-3-7 Java面向对象程序设计教程 41
覆盖
? 覆盖则代表着相关的类之间某一种特征的
多种行为。
? 当设计子类的方法时遇到和父类相似的特
征,而最恰当的方法名已经被父类使用了,
这时可能的解决方法,
?一种办法是把将要设计的子类方法名用一个还
没有被继承链上超类使用过的名字来命名;
?另一种办法是同样采用已经被超类使用过的最
恰当的名字 ——但必须由多态性得以保证。
2012-3-7 Java面向对象程序设计教程 42
使用不同名字的方式
2012-3-7 Java面向对象程序设计教程 43
利用多态机制的重名方式
? 举例,OverrideTest.java
2012-3-7 Java面向对象程序设计教程 44
处理与类型有关的对象,
使用时进行,if‖判断
//创建一个 Shape类的数组对象 shapes,用来存放所有扩展类对象
for(int i=0; i<shapes.length; i++){
if(shapes[i] instanceof Oval)
drawOval();
else if(shapes[i] instanceof Quatrangle)
drawQuatrangle();
else if(shapes[i] instanceof Triangle)
drawTriangle();
else if(shapes[i] instanceof Square)
drawSquare();
else if(shapes[i] instanceof Rectangle)
drawRectangle();
else if …
}
2012-3-7 Java面向对象程序设计教程 45
处理与类型有关的对象,
设计时进行,if‖判断
public void draw(){
//创建一个 Shape类的数组对象 shapes,用来存放所有扩展类对象
for(int i=0; i<shapes.length; i++){
if(shapes[i] instanceof Oval)
drawOval();
else if(shapes[i] instanceof Quatrangle)
drawQuatrangle();
else if(shapes[i] instanceof Triangle)
drawTriangle();
else if(shapes[i] instanceof Square)
drawSquare();
else if(shapes[i] instanceof Rectangle)
drawRectangle();
else if …
}
}
2012-3-7 Java面向对象程序设计教程 46
处理与类型有关的对象,
多态方式
//创建一个 Shape类的数组对象 shapes,用
来存放所有扩展类对象
for(int i=0; i<shapes.length; i++)
shapes[i].draw();
2012-3-7 Java面向对象程序设计教程 47
转型时重名成员的调用问题
? 举例,ConversionTest.java
? 对于重名的成员变量而言,无论是静态的还是非静态的,
当以某个超类声明而以扩展类实例化时,该变量将反映该
超类的性质,而用来实例化的扩展类变量性质将被隐藏掉。
这或许不是我们所希望的。因此,把变量设为 private,
然后转化为相应的成员方法除了方便控制访问权限外,也
才有可能利用对象的多态机制。
? 对于重名的静态成员方法,实际上也是反映该超类的性质,
并不具有我们所理解的多态性质。
? 我们所谓的扩展类方法覆盖超类方法的多态机制只是对非
静态(实例)方法而言的,其它情况则不能实现。
? 而且,这些非静态方法的可访问作用域还必须包含其子类
方可被覆盖,否则即便在子类中有相同构型的方法,依然
无法实现。
2012-3-7 Java面向对象程序设计教程 48
讨论
? 转型时在扩展类的实例中能否访问到超类
中被覆盖的实例方法?
?举例,ConversionTest.java中 Super类的
showNonStatic方法
? 转型时子类中细节内容的调用 ——向下手
工转型
?举例,This类的 showDetail方法
2012-3-7 Java面向对象程序设计教程 49
引用类型变量和对象本身之间的
松绑定机制
? 我们通过引用类型变量获得某个对象的地址,从
而可以访问该对象的数据;也就是说,当前引用
类型变量所指向的对象就是我们正在使用的对象。
? 当引用类型变量与当前的对象分离开并被重新赋
值后,该对象将不再被使用。
? 当一个对象同时被多个引用类型变量所引用,则
我们可以通过这些引用类型变量共享同一个对象。
? 当某个对象不被任何一个引用类型变量所引用时,
该对象就可以被清除了。
? 举例,WhoIsUse.java
2012-3-7 Java面向对象程序设计教程 50
关系运算符,==‖ 何时返回 true?
? 在比较两个同类型的基本类型变量时,如
果二者的值相同则返回 true,否则返回
false;
? 在比较两个同类型的引用类型变量时,如
果二者指向 同一个对象 则返回 true,否则
返回 false。
2012-3-7 Java面向对象程序设计教程 51
讨论,PuzzledString.java
String a1="test";
String b1="test";
? ―a1==b1‖返回 true还是 false?
String a=new String("test");
String b=new String("test");
? ―a==b‖返回 true还是 false?
2012-3-7 Java面向对象程序设计教程 52
equals方法的讨论(一)
? Object类的 equals方法比较对象本身,
public boolean equals(Object obj){
return(this==obj);
}
? Integer类的 equals方法设计成比较数值,
public boolean equals(Object obj){
if(obj instanceof Integer){
return value==((Integer)obj).intValue();
}
return false;
}
? String类的 equals方法设计成比较字符串内容,
参见 src.zip 中 String.java
2012-3-7 Java面向对象程序设计教程 53
equals方法的讨论(二)
? 对于自定义的类,则要根据自己的需要重
写 equals方法:返回 true还是 false将根据
设计意图而定,切不可妄下结论!
? 举例,
ObjectEqual.java
ObjectEqualUpdate.java
2012-3-7 Java面向对象程序设计教程 54
例证:引用类型变量和对象本身之间的关系
new StringBuffer("Contents");
StringBuffer str=new StringBuffer("Contents");
StringBuffer strTest=str;
strTest.append(" add Something.");
strTest=null;
StringBuffer strLure=new StringBuffer("Give you an apple,then leave him!");
strTest=strLure;
strTest=new StringBuffer("It is my business!");
? 例子,AboutReference.java
创建一个对象
strTest通过 str间接得到对象的引用
通过 strTest改变对象内容
变量改变指向:, 改弦易辙, 型
变量改变指向:, 分道扬镳, 型
变量改变指向:, 自立门户, 型
str得到所创建对象的引用
创建了几个对象?
str改变了吗?
str改变了吗?
2012-3-7 Java面向对象程序设计教程 55
方法参数传递的方式
? 基本类型作为方法参数传递均按值传递,
在方法体中对形参的改变并不影响原来的
变量;
? 所有的对象作为方法参数传递均按引用传
递,在方法体中对形参的改变将影响原来
的变量,因为它们都指向同一个对象。
? 举例,MethodArgument.java
2012-3-7 Java面向对象程序设计教程 56
引用类型变量的按值传递
? 当我们把引用类型变量作为实参传给方法
时,仅仅是把实参变量的引用值(表示逻
辑内存地址数据)赋给形参变量,而形参
变量却无法逆向地通过改变自身以达到改
变实参引用值的目的。
? 由于在处理引用变量与对象本身关系时,
C++采用紧绑定机制,而 Java采用松绑定
机制,因此有些例子结论有所不同。
2012-3-7 Java面向对象程序设计教程 57
C++语言按引用传递的例子
// C++语言利用指针变量操控参考的演示
#include <iostream>
using namespace std;
// 定义一个反映坐标点的类
class Coord {
public,
int x;
int y;
};
// Swap函数可以用来交换两个 Coord对象的内容
void swap(Coord &a,Coord &b){
Coord temp;
// 交换两个 Coord对象的内容的操作
temp=a;
a=b;
b=temp;
}
// 定义主函数
int main(){
// 声明两个 Coord对象
Coord ob1,ob2;
// 并赋值
ob1.x=10;
ob1.y=20;
ob2.x=88;
ob2.y=99;
// 输出交换前两个对象的状态
cout << ″交换前两个对象的状态,\n″;
cout << ″ob1,″ << ob1.x << ″,″ << ob1.y << ″\n″;
cout << ″ob2,″ << ob2.x << ″,″ << ob2.y << ″\n″;
swap(ob1,ob2);
// 输出交换后两个对象的状态
cout << ″交换后两个对象的状态,\n″;
cout << ″ob1,″ << ob1.x << ″,″ << ob1.y << ″\n″;
cout << ″ob2,″ << ob2.x << ″,″ << ob2.y << ″\n″;
return 0;
}
交换前两个对象的状态,
ob1,10,20
ob2,88,99
交换后两个对象的状态,
ob1,88,99
ob2,10,20
结论:实参变量所指向的对象
状态已被改变。
2012-3-7 Java面向对象程序设计教程 58
Java语言按引用传递的例子
//,SwapDemo.java
//package g3ds.joop.ch4;
// 定义一个反映坐标点的类
class Coord {
int x;
int y;
}
public class SwapDemo{
// 定义一个和 C++版本程序段类似的 Swap方法
static void swap(Coord a,Coord b){
Coord temp=new Coord();
temp=a;
a=b;
b=temp;
}
// 测试用的主方法
public static void main(String[] args){
Coord ob1=new Coord();
Coord ob2=new Coord();
ob1.x=10;
ob1.y=20;
ob2.x=88;
ob2.y=99;
// 输出运行 Swap方法前两个对象的状态
System.out.println(″运行 Swap方法前两个对象的状态, ″);
System.out.println(″ob1,″+ob1.x+″,″+ob1.y);
System.out.println(″ob2,″+ob2.x+″,″+ob2.y);
swap(ob1,ob2);
// 输出运行 Swap方法后两个对象的状态
System.out.println(″运行 Swap方法后两个对象的状态, ″);
System.out.println(″ob1,″+ob1.x+″,″+ob1.y);
System.out.println(″ob2,″+ob2.x+″,″+ob2.y);
}
}
运行 Swap方法前两个对象的状态,
ob1,10,20
ob2,88,99
运行 Swap方法后两个对象的状态,
ob1,10,20
ob2,88,99
结论:实参变量所指向的对象
状态未被改变。
2012-3-7 Java面向对象程序设计教程 59
讨论,PassByRefValue的执行过程
public class PassByRefValue{
public static void main(String[] agrs){
StringBuffer str1=new StringBuffer(″abc″);
StringBuffer str2=new StringBuffer(″123″);
PassByRefValue byRef=new PassByRefValue();
System.out.println(″main->str1 before swap method,″+str1);
System.out.println(″main->str2 before swap method,″+str2);
byRef.swap(str1,str2);
System.out.println(″main->str1 after swap method,″+str1);
System.out.println(″main->str2 after swap method,″+str2);
}
void swap(StringBuffer str1,StringBuffer str2){
StringBuffer temp=str1;
str1=str2;
str2=temp;
System.out.println(″swap->str1 in swap method,″+str1);
System.out.println(″swap->str2 in swap method,″+str2);
}
}
记作
main->str1
main->str2
记作
swap->str1
swap->str2
main->str1指向 abc
main->str2指向 123
通过 byRef调用 swap方法
main->str1赋予 swap->str1,此时 main->str1,swap->str1指向 abc
main->str2赋予 swap->str2,此时 main->str2,swap->str2指向 123
swap->temp,swap->str1,main->str1指向 abc
swap->temp,main->str1指向 abc
swap->str1,swap->str2,main->str2指向 123
swap->str2,sw p->temp,main->str1指向 abc
swap->str1,main->str2指向 123
输出
main->str1
main->str2
输出
main->str1
main->str2
输出
swap->str1
swap->str2
main-> str1 before swap method,abc
main-> str2 before swap method,123
swap-> str1 in swap method,123
swap-> str2 in swap method,abc
main-> str1 after swap method,abc
main-> str2 after swap method,123
2012-3-7 Java面向对象程序设计教程 60
进一步讨论
? 通过 clone方法实现对象的复制
? 举例,ObjectClone.java
2012-3-7 Java面向对象程序设计教程 61
内部类
? 在一个顶层类( Top level class)中声明另一个
类作为它的成员,这一些类称为内嵌类,非静态
的成员类有时也直接称为内部类。
? 内嵌类和它的顶层类可以分别有不同的继承关系
和实现不同的接口类型。
? 内部类也可以设计成抽象类或者接口。内部类的
声明方式跟一般的类没什么不同,不过就是外部
类的成员。
? 内部类可以使用任意一个访问限定词,不像一般
类只能使用 public和缺省( default)两个。
2012-3-7 Java面向对象程序设计教程 62
内部类的作用
? 程序设计时,有些逻辑上相关的类必须是
要伴随另一个类的存在才有意义,如果两
者分开,可能在类的管理上比较麻烦,所
以我们可以把这样的类写成一个内部类。
? 内部类从结构上看是它所在外部类(顶层
类)的组成部分,理所当然可以访问该外
部类的任何成员,包含声明为 private的成
员。
2012-3-7 Java面向对象程序设计教程 63
内部类的形式
? 作用域范围在整个类中的内部类可分为静
态成员类和实例成员类 ;
? 作用域范围仅属于一个方法甚至某个语句
的内部类称为局部成员类 ;
? 有些局部成员类甚至没有直接给出类名,
称为匿名成员类。
2012-3-7 Java面向对象程序设计教程 64
内部类的命名规则
? 一般是外部类名和相应的内部类名通过, $‖
符号连接起来,如
InnerClass$InstanceInner.class
? 内部类嵌套层数越深,相应的内部类名就
越长,
? 匿名类则用相应的数字符号表示。
2012-3-7 Java面向对象程序设计教程 65
内部类的使用
? 静态成员类加上了 static限定词,让别的类在访问时可以
通过外部类的名称来直接访问。
? 实例成员类就必须先生成外部类的对象后,才能通过这个
对象来访问。
? 在内部类中,除了静态成员类以外,所有非静态成员类体
内不能再声明带有 static关键字的成员。
? 关键字 this指的是内部类的实例,如果要访问外部类的同
名变量则是在 this之前加上外部类的类名。
? 局部成员类在访问包含它的方法体中的其它变量时,该变
量必须是个常量,也就是说声明时要加上 final关键字。
? 局部成员类只能在包含它的方法体中使用,其它的方法或
类基本上是访问不到它的(除非通过方法返回对象)。
2012-3-7 Java面向对象程序设计教程 66
匿名类
? 匿名类也属于局部成员类,但作用域范围最小。
? 匿名类最常用于设计事件处理类,因为这些类既多处理的
事情又单一,我们专门为其命名相当费劲,因此就把这个
命名工作交给 Java的编译器,让它在编译时根据一定的规
则进行命名。
? 不能在其它的场合中访问匿名类了。
? 由于定义匿名类总是类似于
new AnonymousInner(){};
因此没有办法在其中加入任何限定词。
? AnonymousInner只能是一个接口、抽象类或者一般类
(但不能是 final型的)。
? 举例,InnerClass.java
4.4 对象的清除
2012-3-7 Java面向对象程序设计教程 68
垃圾自动回收机制
? Java虚拟机采用一种称为垃圾回收的技术,以确
保仍被引用着的对象仍会驻留在内存里,而当对
象在程序中不再被引用时,它所占用的内存空间
就会被释放。
? 垃圾自动回收机制意味着不必担心所谓的悬挂引
用问题的发生,因为在程序的某处尚被引用着的
对象是不会被当作垃圾回收的。
? 垃圾回收并不能保证总有可用的内存给新生对象,
而且,垃圾回收也确实是个需要内存开销的过程。
2012-3-7 Java面向对象程序设计教程 69
垃圾回收的逻辑过程
? 首先要区分开, 活, 对象和, 死, 对象,
活对象是指那些在执行代码中仍被使用着
的对象,死对象就是要被回收的垃圾;然
后,回收死对象的内存空间。
2012-3-7 Java面向对象程序设计教程 70
垃圾回收模型的引用计数模型
? 当对象 X引用对象 Y时,系统会增加 Y上的引用计
数,而当 X不再引用对象 Y时,系统会减少它的引
用计数。当 Y上的引用计数器减为零之后,Y对象
即不再存活,并可以被回收了,这也同时会使 Y引
用的所有对象的引用计数分别减 1。
? 引用计数模型对于出现循环引用问题时是无效的:
如果 X和 Y彼此引用对方,那么它们的引用计数器
都不会减到零,所以也不会被垃圾回收。同样,
它们直接和间接引用的其它对象也不会因为被判
为不再存活而被回收。
2012-3-7 Java面向对象程序设计教程 71
垃圾回收模型的“标记 —清除”模
型
? 为了找到哪些对象还活跃着,首先要确定一个根对象的集
合,这包括了那些直接可达的对象:例如,在栈里的局部
变量中的对象引用是可达的,因为我们可以用这些变量来
操作对象,所以被局部变量所引用的对象当然是存活的。
一旦确定了根对象的集合,垃圾收集器就可以把那些被根
对象所引用的对象标记为可达对象。
? 然后,它会继续检查这些对象中所包含的对象引用:若被
这些引用所指的对象在第一步中已标为可达的,那么就把
它忽略过去,否则被引用对象会被标记为可达的;接下来
再检查这个刚被打过标记的对象中对其它对象的引用;这
个过程一直进行到没有可达对象尚未标记为止。
? 当整个打标记过程结束之后,死对象就可以从内存中被清
除了。
2012-3-7 Java面向对象程序设计教程 72
Runtime类以及 System类中
与垃圾回收有关的一些常用方法
? public void gc( )
该方法请求虚拟机执行无用对象的回收任务,以
便重用内存资源,
? public void runFinalization( )
该方法请求虚拟机在那些不可达的对象上,调用
从前还没有执行过的 finalize方法。
? public long freeMemory( )
该方法返回系统中可用内存字节数的估计值。
? public long totalMemory( )
该方法返回系统中全部内存资源字节值。
? 举例,FinalizeTest.java
期中复习小测 ……