java反射机制

反射:其实就是通过类的全限定类名,获取该类的字节码文件,然后通过该字节码文件获取字段、构造方法、普通方法等信息

创建对象过程:加载Class文件—匹配对应参数的构造方法—通过构造方法创建实例
那反射与new 对象的区别就是第一步的加载时期,前者是运行期,后者是编译期

那有什么意义呢?
反射属于比较底层的了
就是在某个类还没有设计好的时候,可以保证继续向后开发。
但只是保证功能开发完成,是运行不了的,因为这个类里的某些字段,或构造方法,或普通方法还没写。
而项目真正运行的时候,肯定所有的项目中的类都已经设计完毕,这时候,就可以保证代码正常运行了

作用:

  1. 提高开发灵活度,提高程序的扩展性
  2. 框架(提高开发效率的别人封装好的代码)底层都是使用反射技术。例如:JDBC加载驱动、Tomcat、Spring、BeanUtils.copyProperties()…
  3. 缺点:破坏封装性,性能低下(以后,能不用反射技术就不用)

有Declared获取时,不受权限修饰符的限制,但不能获得父类的。非public的必须setAccessible(true)
直接get获取时,只能获取public的,但可以获得父类的

1、获取构造器,创建对象

public class ReflectConstructor {

	public static void main(String[] args) throws Exception {
		//使用反射机制:通过字节码文件获取源文件,从而使用其内部的字段/构造方法/普通方法
		Class<?> user = Class.forName("cn.itsource.day023.reflect.User");
		
		//getConstructors():获取该类的所有public的构造方法
		Constructor<?>[] constructors = user.getConstructors();
		System.out.println(Arrays.toString(constructors));
		
		//getConstructor(可变参数):根据形参列表获取对应的public的构造方法
		Constructor<?> constructor = user.getConstructor();
		Constructor<?> constructor2 = user.getConstructor(String.class,String.class);
		System.out.println(constructor +"\n"+constructor2);
		
		
		//getDeclaredConstructors():获取所有的构造方法
		Constructor<?>[] declaredConstructors = user.getDeclaredConstructors();
		System.out.println(Arrays.toString(declaredConstructors));
		
		//getDeclaredConstructor(可变参数):根据形参列表获取对应的构造方法
		Constructor<?> declaredConstructor = user.getDeclaredConstructor();
		Constructor<?> declaredConstructor2 = user.getDeclaredConstructor(String.class);
		System.out.println(declaredConstructor +"\n"+declaredConstructor2);
		
		
		//对于public的构造方法,前面获取了之后,通过newInstance(...)来创建该类的对象
		Object user1 = constructor2.newInstance("shp","666");
		System.out.println(user1);
		
		//对于private的构造方法,需要先破坏该类的封装,然后再通过newInstance(...)来创建该类的对象
		declaredConstructor2.setAccessible(true);//要用哪个构造方法,就是哪个构造方法来破坏
		Object user2 = declaredConstructor2.newInstance("shiheping");
		System.out.println(user2);
		
		//上面破坏了该类的封装,但是只是对于那个构造器来说是破坏了的,但是这个类的构造方法仍然是private
		//Constructor<?> constructor3 = user.getConstructor(String.class);//不可以
		
		
		//1. Class类中方法newInstance():创建当前字节码对象(只能调用无参且是public修饰的构造方法)
		//通过字节码clazz也可以获取对象,但是只能获取一个public修饰的无参构造创建的对象
		Object user3 = user.newInstance();
		System.out.println(user3);
	}

}

2、使用普通方法

public class ReflectMethod {

	public static void main(String[] args) throws Exception {
		//使用反射机制:通过字节码文件获取源文件,从而使用其内部的字段/构造方法/普通方法
		Class<?> user = Class.forName("cn.itsource.day023.reflect.User");
		
		//getMethods():获取该类的所有public的普通方法(包括从父类继承过来的)
		Method[] methods = user.getMethods();
		System.out.println(Arrays.toString(methods));
		
		//getMethod(方法名,可变参数):根据形参列表获取对应的public的普通方法
		Method method1 = user.getMethod("testStatic");
		Method method3 = user.getMethod("test2",String.class);
		System.out.println(method1 + "\n"+method3);
		
		//getDeclaredMethods():获取该类自己所有的普通方法
		Method[] declaredMethods = user.getDeclaredMethods();
		System.out.println(Arrays.toString(declaredMethods));
		
		//getDeclaredMethod(方法名,可变参数):根据形参列表获取对应的普通方法
		Method declaredMethod = user.getDeclaredMethod("testPrivate");
		
		
		//=========================执行方法=========================
		//对于类中的非静态方法,只能通过创建对象去调用,所以要先创建对象
		Object user1 = user.newInstance();
		Object invoke = method3.invoke(user1, "shp");
		declaredMethod.invoke(user1);//若方法是无参的,后面就不用写了
		
		//对于类中的静态方法,不用创建对象再去调用,直接传入null
		method1.invoke(null);
	}

}

3、获得字段

public class ReflectTest3 {

	public static void main(String[] args) throws Exception {
		/*
		 * 1. 获取字节码文件(通过反射)
		 */
		//1.1 Class clazz  = Class.forName(全限定路径名) (最多使用)默认就是调用下面的方法
		Class<?> clazz = Class.forName("cn.itsource.day023.User");
		
		/*
		 * 2. 字段		获取字段 的目的就是为了被 对象或者 类  赋值取值
				Field[] fields = clazz.getFields() 获取public修饰的字段
				Field[] fields = clazz.getDeclaredFields() 获取任意权限所有字段
				Field field = clazz.getDeclaredField(String fieldName) 根据字段名获取任意访问权限的指定字段
				Field field = clazz.Field(String fieldName)根据字段名获取public的指定字段
		 */
		//Field[] fields = clazz.getFields() 获取public修饰的字段
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field);
		}
		
		//Field[] fields = clazz.getDeclaredFields() 获取任意权限所有字段
		Field[] fields2 = clazz.getDeclaredFields();
		for (Field field : fields2) {
			System.err.println(field);
		}
		
		/*
		 * 	Field field = clazz.getField(String fieldName)根据字段名获取public的指定字段
		 */
		//获取public int age成员变量
		Field field = clazz.getField("age");
		System.out.println("age : " + field);
		
		/*
		 * 	Field field = clazz.getDeclaredField(String fieldName) 根据字段名获取任意访问权限的指定字段
		 */
		//获取private String name;成员变量
		Field field2 = clazz.getDeclaredField("name");
		System.out.println("name : " + field2);
		
		//获取public static String country;成员变量
		Field field3 = clazz.getDeclaredField("country");
		System.out.println("country : " + field3);
		
		/*
		 * 通过当前的字段对象:	
		 * Field中的方法
			给某一个字段取值
		  	field.get(Object obj);//如果是属于非static,就传入一个对象,如果是静态的,就传入null
		  		obj:对象
			给某一个字段赋值
			field.set(Object obj, Object value);//如果是属于非static,就传入一个对象,如果是静态的,就传入null
		  		obj:对象
				value:值
		 */
		//给public int age成员变量赋值,因为age是实例变量,所以需要创建一个User对象
		Object user = clazz.newInstance();
		//field.set(Object obj, Object value);
		field.set(user, 17);//赋值必须根据变量是否有static修饰决定,age没有static修饰,传入对象。必须传入相同数据类型实参
		
		//获取public int age成员变量值:	field.get(Object obj);
		Object age = field.get(user);
		System.out.println("age = " + age);
		
		//给 public static String country;成员变量赋值	给某一个字段赋值:field.set(Object obj, Object value);
		//field.set(Object obj, Object value);
		field3.set(null, "中国");//静态字段不用传入对象,只要传入一个null即可;"中国"是field3的值
		
		//给 public static String country;成员变量取值	给某一个字段取值:field.get(Object obj);
		Object country = field3.get(null);//静态字段不用传入对象,只要传入一个null即可
		System.out.println("country = " + country);
	}

}

4、BeanUtils.copyProperties()
这个功能也可以通过序列化与反序列化实现,就是对象转json再转对象
在这里插入图片描述
在拷贝的二者对象之间的很多个字段名有差异时,推荐MapStruct这个对象映射工具
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值