第七章 复用类

第七章 复用类

复用代码 是 Java 众多引人注目得功能之一

两种方法 达到复用

  • 组合 :新的类中产生现有类得对象,新的类是由现有类得对象所组成
  • 继承 : 按照现有类 来 创建新的类 采用现有类的形式并在其中添加代码

7.1 组合语法

一个对象 需要 多个 String对象 几个基本类型数据,以及另一个类的对象。

对于非基本类型对象 必须将其引用置于新的类中, 但可以直接定义基本类型数据

每个非基本类型对象 都有一个toString() 方法, 而且变异器需要一个 String而你却只有一个对象时,该方法便会被调用。

初始化引用

  • 在定义对象的地方。构造器调用之前就被初始化了
  • 在类的构造器中
  • 正要使用这些对象之前,惰性初始化
  • 使用实例初始化

练习一

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

public class no1 {
    private String fuselage, wings, tail;
    private Engine e; //e = null;
    public no1() {
        System.out.println("Inside no1()");
        fuselage = "Body";
        wings = "Airfoils";
        tail = "Empennage";
    }
    public String toString() {
        if(e == null) // lazy (delayed) initialization:
            e = new Engine();//构造器初始化  e这个引用指向 Engine 新的实例
        return "fuselage = " + fuselage + "\n " +
                "wings = " + wings + "\n" +
                "tail = " + tail + "\n " +
                "Engine = " + e;
    }
    public static void main(String[] args) {
        no1 N1234 = new no1();
        System.out.println(N1234);
    }
}
=====================================================================
Inside no1()
Engine()
fuselage = Body
 wings = Airfoils
tail = Empennage
 Engine = Constructed

7.2 继承语法

创建一个类的时候 除非已明确的 从 其他类继承 否则就是 隐式地 从Java 的标准根 类 object 进行继承

继承 会 得到 基类中 所有的 域和 方法

append 方法中 用 += 将几个String 对象 连接成s

为了继承 一般的规则是 将所有的数据成员都指定为 private 所有方法都指定为 public(protectde 成员也可以借助导出类来访问。)

**super 关键字 表示超类的意思 **

我们可以在 继承类 中 修改 基类 中的方法,我们如果想再调用基类中的 未被修改的方法 可以 super.method

练习二

import static org.greggordon.tools.Print.*;

public class Sterilizer extends Detergent {
	public void scrub() { append(" Sterilizer.scrub()"); }
	public void sterilize() { append(" sterilize()"); }
	public static void main(String[] args) {
		Sterilizer x = new Sterilizer();
		x.dilute();
		x.apply();	
		x.scrub();
		x.foam();
		x.sterilize();
		println(x);
		println("Testing base class:");
		Detergent.main(args);
	}
}

7.2.1 初始化基类

Java 会自动再导出类的构造器中 插入对基类构造器的调用

练习三

class Art {
    Art() {
        System.out.println("Art constructor"); }
}

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

public class no3 extends Drawing {
    public static void main(String[] args) {
        no3 x = new no3();
    }
}
==============================================================
Art constructor
Drawing constructor

练习四

class A { A(){
    System.out.println("A()");} }

class B extends A { B(){
    System.out.println("B()");} }

class C extends B { C(){
    System.out.println("C()");} }

class D extends C {
    D() {
        System.out.println("D()"); }
    public static D makeD() { return new D(); }
    public static void main(String[] args) {
        D d = new D();
        D d2 = makeD();
    }
}

public class no4 extends D {
    no4() {
        System.out.println("no4"); }
    public static void main(String[] args) {
        no4 e = new no4();
        // test D:
        D.main(args);
    }
}
=========================================================
A()
B()
C()
D()
E()
A()   
A()
B()
C()
D()

练习五

class A1 {
    A1(){
    System.out.println("A1()");}
}

class B1 extends A1 {
    B1(){
    System.out.println("B1()");}
}

class no5 extends A1 {
    B1 b1 = new B1(); // will then construct another A and then a B
    public static void main(String[] args) {
        no5 n5 = new no5(); // will construct an A first
        System.out.println("=========");
    }
}
=======================================================
A1()
A1()
B1()
=========

