反射

反射概述

img

首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。上图是java的内存模型,我们关注的点,一个方法区,一个栈,一个堆,初学的时候老师不深入的话只告诉你java的内存分为堆和栈,易懂点吧!

假如你写了一段代码:Object o=new Object();

运行了起来!

首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。

上面的流程就是你自己写好的代码扔给jvm去跑,跑完就over了,jvm关闭,你的程序也停止了。

为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用。

题主想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,哦启动一下服务器,(脑残)!

反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻,举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc = Class.forName(“com.java.dbtest.TestConnection”);通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!举多个例子,大家如果接触过spring,会发现当你配置各种各样的bean时,是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。

Java文件和.class文件的关系
  • Java文件

    Java文件中包含代码的所有内容,包括类,接口,成员变量,成员方法等

  • .class字节码文件

    .java文件通过 javac 编译工具生成对应的.class字节码文件

    如果使用jdk中提供的反编译工具,可以看到.class文件中包含

    Class完整的包名和类名

    Field成员变量,成员变量的名字和成员变量的数据类型[如果是引用数据类型,也是完整的包名,类名]

    Method成员方法,方法权限修饰,返回值类型,方法名,形式参数列表数据类型

反编译class1

反编译class2

总结:

.class字节码文件中,包含了Java文件的所有内容

代码中的每一个类都会生成对应的字节码文件

程序加载过程和.class文件的关系

在Java文件运行过程中,当程序需要哪一个类参与代码运行,就需要加载这个类的.class字节码文件,该.class字节码文件在程序的加载阶段,存在于内存的[代码区]

.class字节码文件既然加载到内存的代码区

.class文件中包含对应Java程序的所有内容

代码区存在一块空间 ==> .class ==> Java程序所有内容

Java中的万物皆对象

在Java代码中,把在内存代码区保存的.class字节码内存空间,看作是一个对象,该对象中包含了对应的Java文件的所有内容

class文本保存在内存的代码区

class

Class引出

反射必会方法-重点

Class Class.forName(String packageNameAndClassName);
	Class类的静态成员方法,通过完整的包名,类名获取对应的.class文件的Class对象,同时可以作为.class文件加载的方式

        
Class 类名.class;
	通过类名.class方法,获取对应的Class类对象,通常用于方法的参数类型
        

Class 类对象.getClass();
	通过类对象获取对应的.classClass类对象,方法参数,或者说数据类型判断

实例:

image-20210808195007620

Person.java,以下均使用此文件

package cn.ocean888_reflect;

/**
 * @author Anonymous
 */
public class Person {
	//成员变量 Field
	/**
	 * id属性
	 */
	private int id;
	private String name;
	
	public int test = 10;
	public static int testStatic = 10;
	
	//构造方法 Constructor
	public Person() {}
	
	public Person(int id) {
		this.id = id;
	}
	
	public Person(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	private Person(String name) {
		this.name = name;
	}
	
	
	//成员方法 Method
	public void setId(int id) {
		this.id = id;
	}
	
	public int getId() {
		return id;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public void game() {
		System.out.println("大吉大利,腾讯倒闭");
	}
	
	public void game(String name) {
		System.out.println("玩" + name);
	}
	
	private void testPrivate() {
		System.out.println("类内私有化成员方法!!!");
	}
	
	private void testPrivate(String str) {
		System.out.println("吃" + str);
	}
	
	public static void test() {
		System.out.println("静态!!!");
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", test=" + test + "]";
	}
}
package cn.ocean888_reflect;

public class GetClassObject {
	public static void main(String[] args) throws ClassNotFoundException {
		System.out.println("test");
		
		Class<?> forName = Class.forName("cn.ocean888_reflect.Person");
		
		Class<cn.ocean888_reflect.Person> cls = Person.class;
		
		Class<? extends Person> class1 = new Person().getClass();
		
		System.out.println(forName == cls);
		System.out.println(class1 == cls);
		System.out.println(class1 == forName);
	}
}

输出结果:

image-20210808195035247

image-20210808195346450

Constructor构造方法涉及的方法
// 1.
public Constructor[] getConstructor();
	获取当前Class类对象对应Java文件中,public修饰构造方法类对象数组
        
// 2.    
public Constructor[] getDeclaredConstructors();
	[ 暴力反射 ]
	获取当前Class类对象对应Java文件中,所有[构造方法的类对象数组],包括私有化构造方法
        
        
[回顾]
	new Person()  
	new Person(1)
	这里利用了重载的知识点,会根据实际[ 参数类型 ],来选择对应的构造方法
[推理]
	通过Class类对象,获取指定构造方法,需要根据构造方法的所需    
        
        
// 3.   
public Constructor getConstructor(Class... initArgumentTypes);
	根据指定的数据类型,来选择对应的构造方法,可能会抛出异常
	这里有且只能获取类内的指定数据类型public修饰构造方法类对象    
	Class:约束数据类型
	例如:
		需要int类型 int.class
		需要Stirng类型 String.class
		需要Person类型 Person.class
	异常:
		NoSuchMethodException

	... :不定长参数
		构造方法需要的参数类型很多,有可能无参数,有可能有参数,使用...不定长参数类约束使用,增强代码的普适性
	例如:
		无参数 () or (null)
		参数类型int (int.class)            
		参数类型int, String类型 (int.class, String.class)
	
	initArgumentTypes:
		参数名 初始化参数类型复数
            
// 4.       
public Constructor getConstructor(Class... initArgumentTypes);
	[暴力反射]
	根据指定的数据类型,来选择对应的构造方法,可能会抛出异常
	这里可以获取指定参数类型私有化构造方法和非私有化构造方法  
	Class:约束数据类型
	例如:
		需要int类型 int.class
		需要Stirng类型 String.class
		需要Person类型 Person.class
	异常:
		NoSuchMethodException

	... :不定长参数
		构造方法需要的参数类型很多,有可能无参数,有可能有参数,使用...不定长参数类约束使用,增强代码的普适性
	例如:
		无参数 () or (null)
		参数类型int (int.class)            
		参数类型int, String类型 (int.class, String.class)
	
	initArgumentTypes:
		参数名 初始化参数类型复数    
            
            
Object newInstance(Object... initArguments);
	通过Constructor对象来调用,传入当前构造方法所需创建对象的初始化参数,创建对象
	Object:Object类是Java中所有类的基类,这里可以传入任意类型的参数
	... :不定长参数,因为Constructor类对象在获取的过程中,约束的参数个数不确定,这里使用不定长参数来传入数据        

实例:

package cn.ocean888_reflect;

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

/*
 * 操作Constructor
 */
public class GetConstructorObject {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		/*
		 * 根据指定的包名.类名 获取对应Class对象
		 */
		Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
		
		/*
		 * 获取当前Person类内所有非私有化构造方法
		 */
		Constructor<?>[] constructors = cls.getConstructors();
		for (Constructor<?> constructor : constructors) {
			System.out.println(constructor);
		}
		System.out.println("=====================================");
		System.out.println();
		
		
		/*
		 * 暴力反射
		 */
		Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
		for (Constructor<?> constructor : declaredConstructors) {
			System.out.println(constructor);
		}
		System.out.println("=====================================");
		System.out.println();
		
		
		/*
		 * 根据指定参数类型获取Public修饰的构造方法对象
		 * 如果没有指定参数的构造方法,运行异常NoSuchMethodException
		 */
		Constructor<?> constructor1 = cls.getConstructor();
		Constructor<?> constructor2 = cls.getConstructor(int.class);
		Constructor<?> constructor3 = cls.getConstructor(int.class, String.class);
		System.out.println(constructor1);
		System.out.println(constructor2);
		System.out.println(constructor3);
		System.out.println("=====================================");
		System.out.println();
		
		/*
		 * 暴力反射
		 * 通过暴力反射可以获取任意权限修饰符,符合参数要求的构造方法对象
		 */
		Constructor<?> declaredConstructor1 = cls.getDeclaredConstructor();
		Constructor<?> declaredConstructor2 = cls.getDeclaredConstructor(String.class);
		System.out.println(declaredConstructor1);
		System.out.println(declaredConstructor2);
		System.out.println("=====================================");
		System.out.println();
		
		/*
		 * 通过无参数Constructor对象执行newInstance方法
		 * 这里明确是一个Person类型,可以使用强制类型转换
		 * 这里使用的是public修饰的构造方法
		 */
		Person p1 = (Person) constructor1.newInstance(); // 抛出四个异常
		System.out.println(p1);
		System.out.println(new Person());
		
		System.out.println("=====================================");
		System.out.println();
		
		// 给予通过暴力反射获取到的非公开权限成员变量,成员方法,构造方法,操作权限
		// 暴力反射的自由化为所欲为的操作
		declaredConstructor2.setAccessible(true);
		Person p2 = (Person) declaredConstructor2.newInstance("ocean");
		System.out.println(p2);
	}
}

输出结果:

public cn.ocean888_reflect.Person(int)
public cn.ocean888_reflect.Person()
public cn.ocean888_reflect.Person(int,java.lang.String)
=====================================

private cn.ocean888_reflect.Person(java.lang.String)
public cn.ocean888_reflect.Person(int)
public cn.ocean888_reflect.Person()
public cn.ocean888_reflect.Person(int,java.lang.String)
=====================================

public cn.ocean888_reflect.Person()
public cn.ocean888_reflect.Person(int)
public cn.ocean888_reflect.Person(int,java.lang.String)
=====================================

public cn.ocean888_reflect.Person()
private cn.ocean888_reflect.Person(java.lang.String)
=====================================

Person [id=0, name=null, test=10]
Person [id=0, name=null, test=10]
=====================================

Person [id=0, name=ocean, test=10]
Method成员方法涉及到的方法
问题:
调用执行成员方法时,需要考虑那些内容
	调用者
		类名,对象
	方法名
	参数

需要通过Class对象来获取Method对象,有哪些内容需要考虑
    参数
    方法名
    权限修饰符
    
Method获取方法
// 1.
Method[] getMethods();
	获取类内所有public修饰的成员方法,包括从父类继承而来的public修饰方法
        
// 2.
Method[] getDeclaredMethod();
	暴力反射
	获取类内的所有成员方法,但不包括从父类继承而来的方法

// 3.       
Method getMethod(String methodName, Class... parameterTypes);
	根据指定的方法名和对应的参数类型,获取对应的public修饰的成员方法
	methodName:
		方法名,指定获取的是哪一个方法
	parameterTypes:
		Class用于约束当前使用的参数类型
		... 不定长参数,方法参数个数,顺序,有参无参问题
	例如:
		cls是Class类对象
		cls.getMethod("setName", String.class);
		cls.getMethod("getName");


// 4.       
Method getDeclaredMethod(String methodName, Class... parameterTypes);
	根据指定的方法名和对应的参数类型,获取对应的public修饰的成员方法
	methodName:
		方法名,指定获取的是哪一个方法
	parameterTypes:
		Class用于约束当前使用的参数类型
		... 不定长参数,方法参数个数,顺序,有参无参问题
	例如:
		cls是Class类对象
		cls.getMethod("setName", String.class);
		cls.getMethod("getName");

实例:

package cn.ocean888_reflect;

import java.lang.reflect.Method;

public class getMethodObject {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
		/*
		 * 根据指定的包名.类名,获取对应的Class对象
		 */
		Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
		
