Java - day11 - 面向对象进阶 - 包、权限修饰符、final、常量、枚举、抽象类、接口


Java - day11 - 面向对象进阶 - 包、权限修饰符、final、常量、枚举、抽象类、接口

导包

  • 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!
  • 导包格式: import包名.类名;

学完权限修饰符需要具备如下能力

能够识别别人定义的成员的访问范围。

自己定义成员(方法,成员变量,构造器等)一般需要满足如下要求:

  • 成员变量一般私有。
  • 方法一般公开。
  • 如果该成员只希望本类访问,使用private修饰。
  • 如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。

final的作用

  • final关键字是最终的意思,可以修饰(类、方法、变量)修饰类:表明该类是最终类,不能被继承。
  • 修饰方法:表明该方法是最终方法,不能被重写。
  • 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)

final修饰变量的注意

  • final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
  • final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。

案例 —— 说明

  • 现在开发的超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向。

实例:

import javax.swing.*;
import java.awt.event.ActionEvent;

/**
 目标: 常量的其他作用,做信息标志和信息分类(其实也是一种配置形式)
 */
public class mari {
    public static final int UP = 1; // 上
    public static final int DOWN = 2; // 上
    public static final int LEFT = 3; // 左
    public static final int RIGHT = 4; // 右

    public static void main(String[] args) {
        // 1、创建一个窗口对象(桌子)
        JFrame win = new JFrame();
        // 2、创建一个面板对象(桌布)
        JPanel panel = new JPanel();
        // 3、把桌布垫在桌子上
        win.add(panel);
        // 4、创建四个按钮对象
        JButton btn1 = new JButton("上");
        JButton btn2 = new JButton("下");
        JButton btn3 = new JButton("左");
        JButton btn4 = new JButton("右");
        // 5、把按钮对象添加到桌布上去
        panel.add(btn1);
        panel.add(btn2);
        panel.add(btn3);
        panel.add(btn4);
        // 6、显示窗口
        win.setLocationRelativeTo(null);
        win.setSize(300,400);
        win.setVisible(true);


        btn1.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(UP) ; // 让玛丽往上跳
            }
        });
        btn2.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(mari.DOWN) ; // 让玛丽往下跳
            }
        });
        btn3.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(LEFT) ; // 让玛丽往左跑
            }
        });
        btn4.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(RIGHT) ; // 让玛丽往右跑
            }
        });
    }

    public static void move(int flag){
        // 控制玛丽移动
        switch (flag) {
            case UP:
                System.out.println("玛丽往上飞了一下~~");
                break;
            case DOWN:
                System.out.println("玛丽往下蹲一下~~");
                break;
            case LEFT:
                System.out.println("玛丽往左跑~~");
                break;
            case RIGHT:
                System.out.println("玛丽往→跑~~");
                break;
        }
    }
}

选择常量做信息标志和分类:

  • 虽然可以实现可读性,但是入参值不受约束,代码相对不够严谨。

枚举做信息标志和分类:

  • 代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用!

实例:

package com.itheima.d5_enum;

/**
   做信息标志和分类
 */
public enum Orientation {
    UP, DOWN, LEFT, RIGHT;
}
package com.itheima.d5_enum;

import javax.swing.*;
import java.awt.event.ActionEvent;

/**
    目标: 常量的其他作用,做信息标志和信息分类(其实也是一种配置形式)
 */
public class EnumDemo2 {
    public static void main(String[] args) {
        // 1、创建一个窗口对象(桌子)
        JFrame win = new JFrame();
        // 2、创建一个面板对象(桌布)
        JPanel panel = new JPanel();
        // 3、把桌布垫在桌子上
        win.add(panel);
        // 4、创建四个按钮对象
        JButton btn1 = new JButton("上");
        JButton btn2 = new JButton("下");
        JButton btn3 = new JButton("左");
        JButton btn4 = new JButton("右");
        // 5、把按钮对象添加到桌布上去
        panel.add(btn1);
        panel.add(btn2);
        panel.add(btn3);
        panel.add(btn4);
        // 6、显示窗口
        win.setLocationRelativeTo(null);
        win.setSize(300,400);
        win.setVisible(true);


        btn1.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(Orientation.UP) ; // 让玛丽往上跳
            }
        });
        btn2.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(Orientation.DOWN) ; // 让玛丽往下跳
            }
        });
        btn3.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(Orientation.LEFT) ; // 让玛丽往左跑
            }
        });
        btn4.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move(Orientation.RIGHT) ; // 让玛丽往右跑
            }
        });
    }

    public static void move(Orientation o){
        // 控制玛丽移动
        switch (o) {
            case UP:
                System.out.println("玛丽往上飞了一下~~");
                break;
            case DOWN:
                System.out.println("玛丽往下蹲一下~~");
                break;
            case LEFT:
                System.out.println("玛丽往左跑~~");
                break;
            case RIGHT:
                System.out.println("玛丽往→跑~~");
                break;
        }
    }
}