带参数构造器

如果想调用 一个 带参数的基类构造器。就必须用关键字 super显示的编写 调用基类的构造器,并配有参数列表

练习六

class Game {
    Game(int i) {
        System.out.println("Game constructor");
    }
}

class BoardGame extends Game {
    BoardGame(int i) {
        // print("BoardGame constructor"); // call to super must be first
        // statement in constructor 
        super(i); // else: "cannot find symbol: constructor Game()
        System.out.println("BoardGame constructor");
    }
}

public class no6 extends BoardGame {
    no6() {
        super(11);
        System.out.println("Chess constructor");
    }
    public static void main(String[] args) {
        no6 x = new no6();
    }
}
=================================================================
Game constructor
BoardGame constructor
Chess constructor

练习七

class A7 {
    A7(char c, int i) {
        System.out.println("A(char, int)");}
}

class B7 extends A7 {
    B7(String s, float f){
        super(' ', 0);
        System.out.println("B(String, float)");
    }
}

class no7 extends A7 {
    private char c;
    private int i;
    no7(char a, int j) {
        super(a, j);
        c = a;
        i = j;
    }
    B7 b = new B7("hi", 1f); // will then construct another A and then a B
    public static void main(String[] args) {
        no7 c = new no7('b', 2); // will construct an A first
    }
}
====================================================================
A(char, int)
A(char, int)
B(String, float)

练习八

class A8 {
    A8(char c, int i) {
        System.out.println("A(char, int)");}
}

class no8 extends A8 {
    private char c;
    private int i;
    no8() {
        super('z', 3);
        System.out.println("no8()");
    }
    no8(char a, int j) {
        super(a, j);
        c = a;
        i = j;
        System.out.println("no8(char,int)");
    }
    public static void main(String[] args) {
        no8 ex1 = new no8();
        no8 ex2 = new no8('b', 2);
    }
}
=======================================================
A(char, int)
no8()
A(char, int)
no8(char,int)

练习九

package 第七章服用类;

class Component1 {
    Component1() {
        System.out.println("Component1()"); }
}

class Component2 {
    Component2() {
        System.out.println("Component2()"); }
}

class Component3 {
    Component3() {
        System.out.println("Component3()"); }
}

class Root {
    Component1 c1root  = new Component1();
    Component2 c2root;
    Component3 c3root;
    Root() {
        System.out.println("Root()"); }
}

class no9 extends Root {
    Component1 c1no9;
    Component2 c2no9;
    Component3 c3no9;
    no9() {
        System.out.println("no9()"); }
    public static void main(String[] args) {
        no9 s = new no9();
    }
}
=======================================================
Component1()
Root()
no9()

练习十

class Component10 {
    Component10(byte b) {
        System.out.println("Component10(byte)"); }
}

class Component20 {
    Component20(short s) {
        System.out.println("Component20(short)"); }
}

class Component30 {
    Component30(int i) {
        System.out.println("Component30(int)"); }
}

class Root10 {
    Component10 c1root;
    Component20 c2root;
    Component30 c3root;
    Root10(float f) {
        c1root = new Component10((byte)0);
        c2root = new Component20((short)0);
        c3root = new Component30(0);
        System.out.println("Root(foat)");
    }
}

class no10 extends Root10 {
    Component10 c1stem10;
    Component20 c2stem10;
    Component30 c3stem10;
    no10(double d) {
        super(2.78f);
        c1stem10 = new Component10((byte)1);
        c2stem10 = new Component20((short)1);
        c3stem10 = new Component30(1);
        System.out.println("Stem10(double)");
    }
    public static void main(String[] args) {
        no10 x = new no10(2.78);
    }
}
================================================================
Component10(byte)
Component20(short)
Component30(int)
Root(foat)
Component10(byte)
Component20(short)
Component30(int)
Stem10(double)

7.3 代理

这是继承和组合的中庸之道 将一个 成员对象 置于所要构造的类中(就像组合),但于此同时 我们在新类中暴露了 该成员对象的所有方法(就像继承)**

练习十一

