前言: 反射机制是java动态特性的一种,在框架中起着强大的作用(反射是框架设计的灵魂),它可以反射方法,注解,参数,接口等.学会部分原理以及应用我认为对学习框架会起着很大的作用.
1.反射到底是什么玩意儿?
正射:
既然有反射,那正射也会有,相比于反射,我觉得正射对于初学者而言可能会使用得更多,因为它其实就是指类的实例化.
一般情况下,使用某个类时必定会知道它是个怎么样的类,其作用是什么.然后就可以对这个类实例化,对其生存的对象进行一系列操作.
Test test=new Test();//已经知道类的作用和名字等,然后初始化[正射]
反射:
如上例所示,一个类由许多部分组成,比如成员变量,方法,构造方法等信息,而反射的作用就是对一个类进行剖析,把各个
部分去映射
为另一个对象.
正射需要我们去知道这个类由什么组成,反射则只要我们知道这个类的名字就可以知道这个类是什么,有什么作用.
反射的真正描述:
上面两个描述只是对反射进行一个抽象化的映像加深,接下来请查看一下反射的概述
Java反射机制是在运行状态中,对于任何类,都可以知道这个类是什么,怎么用;对于任何对象,都可以调用其方法和属性;反射就是这种动态获取信息和调用对象方法的机制.
要想剖析一个类,就得获取到这个类的字节码文件对象,而剖析使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型对象.
下面用图类进行描述
在反射中起着重要作用的class类是什么?
实际上,从某些方面看,java有两种对象,即实例化的对象和Class对象.其中,每个类的运行时的类型信息就是Class对象去表示的.它包含了与类有关的信息,实际上实例化对象就是通过Class对象去创建的.
Java就是使用Class对象执行RTTI(运行时类型识别,Run-Time Type Identification),多态就是基于RTTI实现的.
每一个类都有一个Class对象,没编译一个新类就会产生一个Class对象,基本类型有Class对象,数组也有Class对象,如果说类是对象抽象和集合的话,Class对象就是对类的抽象和集合.
Class类它没有公共的构造方法,Class对象其实就是类加载时由JVM及通过调用类加载器中的defineClass方法自动构造的,因此无法显示的去声明Class对象,一个类被加载到内存并供其使用需要经历三个阶段:
- 加载, 由类加载器(ClassLoader)执行,通过一个类全限定名获取其定义的二进制字节流(Class字节码),将这个字节流所代表的的静态存储结构转化为方法的运行时数据接口,根据字节码在java堆中生成一个代表这个类的Class对象
- 链接, 在链接阶段将验证Class文件中的字节流包含的信息是否符合当前虚拟机要求,静态域分配存储控件并设置类变量的初始值(默认零值),如果必须,可以将常量池中的符号引用转化为直接引用.
- 初始化, 在此阶段,才开始执行类中定义的java代码,用于执行该类的静态初始器和静态初始块,如果该类有父类,则优先对其父类进行初始化.
所有类都是在对其第一次使用时,动态加载到JVM中的(懒加载).
当程序创建第一个对类的静态成员引用时,就会加载这个类.使用new创建类对象时也会被当做对类的静态成员引用.因此java程序在其开始运行前并非被完全加载,其各个类都是在必须的时候才会加载.
在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载,如果尚未加载,默认类加载器就会根据类全限定名找到.class文件,在这个类的字节码被加载时,它们会接收验证,以确保其没有被破坏或包含不良java代码.一旦某个类的class对象被载入内存,就可以用它创建这个类的所有对象.
反射有啥用?
反射最重要的用途就是开发各种通用框架,比如spring的IOC容器,无论是XML配置Bean还是注解配置,当从容器中获取Bean进行依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring会根据这些信息,需要创建哪些bean,spring就动态创建.
反射其实可以赋予jvm动态编译的能力
java中编译类型两种,动态编译和静态编译,就像设计模式中的代理模式一样,有静态代理和动态代理两种模式
- 静态编译: 一次性编译,在编译的时候把所有模块都编译进去
- 动态编译: 按需求编译,程序在运行的时候,需要用到哪个模块就去编译哪个模块.
既然反射赋予了jvm动态编译的能力,那jvm动态编译/反射的使用场景有哪些?有什么用途?
- 日常的第三方应用开发中,常会遇到某个类的某个成员变量,方法或属性私有化,这时候可以利用java反射机制通过反射获取所需的私有成员或方法
- 当使用IDE时,当输入一个对象或类并想调用它的属性或方法时,一按到点号时编译器自动列出属性或方法,这里就用到了反射.
- 最重要的即使开发框架,这就是反射是框架设计的灵魂的原因.
为什么用反射?它有什么好处?
java反射机制就是增加程序的灵活性,解耦.反射就是一种机制,可以在仅仅知道类全限定名的情况下,了解到整个类内部的结构,并且访问内部的成员和方法等.
举例: 对于大型软件,一个大企业有许多小组和分工,实现不同模块,各小组之间如何协作就很重要.例如组a完成一个接口实现,组b需要使用该接口实现,这时候使用反射机制,组b完全不用知道接口如何实现,只需要知道实现后的类名即可.换句话说,类名完全保存在一个xml或属性中,由组a填充,这样就可以很好的解耦.
优点:
- 增加程序灵活性,避免将固有逻辑写死到代码中
- 代码简介,可读性强,提供了代码的复用率
缺点:
- 相较于直接调用,如果量很多,反射性能会下降
- 存在一些内部暴露和安全隐患
反射为什么会慢?
- 寻址Class字节码文件过程
- 安全管理机制的权限验证等问题
- 若需要调用native方法调用时JNI接口的使用
反射原理
java反射原理
- 将java文件保存到硬盘中
- 对java文件进行编译,生成.class文件
- JVM将字节码文件加载到内存中
- 字节码文件在内存中使用Class类去表示
- 使用反射的时候,首先获取Class类,就可以得到class文件里面所有内容,包括属性,构造方法,普通方法
- 属性通过Field类表示
- 构造方法通过Constructor表示
- 普通方法通过Method表示
反射机制的相关类
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的java程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量即类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
得到Class类的三种方法
假设有类Person.java
public class Person{
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Person(String name, String age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
}
获取Class对象的三种方法
Class class1=Person.class;
Class class2=Class.forName("Person.java路径");//常用
Class class3=new Person().getClass();
使用反射操作属性(注意!!! 当方法或属性用静态修饰,某些方法用到对象时,用null表示,比如:m.invoke(null,“test”));
private static void field(){
//使用反射操作属性(变量)
Class class1=Class.forName("reflect.Person");
//得到所有的属性
//class1.getDeclaredFields();
//得到对象
Person p=(Person)class1.newInstance();
//得到指定属性
Field f=class1.getDeclaredField("name");
//设置方法允许操作私有属性
f.setAccessible(true);
//赋值
f.set(p,"test");
// 获取值(Person中设置属性为私有,需要设置一个方法setAccessible(true))
System.out.println(f.get(p));
}
使用反射操作构造器
private static void constructor(){
Class class2=Class.forName("reflect.Person");
//得到所有的构造方法
//class2.getConstructor();
//得到指定的构造方法
Constructor c=class2.getConstructor(String.class,String.class);
//得到对象
Person p=(Person)c.newInstance("siuhf","oihfa");
}
使用反射操作方法
private static void method(){
Class class3=Class.forName("reflect.Person");
//得到对象
Person p=(Person)class3.newInstance();
//获取所有普通方法
class3.getDeclaredMethods();
//获取指定参数普通方法
Method m=class3.getDeclaredMethod("setName",String.class);
//执行m指定的方法
m.invoke(p,"sihfsi");
}
反射使用实例
1. 反射构建对象
创建类Reflect
public class Reflect{
public void test(){
System.out.println("test");
}
}
通过反射构建
public Reflect getInstance(){
Reflect object=null;
try{
//使用Class类通过全限定名找到class文件创建出class对象,然后调用newInstance方法创建对象
object=(Reflect)Class.forName("cn.f33v.test.Reflect").newInstance();
}catch(Exception e){
e.printStackTrace();
}
return object;
}
2.反射方法
public Object reflectMethod(){
Object result=null;
Class class=Class.forName("cn.f33v.test.Reflect");
Reflect target=class.newInstance();
try{
//获得Method对象
Method m=class.getMethod("sayHello");
//使用args参数在obj上分派此对象表示的方法的结果
result=m.invoke(target,"test");
}catch(Exceptipn e){
e.printStackTrace();
}
return result;
}