文章目录

  • 第九章 面向对象(3)
  • 9.1 抽象类
  • 9.1.1 抽象方法
  • 9.1.2 抽象类
  • 1. 概念
  • 2.抽象类的特点
  • 3. 总结:
  • 9.2 接口(Interface)
  • 9.2.1 概念
  • 9.2.2 接口的特点
  • 9.3 接口的实现(implements)
  • 9.3.1 概念
  • 9.3.2 类与类,类与接口,接口与接口的关系
  • 9.3.3 实现接口和继承类的应用场景
  • 9.3.内部类(面试题重点)
  • 9.3.1 概述
  • 9.3.2 成员内部类
  • 9.3.2.1 普通成员内部类
  • 9.3.2.2 私有成员内部类
  • 9.3.2.3 静态成员内部类
  • 9.3.3 局部内部类(重点)
  • 9.3.3.1 非静态方法定义局部内部类
  • 9.3.3.2 静态方法定义局部内部类
  • 9.3.3 局部内部类(重点)
  • 9.3.3.1 非静态方法定义局部内部类
  • 9.3.4 匿名内部类(重点)
  • 9.4 多态(重点重点)
  • 9.4.1 基本概念
  • 9.4.2 成员变量的访问原则
  • 9.4.3 成员方法的访问原则
  • 9.4.4 引用数据类型的向上向下转型(重难点)
  • 9.4.5 多态的弊端和好处


第九章 面向对象(3)

9.1 抽象类

9.1.1 抽象方法

  1. 抽象方法:java中只有方法声明没有方法实现,并且被关键abstract修饰的方法就是抽象方法。
修饰符 abstract 返回值类型 方法名(参数列表);
  1. 作用: 提取相同功能不同结果的方法的声明部分, 只需要定义一个这样的方法名字,但是不提供方法实现.以后谁想用 这个方法,那就自己添加方法体。
  2. 注意:只能定义在抽象类中和接口中.

案例:

package com.inter;

public interface Usb {
    public abstract  void powerOn();
    public abstract  void powerOff();
    public abstract  void intallDriver();
}
  1. 复习:已学过的4种方法的特点
(1)普通方法:
public 返回值类型  方法名 (参数列表) {
方法体;
}
(2)静态方法:有自己的内存空间,调用方法,直接 类名.方法名
public static  返回值类型  方法名 (参数列表) {
方法体;
}

(3)最终方法:不能被子类重写,但是可以使用
public final  返回值类型  方法名 (参数列表) {
方法体;
}

(4)抽象方法:只能在抽象类,接口中使用,目的是制定标准
public abstract 返回值类型 方法名(参数列表);

9.1.2 抽象类

1. 概念
  1. 属于类的一种,所有对象都是由类创建,但是不是所有的类都能创建对象。
    2.概念: java中被关键字 abstract 修饰的类就是抽象类 用来存储抽象方法的类就是抽象类.
  • 抽象类中一般都会定义抽象方法.
  • 如果不定义抽象方法,直接定义为普通类。
  • 抽象类不能实例化对象,所以抽象类必须被继承,才能被使用
  1. 格式
修饰符 abstract class 类名{
  }
   内容:
(1) 属性(成员变量)
(2)构造方法,
(3)行为(方法)
(4)抽象方法
---- 行为分为普通行为(普通方法,静态方法)+ 特殊行为(抽象方法)
  1. abstract只能修饰类和方法,抽象类除了定义普通类中的所有内容,还可以定义抽象方法.
  2. 子类继承抽象类,必须强制实行父类中的抽象方法(重写)才可以,否则会报错,无法继承。
  3. this就是当前new谁,this就是谁。
2.抽象类的特点
  1. 特点
(1)有构造方法但是不能创建对象, 抽象类不能实例化 抽象类就是用当做父类用的 [天生就是当爹的] 
(2)构造方法给子类创建对象的时候调用使用, 通过super语句使用抽象类中构造方法 
(3)抽象类的抽象方法必须要求子类重写,而且要重写全部的抽象方法 ,一个普通类如果是抽象类的子类,那么这个类在继承抽象类之后, 就必须全部重写抽象方法.否则子类就会编译报错.
  1. 抽象类和抽象方法的关系
