JavaSE(10):Java反射技术及动态代理


十、Java反射技术

 

 

1、反射的概念

反射的引入:当程序在运行时接受一个外部传入的对象,该对象的编译类型是Object,但程序需要调用该对象运行类型的方法:

1、若该对象的编译类型、运行类型都知道,使用nstanceof判断然后强转。

2、编译时无法知道该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,此时需要通过反射获取对象真正的类型。

 

反射机制:类在程序运行时能获取到自身的信息。在java中,只要给定类的名称,就可以根据反射机制获取类的信息。

优点:实现动态创建对象和编译,体现出灵活性。(动态编译:运行时确定类型,绑定对象,灵活,体现多态的引用;静态编译:编译时确定类型,绑定对象)

缺点:对性能有影响,因为反射是一种解释操作,慢于直接执行操作。

 

2、class

Class类:class类用来描述java中所有的类。类:class -> 对象:任意java类(具体的表现形式或者说实现方式,是个各类在内存中唯一存在的独立的字节码,一个类被类加载器加载到内存中,是以字节码的形式占据一块内存空间,所以不同类字节码也不同)。

Class类对象相当于某个java类对象照镜子后得到的一个对象,该对象包含这个类的数据成员名、方法、构造器、实现了哪些接口,等等一系列该类的信息。Class 对象只能由系统建立对象;一个类在 JVM 中只会有一个Class实例;每个类的实例都会记得自己是由哪个 Class 实例所生成 

生成Class对象的过程其实是如此的:

当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。

 

 

Class类的获取:类在加载器加载到内存中时,内存中就有了表征这个类信息的字节码,获取这个类的Class对象,也就是将这些类信息的字节码获取出来。有三种方法:

1) 通过类的class属性,该方法安全性高,程序性能也高,如String.classData.class

2) 使用Class类的forName(String 全类名)方法,可能抛出异常。

3) 调用某个对象的getClass方法,该方法来自Ojbect类。

 

注:

Java基本类型和关键字void通过class属性也表示为class对象,如int.class

Class类中的isPrimitive判定class对象是否是基本类型,isArray判定是否是数组类型。

包装类和Void类的TYPE字段就是其基本类型的class对象(唯一,所以是相同class对象),但是其class属性并不是其基本类型的class,即:Integer.TPYE == int.classInteger.class=int.class注:因为重载的方法是根据参数列表判断重载的,分别以intInteger类参数类型的两个同名方法可以看做重载,编译通过);

数组类型的class对象: Class<int[]> classint = int[].class;

TestClass.java Class类的三种获取方法、包装类和基本类的classTypeclass属性比较、数组的class

package blog10;

import org.junit.Test;


/**
 * Class类的三种获取方法、包装类和基本类的class的Type和class属性比较
 */
public class TestClass {

	//1、Class类的三种获取方法
	@Test
	public void getMyClass(){
		//Method 1:the field class
		Class<?> class1 = TestAAA.class; //这种方法并未加载类,因为没有输出静态代码块
		Class<?> class2 = TestAAA.class; 
		System.out.println(class1);
		System.out.println( class1 == class2); //true,说明是一个对象,对应一个相同的字节码,对应一份编译出来的.class文件
		//Method 2:the method forName of Class
		try {
			Class<?> class3 = Class.forName("blog10.TestAAA"); //该句加载了类,因为输出了"AAA..."
			System.out.println(class3);
			//Class<?> class5 = Class.forName("TestAAA"); //抛出异常,必须使用全类名
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		// Method 3:the method getClass of object
		Class<?> class4 = new TestAAA().getClass(); //肯定加载了类
		System.out.println(class4);
		System.out.println(class4 == class1); //true,同一个类只有一个.class文件,一份字节码,和获取class的方法无关
	}
	
	// 2、包装类和基本数据类型的Class、数组的class
	@Test
	public void compareClass(){
		// 基本数据类型和包装类型,int和Integer为例
		Class<?> in = int.class;
		System.out.println(in.isPrimitive()); //true
		System.out.println(in);//int
		Class<?> in1 = Integer.TYPE;
		System.out.println(in1.isPrimitive());//int
		System.out.println(in1);//int
		Class<?> in2 = Integer.class;
		System.out.println(in2.isPrimitive());//false
		System.out.println(in2);//java.lang.Integer
		System.out.println(in == in1);//true
		System.out.println(in == in2);//false
		//上述种种测试,说明:int.class和Integer.TYPE是相同的,表示基本数据类型的class,
		//而Integer.class是包装类的class,和int.class不同。
		//因为上述原因,可知,在重载函数时,分别以int和Integer为参数的同名方法是不同方法,可算作重载
		
		//	数组的class
		Class<String[]> arrayClass1 = String[].class;
		System.out.println(arrayClass1);//Ljava.lang.String 
		Class<int[]> arrayClass2 = int[].class; //int虽然不能做泛型参数,但是int[]数组类型可以
		System.out.println(arrayClass2);
		System.out.println(arrayClass2.isArray());//true,是数组
	}
}

class TestAAA{
	
