JavaSE基础加强之继承与多态(二)

概述

(一)继承
(二)抽象类
(三)接口
(四)多态
(五)final关键字
(六)权限修饰符
(七)内部类
注意
面向对象的三大特征:封装性继承性多态性

(一)继承

继承是多态的前提,如果没有继承,就没有多态

在继承的关系中,子类就是一个父类,即子类可以被当作父类看待
例如父类是员工,子类是讲师,那么讲师就是一个员工

  • demo01

我们简单实现一下上面的情况

Employee.java

package com.zzq.extendstest;

/**
 * 定义一个父类:员工类
 */
public class Employee {
    public void method(){
        System.out.println("方法执行");
    }
}

Teacher.java

package com.zzq.extendstest;

/**
 * 定义一个子类:讲师类
 */
public class Teacher extends Employee{

}

Assistant.java

package com.zzq.extendstest;

/**
 * 定义了员工的另一个子类:助教类
 */
public class Assistant extends Employee{

}

ExtendsDemo01.java

package com.zzq.extendstest;

public class ExtendsDemo01 {
    public static void main(String[] args) {
        //创建了一个子类对象
        Teacher teacher = new Teacher();
        //Teacher类当中虽然什么都没写,但是会继承来自父类的method()方法
        teacher.method();//方法执行

        //创建另一个子类助教的对象
        Assistant assistant = new Assistant();
        assistant.method();//方法执行
    }
}
  • demo02

继承中成员变量的访问特点


package com.zzq.extendstest;

public class ExtendDemo02 {
    public static void main(String[] args) {
        Fu fu = new Fu();//创建父类对象
        System.out.println(fu.numFu);//只能使用父类的东西
        Zi zi = new Zi();//创建子类对象
        System.out.println(zi.numFu);//可以使用父类的东西
        System.out.println(zi.numZi);//也可以使用子类的东西
    }
}

那如果父类的变量跟子类的变量重名呢?

/**
 * 父类
 */
public class Fu {
    int numFu = 10;
    int num = 100;

    public void methodFu() {
        //因为本类当中有num,所以这里用的是本类的num
        System.out.println(num);//100
    }
}
/**
 * 子类
 */
public class Zi extends Fu {
    int numZi = 20;
    int num = 200;

    public void methodZi() {
        //因为本类当中有num,所以这里用的是本类的num
        System.out.println(num);//200
    }
}
public class ExtendDemo02 {
    public static void main(String[] args) {
        Fu fu = new Fu();//创建父类对象
        System.out.println(fu.num);//100
        Zi zi = new Zi();//创建子类对象
        System.out.println(zi.num);//200
    }
}

有两种方法
1.直接通过子类对象访问成员变量
(等号左边是谁,就优先用谁,没有就继续向上找)
重名的变量是如此,重名的成员方法也是如此
2.通过成员方法间接访问成员变量(多态会详细讲)
(看new的是谁,优先用谁的方法,没有则向上找)

  • demo03

区分三种重名的变量

/**
 * 父类
 */
public class Fu {
    int num = 10;
}


总结
局部变量:直接写成员变量名
本类的成员变量:this.成员变量名
父类的成员变量:super.成员变量名
注意:如果想要调用父类的重名方法,也可以用super.重名的方法()

  • demo04
    其实重名的方法就是Override(重写)
    重写的原则
  1. 子类方法的返回值范围必须小于或等于父类方法的返回值范围
    其中java.lang.Object是最大的
    比如说:
    父类的方法返回值类型是Object,子类重写的方法返回值类型是String是可以的;但是反过来就不可以,会报错的。
  2. 子类方法的权限修饰符必须大于或等于父类方法的权限修饰符
    public > protected > (default) > private
    注意:(default)不是关键字default,而是什么都不写,留空
  • demo05

继承中构造方法的访问特点

  1. 子类构造方法中有一个默认隐含的super();调用,所以一定是先调用了父类的构造方法,后执行子类的构造方法
  2. 可以通过super关键字来实现子类构造调用父类重载构造,如:super(10);
  3. super的父类构造调用,必须是子类构造方法的第一个语句
  4. 一个子类构造只能调用一次父类构造
  5. 子类必须调用父类的构造方法,不写则赠送super();写了则用指定的super调用
  • demo06
    super关键字的三种用法
