java 反射学习

来源:互联网 发布:js input 失去焦点 编辑:程序博客网 时间:2024/06/05 06:19

同一个JVM的所有线程、所有变量都处于同一个进程里,他们都使用该JVM进程的内存区。

 

当系统出现以下几种情况时,JVM进程将被终止:

1、程序运行到最后正常结束

2、程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处结束程序。

3、程序执行过程中遇到未捕获的异常或错误而结束

4、程序所在平台强制结束了JVM进程

 

依此运行下面的两个main方法,说出结果?

public class A {public static int a=6;}

 

public class ATest1 {public static void main(String[] args) {A a = new A();a.a++;System.out.println(a.a);//7}}

 

public class ATest2 {public static void main(String[] args) {A b = new A();System.out.println(b.a);//7}}

 

备注:第一个main显示7没有问题,但是第二个结果也许会说是8,但是运行的结果是7;实际上两次运行JAVA程序处于两个不同的JVM进程中,两个JVM之间并不会共享数据

 

一、类的加载

当程序主动使用某个类时,如果该类还没有被加载到内存中,则系统会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这3个步骤,所以有时候也把这3个步骤统称为类加载或类初始化。

 

类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之创建一个java.lang.Class对象。

 

类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过几成ClassLoader基类来创建自己的类加载器。

 

类加载通常无须等到“首次使用”该类时候才加载该类,java虚拟机规范允许系统预先加载某些类

 

二、类的连接

当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类的连接又可分为如下3个阶段:

1、验证:验证阶段用于检测被加载的类是否有正确的内部结构,并和其他类协调一致。

2、准备:负责为类的静态Field分配内存,并设置默认初始值

3、解析:将类的二进制数据中的符合引用替换成直接引用。

 

三、类的初始化

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态Field进行初始化。

对静态Field指定初始化值有两种方式:

1、声明静态Field时指定初始化值

2、使用静态初始化块为静态Field指定初始值

 

JVM初始化一个类包含如下几个步骤

1、如果这个类还没有被加载和连接,则程序先加载并连接该类

2、假如该类的直接父类还没有被初始化,则先初始化其直接父类

3、假如类中有初始化语句,则系统依此执行这些初始化语句

当执行第2个步骤时,系统对直接父类的初始化步骤也遵循此步骤1~3;如果该直接父类又有直接父类,则系统再次重复这3个步骤来先初始化这个父类......依此类推,所有JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及所有父类(包括直接父类和间接父类)都会被初始化。

 

类初始化的时机

当JAVA程序首次通过下面6种方式来使用某个类或者接口时,系统就会初始化该类或者接口。

1、创建类的实例。使用new操作符来创建实例,通过反射来创建实例,通过反序列化的方式创建实例

2、调用某个类的静态方法

3、访问某个类或接口的静态Field,或者为该静态Field赋值

4、使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象。例如:Class.forName("Person"),如果系统还没有初始化Person类,则这行代码将会导致该Person类被初始化,并返还Person类对应的java.lang.Class对象

5、初始化某个子类。当初始化某个子类,该子类的所有父类都会被初始化

6、直接使用java.exe命令来运行某个主类。当运行某个主类时,程序会先初始化该主类。

 

需要特别指出:

对于一个final型的静态Field,如果该Field的值在编译时就可以确定下来,那么这个Field相当于“宏变量”。java编译器会在编译时就直接把这个Field出现的地方替换成它的值,因此即使程序使用该静态的Field,也不会导致该类的初始化。

 

例如

public class MyTest {static{System.out.println("静态初始化块...");}static final String compileConstant = "疯狂java讲义";}

 

public class CompileConstantTest {public static void main(String[] args) {System.out.println(MyTest.compileConstant);}}

 

运行结果:疯狂java讲义

 

备注:当某个静态Field使用了final修饰,而且它的值可以在编译时就确定下来,那么程序其他地方使用该静态Field时,实际上并没有使用该静态Field,而是相当于使用常量

 

当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。使用Class的forName()静态方法才会导致强制初始化该类。

 

package hb.reflect;public class Tester{static{System.out.println("初始化块");}}

 

package hb.reflect;public class ClassLoaderTest {public static void main(String[] args) throws ClassNotFoundException {ClassLoader c1 = ClassLoader.getSystemClassLoader();//不会执行初始化c1.loadClass("hb.reflect.Tester");System.out.println("系统加载Tester类");//执行初始化内容Class.forName("hb.reflect.Tester");}}

 

原创粉丝点击