今天看啥  ›  专栏  ›  zheng_zq

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

zheng_zq  · 简书  ·  · 2020-02-20 22:10

概述

(一)继承
(二)抽象类
(三)接口
(四)多态
(五)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. super this 两种构造调用,不能同时使用

    因为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;
    }
}



原文地址:访问原文地址
快照地址: 访问文章快照