Java之反射

Apr 26, 2021


反射概念

❶ Java程序可以在运行时加载、探知、使用编译期间完全位置的classes。也就是说Java程序可以加载一个在运行时才得知名称的class,获悉其完整构造(constructors、fields、methods),并生成其对象实体、或对其fields设值、或调用其methods。这种编程机制称为Java反射机制。

反射用途

❶ 反射通常由需要检查或修改Java虚拟机中运行的应用程序的运行时行为的程序使用。 这是一个相对高级的功能,只应由对语言基础有很深了解的开发人员使用。 考虑到这一警告,反射是一种强大的技术,可以使应用程序执行原本不可能的操作。

❷ 可以通过使用类全名创建类实例来使用外部用户定义的类

❸ 开发类浏览器和智能IDE

❹ 在测试工具中用于检测类的内部结构

❺ 在框架开发中用于实现配置信息的处理

❻ 实现Java的动态代理


反射的缺点

反射是强大的,但不应该不分青红皂白地使用。如果无需使用反射即可执行操作,则最好避免使用反射。在通过反射访问代码时,应牢记以下注意事项。

❶ 性能开销

由于反射涉及动态解决的类型,因此无法执行某些 Java 虚拟机优化。因此,反光操作的性能比非反光操作慢,在对性能敏感的应用程序中经常称为代码的部分中应避免。

❷ 安全限制

反射需要在安全管理器下运行时可能不存在的运行时权限。这是代码的一个重要考虑因素,代码必须在受限的安全环境中运行,例如在 Applet 中运行。

❸ 内部暴露

由于反射允许代码执行非反光代码中非法的操作,例如访问字段和方法,因此使用反射可能导致意外的副作用,从而可能使代码功能失调,并可能破坏便携性。反射码(Reflective code)会破坏抽象,因此可能会随着平台的升级而改变行为。

附官方资料


反射实践

在实战之前先补充一些知识点。如下:

java.lang.Class类是所有Reflection API的切入点,是所有反射操作的入口

在Java程序运行的过程中,对程序中的每种类型的对象,Java虚拟机都会实例化一个不可变的java.lang.Class实例,每个对象都是引用或者原始类型

简单来讲,就每个类型的对象都会有对应的Class实例。

获取Class类实例的三种方法,如下:

☞ 对象名.getClass()

☞ 类型名.class

☞ Class.forName()

接下来开始实战~~ 三种方式实现方式

上面是获取对象的Class实例的各种样例,接下来讲其中一种例子,因为反射的原理都是一样的。

import static java.lang.System.out;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;

public class Main {
	private static String s;
	public static void Look()
	{
		try {
			@SuppressWarnings("rawtypes")
			Class c =Class.forName(s);
			
			out.println("***************输出公共的属性Field****************");
			Field[] fs=c.getFields();
			for(Field f:fs)
			{
				System.out.println(f.toGenericString());
			}
			
			out.format("%n%n");
			out.println("***************所有公共的Constructor****************");
			@SuppressWarnings("rawtypes")
			Constructor[] cons=c.getConstructors();
			for(@SuppressWarnings("rawtypes") Constructor con:cons)
			{
				out.println(con.toGenericString());
			}
			out.format("%n%n");
			out.println("***************所有本类自己定义的方法Method****************");
			Method[] ms=c.getDeclaredMethods();
			for(Method m:ms)
			{
				System.out.println(m.toGenericString());
			}
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stubs
		@SuppressWarnings("resource")
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入类名");
		s=scanner.next();
		Look();
	}
}

具体更多的方法(例如获取这个类的所有公共方法,甚至是父类里面的东西等等)可以去其他网站查阅,这里就不细讲了。

或许都会有一个疑惑,就是我们通过获得对象的Class实例,然后知道了里面有哪些属性、方法等,但是我们怎么去用它呢?接来下我们通过代码来发掘问题。

Student 类

public class Student {
	 

	 private String name; // Field(别名:属性,成员变量,数据成员,字段)

	 public Student(String name) { // Constructor(别名:构造方法,构造函数)
	    super();
	    this.name = name;

	 }

	 public Student() {
	    super();
	 }

	 public String getName() {// Method(别名:方法,成员方法,成员函数)
	    return name;
	 }

	 public void setName(String name) {
	    this.name = name;
	 }

	 public void setName() {
	     this.name = "zhangsan";
	 }

	 public String tString() {
	     return "学生的姓名是:" + name;
	 }
	 
	 public String toString() {
	     return "学生的姓名是:" + name;
	 }
	 
	 
}

Main 类

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

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			
			/*创建Class实例*/
			Class c = Class.forName("inflect2.Student");

                        /*获取构造函数的方法,可以有参数,可以无参。具体可以通过IDE看看底层实现。*/
			Constructor cons = c.getDeclaredConstructor();

                        /*这里就是通过调用 newInstance() 的方法来进行创建对象*/
			Object o =cons.newInstance();
			
                        /*获取Class实例里面的一个方法,方法名叫 tString*/
			Method p=c.getDeclaredMethod("tString");
			
			/*用反射技术给属性赋值,这个是直接给属性赋值
                        获取属性*/
			Field f=c.getDeclaredField("name");

                        /*setAccessible 方法下面会讲到*/
			f.setAccessible(true);

                        /*对 o 这个对象里面的属性name 赋值为"一开始是小明"*/
			f.set(o, "一开始是小明");

                        /*执行方法时用invoke方法,这里主要是验证是否成功赋值*/
			System.out.println(p.invoke(o));
			
			/*用反射技术调用方法给属性赋值
                        获取方法名为 setName,并且这个方法有一个参数,是String类型的*/
			Method m=c.getDeclaredMethod("setName",String.class);
			m.invoke(o, "后面变小红");
			System.out.print(p.invoke(o));
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			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();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

还有很多很多的执行方法,其他看其他网址吧