(1)抽象类不一定有抽象方法, 但是一般定义抽象类都会在里面写抽象方法.
(2)有抽象方法的类一定是抽象类!!!
  1. 抽象类的使用场景
抽象类一般用于父类,用来抽取一系列类的公共资源(属性和行为),如果父类的有些方法,即使在父类中提供了方法实现,子类继承过去后依然重写,那么就把这些方法定义为抽象方法,在父类中没必要提供实现 。
  1. 练习
package com.block;

public abstract  class Employee {
    //属性
    String name;
    String number;
    int salary;

    //构造方法
    public  Employee() {

    }

    //有参构造
    public Employee(String name,String number,int salary){
        System.out.println("Employee抽象类中输出this:" + this);
        this.name = name;
        this.number = number;
        this.salary = salary;
    }

   //抽象方法
    public abstract void work();

}

-----------------------------------------
//子类
package com.block;

public class chenXuYuan  extends Employee{
    int jiangj;

    public chenXuYuan(String name, String number, int salary, int jiangj) {
        super();
        this.name = name;
        this.number = number;
        this.salary = salary;
        this.jiangj = jiangj;
    }


    @Override
    public void work() {
        System.out.println("程序员做软件开发");
    }
}
----------------------------------------------------
//子类
package com.block;

public class manager extends Employee {
    int jiangj;
    public manager(String name,String number,int salary,int jiangj) {
        super();
        this.name = name;
        this.number = number;
        this.salary = salary;
        this.jiangj = jiangj;
    }
    @Override
    public void work() {
        System.out.println("项目经理控制进度");
    }
}
--------------------------------------
//子类
package com.block;

public class ceShi extends Employee {

    public ceShi (String name,String number,int sala) {
        super();
        this.name = name;
        this.number = number;
        this.salary = salary;
    }
    @Override
    public void work() {
        System.out.println("测试进行软件测试");
    }
}
------------------------------------------------------
//测试
package com.block;

public class ExampleTest {
    public static void main(String[] args) {
      chenXuYuan c  = new chenXuYuan("张三","1111111",5000,1000);

      ceShi s = new ceShi("李四","22222",4000);

      manager m = new manager("王五","33333",5000,2000);
      
      c.work();
      s.work();
      m.work();
    }
}
3. 总结:
(1)抽象类只能当做父类。
(2)抽象类中的构造方法子类只能调用,不能更改。
(3) 子类必须先实现抽象类的抽象方法。

9.2 接口(Interface)

9.2.1 概念

  1. 概念
(1)接口也是java一种引用数据类型。
(2)接口在Java中是一个抽象类型,是**抽象方法的集合(**只能定义抽象方法,可以默认不写abstract),以interface来声明。
(3)接口之间可以相互继承,普通类可以**实现**接口,从而实现接口的抽象方法。
  1. 格式:
修饰符  interface  接口名{
   接口内容(抽象方法);
}
  1. 注意:
(1)接口不是类(没有用class声明),类描述对象的属性和方法,接口包含类的方法(只声明不实现的抽象方法)
(2)接口不能实例化,但是可以实现,一个实现接口的类,必须实现接口里面的所有方法,否则必须声明为抽象类。
(3)接口的源文件也是.java文件, 也是参与编译的 。
(4)编译后的文件也是字节码文件(*.class) [接口的本质其实还是一个类]. 
(5)运行时字节码文件也需要加载到内存的方法区中
  1. 抽象类和接口的区别
(1)抽象类是把类做增强处理,在原基础上可以增加抽象方法,不能被实例化,只能被继承。
(2)接口不是类,接口是把类做削弱处理,接口拥有抽象类的部分性质,不能被继承,但是可以被实现。
  1. 代码示例:
public interface Car {

    //简写 - 接口中的方法在Java8以前默认的方法都是抽象方法
    public void run();

