java编程思想第四版第七章总结

简介: 实现类的复用通常有两种方式组合:在新的类中产生现有类的对象继承:按照现有类的类型来创造新类

1. 实现类的复用通常有两种方式


  • 组合:在新的类中产生现有类的对象


  • 继承:按照现有类的类型来创造新类


2. 一个特殊的方法toString()


  • 在非基本类型的对象中, 都有toString()方法


  • 当编译器需要一个String而你只有一个对象时, toString()方法会被自动调用


3. 父类方法的定义要求


  • 所有的方法都是public的.


  • 原因: 如果没有修饰符,则是包访问权限, 那么包以外的其他类继承了这个类, 依然不能访问这个父类的方法. 所以, 定义父类的公用方法需要时public的.


  • 为了继承一般的规则是: 成员变量是private的, 方法是public的.


4. 基类初始化


  • java会在子类中自动调用父类的构造器


  • 父类如果没有默认构造器, 子类需要用super显示的调用父类的构造器. 而且调用父类构造器,必须是子类构造器要做的第一件事


  • 基类的默认构造器, 也要显示的调用父类的有参构造器


5. 复用的第三种方式: 代理


  • 代理: java并不直接支持他, 它是继承和组合之间的一种中庸之道.


  • 无论我们使用的时组合, 还是继承. 都会暴露所有对象的成员方法


  • 组合:在类中new一个对象, 然后该对象的所有成员方法在这个类中就都可以被使用。


  • 继承:继承了父类, 那么父类的方法对这个子类来说, 更是完全可见的。


  • 这两种方法都会完全暴掠被使用的那个类的成员方法, 而有的时候, 我们不希望这样, 不想父类或者被调用类的成员方法被暴露在外, 就可以采用代理的方式, 如下例:太空船需要一个控制模块


package net.mindview.reusing;
public class SpaceShipControls {
    //velocity 速度
    void up(int velocity){}
    void down(int velocity){}
    void left(int velocity){}
    void right(int velocity){}
    void forward(int velocity){}
    void back(int velocity){}
    void turboBoost(int velocity){} //发动
}


构造一个太空船, 可以使用组合或者继承。 我们这里使用继承的方式构造。


package net.mindview.reusing;
public class SpaceShip extends SpaceShipControls {
    private String name;
    public SpaceShip(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return name;
    }
    public static void main(String[] args) {
        SpaceShip protector = new SpaceShip("太空号");
        protector.up(2000);
    }
}


然而, SpaceShip并非一个真正的SpaceShipControls类型。太空船是太空船, 并不是太空控制器, 只是使用了太空控制器而已。更准确的说:


SpaceShip包含SpaceShipControls。


而且, 一旦继承, SpaceShipControls的所有方法都在SpaceShip中暴露了。这时我们可以使用代理方式来解决这个问题:


package net.mindview.reusing;
/**
 * 太空船代理
 */
public class SpaceShipDelegation {
    String name;
    public SpaceShipControls controls = new SpaceShipControls();
    public SpaceShipDelegation(String name){
        this.name = name;
    }
    public void up(int velocity){
        controls.up(velocity);
    }
    void down(int velocity){
        controls.down(velocity);
    }
    void left(int velocity){
        controls.left(velocity);
    }
    void right(int velocity){
        controls.right(velocity);
    }
    void forward(int velocity){
        controls.forward(velocity);
    }
    void back(int velocity){
        controls.back(velocity);
    }
    void turboBoost(int velocity){//发动
        controls.turboBoost(velocity);
    }
    public static void main(String[] args) {
        SpaceShipDelegation protector = new SpaceShipDelegation("太空号2");
        protector.up(10000);
    }
}


  • 这就是使用了代理。 主要关注上面的方法是如何传递给底层的controls对象的。 其接口与使用继承得到的接口一样了。 然而,我们使用代理时却得到了更多的控制力。


6. 如何选择组合和继承


  • 组合通常是想在新类中使用现有类的功能而非他的接口。就是说,在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口。而非锁嵌入对象的接口。为取得这个效果, 应该将现有类定义为private的。看下面的这个例子:


package net.mindview.reusing;
class Engine {
    public void start(){}
    public void rev(){}
    public void stop(){}
}
class Wheel {
    public void inflate(int psi){}
}
class Window{
    public void rollup(){}
    public void rolldown(){}
}
class Door{
    public Window window = new Window();
    public void open(){}
    public void close(){}
}
public class Car {
    public Engine engine = new Engine();
    public Wheel[] wheels = new Wheel[4];
    public Door left = new Door(),right = new Door();
    public Car(){
        for(int i=0;i<4; i++){
            wheels[i] = new Wheel();
        }
    }
    public static void main(String[] args) {
        Car car = new Car();
        car.left.window.rollup();
        car.wheels[0].inflate(72);
    }
}


  • 这里面, 显示的car的组成部分, 所以使用的是组合的方式。 而如果有一个“交通工具”和car是什么关系呢? 显然他们车不是交通工具组成的。car确实一类交通工具, 所以,交通工具和car应该是继承的关系。


  • 向上转型


package net.mindview.reusing;
/**
 * 乐器
 */
class Instrument {
    public void play(){}
    //用乐器弹奏曲调
    static void tune(Instrument i){
        i.play();
    }
}
//乐器的一种
public class Wind extends Instrument{
    public static void main(String[] args) {
        Wind wind = new Wind();
        //使用wind弹奏曲调
        Instrument.tune(wind);
    }
}


  • 上面的Instrument类中, 接收的Instrument的引用,在使用的时候, 我们可以将继承了Instrument的乐器传给tune方法。这称之为向上转型。


  • 到底什么时候使用组合, 什么时候使用继承呢?


  • 一个最清晰的判断就是, 问问自己,是否需要从新类向基类向上转型。如果需要转型, 那么就必须使用继承,否则就要好好想想了, 是否有使用集成的必要性。


7.final关键字


  • final关键字的含义通常是“这是无法改变的


  • 作用于成员变量, 表示该变量编译时不可变。


  • 作用于成员变量, 和public static一起使用, 表示全局常量


  • 成员变量是对象类型时使用final表示, 对象的引用不可改变。

 

  • 空白final


  • 所谓“空白final“指的是, 在定义final变量的时候不给他赋初始值, 而是在构造函数中为其赋值。这样就提供了更大的灵活性。使用方法如下所示:


package net.mindview.reusing;
class Poppet {
    private int i;
    Poppet(int i){
        this.i = i;
    }
}
public class BlankFinal {
    //定义的时候赋初始值
    private final int i = 0;
    //空白final
    private final int j;
    //空白final引用
    private final Poppet p;
    public BlankFinal(){
        j = 1;
        p = new Poppet(1);
    }
    public BlankFinal(int x){
        j = x;
        p = new Poppet(x);
    }
    public static void main(String[] args) {
        new BlankFinal();
        new BlankFinal(4);
    }
}


在参数列表中以声明的方式将参数指明为final


  • 如果final作用的参数是对象,则表明在方法中无法更改参数引用所指向的对象。


  • 如果final作用的是基本类型的参数, 则表示不可以改变传递过来的参数值。


package net.mindview.reusing;
class Gizmo{
    public void spin() {}
}
//final作用于方法参数
public class FinalArguments {
    //final作用于对象类型的方法参数
    void with(final Gizmo g) {
        //在这里不可以对传递过来个g重新指向新的引用
        //g = new Gizmo();
    }
    void without(Gizmo g){
        g = new Gizmo();
        g.spin();
    }
    //final作用于基本类型的方法参数
    void f(final int i) {
        //在这里不可以对i重新赋值
        //i++;
    }
    int g(final int i) {
        return i+1;
    }
    public static void main(String[] args) {
        FinalArguments bf = new FinalArguments();
        bf.with(null);
        bf.without(null);
    }
}


  • final方法


  • 方法被定义final,则不可以被继承类修改。也就是确保方法的行为保持不变,不可被覆盖。


  • final 类


  • 当某各类被定义为final时,不可被继承

 

  忠告:再设计类的时候, 将方法定义为final的,应该说是明智的。你可能会觉得,没人想要覆盖你的方法,但预见类是如何被复用是很困难的, 特别是对于一个通用类而言,更是如此。如果你将方法定义为final的, 可以防止其他程序员在项目中通过继承来复用你的类。

 

相关文章
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
18 2
|
9天前
|
Java
死磕-java并发编程技术(二)
死磕-java并发编程技术(二)
|
9天前
|
存储 Java 调度
死磕-java并发编程技术(一)
死磕-java并发编程技术(一)
|
9天前
|
设计模式 缓存 Java
死磕-高效的Java编程(一)
死磕-高效的Java编程(一)
|
11天前
|
算法 Java
JAVA并发编程系列(8)CountDownLatch核心原理
面试中的编程题目“模拟拼团”,我们通过使用CountDownLatch来实现多线程条件下的拼团逻辑。此外,深入解析了CountDownLatch的核心原理及其内部实现机制,特别是`await()`方法的具体工作流程。通过详细分析源码与内部结构,帮助读者更好地理解并发编程的关键概念。
|
10天前
|
存储 Java
Java编程中的对象和类
【8月更文挑战第55天】在Java的世界中,“对象”与“类”是构建一切的基础。就像乐高积木一样,类定义了形状和结构,而对象则是根据这些设计拼装出来的具体作品。本篇文章将通过一个简单的例子,展示如何从零开始创建一个类,并利用它来制作我们的第一个Java对象。准备好让你的编程之旅起飞了吗?让我们一起来探索这个神奇的过程!
25 10
|
11天前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
11天前
|
Java
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
|
11天前
|
设计模式 安全 Java
Java 编程中的设计模式:单例模式的深度解析
【9月更文挑战第22天】在Java的世界里,单例模式就像是一位老练的舞者,轻盈地穿梭在对象创建的舞台上。它确保了一个类仅有一个实例,并提供全局访问点。这不仅仅是代码优雅的体现,更是资源管理的高手。我们将一起探索单例模式的奥秘,从基础实现到高级应用,再到它与现代Java版本的舞蹈,让我们揭开单例模式的面纱,一探究竟。
23 11
|
9天前
|
算法 安全 Java
JAVA并发编程系列(12)ThreadLocal就是这么简单|建议收藏
很多人都以为TreadLocal很难很深奥,尤其被问到ThreadLocal数据结构、以及如何发生的内存泄漏问题,候选人容易谈虎色变。 日常大家用这个的很少,甚至很多近10年资深研发人员,都没有用过ThreadLocal。本文由浅入深、并且才有通俗易懂方式全面分析ThreadLocal的应用场景、数据结构、内存泄漏问题。降低大家学习啃骨头的心理压力,希望可以帮助大家彻底掌握并应用这个核心技术到工作当中。
下一篇
无影云桌面