Java 反射机制

Java Reflection

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

Class 类

在Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头。

我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例!

  1.每一个运行时类只加载一次!

  2.有了Class的实例以后,我们才可以进行如下的操作:

    1)*创建对应的运行时类的对象

    2)获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...)

    3)*调用对应的运行时类的指定的结构(属性、方法、构造器)

    4)反射的应用:动态代理

Class本身也是一个类

Class 对象只能由系统建立对象

实例化Class类对象

  1.调用运行时类本身的.class属性

    Class clazz1 = Person.class;

  2.通过运行时类的对象获取

    Person p = new Person();
           Class clazz3 = p.getClass();

  3.通过Class的静态方法获取.

    String className = "com.atguigu.java.Person";
           Class clazz4 = Class.forName(className);

  4.(了解)通过类的加载器

    ClassLoader classLoader = this.getClass().getClassLoader();
           Class clazz5 = classLoader.loadClass(className);

了解:ClassLoader
  

 

ClassLoader loader = this.getClass().getClassLoader();
InputStream is = loader.getResourceAsStream("com\\atguigu\\java\\jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String name = pros.getProperty("user");
System.out.println(name);

 此方法可以获取包内的文件做为输入流。

创建类对象并获取类的完整结构

创建运行时类的对象:

调用Class对象的newInstance()方法

  要  求:    1)类必须有一个无参数的构造器。       2)类的构造器的访问权限需要足够。

//1.根据全类名获取对应的Class对象
String name = “atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person)con.newInstance("Peter",20);
System.out.println(p2);

 通过反射调用类的完整结构

全部的Field

  public Field[] getFields()  返回此Class对象所表示的类或接口及其父类的public的Field。

  public Field[] getDeclaredFields()  返回此Class对象所表示的类或接口本身的全部Field。

  Field方法中:

    public int getModifiers()  以整数形式返回此Field的修饰符

      String str1 = Modifier.toString(f.getModifiers());

    public Class<?> getType()  得到Field的属性类型

    public String getName()  返回Field的名称。

全部的方法

  public Method[] getDeclaredMethods()   返回此Class对象所表示的类或接口本身的全部方法

  public Method[] getMethods()   返回此Class对象所表示的类或接口及其父类中的public的方法

  Method类中:

    public Class<?> getReturnType()  取得全部的返回值

    public Class<?>[] getParameterTypes()  取得全部的参数

    public int getModifiers()  取得修饰符

    public String getName()  返回方法名

    public Class<?>[] getExceptionTypes()  取得异常信息

    public Class<?> Annotation[] get Annotations()  取得注解

全部的构造器

  public Constructor<T>[] getConstructors()   返回此 Class 对象所表示的类的所有public构造方法。

  public Constructor<T>[] getDeclaredConstructors()  返回此 Class 对象表示的类声明的所有构造方法。

  Constructor类中:

    取得修饰符: public int getModifiers();

    取得方法名称: public String getName();

    取得参数的类型:public Class<?>[] getParameterTypes();

所继承的父类

    public Class<? Super T> getSuperclass()   返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

    public Type getGenericSuperclass();     获取带泛型的父类  

//3*.获取父类的泛型
public void test3(){
	Class clazz = Person.class;
	Type type1 = clazz.getGenericSuperclass();	
	ParameterizedType param = (ParameterizedType)type1;
	Type[] ars = param.getActualTypeArguments();
	System.out.println(((Class)ars[0]).getName());
}

泛型相关

  获取父类泛型类型:Type getGenericSuperclass()
  泛型类型:ParameterizedType
  获取实际的泛型类型参数数组:getActualTypeArguments()

其他的

  public Class<?>[] getInterfaces()   确定此对象所表示的类或接口实现的接口。

  public Package getPackage()      获取所在的包

通过反射调用类中的指定方法、指定属性

调用指定属性

  public Object get(Object obj) 取得指定对象obj上此Field的属性内容

  public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

    注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。

    public void setAccessible(true)访问私有属性时,让这个属性可见。

调用指定方法

  1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

  2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

    说明:1.Object 对应原方法的返回值,若原方法无返回值,此时返回null

       2.若原方法若为静态方法,此时形参Object obj可为null

       3.若原方法形参列表为空,则Object[] args为null

       4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

 动态代理---反射的应用

代理设计模式的原理:

  使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上

静态代理:要求被代理类和代理类同时实现相应的一套接口;通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的同样的方法的调用。

动态代理:在程序运行时,根据被代理类及其实现的接口,动态的创建一个代理类。当调用代理类的实现的抽象方法时,就发起对被代理类同样方法的调用。

//动态代理的使用,体会反射是动态语言的关键
interface Subject {
	void action();
}
// 被代理类
class RealSubject implements Subject {
	public void action() {
		System.out.println("我是被代理类,记得要执行我哦!么么~~");
	}
}
class MyInvocationHandler implements InvocationHandler {
	Object obj;// 实现了接口的被代理类的对象的声明
	// ①给被代理的对象实例化②返回一个代理类的对象
	public Object blind(Object obj) {
		this.obj = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this);
	}
	//当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//method方法的返回值时returnVal
		Object returnVal = method.invoke(obj, args);
		return returnVal;
	}
}
public class TestProxy {
	public static void main(String[] args) {
		//1.被代理类的对象
		RealSubject real = new RealSubject();
		//2.创建一个实现了InvacationHandler接口的类的对象
		MyInvocationHandler handler = new MyInvocationHandler();
		//3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。
		Object obj = handler.blind(real);
		Subject sub = (Subject)obj;//此时sub就是代理类的对象		
		sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用
		
		//再举一例
		NikeClothFactory nike = new NikeClothFactory();
		ClothFactory proxyCloth = (ClothFactory)handler.blind(nike);//proxyCloth即为代理类的对象
		proxyCloth.productCloth();		
	}
}
posted @ 2016-05-24 16:05  岳灵珊  阅读(208)  评论(0编辑  收藏  举报