    // //完整写法
    public abstract  void stop();
}

9.2.2 接口的特点

  1. 接口的特性
(1) 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
(2)接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
(3) 接口中的方法是不能在接口中实现的,只是声明,只能由实现接口的类来实现接口中的方法。
  1. 接口支持多继承的理解:
(1)类与类之间是单继承,支持多重继承,一个父类可以有多个子类,一个子类只能有一个父类
(2) 抽象类只是类的增强,在继承体系中,完全遵从java普通类的继承规则
(3) 普通类和接口之间,接口脱离类的约束,没有继承这一说法,但是普通类可以实现接口,并且可以实现多个接口。
(4)重点:接口与接口之间可以继承,(接口不是类,但是接口与接口是同类,可以有继承体系)
  1. 接口与类的区别(面试题)
接口不是类:
			 (1)接口不能用于实例化对象
			(2) 接口不能定义构造方法(构造器)
			(3) 接口中所有方法都必须是抽象方法--java8之后可以定义默认(default)普通方法,格式:
				      修饰符 default 返回值类型 方法名(参数列表){
				          方法体 ;
				 ------此时default不是访问修饰符,是接口中普通方法的定义规则。
			(4) 接口不能包含成员变量,除了static和final变量(就是只能定义常量)
			(5) 接口不是被类继承,是实现(implement).
			 (5)接口支持多继承
  1. 抽象类和接口的区别
(1) 抽象类中的方法可以有方法体(可以有普通方法),,但是接口中的方法只能声明,没有普通方法(java8以后包含java8可以用deafult声明普通方法 )。
(2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final(public final) 类型的。
(3)接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
(4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。(**单继承多实现**)

9.3 接口的实现(implements)

9.3.1 概念

  1. 概念:
(1)实现关系:接口中的规则描述的只有声明没有具体的实现内容,要想被使用必须要把实现内容具体描述出来,并且,只能通过类对接口规则进行补充,类就和接口产生了关系,这种关系就叫实现关系
(2)接口的实现:类通过关键字implement连接接口,就可以对接口中的规则进行补充,这种类连接接口的方式叫做接口的实现。
(3)注意事项:类对接口中的规则进行补充,就有了接口描述的规则的功能,并且必须实现接口中的所有抽象方法(重写)。
             一个类实现接口,相当于继承了接口。
             一个类可以同时实现多个接口。
             多实现的多个接口中不要出现方法名相同,返回值类型不同的规则 --- 没法重写--因为不符合方法重载 的规则。
  1. 格式:
修饰符 class 类名  implements 接口名1,接口名2……{
     类内容
 }
 //单实现:一个类实现1个接口
  多实现:一个类实现多个接口
  1. 代码示例:
public interface Car {

    //简写 - 接口中的方法在Java8以前默认的方法都是抽象方法
    public void run();

    // //完整写法
    public abstract  void stop();
}
-------------------------------------
//接口类
public class TuoLaJi implements Car {
    String color;
    double prace;
    
    @Override
    public void run() {
        System.out.println("拖拉机跑得慢");
    }

    @Override
    public void stop() {
        System.out.println("拖拉机停得慢");
    }
}

9.3.2 类与类,类与接口,接口与接口的关系

(1)类与类的关系:
     只能是继承关系(父子关系),java中的继承只支持单继承,多层继承。
(2)类与接口的关系:
     只能是实现的关系(干父子关系),一个类实现一个接口就相当于继承了这个接口。
(3)接口和接口的关系:
     继承关系,java中接口支持所有继承关系
     单继承:A接口继承B接口
     多层继承:A接口继承B接口,B接口继承C接口
     多继承:A接口继承B,C,D,F,接口,一个接口可以继承多个接口(一个儿子多个爹)
(4)注意事项:
     多个父接口有相同的抽象方法,相当于只继承一个抽象方法,若有类实现这个子接口也只需要实现一个同名的抽象方法。

9.3.3 实现接口和继承类的应用场景

抽象类是做标准的,接口也是做标准的,如何选择?

注意:抽象类是对类的增强,接口是对类的弱化(提取了抽象部分的功能)。
(1)选择抽象类:定义的父类天生就具有一些属性和属性,定义的时候一定为其实现一些功能。
(2)选择接口:定义的父类完全只做标准,不需要实现功能。

一个类可以在继承父类的时候,再实现多个接口
格式:
修饰符 class 子类 extends 父类 (普通类或抽象类) implements 接口1, 接口2 { }

9.3.内部类(面试题重点)

9.3.1 概述

  1. 内部类的概念:
(1)定义在一个类或一个方法中的另一个类称之为内部类。
(2)一个类如果定义在某个**大括号**里,这个类就是内部类
  1. 为什么有内部类?
(1)进行资源封装
(2)变相的实现多继承--A类中有三个内部类B,C,D,用E类继承A类,就相当于E继承了A,B,C,D
  1. 格式:
修饰符 class  外部类名{
        修饰符 class  内部类名{
      
        }
   
}
  1. 分类:
(1) 成员内部类:
              普通成员内部类,
              私有成员内部类
              静态成员内部类
(2)局部内部类:定义在方法中的内部类
(3)匿名内部类(重点)

9.3.2 成员内部类

1. 定义位置:定义在类的成员位置(类中方法外,类体中)。
2. 特点:要实例化内部类,必须先实例化外部类。
3. 分类:
              普通成员内部类
              私有成员内部类
              静态成员内部类
9.3.2.1 普通成员内部类
1. 格式:
	 public class  主类名称 {
	    public class  普通成员内部类名称{  
	    
	    }
	}

2. 特点:

(1)外部类定义任何内容不受限制。
(2)普通成员内部类只能定义非静态的资源(静态的成员调用方式是 类名.属性名  ,而内部类不可以直接使用 类名.属性名 )
(3)普通成员内部类访问规则:必须先实例化外部类,再实例化内部类。实例化过后,静态就没有意义了。
(4)普通成员内部类可以使用外部类的所有资源(包括私有资源)。

3. 实例化方式(举例)

     	第1种:
     	 外部类名 对象名 = new 外部类名();
     	 外部类名.私有成员内部类名 对象名 =  外部类名.new 私有成员内部类名();
	     Student student = new Student();
	     Student.Monitor monitor1 = student.new Monitor();
	
	第2种:
	     外部类名.私有成员内部类名 对象名 = new 外部类名().new 私有成员内部类名
	     Student.Monitor monitor2 = new Student().new Monitor;

代码示例:

public class Student {
    public String name = "学生";
    public int age = 18;

    public class Monitor{
        public String name = "班长";
        public String hobbits = "打篮球";
        public void manage() {
            System.out.println(age);
            System.out.println("管理班级");
        }
    }
}
-----------------------------
public class Test {
    public static void main(String[] args) {
        //第1种
        Student student = new Student();
        Student.Monitor monitor = student.new Monitor();

        //第2种
        Student.Monitor monitor1 = new  Student().new Monitor();
        System.out.println(monitor.name);
        System.out.println(monitor.hobbits);
        monitor.manage();
    }
}
9.3.2.2 私有成员内部类
1. 格式:

	 public class  主类名称 {
	    private class  普通成员内部类名称{  
	    
	    }
	}

2. 特点:
(1)外部类定义任何内容不受限制。
(2)私有成员内部类只能定义非静态的资源不能定义静态资源。
(3)私有成员内部类可以使用外部类的所有资源(包括私有资源

3. 实例化方式:
     	第1种:
     	 外部类名 对象名 = new 外部类名();
     	 外部类名.私有成员内部类名 对象名 =  外部类名.new 私有成员内部类名();
	     Student student = new Student();
	     Student.Monitor monitor1 = student.new Monitor();
	
	第2种:
	     外部类名.私有成员内部类名 对象名 = new 外部类名().new 私有成员内部类名
	     Student.Monitor monitor2 = new Student().new Monitor;
        
    第3种:
         私有成员内部类名 对象名 = new 私有成员内部类名();
	     Monitor monitor2 = new Monitor();
注意:
      实例化方式与普通成员内部类相同。 因为是私有的,所以只有在当前类中才可以实例化,但是其他类可以通过外部类进行方法调用。
      完全走封装的体系,私有化内部类,对外提供公开的方法(把内部类隐藏得更深)。

代码示例:

public class Student {
    public String name = "学生";
    public int age = 18;

    private class Monitor{
        public String name = "班长";
        public String hobbits = "打篮球";
        public void manage() {
            System.out.println(age);
            System.out.println("管理班级");
        }
    }
    //只能在当前类中实例化,方法有3种,可创建方法供其他类使用
    public void show() {
        //第1种
        Student student = new Student();
        Student.Monitor monitor = student.new Monitor();

        //第2种
        Student.Monitor monitor1 = new  Student().new Monitor();

        //第3种
        Monitor monitor2 = new Monitor();

        System.out.println(monitor.name);
        monitor.manage();
    }

}
-------------------------------------------
public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.show();
    }
}
9.3.2.3 静态成员内部类
1. 格式:

	 public class  主类名称 {
	    private static class  普通成员内部类名称{  
	    
	    }
	}

2. 特点:
(1)外部类定义任何内容不受限制。
(2)静态成员内部类可以定义任何内容不受限制。
(3)静态成员内部类不能使用外部类的非静态资源,可以直接使用外部类的静态资源。

3. 实例化方式:
	       外部类名.静态成员内部类名 对象名 = new 外部类名.静态成员内部类名
	       Student.Monitor monitor2 = new Student.Monitor;

代码示例:

public class Student {
    public String name = "学生";
    public static int age = 18;

    public static  class Monitor{
        public String name = "班长";
        public String hobbits = "打篮球";
        public void manage() {
            System.out.println(age);
            System.out.println("管理班级");
        }
    }
}
-------------------------------------------
public class Test {
    public static void main(String[] args) {
        Student.Monitor monitor = new Student.Monitor();
        monitor.manage();
        System.out.println(monitor.name);
        System.out.println(monitor.hobbits);

    }
}

9.3.3 局部内部类(重点)

1. 概念:定义在方法体中的内部类就是局部内部类。

2. 格式:
       修饰符 返回值类型 方法名(参数列表) {
       class 局部内部类名
       
       }
9.3.3.1 非静态方法定义局部内部类
1. 格式:
    public void 方法名(){
       class 局部内部类名{
         
       }
    }

2. 特点:
  (1)局部内部类中只能定义非静态的资源。
  (2)可以使用所在类中的以及所在方法中的所有资源。

3. 访问方式(实例化方式):
    Monitor monitor = new Monitor();
    通过方法在当前类中提供局部内部类的调用(在局部内部类所在方法中实例化内部类),之后再提供此方法给其他类使用。

代码示例:

public class Student {
    public String name = "学生";
    public static int age = 18;

    public void show() {
        class Monitor{
            public String name = "班长";
            public String hobbits = "打篮球";
            public void manage() {
                System.out.println(age);
                System.out.println("管理班级");
            }
        }

        Monitor monitor = new Monitor();
        System.out.println(monitor.name);
        monitor.manage();
    }
}
---------------------------------------
public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.show();
    }
}
9.3.3.2 静态方法定义局部内部类
1. 格式:
    public static void 方法名(){
       class 局部内部类名{
         
       }
    }

