14.7 使用clone( )和Cloneable接口
由Object类定义的绝大部分方法在本书其他部分讨论。而一个特别值得关注的方法是clone( )。clone( )方法创建调用它的对象的一个复制副本。只有那些实现Cloneable接口的类能被复制。Cloneable接口没有定义成员。它通常用于指明被创建的一个允许对对象进行位复制(也就是对象副本)的类。如果试图用一个不支持Cloneable接口的类调用clone( )方法,将引发一个CloneNotSupportedException异常。当一个副本被创建时,并没有调用被复制对象的构造函数。副本仅仅是原对象的一个简单精确的拷贝。
复制是一个具有潜在危险的操作,因为它可能引起不是你所期望的副作用。例如,假如被复制的对象包含了一个称为obRef的引用变量,当副本创建时,副本中的obRef如同原对象中的obRef一样引用相同的对象。如果副本改变了被obRef引用的对象的内容,那么对应的原对象也将被改变。这里是另一个例子。如果一个对象打开一个I/O流并被复制,两个对象将可操作相同的流。而且,如果其中一个对象关闭了流,而另一个对象仍试图对I/O流进行写操作的话,将导致错误。由于复制可能引起问题,因此在Object内,clone( )方法被说明为protected。这就意味着它必须或者被由实现Cloneable的类所定义的方法调用,或者必须被那些类显式重载以便它是公共的。让我们看关于下面每一种方法的例子。下面的程序实现Cloneable接口并定义cloneTest( )方法,该方法在Object中调用clone( )方法:
// Demonstrate the clone() method.
class TestClone implements Cloneable {
int a;
double b;
// This method calls Object's clone().
TestClone cloneTest() {
try {
// call clone in Object.
return (TestClone) super.clone();
} catch(CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return this;
}
}
}
class CloneDemo {
public static void main(String args[]) {
TestClone x1 = new TestClone();
TestClone x2;
x1.a = 10;
x1.b = 20.98;
x2 = x1.cloneTest(); // clone x1
System.out.println("x1: " + x1.a + " " + x1.b);
System.out.println("x2: " + x2.a + " " + x2.b);
}
}
这里,方法cloneTest( )在Object中调用clone( )方法并且返回结果。注意由clone( )方法返回的对象必须被强制转换成它的适当类型(TestClone)。下面的例子重载clone( )方法以便它能被其类外的程序所调用。为了完成这项功能,它的存取说明符必须是public,如下所示:
// Override the clone() method.
class TestClone implements Cloneable {
int a;
double b;
// clone() is now overridden and is public.
public Object clone() {
try {
// call clone in Object.
return super.clone();
} catch(CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return this;
}
}
}
class CloneDemo2 {
public static void main(String args[]) {
TestClone x1 = new TestClone();
TestClone x2;
x1.a = 10;
x1.b = 20.98;
// here, clone() is called directly.
x2 = (TestClone) x1.clone();
System.out.println("x1: " + x1.a + " " + x1.b);
System.out.println("x2: " + x2.a + " " + x2.b);
}
}
由复制带来的副作用最初一般是比较难发现的。通常很容易想到的是类在复制时是很安全的,而实际却不是这样。一般在没有一个必须的原因的情况下,对任何类都不应该执行Cloneable。
14.8 Class
Class封装对象或接口运行时的状态。当类被加载时,类型Class的对象被自动创建。不能显式说明一个类(Class)对象。一般地,通过调用由Object定义的getClass( )方法来获得一个类(Class)对象。由Class定义的一些最常用的方法列在表14-13中。
表14-13 由Class 定义的一些方法
方法 描述
static Class forName(String name)
throws ClassNotFoundException
返回一个给定全名的Class对象
static Class forName(String name,
Boolean how, ClassLoader ldr)
throws ClassNotFoundException
返回一个给定全名的Calss对象。对象由ldr指定的加载程序加载。如果how为true,对象被初始化,否则它将不被初始化(在Java 2中新增加的)
Class[ ] getClasses( ) 对每一个公共类和接口,返回一个类(Class)对象。这些公共类和接口是调用对象的成员ClassLoader getClassLoader( ) 返回一个加载类或接口的ClassLoader对象,类或接口用于实例化调用对象
续表
方法 描述
Constructor[ ] getConstructors( )
throws SecurityException
对这个类的所有的公共构造函数,返回一个Constructor对象
Constructor[ ] getDeclaredConstructors( )
throws SecurityException
对由这个类所声明的所有构造函数,返回一个Constructor对象
Field[ ] getDeclaredFields( )
throws SecurityException
对由这个类所声明的所有域,返回一个Field对象
Method[ ] getDeclaredMethods( )
throws SecurityException
对由这个类或接口所声明的所有方法,返回一个Method对象
Field[ ] getFields( )
throws SecurityException
对于这个类的所有公共域,返回一个Field对象
Class[ ] getInterfaces( ) 当调用对象时,这个方法返回一个由该对象的类类型实现的接口数组。当调用接口时,这个方法返回一个由该接口扩展的接口数组
Method[ ] getMethods( )
throws SecurityException
对这个类中的所有公共方法,返回一个Method对象
String getName( ) 返回调用对象的类或接口的全名
ProtectionDomain getProtectionDomain( ) 返回与调用对象有关的保护范围(在Java 2中新增加的)
Class getSuperclass( ) 返回调用对象的超类。如果调用对象是类型Object的,则返回值为空(null)
Boolean isInterface( ) 如果调用对象是一个接口,则返回true。否则返回false
Object newInstance( )
throws IllegalAccessException,
InstantiationException
创建一个与调用对象类型相同的新的实例(即一个新对象)。这相当于对类的默认构造函数使用new。返回新对象
String toString( ) 返回调用对象或接口的字符串表达式
由Class定义的方法经常用在需要知道对象的运行时类型信息的场合。如同表14-13中所说明的那样,由Class提供的方法确定关于特定的类的附加信息。例如它的公共构造函数,域以及方法。这对于本书后面将要讨论的Java Beans函数是很重要的。下面的程序说明了getClass( ) (从Object继承的)和getSuperclass( )方法(从Class继承的):
// Demonstrate Run-Time Type Information.
class X {
int a;
float b;
}
class Y extends X {
double c;
}
class RTTI {
public static void main(String args[]) {
X x = new X();
Y y = new Y();
Class clObj;
clObj = x.getClass(); // get Class reference
System.out.println("x is object of type: " +
clObj.getName());
clObj = y.getClass(); // get Class reference
System.out.println("y is object of type: " +
clObj.getName());
clObj = clObj.getSuperclass();
System.out.println("y's superclass is " +
clObj.getName());
}
}
这个程序的输出如下所示:
x is object of type: X
y is object of type: Y