• 1. 面向对象程序设计1教材4~5章 抽象与 封装 类定义 重载 继承 多态
  • 2. 抽象所有编程语言都提供“抽象”功能。 你所能解决问题的复杂程度取决于抽象的种类和质量。 汇编语言对于计算机(机器语言)是一步很小的抽象。 过程化语言(高级语言)对于汇编语言的抽象迈了一大步。但你所考虑的仍然是机器的结构而非问题的结构。 面向对象方法为程序员提供了一种表示问题空间元素的全新的方法,而且不受问题类型的约束。
  • 3. 抽象问题空间元素→方法空间中的表示:对象 “对象”是现实世界的实体或概念在计算机逻辑中的抽象表示。 面向对象的程序设计是以要解决的问题中所涉及到的各种对象为主要考虑因素。 面向对象语言更加贴近人的思维方式。OOP允许你用问题空间中的术语来描述问题。 在面向对象程序设计中还需要一些其他对象,而它们没有在问题空间的对应体。
  • 4. 对象“对象”就象是一台小“计算机”: 对象有状态 对象有你要求它执行的操作 现实世界中的对象: 它们都有自己的状态 它们又都有自己的行为 程序中的对象 用属性表示状态 用方法(代码)实现行为
  • 5. 对象与过程考虑对象 —考虑过程 :前者更有意义 设计对象 —设计过程:前者更富挑战性,尤其是设计可重用的对象。 面向对象的程序设计和问题求解力求符合人们日常自然的思维习惯,降低分解问题的难度和复杂性,提高整个求解过程的可控制性、可监测性和可维护性,从而达到以较小的代价和较高的效率获得较满意效果的目的。
  • 6. 封装-信息隐藏“ 封装”是一种组织软件的方法。其基本思想是把客观世界中联系紧密的元素及相关操作组织在一起,使其相互关系隐藏在内部,而对外仅仅表现为与其他封装体间的接口。从而构造出具有独立含义的软件实现。 封装并不是一个新的概念,但在面向对象的程序设计中提出了一种全新的封装方法-类
  • 7. 封装-信息隐藏每个类里都封装了相关的数据和操作。在实际的开发过程中,类多用来构建系统内部的模块。 由于封装特性把类内的数据保护得很严密,模块与模块间仅通过严格控制的界面进行交互,使它们之间耦合和交叉大大减少,从而降低了开发过程的复杂性,提高了效率和质量,减少了可能的错误,同时也保证了程序中数据的完整性和安全性。
  • 8. 封装-软件复用封装使得对象: 对内成为一个结构完整、可自我管理、自我平衡、高度集中的整体。 对外则是一个功能明确、接口单一、可在各种合适的环境下都能独立工作的有机的单元。 类的封装特性使得类可重用性大为提高,这样的有机单元特别适宜构建大型标准化的应用软件系统,可以大幅度地提高生产效率。
  • 9. 类定义—属性说明定义Employee类 class Employee { String name ; int age ; float salary ; } 创建一个Employee类的对象(职工对象) Employee e1 = new Employee( ) ; e1.name=“张立” ; e1.age=21; e1.salary = 528.37F ;final 类型 常量名=值实例变量—成员变量 其生命周期与对象存在的时间相同。
  • 10. 类定义—方法说明为Employee类增加打印方法——将数据和代码封装在一个单一的实体中。 class Employee { String name; int age; float salary ; void print() { System.out.println(name + " 年龄为:" + age + " 月薪为: " + salary ) ; } //print() 方法 } //class
  • 11. 对象方法的含义Employee emp = new Employee( ) ; emp.name="张立" ; emp.age=21; emp.salary = 528.37F ; 一旦对象被建立并被初始化,就可以调用该方法打印这个对象的数据。 emp.print() ; 对象emp执行print 方法操作自己的数据;而不是从外部操作对象的数据。 向对象发消息,让对象做某件事。4-1
  • 12. 对象的几个特征Everything is an object 原则上说,你可以将所要解决的问题中的任何一个“概念组件”在程序中表示为一个对象。 对象类似一个记录,可以存储数据,但你还可以让它执行某些操作(作用在它自身的数据上)。 A program is a bunch of objects telling each other what to do by sending messages 所谓 对一个对象发信息,就是调用该对象的一个方法。 Every object has a type 每一个对象都是某个类的一个实例。 对象+消息
  • 13. 信息隐藏-封装属性的访问权限 private public 默认(相当于package} 使用private 修饰符修饰属性后,就不能从对象的外部直访问这些属性(类内方法可以访问) ,而只能通过该类的方法来操作这些属性。 私有属性—公有方法 方法可以保护数据(做各种合法性检查)。 使得代码可维护性好(代码可变,接口不变)
  • 14. 信息隐藏-封装(电梯例)电梯类定义(没有体现封装的类定义) class BadElevator{ public boolean doorOpen=false; //表示电梯门的状态 public int floor = 1; //表示电梯停靠层数 public final int TOP_FLOOR=5; //定义电梯最髙层 public final int BOTTOM_FLOOR=1; //定义电梯最低层 }
  • 15. 电梯操作(操作BadElevator类的对象) public class test1{ public static void main(String[] args) { BadElevator e1 = new BadElevator(); //创建一个电梯对象 e1.floor--; // 到地下室了 e1.floor++; e1.floor=7; // 没有7层 e1.doorOpen=true; // //通过改变对象属性值实现开门动作 e1.doorOpen=false; //关门 e1.floor=1; // 到第一层 e1.doorOpen=true; // 门开,住户上下电梯。 e1.floor++; // 门还没关,电梯就开了。 e1.doorOpen=false ; e1.floor-- ; e1.floor-- ; } } 信息隐藏-封装(电梯例)对象自身没有保护, 这样操作会产生很多误动作4-2
  • 16. 信息隐藏-封装(电梯例)电梯类定义(体现封装的类定义) public class Elevator { private boolean doorOpen=false; //门的开关状态 private int floor =1 ; //电梯停靠层数 private int weight = 0 ; //表示当前乘客总重量 final int CAPACITY=1000 ; //电梯乘客重量上限 final int TOP_FLOOR=5 ; //电梯最高停靠层数 final int BOTTOM_FLOOR=1 ; //电梯最低停靠层数 // 以下是方法定义 public void openDoor() { // 开门方法 doorOpen=true ; //通过公共方法操作私有属性 } 属 性 定 义4-3
  • 17. 信息隐藏-封装(电梯例)public void closeDoor() { //关门方法,关门时检查是否超重 checkWeightSensors(); //调用测试重量的方法 if (weight<=CAPACITY) //如果没有超重 doorOpen=false ; //关门 else //如果超重,则不关门,且通过响声提示 for(int counter=0;counter<10;counter++) System.out.print((char) 7) ; //ASCII码7为beep } private void checkWeightSensors() { // 检查超重的方法(内部方法) weight=(int) (Math.random()*1500) ; //通过随机数模拟乘客重量 System.out.println("乘客重量是: " + weight); //显示乘客重量 }
  • 18. 信息隐藏-封装(电梯例)public void goUp() { // 上升一层的方法定义 if(!doorOpen) { if(floor
  • 19. 信息隐藏-封装(电梯例)public void goDown() { // 下降一层的方法定义 if(!doorOpen) { if(floor>BOTTOM_FLOOR) { floor-- ; //如果门已关、且没到低层,才能下降。 System.out.println(floor) ; } else System.out.println("已在底层"); } else System.out.println("门没关!"); }
  • 20. 信息隐藏-封装(电梯例)public void setFloor(int goal) { //指定停靠层的方法定义 if(goal>=BOTTOM_FLOOR && goal <=TOP_FLOOR) { while(floor!=goal) { if(floor
  • 21. 信息隐藏-封装(电梯例)电梯操作(操作Elevator类的对象) public class Test2 { public static void main(String args[]) { Elevator e2 = new Elevator(); e2.openDoor(); e2.closeDoor(); e2.goDown(); e2.goUp(); e2.goUp(); e2.openDoor(); e2.closeDoor(); e2.goDown(); 通过接口,对电梯的任何操作都不会导致误动作。
  • 22. e2.openDoor(); e2.goDown(); e2.closeDoor(); e2.goDown(); e2.goDown(); int curFloor = e2.getFloor(); if (curFloor!=5 && !e2.isOpen()) e2.setFloor(5); e2.openDoor(); e2.closeDoor(); e2.setFloor(10); System.out.println("测试结束 "); } } 信息隐藏-封装(电梯例)4-3
  • 23. 类方法的定义规则方法名后的小括号是方法的标志。 形式参数是方法从调用它的环境输入的数据。 返回值是方法在操作完成后返还给调用它的环境的数据。 要指定方法返回值类型。如没有返回值,类型要写 void 例:public boolean isOpen() { return doorOpen ; } 返回布尔值 方法名相同、但参数不同,是不同的方法。 与类名相同的所有方法都是类的构造方法。[修饰符] 返回类型 方法名([参数列表]) [throws 例外] {方法定义}见后面 的解释
  • 24. 方法体的定义在方法中定义的变量称为局部变量。 生命周期:执行进入方法体时建立,退出方法时撤消。 局部变量使用之前必须初始化。 在方法体中可以访问所有的类属性,可以调用所有类中定义的方法。 public void closeDoor() { //关门方法,关门时检查是否超重 checkWeightSensors(); //调用类中定义的方法 if (weight<=CAPACITY) //访问类属性 doorOpen=false ; //访问类属性(为类属性赋值) else //如果超重,则不关门,且通过响声提示 for(int counter=0;counter<10;counter++) System.out.print((char) 7) ; //ASCII码7为beep }
  • 25. 方法的参数方法的参数要有名有型,参数的作用域在本方法中 在方法体中,可以象属性及方法体内定义的变量那样使用参数。 方法的参数采用值传送 public void setFloor(int goal) { //指定停靠层的方法定义 if(goal>=BOTTOM_FLOOR && goal <=TOP_FLOOR) { while(floor!=goal) { //goal是参数,floor是属性 if(floor
  • 26. 不同数据类型参数的值传送含义基本类型 值传送3.143.14AB引用类型 值传送0x2F0x2FAB
  • 27. 参数传送例class Par{ void m1(int pi) { pi = 100; } void m2(StringBuffer ps) { ps = ps.append("--Shanghai") ; } void m3(Employee pe ) { pe.age=100 ; } } //class Par class Employee { String name ; int age ; float salary ; } //class Employee
  • 28. 参数传送例public class Test { public static void main(String[] args) { Par par1 = new Par() ; int x =50 ; par1.m1(x) ; //值传送,方法的执行不会改变实参x的值 System.out.println("x的值是:" + x) ; StringBuffer s= new StringBuffer("Beiging") ; par1.m2(s) ; //值传送,此处传送的是引用型变量s的值 // 但s内容是指向字符串的指针 System.out.println("s的值是:" + s);
  • 29. 参数传送例 Employee emp = new Employee( ) ; emp.name="张立" ; emp.age=50; emp.salary = 528.37F ; par1.m3(emp) ; //此处传送的是指向emp对象的指针 System.out.println("emp的age值是:" + emp.age); } //main() } //Test 4-4
  • 30. 参数传送例(数组作为参数)public class Test { public static void main(String[] args) { int[] aaa ={23,14,35,2,71,55,82,41,11,6}; Ary ay1 = new Ary(aaa); ay1.print(); //调用ay1对象的打印方法,打印数组内容 ay1.order(); //调用ay1对象的排序方法,对数组元素排序 ay1.print(); //调用打印方法,打印排序后的数组内容 for(int m=0;m
  • 31. 参数传送例(数组作为参数)class Ary { int[] m_ary ; //定义一个数组做为Ary类的属性 Ary(int[] p){ //构造函数 m_ary = p ; } void print(){ //打印数组方法 for (int i=0;i
  • 32. 参数传送例(数组作为参数) void order(){ //数组排序方法(数组下标从0开始) int p1, p2, temp; p2 = m_ary.length-1; while(p2>0){ for(p1=0 ; p1m_ary[p1+1]) { temp = m_ary[p1] ; m_ary[p1] = m_ary[p1+1] ; m_ary[p1+1] = temp; } p2 = p2 -1 ; } // while } //order } // class Ary4-5
  • 33. thisthis 只能在方法中使用,表示当前对象 this 适用用于一些特殊的场合。 例: public class MyDate { int day , month , year ; public void born() { Birthday bd = new Birthday(this) ; … } }
  • 34. this 例class Count { private int i = 0; //属性定义 Count increment() { i++; return this; } //增量方法 /* 增量方法对属性加1,并将增量后的对象做为方法返回值 */ void print() { System.out.println("i = " + i); } //打印方法 } public class Test { public static void main(String[] args) { Count c = new Count(); //创建Count类的一个对象c c.increment().increment().increment().print(); } }i为1的对象i为2的对象i为3的对象4-6
  • 35. 方法的重载用同一个名字来表示不同的方法。 这些方法同名的原因,是因为它们的最终功能和目的都相同或相似,但由于在完成同一功能时,可能有不同的具体情况,所以需要定义含不同具体内容的方法。 方法名必须相同(是同一类中定义的同名方法)。 方法的参数必须不同。同名不同方法是靠不同形式的参数列表来区分。形式参数的个数、类型、顺序只要有一种不同就可以区分。 返回类型可以不同,但不是重载的标志。 利用“重名”现象提高程序的抽象度和简洁性。
  • 36. 方法的重载例:(相同类型的工作,不同的参数) public void println(int i) public void println(float f) public void println(String s) Integer类的方法重载例: String toString() static String toString(int i) static String toString(int i , int radix) Double 类的方法重载例: String toString() static String toString(double d)System.out.println
  • 37. 方法重载例class Over{ void m1(int i){ System.out.println("m1(int): " + i); } void m1(float f){ System.out.println("m1(float): " + f); } void m1(double d){ System.out.println("m1(double): " + d); } void m1(int i, float f){ System.out.println("m1(int,float): " + i +" " + f); } void m1(float f,int i){ System.out.println("m1(float,int):" + f +" " + i); } }注意:方法名全相同
  • 38. 方法重载例public class Test { public static void main(String[] args){ Over o = new Over(); o.m1(3); o.m1(3.14F); o.m1(3.14); o.m1(3,3.14F); o.m1(3.14F,3); } }4-7
  • 39. 系统类方法重载例PrintStream类(System.out的类型) 该类提供了一组打印不同类型数据的同名方法: println() println(boolean x) println(char x) println(int x) println(long x) println(float x) println(double x) println(String x)
  • 40. 构造方法实例化对象-初始化对象 类名 对象引用名 = new 构造方法(); 例:Employee emp = new Employee(…); new运算符的参数就是调用类的构造方法,初始化新建的对象。 new运算符为对象分配内存空间,并返回对该对象的一个引用。 构造方法的名字必须是类的名字。 构造方法没有返回类型说明。 当在类的定义中,没有定义任何构造方法时,系统会为该类配一个隐含的构造方法(如:Employee()),方法中没有内容。 一旦定义了一个构造方法,隐含构造方法失效!
  • 41. 定义构造方法例 class Employee { String name; int age; float salary ; Employee(String n,int a,float s){ name = n ; if(a>=18 && a<=60) age = a ; else { System.out.println("年龄不在正确范围内"); System.exit(1); } salary = s; } }构造方法定义
  • 42. 定义构造方法例public class Test { public static void main(String[] args){ Employee emp = new Employee("王强",31,1500F); System.out.println("王强的年龄是:" + emp.age) ; } } 注意:如果再用下述方法创建Employee类的对象: Employee emp = new Employee() ; 系统会给出错误信息:4-8
  • 43. 对象初始化三步曲当为对象分配内存空间后,首先将属性单元置为: 0 (数值型,字符型) false (逻辑型) null (引用型) 这样,在对象建立后,就有明确的状态,属性都有值。 执行显式初始化(即类定义中,对属性进行的初始化) 例 : class Employee { String name = "aaa" ; int age = 20 ; float salary = 1000F ; } 执行构造方法
  • 44. 构造方法重载例class Employee { String name; int age; float salary ; Employee() { } //没有参数的构造方法 Employee(String n) { //有一个参数的构造方法 name = n ; //为职工姓名赋值 } Employee(String n , int a ){ //两个参数的构造方法 name = n ; //为职工名字赋值 age = a ; //为职工年龄赋值 }
  • 45. 构造方法重载例 Employee(String n,int a,float s){ //三个参数的构造方法 name = n ; age = a ; salary = s; } void print() { //打印职工对象信息的方法 System.out.println("职工" + name + " 年龄:" + age + " 工资:" + salary ); } } //class Employss
  • 46. 构造方法重载例public class Test { public static void main(String[] args){ Employee emp0 = new Employee() ; emp0.print() ; Employee emp1 = new Employee("赵红") ; emp1.print (); Employee emp2 = new Employee("李力",20) ; emp2.print (); Employee emp3 = new Employee("王强",30,3000F) ; emp3.print (); } }4-9
  • 47. 系统类构造方法的重载例Integer类构造方法的重载 方法1:Integer(int value) 例:new Integer(38) 方法2:Integer(String s) 例: new Integer("38") Float类构造方法的重载 方法1:Float(Float value) 例:new Float(3.14) 方法2:Float(String s) 例:new Float("3.14") String类构造方法的重载 方法1: String() 例: new String() //空串 方法2: String(char[] value) 例:char data[] = {'a', 'b', 'c'}; String str = new String(data); //字符串abc 方法3:String(char[] value, int offset, int count) 例:char data[] = {'a', 'b', 'c','d','e'}; String str = new String(data,1,3); //字符串bcd
  • 48. 继承新类必须在已有类的基础上构造。 在已有类的基础上构造新类的过程称为派生。 派生出的新类称为已有类的子类。已有类称为超类(父类)。 子类继承父类的方法和属性。 当没有显式指定父类时,父类隐含为java.lang包中的Object类。 父类子类继承 extends
  • 49. 继承继承是一种由已有的类创建新类的机制,是面向对象程序设计的基石之一。 继承体现了软件的复用,子类继承父类: 父类中的属性不需要再定义。 父类中的方法不需要再重新编写 子类继承父类的非私有属性和方法,还可以定义新的属性和方法,改写父类的方法。 子类是在父类的基础上,做一些个性化的定义。 Java中每个类都有父类。隐含为java.lang包中的Object类。
  • 50. 类的继承树学生小学生中学生大学生研究生本科生在"学生"类中定义好各类学生共性的内容,各子类中只需定义自己特有的内容。 父类"学生"类实际上是对各类学生的概括和抽象。
  • 51. 类的继承树图形三维图形二维图形园形三角形矩形正方形圆锥体球体立方体
  • 52. 类继承例属性: name age salary 方法: upSalary getInfoEmployeeEngineerManagerSecretaryDirector属性: allowance 方法: setAllowance getInfo属性: telephone department 方法: setTel getInfo注: allowance 津贴 department 部门
  • 53. 类的继承-属性与方法方法: upSalary getInfo属性: name age salaryEmployee类方法: upSalary setAllowance getInfo属性: name age salary allowanceManager类方法: upSalary setAllowance getInfo setTel属性: name age salary allowance telephone departmentDirector类
  • 54. 继承例代码class Employee { //Employee类定义 String name ; int age ; float salary ; Employee(String n,int a,float s){ //构造方法 name = n ; age = a ; salary = s; } void upSalary(float inc) { //方法-提薪 salary = salary + inc ; } String getInfo() { //方法-取信息 return "姓名:"+name+" 年龄:"+age+" 工资:"+salary ; } }
  • 55. 继承例代码/* Manager类定义,继承Employee类 */ class Manager extends Employee { float allowance ; //再定义一个属性(津贴) Manager(String n,int a,float s , float aa){ //构造方法 super(n,a,s) ; //调用父类构造方法(为3个属性赋值) allowance = aa ; //为allowance属性赋值 } void setAllowance(float aa) { //方法-定津贴数 allowance = aa ; } String getInfo() { //方法-取信息 return "管理者:" + super.getInfo() + " 津贴:" + allowance ; } }
  • 56. 继承例代码/* Director类定义,继承Manager 类 */ class Director extends Manager { String telephone ; //定义领导特有的属性(电话) String department ; //定义领导特有的属性(部门) Director(String n,int a,float s,float aa,String tel,String dep){ super(n,a,s,aa) ; //调用父类(Manager)构造方法 telephone = tel ; //为属性赋值 department = dep; //为属性赋值 } void setTel(String tel) { //方法-设置电话号码 telephone = tel ; } String getInfo() { //方法-取信息 return name + " 是 " + department + " 部门的领导" ; } }
  • 57. 继承例代码public class Test { public static void main(String[] args){ Employee emp = new Employee("赵兵",28,1500.0F); Manager mgr = new Manager("徐军",35,1800.0F, 500.0F); Director dir = new Director("黄帅",45,3000.0F,500.0F, "2430","计划司"); System.out.println(emp.getInfo()); System.out.println(mgr.getInfo()); System.out.println(dir.getInfo()); System.out.println("------------------------------------------------------");
  • 58. 继承例代码 emp.upSalary(200F); //给emp对象发消息:提薪(提emp工资) mgr.upSalary(200F); //给mgr对象发消息:提薪(提mgr工资) dir.upSalary(200F); //给dir对象发消息:提薪(提dir工资) mgr.setAllowance(666F); //给mgr对象定津贴,emp没有津贴 dir.setAllowance(888F); //给dir对象定津贴 dir.setTel("3333"); //给dir设置电话号码(只有dir有电话) System.out.println(emp.getInfo()); System.out.println(mgr.getInfo()); System.out.println(dir.getInfo()); System.out.print("领导的新津贴是:" + dir.allowance+" "); System.out.println("领导的新电话是:" + dir.telephone); } }4-10
  • 59. 继承例代码程序运行结果:
  • 60. 调用方法中的问题给对象发错误的消息: emp.setAllowance(666F); //mgr对象没有定津贴的方法 mgr.setTel("3333"); //mgr没有电话,也没有设置电话的方法 编译出错:
  • 61. 继承中的构造方法子类继承父类所有的属性和方法(非private),但不继承构造方法。子类应该定义自己的构造方法。 为了保证安全性,在子类的所有构造方法中,必须首先调用一个父类的构造方法, 子类可在自己的构造方法中使用super(…) 来调用父类带参数的构造方法。super(…)调用语句必须是子类构造方法中的第一个可执行语句。 若子类 的构造方法中没有super(…)语句,系统将隐含调用父类无参数构造方法。
  • 62. 子类调用父类的构造方法Manager类的构造方法例: class Manager extends Employee { float allowance ; //再定义一个属性(津贴) Manager(String n,int a,float s , float aa){ //构造方法 super(n,a,s) ; //首先调用父类构造方法 allowance = aa ; //为allowance属性赋值 } ... ... Director类的构造方法例: class Director extends Manager { ... ... Director(String n,int a,float s,float aa,String tel,String dep){ super(n,a,s,aa) ; //调用父类(Manager)构造方法 telephone = tel ; //为其他属性赋值 department = dep; //为其他属性赋值 } ... ...
  • 63. 子类隐含调用父类的构造方法Manager类的构造方法改编如下: class Manager extends Employee { float allowance ; Manager(String n,int a,float s , float aa){ name = n ; age = a ; salary = s; allowance = aa ; } Manager的构造方法 要隐含调用父类无参数 的构造方法: Employee() 但Employee类没有这个构造方法(见上例),所以编译出错。
  • 64. 子类隐含调用父类的构造方法如果Employee类定义两个构造方法 class Employee { String name ; int age ; float salary ; Employee() { } ; //无参数构造方法 Employee(String n,int a,float s){ // 3个参数的构造方法 name = n ; age = a ; salary = s; } ... 因为Employee类定义了一个无参数的构造函数,前页Manager类的构造方法出现的问题就解决了。
  • 65. 域的继承与扩充继承 扩充方法: upSalary getInfo属性: name age salaryEmployee类方法: upSalary setAllowance getInfo属性: name age salary allowanceManager类方法: upSalary setAllowance getInfo setTel属性: name age salary allowance telephone departmentDirector类
  • 66. 方法的继承、扩充与覆盖继承 扩充 覆盖方法: upSalary getInfo属性: name age salaryEmployee类方法: upSalary setAllowance getInfo属性: name age salary allowanceManager类方法: upSalary setAllowance getInfo setTel属性: name age salary allowance telephone departmentDirector类
  • 67. 方法覆盖在面向对象的程序设计中,子类可以把从父类那里继承来的某个方法改写,形成同父类方法同名、解决的问题也相似、但具体实现和功能却不尽一致的新方法。 定义与父类完全相同的方法,实现对父类方法的覆盖: 完全相同的方法名 完全相同的参数列表 完全相同类型的返回值 注意:在满足上述三个条件同时,还必须保证:访问权限不能缩小,抛出的例外要相同 三个条件有一个不满足,就不是方法的覆盖,而是子类自己定义与父类无关的方法,父类的方法未被覆盖,因而仍然存在。 调用父类被覆盖的方法— super.方法名
  • 68. 方法覆盖虽然方法名相同,但对象不同,调用的是不同的方法: public class Test { public static void main(String[] args){ Employee emp = new Employee("赵兵",28,1500.0F); Manager mgr = new Manager("徐军",35,1800.0F, 500.0F); Director dir = new Director("黄帅",45,3000.0F,500.0F, "2430","计划司"); System.out.println( emp.getInfo() ); System.out.println( mgr.getInfo() ); System.out.println( dir.getInfo() ); } }
  • 69. 系统类中方法覆盖例系统类 java.lang.Object 方法:public boolean equals(Object obj) Object类所定义的equals方法的功能是:比较两个引用型变量,当两个引用型变量相等时(即两个引用型变量指向同一个对象时)为真。 用户定义的Myclass类-继承Object类 class Myclass { //继承了Object类的equals方法 String s ; Myclass(String ps) { s = ps ;} } 系统类String-继承Object类 覆盖了Object类的equals方法 String类重新定义的equals方法的功能是:比较两个对象,当两个对象的类型和内容相同时为真。
  • 70. 系统类中方法覆盖例class Myclass { String s ; Myclass(String ps) { s = ps; } } public class Test { public static void main(String[] args) { Myclass mc1 = new Myclass("wang"); Myclass mc2 = new Myclass("wang"); if (mc1.equals(mc2)) //比较mc1和mc2两个引用型变量 System.out.println("mc1等于mc2") ; else System.out.println("mc1不等于mc2") ;
  • 71. 系统类中方法覆盖例 String s1 = new String("wang") ; String s2 = new String("wang") ; if (s1.equals(s2)) //比较s1和s2两个引用型变量指向的对象 System.out.println("s1等于s2") ; else System.out.println("s1不等于s2") ; } } 4-11
  • 72. 覆盖:子类对父类方法的覆盖(不同类的同名方法) 重载:一个类中,同名方法(参数不同) class Employee { void upSalary(float inc) { salary = salary + inc ; } void upSalary() { salary = salary * 1.1F ; } ... } class Manager extends Employee { void upSalary() { salary = salary * 1.2F ; } ... } 方法覆盖与方法重载重载覆盖
  • 73. 类定义中属性及方法功能属性-对象的数据描述 描述每个类对象的状态; 同一个类的不同对象主要是用属性值来区分; 方法-对象的功能体现 围绕对象的属性进行各种操作; 与其他类或对象进行数据交换; 类定义中对象的应用 在类定义中,属性也可以是另一个类的对象; 在方法定义中,方法的参数可以是一个对象类型;
  • 74. 类定义例(对象作为方法的参数)class Department { int m_dno ; //部门号 String m_dname ; //部门名称 int m_num ; //部门员工数 Director m_dir ; //部门主管 Department(int dno,String dname,int num, Director d){ m_dno= dno ; m_dname=dname; m_num=num; m_dir=d; } int getDno() { return m_dno ; }
  • 75. 类定义例(对象作为方法的参数) boolean setDno( int newDno) { if( newDno>0) { m_dno=newDno ; return true; } else return false; } boolean isSameDept( Department d) { //比较两个对象 if (m_dno == d.getDno() ) return true ; else return false ; } } //class Department
  • 76. 类定义例(对象作为方法的参数)public class Test { public static void main(String[] args){ Director dir1=new Director("黄帅",45,3000.0F,500.0F, "2430","计划司"); Director dir2=new Director("郑众",58,4000.0F,500.0F, "3395","装备司"); Department dept1=new Department(1,"计划司",20,dir1); Department dept2=new Department(2,"装备司",30,dir2); System.out.println(dept1.getDno()) ; System.out.println(dept1.isSameDept(dept2)); } }4-12
  • 77. 对象引用的多态性一个对象只有一种形式(构造函数确定的) 一个对象引用是多态的,因为它可以指向不同形式的对象。 Employee e1 = new Employee(…) ; Employee e2 = new Manager(…) ; //将管理员看作是职工 Employee e3 = new Director(…) ; //将领导看作是职工 但下面的语句是错误的: Director d1 = new Employee(…) ; //错:职工不是领导 Director d2 = new Manager(…) ; //错:管理员不是领导 Director d3 = new Director(…) ;
  • 78. 多态应用-方法放的通配参数public class Test { public static void main(String[] args){ Employee emp1 = new Employee("赵兵",28,1500.0F); Manager mgr1 = new Manager("徐军",35,1800.0F, 500.0F); Director dir1 = new Director("黄帅",45,3000.0F,500.0F, "2430","计划司"); CalTax ct1 = new CalTax() ; ct1.findTax(emp1); ct1.findTax(mgr1); ct1.findTax(dir1); } } class CalTax { void findTax(Employee e){ System.out.println(e.salary*0.1F) ; } }利用参数的多态性,该方法可接收通配对象。4-13
  • 79. 多态应用-数组的通配同质收集(collection)-一个数组可存储几种类型的对象 public class Test { public static void main(String[] args){ Employee[] staff = new Employee[3] ; staff[0] = new Employee("赵兵",28,1500.0F); staff[1] = new Manager("徐军",35,1800.0F, 500.0F); staff[2] = new Director("黄帅",45,3000.0F,500.0F, "2430","计划司"); for (int i=0;i
  • 80. 多态应用-数组的通配4-13的执行结果: 子类继承父类的属性和方法,或子类覆盖父类的方法,其属性名和方法名是一样的。但程序运行时,实际对象决定访问的属性和调用的方法。 任何对父类合法的操作,对子类也是合法的。
  • 81. 多态中的问题如果4-13的main()方法中加入下述测试程序段: staff[1].allowance = 600 ; staff[1].setAllowance(600); 编译出错。编译时系统从语法上看(而不是考虑实际的对象,对象是在运行时才确定),employee类没有allowance属性,也没有setAllowance(float)方法。
  • 82. 实例类型的判断与转换问题代码改写如下: if( staff[1] instanceof Manager) { // ① Manager m1 = (Manager)staff[1] ; // ② m1.setAllowance(600.0F) ; // ③ } 代码说明: ① instanceof运算符:判断一个实际的对象是否为某个类的实例; ② 如果是,可通过类型转换将对象转换为实际的类型; ③ 转换为Manager类型的对象后,Manager类的属性和方法就可以使用了。 4-15