2. 特点:
  (1)局部内部类中只能定义非静态的资源。
  (2)只能使用外界的静态资源【所在类的静态资源】

3. 访问方式(实例化方式):
 在局部内部类所在方法中实例化内部类,在其他类中 静态方法直接类名.方法名 调用。

代码示例:

public class Student {
    public String name = "学生";
    public static int age = 18;

    public static  class Monitor{
        public String name = "班长";
        public String hobbits = "打篮球";
        public void manage() {
            System.out.println(age);
            System.out.println("管理班级");
        }
    }
}
-------------------------------------------
public class Test {
    public static void main(String[] args) {
        Student.Monitor monitor = new Student.Monitor();
        monitor.manage();
        System.out.println(monitor.name);
        System.out.println(monitor.hobbits);

    }
}

9.3.3 局部内部类(重点)

1. 概念:定义在方法体中的内部类就是局部内部类。

2. 格式:
       修饰符 返回值类型 方法名(参数列表) {
       class 局部内部类名
       
       }
9.3.3.1 非静态方法定义局部内部类
1. 格式:
    public void 方法名(){
       class 局部内部类名{
         
       }
    }

2. 特点:
  (1)局部内部类中只能定义非静态的资源。
  (2)可以使用所在类中的以及所在方法中的所有资源。

