Java反射机制

一、反射简介:

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

反射库(feflection library)提供了一个非常丰富且精心设计的工具集。以便编写能够动态操纵Java代码的程序。这项功能被大量应用于JavaBean中。

1.1反射机制提供的功能:

1、在运行时分析类的能力,判断任意一个对象所属的类。

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

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

4、在运行时调用任意一个对象的成员变量和方法。

5、生成动态代理。

1.2 反射相关的主要API:

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造方法

1.3 Class类

对照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

Class本身也是一个类

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

一个类在 JVM 中只会有一个Class实例

一个Class对象对应的是一个加载到JVM中的一个.class文件

每个类的实例都会记得自己是由哪个 Class 实例所生成

通过Class可以完整地得到一个类中的完整结构

                                                                             Class类的常用方法

方法名

功能说明

static Class  forName(String name) 

返回指定类名 name 的 Class 对象

Object newInstance() 

调用缺省构造函数,返回该Class对象的一个实例

getName() 

返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称

Class getSuperClass()

返回当前Class对象的父类的Class对象

Class [] getInterfaces() 

获取当前Class对象的接口

ClassLoader getClassLoader() 

返回该类的类加载器

Class getSuperclass() 

返回表示此Class所表示的实体的超类的Class

Constructor[] getConstructors()

返回一个包含某些Constructor对象的数组

Field[] getDeclaredFields()

返回Field对象的一个数组

Method getMethod(String name,Class  …  paramTypes)

返回一个Method对象,此对象的形参类型为paramType

二、创建class实例(重要)

2.1 过程:源文件通过编译(javac.exe)以后,得到一个或多个.class文件。.class.class文件经过运行(java.exe)这步,
    就需要进行类的加载(通过JVM的类的加载器),记载到内存中的缓存。每一个放入缓存中的.class文件就是一个Class的实例!

2.2  Class的一个对象,对应着一个运行时类。相当于一个运行时类本身充当了Class的一个实例。

2.3 java.lang.Class是反射的源头。接下来涉及到反射的类都在java.lang.reflect子包下。如:Field  Method Constructor  Type Package..
        当通过Class的实例调用getMethods() --->Method , getConstructors() ---->Constructor

 2.4实例化Class的方法(四种):
        2.4.1调用运行时类的.class属性
        Class clazz1 = Person.class;
        System.out.println(clazz1);

        Class clazz2 = Creator.class;
        System.out.println(clazz2);
       2.4.2通过运行时类的对象,调用其getClass()方法
        Person p = new Person();
        Class clazz3 = p.getClass();
        System.out.println(clazz3);
        2.4.3调用Class的静态方法forName(String className)。此方法报ClassNotFoundException
        String className = "com.xiaomifeng1010.java.Person";
        Class clazz4 = Class.forName(className);
        System.out.println(clazz4);

        2.4.4其他方式(不做要求)

        ClassLoader cl = this.getClass().getClassLoader();

        Class clazz4 = cl.loadClass(“类的全类名”);

三、反射的应用

3.1 创建Class实例之后,应用一:可以创建对应的运行时类的对象(重点)

package com.xiaomifeng1010.review.Animal
public class Animal {
	private String name;
	public int age;
	static String desc = "我是一个动物";
	
	public Animal() {
		super();
		System.out.println("!!!");
	}

	private Animal(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public static void info(){
		System.out.println("动物");
	}
	
	public void show(String desc){
		System.out.println("我是一个:" + desc);
	}
	
	
	
	private int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public static String getDesc() {
		return desc;
	}

	public static void setDesc(String desc) {
		Animal.desc = desc;
	}

	@Override
	public String toString() {
		return "Animal [name=" + name + ", age=" + age + "]";
	}
	
	
}
//获取运行时类的对象:方法一
	@Test
	public void test1() throws Exception{
		Class clazz = Class.forName("com.xiaomifeng1010.review.Animal");
		Object obj = clazz.newInstance();
		Animal a = (Animal)obj;
		System.out.println(a);
	}
	//调用指定的构造器创建运行时类的对象
	@Test
	public void test2() throws Exception{
		Class clazz = Animal.class;
		Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
		cons.setAccessible(true);
		Animal a = (Animal)cons.newInstance("xiaomifeng",18);
		System.out.println(a);
	}

注意:一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。比如上边的int不是类,但int.class是一个Class类型的对象。 

3.2 应用二:获取对应的运行时类的完整的类的结构:属性、方法、构造器、包、父类、接口、泛型、注解、异常、内部类。
如:Method[] m1 = clazz.getMethods() :获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
      Method[] m2 = clazz.getDeclaredMethods():获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)

3.3 应用三:调用对应的运行时类中指定的结构(某个指定的属性、方法、构造器)(重点)

//调用指定属性
@Test

