您的位置:首页 > 职场人生

黑马程序员_基础加强_关于反射的一些学习

2011-11-15 13:25 459 查看
----------------------android培训、java培训、期待与您交流!
----------------------

问题:

在运行时,对一个JAVA类,能否知道属性和方法;能否调用它的任意方法?

答案是可以的,JAVA提供一种反射机制可以实现。

一、什么是JAVA的反射机制

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过ReflectionAPIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public,static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。

换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构。

二、JDK中提供的ReflectionAPI

Java反射相关的API在包java.lang.reflect中,JDK1.6.0的reflect包如下图:





Member接口该接口可以获取有关类成员(域或者方法)后者构造函数的信息。
AccessibleObject类该类是域(field)对象、方法(method)对象、构造函数(constructor)对象的基础类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。
Array类该类提供动态地生成和访问JAVA数组的方法。
Constructor类提供一个类的构造函数的信息以及访问类的构造函数的接口。
Field类提供一个类的域的信息以及访问类的域的接口。
Method类提供一个类的方法的信息以及访问类的方法的接口。
Modifier类提供了static方法和常量,对类和成员访问修饰符进行解码。
Proxy类提供动态地生成代理类和类实例的静态方法。

三、JAVA反射机制提供了什么功能

Java反射机制提供如下功能:

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判段任意一个类所具有的成员变量和方法

在运行时调用任一个对象的方法

在运行时创建新类对象

在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。

这里首先定义用于测试的类:

classType{

publicintpubIntField;

publicStringpubStringField;

privateintprvIntField;

publicType(){

Log("DefaultConstructor");

}

Type(intarg1,Stringarg2){

pubIntField=arg1;

pubStringField=arg2;

Log("Constructorwithparameters");

}

publicvoidsetIntField(intval){

this.prvIntField=val;

}

publicintgetIntField(){

returnprvIntField;

}

privatevoidLog(Stringmsg){

System.out.println("Type:"+msg);

}

}

classExtendTypeextendsType{

publicintpubIntExtendField;

publicStringpubStringExtendField;

privateintprvIntExtendField;

publicExtendType(){

Log("DefaultConstructor");

}

ExtendType(intarg1,Stringarg2){

pubIntExtendField=arg1;

pubStringExtendField=arg2;

Log("Constructorwithparameters");

}

publicvoidsetIntExtendField(intfield7){

this.prvIntExtendField=field7;

}

publicintgetIntExtendField(){

returnprvIntExtendField;

}

privatevoidLog(Stringmsg){

System.out.println("ExtendType:"+msg);

}

1、获取类的Class对象

Class类的实例表示正在运行的Java应用程序中的类和接口。获取类的Class对象有多种方式:

调用getClassBooleanvar1=true;

Class<?>classType2=var1.getClass();

System.out.println(classType2);

输出:classjava.lang.Boolean

运用.class语法Class<?>classType4=Boolean.class;

System.out.println(classType4);

输出:classjava.lang.Boolean

运用staticmethodClass.forName()Class<?>classType5=Class.forName("java.lang.Boolean");

System.out.println(classType5);

输出:classjava.lang.Boolean

运用primitivewrapperclasses的TYPE语法

这里返回的是原生类型,和Boolean.class返回的不同

Class<?>classType3=Boolean.TYPE;

System.out.println(classType3);

输出:boolean

2、获取类的Fields

可以通过反射机制得到某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。JAVA的Class<T>类提供了几个方法获取类的属性。

publicFieldgetField(Stringname)返回一个Field对象,它反映此Class对象所表示的类或接口的指定公共成员字段
publicField[]getFields()返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段
publicFieldgetDeclaredField(Stringname)返回一个Field对象,该对象反映此Class对象所表示的类或接口的指定已声明字段
publicField[]getDeclaredFields()返回Field对象的一个数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段

输出:

publicintcom.quincy.ExtendType.pubIntExtendField

publicjava.lang.Stringcom.quincy.ExtendType.pubStringExtendField

publicintcom.quincy.Type.pubIntField

publicjava.lang.Stringcom.quincy.Type.pubStringField

publicintcom.quincy.ExtendType.pubIntExtendField

publicjava.lang.Stringcom.quincy.ExtendType.pubStringExtendField

privateintcom.quincy.ExtendType.prvIntExtendField

可见getFields和getDeclaredFields区别:

getFields返回的是申明为public的属性,包括父类中定义,

getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。

3、获取类的Method

通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法

Class<T>类提供了几个方法获取类的方法。

publicMethodgetMethod(Stringname,Class<?>...parameterTypes)返回一个Method对象,它反映此Class对象所表示的类或接口的指定公共成员方法

publicMethod[]getMethods()返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共member方法

publicMethodgetDeclaredMethod(Stringname,Class<?>...parameterTypes)

返回一个Method对象,该对象反映此Class对象所表示的类或接口的指定已声明方法

publicMethod[]getDeclaredMethods()返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

Class<?>classType=ExtendType.class;

//使用getFields获取属性
Field[]fields=classType.getFields();
for(Fieldf:fields)
{
System.out.println(f);
}

System.out.println();

//使用getDeclaredFields获取属性
fields=classType.getDeclaredFields();
for(Fieldf:fields)
{
System.out.println(f);
}


输出:

publicvoidcom.quincy.ExtendType.setIntExtendField(int)

publicintcom.quincy.ExtendType.getIntExtendField()

publicvoidcom.quincy.Type.setIntField(int)

publicintcom.quincy.Type.getIntField()

publicfinalnativevoidjava.lang.Object.wait(long)throwsjava.lang.InterruptedException

publicfinalvoidjava.lang.Object.wait()throwsjava.lang.InterruptedException

publicfinalvoidjava.lang.Object.wait(long,int)throwsjava.lang.InterruptedException

publicbooleanjava.lang.Object.equals(java.lang.Object)

publicjava.lang.Stringjava.lang.Object.toString()

publicnativeintjava.lang.Object.hashCode()

publicfinalnativejava.lang.Classjava.lang.Object.getClass()

publicfinalnativevoidjava.lang.Object.notify()

publicfinalnativevoidjava.lang.Object.notifyAll()

privatevoidcom.quincy.ExtendType.Log(java.lang.String)

publicvoidcom.quincy.ExtendType.setIntExtendField(int)

publicintcom.quincy.ExtendType.getIntExtendField()

4、获取类的Constructor

通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例

Class<T>类提供了几个方法获取类的构造器。

publicConstructor<T>getConstructor(Class<?>...parameterTypes)返回一个Constructor对象,它反映此Class对象所表示的类的指定公共构造方法

publicConstructor<?>[]getConstructors()返回一个包含某些Constructor对象的数组,这些对象反映此Class对象所表示的类的所有公共构造方法

publicConstructor<T>getDeclaredConstructor(Class<?>...parameterTypes)

返回一个Constructor对象,该对象反映此Class对象所表示的类或接口的指定构造方法

publicConstructor<?>[]getDeclaredConstructors()

返回Constructor对象的一个数组,这些对象反映此Class对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法

01
//使用getConstructors获取构造器

02
Constructor<?>[]constructors=classType.getConstructors();
03
for
(Constructor<?>m:constructors)
04
{
05
System.out.println(m);
06
}
07
08
System.out.println();
09
10
//使用getDeclaredConstructors获取构造器
11
constructors=classType.getDeclaredConstructors();
12
for
(Constructor<?>m:constructors)
13
{
14
System.out.println(m);
15
}
16
17
输出:
18
public

com.quincy.ExtendType()
19
20
public

com.quincy.ExtendType()
21
com.quincy.ExtendType(
int
,java.lang.String)
5、新建类的实例

通过反射机制创建新类的实例,有几种方法可以创建

调用无自变量ctor1、调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败.

Class<?>classType=ExtendType.class;

Objectinst=classType.newInstance();

System.out.println(inst);

输出:

Type:DefaultConstructor

ExtendType:DefaultConstructor

com.quincy.ExtendType@d80be3

2、调用默认Constructor对象的newInstance方法

Class<?>classType=ExtendType.class;

Constructor<?>constructor1=classType.getConstructor();

Objectinst=constructor1.newInstance();

System.out.println(inst);

输出:

Type:DefaultConstructor

ExtendType:DefaultConstructor

com.quincy.ExtendType@1006d75

调用带参数ctor3、调用带参数Constructor对象的newInstance方法

Constructor<?>constructor2=

classType.getDeclaredConstructor(int.class,String.class);

Objectinst=constructor2.newInstance(1,"123");

System.out.println(inst);

输出:

Type:DefaultConstructor

ExtendType:Constructorwithparameters

com.quincy.ExtendType@15e83f9

6、调用类的函数

通过反射获取类Method对象,调用Field的Invoke方法调用函数。

01
Class<?>classType=ExtendType.
class
;
02
Objectinst=classType.newInstance();
03
MethodlogMethod=classType.<STRONG>getDeclaredMethod</STRONG>(
"Log"
,String.
class
);
04
logMethod.invoke(inst,
"test"
);
05
06
输出:
07
Type:DefaultConstructor
08
ExtendType:DefaultConstructor

09
<FONTcolor=#ff0000>Classcom.quincy.ClassTcannotaccessamemberof
class
com.quincy.ExtendTypewithmodifiers
"private"
</FONT>
10
11
<FONTcolor=#ff0000>上面失败是由于没有权限调用
private
函数,这里需要设置Accessible为
true
;</FONT>
12
Class<?>classType=ExtendType.
class
;
13
Objectinst=classType.newInstance();
14
MethodlogMethod=classType.getDeclaredMethod(
"Log"
,String.
class
);
15
<FONTcolor=#ff0000>logMethod.setAccessible(
true
);</FONT>
16
logMethod.invoke(inst,
"test"
);
7、设置/获取类的属性值