3. 访问方式(实例化方式):
    Monitor monitor = new Monitor();
    通过方法在当前类中提供局部内部类的调用(在局部内部类所在方法中实例化内部类),之后再提供此方法给其他类使用。

代码示例:

public class Student {
    public String name = "学生";
    public static int age = 18;

    public void show() {
        class Monitor{
            public String name = "班长";
            public String hobbits = "打篮球";
            public void manage() {
                System.out.println(age);
                System.out.println("管理班级");
            }
        }

        Monitor monitor = new Monitor();
        System.out.println(monitor.name);
        monitor.manage();
    }
}
---------------------------------------
public class Test {
    public static void main(String[] args) {
        //调用静态方法不需要初始化,实例化
        Student.show();
    }
}

9.3.4 匿名内部类(重点)

1. 概念:没有名字的内部类,有自己独有的类定义方式。匿名内部类本质是一个子类或实现类的对象。

2. 格式:
	 new 父类类名(参数) { 
	       重写父类中方法
	 }
	 或
	 new 接口名(参数){ 
	       重写接口的抽象方法
	 }

3. 匿名内部类的使用前提:必须拥有继承关系或接口实现关系(重点)。

4. 场景:
匿名内部类其实是对父类的子类对象的体现:继承关系下,匿名内部类的本质就是一个父类的子类对象。可以把匿名内部类想象为,为一个父类快速提供一个子类对象便捷方式. 现有一个父类想要使用它的子类对象做事情,但是没有定义子类就直接创建子类的对象,这个时候需要匿名内部类。(创建一个父类对象,但是因为父类需要一个子类的实现,而我们又没有提供,那么默认会生成一个子类对象,这个对象就是匿名对象,多态中使用多)

