JAVA类复用的方式:组合和继承 --《JAVA编程思想》19

本文介绍了编程中代码复用的两种关键方式:组合与继承。组合通过类引用实现,而继承则涉及基类和导出类的关系,包括super关键字、protected成员和向上转型。理解这两种方式有助于提升代码效率和可维护性。
摘要由CSDN通过智能技术生成

在编程过程中,常常需要对一部分类进行重复利用,以减轻开发的工作量,也方便日后代码的维护和拓展。

实现代码复用主要有两种方式:组合和继承。

一、组合

组合:将需要使用的类引用置于新类中即可。

public class Room {

    String name;

    public Room(String name) {
        this.name = name;
    }

    public void cleanRoom() {
        System.out.println(name + " room cleaned");
    }
}
public class House {

    private String color;

    private Room room;

    public House(String color, Room room) {
        this.color = color;
        this.room = room;
    }

    public static void main(String[] args) {
        Room room = new Room("Lisa");
        House house = new House("white", room);
        house.room.cleanRoom();
    }
}
Lisa room cleaned

通过组合引用对象时,一定要确保对象在调用前被成功初始化,否则会抛出空指针异常。

可以通过三种方式初始化引用对象:

(1)定义对象时,直接初始化

public class House {

    private String color;

    private Room room = new Room("Lisa");

}

(2)在构造器中初始化

public class House {

    private String color;

    private Room room;

    public House(String color, Room room) {
        this.color = color;
        this.room = room;
    }
}

(3)在使用对象之前,初始化对象。这被称为惰性初始化,在不必每次都生成对象的场景下,可以减轻程序的负担。

public class House {

    private String color;

    private Room room;

    public House(String color) {
        this.color = color;
    }

    public static void main(String[] args) {
        House house = new House("white");
        if (house.room == null){
            house.room = new Room("Lisa");
        }
        house.room.cleanRoom();
    }
}

二、继承

继承:通过 extend 关键字声明继承其他类,获得基类中所有的成员和方法(基类中的 private 成员和方法无法获得)。倘若不声明,则默认隐式继承标准根类 Object (这也解释了为何任何一个类都默认带有 toString() 、equals() 方法 )。

下面依次来了解使用继承的几个注意点:

(1) super 关键字

通过继承创建的类被称为导出类,被继承的类称为基类。每个导出类内部都隐式包含了一个基类的子对象,在初始化导出类时,会优先初始化基类,初始化过程是根据继承的结构从上至下的。

public class Art {

    public Art() {
        System.out.println("Art init");
    }
}
public class Drawing extends  Art {

    public Drawing() {
        System.out.println("Drawing init");
    }
}

public class Cartoon extends Drawing {

    public Cartoon() {
        System.out.println("Cartoon init");
    }

    public static void main(String[] args) {
        Cartoon cartoon = new Cartoon();
    }
}
Art init
Drawing init
Cartoon init

假设基类只提供带参数的构造方法,在初始化导出类时,必须将参数传入基类的构造方法进行初始化。

public class Game {

    private String name;

    public Game(String name) {
        this.name = name;
        System.out.println("Game init");
    }
}

public class BoardGame extends Game {

    public BoardGame(String name) {
        super(name);
        System.out.println("BoardGame init");
    }
}

这里出现了一个新的关键字 super ,它用来表示继承的基类,super() 代表基类的构造方法。

除此之外,我们还可以通过 super 调用基类的其他方法。

public class Game {

    private String name;

    public Game(String name) {
        this.name = name;
        System.out.println("Game init");
    }

    @Override
    public String toString() {
        return "Game{" +
                "name='" + name + '\'' +
                '}';
    }
}`
public class BoardGame extends Game {

    public BoardGame(String name) {
        super(name);
        System.out.println("BoardGame init");
        System.out.println(super.toString());
    }

    public static void main(String[] args) {
        BoardGame boardGame = new BoardGame("chess");
    }
}
Game init
BoardGame init
Game{name='chess'}

(2) protected 关键字

在使用继承的过程中,我们想定义一些成员变量或者方法供导出类使用,又不想暴露给其他外部类,此时 protected 关键字便派上了用场。

protected 定义在成员变量或者方法前,此域便只能由同包类或者导出类可以访问。

public class Game {

    private String name;

    public Game(String name) {
        this.name = name;
        System.out.println("Game init");
    }

    protected void startGame() {
        System.out.println("game start");
    }

}
public class BoardGame extends Game {

    public BoardGame(String name) {
        super(name);
        System.out.println("BoardGame init");
        startGame();
    }

    public static void main(String[] args) {
        BoardGame boardGame = new BoardGame("chess");
    }
}
Game init
BoardGame init
game start

(3)向上转型

这里需要注意一个概念:导出类可以是基类,但基类不一定是导出类。比如:创建一个 Instrument 基类代表乐器,再创建一个 piano 导出类代表钢琴。我们可以说钢琴是乐器,但不能说乐器一定就是钢琴。

下面来看看在继承中,如何表述这种关系:

public class Instrument {

    private String color;

    public Instrument(String color) {
        this.color = color;
    }

    public void play() {
        System.out.println(color + " instrument play");
    }

    public static void tune(Instrument instrument) {
        instrument.play();
    }

}

public class Piano extends Instrument {

    public Piano(String color) {
        super(color);
    }
    
}

    public static void main(String[] args) {
        Instrument instrument = new Instrument("blue");
        Instrument.tune(instrument);
        System.out.println("----second tune-----");
        Piano piano = new Piano("black");
        Instrument.tune(piano);
    }
blue instrument play
----second tune-----
black instrument play

tune() 方法不仅可以接收 Instrument 类型的引用,还可以接收其导出类 Piano 的引用。这是因为程序对 piano 类执行了向上转型,将 Piano 转换为 Instrument 进行调用。因为导出类是包含基类所有方法的,所以往往是安全的。故在调用tune() 方法时,即使没有指定参数的转换类型,编译器也会自动执行向上转型。

小结

复用类的方式分为两种:

1.组合
通过直接引用复用类为自身成员变量。在调用复用类时,一定得确保复用类已被正确初始化。

2.继承
通过 extend 关键字继承复用类的成员变量和方法。

继承中可通过 super 关键字访问基类的成员变量和方法;

可通过 protected 关键字让成员或者方法供包外部的导出类访问;

当方法参数为基类时,可将导出类传入方法,触发自动向上转型进行调用。

关于如何选择组合和继承?

倘若不需要对复用类进行拓展,推荐直接使用组合;反正,则推荐使用继承。不过,我们还是应当慎重考虑使用继承的场景,因为继承会隐藏代码的具体实现,不利于代码的可读性和可维护性。

本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BaymaxCS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值