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

Java 之反射

2016-05-14 23:12 197 查看
经典原理:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html

什么叫反射?

光在两种物质分界面上改变传播方向又返回原来物质中的现象,叫做光的反射。这里的反射指的是我们物理学中的反射定义,而在我们java 编程反射 是反射类的类型的一种技术,大部分时间我们都是看到和操作的都是类型,那么反射是对类型的操作.

概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

特点:1是Java被视为动态(或准动态)语言的一个关键性质

Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。

换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构

JAVA反射机制提供了什么功能

Java反射机制提供如下功能:

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判段任意一个类所具有的成员变量和方法

在运行时调用任一个对象的方法

在运行时创建新类对象

在使用Java的反射功能时,基本首先都要获取类的Class对象,再通Class对象获取其他的对象。

定义

反射就像一面镜子,可以看到类的内部所有信息,反射被称为类类型,Class 类型是有jvm进行创建的,每一个类在jvm中都有一份class 信息,包含着这个类的所有信息。在运行时期可以进行动态的获取;

获取反射Class的三种方式:

1:直接由类获取 比如:Class clazz=Person.class;

2:由对象获取:Person p=new Person();

Class clazz=p.class;

3:由类的全类名获取

Class clazz=Class.ForName(“com.fly.Person”);

Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能。

类加载器

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

(1)Bootstrap ClassLoader : 将存放于\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar 名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用

(2)Extension ClassLoader : 将\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库加载。开发者可以直接使用扩展类加载器。

(3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。

(4)Custom ClassLoader

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

ClassLoader classLoader=ClassLoader.getSystemClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@73d16e93
ClassLoader parent = classLoader.getParent();
System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@15db9742
ClassLoader parent2 = parent.getParent();
System.out.println(parent2);//null


工作过程:如果一个类加载器接收到了类加载的请求,它首先把这个请
bf47
求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

1.类的加载过程

JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示:



1) 装载:查找并加载类的二进制数据;

2)链接:

验证:确保被加载类的正确性;

准备:为类的静态变量分配内存,并将其初始化为默认值;

解析:把类中的符号引用转换为直接引用;

3)初始化:为类的静态变量赋予正确的初始值;





演示类加载器的树状组织结构

ClassLoader loader =ClassLoaderTree.class.getClassLoader();
while (loader != null) {
System.out.println(loader.toString());
loader = loader.getParent();
}


第一个输出的是 ClassLoaderTree类的类加载器,即系统类加载器。它是 sun.misc.LauncherAppClassLoader类的实例;第二个输出的是扩展类加载器,是sun.misc.LauncherExtClassLoader类的实例。需要注意的是这里并没有输出引导类加载器,这是由于有些 JDK 的实现对于父类加载器是引导类加载器的情况,getParent()方法返回 null。

类加载器的代理模式

>

类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器ClassLoaderA和 ClassLoaderB分别读取了这个 Sample.class文件,并定义出两个 java.lang.Class类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常ClassCastException。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: