本文参照大苞米的博客而写,为java反射做一次总结了。
public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class
对象。
forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
getAnnotations() 返回此元素上存在的所有注释。 |
getClassLoader() 返回该类的类加载器。 加载 Java 类到 Java 虚拟机中 |
getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 |
getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法 |
getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 |
getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 |
getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 |
getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 |
getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 |
isAnnotation() 如果此 Class 对象表示一个注释类型则返回 true。 |
isArray() 判定此 Class 对象是否表示一个数组类。 |
isEnum() 当且仅当该类声明为源代码中的枚举时返回 true。 |
Constructor
java.lang.Object java.lang.reflect.AccessibleObject java.lang.reflect.Constructor<T>
public final class Constructor<T> extends AccessibleObject implements GenericDeclaration, Member
Constructor
允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出IllegalArgumentException
。
Method
java.lang.Object java.lang.reflect.AccessibleObject java.lang.reflect.Method
public final class Method extends AccessibleObject implements GenericDeclaration, Member
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
Method
允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出IllegalArgumentException
。
类 Field
java.lang.Object java.lang.reflect.AccessibleObject java.lang.reflect.Field
public final class Field extends AccessibleObject implements Member
Field
提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
public class AccessibleObject extends Object implements AnnotatedElement
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible
标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象
setAccessible(AccessibleObject[] array, boolean flag) 使用单一安全性检查(为了提高效率)为一组对象设置 accessible 标志的便捷方法 |
setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。 |
public interface InvocationHandler
InvocationHandler
是代理实例的调用处理程序 实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke
方法。
类 Proxy
java.lang.Object java.net.Proxy
public class Proxy extends Object
此类表示代理设置,通常为类型(http、socks)和套接字地址。Proxy
是不可变对象。
代码部分: 一个JavaBean和一个测试类
public class Person {
private long id;
private int age;
private String name;
//每个属性的set和get方法,toString方法
//俩个构造方法
}
测试类:TestPerson.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestPerson {
public static void main(String[] args) throws Exception{
//使用Class类的静态方法forName获取Person类的class对象
//使用类的.class语法
//使用对象的getClass()方法(java.lang.Object类中的方法)
//Class<?> classType=Class.forName("day100_reflect.Person");
//Class<?> classType=Person.class;
Class<?> classType=new Person().getClass();
//调用Person类的俩个参数构造方法生成对象
Constructor constructor=classType.getConstructor(new Class[]{String.class,int.class});
//实例化对象
Object obj=constructor.newInstance(new Object[]{"Jack",25});
//获取类中所有构造方法
Constructor[] constructors=classType.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i].toString());
}
//获取类中所有方法名称
Method[] methods=classType.getDeclaredMethods();
for(int i=0;i<methods.length;i++){
System.out.println(methods[i].toString());
}
//获取类中所有变量名称
Field[] fields=classType.getDeclaredFields();
for(int i=0;i<fields.length;i++){
System.out.println(fields[i].toString());
}
//获取setId方法
Method setId=classType.getDeclaredMethod("setId",new Class[]{long.class} );
//调用setId方法设置Id
setId.invoke(obj, new Object[]{10});
//调用toString方法输出
Method toString=classType.getDeclaredMethod("toString", new Class[]{});
String result=(String)toString.invoke(obj, new Object[]{});
//获取结果
System.out.println(result);
}
}
我们再来看一下反射所花费的时间对比吧,反射用的多了会降低系统的性能。
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class NormalWay {
public static void main(String[] args) throws Exception{
long startTime=System.currentTimeMillis();
for(int i=0;i<100000;i++){
Person p=new Person();
p.setAge(25);
p.setId(15);
p.setName("noraml");
}
long endTime=System.currentTimeMillis();
System.out.println("普通设置参数需要花费时间为:"+(endTime-startTime)+"ms");
startTime=System.currentTimeMillis();
for(int i=0;i<100000;i++){
Class<?> pclass=Person.class;
Constructor<?> constructor=pclass.getConstructor(new Class[]{});
// Object obj= constructor.newInstance();
Object obj=pclass.newInstance();
Method setAge=pclass.getMethod("setAge",new Class[]{int.class});
setAge.invoke(obj, new Object[]{52});
Method setId=pclass.getMethod("setId",new Class[]{long.class});
setId.invoke(obj, new Object[]{51});
Method setName=pclass.getMethod("setName",new Class[]{String.class});
setName.invoke(obj, new Object[]{"special"});
}
endTime=System.currentTimeMillis();
System.out.println("反射设置参数需要花费时间为:"+(endTime-startTime)+"ms");
}
}
/**
*错误: Method setAge=pclass.getMethod("setAge",new Class[]{int.class});
* setAge.invoke(pclass,new Object[]{22});
* 激活不是根据类,而是根据对象实例中的方法来激活
*/
运行结果:
普通设置参数需要花费时间为:17ms
反射设置参数需要花费时间为:728ms
在上面我们使用反射可以获取一个对象的所有属性和方法,构造方法,注解等等信息,还可以通过invoke()方法来激活,设置属性参数等等,但是都是基于public,如果是私有方法怎么办?
解决方法: Field,Method,Constructor三个类都继承了AccessibleObject这个类,这个类控制权限的。 可以通过set方法来修改权限从而畅通无阻。
setAccessible(boolean flag)
将此对象的 accessible
标志设置为指示的布尔值。
//取得属性
Field name=dclass.getDeclaredField("name");
//禁止name的访问检查控制
name.setAccessible(true);
name.set(obj, "kenken");
//取得say方法
Method method=dclass.getDeclaredMethod("say", new Class[]{});
method.setAccessible(true);
method.invoke(obj, new Object[]{});
/**
* java封装性是给菜鸟看的
* 可以使用反射来访问私有的东西
* 个人感觉:既然在运行的时候JVM已经加载了class字节码文件,既然可以写入,那么就可以还原
* 那么原来的类构造一清二楚,赤裸裸的随便看了
* @param args
* @throws Exception
*/
/*
错误: 在获取属性和方法时使用的是dclass.getField("name");
这样不行的原因是:
getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
而
getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
*/
下面就是代理模式了。还记得我第一次接触代理时是学习三大框架时,搞得很纠结、啥都不懂。java没有深入的研究过。
静态代理是为了对代理的概念有一点理解。
大致是这样的,a通过b来访问c中的服务,实际上就是你通过我这篇博客来学习大苞米写的博客。 这个很通俗了吧。
代码部分:
public abstract class Subject {
//空的抽象方法,这个方法需要代理角色和真实角色都去实现它
public abstract void request();
}
//真实角色对象,继承自抽象角色,重写定义的方法。
public class RealSubject extends Subject {
//在重写的方法中实现需要的操作,这里只是简单打印一句
@Override
public void request() {
System.out.println("This is real subject");
}
}
package day102_reflect_ProxySubject;
//代理角色,同样继承抽象角色,重写抽象角色中的方法
public class ProxySubject extends Subject {
//在代理角色内部对真实角色的引用
private RealSubject realSubject;
//重写的实现抽象角色中的方法
@Override
public void request() {
this.preRequest(); //调用真实角色操作之前附加的操作
if(realSubject==null){
realSubject=new RealSubject();
}
realSubject.request(); //真实角色所完成的操作
this.postRequest(); //在真实角色操作之后所附加的操作
}
private void preRequest(){
System.out.println("pre request");
}
private void postRequest(){
System.out.println("post request");
}
}
public class Test {
public static void main(String[] args) {
Subject subject=new ProxySubject();
subject.request();
}
}
/*
抽象角色:
声明真实对象和代理对象的共同接口
真实对象:
代理角色所代表的真实对象,是我们最终要引用的对象
代理角色:
1.代理对象内部含有对真实对象的引用,从而可以操作真实的对象。
2.代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。
3.代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
*/
下一篇学习动态代理。
我是菜鸟,我在路上。
2015年5月27日17:47:39