zoukankan      html  css  js  c++  java
  • 第七章 复用类

    第七章 复用类

    复用代码 是 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 总结

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

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

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

  • 相关阅读:
    ASP.NET Core中的Action的返回值类型
    ASP.NET Core中的Controller
    ASP.NET Core Authentication and Authorization
    ASP.NET Core
    ASP.NET Core ActionFilter引发的一个EF异常
    使用Github Packages功能上传nuget包到Github
    「每日五分钟,玩转JVM」:线程共享区
    JVM(二):画骨
    Spring Boot 2.x (十八):邮件服务一文打尽
    一道面试题
  • 原文地址:https://www.cnblogs.com/AronJudge/p/14361113.html
Copyright © 2011-2022 走看看