class Cleanser {
    private String s = "Cleanser";
    public void append(String a) { s += a; }
    public void dilute() { append(" dilute()"); }
    public void apply() { append(" apply()"); }
    public void scrub() { append(" scrub()"); }
    public String toString() { return s; }//print 会自动调用 toString 方法
    public static void main(String[] args) {
        Cleanser x = new Cleanser();
        x.dilute();
        x.apply();
        x.scrub();
        System.out.println(x);
    }
}

public class no11 {
    private String s = "no11";
    Cleanser c = new Cleanser();
    public void append(String a) { s += a; }
    // two methods delegated entirely to Cleanser c:
    public void dilute() {
        c.dilute();
    }
    public void apply() {
        c.apply();
    }
    // method delegated in part to Cleanser c:
    public void scrub() {
        append(" scrub()");
        c.scrub();
    }
    public void foam() { append(" foam()"); }
    public String toString() { return s + " " + c; }
    public static void main(String[] args) {
        no11 x = new no11();
        x.dilute();
        x.apply();
        x.scrub();
        x.foam();
        System.out.println(x);
        System.out.println("Testing base class:");
        Cleanser.main(args);
    }
}
==================================================================
no11 scrub() foam() Cleanser dilute() apply() scrub()
Testing base class:
Cleanser dilute() apply() scrub()

7.4 结合使用 组合 和 继承

配合 以 必要的 构造器来初始化 , 来 创建更复杂的类

7.4.1 确保正确清理

如果你想要某个类清理一些东西,就必须显示地编写一个特殊方法做这件事 确保客户端程序员显示的调用这一方法 必须置于 finally语句中,以防止异常出现

练习12

class Componenta {
    Componenta() {
        System.out.println("Componenta()"); }
    void dispose() {
        System.out.println("Componenta.dispose()"); }
}

class Componentb {
    Componentb() {
        System.out.println("Componentb()"); }
    void dispose() {
        System.out.println("Componentb.dispose()"); }
}

class Componentc {
    Componentc() {
        System.out.println("Componentc()"); }
    void dispose() {
        System.out.println("Componentc.dispose()"); }
}

class Root2 {
    Componenta c1root;
    Componentb c2root;
    Componentc c3root;
    Root2() {
        System.out.println("Root()");
        c1root = new Componenta();
        c2root = new Componentb();
        c3root = new Componentc();
    }
    void dispose() {
        c3root.dispose();
        c2root.dispose();
        c1root.dispose();
        System.out.println("Root2.dispose()");
    }
}

class no12 extends Root2 {
    Componenta c1no12;
    Componentb c2no12;
    Componentc c3no12;
    no12() {
        super();
        System.out.println("no12()");
        c1no12 = new Componenta();
        c2no12 = new Componentb();
        c3no12 = new Componentc();
    }
    void dispose() { // 反向清楚
        c3no12.dispose();
        c2no12.dispose();
        c1no12.dispose();
        super.dispose();
        System.out.println("no12.dispose()");
    }
    public static void main(String[] args) {
        no12 s = new no12();
        try {
            // Code and exception handling...
        } finally {
            s.dispose();
        }
    }
}
=======================================================
Root()
Componenta()
Componentb()
Componentc()
no12()
Componenta()
Componentb()
Componentc()
Componentc.dispose()
Componentb.dispose()
Componenta.dispose()
Componentc.dispose()
Componentb.dispose()
Componenta.dispose()
Root2.dispose()
no12.dispose()

7.4.2 名称屏蔽

@override

当你想覆写某个方法的时候 你可以选择添加这个注解

练习十三

class ThreeWay {
	void number(byte b) { println(b); }
	void number(short s) { println(s); }
	void number(int i) { println(i); }
}

class Overload extends ThreeWay {
	void number(float f) { println(f); }
	public static void main(String[] args) {
		Overload ov = new Overload();
		ov.number((byte)0);
		ov.number((short)1);
		ov.number(2);
		ov.number(3.0f);
	}
}
=========================================
0
1
2
3.0

7.5 在组合与继承之间选择

  • 组合 是显示地这样做

  • 继承 是隐式地做

  • 组合技术 通常 用于想在新类中使用 现有类的功能而并非它的接口这种情形 即在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口。需要在新类中嵌入一个现有类的private对象

  • 允许类的用户直接访问新类中的组合成分是极具意义的;将成员对象 声明为 public

  • is - a 是一个的关系 继承

  • has - a 有一个 的关系 组成

