Upload
nash-burks
View
80
Download
3
Embed Size (px)
DESCRIPTION
第 3 章 Java 与面向对象技术. 广东工业大学计算机学院. 本章内容. 3.1 面向对象编程基础 3.2 类的编程实现 3.3 对象的使用 3.4 包. 3.5 文档注释 3.6 类设计技巧 3.7 类的继承实例 3.8 多态. 什么是面向对象. 面向对象 — 将现实世界的复杂事物抽象为一个个对象 如 : 灯泡 对象的二要素 — 状态和行为 如:灯泡的状态有:亮或灭,功率,电压 行为有:开、关 软件对象 — 对现实对象进行模拟,状态用变量表示,行为用方法实现。 - PowerPoint PPT Presentation
Citation preview
LOGO
第 3 章Java 与面向对象技术
广东工业大学计算机学院
广东工业大学计算机学院2
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院3
什么是面向对象 面向对象 — 将现实世界的复杂事物抽象为一个个对象 如 : 灯泡 对象的二要素 — 状态和行为 如:灯泡的状态有:亮或灭,功率,电压 行为有:开、关 软件对象 — 对现实对象进行模拟,状态用变量表示,行为用方法实现。 — 由数据 ( 变量 ) 和相关方法组成的软件包。 面向对象程序设计—以对象作为编程基础的程序设计方法。
广东工业大学计算机学院4
面向对象的基本概念( 1 )
class Car { int color_number; int door_number; int speed; void brake() { … }; void speedUp() {…}; void slowDown() { … };}
Car 类类 :对有共同特点的软件对象的抽象。
• 货车• 小轿车• 公交车
…
抽象
广东工业大学计算机学院5
类是描述一类对象的“基本原型”,它定义一种对象所拥有的数据和能完成的操作,在面向对象的程序设计中,类是程序的基本单元。对象在程序中的是类的一个实例。类产生实例的过程充分体现了类的可重用性。
面向对象的基本概念( 2 )
广东工业大学计算机学院6 广东工业大学计算机学院6
封装把对象的所有组成部分组合在一起,并使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。
封装定义程序如何引用对象的数据 方法
数据
对象 B
私有数据
方法对象 A
公有数据
面向对象基本概念( 3 )
广东工业大学计算机学院7
一个新类可以通过继承已有类的数据和方法的基础上,添加部分新的数据和方法得到,这种性质称为类的继承性。用继承方法得到的新类称为原类的子类, 而原类对子类而言成为超类。父类与子类是一般与特殊的关系。
面向对象的基本概念( 4 )
广东工业大学计算机学院8 广东工业大学计算机学院8
面向对象基本概念( 5 )重载 - 一个类可包含多个名字相同的方法,但这些方法的参数不同。
类void show(int int_num)
void show(double double_num)
void show(int int_ um, float float_um)
123
1.23
123,1.23
广东工业大学计算机学院9
面向对象基本概念( 6 )多态性:允许将父对象设置成为一个或多个它的子类对象相等,在赋值之后,父对象就可以根据当前赋值给它的子类对象的特性以不同的方式运作的特性。
Employee
Manager
TeamLeader
Programmer
getSalary( )
getSalary( )
getSalary( )
Employee e;
Manager m;
TeamLeader t;
…
e=m;
e.getsalary();
getSalary( )
广东工业大学计算机学院10
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院11
类定义Java 中,类定义的基本语法如下:[Specifier] class ClassName [extends
SuperClassName] [implements InterfaceName]{ // 类的域(成员变量)定义 // 类的方法定义 … }
例如 :public class School { private ArrayList students;
private void addStudent(Person person) { students.add(person); }}
广东工业大学计算机学院12
域的定义域 ( 成员变量 / 属性 / 字段 ) 定义的方式如下:
[Specifier] type variableName;
例如:int x = 3, y; // 定义了整型变量 x 和 ypublic Date today ; // 定义了 Date 型类变量
todayprivate Char [ ] m ; // 定义了字符型数组变量
m
广东工业大学计算机学院13
方法的定义 方法由方法头和方法体构成,方法定义的语法如下:[Specifier] [static] returnType methodName([parameterList]){ //method body}
例如:private String getName(Person person){ …… return …;}
public static void main(String[] args){ ……}
广东工业大学计算机学院14
public class Person { // 方法定义 public String getName( ) { return name; } public void setName(String s) { name = s; } public double getIncome( ) { return salary; } public void setIncome(double d) { salary = d; }
// 域定义 private String name; // 姓名 private int sex; // 性别 private double salary; // 薪水 private int age; // 年龄 }
类定义举例一种常用的编程风格是:1. 方法位于域的前面
2. 公有 (public) 方法位于所有方法的最前面
3. 一般情况下,将成员变量定义为 private
广东工业大学计算机学院15
final 的类成员变量被定义为 final 的类成员变量在对象初始化之后,不会再被修改。例如:
public class Employee { …… private final String name;}
注意: final 修饰符大多应用于基本数据类型(primitive) 的数据,或者不可变(immutable) 类的域。如果类中每个方法都不会改变对象的状态,这种类就是不可变类。
广东工业大学计算机学院16
final应用于可变对象有可能造成混乱对于可变的类,使用 final 修饰符有可能对读者造成混乱。例如:
public class Employee { private final Date hireDate;}
此语句仅仅意味着定义了一个 Date 类型的引用hireDate ,该引用在 Employee 对象初始化之后不能改变;
但不是指该引用指向的 hireDate 对象的状态不能发生改变。
广东工业大学计算机学院17
静态 (static) 域( 1 )如果将某个类中的域定义为 static ,那么该类的所有对象都会共享这个域。例如:假定需要给每一个雇员赋予一个唯一的标识码,可对 Employee 类添加一个实例域 id 和一个静态域 nextId 。
public class Employee { private int id; private static int nextId = 1;}
广东工业大学计算机学院18
静态 (static) 域( 2 )则每个雇员对象都有一个自己的 Id 域,但这个类的所有实例却共享一个 nextId 域。 nextId 域有何用途?
public void setId() { id = nextId; nextId++;}
假定为 harry雇员设定标识码:harry.setId();那么 harry 对象的 Id 域将被设置,并且静态域 nextId 的值加 1.
nextId 的用途是:指示下一个雇员应当分配的 ID 。
广东工业大学计算机学院19
静态域的注意事项 1. 静态 (static) 域不同于 final 域。静态不等于不变,静态域在程序运行过程中可以改变值。 2. 静态域在装载类的时候被分配内存并初始化,类只能被装载一次,所以静态变量在内存中只有一个拷贝,即使没有对象被建立,该静态域仍然存在;所有对象都共享一个静态域。 区别:非静态域在创建对象时被分配内存并初始化,所以每个对象都有各自的非静态域 3. 静态域用类名访问,而不是用对象访问。
广东工业大学计算机学院20
常量静态常量 ( 类常量 ) :
public class Math { public static final double PI = ……;}
在程序中,可以通过 Math.PI 来访问该常量。
广东工业大学计算机学院21
静态变量和实例变量示例public class Count { public Count() { counter++; serialNumber = counter; System.out.println("My serialNumber is " + serialNumber); }
public static void main(String args[]){
System.out.println("At first,counter="+ counter); Count count1=new Count();
System.out.println("after creat count1, counter="+counter); Count count2=new Count(); System.out.println("At last counter="+counter);
System.out.println("count1.serialNumber="+count1.serialNumber); System.out.println("count2.serialNumber="+count2.serialNumber); System.out.println("count1.counter="+count1.counter); System.out.println("Count.counter="+counter); }
private int serialNumber; private static int counter;
}
广东工业大学计算机学院22
方法的隐式与显式参数 public class Person { // 方法定义 …… public void setIncome(double d) { salary = d; }
// 域定义 …… private double salary; private int age; }
如果新建一个对象:Person menber007 = new Person();
并调用 setIncome 方法:menber007.setIncome(8888.88);
setIncome 方法有两个参数:•隐式参数 this
•显式参数 double d
广东工业大学计算机学院23
方法的注意事项( 1 ) 参数名可以与类的域名相同,如果相同,那么参数名将在方法体内隐藏同名的域。例如:public class Rectangle{ double x; double y; double computeArea(double x, double y){ double s = 0; s = x * y; return s; }}
当需要同时使用同名的方法参数和类的域名时,可以使用 this 关键字: double computeArea(double x, double y){ double s = 0; if (this.x > x) s = x * y; return s; }
广东工业大学计算机学院24
方法的注意事项( 2 )注意:不要编写返回引用可变对象的域的方法。public class Employee { …… public Date getHireDay( ) { return hireDay; } private Date hireDay;}
当遇到下面的代码,就能使 hireDay 字段的封装性被破坏:Employee harry = new Employee();Date date = harry.getHireDay();double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 *1000;date.setTime(date.getTime() – (long)tenYearsInMilliSeconds);
广东工业大学计算机学院25
破坏封装的过程 原因:上面的代码提供了另一条直接更改可变对象的状态的途径。
harry =
d =
Employee
name =
salary =
hireDay =
Date
years/months/days
解决办法:对于可变对象,仅由访问器方法提供一个对象副本。public class Employee { public Date getHireDay( ) { return (Date)hireDay.clone(); } private Date hireDay;}
经验:如果需要返回一个可变数据域的拷贝,就应该实现克隆。
广东工业大学计算机学院26
方法的参数传递方式 在程序设计语言中,有两种常见的参数传递方式: 1. 值调用 (call by value) :表示方法接收的是调用者提供的值。 2. 引用调用 (call by reference) :表示方法接收的是调用者特供的变量地址。 Java采用的是值调用。即方法得到的是所有参数值的一个拷贝,方法并不能改变实参的值。
广东工业大学计算机学院27
方法参数的类型一个方法不能修改一个基本数据类型的参数。例如:
public static doubleValue(double x) { x = 2 * x;}……double percent = 10;doubleValue(percent);
这样的调用语句并不能改变 percent 变量的值。
广东工业大学计算机学院28
方法参数的类型 ( 续 )
然而,方法参数有两种类型: 基本数类型(数值、布尔值) 对象引用
下列方法能够改变引用所指向的对象的状态:public static doubleValue(Employee e) { e.raiseSalary(200); // 将 e 对象的薪金提高 200}……Employee harry = new Employee(…);doubleValue(harry);
Java 对“对象引用”这种参数仍然是使用值传递的方式。
广东工业大学计算机学院29
Java 对“对象引用”类型参数的传递 在 Java 中,对对象引用参数是采用值传递的方式。
public static void swap (Employee x, Employee y) { Employee temp = x; x = y; y = temp;}……Employee alice = new Employee (“Alice”, …);Employee bob = new Employee (“Bob”, …);swap(alice, bob);……
在执行了 swap(alice, bob) 后, alice仍然是“ Alice” , bob仍然是“ Bob” 。 Why? 原因是: Java采用值传递方式,将 alice 和 bob 的副本传给了 swap 方法作参数,而 alice 和 bob 是对象的地址。
广东工业大学计算机学院30
Java 中引用的值传递图示
在 swap(x, y) 中,引用x 和引用 y的值已经被交换;
但在 swap(x, y)执行完毕后, x 和 y都被丢弃,原来的实参 alice 和 bob 的值不变。
广东工业大学计算机学院31
静态方法 静态方法是不能向对象实施操作的方法。又称为类方法。例如
public static int getNextId() { return nextId;}
注意:1. 静态方法没有 this 参数。2. 静态方法不能操作任何实例对象。3. 静态方法不能访问任何实例域。4. 静态方法不属于任何对象。
广东工业大学计算机学院32
静态方法注意1. 静态方法也和静态变量一样,不需创建类的实例,可以直接通过类名被访问。2. static 方法不能被修饰成 protected 和
abstract 。
广东工业大学计算机学院33
静态方法示例public class GeneralFunction { public static int addUp(int x, int y) { return x + y; }}
public class UseGeneral { public static void main (String[] args) { int a = 9; int b = 10; int c = GeneralFunction.addUp(a, b); System.out.println("addUp() gives " + c); }}
演示 UseGeneral.java
广东工业大学计算机学院34
Math 类 所有的 Math 成员都是静态的 , 不必为该类创建实例 , 直接用 Math.method(variable) 调用这些方法 例如 : int a=-1; a=Math.abs(a); // 将 a 作绝对值运算后所得的结果赋给 a
public static double sin(double a) public static double cos(double a) public static double tan(double a) public static double asin(double a) public static double acos(double a)
广东工业大学计算机学院35
对象的构造对象的构造有几种方式:
1. 构造器2. 在域的声明中赋值3. 初始化块
广东工业大学计算机学院36
构造器 (constructor)
构造器(构造方法)- 用来初始化新创建的对象的方法,在创建对象时自动调用,名字与类名相同,无返回参数。 ( 即构造函数总是伴随着 new 操作符使用 ) 举例:
class Employee { public Employee(String n, double s,) { name = n; salary = s; } private String name; private double salary;}
Employee e=new Employee(“ 张三” ,5000.0);
广东工业大学计算机学院37
构造方法特点( 1 ) 所有的 Java 类都有构造方法,并且一个类可以包含一个或者多个构造方法。所有的构造方法的名字都与类名相同,但所带参数的个数和类型不同。(又称为重载构造方法) 举例:class Employee { public Employee(String n, double s, int year, int month, int day) { …… } public Employee(String n, double s,) { …… }
}
广东工业大学计算机学院38
构造方法特点( 2 ) 一个构造方法可以调用同一个类的另一个构造方法。
public class Employee { public Employee(String aName, double aSalary) { name = aName; salary = aSalary; } public Employee(double aSalary) { this(“Emplyoee #” + this.nextId, aSalary); //调用上一个构造方法 } public Employee( ) { } 。。。。。。 private String name; private double salary; private static int nextId;}
注意:调用另一个构造方法的 this 语句必须放在构造方法的第一个语句。
广东工业大学计算机学院39
默认的构造方法 在编写一个类的时候,如果没有编写构造方法,则系统会自动提供一个默认的构造方法。 默认构造方法:没有参数,其功能为将该类各域值初始化成 Java规定的对应默认值
( 0 , false, null )。 例如: 若 Employee 类没有定义构造方法,则默认有如下构造方法:
public class Employee{ public Employee( ) { name = “”; salary = 0; hireDay = new Date();}……private String name;private double salary;Date hireDay;
}
注意:若有编写构造方法,则不存在默认构造方法。
广东工业大学计算机学院40
构造方法——小结注意:
1. 构造器与类同名,没有返回值。(不用写出返回值为 void ,非构造函数则需要)2. 每个类可以不显式定义构造方法。3. 构造器可以有 0 个、 1 个或以上的参数。4. 构造方法总是伴随着 new 操作符一起使用。
广东工业大学计算机学院41
在域的声明中赋值在域的声明语句中,可以同时给域赋一个初始值,这赋值语句会在创建对象时被自动执行。例如:
class Employee { public Employee ( ) { …… } private static int nextId = 1000; private int id = 0;
}
广东工业大学计算机学院42
初始化块 在一个类的声明中,可以有多个代码块,只要构造类的对象,这些块就会被执行。 例如:
class Employee { public Employee ( ) { …… } private static int nextId; private int id;
{ id = nextId; nextId++; }}
注意:在构造对象的过程中,首先会运行初始化块,然后才运行构造器方法的主体部分。
广东工业大学计算机学院43
静态初始化块 静态初始化块 - 在类第一次加载时(第一次放到类空间准备运行时),执行静态初始化块。静态初始化块只执行一次。 例如:
class Employee { public Employee ( ) { …… } private static int nextId; private int id;
static { Random generator = new Random; // 为 nextId 赋一个随机值 nextId = generator.nextInt(10000); }}
广东工业大学计算机学院44
对象初始化顺序 创建一个对象时,对该对象的初始化处理顺序如下: 1 、执行静态初始化块;(只在创建第一个对象时执行一次); 2 、所有数据被初始化为默认值 (0, false, null) ; 3 、进入所调用构造方法(即方法参数赋值); 4 、按照类定义中出现的次序依次执行所有域初始化语句和初始化块;(暂不执行构造方法中的语句) 5 、如果所调用的构造方法调用了第二个构造方法,则执行第二个构造方法主体; 6 、执行第一个构造方法的主体;
广东工业大学计算机学院45
对象初始化顺序举例import java.util.*;public class ConstructorTest{ public static void main(String[] args) { Employee[] staff = new Employee[2]; staff[0] = new Employee("Harry", 40000); staff[1] = new Employee(60000); // 显示 Employee 对象的域值 …… }}
class Employee{ public Employee(String n, double s) { name = n; salary = s; } public Employee(double s) { this("Employee #" + nextId, s); int i=0; i=i+1; } …… private static int nextId; private int id = 10; private String name = ""; private double salary;
static { Random generator = new Random(); nextId = generator.nextInt(10000); }
{ id = nextId; nextId++; }}
广东工业大学计算机学院46
类的结束方法 finalize()
当一个对象没有任何引用指向它的时候,该对象就会被Java虚拟机认为是可回收的“垃圾”。
Java 有自动的垃圾回收机制,它实际上是一条虚拟机线程,周期性地把不再被引用的对象从内存中清除。 这个垃圾收集器是自动执行的,我们也可以通过调用系统类的 System.gc() 方法来显式地运行垃圾收集程序 ( 比如在创建大量垃圾代码之后或者在需要大量内存代码之前运行垃圾收集器 ) 。 在一个对象被系统垃圾收集器处理之前,对象也可调用自己的 finalize() 方法进行析构处理,这个过程就是所说的最后处理,也有的参考资料称之为结束方法。
广东工业大学计算机学院47
类的结束方法 finalize()
尤其是结束方法在一般情况下是不需要的,而提供结束方法 finalize() 的目的是让程序员有机会释放掉不能被自动内存管理器直接使用的资源或是不能自动释放掉的资源。但调用系统类的 System.gc() 方法,显式调用
finalize() 方法并不能保证垃圾回收线程立即被唤醒,实际情况与操作系统有关。Java 中构造方法和结束方法在类中均是可选的。
广东工业大学计算机学院48
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院49
对象的使用 1
可以用点“ .”操作符来调用对象实例的域和方法。 1. 域的引用 形式如下: objectreference.variablename;例如: firstPerson.name = “Henry”;2. 静态域的应用 className.variablename;例如: System.out.println(“hello”);
广东工业大学计算机学院50
对象的使用 2
2. 方法的调用 一般形式如下: objectreference.methodname ( para1 , para2 ,…) ;例如: Person firstPerson = new Person(); firstPerson.setName(“Harry”);
3.静态方法的调用className.methodname ( para1 , para2 ,…);例如: double d= Math.pow(x,a); // 计算 x 的 a次幂;
广东工业大学计算机学院51
域和方法的访问控制变 量 和 方 法 的 访 问 控 制 符 有 : private 、默认、 protected 及 public 。它们的具体访问范围如下:
访问指示 类 包 子类 所有private √
默认 ( 无修饰符 ) √ √
protected √ √ √
public √ √ √ √
广东工业大学计算机学院52
域和方法的访问控制举例 包 1 包 2
public int v1;protected int v2;
int v3private int v4;
ClassA
ClassB
ClassC
ClassD extends ClassA
ClassB, ClassC,ClassD 分别可以访问ClassA 的哪些成员变量?
广东工业大学计算机学院53
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院54
包的组织方式 Java 中的包 (Package)其实指的就是目录,它是将类
(class) 或者接口 (interface) 分类按照一定的规则存放在不同的文件夹中。标准的 Java 类库分布在多个包中,如 java.lang 、 java.util 等。
sun 公司建议以公司域名的逆序作为包名。
广东工业大学计算机学院55
包的作用 Java 以包的形式组织类库 , 相当于其他编程语言的标准库 . 这些包中有许多已编好的类与接口让编程人员引用 , 充分体现了面向对象语言的代码可重用性 .
利用文件系统的目录管理来管理包, 避免同名的类发生冲突。若类名相同 , 只要包名不同就不会冲突 . 包名用“ .”指明目录的层次 , 如 java.awt.image 表明image 包在 java\awt\目录下 .
除了 Java 的开发环境提供了许多包之外 , Java 语言还提供了 package 语句让编程人员自己定义包 , 另外提供了 import 语句让编程人员引入各包中的类 .
广东工业大学计算机学院56
包的导入与使用一个类可以使用它所在的包中的所有类,以及其它包中的公有类 ( 声明为 public 的类 ) 。使用其它包中的公有类,有两种方法:
1.每个类名之前添加完整的包名java.util.Date today = new java.util.Date()
2. 使用 import 语句导入import java.util.*; 或import java.util.Date;
广东工业大学计算机学院57
类冲突的情况 在不同的包中,有可能存在名字相同的类。例如:
java.util 和 java.sql 包中都有日期 (Date) 类。 如果要同时使用到这两个包,又要用到 java.sql 包中
Date 类时,需要明确导入: import java.util.*; import java.sql.*; import java.sql.Date;
如果两个 Date 类都要使用到,那只能将完整的包名写出来: java.util.Date today = new java.util.Date()
广东工业大学计算机学院58
静态导入从 JDK5.0 开始, import 语句增加了导入静态方法和静态域的功能。例如,如果在源文件的顶部添加一条指令:
import static java.lang.System.*;
那么就可以使用 System 类的静态方法和静态域,而不必加类名前缀,例如: out.println(”Goodbye”);
另外还可以将特定的方法或者域导入: import static java.lang.System.out;
广东工业大学计算机学院59
自定义包自定义包 如果希望把自己编写的类与接口放在一个包中 , 可以用
package 语句来定义一个包 .格式如下 :
package pkgname;//指明下面声明的类与接口都位于 pkgname 包中 . 例如 :
package Mypack; class Myclass { ... }
广东工业大学计算机学院60
自定义包语句说明: 若源文件中没有该语句 , 则当前的文件中声明的类与接口都放在一个无名包中 .
package 语句位于 Java源文件的第一句 package 语句在一个源文件中仅能出现 0 或 1次。
广东工业大学计算机学院61
Jar 文件类文件 (.class 文件,不是 .java 文件 ) 存储在文件系统的子目录中。另外还可以打包成 JAR 格式压缩存储。在一个 JAR 包中可以包含多个类文件和子目录。例如:在运行时库的数千个类都包含在运行时库文件 rt.jar 中。在 JDK 的 jre/lib 子目录可以找到这个文件。
广东工业大学计算机学院62
如何共享包 如何共享包?步骤如下: 1. 将类放到一个或者多个指定目录下。这个目录是包的树状结构的基目录,例如:
D: /home/user/classdir 如果希望将 cn.edu.gdut.MyClass 类添加到基目录中,则这个类文件必须放置在: D: /home/user/classdir/cn/edu/gdut 目录下。
2. 设置类路径 Classpath 。类路径是所有基目录的集合,基目录中的子目录可以用于包含类文件。 如何设置类路径取决于编译环境。如果是 NetBeans ,则可以在项目的“属性”对话框中设置。
广东工业大学计算机学院63
自定义包和包引入示例package mypackage1;import java.util.*;
public class Tokenizer{
public void PrintTokens(String s)
{ StringTokenizer st = new StringTokenizer(s); System.out.println( "Token
Total: "+st.countTokens()); while(st.hasMoreElements()){
System.out.println(st.nextToken());
} }}
package mypackage2;import java.util.*;import mypackage1.Tokenizer;
class PackageTest { public static void main(String args[]){ String s="The Java platform is the ideal platform for network computing"; Tokenizer tk=new Tokenizer(); tk.PrintTokens(s); }}
演示 PackageTest
广东工业大学计算机学院64
编译与运行 假设 Tokenizer.java 和 PackageTest.java 文件在同一个目录下 ( 1 )先进入 Tokenizer.java 所在的目录 ( 2 )以当前目录下的子目录 classes 为基目录 (classes目录必须已经存在 ) ,编译 Tokenizer.java 文件 javac –d e:\classes Tokenizer.java ( javac –d .\classes Tokenizer.java 编译到当前目录的子目录
classes 下 )
( 3 )以 e:\classes 为基目录,编译 PackageTest.java 文件(必须已经设置 Classpath 包含 e:\classes ) javac –d e:\classes PackageTest.java
( 4 )先进入基目录 e:\classes ,再运行 PackageTest.class 文件 java mypackage2.PackageTest
广东工业大学计算机学院65
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院66
文档注释 文档注释非常重要, Java也提供了 javadoc 实用程序帮助程序员由注释生成文档。 javadoc从下面几个地方抽取注释生成文档:
包 公有类与接口 公有的和受保护的 (protected) 方法 公有的和受保护的域
所以应当为这几部分编写注释。注释应当放在上述特性的前面。 以 /** 开始, */ 结束。
广东工业大学计算机学院67
在 NetBeans 中生成文档打开需要生成文档的项目,然后在项目的“属性”窗口中选择 ,如图所示。需要查看文档时,将光标定位在类名上,然后使用快捷键
“ Ctrl+ shift + space”
广东工业大学计算机学院68
1. 类的注释类注释必须放在 import 语句之后、类定义之前:
广东工业大学计算机学院69
2. 方法的注释每个方法注释必须放在所描述的方法之前。除了通用标记之外,还可以使用一下标记:
@param variable description :向当前方法的“ param” (参数)部分增加一个条目。 @return description :向当前方法添加“ return” (返回)部分。 @throws class description :表示该方法有可能抛出异常。
广东工业大学计算机学院70
域注释只需要对公有域 ( 通常指静态常量 ) ,建立文档。
此外还有一些通用注释标记,如:@author name
@version
@since
@deprecated text
@see reference
此外,还可以为包建立一个注释;也可以为整个工程添加一个“ OverView” 注释。
广东工业大学计算机学院71
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院72
类设计技巧1. 尽量要将数据设计为私有 private 。目的是为了保持严密的封装性。
2. 要对数据进行初始化。• 一些 Java IDE 会对对象的实例域进行初始化,但程序员应当显式地初始化所有的数据。• Java 不会对局部变量进行初始化。
广东工业大学计算机学院73
类设计技巧(续)3. 不要在类中使用过多的基本数据类型。可以使用其它类替换多个相关的基本数据类型的使用,以易于理解及修改。 例如:
private String street;private String city;private String state;priivate int zip;
上面几个数据类型实际上构成了一个“ Address” 类。所以可以先建立一个“ Address” 类,然后用该类的引用替换上面几个基本数据类型的定义: priavte Address address;
广东工业大学计算机学院74
类设计技巧(续)4. 不是所有的域都需要独立的域访问器方法和修改器方法。 例如“雇员的雇用日期”域,它只需要在初始化时确定,之后不应再被修改,所以不应当为这个域设立修改器方法。5. 类名和方法名要能体现它们的职责,尽量做到“望文知义”。
广东工业大学计算机学院75
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院76
继承:基于已存在的类(父类 /超类 /基类)构造一个新类(子类 /派生类)。子类(继承)父类的域和方法,子类与父类是特殊与一般( is-a )的关系。bicycle(super class)
Mountain bikes Racing bikes Tandem bikes
subclass
继承
广东工业大学计算机学院77
继承的定义形式 class Sub extends Base{ …… }
Sub 类会自动拥有在 Base 类中定义的域和方法。
广东工业大学计算机学院78
继承的实例( 1 )假设存在一个 Employee 类 public class Employee { public Employee(String n, double s, int year, int
month, int day) { …… }
public String getName() { …… }
public double getSalary() { …… }
public Date getHireDay() { …… }
public void raiseSalary(double byPercent) { …… }
private String name; private double salary; private Date hireDay; }
广东工业大学计算机学院79
类的继承实例( 2 )现在需要定义 Manager 工种,因为 Manager
和 Employee 之间存在的“ is – a” 关系。所以可以将 Manager 设计为 Employee 的子类。class Manager extends Employee { // 添加方法和域}
注意:子类拥有父类的所有域和方法。
广东工业大学计算机学院80
增加域 / 方法 子类可以比父类拥有更多的域和方法。 例如:在 Manager 类中增加一个用于存储奖金信息的域,以及一个用于设置这个域的方法:
class Manager extends Employee { . . . public void setBonus (double b) { bonus = b; }
private double bonus; }
Manager 对象可以使用 setBonus 方法 : Manager boss = …; boss. setBonus(8888);
setBonus 方法没有在 Employee 中定义,所以 Employee 对象不能使用它。
广东工业大学计算机学院81
Manager 类继承了什么? Manager 类自动继承了超类中的方法和域。 1. Manager 类继承的方法包括: getName 、
getSalary 、 getHireDay 、 raiseSalary 。 2. Manager 类继承的域包括:
name 、 salary 、 hireDay 。但在 Manager 类中不能直接访问这些域。 在定义子类的时候,仅需要指出子类与超类的不同之处。因此类的设计原则之一是:
尽量将通用的方法放置于超类之中; 将具有特殊用途的方法放置在子类中。
广东工业大学计算机学院82
方法的覆盖 (override)
有些方法在子类 Manager 中并不适用。例如, Manager 类对象的总薪水是由薪水 + 奖金,所以Manager 类中的 getSalary 方法应当返回薪水和奖金的总和 (bonus + salary).
所以需要通过在 Manager 类中重新定义 getSalary方法覆盖 (override) 超类中的 getSalary 方法。class Manager extends Employee { . . . public double getSalary() { . . . } . . . }
广东工业大学计算机学院83
覆盖方法的实现 在 Manager 类中, getSalary 方法应当这样实现:
public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; }
注意: 1. 不能直接访问超类中的 salary 域:
return salary + bonus; 会导致编译错误。因为 salary 是超类的私有字段。 2. 不能使用子类的 getSalary 方法获取超类的 salary 字段:
double baseSalary = getSalary(); 这样会导致无限次循环调用自己。 3. super 是一个指示编译器调用超类方法的特有关键字。调用超类的公有方法和和公有字段时,必须使用该关键字指明。
广东工业大学计算机学院84
JDK5.0 中对覆盖方法的改进 在 JDK5.0 以前的版本中,进行方法覆盖时,要求新方法的返回类型必须与原方法的返回类型一致。 在 JDK5.0 中,则放宽了要求:新方法的返回类型可以是原方法的返回类型的子类型。 例如: 假设在类 A 中有一个返回类型的 Employee 的方法 f; 假设 B 是 A 的子类,如果要在 B 中覆盖 f,则在 B 类中定义的 f可以是:
Employee f(…) { …… }Manager f(…) { …… }
注意: 1. 覆盖方法和原方法的签名 (signature)必须相同。方法的签名包括方法的名字和参数列表。 2. 覆盖方法的可见性不能低于原方法的可见性。特别地,如果超类方法是
public ,则子类方法必须也是 public 。
广东工业大学计算机学院85
super 关键字在构造器中的应用 Manager 类的构造器实现如下:
public Manager(String n, double s, int year, int month, int day) { super(n, s, year, month, day); bonus = 0; }
关键字 super 的含义:是“调用超类 Employee 中含有 n, s, year, month 和 day 参数的构造器” 。(由于 Manager 类不能访问 Employee 类的私有域,所以必须利用 Employee 类的构造方法对这部分私有域进行初始化。)
注意: 1. 如果子类的构造器没有显式地调用超类的构造器,则会自动调用默认 ( 没有参数 ) 的构造器; 2. 如果超类中没有不带参数的构造器,而子类的构造器中有没有显式地调用其它构造器, Java 编译器将报告错误。
广东工业大学计算机学院86
super 与 this 的比较 this 关键字有两个用途:
1. 引用隐式参数 2. 调用该类其它的构造器
同样 super 关键字也有两个用途: 1. 调用超类的方法 2. 调用超类的构造器
注意: 1. 在调用构造器时, super 与 this 的使用方式很相似:super(…); // this(…)
2. 调用构造器的语句只能作为另一个构造器的第一条语句出现。
public Employee(double aSalary) { this(“Emplyoee #” + this.nextId, aSalary); }
广东工业大学计算机学院87
本章内容3.1 面向对象编程基础 3.2 类的编程实现3.3 对象的构造与使用3.4 包
3.5 文档注释3.6 类设计技巧3.7 类的继承实例3.8 多态
广东工业大学计算机学院88
多态 多态源自置换法则:在程序中出现超类对象的任何地方都可以用子类对象置换。 例如,可以将一个子类的对象赋给超类变量:
Employee e; e = new Employee(. . .); // e 被定义为 Employee 对象e = new Manager(. . .); // e指向 Manager 对象也可以
在 Java 中,对象变量是多态的,一个 Employee 类型变量既可以引用 Employee 类型对象,也可以引用一个 Employee 类的任何一个子类的对象 ( 例如 Manager) 。 注意:不能将一个超类的引用赋予子类变量:
Employee e; Manager m = e; // ERROR
广东工业大学计算机学院89
举例: ManagerTest
给出一个例子,实现如下功能:1. 创建一个新经理对象
Manager boss = new Manager(“Bill", 80000, 1987, 12, 15); boss.setBonus(5000);
2. 定义一个 3 个雇员的数组:Employee[] staff = new Employee[3];
3. 将经理和雇员放置于数组中:staff[0] = boss; staff[1] = new Employee("Harry", 50000, 1989, 10, 1); staff[2] = new Employee("Tony", 40000, 1990, 3, 15);
广东工业大学计算机学院90
举例: ManagerTest( 续 )
给出一个例子,实现如下功能 ( 续 ) :4. 输出每个人的薪水:
for (Employee e : staff) System.out.println(e.getName() + " " +
e.getSalary()); 输出结果如下:
Bill 85000.0 Harry 50000.0 Tony 40000.0
e.getSalary() 能确定执行哪个 getSalary 方法 ( 子类的?超类的? ) 。
staff[0] = boss; //Manager 对象staff[1] = new Employee("Harry", …); staff[2] = new Employee("Tony", …);
ManagerTest
广东工业大学计算机学院91
方法调用过程 下面是对象方法调用过程的详细描述 ( 动态绑定 ) : 1. 编译器查看对象的声明类型和方法名。假设调用
obj.f(param) ,且 obj 被声明为 C 类的对象。 编译器会一一列举 C 类中所有名为 f 的所有方法及其超类中名为 f而且是子类可访问的方法。——至此,编译器已经获得所有可能被调用的方法。 2. 编译器将寻找与方法签名 (signature) 与 f 匹配的方法,这个过程成为重载解析 (overloading
resolution) 。(如果 C 类的方法与超类签名相同,则C 类方法覆盖超类方法)——至此,编译器已获得需要调用的方法的名字和参数类型。
广东工业大学计算机学院92
方法调用过程(续) 3. 调用哪个方法将依赖于隐式参数的类型,并在运行时动态绑定。例如:
Manager m = new Manager(); //此时m 为 Manager类型的对象; …… (Employee)m.gerSalary(); //m 被转换成 Employee对象了,所以调用的 gerSalary 的方法是 Employee类中的方法。
实际上,动态绑定时每次都要搜索所有的可用方法,所以虚拟机预先为每个类创建一个方法表 (method table) 。另外 如果方法是 private 、 static 、 final 或者构造器,则编译器可以准确知道应该调用哪个方法。这种调用方式称为静态绑定 (static binding) 。
广东工业大学计算机学院93
阻止继承: final 类和 final 方法有时候希望禁止某个类派生出子类。final 类:不允许扩展的类。只需要在定义该类时加上 final 修饰符。final 方法:将一个方法声明为 final 之后,子类就不能覆盖这个方法。 (final 类中的所有方法自动地声明为 final 方法,但该类中的域不会自动声明为 final)
意义:阻止继承后,能确保 final 类和方法不会改变语意,即使是在子类中。
广东工业大学计算机学院94
final 类和方法举例public final Executive extends Manager { …}
Executive 类已经是所有行政职务的最高层,完全有理由将其确定为 final
class Employee { public final String getName() {…}}
广东工业大学计算机学院95
方法重载 (overload)
对于类的方法(包括从父类中继承的方法),如果有两个方法的方法名相同,但参数不一致,那么可以说,一个方法是另一个方法的重载方法。 重载方法必须满足以下条件:
方法名相同。 方法的参数类型、个数、顺序至少有一项不相同。 方法的返回类型可以不相同。 方法的访问控制符可以不相同。
//java.lang.Math 类的用于取最大值的 max 方法,// 有多个重载方法。public static int max(int a,int b)public static long max(long a,long b)public static float max(float a,float b)public static double max(double a,double b)
int a=Math.max(1,2);double d=Math.max(1,2.0);
广东工业大学计算机学院96
方法重载 (overload)
以下 Sample 类中已经定义了一个 amethod() 方法:public class Sample{ public void amethod(int i, String s){} // 加入其他方法}
下面哪些方法可以加入到 Sample 类中,并且保证编译正确呢?A ) public void amethod(String s, int i){} // 可以B ) public int amethod(int i, String s){return 0;} // 不可以C ) private void amethod(int i, String mystring){} // 不可以D ) public void Amethod(int i, String s) {} // 可以