您的位置:首页 > 编程语言 > Java开发

java学习笔记---类型信息(type information)、反射机制与动态代理

2013-04-28 18:54 1091 查看
(1)Class对象

要理解RTTI在java 中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由成为Class对象的特殊对象完成的,它包含了与类有关的信息。

Class.forName(String classname):Class类的静态方法,参数字符串必须包含包名,返回Class对象。

package cp14;

interface HasBatteries{}
class Toy
{
Toy(){}
Toy(int i ){}
void play()
{
System.out.println("Play it!");
}
}
class FancyToy extends Toy implements HasBatteries
{
FancyToy(){super(1);}
}
public class ToyTest
{
static void printInfo(Class c)
{
System.out.println("Class name: "+c.getName());//得到该类的全名,包括包名
System.out.println("Is interface()? "+c.isInterface());//判断该类是否为接口
System.out.println("Simple name: "+c.getSimpleName());//得到该类的的简单名字,也就是类名
System.out.println("Canonical name: "+c.getCanonicalName());//得到该类的全名,包括包名

}
public static void main(String[] args)
{
Class c=null;
try
{
c=Class.forName("cp14.FancyToy");
}
catch(ClassNotFoundException e)
{
System.out.println("Class not found!");
System.exit(1);
}
printInfo(c);
for(Class face:c.getInterfaces())//得到该类实现的所有接口
{
printInfo(face);
}
Class up=c.getSuperclass();
Object obj=null;
try
{
//使用newInstance()来创建的类必须带有默认的构造器
obj=(Toy)up.newInstance();//由该类的Class对象创建该类的实例,必须满足该类有一个午餐构造方法
}
catch(Exception e)
{
e.printStackTrace();
System.exit(1);
}
printInfo(obj.getClass());
//此时Toy对象向上转型为了Object对象,必须向下转型后才能调用Toy特有的方法
//obj.play();
((Toy)obj).play();
}
}

运行结果:

Class name: cp14.FancyToy
Is interface()? false
Simple name: FancyToy
Canonical name: cp14.FancyToy
Class name: cp14.HasBatteries
Is interface()? true
Simple name: HasBatteries
Canonical name: cp14.HasBatteries
Class name: cp14.Toy
Is interface()? false
Simple name: Toy
Canonical name: cp14.Toy
Play it!


java还提供了另一种方法来生成Class对象的引用,即使用类字面常量。类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。另外对于基本数据类型的包装类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象,如下所示:

boolean.classBoolean.TYPE
char.classCharacter.TYPE
byte.classByte.TYPE
short.classShort.TYPE
int.classInteger.TYPE
long.classLong.TYPE
float.classFloat.TYPE
double.classDouble.TYPE
void.classVoid.TYPE
当使用“.class”来创建对Class对象的引用时,不会自动的初始化该Class对象。为了使用类而坐的准备工作实际包含三个步骤:

1)加载,这是由类加载器执行的。该步骤查找字节码,并从这些字节码中创建一个Class对象。

2)链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。

3)初始化。如果该类具有超累,则对其初始化,执行静态初始化器和静态初始化块。

如果一个static final值是“编译器常量”,那么这个值不需要对其所属类进行初始化就可以被读取。但是只是将一个域设置为static或者final的,还不足以确保这种行为。

Class引用所指向的Class对象可以进行限定,这里使用了Class的泛化语法。

当将泛型语法用于Class对象时,newInstance ()方法将返回该对象的确切类型,而不仅仅是在ToyTest.java中看到的基本的Object。

public class GenericToyTest
{
public static void main(String[] args) throws Exception
{
Class<FancyToy> ftClass=FancyToy.class;
FancyToy fancyToy=ftClass.newInstance();
Class <? super FancyToy> up=ftClass.getSuperclass();
Object obj=up.newInstance();
//写成下面这种形式将无法编译,这简直不可思议
//Class <Toy> up3=ftClass.getSuperclass();
Class <Toy> up3=(Class<Toy>) ftClass.getSuperclass();
Toy obj3=up3.newInstance();
obj3.play();
}
}