/**
 * 父类
 */
public class Fu {
    int num = 10;

    public Fu() {
    }

    public void method() {
        System.out.println("父类方法");
    }
}

  • demo07

this关键字的三种用法

super关键字用来访问父类内容,而this关键字用来访问本类内容

注意

  1. this(...);调用也必须是构造方法的第一个语句,并且是唯一一个。
  2. superthis两种构造调用,不能同时使用

    因为this和super都要放在第一互相冲突,所以这里的super();不再赠送
  • demo08

this和super关键字内存图解

Java继承的三个特征

(二)抽象类

抽象方法:加上abstract关键字,然后去掉大括号,直接分号结束
抽象方法所在的类必须要是抽象类才行,在class之前写上abstract即可
抽象类也能有普通方法

  • demo01

如何使用抽象类抽象方法

1.不能直接new抽象类对象
2.必须用一个子类来继承抽象父类
3.子类必须覆盖重写(实现)抽象父类当中的所以抽象方法
4.创建子类对象进行使用

  • demo02
    注意事项
    1.抽象类不能创建对象,只能创建其非抽象子类的对象
    2.抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员使用的
    3.抽象类中不一定含有抽象方法,但是有抽象方法的类必定是抽象类
    没有抽象方法的抽象类也不能直接创建对象,在一些特殊场景下有用途
    4.抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类
    5.假设爷爷有两个抽象方法,父亲(也是抽象类)只实现了一个,那么剩下一个儿子就要实现

(三)接口

接口就是多个类的公共规范
接口就是一种引用数据类型,最重要的内容就是其中的抽象方法
备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class

  1. 如果是Java 7,那么接口中可以包含的内容有:
    1.常量
    2.抽象方法
  2. 如果是Java 8,还可以包含有:
    1.默认方法
    2.静态方法
  3. 如果是Java 9,还可以额外包含有:
    1.私有方法
  • demo01

接口的简单使用

/**
 * 在任何版本的Java中,接口都能定义抽象方法
 */
public interface MyInterfaceAbstract {
    //这是一个抽象方法
    public abstract void methodAbs1();
    //选择性省略关键字
    abstract void methodAbs2();
    public void methodAbs3();
    void methodAbs4();
}

注意事项
1.接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2.这两个关键字修饰符,可以选择性地省略

  • demo02

配合实现类使用

/**
 * 实现类
 */
public class MyInterfaceAbsImpl implements MyInterfaceAbs {

    @Override
    public void methodAbs1() {
        System.out.println("这是第一个方法");
    }

    @Override
    public void methodAbs2() {
        System.out.println("这是第二个方法");

    }

    @Override
    public void methodAbs3() {
        System.out.println("这是第三个方法");

    }

    @Override
    public void methodAbs4() {
        System.out.println("这是第四个方法");

    }
}
/**
 * 创建实现类对象
 */
public class Demo01 {
    public static void main(String[] args) {
        MyInterfaceAbsImpl myInterfaceAbs = new MyInterfaceAbsImpl();
        myInterfaceAbs.methodAbs1();
        myInterfaceAbs.methodAbs2();
        myInterfaceAbs.methodAbs3();
        myInterfaceAbs.methodAbs4();
    }
}

注意事项
1.接口不能直接使用,必须有一个实现类来实现接口
2.接口的实现类必须覆盖重写(实现)接口中的所有抽象方法
3.创建实现类的对象,进行使用
4.如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己必须是抽象类

  • demo03

从Java 8开始,接口中允许定义默认方法

备注:接口当中的默认方法,可以解决接口升级的问题

/**
 * 接口类
 */
public interface MyInterfaceDefault {
    //抽象
    public abstract void methodAbs();

    //默认方法
    public default void methodDefault() {
        System.out.println("新添加的默认方法");
    }
}
/**
 * 实现类A
 */
public class MyInterfaceDefaultImplA implements MyInterfaceDefault {
    @Override
    public void methodAbs() {
        System.out.println("实现了抽象方法 AAA");
    }
}
/**
 * 实现类B
 */
