首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >类加载与Class对象

类加载与Class对象

作者头像
用户5745563
发布2019-07-03 17:31:10
6330
发布2019-07-03 17:31:10
举报
文章被收录于专栏:码思客码思客码思客

java零基础入门-高级特性篇(十四) 类加载与反射 1

初学者有时候在做练习的时候,可能会碰到一个十分诡异的问题。今天老师讲到了String字符串,好开心,打开eclipse开始写代码,然后一顿操作,只见双手在键盘上飞驰,行云流水般写出如下代码,嘴角露出一丝微笑,String也不过如此嘛。

报错

等等,咋报错了?这是个什么情况?哪里有错?为什么连个String类型都不给我用了?为什么!!!接下来轻则怀疑自己智商,重则自暴自弃,从此放弃走上码农之路。

其实大家仔细一看,哦~原来是类的名字有问题啊,怎么能定义一个叫String的类呢,这样写肯定报错呀。但请各位再仔细想一想,为什么就不能定义一个叫String的类呢?恐怕能说出其中原因的同学并不多。

类加载器与双亲委派模型

首先来回忆一下前面的知识。编写完java文件后,jvm是不能直接运行java文件的,首先要将java文件编译成class文件以后,jvm再把class文件加载到内存中,创建一个Class对象,这时候才可以使用这个类。加载这个动作,就是由类加载器完成的。在jvm中有三个内置类加载器,Bootstrap ClassLoader,Extension ClassLoader,Application ClassLoader。

Bootstrap ClassLoader:启动类加载器,用于加载java核心库jre/lib/rt.jar

