一、接口

1.概述

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

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

public class 类名.java–>.class

public interface 接口名.java–>.class

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

接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

接口中成员

  • 常量(jdk7及其以前)
  • 抽象方法(jdk7及其以前)
  • 默认方法(jdk8额外增加)
  • 静态方法(jdk8额外增加)
  • 私有方法(jdk9额外增加)

2.定义格式

  • 格式:
public interface 接口名称 {
    - 常量(jdk7及其以前)
    - 抽象方法(jdk7及其以前)
    - 默认方法(jdk8额外增加)
    - 静态方法(jdk8额外增加)
    - 私有方法(jdk9额外增加)
}
  • 案例
public interface IA {
    // 常量 默认修饰符是 public static final 这3个修饰符可以省略不写
    public static final int NUM = 10;
    int NUM1 = 20;

    // 抽象方法 默认修饰符是 public abstract 这2个修饰符可以省略不写
    public abstract void method1();
    void method2();

    // 默认方法 默认修饰符是 public default public可以省略,default不可以省略
    public default void method3(){
        System.out.println("IA 接口默认方法");
    }

    // 静态方法 默认修饰符 public static public可以省略,static不可以省略
    public static void method4(){
        System.out.println("静态方法");
    }

    // 私有方法 修饰符 private   private不可以省略
    private void method5(){
        System.out.println("私有非静态方法");
    }

    private static void method6(){
        System.out.println("私有静态方法");
    }
}

public class Test {
    public static void main(String[] args) {
        /*
            接口的定义:
                public interface 接口名{
                     jdk7及其以前:  常量,抽象方法
                     jdk8: 额外增加默认方法和静态方法
                     jdk9及其以上: 额外增加了私有方法
                }
         */
        System.out.println(IA.NUM1);// 10
    }

    // 类中的默认方法,使用默认权限修饰符(空)
    void method(){

    }
}

3.实现接口

实现概述

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为**接口的子类。**实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

实现格式

  • 类可以实现一个接口,也可以同时实现多个接口(干爹)。
  • 类实现接口后,必须重写接口中所有的抽象方法,否则该类必须是一个“抽象类”。
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{//一定要先继承,后实现
}

4.接口中成员的访问特点

  • 接口中方法可以定义方法体。
  • 接口中的常量: 主要是供接口直接使用
  • 接口中的抽象方法: 供实现类重写的
  • 接口中的默认方法: 供实现类继承的(实现类中可以直接调用,实现类对象也可以直接调用)
  • 接口中的静态方法: 只供接口直接调用,实现类继承不了
  • 接口中的私有方法: 只能在接口中直接调用,实现类继承不了
public interface IA {
    //  接口中的常量: 主要是供接口直接使用
    public static final int NUM = 10;

    // 接口中的抽象方法: 供实现类重写的
    public abstract void method1();

    // 接口中的默认方法: 供实现类继承使用(实现类中可以直接调用,实现类对象也可以直接调用)
    public default void method2(){
        System.out.println("默认方法method2");
        method4();
        method5();
    }

    // 接口中的静态方法: 只供接口直接调用,实现类继承不了
    public static void method3(){
        System.out.println("静态方法method3");
        method5();
    }

    // 接口中的私有方法: 只能在接口中直接调用,实现类继承不了
    private void method4(){// 只能在接口的默认方法中调用
        // 方法体
        method5();
    }

    private static void method5(){//
        // 方法体
    }
}

实现类:

/**
 * @Author:pengzhilin
 * @Date: 2020/4/16 11:59
 */
public class ImpA implements IA{

   /* @Override
    public void method2() {

    }*/

    @Override
    public void method1() {
        System.out.println("重写接口中的method1抽象方法");
    }
}


测试类:

/**
 * @Author:pengzhilin
 * @Date: 2020/4/16 11:54
 */
public class Test {
    public static void main(String[] args) {

        System.out.println(IA.NUM);// 10

        // 创建实现类对象,访问NUM常量
        ImpA ia = new ImpA();
        System.out.println(ia.NUM);// 10

        // 调用method2方法
        ia.method2();

        // 通过接口名调用接口中的静态方法
        IA.method3();
        //ia.method3();// 编译报错,
    }
}

5. 多实现时的几种冲突情况

公有静态常量的冲突

  • 实现类不继承冲突的变量
interface IA{
   public static final int a = 10;
   public static final int b= 20;
}
interface IB{
   public static final int a = 30;
}
class Zi implements IA,IB{
   //只继承了b,没有继承a,因为a冲突了
}

public class Demo {
   public static void main(String[] args) {
       Zi z = new Zi();
    //   System.out.println(z.a);//编译错误
       System.out.println(z.b);
   }
}

