第七章 复用类
复用代码 是 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 + "
" +
"wings = " + wings + "
" +
"tail = " + tail + "
" +
"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 FileTyporaThink 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 FileTyporaThink 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 总结
组合一般是将 现有类型 作为 新类型 底层实现的 一部分来 加以复用
继承是 复用其接口 可以 向上转型 至 基类
程序 开发 是一种 增量过程 , 犹如人类 学习 一样 视为一种 有机的,进化着的生命体去 培养,而不是盖摩天大楼。