		/*
		 * 获取类内所有public修饰的成员方法,包括从父类继承的方法
		 */
		Method[] methods = cls.getMethods();
		for (Method method : methods) {
			System.out.println(method);
		}
		System.out.println("====================================");
		System.out.println();
		
		/*
		 * 暴力反射
		 * 获取类内所有的成员方法,包括私有化成员方法,不包括父类继承而来的方法
		 */
		Method[] methods2 = cls.getDeclaredMethods();
		for (Method method : methods2) {
			System.out.println(method);
		}
		System.out.println("====================================");
		System.out.println();
		
		/*
		 * 根据指定的方法和参数类型,获取类内public修饰的成员方法
		 */
		Method game1 = cls.getMethod("game");
		Method game2 = cls.getMethod("game", String.class);
		System.out.println(game1);
		System.out.println(game2);
		
		
		/* 
		 * 根据指定的方法和参数类型,获取类内的private修饰的成员方法
		 */
		Method test1 = cls.getDeclaredMethod("testPrivate");
		Method test2 = cls.getDeclaredMethod("testPrivate", String.class);
		System.out.println(test1);
		System.out.println(test2);
	}
}

输出结果

image-20210809153047674

Method执行方法和权限
Object invoke(Object obj, Object... argument);
	通过method类对象调用,执行对应的方法,需要的参数
	obj:
		执行当前方法的执行者
	argument:
		Object... 不定长参数,当前方法执行所需的实际参数