公有抽象方法的冲突

  • 实现类只需要重写一个
interface IA{
    public void show();
}
interface IB{
    public void show();
}
class Zi implements IA,IB{
    @Override
    public void show() {//子类只需要重写一个show()即可
        System.out.println("子类的show()...");
    }
}
public class Demo {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

公有默认方法的冲突

  • 实现类必须重写一次最终版本
interface IA{
   public default void show(){
       System.out.println("IA");
   }
}
interface IB{
   public default void show(){
       System.out.println("IB");
   }
}
class Zi implements IA,IB{
   @Override
   public void show() {//必须重写一次的show()
       System.out.println("Zi的show()....");
   }
}
public class Demo {
   public static void main(String[] args) {
       Zi z = new Zi();
       z.show();
   }
}

公有静态方法的冲突

  • 静态方法是直接属于接口的,不能被继承,所以不存在冲突
interface IA{
   public static  void show(){
       System.out.println("IA");
   }
}
interface IB{
   public static void show(){
       System.out.println("IB");
   }
}
class Zi implements IA,IB{

}
public class Demo {
   public static void main(String[] args) {
       Zi z = new Zi();
       z.show();//编译错误,show()不能被继承。
   }
}

私有方法的冲突

  • 私有方法只能在本接口中直接使用,不存在冲突

6. 接口和接口的关系

接口与接口之间的关系

  • 接口可以“继承”自另一个“接口”,而且可以“多继承”。
interface IA{}
interface IB{}
interface IC extends IA,IB{//是“继承”,而且可以“多继承”
}

接口继承接口的冲突情况

  • 公用静态常量冲突
interface IA{
    public static final int a = 10;
    public static final int b = 30;
}
interface IB{
    public static final int a = 20;
}
interface IC extends IA,IB{//没有继承a
}
//测试:
main(){
    System.out.println(IC.a);//错误的
}
  • 公有抽象方法冲突
interface IA{
    public void show();
}
interface IB{
    public void show();
}
interface IC extends IA,IB{//IC只继承了一个show()
}
class Zi implements IC{
    //重写一次show()
    public void show(){
    }
}
  • 公有默认方法的冲突
interface IA{
   public default void d1(){
   }
}
interface IB{
   public default void d1(){
   }
}
interface IC extends IA,IB{//必须重写一次d1()
   public default  void d1(){
   }
}
  • 公有静态方法和私有方法
不冲突,因为静态方法是直接属于接口的,只能使用接口直接访问,而私有方法只能在接口中访问,也没有冲突.

7. 实现类继承父类又实现接口时的冲突

  • 父类和接口的公有静态常量的冲突
不继承
class Fu{
    public static final int a = 10;
}
interface IA{
    public static final int a = 20;
}
class Zi extends Fu implements IA{//没有继承a变量
}
public class Demo {
    public static void main(String[] args) {
        System.out.println(Zi.a);//编译错误
    }
}
  • 父类和接口的抽象方法冲突
必须重写
abstract class Fu{
    public abstract void show();
}
interface IA{
    public void show();
}
class Zi extends Fu implements IA{// 必须重写
    
}
//测试:
main(){
    Zi z = new Zi();
    z.show();//a
}
  • 父类和接口的公有默认方法的冲突
优先使用父类方法
class Fu{
   public void show(){
       System.out.println("a");
   }
}
interface IA{
   public default void show(){
       System.out.println("b");
   }
}
class Zi extends Fu implements IA{
}
//测试:
main(){
   Zi z = new Zi();
   z.show();//a
}
  • 父类和接口的公有静态方法
只继承父类的静态方法
class Fu{
   public static void show(){
       System.out.println("fu...");
   }
}
interface IA{
   public static void show(){
       System.out.println("IA...");
   }
}
class Zi extends Fu implements IA{//只继承了"父类"的静态方法,没有继承接口的静态方法

}
public class Demo {
   public static void main(String[] args) {
       Zi.show();//fu…
   }
}
  • 父类和接口的私有方法
不存在冲突

8. 抽象类和接口的练习

需求:

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

1、举例:

犬: —>父类 抽象类

行为:吼叫;吃饭; ----- 抽象类

缉毒犬: —> 继承犬类

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

缉毒接口:

缉毒的功能(抽象方法)

  • 如果所有子类都有的功能: 通用功能(非抽象方法),非通用功能(抽象方法),定义到父类中
  • 如果某个功能是一个类额外增加的,那么就可以把这个额外的功能定义到接口中,再这个类去实现
分析

由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。

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

