Java 反射操作(完全版)

本文深入解析Java反射机制,涵盖Class类的使用、属性Field、构造方法Constructor和Method的获取,以及在Android插件化、Hook技术中的关键作用。通过实例演示如何动态创建对象、调用方法并修改属性,适合进一步学习和实践。

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

前言

相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有个开源框架jOOR相信很多人都用过,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架,当然这里所讲的反射技术,是学习Android插件化技术、Hook技术等必不可少的!


一、什么是反射?

定义:通过Class动态获取类的相关信息,动态的的创建对象,动态的调用方法,动态的改变对象上面的属性,这种方式就称为反射.

  • 每个类在运行的时候都会有对应的Class
  • 我们要想执行反射操作,必须先要获取指定的Class

反射在开发中的作用:要创建那个类的对象,调用该对象的那个方法,这些信息被写到一个文件中,程序运行的时候,读取这些信息,然后根据这些信息,动态的创建对象,动态的调用方法(后面的框架都是这么做的)

二、反射相关的类和接口

在这里插入图片描述

三.Class类

类对象有哪些信息?

  1. 包名信息
  2. 类的信息: 修饰符,类名 ,继承的基类,实现的接口
  3. 类的属性信息: 修饰符,类型,属性名称 ,缺省值
  4. 类的方法的信息: 修饰符,返回类型,方法名称,参数列表,抛出的异常
  5. 类的构造方法的信息: 修饰符,类名,参数列表,抛出的异常

为了描述类对象的相关信息,我们需要定义一个Class类
因为类对象的相关信息保存在Class类中,所以Class类中会有获取这些信息的方法

java中的每个类在运行的时候都会有一个对象的Class,这个Class就是我们上面说的Class
我们要想分享一个类的相关信息,需要先获取到这个类对象的Class

如果来获取指定类(引用类型)的Class呢? (获取引用类型Class的不同方式)

  1. Class claz = Class.forName(“完整的类名”)
  2. 类名.class
  3. 对象.getClass();

获取基本类型的Class的不同方式

  1. 基本类型.class ; 例如: int.class
  2. 包装器类型.TYPE; 例如: Integer.TYPE , 获取到的是int的class

3.1 通过Class获取属性Field

Java中把类中的属性封装成Field

  1. 相关的方法
    A. 获取多个属性的方法
    (a)public Field[] getFields():
    则此方法返回该类及其所有超类的公共字段
    (b)public Field[] getDeclaredFields() :
    获取本类中声明的所有的字段(公开的,保护的,缺省的,私有的),但是没有继承的

B. 获取单个的属性的方法
(a) public Field getField(String name) :
获取指定名称的公开的字段,包括本类中和继承下来的公开的字段
(b) public Field getDeclaredField(String name)
获取指定名称的字段,包括本类中声明的字段

3.2 通过Class获取构造方法Constructor

Java中把构造方法封装成Constructor

  1. 获取多个构造方法
    (a) public Constructor<?>[] getDeclaredConstructors()
    获取类中声明的构造方法,包括公开的,保护的,缺省的,私有的构造方法
    (b) public Constructor<?>[] getConstructors()
    获取本类中所有的公开的构造方法

  2. 获取单个的构造方法
    (a) public Constructor getConstructor(Class<?>… parameterTypes)
    获取指定的公开的构造方法,通过Class[]指定参数列表
    (b) public ConstructorgetDeclaredConstructor(Class<?>…parameterTypes)
    获取指定的声明的构造方法 ,可以是公开的,保护的,缺省的,私有的

3.2.1 构造器的作用是什么? 是用来创建对象的!

因为构造器是用来创建对象的,所以构造器上面有一个newInstance()的方法
newInstance() 是创建实例的意思,也就是创建对象!

Class上面也有newInstance()的方法,该方法没有任何参数, 实际上就是调用的类中无参数的构造方法

3.3 通过Class获取构造方法Method

因为:方法是用来被调用的; 所以Method上面有一个invoke()方法

四.完整代码演示

package com.oracle.refelect.test2;

import java.io.Serializable;

public class Student extends Person implements Serializable {

	/**
	 * 学生类
	 */
	private static final long serialVersionUID = 8855872842402324442L;
	private String className;
	private int score;

