自学Java基础知识第九天
day09
1. 多态
1.1 多态的概述
- 多态 : 面向对象中一大特征, 多态表示事物的多种形态
- 多态发生的前提:
1) 需要子父类继承关系(类与接口实现关系)
2) 需要子类重写从父类继承来的方法功能
3) 多态表达式 : 父类的引用指向子类对象
举例 :
class Person{}
class Teacher extends Person{}
class Doctor extends Person{}
Person p; // 父类引用
new Teacher(); // 子类对象
new Doctor() ; // 子类对象
// 多态的表达式
Person p = new Teacher();
Person p1 = new Doctor();
1.2 多态中成员方法的使用特点
- 编译看左, 运行看右
a : 编译看左 : 在使用多态表达式进行方法功能调用, 编译时期, 看等号左边类型中是否定义了调用的方法功能; 如果等号左边定义了这个方法, 那么可以调用; 如果等号左边没有定义这个方法, 那么无法进行调用
b: 运行看右 : 在使用多态表达式进行方法功能调用, 运行时, 动态绑定等号右边的类型中的方法实现(运行子类中的重写方法)
代码
package com.ujiuye.duotai; public class Person { public void eat() { System.out.println("吃饭"); } } |
package com.ujiuye.duotai; public class Teacher extends Person { @Override public void eat() { System.out.println("教师今天中午吃凉皮"); }
public void teach() { System.out.println("教师可以上课"); } } |
package com.ujiuye.duotai; public class Doctor extends Person { @Override public void eat() { System.out.println("医生每天吃饭很清淡"); }
public void save() { System.out.println("医生可以治病救人"); } } |
package com.ujiuye.duotai; public class TestDuoTai { public static void main(String[] args) { // 1. 多态表达式 : 父类引用指向子类对象 // 如果等号左边定义了这个方法, 那么可以调用; 如果等号左边没有定义这个方法, 那么无法进行调用 Person p = new Teacher();// 多态向上转型 p.eat(); // The method teach() is undefined for the type Person // p.teach();
System.out.println("---------------");
Person p1 = new Doctor(); p1.eat(); // p1.save(); } } |
1.3 多态的向上向下转型
- 多态向上转型:
父类引用指向子类对象 : Person p = new Teacher();
理解 : Teacher作为一个后辈年轻人, 现在变成一个Person前辈老年人, 年龄向上增长
本质 : 缩小了p引用的使用范围, 只能使用子父类中的共有的方法功能
- 多态向下转型:
多态向上转型使用有弊端, 子类特有方法功能,通过多态表达式无法调用, 解决问题需要使用多态的向下转型, 有公式: 将指向子类的父类引用, 恢复成子类类型本身
Teacher t = (Teacher)p; // 通过向下转型, 可以使用Teacher子类中的特有方法功能
本质 : 增加了t变量使用范围, 可以在整个Teacher类型中使用
代码
package com.ujiuye.duotai; public class TestDuoTai { public static void main(String[] args) { // 1. 多态表达式 : 父类引用指向子类对象 // 如果等号左边定义了这个方法, 那么可以调用; 如果等号左边没有定义这个方法, 那么无法进行调用 Person p = new Teacher();// 多态向上转型 p.eat(); // The method teach() is undefined for the type Person // p.teach();
// 2. 多态向下转型 Teacher t = (Teacher)p; t.eat(); t.teach();
System.out.println("---------------");
Person p1 = new Doctor(); p1.eat(); // p1.save(); Doctor d = (Doctor)p1; d.eat(); d.save(); } } |
1.4 多态的好处
- 多态能够提高代码的扩展性(灵活性)
- 体现 : 当定义一个方法功能, 方法形式参数设计一个父类类型, 方法调用时, 可以提供父类类型对象或者父类任意一个子类对象作为方法功能的实际参数
案例 : 现在有一个农场, 农场类中有一个方法功能”投食feed”, 能根据feed功能参数列表支持任意动物类型参数, 根据给出的动物类型, 将不同动物需要投喂的食物获取出来, 请利用多态设计出此功能
// 分析 :public void feed(猫/狗/羊...)
// public void feed(Animal a) // 让Animal动物作为方法形式参数, 实际参数可以传递Animal的任意子类对象
代码
package com.ujiuye.duotaitest; // 所有动物的父类 public class Animal { // 每个动物都需要吃饭: 对应每个动物投食过程 public void eat() { System.out.println("喂食"); } } |
package com.ujiuye.duotaitest; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃猫粮"); } } |
package com.ujiuye.duotaitest; public class Dog extends Animal { @Override public void eat() { System.out.println("狗啃骨头"); } } |
package com.ujiuye.duotaitest; // 农场类型 public class Farmer { // 定义出一个方法功能, 能让所有动物都能有对应的投食结果 // a调用eat方法功能, 会根据等号右边动态绑定调用 public void feed(Animal a) { // Animal a = new Cat(); // Animal a = new Dog(); // Animal a = new Sheep(); a.eat(); } } |
package com.ujiuye.duotaitest; public class TestFarmer { public static void main(String[] args) { // 创建出农场对象 Farmer f = new Farmer(); // 调用投喂功能 Animal cat = new Cat(); f.feed(cat);// feed(Animal a) Animal a = new Cat();
Animal dog = new Dog(); f.feed(dog);// feed(Animal a)
Sheep sh = new Sheep(); f.feed(sh);// feed(Animal a) } } |
多态农场案例内存简图:
2. 抽象类
2.1 抽象方法
不论Animal动物中的eat方法如何实现, 都无法满足所有动物对于吃的需求, 于是Animal类型中的eat方法功能, 只对方法声明但是不做方法实现, 这样方法称为抽象方法
需要使用关键字 : abstract 进行修饰
public abstract void eat(); // 抽象方法没有方法体
2.2 抽象类
抽象方法只能定义在抽象类中,在类的修饰符上,使用abstract关键字
abstract class Animal{
// 可以定义出抽象方法
}
代码
package com.ujiuye.abstractdemo; public abstract class Animal {
// 定义出一个抽象方法 // The abstract method eat in type Animal can only be defined by an abstract class // 抽象方法只能定义在抽象类中 // 抽象方法存在意义就是为了给子类限定需要实现的规则 : 例如 , 只要是动物, 那么必须吃饭 public abstract void eat(); } |
2.3 抽象类的特点
- 抽象类和抽象方法, 都需要使用abstract 关键字进行修饰
- 抽象类与抽象方法关系:
1) 抽象方法只能存在于抽象类中
2) 抽象类中不一定有抽象方法
- 抽象类不能实例化对象(实例化就表示new)
因为抽象类中可能含有抽象方法, 但是抽象方法因为没有方法体,不能运行, 因此抽象类不能实例化对象, 避免通过对象名.调用抽象方法
- 抽象类存在意义: 就是为了当父类
抽象类需要一个子类, 继承抽象父类中所有抽象方法, 让子类重写父类中的抽象方法
抽象类的子类前途:
a : 子类将父类的所有抽象方法重写, 子类就是普通子类, 可以创建子类对象, 调用重写方法
b : 子类没有将父类中的所有抽象方法全部重写, 子类还是一个抽象类, 不能实例化对象
抽象类中可以不包含抽象方法
package com.ujiuye.abstractdemo; // 抽象类中不一定有抽象方法 public abstract class AbstractClass { int i = 10; public void fun() { System.out.println("-----------"); } } |
抽象类的子类:
package com.ujiuye.abstractdemo; public abstract class Animal { // 定义出一个抽象方法 // The abstract method eat in type Animal can only be defined by an abstract class // 抽象方法只能定义在抽象类中 // 抽象方法存在意义就是为了给子类限定需要实现的规则 : 例如 , 主要是动物, 那么必须吃饭 public abstract void eat(); } |
package com.ujiuye.abstractdemo; // 实现了父类所有抽象方法的子类 public class Cat extends Animal { @Override public void eat() { /* * 抽象方法子类重写: * 1) abstract关键字去掉 * 2) 给方法添加大括号(方法体) * */ System.out.println("猫吃鱼"); } } |
package com.ujiuye.abstractdemo;
public abstract class Dog extends Animal{ // Dog类中继承到了父类中的抽象方法eat } |
package com.ujiuye.abstractdemo; public class TestAbstract { public static void main(String[] args) { // 1. 抽象类不能实例化对象 // Animal a = new Animal();
// AbstractClass ac = new AbstractClass();
// 2. 实现了所有抽象方法的子类, 可以正常创建对象使用 Cat c = new Cat(); c.eat(); } } |
2.4 抽象类中的成员
抽象类组成方面 : 先当做一个普通类, 基础上还能定义出抽象方法
- 抽象类, 可以定义成员变量
- 抽象类, 可以定义非抽象方法
- 抽象类, 可以定义出构造方法, 一个类型是否可以定义出构造, 与这个类型能否创建对象无关, 与类型中是否能定义成员变量有关; 只要类型中可以定义成员变量, 那么这个类型中就可以定义出构造方法
- 抽象类, 可以定出抽象方法
代码
package com.ujiuye.abstractdemo; public abstract class AbstractClass { // 1. 抽象类中可以定义成员变量 int i = 10; static int j = 20;
// 2. 抽象类中可以定义非抽象方法 public void fun() { System.out.println("-----------"); }
public static void fun1() { System.out.println("+++++++++"); }
// 3. 抽象类中可以定义构造方法 public AbstractClass() { System.out.println("我是抽象父类构造"); }
// 4. 抽象类中可以定义抽象方法 public abstract void eat(); } |
3. 接口
3.1 接口的概述
- 接口 : 接口是一种类型, 类型中定义了一系列规则限定,全是规则,接口中全部都是抽象方法(JDK8版本开始, 接口中添加非抽象方法, 目前学习的方面, 认为接口中全是抽象方法)
3.2 接口的使用特点
- 接口定义:
使用关键字interface 定义出一个接口类型
修饰符 interface 接口名{
// 目前全部抽象方法
}
注意 : 接口源代码也是.java文件, 编译后文件也是.class文件
- 接口不能实例化对象, 因为接口中全部都是抽象方法, 抽象方法不能运行
- 接口需要一个实现类, 将接口中的所有抽象方法进行重写
类与接口之间的关系--->实现关系, 使用关键字 implements, 实现关系与继承关系非常相似
举例 : public interface MyInter{}
class MyInterImpl implements MyInter{
// 将接口中的所有抽象方法重写;
}
- 接口实现类前途:
a : 实现类将父接口的所有抽象方法重写, 实现类就是普通类, 可以创建对象, 调用重写方法
b : 实现类没有将父接口中的所有抽象方法全部重写, 实现类还是一个抽象类, 不能实例化对象
代码
package com.ujiuye.interfacedemo; public interface MyInter { // 定义抽象方法 public abstract void fun();
public abstract int getSum(int x, int y ,int z); } |
package com.ujiuye.interfacedemo; public class MyInterImpl implements MyInter{
@Override public void fun() { System.out.println("fun-----"); }
@Override public int getSum(int x, int y, int z) { return x + y + z; } } |
package com.ujiuye.interfacedemo; public abstract class MyInterImpl2 implements MyInter {
@Override public int getSum(int x, int y, int z) { return 0; } } |
package com.ujiuye.interfacedemo; public class TestInterface { public static void main(String[] args) { // 1. 接口不能实例化对象 // MyInter my = new MyInter();
// 2. 实现了接口中抽象方法的实现类, 可以正常使用 MyInterImpl my = new MyInterImpl(); my.fun();// fun----- System.out.println(my.getSum(3, 4, 5));// 12
// 3. 没有实现接口所有抽象方法的类是个抽象类, 无法new对象 // MyInterImpl2 my2 = new MyInterImpl2(); } } |
3.3 接口中的成员
- 接口中有成员常量:
接口中所有成员变量默认使用 : public static final , 不写修饰默认补全, 写一部分,不认补全
- 接口中有抽象方法: 所有抽象方法默认修饰 public abstract, 修饰符不写或者写一部分, 默认补全
- 接口中不能定义构造, 因为接口中没有成员变量, 全是成员常量
代码
package com.ujiuye.interfacedemo; public interface MyInter成员 { // 1. 接口中所有成员变量默认使用 : public static final , 不写修饰默认补全, 写一部分,不认补全 int I = 10; static int J = 20; public int W = 30; final int Q = 40;
// 2. 抽象方法 void fun();
public void fun1();
abstract void fun2();
public abstract void fun3();
// 3. 定义出构造 //Interfaces cannot have constructors : 接口中不能定义构造器 /*public MyInter成员() {
}*/ } |
3.4 类与类,类与接口,接口与接口之间的关系
- 类与类 : 继承关系, 使用extends关键字, 单继承性, 可以多层继承
- 类与接口 : 实现关系, 使用implements , 可以多实现, 一个类可以直接实现多个接口
interface A{}
interface B{}
class C implements A,B{
// 要求C类将A和B中的所有抽象方法重写即可
}
- 类与接口:
类继承一个父类同时还能实现多个接口, 父类优先
- 接口与接口 : 继承关系, 因为接口中全部是抽象方法, 可以多继承
interface A{}
interface B{}
intreface C extends A,B{
// 包含了A和B两接口中的所有抽象方法
}
C接口还需要实现类, 实现类需要将C接口中所有抽象方法全部重写
代码
package com.ujiuye.interfacegx; public interface A { void fun(); void fun1(); } |
package com.ujiuye.interfacegx; public interface B { void fun(); } |
package com.ujiuye.interfacegx; public class Fu { public void fun() { System.out.println("fun-----父"); } } |
package com.ujiuye.interfacegx; public class C extends Fu implements A, B { @Override public void fun() { System.out.println("C----fun"); super.fun(); }
@Override public void fun1() { System.out.println("C----fun1"); } } |
package com.ujiuye.interfacegx; public class Test { public static void main(String[] args) { C c = new C(); c.fun(); c.fun1(); } } |
接口的多继承
package com.ujiuye.interfacegx; public interface All_Interface extends A,B{
} |
package com.ujiuye.interfacegx; public class All_InterfaceImpl implements All_Interface { @Override public void fun() { }
@Override public void fun1() { } } |