	public TestAAA() {
	}
	
	static{
		System.out.println("AAA...");
	}
}


 

3、class类的使用

获取class的基本属性(类名,包名之类)、表征的类的字段Filed、方法Method、构造器Constructor。通常包括四类方法:获取全部(考虑修饰符Modifiers)、获取特定(考虑Modifiers)、获取全部(忽略修饰符,暴力获取)、获取特定(忽略修饰符,暴力获取)。

TestGetInfo.java(利用Class类对象获取各种该对象对应的类的信息)

package blog10;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 利用Class对象获取各种Class类的信息
 */
public class TestGetInfo extends A implements B,C{
	public class D{/*内部类、接口可看做是一种封装类型的属性*/}
	public interface E{	}

	//获取基本属性
	public void getFounderInfo() {
		Class<?> myClass = TestGetInfo.class;
		System.out.println(myClass); //blog10.TestGetInfo
		System.out.println(myClass.getPackage());//获取包名:package blog10
		System.out.println(myClass.getName());//获取全类名:blog10.TestGetInfo
		System.out.println(myClass.getSimpleName());//获取类名:TestGetInfo
		
		//获取父类
		Class<?> parent = myClass.getSuperclass();
		System.out.println(parent); //blog10.A
		//获取接口
		System.out.println(myClass.getInterfaces());//Ljava.lang.class 可见是一个数组
		Class<?>[]/*表示任意类型的class类型的数组*/ interfaces = myClass.getInterfaces();
		for(Class<?> cla : interfaces){
			System.out.println(cla); //遍历输出所有实现的接口:interface blog10.B;interface blog10.C
		}
		//获取内部类或内部接口,虽然看做是属性field,但是不能用field的方法获取
		Class<?>[] innerClass = myClass.getClasses();//方法getDeclaredClasses()获取所有的,包括private的
		for(Class<?> cla:innerClass){
			System.out.println(cla);//遍历输出内部类和接口如下:
			// class blog10.TestGetInfo$D ,可见内部类与外部类以$符号连接输出
			// interface blog10.TestGetInfo$E
		}
		
		//获取元素(filed、method或者内部类、或者本类)修饰符getModifiers()
		System.out.println(myClass.getModifiers()); //输出1,表示public
		System.out.println(Modifier.toString(myClass.getModifiers())); // public 
		System.out.println(parent.getModifiers());//输出0,表示父类A的修饰符为空	
	}
	
