【JAVA】Day12:设计模式基石----接口、多态、引用类型详解

2022年05月14日 阅读数:8
这篇文章主要向大家介绍【JAVA】Day12:设计模式基石----接口、多态、引用类型详解,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

本专栏将从基础开始,按部就班,由浅入深讲解Java的基本使用,但愿你们都可以从中有所收获,也请你们多多支持。
专栏地址:26天高效学习Java编程
相关软件地址:软件地址
全部代码地址:代码地址
若是文章知识点有错误的地方,请指正!你们一块儿学习,一块儿进步。
若是感受博主的文章还不错的话,还请关注、点赞、收藏三连支持一下博主哦html

本文将详细讲解以下内容:java

  • 接口
    • 定义接口
    • 实现接口
    • 接口中成员访问特色
  • 多态
    • 实现多态
    • 多态成员访问特色
    • 多态的好处和弊端
    • 解决弊端—引用类型转换
  • 内部类
    • 匿名内部类

文章目录

第一章 接口

知识点–3.1 概述

目标:

  • 引用数据类型除了类其实还有接口,接下来学习接口的概述

讲解:

概述: 接口是Java语言中的一种引用类型,是方法的"集合",因此接口的内部主要就是定义方法,包含常量,抽象方法(JDK 7及之前),默认方法和静态方法(JDK 8),私有方法(jdk9)。git

接口的定义,它与定义类方式类似,可是使用 interface 关键字。它也会被编译成.class文件,但必定要明确它并非类,而是另一种引用数据类型。web

public class 类名{}–>.class面试

public interface 接口名{}->.class编程

引用数据类型:数组,类,接口。数组

接口的使用,它不能建立对象,可是能够被实现(implements ,相似于被继承)。一个实现接口的类(能够看作是接口的子类),须要实现接口中全部的抽象方法,建立该类对象,就能够调用方法了,不然它必须是一个抽象类。安全

小结:

  • 接口是java语言中的一种引用数据类型
  • 接口中的成员:
    • 常量(jdk7及其之前)
    • 抽象方法(jdk7及其之前)
    • 默认方法和静态方法(jdk8额外增长)
    • 私有方法(jdk9额外增长)
  • 定义接口使用interface关键字—接口编译后产生class文件
  • 接口不能建立对象,须要使用实现类实现接口(相似于继承),实现接口的类叫作实现类(子类)

知识点–3.2 定义格式

目标:

  • 如何定义一个接口

讲解:

格式

public interface 接口名称 {
   
   
    // 常量(jdk7新增)
    // 抽象方法(jdk7新增)
    // 默认方法(jdk8新增)
    // 静态方法(jdk8新增)
    // 私有方法(jdk9新增)
}

案例

//函数的默认是public abstract,变量的类型是public static final,对于缺乏的修饰符用默认的(对于prive则不能用abstract修饰,由于子类不能实现private函数,default和static只能拥一个,不能两个都用)。
public interface IA {
   
   
    // 常量(jdk7及其之前) 使用public static final关键字修饰,这三个关键字均可以省略
    public static final int NUM1 = 10;
    int NUM2 = 20;

    // 抽象方法(jdk7及其之前) 使用public abstract关键字修饰,这2个关键字能够省略一个,也能够都省略
    public abstract void method1();
    void method2();

    // 默认方法(jdk8) 使用public default关键字修饰,public能够省略,default不能够省略,接口提供一个默认实现的方法,而且不强制实现类重写此方法
    public default void method3(){
   
   
        System.out.println("默认方法 method3");
    }

    // 静态方法(jdk8) 使用public static关键字修饰,public能够省略,static不能够省略
    public static void method4(){
   
   
        System.out.println("静态方法 method4");
    }
    
    // 私有方法(jdk9) 使用private关键字修饰,private不能够省略
    private static void method5(){
   
   
        System.out.println("私有静态方法  method5");
    }

    //private不能使用abstract修饰,由于子类不能实现private的函数
    private void method6(){
   
   
        System.out.println("私有非静态方法  method6");
    }
}

知识点–3.3 实现接口

目标

  • 掌握什么是实现,以及如何实现接口

讲解

实现概述

类与接口的关系为实现关系,即**类实现接口,该类能够称为接口的实现类,也能够称为接口的子类。**实现的动做相似继承,格式相仿,只是关键字不一样,实现使用 implements关键字。ide

实现格式
  • 类能够实现一个接口,也能够同时实现多个接口。svg

    • 类实现接口后,必须重写接口中全部的抽象方法,不然该类必须是一个“抽象类”。

      public interface IA{
             
             
          public void show1();
      }
      public interface IB{
             
             
          public void show2();
      }
      public class Zi implements IA ,IB{
             
             
          public void show1(){
             
             
          }
          public void show2(){
             
             
          }
      }
      
  • 类能够在“继承一个类”的同时,实现一个、多个接口;

    public class Fu{
         
         }
    public interface IA{
         
         }
    public interface IB{
         
         }
    public class Zi extends Fu implements IA,IB{
         
         //必定要先继承,后实现
    }
    
    

知识点–3.4接口中成员的访问特色

目标

  • 掌握接口中成员访问特色

讲解

接口中成员访问特色概述
  接口中成员的访问特色:
                接口中的常量: 主要是供接口直接使用
                接口中的抽象方法: 供实现类重写的
                接口中的默认方法: 供实现类继承的(实现类中能够直接调用,实现类对象也能够直接调用)
                接口中的静态方法: 只供接口直接调用,实现类继承不了
                接口中的私有方法: 只能在接口中直接调用,实现类继承不了
案例演示
public interface IA {
   
   
    // 常量
    public static final int NUM = 10;

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

    // 默认方法
    public default void method2(){
   
   
        //method4();
        //method5();
        System.out.println("IA 接口中的默认方法method2");
    }

    // 静态方法
    public static void method3(){
   
   
        //method5();
        System.out.println("IA 接口中的静态方法method3");
    }

    // 私有方法
    private void method4(){
   
   
        System.out.println("IA 接口中的私有方法method4");
    }

    private static void method5(){
   
   
        System.out.println("IA 接口中的私有方法method5");
    }
}



public class Imp implements IA {
   
   
    // 重写接口的抽象方法
    @Override
    public void method1() {
   
   
        System.out.println("实现类重写IA接口中的抽象方法");
    }

    // 重写接口的默认方法
    @Override
    public void method2() {
   
   
        System.out.println("实现类重写IA接口中的默认方法");
    }

}


public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            接口中成员的访问特色:
                常量:主要是供接口名直接访问
                抽象方法:就是供实现类重写
                默认方法:就是供实现类重写或者实现类对象直接调用
                静态方法: 只供接口名直接调用
                私有方法: 只能在本接口中调用

         */
        // 访问接口常量
        System.out.println(IA.NUM);// 10  推荐
        //System.out.println(Imp.NUM);// 10 不推荐 常量被实现类继承了

        // 建立实现类对象调用方法
        Imp imp = new Imp();

        // 访问抽象方法
        imp.method1();

        // 访问默认方法
        imp.method2();

        // 接口名访问静态方法
        IA.method3();
        //Imp.method3();// 编译报错,没有继承
    }
}

小结

  • 接口中成员访问特色:
    • 常量:主要是供接口名直接访问
    • 抽象类:就是用来给实现类重写的
    • 默认方法:只供实现类重写或者实现类对象直接调用
    • 静态方法:只供接口名直接调用
    • 私有方法:只能在本接口中调用

知识点–3.5 多实现时的几种冲突状况

目标

  • 理解多实现时的几种冲突状况

讲解

公有静态常量的冲突
  • 实现类不继承冲突的常量
interface A{
   
   
    public static final int NUM1 = 10;
}
interface B{
   
   
    public static final int NUM1 = 20;
    public static final int NUM2 = 30;
}
class Imp implements A,B{
   
   

}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有静态常量的冲突: 若是多个接口中有相同的常量,那么实现类就没法继承
         */
        //System.out.println(Imp.NUM1);// 编译报错,没法访问
        System.out.println(Imp.NUM2);// 30
    }
}

公有抽象方法的冲突
  • 实现类只须要重写一个
interface A{
   
   
    public abstract void method();
}
interface B{
   
   
    public abstract void method();
}
class Imp implements A,B{
   
   
    @Override
    public void method() {
   
   
        System.out.println("实现类重写");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有抽象方法的冲突:实现类只须要重写一个 
         */
    }
}

公有默认方法的冲突
  • 实现类必须重写一次最终版本
interface A{
   
   
    public default void method(){
   
   
        System.out.println("A 接口的默认方法method");
    }
}
interface B{
   
   
    public default void method(){
   
   
        System.out.println("B 接口的默认方法method");
    }
}
class Imp implements A,B{
   
   
    @Override
    public void method() {
   
   
        System.out.println("实现类重写的默认方法");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有默认方法的冲突:实现类必须重写一次最终版本 
         */
        Imp imp = new Imp();
        imp.method();
    }
}

公有静态方法的冲突
  • 静态方法是直接属于接口的,不能被继承,因此不存在冲突

    face A{
         
         
        public static void method(){
         
         
            System.out.println("A接口的静态方法method");
        }
    }
    interface B{
         
         
        public static void method(){
         
         
            System.out.println("B接口的静态方法method");
        }
    }
    class Imp implements A,B{
         
         
    
    }
    public class Test {
         
         
        public static void main(String[] args) {
         
         
            /*
                公有静态方法的冲突:静态方法是直接属于接口的,不能被继承,因此不存在冲突
             */
        }
    }
    
    
私有方法的冲突
  • 私有方法只能在本接口中直接使用,不存在冲突

小结

 多实现时的几种冲突状况:
                - 公有静态常量的冲突:实现类不继承冲突的常量
                - 公有抽象方法的冲突:实现类只须要重写一个
                - 公有默认方法的冲突:实现类必须重写一次最终版本
                - 公有静态方法的冲突:静态方法是直接属于接口的,不能被继承,因此不存在冲突
                - 私有方法的冲突:私有方法只能在本接口中直接使用,不存在冲突

知识点–3.6 接口和接口的关系

目标

  • 理解接口与接口之间的关系,以及接口继承时的冲突状况

讲解

接口与接口之间的关系
  • 接口能够“继承”自另外一个“接口”,并且能够“多继承”。

    interface IA{
         
         }
    interface IB{
         
         }
    interface IC extends IA,IB{
         
         //是“继承”,并且能够“多继承”
    }
    
接口多继承接口的冲突状况
公有静态常量的冲突
  • 子接口没法继承父接口中冲突的常量
interface A{
   
   
    public static final int NUM1 = 10;
}
interface B{
   
   
    public static final int NUM1 = 20;
    public static final int NUM2 = 30;
}
interface C extends A,B{
   
   

}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有静态常量的冲突: 子接口没法继承父接口中冲突的常量
         */
        //System.out.println(C.NUM1);// 编译报错,说明没法继承
        System.out.println(C.NUM2);// 30
    }
}

公有抽象方法冲突
  • 子接口只会继承一个有冲突的抽象方法
interface A{
   
   
    public abstract void method();
}
interface B{
   
   
    public abstract void method();
}
interface C extends A,B{
   
   

}
class Imp implements C{
   
   
    @Override
    public void method() {
   
   
        System.out.println("实现接口的抽象方法");
    }
}

public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有抽象方法的冲突:子接口只会继承一个有冲突的抽象方法
         */
        Imp imp = new Imp();
        imp.method();
    }
}

公有默认方法的冲突
interface A{
   
   
    public default void method(){
   
   
        System.out.println("A 接口中的默认方法method");
    }
}
interface B{
   
   
    public default void method(){
   
   
        System.out.println("B 接口中的默认方法method");
    }
}

