黑马程序员——Java反射&动态代理

-------android培训java培训、期待与您交流! ----------

一、反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,

都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

要想解剖一个类,必须先要获取到该类的字节码文件对象而解剖使用的就是Class类中的方法.所以先要获取到每

一个字节码文件对应的 Class 类型的对象 .

1:类加载器

负责将.class文件加载到内在中,并为之生成对应的Class对象。简单的说就是加载类的工具
类加载器的组成

A: Bootstrap ClassLoader 根类加载器

也被称为引导类加载器,负责Java核心类的加载

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

B: Extension ClassLoader 扩展类加载器

负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

C: Sysetm ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

2:Class类:

成员变量 Field
构造方法 Constructor

成员方法Method

3:获取class文件对象的方式有三种:

A:Object类的getClass()方法

Person p = new Person();

Class c = p.getClass();

B:数据类型的静态属性class

Class c2 = Person.class;任意数据类型具备一个class静态属性,看上去要比第一种方式简单。

C:Class类中的静态方法

public static Class forName(String className): className代表全限定名。

Class c3 = Class.forName("Person");

第三种和前两种的区别 

前两种你必须明确Person类型.

后面是你我这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了

public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		// 方式1
		Person p = new Person();
		Class c = p.getClass();

		Person p2 = new Person();
		Class c2 = p2.getClass();

		System.out.println(p == p2);// false
		System.out.println(c == c2);// true

		// 方式2
		Class c3 = Person.class;
		// int.class;
		// String.class;
		System.out.println(c == c3);//true

		// 方式3
		// ClassNotFoundException
		Class c4 = Class.forName("reflect.Person");//来自配置文件
		System.out.println(c == c4);//true
	}
}
4:通过反射获取构造方法并使用

获取所有构造方法

public Constructor[] getConstructors(): 所有公共构造方法

public Constructor[] getDeclaredConstructors(): 所有的构造方法

获取单个构造方法

public Constructor<T> getConstructor(Class<?>... parameterTypes)

参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象

创建对象

public T newInstance(Object... initargs)

public T newInstance()

import java.lang.reflect.Constructor;
public class ReflectDemo {
	public static void main(String[] args) throws Exception {
		// 获取字节码文件对象
		Class c = Class.forName("反射.Person");

		// 获取构造方法
		Constructor[] cons = c.getDeclaredConstructors();
		for (Constructor con : cons) {
		      System.out.println(con);
		}
		
		//获取单个构造方法
		Constructor con = c.getConstructor();//返回的是构造方法对象
		
		//public T newInstance(Object... initargs)
		//使用此Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
		Object obj = con.newInstance();
		System.out.println(obj);<pre name="code" class="java">                //获取带参构造方法对象
		Constructor con2 = c.getConstructor(String.class, int.class, String.class);
	
		//通过带参构造方法对象创建对象
		Object obj2 = con2.newInstance("Donald", 27, "北京");
		System.out.println(obj2);<pre name="code" class="java">                
                //获取私有构造方法对象
		Constructor con3 = c.getDeclaredConstructor(String.class);
		//用该私有构造方法创建对象
		//暴力访问
		con3.setAccessible(true);//值为true则指示反射的对象在使用时应该取消Java语言访问检查
		Object obj3 = con.newInstance("Jesus");
		System.out.println(obj3);<pre name="code" class="java">       }
}
 
 
 

5:通过反射获取成员变量并使用

获取所有变量

public Field[] getFields(): 获取所有公共的成员变量。

public Field[] getDeclaredFields(): 获取所有的成员变量。

获取单个方法

public Field getField();

public Field getDeclaredField();

修改成员的值

public void set(Objectobj,Object value)

将指定对象变量上此Field对象表示的字段设置为指定的新值。

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

public class ReflectDemo {
	public static void main(String[] args) throws Exception {
		Class c = Class.forName("reflect.Person");

		//Field[] fields = c.getFields();//访问所以公共的成员变量
		Field[] fields = c.getDeclaredFields();//获取所有的成员变量
		for(Field field : fields){
		     System.out.println(field);
		}

		//通过无参构造方法创建对象
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();
		
		//获取单个成员变量
		//获取address并对其赋值
		Field addressField = c.getField("address");
		//public void set(Object obj, Object value)
		//将指定对象变量上此Field对象表示的字段设置为指定的新值。
		addressField.set(obj, "北京");//给obj对象的addressField字段设置为“北京”.
		System.out.println(obj);
		
		//获取name并对其赋值
		Field nameField = c.getDeclaredField("name");
		nameField.setAccessible(true);
		nameField.set(obj, "Donald");
		System.out.println(obj);
		
		//获取age并对去赋值,私有变量
		Field ageField = c.getDeclaredField("age");
		ageField.setAccessible(true);//暴力访问
		ageField.set(obj, 27);
		System.out.println(obj);
	}
}

