Java类加载器与反射

一个命令对应一个进程

 当我们启动一个Java程序,即启动一个main方法时,都将启动一个Java虚拟机进程,不管这个进程有多么复杂。而不同的JVM进程之间是不会相互影响的。这也就是为什么说,Java程序只有一个入口——main方法,让虚拟机调用。而两个mian方法,对应的是2个JVM进程,启动的是两个不同的类加载器,操作的实际上是不同的类。故而不会互相影响。

类加载

 当我们使用一个类,如果这个类还未加载到内存中,系统会通过加载、连接、初始化对类进行初始化。

1、类加载:指的是将类的class文件读入JVM,并为之创建一个Class对象。

2、类连接:指的是把类的二进制数据合并到JRE中,这又分为3个阶段:

 a)、校验:检查载入Class文件数据的正确性。

 b)、准备:给类的静态变量分配存储空间,并进行默认初始化。

 c)、解析:将类的二进制数据中的符号引用替换成直接引用。

3、初始化:对类的静态变量、静态初始化块进行初始化。

(注意:一个final类型的静态属性,如果在编译时已经得到了属性值,那么调用该属性时,不会导致该类初始化,因为这个相当于使用常量;

使用ClassLoader()方法,只是加载该类,并未初始化。)

三、类加载器。

类加载器就是负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象,它负责加载所有的类,而一旦一个类被加载入JVM中,就不会被再次载入了。

在Java中,一个类用其全限定类名(即包名+类名)作为标识。

而在JVM中,一个类用其全限定类名和其类加载器作为标识。

JVM运行时会产生3个ClassLoader,分别为:BootstrapClassLoader(根类加载器)、ExtClassLoader(扩展类加载器)和AppClassLoader(系统类加载器)。UML结构如下:
这里写图片描述

四、类加载机制。

 JVM的类加载机制主要有如下三种机制:

1、全盘负责:当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入。

2、父类委托:先委托父加载器试图加载该类,只有在父加载器无法加载该类时才从自己的类路径中查找并装载该类。

3、缓存机制:缓存机制保证所有被家加载过的类都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜寻。缓存中搜寻不到,才会重新读取该类对应的二进制数据,并创建对应的Class对象。

(注意:类加载器之间的父子关系,并不是类继承上的父子关系,而是类加载器实例之间的关系)

自定义类加载器,重写ClassLoader的loadClass打破双亲委托,findClass遵循双亲委托,defineClass方法将字节码转换成Class对象,并校验有效性,该方法是final方法。

补充:URLClassLoader是系统类加载器和扩展类加载器的父类(除根类加载器之外的所有加载器都是ClassLoader子类的实例,根加载器是JVM自身实现的),它既可以从本地文件获取二进制文件加载类,也可以从远程主机获取二进制文件加载类
五、反射
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
以下是反射简单用法。

1、ClassLoader对象。

可以通过2种方式获得当前ClassLoader对象,一种是通过当前线程:

ClassLoader loader = Thread.currentThread().getContextClassLoader();

若已经有了Class对象,那么可以通过Class对象的getClassLoader()方法:

ClassLoader loader2 = Xxx.class.getClassLoader();

2、Class对象。

获得Class对象有3种方式:

 a)、Class类的forName方法,传入全限定类名。需要抛出异常ClassNotFoundException

 b)、调用该类的class属性。

 c)、调用某对象的getClass()方法。

3、通过Class对象创建对象:

例如有一个Apple类。Class clazz = Class.forName(“com.stopTalking.crazy.Apple”);

该Apple类有一个默认构造器、一个String类型参数的构造器、一个String类型、int类型的构造器。

 获得Apple对象有2种方式:

 a)、用Apple类默认构造器:Object o1 = clazz.newInstance();

 b)、用一个String类型参数的构造器:Object 02 = clazz.getConstructor(String.class).newInstance(“apple”);

 c)、用一个String类型、int类型的构造器:Object 02 = clazz.getConstructor(String.class,int.class).newInstance(“apple”,”3”);

4、调用方法:

Method m = clazz.getMethod(方法名,参数类型);//根据方法名和参数类型创建Method对象。

m.invokej(对象,属性值);//根据对象和方法参数调用方法。

若需调用private方法,先调用Method对象的setAccessible(true);

5、访问属性:

Field f = clazz.getDeclaredField(属性名);//根据属性名获取Field对象

f.get(对象);//根据对象得到属性值。

6、操作数组:

Object arr = Array.newInstance(String.class,10);//创建一个简单的String数组。

Array.set(arr,5,”abc”);//给该数组下标为5的元素赋值

Object name = Array.get(arr,5);//获取该元素

六.反射和泛型

通过field对象的f.getType()方法可以获得普通类型的field,f.getGenericType()强制转化为ParameterizedType对象,通过getRawType()获得原始类型,通过getActualTypeArguments()获得泛型参数类型

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值