大家好,又见面了,我是你们的朋友全栈君。
最近一段时间,对于 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 类的对象
-
通过类的静态成员表示。每个类都有一个隐含的静态成员class,表示如下:
Class c1 = MyClass.class;
-
通过类对象的
getClass()
方法。由1不难理解,既然存在静态变量,那么通过对象的 getter 方法,就可以获取静态成员class:Class c2 = mClass1.getClass();
-
通过
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 对象进行反射操作,如何实现程序运行时动态加载类,在后面的分享中会继续向大家介绍的。
我将所理解知识梳理并整理出来,一方面供大家参考交流,另一方面因为我也处在学习的阶段,希望能得到更多人的指导。如果文中有任何错误或遗漏的地方,还望您给予指正,我会积极完善!
扫描下方二维码,关注我的公众号,及时获取最新文章推送!
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141780.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...