您的位置:首页 > 编程语言 > Java开发

【JAVA基础】反射机制

2017-12-24 21:17 120 查看
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制

一、什么是java反射

反射使程序代码能够接入装载到JVM中的类的内部信息,允许在编写与执行时,而不是源代码中选定的类协作的代码,是以开发效率换运行效率的一种手段。这使反射称为构建灵活应用的主要工具。

反射的作用:

1、调用一些私有方法,实现黑科技。比如双卡短信发送、设置状态栏颜色、自动挂电话等。

2、实现序列化与反序列化,比如PO的ORM,Json解析等

3、实现跨平台兼容,比如jdk中的SocketImpl的实现

4、通过xml或注解,实现依赖注入,注解处理,动态代理,单元测试等功能。比如Retrofit、Spring或者Dagger

二、class类

1、class是一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Field,描述构造类的Constructor等属性

2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。

3、对于每个类而言。jre都为其保留一个不变的class类型的对象。一个class对象包含了特定某个类的有关信息

4、class对象只能由系统建立对象

5、一个类在jvm中只会有一个class实例

[b]反射机制获取类的三种方法[/b]

1、通过类名

2、通过getClass()

3、通过全类名获取

/**
* 反射机制获取类有三种方法
*/
@Test
public void testGetClass() throws ClassNotFoundException {
Class clazz = null;

//1 直接通过类名.Class的方式得到
clazz = Person.class;
System.out.println("通过类名: " + clazz);

//2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)
Object obj = new Person();
clazz = obj.getClass();
System.out.println("通过getClass(): " + clazz);

//3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
clazz = Class.forName("com.java.reflection.Person");
System.out.println("通过全类名获取: " + clazz);
}


[b]利用newInstance创建对象:调用的类必须有无参的构造器[/b]

无参构造器:Person{name=’null’,age=0}

/**
* Class类的newInstance()方法,创建类的一个对象。
*/
@Test
public void testNewInstance()
throws ClassNotFoundException, IllegalAccessException, InstantiationException {

Class clazz = Class.forName("com.java.reflection.Person");

//使用Class类的newInstance()方法创建类的一个对象
//实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)
//一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器
Object obj = clazz.newInstance();
System.out.println(obj);
}


三、ClassLoader类加载器

类加载器是用来把类装载进jvm的。jvm规范定义了两种类型的类装载器:启动类家载器和用户自定义加载器。jvm在运行时会产生3个类加载器组成的初始化加载器层次结构,如下图所示:



类加载器详解:http://blog.csdn.net/ochangwen/article/details/51473120

/**
* ClassLoader类装载器
*/
@Test
public void testClassLoader1() throws ClassNotFoundException, IOException {
//1、获取一个系统的类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统的类加载器-->" + classLoader);

//2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))
classLoader = classLoader.getParent();
System.out.println("扩展类加载器-->" + classLoader);

//3、获取扩展类加载器的父类加载器
//输出为Null,无法被Java程序直接引用
classLoader = classLoader.getParent();
System.out.println("启动类加载器-->" + classLoader);

//

//4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器
classLoader = Class.forName("com.java.reflection.Person").getClassLoader();
System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);

//5、测试JDK提供的Object类由哪个类加载器负责加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);
}


四、反射在native的实现

反射在java中可以直接调用,不过最终调用的认识native方法,以下为主流反射操作的实现

1、Class.forName

Class.forName可以通过寻找Class对象,比如Class.forName(“java.lang.String”)。

在JDK的源码实现中,可以发现最终调用的是native方法forName0(),它在JVM中调用的实际是findClassFromClassLoader(),原理与ClassLoader的流程一样。

2、getDeclareFields的实现

在jdk源码中,可以知道class.getDeclaredFields()方法实际调用的是native方法getDeclaredFields0(),它在JVM主要实现步骤如下:

(1)根据class结构体信息,获取field_count与fields[]字段,这个字段早已在load过程中被放入了

(2)根据field_count的大小分配内存、创建数组

(3)将数组进行forEach循环,通过fields[]中的信息依次创建Object对象

(4)返回数组指针

3、Method.invoke的实现

以下为无同步、无异常的情况下调用的步骤:

(1)创建Frame

(2)如果对象flag为native,交给native_handler进行处理

(3)在frame中执行java代码

(4)弹出Frame

(5)返回执行结果的指针

4、class.newInstance的实现

(1)检测权限、预分配空间大小等参数

(2)创建Object对象,并分配空间

(3)通过Method.invoke调用构造函数(())

(4)返回Object指针
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: