JAVA反射调用的确是一种很神奇的机制,在项目中使用后恋恋不忘,现将其好好整理一下。
参考: 学习JAVA应该如何理解反射?知乎 https://www.zhihu.com/question/24304289
反射机制概述
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制。
反射机制主要提供了以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意有一个类所具有的的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理
- 反射最重要的用途就是开发各种通用框架。大大提高了程序的扩展性 。很多框架(比如Spring、Tomcat)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
举个例子:
Tomcat服务器程序,提供了处理请求和应答的方式。因为具体的处理动作不同,所以对外提供了一个接口,由开发者来实现具体的请求和应答处理。该接口还有一个配置文件可供修改。
interface Servlet //Tomcat提供的接口
class MySevlet implements Servlet{
//定义了具体的请求和应答处理方式
}
配置文件: web.xml
<servlet-name>
MySevlet
</servlet-name>
Tomcat动态的获取自定义的类,并调用其方法,这个过程就是反射调用。
从JAVA虚拟机看反射
作者:郑剑锋
链接:https://www.zhihu.com/question/24304289/answer/147529485
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
首先我们了解一下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的特性了!当然这里只是举了反射的一个应用,实际还有其他作用,只是这个例子能更好地理解!
Class类
深入研究java.lang.Class类 http://lavasoft.blog.51cto.com/62575/15433/
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
/*
该类就可以获取字节码文件中的所有内容
反射就是要依靠该类来完成的
*/
class Class{
提供获取字节码文件中的内容。
比如:
名称
字段
构造函数
一般函数
}
获得Class对象
在反射调用中,如果想要对字节码文件进行解剖,必须要有字节码文件对象。
三种方式获取Class对象:
- Object类中的getClass方法。 这也是最常见的产生Class对象的方法。想要用这种方式,必须要明确具体的类,并创建对象,麻烦。
Person p = new Person();
Class clazz = p.getClass();
- 直接获取某一个对象的class. 任何数据类型都具备一个静态的属性.class来获取其对应的class对象。相对简单,但还是要明确用到的类中的静态成员。还是不够扩展
Class clazz = Person.class;
- 使用Class类中的forName静态方法 只要通过给定的类的字符串名称就可以获取该类,更为扩展
.但是这里的className**需要类的完全限定名**,因为它是字符串,和导没导包没任何关系
Class clazz = Class.forName("com.example.hgx.Person");
//得添加异常ClassNotFoundException
反射的基本运用
反射方式创建实例
- 创建空参的实例。 使用Class对象的newInstance()方法来创建Class对象对应类的实例。
//原始:new的时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,
//并创建该字节码文件对象,并接着创建该字节文件对应的Person对象。
Person p = new Person();
//反射:找寻该类的名称,并加载进内存,并产生Class对象
//在产生类的对象
Strint className = "com.example.hgx.Person";
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
2、创建带参数的实例。 这时要通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。
class Person {
public String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
//获取Person类带一个(String,int)参数的构造器
Strint className = "com.example.hgx.Person";
Class clazz = Class.forName(className);
Constructor constructor = clazz.getConstructor(String.class,int.class);
Object obj = constructor.newInstance("小明",24);
反射方式获取字段(成员变量)
主要是这几个方法
- getField: 访问公有的成员变量
- getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量
Strint className = "com.example.hgx.Person";
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
//访问public字段
Field field = clazz.getField("name");
field.set(obj,"小明");
Object o = field.get(obj);
//访问private字段,要取消权限检查.暴力访问
Field field1 =clazz.getDeclaredField("age");
field1.setAccessible(true);
field1.set(obj,24);
Object o1 = field1.get(obj);
反射方式获取方法
获取某个Class对象的方法集合,主要有以下几个方法:
- getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
- getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
- getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
public Method getMethod(String name, Class<?>... parameterTypes)
例子:
Class clazz = Class.forName("com.example.hgx.Person");
Method[] methods = clazz.getMethods();//获得公有方法
for(Method method: methods){
System.out.println(method);
}
Method[] all_methods = clazz.getDeclaredMethods();//只获取本类中的所有方法,包含私有
//调用方法
Method method1 = clazz.getMethod("show",null);//Person中的show()方法,空参
Object obj = clazz.newInstance();
method1.invoke(obj,null);
//调用带参方法
Method method = clazz.getMethod("paramMethod",String.class,int.class);
Object obj1 = clazz.newInstance();
method.invoke(obj1,"小明",24);