interface C extends A,B{
   
   

    @Override
    public default void method() {
   
   
        System.out.println("重写父接口中的method方法");
    }
}

class Imp implements C{
   
   

}

public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有默认方法的冲突:子接口中必须重写一次有冲突的默认方法
            面试题:
                实现类重写接口中的默认方法,不须要加default
                子接口重写父接口中的默认方法,必须加default
         */
        Imp imp = new Imp();
        imp.method();// 重写父接口中的method方法
    }
}

公有静态方法和私有方法
  • 不冲突,由于静态方法是直接属于接口的,只能使用本接口直接访问,而私有方法只能在接口中访问,也没有冲突
interface A{
   
   
    public static void method(){
   
   
        System.out.println("A 接口的静态方法method");
    }
}
interface B{
   
   
    public static void method(){
   
   
        System.out.println("B 接口的静态方法method");
    }
}
interface C extends A,B{
   
   
    
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有静态方法的冲突: 不存在冲突,由于静态方法是直接属于接口的,只供本接口直接调用
         */
        //C.method();// 编译报错,由于没有继承
    }
}

小结

  - 接口与接口之间的关系: 继承关系
                    单继承: A接口继承B接口
                    多继承: A接口同时继承B接口,C接口,...
                    多层继承: A接口继承B接口,B接口,继承C接口
                    格式:
                        public interface 接口名 extends 接口名1,接口名2,...{
   
   
                            
                        }

- 接口多继承时的冲突状况
    - 公有静态常量的冲突:子接口没法继承父接口中冲突的常量
    - 公有抽象方法的冲突:子接口只会继承一个有冲突的抽象方法
    - 公有默认方法的冲突:子接口中必须重写一次有冲突的默认方法(注意要加default)
    - 公有静态方法和私有方法的冲突:
		不冲突,由于静态方法是直接属于接口的,只能使用本接口直接访问,而私有方法只能在接口中访问,也没有冲突

面试题:
     实现类重写接口中的默认方法,不须要加default
      子接口重写父接口中的默认方法,必须加default

知识点–3.7 实现类继承父类又实现接口时的冲突

目标

  • 实现类继承父类又实现接口时的冲突
父类和接口的公有静态常量的冲突
  • 子类没法继承有冲突的常量
class Fu{
   
   
    public static final int NUM1 = 10;
    public static final int NUM2 = 100;
}
interface A{
   
   
    public static final int NUM1 = 20;

}
class Zi extends Fu implements A{
   
   

}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有静态常量的冲突:子类没法继承有冲突的常量
         */
        //System.out.println(Zi.NUM1);// 编译报错
        System.out.println(Zi.NUM2);

    }
}

父类和接口的抽象方法冲突
abstract class Fu{
   
   
    public abstract void method();
}
interface A{
   
   
    public abstract void method();
}
class Zi extends Fu implements A{
   
   
    @Override
    public void method() {
   
   
        System.out.println("Zi 重写有冲突的抽象方法");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有抽象方法的冲突:子类必须重写一次有冲突的抽象方法
         */
        Zi zi = new Zi();
        zi.method();
    }
}
父类和接口的公有默认方法的冲突
  • 只会访问父类的
class Fu{
   
   
    public void method(){
   
   
        System.out.println("Fu 类中的默认方法method");
    }
}
interface A{
   
   
    public default void method(){
   
   
        System.out.println("A 接口中的默认方法method");
    }
}
class Zi extends Fu implements A{
   
   

}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有默认方法的冲突:只会访问父类的
         */
        Zi zi = new Zi();
        zi.method();// Fu 类中的默认方法method
    }
}

父类和接口的公有静态方法
  • 只会访问父类的静态方法
class Fu{
   
   
    public static void method(){
   
   
        System.out.println("Fu 类中的静态方法method");
    }
}
interface A{
   
   
    public static void method(){
   
   
        System.out.println("A 接口中的静态方法method");
    }
}
class Zi extends Fu implements A{
   
   
    
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            公有静态方法的冲突:只会访问父类的静态方法
         */
        Zi.method();
    }
}

父类和接口的私有方法
  • 不存在冲突

小结

  实现类继承父类又实现接口时的冲突:
                - 公有静态常量的冲突:子类没法继承有冲突的常量
                - 公有抽象方法的冲突:子类必须重写一次有冲突的抽象方法
                - 公有默认方法的冲突:只会访问父类的
                - 公有静态方法的冲突:只会访问父类的静态方法
                - 私有方法的冲突: 不存在冲突

实操–3.8 抽象类和接口的练习

需求:

经过实例进行分析和代码演示抽象类和接口的用法。

一、举例:

​ 犬: —抽象父类

​ 行为:吼叫;吃饭;

​ 缉毒犬:继承犬类,实现缉毒接口

​ 行为:吼叫;吃饭;缉毒;

​ 缉毒接口:

​ 缉毒

  • 若是一个父类中的某个方法,全部子类都有不一样的实现,那么该方法就应该定义成抽象方法,因此该父类就是抽象类 (父类通常都是抽象类)
  • 若是某个功能是一个类额外增长的,那么就能够把这个额外的功能定义到接口中,再这个类去实现

分析:

​ 因为犬分为不少种类,他们吼叫和吃饭的方式不同,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。当描述行为时,行为的具体动做不能明确,这时,能够将这个行为写为抽象行为,那么这个类也就是抽象类。

​ 但是有的犬还有其余额外功能,而这个功能并不在这个事物的体系中 , 例如 : 缉毒犬。缉毒的这个功能有好多种动物都有 , 例如 : 缉毒猪 , 缉毒鼠。咱们能够将这个额外功能定义接口中 ,让缉毒犬继承犬且实现缉毒接口 , 这样缉毒犬既具有犬科自身特色也有缉毒功能。

  • 额外的功能—> 在接口中定义,让实现类实现
  • 共性的功能—> 在父类中定义,让子类继承

