栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

八、面向对象三-接口与多态

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

八、面向对象三-接口与多态

一、接口 1.1 接口介绍
  • 当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义为接口,接口也是一种引用类型,它比抽象类还要抽象

接口存在的两个重要意义:

  • 规则的定义(实现接口必须实现其所有抽象方法)
  • 程序的扩展性(需要实现重写其抽象方法)
1.2 接口的定义和特点
  • 接口用关键字interface来定义
    • public interface 接口名{}
public interface InterDemo1 {
    // 这是一个接口
    public abstract void test();
}
  • 接口不能实例化,即不能创建接口对象
  • 接口和类之间是实现关系,通过implements关键字表示
    • public class 类名 implements 接口名{}
  • 接口的子类(实现类),要么重写接口中所有抽象方法,要么作为抽象类定义
public class InterDemo1Impl implements InterDemo1{

    @Override
    public void test() {
        System.out.println("重写test...");
    }
}
public abstract class InterDemo1Impl2 implements InterDemo1{
}

注:

  • 接口和类的实现关系,可以单实现,也可以多实现。public class 类名 implements 接口名1, 接口名2{}
  • 可以多实现的原因:因为接口内方法都没有具体实现,虽然存在同名冲突,但都要重写,因此可以多实现
1.3 接口中的成员特点
  • 接口中的成员变量:只能是常量,且默认是静态的
    • 若不写修饰符,默认修饰符:public static final
  • 没有构造方法
    • 因为接口主要是扩展功能的,而没有具体存在,即无法实例化
  • 在JDK7之前,成员方法只能是抽象方法
    • 若不写修饰符,则默认修饰符:public abstract
public interface InterDemo1 {
    // 这是一个接口
    int i = 10;  // 成员变量默认修饰符public static final
    public static final int b = 20;

    
    public abstract void test();

    // 没有构造方法
    // public InterDemo1(); 报错

    void test2();  // 成员方法默认修饰符public abstract
    
}
1.4 JDK8版中接口成员的特点 1.4.1 JDK8版本后,允许在接口内定义默认方法
  • 默认方法:使用关键字default修饰的非抽象方法,可以拥有方法体
  • 作用:解决接口升级问题

接口中默认方法的定义格式:

  • 格式:public default 返回值类型 方法名(参数列表){}
  • 范例:public default void show(){}

接口中默认方法的注意事项:

  • 默认方法不是抽象方法,所以不强制被重写,即可被实现类直接继承使用
  • 但是可以被重写,重写的时候必须去掉default关键字
  • public可以省略,default不能省略,否则系统默认其为抽象方法,不能拥有方法体
public interface InterDemo2 {
    // JDK8版本后,允许在接口内定义默认方法
    // 格式:public default 返回值类型 方法名(参数列表){}
    public default void test() {
        System.out.println("这是一个默认方法,必须要加default修饰");
    }

    // public void test2(){} 若略去default则编译报错,但public可以省略
    // Interface abstract methods cannot have body,系统默认其为抽象方法

}
// 实现类可以不重写默认函数,当然也可以重写
public class InterDemo2Impl implements InterDemo2{
    public void impl2(){
        test();
    }
}
public class Test {
    public static void main(String[] args) {
        InterDemo2Impl impl2 = new InterDemo2Impl();
        impl2.impl2();  // 打印:这是一个默认方法,必须要加default修饰
    }
}
1.4.2 JDK8版本后,接口中允许定义static静态方法

接口中静态方法的格式:

  • 格式:public static 返回值类型 方法名(参数列表){}
  • 范例:public static void show(){}

接口中静态方法的注意事项:

  • 静态方法中只能通过接口名调用,不能通过实现类名或者对象名调用
  • public可以省略,static不能省略,否则系统默认其为抽象方法,不能拥有方法体
public interface InterDemo3 {
    // JDK8版本后,接口中允许定义static静态方法
    // 格式:public static 返回值类型 方法名(参数列表){}
    public static void test() {
        System.out.println("这是一个静态方法,必须要去掉default,且只能通过接口名调用或被接口中其他静态函数使用");
    }

    // public void test2(){} 报错,public可以省略,static不能省略
    // Interface abstract methods cannot have body,系统默认其为抽象方法

}
public class InterDemo3Impl implements InterDemo3{
    public void impl3(){
        // 实现类中只能通过接口名.的方式调用
        InterDemo3.test();
    }
}
1.5 JDK9版本中接口成员的特点

