zoukankan      html  css  js  c++  java
  • ThinkJava-复用类

    7 .2 继承语法
    例子:
    package com.cy.reusing;
    
    import static com.java.util.Print.*;
    
    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; 
      }
      public static void main(String[] args) {
        Cleanser x = new Cleanser();
        x.dilute(); x.apply(); x.scrub();
        print(x);
      }
    }    
    
    public class Detergent extends Cleanser {
      // Change a method:
      public void scrub() {
        append(" Detergent.scrub()");
        super.scrub(); // Call base-class version
      }
      // Add methods to the interface:
      public void foam() { append(" foam()"); }
      // Test the new class:
      public static void main(String[] args) {
        Detergent x = new Detergent();
        x.dilute();
        x.apply();
        x.scrub();
        x.foam();
        print(x);
        print("Testing base class:");
        Cleanser.main(args);
      }    
    } 
    /* Output:
    Cleanser dilute() apply() Detergent.scrub() scrub() foam()
    Testing base class:
    Cleanser dilute() apply() scrub()
    *///:~
    View Code
    Cleanser中所有的方法都必须是public的,这一点非常重要。请记住,如果没有加任何访问
    权限修饰词,那么成员默认的访问权限是包访问权限, 它仅允许包内的成员访问。因此,在此
    包中,如果没有访问权限修饰词,任何人都可以使用这些方法。例如, Detergent就不成问题。
    但是,其他包中的某个类若要从CIeanser中继承,则只能访问public成员。所以,为了继承, 一
    般的规则是将所有的数据成员都指定为private ,将所有的方法指定为public (稍后将会学到,
    protected成员也可以借助导出类来访问)。当然,在特殊情况下,必须做出调整,但上述方法的
    确是一个很有用的规则。
    正如我们在scrub()中所见,使用基类中定义的方法及对它进行修改是可行的。在此例中,
    你可能想要在新版本中调用从基类继承而来的方法。但是在scrub()中,并不能直接调用scrub() ,
    因为这样做将会产生递归,而这并不是你所期望的。为解决此问题, Java用super关键字表示超
    类的意思,当前类就是从超类继承来的。为此,表达式super.scrub()将调用基类版本的scrub()
    方法.
     
     
     
    7.8 final关键字
    7.8.1 final 数据
    一个既是static又是final的域只占据一段不能改变的存储空间。
    当对对象引用而不是基本类型运用final时, 其含义会有一点令人迷惑。对于基本类型,
    final使数值恒定不变; 而用于对象引用, final使引用恒定不.变。一旦引用被初始化指向一个对
    象,就无法再把它改为指向另一个对象。然而,对象其自身却是可以被修改的, Java并未提供
    使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果)。这一限制同样
    适用数组,它也是对象。
     
    fínal 参数:
    Java允许在参数列表中以声明的方式将参数指明为final。这意味着你无法在方法中更改参数
    引用所指向的对象:
    package com.cy.reusing;
    
    class Gizmo {
      public void spin() {}
    }
    
    public class FinalArguments {
      void with(final Gizmo g) {
        //! g = new Gizmo(); // Illegal -- g is final
      }
      void without(Gizmo g) {
        g = new Gizmo(); // OK -- g not final
        g.spin();
      }
      // void f(final int i) { i++; } // Can't change
      // You can only read from a final primitive:
      int g(final int i) { 
          return i + 1; 
      }
      public static void main(String[] args) {
        FinalArguments bf = new FinalArguments();
        bf.without(null);
        bf.with(null);
      }
    } ///:~
    View Code
     
    方法f()和g()展示了当基本类型的参数被指明为final时所出现的结果:你可以读参数,但却
    无法修改参数.这一特性主要用来向匿名内部类传递数据,我们将在第10章中学习它.
     
    7.8.2 final 方法
    使用final方法的原因有两个.第一个原因是把方法锁定,以防任何继承类修改它的含义.
    这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。
    过去建议使用final方法的第二个原因是效率。
    类中所有的private方法都隐式地指定为是final的。由于无法取用private方法,所以也就无
    法覆盖它.可以对private方法添加final 修饰词,但这并不能给该方法增加任何额外的意义。
    这一问题会造成混淆.因为,如果你试图覆盖一个private方法(隐含是final的) ,似乎是奏
    效的,而且编译器也不会绘出错误信息:
    package com.cy.reusing;
    
    import static com.java.util.Print.*;
    
    class WithFinals {
      // Identical to "private" alone:
      private final void f() { print("WithFinals.f()"); }
      // Also automatically "final":
      private void g() { print("WithFinals.g()"); }
    }
    
    class OverridingPrivate extends WithFinals {
      private final void f() {
        print("OverridingPrivate.f()");
      }
      
      private void g() {
        print("OverridingPrivate.g()");
      }
    }
    
    class OverridingPrivate2 extends OverridingPrivate {
      public final void f() {
        print("OverridingPrivate2.f()");
      }
      public void g() {
        print("OverridingPrivate2.g()");
      }
    }
    
    public class FinalOverridingIllusion {
      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();
        //! op.g();
        
        // Same here:
        WithFinals wf = op2;
        //! wf.f();
        //! wf.g();
      }
    } 
    /* Output:
    OverridingPrivate2.f()
    OverridingPrivate2.g()
    *///:~
    View Code
    "覆盖"只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型
    为它的基本类型并调用相同的方法(这一点在下一章阐明)。 如果某方法为prlvåte ,它就不是基
    类的接口的一部分。它仅是一些隐藏于类中的程序代码,只不过是具有相同的名称而已。但如
    果在导出类中以相同的名称生成-个publlc 、protected或包访问权限方法的话,该方法就不会产
    生在基类中出现的"仅具有相同名称"的情况。此时你并没有覆盖该方法,仅是生成了一个新
    的方法。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的
    组织结构的原因而存在外,其他任何事物都不需要考虑到它。
     
     
    7.8.3 final 类
    当将某个类的整体定义为final时(通过将关键字final置于它的定义之前), -就表明了你不打
    算继承该类,而且也不允许别人这样傲。换句话说,出于某种考虑,你对该类的设计永不需要
    做任何变动,或者出于安全的考虑,你不希望它有子类。
     
    package com.cy.reusing;
    
    class SmallBrain {}
    
    final class Dinosaur {
      int i = 7;
      int j = 1;
      SmallBrain x = new SmallBrain();
      void f() {}
    }
    
    //! class Further extends Dinosaur {}
    // error: Cannot extend final class 'Dinosaur'
    
    public class Jurassic {
      public static void main(String[] args) {
        Dinosaur n = new Dinosaur();
        n.f();
        n.i = 40;
        n.j++;
      }
    } ///:~
    View Code
    请注意, final类的域可以根据个人的意愿选择为是或不是final。不论类是否被定义为final ,
    相同的规则都适用于定义为final的域。然而,由于final类禁止继承,所以final类中所有的方法
    都隐式指定为是final的,因为无法覆盖色们。在final类中可以给方法添加final修饰词,但这不
    会增添任何意义。
     
    7.9 初始化及类的加载
    7.9. 1 继承与初始化
    了解包括继承在内的初始化全过程,以对所发生的一切有个全局性的把握,是很有益的。
    请看下例:
    package com.cy.reusing;
    
    import static com.java.util.Print.*;
    
    class Insect {
      private int i = 9;
      protected int j;
      Insect() {
        print("i = " + i + ", j = " + j);
        j = 39;
      }
      
      private static int x1 = printInit("static Insect.x1 initialized");
      
      static int printInit(String s) {
        print(s);
        return 47;
      }
    }
    
    public class Beetle extends Insect {
      private int k = printInit("Beetle.k initialized");
      
      public Beetle() {
        print("k = " + k);
        print("j = " + j);
      }
      
      private static int x2 = printInit("static Beetle.x2 initialized");
      
      public static void main(String[] args) {
        print("Beetle constructor");
        Beetle b = new Beetle();
      }
    } 
    /* Output:
    static Insect.x1 initialized
    static Beetle.x2 initialized
    Beetle constructor
    i = 9, j = 0
    Beetle.k initialized
    k = 47
    j = 39
    *///:~
      在Beetle上运行Java时,所发生的第-件事情就是试图访问Bettle.main() (一个static方法) ,
    于是加载器开始启动并找出Beetle类的编译代码(在名为BattIe.class的文件之中)。在对它进行
    加载的过程中,编译器注意到它有一个基类(这是由关键字extends得知的) .于是它继续进行加
    载。不管你是否打算产生一个该基类的对象,这都要发生(请尝试将对象创建代码注释掉,以
    证明这一点)。
      如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类
    中的static初始化(在此例中为Insect) 即会被执行,然后是下一个导出类,以此类推。这种方
    式很重要,因为导出类的static初始化可能会依赖于基类成员能否被正确初始化。
      至此为止,必要的类都已加载完毕,对象就可以被创建了。首先,对象中所有的基本类型
    都会被设为默认值,对象引用被设为null——这是通过将对象内存设为二进制零值而一举生成的。
    然后,基类的构造器会被调用。在本例中,它是被自动调用的。但也可以用super来指定对基类
    构造器的调用(正如在Beetle()构造器中的第一步操作)。基类构造器和导出类的构造器一样,
    以相同的顺序来经历相同的过程。在基类构造器完成之后,实例变量按其次序被初始化。最后,
    构造器的其余部分被执行。
     
     
     
     
     
     
    ----------------
  • 相关阅读:
    BAT都来参加的 DevOps Master 培训
    如何快速复制BAT级的DevOps工具链
    DevOps开源工具的三种分类整理
    Devops成功的八大炫酷工具
    阿里CI/CD、DevOps、分层自动化技术
    Android爬坑之旅:软键盘挡住输入框问题的终极解决方式
    Android应用程序窗体View的创建过程
    LeetCode Convert Sorted List to Binary Search Tree
    Spark Streaming性能优化系列-怎样获得和持续使用足够的集群计算资源?
    android nfc中Ndef格式的读写
  • 原文地址:https://www.cnblogs.com/tenWood/p/8411979.html
Copyright © 2011-2022 走看看