38_3反射【获取class对象、 操作属性、 操作方法(构造方法)】

本文介绍了Java反射机制,包括如何获取Class对象、操作类的属性和方法,强调了使用Class.forName和ClassLoader获取Class文件路径,以及如何通过getFields、getDeclaredFields、getConstructors等方法进行操作。还提到了封装反射操作的工具类,以提高代码的可维护性和灵活性。
摘要由CSDN通过智能技术生成

day38下

反射

前言

使用到一个类,JVM会将该类的class文件加载到方法区(类加载机制),

同时会在堆内存中创建该类的class对象,class对象的作为class文件的访问入口

Java的反射机制

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

理解:

反射实际上就是获取class对象,通过class对象访问class文件中的内容

访问内容:

  1. 属性
  2. 构造方法
  3. 普通方法(成员方法、静态方法、抽象方法…)
  4. 方法上的参数
  5. 方法上的返回值
  6. 数组

关键点-获取Class文件对象

利用反射获取class对象

问:class文件中包含了什么内容?
class文件包含了该类所有的信息

注意:

该类的class文件只在一次到方法区中,

该类的class对象在程序中是唯一的。

所以不管使用那种方式获取class对象都是同一个

public class Test01 {

	public static void main(String[] args) throws ClassNotFoundException {
		
		//获取class对象方式1
		Class<? extends Student> clazz1 = Student.class;
		
		//获取class对象方式2
		Student stu = new Student();
		Class<? extends Student> clazz2 = stu.getClass();
		
		//获取class方式3
		Class<?> clazz3 = Class.forName("com.qf.reflex01.Student");
		
		System.out.println(clazz1 == clazz2);//true
		System.out.println(clazz1 == clazz3);//true
	}
}

问:那种方法更好?

推荐path的获取方法更好,使用配置文件,可维护性更高;
但还是要根据不同情况相应使用三种方法

经验:配置文件 + Class.forName()

public class Test02 {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		
		Properties p = new Properties();
		p.load(Test02.class.getClassLoader().getResourceAsStream("classPath.properties"));
		String path = p.getProperty("path");
		
		Class<?> clazz = Class.forName(path);
		System.out.println(clazz);
	}
}
配置文件
//配置文件:classPath.properties文件
path=com.qf.reflex01.Student

注意:

父类子类都有toString情况,注意子类toString的重写【自动生成时注意添加父类的toString,不然可能出现输出不了对应预想的结果】

下面操作属性就会出现

public class Person {

	private String name;
	private char sex;
	private int age;
	//有参、无参构造、get、set、toString方法【略】

}
public class Student extends Person{
	
	private String classId;
	private String id;
	
	public static final String str = "风华雪月";
	//有参、无参构造、get、set、toString方法【略】

}

反射访问内容

反射操作注意设置权限

操作属性

带Declared的方法都能获取私有的
乱填属性名未匹配对应属性异常(用try-catch)
属性参数值=修饰符值累加(数字)判断其修饰情况(即用什么修饰)

操作属性方式:

直接设置属性(set方法,前提类和对象都有)
通过反射设置属性(优点:后面配置文件设置,底层自动创建类和对象)

public class Test03 {

	public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		
//		Properties p = new Properties();
//		p.load(Test03.class.getClassLoader().getResourceAsStream("classPath.properties"));
//		String path = p.getProperty("path");
		
//		Class<?> clazz = Class.forName(path);
		
		//获取本类及其父类公有的属性对象
//		Field[] fields = clazz.getFields();
//		for (Field field : fields) {
//			System.out.println(field);
//		}
		
		//获取本类所有的属性对象
//		Field[] fields = clazz.getDeclaredFields();
//		for (Field field : fields) {
//			System.out.println(field);
//		}
		
		//获取本类及其父类所有的属性对象
//		for(Class<?> c = clazz;c!=null;c=c.getSuperclass()){
//			Field[] fields = c.getDeclaredFields();
//			for (Field field : fields) {
//				System.out.println(field);
//			}
//		}
		
		
//		try {
//			//获取本类指定的属性对象
//			Field field = clazz.getDeclaredField("str");
//			System.out.println(field);
//			
//			//获取属性参数值
//			int modifiers = field.getModifiers();
//			System.out.println("是否使用public修饰:" + Modifier.isPublic(modifiers));
//			System.out.println("是否使用stati修饰:" + Modifier.isStatic(modifiers));
//			System.out.println("是否使用final修饰:" + Modifier.isFinal(modifiers));
//			System.out.println("是否使用private修饰:" + Modifier.isPrivate(modifiers));
//			System.out.println("是否使用protected修饰:" + Modifier.isProtected(modifiers));
//			System.out.println("是否使用transient修饰:" + Modifier.isTransient(modifiers));
//			
//		} catch (NoSuchFieldException e) {//属性名没有配到对应的属性就报该异常
//			e.printStackTrace();
//		} catch (SecurityException e) {
//			e.printStackTrace();
//		}
		
		//通过反射设置对象里的属性
//		Student stu = new Student("奇男子", '男', 23, "2401", "001");
//		Field field = clazz.getDeclaredField("id");
//		field.setAccessible(true);//设置修改权限
//		field.set(stu, "666");//设置stu对象里的id属性为"666"的值
//		System.out.println(stu);
		
		//通过反射工具类设置对象里的属性
		Student stu = new Student();
		ReflexUtil.setField(stu, "name", "奇男子");
		ReflexUtil.setField(stu, "sex", '男');
		ReflexUtil.setField(stu, "age", 23);
		ReflexUtil.setField(stu, "classId", "2401");
		ReflexUtil.setField(stu, "id", "001");
		
		System.out.println(stu);
	}
}

操作方法

构造方法

不能获取父类的构造方法【一般考虑本类的构造方法】
反射工具类再添加创建对象的方法【外面传数组】

调用构造函数创建对象

public class Test04 {

	public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
//		Properties p = new Properties();
//		p.load(Test04.class.getClassLoader().getResourceAsStream("classPath.properties"));
//		String path = p.getProperty("path");
		
//		Class<?> clazz = Class.forName(path);
		
		//获取该类公有的构造方法对象
//		Constructor<?>[] constructors = clazz.getConstructors();
//		for (Constructor<?> constructor : constructors) {
//			System.out.println(constructor);
//		}
		
		//获取该类所有的构造方法对象
//		Constructor<?>[] constructors = clazz.getDeclaredConstructors();
//		for (Constructor<?> constructor : constructors) {
//			System.out.println(constructor);
//		}
		
		//获取无参构造对象
//		Constructor<?> constructor = clazz.getDeclaredConstructor();
//		//利用无参构造对象创建该类的对象
//		constructor.setAccessible(true);//设置修改权限
//		Student stu = (Student) constructor.newInstance();
//		System.out.println(stu);
		
		//利用无参构造对象创建该类的对象 -- 简化版
//		Student stu = (Student) clazz.newInstance();
//		System.out.println(stu);
		
		//获取有参构造对象
//		Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,char.class,int.class,String.class,String.class);
//		//利用有参构造对象创建该类的对象
//		constructor.setAccessible(true);//设置修改权限
//		Student stu = (Student) constructor.newInstance("奇男子",'男',23,"2401","001");
//		System.out.println(stu);
		
		//利用反射工具类去创建对象 -- 底层调用无参构造
//		Student stu = ReflexUtil.newInstance(Student.class, null, null);
//		System.out.println(stu);
		
		//利用反射工具类去创建对象 -- 底层调用有参构造
		Class<?>[] parameterTypes = {String.class,char.class,int.class,String.class,String.class};
		Object[] paremeters = {"奇男子",'男',23,"2401","001"};
		Student stu = ReflexUtil.newInstance(Student.class, parameterTypes, paremeters);
		System.out.println(stu);
	}
}

反射的工具类

将学习的反射相应操作封装到工具类
utils包下
这里反射工具类写了获取和设置的方法【这里获取属性用到getClass()更方便】

public class ReflexUtil {

	/**
	 * 获取当前类及其父类的属性对象
	 * @param clazz class对象
	 * @param name 属性名
	 * @return 属性对象
	 */
	public static Field getField(Class<?> clazz,String name){
		
		for(Class<?> c = clazz;c != null;c = c.getSuperclass()){
			try {
				Field field = c.getDeclaredField(name);
				return field;
			} catch (NoSuchFieldException e) {
			} catch (SecurityException e) {
			}
		}
		return null;
	}
	
	/**
	 * 设置对象中的属性
	 * @param obj 对象
	 * @param name 属性名
	 * @param value 属性值
	 */
	public static void setField(Object obj,String name,Object value){
		
		Field field = getField(obj.getClass(), name);
		if(field != null){
			field.setAccessible(true);
			try {
				field.set(obj, value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 创建对象
	 * @param clazz class对象
	 * @param paremeterType 构造方法中的参数类型数组
	 * @param paremeters 构造方法中的参数数据
	 * @return 对象
	 */
	public static <T> T newInstance(Class<T> clazz,Class<?>[] parameterTypes,Object[] paremeters){
		
		try {
			Constructor<T> constructor = clazz.getDeclaredConstructor(parameterTypes);
			constructor.setAccessible(true);
			T obj = constructor.newInstance(paremeters);
			return obj;
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return null;
	}
}
  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值