(2)类型转换前先做检查

某个对象是不是某个类的实例

静态检查:instanceof()

Pet pet=new Pet();
if(pet instanceOf Pet)
{
System.out.println("true");
}

动态检查:isInstance()

Pet pet=new Pet();
Class<? extends Pet> cla=Pet.class;
if(cla.isInstance(pet)){}


(3)instanceof与Class的等价性

class Base{}
class Derived extends Base{}
public class FamilyVsExactType
{
static void test(Object x)
{
System.out.println("Testing x of type "+x.getClass());
System.out.println("x instanceof Base "+(x instanceof Base));
System.out.println("x instanceof Derived "+(x instanceof Derived));
System.out.println("Base isInstance(x)  "+Base.class.isInstance(x));
System.out.println("Derived isInstance(x)  "+Derived.class.isInstance(x));
System.out.println("x.getClass()==Base.class "+(x.getClass()==Base.class));
System.out.println("x.getClass()==Derived.class "+(x.getClass()==Derived.class));
System.out.println("x.getClass().equals(Base.class) "+(x.getClass().equals(Base.class)));
System.out.println("x.getClass().equals(Derived.class) "+(x.getClass().equals(Derived.class)));
}
public static void main(String[] args)
{
test(new Base());
test(new Derived());
}
}

输出结果是:

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 cp14.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

instanceof()和isInstance()结果完全一样,equals()和==也一样。instanceof保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”而如果使用==比较实际的Class对象,就没有考虑继承----它或者是这个确切类型,或者不是。

(4)动态代理

代理是基本的设计模式之一,它是你为了提供的额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。下面是一个用来展示代理结构的简单示例:

interface Interface
{
void doSomething();
void doSomethingElse();
}
class RealObject implements Interface
{
public void doSomething()
{
System.out.println("do something.");
}
public void doSomethingElse()
{
System.out.println("do something else.");
}
}
class SimpleProxy implements Interface
{
private Interface proxied;
public SimpleProxy(Interface proxied)
{
this.proxied=proxied;
}
public void doSomething()
{
System.out.println("SimpleProxy do something.");
proxied.doSomething();
}
public void doSomethingElse()
{
System.out.println("SimpleProxy do something else.");
proxied.doSomethingElse();
}
}

public class SimpleProxyDemo
{
static void consumer(Interface iface)
{
iface.doSomething();
iface.doSomethingElse();
}
public static void main(String[] args)
{
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}

输出结果是:

do something.
do something else.
SimpleProxy do something.
do something.
SimpleProxy do something else.
do something else.

java的动态代理比代理的思想要向前迈进了一步,因为它可以动态创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器(InvocationHandler),他的工作是解释调用的类型并确定相应的对策。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface SomeMethods
{
void boring1();
void boring2();
void interesting();
void boring3();
}
class Implementation implements SomeMethods
{
public void boring1()
{
System.out.println("boring1");
}
public void boring2()
{
System.out.println("boring2");
}
public void boring3()
{
System.out.println("boring3");
}
public void interesting()
{
System.out.println("interesting");
}
}
class MethodSelector implements InvocationHandler
{
private Object proxied;
public MethodSelector(Object proxied)
{
this.proxied=proxied;
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable
{
if(method.getName().equals("interesting"))
{
System.out.println("Proxy detected interesting method!");
}
return method.invoke(proxied, args);
}
}
public class SelectingMethod
{
public static void main(String[] args)
{
SomeMethods proxy=(SomeMethods)Proxy.newProxyInstance
(SomeMethods.class.getClassLoader(),
new Class[]{SomeMethods.class},
new MethodSelector(new Implementation()));
proxy.boring1();
proxy.boring2();
proxy.boring3();
proxy.interesting();
}
}

输出结果是:

boring1
boring2
boring3
Proxy detected interesting method!
interesting

通过调用Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递给它),一个你希望代理实现的接口列表(不是类或者抽象类),以及InvocationHandler接口的一个实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: