天天看点

Java反射 + 浅谈动态代理

当了那么久的小白,今天我也来点干货。

保姆级反射浅谈

    • 什么是反射
    • 个人对`Class`类的理解
    • 获取类的类对象的三种方式
    • 通过反射获取类的所有属性、方法和构造器
    • 通过反射的方法赋值
      • 方法一(反射调用构造器setXxx方法)
      • 方法二(Method反射调用自身)
      • 方法三(暴力法)
    • 浅谈静/动态代理(下回分解)

什么是反射

JAVA反射机制是在运行状中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

个人对

Class<T>

类的理解

顾名思义Class是类的类,这怎么去理解呢,类本来就是属性和行为的抽象,那么Class类就是所有类相同属性和行为的抽象,我的理解是把所以类的共同点都抽离出来构建的一个类,比如类都有自己的方法、属性、构造器等。我们把这些特性剥离出来,抽象出一个Class类。

参数类型

T - 由此类对象建模的类的类型。 例如, String.class的类型是

Class<String>

。 如果正在建模的类是未知的,请使用Class<?> 。

获取类的类对象的三种方式

//方法一:
Class<?> clas = Class.forName("java.lang.Object");
//方法二:通过类的名字打打点class,如
Class<?> clas1 = Object.class;
//方法三:通过该类的对象去调用getClass()方法,如
Class<?> clas2 = new Object().getClass();
System.out.println(clas.toString());
//它们的结果都是: class java.lang.Object
           

通过反射获取类的所有属性、方法和构造器

我们可以调用getDeclaredFields()、getDeclaredMethods()和getDeclaredConstructors()来获取这个类的所有属性和方法。

这里我写了一个封装类Person。

修饰 属性/方法 数据类型/返回值 getter setter
private id Integer
private name String
private age String
public toString() String - -
public printInfo() - - -

再花点时间做个测试类

//获得一个类的类对象
		Class<?> clazz = Person.class;
		//获得传递过来的类的所有方法
		Method[] methods = clazz.getDeclaredMethods();
		
		//System.out.println(Arrays.toString(methods));
		for(Method m : methods){
			System.out.println(m);
		}
		System.out.println("---------------------------------");
		//获得类的所有属性
		Field[] fields = clazz.getDeclaredFields();
		for(Field f : fields){
			System.out.println(f);
		}
		System.out.println("---------------------------------");
		//获得类的所有的构造器
		Constructor<?>[] cs = clazz.getDeclaredConstructors();
		
		for(Constructor c : cs){
			System.out.println(c);
		}
	}
           

控制台输出如下:

public java.lang.String cn.reflect.Person.toString()
public java.lang.String cn.reflect.Person.getAddress()
public java.lang.String cn.reflect.Person.getName()
public java.lang.Integer cn.reflect.Person.getId()
public void cn.reflect.Person.setName(java.lang.String)
public void cn.reflect.Person.setId(java.lang.Integer)
public void cn.reflect.Person.setAddress(java.lang.String)
private static void cn.reflect.Person.printInfo()
---------------------------------
private java.lang.Integer cn.reflect.Person.id
private java.lang.String cn.reflect.Person.name
private java.lang.String cn.reflect.Person.address
---------------------------------
public cn.reflect.Person()
public cn.reflect.Person(java.lang.Integer,java.lang.String,java.lang.String)
           

如果想获得单独的属性我们可以调用getDeclaredField(String name),这里就不赘述了。这里提醒一点,当你想去获取带有参数的方法时要特别注意,例如setName(String name)就带有参数,当调用getDeclaredMethod(String name, Class<?>...parameterTypes)反射前者时,因为方法可能会出现方法重载的情况,所以必须指定其参数列表,即将每个参数所对应的类的类对象添加到Class<?>[]{}之中。(Class<?>...parameterTypes理解为Class<?>[]{} )

Class<?> clas = Person.class;
Method method = clas.getDeclaredMethod("setName", new Class[]{String.class})
           

常用的反射getDeclaredConstructor方法同理,另外如果该方法没有参数列表,我们可以将其改为new Class[]{}或null(null不会报错,但不推荐)

通过反射的方法赋值

方法一(反射调用构造器setXxx方法)

1)获得目标类的类对象

2)用类对象获得目标类的构造器

3)根据类的默认构造器来获得一个对象

4)获得类的所以方法

5)根据方法名获得属性对应的数据类型

6)反射调用setXxx方法

Class<?> class1 = Class.forName("cn.reflect.Person");
		//获得构造器
		Constructor<?> constructor = class1.getDeclaredConstructor(new Class[]{});
		//根据类的默认构造器来获得一个对象
		Object instance = constructor.newInstance(new Object[]{});
		System.out.println(instance);
		//获得类的所有方法
		Method[] methods = class1.getDeclaredMethods();
		for(Method m :methods){
			//获得方法的名字
			String name = m.getName();
			//获得是否以set开始
			boolean startsWith = name.startsWith("set");
			if(startsWith){
				//获得到了set字符串的后面的值
				String fieldName = name.substring(3);
				//获得属性的名字
				fieldName = fieldName.substring(0, 1).toLowerCase()+fieldName.substring(1);
				//获得方法所对应的属性
				Field field = class1.getDeclaredField(fieldName);
				//获得属性的具体类型,获得属性对应的类型
				Class<?> type = field.getType();
				if(type == Integer.class){
					//反射调用set方法
					m.invoke(instance, new Object[]{1});
				}
				if(type == String.class && "address".equals(fieldName)){
					//反射调用set方法
					m.invoke(instance, new Object[]{"北京"});
				}
				if(type == String.class && "name".equals(fieldName)){
					//反射调用set方法
					m.invoke(instance, new Object[]{"李华"});
				}
			}
		}
		System.out.println(instance);
           

控制台输出:

Person [id=null, name=null, address=null]
Person [id=1, name=李华, address=北京]
           

方法二(Method反射调用自身)

1)…

2)…

3)指定setXxx方法

4)通过Method反射调用自身;关键方法invoke(Obkect obj, Object…args)

如果底层方法是实例方法,第一个参数就是该对象实例,第二个参数是方法的参数列表,invoke的返回值就是实例方法的返回值。

如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。

如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null

Class<?> class1 = Class.forName("cn.reflect.Person");
		//获得构造器
		Constructor<?> constructor = class1.getDeclaredConstructor(new Class[]{});
		//根据类的默认构造器来获得一个对象
		Object instance = constructor.newInstance(new Object[]{});
		Method method = class1.getDeclaredMethod("setName", new Class[]{String.class});
		
		Object invoke = method.invoke(instance, new Object[]{"李华"});
		System.out.println(instance);
           

控制台输出

方法三(暴力法)

1)…

2)…

3)根据方法名字来获得属性对象

4)破坏掉私有属性,然后赋值

Class<?> class1 = Class.forName("cn.reflect.Person");
	//获得构造器
	Constructor<?> constructor = class1.getDeclaredConstructor(new Class[]{});
	//根据类的默认构造器来获得一个对象
	Object instance = constructor.newInstance(new Object[]{});
	//根据方法名字来获得属性对象
	Field field = class1.getDeclaredField("name");
	//破坏掉私有属性
	field.setAccessible(true);
	field.set(instance, "李华");
	System.out.println(instance);
           

控制台输出:

浅谈静/动态代理(下回分解)