练习十四

class Engine {
    public void start() {}
    public void rev() {}
    public void stop() {}
    public void service() { System.out.println("service engine"); }
}

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[] wheel = new Wheel[4];
    public Door
            left = new Door(),
            right = new Door(); // 2-door
    public Car() {
        for(int i = 0; i < 4; i++)
            wheel[i] = new Wheel();
    }
    public static void main(String[] args) {
        Car car = new Car();
        car.left.window.rollup();
        car.wheel[0].inflate(72);
        car.engine.service();
    }
}
======================================================
service engine

7.6 protected 关键字

​ 在理想世界中 , 仅靠 private就以及足够了。 但实际项目中,经常会想要某些事物尽可能的对这个世界隐藏起来。但任允许导出类访问他们

**对类用户而言,这是private的,但对任何继承类或其他位于同一个包内的类来说是 可以 访问的 **

练习十五

* package reusing.ex15;
* public class BasicDevice {
*	private String s = "Original";
*	public BasicDevice() {	s = "Original"; }
*	protected void changeS(String c) { // outside package, only derived 
*		s = c;			// classes can access protected method	
*	}
*	public void showS() {
*		System.out.println(s);
*	}
* }
*/

import reusing.ex15.*;

class DeviceFail {	
	public static void main(String[] s) {
		BasicDevice fail = new BasicDevice();
		fail.showS();
		// fail.changeS("good-bye"); // cannot access protected method 	
	}
}

public class Device extends BasicDevice {
	void changeBasic(String t) {
		super.changeS(t); // calls protected method
	}	
	public static void main(String[] args) {
		Device d = new Device();
		d.showS();
		d.changeBasic("Changed"); // derived class can access protected
		d.showS();
		DeviceFail.main(args);
	}
}
=================================================================
Original
Changed
Original

7.7 向上转型

继承中最重要的方面是 用来表现新类和基类之间的关系:

新类是现有类的一种类型

新类的对象· 同样 也是 一种 基类 对象

将 新类的引用 转换成 基类的引用的 动作 称之为 向上转型

![image-20210131195949998](E:\Work File\Typora\Think of Java\向上转型.png)

7.7.1 为什么 称之为 向上 转型

向上转型 是 一个较专用类型 向 较通用类型 转换,所以 总是很安全。类接口唯一可能发生的事情是丢失方法

7.7.2 再论 组合 与 继承

在面向 对象编程中 ,生成和使用程序代码 最有可能 采用的 方法就是 直接将数据 和 方法 包装仅 一个类中,并使用 该类的对象

练习十六

class Amphibian {
    protected void swim() {
        System.out.println("Amphibian swim");
    }
    protected void speak() {
        System.out.println("Amphibian speak");
    }
    void eat() {
        System.out.println("Amphibian eat");
    }
    static void grow(Amphibian a) {
        System.out.println("Amphibian grow");
        a.eat();
    }
}

public class Frog extends Amphibian {
    public static void main(String[] args) {
        Frog f = new Frog();
        // call base-class methods:
        f.swim();
        f.speak();
        f.eat();
        // upcast Frog to Amphibian argument:
        Amphibian.grow(f);
    }
}
================================================
Amphibian swim
Amphibian speak
Amphibian eat
Amphibian grow
Amphibian eat

练习十七

class Amphibian1 {
    protected void swim() {
        System.out.println("Amphibian1 swim");
    }
    protected void speak() {
        System.out.println("Amphibian1 speak");
    }
    void eat() {
        System.out.println("Amphibian1 eat");
    }
    static void grow(Amphibian1 a) {
        System.out.println("Amphibian1 grow");
        a.eat();
    }
}