实现:

// 抽象父类
public abstract class Dog {
   
   
    public abstract void houJiao();
    public abstract void eat();
}


public interface JiDu {
   
   
    public abstract void jiDu();
}


public class JiDuDog extends Dog implements JiDu{
   
   
    @Override
    public void houJiao() {
   
   
        System.out.println("缉毒犬找到了毒品,开始吼叫,汪汪汪....");
    }

    @Override
    public void eat() {
   
   
        System.out.println("缉毒以前,开始吃骨头...");
    }

    @Override
    public void jiDu() {
   
   
        System.out.println("吃完东西后,开始使用鼻子查找毒品....");
    }
}


public class Test {
   
   
    public static void main(String[] args) {
   
   
        // 建立缉毒狗对象
        JiDuDog jd = new JiDuDog();
        jd.eat();
        jd.jiDu();
        jd.houJiao();
    }
}

小结:

  • 额外的功能—> 在接口中定义,让实现类实现
    • 若是能够肯定的通用功能,使用默认方法
    • 若是不能肯定的功能,使用抽象方法
  • 共性的功能—> 在父类中定义,让子类继承
    • 若是能够肯定的通用功能,使用默认方法
    • 若是不能肯定的功能,使用抽象方法

第二章 多态

知识点-- 概述

目标:

  • 了解什么是多态,以及造成多态的条件

讲解:

引入

多态是继封装、继承以后,面向对象的第三大特性

生活中,好比跑的动做,小猫、小狗和大象,跑起来是不同的。再好比飞的动做,昆虫、鸟类和飞机,飞起来也是不同的。可见,同一行为,经过不一样的事物,能够体现出来的不一样的形态。多态,描述的就是这样的状态。

定义

  • 多态是指同一行为,对于不一样的对象具备多个不一样表现形式。
  • 程序中多态: 是指同一方法,对于不一样的对象具备不一样的实现.

前提条件【重点】

  1. 继承或者实现【二选一】
  2. 父类引用指向子类对象\接口引用指向实现类对象【格式体现】
  3. 方法的重写【意义体现:不重写,无心义】

小结:

  • 多态: 是指同一行为,对于不一样的对象具备多个不一样表现形式。
  • 条件:
    • 继承或者实现
    • 父类引用指向子类的对象\接口引用指向实现类对象
    • 方法的重写

知识点-- 实现多态

目标:

  • 如何实现多态

讲解:

多态的体现:父类的引用指向它的子类的对象

父类类型 变量名 = new 子类对象;
变量名.方法名();

父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

class Animal{
   
   
    public void eat(){
   
   
        System.out.println("吃东西");
    }
}

class Dog extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("狗吃骨头...");
    }
}

class Cat extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("猫吃鱼...");
    }
}

public class Test1 {
   
   
    public static void main(String[] args) {
   
   
        /*
            多态: 同一种行为,不一样的事物具备不一样的表现形态
            实现多态:
                1.继承或者实现
                2.父类引用指向子类对象\接口引用指向实现类对象
                3.方法重写
         */
        // 父类引用指向子类对象
        Animal anl = new Dog();// 多态
        anl.eat();// 狗吃骨头...

        Animal anl1 = new Cat();
        anl1.eat();// 猫吃鱼...
    }
}

小结:

  • 父类的引用指向子类的对象

知识点-- 多态时访问成员的特色

目标

  • 掌握多态时访问成员的特色

讲解:

  • 直接看演示代码:
class Animal{
   
   
    int num = 10;
    public void method1(){
   
   
        System.out.println("Animal 非静态method1方法");
    }
    public static void method2(){
   
   
        System.out.println("Animal 静态method2方法");
    }
}
class Dog extends Animal{
   
   
    int num = 20;

    public void method1(){
   
   
        System.out.println("Dog 非静态method1方法");
    }

    public static void method2(){
   
   
        System.out.println("Dog 静态method2方法");
    }
}

public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            多态时访问成员的特色:
                成员变量:访问的是子类
                成员方法:
                    非静态方法:访问的是子类
                    静态方法: 访问的是父类
         */
        // 父类的引用指向子类的对象
        Animal anl = new Dog();
        System.out.println(anl.num);// 10

        anl.method1();// Dog 非静态method1方法

        anl.method2();// Animal 静态method2方法

    }
}

知识点-- 多态的形式

目标:

  • 多态的几种表现形式

讲解:

  • 多态的表现形式:

    • 普通父类多态

      public class Fu{
             
             }
      public class Zi extends Fu{
             
             }
      public class Demo{
             
             
          public static void main(String[] args){
             
             
              Fu f = new Zi();//左边是一个“父类”
          }
      }
      
      
    • 抽象父类多态

      public abstract class Fu{
             
             }
      public class Zi extends Fu{
             
             }
      public class Demo{
             
             
          public static void main(String[] args){
             
             
              Fu f = new Zi();//左边是一个“父类”
          }
      }
      
      
    • 父接口多态

    public interface A{
         
         }
    public class AImp implements A{
         
         }
    public class Demo{
         
         
        public static void main(String[] args){
         
         
            A a = new AImp();//左边是一个“接口”
        }
    }
    

知识点-- 多态的应用场景:

目标:

  • 掌握多态在开发中的应用场景

讲解:

多态的几种应用场景:

  • 变量多态 -----> 意义不大

    class Animal{
         
         
        public void eat(){
         
         
            System.out.println("吃东西...");
        }
    }
    class Dog extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("狗吃骨头");
        }
    }
    class Cat extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("猫吃鱼");
        }
    }
    public class Test {
         
         
        public static void main(String[] args) {
         
         
            // 变量多态: 父类类型的变量指向子类类型的对象
            // 若是变量的类型为父类类型,该变量就能够接收该父类类型的对象或者其全部子类对象
            Animal anl = new Dog();
            anl.eat();
    
            anl = new Cat();
            anl.eat();
        }
    }
    
    
  • 形参多态----> 经常使用

    class Animal{
         
         
        public void eat(){
         
         
            System.out.println("吃东西...");
        }
    }
    class Dog extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("狗吃骨头");
        }
    }
    class Cat extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("猫吃鱼");
        }
    }
    public class Test {
         
         
        public static void main(String[] args) {
         
         
            // 形参多态:参数类型为父类类型,该参数就能够接收该父类类型的对象或者其全部子类对象
            Dog d = new Dog();
            method(d);
    
            System.out.println("===============================");
    
            Cat c = new Cat();
            method(c);
        }
    
        // 需求: 定义一个方法,带有一个参数,该参数能够接收Animal类对象以及Animal类的全部子类对象
        // method(d); ====实参赋值给形参的时候==> Animal anl = new Dog();
        // method(c); ====实参赋值给形参的时候==> Animal anl = new Cat();
        public static void method(Animal anl){
         
         
            anl.eat();
        }
    
    }
    
    
  • 返回值多态—> 经常使用

    class Animal{
         
         
        public void eat(){
         
         
            System.out.println("吃东西...");
        }
    }
    class Dog extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("狗吃骨头");
        }
    }
    class Cat extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("猫吃鱼");
        }
    }
    public class Test {
         
         
        public static void main(String[] args) {
         
         
            // 返回值多态:若是返回值类型为父类类型,那么就能够返回该父类类型的对象或者其全部子类对象
            Animal anl = method();
            anl.eat();
        }
    
        public static Animal method(){
         
         
            //return new Animal();
            //return new Dog();
            return new Cat();
        }
    
        public static Animal method1(){
         
         
            if (1==1){
         
         
                // 条件1成立
                return new Animal();
            }else if (2==2){
         
         
                // 条件2成立
                return new Dog();
            }else{
         
         
                // 不然
                return new Cat();
            }
        }
    }
    
    

小结:

变量多态 -----> 意义不大:若是变量的类型为父类类型,该变量就能够接收该父类类型的对象或者其全部子类对象
形参多态----> 经常使用: 若是参数类型为父类类型,该参数就能够接收该父类类型的对象或者其全部子类对象
返回值多态---> 经常使用: 若是返回值类型为父类类型,那么就能够返回该父类类型的对象或者其全部子类对象

知识点-- 多态的好处和弊端

目标:

  • 实际开发的过程当中,父类类型做为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。但有好处也有弊端

讲解:

  • 好处
    • 提升了代码的扩展性
  • 弊端
    • 多态的状况下,只能调用父类的共性内容,不能调用子类的特有内容。
  • 示例代码
class Animal{
   
   
    public void eat(){
   
   
        System.out.println("吃东西...");
    }
}
class Dog extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("狗吃骨头...");
    }

    // 特有的功能
    public void lookHome(){
   
   
        System.out.println("狗在看家...");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            多态的好处和弊端:
                好处:提升代码的复用性
                弊端:没法访问子类独有的方法或者成员变量,由于多态成员访问的特色是,编译看父类
         */
        // 父类的引用指向子类的对象
        Animal anl = new Dog();
        anl.eat();
        //anl.lookHome();// 编译报错,由于多态成员访问的特色是,编译看父类,而父类中没有子类独有的功能
    }
}

小结:

多态的好处和弊端:
      好处:提升代码的复用性
      弊端:没法访问子类独有的方法或者成员变量,由于多态成员访问的特色是,编译看父类

知识点-- 引用类型转换

目标:

  • 向上转型与向下转型,instanceof关键字

讲解:

向上转型
  • 子类类型向父类类型向上转换的过程,这个过程是默认的。

     Aniaml anl = new Cat();  
    
向下转型
  • 父类类型向子类类型向下转换的过程,这个过程是强制的。

     Aniaml anl = new Cat();  
     Cat c = (Cat)anl;//向下转型
    
  • 示例代码

    class Animal{
         
         
        public void eat(){
         
         
            System.out.println("吃东西...");
        }
    }
    class Dog extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("狗吃骨头...");
        }
    
        // 特有的功能
        public void lookHome(){
         
         
            System.out.println("狗在看家...");
        }
    }
    class Cat extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("猫吃鱼...");
        }
    }
    
    class Person{
         
         }
    public class Test {
         
         
        public static void main(String[] args) {
         
         
            /*
                引用类型转换:
                    向上转型:子类类型向父类类型向上转换的过程,这个过程是默认\自动的。
                    向下转型:父类类型向子类类型向下转换的过程,这个过程是强制\手动的。
                            格式: 子类类型 对象名 = (子类类型)父类类型的变量;
                            注意:
                                1.向下转型的时候:右边父类类型的变量必定要指向要转型的子类类型的对象
                                2.无论是向上转型仍是向下转型,必定知足父子类关系或者实现关系
             */
            // 向上转型:
            Animal anl = new Dog();
    
            // 向下转型:
            Dog dog = (Dog)anl;
    
            System.out.println("===================================");
            // 注意:右边父类类型的变量必定要指向要转型的子类类型的对象
            //Animal anl1 = new Animal();
            //Dog d1 = (Dog)anl1;// 运行报错,类型转换异常ClassCastException
    
    
            //Animal anl2 = new Cat();
            //Dog d2 = (Dog)anl2;// 运行报错,类型转换异常ClassCastException
    
            //Animal anl3 = new Person();// 编译报错,由于Animal和Person不是父子关系
            //Animal anl3 = (Animal) new Person();// 编译报错,由于Animal和Person不是父子关系
    
        }
    }
    
    