  • 额外的功能—> 在接口中定义,让实现类实现
  • 共性的功能—> 在父类中定义,让子类继承
实现
//定义缉毒接口 缉毒的词组(anti-Narcotics)比较长,在此使用拼音替代
interface JiDu{
    //缉毒
	public abstract void jiDu();
}
//定义犬科,存放共性功能
abstract class Dog{
    //吃饭
	public abstract void eat();
    //吼叫
	public abstract void roar();
}
//缉毒犬属于犬科一种,让其继承犬科,获取的犬科的特性,
//由于缉毒犬具有缉毒功能,那么它只要实现缉毒接口即可,这样即保证缉毒犬具备犬科的特性,也拥有了缉毒的功能
class JiDuQuan extends Dog implements JiDu{
	public void jiDu() {
	}
	void eat() {
	}
	void roar() {
	}
}

//缉毒猪
class JiDuZhu implements JiDu{
	public void jiDu() {
	}
}

9. 抽象类和接口的区别

1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
4. 抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
5. 抽象类中可以包含静态方法,接口中不能包含静态方法
6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
7. 一个类可以实现多个接口,但只能继承一个抽象类。

二、多态

1.概述

  • 多态:多态是继封装、继承之后,面向对象的第三大特性, 是指同一行为,对于不同的对象具有多个不同表现形式。
  • 程序中多态: 是指同一方法,对于不同的对象具有不同的实现.
  • 前提条件:
  • 继承或者实现
  • 父类引用指向子类对象:Fu fu = new Zi();
  • 方法的重写。

2. 实现多态

  • 多态的体现:父类的引用指向它的子类的对象
父类类型 变量名 = new 子类对象;
变量名.方法名();
  • 父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
class Animal{}
class Cat extends Animal{}
class Dog extends Animal{}
clsss Person{}
//测试类:
main(){
   Animal a1 = new Cat();
   Animal a2 = new Dog();
   Animal a3 = new Person();//编译错误,没有继承关系。
}

3. 多态时访问成员的特点

  • 多态时成员变量的访问特点
  • 编译看左边,运行看左边
  • 简而言之:多态的情况下,访问的是父类的成员变量
  • 多态时成员方法的访问特点
  • 非静态方法:编译看左边,运行看右边
  • 简而言之:编译的时候去父类中查找方法,运行的时候去子类中查找方法来执行
  • 静态方法:编译看左边,运行看左边
  • 简而言之:编译的时候去父类中查找方法,运行的时候去父类中查找方法来执行
  • 注意:多态的情况下是无法访问子类独有的方法
  • 演示代码:
public class Demo1 {
    public static void main(String[] args) {
        // 父类的引用指向子类的对象
        Animal anl1 = new Dog();
        // 访问非静态方法
        anl1.eat();

        // 访问成员变量num
        System.out.println(anl1.num);//10

        // 访问静态方法
        anl1.sleep();

        // 多态想要调用子类中独有的方法
        // anl1.lookHome(); 错误的,无法访问  多态的弊端:无法访问子类独有的方法

    }
}

public class Animal {

    int num = 10;

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

    public static void sleep(){
        System.out.println("Animal类中的睡觉方法...");
    }

}

public class Dog extends Animal {

    int num = 20;

   // 重写
    public void eat() {
        System.out.println("狗吃骨头");
    }

    public static void sleep(){
        System.out.println("Dog类中的睡觉方法...");
    }

    public void lookHome(){
        System.out.println("狗正在看家...");
    }

}

4. 多态的好处和弊端

  • 好处
  • 提高了代码的扩展性
  • 弊端
  • 多态的情况下,只能调用父类的共性内容,不能调用子类的特有内容。
  • 示例代码
// 父类
public abstract class Animal {  
  public abstract void eat();  
}  
// 子类
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 class Test {
  public static void main(String[] args) {
      // 创建对象
      Cat c = new Cat();  
      Dog d = new Dog(); 

      // 调用showCatEat 
      showCatEat(c);
      // 调用showDogEat 
      showDogEat(d); 

      /*
      以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
      而执行效果一致
      */
      showAnimalEat(c);
      showAnimalEat(d); 
  }

  public static void showCatEat (Cat c){
      c.eat(); 
  }

  public static void showDogEat (Dog d){
      d.eat();
  }

  public static void showAnimalEat (Animal a){
      a.eat();
  }
}

// 多态的弊端============
public static void main(String[] args) {
      // 多态形式,创建对象
      Aniaml anl = new Cat();  
      anl.eat();
  	  anl.catchMouse();// 编译报错, 编译看父类(左边),父类中没有定义catchMouse方法
}

5. 引用类型转换

向上转型

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

向下转型

  • 父类类型向子类类型向下转换的过程,这个过程是强制的。
Aniaml anl = new Cat();  
 Cat c = (Cat)anl;//向下转型
 c.catchMouse();// 可以访问 子类独有的功能,解决多态的弊端

instanceof关键字