通过反射获取类的Field对象,调用Field方法设置或获取值

1
Class<?>classType=ExtendType.
class
;
2
Objectinst=classType.newInstance();
3
FieldintField=classType.getField(
"pubIntExtendField"
);
4
intField.<STRONG>setInt</STRONG>(inst,
100
);
5
int
value=intField.<STRONG>getInt</STRONG>(inst);
1
四、动态创建代理类

代理模式:代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。

代理模式的角色:

抽象角色:声明真实对象和代理对象的共同接口

代理角色:代理角色内部包含有真实对象的引用,从而可以操作真实对象。

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

动态代理:

java.lang.reflect.ProxyProxy提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类

InvocationHandler是代理实例的调用处理程序实现的接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法。

动态Proxy是这样的一种类:

它是在运行生成的类,在生成时你必须提供一组Interface给它,然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

步骤:

1、定义抽象角色

publicinterfaceSubject{

publicvoidRequest();

}

2、定义真实角色

publicclassRealSubjectimplementsSubject{

@Override

publicvoidRequest(){

//TODOAuto-generatedmethodstub

System.out.println("RealSubject");

}

}

3、定义代理角色

publicclassDynamicSubjectimplementsInvocationHandler{

privateObjectsub;

publicDynamicSubject(Objectobj){

this.sub=obj;

}

@Override

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)

throwsThrowable{

//TODOAuto-generatedmethodstub

System.out.println("Method:"+method+",Args:"+args);

method.invoke(sub,args);

returnnull;

}

}

4、通过Proxy.newProxyInstance构建代理对象

RealSubjectrealSub=newRealSubject();

InvocationHandlerhandler=newDynamicSubject(realSub);

Class<?>classType=handler.getClass();

Subjectsub=(Subject)Proxy.newProxyInstance(classType.getClassLoader(),

realSub.getClass().getInterfaces(),handler);

System.out.println(sub.getClass());

5、通过调用代理对象的方法去调用真实角色的方法。

sub.Request();

输出:

class$Proxy0新建的代理对象,它实现指定的接口

Method:publicabstractvoidDynamicProxy.Subject.Request(),Args:null

RealSubject调用的真实对象的方法

待续...

----------------------android培训、java培训、期待与您交流!
----------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