instanceof关键字
  • 向下强转有风险,最好在转换前作一个验证 :

  • 格式:

    变量名 instanceof 数据类型 
    若是变量属于该数据类型,返回true。
    若是变量不属于该数据类型,返回falseif( anl instanceof Cat){
         
         //判断anl是否能转换为Cat类型(anl是不是Cat类或Cat类的子类),若是能够返回:true,不然返回:false
        Cat c = (Cat)anl;//安全转换
    }
    
  • 示例代码

    class Animal{
         
         
        public void eat(){
         
         
            System.out.println("吃东西...");
        }
    }
    class Dog extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("狗吃骨头...");
        }
    
        // 特有的功能
        public void lookHome(){
         
         
            System.out.println("狗在看家...");
        }
    }
    class Cat extends Animal{
         
         
        @Override
        public void eat() {
         
         
            System.out.println("猫吃鱼...");
        }
    }
    public class Test {
         
         
        public static void main(String[] args) {
         
         
            /*
                instanceof关键字:
                    为何要有instanceof关键字?
                        由于在引用类型转换的时候很容易出现类型转换异常,因此为了提升代码的严谨性,转型以前得先判断一下
                    怎么使用instanceof关键字判断呢?
                        if(变量名 instanceof 数据类型){
    
                        }
                     执行:
                        判断前面变量指向的对象类型是不是后面的数据类型:
                            若是前面变量指向的对象类型是属于后面的数据类型,那么就返回true
                            若是前面变量指向的对象类型不是属于后面的数据类型,那么就返回false
             */
            // 向上转型
            Animal anl = new Cat();
    
            // 向下转型
            //Dog  d = (Dog)anl;// 运行的时候会出现类型转换异常
            // 先判断,再转型
            if (anl instanceof Dog){
         
         
                Dog  d = (Dog)anl;
            }
    
            System.out.println("正常结束");
        }
    }
    
    

小结

 引用类型转换:
	向上转型:子类类型向父类类型向上转换的过程,这个过程是默认\自动的。
    向下转型:父类类型向子类类型向下转换的过程,这个过程是强制\手动的。
        格式: 子类类型 对象名 = (子类类型)父类类型的变量;
注意:
	1.向下转型的时候:右边父类类型的变量必定要指向要转型的子类类型的对象
    2.无论是向上转型仍是向下转型,必定知足父子类关系或者实现关系

instanceof关键字:
	if(变量名 instanceof 数据类型){
   
   }
    若是变量属于该数据类型,返回true。
    若是变量不属于该数据类型,返回false

解决多态的弊端

class Animal{
   
   
    public void eat(){
   
   
        System.out.println("吃东西...");
    }
}
class Dog extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("狗吃骨头...");
    }

    // 特有的功能
    public void lookHome(){
   
   
        System.out.println("狗在看家...");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            解决多态的弊端:
                弊端:没法访问子类独有的方法或者成员变量,由于多态成员访问的特色是,编译看父类
         */
        // 父类的引用指向子类的对象
        Animal anl = new Dog();// 向上转型
        anl.eat();// 狗吃骨头...

        //anl.lookHome();// 编译报错,由于多态成员访问的特色是,编译看父类,而父类中没有子类独有的功能

        // 先判断,后转型
        if (anl instanceof Dog){
   
   
            Dog d = (Dog)anl;// 向下转型
            d.lookHome();// 狗在看家...
        }

        System.out.println("正常结束");
    }
}

多态的应用场景综合案例

class Animal{
   
   
    public void eat(){
   
   
        System.out.println("吃东西...");
    }
}
class Dog extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("狗吃骨头...");
    }

    // 特有的功能
    public void lookHome(){
   
   
        System.out.println("狗在看家...");
    }
}
class Cat extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("猫吃鱼...");
    }
    // 特有的功能
    public void catchMouse(){
   
   
        System.out.println("猫抓老鼠...");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        Dog d = new Dog();
        method(d);

        System.out.println("==========================");

        Cat c = new Cat();
        method(c);
    }

    // 形参多态: 若是父类类型做为方法的形参类型,那么就能够接收该父类类型的对象或者其全部子类的对象
    public static void method(Animal anl){
   
   
        anl.eat();
        //anl.lookHome();// 编译报错
        // anl.catchMouse();// 编译报错
        if (anl instanceof Dog){
   
   
            Dog d = (Dog)anl;// 向下转型 Dog类型
            d.lookHome();
        }

        if (anl instanceof Cat){
   
   
            Cat c = (Cat)anl;// 向下转型 Cat类型
            c.catchMouse();
        }

    }
}

第三章 内部类

知识点-- 内部类

目标:

  • 内部类的概述

讲解:

什么是内部类

将一个类A定义在另外一个类B里面,里面的那个类A就称为**内部类,B则称为外部类**。

成员内部类

  • 成员内部类 :定义在类中方法外的类。

定义格式:

class 外部类 {
   
   
    class 内部类{
   
   

    }
}

在描述事物时,若一个事物内部还包含其余事物,就可使用内部类这种结构。好比,汽车类Car 中包含发动机类Engine ,这时,Engine 就可使用内部类来描述,定义在成员位置。

代码举例:

class Car {
   
    //外部类
    class Engine {
   
    //内部类

    }
}

访问特色

  • 内部类能够直接访问外部类的成员,包括私有成员。
  • 外部类要访问内部类的成员,必需要创建内部类的对象。

建立内部类对象格式:

外部类名.内部类名 对象名 = new 外部类型().new 内部类型()

访问演示,代码以下:

// 外部类
public class Body {
   
   

    public void methodW1(){
   
   
        // 访问内部类的成员
        //Body.Heart bh = new Body().new Heart();
        Heart bh = new Heart();
        System.out.println(bh.numN);// 10
        bh.methodN1();// 内部类的成员方法 methodN1
    }

    // 成员变量
    private int numW = 100;

    // 成员方法
    private void methodW2(){
   
   
        System.out.println("外部类的成员方法 methodW2");
    }


    // 内部类
    public class Heart{
   
   
        // 成员变量
        int numN = 10;

