运行时类型信息使得你可以在程序运行时发现和使用类型信息
- 为什么需要RTTI(运行时类型信息):比如,我们使用```
Interface inter=new InterfalceImp();
为了知道这个inter到底是哪个实现类型的实例来方便做对应的操作,例如,三角形是Shape的实现类型,圆也是Shape的实现类型,那我们要针对Shape做旋转操作,这就要判断shape倒底是什么类型的对象,因为对圆做旋转操作毫无意义。
2. Class对象:生成Class对象的引用:Class.forName("全类名")还有使用类字面量:ClassType.class。前一种会抛出异常(ClassNotFoundException),后一种不会。**当使用后一种来创建Class对象的引用时,不会自动地初始化该Class对象**,为了类的使用而做的准备工作有三个:1:加载,2:链接,3:初始化。初始化被延迟到了静态方法(构造器隐式地是静态的)或者非常数表态域进行首次引用时才执行:
```java
class Initable{
static final int staticFinal=47;
static final int staticFinal2=ClassTest.rand.nextInt(1000);
// static final int staticFinal2=78;
static {
System.out.println("Initializing Initable");
}
}
class Initable2{
static int staticNotFinal=147;
static {
System.out.println("Initializing Initabl2");
}
}
class Initable3{
static int staticNotFinal=74;
static {
System.out.println("Initializing Initabl3");
}
}
public class ClassTest {
public static Random rand=new Random(47);
public static void main(String[] args){
Class initable=Initable.class;
System.out.println("After creating Initable ref");
System.out.println(Initable.staticFinal);
System.out.println(Initable.staticFinal2);
System.out.println(Initable2.staticNotFinal);
try {
Class initable3=Class.forName("classtest.Initable3");
System.out.println("After Initable3 create ref");
System.out.println(Initable3.staticNotFinal);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
After creating Initable ref
47
Initializing Initable
258
Initializing Initabl2
147
Initializing Initabl3
After Initable3 create ref
74
如果一个static不是final的,那么在对它访问时,总是要求在它读取之前,首先进行链接(为这个域分配存储空间)和初始化(初始该存储空间)。
- cast()转型:
interface Building{}
class House implements Building{}
public class ClassTest {
public static void main(String[] args){
Building building=new House();
Class<House> houseType=House.class;
House house=houseType.cast(building);
//或者也可以这样
house=(House) building;
}
}
cast方法接受参数对象,它与上面main方法最后一行相比,做了很多额外的工作 。新的转型语法对于无法使用普通转型的情况显得非常有用,在你编写泛型代码时,如果你存储了Class引用,并希望以后通过这个引用来执行转型,这种情况就会发生。不过很少有用到,在JAVA SE5中另一个没有任何用处的新特性就是Class.asSubclass()。该方法允许你将一个对象转型为一个更具体的对象。
- 动态的instanceof:Class.isInstance方法提供了一种动态地测试对象途径。instanceof与isInstance保持了类型的概念,它指的是"你是这个类吗或是你是这个类的派生类吗?",而如果用==比较实际的Class对象,就没有考虑继承-它是不是影视个确切的类型。
5.动态代理:代理是基本的设计模式之一,它是你为了提供额外的或不同操作,而插入的用来代替"实际"对象的对象。Java的动态代理比代理的思想更近一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理所做的调用都会被重定向到单一的调用处理器上。
interface Interface{
void doSomething();
void somethineElse(String string);
}
class DynamicProxyHandler implements InvocationHandler{
private Object proxied;
public DynamicProxyHandler(Object proxied){
this.proxied=proxied;
}
@Override
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 RealObject implements Interface{
@Override
public void doSomething() {
System.out.println("realobject doSomething");
}
@Override
public void somethineElse(String string) {
System.out.println("realobject somethingelse:"+string);
}
}
class SimpleDynamicProxy{
public static void consumer(Interface iface){
iface.doSomething();
iface.somethineElse("kwkw");
}
}
public class ClassTest {
public static void main(String[] args){
RealObject real=new RealObject();
SimpleDynamicProxy.consumer(real);
Interface inter= (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),new Class[]{Interface.class},new DynamicProxyHandler(real)
);
SimpleDynamicProxy.consumer(inter);
}
}
运行结果:
realobject doSomething
realobject somethingelse:kwkw
*** proxy:class classtest.$Proxy0.method:public abstract void classtest.Interface.doSomething().args:null
realobject doSomething
*** proxy:class classtest.$Proxy0.method:public abstract void classtest.Interface.somethineElse(java.lang.String).args:[Ljava.lang.Object;@4dc63996
kwkw
realobject somethingelse:kwkw
可以通过静态方法Proxy.newProxyInstance可以创建动态代理。
- 空对象:当使用内置的null表示缺少对象时,这显得枯燥。问题在于null除了在你试图用它执行任何操作来产生NullPointerException之外,它自己没有任何用处。你可以假设所有对象都是有效的,而不必浪费编程精力支检查null。
interface Null{}
class Person{
public final String first;
public final String second;
public final String address;
public Person(String first,String second,String address){
this.first=first;
this.second=second;
this.address=address;
}
public static class NullPerson extends Person implements Null{
private NullPerson(){
super("None","None","None");
}
}
public static final Person NULL=new NullPerson();
}
你就可以使用Person.NULL来表示Person的空对象。也可以用Person.NULL来判断空对象了。空对象的逻辑变体是模拟对象与桩。但是模拟对象与桩都只是假扮可以传递实际信息的存活对象,而不是像空对象那样可以成为null更加智能化的替代物。
- 接口与类型信息:当我们用B继承A接口时,并不希望在使用"A a=new B()"初始a之后再将a向下转型为B的对象实体,这个时候你需要将限定的B的访问控制符,使得在访问控制以外的地方不能访问B,再提供一个public的初始化B的地方。但是我们通过反射任然可以访问这个B的信息。即使我们发布编译之后的文件,任然可以使用JDK自带的javap反编译。但是反射也带来它的好处。在类中留下后门这一事实,也许可以让你解决某些特定的问题。