JDK9版本后,支持在接口中定义私有方法

定义方式与在普通类中定义私有函数相同:

  • 普通私有函数: private 返回值类型 方法名(参数列表){}
    • 范例1: private void show(){}
  • 静态私有函数: private static 返回值类型 方法名(参数列表){}
    • 范例2: private static void method(){}
public interface InterDemo4 {
    // JDK9版本后,支持在接口中定义私有方法
    // 普通私有方法格式:private 返回值类型 方法名(参数列表){}
    private void test() {
        System.out.println("这是一个私有方法,必须要去掉default,且只能在接口内部由默认非静态方法调用");
    }

    // 静态私有方法格式:private static 返回值类型 方法名(参数列表){}`
    private static void test2() {
        System.out.println("这是一个静态私有方法,必须要去掉default,且只能在接口内部由静态方法调用");
    }

    // 使用默认非静态方法调用
    public default void func1() {
        test();
    }

    // 使用静态方法调用
    public static void func2() {
        test2();
    }

}
1.6 接口的使用思路☆
  1. 如果发现一个类中所有的方法都是抽象方法,那么就可以将该类,改进为一个接口
  2. 涉及到了接口大面积更新方法,而不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法(使用default关键字修饰,且去掉abstract关键字)
  3. 希望默认方法调用的更加简洁,可以考虑设计为static静态方法(需要去掉default关键字),切记没有默认静态方法的说法
  4. 默认方法中出现了重复的代码,可以考虑取出一个私有方法(需要去掉default关键字)
1.7 类和接口的关系
  • 类和类之间的关系
    • 继承关系,只能单继承,不能多继承,但是可以多层继承
  • 类和接口之间的关系
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时,实现多个接口class A A extends B implements
    • 注:如果extends继承的父类和接口出现了相同的方法声明,但是代码逻辑不一样,则系统会优先调用extends继承的父类
  • 接口和接口的关系
    • 继承关系,可以单继承,也可以多继承
二、多态 2.1多态概述
  • 定义:同一个对象,在不同时候表现出来的不同形态
    • 举例:猫
      • 我们可以说猫是猫:猫 cat = new 猫();
      • 我们也可以说猫是动物:动物 animal = new 猫();
      • 这里猫在不同时刻表现出来了不同的形态,这就是多态
  • 多态的三个前提条件
    • 必须要有 继承或实现 关系
    • 必须要有方法重写
    • 必须要有父类引用(引用类型变量),指向子类对象(实例化后的具体对象)
class Animal {
    public void eat(){
        System.out.println("动物吃饭");
    }
}
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
public class Test01 {
    public static void main(String[] args) {
        // 当前事物, 是一只猫
        Cat c = new Cat();
        // 当前事物, 是一只动物
        Animal a = new Cat();
        a.eat();

    }
}
2.2 多态中成员访问特点
  • 构造方法:同继承一样,子类默认会通过super()访问父类的空参构造方法

  • 成员变量:编译看左边(父类),执行看左边(父类)

    • 即调用时先看父类中是否有此变量,运行时也使用的是父类中的变量
  • 成员方法:编译看左边(父类),执行看右边(子类)

    • 即调用时查看父类中是否有此方法,但运行时调用子类重写后的方法(多态)
public class Test01 {
    public static void main(String[] args) {
        Father f = new Child();
        // 若父类中无变量a或test方法,则编译报错
        System.out.println(f.a);  // 10 
        f.test();  // 子类test...
    }
}

class Father{
    int a = 10;

    public void test(){
        System.out.println("父类test...");
    }

}

class Child extends Father{
    int a = 20;

    public void test(){
        System.out.println("子类test...");
    }
}

为什么成员变量和成员方法的访问不一样呢?

  • 因为成员方法有重写,而成员变量没有
2.3 多态的好处和弊端
  • 多态的好处:提高了程序的扩展性
    • 具体体现:定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的任意子类对象
  • 多态的弊端:不能使用子类的特有功能,因为编译时必须要求父类中存在其方法的声明
2.4 多态中的转型
  • 向上转型

    • 父类引用指向子类对象就是向上转型
    • 会自动完成转换
  • 向下转型(强制类型转换)

    • 格式:子类型 对象名 = (子类型)父类引用;
public class Test02 {
    public static void main(String[] args) {
        // 向上转型
        Animal animal = new Dog();
        // 向下转型  子类型 对象名 = (子类型)父类引用;
        Dog dog = (Dog) animal;
    }
}

abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("Dog ...eat");
    }
}
2.5 多态中转型存在的风险
  • 风险

    如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException

  • 解决方案

    • 关键字

      instanceof

    • 使用格式

      变量名 instanceof 类型

    • 通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果(可增加if语句进行条件判断)
public class Test02 {
    public static void main(String[] args) {
        // 向上转型
        Animal animal = new Dog();
        // 向下转型  子类型 对象名 = (子类型)父类引用;
        if(animal instanceof Dog){
            System.out.println("引用变量的实际类型是目标类型");
            Dog dog = (Dog) animal;
        }else {
            System.out.println("引用变量的实际类型不是目标类型");
        }
    }
}

abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("Dog ...eat");
    }
}
三、内部类 3.1 内部类的基本使用
  • 内部类概念

    • 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
  • 内部类定义格式

    class 外部类名{
    	修饰符 class 内部类名{
    	}
    }
    
  • 内部类的访问特点 (类似于成员方法)

    • 内部类可以直接访问外部类的成员,包括私有
    • 外部类要访问内部类的成员,必须创建对象
3.2 成员内部类
  • 成员内部类的定义位置

    • 在类中方法,跟成员变量是一个位置
  • 外界创建成员内部类格式

    • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
    • 举例:Outer.Inner oi = new Outer().new Inner();(要记住)
public class OuterTest1 {
    public static void main(String[] args) {
        // 外界创建成员内部类
        // 格式Outer.Inner oi = new Outer().new Inner();
        Outer.Inner oi = new Outer().new Inner();
        oi.test();  // Inner....
    }
}

class Outer{
    class Inner{
        public void test(){
            System.out.println("Inner....");
        }
    }
}
  • 私有成员内部类
    • 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法
    • 仅由方法内部创建内部类对象并调用。
public class OuterTest2 {
    public static void main(String[] args) {
        new Outer().useInner();
    }
}

class Outer {
    private class Inner {
        public void test() {
            System.out.println("Inner....");
        }
    }

    public void useInner(){
        // 私有内部类仅由方法内部创建内部类对象并调用
        Inner inner = new Inner();
        inner.test();
    }
}
  • 静态成员内部类(静态成员随外部类的加载而加载)
    • 静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
    • 静态成员内部类中的静态方法:外部类名.内部类名.方法名();
public class OuterTest3 {
    public static void main(String[] args) {
        // 静态成员内部类访问格式
        // 外部类名.内部类名 对象名 = new 外部类名.内部类名();
        Outer.StaticInner inner = new Outer.StaticInner();
        inner.testStaticMethod();  // Inner....

        // 静态成员内部类中的静态方法:外部类名.内部类名.方法名();
        Outer.StaticInner.testStaticMethod();  // Inner....

        // 使用内部提供的方法
        new Outer().useInner();  // Inner....
    }
}

class Outer {
    public static class StaticInner {
        public static void testStaticMethod() {
            System.out.println("Inner....");
        }
    }

    public void useInner() {
        StaticInner.testStaticMethod();  // Inner....
    }
}
3.3 局部内部类
  • 局部内部类定义位置

    • 局部内部类是在方法中定义的类
  • 局部内部类访问方式

    • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
    • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
public class OuterTest4 {
    static String s0 = "访问外部内静态成员变量";
    public static void main(String[] args) {
        String s1 = "访问方法内局部变量";
        class MethodInner {
            String s2 = "访问类内成员变量";

            public void test() {
                System.out.println("局部内部类,在方法内定义");
                System.out.println(s0);   // 在静态方法内定义,只能访问静态成员变量
                System.out.println(s1);
                System.out.println(s2);
            }
        }
        // 只有在方法内能访问得到
        new MethodInner().test();

    }
}
3.4 匿名内部类
  • 匿名内部类的前提

    • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
  • 匿名内部类的格式

    • 格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }
new Inter(){
    @Override
    public void method(){}
} 
  • 匿名内部类的本质

    • 本质:是一个继承了该类或者实现了该接口的子类匿名对象
  • 匿名内部类的细节

    • 匿名内部类可以通过多态的形式接受
Inter i = new Inter(){  // 用接口类或抽象类对象以多态的形式接受
  @Override
    public void method(){
        
    }
}
  • 匿名内部类直接调用方法
interface Inter{
    void method();
}