package cn.ocean888_reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetMethodObject2 {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
		
		Method game1 = cls.getMethod("game");
		Method game2 = cls.getMethod("game", String.class);
		
		Method testPrivate1 = cls.getDeclaredMethod("testPrivate");
		Method testPrivate2 = cls.getDeclaredMethod("testPrivate", String.class);

		Object obj = cls.getConstructor().newInstance();
		
		
		/*
		 * 调用public方法
		 */
		game1.invoke(obj);
		game2.invoke(obj, "贪吃蛇大作战");
		
		/*
		 * 首先基于权限,再调用private方法
		 */
		testPrivate1.setAccessible(true);
		testPrivate1.invoke(obj);
		testPrivate2.setAccessible(true);
		testPrivate2.invoke(obj, "泡泡");
	}
}

image-20210809155153513

Field成员变量涉及到方法
获取方法
Field[] getFields();
	获取类内所有public修饰的成员变量
Field[] getDeclaredFields();
	获取类内所有成员变量,包括私有化成员变量
Field getField(String fieldName);
	获取指定变量名的成员变量对象,要求是public修饰的成员变量
Field getDeclaredField(String fieldName);
	获取指定变量名的成员变量对象,包括private私有化修饰的成员变量

实例:

package cn.ocean888_reflect;

import java.lang.reflect.Field;

public class GetFieldObject {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
		Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
		
		Field[] fields = cls.getFields();
		for (Field field : fields) {
			System.out.println(field);
		}
		System.out.println("=====================================");

		
		Field[] declaredFields = cls.getDeclaredFields();
		for (Field field : declaredFields) {
			System.out.println(field);
		}
		System.out.println("=====================================");

		
		Field testField = cls.getField("test");
		System.out.println(testField);
		System.out.println("=====================================");

		
		Field id = cls.getDeclaredField("id");
		System.out.println(id);
	}
}

image-20210809170532263

设置方法
void set(Object obj, Object value);
	设置指定调用者中对应成员变量的数据
	obj:调用者
	value:对应当前成员变量需要赋值的内容
        
Object get(Object obj);
	获取指定调用者中指定成员变量的数据
	obj:调用者        

实例:

		Object obj = cls.getConstructor().newInstance();
		System.out.println(obj);
		
		// 设置public属性的test的参数值
		testField.set(obj, 20);
		System.out.println(obj);
		
		// 设置private属性的id参数值
		id.setAccessible(true);
		id.set(obj, 88);
		System.out.println(id.get(obj));

image-20210809194549308

给予暴力反射私有化内容的权限操作
setAccessible(boolean flag);
给予Constructor,Method,Field对象,私有化内容,操作权限设置
true 表示可以操作

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