	//获取字段、方法、构造器
	public static void getImportantInfo() throws ClassNotFoundException, NoSuchMethodException, SecurityException{
		
		Class<?> cla = Class.forName("blog10.Test");
		
		//获取构造器
		Constructor<?>[] con = cla.getConstructors();//获取所有本类public构造器
		for(Constructor<?> c:con){
			System.out.println(c);
			/*
			public blog10.Test()
			public blog10.Test(int)
			*/
		}
		
		Constructor<?> con1 = cla.getConstructor(int.class);  //获取本类指定的public构造器
		System.out.println(con1);//public blog10.Test(int)

		Constructor<?>[] con2 = cla.getDeclaredConstructors();//暴力反射获取所有的构造器,忽略修饰符
		for(Constructor<?> c:con2){
			System.out.println(c);// Test() Test(int) Test(String)
		}
		
		Constructor<?> con3 = cla.getDeclaredConstructor(String.class);
		System.out.println(con3);//Test(String)
		
		Constructor<?>[] con4 = cla.getSuperclass().getDeclaredConstructors();//暴力获取父类所有构造器
		for(Constructor<?> c:con4){
			System.out.println(c); //A(string) A()
		}
		
		// 获取字段field
		Field[] f = cla.getDeclaredFields(); //直接演示暴力获取字段
		for(Field a:f){
			System.out.println(a); //private int t ;public int t2
		}
		
		//获取方法Method:
		//getMethods:获取了所有的方法包括从父类继承而来的方法,但不能获取到private方法
		//getDeclaredMethods:只能获取子类定义的方法,包括private方法
		Method [] ms = cla.getMethods(); // 获取所有的public方法,包括继承自父类的
		for(Method ma:ms){
			System.out.println(ma); //连超类Ojbect的方法都打印出来了
		}
		
		Method m = cla.getMethod("mT1", String.class); //获取指定的public method 
		System.out.println(m); // public void Test.mT1(String)
		
		ms = cla.getDeclaredMethods();// 暴力获取所有的方法,不受修饰符限制
		for(Method mb:ms){
			System.out.println(mb); //但是不能获取到直接或间接父类继承而来的方法
		}
	}
	
	public static void main(String[] args) throws Exception{
		@SuppressWarnings("unused")
		TestGetInfo myTest= new TestGetInfo();
		//myTest.getFounderInfo();
		TestGetInfo.getImportantInfo();
	}
}

@SuppressWarnings("unused")
class A{
	private int a;
	public int b;
	
	public A(){}
	private A(String s){}
	
	private void mA(){}
	public void mB(){}
}
interface B{
	
}
interface C{
	
}

@SuppressWarnings("unused")
class Test extends A{
	private int t;
	public int t2;
	
	public Test(){}
	private Test(String s){}
	public Test(int i){}
	
	private void mT(){}
	public void mT1(String s){}
}	


 

4、反射

利用反射动态创建class对应的类的对象:1Class.newInstance,此时class对象对应的类必须要有无参的构造器。2Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance方法创建对象,若该构造方法被private,则需要先设置访问权限为truesetAccessible方法。

TestNewInstance(两种反射创建对象的方法,注:第二种方法功能强大,甚至能破解单例,所以一般的单例不安全,但是枚举实现的单例还是没法破解)

package blog10;

import java.lang.reflect.Constructor;


/**
 * 两种反射创建对象的方法
 */
public class TestNewInstance {

	public static void main(String[] args) throws Exception {
		// new 方法创建对象
		System.out.println(new Student());
		
		// 反射方法创建对象
		Class<?> cla = Class.forName("blog10.Student");
		System.out.println(cla.newInstance()); //必须保证默认的无参构造器存在,调用的就是它,所以和上面的new结果一样
		
		// 调用指定的构造器创建对象
		Constructor<?> c = cla.getDeclaredConstructor(String.class,int.class,int.class,int.class);
		Object obj = c.newInstance("小明",23,4,34); //若该构造器为私有的,需要setAccessible
		System.out.println(obj); // 存在这种方式,所以一般的单例就不安全了
	}
}

class Person {
	private String name;
	private int age;
	int weight;
	int height;
	
	@SuppressWarnings("unused")
	private int test(String name,int age){
		System.out.println("!!!");
		return age;
	}
	
	@SuppressWarnings("unused")
	private void PrintInfo2(){
		System.out.println("a Person...");
	}

	public int test(String name,Integer age,Integer ID){
		System.out.println("age:" + age);
		System.out.println("ID:" + ID);
		return age;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

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

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
		System.out.println("Person有参构造器");
	}

