一、多态
1.编写程序来学习什么是多态
1)先编写一个女孩和猫玩的程序
创建Cat类,编写猫的方法
public class Cat {
public void shout(){
System.out.println("我是小猫,我会喵喵叫");
}
public void scratch(){
System.out.println("我是小猫,我会挠人");
}
}
创建Girl类,调用Cat的方法
public class Girl {
//写个女孩和猫玩的方法
public void play(Cat cat){
cat.shout();
}
}
创建对象,实现女孩和猫玩
public class Test {
public static void main(String[] args) {
//让女孩和小猫玩
//1.创建小猫对象
Cat c = new Cat();
//2.创建女孩对象
Girl g = new Girl();
//2.让女孩和小猫玩,把c这个实例作为实参传给g的play方法
g.play(c);
}
}
运行后可以实现
2)再编写一个女孩和狗玩的程序
创建Dog类
public class Dog {
public void shout(){
System.out.println("我是小狗,我会汪汪叫");
}
public void guard(){
System.out.println("我是小狗,我会看家");
}
}
在Girl类中加一条实现调用Dog类的方法
public class Girl {
//写个女孩和猫玩的方法
public void play(Cat cat){
cat.shout();
}
//女孩和狗玩,方法的重写,方法名相同,参数不同
public void play(Dog dog){
dog.shout();
}
}
创建对象,实现女孩和狗玩
public class Test {
public static void main(String[] args) {
//实现女孩和猫玩
//1.创建猫对象
Cat c = new Cat();
//2.创建女孩对象
Girl g = new Girl();
//2.让女孩和猫玩,把c这个实例作为实参传给g的play方法
g.play(c);
//实现女孩和狗玩
Dog d = new Dog();
g.play(d);
}
}
3)由此可见,如果有一百个动物,女孩和一百个动物玩,需要在Girl类中添加一百个分别和这些动物玩的代码,而这些代码中调用的都是shout方法,也就是说这些类都有一个共同的shout方法,所以可以运用之前继承的概念,提取这些类中共同的方法,编写一个父类,动物类Animal
public class Animal {
public void shout(){
System.out.println("我是动物,我会叫");
}
}
让Cat、Dog类都继承Animal类,并且Cat、Dog类中的shout方法就是Animal类中的方法的重写
public class Cat extends Animal{
public void shout(){
//对Animal类shout方法的重写
System.out.println("我是小猫,我会喵喵叫");
}
public void scratch(){
System.out.println("我是小猫,我会挠人");
}
}
public class Dog extends Animal{
public void shout(){
//对Animal类shout方法的重写
System.out.println("我是小狗,我会汪汪叫");
}
public void guard(){
System.out.println("我是小狗,我会看家");
}
}
4)那么在Girl方法中再调用shout方法就可以直接调用Animal的shout方法
public class Girl {
// //写个女孩和猫玩的方法
// public void play(Cat cat){
// cat.shout();
// }
// //女孩和狗玩,方法的重写,方法名相同,参数不同
// public void play(Dog dog){
// dog.shout();
// }
//和动物玩
public void play(Animal animal){
animal.shout();
}
}
5)但是现在我们要实现的是女孩和具体的动玩,比如猫,那么实现程序如下
public class Test {
public static void main(String[] args) {
// //2.创建女孩对象
Girl g = new Girl();
//但是现在我们要实现的是让女孩和具体的动物玩,例如和猫玩
//于是先创建一个猫的实例
Cat c = new Cat();
//因为在Girl类里面没有和猫玩的方法,只有和动物玩的方法,于是我们可以让这个动物等于猫
Animal an = c;
//再运行程序可以实现和猫玩
g.play(an);
}
}
这样就可以实现女孩和猫玩,那么如果我想实现女孩和狗玩,我只需要创建一个Dog类的对象,让an等于这个对象,就不用修改Girl类中的代码了
public class Test {
public static void main(String[] args) {
Girl g = new Girl();
//但是现在我们要实现的是让女孩和具体的动物玩,例如和猫玩
//于是先创建一个猫的实例
//Cat c = new Cat();
Dog d = new Dog();
//因为在Girl类里面没有和猫玩的方法,只有和动物玩的方法,于是我们可以让这个动物等于猫
Animal an = d;
//再运行程序可以实现和猫玩
g.play(an);
}
}
像以上这种先有子类,再抽取父类的方式,叫做泛化,而先有父类,再有子类叫做继承
二、多态的概念
1.什么是多态
多态就是指多种状态,同一个行为(方法),不同的的子类表现出不同的形态(动物中的猫喵喵叫,狗汪汪叫)。在Java中就是同一个方法调用,由于对象的不同会产生不同的结果。
2.多态是方法的多态,与属性无关,重写也是
3.多态的优势
提高代码的扩展性(添加一种状态只需要添加相应的类和创建相应的对象),符合开闭原则。想要扩展性更好的话需要使用反射。
4.多态的要素
1)继承:Cat extends Animal、Dog extends Animal···
2)重写:Cat、Dog类对Animal类shout方法的重写(以下几种不能被重写:①static修饰的方法,它属于类,不属于实例②final修饰的方法:常量③3.private方法:私有的)
3)父类引用指向子类对象
Cat c = new Cat(); Animal an = c;
上面两句代码可以合为一句
Animal an = new Cat();
=左侧:编译期类型
=右侧:运行期类型
代码运行时,shout方法的参数被进行了两次赋值Animal animal = an = new Cat()
public void play(Animal animal){//这里接受到的实参是an,也就是Animal animal = an = new Cat()
animal.shout();
}
也可以直接用父类的对象来调用shout方法, 其实调用的就是子类的shout方法
Cat c = new Cat();
Animal an= c; //这里an和c指向的是同一地址,那么an调shout方法也就是c调用shout方法,所以呈现的结果是cat的结果
an.shout();
4)经典使用场景:
父类作为方法的形参,调用方法时将子类的对象作为实参传入,这样在调用方法时,根据传入的子类不同,实现的效果也不同,这就是多态。
父类作为方法的返回值,简单工厂设计模式。
三、引用类型的类型转换
1.类型转换实例
1)在上面的代码中,可以使用an直接调用子类的方法shout,但是却不可以调用Animal类中没有而子类中有的方法和属性,因为=左边是编译期的类型,在编译时,编译期认为an是Animal类型,而Animal类中只有shout方法而没有scratch方法,所以会报错。并且在内存中Animal也只能访问到Animal中的属性和方法、还有子类中重写的方法。
Cat c = new Cat();
Animal an= c; //这里an和c指向的是同一地址,那么an调shout方法也就是c调用shout方法,所以呈现的结果是cat的结果
an.shout();
//an.scratch();//报错
/*这里=左边的an是编译期的类型,在编译期,编译器认为an是Animal类型的,而在Animal类型中没有scratch()方法
虽然an和p指向同一地址,但是这是运行后Animal an= c后才会指向同一地址,编译期还没有这一步骤,所以报错
*/
an.age=10;
//an.weight=10.5 //属性赋值也报错,原因同上
2)可以使用向下转型使an可以访问到子类的属性和方法
//将Animal类型的an转换为Cat类型,并且使用一个Cat类型的对象来接收
Cat cat=(Cat)an; //父类转为子类,向下转型
cat.scratch(); //转型后使用cat对象来调用Cat类的方法,则可以调用
cat.weight=10.5;
在内存中的分析为:
2.在多态中,父类要想使用子类的方法,要进行类型转换
Java中的类型有基本数据类型和引用数据类型
基本数据类型转换:64位>32>16>8 高-->低:强制转换, 低--->高:直接转换
引用数据类型转换:父--->子 强制转换, 子--->父,直接转换
类型转换的作用:方便方法的调用,减少重复的代码
1)先写三个空的Person、Student、Teacher方法
在main方法中输入
import com.rzd.oop.demo11.Person;
import com.rzd.oop.demo11.Student;
import com.rzd.oop.demo11.Teacher;
public class Application {
public static void main(String[] args) {
//先写个多态
Object object = new Student(); //Object是Student的父类,可以写多态
//Object>Person>Student
//Object>Person>Teacher
//Object>String
//由于object是Student类型的,那么object是Student及其父类的类型
System.out.println(object instanceof Object); //true
System.out.println(object instanceof Student); //true
System.out.println(object instanceof Person); //true
System.out.println(object instanceof Teacher); //false Student和Teacher是平行的
System.out.println(object instanceof String); //false Student和String无关
System.out.println("==================================");
Person person = new Student(); //这里new出来的person到底是什么类型?
//person是Person类型的
System.out.println(person instanceof Object); //true
System.out.println(person instanceof Student); //true
System.out.println(person instanceof Person); //true
System.out.println(person instanceof Teacher); //false Person是Teacher的父
//System.out.println(person instanceof String); //false instanceof两边的类型没有继承关系就会编译报错
Student student = new Student();
System.out.println(student instanceof Object); //true
System.out.println(student instanceof Student); //true
System.out.println(student instanceof Person); //true
//System.out.println(student instanceof Teacher); //false instanceof两边的类型没有继承关系就会编译报错
//System.out.println(student instanceof String); //false instanceof两边的类型没有继承关系就会编译报错
}
}
先在子类中写一个方法study()
public class Student extends Person{
public void study(){
System.out.println("Student在学习");
}
}
在main方法中创建一个父类的obj对象,调用子类的方法
//类型转换
Person obj = new Student(); //将student类的oj转换为Person类,子--->父,低--->高,是直接转换
obj.study();//这行无法运行,报错。
// 这里表示,obj本来是Student,是子类,被转换成了父类之后,不能调用本身的study方法了,说明子类转换为父类会丢失自己本来的方法
当子类转换成父类时,会丢失方法。
所以父类使用子类本身的方法, 需要强制转换,将父类转换为子类
public class Application {
public static void main(String[] args) {
//类型转换
Person obj = new Student(); //将student类的oj转换为Person类,子--->父,低--->高,是直接转换
obj.study();//这行无法运行,报错。
// 这里表示,obj本来是Student,是子类,被转换成了父类之后,不能调用本身的study方法了,说明子类转换为父类会丢失自己本来的方法
//父类要想使用子类的方法,还需要类型转换.obj现在是是Person类型,要转换为Student类型,高转低,要强制转换
//先输入(Student)obj;,Alt+回车,选择第二个。
//这里就是用一个Student类型的对象student1来接收被强制转换为Student类型的对象obj
Student student1 = (Student) obj;
//然后student1就可以使用Student的方法
student1.study();
//上面两行可以简化成
((Student) obj).study();
}
}
四、简单工厂设计模式
不仅可以用父类做方法的形参,还可以用父类做方法的返回值,真实返回的对象可以是父类的任意一个子类对象。
简单工厂设计模式中工厂(petStore类)只负责创建对象,将创建和使用分开,解决了在代码中大量创建对象的问题。
1.简单工厂设计模式的要素
1)工厂类中定义static方法,通过类名直接调用
2)返回值类型定义为父类类型,返回值可以是任意子类类型
3)调用方法传入参数,工厂根据参数创建对应子类对象
2.上面的动物类,可以提取出他们都由宠物店提供,那么可以创建一个宠物店类,来提供动物。
package com.rzd.no02oop.demo10;
public class Petstore {
//提供动物,,这里定义为静态方法,可以再Test1中直接雷鸣.方法名调用
public static Animal getAnimal(String petName){
//这里需要返回的是动物,所以返回值定义为Animal类型
//定义一个动物,局部变量要赋初始值
Animal an = null;
if ("猫".equals(petName)){
an=new Cat();
}else if ("狗".equals(petName)){
an=new Dog();
}
return an;
}
}
这里如果传入的参数是猫,那么就是an=new Cat()。
package com.rzd.no02oop.demo10;
public class Test1 {
public static void main(String[] args) {
Girl g = new Girl();
Animal an=Petstore.getAnimal("猫");
g.play(an);
}
}