Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
1.什么是反射
Java反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
一般而言,开发者们常常谈论到的动态语言,大概认同的一个定义是:“程序运行时,允许改变程序结构或者变量类型,这种语言成为动态语言”。从这个观点来看,Pert、Python、Rudy是动态语言,C++、Java、C#不是动态语言。
在这样的定义下和分类下,Java不是动态语言,它却有一个非常突出的动态相关机制:Reflection。这个词的意思是“反射、影像、倒影”,在Java里指的是我们可以运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才知道名称的“class”,并获得它的完成构造(但不包括method定义),并生成相应的对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
2.反射有什么用
(图片来自知乎)
JVM:Java的虚拟机。java之所以能跨平台使用就是因为JVM,这个东西可以理解成一个一个进程、程序,只不过它的作用是用来跑Java代码的。上图是java的内存模型,关注点:方法区;堆;栈。
为了理解JVM,我们写一个java程序:
Object o = new Object();
运行!
首先,JVM启动,后缀“.java”的文件会被编译成一个“.class”的文件;
然后被类加载器加载进JVM的内存中,类Object加载的方法区,创建了Object类的class对象到堆中(并不是new出来的对象,而是类的类型对象,每一个类只有一个class对象,作为方法区类的数据结构的接口)。
JVM创建对象前,会先检查类是否被加载,寻找类对应的class对象,如果已经加载好,则会为对象分配内存、初始化,也就是:new Object()
。
当程序逐行运行到末尾,程序结束,JVM关闭,程序的运行也就完成了。
上述的程序对象是我们自己new出来的,程序相当于写死了放进JVM去运行。假如这时,我们突然需要某个请求需要用到某个类,但是这个类没有加载进JVM,是不是需要,终止程序,新写一段代码,new一下,再启动服务器?特别小的程序可能这是最快的方法,但当程序非常庞大,而有急需效率解决的时候,反射就给我们提供了一个非常便捷的处理方式,使得我们可以在程序运行过程中,通过更改一些设置,就可以轻松实现类的加载。
比如,我们的项目的Dao层,有时候使用的MySQL,有时候使用crale,需要动态地根据实际情况加载驱动类,这个时候就需要使用Java的反射了。
假设有两个类:
com.java.dbtest.mysqlConnection;
com.java.dbtest.oracleConnection;
我们可能要用到,这时候,我们就可以把程序写的比较动态化,通过
Class tc = Class.forName(“com.java.dbtest.mysqlConnection”);
通过全类名让JVM在服务器中找到并加载这个这个类,如果我们需要使用oracle,更换一下全类名即可。
3.怎么使用反射
反射的实现过程其实就是反编译:即.class→.java
通过反射机制,访问java对象的属性、方法、构造方法等。
以下是通过手动输入一个类的全路径,查询该类的所有方法的实例,文件未找到时,返回异常:未找到类文件(ClassNotFoundException)。
public class Test{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
System.out.print("请输入一个java全类名:");
String cName = input.next();
showField(cName);
}
public static void main(String name){
try{
Class c = Class.forName(name);
Method m[] = c.getDeclaredMethods();
System.out.print("该java类的方法有:");
for(int i = 0;i < m.length;i++){
System.out.print(m[i].toString());
}
} catch (ClassNotFoundException e){
System.out.print("类文件未找到!" + e);
}
}
}
4.Class类
java.lang.Class类。
Class类的实例表示正在运行的Java应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。基本的Java类型(boolean、byte、char、short、int、long、float和double)和关键字void也表示为Class对象。
Class类是Java反射机制的入口,封装了一个类或者接口的运行时信息,通过调用Class类的方法可以获取这些信息。
定义: public final class Class implements Serializable
说明: final修饰,不可以被子类继承
构造器: private Class()
使用方法:
方法 | 案例 |
---|---|
使用静态forName()方法 | Class c = Class.forName(“java.lang.Object”); |
使用类名.class | Class c = Car.class; |
包装类.TYPE | Class c = Integer.TYPE; |
使用对象中的getClass()方法 | String name = “user”; Class c = name.getClass(); |
Class类.getSuperClass() | Class c = String.TYPE.getSuperClass(); |
常用方法:
方法 | 方法摘要 |
---|---|
Field[] getFields() | 返回一个包含Field 对象数组,存放该类或接口的所有可访问公共属性(含继承的公共属性)。 |
Field getField(String name) | 返回一个指定公共属性名的Field 对象(含继承的公共属性)。 |
Field[] getDeclaredFields() | 返回一个包含Field 对象数组,存放该类或接口的所有属性(不含继承的属性)。 |
Field getDeclaredField(String name) | 返回一个指定属性名的Field 对象(不含继承的属性) 。 |
Method[] getMethods() | 返回一个包含Method对象数组,存放该类或接口的所有可访问公共方法(含继承的公共方法)。 |
Method getMethod(String name,Class[] args) | 返回一个公共的指定名称和参数的Method对象(含继承的公共方法)。 |
Method[] getDeclaredMethods() | 返回一个包含Method对象数组,存放该类或接口的所有方法(不含继承的方法)。 |
Method[] getDeclaredMethod(String name,Class[] args) | 返回一个指定名称和参数的Method对象(不含继承的方法)。 |
Constructor[] getConstrutors() | 返回一个包含Constructor 对象数组,存放该类的所有公共构造方法。 |
Constructor getConstrutor(Class[] args) | 返回一个指定参数列表的公共Constructor对象。 |
Constructor[] getDeclaredConstructors() | 返回一个包含Constructor 对象数组,存放该类的所有构造方法。 |
Constructor getDeclaredConstrutor(Class[] args) | 返回一个指定参数列表的Constructor对象。 |
T newInstance() | 使用无参构造方法创建该类的一个新实例。 |
String getName() | 以 String 的形式返回该类的完整名字。 |