	// 反射时调用该构造器
	public Person() {
		System.out.println("Person无参构造器");
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

class Student extends Person {
	
	private int ID;
	public int mark;
	
	public static void test1(int...args){
		System.out.println("基本数据类型传入可变参数列表....");
	}
	public static void test2(String...args){
		System.out.println("引用类型传入可变参数列表....");
	}
	
	public static void staticMethod(){
		System.out.println("This is a static method...");
	}
	
	public void setMark(int mark){
		this.mark = mark;
	}
	
	@SuppressWarnings("unused")
	private void PrintInfo1(){
		System.out.println("a student...");
	}

	public Student(String name, int age, int iD,int mark) {
		super(name, age);
		ID = iD;
		this.mark = mark;
	}
	


	@Override
	public String toString() {
		return super.toString()+"Student [ID=" + ID + ", mark=" + mark + "]";
	}

	//无参构造器给反射用
	public Student(){
		System.out.println("Student 无参构造器");
	}
}



使用反射调用方法Method:获取到Method之后,调用其invoke方法执行底层方法(共有方法、私有方法、静态方法、带返回值方法、不带返回值方法、带可变参数方法、)。

TestRunMethod.java (反射调用各类方法实例)

package blog10;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 反射调用各种类型的方法方法
 */
public class TestRunMethod {

	public static void main(String[] args) throws Exception {
		Class<?> cla = Class.forName("blog10.Student");
		
		//调用void public方法:setMark 为例
		Method m = cla.getMethod("setMark", int.class);
		Constructor<?> c = cla.getDeclaredConstructor(String.class,int.class,int.class,int.class);
		Object obj = c.newInstance("小明",23,4,34); 
		System.out.println(obj); // mark = 34
		m.invoke(obj,60);
		System.out.println(obj); //mark = 60
		
		// 调用私有方法
		m = cla.getDeclaredMethod("PrintInfo1");
		m.setAccessible(true);
		m.invoke(obj);
		
		// 调用静态方法,不用传入对象到invoke,所以Object参数为null,参数列表视方法的参数了列表而定
		m = cla.getMethod("staticMethod");
		m.invoke(null);
		
		// 调用可变参数列表,将可变参数看做一个数组
		m = cla.getMethod("test1", int[].class);
		m.invoke(null, new int[]{1,2,3});
		m = cla.getMethod("test2", String[].class);
		m.invoke(null, (Object)new String[]{"A","B","C"}); //这里的强转千万不能少,少了运行错误的!!!!
		m.invoke(null, new Object[]{new String[]{"V","D","A"}}); //推荐写法
	}
}



使用反射操作字段Field:获取getXXXget方法(字段为基本类型int getInt(Object)、字段为引用类型String get(Object))。设置setXXX或者set方法(set(Object,value))。若字段为private,则需要设置访问权限。

TestReflectField.java(反射关于字段的操作)

package blog10;

import java.lang.reflect.Field;

/**
 *  反射关于字段Field的操作: 获取类  获取字段  赋值字段
 */
public class TestReflectField {

	public static void main(String[] args) throws Exception {
		//获取
		Class<?> cla = Class.forName("blog10.Student");
		Field f =cla.getDeclaredField("ID");
		f.setAccessible(true);
		System.out.println(f);
		//操作
		Object obj = cla.newInstance();
		System.out.println(obj); // ID = 0 ,默认无参构造器创建对象各属性都为默认值
		f.set(obj, 9527);
		System.out.println(obj); // ID = 9257
		
		Object o = f.get(obj);
		System.out.println(o); //字段的提取,赋值号左右必须类型匹配,分为引用类型和基本数据类型两种情况
		int i = f.getInt(obj);
		System.out.println(i); //这两种提取结果一样
	}
}



使用反射获取泛型:

TestReflectGenric.java(反射关于泛型的操作)

package blog10;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * 反射关于泛型的操作:field、method、class的泛型操作都是一致的,基本步骤:
 * 1、获取类
 * 2、获取getGeneric泛型的元素。返回的都是Type类型
 * 3、将Type类型返回值下转成ParameterizedType带参数的类型
 * 4、调用ParameterizedType的getActualTypeArguements返回该类型的参数的数组(Type类型)
 */
public class TestReflectGeneric {
	Map<String,Integer> map = new HashMap<>();
	
	public static void main(String[] args) throws Exception {
		Class<?> cla = TestReflectGeneric.class;
		Field f = cla.getDeclaredField("map");
		System.out.println(f); // TestReflectGeneric.map
		
		Class<?> ct1 = f.getType();
		System.out.println(ct1);//返回f对应的字段声明的返回类型:java.util.Map
		Type t = f.getGenericType();//返回f对应的字段的泛型类型
		System.out.println(t);//java.util.Map<String,Integer>
		//Type中没有实际方法,向下转型到其子类,调用其子类的方法
		ParameterizedType pt = (ParameterizedType)t;
		System.out.println(pt.getRawType()); //getRawTpye返回此类型的类或接口
		Type[] ta = pt.getActualTypeArguments();//返回此类的实际参数类型的Type数组
		for(Type ts:ta){
			System.out.println(ts);//分别输出:string ,Integer
		}
	}
}



使用反射获取注解:

关于注解:Annotation 其实就是代码里的特殊标记这些标记可以在编译类加载运行时被读取并执行相应的处理通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下在源文件中嵌入一些补充信息;Annotation 可以像修饰符一样被使用可用于修饰包,构造器方法成员变量参数局部变量的声明这些信息被保存在 Annotation 的 “name=value” 对中.Annotation 能被用来为程序元素(方法成员变量等设置元数据。所谓的元数据就是修饰数据的一些数据。

使用 Annotation 时要在其前面增加 符号并把该 Annotation 当成一个修饰符使用用于修饰它支持的程序元素

自定义注解:定义新的 Annotation 类型使用 @interface 关键字;Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明其方法名和返回值定义了该成员的名字和类型.;可以在定义 Annotation 的成员变量时为其指定初始值指定成员变量的初始值可使用 default 关键字;没有成员定义的 Annotation 称为标记包含成员变量的 Annotation 称为元数据 Annotation

JDK的元Annotation用于修饰其他的Annotation@Retention(注解的生命周期)@Target(注解修饰的对象)。

TestReflectAnnotation.java (注解的使用、定义,反射关于注解的操作)

package blog10;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;



/**
 * 初解及注解的应用与反射
 */
public class TestReflectAnnotation {
	//使用注解
	public static void main(String[] args) throws Exception {
		Class<?> clazz = Class.forName("blog10.MyStudent");
		Method method = clazz.getDeclaredMethod("setMark", int.class); //获取这个带注解的方法
		Annotation annotation = method.getAnnotation(MarkRange.class); //参数为指定的annotation的class类型
		int value = 80; //得到了方法的注解annotation,假设现在反射调用方法,要设定mark=130
		
		//当value超出注解标注的范围,直接抛出错误,比如value = 130,程序异常
		if(annotation!=null){
			if(annotation instanceof MarkRange){
				MarkRange markRange = (MarkRange)annotation;
				//强转类型
				if(value<markRange.min() || value > markRange.max()){
					throw new RuntimeException("错误!成绩超出范围");
				}
			}
		}
		
		Object obj = clazz.newInstance();
		System.out.println(obj);
		method.setAccessible(true);
		method.invoke(obj, value);
		System.out.println(obj);
		
	}
}


//自定义注解
@Retention(RetentionPolicy.RUNTIME)   //运行时可见
@Target(value={ElementType.METHOD})  // 作用范围:method
@interface MarkRange {
	public int min() default 0;
	public int max() default 100;//设置参数,mark的最大最小值,规定mark取值范围
}

class MyStudent extends Person{
	private int ID;
	public int mark;
	
	@MarkRange(min=10,max=90) /*可以不写括号内容,因为已经有default(0——100)*/
	public void setMark(int mark){
		this.mark = mark;
	}
	
	@SuppressWarnings("unused")
	private void PrintInfo1(){
		System.out.println("this is my student...");
	}

	public MyStudent(String name, int age, int iD,int mark) {
		super(name, age);
		ID = iD;
		this.mark = mark;
	}
	
	@Override
	public String toString() {
		return super.toString()+"Student [ID=" + ID + ", mark=" + mark + "]";
	}

	//无参构造器给反射用
	public MyStudent(){
		System.out.println("Student 无参构造器");
	}
}

 


ReflectionUtils.java (一个各种反射操作的工具类)

package blog10;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ReflectionUtils {
	//自定义一个ReflectionUtils工具类,用于反射时调用
	
	//方法1:通过反射,获取定义Class时声明的父类的泛型参数的类型
	//如:public A extends B<C,D> ,获取C.class 和D.class
	@SuppressWarnings("rawtypes")
	public static Class getSuperClassGenericType(Class clazz,int index ){
		Type genType = clazz.getGenericSuperclass();
		if(!(genType instanceof ParameterizedType)){
			return Object.class;//如果父类的泛型没有参数,即没有使用泛型。返回Object.class、
		}
		//若genType instanceof ParameterizedType,则强转,然后通过ParameterizedType的方法获取参数数组
		Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
		if(index>params.length || index<0){
			throw new RuntimeException("函数传入的参数index错误!");
		}
		
		if(!(params[index] instanceof Class)){
			//若参数不是Class类型的,返回Object.class
			return Object.class;
		}
		
		return (Class) params[index];
	}
	
	//方法2:通过反射,获取Class定义中声明的父类的泛型参数
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static<T> Class<T> getSuperGenericType(Class clazz){
		return getSuperClassGenericType(clazz, 0);
	}
	
	
	
	//方法3:循环向上转型,获取指定的方法
	public static Method getDeclaredMethod(Object obj,String methodName,Class<?> [] parameterTypes){
		for(Class<?> clazz = obj.getClass();clazz != Object.class;clazz = clazz.getSuperclass()){
			try {
				return clazz.getDeclaredMethod(methodName, parameterTypes);
			} catch (NoSuchMethodException | SecurityException e) {
				//指定的Method不在当前类,向上转到父类中寻找 
			}
		}
		return null;
	}
	
	
	//方法4:直接调用对象方法,忽略访问权限修饰符
	@SuppressWarnings("rawtypes")
	public static Object invokeMethod(Object obj,String methodName,
			Class [] parameterTypes) throws InvocationTargetException {
		
		Method method = getDeclaredMethod(obj, methodName, parameterTypes);
		if(method == null){
			throw new IllegalArgumentException("can not find the method!");
		}
		
		method.setAccessible(true);
		
		try {
			return method.invoke(obj, (Object)parameterTypes);
		} catch (IllegalAccessException  e) {
			System.out.println("不可能抛出的异常");
		}
		
		return null;
	}

	
	//方法5:直接设置对象属性值,忽略private/protected修饰
	@SuppressWarnings("rawtypes")
	public static void setFieldValue(Object object,String fieldName,Object value) {
	
		Field field =  null;
		Class clazz = object.getClass();
		for(;clazz!=Object.class;clazz = clazz.getSuperclass()){
			try {
				field = clazz.getDeclaredField(fieldName);
				if(field!=null){
					field.setAccessible(true);
					field.set(object, value);
					return;
				}
			} catch (Exception e) {
			}
		}
	}
	
	//方法6:直接读取对象的属性值,忽略private/proteccted修饰符,也不经过getter
	@SuppressWarnings({ "rawtypes" })
	public static Object getFieldValue(Object object,String fieldName){
		Class clazz = object.getClass();
		for(;clazz!=Object.class;clazz = clazz.getSuperclass()){
			try {
				Field field  = clazz.getDeclaredField(fieldName);
				if(field!=null){
					field.setAccessible(true);
					return (Object)field.get(object);
				}
			} catch (Exception e) {
			}
		}
	
		//没有获取到的时候返回null
		System.out.println("have not get the field vaule...");
		return null;
	}
}


 

5、动态代理

动态代理概念和Proxy类:Java动态代理主要涉及到两个类:

1) InvocationHandler:该接口中仅定义了一个Object : invoke(Object proxy, Method method, Object[] args);参数proxy指代理类,method表示被代理的方法,argsmethod中的参数数组,返回值Object为代理实例的方法调用返回的值。这个抽象方法在代理类中动态实现

2) Proxy:所有动态代理类的父类,提供用于创建动态代理类和实例的静态方法。

 

所谓动态代理类是在运行时生成的class,在生成它时,你必须提供一组interface给它,则动态代理类就宣称它实现了这些interface。当然,动态代理类就充当一个代理,你不要企图它会帮你干实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

使用动态代理实现AOPAspect orient Program 面向切片编程。

代理设计模式:使用一个代理将对象包装起来。

TestAOP.java(动态代理实例)

package blog10;

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

import org.junit.Test;
/**
 * 动态代理应用
 */
public class TestAOP {
	//动态代理:面向切面编程 (Aspect Orient Program)
	//此时想为ArithClacImp中四个方法在调用时都添加前置和后置的一些日志。若没个方法都手动添加,则代码膨胀严重,且维护困难
	//此时可以用一个模板,将所有的方法嵌套到这个模板中,任何一个方法被调用时都会调用这个模板
	//此时就用到了动态代理:每个方法都需要添加的代码又叫做横切关注点:日志、验证、事务等等
	