public class Frog17 extends Amphibian1 {
    @Override protected void swim() {
        System.out.println("Frog swim");
    }
    @Override protected void speak() {
        System.out.println("Frog speak");
    }
    @Override void eat() {
        System.out.println("Frog eat");
    }
    static void grow(Amphibian1 a) {
        System.out.println("Frog grow");
        a.eat();
    }
    public static void main(String[] args) {
        Frog17 f = new Frog17();
        // call overridden base-class methods:
        f.swim();
        f.speak();
        f.eat();
        // upcast Frog17 to Amphibian argument:
        f.grow(f);
        // upcast Frog17 to Amphibian and call Amphibian method:
        Amphibian1.grow(f);
    }
}
=======================================================================
Frog swim
Frog speak
Frog eat
Frog grow
Frog eat
Amphibian1 grow
Frog eat

7.8 final关键字

  • 数据
  • 方法

7.8.1 final 数据

许多编程语言 都有某种方法,向编译器告知 数据是 恒定不变的。有时数据的恒定不变时 很有用的

  • 一个永不改变的编译时常量
  • 一个在运行时被初始化的值,而你不希望它被改变

​ 一个 即使 static 优势 final 的域 值占据 一段不能改变的存储空间

当对 对象 引用 而不是 基本 类型 引用 final 时,其含义 会有一点令人迷惑

  • 基本类型 final 使数值恒定不变
  • 使引用 恒定不变

public static final int VALUE_THREE = 99

  • public 可以 被用于 包外
  • static 强调只有 一份
  • final 是一个常量

private final int i4 = rand.nextInt(20);

static final int INT_5 = rand.nextInt(20);

final 数据定义为 静态 和 非静态的 区别。 此区别只有当数值在运行时初始化时才会显现

i4 可以 通过 创建 多个对象而加以改变 但是 INT_5 不行 因为 static在装置时已经被初始化,而不是每次创建对象的时都初始化

private Value v1 = new Value(11);

private final Value v2 = new Value(22); 无法将 v2 再次指向 另一个 新对象

private static final Value VAL_3 = new Value(33);

使引用成为 final 没有 使 基本类型 称为 fianl 的用处大

练习十八

class Test {
    Test() {
        System.out.println("Test()"); }
}

public class no18 {
    private String name;

    public no18(String s) {
        name = s;
    }

    static final Test sft = new Test(); // constant reference address  // 只执行了一次 初始化 后面 再也不变 所以输出 三个
    private final Test ft = new Test();
    static final String SFS = "static final"; // class constant
    private final String fs = "final";
    private static Random rand = new Random();
    static final int SFI = rand.nextInt(); // class constant
    private final int fi = rand.nextInt();

    public String toString() {
        return (name + ": " + sft + ", " + ft + ", " + SFS + ", " + fs + ", " + SFI + ", " + fi);
    }

    public static void main(String[] args) {
        no18 d1 = new no18("d1");
        no18 d2 = new no18("d2");
        no18 d3 = new no18("d3");
        System.out.println(d1);
        System.out.println(d2);
        System.out.println(d3);
    }
}
=========================================================================
Test()
Test()
Test()
Test()
d1: 第七章服用类.Test@4554617c, 第七章服用类.Test@74a14482, static final, final, 861975556, -1670600495
d2: 第七章服用类.Test@4554617c, 第七章服用类.Test@1540e19d, static final, final, 861975556, -413232524
d3: 第七章服用类.Test@4554617c, 第七章服用类.Test@677327b6, static final, final, 861975556, -592188869 

空白 final

Java 允许 生成 “空白 final” .

空白 final 是指 被 声明为 final 但又未 给定 初值 的 域。

必须在 域 的 定义处 或者 每个 构造器 中 表达式对final 进行 赋值,这是 final 域 唉 使用 前 总是被初始化的原因所在。

![image-20210131232446185](E:\Work File\Typora\Think of Java\空final.png)

final 参数

Java 允许 参数 列表 以 声明的方式 将 参数指明为 final 你可以 都 参数 但却 无法修改参数

7.8.2 final 方法

使用 final 方法 两个 原因

  • 方法锁定 以防止任何继承类 修改 它的含义 确保 在 继承中使用方法行为 保存不变 并且不会被覆盖
  • 效率 如果 将 一个方法指明为 final 就是同意 编译器针对该方法的所有调用都转为 内嵌调用

