Think in Java 第七章(复用类)

2018-09-27  本文已影响0人  咱就一山里人
信仰

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

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

7.1 组合语法

将对象引用置于新类中。

7.2 继承语法

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

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.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.8 final关键字

表示无法改变。

7.9 初始化及类的加载

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

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()构造其中的第一步操作))。基类构造器和导出类的构造器一样,以相同的顺序来经理相同的过程。在基类构造器完成之后,实例变量按其次被其次序初始化。最后,构造器 的其余部分被执行。

上一篇下一篇

猜你喜欢

热点阅读