public void test3() throws Exception{

    Class clazz = Class.forName("com.xiaomifeng1010.review.Animal");

    Object obj=clazz.newInstance();

    Animal a=(Anmal)obj;

    //调用非public的属性
    Field f1=clazz.getDeclaredField("name");
    f1.setAccessible(true);
    f1.set(a,"albert");
    //调用public的属性
    Field f2 = clazz.getField("age");
	f2.set(a, 9);
	System.out.println(f2.get(a));
	System.out.println(a);
	//调用static的属性
	Field f3 = clazz.getDeclaredField("desc");
	System.out.println(f3.get(null));
}
//调用指定的方法
@Test
public void test4() throws Exception{
	Class clazz = Class.forName("com.xiaomifeng1010.review.Animal");
	Object obj = clazz.newInstance();
	Animal a = (Animal)obj;
	
	//调用非public的方法
	Method m1 = clazz.getDeclaredMethod("getAge");
	m1.setAccessible(true);
	int age = (Integer)m1.invoke(a);
	System.out.println(age);
	//调用public的方法
	Method m2 = clazz.getMethod("show", String.class);
	Object returnVal = m2.invoke(a,"二哈");
	System.out.println(returnVal);
	//调用static的方法
	Method m3 = clazz.getDeclaredMethod("info");
	m3.setAccessible(true);
//		m3.invoke(Animal.class);
	m3.invoke(null);
	
}

Class对象功能:
    * 获取功能:
        1. 获取成员变量们
            * Field[] getFields() :获取所有public修饰的成员变量
            * Field getField(String name)   获取指定名称的 public修饰的成员变量

            * Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
            * Field getDeclaredField(String name)  
        2. 获取构造方法们
            //获取所有public修饰的构造方法
            * Constructor<?>[] getConstructors()  
            * Constructor<T> getConstructor(类<?>... parameterTypes)  
            
            //获取所有构造方法。
            * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
            * Constructor<?>[] getDeclaredConstructors()
            *setAccessible(true):暴力反射
        3. 获取成员方法们:
            //获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
            * Method[] getMethods()  
            * Method getMethod(String name, 类<?>... parameterTypes)  
            
            //获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)
            * Method[] getDeclaredMethods()  
            * Method getDeclaredMethod(String name, 类<?>... parameterTypes)  

        4. 获取全类名    
            * String getName()  
            *setAccessible(true):暴力反射

总结:

获取属性,方法,构造方法时,带有declared的获取方式,是获取所有的属性,方法,构造方法,而没有带declared的获取方式,只获取public修饰的属性,方法,构造方法。

四、动态代理

4.1 体会反射的动态性
代理设计模式的原理: 
     使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上。

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

静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。 最好可以通过一个代理类完成全部的代理功能.

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

     动态代理使用场合: 调试 远程方法调用
涉及到的技术点:①提供一个实现了InvocationHandler接口实现类,并重写其invoke()方法
              ②Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),h);
            //注:obj:被代理类对象 ; h:实现了InvocationHandler接口的实现类的对象

4.2动态代理步骤:

1、创建一个实现接口 实现了InvocationHandler接口实现类,并重写其invoke()方法

2、创建被代理的类以及接口

3、通过proxy的静态方法

//newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
   RealSubject target = new RealSubject();
// Create a proxy to wrap the original implementation
            DebugProxy proxy = new DebugProxy(target);
 // Get a reference to the proxy through the Subject interface
          Subject sub = (Subject) Proxy.newProxyInstance(
            Subject.class.getClassLoader(),
            new Class[] { Subject.class }, proxy);

4、通过Subject代理调用RealSubject实现类的方法

 String info = sub.say(“Albert", 18);
     System.out.println(info);

4.3动态代理与AOP(Aspect Orient Programming)

前面介绍的proxy和InvocationHandler,很难看出这种动态代理模式的优势,下面介绍一种更实用的动态代理机制。

 

public interface Dog{
      void info();
      void run();
}
public class HuntingDog implements Dog{
	public void info(){
		System.out.println("我是一只猎狗");
	}
	public void run(){
		System.out.println("我奔跑迅速");
	}
}

 

public class DogUtil{
	public void method1(){
		System.out.println("=====模拟通用方法一=====");
	}
	public void method2(){
		System.out.println("=====模拟通用方法二=====");
	}
}
public class MyInvocationHandler implements InvocationHandler{
	// 需要被代理的对象
	private Object target;
	public void setTarget(Object target){
		this.target = target;}
// 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
	public Object invoke(Object proxy, Method method, Object[] args)
		throws Exception{
		DogUtil du = new DogUtil();
		// 执行DogUtil对象中的method1。
		du.method1();
		// 以target作为主调来执行method方法
		Object result = method.invoke(target , args);
		// 执行DogUtil对象中的method2。
		du.method2();
		return result;}}
public class MyProxyFactory{
	// 为指定target生成动态代理对象
	public static Object getProxy(Object target)
		throws Exception{
		// 创建一个MyInvokationHandler对象
		MyInvokationHandler handler = 
		new MyInvokationHandler();
		// 为MyInvokationHandler设置target对象
		handler.setTarget(target);
		// 创建、并返回一个动态代理对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader()
			, target.getClass().getInterfaces() , handler);
	}
}
public class Test{
	public static void main(String[] args) 
		throws Exception{
		// 创建一个原始的HuntingDog对象,作为target
		Dog target = new HuntingDog();
		// 以指定的target来创建动态代理
		Dog dog = (Dog)MyProxyFactory.getProxy(target);
		dog.info();
		dog.run();
	}
}

使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理 。

这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理

面试题:

描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 
是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。
 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值