发现 一个 final 方法

  • 跳过 插入程序 代码 这种正常方式而执行方法调用机制(将参数 压入栈,跳至方法代码处并执行,然后跳回并清理 栈中参数 处理返回值)
  • 以方法体中的实际代码副本来替代方法调用
  • 消除了 方法调用的开销

在 Java5/6时,应该让编译器和JVM去处理效率问题只有在想要明确禁止覆盖时,才将方法设置为final的

final 和 private 关键字

​ 类中所有的 private 方法 可以 隐式的指定为 final

覆盖 只有某方法时 基类接口的 一部分时才会出现。即 ,必须能将一个对象向上转型为 它的基本类型 并 调用相同的方法。

final 域 无法 被覆盖

练习二十

class WithFinals {
    // Identical to private alone:
    private final void f() {
        System.out.println("WithFinals.f()"); }
    // Also automatically "final":
    private void g() {
        System.out.println("WithFinals.g()"); }
}

class OverridingPrivate extends WithFinals {
    // attempt to override:
    private final void f() {
        System.out.println("OverridingPrivate.f()"); }
    private void g() {
        System.out.println("OverridingPrivate.g()"); }
    // @Override: compiler finds error - does NOT override
    // @Override private final void f() { print("OverridingPrivate.f()"); }
    // @Override private void g() { print("OverridingPrivate.g()"); }
}

class OverridingPrivate2 extends OverridingPrivate {
    // attempt to override:
    public final void f() {
        System.out.println("OverridingPrivate2.f()"); }
    public void g() {
        System.out.println("OverridingPrivate2.g()"); }
    // use @Override so compiler with say "does NOT override or implement"
    // @Override public final void f() { print("OverridingPrivate2.f()"); }
    // @Override public void g() { print("OverridingPrivate2.g()"); }
}

public class FinalOverridingIllusionEx {
    public static void main(String[] args) {
        OverridingPrivate2 op2 = new OverridingPrivate2();
        op2.f();
        op2.g();
        // You can upcast:
        OverridingPrivate op = op2;
        // But you can't call the methods:
        //! op.f(); // f() has private access in OverridingPrivate
        //! op.f();
        // Same here:
        WithFinals wf = op2;
        //! wf.f(); // f() has private access in WithFinals
        //! wf.g();
    }
}
=============================================================================
OverridingPrivate2.f()
OverridingPrivate2.g()

练习二十一

class WithFinal {
    final void f() {
        System.out.println("WithFinal.f()"); }
    void g() {
        System.out.println("WithFinal.g()"); }
    final void h() {
        System.out.println("WitFinal.h()"); }
}

class OverrideFinal extends WithFinal {
    // attempt to override:
    // public final void f() { print("OverrideFinal.f()"); } // no can do
    @Override public void g() {
        System.out.println("OverrideFinal.g()"); } // OK, not final
    // final void h(); { print("OVerrideFinal.h()"); } // cannot override final
}

public class no22 {
    public static void main(String[] args) {
        OverrideFinal of = new OverrideFinal();
        of.f();
        of.g();
        of.h();
        // Upcast:
        WithFinal wf = of;
        wf.f();
        wf.g();//方法被重写了
        wf.h();
    }
}
===============================================================
WithFinal.f()
OverrideFinal.g()
WitFinal.h()
WithFinal.f()
WithFinal.g()
WitFinal.h()

7.8.3 final 类

一个 final 类 不能 被 继承 可能出于安全考虑,该类的设计 用不需要改变

练习 二十二

class SmallBrain {}

final class Dinosaur {
    SmallBrain x = new SmallBrain();
}

// class Further extends Dinosaur {} // error: cannot inherit from final Dinosaur

public class no22 {
    public static void main(String[] args) {
        Dinosaur d = new Dinosaur();
    }
}

7.9 初始化及类的加载

在许多语言中 程序作为启动过程的一部分 被加载。然后是 初始化,紧接着程序开始运行。初始化必须小心控制。

Java 采用了一种不同的加载方式,记住,每个类的编译代码都存在自己的独立文件中,该文件只需要使用程序代码时才会被加载。初次使用被加载 加载 发生于创建类的第一个对象之时。 但是 当 访问 static 域 或者 方法时 也会被加载

