理解java Class类

http://blog.csdn.net/bingduanlbd/article/details/8424243/

Java Class类理解:

首先,Class是一个java类,跟Java API中定义的诸如Thread、Integer类、我们自己定义的类是一样,也继承了Object(Class是Object的直接子类)。总之,必须明确一点,它其实只是个类,只不过名字比较特殊。更进一步说,Class是一个java中的泛型类型。

对于我们自己定义的类,我们用类来抽象现实中的某些事物,比如我们定义一个名称为Car的类来抽象现实生活中的车,然后可以实例化这个类,用这些实例来表示我的车、你的车、黄的车、红的车等等。

好了,现在回到Class 类上来,这个类它抽象什么了?它的实例又表示什么呢?

在一个运行的程序中,会有许多类和接口存在。我们就用Class这个来来表示对这些类和接口的抽象,而Class类的每个实例则代表运行中的一个类。例如,运行的程序有A、B、C三个类,那么Class类就是对A、B、C三个类的抽象。所谓抽象,就是提取这些类的一些共同特征,比如说这些类都有类名,都有对应的hashcode,可以判断类型属于class、interface、enum还是annotation。这些可以封装成Class类的域,另外可以定义一些方法,比如获取某个方法、获取类型名等等。这样就封装了一个表示类型(type)的类。

需要注意的是,这个特殊的Class类没有公开的构造函数,那怎么获取Class类的实例呢?有几个途径。

1.      当Java虚拟机载入一个类的时候,它就会自动创建一个Class类的实例来表示这个类。例如,虚拟机载入Car这个来的时候,它就会创建一个Class类的实例。然后可以通过以下方法获得这个Class对象:

java.lang.Class classObj = ClassName.class;

2. 可以通过调用类加载器(ClassLoader)的defineClass()方法来得到一个实例。这个方法接受一个byte数组,载入这个byte数组否成的class类,同时实例化一个Class对象。

3. ClassName.class( )   ClassName.getClass( )

现在来分析一下Class类的源码(java.lang.Class):

public final

class Class<T>implementsjava.io.Serializable,

                            java.lang.reflect.GenericDeclaration,

                             java.lang.reflect.Type,

                             java.lang.reflect.AnnotatedElement {…

ClassClass<T>,前一个Class表示这是一个类的声明,第二个Class是类的名称,<T>表示这是一个泛型类,带有参数T.同时,Class类实现了许多接口。

紧接着定义了几个静态变量:

private static final int ANNOTATION= 0x00002000;

private static final int ENUM      = 0x00004000;

private static final int SYNTHETIC = 0x00001000;

接着定义一个本地方法registerNatives(),并在静态块中调用:

private static native void registerNatives();

static {

registerNatives();

}

私有的构造函数:

private Class() {}

访问修饰符是private,程序员是无法直接调用这个构造函数,只能通过JVM来调用它,构造一个Class实例。

public String toString() {

return (isInterface() ?"interface " : (isPrimitive() ? "" : "class"))

            + getName();

}

这是Class对象实例的字符串表示方法,应该不陌生。那么,它返回什么东西呢?

>如果这个Class对象实例所表示的是一个Java类,则返回class full_classname.

例如java.lang.Math.java这个类,它所对应的Class实例的toString方法返回的就是class java.lang.Math

>如果是接口,将class改成interface。还有一种特殊情况,如果Class实例表示的是void类型,则发挥void。如果是基本类型,一样的返回基本类型的名称。

静态方法forName:

public static Class<?> forName(String className)

throws ClassNotFoundException{

returnforName0(className,true, ClassLoader.getCallerClassLoader());

}

根据给定的类名参数className,查找与className相对应的Class实例,然后加载、连接该实例对象,之后返回这个Class实例。其中例如以下代码段将输出:

class java.lang.Thread

public class ClassTest {

/**

     * @param args

     */

public static void main(String[] args) {

try {

           System.out.println( Class.forName("java.lang.Thread") );

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

           System.out.println("No ClassNamed java.lang.Thread");

       }

    }

}

forName方法重载:

public static Class<?> forName(String name, boolean initialize,

                                   ClassLoaderloader)

throws ClassNotFoundException

    {

if (loader ==null) {

            SecurityManager sm = System.getSecurityManager();

if (sm !=null) {

                ClassLoader ccl = ClassLoader.getCallerClassLoader();

if (ccl !=null) {

                    sm.checkPermission(

                        SecurityConstants.GET_CLASSLOADER_PERMISSION);

                }

            }

        }

returnforName0(name, initialize,loader);

    }

注意到这个forName重载方法中多了两个方法参数,其中initialize这个boolean类型指定是否要初始化对应的Class实例,loader指定加载Class实例的加载器。留意这个方法可能抛出的异常还是比较多的,比如连接失败、找不到对应的类等等。

forName0本地方法:

private static native Class<?> forName0(String name,boolean initialize,

                                            ClassLoaderloader)

throws ClassNotFoundException;

这是一个本地方法,在前面的静态方法forName的两个版本中都调用了这个本地方法。

newInstance()方法:

public T newInstance()          // T是个泛型参数,是Class实例所表示的Java类

throws InstantiationException,IllegalAccessException

    {

if (System.getSecurityManager()!=null) {

            checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());

        }

return newInstance0();     //这是一个本地方法,也在Class类中定义

}

