JAVA 的反射

本文围绕Java反射机制展开,介绍了反射概要、优缺点及字节码对象的三种获取方式。详细阐述了反射操作构造方法、成员变量、成员方法的步骤和代码案例,包括不同权限的操作,如私有成员的反射操作,最后给出了反射的综合案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

							Java的反射

1、反射机制的概述和字节码对象的获取方式

1、反射概要

反射,一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

优点:

1、反射提高了程序的灵活性和扩展性。

2、降低耦合性,提高自适应能力。

3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

2、字节码文件获取的三种方式

1、对象名.getCalss(); 						// 次方法来自于Object 对象已经存在的情况下, 可以使用这种方式

2、类名.class 								// 类名.class这是一个静态的属性, 只要知道类名, 就可以获取

3、Class.forName("com.itheima_01.Student"); 	// 通过Class类中的静态方法, 指定字符串, 该字符串是类的全类名(包名+类名)

注意:此处将会抛出异常都系 ClassNotFoundException 防止传入错误的类名

3、案例代码

package com.itcast_01;

	/*
	* 反射:
	* 	在运行时,我们可以获取任意一个类的所有方法和属性
	* 	在运行时,让我们调用任意一个对象的所有方法和属性
	*
	* 反射的前提:
	* 	要获取类的对象(Class对象)
	*/

public class ReflectDemo {

	public static void main(String[] args) throws ClassNotFoundException {

		// 通过Object的getClass()方法获取,必须要有对象
		Student s = new Student();
		Class clazz = s.getClass();

		// 通过类名获取字节码对象
		Class clazz2 = Student.class;

		// static Class<?> forName(String className)
		Class clazz3 = Class.forName(\"com.itheima_01.Student\");

		System.out.println(clazz == clazz2);
         System.out.println(clazz == clazz3);
         System.out.println(clazz);
	}
}

4、问题: 字节码对象是用来描述什么的?

用来描述.class文件的

	面向对象阶段的时候讲过java中描述事物都是通过类的形式

   		 而字节码文件也可以看做为一种事物, 如何描述这种事物? 那就看看这个事物是由什么组成的了

	1、成员变量 	==	Filed

	2、成员方法	==	Method

	3、构造方法	==	Constructor	

2、反射操作构造方法(Constructor)

1、通过获取的构造创建对象

步骤:

1、获得Class对象

2、获得构造

3、通过构造对象获得实例化对象

package com.itcast_01;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/*
 *	通过反射获取构造方法并使用
 *			Constructor<?>[] getConstructors()  
 *			Constructor<T> getConstructor(Class<?>... parameterTypes) 
 *			  newInstance()   
 *Constructor:
 *	 	 newInstance(Object... initargs)  
 */
public class ReflectDemo2 {
	public static void main(String[] args) throws ReflectiveOperationException {
		Class clazz = Class.forName("com.itheima_01.Student");
		
		//method(clazz);
		//Constructor<T> getConstructor(Class<?>... parameterTypes) 
		//method2(clazz);
		//method3(clazz);
		
		Object obj = clazz.newInstance();
		System.out.println(obj);
	}

	private static void method3(Class clazz)throws Exception {
		Constructor c = clazz.getConstructor(String.class,int.class);//获取有参构造,参数1类型为String,参数2类型为int
		System.out.println(c);
		Object obj = c.newInstance("lisi",30);
		System.out.println(obj);
	}

	private static void method2(Class clazz)throws Exception {
		Constructor c = clazz.getConstructor();//获取无参构造
		System.out.println(c);
		Object obj = c.newInstance();
		System.out.println(obj);
	}

	private static void method(Class clazz) {
		//Constructor<?>[] getConstructors() :获取所有public修饰的构造方法 
		Constructor[] cs = clazz.getConstructors();
		for (int i = 0; i < cs.length; i++) {
			System.out.println(cs[i]);
		}
	}
}

2、问题: 直接通过Class类中的newInstance()和获取getConstructor()有什么区别?

newInstance()方法, 只能通过空参的构造方法创建对象

getConstructor(Class<T>... parameterTypes)方法, 方法接受一个可变参数, 可以根据传入的类型来匹配对应的构造方法

总结(只能获取public修饰的构造方法 )

Constructor<?>[] getConstructors()  //   方法1: 获取该类中所有的构造方法, 返回的是一个数组

Constructor<T> getConstructor(Class<?>... parameterTypes)   //  方法2: 方法接受一个可变参数, 可以根据传入的类型, 来匹配对应的构造方法

3、获取任何权限的构造方法(推荐是使用这两种)

Constructor<?>[] getDeclaredConstructors()  //   方法1: 获取该类或接口中所有的构造方法, 返回的是一个数组

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)   //  方法2: 方法接受一个可变参数, 可以根据传入的类型, 来匹配对应的构造方法

3、反射操作成员变量(Field)

  1. 通过反射获取该类的字节码对象

    Class clazz = Class.forName(“com.itcast.Person”);

  2. 创建该类对象

    Object obj = clazz.newInstance(); // 调用了类中的无参构造方法

  3. 获取该类中需要操作的字段(成员变量)

    getField(String name) --> 方法传入字段的名称.

    Field f = clazz.getField(“age”); // 注意: 此方法只能获取公共的字段

  4. 通过字段对象中的方法修改属性值

    void set(Object obj, Object value)
    参数1): 要修改那个对象中的字段;
    参数2): 将字段修改为什么值.f.set(p, 23);

1、案例代码

package com.itcast_01;

import java.lang.reflect.Field;

	/*
	* 通过反射获取成员变量并使用
	* Field[] getFields()     获取所有public修饰的成员变量,返回一个数组,
	* Field getField(String name)    获取指定的public修饰的成员变量  返回单个的成员变量名
	*
	* Field[] getDeclaredFields()         获取所有的成员变量名
	* Field getDeclaredField(String name)    获取指定的成员变量名
	*
	* Field的取值和存值的方法
	* 	Object get(Object obj)   
	* 	void set(Object obj, Object value)
	*/

public class ReflectDemo3 {

	public static void main(String[] args) throws ReflectiveOperationException {

		// 获取学生类的字节码对象

		Class clazz = Class.forName("com.itcast_01.Student");

		// 获取学生类的对象

		Object stu = clazz.newInstance();

		// Field getField(String name) :根据字段名称获取公共的字段对象

		Field f = clazz.getField("age");	// 获取成员变量对象

		// System.out.println(f);

		// void set(Object obj, Object value)

		f.set(stu,28);		// 通过成员变量对象,修改指定对为指定的值

		// Object get(Object obj)

		Object age = f.get(stu);// 通过对象获取成员变量的值

		System.out.println(age);

		System.out.println(stu);

	}

		private static void method(Class clazz){

		// Field[] getFields() :获取公共的成员变量

			Field[] fs = clazz.getFields();

		for (int i = 0; i < fs.length; i++) {

			System.out.println(fs[i]);

		}

			System.out.println("----------");

			//getDeclaredFields() :获取所有的成员变量

			Field[] fs2 = clazz.getDeclaredFields();

			for (int i = 0; i < fs2.length; i++) {

				System.out.println(fs2[i]);

			}
		}
	}

2、方法总结

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

                 Field[] getFields()              			   --> 	返回该类所有(公共)的字段

                 Field getField(String name)      		   --> 	返回指定名称字段

                Field[] getDeclaredFields()      		   --> 	暴力反射获取所有字段(包括私有) 

                Field getDeclaredField(String name)	   --> 	暴力反射获取指定名称字段

          Field:

                  Object get(Object obj)          		   --> 	Field对象调用, 返回传入对象的具体字段

                  void set(Object obj, Object value) 	   -->  	Field对象调用

                               参数1: 要修改的对象

                               参数2: 将此对象的字段修改为什么值.

3、反射private成员变量(练习)

反射private属性执行流程

  1. 获取学生类字节码对象
  2. 获取学生对象
  3. 通过getDeclaredField方法获取私有字段
  4. 通过setAccessible让jvm不检查权限
  5. 通过set方法设置对象为具体的值

案例代码

package com.itcast_01;

import java.lang.reflect.Field;

	/*
	* 通过反射获取私有成员变量并使用
	* Field[] getDeclaredFields()
	* Field getDeclaredField(String name)
	*/

	public class ReflectDemo4 {

	public static void main(String[] args) throws ReflectiveOperationException {

		// 获取学生类的字节码对象

			Class clazz = Class.forName("com.itheima_01.Student");

		// 获取学生对象

			Object stu = clazz.newInstance();

		// 获取私有的字段对象

			Field f = clazz.getDeclaredField("name");

			f.setAccessible(true);	// 设置反射时取消Java的访问检查,暴力访问

		// 给获取的成员变量赋值

			f.set(stu, "lisi");

			Object name = f.get(stu);

			System.out.println(name);
		}
}

方法总结

           Field[] getDeclaredFields()      			--> 		暴力反射获取所有字段(包括私有) 

           Field getDeclaredField(String name) 	--> 		暴力反射获取指定名称字段

           void setAccessible(boolean flag) 		--> 		让jvm不检查权限

4、反射获取成员方法(Method)

1、反射获取普通成员方法

反射public方法执行流程

  1. 获取学生类字节码对象
  2. 反射手段创建学生对象
  3. 调用getMethod方法获取Method对象, 方法形参接受方法的名字
  4. 调用Method方法中的invoke()将方法运行

案例代码

package com.itcast_01;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

	/*
	* 通过反射获取成员方法并使用
	* Method getMethod(String name, Class<?>... parameterTypes)
	* Method:
	* 	Object invoke(Object obj, Object... args)  
	*/

public class ReflectDemo5 {

	public static void main(String[] args) throws ReflectiveOperationException {

		//获取学生类的字节码对象

		Class clazz = Class.forName("com.itheima_01.Student");

		//获取学生类的对象

		Object stu = clazz.newInstance();

		//获取无参有返回值的方法

		Method m = clazz.getMethod("getName");

		Object obj = m.invoke(stu);   // 执行

		System.out.println(obj);

	}

	private static void method2(Class clazz, Object stu) throws Exception {

		//获取有参无返回值的方法

		Method m = clazz.getMethod("setName", String.class);

		m.invoke(stu, "lisi");

		System.out.println(stu);

	}

	private static void method(Class clazz, Object stu) throws Exception {

		//获取无参无返回值的方法

		Method m = clazz.getMethod("method");

		m.invoke(stu);

	}
}

方法总结

Class:

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

        // 此方法由字节码对象调用

        // 参数1: 要反射的方法名称

        // 参数2: 此方法需要接受的参数类型(注意,传入的都是字节码)

Method:

Object invoke(Object obj, Object... args) 

        // 方法由Method对象调用

        // 参数1: 要由那个对象调用方法

        // 参数2: 方法需要的具体实参(实际参数)

2、私有的成员方法怎么玩?

// 获取字节码对象

	Class clazz = Class.forName("com.heima.Student");

// 创建学生对象

	Object stu = clazz.newInstance();

// 暴力反射获取方法

	Method method = clazz.getDeclaredMethod("method");

// 让jvm不检查权限

	method.setAccessible(true);

// 执行方法

	method.invoke(stu);

5、反射的综合案例

学生类

package com.reflect;

public class Student {
	private String name;
	private String sex;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}

给学生类的属性赋值

package com.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

/*反射案例 通过两种方法给Student类中的成员赋值
*	1、使用java中的反射技术将类中的属性与map中的key相同名称的,使用反射技术将key对应的value值赋值给属性.
*
*	2、通过get/setXxx的方法
*/
public class Demo1 {

	@Test
	public void test01() throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();

		map.put("name", "张三");
		map.put("sex", "男");
		map.put("age", 21);
		map.put("address", "上海");

		// 获取Student的字节码
		Class clazz = Class.forName("com.reflect.Student");

		// 获取字节码的无参对象
		Object obj = clazz.newInstance();
		// 获取类中的成员变量名Field
		Field[] fieldNames = clazz.getDeclaredFields();
		// 得到map中所有的key
		Set<String> key = map.keySet();

		// 遍历获取的成员变量
		for (Field name : fieldNames) {
			// 获取属性名
			String field = name.getName();
			if (key.contains(field)) {
				// 给Student属性赋值
				name.setAccessible(true); // 取消jvm的检查
				name.set(obj, map.get(field));
			}
		}
	}

	@Test
	public void test02() throws Exception {
		// 采用get/set方法存入属性值
		Map<String, Object> map = new HashMap<String, Object>();

		map.put("name", "张三");
		map.put("sex", "男");
		map.put("age", 21);
		map.put("address", "上海");

		// 获取字节码
		Class clazz = Class.forName("com.reflect.Student");

		// 获取对象
		Object obj = clazz.newInstance();

		// 获取setXxx的成员方法
		Method[] methods = clazz.getDeclaredMethods();

		// 遍历map
		for (String key : map.keySet()) {
			// 将所有的key上加上set 不区分大小写
			String mname = "set" + key;
			for (Method method : methods) {
				String m = method.getName();
				if (mname.equalsIgnoreCase(m)) {
					// 给属性赋值
					method.invoke(obj, map.get(key));
				}

			}
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值