一. 多态
-
定义
- 一个事物的多种形态
-
多态的前提
- 要有父子关系(决定了可以使用多态)
- 要有父类引用指向子类对象
- 要有方法重写(决定了代码有没有实际意义)
-
演示
public class Father { public void method(){ System.out.println("父类method方法"); } } //子类继承父类 public class Son extends Father { //重写父类的方法 public void method(){ System.out.println("子类的method方法"); } } public static void main(String[] args) { //父类类型的变量引用子类类型的实例对象 Father father = new Son(); father.method(); }
-
注意事项
- 可以做哪些工作看父类类型
- 实际做工作的看子类对象
- 优点: 提高了代码的扩展性, 复用性,维护性, 可以当作形参,用来接收任意类型的子类对象
- 缺点: 不能使用子类的特有属性和方法
-
演示
- 学校有授课功能, 但是需要聘请老师讲课, 编程模拟
public class School { public void teach(Teacher teacher){ teacher.teach(); } } public class Teacher { public void teach(){ System.out.println("老师上课"); } } public class MusicTeacher extends Teacher{ public void teach() { System.out.println("优美的歌声从教室中飘出"); } } public class MathTeacher extends Teacher{ public void teach() { System.out.println("认真的讲解着每一个运算步骤"); } } public static void main(String[] args) { School school = new School(); //调用学校的授课功能, 传递一个音乐老师过去 school.teach(new MusicTeacher()); //调用学校的授课功能, 传递一个数学老师过去 school.teach(new MathTeacher()); }
-
测试题
- 动物都有吃饭的功能, 但是小狗吃的是狗粮, 小猫吃的猫粮,当我们将小猫小狗体貌特征遮盖住的时候, 如何判定吃东西的是小猫还是小狗? 编程模拟
二. 多态成员访问的特点
-
成员变量
- 编译看左边(父类) , 运行看左边(父类) (静态绑定)
- 一般中情况使用极少, 成员变量一般都是私有化的
public class Father { String name = "小红"; } public class Son extends Father { String name = "小明"; } public static void main(String[] args) { Father father = new Son(); String str = father.name; System.out.println(str); //小红 }
-
成员方法
- 编译看左边(父类), 运行看右边(子类), 动态绑定
- 子类中如果对父类中的方法进行了重写, 父类中的方法无论如何都无法被访问, 这也是java动态绑定的结果
- 动态绑定会选择最优执行方法, 只要子类重写了父类的方法, 无论在什么地方调用, 都会先找子类
public class Father { public void method(){ System.out.println("父类method方法"); } } public class Son extends Father { public void method(){ System.out.println("子类method方法"); } } public static void main(String[] args) { Father father = new Son(); father.method();//子类method方法 }
-
静态方法
- 编译看左边(父类),运行看左边(父类)
public class Father { public static void method(){ System.out.println("父类method方法"); } } public class Son extends Father { public static void method(){ System.out.println("子类method方法"); } } public static void main(String[] args) { Father father = new Son(); father.method();//父类method方法 //Father.method(); }
-
测试题
- 判断结果
package com.qianfeng.duotai; class Fu{ public void show() { System.out.println("fu show"); } } class Zi extends Fu { public void show() { System.out.println("zi show"); } public void method() { System.out.println("zi method"); } } class TestDemo { public static void main(String[] args) { Fu f = new Zi(); f.method(); f.show(); } }
- 判断结果
class A { public void show() { show2(); } public void show2() { System.out.println("我"); } } class B extends A { public void show2() { System.out.println("爱"); } } class C extends B { public void show() { super.show(); } public void show2() { System.out.println("你"); } } public static void main(String[] args) { A a = new B(); a.show(); B b = new C(); b.show(); }
三. 多态转型
-
向上转型
- 将子类对象看作是父类类型, 也就是我们平时使用的多态的形式
- 这种情况下, 无法调用子类特有的功能
-
向下转型
-
将父类引用指向的子类对象再转回子类类型
-
这种转型是有危险的, 因为是强制性的, 一旦转向的类型跟这个对象不匹配, 就会报错java.lang.ClassCastException (类型转换异常, 属于运行时异常)
public static void main(String[] args) { Father f = new Son(); //转回子类类型 Son son = f; // }
-
四. final关键字
-
定义
- final 表示最终的, 无法被修改的
-
final修饰的特点
- 修饰类, 类不能被继承
- 修饰变量, 变量就变成了常量, 只能被赋值一次(初始化的时候赋值 )
- 修饰方法, 方法不能被重写
-
final 修饰局部变量
- 基本数据类型 , 是值不能被改变
- 引用数据类型, 是地址值不能被改变, 对象中的属性可以改变
- final可以修饰形参
-
final 修饰成员变量的初始化时机
-
必须手动赋值
-
对象初始化完毕之前
-
类初始化完成前(静态)
public class Demo { final String name = "小红"; { //name = "小红"; } public Demo(){ //name = "小红"; } }
-
-
测试题
- 判断结果
public class Demo { final String name ="小红"; public void method(){ name = "小明"; System.out.println(name); } //final修饰变量的时候,只能赋一次值 public void method2(final String name){ System.out.println(name); final int age; age = 10; System.out.println(age); } } public static void main(String[] args) { Demo demo = new Demo(); demo.method(); demo.method2("小红"); }
五. 抽象类
-
定义
- 抽象类没有具体的代码实现, 只是规定了方法的基本属性, 然后由子类去实现具体的代码, 抽象类主要是为了定义规则而存在的
-
特点
- 抽象类和抽象方法必须用abstract 关键字修饰
- abstract class 类名{}
- abstract 返回追类型 方法名();
- 抽象类不一定有抽象方法, 有抽象方法的类一定是抽象类或者接口
- 抽象类不能实例化, 必须由子类继承并重写抽象方法来实例化
- 抽象类的子类
- 要么是抽象类
- 要么重写抽象类中的所有抽象方法(普通类)
- 抽象类和抽象方法必须用abstract 关键字修饰
-
演示
- 公司的员工必须都要有打卡和工作的功能, 所有员工都是使用指纹打卡, 但是工作的方式因岗位的不同而不同
public abstract class Employee { public void sign(){ System.out.println("指纹打卡"); } public abstract void work(); } public class Hr extends Employee{ @Override public void work() { System.out.println("招聘人才"); } } public class Coder extends Employee{ @Override public void work() { System.out.println("努力的敲代码"); } }
-
测试题
- 汽车工厂的已经将车的主体完成,汽车已经可以开动了, 后期需要根据不同的用途安装不同的部件, 货车需要安装货箱, 用来拉货, 客车需要安装座椅,用来拉人
-
抽象类成员方法的特点
- 抽象方法 , 强制要求子类做的事情
- 非抽象方法, 子类继承的方法, 提高代码的复用性
-
优点
- 强制子类实现父类的方法功能
- 提高代码的扩展性, 便于后期的维护
- 形成一套规范(重点)
-
测试题
- 具体事物:猫,狗
- 共性:姓名,年龄,吃饭 (抽象类中成员变量的使用和普通类是一样的)
- 猫的特性 : 抓老鼠
- 狗的特性 : 看家
六. 接口
-
定义
- 接口其实就是抽象类的升级版, 接口里面都是抽象方法
- 从狭义上将来说, 接口就是指java 中的interface
- 从广义上的角度讲对外提供规则的都是接口
- 接口就是一套标准的规范(规则)
-
特点
- 接口用关键字interface表示
- interface 接口名{ }
- 接口中方法上的abstract关键字可以省略
- 类实现接口用implements表示
- class类名 implements 接口名{ }
- 接口不能实例化
- 接口按照多态的方法实例化
- 接口的子类
- 可以是接口,使用extends来继承
- 可以是抽象类, 但意义不大
- 可以是实体类, 要重写接口中的所有抽象方法( 主要使用 )
- 接口中的成员变量必须是 公共的 静态的 最终的 , 也就是常量
- public ,static , final 关键字可以省略 (建议省略)
- 接口可以继承接口
- 接口用关键字interface表示
-
演示
//接口1 public interface MyInterface { //定义了静态常量 static final 可以省略 static final String NAME = "小红"; //定义了抽象方法 public void method(); //定义了抽象方法 public void method2(); } //接口2 继承了接口1 public interface MyInterface2 extends MyInterface { //定义了抽象方法 public void method2(); } //定义实现类实现接口 public class MyInterfaceSon implements MyInterface2{ //重写抽象方法 @Override public void method() { System.out.println("重写method抽象方法"); } //重写抽象方法 @Override public void method2() { System.out.println("重写method2抽象方法"); } }
-
测试题
- 定义老师接口 讲课方法, 辅导作业方法
- 定义实现的子类, 不同老师不同的讲课方式和辅导作业的方式
七. 类 , 接口之间的关系
- 类与类
- 继承关系, 只能单继承 ,不能多继承 ,但是可以多层继承
- 多层继承: 子类继承父类,父类继承祖父类
- 如果多个父类出现相同的方法,无法区别
- 类与接口
- 实现关系,可以单实现, 也可以多实现, 在继承类的同时,可以多实现
- 多个接口中出现了名称和参数列表相同的方法, 但是返回值不同, 会报错
- 接口与接口
- 继承关系 ,可以单继承, 也可以多继承
- 如果多个父类中出现了相同的,子类用谁的都可以
总结:
-
多态
一个事物的多种形态
一个子类对象,可以被看做是父类类型
父类引用指向子类对象(父类类型的变量可以存储子类类型的对象)
-
多态的优缺点
提高了代码的扩展性 , 复用性和维护性
子类的特有方法和属性无法使用了
-
多态下,对象成员访问的特点
成员变量 : 编译看左边, 运行看左边 (基本无用, 成员变量一般私有化)
成员方法 : 编译看左边, 运行看右边 (主要使用方式)
静态成员 : 编译看左边, 运行看左边 (基本无用,静态使用类名调用)
-
转型
子类对象被父类变量引用 : 向上转型
父类引用再赋值给子类变量 : 向下转型 (有风险,强转)
-
final关键字
final修饰类, 类不能被继承
final修饰方法, 方法不能被重写
final修饰变量 , 变量只能赋值一次
final修饰成员变量, 必须在初始化完成前手动给成员变量赋值
-
抽象类
一个特殊的无法实例化的类, 内部可以写抽象方法
抽象方法 :
只有方法的结构,没有具体的代码,使用 abstract 关键字标识
意义 :
定义规则, 强制子类重写
特点 :
可以有抽象方法,也可以有非抽象方法
抽象类无法实例化(创建对象)
子类可以是抽象类,可以是非抽象类
-
接口
一种特殊的抽象类, 里面全是抽象方法 , 而且省略的很多的关键字
定义接口的关键字是 interface , 普通类实现接口 implements
接口的方法省略 public abstract
接口中的成员变量成略了 public static final
-
类,接口之间的关系
类和类 : 继承关系, 只能但继承,可以多层继承
接口和接口 : 继承关系, 可以多继承
类和接口 : 实现关系, 可以多实现
作业
-
第一题
- 一档选秀节目, 设计了一个平台供参与者展示才艺, 不同的参与者有不同的才艺. 使用多态编程模拟
-
第二题
- 学生都有学习和做作业的能力 , 但是学习的方式不同, 使用抽象类的方法模拟编程
-
第三题
- 创建一个乐器接口 Instrument, 定义演奏方法play, 定义两个实现类 钢琴 Piano, 小提琴 Violin ,分别实现乐器接口,并做出不同的演奏
-
第四题
- 国家规定, 汽车必须能点火, 能开动, 国家将文件发送到汽车厂商的手中, 厂商生产的汽车必须符合要求, 可以有特性功能, 编程模拟
-
扩展题
class A{ public String show(D obj){ //D obj = new D(); return ("A and D"); } public String show(A obj){ //A obj = new D(); return ("A abd A"); } } class B extends A{ public String show(B obj){ return ("B and B"); } public String show(A obj){ return ("B and A"); } } class C extends B{ } class D extends B{ } 问题:以下输出结果是什么? A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println(a1.show(b)); ① A abd A System.out.println(a1.show(c)); ② A abd A System.out.println(a1.show(d)); ③ A and D System.out.println(a2.show(b)); ④ B and A System.out.println(a2.show(c)); ⑤ B and A System.out.println(a2.show(d)); ⑥ A and D System.out.println(b.show(b)); ⑦ B and B System.out.println(b.show(c)); ⑧ B and B System.out.println(b.show(d)); ⑨ A and D //能执行哪些方法,看变量的类型 //如果有多态,那么实际执行那个方法看子类 //如果有多个方法可行,找最合适的,也就是最接近的