Thinking In Java笔记(第七章 复用类)

第七章 复用类

复用代码是Java众多引人注目的功能之一,但想要成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它还必须能够做更多的事情。

Java中所有事物都是围绕着类来展开的。通过创建新类来复用代码,不必重新开头编写。此方法的窍门在于使用类而不破坏现有程序代码。本章中有两种代码重用机制来达到这一目的:

  1. 只需要在新的类中生成现有类的对象。由于新的类是由现有类的对象所组成的,这种方法通常成为组合。该方法只是复用了现有程序代码功能,而非形式上和之前的类有相似之处。
  2. 第二种方法更加细致,它按照现有类的类型来创建新类。无需改变现有类的形式,采用往现有类的形式并向其中添加新的代码。这种方式成为继承。继承是面向对象的基石之一。

7.1 组合语法

组合语法只需要将对象引用置于新类中即可。例如,假如现在需要一个新的类型,其中需要多个String对象,几个基本数据类型,以及另外一个类的对象。可以如下定义新的类型:

class Apple {
    private String s;
    Apple() {
        System.out.println("Apple()");
        s = "Constructed";
    }
    public String toString() {
        return s;
    }
}

public class Desk {
    private String s1, s2, s3;
    private Apple apple1;
    private int appleCount;
    private float deskHeight;
}

上面的例子中,新创建的类Desk运用了组合的语法,重用了部分代码,将3个String类型数据,2个基本类型数据和一个类的引用组合在一起了。

7.2 继承语法

继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时,总是在继承,因此除非已明确指出从其他类继承,否则就是在隐式的从Java的标准根类Object进行继承。

和组合语法不同,继承需要通过关键字extends来声明”新类的旧类相似”。当这么做时,会自动得到基类中所有的域和方法。例如:

class Pet {
    private String s = "The pet is ";
    public void append(String a) {
        s += a;
    }
    public void toString() {
        return s;
    }
}

public class Dog extends Pet {
    public void bark() {
        System.out.println("woo...");
    }
    public static void main(String[] args) {
        Dog d = new Dog();
        d.append("dog");
        System.out.println(d.toString());
        d.bark();
    }
}

上面的例子中,Dog类继承了Pet类,获得了Pet的域及方法。

7.2.1 初始化基类

继承并不只是复制基类的接口。当创建了一个导出类的对象时,该对象中包含了一个基类的子对象。这个自对象与用基类直接创建出来的对象是一样的。二者的区别在于,后者来自于外部,而基类的子对象被包含在了导出类对象内部。

在导出类中对基类的子对象也很关键,需要在导出类的构造方法中调用基类的构造器来执行初始化,Java会自动在导出类的构造器中插入对基类构造器的调用。例如:

class Animal {
Animal() {
    System.out.println("Animalss...");
}

Animal(int a) {
    System.out.println(a);
}
}

class Pet extends Animal {

Pet() {
    System.out.println("Pet...");
}

Pet(int a) {
    System.out.println(a);
}
}

public class JavaTest extends Pet{

JavaTest(int a) {
    System.out.println("JavaTest...");
}
public static void main(String[] args) {
    JavaTest jt = new JavaTest(1);
    }
}
输出结果为:
Animalss...
Pet...
JavaTest...

从上面的结果可以看出,Java编译器会帮我们自动调用基类的默认构造方法,如果想通过不同的构造器对内部基类进行初始化,可以通过super()方法来调用。构建过程是从基类”向外”扩散的,最开始调用基类的构造方法,再逐渐调用导出类的构造方法。

7.3 代理

代理在Java中没有直接的支持,是在继承和组合之间的中庸之道.代理这个东西还需要详细的了解,在后面会单独去学习,整理出一篇博客

7.4.2 名称屏蔽

Java中基类拥有某个已经被多次重载的方法名,在导出类中重新定义该方法名并不会屏蔽在基类中的任何版本。例如:

class BClass {
public void func() {
    System.out.println("1...");
}
public void func(int i) {
    System.out.println("2...");
}
public void func(String s) {
    System.out.println("3...");
}
}

class SClass extends BClass {

public void func(float a) {
    System.out.println("6...");
}
}

public class JavaTest{
public static void main(String[] args) {
    SClass s = new SClass();
    s.func(1f);
    s.func();
    s.func("");
}
}

运行结果为:
6...
1...
3...

在继承类中又重载了该方法名一次,但是在之前子类中重载的方法都还在。

7.7 向上转型

在Java的继承中,基类和导出类的关系可以概括为“导出类是基类的一种类型“。例如有一个名为Instrument的代表乐器的基类和一个名为Wind的导出类。由于继承可以确保基类中所有的方法在导出类中同样有效,所以能够向基类发送的所有信息也可以向导出类发送。看下面的例子:

class Instrument {
    public void play() {}
    static void tune(Insatument i) {
        // ...
        i.play();
    }
}

public class Wind extends Instrument {
    public static void main(String[] args) {
    Wind flute = new Wind();
    Instrument.tune(flute);
}
}

上面的例子中,tune()方法的参数能接受Instrument的引用,但是调用的时候传入的是Wind的一个引用。在tune方法中,程序代码可以对Instrument和它所有的导出类起作用,这种将导出类引用转换成基类引用的动作,我们称之为向上转型。向上转型的过程中,类接口中唯一可能发生的事情是丢失方法。由于是从一个较专用类型转换成一个通用类型,所以总是安全的。

7.7.2 再论组合与继承

在面向对象编程中,有一句话叫做“组合优于继承”,尽管继承被许多人多次强调,但不意味着尽可能的去使用继承。一个最清晰的判断方法就是要问一问自己是否需要从新类向基类进行向上转型。如果必须向上转型,则继承是必要的。后面会专门去写一篇博客来认真的思考,组合优于继承这句话。

7.8 final关键字

关于final关键字的说明,单独将其拿出来和static关键字做了一次理解,点击进入

时间: 2025-01-12 18:32:25

Thinking In Java笔记(第七章 复用类)的相关文章

《JAVA编程思想》学习笔记——第七章 复用类

复用类的主要方式有两种:组合,继承 组合 例: class A {} class B {A a;} 继承 继承是所有OOP语言和Java语言不可缺少的组成部分.当创建一个类时,总是在继承,因此,除非已明确指出要从其它类中继承,否则就是在隐式地从Java的标准根类Object进行继承. 继承适用关键词:extends,继承会自动得到基类中所有的域和方法 super super关键字表示超类的意思,当前类就是从超类继承来的.调用超类方法:super.funcName(); 初始化基类 无参构造器,J

《Java编程思想》笔记 第七章 复用类

1.组合 将其他类的对象引用置于新的类中. 3.继承 关键词extends  一个类继承基类后自动获得 基类的所有域(包括字段 引用 内部类 )和方法,当然不包括private,子类中调用继承下来的方法也不需要基类对象引用.继承相当于对基类的一个扩展,因为基类有的它都有,再额外添加了一些域和方法(或覆写方法)而已. 4.super 4.1 super.f() 调用基类的f()方法. 4.2 构造器中的super(args) 调用基类构造器,且只能在第一行. 4.3 由于构造器被重载后默认构造器不

第七章-复用类-继承语法-2初始化基类-带参构造器

书上代码示例: 1 package com.learnJava.test; 2 3 /** 4 * @Author zhuchangli 5 * @Date 2019/9/14 6 **/ 7 class Game{ 8 Game(int i){ 9 System.out.println("Game constructor"); 10 } 11 } 12 13 class BoardGame extends Game{ 14 BoardGame(int i){ 15 super(i);

第七章-复用类-代理

练习11:(3)修改 Detergent.java,让它使用代理. 一:我的答案: 1 package com.learnJava.test; 2 3 /** 4 * @Author zhuchangli 5 * @Date 2019/9/15 6 **/ 7 public class DetergentDelegation { 8 9 private Cleanser cleanser = new Cleanser(); 10 private String s = "Cleanser &quo

第七章-复用类-组合语法

课后习题:练习1:创建一个简单的类,在第二个类中,将引用定义为第一个类的对象.运用惰性初始化来实例化这个对象. 我的答案: 一,个人. 1 package com.learnJava.test; 2 3 /** 4 * @Author zhuchangli 5 * @Date 2019/9/14 6 **/ 7 8 // 灶具 9 class Cooker{ 10 private String pot; // 锅 11 private String gas; // 煤气 12 Cooker(){

第七章-复用类-继承语法-1

练习2: 从Detergent 中 继承产生一个新的类.覆盖scrub() 并添加一个名为sterilize() 的新的方法. 一:我的答案: 1 package com.learnJava.test; 2 3 /** 4 * @Author zhuchangli 5 * @Date 2019/9/14 6 **/ 7 class Cleanser{ 8 private String s = "Cleanser"; 9 public void append(String a) { s

Android群英传笔记——第七章:Android动画机制和使用技巧

Android群英传笔记--第七章:Android动画机制和使用技巧 想来,最近忙的不可开交,都把看书给冷落了,还有好几本没有看完呢,速度得加快了 今天看了第七章,Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有 Android视图动画' Android属性动画 Android动画

“全栈2019”Java第八十七章:类中嵌套接口的应用场景(拔高题)

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第八十七章:类中嵌套接口的应用场景(拔高题) 下一章 "全栈2019"Java第八十八章:接口中嵌套接口的应用场景 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"J

Java学习笔记—第七章 类的深入解析

第七章 类的深入解析 1. 继承 1.1 类继承的方法:在Java中,子类对父类的继承是在类的声明中使用extends关键字来指明的.其一    般格式为:[类修饰符] class <子类名> extends <父类名>{ 类体内容 }.一个类只能直接继承一个    父类,一个父类可以有多个子类. 1.2 成员变量的继承和隐藏:基于父类创建子类时,子类可以继承父类的成员变量和成员方法.但是,     如果在父类和子类中同时声明了一个同名变量,则这两个变量在程序运行时同时存在.即:父