5. 匿名内部类作为对象直接使用:
    (1)直接当做匿名对象调用资源使用(只能使用一次)
        格式: new 父类类名(参数){
              重写父类中相关功能
        }.方法名;

    (2)可以赋值给父类类型的变量
           父类类型 对象名 = new 父类类名(参数) {
                   重写父类中相关功能
           };
           对象名.方法名();

6.匿名内部类在开发中的使用:
   当发现某个方法需要类的子类对象,就可以传递一个匿名内部类过去,简化传统的代码。

代码示例:

public abstract  class Animal {
    public abstract void eat();
}
------------------------------------
public class AnimalExample {
    public static void main(String[] args) {
        //直接当做一个匿名对象调用资源使用,只能使用一次
        new Animal() {

            @Override
            public void eat() {
                System.out.println("小狗爱吃肉骨头");
            }
        }.eat();
        //2.赋值给父类类型的变量
        Animal animal = new Animal() {

            @Override
            public void eat() {
                System.out.println("鸭子喜欢吃虫虫......");
            }
        }; //记住加分号
        animal.eat(); //可以直接调用,匿名内部类中已经实现了抽象方法。
    }
}

9.4 多态(重点重点)

9.4.1 基本概念

java章节 java第九章_java

1. 概念
(1)多态就是同一个行为具有多个不同表现形式或者形态的能力。
(2)多态就是  ,同一个接口使用不同的实例去执行不同的操作。
     例如:一个人在不同的场景下有不同的身份。


