第三章 面向对象程序设计
DESCRIPTION
第三章 面向对象程序设计. 浙江工业大学 计算机学院 赵小敏 [email protected] http://210.32.200.159:9001. 主要内容. 3.1 类和对象的基本概念 3.2 类的设计 3.3 实例对象的创建和使用 3.4 封装性 3.5 继承性 3.6 多态性 3.7 修饰词 abstract 、 static 和 final 3.8 接口 3.9 包 3.10 内部类. 3.1 类和对象的基本概念. 对象是客观世界中的某个具体事物, 对象的概念是面向对象技术的核心。 - PowerPoint PPT PresentationTRANSCRIPT
2
主要内容 3.1 类和对象的基本概念 3.2 类的设计 3.3 实例对象的创建和使用 3.4 封装性 3.5 继承性 3.6 多态性 3.7 修饰词 abstract、 static和 final 3.8 接口 3.9 包 3.10 内部类
3
3.1 类和对象的基本概念
对象是客观世界中的某个具体事物,对象的概念是面向对象技术的核心。
面向对象技术中的对象就是现实世界中某个具体的物理实体在计算机逻辑中的映射和体现,它可以是有形的,也可以是无形的。
比如:电视是一个具体存在的,拥有外形、尺寸、颜色等外部特性 ( 或称属性 ) 和开、关、设置等功能的实体。
4
对象的基本概念 从程序设计的角度来看,事务的属性或特性可
以用变量来表示,行为或功能则用方法来反映
面向对象的程序设计方法就是将客观事物抽象成为“类”,并通过类的“继承”实现软件的可扩充性和可重用性
5
类 (class)
类是同种对象的集合与抽象。 在面向对象的程序设计中,定义类的概念来表
述同种对象的公共属性和特点。
类是一种抽象的数据类型,它是具有一定共性的对象的抽象,而属于类的某一对象则被称为是类的一个实例,是类的一次实例化的结果。
6
例如:例如: 日常接触的电视有很多,如松下电视、长虹电视
等都属于电视的范畴,这些实体在面向对象的程序设计中将被映射成不同的对象。不难看出,这些代表不同的电视实体的对象之间存在着很多实质性的共同点。
类是抽象的概念,如“电视”,那么对象就是某
一个具体的电视,如“小王的那台 2008 年出产的创维牌 42 吋液晶电视”。
7
用面向对象程序设计解决实际问题的基本思想 首先将实际存在的实体抽象成概念世界的抽象数
据类型,这个抽象数据类型里面包括了实体中与需要解决的问题相关的数据和属性;
然后再用面向对象的工具,如 java 语言,将这个抽象数据类型用计算机逻辑表达出来,即构造计算机能够理解和处理的类;
最后将类实例化就得到了现实世界实体的面向对象的映射——对象,在程序中对对象进行操作,就可以模拟现实世界中的实体上的问题并解决之。
8
对象、实体与类关系图
抽象数据类
概念世界
实体
现实世界
对象
类计算机逻辑的实现
映射
9
案例:学生成绩查询系统
1. 教师登录系统后可输入授课课程的成绩供学生查询,并可统计学生的平均成绩和各等级的学生人数;
2. 学生登录系统后可查询自己的各门课程成绩;
10
系统分析 (1) :确定对象
找出问题描述领域中的主要名词 对象可能是: 简单的或复杂的(如学生,课程) 真实的或概念的(如课程成绩) 对象有: 属性 操作 ( 或功能行为 )
11
确定学生成绩查询系统中的对象
学生成绩查询系统对象可能有
学生 教师
JAVA程序设计
课程
Lee 80John 75Mark 90Henry 65Flank 82
成绩
12
测试对象 与问题域的相关性 对象是否在问题陈述的界限之内? 系统是否必须有此对象才能完成任务? 在用户与系统的交互中是否必须有此对象? 独立存在性 对象之间可以是相关的,但仍是独立存在的 评估一个可能的对象是否有此特征时,应考虑此对
象是否需要独立存在,而不是作为另外一个对象的属性而存在
要有属性和操作
13
系统分析 (2) :确定对象属性和操作 属性:是对象的特征,属性知道某些事情。 可以是数据或对象 对学生对象来说,可能包括学号和选修课程 操作:是对象执行的动作,操作根据所知道的来做某些事情。
可以是对象做出的或施加给对象的动作,往往会影响对象的属性
对教师对象来说,可能是上报成绩和修改成绩
14
学生成绩查询系统案例研究中的对象可能的属性和操作 学生 属性有姓名、性别、学号、班级、专业、院系、
学校、登录名和密码等 操作有登录、查询成绩和聊天等 教师 属性有姓名、性别、工号、院系、学校、登录名
和密码等 操作有登录、上报成绩、统计成绩、查询成绩、
修改成绩等
15
学生成绩查询系统案例研究中的对象可能的属性和操作(续) 课程 属性课程名、课程编号、学时、学分、学期、授课教
师和选修学生等 操作有设置授课教师、获取授课教师、设置选修学生
和获取选修学生等 成绩 属性有课程、学生和分数等 操作有设置课程编号、获取课程编号、设置学生编号、获取学生编号、设置分数和获取分数等
16
系统分析 (3) :为对象建模
CoursenamecourseIDtermclassHourcreditHourteacherstudentssetTeacher()getTeacher()setStudent()getStudent()
ScorecourseIDstudentIDscoressetScore()getScore()setStudentIDgetStudentIDsetCourseIDgetCourseID
StudentnamesexstuIDclassnamemajorcollegeSchoolloginNamepasswordlogin()searchScore()chat()
TeachernamesexteacherIDcollegeSchoolloginNamepasswordlogin()reportScore()statisticsScore()inquireScore()modifyScore()chat()
对象属性
操作
17
作业:简单图书管理系统分析
3.1简单图书管理系统功能包括读者可以借书和还书以及查询个人借阅信息和查询图书信息,图书管理员可管理书籍基本信息和读者基本信息、更新图书借阅或归还信息以及更新图书状态等。
请分析该系统,给出对象,并为对象建模。
18
3.2 类的设计
类的格式 < 类首声明 >
{< 类主体 >} 类首声明定义类的名字、访问权限以及与其
它类的关系等。 类主体定义类的成员,包括变量(数据)和方
法(行为)
19
类首声明 [< 修饰符 >] class < 类名 > [extends <超类名 >]
[implements < 接口名 >] class: 类定义的关键字; extends: 表示类和另外一些类(超类)的继承
关系; implements: 表示类实现了某些接口; 修饰符 : 表示类访问权限( public 、缺省方式
等)和一些其它特性( abstract、 final 等);一个类可以同时有多个修饰符 (任意排序 ) ,但不能有相同的修饰符。
20
类的修饰符 缺省方式: class没有修饰符,只能被同一个
包名中的类访问
public :能被所有的类访问
abstract: 修饰的类被称为抽象类。抽象类就是没有具体对象的概念类。
final :不能被继承,即类不能有子类
21
类主体< 类首声明 > { // 类首,以下为类主体 < 成员变量的声明 > < 成员方法的声明及实现 >}
成员变量即类的数据,反映了类的属性和状态。 成员方法即类的行为(对数据的操作)• 成员方法可以是普通方法、构造方法、 main 方法
22
例:声明一个学生类1. public class Student {2. String name;3. char sex;4. int stuID;5. public Student(){ }6. public Student(String stuName,char sex,int stuID){7. name=stuName;8. this.sex=sex;9. this.stuID=stuID;10. }11. public void setName(String stuName){12. name=stuName;13. }14. public void setSex(char sex){15. this.sex=sex;16. }17. public void setStuID(int stuID){18. this.stuID=stuID;19. }20. }
23
[< 修饰符 >][static][final][transient] < 变量类型 > <变量名 > static: 表示是一个类成员变量(静态变量); final: 表示是一个常量 ( 最终成员变量); 例: final double PI=3.1415926; transient: 表示一个临时变量 修 饰符 : 表 示 变 量 的访问权限(缺省访问、 public、 protected和 private)
作用域:整个类。
成员变量的声明格式
24
[< 修饰符 >]<返回类型 > < 方法名 > ([<参数表列>]) [throws <异常类 >] { 方法体 }修饰符:方法的访问权限 (缺省的、 public、 protected 和private)static: 类方法(静态方法);abstract: 抽象方法(无方法体的方法);final: 最终方法(不能被子类改变)。throws: 表示抛出异常
成员方法的声明格式
25
构造方法
类的构造方法是一种特殊方法,作用是在创建对象时 Java 系统调用构造方法去初始化新建对象的成员变量 ;
例: Student s1=new Student(“张三” ,’男’ ,20080301);
如果没有自定义构造方法,则 Java调用类的默认构造方法。 Java 将使用缺省值来初始化成员变量。
例: Student s2=new Student();
26
构造方法的特性 类的构造方名必须和类名相同;
构造方法没有返回值(在构造方法名字前连 void也不要加);
可以有 public, protected, private 等任何访问的修饰者没有修饰。 但不能有以下非访问性质的修饰: abstract, final, native, static 或 synchronized 。
27
3.3 实例对象的创建和使用
创建对象的两种方式:(1) 先声明对象,然后实例对象例: Student s; //声明对象 s=new Student(); // 实例化对象
(2)< 类名 > < 对象名 >=new < 类名 >(参数 ) ;例: Student s=new Student();
28
实例化(创建对象)
实例化就是为对象分配存储空间,并同时对对象进行初始化。
用 new 运算符和类的构造方法 Student( ) 来完成。
29
例: Shirt.java1. public class Shirt{2. char size;3. float price;4. boolean longSleeved;5. public static void main (String args[]){6. Shirt myShirt= new Shirt();7. Shirt anotherShirt=new Shirt();
8. myShirt.size = 'M';9. myShirt.price = 22.99F;10. myShirt.longSleeved = false;
11. anotherShirt.size = 'L';12. anotherShirt.price = 29.99F;13. anotherShirt.longSleeved = true;14. 15. anotherShirt=myShirt;16. anotherShirt.size= 'S';17. }18. }
30
堆栈内存( 在程序空间
内 )
堆内存( 附加内存在程序空间
外 )
执行下列代码的内存分配 1
0X99F31244myShirt
0X99F03312anotherShirt
‘ \u0000’0.0
false
sizeprice
longSleeved
Shirt
‘ \u0000’0.0
false
sizeprice
longSleeved
Shirt
6. Shirt myShirt = new Shirt();7. Shirt anotherShirt = new Shirt();
31
堆栈内存( 在程序空间
内 )
堆内存( 附加内存在程序空间
外 )
执行下列代码的内存分配 2
0X99F31244myShirt
0X99F03312anotherShirt
‘ M’22.99Ffalse
sizeprice
longSleeved
Shirt
8. myShirt.size = 'M';9. myShirt.price = 22.99F;10. myShirt.longSleeved = false;
‘ \u0000’0.0
false
sizeprice
longSleeved
Shirt
32
堆栈内存( 在程序空间
内 )
堆内存( 附加内存在程序空间
外 )
执行下列代码的内存分配 3
0X99F31244myShirt
0X99F03312anotherShirt
‘ L’29.99F
true
sizeprice
longSleeved
Shirt
11. anotherShirt.size = ‘L';12. anotherShirt.price = 29.99F;13. anotherShirt.longSleeved = true;
‘ M’22.99Ffalse
sizeprice
longSleeved
Shirt
33
堆栈内存( 在程序空间
内 )
堆内存( 附加内存在程序空间
外 )
执行下列代码的内存分配 4
0X99F31244myShirt
0X99F033120X99F31244anotherShi
rt
‘ L’29.99F
true
sizeprice
longSleeved
Shirt
15. anotherShirt=myShirt;
‘ M’22.99Ffalse
sizeprice
longSleeved
Shirt
34
堆栈内存( 在程序空间
内 )
堆内存( 附加内存在程序空间
外 )
执行下列代码的内存分配 5
0X99F31244myShirt
0X99F033120X99F31244anotherShi
rt
‘ L’29.99F
true
sizeprice
longSleeved
Shirt
16. anotherShirt.size=‘S’;
‘ M’22.99Ffalse
sizeprice
longSleeved
Shirt
‘S’
35
练习:仔细阅读下面的程序 ,输出结果是什么?1. class ValHold{2. public int i = 10;3. }4. ObParm5. public class {6. public static void main(String
argv[]) {7. ObParm o = new ObParm();8. o.amethod();9. }
10. public void amethod() {11. int i = 99;12. ValHold v = new ValHold();13. v.i = 30;14. another(v , i);15. System.out.print(v.i);16. }
16. public void another(ValHold v, int i) {
17. i = 0;18. v.i = 20;19. ValHold vh = new ValHold();20. v = vh;21. System.out.print(v.i);22. System.out.print(i);23. }24. }
输出结果:10020
36
3.4 封装性 目的 限制在类的外部对类内部成员进行访问,只通过公
共接口来访问类的成员数据。 屏蔽细节:这是程序设计的基本思想方法,便于程
序功能的扩展和程序的维护。 封装类 大多数变量都是私有的,变量通过他们自己类的方
法修改 ,其它类都只与一个类中很少的几部分接口; 若变量或方法为公有,它就是接口的一部分,其它
类可以访问它;若为私有,它是实现的一部分,只有类自己的成员可以访问它
37
访问权限的设置
权 限 同一类 同一包 不同包的子类 所有类public 允许 允许 允许 允许
protected 允许 允许 允许 不允许默 认 允许 允许 不允许 不允许private 允许 不允许 不允许 不允许
38
类的访问权限的设置
类的权限有两种:默认和 public。
如果在一个源程序文件中,声明了若干个类的话,只能有一个类的权限关键字是 public 。这个类的名字应该和程序文件同名, main 方法也应该在这个类中。
39
类的成员的访问权限设置
用权限关键字设置类的成员的权限,以此决定是否允许其类外部的代码访问这些成员。
例: private int name,sex,age,stuID; public void setName(String name);
40
访问控制方式: public
被修饰为 public 的类是公共类,它可供所在包的其他类使用,也可供其他包中的类使用,在程序中可以用 import 语句引入其他包中的 public 类。
被 public 定义的类中成员变量或方法可以供其他类访问,表明对外开放的方式;一般类的定义中,含有少量 ( 或者没有 ) public 变量,部分 public 方法。
41
访问控制方式: private
设置 private访问权限,则该类的成员只能被同一类中的成员访问,而不让其他类进行访问。
目的 : 隐藏具体的实现细节
变量常常是 private 采用 public “get” 方法读取数据 采用 public “set” 方法写数据
42
访问控制方式: protected
同一个包内的所有类的所有方法都能访问该成员
如果不在同一个包内的类的方法要访问该成员,则该类必须是该成员所在的类的子类
43
访问控制方式: 默认 ( 缺省方式 )
默认的权限,该类的成员能被同一包中的类访问。
44
封装性的可以带来的优点
基本思想 : 提供对外的通讯方式,封装内部的实现机制
增加内部实现部分的可替换性 减小类之间的耦合关系,方便模块划分 容易保证类内部数据间的一致性,从而提高
软件的可靠性
45
何时为 public或 private?
基本思想 类常常是 public 成员变量常常是 private. 构造方法一般是 public. 方法“ get” 与“ set” 是 public. 其它方法需要根据实际的需要而定
上面的思想可以用于大部分的程序设计 ( 也许90%)
46
例子:根据以下的设计要求编写 java源代码。类名 : Student变量 ( 访问权限均为 private): name(String) // 姓名 age(int) // 年龄方法 : 构造方法 ( 没有参数 , 设置姓名为“无名氏”,年龄为 20) setName( 有一个名为 name的 String 型参数 ,将 name 的值设为
这个新值 ), getName ( 没有参数 , 返回姓名 ) setAge( 有一个名为 age的 int 型参数 ,将 age 的值设为这个新值 ),
getAge ( 没有参数 , 返回年龄 ) isSameAge( 有一个参数 s, 是另一个 Student 对象的引用 , 如果
两个 Student 对象的 age 相同 , 则返回 true, 否则返回 false)
47
1. public class Student{2. private String name;3. private int age;
4. public Student(){5. name=" 无名氏 ";6. age=20;7. }8. public void setName(String name){9. this.name=name;10. }11. public String getName(){12. return name;13. }14. public void setAge(int age){15. this.age=age;16. }17. public int getAge(){18. return age;19. }
48
21. public boolean isSameAge(Student s){22. if(s.age-this.age==0) return true;23. return false;24. }25. public static void main(String args[]){26. Student s1=new Student();27. Student s2=new Student();28. s2.age=20;29. System.out.println(s1.isSameAge(s2));30. }31. }
Student 类(续)
49
作业3.2 、根据以下的设计要求编写 java 源代码。类名 : Teacher实例数据 ( 均为 private): name(String) // 姓名 teacherID(int) // 工号方法 : 构造方法 ( 没有参数 , 设置姓名为“无名氏”,年龄为 20) setName( 有一个名为 name的 String 型参数 ,将 name 的值设为这个
新值 ), getName ( 没有参数 , 返回姓名 ) setTeacherID ( 有一个名为 tID的 int 型参数 ,将 teacherID 的值设为这
个新值 ), getTeacherID ( 没有参数 , 返回工号 )
50
作业3.3 、创建一个名为 Rectangle 的类来表示一个使
用宽度和高度来改变量的矩形,矩形的宽度和高度由构造方法来确定。为 Rectangle 类创建下列方法:
① getArea返回矩形的面积,要求长和高的范围为10~50;
② getPerimeter返回矩形的周长 ;③ drawRect 使用星号 (*) 作为描绘字符画出该矩形
的形状(假设宽度和高度为整数) ; 在另一个类 TestRectangle 中编写main 方法来测试 Rectangle 类。
51
作业3.4 、请编写 2 类
第一个类 RootPrime 包含下面两个方法: int getRootNum(double a, double b, double c)
返回 ax^2+bx+c=0 的不重复实数根的个数,要求讨论a,b,c 的系数情况
int isPrime(int x)
判断 x 是否为正素数,是返回 1 ,否则返回 0; 第二个类 TestRootPrime ,调用第一个类中的两
个方法,计算并输出 getRootNum (1,2,3) 、 getRootNum(0,2,1) 、 isPrime(46) 、 isPrime(-23) 、 IsPrime(191) 的值
52
3.5 继承性
继承是复用程序代码的有力手段,当多个类(Sub1, Sub2…) 之间存在相同的属性和方法时,可从这些类抽象出父类 SuperClass 。
被继承的类称为超类(父类)。 从超类派生出来(继承超类)的新类称子类。 Java只支持单重继承,不支持多重继承
53
继承的层次结构
一个子类成为另一个子类的超类。
人
学生 行政人员教师本科生 研究生
54
继承的基本语法
[< 修饰符 >] class <子类名 > extends <超类名 >{
子类体 }
例:public class SubClass extends SuperClass{
… …
}
55
Student和 Teacher 继承People
继承使Student和Teacher具有了
People中所有的成员
People
namesexagegetName()getSex()getAge()printInfo()
Student
studentIDmajor
getStudentID()getMajor()
Teacher
teacherIDdepartment
getTeacherID()getDepartment()
56
例: Student和 Teacher 继承People1. public class People {2. String name;3. char sex;4. int age;5. public String getName(){6. return name;7. }8. public char getSex(){9. return sex;10. }11. public int getAge(){12. return age;13. }14. public void printInfo(){15. System.out.println(“name:”+name+“ sex:”+sex+“
age:”+age);16. }17. }
57
例: Student和 Teacher 继承People1. public class Student extends People{
2. String studentID;
3. String major;
4. public String getMajor(){
5. return major;
6. }
7. public String getStudentID(){
8. return studentID;
9. }
10. }
58
例: Student和 Teacher 继承People1. public class Teacher extends People{
2. String teacherID;
3. String department;
4. public String getDepartment(){
5. return department;
6. }
7. public String getTeacherID(){
8. return teacherID;
9. }
10. }
59
子类继承超类的成员变量
继承原则:子类只继承超类中非 private 的成员变量
隐藏原则:子类的成员变量和超类的成员变量同名时,超类的成员变量被隐藏(不能继承)
60
子类继承超类的成员方法
继承原则:子类只继承超类中非 private 的成员方法。
覆盖原则:子类的成员方法和超类的成员方法同名时,超类的成员方法被子类的成员方法覆盖(不能继承)。
61
super 关键字
super 表示对某个类的超类的引用。 如子类和超类有同名的成员变量或方法,则:
super.< 成员变量名 >super.< 成员方法名 >
表示引用超类的成员(如无 super 则表示子类中的同名成员)
62
this 关键字 在类定义中的方法中需要引用正在使用该方法的对
象时,可以用“ this” 表示。 用 this引用的使用方法如下: 用 this指代对象本身 访问本类的成员: this.< 变量名 > this.< 方法名 [ 参数列表 ]> 调用本类的构造方法: this([参数列表 ]>)
63
构造方法的继承 构造方法不存在继承关系。 如果子类没有构造方法,则它默认调用超类无参数
的构造方法; 如果子类有构造方法,那么在创建子类的对象时,
则将先执行超类的构造方法,然后再执行自己的构造方法。
对于超类中包含有参数的构造方法,子类可以通过在自己的构造方法中使用 super 关键字来引用,而且必须是子类构造方法中的第一条语句。
用 super调用超类的构造方法: super([参数列表 ])
64
例:通过 super 关键字实现对父类构造方法与成员方法的访问1. class People {
2. private String name ;
3. private char sex;
4. private int age;
5.
6. public People(String name,char sex,int age){
7. this.name = name;
8. this.sex=sex;
9. this.age = age;
10. }
11. public String getDetails( ){
12. return "Name:"+name+"\nsex:"+sex+"\nage: "+age;
13. }
14. }
65
例:通过 super 关键字实现对父类构造方法与成员方法的访问16. class Student extends People {
17. private String studentID;
18. private String major;
19.
20. public Student(String name,char sex,int age,String stuID,String major){
21. super(name,sex,age);
22. this.studentID=stuID;
23. this.major=major;
24. }
25. public String getDetails( ){
26. return super.getDetails( )+"\nStudentID:"+ studentID+"\nMajor:"+major;
27. }
28. }
29. public class TestSuper{
30. public static void main(String[] srgs){
31. Student s = new Student("TOM",'男 ',20,"20070308017","Computer");
32. System.out.println(s.getDetails());
33. }
34. }
66
子类对象的创建与实例化过程
1 、分配对象所需的全部内存空间,并初始化为“ 0”值
2 、按继承关系,自顶向下显式初始化3 、按继承关系,从顶向下调用构造方法
注意:一般子类构造方法的第 1 行通过 super 来调用父类的构造方法,若不使用 supper 关键字指定,则自动调用父类的无参数构造方法。如果父类中没有无参数的构造方法,则将产生错误。
67
68
练习:阅读下面的程序,经编译后,运行结果是什么1. class Super{ 2. public int i=0; 3. public Super(){ 4. i=1; 5. } 6. } 7. public class Sub extends Super{ 8. public Sub(){ 9. i=2; 10. } 11. public static void main(String args[]){ 12. Sub s=new Sub(); 13. System.out.println(s.i); 14. } 15. }
运行结果: 2
69
练习:阅读下面的程序,经编译后,运行结果是什么1. class Person{ 2. public Person(){ 3. System.out.println("hi!"); 4. } 5. public Person(String s){ 6. this(); 7. System.out.println("I am "+s); 8. } 9. }10. public class Who extends Person{ 11. public Who(){ 12. this("I am Tony"); 13. } 14. public Who(String s){ 15. super(s); 16. System.out.println("How do you do?"); 17. } 18. public static void main(String args[]){ 19. Who w = new Who("Tom"); 20. }21. }
70
继承关系中对成员的访问(最近匹配原则) ( 1 )在子类中访问属性和方法时将优先查找自己定义的属性
和方法。如果该成员在本类存在,则使用本类的,否则,按照继承层次的顺序到其祖先类查找。
( 2) this 关键字特指本类的对象引用,使用 this 访问成员则首先在本类中查找,如果没有,则到父类逐层向上找。
( 3) super 特指访问父类的成员,使用 super 首先到直接父类查找匹配成员,如果未找到,再逐层向上到祖先类查找
71
终止继承
出于安全性方面的考虑,要避免子类继承超类的某个方法,可以使用“ final” 关键字来使继承终止。 这样使此方法不会在子类中被覆盖(即子类中不能有和此方法同名的方法)
不能被继承的类称为最终类。 如: final class Last;
用 final说明的成员方法为最终方法。 如: public final void printsuper( )
72
测试类的继承关系
在 java 中,类一次只能继承一个超类
使用“是一个( is a)短语验证继承” 例如: Person和 Student 继承是正确的,因为
一个 Student 是一个 Person ,即 Studen is a Person
注:类只有用 public、 final 或缺省定义,如果用final 定义的类则不能被继承。
73
测试继承相关类1. public class Shirt{2. char size;3. String material;4. float price;5. int numberOfButtons;6. // and so on7. }1. public class Skirt{2. char size;3. String material;4. float price;5. int length;6. // and so on7. }
74
测试继承错误的例子1. public class Shirt{2. char size;3. String material;4. float price;5. // and so on6. }
1. public class Skirt extends Shirt{
2. int length;
3. // and so on
4. }
验证该继承的有效性:“子类是超类”如果这句话有意义,则该继承正确。
本示例的验证短语是“裙子是衬衣”,显示不符合现实,所以这个继承是无效的。
75
测试继承1. public class Clothing{2. char size;3. String material;4. float price;5. // and so on6. }
1. public class Shirt extends Clothing{
2. int numberOfButtons;
3. // and so on
4. }
1. public class Skirt extends Clothing{
2. int length;
3. // and so on
4. }
76
作业
3.5请为管理学校中教师的工作证和学生的学生证设计一个类体系结构,尽可能保证代码的重用率。假设工作证包括编号、姓名、姓名、出生年月、部门、职务和签发工作日期;学生证包括编号、姓名、姓名、出生年月、学院、专业、入校时间及每学年的注册信息等。
77
3.6 多态性
多态性是指同一个名字的若干个方法,有不同的实现(即方法体中的代码不一样)。
多态的两种表现形式:( 1) 同一类中的方法多态,称为方法重载
overloading :同一类中允许多个同名方法,通过参数的数量、类型的差异进行区分。
( 2) 子类对父类方法的重新定义,称为方法覆盖overriding :方法名、返回值和参数形态完全一样。
78
方法的重载
一个类中,有若干个方法名字相同,但方法的参数不同,称为方法的重载。
在调用时, Java执行方法名、参数个数和参数类型完全相同的方法体。
79
Java标准输出方法的重载
80
例:下面程序中第 4 行调用哪一行的 show 方法?1. public class Test{2. public static void main(String argv[]) {3. Test t = new Test();4. t.show(1+3);5. }6. public void show(int Val1,int Val2){7. System.out.println(Val1+Val2);8. }9. public void show(int Val1) {10. System.out.println(Val1);11. }12. public void show(int Val1,int Val2,int Val3) {13. System.out.println(Val1+Val2+Val3);14. }15. }
81
方法重载的原则 方法名相同。 方法的参数类型、个数、顺序只要有一项不相同。 返回类型、修饰符可相同或不相同。 public int area(int r)
public float area(int h,float r)
public float area(float h,int r)
是正确的方法重载,而下面的则不是正确的方法重载:
public int area(float h,float r)
public float area(float h,float r) 构造方法也能重载
82
练习:选择题下述哪一组方法,是一个类中方法重载的正确写法? A) int addValue( int a, int b ){return a+b;}
float addValue ( float a, float b) {return a+b;}
B) int addValue (int a, int b ){value=a+b; }
float addValue ( int a, int b) {return (float)(a+b);}
C) int addValue( int a, int b ){return a+1;}
int addValue ( int a, int b) {return a+b;}
D) int addValue( int a, int b ) {return a+b;}
int addValue ( int x, int y ) {return x+y;}
83
激活重载的构造方法 在一个构造方法中可以利用另一个构造方法。1. class People{2. private String name;3. private int age;4. public People(String n, int age){5. name = n ;6. this.age = age;7. }8. public People( String n){
this(n,0); 9. }10. public People( ){ 11. this(“Unknown”); 12. }13. }
84
一般地,重载的方法应当具有相似的行为 ( 功能 )
1. public class ShapeArea{ 2. public double area(float r){ 3. return Math.PI*r*r;4. } 5. public double area(float a,float b){6. return a*b;7. }8. public double area(float a,float b,float c){9. float d;10. d=(a+b+c)/2; 11. return Math.sqrt(d*(d-a)*(d-b)*(d-c));12. }13. public static void main(String args[]){14. ShapeArea s=new ShapeArea();15. System.out.println("The area of circle : "+s.area(3));16. System.out.println("The area of rectangle : "+s.area(7,4));17. System.out.println("The area of triangle : "+s.area(3,4,5));18. }19. }
例:在类 ShapeArea 中声明三个同名方法 area ,求圆、矩形和三角形的面积
85
方法的覆盖(重写) 在子类和超类中有同名的方法(参数也相同),子类中的方法覆盖超类的方法。
如果超类和子类有同名且参数相同的方法,那么超类的对象调用超类的方法,子类的对象调用子类的方法。
通过覆盖可以使同名的方法在不同层次的类中有不同的实现
86
方法覆盖的原则( 1 )
1 、子类方法的名称、参数签名和返回类型必须与其父类的方法的名称、参数签名和返回类型一致
public class Base{public void method(){...}
}public class Sub extends Base{ public void method(){...} public int method(int a){ return 0; }}
87
方法覆盖的原则( 2 )
2 、子类方法不能缩小父类方法的访问权限public class Base{
public void method(){...}
}
public class Sub extends Base{
protected void method(){...}//编译错误,子类方法缩小了父类方法的访问权限
}
88
方法覆盖的原则( 3 )
3 、子类方法不能抛出比父类方法更多的异常4 、方法覆盖只存在于子类和父类(包括直接父类和间接父
类)之间。在同一个类中方法只能被重载,不能被覆盖。5 、父类的静态方法不能被子类覆盖为非静态的方法,反之亦
然;6 、子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。
7 、父类的私有方法不能被覆盖8 、父类的抽象方法可以被子类覆盖:子类实现父类的方法或
重新声明父类的抽象方法。
89
练习:阅读下面的程序,运行结果是?1. public class TestDemo {2. int m=5;3. public void some(int x) {4. m=x;5. }6. public static void main(String args []) {7. new Demo().some(7);8. }9. }10. class Demo extends TestDemo {11. int m=8;12. public void some(int x) {13. super.some(x);14. System.out.println(m);15. }16. }A. 5 B. 8 C. 7 D .无任何输出 E .编译错误
答案: CSystem.out.println(super.m);
答案: B
90
练习:选择题对于下列代码: public class SupperClass { public int sum( int a, int b) { return a+b; } } class SubClass extends SupperClass { } 下述哪个方法可以加入类 SubClass? ( ) A) int sum ( int a, int b ){ return a+b;} B) public void sum (int a, int b ){ return ;} C) public float sum ( int a, int b){ return a+b;} D) public int sum ( int a, int b ) { return a+b;}
答案: D
1.对于方法的重载,在程序编译时,根据调用语句中给出的参数,就可以决定在程序执行时调用同名方法的版本,称为编译时的绑定 (静态绑定 ) 。
2.对于方法的覆盖,要在程序执行时,才能决定调用同名方法的版本,称为运行时的绑定 (动态绑定 ) 。
3.当子类对象赋给超类对象或作为参数传递给超类对象时,超类对象调用子类中的同名方法。
静态绑定和动态绑定
子类对象赋值给父类对象(情形 1) 1. class Supclass {2. public void print(){3. System.out.println("this is 父类 print() 方法 "+"——此时
对象 "+this.toString());4. }5. } 6. public class Subclass extends Supclass {7. public static void main(String[] args) {8. Supclass sup=new Subclass();9. sup.print();10. System.out.println("此时对象 "+sup.toString());11. }12. }
输出结果 this is 父类 print() 方法——此时对象是
Subclass@11b86e7
此时对象是 Subclass@11b86e7
说明: Supclass sup=new Subclass();虽然声明的对象是父类对象,但实际的内存空间是子类对象的。继承父类的方法 public void print() 被调用,输出的是子类对象名字解析。
结论:编译时声明的对象是父类对象,但运行时却是子类对象。子类没有重写父类的方法,则此时的对象调用继承父类的方法。
子类对象赋值给父类对象(情形 2) 1. class Supclass {2. public void print(){3. System.out.println("this is 父类 print() 方法 "+"——此时对象 "+this.toString());
4. }5. } 6. public class Subclass extends Supclass {7. public void print(){8. System.out.println("this is 子类 print() 方法 "+"此时对象 "+this.toString());
9. }10. public static void main(String[] args) {11. Supclass sup=new Subclass();12. sup.print();13. System.out.println("此时对象 "+sup.toString());14. }15. }
输出结果 this is 子类 print() 方法——此时对象是
Subclass@11b86e7 此时对象是 Subclass@11b86e7
说明:子类重写了父类的 print() 方法,此时的调用的是子类的 print() 方法。
结论:此时对象运行时确实是子类对象,如果子类没有重写父类的方法,则此时的对象调用继承父类的方法;否则,此时的对象调用子类方法
问题:将子类对象赋值给父类对象 (即 Supclass sup=new Subclass()), 得到的就是子类对象 ,即 sup 就是子类对象 ?
子类对象赋值给父类对象(情形 3) 1. class Supclass {
2. protected String className=" 父类属性 ";3. public void print(){4. System.out.println("this is 父类 print() 方法 "+" 此时对象 "+this.toString());
5. }6. } 7. public class Subclass extends Supclass {8. protected String className=" 子类属性 ";9. public void print(){10. System.out.println("this is 子类 print() 方法 "+" 此时对象 "+this.toString());
11. }12. public static void main(String[] args) {13. Supclass sup=new Subclass();14. System.out.println(" 此时的属性是 :"+sup.className);15. }16. }
输出结果
此时的属性是:父类属性 说明:给父类添了一个属性 className,子类
重写了这个属性。 但输出此时对象的属性时 ,却是父类的属性。
结论:将子类对象赋值给父类对象 , 方法和属性和我们正统的继承关系很不一样。
子类对象赋值给父类对象(情形 4)1. class Supclass {
2. protected String className="父类属性 ";3. public void print(){ 4. System.out.println("this is 父类 print() 方法 "+"此时对象 "+this.toString());5. }6. } 7. public class Subclass extends Supclass {8. protected String className="子类属性 ";9. protected String otherClassName="其它子类属性 ";10. public void print(){11. System.out.println("this is 子类 print() 方法 "+"此时对象 "+this.toString());12. }13. public void println(){14. System.out.println("this is 子类 println() 方法 "+"此时对象 "+this.toString());15. }16. public static void main(String[] args) {17. Supclass sup=new Subclass();18. sup.println();19. System.out.println("此时的属性是 :"+sup.otherClassName);20. }21. }
编译出错
第 16 行编译出错:找不到符号 println();第 17 行编译出错:找不到符号 className
说明:给子类扩展了其它的属性和方法(子类中非覆盖的变量和方法)。 此时对象无法访问这些属性和方法。
问题 :此时对象究竟是子类对象 ,还是父类对象 ?
子类对象赋值给父类对象的总结 将子类对象赋值给父类对象 ,所得到对象是这样的一
个对象 : 它是一个编译是为父类对象,但运行却是一个子类对象,具体特征如下:
1. 被声明为父类对象;2. 拥有父类属性;3. 占用子类的内存空间;4. 子类方法覆盖父类的方法时,此时对象调用的是子类
的方法 ;否则,自动调用继承父类的方法。5. 无法访问子类中非覆盖的变量和方法。
1. class ParentClass{2. int x=0;3. int sum(int a,int b, int c){4. return a+b+c;5. }6. int sum(int a,int b){ 7. return a+b;8. }9. }10. class ChildClass extends ParentClass{11. public ChildClass( ) {x=10;}12. int sum(int a,int b){13. return a+b+1;14. }15. }16. class Test{17. public static void main(String args[]){ 18. ParentClass p=new ChildClass();19. System.out.println(p.sum(5,5,5)); 20. System.out.println(p.sum(5,5)); 21. System.out.println(p.x); 22. }23. }
练习:阅读左边的程序代码,写出程序运行的打印结果:
15 11 10
102
3.7 修饰词 abstract、 static和final abstract :可修饰类和方法 static :可修饰变量和方法 final :可修饰类、变量和方法以及参数
103
抽象类和抽象方法
抽象类是专门设计来让子类继承的类。
抽象类提供一个类型的部分实现,可以有实例变量,构造方法,抽象方法和具体方法。一个抽象类不会有实例。
抽象方法是必须被子类覆盖的方法。
104
抽象类的作用抽象类的作用 抽象类通常代表一个抽象概念,可以用一句话
来概括抽象类的用途:抽象类是用来继承的。
反过来可以说具体类不是用来继承的,只要有可能,我们不要从具体类继承。
105
声明抽象类和方法格式
abstract class < 类名 >{
成员变量; 方法() { 方法体 } ; // 定义一般方法 abstract 方法 ( ); // 定义抽象方法} 抽象方法不用实现代码,而是在子类中实现所有
的抽象方法。 对于成员方法,不能同时用 static和 abstract说明。对于类,不能同时用 final和 abstract说明。
106
抽象类 抽象类也可有普通的成员变量或方法。 抽象类不能直接用来生成实例。一般可通过定义子类进行
实例化。 可以生成抽象类的变量,该变量可以指向具体的一个子类
的实例。 abstract class Employee{
abstract void raiseSalary(int i) ;
}
class Manager extends Employee{
void raiseSalary(int i ){ ….}
}
Employee e = new Manager( ) ;
107
例:抽象类的例子1. abstract class Shape{2. abstract protected double area();3. abstract protected void draw();4. }5. class Rectangle extends Shape{6. float width,length;7. Rectangle(float w,float l){8. width=w;9. length=l;10. }11. public double area(){ 12. return width*length;}13. public void draw(){};14. }15. public class ShapeDemo{16. public static void main(String args[]){17. Rectangle r=new Rectangle(6,12);18. System.out.println("The area of rectangle:"+r.area());19. }20. }
108
练习:以下程序的编译和运行结果为? 1. abstract class Base{2. abstract public void myfunc();3. public void another(){4. System.out.println("Another method");5. }6. }7. public class Abs extends Base{8. public static void main(String args[ ]){9. Abs a = new Abs();10. a.amethod();11. }12. public void myfunc(){13. System.out.println("My Func");14. } 15. public void amethod(){16. myfunc(); 17. }18. }
A 输出结果为 My FuncB 编译指示 Base 类中无抽象方法C 编译通过但运行时指示 Base 类中无抽象方法D 编译指示 Base 类中的 myfunc 方法无方法体
答案: A
109
static 关键字 static 修饰类的变量,称为类变量,它在该类所有
实例之间是共享的。在加载该类时,只分配一次空间,并初始化。例: class Employee {
…static int com ;…}
则运行时,com
com com com
e1 e2 e3
110
类变量 类变量可以是 public 或 private 对于 public 类型的类变量,可以在类外直接用类名调用而不需要初始化。
1. public class StaticVar{2. public static int number ;3. }4. public class Otherclass{5. public void method(){6. int x = StaticVar.number ;7. }8. }
111
类方法 static 修饰类的方法,称为类方法,它可以直接被调用,
而不需要生成任何实例 类方法不属于类的某个对象,可以被由该类创建的所有对
象使用,也可以被其它类引用。1. public class GeneralFunction{2. public static int addUp(int x, int y){3. return x+y ;4. }5. }
6. public calss UseGeneral{7. public void method(){8. int c = GeneralFunction.addUp(9,10);9. }10. }
112
类方法应注意的两个问题1 、静态方法中没有 this 指针,不能访问所属类的非静态变
量和方法,只能访问方法体内的局部变量、参数和静态变量。
① class AccessVar{
② int x;
③ public static void setX(){
④ x=9;
⑤ }
⑥ }
2 、子类不能重写父类的静态方法,也不能把父类的非静态方法重写为静态方法,但可在子类重可以声明与父类静态方法相同的方法,将父类的静态方法隐藏。
编译出错,无法从静态上下文中引用非静态变量 x
113
例: static 修饰的成员属性例子(P86)1. public class Book{2. public int id; // 书的编号3. public static int bookNumber = 0; // 书的总数
4. public Book( ){5. bookNumber ++;6. } // Book 构造方法结束
7. public void info( ){8. System.out.println( "当前书的编号是: " + id);9. } // 方法 info 结束
10. public static void infoStatic( ){11. System.out.println( "书的总数是: " + bookNumber);12. } // 方法 infoStatic 结束
114
例: static 修饰的成员属性例子13. public static void main(String args[ ]){14. Book a = new Book( );15. Book b = new Book( );16. a.id = 1101;17. b.id = 1234;18. System.out.print( " 变量 a 对应的 ");19. a.info( );20. System.out.print( " 变量 b 对应的 ");21. b.info( );22. Book.infoStatic( );23. System.out.println( " 比较 (a.bookNumber==Book.bookNumber)"24. + " 的结果是: " + (a.bookNumber==Book.bookNumber));25. System.out.println( " 比较 (b.bookNumber==Book.bookNumber)"26. + " 的结果是: " + (b.bookNumber==Book.bookNumber));27. } // 方法 main 结束28. } // 类 Book 结束
115
例:子类重写父类的静态方法,将父类的非静态方法重写为静态方法,都会出现编译错误1. class StaticMethodSupClass{2. public void methodOne(int i){}3. public void methodTwo(int i){}4. public static void methodThree(int i){}5. public static void methodForth(int i){}6. }7. class StaticMethodSubClass extends
StaticMethodSupClass{8. public static void methodOne(int i){} 9. public void methodTwo(int i){}10. public void methodThree(int i){}11. public static void methodForth(int i){}12. }
//错误
//错误//正确
//正确
116
final 关键字 final 修饰类,称为最终类,改类不能被继承 final 修饰方法,该方法不能被其所在类的子
类覆盖(重写) final 修饰基本变量,即为常量,值无法改变;
final 修饰引用类型变量,该引用变量不能改变,即不能重新赋值,但对象内的成员可以改变
final 修饰方法的参数,表示该方法不期望被传进来的参数有任何改变
117
1. class Student{2. private final long StudentID;3. private static long number=2007030801;4. public Student(){5. StudentID = number++;6. }7. public long getID(){8. return StudentID;9. }10. public static void main(String[] args){11. Student [ ] s = new Student[5];12. for ( int i=0; i<s.length; i++){13. s[i]=new Student();14. System.out.println("The StudentID is "+s[i].getID());15. }16. }17. }
例 :声明类的 final 变量并在构造方法中赋值
118
3.8 接口 接口是在程序开发的早期建立一组协议(规范公
共的操作接口)而不具体实现,便于设计更合理的类层次,代码更灵活
在 Java 中,一个类只能有一个超类。但 Java提供了接口用于实现多重继承,一个类可以有一个超类和多个接口。
接口 (interface) 是一个特殊的类:由常量和抽象方法组成。
119
接口的定义[public] interface 接口名 [extends 父接口名列表 ] {
域类型 域名 = 常量值 ; //常量域声明 返回类型 方法名 ( 参数列表 ) [throw 异常列表 ]; // 抽象
方法声明 } 接口只包括常量定义和抽象方法。 接口具有继承性 , 一个接口还可以继承多个父接口,
父接口间用逗号分隔。 系统默认 , 接口中所有属性的修饰都是 public static
final ,也就是静态常量; 系统默认 , 接口中所有方法的修饰都是 public
abstract 。
120
接口定义的形式 1
接口中声明的变量全部为 final 和 static 类型的,并且这个接口的作用在于定义一些值不能改变的变量。举个例子:public interface ObjectConstants{ public static final String SPACE = new String(" "); public static final char FORMAT = '\t';}
121
接口定义的形式 2
接口中只定义可供实现的抽象方法 public interface Flyer {
public void takeoff(); public void land();
public void fly();
}
122
接口定义的形式 3
接口中既声明变量,又定义可供实现的抽象方法。
如非必要,一般会将这样一个接口定义拆分成两个接口定义
123
接口的实现 (implements)
接口定义了一套行为规范,一个类实现这个接口就要遵守接口中定义的规范,实际上就是要实现接口中定义的所有方法。
接口实现的格式: class < 类名 > implements 接口名 1 ,接口名 2 ,…
一个接口可以被一个或多个类实现。 当一个类实现了一个接口,它必须实现接口中所
有的方法,这些方法都要被说明为 public ,否则会产生访问权限错误。
124
例 : 接口的实现
用面向对象的思想定义一个接口 Area ,其中包含一个计算面积的方法 CalsulateArea() ,然后设计 MyCircle和MyRectangle 两个类都实现这个接口中的方法 CalsulateArea() ,分别计算圆和矩形的面积,最后写出测试以上类和方法的程序 TestArea.java 。
125
1. interface Area{2. public double CalsulateArea();3. }4. class MyCircle implements Area{5. double r;6. public MyCircle(double r){7. this.r=r;8. }9. public double CalsulateArea(){10. return Math.PI*r*r;11. }12. }
126
13. class MyRectangle implements Area{14. double width,height;15. public MyRectangle(double w,double h){16. width=w;17. height=h;18. }19. public double CalsulateArea(){20. return width*height;21. }22. }23. class TestArea{24. public static void main(String []args){25. MyCircle c=new MyCircle(2.0);26. java.util.Formatter f=new java.util.Formatter();27. System.out.println("圆的面
积: "+f.format("%.2f",c.CalsulateArea()));28. MyRectangle r=new MyRectangle(2.0,3.0);29. System.out.println("矩形的面积: "+r.CalsulateArea());30. }31. }
127
接口和抽象类的区别 接口可以多重继承,抽象类不可以 抽象类内部可以有实现的方法,接口则没有实现的
方法 接口与实现它的类不构成类的继承体系,即接口不
是类体系的一部分。因此,不相关的类也可以实现相同的接口。而抽象类是属于一个类的继承体系,并且一般位于类体系的顶层。
接口的优势:通过实现多个接口实现多重继承,能够抽象出不相关类之间的相似性。
创建类体系的基类时,若不定义任何变量并无需给出任何方法的完整定义,则定义为接口;必须使用方法定义或变量时,考虑用抽象类。
128
练习
下列关于接口和抽象类的说法不正确的是? ( )
A) 接口也有构造方法B) 实现接口时,需对接口中的所有方法都要实现C) 抽象类也有构造方法 D) 抽象类可以派生出子类
129
练习以下是接口 I 的定义:interface I{
void setValue(int val);int getValue();
}以下哪段代码能通过编译?A 、 class A extends I{ int value; void setValue(int val){value=val;} int getValue(){return value;}}B 、 class B implements I{ int value; void setValue(int val){value=val;}}C 、 interface C extends I{
void increment( );}D 、 interface D implements I{
void increment();}
130
3.9 包 包 (package) 是类的逻辑组织形式。 Java提供的用于程序开发的类就放在各种包中。
也可以自己创建包。 一般在创建项目时为了便于管理,源文件和字节码文件是分开保存在不同的目录中的。
① 项目的文件夹为 myproject;
② 源文件保存在 myproject 下的 src文件夹中;③ 字节码文件保存在 myproject 下的 classes文件夹中
131
Java 的类和包 java常用的包有:java.lang 语言包,唯一一个不要把它明确引
入程序的包java.util 实用包java.awt 抽象窗口工具包java.text 文本包java.io 输入输出流的文件包java.applet Applet应用程序java.net 网络功能
132
包与文件夹 在实际项目开发中,一般每个类都应该定义在
某个包中。这样一方面易于管理,另一方面可以有效地防止命名冲突。包的名称一般按公司的域名反向书写。如: com.misxp
如果一个类是这样定义的:package com.misxp;
public class PackageExercise
…
那么这个类所在的文件 PackageExercise.java应该保存在 myproject/src/com/misxp 下
133
包与文件夹
它的编译好的字节码文件应该保存在myproject/classes/com/misxp 下。
如何做到以上要求,简单的办法是: 进入命令行状态 让 src 成为当前文件夹 执行命令 javac –d ../classes
com/misxp/*.java
134
引用 Java 定义的包 导入包如果要使用 Java 类中的包,要在源程序中用 import 语句导入。 import <包名 1>[.<包名 2>[.<包名 3>……]].< 类名 >|*;如果有多个包或类 , 用“ .” 分割,“ *”表示包中所有的类。如:import java.applet.Applet;//导入 Java.applet包中的 Applet 类import java.awt.* //导入 Java.awt包中所有的类
Java包的路径 用环境变量 classpath 设置对 Java包的搜索路径。切换到
DOS 方式,用下面命令: set classpath =.; d:\jdk1.6\lib
135
自定义包 如果在程序中没有声明包,类就放在默认的包中,
这个包是没有名字的。默认包适用于小的程序,如果程序比较大,就需要创建自己的包。
声明包的格式 package < 包名 >;
声明一个包的语句要写在源程序文件的第一行。 存放位置 在运行目录下创建一个和包同名的文件夹(字母
大小写也一样),将编译产生的 .class文件放到此文件夹中。
136
引用包中的类 如果引用其它包中的类,可用 import 语句 包路径的层次结构 包对应于一个文件夹,而类则是文件夹中的一个文件。包路径也可以有层次结构,如:
package firstpackage.package1 用“ .” 将包的名字分开,形成包路径的层
次, package1是 Firstpackage文件夹的子文件夹。 Java 源程序文件的结构,一个 Java 的源程序文件依
次有以下部分: 包的声明语句 包的导入语句 类的声明
137
包和访问权限 一个包中只有访问权限为 public 的类才能被其它包
引用(创建此类的对象),其它有默认访问权限的类只能在同一包中使用。
在不同包中类成员的访问权限。 public 类的 public 成员可以被其它包的代码访问。
它的 protected 成员可以被由它派生的其它包中的子类访问。
默认访问权限类的成员,不能被其它包的代码访问
138
例:创建包 firstpackage 和类 Date, 同时在PersonDemo.java 创建类 Person
1. package firstPackage;2. import java.util.*;3. public class Date{4. private int year,month,day;5. public Date(int y,int m,int d){6. year=y;7. month=m;8. day=d;9. }10. public Date(){;}11. public int thisyear(){12. return
Calendar.getInstance().get(Calendar.YEAR);13. }14. }
139
1. import firstPackage.Date;2. class Person{ 3. String name;4. int age;5. public Person(String na,int ag){6. name=na;7. age=ag;8. }9. public Person(){;}10. public int birth(int y){ 11. return y-age+1;12. }13. }14. public class PersonDemo{15. public static void main(String args[]){16. Person ps=new Person("Tom",21);17. Date now=new Date();18. int y=now.thisyear();19. System.out.println(ps.name+" was born in "+ps.birth(y));20. }21. }
140
程序的发布 把开发好的程序交给用户就叫发布。总不能让用户也像我们前面讲的一样去执行吧。而且,一个较大的软件肯定有很多的字节码文件,把一大堆字节码文件交给用户也不方便。
我们希望: 把许多字节码文件打包成一个文件 用户双击这个文件就可以运行程序,就像我们运行
windows 中的应用程序一样。
141
Jar与 JAR文件
JDK 中有一个实用工具 jar.exe 可以完成打包工作。打包好的文件扩展名一般为 jar ,所以叫JAR文件。
由于 JAR文件中有很多类,如果想让该 JAR文件可以直接运行,我们就必须告诉 java虚拟机那一个类是包含main 方法的主类。这是通过编辑一个 .mf 的文件来实现的。
142
.mf文件
my.mf文件应该包含以下一行内容:Main-Class: 主类的完整名称如:Main-Class: PersonDemo
这一行后面必须回车换行,否则可能出错。另外,冒号后面必须空一格。
143
创建可执行的 JAR文件
(1) 使用工具 jar.exe 可以创建可执行的 JAR文件。 进入命令行状态 让 classes文件夹成为当前文件夹 执行以下命令:Jar cmf my.mf Test.jar *.class firstPackage
就可以得到 Test.jar打包文件。 Test可以自己取。
双击 test.jar 或用命令 java –jar Test.jar 就可以运行程序。
144
创建可执行的 JAR文件
(2) 使用 Eclipse的 export功能① 选中项目名称或需打包的类 ,单击右键 选中
export
② 选择 jar file
③ 选择生成 .jar文件后的存放目录,并按向导操作下一步
④ 填写含有 main 方法的主类类名
145
3.10 内部类 内部类的类名只用于定义它的类或语句块之内,
在外部引用它时必须给出带有外部类名的完整名称,并且内部类的名字不允许与外部包类的名字相同。
内部类可以是抽象类或接口,若是接口,则可以由其它内部类实现
按照内部类是否含有显示的类名,可将内部类分为:
1. 实名内部类2. 匿名内部类
146
1 、实名内部类 格式:[ 类修饰词表 ] class 类名 [extends 父类名 ]
[implements 接口名列表 ]{ 类体} 实名内部类的封装性增加了保护模式和私有模式,即
实名内部类的修饰词可以是 protected或 private 实名内部类的修饰词可以是 static ,称为静态实名内
部类 没有 static 修饰的内部类,称为不具有静态属性的实
名内部类,它的成员域若有静态属性,则必须有 final属性,但不能有静态属性的方法
147
创建实名内部类
创建静态实名内部类格式: new 外部类名 . 实名内部类名 ( 构造方法调用
参数列表 ) 创建不具有静态属性的实名内部类格式: 外部类表达式 .new 实名内部类名 ( 构造方法
调用参数列表 )
148
访问实名内部类成员
访问静态实名内部类的静态成员域格式: 外部类名 . 实名内部类名 . 静态成员域名 访问静态实名内部类的不具有静态属性的成员域格式: 表达式 . 成员域名 调用静态实名内部类的静态成员方法格式: 外部类名 . 实名内部类 . 静态成员方法名 ( 成员方法
调用参数列表 ) 调用静态实名内部类的不具有静态属性的成员方法格式:
表达式 . 成员方法名 ( 成员方法调用参数列表 )
149
访问实名内部类成员 (续 )
访问不具有静态属性的实名内部类的静态成员域格式:
外部类名 . 实名内部类名 . 静态成员域名 访问不具有静态属性的实名内部类的不具有静
态属性的成员域格式: 表达式 . 成员域名 访问不具有静态属性的实名内部类的不具有静
态属性的成员方法格式: 表达式 . 成员方法名 ( 成员方法调用参数列表 )
150
内部类访问外包类成员1. public class Outer{ 2. private int size;3. /** 定义内部类 Inner */4. public class Inner{ 5. // 将外包类的成员变量 size递增6. public void doStuff(){ 7. size++; 8. }9. }10. Inner i=new Inner(); // 成员变量 i指向 Inner 类的对象11. public void increaseSize(){ 12. i.doStuff(); //调用内部类 Inner 的方法13. }14. public static void main(String[] a){ 15. Outer o=new Outer();16. for (int i = 0; i<4; i++){17. o.increaseSize();18. System.out.println("The value of size : "+o.size);19. }20. }21. }
151
内部类中加上修饰符访问同名外部包类成员1. public class Outer{ 2. private int size;3. /** 定义内部类 Inner */4. public class Inner{ 5. private int size;6. public void doStuff(int size){ 7. size++; // 存取局部变量8. this.size++; // 存取内部类的成员变量9. Outer.this.size++; // 存取其外包类 Outer 的成员变量10. System.out.println("size in Inner.doStuff(): "+size);11. System.out.println("size of the Inner class: "+this.size);12. System.out.println("size of the Outer class:
"+Outer.this.size);13. }14. }15. Inner i=new Inner(); // 成员变量 i指向 Inner 类的对象16.
17. public void increaseSize(int s){ 18. i.doStuff(s); //调用内部类 Inner 的方法19. }20. public static void main(String[] a){ 21. Outer o=new Outer();22. o.increaseSize(10);23. }24. }
152
在外包类的语句块中定义内部类1. class Outer{ 2. private int size=5;3. /** 方法 makeInner() ,返回一内部类对象 */4. public Object makeInner(final int finalLocalVar){ 5. int LocalVar=6;6. class Inner{ 7. public String toString(){ 8. return ("#<Inner size="+size+9. " finalLocalVar="+finalLocalVar+">");10. }11. }12. return new Inner(); // 方法 makeInner()返回一内部类对
象13. }14. public static void main(String[] args){15. Outer outer=new Outer ();16. Object obj=outer.makeInner(40);17. System.out.println("The object is "+obj.toString());18. }19. }
153
在外包类的其它类中访问内部类1. class Outer{ 2. private int size;3. class Inner{ 4. void doStuff(){ 5. size++; 6. System.out.println("The size value of the Outer class:
"+size); 7. }8. }9. }10. public class TestInner{11. public static void main(String[] a){ 12. Outer out=new Outer();
13. Outer.Inner in =out.new Inner();14. in.doStuff();15. }16. }
154
2 、匿名内部类
匿名内部类不具有类名,不具有抽象和静态属性,并且不能派生出子类。
定义匿名内部类的格式:new 父类名 ( 父类型的构造方法的调用参数列表 ){
类体}
155
例:父类型为类的匿名内部类p961. abstract class J_Class{
2. int m_data;3. public J_Class( int i ){4. m_data = i;5. } // J_Class 构造方法结束6. public abstract void mb_method( );7. } // 接口 J_Class 结束
8. public class J_InnerClass{9. public static void main(String args[ ]) {10. J_Class b = new J_Class( 5 ){11. public void mb_method( ) {12. System.out.println( "m_data=" + m_data );13. } // 方法 mb_method 结束14. }; // 父类型为类 J_Class 的匿名内部类结束15. b.mb_method( );16. } // 方法 main 结束17. } // 类 J_InnerClass 结束
156
例:父类型为接口的匿名内部类 p99
1. interface J_Interface{2. public static int m_data = 5;
3. public abstract void mb_method( );4. } // 接口 J_Interface 结束
5. public class J_InnerInterface{6. public static void main(String args[ ]) {7. J_Interface b = new J_Interface( ){8. public void mb_method( ){9. System.out.println( "m_data=" + m_data );10. } // 方法 mb_method 结束11. }; // 实现接口 J_Interface 的匿名内部类结束12. b.mb_method( );13. } // 方法 main 结束14. } // 类 J_InnerInterface 结束
157
作业
3.6 定义一个 Rectangle(长方形 ) 实现AreaInterface 接口 , 该类有两个 private 访问权限的双精度浮点型变量 x(长 )和 y(宽 );定义一个 public 访问权限的构造方法,用来给类变量赋值;实现 area() 方法得到长方形的面积;定义 toString() 方法,返回一段字符串信息,内容如下格式:“该长方形面积为:”+面积;
定义一个 TestArea 类,在他的 main() 方法中;创建一个 Rectangle 的实例,长为 10.0 ,宽为 20.0 ,输出它的面积;
158
作业
P114 9
159
Questions?