浅谈 Java 中的 Class 类

来源:互联网 发布:mac看图软件推荐 编辑:程序博客网 时间:2024/05/03 01:24

最近一段时间,对于 java 的反射比较感兴趣,于是打算通过深入学习来更好的了解反射的实现机制。于是开始查阅有关反射的博文,但是在学习的过程中,发现 Class 类始终伴随着反射出现。因为多少接触过 Class 类,所以有关反射的代码还是能看懂的,不会影响我学习反射的知识,本就可以直接略过。

但是仔细一想,学习是一个主动的过程,要积极的去解决每一个小的问题,争取在求知过程中不留存疑惑,否则到最后会发现因为这些小问题的存在,使得自己的理解始终停在“知其然,不知所以然”的层面。既然接触到了 Class 类,就应该顺带消化了,这样的才能学到更多的东西。

后续我会和大家分享我在学习反射时的一些心得,在此之前,先分享一下我学习 Class 类的一些体会哈。此次分享没有太多高深的知识,但是还是觉得只要您认真的看完本文,多少会有收获的,哪怕是巩固一下也不错,嘿嘿 : )

面向对象

我们都知道,java 是一门面向对象的语言。在面向对象的世界里,万事万物皆对象,除了静态成员(因为静态成员属于某个类,而不是对象)和普通数据类型。

在面向对象的语言中,我们擅长将现实世界中的一个实际存在的事物抽象并封装成一个类,并在类中添加相应的成员变量(属性)和方法,然后我们就可以创建该类的对象,该对象持有属于自己的成员变量和方法。

既然万事万物皆对象,那么我们的类是不是对象呢?是的,我们写的每一个类都是对象,是 java.lang.Class 类的对象。也就是说,每一个类既有自己的对象,同时也是 Class 类的对象。那么,该如何表示 Class 类的对象呢,让我们接着往下看,以进一步了解 Class 类。

java.lang.Class 类

初识 Class 类

很遗憾直到现在我才认识到 Class 的意义,不敢说这次接触就彻底弄懂了 Class 类,但至少目前所理解的内容可以帮助我更好的分析其他问题了。

都说学习技术最好的方法就是查看源码和官方 API ,那么这次我也这么来,虽然我技术一般,但是格调还是要跟上的!我发现了一篇比较好的从源码角度分析 Class 类的博客,在这里推荐给大家,感兴趣的可以跟着这篇文章把 Class 类的源码过一遍,一定会有收获的。当然,您现在不看也没关系,因为推荐的博客中只有一点是我接下来要强调的,我会在后面给大家概括出来。点我跳转

先来看看 Class 类的构造方法(有删减):

    /*     * Private constructor.      * Only the Java Virtual Machine     * creates Class objects.     */    private Class(ClassLoader loader) {        classLoader = loader;    }

根据注释可知,Class 类的构造方法是私有的,只有 Java 虚拟机可以创建该类的对象,因此我们无法在代码中显式地声明一个 Class 对象。这就是我要强调的内容,其对于后面内容的理解十分重要。

Class 类与其他类的关系

由类或类对象得到 Class 类的对象

自定义一个类 MyClass,并声明该类的对象:

class MyClass{}MyClass mClass1 = new MyClass();

在上面说过,Class 类的构造方法是私有的,只有 java 虚拟机可以调用该方法创建该类的对象。也就是说我们无法像定义普通类对象一样,通过 new 直接创建 Class 类的对象。

但是,我们依然可以通过其他方式得到 Class 类的对象

  1. 通过类的静态成员表示。每个类都有一个隐含的静态成员class,表示如下:

    Class c1 = MyClass.class;
  2. 通过类对象的 getClass() 方法。由1不难理解,既然存在静态变量,那么通过对象的 getter 方法,就可以获取静态成员class:

    Class c2 = mClass1.getClass();
  3. 通过 Class 类的静态方法 forName() 方法获取 Class 的对象。区别于通过 new 创建对象(编译时静态加载),在开发时如果我们需要动态加载我们的功能模块,该方法可以帮助我们实现在程序运行时类的动态加载。

    try {        //注意,forName()需要传入类的全路径        //如果当前类与参数类在同一包下即可省略包名        mClass = Class.forName("custom.OtherClass");    } catch (ClassNotFoundException e) {        e.printStackTrace();    }

如果我们的程序中没有 OtherClass 这个类,使用 Class.forName() 动态加载时,在程序编译时刻是不会报错的,只有在运行时刻检测到没有该类才会出错。

类的动态加载静态加载是 Java 中一个重要的知识点,但因为我们开发时通常都使用 IDE, 其帮助我们自动实现了程序的编译和运行,使得我们常常忽略了程序的编译时和运行时状态。说着说着,我发现这块我有必要再写一篇博客记录一下,今天就先不讨论了。

由 Class 类的对象得到类的对象

我们可以通过类或类对象得到 Class 类的对象,反过来,我们也可以由 Class 类的对象得到类的对象:

MyClass mClass2 = (MyClass)c2.newInstance();

需要提醒您的是:c2.newInstance() 需要调用 MyClass 类的无参构造方法!如果 MyClass 类中存在显示的有参构造方法,会覆盖默认的无参构造方法,同时又没有显示的声明无参构造方法,那么执行这段代码时会直接导致程序Crash掉。下面为大家演示一下:

示例代码,请看注释:

/** * Created by Mr Rui on 2016/5/1. */public class TestDemo {    public static void main(String args[]){        //得到 Class 对象 mClass        Class mClass = MyClass.class;        try {            // 由 mClass 实例化 MyClass 的对象,            // 需要调用 MyClass 的无参构造方法            MyClass mMyClass =                     (MyClass) mClass.newInstance();        } catch (Exception e) {            e.printStackTrace();        }    }}class MyClass{    //有参构造方法,会覆盖默认的无参构造方法    public MyClass(String s){}}

程序异常:

java.lang.InstantiationException: MyClass    at java.lang.Class.newInstance(Class.java:427)    at TestDemo.main(TestDemo.java:12)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)    at java.lang.reflect.Method.invoke(Method.java:497)    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)Caused by: java.lang.NoSuchMethodException: MyClass.<init>()    at java.lang.Class.getConstructor0(Class.java:3082)    at java.lang.Class.newInstance(Class.java:412)    ... 6 more

解决办法就是在 MyClass 中的显示的添加一个 无参构造方法,就不再提供示例了。

针对这一点,相信有许多人在使用第三方框架或者开源库时,遇到过因为在类中添加了带参数的构造方法而导致程序出错的情况!针对这个,我的理解是:有些框架是基于反射实现的,它根据我们传入的类对象,使用其 newInstance() 方法获取 Class 对象,进而进行后续的反射操作(不在本文的讨论范围)。可是因为我们无意覆盖了默认的无参构造方法,导致程序无法正常获取 Class 对象,所以就出错了。说到这儿,您应该能理解其中缘由了吧!

这次分享的内容比较少,归结起来就是如何获取 Class 类的对象,以及如何由 Class 对象得到类对象。至于如何使用 Class 对象进行反射操作,如何实现程序运行时动态加载类,在后面的分享中会继续向大家介绍的。

我将所理解知识梳理并整理出来,一方面供大家参考交流,另一方面因为我也处在学习的阶段,希望能得到更多人的指导。如果文中有任何错误或遗漏的地方,还望您给予指正,我会积极完善!

Java 反射基础(上)

2 0