6:获取成员方法并使用

获取所有方法

public Method[] getMethods():获取自己包括父亲的所有公共方法

public Method[] getDeclaredMethods(): 获取自己的公共方法

获取单个方法

public Method getMethod(String name, Class<?>... parameterTypes);

第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型

public Method getDeclaredMethods();

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

public class ReflectDemo {
	public static void main(String[] args)throws Exception {
		Class c = Class.forName("reflect.Person");
		
		//获取所有的方法
		Method[] methods = c.getMethods();//获取自己的包括父亲的公共方法。
		methods = c.getDeclaredMethods();//获取自己的公共方法。
		for(Method method : methods){
		        System.out.println(method);
		}
	
		Constructor  con = c.getConstructor();
		Object obj = con.newInstance();
		
		//获取单个方法并使用
		Method m1 = c.getMethod("show");  
		//obj.m1();//错误
		//public Object invoke(Object obj, Object... args)
		//返回值是Object接收,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数。
		m1.invoke(obj);//调用obj对象的m1方法。
		
		System.out.println("--------------------");
		//public void method(String s)
		Method m2 = c.getMethod("method", String.class);
		m2.invoke(obj, "hello");
		System.out.println("-------------------");
		//public String getString(String s, int i)
		Method m3 =c.getMethod("getString", String.class, int.class);
		Object objString = m3.invoke(obj, "hello", 100);
		System.out.println(objString);
		String s = (String)m3.invoke(obj, "hello", 100);
		System.out.println(s);
		
		//获取私有方法
		Method m4 = c.getDeclaredMethod("function");
		m4.setAccessible(true);
		m4.invoke(obj);				
	}
}
7:通过反射越过泛型检查

在一个ArrayList<Integer>对象集合中添加一个字符串数据,

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class ArrayListDemo {
	public static void main(String[] args) throws Exception {
		// 创建集合对象
		ArrayList<Integer> array = new ArrayList<Integer>();

		// array.add("hello");
		// array.add(10);

		Class c = array.getClass(); // 集合ArrayList的class文件对象
		Method m = c.getMethod("add", Object.class);

		m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello
		m.invoke(array, "world");
		m.invoke(array, "java");

		System.out.println(array);
	}
}

8:通过配置文件运行类中的方法

import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {
	public static void main(String[] args) throws Exception {

		// 加载键值对数据
		Properties prop = new Properties();
		FileReader fr = new FileReader("class.txt");//class.txt代替配置文件
		prop.load(fr);
		fr.close();

		// 获取数据
		String className = prop.getProperty("className");
		String methodName = prop.getProperty("methodName");

		// 反射
		Class c = Class.forName(className);

		Constructor con = c.getConstructor();
		Object obj = con.newInstance();

		// 调用方法
		Method m = c.getMethod(methodName);
		m.invoke(obj);
	}
}

二、动态代理

代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
动态代理:在程序运行过程中产生的这个对象
动态代理其实就是通过反射来生成一个代理

A: JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

B: JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

C: CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

D: 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,

还可以在代理方法中的如下四个位置加上系统功能代码:

a: 在调用目标方法之前

b: 在调用目标方法之后

c: 在调用目标方法前后

d: 在处理目标方法异常的catch块中

让jvm创建动态类及其实例对象,需要的信息

(1)生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;

(2)产生的类字节码必须有个一个关联的类加载器对象;

(3)生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

创建动态类的实例对象的步骤:

A: 用反射获得构造方法

B: 编写一个最简单的InvocationHandler类

C: 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

D: 打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

E: 将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {  
    public static void main(String[] args) throws Exception{   
        //接收两个参数,一个是后边参数的字节码的加载器,一个是所要实现代理的接口的字节码  
        Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  
        System.out.println(clazzProxy1.getName());//打印代理的名字     $Proxy0  
          
        System.out.println("----------begin constructors list----------");  
        /*想打印出如下格式:  
         * $Proxy0()  
        $Proxy0(InvocationHandler,int)*/  
        Constructor[] constructors = clazzProxy1.getConstructors();//获取代理的构造函数  
        for(Constructor constructor : constructors){  
            String name = constructor.getName();//获取代理的构造函数的name  
            StringBuilder sBuilder = new StringBuilder(name);  
            sBuilder.append('(');  
            Class[] clazzParams = constructor.getParameterTypes();//获取代理的构造函数的参数  
            for(Class clazzParam : clazzParams){  
                sBuilder.append(clazzParam.getName()).append(',');  
            }  
            if(clazzParams!=null && clazzParams.length != 0)//稳妥的判断是否是一个参数,不是就删掉最后的,  
                sBuilder.deleteCharAt(sBuilder.length()-1);  
            sBuilder.append(')');  
            System.out.println(sBuilder.toString());    //$Proxy0(java.lang.reflect.InvocationHandler)        
        }  
  
        System.out.println("----------begin methods list----------");  
        /*$Proxy0()  
        $Proxy0(InvocationHandler,int)*/  
        Method[] methods = clazzProxy1.getMethods();//获取代理身上的方法  
        for(Method method : methods){  
            String name = method.getName();  
            StringBuilder sb = new StringBuilder(name);  
            sb.append('(');  
            Class[] clazzParams = method.getParameterTypes();  
            for(Class clazzParam : clazzParams){  
                sb.append(clazzParam.getName()).append(',');  
            }  
            if(clazzParams!=null && clazzParams.length != 0)  
                sb.deleteCharAt(sb.length()-1);  
            sb.append(')');  
            System.out.println(sb.toString());              
        }  
          
        System.out.println("----------begin create instance object----------");  
        //Object obj = clazzProxy1.newInstance();  
               //方式一:通过接口的子类创建对象    
        Constructor constructor =  clazzProxy1.getConstructor(InvocationHandler.class);  
        //获取代理身上的构造函数  
        //创建内部类MyInvocationHandler1,目的是传递给代理的构造器  
        class MyInvocationHandler1 implements InvocationHandler{  
  
            public Object invoke(Object proxy, Method method, Object[] args)  
                    throws Throwable {  
                // TODO Auto-generated method stub  
                return null;  
            }  
          
        }  
        //方式二:匿名内部类   
        Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());  
          
        System.out.println(proxy1);//没有错误  
        proxy1.clear();//没有错误  
        //proxy1.size();报错,因为,代理调用size方法,其实是调用了MyInvocationHandler1中的invoke,他的返回值是null  
          
        //用了匿名内部类的方法实现  
        Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){  
            public Object invoke(Object proxy, Method method, Object[] args)  
                    throws Throwable {  
                return null;  
            }  
              
        });          
        final ArrayList target = new ArrayList();             
        Collection proxy3 = (Collection)getProxy(target,new MyAdvice());  
        proxy3.add("zxx");  
        proxy3.add("lhm");  
        proxy3.add("bxd");  
        System.out.println(proxy3.size());  
        System.out.println(proxy3.getClass().getName());  
    }  
  
    private static Object getProxy(final Object target,final Advice advice) {           
        //方式三,newProxyInstance这个方法需要三个参数,可以直接创建target的代理对象  
        Object proxy3 = Proxy.newProxyInstance(  
                target.getClass().getClassLoader(),  
                /*new Class[]{Collection.class},*/  
                  
                //获取target上的接口  
                target.getClass().getInterfaces(),  
                new InvocationHandler(){  
                  
                    public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable{
                        //把上边的代码封装到一个类中,然后调用该类的方法,就实现了方法的封装
                        advice.beforeMethod(method);  
                        Object retVal = method.invoke(target, args);  
                        advice.afterMethod(method);  
                        return retVal;                        
                          
                    }  
                });  
        return proxy3;  
    }  
}  
import java.lang.reflect.Method;  	  
	public interface Advice {  
	    void beforeMethod(Method method);  
	    void afterMethod(Method method);  
	}  
import java.lang.reflect.Method;  
//创建实现Advice接口的子类   
public class MyAdvice implements Advice {  
    long beginTime = 0;  
    public void beforeMethod(Method method) {  
        System.out.println("到黑马来学习啦!");  
        beginTime = System.currentTimeMillis();  
    }  
    public void afterMethod(Method method) {  
        System.out.println("从黑马毕业上班啦!");        
        long endTime = System.currentTimeMillis();  
        System.out.println(method.getName() + " running time of " + (endTime - beginTime));  
    }  
} 

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值