【Java】深入理解反射机制

反射是非常重要的知识


JAVA让我们运行时识别对象和类的信息,有俩个方式:一个是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息。另一种是反射机制,它允许我们运行时发现和使用类的信息。
–编程思想

反射的描述

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

要想达到这样的效果,必须先要获取到该类的字节码文件对象,而达到这样效果使用的是Class类中的方法,所以先要获取到每个字节码文对应的Class类型对象。

每个类都会产生一个对应的Class对象,且每个类只有一个,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM

JAVA中反射的实现过程:
JAVA语言在编译之后会产生成一个.class文件,反射就是通过字节码文件找到某一个类,类中的方法以及属性等。
下图2中,就是找到Class文件并创建Class对象(摘自网上)
在这里插入图片描述
下面是jdk到解释,说明反射就是把类中的各个成分反映为一个个对象

Class类的类表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注释是一种界面。 每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。 原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象。

类没有公共构造函数。 相反, 类对象由Java虚拟机自动构建,因为加载了类,并且通过调用类加载器中的defineClass方法。

我们不需要自己创建Class类,JVM自动帮我们创建好了。

反射的作用

JAVA反射的作用是什么:
反射机制指的是程序在运行时能够获取自身的信息。在JAVA中,只要给定类的名字,那么就可以通过反射机制来获取类的所有信息。

获取Class对象的三种方式

//1.java中每个类型都有class 属性**
    Class cls1 = String.class;
    Class cls2 = int.class;
    Class cls3 = Employee.class;	(Employee是一个类)
    
//2.通过class类的静态方法 Class.forName(ClassName)
    Class cls1 = Class.forName("Employee");
    Class cls2 = Class.forName("java.lang.String");
    
//3.通过类的getClass()方法(Object的方法,因为所以方法都继承Object)
    Employee employee = new Employee();
    Class cls = employee.getClass();

反射的实现主要借助以下四个类:class:类的对象,Constructor:类的构造方法,field:类中的属性对象,Method类中的方法对象。

通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问:

通过反射获取构造方法并使用

package fanshe;

public class Student {
	//默认的构造方法
	Student(String str){
		System.out.println("默认的构造方法 s= "+str);
	}
	
	//无参数构造方法
	public Student() {
		System.out.println("调用了公有,无参数构造方法执行了。。。");
	}
	
	//有一个参数的构造方法
	public Student(char name) {
		System.out.println("姓名:"+name);
	}
	
	//有多个参数的构造方法
	public Student(String name,int age) {
		System.out.println("姓名:"+name+"年龄:"+age);
	}
	
	//受保护的构造方法
	protected Student(boolean n) {
		System.out.println("受保护的构造方法 n = "+n);
	}
	
	//私有构造方法
	private Student(int age) {
		System.out.println("私有的构造方法   年龄:"+age);
	}
}
package fanshe;

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

public class Constructors {
	//加载class对象
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//加载class对象
		Class cls = Class.forName("fanshe.Student");
		//Class cls = new Student().getClass();
		//Class cls = Student.class;
		
		//获取所有公有的构造方法
		System.out.println("**********************所有公有构造方法*********************************");
		Constructor[] conArray = cls.getConstructors();
		for(Constructor c:conArray) {
			System.out.println(c);
		}
		
		System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
		conArray = cls.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("*****************获取公有、无参的构造方法*******************************");
		Constructor con = cls.getConstructor(null);
		System.out.println("con = " + con);
		//创建此对象的一个新实例对象
		Object obj = con.newInstance();
		
		System.out.println("******************获取私有构造方法,并调用*******************************");
		con = cls.getDeclaredConstructor(char.class);
		System.out.println(con);
		
		con.setAccessible(true);
		//用新实例对象的方法设置参数
		obj = con.newInstance('男');

	}
}

输出
在这里插入图片描述

获取成员变量并调用

package fanshe;
public class Employee {
	public Employee() {	
	}	
	//***********字段******************
	public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]";
	}
}
package fanshe;


