------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
3.7 多态
3.7.1、理解多态
多态可以理解为事物存在的多种体现形态。例如下面的代码:
Cat c = new Cat(); Animal a = new Cat();
建立一个猫的对象,可以用猫这个类引用,也可以用动物这个类引用。
3.7.2、多态的前提
1、类与类之间必须有关系,要么继承,要么实现。
2、存在覆盖。父类中有方法被子类重写(其实没有也是可以的,但是如果没有这个就没有意义)。
3.7.3、多态中成员的特点
1、多态中非静态成员变量的特点
在编译时,参阅引用型变量所属的类中是否有调用的成员变量。如果有,编译通过,如果没有,编译失败。在运行时,调用的成员变量也是引用型变量所属的类中的成员变量。
简单来说:编译看左边,运行看左边。
class Fu{ public int num = 100; } class Zi extends Fu{ public int num = 200; } class DuoTaiDemo{ public static void main(String[] args){ Fu f = new Zi(); System.out.println(f.num); } }
运行结果:
2、多态中非静态成员方法的特点
在编译时,参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有,编译失败。在运行时,调用的方法是对象所属类中的该方法。
简单来说:编译看左边,运行看右边。
class Fu{ public int num = 100; public void show_1(){ System.out.println("show Fu_1"); } public void show_2(){ System.out.println("show Fu_2"); } } class Zi extends Fu{ public int num = 200; public void show_1(){ System.out.println("show Zi_1"); } } class DuoTaiDemo{ public static void main(String[] args){ Fu f = new Zi(); //System.out.println(f.num); f.show_1(); f.show_2(); } }
运行结果:
在调用show_1()方法时,调用的是子类中的该方法,在调用show_2()方法时,子类中没有,所以调用的是父类中的该方法。
3、多态中静态成员方法的特点
在编译时,参阅引用型变量所属的类中是否有调用的成员方法。如果有,编译通过,如果没有,编译失败。在运行时,调用的成员方法也是引用型变量所属的类中的成员方法。
简单来说:编译看左边,运行看左边。
class Fu{ public int num = 100; public void show_1(){ System.out.println("show Fu_1"); } public void show_2(){ System.out.println("show Fu_2"); } public static void function(){ System.out.println("function Fu"); } } class Zi extends Fu{ public int num = 200; public void show_1(){ System.out.println("show Zi_1"); } public static void function(){ System.out.println("function Zi"); } } class DuoTaiDemo{ public static void main(String[] args){ Fu f = new Zi(); //System.out.println(f.num); //f.show_1(); //f.show_2(); f.function(); } }
运行结果:
3.7.4、多态的利弊
好处:提高了代码的扩展性
坏处:只能使用父类的引用访问父类中的成员。想要调用子类中特有的方法时,需要将父类的引用经过向下转型为子类成员。
class Animal{ public void eat(){ System.out.println("吃东西"); } } class Cat extends Animal{ public void eat(){ System.out.println("吃鱼"); } public void catchMouse(){ System.out.println("捉老鼠"); } } class Dog extends Animal{ public void eat(){ System.out.println("吃骨头"); } public void lookDoor(){ System.out.println("看门"); } } class DuoTaiDemo2{ public static void main(String[] args){ //自动类型提升,猫对象提升到了动物类型。但是特有功能无法访问,作用就是限制对特有功能的访问。 //专业讲:向上转型,将子类型隐藏。就不能使用子类的特有方法了。 Animal a = new Cat(); a.eat(); //a.catchMouse();//报错 //如果还想用具体动物猫的特有功能。 //你可以将该对象进行向下转型。 Cat c = (Cat)a; //向下转型的目的是为了能够使用子类中的特有方法。 c.eat(); c.catchMouse(); //注意:对于转型,自始至终都是子类对象在做类型的变化。 //Animal a = new Dog(); //Cat c = (Cat)a;//但是类型不能随意转换,否则可能会报出ClassCastException的异常 } public static void method(Animal a){//接收时用Animal类的对象接收 a.eat(); } }
运行结果:
多态练习:
1、猫狗案例多态版
/* 多态练习:猫狗案例 */ class Animal{ public void eat(){ System.out.println("吃饭"); } } class Dog extends Animal{ public void eat(){ System.out.println("狗吃肉"); } public void lookDoor(){ System.out.println("狗看门"); } } class Cat extends Animal{ public void eat(){ System.out.println("猫吃鱼"); } public void playGame(){ System.out.println("猫玩游戏"); } } class DuoTaiTest{ public static void main(String[] args){ //定义为狗 Animal a = new Dog(); a.eat(); System.out.println("------------"); //还原成狗 Dog d = (Dog)a; d.eat(); d.lookDoor(); System.out.println("------------"); //变成猫 a = new Cat(); a.eat(); System.out.println("------------"); //还原成猫 Cat c = (Cat)a; c.eat(); c.playGame(); //Dog dd = (Dog)a; 运行时出错,ClassCastException //Dog dd = new Animal(); 编译时出错,不兼容类型 //Dog dd = new Cat(); 编译时出错,不兼容类型 } }
运行结果:
3.8 抽象类
3.8.1 抽象类的概述
Java中定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象方法是怎么来的呢?
多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
3.8.2 抽象类的特点
1、抽象类和抽象方法必须用abstract关键字修饰。
2、抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类。
3、抽象类不可以用new建立对象,但是有构造方法, 构造方法的作用是用于子类访问父类数据的初始化。
4、抽象类中的抽象方法要想被使用,必须由子类复写所有抽象方法后,然后建立子类对象调用。如果子类只复写了部分抽象方法,那么该子类还是一个抽象类。
abstract class Animal{ //抽象方法 public abstract void eat(); //public abstract void eat(){}//这个是空方法体,不是没有方法体,所以会报错 //抽象类有构造方法 public Animal(){} } //子类是抽象类 abstract class Dog extends Animal {} //子类是具体类,重写所有抽象方法 class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } } class AbstractDemo { public static void main(String[] args) { //创建对象 //Animal是抽象的; 无法实例化 //Animal a = new Animal(); //通过多态的方式 Animal a = new Cat(); a.eat(); } }
运行结果:
3.8.3
抽象类的成员特点
代码:
/* 抽象类的成员特点: 成员变量:可以使变量,也可以是常量。 构造方法:有,用于子类访问父类数据的初始化。 成员方法:既可以是抽象的,也可以是非抽象的。 */ abstract class Animal{ //成员变量 public int num = 10; public final int num2 = 20; //构造方法 public Animal(){} //成员方法 public abstract void show(); public void method(){ System.out.println("method run"); } } class Dog extends Animal{ public void show(){ System.out.println("Dog show"); } } class AbstractDemo2{ public static void main(String[] args){ //采用多态 Animal a = new Dog(); a.num = 100; System.out.println(a.num); //a.num2 = 200; System.out.println(a.num2); System.out.println("-----------------"); a.show(); a.method(); } }
运行结果:
结论:
抽象类的成员变量可以使变量,也可以是常量;构造方法也有,作用是用于子类访问父类数据的初始化;成员方法既可以是抽象的,也可以是非抽象的。
3.8.4 练习题
需求:我们在开发一个系统时需要对员工类进行设计,员工包含3个属性:姓名、工号以及工资。经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
分析:
普通员工类(以程序员为例):
成员变量:姓名、工号、工资
构造方法:
成员方法:工作
经理类:
成员变量:姓名、工号、工资、奖金
构造方法:
成员方法:工作
//定义员工类 abstract class Employee{ private String name; private String id; private int salary; public Employee(){} public Employee(String name,String id,int salary){ this.name = name; this.id = id; this.salary = salary; } public String getName(){ return name; } public void setName(String name){ this.name = name; } public String getId(){ return id; } public void setId(String id){ this.id = id; } public int getSalary(){ return salary; } public void setSalary(int salary){ this.salary = salary; } public abstract void work(); } class Programmer extends Employee{ public Programmer(){} public Programmer(String name,String id,int salary){ super(name,id,salary); } public void work(){ System.out.println("写代码"); } } class Manager extends Employee{ //奖金 private int bonus; public Manager(){} public Manager(String name,String id,int salary){ super(name,id,salary); } public Manager(String name,String id,int salary,int bonus){ super(name,id,salary); this.bonus = bonus; } public void work(){ System.out.println("跟客户谈需求"); } public int getBonus(){ return bonus; } public void setBonus(int bonus){ this.bonus = bonus; } } class AbstractTest4{ public static void main(String[] args){ //测试普通员工类 Employee e = new Programmer(); e.setName("小明"); e.setId("czbk001"); e.setSalary(10000); System.out.println(e.getName()+"---"+e.getId()+"---"+e.getSalary()); e.work(); System.out.println("---------------------"); e = new Programmer("小明","czbk001",10000); System.out.println(e.getName()+"---"+e.getId()+"---"+e.getSalary()); e.work(); System.out.println("---------------------"); /* e = new Manager(); e.setName("小刚"); e.setId("czbk010"); e.setSalary(8K); e.setBonus(2K); */ //由于子类有特有的内容,所以我们用子类来测试 Manager m = new Manager(); m.setName("小刚"); m.setId("czbk010"); m.setSalary(8000); m.setBonus(2000); System.out.println(m.getName()+"---"+m.getId()+"---"+m.getSalary()+"---"+m.getBonus()); m.work(); System.out.println("---------------------"); m = new Manager("小刚","czbk010",8000,2000); System.out.println(m.getName()+"---"+m.getId()+"---"+m.getSalary()+"---"+m.getBonus()); m.work(); } }
运行结果:
面试题:
1、一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
可以,目的就是为了不让创建对象。
2、abstract不能和哪些关键字共存?
private、final、static
3.9 接口
3.9.1 理解接口
抽象类中的方法可以没有一个抽象方法,也可以抽象方法和一般方法各占一些,还可以全都是抽象方法。当抽象类中的方法全都是抽象方法时,这个类可以通过接口的形式来实现。接口使用interface来定义,格式为:interface 接口名 { }。子类中用implements来实现,格式为:子类名 implements 接口名 { }。
3.9.2 接口的成员特点
成员变量:只能是常量,并且是静态的。默认修饰符:public static final
构造方法:接口类没有构造方法。
成员方法:只能是抽象方法。默认修饰符:public abstract
3.9.3 类与类、类与接口、接口和接口的关系
类与类:继承关系,只能单继承,可以多层继承
类与接口:实现关系,可以单实现,也可多实现,并且还可以在继承一个类的同时实现多接口。
接口与接口:继承关系,可以单继承,也可以多继承
3.9.4 抽象类和接口的区别
1、成员区别
成员变量:在抽象类中可以是变量,也可以是常量;在接口中只能是常量。
构造方法:抽象类有构造方法;接口没有构造方法。
成员方法:在抽象类中可以是抽象的,也可以是非抽象的;在接口中必须是抽象的。
2、设计理念区别
抽象类:被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
接口:被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。
3.9.5 接口练习
猫狗案例,加入跳高的额外功能
/* 猫狗案例,加入跳高的额外功能 分析:从具体到抽象 猫: 姓名,年龄 吃饭,睡觉 狗: 姓名,年龄 吃饭,睡觉 由于有共性功能,所以我们抽取了一个父类: 动物: 姓名,年龄 吃饭(); 睡觉(){} 猫 继承动物 狗 继承动物 跳高的额外功能是一个新扩张功能,所以我们要定义一个接口 接口: 跳高 部分猫 实现跳高接口 部分狗 实现跳高接口 */ interface Jumpping{ //跳高功能 public abstract void jump(); } abstract class Animal{ private String name; private int age; public Animal(){} public Animal(String name,int age){ this.name = name; this.age = age; } public String getName(){ return name; } public void setName(String name){ this.name = name; } public int getAge(){ return age; } public void setAge(int age){ this.age = age; } //吃饭是抽象的 public abstract void eat(); //睡觉是具体的 public void sleep(){ System.out.println("睡觉"); } } //具体猫类 class Cat extends Animal{ public Cat(){} public Cat(String name,int age){ super(name,age); } public void eat(){ System.out.println("猫吃鱼"); } } //具体狗类 class Dog extends Animal{ public Dog(){} public Dog(String name,int age){ super(name,age); } public void eat(){ System.out.println("狗吃肉"); } } //有跳高功能的猫 class JumpCat extends Cat implements Jumpping{ public JumpCat(){} public JumpCat(String name,int age){ super(name,age); } public void jump(){ System.out.println("跳高猫"); } } //有跳高功能的狗 class JumpDog extends Dog implements Jumpping{ public JumpDog(){} public JumpDog(String name,int age){ super(name,age); } public void jump(){ System.out.println("跳高狗"); } } class InterfaceTest{ public static void main(String[] args){ JumpCat jc = new JumpCat(); jc.setName("多啦A梦"); jc.setAge(3); System.out.println(jc.getName()+"---"+jc.getAge()); jc.eat(); jc.sleep(); jc.jump(); System.out.println("--------------------"); jc = new JumpCat("加菲猫",2); System.out.println(jc.getName()+"---"+jc.getAge()); jc.eat(); jc.sleep(); jc.jump(); } }
运行结果:
版权声明:本文为博主原创文章,未经博主允许不得转载。