class Test{
    public static void main(String[] args){
        new Inter(){
            @Override
            public void method(){
                System.out.println("我是匿名内部类");
            }
        }.method();	// 直接调用方法
    }
}
  • 函数传参使用接口方法
public class TestInter {
    public static void main(String[] args) {
        useInter(new Inter() {
            @Override
            public void test() {
                System.out.println("使用匿名内部类向方法传参");
            }
        });
    }

    public static void useInter(Inter inter) {
        inter.test();
    }
}

interface Inter {
    public void test();
}
四、Lambda表达式 4.1 概述
  • 格式:(形式参数) -> {代码块}

    • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
    • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
    • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
  • 组成Lambda表达式的三要素:

    • 形式参数,箭头,代码块
public class TestInterLambda {
    public static void main(String[] args) {
        // lambda表达式帮我们简化了创建对象、实现接口等繁琐的语法结构,而更关注于重写的方法逻辑本身
        // 这种不关心谁来做(哪个对象去做),而只关心怎么做的思想叫函数式编程思想
        useInter(()->{
            System.out.println("使用lambda表达式简化匿名内部类");
        });
    }

    public static void useInter(Inter inter) {
        inter.test();
    }
}

interface Inter {
    public void test();
}
4.2 Lambda表达式练习
  • 有参数无返回值
public class TestInter {
    public static void main(String[] args) {
        // 匿名内部类方式实现
        useInter(new Inter() {
            @Override
            public void test(String message) {
                System.out.println("匿名内部类" + message);
            }
        });
        // lambda表达式方式实现
        useInter((String message) -> {
            System.out.println("带形参的lambda表达式" + message);
        });
    }

    public static void useInter(Inter inter) {
        inter.test("message");
    }
}

interface Inter {
    public void test(String message);
}
  • 有返回值无参数
public class TestInter02 {
    public static void main(String[] args) {
        // 匿名内部类方式实现
        useInterRandNum(new RandNum() {
            @Override
            public int returnRandom() {
                Random random = new Random();
                int num = random.nextInt(10) + 1;
                return num;
            }
        });

        // lambda表达式方式实现;必须要有return,否则报错
        useInterRandNum(() -> {
            Random random = new Random();
            int num = random.nextInt(10) + 1;
            return num;
        });
    }

    public static void useInterRandNum(RandNum inter) {
        int num = inter.returnRandom();
        System.out.println("随机数为" + num);
    }
}

interface RandNum {
    public int returnRandom();
}
  • 有参数有返回值
public class TestInter03 {
    public static void main(String[] args) {
        // 匿名内部类方式实现
        useCalculator(new Calculator() {
            @Override
            public int calculate(int a, int b) {
                return a + b;
            }
        });

        // lambda表达式方式实现
        useCalculator((int a, int b) -> {
            return a + b;
        });
    }

    public static void useCalculator(Calculator calculator) {
        int sum = calculator.calculate(10, 20);
        System.out.println("求和结果为" + sum);
    }
}

interface Calculator {
    public int calculate(int a, int b);
}
4.3 Lambda表达式的省略模式
  • 省略的规则
    • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个,要省全省
    • 如果参数有且仅有一个,那么小括号可以省略
    • 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
  • 例如改写上例
public class TestInter03 {
    public static void main(String[] args) {
        // lambda表达式方式实现
        // 省略了形参的参数类型,不能只省略一个,要省全省
        // 省略了大括号、return和分号(因为代码块语句只有一句)
        useCalculator((a, b) -> a + b);
    }

    public static void useCalculator(Calculator calculator) {
        int sum = calculator.calculate(10, 20);
        System.out.println("求和结果为" + sum);
        
    }
}

interface Calculator {
    public int calculate(int a, int b);
}
4.4 Lambda表达式的使用前提
  • 使用Lambda必须要有接口,不能操作普通类或抽象类
  • 并且要求接口中有且仅有一个 抽象方法,多个抽象方法只能选择使用匿名内部类或实现类
  • 总结:lambda只适用于只有一个抽象方法的接口
4.5 Lambda表达式和匿名内部类的区别
  • 所需类型不同
    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
    • Lambda表达式:只能用于接口
  • 使用限制不同
    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
    • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
  • 实现原理不同
    • 匿名内部类:编译之后,产生一个单独的.class字节码文件,会保存在磁盘中!
    • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成代码块,不会保存在磁盘中
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/877270.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号