美文网首页
Think in Java 第七章(复用类)

Think in Java 第七章(复用类)

作者: 咱就一山里人 | 来源:发表于2018-09-27 11:10 被阅读0次
    信仰

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

    ---继承和组合两种代码重用机制

    7.1 组合语法

    将对象引用置于新类中。

    7.2 继承语法

    没有直接用extends明确的继承类时,会隐式继承根类Object。

    • 7.21 初始化基类
      Java会在导出类的构造器中插入对基类构造器的调用
      继承不只是复制基类的接口的新类,当创建一个导出类对象时,该类包含了一个基类的子对象。这个子对象与你用基类直接创建的对象时一样的。二着区别在于后者来自于外部,而基类的子对象被包装在导出类的对象外部。
    class Art{
      Art(){print("Art constructor");}
    }
    
    class Drawing extends Art{
      Drawing (){print("Drawing constructor");}
    }
    
    public class Cartoon extends Drawing {
      public Cartoon (){print("Cartoon constructor");}
      public stataic void main(String[] args){
      Cartoon x = new Cartoon();
      }
    }
    
    结果
    Art constructor
    Drawing constructor
    Cartoon constructor
    

    构建过程是从基类向外扩散,基类在导出类构造器可以访问它之前,已经完成了初始化。(如果没有默认的构造器,或者想调用带参的构造器,必须用super显示地编写调用基类构造器语句)

    7.3 代理

    Java中没有直接支持代理。我认为的代理是两个没有直接关系的类同过代理类得到协调(可以更好的设计类 功能明确 清晰)

    7.4 结合使用组合和继承

    • 7.41 确保正确清理
      我以为垃圾回收机制很智能,其实不然,不能依赖垃圾回收机制处理任何事情,需要你自己写方法,但不要使用finalize()。
    • 7.42 名称屏蔽
      如果Java的基类拥有某个已被多次重载的方法名称,那么导出类中重新定义该方法名称并不会屏蔽在基类中的任何版本。无论是在该层或者它的基类中对方法进行定义,重载机制都可以正常工作。
    7.5 在组合与继承之间选择

    组合是想在新类中使用现有类的功能而非他的接口。在新类中嵌入某个对象,让其实现需要的功能。新类用户看到的只是新类定义的接口,而非所嵌入对象的接口。这时需要在新类中嵌入一个private对象。

    class Engine{
    public void start(){}
    public void rev() {}
    public void stop(){}
    }
    
    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();
      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(77);
       }
    }
    

    成员成为public将又处于客户端程序员了解怎样使用类,降低了类开发者面临的复杂度。但是,这只是一个特例,一般情况下应该使域成为private.
    继承,使用某个类,开发一个通用特殊版本(通用类)。
    is-a:继承
    has-a:组合

    7.6 protected 关键字

    类用户而言,protected修饰后是private,对于任何继承于此类的导出类或其他任何位于同一个包内的类来说,它却可以访问。
    尽管可以创建protected域,但是最好的方式还是讲域保持为private,你应该一直保留“更改底层实现”的权利。然后通过protected方法来控制类的继承职者的访问权限。

    7.7 向上转型

    • 7.71 为什么称为向上转型
      从一个专用类型向通用类型的转型。安全,导出类是基类的超集。(可能丢失方法)

    • 7.72 再论组合与继承

    7.8 final关键字

    表示无法改变。

    • 7.81 final数据
      一个即是static 又是final的域只占据一段不能改变的存储空间。(编译期常量)
      修饰对象时,引用不能改变,对象自身可以修改。
    • 7.82 final方法(设计类,明确禁止覆盖)
      1.把方法锁定,防止任何继承类修改他的含义。(使其方法不会被覆盖)
      2.效率,在Java早期实现中,就是同意编译器针对该方法的所有调用都转为内嵌调用。但是方法很大时,不太会带来性能的提高。在使用Java SE5/6 虚拟机会进行优化,因此不需要使用final方法进行优化了。
      final和private关键字
      类中所有的private方法都隐式指定为final.
    • 7.83 final类
      不可以被继承,没有子类(final类中所有的方法都隐式指定为final)
    • 7.84 有关final忠告(没看懂)
    7.9 初始化及类的加载

    Java中每个类的编译代码都在于它自己的独立文件中。该文件在只在需要使用程序代码时才会被加载。一般来说,可以说“类的代码在初次使用才加载。”指的是加载发生于创建类的第一个对象时,但是当访问static域或方法时,也会发生加载。(定义为static的东西只会被初始化一次)

    • 7.91 继承与初始化
    class insect{
       private int i = 9;
       protected int j;
       insect(){
       print("i ="+i+",j="+j);
       j = 39;
       }
      private static int x1 = printInt("staatic Insect.x1 initialized");
      static int printInt(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 = printInt("static Beetle.x2 initialized");
    public static void main(String[] args){
    print("Beetle constructor");
    Bettle b = new Beetle();
    }
    
    }
    
    //输出
    static insect.x1 initialized
    static beetle.x2 initialized
    i = 9, j = 0
    Beetle.k initialized
    k = 47
    j = 39
    

    在Bettle上运行Java时,所发生的第一件事情就是试图访问Bettle.main()(一个static方法),于是加载器开始启动并找出Beetle类的编译代码(在名为Bettle.class文件中)。在对它进行加载的过程中,编译器注意到它有一个基类(这是由关键字extends得知),于是它在继续进行加载。不管你是否打算生成一个该基类对象,这都要发生。
    如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的static初始化(在此例子insect)会被执行,然后是下一个导出类,以此类推,这种方式很重要,因为导出类的static初始化可能会依赖基类成员能否被正确初始化。
    至此为止,必要的类都以加载完毕,对象就可以被创建了。首先,对象中所有的基本类型都会被设置为默认值,对象引用被设置为null(这是通过将对象内存设置为二进制零值而一举生成的,然后基类的构造器会被调用。在本例中,它是被自动调用的。但也是可以用super来指定对基类构造器的调用(正如在Bettle()构造其中的第一步操作))。基类构造器和导出类的构造器一样,以相同的顺序来经理相同的过程。在基类构造器完成之后,实例变量按其次被其次序初始化。最后,构造器 的其余部分被执行。

    相关文章

      网友评论

          本文标题:Think in Java 第七章(复用类)

          本文链接:https://www.haomeiwen.com/subject/coucoftx.html