注意这是一个实例方法,必须由Class类的实例对象调用。例如,有一个代表java.lang.Thread类的Class实例对象objec1,也就是说,泛型参数T此时就是Thread,object1这个实例代表Thread这个类。好了,现在调用object1的newInstance方法,即object1.newInstance(),此时这个调用将返回一个Thread类的对象。简单验证:

public class ForName {

/**

     * @paramargs

     * @throwsIllegalAccessException

     * @throwsInstantiationException

     */

public static void main(String[] args) throws InstantiationException, IllegalAccessException {

       Class<?> c = null ;

try {

           c = Class.forName("java.lang.Thread");

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       }

       Thread thread = (Thread) c.newInstance(); //类型转化一下

       System.out.println(thread.getId());

    }

}

在我机子中,上述代码输出 8 。即c.newInstance产生一个ID为8的新线程。

getClassLoader:

public ClassLoader getClassLoader() {

        ClassLoader cl = getClassLoader0();

if (cl ==null)

return null;   // Bootstrap

        SecurityManager sm = System.getSecurityManager();

if (sm !=null) {

            ClassLoader ccl = ClassLoader.getCallerClassLoader();

if (ccl !=null && ccl != cl&& !cl.isAncestor(ccl)) {

               sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);

            }

        }

return cl;

    }

//Package-private to allow ClassLoader access

native ClassLoadergetClassLoader0();   // 本地方法

这个方法返回该Class对象代表的类的类加载器。如果类的加载器是Bootstrap,则返回null。下面的代码输出:The ClassLoader of Thread Class is Bootstrap

Class<?> classObj= Thread.class;

       ClassLoader loader = classObj.getClassLoader();

if (loader ==null) {

           System.out.println("TheClassLoader of Thread Class is Bootstrap");

       } else {

           System.out.println(loader);

       }

获取父类方法:getSuperclass()

public native Class<? super T> getSuperclass();

这是一个本地方法,这里的逻辑有点饶,方法返回的是这个Class对象所代表的Java类的父类对应的的Class 对象。

例如: Thread.class.getSuperclass()将返回一个代表Thread类的Class对象,Thread.class.getSuperclass().toString()则输出这个Class对象的字符串表示:classjava.lang.Object。其实这里的关系无非就是说Thread的超类是Object。只是饶了Class对象这个弯子,至于这么绕有什么好处,还没有深刻体会。