  • 向下强转有风险,最好在转换前做一个验证 :
  • 格式:
/*
变量名 instanceof 数据类型 
变量名 instanceof 数据类型 
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
*/
if( anl instanceof Cat){//判断anl是否能转换为Cat类型,如果可以返回:true,否则返回:false
    Cat c = (Cat)anl;//安全转换
}

6.多态的应用场景

  • 变量多态 — 不常见
  • 形参多态 ---- 常见
  • 返回值多态 — 常见
//定义一个Animal类,让Dog和Cat类继承Animal类:
public class Animal {
    public void eat(){
        System.out.println("吃东西...");
    }
}
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }
}

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

public class Demo1 {
    public static void main(String[] args) {
        /*
            多态的应用场景:
                1.变量多态
                2.形参多态
                3.返回值多态
         */
        // 1.变量多态
        Animal anl = new Dog();
        anl.eat();

        // 2.形参多态
        Dog dog = new Dog();
        invokeEat(dog);

        Cat cat = new Cat();
        invokeEat(cat);// 实参赋值给形参: Animal anl = new Cat();

        // 3.返回值多态
        Animal anl2 = getAnimal();// 返回值赋值给变量: Animal anl2 = new Dog()
    }

    //  3.返回值多态
    // 结论:如果方法的返回值类型为父类类型,那么就可以返回该父类对象以及其所有子类对象
    public static Animal getAnimal(){
//        return new Animal();
        return new Dog();
//        return new Cat();
    }

    // 形参多态: 当你调用invokeEat方法的时候,传入Animal类的子类对象
    // Animal anl = dog; ====>  Animal anl = new Dog();
    // 结论:如果方法的参数是父类类型,那么就可以接收所有该父类对象以及其所有子类对象
    // Object类:是java中所有类的父类,所以如果参数为Object类型,那么就可以传入一切类的对象
    public static void invokeEat(Animal anl){
        anl.eat();
    }

}

7. 多态的几种表现形式

  • 普通父类多态
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();
    }
}

三、内部类

1.概述

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

2.成员内部类

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

    }
}

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

  • 示例代码
class Car { //外部类
    class Engine { //内部类

    }
}
  • 访问特点
  • 内部类可以直接访问外部类的成员,包括私有成员。
  • 外部类要访问内部类的成员,必须要建立内部类的对象。
  • 创建内部类对象格格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
  • 示例代码
public class Body {// 外部类

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

    int num = 100;

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

    public void methodW2(){
        System.out.println("外部类中的methodW2方法...");
        // 创建内部类对象
        Body.Heart bh = new Body().new Heart();
        // 访问内部类成员变量
        System.out.println("内部类成员变量numN:"+bh.numN);
        // 访问内部类成员方法
        bh.methodN2();
    }

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

        int num = 200;

        // 成员方法
        public void methodN(){
            System.out.println("内部类中的methodN方法...");
            // 访问外部类成员变量
            System.out.println("外部类成员变量:"+numW);
            // 访问外部类成员方法
            methodW1();
        }

        public void methodN2(){
            System.out.println("内部类中的methodN2方法...");
        }

        public void methodN3(){
            int num = 300;
            System.out.println("局部变量num:"+num);// 300
            System.out.println("内部类成员变量num:"+this.num);// 200
            System.out.println("外部类成员变量num:"+Body.this.num);// 100
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        // 测试
        // 创建外部类对象,调用外部类的方法methodW2
        Body body = new Body();
        body.methodW2();

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

        // 创建内部类对象,调用内部类的methodN方法
        Body.Heart heart = new Body().new Heart();
        heart.methodN();

        System.out.println("=======================");
        heart.methodN3();// 300  200  100
    }
}

3.匿名内部类

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

  • 示例代码
public abstract class Animal {
    public abstract void eat();
}
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }
}
public class Test {
    public static void main(String[] args) {
        /*
            - 匿名内部类的概述:本质就是继承了父类或者实现了接口的匿名子类的对象
            - 匿名内部类的格式:
                new 类名\接口名(){
                    方法重写
                };

            - 匿名内部类的作用: 为了简化代码,并没有什么特殊的功能

            需求: 调用Aniaml类的eat()方法
            1.创建一个子类继承Animal类
            2.在子类中重写eat方法
            3.创建子类对象
            4.使用子类对象调用eat方法

            想要调用抽象类中的方法,必须具备以上4步,那能不能减少呢? 可以 使用匿名内部类
         */
        Animal anl1 = new Dog();
        anl1.eat();

        Animal anl2 = new Animal() {
            @Override
            public void eat() {
                System.out.println("Animal子类的eat方法...");
            }
        };
        anl2.eat();

    }
}