Class对象

来源:互联网 发布:中国网络被攻击2017 编辑:程序博客网 时间:2024/04/29 19:58

Java程序运行时类型信息RTTI可以使我们在程序运行时发现和使用使用类型信息。

Class对象

Class对象是一个特殊的对象,每个类都有一个且仅有一个Class对象。Class对象就是用来创建类的所有“常规”实例对象的,Java通过Class对象来执行其RTTI。

对于每个新类,首先JVM编译生成.class文件,当我们使用一个类的实例时,JVM首先检查这个类是否已经加载到内存中。如果没有加载到内存中,首先就是类加载器动态的加载到JVM内存中。一旦某个类的Class对象被加载到了内存中,它就被用来创建这个类的所有对象。

从上面可知,某一个类的实例对象和Class对象不是一个东西。我们是首先必须生成Class对象才能生成实例对象。那么生成Class对象的方式有哪些呢?有如下三种:
1、Class.forName(“类的全限定名”);(这里必须是包名+类名)
2、类名.class
3、实例对象.getClass();

关于类的加载机制可以参考JVM部分的博客:http://blog.csdn.net/u010853261/article/details/53981375
从上面的blog可知,类的加载过程可以分为:预加载、连接、初始化。经过这三部之后该类就可以创建实例并使用了。而在初始化阶段才真正的类中定义的Java程序代码(或则说字节码),比如给static变量赋予用户指定的值以及执行静态代码块也是在这一步执行。

下面有个测试代码,分析这三种获取Class对象的区别:

package test;/** * Created by louyuting on 17/1/14. */class First{    private static int staticA = 1;    private int B=20;    public First() {        System.out.println("First类的构造函数");    }    static {        System.out.println("staticA="+staticA);        staticA=2;        System.out.println("静态域的Loading First");        System.out.println("静态域的Loading First"+B);    }    {        System.out.println("staticA="+staticA);        System.out.println("B=" + B);        System.out.println("非静态域参数初始化");    }}class Second{    static {        System.out.println("Loading Second");    }}class Third{    static {        System.out.println("Loading Third");    }}public class ClassTest {    @org.junit.Test    public void test1() throws ClassNotFoundException {        Class first1 = First.class;//没有加载类        System.out.println("first1:" + first1);    }    @org.junit.Test    public void test2() throws ClassNotFoundException {        Class first2 = Class.forName("test.First");//加载类到JVM        System.out.println("first2:" + first2);    }    @org.junit.Test    public void test3() throws ClassNotFoundException {        Class first3 = new First().getClass();//加载类到JVM内存并把非静态域参数初始化.        System.out.println("first3:" + first3);    }}

上面有三个测试函数test1()、test2()和test3()。这三个测试函数实际就是三种获取Class对象的方式。下面看看三种运行结果:
1)test1的运行结果:

first1:class test.First

从test1()的运行结果可知,First.class这种方式获取class对象的时候,在JVM中加载类的时候并没有执行初始化动作,所以静态域和静态代码块都没有被执行。

2)test2()的运行结果如下:

//output:staticA=1静态域的Loading Firstfirst2:class test.First

运行结果可知,Class.forName(“”);这种方式获取class对象的时候,在JVM中加载类的时候执行了初始化动作,所以静态域和静态代码块都被执行了,从输出的结果就可知。但是类的非静态域就没有被执行,我在写测试代码时,准备在static静态块中输出B值,发现直接报错,“说没有B这个静态域变量可以引用”,说明非静态域参数这时没有被初始化。

3)test3()的运行结果如下:

//output:staticA=1静态域的Loading FirststaticA=2B=20非静态域参数初始化First类的构造函数first3:class test.First

test3()这种方式是获取Class对象方式是直接先new一个First实例对象,从上文的理论可知,首先肯定实现加载First类的Class对象到JVM中并成功初始化了才能够使用该类的实例对象。所以首先就是在加载Class对象那个时就执行了静态域,之后在new实例对象时执行了非静态域的参数实例化。之后执行构造器。

上面测试代码的github地址

对上面的内容做一个总结:

上面主要是介绍了三种获取某一个类的Class对象的方法。但是一般情况下推荐使用“.class”这种方式,因为这样做不仅简单而且更加安全,因为它在编译时就会收到检查。但是以这种方式需要注意的一点是:使用“.class”来创建对Class对象的引用时不会自动的初始化该Class对象,初始化操作被延迟到了对静态方法(构造器是隐式的静态方法)或则静态域首次进行引用时才执行。而其余的方式都是马上就进行了初始化。

0 0
原创粉丝点击