import java.lang.reflect.Field;

public class Fields {
	public static void main(String[] args) throws Exception {
		//1.获取class对象
		Class cls = Class.forName("fanshe.Employee");
		
		//2.获取字段
		System.out.println("***********获取所有公有字段************************");
		Field []fieldArray = cls.getFields();
		for(Field f: fieldArray) {
			System.out.println(f);
		}
		System.out.println("***********获取所有的字段(包括私有,受保护,默认的)*******");
		fieldArray = cls.getDeclaredFields();
		for(Field f: fieldArray) {
			System.out.println(f);
		}
		
		System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
		fieldArray = cls.getDeclaredFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		System.out.println("*************获取公有字段**并调用***********************************");
		Field f = cls.getField("name");
		System.out.println(f);
		//获取一个对象
		Object obj = cls.getConstructor().newInstance();//产生Student对象--> Employee emp = new Employee();
		//为字段设置值
		f.set(obj, "刘德华");
		//验证
		Employee emp = (Employee)obj;
		System.out.println("验证姓名:" + emp.name);
		
		
		System.out.println("**************获取私有字段****并调用********************************");
		f = cls.getDeclaredField("phoneNum");
		System.out.println(f);
		f.setAccessible(true);//暴力反射,解除私有限定
		f.set(obj, "18888889999");
		System.out.println("验证电话:" + emp);

	}
}

在这里插入图片描述

获取成员方法并调用

package fanshe;

public class School {
		public void show1(String s){
			System.out.println("调用了:公有的,String参数的show1(): s = " + s);
		}
		protected void show2(){
			System.out.println("调用了:受保护的,无参的show2()");
		}
		void show3(){
			System.out.println("调用了:默认的,无参的show3()");
		}
		private String show4(int age){
			System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
			return "abcd";
		}
}

package fanshe;

import java.lang.reflect.Method;

public class MethodClass {
 
	public static void main(String[] args) throws Exception {
		//1.获取Class对象
		Class cls = Class.forName("fanshe.School");
		//2.获取所有公有方法
		System.out.println("***************获取所有的”公有“方法*******************");
		cls.getMethods();
		Method[] methodArray = cls.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************获取所有的方法,包括私有的*******************");
		methodArray = cls.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************获取公有的show1()方法*******************");
		Method m = cls.getMethod("show1", String.class);
		System.out.println(m);
		//实例化一个Student对象
		Object obj = cls.getConstructor().newInstance();
		m.invoke(obj, "刘德华");
		
		System.out.println("***************获取私有的show4()方法******************");
		m = cls.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true);//解除私有限定
		Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
		System.out.println("返回值:" + result);	
	}
}

在这里插入图片描述

利用反射运行配置文件

在src目录下建一个pro.txt配置文件,用来更新操作
在这里插入图片描述
pro.txt内容:

ClassName=fanshe.Student1
MethodName=show

package fanshe;

public class Student1 {
	public void show() {
		System.out.println("is show");
	}
}
package fanshe;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo {
	public static void main(String[] args) throws Exception {
		//通过反射获取class对象
		Class cls = Class.forName(getValue("ClassName"));
		//获取show方法
		Method m = cls.getMethod(getValue("MethodName"));
		//调用show方法
		m.invoke(cls.getConstructor().newInstance());
		
	}

	private static String getValue(String key) throws Exception {
		Properties pro = new Properties();//获取文件配置对象
		FileReader in = new FileReader("pro.txt");
		pro.load(in);	//将流加载到配置文件对象中
		in.close();
		return pro.getProperty(key);
	}
}

运行后

is show

当我们需要更改类时,创建一个新类Student2,然后只需要更改pro.txt的内容,就可以运行了。

ClassName=fanshe.Student2
MethodName=show

package fanshe;

public class Student2 {
	public void show() {
		System.out.println("is show2");
	}
}

其他不用改
输出:

is show2

以前写过的一些方法,那时理解很浅
点我

发布了66 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ACofKing/article/details/90488683