一、Java反射机制概念与类加载器
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。defineClass(string name ,byte[ ] b,int off , int len) 将类文件的字节数组转化为JVM中的class对象
Class对象是没办法用new关键字得到的,因为它是jvm生成用来保存对应类的信息的,换句话说,当我们定义好一个类文件并编译成.class字节码后,编译器同时为我们创建了一个Class对象并将它保存.class文件中。在jvm内部有一个类加载机制,即在需要的时候(懒加载)将.class文件和对应的Class对象加载到内存中。
Java程序要经过两大步骤:编译,运行;
1、源文件由编译器编译成字节码(ByteCode)
2、字节码由java虚拟机解释运行。
第一步(编译): 创建完源文件之后,程序会被编译器编译为.class文件。Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用。。编译后的字节码文件格式主要分为两部分:常量池和方法字节码。
第二步(运行):java类运行的过程大概可分为两个过程:1、类的加载 2、执行。
java程序经过编译后形成*.class文件。通过类加载器将字节码(*.class)加载入JVM的内存中。JVM将类加载过程分成加载,连接,初始化三个阶段,其中连接阶段又可分为验证,准备,解析。
验证:载入class文件的正确性
准备:给类的静态变量分配存储空间
解析:将符号引用转化为直接引用
二、类加载器(ClassLoader)
1、在加载阶段,虚拟机需要完成以下三件事情:
通过一个类的全限定名来获取其定义的二进制字节流。
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口。
JVM 的类加载是通过 ClassLoader 及其子类来完成的。
JVM在运行时,会产生3个类加载器
1,Bootstrap ClassLoader:启动类加载器
负责加载$JAVA_HOME中jre/lib/里所有的 class(JDK 代表 JDK 的安装目录,下同),或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如 rt.jar,所有的java.*开头的类均被 Bootstrap ClassLoader 加载)。启动类加载器由 C++ 实现,不是 ClassLoader 子类。无法被 Java 程序直接引用的。
2,Extension ClassLoader:扩展类加载器
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3,App ClassLoader:应用程序类加载器
负责加载 classpath 中指定的 jar 包及目录中 class
verbose命令的使用
-verbose[:class|gc|jni] 在输出设备上显示虚拟机运行信息。
可以通过-verbose:class 命令在控制台看到在程序运行的时候有多少类被加载。
用来监视虚拟机内存回收的情况。
[Full GC 256K->160K(124096K), 0.0042708 secs]
箭头前后的数据256K和160K分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有256K-160K=96K的对象容量被回收,括号内的数据124096K为堆内存的总容量,收集所需要的时间是0.0042708秒(这个时间在每次执行的时候会有所不同)。
-verbose:jni表示输出native方法(本地方法)调用的相关情况,一般用于诊断jni调用错误信息。jni:Java native interface 本地接口
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递。
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,
保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
一定要注意:这样可避免别人写入具有破坏性质的类加载到核心类中。
三、class对象的获取
1.通过实例变量的getClass()方法:
Dog dog =
new
Dog();
Class d = dog.getClass();
2.通过类Class的静态方法forName():
try
{
Class dog1 = Class.forName(
"Dog"
);
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
3.直接给出对象类文件的.class:java中每个类型都有class 属性.
Class dog2 = Dog.
class
;
(三)利用反射创建初始化对象
1、创建对象
- Class c =Class.forName("Employee");
- //创建此Class 对象所表示的类的一个新实例
- Objecto = c.newInstance(); //调用了Employee的无参数构造方法.
四、获取属性与方法:
a,先看获取所有的属性的写法:
- //获取整个类
- Class c = Class.forName("java.lang.Integer");
- //获取所有的属性?
- Field[] fs = c.getDeclaredFields();
- //定义可变长的字符串,用来存储属性
- StringBuffer sb = new StringBuffer();
- //通过追加的方法,将每个属性拼接到此字符串中
- //最外边的public定义
- sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");
- //里边的每一个属性
- for(Field field:fs){
- sb.append("\t");//空格
- sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等
- sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字
- sb.append(field.getName()+";\n");//属性的名字+回车
- }
- sb.append("}");
- System.out.println(sb);
b,获取特定的属性,对比着传统的方法来学习:
- public static void main(String[] args) throws Exception{
- <span style="white-space:pre"> </span>//以前的方式:
- /*
- User u = new User();
- u.age = 12; //set
- System.out.println(u.age); //get
- */
- //获取类
- Class c = Class.forName("User");
- //获取id属性
- Field idF = c.getDeclaredField("id");
- //实例化这个类赋给o
- Object o = c.newInstance();
- //打破封装
- idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。
- //给o对象的id属性赋值"110"
- idF.set(o, "110"); //set
- //get
- System.out.println(idF.get(o));
- }
2,获取方法,和构造方法,不再详细描述,只来看一下关键字:
方法关键字 | 含义 |
getDeclaredMethods() | 获取所有的方法 |
getReturnType() | 获得方法的放回类型 |
getParameterTypes() | 获得方法的传入参数类型 |
getDeclaredMethod("方法名",参数类型.class,……) | 获得特定的方法 |
|
|
构造方法关键字 | 含义 |
getDeclaredConstructors() | 获取所有的构造方法 |
getDeclaredConstructor(参数类型.class,……) | 获取特定的构造方法 |
|
|
父类和父接口 | 含义 |
getSuperclass() | 获取某类的父类 |
getInterfaces() | 获取某类实现的接口
|