public class MyInterfaceDefaultImplB implements MyInterfaceDefault {
    @Override
    public void methodAbs() {
        System.out.println("实现了抽象方法 BBB");
    }
}
public class Demo02 {
    public static void main(String[] args) {
        MyInterfaceDefaultImplA a = new MyInterfaceDefaultImplA();
        MyInterfaceDefaultImplB b = new MyInterfaceDefaultImplB();
        a.methodAbs();//实现了抽象方法 AAA
        b.methodAbs();//实现了抽象方法 BBB
        a.methodDefault();//新添加的默认方法
    }
}


注意事项
1.接口的默认方法,可以通过接口的实现类对象直接调用
2.接口的默认方法,也可以通过接口实现类进行覆盖重写

  • demo04

从Java 8开始,接口中允许定义静态方法

如果存在一些所有实现类实现方法都一样的方法,那么可以将其写成静态

/**
 * 接口类
 */
public class MyInterfaceStatic {

    public static void methodStatic() {
        System.out.println("这是接口的静态方法");
    }
}
public class Demo03 {
    public static void main(String[] args) {
        MyInterfaceStatic.methodStatic();//直接用类名调用
    }
}
  • demo05

从Java 9开始,接口允许定义私有方法

1.普通私有方法可以解决多个默认方法之间重复代码问题

/**
 * 接口类
 */
public interface MyInterfacePrivateA {
    public default void methodDefault1() {
        System.out.println("默认方法1");
        common();
    }

    public default void methodDefault2() {
        System.out.println("默认方法2");
        common();
    }

    private void common() {
        System.out.println("默认方法1");
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }

2.静态私有方法可以解决多个静态方法之间重复代码问题

/**
 * 接口类
 */
public interface MyInterfacePrivateA {
    public static void methodDefault1() {
        System.out.println("默认方法1");
        common();
    }

    public static void methodDefault2() {
        System.out.println("默认方法2");
        common();
    }

    private static void common() {
        System.out.println("默认方法1");
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}
  • demo06

接口中定义常量

注意事项

  1. 必须使用public static final三个关键字
    如果省略了这三个关键字,效果也是一样的(都是不可修改)
  2. 接口当中的常量必须赋值,不能不赋值
  3. 常量的命名必须全是大写,可用下划线
public interface MyInterfaceConst {
    public static final int NUM_OF_MY_CLASS = 10;
}
public class Demo04 {
    public static void main(String[] args) {
        System.out.println(MyInterfaceConst.NUM_OF_MY_CLASS);
    }
}
  • demo07
    注意事项
    1.接口是没有静态代码块或者构造方法的,因为接口不能被new
    2.一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
    3.如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可
    4.如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类必须对冲突的默认方法进行覆盖重写
    5.一个类如果直接父类中的方法和接口中的默认方法产生了冲突,优先使用父类当中的方法
  • demo08
    1.类与类之间是单继承的,直接父类只有一个
    2.类与接口之间是多继承的,一个类可以实现多个接口
    3.接口与接口之间是多继承的

(四)多态

extends继承或者implements实现是多态性的前提

代码当中体现多态性:父类引用指向子类对象
父类名称 对象名 = new 子类名称();
接口名称 对象名 = new 实现类名称();

  • demo01

多态的简单使用

public class Fu {
    public void method() {
        System.out.println("父类方法");
    }

    public void methodFu() {
        System.out.println("父类特有方法");
    }
}
public class Zi extends Fu {
    @Override
    public void method() {
        System.out.println("子类方法");
    }
}
public class MultiDemo01 {
    public static void main(String[] args) {
        //使用多态的写法
        Fu obj = new Zi();
        obj.method();//子类方法
        obj.methodFu();//父类特有方法
    }
}

多态:左父右子
1.当父类和子类都有相同的方法时,优先执行子类的
2.当父类拥有子类没有的方法时,执行父类的特有方法

  • demo02

多态中成员变量的使用特点

(前面继承demo02有讲到)
访问成员变量的两种方式:
1.直接通过对象名称访问成员变量
(看等号左边是谁,就优先用谁,没有则向上找)

public class Fu {
    int num = 10;