7.9.1 继承与初始化

继承在内 的 初始化 全过程 , 以对所发生的一切有个全局性 把握。

  • 在对主方法加载的过程中(加载器找到主类的编译代码 (类名.class文件))
  • 编译器注意到它有一个 基类 (extends 得知), 于是它对基类进行加载(不管你是否要产生一个对象 这都要发生)
  • 如果该基类还有其自身的 基类 , 那么继续加载向 上一个 基类,
  • 跟基类中 的static 初始化 即会被执行,然后 导出类的 static 初始化(这样 很重要 因为导出类的 初始化 可能 依赖 基类的初始化)
  • 所有的类 加载完毕 对象就可以被创建了
  • 对象中 所有 基本 类型都会被 设为 默认值 引用被设为 null( 这是 通过 将 对象 内存 设为 二进制 0 值 而一举生成的)
  • 然后 基类的 构造器 会被 调用 (可以 自动 也可以 用 super 来 指定 基类 构造器的 调用)
  • 基类 构造器 和 导出类 构造器一样 以 相同 的顺序 来 经历相同的 过程
  • 基类 构造器 完成 之后 实例 变量 按其顺序 被 初始化

练习二十三

class A23 {
    static int j = printInit("A23.j initialized");
    static int printInit(String s) {
        System.out.println(s);
        return 1;
    }
    A23() {
        System.out.println("A23() constructor"); }
}

class B23 extends A23 {
    static int k = A23.printInit("B23.k initialized");
    B23() {
        System.out.println("B23() constructor"); }
}

class C23 {
    static int n = printInitC("C23.n initialized");
    static A23 a = new A23();
    C23() {
        System.out.println("C23() constructor"); }
    static int printInitC(String s) {
        System.out.println(s);
        return 1; 
    }
}

class D23 {
    D23() {
        System.out.println("D23() constructor"); }
}

public class no23 extends B23 {
    static int i = A23.printInit("no23.i initialized");
    no23() {
        System.out.println("no23() constructor"); }
    public static void main(String[] args) {
        // accessing static main causes loading (and initialization)
        // of A, B, & no23
        System.out.println("hi");
        // call constructors from loaded classes:
        no23 lc = new no23();
        // call to static field loads & initializes C:
        System.out.println(C23.a);
        // call to constructor loads D:
        D23 d = new D23();
    }
}
===========================================================================
A23.j initialized
B23.k initialized
no23.i initialized
hi
A23() constructor
B23() constructor
no23() constructor
C23.n initialized
A23() constructor
第七章服用类.A23@1b6d3586
D23() constructor

练习二十四

class Insect {
    private int i = 9;
    protected int j;
    Insect() {
        System.out.println("i = " + i + ", j = " + j);
        j = 39;
    }
    private static int x1 = printInit("static Insect.x1 initialized");
    static int printInit(String s) {
        System.out.println(s);
        return 47;
    }
}

class Beetle extends Insect {
    private int k = printInit("Beetle.k initialized");
    public Beetle() {
        System.out.println("k = " + k);
        System.out.println("j = " + j);
    }
    private static int x2 = printInit("static Beetle.x2 initialized");
}

public class no24 extends Beetle {
    private int n = printInit("Scarab.n initialized");
    public no24() {
        System.out.println("n = " + n);
        System.out.println("j = " + j);
    }
    private static int x3 = printInit("static Scarab.x3 initialized");
    public static void main(String[] args) {
        System.out.println("Scarab constructor");
        no24 sc = new no24();
    }
}
=======================================================================
static Insect.x1 initialized
static Beetle.x2 initialized
static Scarab.x3 initialized
Scarab constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
Scarab.n initialized
n = 47
j = 3

7.10 总结

组合一般是将 现有类型 作为 新类型 底层实现的 一部分来 加以复用

继承是 复用其接口 可以 向上转型 至 基类

程序 开发 是一种 增量过程 , 犹如人类 学习 一样 视为一种 有机的,进化着的生命体去 培养,而不是盖摩天大楼。

posted @ 2021-02-02 11:39  AronJudge  阅读(76)  评论(0编辑  收藏  举报