java的反射机制和JDk的动态代理

文章参考(必须先看文章):

对象的创建过程

 new创建对象的过程:

  1. JVM申请一块内存空间
  2. JVM调用类私有的构造器并传入ClassLoader(类加载器)
  3. 类加载器加载对应类的字节码,JVM创建Class对象

首先,在很多初学者的印象中,类和对象的关系是这样的:

虽然知道源代码经过javac命令编译后会在磁盘中得到字节码文件(.class文件),也知道java命令会启动JVM将字节码文件加载进内存,但也仅仅止步于此了。至于从字节码文件加载进内存到堆中产生对象,期间具体发生了什么,他们并不清楚。

所谓“万物皆对象”,字节码文件也难逃“被对象”的命运。它被加载进内存后,JVM为其创建了一个对象,以后所有该类的实例,皆以它为模板。这个对象叫Class对象,它是Class类的实例。

大家想想,Class类是用来描述所有类的,比如Person类,Student类...那我如何通过Class类创建Person类的Class对象呢?这样吗:

Class clazz = new Class();

好像不对吧,我说这是Student类的Class对象也行啊。有点晕了...

其实,程序员是无法自己new一个Class对象的,它仅由JVM创建。

  • Class类的构造器是private的,杜绝了外界通过new创建Class对象的可能。当程序需要某个类时,JVM自己会调用这个构造器,并传入ClassLoader(类加载器),让它去加载字节码文件到内存,然后JVM为其创建对应的Class对象
  • 为了方便区分,Class对象的表示法为:Class<String>,Class<Person>

所以借此机会,我们不妨换种方式看待类和对象:

       也就是说,要得到一个类的实例,关键是先得到该类的Class对象!只不过new这个关键字实在太方便,为我们隐藏了底层很多细节,我在刚开始学习Java时甚至没意识到Class对象的存在。

Java反射机制

JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

java中那些对象可以有Class对象

  • Class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
  • interface:接口
  • [ ]:数组
  • enum:枚举
  • annotation:注解@interface
  • 基本数据类型
  • void
        Class c1 = Object.class;  //类         class java.lang.Object
        Class c2 = Comparable.class;//接口     interface java.lang.Comparable
        Class c3 = String[].class; //一维数组   class [Ljava.lang.String;
        Class c4 = int[][].class;//二维数组     class [[I
        Class c5 = Override.class;//注解       interface java.lang.Override
        Class c6 = ElementType.class;//枚举     class java.lang.annotation.ElementType
        Class c7 = Integer.class;//基本数据类型  class java.lang.Integer
        Class c8 = void.class;//void           void
        Class c9 = Class.class;//class         class java.lang.Class

获得Class对象主要有三种方法

(1)Object-->getClass

(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

(3)通过class类的静态方法:forName(String className)(最常用)

package fanshe;
 
public class Fanshe {
	public static void main(String[] args) {
		//第一种方式获取Class对象  
		Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
		Class stuClass = stu1.getClass();//获取Class对象
		System.out.println(stuClass.getName());
		
		//第二种方式获取Class对象
		Class stuClass2 = Student.class;
		System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
		
		//第三种方式获取Class对象
		try {
			Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
			System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
}

创建实例:通过反射来生成对象主要有两种方法:

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

Class<?> c = String.class;
Object str = c.newInstance();

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。

//获取String的Class对象
Class<?> str = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=c.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance(“hello reflection”);

反射机制常用的类:

  • Java.lang.Class;
  • Java.lang.reflect.Constructor;
  • Java.lang.reflect.Field;
  • Java.lang.reflect.Method;
  • java.lang.reflect.AccessibleObject
  • java.lang.reflect.Proxy
  • Java.lang.reflect.Modifier;
     
/*
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 * 
 * 1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有; 		
 * 		3).调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */

/*
 * 获取成员变量并调用:
 * 
 * 1.批量的
 * 		1).Field[] getFields():获取所有的"公有字段"
 * 		2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
 * 2.获取单个的:
 * 		1).public Field getField(String fieldName):获取某个"公有的"字段;
 * 		2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
 * 
 * 	 设置字段的值:
 * 		Field --> public void set(Object obj,Object value):
 * 					参数说明:
 * 					1.obj:要设置的字段所在的对象;
 * 					2.value:要为字段设置的值;
 */

/*
 * 获取成员方法并调用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 	 调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
):
 */
public class MethodClass {
 
	public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("ArrayGrammer.Person");
//        获取public成员,获取值 设置值
        Field name = aClass.getField("name");
        Person person= (Person) aClass.newInstance();
        name.set(person,"鲍鱼");
        String o = (String)name.get(person);
//        获取private成员,获取值,设置值
        Field age = aClass.getDeclaredField("age");
        age.setAccessible(true);
        age.set(person,12);
        Integer integer = (Integer)age.get(person);
//        获取所有成员
        Field[] declaredFields = aClass.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));
		//1.获取Class对象
		Class stuClass = Class.forName("fanshe.method.Student");
		//2.获取所有公有方法
		System.out.println("***************获取所有的”公有“方法*******************");
		stuClass.getMethods();
		Method[] methodArray = stuClass.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************获取所有的方法,包括私有的*******************");
		methodArray = stuClass.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************获取公有的show1()方法*******************");
		Method m = stuClass.getMethod("show1", String.class);
		System.out.println(m);
		//实例化一个Student对象
		Object obj = stuClass.getConstructor().newInstance();
		m.invoke(obj, "刘德华");
		
		System.out.println("***************获取私有的show4()方法******************");
		m = stuClass.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true);//解除私有限定
		Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
		System.out.println("返回值:" + result);	
	}
}

JDK的动态代理

能否不写代理类,而直接得到代理Class对象,然后根据它创建代理实例(反射)。(核心思想)

实现相同接口:

保证代理对象的内部结构和目标对象一致,这样我们对代理对象的操作最终都可以通过invoke方法转移到目标对象身上,代理对象只需专注于增强代码的编写

静态代理

假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志。

你如何在不修改已有代码的前提下,完成这个需求?

我首先想到的是静态代理。具体做法是:

1.为现有的每一个类都编写一个对应的代理类,并且让它实现和目标类相同的接口(假设都有)

 2.在创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。也就是说,代理对象 = 增强代码 + 目标对象(原对象)。有了代理对象后,就不用原对象了

 动态代理(能否不写代理类,而直接得到代理Class对象,然后根据它创建代理实例(反射))

  • Proxy有个静态方法:getProxyClass(ClassLoader, interfaces),只要你给它传入类加载器和一组接口,它就给你返回代理Class对象。

  •  调用代理对象的方法,最终都会调用InvocationHandler的invoke()方法。在invoke方法中调用目标对象的方法和增强代码

 

Proxy.newProxyInstance实现JDK动态代理
  • 创建一个接口
public interface Ocean {
    void oceanfood();
}
  • 创建接口的实现类
public class Fish implements Ocean{
    @Override
    public void oceanfood() {
        System.out.println("鱼吃草");
    }
}
  • 不创建代理类的情况下,获取代理对象实例

public class DynamicProxyObj {
    public static void main(String[] args) {
        Ocean proxyInstance = (Ocean)Proxy.newProxyInstance(Fish.class.getClassLoader(), Fish.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("在方法前执行");

                Fish fish = new Fish();
                Object invoke = method.invoke(fish);

                System.out.println("在方法后执行");
                return invoke;
            }
        });
        proxyInstance.oceanfood();



    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值