    public void showNum() {
        System.out.println(this.num);
    }
}
public class Zi extends Fu {
    int num = 20;

}
public class MultiDemo01 {
    public static void main(String[] args) {
        //使用多态的写法,父类引用指向子类对象
        Fu obj = new Zi();
        System.out.println(obj.num);//10
        obj.showNum();//子类没有覆盖重写,方法属于父类,结果:10
    }
}

2.间接通过成员方法访问成员变量
(看new的是谁,优先用谁的方法,没有则向上找)

  • demo05
    口诀
  1. 成员方法:编译看左,运行看右
    只要方法是属于左边的就可以通过编译,不会报错;但是具体运行的是什么方法就看右边
    如果右边没有,再往上找
  2. 成员变量:编译看左,运行看左
    只要变量是属于左边的就可以通过编译,不会报错;具体调用什么变量也是看左边
    如果左边没有,再往上找
  • demo04

使用多态的好处

  • demo05

对象的向上转型


代码实现

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

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
public class MultiDemo02 {
    public static void main(String[] args) {
        //对象的向上转型就是父类引用指向父类对象
        Animal animal = new Cat();
        animal.eat();//猫吃鱼
    }
}

但是向上转型有一个弊端,比如说给子类写一个特有方法,就无法调用特有方法!


因为我们把猫当成动物,并非所有动物都会捉老鼠

  • demo06

对象的向下转型

我们可以用向下转型来把动物还原

代码实现

同样,向下转型也有弊端,如下:


把原来是的动物转换成,会抛出类转换异常

  • demo07

如何才能知道一个父类的引用对象,本来是什么子类?
使用instanceof关键字进行分析

格式:
对象 instanceof 类型
这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面对象的实例
代码实现

public class InstanceofDemo01 {
    public static void main(String[] args) {
        Animal animal = new Cat();//本来是一只猫
        animal.eat();//猫吃鱼

        //如果希望调用子类特有方法,需要向下转型
        //判断一下父类引用animal本来是不是Dog
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        //判断一下父类引用animal本来是不是Cat
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
}

那么向上转型向下转型有什么作用呢?
比如说下面这个方法,我是不知道我会接收到猫还是狗,我只知道接收到的一定是动物,那我可以用向下转型来判断;比如说把动物还原回狗

而别人如果给的是狗,即giveMeAPet(new Dog());,则在传参的过程中自动使用了向上转型,把狗转变为动物

  • demo08

案例:笔记本电脑



USB接口作为统一规范,鼠标和键盘都向上转型成USB接口设备,笔记本再把它向下转型回原本的设备
代码实现

public interface USB {
    public abstract void open();//打开设备

    public abstract void close();//关闭设备
}
public class Mouse implements USB {
    @Override
    public void open() {
        System.out.println("打开鼠标");
    }

    @Override
    public void close() {
        System.out.println("关闭鼠标");
    }

    //特有方法
    public void click() {
        System.out.println("鼠标点击");
    }
}
public class Keyboard implements USB {
    @Override
    public void open() {
        System.out.println("打开键盘");
    }

    @Override
    public void close() {
        System.out.println("关闭键盘");
    }

    //特有方法
    public void type() {
        System.out.println("键盘输入");
    }
}
public class Computer {
    public void powerOn() {
        System.out.println("笔记本电脑开机");
    }

    public void powerOff() {
        System.out.println("笔记本电脑关机");
    }

    //使用USB设备的方法,使用接口作为方法的参数
    public void useDevice(USB usb) {
        usb.open();//打开设备
        if (usb instanceof Mouse) {
            Mouse mouse = (Mouse) usb;//向下转型
            mouse.click();
        } else if (usb instanceof Keyboard) {
            Keyboard keyboard = (Keyboard) usb;//向下转型
            keyboard.type();
        }
        usb.close();//关闭设备
    }
}
public class DemoMain {
    public static void main(String[] args) {
        //创建一个笔记本电脑对象
        Computer computer = new Computer();
        computer.powerOn();//电脑开机

        //准备一个鼠标,供电脑使用
        //首先进行向上转型,把鼠标当作USB设备
        USB usbMouse = new Mouse();
        computer.useDevice(usbMouse);//打开鼠标,鼠标点击,关闭鼠标

        //准备一个键盘
        Keyboard keyboard = new Keyboard();//没有用多态写法
        //方法参数是USB类型,传递进去的是实现类对象
        //传参的过程中自动完成了向上转型
        computer.useDevice(keyboard);//打开键盘,键盘输入,关闭键盘

        computer.powerOff();//电脑关机
    }
}

(五)final关键字

final关键字代表最终不可改变的
常见四种用法:
1.可以用来修饰一个
2.可以用来修饰一个方法
3.还可以用来修饰一个局部变量
4.还可以用来修饰一个成员变量

  • demo01

final关键字用于修饰类

含义:当前这个类不能有任何的子类
注意:一个类如果是final,那么其中所有的成员方法都无法进行覆盖重写

public final class MyClass {
    public void method() {
        System.out.println("方法执行");
    }
}

  • demo02

final关键字用于修饰成员方法

当ifnal关键字用来修饰一个成员方法的时候,这个方法就是最终方法,也就是不能被覆盖重写

public class Fu {
    public final void method() {
        System.out.println("父类方法执行");
    }
}


同时注意:final关键字和abstract关键字不能同时使用,互相矛盾,因为abstract是一定要被覆盖重写,而final是一定不能被覆盖重写

  • demo03

final关键字用来修饰局部变量

一旦使用final关键字来修饰局部变量,那么这个变量就不能进行更改

下面是正确写法,只要保证有唯一一次赋值即可

final int num;
num = 10;

注意

  1. 对于基本类型来说,不可变说的是变量当中的数据不可改变
  2. 对于引用类型来说,不可变说的是变量当中的地址值不可改变

    但是里面的内容是可以改变的,如下:
  • demo04

final关键字用来修饰成员变量

  1. 由于成员变量具有默认值,所以用了final的同时必须赋值,不会再给默认值了
  2. 对于final的成员变量,要么直接赋值,要么通过构造方法赋值
    直接赋值的话,不能使用相应的set方法

    如果使用构造方法赋值,则不允许存在空参构造方法,或者是该空参构造方法也有赋值
    必须保证类当中所以重载的构造方法,都最终会对final的成员变量进行赋值



    总结:尽管像String类型的成员变量有默认值NULL,但是被认为是没有意义的,所以报错。

(六)权限修饰符

Java中有四种修饰符
public>protected>(default)>private
注意:(default)不是关键字,而是什么都不写

(七)内部类

如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类
例如:身体和心脏的关系;汽车和发动机的关系
分类
1.成员内部类
2.局部内部类(包含匿名内部类)
注意
1.内用外,随意访问
2.外用内,需要内部类对象

  • demo01

成员内部类的定义

public class Body {//外部类

    public class Heart {//成员内部类

        //内部类的方法
        public void beat() {
            System.out.println("心脏跳动");
            System.out.println("我叫:" + name);//内部访问外部
        }
    }

    //外部类的成员变量
    private String name;

    //外部类的方法
    public void methodBody() {
        System.out.println("外部类的方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • demo02

成员内部类的使用

两种方式

  1. 间接方式:
    在外部类的方法当中,使用内部类;然后main只是调用外部类的方法

  2. 直接方式:
    外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
  • demo03

内部类的同名变量访问

  • demo04

局部内部类的定义

如果一个类是定义在一个方法内部的,那么这就是一个局部内部类
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了

public class Outer {

    public void methodOuter() {
        class Inner {//局部内部类
            int num = 10;

            public void methodInner() {
                System.out.println(num);//10
            }
        }

        //只有自己所属的方法才能调用
        Inner inner = new Inner();
        inner.methodInner();
    }
}
public class domain {
    public static void main(String[] args) {
        Outer obj = new Outer();
        obj.methodOuter();//10
    }
}

小结类的权限修饰符
1.外部类:public / (default)
2.成员内部类:public / protected / (default) / private
3.局部内部类:因为只有自己所属的方法才能调用,所以什么都不能写,这并非(default)

  • demo05

局部内部类的final问题

局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final的

JDK7之前,必须强制性写上final
JDK8开始,只要局部变量事实不变,那么final关键字可以省略

JDK7之后,可以不写final,但是必须保证不再改变,如果改变了依然报错


要加final的原因
1.new出来的对象存在堆内存
2.局部变量是跟着方法走的,在栈内存当中
3.方法运行结束之后,立刻出栈,局部变量就会立刻消失
4.new出来的对象会在当中持续存在,直到垃圾回收消失
5.只要保证变量一直不变,可以复制一份进对象,这样即使存在于中的局部变量消失了也无所谓

  • demo06

匿名内部类

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次
那么这种情况下就可以省略该类的定义,而改为使用匿名内部类
匿名内部类的定义格式

接口名称 对象名 = new 接口名称(){
        //覆盖重写接口中所有的抽象方法
};
  • 在介绍匿名内部类之前,先来看看普通的接口写法
public interface MyInterface {
    public abstract void method();
}
public class MyInterfaceImpl implements MyInterface {
    @Override
    public void method() {
        System.out.println("实现类覆盖重写了方法");
    }
}
public class DemoMain {
    public static void main(String[] args) {
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.method();
    }
}
  • 在来看看使用了匿名内部类之后是怎么样的
public interface MyInterface {
    public abstract void method();
}
public class DemoMain {
    public static void main(String[] args) {
        //使用匿名内部类
        MyInterface obj = new MyInterface() {
            @Override
            public void method() {
                System.out.println("匿名内部类实现了方法");
            }
        };
        obj.method();
    }
}

这样子我们可以省略掉实现类
对“new 接口名称(){…};”进行解析:
1.new代表创建对象的动作
2.接口名称就是匿名内部类需要实现的接口的名称
3.{…}这才是匿名内部类的内容

  • demo07

匿名内部类的注意事项

  1. 匿名内部类在创建对象的时候,只能使用唯一的一次
  2. 如果希望多次创建对象,而且类的内容意义的话,要么写两次匿名内部类(如下),要么像之前那样写一个实现类
  3. 使用了匿名内部类,而且省略了对象名称,也就是匿名对象
    匿名对象:在调用方法的时候只能调用唯一一次,如果希望同一个对象调用多次方法,必须给对象起名字
  4. 匿名内部类是省略了实现类或者子类;匿名对象是省略了对象名称
    强调:匿名内部类和匿名对象不是一回事!
  • demo08

类作为成员变量类型

//武器类
public class Weapon {

    private String code;//武器的代号

    public Weapon() {
    }

    public Weapon(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

武器这个类当成成员变量的类型

//游戏当中的英雄角色类
public class Hero {

    private String name;//英雄名字
    private int age;//英雄的年龄
    private Weapon weapon;//武器

    public Hero() {
    }

    public Hero(String name, int age, Weapon weapon) {
        this.name = name;
        this.age = age;
        this.weapon = weapon;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Weapon getWeapon() {
        return weapon;
    }

    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}

  • demo09

接口作为成员变量类型

public interface Skill {
    public abstract void use();//释放技能的抽象方法
}
public class Hero {

    private String name;//英雄的名称
    private Skill skill;//英雄的技能

    public Hero() {
    }

    public Hero(String name, Skill skill) {
        this.name = name;
        this.skill = skill;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Skill getSkill() {
        return skill;
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }
}

传统写法,使用实现类,如下:

public class SkillImpl implements Skill {
    @Override
    public void use() {
        System.out.println("法术攻击");
    }
}
public class DemoGame {
    public static void main(String[] args) {
        Hero hero = new Hero();
        hero.setName("zhangsan");

        //设置英雄的技能
        hero.setSkill(new SkillImpl());
    }
}

如果使用匿名内部类,则可以省略实现类,如下:

public class DemoGame {
    public static void main(String[] args) {
        Hero hero = new Hero();
        hero.setName("zhangsan");

        //设置英雄的技能
        hero.setSkill(new Skill() {
            @Override
            public void use() {
                System.out.println("法术攻击");
            }
        });
    }
}
  • demo10

接口作为方法的参数或返回值

public class DemoInterface {
    public static void main(String[] args) {
        //左边是接口名称,右边是实现类名称,这就是多态写法
        List<String> list = new ArrayList<>();
        List<String> result = addNames(list);
        for (int i = 0; i < result.size(); i++) {
            System.out.println(result.get(i));
        }
    }

    public static List<String> addNames(List<String> list) {
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        return list;
    }
}
发布了109 篇原创文章 · 获赞 2 · 访问量 1196

猜你喜欢

转载自blog.csdn.net/qq_42528769/article/details/104447115