Practical Java(重点版)之对象(完)

 1. 运用interfaces 支持多重继承(multiple inheritance)。

Java的接口是支持多继承的。Java是不支持实现上的多继承。

2. 避免interfaces 中的函数发生冲突。

如果两个接口具有相同的方法名字,但是其返回类型不一样,那么其实现类不能够同时实现这两个接口,编译会报错。

如果两个接口的具有相同的方法名字,并且返回类型也一致,那么其实现类可以同时实现这两个接口,编译不会报错。

如果两个接口声明的变量是一模一样的话,那么在实现类中调用变量的时候,需要指定是哪个接口的变量,或者编译不会通过。

3. 如需提供部分实现(partial implementation),请使用abstract classes(抽象类)。

所谓abstract class 是一种本身不能被具现化(instantiated)的class,但允许为其内声明的函数提供实现代码。不同与interface(接口),接口是不能够有实现。

4.  区分interface、abstract class和concrete class。

 

Interface

Abstract class

Concrete class

描述

一种契约(contract)表示法,不

带实现

一种契约(contract)表示法,带有部分实现

一种具象(具体)实现,经常是某个interface 或者某个abstract class 的实现

使用时机

当你想要支持单一或多个interface 继承,或为了确定某一marker interface(标识接口)

当你打算提供带有部分实现的class

当你打算提供一份完

整的具象实现

内容限制

Public 函数以及public static final 常量

无任何限制

无任何限制

实现

不允许实现

允许部分实现

全部实现

 

interface、abstract classes 和concrete classes 的总结

 

支持多重继承

支持抽象函数

允许实现

允许创建实体

允许部分实现

Interface

Y

Y

N

N

N

 

abstract classes

N

Y

Y

N

Y

 

concrete classes

N

N

Y

Y

N

 

interface 的所有函数都暗自为abstract。

5.  审慎地定义和实现immutable classes (不可变类)。

不可变对象是一种一点构建好就不再变化的object,在其生存期间不可被改变内容,不可变类通常用来表示字符串、颜色和数字。由于不可变对象永远不会改变本身数据,所以没有必要对他们同步控制。

Class的定义和实现多方面合作才可能造就不变性,以下几个步骤是必须的:

a.  将class中的所有数据声明为private;(如果声明为其他修饰符的话,实例类则可以修改数据信息)

b.  只提供取值方法(getter),不允许存在设置方法(setter);(如果提供了setter方法,那么其数据信息肯定会被修改掉)

c.  将class声明为final(即使提供了getter方法,但是在子类中也覆盖父类方法的话,仍然有可能是修改数据,因此需要将类声明为不能够被继承);

d.  从获取器返回reference to mutable object之前,先克隆(cloning)那些mutable objects;

e.  将[传递给构造函数之reference to mutable object]先克隆(clone);

f.  在构造函数中设定class 内含的所有数据。(由于数据信息不能够被修改,所以其赋值只能够是在构造函数)

6.  欲传递或接收mutable objects(可变对象)之object references时,请实施clone()。

实现immutable class(不可变类)时,必须对传入或传出之mutable objects(可变对象)进行克隆(clone)动作。如果一个object及其指涉(引用)所有objects都不会被改变,该object便是为恒常不变的(immutable)。如果不实施克隆动作,你的对象不变性(immutability)就得不到保证。这是因为他处可能保留了这个immutable object 内的某个object 的reference,并对其进行了修改,从而冲破了不变性的[界限]。

就是说,如果一个对象A中还引用了其他对象的引用B,那么引用B中的数据是可以被修改掉。如下代码:

public class User {

   private String name;

   private int age;

 

   public String getName() {

      return name;

   }

 

   public void setName(String name) {

      this.name = name;

   }

 

   public int getAge() {

      return age;

   }

 

   public void setAge(int age) {

      this.age = age;

   }

}

 

public final class DiskInfo {

   private String disk;

   private User user;

 

   public DiskInfo(String disk, User user) {

      this.disk = disk;

      this.user = user;

   }

 

   public User getUser() {

      return user;

   }

 

   public String disk() {

      return disk;

   }

 

