14 前言
运行时类型信息,即在运行时识别一个对象的类型,使得你在运行时可以发现和使用类型信息,主要有两种形式,一种是传统的RTTI,他假定我们在编译时已经知道了所有的类型,另一种是反射机制,他允许我们在运行时发现和使用类的信息
14.1
在java中,所有的类型转换都是在运行时进行正确性检查的,这也是RTTI名字的含义,即在运行时识别一个对象的类型
14.2 Class对象
生成指定类或接口的class对象的引用的方法: getclass()或Class.forName(),或使用类字面常量,而newInstance()方法用于创建此Class 对象所表示的类的一个新实例。
class的newInstance()方法是实现虚拟构造器的一种途径,使用newInstance()来创建的类必须具有默认的构造器
类字面常量可以用于类,接口,数组,以及基本数据类型
//: typeinfo/ClassInitialization.java
import java.util.*;
class Initable {
static final int staticFinal = 47;
static final int staticFinal2 =
ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3 {
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
Class initable = Initable.class;
System.out.println("After creating Initable ref");
// Does not trigger initialization:
System.out.println(Initable.staticFinal);
// Does trigger initialization:
System.out.println(Initable.staticFinal2);
// Does trigger initialization:
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
} /* Output:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
*///:~
14.2.3 新型的转型语法
class Building {}
class House extends Building {}
public class ClassCasts {
public static void main(String[] args) {
Building b = new House();
Class<House> houseType = House.class;
House h = houseType.cast(b);
h = (House)b; // ... or just do this.
}
} ///:~
14.3 类型转换前先做检查
14.3.1
目前,RTTI形式包括:
1)传统的类型转换
2)代表对象的类型的Class对象,通过查询Class对象可以获得运行时所需信息
3 ) 关键字 instanceof
注:要想知道一个对象的确切类型,可以通过RTTI来实现,但是前提是,这个对象的类型在编译时,必须已知,这样RTTI才能识别他,或者说编译器必须知道所有要通过RTTI来处理的类
//: typeinfo/pets/Pets.java
// Facade to produce a default PetCreator.
package typeinfo.pets;
import java.util.*;
public class Pets {
public static final PetCreator creator =
new LiteralPetCreator();
public static Pet randomPet() {
return creator.randomPet();
}
public static Pet[] createArray(int size) {
return creator.createArray(size);
}
public static ArrayList<Pet> arrayList(int size) {
return creator.arrayList(size);
}
} ///:~
不理解原书为什么要创建一个这样的外观?
14.3.2 动态的isInstanceof
Class.isInstanceof提供了一种动态测试对象的途径,即在运行时,可以判定具体的类型,这其中暗含着多态机制
14.5 instanceof和Class的等价性
//: typeinfo/FamilyVsExactType.java
// The difference between instanceof and class
package typeinfo;
import static net.mindview.util.Print.*;
class Base {}
class Derived extends Base {}
public class FamilyVsExactType {
static void test(Object x) {
print("Testing x of type " + x.getClass());
print("x instanceof Base " + (x instanceof Base));
print("x instanceof Derived "+ (x instanceof Derived));
print("Base.isInstance(x) "+ Base.class.isInstance(x));
print("Derived.isInstance(x) " +
Derived.class.isInstance(x));
print("x.getClass() == Base.class " +
(x.getClass() == Base.class));
print("x.getClass() == Derived.class " +
(x.getClass() == Derived.class));
print("x.getClass().equals(Base.class)) "+
(x.getClass().equals(Base.class)));
print("x.getClass().equals(Derived.class)) " +
(x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
} /* Output:
Testing x of type class typeinfo.Base
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class)) true
x.getClass().equals(Derived.class)) false
Testing x of type class typeinfo.Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class)) false
x.getClass().equals(Derived.class)) true
*///:~
注: A.getClass()即为A的信息,与其父类无关
14.6 反射
对于RTTI来说,编译器在编译时打开和检查.class文件,即我们可以用普通的方式来得到类的.class文件,但对于反射来说,在编译期,.class文件是不可获取的,只有在运行时打开和检查.class文件,并且反射支持"远程方法调用",即RMI,Class类和java.lang.reflect类库对反射一起提供了支持
//: typeinfo/ShowMethods.java
// Using reflection to show all the methods of a class,
// even if the methods are defined in the base class.
// {Args: ShowMethods}
import java.lang.reflect.*;
import java.util.regex.*;
import static net.mindview.util.Print.*;
public class ShowMethods {
private static String usage =
"usage:\n" +
"ShowMethods qualified.class.name\n" +
"To show all methods in class or:\n" +
"ShowMethods qualified.class.name word\n" +
"To search for methods involving 'word'";
private static Pattern p = Pattern.compile("\\w+\\.");
public static void main(String[] args) {
if(args.length < 1) {
print(usage);
System.exit(0);
}
int lines = 0;
try {
Class<?> c = Class.forName(args[0]);
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
if(args.length == 1) {
for(Method method : methods)
print(
p.matcher(method.toString()).replaceAll(""));
for(Constructor ctor : ctors)
print(p.matcher(ctor.toString()).replaceAll(""));
lines = methods.length + ctors.length;
} else {
for(Method method : methods)
if(method.toString().indexOf(args[1]) != -1) {
print(
p.matcher(method.toString()).replaceAll(""));
lines++;
}
for(Constructor ctor : ctors)
if(ctor.toString().indexOf(args[1]) != -1) {
print(p.matcher(
ctor.toString()).replaceAll(""));
lines++;
}
}
} catch(ClassNotFoundException e) {
print("No such class: " + e);
}
}
} /* Output:
public static void main(String[])
public native int hashCode()
public final native Class getClass()
public final void wait(long,int) throws InterruptedException
public final void wait() throws InterruptedException
public final native void wait(long) throws InterruptedException
public boolean equals(Object)
public String toString()
public final native void notify()
public final native void notifyAll()
public ShowMethods()
*///:~
注,输出中包含一个public的默认构造器,若要将该类的修饰符public去掉,那么输出中将不再显示这个构造器了,这个构造器被赋予了一定的和类等同的访问权限
代理
核心作用:通过代理,控制对对象的访问,可以详细控制某个对象的方法,在调用这个方法前做前置处理,在调用这个方法后做后置处理(即AOP的微观实现)
静态代理
//: typeinfo/SimpleProxyDemo.java
import static net.mindview.util.Print.*;
interface Interface {
void doSomething(); //抽象角色
void somethingElse(String arg);
}
class RealObject implements Interface { //真实角色
public void doSomething() { print("doSomething"); }
public void somethingElse(String arg) {
print("somethingElse " + arg);
}
}
class SimpleProxy implements Interface {
private Interface proxied; //代理角色
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
print("SimpleProxy doSomething");
proxied.doSomething();
}
public void somethingElse(String arg) {
print("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);//调用真实角色的相关方法,这个方法一般来说只有真实角色来实现才真的有意义
}
}
class SimpleProxyDemo { //客户端
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
} /* Output:
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo
*///:~
动态代理
//: typeinfo/SimpleDynamicProxy.java
import java.lang.reflect.*;
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
// Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(real));
consumer(proxy);
}
} /* Output: (95% match)
doSomething
somethingElse bonobo
**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null
doSomething
**** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816
bonobo
somethingElse bonobo
*///:~
实例2:
//: typeinfo/SimpleProxyDemo.java
import static net.mindview.util.Print.*;
interface Interface {
void doSomething();
void somethingElse(String arg);
}
class RealObject implements Interface {
public void doSomething() { print("doSomething"); }
public void somethingElse(String arg) {
print("somethingElse " + arg);
}
}
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
print("SimpleProxy doSomething");
proxied.doSomething();
}
public void somethingElse(String arg) {
print("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);
}
}
class SimpleProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
} /* Output:
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo
*///:~
14.8 空对象(有些不明白)
回看think in java
14.9 接口和类型信息
//: typeinfo/interfacea/A.java
package typeinfo.interfacea;
public interface A {
void f();
} ///:~
//: typeinfo/InterfaceViolation.java
// Sneaking around an interface.
import typeinfo.interfacea.*;
class B implements A {
public void f() {}
public void g() {}
}
public class InterfaceViolation {
public static void main(String[] args) {
A a = new B();
a.f();
// a.g(); // Compile error
System.out.println(a.getClass().getName());
if(a instanceof B) {
B b = (B)a;//该程序证明接口在较低耦合度方面,还是有缺陷的,由此处可知
b.g();
}
}
} /* Output:
B
*///:~
一种解决方案是,使用类而不是接口,这就要求这些类对自己负责,但是这种方式还是存在一定缺陷
最简单的方式是,使用包访问权限,这样在包外部的客户端就看不到他了
//: typeinfo/packageaccess/HiddenC.java
package typeinfo.packageaccess;
import typeinfo.interfacea.*;
import static net.mindview.util.Print.*;
class C implements A {
public void f() { print("public C.f()"); }
public void g() { print("public C.g()"); }
void u() { print("package C.u()"); }
protected void v() { print("protected C.v()"); }
private void w() { print("private C.w()"); }
}
public class HiddenC {
public static A makeA() { return new C(); }
} ///:~
注,即使makeA()返回的是C类型,你在包的外部仍不能使用A之外的任何方法,因为你不能在包外命名C
//: typeinfo/HiddenImplementation.java
// Sneaking around package access.
import typeinfo.interfacea.*;
import typeinfo.packageaccess.*;
import java.lang.reflect.*;
public class HiddenImplementation {
public static void main(String[] args) throws Exception {
A a = HiddenC.makeA();
a.f();
System.out.println(a.getClass().getName());
// Compile error: cannot find symbol 'C': //包的外部无任何C类型可用
/* if(a instanceof C) {
C c = (C)a;
c.g();
} */
// Oops! Reflection still allows us to call g():
callHiddenMethod(a, "g");
// And even methods that are less accessible!
callHiddenMethod(a, "u");
callHiddenMethod(a, "v");
callHiddenMethod(a, "w");
}
static void callHiddenMethod(Object a, String methodName)
throws Exception {
Method g = a.getClass().getDeclaredMethod(methodName);
g.setAccessible(true);
g.invoke(a);
}
} /* Output:
public C.f()
typeinfo.packageaccess.C
public C.g()
package C.u()
protected C.v()
private C.w()
*///:~
利用反射机制却可以调用任何非公共访问权限的方法,对域来说,也是如此,即便是private域,如下示例:
//: typeinfo/InnerImplementation.java
// Private inner classes can't hide from reflection.
import typeinfo.interfacea.*;
import static net.mindview.util.Print.*;
class InnerA {
private static class C implements A { //当接口的实现是静态内部类时
public void f() { print("public C.f()"); }
public void g() { print("public C.g()"); }
void u() { print("package C.u()"); }
protected void v() { print("protected C.v()"); }
private void w() { print("private C.w()"); }
}
public static A makeA() { return new C(); }
}
public class InnerImplementation {
public static void main(String[] args) throws Exception {
A a = InnerA.makeA();
a.f();
System.out.println(a.getClass().getName());
// Reflection still gets into the private class:
HiddenImplementation.callHiddenMethod(a, "g");
HiddenImplementation.callHiddenMethod(a, "u");
HiddenImplementation.callHiddenMethod(a, "v");
HiddenImplementation.callHiddenMethod(a, "w");
}
} /* Output:
public C.f()
InnerA$C
public C.g()
package C.u()
protected C.v()
private C.w()
*///:~
//: typeinfo/AnonymousImplementation.java
// Anonymous inner classes can't hide from reflection.
import typeinfo.interfacea.*;
import static net.mindview.util.Print.*;
class AnonymousA {
public static A makeA() {
return new A() { //<span style="word-spacing: normal; font-family: 'Microsoft Yahei', Simsun;">当接口的实现</span><span style="font-family: 'Microsoft Yahei', Simsun; word-spacing: normal;">匿名内部类时</span><span style="word-spacing: normal; font-family: 'Microsoft Yahei', Simsun;">
</span> public void f() { print("public C.f()"); }
public void g() { print("public C.g()"); }
void u() { print("package C.u()"); }
protected void v() { print("protected C.v()"); }
private void w() { print("private C.w()"); }
};
}
}
public class AnonymousImplementation {
public static void main(String[] args) throws Exception {
A a = AnonymousA.makeA();
a.f();
System.out.println(a.getClass().getName());
// Reflection still gets into the anonymous class:
HiddenImplementation.callHiddenMethod(a, "g");
HiddenImplementation.callHiddenMethod(a, "u");
HiddenImplementation.callHiddenMethod(a, "v");
HiddenImplementation.callHiddenMethod(a, "w");
}
} /* Output:
public C.f()
AnonymousA$1
public C.g()
package C.u()
protected C.v()
private C.w()
*///:~
//: typeinfo/ModifyingPrivateFields.java
import java.lang.reflect.*;
class WithPrivateFinalField {
private int i = 1;
private final String s = "I'm totally safe";
private String s2 = "Am I safe?";
public String toString() { //即便是private域
return "i = " + i + ", " + s + ", " + s2;
}
}
public class ModifyingPrivateFields {
public static void main(String[] args) throws Exception {
WithPrivateFinalField pf = new WithPrivateFinalField();
System.out.println(pf);
Field f = pf.getClass().getDeclaredField("i");
f.setAccessible(true);
System.out.println("f.getInt(pf): " + f.getInt(pf));
f.setInt(pf, 47);
System.out.println(pf);
f = pf.getClass().getDeclaredField("s");
f.setAccessible(true);
System.out.println("f.get(pf): " + f.get(pf));
f.set(pf, "No, you're not!");
System.out.println(pf);
f = pf.getClass().getDeclaredField("s2");
f.setAccessible(true);
System.out.println("f.get(pf): " + f.get(pf));
f.set(pf, "No, you're not!");
System.out.println(pf);
}
} /* Output:
i = 1, I'm totally safe, Am I safe?
f.getInt(pf): 1
i = 47, I'm totally safe, Am I safe?
f.get(pf): I'm totally safe
i = 47, I'm totally safe, Am I safe?
f.get(pf): Am I safe?
i = 47, I'm totally safe, No, you're not!
*///:~