Extension ClassLoader:扩展类加载器,用于加载java扩展库jre/ext/*.jar

Application ClassLoader:应用程序类加载器,自己写的代码都是使用这个加载器来加载的

先来看看这几个加载器的工作流程

工作过程

这个类的加载过程有一个更加专业的名字,叫做双亲委派模型。这个模型名字看名字不好理解,来想象一下下面这个场景。小明一家人一起吃饭,看到大鸡腿就想吃,但是中华文明优良传统告诉他,得先问问爸爸,这个大鸡腿我能吃吗?然后爸爸又看看爷爷,问爷爷要吃吗?爷爷说还是给小明吃吧,这时候爸爸再告诉小明,你可以吃了。爸爸和爷爷是双亲,能不能吃要先看上面,如果恰好爷爷也想吃,小明也只能干瞪眼了。

再来看这个双亲委派模型,在加载类的时候,应用程序类加载器不会直接去加载它,它先要问它的父类加载器,它的父类加载器再去问爷类加载器,爷类加载器一看,不该我管,丢给父类,父类一看,也不是我管,最后丢回来给应用程序类加载器。

再来看看上面那个String的例子,为什么会报错。java本身有一个String的类型,String在哪?

java.lang.String

注意String最上面的jar包,Bootstrap ClassLoader的管辖范围就是这个jar包,现在知道程序报错的原因了没?根据类的加载流程,我们希望得到的String是应该被启动类加载器加载的String,如果String的类路径是java.lang.String,就是我们想要的String。但是上个例子里面,我们自定义了一个String,它的类路径不是java.lang.String,所以他不会被启动类加载器加载,而是最后被应用程序类加载器加载,所以它并不是我们想要的那个String,不是那个可以用字符串赋值的类型String,这时候将一个字符串赋值给这个“String”,在编译的时候就会出错,因为我们自定义的String不具备这个接收字符串赋值的功能。

Class类型

上面说过,jvm把class文件加载到内存中时,会创建一个Class对象。这个Class对象是什么呢?类不是用class定义的么,怎么还有一个Class?别晕,现在就来详细说说Class是干什么的。

先从Class存在的意义来说。类是一类事物的描述,类包含了这类事物的信息,比如车这个类,包含了车的类型,用途,行为信息,可以在路上跑。鱼这个类,包含了鱼的种类,名称,行为信息,可以在水里游。

类的属性和行为

从上图可以看到,用属性和行为可以描述一类事物。那么“类”这个事物,是否也可以被描述呢?答案是肯定的,Class类就是用来描述类的信息的。Class也是一种类型,它专门用来描述类的特征。

类类型

车这个类可以用名称,类型,行为来描述。类这个类可以用属性,行为来描述,所有的类都可以有属性,都可以有行为。可以将Class类看做是普通类型的更高层抽象,用来描述普通类如何构成,包含内容等信息。

再从类的加载和对象创建来说。每写完一个类文件,首先会被编译成.class文件,然后在运行时,这个.class文件会被加载到jvm中,如果是第一次加载这个类,那么会同时生成这个类对应的Class对象。当使用new关键字创建类的对象的时候,会首先去这个类对应的Class对象获取该类的信息,然后创建对象,所以可以将Class对象看做是类的模板,同一个类的对象创建都使用这个模板。类创建的对象可以有很多,但是模板只有一份,也就是说每个类对应的Class对象只有一个。

关系

java文件被编译加载后创建Class对象,当这个java文件的类需要创建对象的时候,也就是使用new关键字创建对象的时候,会去获取那个已经被创建好的Class对象中的信息。这里要注意一个重点,获取Class对象信息的时候是运行时,只有在运行时才能通过Class获取类的信息。

获取Class对象

既然Class对象包含这么多信息,那么在程序中如何获取Class对象?获取Class的方法有三种:

1.Class.forName("类名"); 通过类名字符串获取Class对象。

2.通过类的对象调用getClass() 获取该类型的Class对象

3.通过类型直接获取Class对象

获取Class对象

先看第一种方法,通过类型的全限定名获取Class对象。通过这种方式可以直接使用一个字符串来获取Class对象,这种方式使用起来比较灵活,当需要对类型进行配置的时候,可以将对象的全限定名写入到配置文件中,这样就可以在不修改代码的情况下,通过修改配置的方式来改变创建对象的类型。需要注意的是,通过Class.forName()方法获取的对象是无法确定类型的,所以使用无限制泛型通配符。

第二种方法,是通过对象的实例的getClass()方法来获取Class对象,这种方式要先创建类型的对象,再根据创建的对象来获取Class对象。由于根据已知类型的对象获取的Class,所以这个Class对象的类型可以是User或其子类。

第三种方法,直接根据类型获取Class对象,这种方式最直接,连方法都不用调用,直接获取Class对象,速度最快效率最高。一般情况使用这种方法获取Class对象最为普遍。

最后要注意的一点是,同一个类只会有一个Class对象,上例中第二种和第三种方法获取到Class对象后,将获取到的对象与第一种方法获取到的Class对象进行地址比较,结果都是true。可以说明,不管用哪种方法获取Class对象,都只会获取到同一个并且是该类唯一一个Class对象。

现在已经获取到Class对象了,那么该如何使用它呢?前面说过,Class对象有类里面所有的信息,所以可以通过Class获取到类里面所有的构造器,方法,成员变量等等所有的信息。

获取类信息

Class对象可以获取到所有信息,上图只是一个普通的类,如果获取到ClassInfoDemo这个类对应的Class对象,就可以通过调用这个对象的方法获取如上图中包,成员变量,构造器,方法等信息。如果类还有其他信息比如注解,实现接口方法,内部类,外部类等等信息,都可以通过Class对象的对应方法获取,可见Class是一个功能非常强大的类。

讲了半天类的加载和Class对象,这些知识点有什么用?这都是为了接下来登场的知识点-反射打好基础,反射技术十分重要,在各大框架中被频繁的使用,所以在学习的过程中,了解反射的意义对后续框架的学习有很大的帮助。

本文为了方便理解,隐藏了部分类加载中的细节,如需深入了解,请自行查阅相关资料。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码思客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档