实例:

package oop;

public class Test {
    public static void main(String[] args) {
        GoldCard c = new GoldCard();
        c.setMoney(10000); // 父类的
        c.setName("三石");
        c.pay(300);
        System.out.println("余额:" + c.getMoney());
    }
}
package oop;

/**
     抽象父类
 */
public abstract class Card {
    private String name; // 主人名称
    private double money;

    /**
      子类一定要支付的,但是每个子类支付的情况不一样,所以父类把支付定义成抽象方法,交给具体子类实现
     */
    public abstract void pay(double money);

    public String getName() {
        return name;
    }

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

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
package oop;

/**
   金卡
 */
public class GoldCard extends Card{
    @Override
    public void pay(double money) {
        // 优惠后的金额算出来:
        double rs = money * 0.8;
        double lastMoney = getMoney() - rs;
        System.out.println(getName() + "当前账户总金额:"
                + getMoney() +",当前消费了:" + rs +",消费后余额剩余:" +
                lastMoney);

        setMoney(lastMoney); // 更新账户对象余额
    }
}

抽象类的特征和注意事项

  • 类有的成员(成员变量、方法、构造器)抽象类都具备
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
  • 不能用abstract修饰变量、代码块、构造器。
  • 最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)

final和abstract是什么关系?

  • 互斥关系
  • abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
  • 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。

什么时候使用模板方法模式

  • 使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。

模板方法模式实现步骤

  • 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码。
  • 模板方法中不能决定的功能定义成抽象方法让具体子类去实现。

模板方法我们是建议使用final修饰的,这样会更专业,那么为什么呢?

  • 答:模板方法是给子类直接使用的,不是让子类重写的,
  • 一旦子类重写了模板方法,则模板方法就失效了,因此,加上final后可以
  • 防止子类重写了模板方法,这样更安全、专业

模板方法模式解决了什么问题?

  • 提高了代码的复用性
  • 模板方法已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现,因此,使用者只需要关心自己需要实现的功能即可。

接口的定义与特点

接口的格式如下:

//接口用关键字**interface**来定义
public interface 接口名 {
     //常量
     //抽象方法
}
  • JDK8之前接口中只能是抽象方法和常量,没有其他成分了。
  • 接口也是一种规范。
  • 规范一定是公开的,public abstract可以省略不写。

接口的用法:

  • 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。

    修饰符 class 实现类 implements 接口1,接口2,接口3 ,... {
    }
    
  • 实现的关键字: **implements**
  • 接口可以被类单实现,也可以被类多实现。

接口实现的注意事项:

  • 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。

实例:

package interface2;
public interface Law {
    void rule(); // 遵章守法
}
package interface2;
/**
   实现类(子类)
 */
public class PingPongMan implements SportMan , Law{
    private String name;
    public PingPongMan(String name) {
        this.name = name;
    }

    @Override
    public void rule() {
        System.out.println(name + "要遵章守法,不能随意外出,酗酒,约会~~~");
    }

    @Override
    public void run() {
        System.out.println(name + "必须要跑步训练~~");
    }

    @Override
    public void competition() {
        System.out.println(name + "需要参加国际比赛~~");
    }
}
package interface2;
public interface SportMan {
    void run();
    void competition();
}
package interface2;
public class Test {
    public static void main(String[] args) {
        PingPongMan p = new PingPongMan("张继科");
        p.rule();
        p.run();
        p.competition();
    }
}

接口与接口的关系:多继承

  • 接口和接口的关系:多继承,一个接口可以同时继承多个接口。

接口多继承的作用

  • 规范合并,整合多个接口为同一个接口,便于子类实现。

JDK8版本开始后,Java只对接口的成员方法进行了新增

如何能在丰富接口功能的同时又不对子类代码进行更改呢?

  • 允许接口中直接定义带有方法体的方法

第一种:默认方法

  • 类似之前写的普通实例方法:必须用default修饰默认会public修饰。
  • 需要用接口的实现类的对象来调用
default void run(){
     system.out.print1n("--开始跑--");
}

第二种:静态方法

  • 默认会public修饰,必须static修饰。
  • 注意:接口的静态方法必须用本身的接口名来调用。
static void inAddr(){
       System.out.println("学习Java! ");
}

第三种:私有方法

  • 就是私有的实例方法,必须使用private修饰,从JDK 1.9才开始有的。
  • 只能在本类中被其他的默认方法或者私有方法访问。
private void go(){
       system.out.println("--准备--");
}

接口的注意事项

  • 接口不能创建对象
  • 一个类实现多个接口,多个接口中有同样的静态方法不冲突。
  • 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
  • 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
  • 一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。

声明:三二一的一的二|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - Java - day11 - 面向对象进阶 - 包、权限修饰符、final、常量、枚举、抽象类、接口


三二一的一的二