目录
主要内容
- Object类
- 继承情况下构造方法的调用过程
- super关键字
- ==和equals方法
- 组合
- 引入和使用多态
- 多态之向上转型
- 多态之向下转型
学习目标
节数 | 知识点 | 要求 |
第一节(继承) | Object类 | 理解 |
成员变量的隐藏 | 了解 | |
继承情况下构造方法的调用过程 | 掌握 | |
第二节(继承) | super关键字 | 理解 |
==和equals方法 | 掌握 | |
组合 | 了解 | |
第三节(多态) | 引入和使用多态 | 理解 |
多态之向上转型 | 掌握 | |
第四节(多态) | 多态之向下转型 | 掌握 |
简单工厂模式-返回值是父类类型 | 理解 |
第一节 继承
1.1. Object类
Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
面试题目:请你写出Object类的6个方法
方法摘要 | |
| |
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
|
以上方法是Object类的所有方法吗?不是 是Object类所有的public方法。 除此之外可能还有private、protected、默认的方法
方法摘要 | |
|
|
|
|
扩展:native关键字
- 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
- 在定义一个native方法时,并不提供实现体,因为其实现体是由非Java语言在外面实现的。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
- JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。
1.2. 成员变量的隐藏
如果父类和子类中有同名的成员变量,不存在变量的重写,分别占有自己的空间。子类的成员变量优先,称为成员变量的隐藏。
【示例1】成员变量的隐藏
public class Animal { String color="Animal的color"; int age; public String getColor(){ return color; } } public class Dog extends Animal { String color ="Dog的color"; String nickName; public String getColor(){ return color; } public String getSuperColor(){ return super.getColor(); } public void show(){ String color = "方法的color"; System.out.println(color); System.out.println(this.color); System.out.println(super.color); } public static void main(String[] args) { Dog dog = new Dog(); dog.show(); System.out.println(dog.getColor()); System.out.println(dog.getSuperColor()); } }
1.3. 继承情况下构造方法的调用过程
继承条件下构造方法的执行顺序
- 构造方法的第一条语句默认是super(),含义是调用父类无参数的构造方法
- 构造方法的第一条语句可以显式的指定为父类的有参数构造方法:super(.....)
- 构造方法的第一条语句可以显式的指定为当前类的构造方法:this(.....)
注意事项
- 每个类最好要提供无参数的构造方法
- 构造方法的第一条语句可以是通过super或者this调用构造方法,须是第一条语句
- 构造方法中不能同时使用super和this调用构造方法,并不是说不能同时出现this和super
【示例2】继承情况下构造方法的调用过程
public class Animal { String color; private int age; public Animal(){ super(); } public Animal(String color,int age){ this.color = color; this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public class Dog extends Animal { private String nickName; private String type; public Dog(){ super(); } public Dog(String color,int age,String nickName){ //super(); super(color,age); //this(color,age,nickName,"aaa"); this.nickName = nickName; } public Dog(String color,int age,String nickName,String type){ //super(); //super(color,age); this(color,age,nickName); //this.nickName = nickName; this.type = type; } public String toString() { return this.color+" "+ this.getAge()+" "+this.nickName+" "+this.type; } public static void main(String[] args) { Dog dog = new Dog("黑色",3,"旺财","泰迪"); System.out.println(dog.toString()); } }
本节作业
- Object类的常用方法及其作用
- 说明继承条件下构造方法的执行过程
第二节 继承
2.1. super关键字
前面的示例中已经多次使用到了super关键字。
super“可以看做”是直接父类对象的引用。每一个子类对象都会有一个super引用指向其直接父类对象。
使用super可以
- 调用成员变量 super.color
- 调用成员方法 super.introduce();
- 调用构造方法 super(color,age);
注意
- 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
- 在一个类中,若是构造方法的第一行代码没有显式的调用super(...)或者this(...);那么Java默认都会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。
2.2. ==和equals方法
“==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。
Object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。比如,判断两个Dog是否是一个Dog,要求color、age、nickName等所有属性都相同。
Object 的 equals 方法默认就是比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。显然,这无法满足子类的要求,可根据要求重写equals方法。
【示例3】重写equals方法
public class Animal { String color; private int age; public boolean equals(Object obj) { Animal other = (Animal) obj; //如果参数是null、,直接返回false if(obj == null){ return false; } //如果两个变量指向同一个空间,直接返回true if(this == obj ){ return true; } if(this.color.equals(other.color) && this.age == other.age){ return true; }else{ return false; } } } public class Dog extends Animal { private String nickName; private String type; public boolean equals(Object obj) { Dog other = (Dog) obj; boolean flag = super.equals(obj); if (!flag) { return false; } else { //如果Animal的equals返回true,需要再比较nickName、type if (this.nickName.equals(other.nickName) && this.type.equals(other.type)) { return true; } else { return false; } } } }
2.3. 组合
继承和组合是复用代码的两种方式;
继承 is-a Dog is-a Animal Cat is-a Animal
组合 has-a Computer has-a cpu memery mainBoard。
扩展: 面向对象的设计原则之一:组合聚合复用原则(优先使用组合,而不是继承)
- 除非两个类之间是“is-a”的关系,否则不要轻易的使用继承,不要单纯的为了实现代码的重用而使用继承,因为过多的使用继承会破坏代码的可维护性,当父类被修改时,会影响到所有继承自它的子类,从而增加程序的维护难度和成本。
- 不要仅仅为了实现多态而使用继承,如果类之间没有“is-a”的关系,可以通过实现接口与组合的方式来达到相同的目的。设计模式中的策略模式可以很好的说明这一点,采用接口与组合的方式比采用继承的方式具有更好的可扩展性
【示例4】定义CPU类及其子类
public class Cpu { private String name;//intel amd private double rate;//速度 频率 public void calc(){ System.out.println("cpu calc......"); } public void ctrl(){//control System.out.println("cpu control..."); } } public class AMDCpu extends Cpu { public void calc() { System.out.println("AMD calc"); } } public class IntelCpu extends Cpu { public void calc() { System.out.println("IntelCpu calc"); } }
【示例5】定义MailBoard 类和Memory 类
public class MailBoard { public void connect(){ System.out.println(" mainBoard connecting ....."); } } public class Memory { public void process(){ System.out.println("memory process....."); } }
【示例6】使用组合定义Computer类
public class Computer{ private Cpu cpu = new Cpu(); private Memory memory = new Memory(); private MailBoard mailBoard = new MailBoard(); private void setCpu(Cpu cpu){ this.cpu = cpu; } public void computer(){ cpu.calc(); cpu.ctrl(); memory.process(); mailBoard.connect(); } public static void main(String[] args) { Computer computer = new Computer(); //computer.setCpu(new IntelCpu()); Cpu cpu = new AMDCpu(); Cpu cpu2 = new IntelCpu(); Cpu cpu3 = new Cpu(); computer.setCpu(cpu); computer.computer(); } }
本节作业
- super关键字的作用和使用
- ==和equals()的区别和联系
- 重写Animal和Dog类的equals()方法并进行判断
- 组合和继承的联系和区别
第三节 多态
多态(polymorphism)是面向对象三大特征之一。同一行为,通过不同的子类,可以体现出来的不同的形态。
3.1. 引入和使用多态
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游,王五是听音乐; 同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。
编译器类型指的是‘=’左边的类型,运行期类型指的是‘=’右边的类型。当有继承关系时,可能发生编译期类型和运行期类型不同的情况,即编译期类型是父类类型,运行期类型是子类类型。即:父类引用指向子类对象
多态的要点:
- 多态是方法的多态,不是属性的多态(多态与属性无关)。
- 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
【示例7】为多态做准备:定义子类是重写方法
public class Programmer { public String name ="proName";//姓名 //1.子类继承的 public void writeCode(){ System.out.println("writing code............"); } //2.子类重写的 public void eat(){ System.out.println("eating with mouse............."); } } public class Chinese extends Programmer{ public String name = "ChinName"; //1.从父类继承一个方法 writeCode() //2.重写父类的一个方法 eat public void eat() { System.out.println(" Chinese eat rice with chopsticks....."); } //3.子类特有的方法 public void playShadowBoxing(){ System.out.println("Chinese play showdowBoxing every day......"); } } public class English extends Programmer { //1.从父类继承一个方法 writeCode() //2.重写父类的一个方法 eat public void eat() { System.out.println("English eating meat with knife....."); } //3.子类特有的方法 public void raceHorse(){ System.out.println("English like racing horse......"); } } public class Italian extends Programmer { //1.从父类继承一个方法 writeCode() //2.重写父类的一个方法 eat public void eat() { System.out.println("Italian eating pizza with hand......"); } //3.子类特有的方法 public void playFootBall(){ System.out.println("Italian like play football....."); } }
【示例8】实现eat()多态
public class Test { // public static void showEat(Chinese ch){ // ch.eat(); // } // public static void showEat(Italian it){ // it.eat(); // } // public static void showEat(English en){ // en.eat(); // } public static void showEat(Programmer pro){ pro.eat(); } public static void main(String[] args) { //Chinese ch = new Chinese(); Programmer ch = new Chinese(); showEat(ch); English en = new English(); showEat(en); Italian it = new Italian(); showEat(it); Programmer pro = new Programmer(); showEat(pro); } } public class Test2 { public static void main(String[] args) { Chinese ch = new Chinese(); ch.writeCode();//从父类继承的 ch.eat();//重写父类的 ch.playShadowBoxing();//子类特有的方法 Programmer ch2 = new Chinese(); ch2.writeCode(); //非多态 ch2.eat(); // 多态 调用的是子类重写的方法 //ch2.playShadowBoxing(); // 无法调用子类特有的方法 } }
使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,符合开闭原则。
父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。另外即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。
扩展: 面向对象的设计原则之一:开闭原则OCP
- 对扩展开放,对修改关闭
- 更通俗翻译:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能
- 面向对象设计需要遵守的最高原则,也是最终目标
3.2. 多态之向上转型
将子类对象赋给父类引用,称为向上转型(upcasting),自动进行类型转换。向上转型可以调用的子类继承的方法,但不能调用子类特有的方法。需要特别理解的是如果子类重写了父类的方法,向上转型后通过父类引用调用的却是真实子类重写的方法,
【示例7】 向上转型
public class TestPoly { public static void main(String[] args) { //基本数据类型的自动转换 int n = 10; System.out.println( n ); double d = n; // 左>右 自动转换 System.out.println(d); //引用数据类型的自动转换 Programmer programmer = new Chinese(); // 自动转换 向上转型 左>右 programmer.writeCode(); programmer.eat(); //programmer.playShadowBoxing(); } }
理解向上转型
- 招聘程序员,来个英国籍程序员,满足要求,不需要特别声明
- 不管是哪个国籍的,写到代码都是Java代码
- 中午休息了,大家都去食堂开始吃饭,原形毕露
- 老板随便找一个程序员,说一起赛马吧,不可以;因为对方可能是中国或意大利程序员
本节作业
- 多态的含义及其前提条件
- 使用多态实现不同国籍的程序员吃饭。
第四节 多态
4.1. 多态之向下转型
将父类的引用变量转换为子类类型,称为向下转型(downcasting)。向下转型后就可以调用子类特有的方法了。
- 需要进行强制转换Chinese ch = (Chinese)pro;
- 强制转换不是做手术,必须转换成真实子类型,否则ClassCastException;
- 向下转型之前肯定发生了向上转型
- 为了避免ClassCastException,向下转型之前使用instanceof先判断一下
pro instanceof Italian
对象 instanceof 类或者接口
使用instancof的前提:左边的对象和右边的类型在继承树上有上下级关系
【示例8】向下转型
public class TestPoly2 { public static void main(String[] args) { //基本数据类型的强制转换 double d = 3.14; System.out.println(d); int n = (int)d; // 左 < 右 做手术 System.out.println(n); //引用数据类型的强制转换 Programmer programmer = new Chinese(); programmer.eat();//多态 //programmer.playShadowBoxing(); // Chinese ch = (Chinese) programmer; // 左<右 不做手术,必须转换成原来//的真实子类型 // ch.playShadowBoxing();// // English en = (English)programmer; // en.raceHorse(); if(programmer instanceof Chinese){ Chinese ch = (Chinese) programmer; ch.playShadowBoxing(); }else if(programmer instanceof English){ English en = (English) programmer; en.raceHorse(); }else{ Italian it = (Italian) programmer; it.playFootBall(); } //java.lang.ClassCastException: // com.bjsxt.poly0.Chinese cannot be cast to com.bjsxt.poly0.English System.out.println(programmer instanceof Chinese); //false System.out.println(programmer instanceof English); //true System.out.println(programmer instanceof Programmer);//true System.out.println(programmer instanceof Object);//true //System.out.println(programmer instanceof String); } }
注意:多态之和方法有关,和属性无关。入下面示例所示。
【示例9】多态和属性无关
public class TestPoly3 { public static void main(String[] args) { Chinese chinese = new Chinese(); System.out.println(chinese.name); Programmer programmer = new Programmer(); System.out.println(programmer.name); Programmer programmer2 = new Chinese(); System.out.println(programmer2.name); //ChinName proName programmer2.eat(); } }
4.2. 简单工厂模式-返回值是父类类型
不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
【示例10】使用父类作为返回值
public class TestPoly4 { public static void main(String[] args) { //自己培养了一个程序员 //Programmer pro = new Chinese(); Programmer pro = SxtSchool.getProgrammer("en"); //让程序员干活 pro.writeCode(); } } class SxtSchool{ public static Programmer getProgrammer(String type){ Programmer pro = null; if("ch".equals(type)){ pro = new Chinese(); }else if("en".equals(type)){ pro = new English(); }else{ pro = new Italian(); } return pro; } }
以上代码其实是简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是
- 定义一个static方法,通过类名直接调用
- 返回值类型是父类类型,返回的可以是其任意子类类型
- 传入一个字符串类型的参数,工厂根据参数创建对应的子类产品
扩展: 设计模式
- 有23中经典的设计模式,是一套被多数人知晓、经过分类编目的、反复使用的优秀代码设计经验的总结。每个设计模式均是特定环境下特定问题的处理方法。
- 面向对象设计原则是面向对象设计的基石,面向对象设计质量的依据和保障,设计模式是面向对象设计原则的经典应用。
- 简单工厂模式并不是23中经典模式的一种,是其中工厂方法模式的简化版
本节作业
- 向下转型的相关注意事项
- 面向对象设计原则有哪些
- 23种设计模式分为哪3大类,分别是哪些
- 使用简单工厂模式模拟sxt培训不同专业的程序员