首先要生成Class对象,然后再生成Instance。那Class对象的生成方式有哪些呢,以及其中是如何秘密生成的呢?

Class对象的生成方式如下:

1.Class.forName("类名字符串")  (注意:类名字符串必须是全称,包名+类名)

2.类名.class

3.实例对象.getClass()

通过一段小程序,来观察一下Class对象的生成的原理。

  1. /**
  2.  * 2012-2-6
  3.  * Administrator
  4.  */
  5. /**
  6.  * @author: 梁焕月 
  7.  * 文件名:TestClass.java 
  8.  * 时间:2012-2-6上午10:01:52  
  9.  */
  10. public class TestClass {  
  11. public static void main(String[] args)  
  12. {  
  13. try {  
  14. //测试Class.forName()
  15. Class testTypeForName=Class.forName("TestClassType");          
  16. System.out.println("testForName---"+testTypeForName);  
  17. //测试类名.class
  18. Class testTypeClass=TestClassType.class;  
  19. System.out.println("testTypeClass---"+testTypeClass);  
  20. //测试Object.getClass()
  21. TestClassType testGetClass= new TestClassType();  
  22. System.out.println("testGetClass---"+testGetClass.getClass());  
  23. catch (ClassNotFoundException e) {  
  24. // TODO Auto-generated catch block
  25. e.printStackTrace();  
  26. }  
  27. }  
  28. }  
  29. class TestClassType{  
  30. //构造函数
  31. public TestClassType(){  
  32. System.out.println("----构造函数---");  
  33. }  
  34. //静态的参数初始化
  35. static{  
  36. System.out.println("---静态的参数初始化---");  
  37. }  
  38. //非静态的参数初始化
  39. {  
  40. System.out.println("----非静态的参数初始化---");  
  41. }          
  42. }  

测试的结果如下:

---静态的参数初始化---

testForName---class TestClassType

testTypeClass---class TestClassType

----非静态的参数初始化---

----构造函数---

testGetClass---class TestClassType

根据结果可以发现,三种生成的Class对象一样的。并且三种生成Class对象只打印一次“静态的参数初始化”。

我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new类实例对象的时候加载。

因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。

所以,生成Class对象的过程其实是如此的:

当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏行者常至

泛型中获取到Class

60820
来自专栏Android干货园

Kotlin中级(7)- - - Kotlin类之抽象类、重载和重写.md

abstract 其中值得注意的是:抽象可以分为抽象类、抽象函数、抽象属性。而一个抽象类和普通类的区别在于抽象类除了可以有其自己的属性、构造函数、方法等组成部...

12520
来自专栏黑泽君的专栏

父类中如果没有无参构造方法(也即父类中只给了带参构造方法),子类的构造方法怎么办?

父类中如果没有无参构造方法(也即父类中只给了带参构造方法),子类的构造方法怎么办?

27510
来自专栏欧阳大哥的轮子

C++运算符重载详解

C++语言的一个很有意思的特性就是除了支持函数重载外还支持运算符重载,原因就是在C++看来运算符也算是一种函数。比如一个 a + b 的加法表达式也可以用函数的...

30530
来自专栏北京马哥教育

一篇搞定Python正则表达式

1. 正则表达式语法 1.1 字符与字符类     1 特殊字符:.^$?+*{}[]()|       以上特殊字符要想使用字面值,必须使用进行转义    ...

34760
来自专栏java技术学习之道

最详细的java泛型详解

21050
来自专栏微信公众号:Java团长

理解Java的三大特性之多态

封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方...

8110
来自专栏塔奇克马敲代码

第6章 函数

20870
来自专栏微信公众号:Java团长

Java泛型详解——绝对是对泛型方法讲解最详细的,没有之一!

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决...

10910
来自专栏小樱的经验随笔

【Java学习笔记之二十四】对Java多态性的一点理解

面向对象编程有三大特性:封装、继承、多态。       封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内...

35070

扫码关注云+社区

领取腾讯云代金券