	public String schoolName; // 学校名称

	private static String teacherName;

	public static String getTeacherName() {
		return teacherName;
	}

	public static void setTeacherName(String teacherName) {
		Student.teacherName = teacherName;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	public Student() {
		super();
	}

	public Student(String className, int score) {
		super();
		this.className = className;
		this.score = score;
	}

}

package com.Oracle.demo.s91.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;

public class GetClassBaseInfo {
	
	/**
	 * 获取类的基本相关信息 类的基本信息包括: 包名, 修饰符,类名,基类,接口
	 */
	public static void getPackageName() {
		
		try {
			Class clazz = Class.forName("java.lang.String");
			//Class clazz = String.class;
			
			String packageName = clazz.getPackage().getName();// 获取包名信息
			String className = clazz.getName();// 获取类名信息
			int a = clazz.getModifiers();
			String mod = Modifier.toString(a);// 获取修饰符信息
			String superName = clazz.getSuperclass().getName();// 获取基类的信息
					
			Class[] interfaces = clazz.getInterfaces();// 获取实现的接口的信息
			for (Class class1 : interfaces) {
				String str = class1.getName();
				System.out.println(str);
				
			}
			System.out.println("=====");
			System.out.println(packageName);
			System.out.println(className);
			System.out.println( mod );
			System.out.println(superName);
			System.out.println();
			
			String sre2 = null;
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取类的属性的相关信息 类属性的信息包括: 修饰符, 类型,属性名称,缺省值 Java中把属性封装成Field
	 */
	public static void getFieldsName() {
		
		Class claz = Teacher.class;
		// 获取多个属性的不同方式
		// 1. public Field[] getFields(): 则此方法返回该类及其所有超类的公共字段
		// 2. public Field[] getDeclaredFields() :
		// 获取本类中声明的所有的字段(公开的,包含的,缺省的,私有的),但是没有继承的
		Field[] fields = claz.getDeclaredFields();
		for (Field field : fields) {
			String mod = Modifier.toString(field.getModifiers());// 修饰符
			System.out.print(mod+" ");			
			System.out.print(field.getType().getName() + " ");// 属性类型
			System.out.print(field.getName() + " ");// 属性名称
			System.out.println();
		}
    }	
		
	/**
	 * 获取类中的构造方法的相关信息
	 */
	public static void getConstructorInfo() {
				
		Class claz1= Teacher.class;
		
		// 获取类中所有的声明的构造方法,包括公开的,保护的,缺省的,私有的
		Constructor[] de = claz1.getDeclaredConstructors();
		for (Constructor con : de) {
			//System.out.println(con);
			
			System.out.println(Modifier.toString(con.getModifiers()) + " "); // 修饰符
			System.out.print(con.getName() + " (");//方法名
			Class[] parameterTypes = con.getParameterTypes();//参数类型
			for (Class class1 : parameterTypes) {
				System.out.print(class1.getName() + ",");	//参数名		
			}
			System.out.print(")");
			
			Class[] exceptionClasses = con.getExceptionTypes();//异常类型
			if(exceptionClasses.length > 0) {
				System.out.print(" throws ");
				for (Class exception : exceptionClasses) {
					System.out.print(exception.getName() + ",");
				}
			}
			System.out.println();
		}		
		
		
		// 获取单个的指定参数的构造方法
		/*Class[] paramClasses1 = { String.class, Integer.TYPE }; // 指定参数列表
		try {
			Constructor con = claz.getDeclaredConstructor(paramClasses1);

			System.out.print(Modifier.toString(con.getModifiers()) + " "); // 修饰符
			System.out.print(con.getName() + " (");
			Class[] paramClasses = con.getParameterTypes();
			for (Class paramclass : paramClasses) {
				System.out.print(paramclass.getName() + ",");
			}
			System.out.print(")");

			Class[] exceptionClasses = con.getExceptionTypes();
			if (exceptionClasses.length > 0) {
				System.out.print(" throws ");
				for (Class exceptionClass : exceptionClasses) {
					System.out.print(exceptionClass.getName() + ",");
				}
			}
		} catch (NoSuchMethodException | SecurityException e) {

			e.printStackTrace();
		}*/
}		
			
	// 获取类中的指定的构造器,然后通过构造器来创建对象
	public static Object createObject() {
		Object obj = null;
		
		Class claz = Teacher.class;
		Class[] paramClasses = {String.class, String.class, int.class, int.class, String.class};
		
		try {
			// 获取类中的指定的构造器
			Constructor con = claz.getDeclaredConstructor(String.class, String.class, int.class, int.class, String.class);
			
			// 设置私有的成员可以访问
			con.setAccessible(true);
			
			// 因为构造器是用来创建对象的,所以构造器上有newInstance()的方法
			Object[] params = {"时间福建省", "大专", 200000, 30, "女"};
			obj = con.newInstance(params);
			System.out.println(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 obj;
		
	}
		
	// 通过反射机制,调用指定的方法
	public static void invokeMethod() {
		Class claz = Teacher.class;
		Object obj = createObject();// 调用createObject()方法,创建Student的对象(被当做Object类型)
		
		// 获取指定参数的方法
		// getDeclaredMethod()方法的第一个参数是方法名称,第二个参数是参数列表的数组
		// 因为toString1()方法没有参数列表,所以第二个参数不用
		// Method method = claz.getDeclaredMethod("toString1");

		try {
			//Method method = claz.getDeclaredMethod("toString1" );
			Method method = claz.getDeclaredMethod("toString2", String.class, String.class );
			
			method.setAccessible(true);
			
			// toString1()方法是一个Student类中的实例方法,必须要有一个对象才能调用实例方法toString1()
			// invoke()方法的第一个参数是一个Object,就是上面说的所需要的对象
			// 我们调用方法的时候,方法会有实参列表,所以第二个参数是一个Object[],表示调用该方法的时候所需要的实参数组
			// toString1()方法没有实参,所以invoke()方法的第二个参数不需要
			// 我们调用方法的时候,方法会有返回值;而调用method.invoke()的返回值,就是调用方法之后的返回结果
			// Object res = method.invoke(obj);
			Object ins = method.invoke(obj, "已婚", "3个");
			
			System.out.println("调用方法的返回结果:"  + ins);
			
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

	// 调用类中私有的静态方法
	// 调用静态方法的时候不需要对象
	public static void invokeStaticMethod() {
		Class claz = Teacher.class;
		
		try {
			Method method = claz.getDeclaredMethod("privateStaticMethod");
			method.setAccessible(true);
			
			// 调用静态方法的时候不需要对象 ,所以可以传递一个null
			method.invoke(null);						
			
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
	
	// 通过反射机制动态的创建对象,并且动态改变对象上的属性
	public static void changeFieldValue() {
		// 创建对象的时候,对象的属性值如下: "张三", 23, "计算机1班", 98, "清华大学"
		Object obj = createObject(); // 动态的创建Student的对象,被当做Object类型
		
		// 通过对象来获取Class
		Class claz = obj.getClass();
		
		
		try {
			// 通过Class获取类中指定名称的Field
			// getDeclaredField()方法的参数是一个字符串,表示属性名称
			Field field = claz.getDeclaredField("sex");
			
			field.setAccessible(true);// 设置私有的属性可访问
			
			// 取得该属性的值
			// Student类中的schoolName 是一个实例属性,必须要有对象之后,该属性才会有效
			// field.get()方法的参数是一个Object,也就是上面说的对象
			// field.get(obj)方法返回的就是该属性的值
			Object ject = field.get(obj);
			System.out.println(ject);
			
			field.set(obj, "男"); // 改变对象上面的schoolName属性的值
			System.out.println(ject);
			
			
			
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	
	public static void main(String[] args) {
		
		//getPackageName() ;
		//getFieldsName();
		//getConstructorInfo();
		//createObject();
		//invokeMethod();
		//invokeStaticMethod();
		//changeFieldValue();
	}

}

五.反射应用场景

反射在开发中的作用:要创建那个类的对象,调用该对象的那个方法,这些信息被写到一个文件中,程序运行的时候,读取这些信息,然后根据这些信息,动态的创建对象,动态的调用方法(后面的框架都是这么做的)

ClassName=com.oracle.refelect.test4.Student
ConstructorParames=java.lang.String;int;java.lang.String;int;java.lang.String
MethodName=toString1

以上为配置文件信息




package com.Oracle.demo.s91.Reflect;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/*
要创建那个类的对象,调用该对象的那个方法,这些信息被写到一个文件中,程序运行的时候,读取这些信息,
然后根据这些信息,动态的创建对象,动态的调用方法(后面的框架都是这么做的)
*/

public class ReflectApply {
	
	private static String className;// 类名
	private static String constructorParams;// 构造器的参数列表
	private static String methodName;// 要调用的方法名称
   //方法参数列表
	
	static {
		
		// 使用流连接配置文件
		// RefelectTest.class.getResourceAsStream("/obj.txt")这种方式是专门读取src下面的文件的
		// RefelectTest.class.getClassLoader().getResourceAsStream("obj.txt")也可以读取src下面指定名称的文件
		// RefelectTest.class.getResourceAsStream("obj.txt"); 会去类所在的包中读取文件
	
		try(InputStream is = ReflectApply.class.getResourceAsStream("obj.properties");){
			// 为了读取obj.properties这种特殊格式的文件,创建一个Properties类的对象
			Properties pro = new Properties();
			pro.load(is);// 让Properties类的对象和流连接起来,这样就可以通过Properties类的对象读取文件的内容
			
			className = pro.getProperty("ClassName");
			constructorParams = pro.getProperty("CondtructorParams");
			methodName = pro.getProperty("MethodName");			
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//从参数字符串中分析出指定方法所需要的参数的Class[],因为在获取构造方法,获取方法的时候必须要通过Class[] 来指明参数列表
	private static Class[] getMethodParam(String str) {
		//java.lang.String,int,java.lang.String,int;java.lang.String
		
		String[] strArr = str.split(",");
		Class[] paramClasses = new Class[strArr.length];
		
			try {
				for(int i=0;i<paramClasses.length;i++) {
				    Class claz = getClassByStr(strArr[i]);
					paramClasses[i] = claz;
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}			
		return paramClasses;
	}

	//根据参数的名称,获取参数的Class
	private static Class getClassByStr(String param) throws ClassNotFoundException {
		Class claz = null;
		if(param.indexOf(".") != -1) {// 如果param中有. 则表示这是一个引用类型
			claz = Class.forName(param);
		}
		else { //参数是基础类型
		if(param.equals("byte")) {
			claz = byte.class;
		}
		else if(param.equals("short")) {
			claz = short.class;
		}
		else if(param.equals("int")) {
			claz = int.class;
		}
		else if(param.equals("long")) {
			claz = long.class;
		}
		else if(param.equals("char")) {
			claz = char.class;
		}
		else if(param.equals("float")) {
			claz = float.class;
		}
		else if(param.equals("double")) {
			claz = double.class;
		}
		else if(param.equals("boolean")) {
			claz = boolean.class;
		}
		else {
			throw new ClassNotFoundException("指定的类型:" + param + "不存在");
		}
	}
		return claz;
	}
	
	// 动态的创建对象,动态的调用方法
	public static void invokeMethod() {
		
		try {
			// 获取指定类名的Class对象
			Class claz = Class.forName(className);
			
			// 获取指定参数列表的构造方法
			// 首先要获取构造方法的参数列表
			Class[] paramClasses = getMethodParam(constructorParams);
			Constructor con = claz.getDeclaredConstructor(paramClasses);
			con.setAccessible(true);
			
			// 通过指定的构造器创建对象
			Object obj = con.newInstance("健身房看时间", "康师傅", 200000, 30, "3个");
			
			// 获取指定名称,指定参数列表的方法
			Method method = claz.getDeclaredMethod(methodName);
			//Method method = claz.getDeclaredMethod(methodName, parameterTypes);
			method.setAccessible(true);
			
			// 调用方法的时候,需要传递实参; 调用方法之后,方法会有返回类型
			// method.invoke(obj, "HelloWorld", 23) 就是我们自己调用方法:
			// stu.toString1("Hello",23);
			// 调用toString1()方法,会有返回结果,所以method.invoke()返回的结果就是调用方法之后的返回结果
			Object ins = method.invoke(obj);
			System.out.println(ins);
			
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} 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();
		}
	}
	
	public static void main(String[] args) {
		invokeMethod();
	}

}

以上均为个人的学习见解,如有错误,敬请指正!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值