        // 成员方法
        public void methodN1(){
   
   
            System.out.println("内部类的成员方法 methodN1");
        }

        public void methodN2(){
   
   
            // 访问外部类的成员
            System.out.println(numW);
            methodW2();
        }
    }


}


public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            - 什么是内部类:将一个类A定义在另外一个类B里面,里面的那个类A就称为内部类,外面的那个B类则称为外部类。
            - 成员内部类的格式:
                 public class 外部类{
                     public class 内部类{

                    }
                 }
            - 成员内部类的访问特色:
                在其余类中,访问内部类的成员,得先建立内部类对象:
                    外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
                在外部类中,访问内部类的成员,得先建立内部类对象:
                    外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
                    内部类名 对象名 = new 内部类名();

                在内部类中,能够直接访问外部类的一切成员(包含私有的):

         */
        // 建立内部类的对象
        Body.Heart bh = new Body().new Heart();
        System.out.println(bh.numN);// 10
        bh.methodN1();// 内部类的成员方法 methodN1

        System.out.println("=======================");
        // 建立外部类对象
        Body b = new Body();
        b.methodW1();

        System.out.println("=======================");
        bh.methodN2();// 100    外部类的成员方法 methodW2

    }
}

小结:

内部类:将一个类A定义在另外一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
成员内部类的格式:
    public class 外部类名{
   
   
         public class 内部类名{
   
   

        }
    }
成员内部类的访问特色:
    - 内部类能够直接访问外部类的成员,包括私有成员。
    - 外部类要访问内部类的成员,必需要创建内部类的对象。

成员内部类的建立方式:
    外部类名.内部类名 对象名 = new 外部类名().new 内部类名();

知识点-- 匿名内部类

目标:

  • 匿名内部类

讲解:

概述

  • 匿名内部类 :是内部类的简化写法。它的本质是一个带具体实现的 父类或者父接口的 匿名的 子类对象

代码一

abstract class Animal{
   
   
    public abstract void eat();
}
class Dog extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("狗吃骨头...");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            匿名内部类:
                概述:本质其实就是一个类的匿名子类的对象
                做用:就是用来简化代码的,没有其余的功能
                格式:
                    new 类名(){
                        实现抽象方法
                    };
         */
        // 需求:调用Animal类的eat方法
        // 1.建立一个子类继承Animal类
        // 2.在子类中重写eat抽象方法
        // 3.建立子类对象
        // 4.使用子类对象调用eat方法
        Dog d = new Dog();// 建立Animal子类对象
        d.eat();// d---->是Animal类的子类的对象
        // 问题:以上4步一步都不能少,有点麻烦,是否能够简化代码?
        // 解决:匿名内部类能够简化代码,由于它能够不建立子类的状况下,直接获得一个类的子类对象

        System.out.println("==========================");
        // 建立Animal子类对象<=====>Animal类的匿名内部类
        // 父类的引用指向子类的对象
        Animal anl = new Animal() {
   
   
            @Override
            public void eat() {
   
   
                System.out.println("匿名内部类");
            }
        };// 是Animal类的子类的对象
        anl.eat();
    }
}

代码二

interface A{
   
   
    public abstract void show();
}
class Imp implements A{
   
   
    public void show(){
   
   
        System.out.println("实现类实现show方法");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            匿名内部类:
                概述:本质是一个接口的匿名实现类的对象
                格式:
                    new 接口名(){
                        实现抽象方法
                    };
         */
        // 需求:调用A接口的show方法
        // 1.建立实现类实现A接口
        // 2.在实现类中重写show方法
        // 3.建立实现类对象
        // 4.使用实现类对象调用show方法
        Imp imp = new Imp();// imp就是接口的实现类的对象
        imp.show();

        System.out.println("==============================");
        // 简化: 匿名内部类
        A a = new A() {
   
   
            @Override
            public void show() {
   
   
                System.out.println("匿名内部类");
            }
        };
        a.show();
    }
}

小结

对于类:
	概述:本质其实就是一个类的匿名子类的对象
    格式:
        new 类名(){
   
   
            实现抽象方法
        };
对于接口:
	概述:本质是一个接口的匿名实现类的对象
    格式:
        new 接口名(){
   
   
            实现抽象方法
        };
匿名内部类做用:就是用来简化代码的,没有其余的功能
使用场景:
	若是方法的形参类型为抽象类或者接口类型,那么为了简化代码,能够直接传入该抽象类或者接口的匿名内部类
  • 补充

      // 匿名子类的匿名对象
            new Imp().show();// 实现类的匿名对象调用show方法
            new A() {
         
         
                @Override
                public void show() {
         
         
                    System.out.println("匿名内部类");
                }
            }.show();// 匿名实现类的匿名对象调用show方法
    

第四章 引用类型使用小结

目标

​ 实际的开发中,引用类型的使用很是重要,也是很是广泛的。咱们能够在理解基本类型的使用方式基础上,进一步去掌握引用类型的使用方式。基本类型能够做为成员变量、做为方法的参数、做为方法的返回值,那么固然引用类型也是能够的。在这咱们使用两个例子 , 来学习一下。

讲解

6.1 类名做为方法参数和返回值

class Person{
   
   
    public String name;
    public int age;

    public Person(String name, int age) {
   
   
        this.name = name;
        this.age = age;
    }

    public void show(){
   
   
        System.out.println(name+","+age);
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        /*
            类名做为方法参数和返回值:
         */
        // 建立Person
        Person p = new Person("冰冰",18);
        method1(p);
        System.out.println("=========================================");
        // 调用method2;
        Person person = method2(p);
        person.show();// 冰冰,20
    }

    // 类做为方法的参数类型
    public static void method1(Person p){
   
   
        p.show();// 冰冰,18
    }

    // 类做为方法的参数类型和返回值类型
    public static Person method2(Person p){
   
   
        p.age = 20;// 把age改成20
        return p;
    }
}