	//代理设计模式原理:使用一个代理将对象包装起来,然后用这个代理代替原来的对象,任何对原始对象的操作都通过代理
	//代理对象决定是否及何时将方法调用转到原来的对象上 
	//实现:
	@Test
	public void testProxy(){
		//原业务实现类
		final ArithClac arithClac = new ArithClacImp();
		//代理类
		ArithClac proxy = 
			(ArithClac)Proxy.newProxyInstance(arithClac.getClass().getClassLoader(),
					arithClac.getClass().getInterfaces(), //此句此处可以直接这样写:new Class[]{ArithClac.class}表示动态代理对象实现的接口和原对象实现的接口相同
					new InvocationHandler(){
			//通过Proxy代理建立一个代理的对象。传入三个参数
			//1.这个代理对象(由动态代理根据原始对象处理创建的对象)的类加载器,通常与被代理的对象的加载器相同。因为不是new出来的对象,而是自己手动newProxyInstance创建的对象,所以需要制定加载器
			//2.由动态代理产生的对象必须实现的所有接口(的类型),因为不唯一,可以实现多接口,所以是Class数组类型
			//3.调用代理对象的时候,应该产生的行为:匿名内部类InvocationHandle接口的实现
					public Object invoke(Object proxy,Method method,Object[] args)
					//invoke三个参数:proxy:被代理的对象,必须是final修饰的对象
					//				Method:正在被调用的方法
					//				args:调用方法时被传入的参数
							throws Throwable {
								//调用方法时,添加前置日志:
								System.out.println("the method " + method.getName() + " begins with : "+ Arrays.asList(args));
								Object result = method.invoke(arithClac, args);
								System.out.println("the method " + method.getName() + " ends with :" + result);
								return result;
						}		
					});
		
		//测试:
		int result = proxy.add(10, 2);
		System.out.println(result);
		
		result =  proxy.sub(8, 4);
		System.out.println(result);
		
		//关键的两个语句:1). ArithClac proxy = 
//				(ArithClac)Proxy.newProxyInstance(arithClac.getClass().getClassLoader(),
//				 new Class[]{ArithClac.class}, 
//				 new InvocationHandler(){} );
		//意为:ArithClac proxy创建一个动态代理对象(源对象声明为ArithClac类型的)
		//newProxyInstance产生一个对象,该对象由arithClac.getClass().getClassLoader()加载,实现Class[]{ArithClac.class}这些接口
		//且调用该动态代理产生的新的代理对象的方法时做出的动作:InvocationHandler(){} ,函数体写出具体实现
		
		//2). public Object invoke(Object proxy,Method method,Object[] args)
		//调用方法是的动作:proxy为该动态代理对象对应的原来的对象,method为调用的方法,args为方法的参数 
		
		//注:20行的final的语义:若无final,当testProxy方法调用完之后,有可能arithClac会被变成垃圾回收,而ArithClac proxy这个动态代理对象
		//还需要继续使用,这个时候,调用proxy方法时就会在34行invoke方法中访问到不存在的arithClac,而出错,所以需要让proxy延长生命,使用final。
	}
}

interface ArithClac {	
	int add(int i,int j);
	int sub(int i,int j);
	void mul(int i,int j);
	void div(int i,int j); 
}

//具体的实现类
class ArithClacImp implements ArithClac{
	@Override
	public int add(int i, int j) {
		int result = i + j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result  = i-j;
		return result;
	}

	@Override
	public void mul(int i, int j) {
		int result = i*j;
		System.out.println("result:"+result);
	}

	@Override
	public void div(int i, int j) {
		int result = i/j;
		System.out.println("result:"+result);	
	}	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值