   public static void main(String[] args) {

      User u = new User();

      u.setAge(1);

      u.setName("test1");

      DiskInfo d = new DiskInfo("test", u);

      User u1 = d.getUser();

      System.out.println("-----" + u1.getName());

      u1.setName("test2");

     

      User u2 = d.getUser();

      System.out.println("-----" + u2.getName());

   }

 

}

其输入结果:

-----test1

-----test2

可以看到上述的结论是正确的。因此为了保持数据的不变性,需要将mutable对象进行克隆。

克隆:Shallow Cloning(浅层克隆)。此技术对对象进行逐位拷贝(bitwise copy)。如果cloned object 包含了一些object references,那么新的object 将内含与[cloned object 所含之object reference]完全一致的副本。所以new object 和cloned object 依然共享数据。使用了克隆的代码如下:

package com.study6;

 

public class User implements Cloneable{

   private String name;

   private int age;

 

   public String getName() {

      return name;

   }

 

   public void setName(String name) {

      this.name = name;

   }

 

   public int getAge() {

      return age;

   }

 

   public void setAge(int age) {

      this.age = age;

   }

   @Override

   protected Object clone(){

      try {

        return super.clone();

      } catch (CloneNotSupportedException e) {

        throw new InternalError();

      }

   }

}

 

package com.study6;

 

public final class DiskInfo {

   private String disk;

   private User user;

 

   public DiskInfo(String disk, User user) {

      this.disk = disk;

      this.user = (User)user.clone();

   }

 

   public User getUser() {

      return (User)user.clone();

   }

 

   public String disk() {

      return disk;

   }

 

   public static void main(String[] args) {

      User u = new User();

      u.setAge(1);

      u.setName("test1");

      DiskInfo d = new DiskInfo("test", u);

      User u1 = d.getUser();

      System.out.println("-----" + u1.getName());

     

      u1.setName("test2");

      User u2 = d.getUser();

      System.out.println("-----" + u2.getName());

   }

 

}

输出结果:

-----test1

-----test1

 

Deep Cloning(深层克隆):缺省情况下,Vector clone()执行的是浅层克隆(shallow clone)。Vector的值域driveShare其实是个object reference,因此当代码对Vector进行克隆时,虽然建立了一个新副本,其内容(references to User object)却没有被克隆。因此需要深层克隆。

在实现—个immutable class(不可变类)时,请遵循下列规则:

a.声明这个class 为final。

b.声明所有数据为private。

c.只提供取值函数(getter),不提供设值函数(setter)。

d.在构造函数中设置所有instance 数据。

e. 如果函数返回reference to mutable objects,请克隆那些mutable objects。

f.如果函数接受reference to mutable objects,请克隆那些mutable objects。

g.如果缺省之浅层克隆(shallow clone)不能符合immutable object 的正常行为请实现出深层克隆(deep clone)。

7.     使用继承(inheritance)或委托(delegation)来定义immutable classes(不可变类)。

另外三个你可以用来定义immutable classes 的技术:

a.immutable interface(不可变接口)。

b.公共接口或基类(common interface or base class)。

c.immutable delegation class (不可变的委托类)。

技术

优点

缺点

Immutable inference 不可变(恒常)接口

简单易行。无需付出性能上的代价

有破绽,可被破坏。(转型会破坏不变性)

Common interface or base calss 公共接口或基类

没有破绽。清晰地将

mutable object 和immutable

objects 分离

需实现更多的classes,完成更深的classes 继承系(一个接口必须有多个实现类)

Immutable delegation class不可变(恒常)委托类

没有破绽。当你无法修改既

有的mutable object 源码

时,此法可用

需付出性能上的代价(不可变类中含有一个可变类的引用,造成性能下降)

8.实现clone()时记得调用super.clone()。

clone ()中首先必须调用super.clone(),可确保java.long.Object 中的clone会被调用,因而使得克隆件得以被正确建构出来。java.1ang.Object 的clone()函数会创建一个正确型别的新对象,并执行浅层克隆(shallow clone,即从[被克隆物]复制所有位域(fields)到[新对象]身上)。即使需要深层克隆(deep clone),仍然斋要调用java.1ang.Object 的clone()函数,才能创建对象的正确型别。