6.2 抽象类做为方法参数和返回值

  • 抽象类做为形参:表示能够接收任何此抽象类的"子类对象"做为实参;
  • 抽象类做为返回值:表示"此方法能够返回此抽象类的任何子类对象";
abstract class Animal{
   
   
    public abstract void eat();
}
class Dog extends Animal{
   
   
    @Override
    public void eat() {
   
   
        System.out.println("狗吃骨头...");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        // 调用method1,就得传入Animal抽象类的子类对象
        method1(new Dog());

        System.out.println("========================");
        // 调用method1,就得传入Animal抽象类的子类对象
        method1(new Animal() {
   
   
            @Override
            public void eat() {
   
   
                System.out.println("匿名内部类的方式...");
            }
        });

        System.out.println("========================");
        // 调用method2方法,会返回一个Animal类的子类对象
        //Animal anl = method2();
        Dog d = (Dog)method2();
    }

    // 抽象类做为方法参数类型
    public static void method1(Animal anl){
   
   
        anl.eat();
    }

    // 抽象类做为方法返回值类型
    public static Animal method2(){
   
   
        return new Dog();
    }
}

6.3 接口做为方法参数和返回值

  • 接口做为方法的形参:【同抽象类】
  • 接口做为方法的返回值:【同抽象类】
interface A{
   
   
    void show();
}
class Imp implements A{
   
   
    public void show(){
   
   
        System.out.println("实现类的方式实现show方法");
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        // 接口做为方法参数和返回值
        // 调用method1方法,就得传入A接口的实现类对象
        method1(new Imp());

        System.out.println("===================");

        // 调用method1方法,就得传入A接口的匿名内部类
        method1(new A() {
   
   
            @Override
            public void show() {
   
   
                System.out.println("匿名内部类的方式实现show方法");
            }
        });

        System.out.println("===================");

        // 调用method2方法,就会返回A接口的实现类对象
        //A a = method2();
        Imp imp = (Imp) method2();


    }

    // 接口做为方法参数
    public static void method1(A a){
   
   
        a.show();
    }

    // 接口做为方法返回值
    public static A method2(){
   
   
        return new Imp();
    }
}

6.4 类名做为成员变量

​ 咱们每一个人(Person)都有一个身份证(IDCard) , 为了表示这种关系 , 就须要在Person中定义一个IDCard的成员变量。定义Person类时,代码以下:

class Person {
   
   
    String name;//姓名
    int age;//年龄
}

​ 使用String 类型表示姓名 , int 类型表示年龄。其实,String自己就是引用类型,咱们每每忽略了它是引用类型。若是咱们继续丰富这个类的定义,给Person 增长身份证号 , 身份证签发机关等属性,咱们将如何编写呢?这时候就须要编写一个IDCard类了

修改Person类:

class Person{
   
   
    String name;// 引用数据类型定义成员变量  String类
    int age;// 基本类型定义成员变量
    IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
   
   
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }
    // ...
}
class IdCard{
   
   
    String idNum;// 身份证号码
    String address;// 地址

    public IdCard(String idNum, String address) {
   
   
        this.idNum = idNum;
        this.address = address;
    }
    // ....
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        // 建立IdCard对象
        IdCard idCard = new IdCard("440330200010101919","广东省深圳市宝安区公安局");
        // 建立Person对象
        Person p = new Person("张三",18,idCard);
        System.out.println(p.name+","+p.age+","+p.idCard.idNum+","+p.idCard.address);// java支持链式编程
    }
}

类做为成员变量时,对它进行赋值的操做,实际上,是赋给它该类的一个对象。同理 , 接口也是如此 , 例如咱们笔记本案例中使用usb设备。在此咱们只是经过小例子 , 让你们熟识下引用类型的用法 , 后续在我们的就业班学习中 , 这种方式会使用的不少。

6.5 抽象类做为成员变量

  • 抽象类做为成员变量——为此成员变量赋值时,能够是任何它的子类对象
abstract class Pet{
   
   
    String name;

    public Pet(String name) {
   
   
        this.name = name;
    }
}
class Dog extends Pet{
   
   

    public Dog(String name) {
   
   
        super(name);
    }
}
class Person{
   
   
    String name;
    int age;
    Pet pet;

    public Person(String name, int age, Pet pet) {
   
   
        this.name = name;
        this.age = age;
        this.pet = pet;
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        // 抽象类做为成员变量:传入抽象类的子类对象
        Pet pet = new Dog("旺财");
        Person p = new Person("张三",18,pet);
        System.out.println(p.name);
        System.out.println(p.age);
        System.out.println(p.pet.name);

    }
}

6.6 接口做为成员变量

  • 接口类型做为成员变量——【同抽象类】
abstract interface Pet{
   
   

}
class Dog implements Pet{
   
   


}
class Person{
   
   
    String name;
    int age;
    Pet pet;

    public Person(String name, int age, Pet pet) {
   
   
        this.name = name;
        this.age = age;
        this.pet = pet;
    }
}
public class Test {
   
   
    public static void main(String[] args) {
   
   
        // 接口做为成员变量:传入接口的实现类对象
        Pet pet = new Dog();
        Person p = new Person("张三",18,pet);
        System.out.println(p.name);
        System.out.println(p.age);
        System.out.println(p.pet);

    }
}

英雄: name,皮肤,法术(接口)

小结

  	  - 类名做为方法参数和返回值:能够直接传入该类的对象;返回该类的对象
      - 抽象类做为方法参数和返回值:只能传入该类的子类对象;返回该类的子类对象
      - 接口做为方法参数和返回值:只能传入该接口的实现类对象;返回该接口的实现类对象
          传递的都是地址值,返回的也是地址值
          
      - 类做为成员变量    : 赋该类的对象
      - 抽象类做为成员变量 ; 赋该类的子类对象
      - 接口做为成员变量   : 赋该接口的实现类对象