2. 多态存在的三个必要条件:
(1)父子继承(普通类继承,抽象类继承)/ 接口实现
(2) 重写
(3)父类引用指向子类对象--Parent p = new Child();

3. 多态的优点
(1) 消除类型之间的耦合性
(2) 可替换性
(3)可扩充性
(4)接口性
(5)灵活性


4. 多态特点总结
(1)父类的引用指向子类对象
      父类类型的变量
      指向子类对象:父类类型的变量存放了子类对象的地址值
(2)接口类型引用指向实现类对象
       接口类型引用:接口类型的变量
       指向实现类对象:接口类型的变量存放了实现类对象的地址值  

5. 格式:
    父类名  变量名(对象名) = new 子类名(实参)
    接口名  变量名 = new  实现类名(实参)

案例一:继承关系中的多态

/**
 * 父类
 * 描述:打印机抽象类
 */

public abstract class Printer {
    //功能:打印
    public abstract void print();
}
-------------------------------
/**
 * 子类
 * 描述:打印机子类--黑白打印机
 */

public class BlackPrinter extends Printer{
    //重写父类方法
    @Override
    public void print() {
        System.out.println("打印黑白文件");

    }
}




/**
 * 子类
 * 描述:打印机的子类--彩色打印机
 */

public class ColorPrinter extends Printer{

    //重写父类方法
    @Override
    public void print() {
        System.out.println("打印彩色文件");
    }
}
-------------------------------------------
/**
 * 描述:打印机实例
 */

public class PrinterExample {
    public static void main(String[] args) {
        //1.没有运用多态时的定义
        ColorPrinter colorPrinter = new ColorPrinter();
        BlackPrinter blackPrinter = new BlackPrinter();
        colorPrinter.print();
        blackPrinter.print();


        //2.运用多态时的定义(父类对象指向子类引用)
        Printer printer1 = new ColorPrinter();
        Printer printer2 = new BlackPrinter();
        printer1.print();
        printer2.print();
        // 这里代码中直接体现了两个不同子类都可以用Printer接收,那么我们称为多态(一个类的不同表现形态)
    }
}

案例二:实现关系中的多态

/**
 * 描述:打印机接口
 */

public interface Printer {
    //功能:打印
    public abstract void print();
}
------------------------------------
/**
 * 描述:打印机接口实现类--黑白打印机
 */

public class BlackPrinter implements Printer{
    //重写接口抽象方法
    @Override
    public void print() {
        System.out.println("打印黑白文件");

    }
}



/**
 * 描述:打印机接口实现类--彩色打印机
 */

public class ColorPrinter implements Printer{

    //重写接口抽象方法
    @Override
    public void print() {
        System.out.println("打印彩色文件");
    }
}

-------------------------------
/**
 * 描述:打印机实例
 */

public class PrinterExample {
    public static void main(String[] args) {
        //1.接口中可以直接使用实现类自己去接收,但是没有多态
        ColorPrinter colorPrinter = new ColorPrinter();
        BlackPrinter blackPrinter = new BlackPrinter();
        colorPrinter.print();
        blackPrinter.print();


        //2.运用多态
        Printer printer1 = new ColorPrinter();
        Printer printer2 = new BlackPrinter();
        printer1.print();
        printer2.print();

    }
}

9.4.2 成员变量的访问原则

1. 编译看左边(父类) , 运行看左边 Fu f = new Zi();
2. 多态中因为使用父类型去接收,在输出变量信息时,一定需要父类中的定义,子类型中定义的变量这时候不能使用。
3. 如果非要使用子类型自定义的变量,脱离多态用子类型自己去接收, Zi z = new  Zi();

代码示例:

/**
 * 描述:打印机实例
 */

public class PrinterExample {
    public static void main(String[] args) {
        //1.没有运用多态时的定义
        ColorPrinter colorPrinter = new ColorPrinter();
        BlackPrinter blackPrinter = new BlackPrinter();
        colorPrinter.print();
        blackPrinter.print();


        //2.运用多态时的定义(父类对象指向子类引用)
        //多态的体现(父类引用指向子类对象):儿子有一个快递,父亲有权利签收
        Printer printer1 = new ColorPrinter();
        Printer printer2 = new BlackPrinter();
        printer1.print();
        printer2.print();
        // 这里代码中直接体现了两个不同子类都可以用Printer接收,那么我们称为多态(一个类的不同表现形态)
    }
}

9.4.3 成员方法的访问原则

1. 编译看左边(父类)
2. 运行时分为两种情况:
   (1)子类重写,运行看右边(子类)
   (2)子类不重写,运行看左边(父类)

代码示例:

/**
 *非抽象类
 */

public class Tree {
    public void getSunShine() {
        System.out.println("父类的光合作用");
    }
}
----------------------------------
/**
 * Tree的子类
 */

public class YangTree extends Tree{
   /**@Override
    public void getSunShine() {
        System.out.println("子类杨树的光合作用");
    }
   */
}
-----------------------
public class TreeExample {
    public static void main(String[] args) {
        Tree tree = new YangTree();

        //(1)子类重写,运行看右边(子类)
        //(2)子类不重写,运行看左边(父类)
        tree.getSunShine();

    }
}

9.4.4 引用数据类型的向上向下转型(重难点)

1. 向上转型:
   子类对象从子类的类型变为父类的类型体现,向上转型就是多态的体现。
   向上转型是自动的,小类型向大类型
   如:Printer printer1  =  new ColorPrinter

3. 向下转型:
   前提:必须已经发生向上转型,才能做向下转型的操作,多态对象回归自己原本类型的过程
   格式:子类类型 变量名 = (子类名称)多态对象名
       ColorPrinter  s = ( ColorPrinter) printer1

3.转型过程中的Bug问题
   (1)不确定引用原类型(向上转型的类型)是什么,程序不会报错,但是运行时有问题,会提示类型转化异常(ClassCastException类型转化异常)
    如:Exception in thread "main" java.lang.ClassCastException: com.its.morestatus.ColorPrinter cannot be cast to com.its.morestatus.NoColorPrinter at com.its.morestatus.PrinterExample.main(PrinterExample.java:13)
   (2)规避方式:
    转换之前需要判断转换的类型是不是多态对象之前的类型
    使用关键字 instanceof 判断向下转换类型是不是原本类型
    格式:
               boolean  变量名 = 多态对象名 instanceof 指定的数据类型
               -----如果指定对象是原对象返回true,如果指定对象不是原对象返回false。

代码示例

/**
 * 描述:打印机实例
 */

public class PrinterExample {
    public static void main(String[] args) {
        //1.向上转型:子类对象从子类的类型变为父类的类型体现,向上转型就是多态的体现。
        Printer printer1 = new ColorPrinter();

        //2.向下转型: 前提:必须已经发生向上转型,才能做向下转型的操作,多态对象回归自己类型的过程
        // //向下转型 BUG-因为我不确定向上转型的原类型是什么?随便使用黑白打印机转化
        if(printer1 instanceof BlackPrinter) {
           BlackPrinter blackPrinter =(BlackPrinter)printer1;
           blackPrinter.print();
        }
        if(printer1 instanceof ColorPrinter){
           ColorPrinter colorPrinter = (ColorPrinter)printer1;
           colorPrinter.print()
        }
         
    }
}

9.4.5 多态的弊端和好处

多态的弊端:
  1. 一旦产生多态,相当于缩小了真实类型对象的访问范围,原因是只能访问到父类中有的内容,或者父子类中都出现的内容;无法访问子类独有的内容。
     解决方法:向下转型,回复真实对象访问范围。

多态的好处:
  1. 增强代码的扩展性,提升代码的复用性。
     比如:在定义方法的时候,如果参数类型定义为父类类型形参.那么实参的范围就比较大了. 实参可以是父类类型 的对象,也可以它子类类型对象(会自动向上转型)
          如果一个返回值类型父类类型,那么return关键后就可以跟父类对象当做返回值,也可以跟子类对象当做返回值