任何classes 如果支持克隆操作,就必须实现Cloneable interface——这是一个标识接口(marker interface),也就是说它并没有实现任何函数,任何classes 如果实现Cloneable,就是宣称它们支持克隆操作。

9. 别只依赖finalize()清理non—memory(内存以外)的资源

在垃圾问收器(garbage collector)回收某个class object 之前,JVM 会调用这个class 的finalize()。这个函数有时被宣扬为[在回收某个对象的内存之前确保释放non-memory 资源]的有效方法。由于垃圾回收器仅仅释放对象内存,finalize()因此提供了一种释放其他资源的办法。

对象的finalize()函数只有在垃圾回收器释放对象占用的空间之前才会被调用。

当垃圾回收器执行时,可能并非所有符合回收条件的对象都被回收(实际情况取决于垃圾回收机制所采用之算法),此外亦无法保证finalize()在可预期的时间上执行。这是由于[终结(finalization)动作[与垃圾回收]之间的异步(asynchronous)本性造成的。由此推断,finalize()未必在程序结束前被执行。这意味即使你正确撰写了finalize()来释放non-memory 资源,你的程序结束前仍有可能耗尽这些资源。

避免这个问题的可能办法之一是使用System.runFinalization()。这个函数要求JVM 在垃圾回收器下次运行之前,对所有标记为[可终结(finalizable)]对象调用其finalize()。是否能够做到这一点,取决于垃圾回收器和这个函数所采用的算法。但不能保证调用该函数就一定会执行起finalize()。

早先的另一个可用函数是System.runFinalization(),现已作废。这个函数仅仅保证所有对象的finalize()将在JVM 退出之前被执行。这意味等到finalize()执行时,你可能已经耗尽了资源。通常你会希望finalize()在你程序运行期间被执行,而不是等到最后JVM退出时刻。这个函数被认为是不安全的,因此在Java2 中被废除了。

因此不能把希望单单寄托于[finalize()被调用]这件事情上,需要自己编写清理no-memory资源的函数,并且结合finalize方法一起使用。可以自己编写一个清理函数,并且该函数是public的,同时要被finalize函数所调用(确定JVM调用finalize时可以调用到清理函数)。将清理函数声明为public,是为了可以再任何时刻都可以清理资源。将清理函数被声明为synchronized,这可以保证多个线程(threads)不会针对相同的对象同时进入这个函数。

所有finalize()都应当调用super.finalize()以确保任superclass 的finalize()被调用。由于无法保证finalize()是否被调用、何时被调用,所以你应当提供一个Public 专员函数,用以执行non-momory 资源清理工作;该函数应当由class 的finalize()调用之。

10. 在构造函数内调用non—final函数时要当心

当新建一个对象的时候,构造方法能够被调用。

package com.study6;

 

public class NoFinalTest {

 

   /**

    * @param args

    */

   public static void main(String[] args) {

      Sub s = new Sub();

      System.out.println(s.getNum());

   }

 

}

 

class Base {

   private int value;

 

   public Base() {

      System.out.println("base");

      value = lookUp();

   }

 

   public int lookUp() {

      System.out.println("base lookUp");

      return 5;

   }

 

   public int getNum() {

      return value;

   }

}

 

class Sub extends Base {

   private int num = 10;

 

   public int lookUp() {

      System.out.println("Sub lookUp " + num);

      return num;

   }

}输出结果为:

base

Sub lookUp 0

0

.这不是我们想要的结果,不过从上可以看出来num并还没有赋值。

分析:该函数返回instance变量num,它在instance 变量初始化时被赋值为10.事实真象是,当Sub的1ookup()开始执行时,其instance变量初始化工作还未来得及进行呢。

当构造函数调用non-final 函数时,就有可能发生这种错误。如果该函数被一个derived class 覆写,而是该函数返回一个[instance 变量初始化期间]被初始化的值,就会产生问题。

发布了42 篇原创文章 · 获赞 0 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章