写点什么

反射 API

用户头像
彭阿三
关注
发布于: 2020 年 10 月 10 日

前言

在面试中,经常会被问到在Java反射中Class.forName()加载类和使用ClassLoader()加载类的区别



反射API

定义

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

用途

在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射。



反射机制的相关类

Java反射相关的类如下:





Class类

Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。



获得类相关的方法





获得类中属性相关的方法





获得类中注解相关的方法





获得类中构造器相关的方法





获得类中方法相关的方法





类中其他重要的方法



Field类

Field代表类的成员变量(成员变量也称为类的属性)。



Method类

Method代表类的方法。





Constructor类

Constructor代表类的构造方法。



Java反射中,Class.forName和ClassLoader的区别

在java中Class.forName()和ClassLoader都可以对类进行加载。ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的。



Class.forName(String className)源码:



@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

最后调用的方法是forName0这个方法,在这个forName0方法中的第二个参数被默认设置为了true,这个参数代表是否对加载的类进行初始化,设置为true时会类进行初始化,代表会执行类中的静态代码块,以及对静态变量的赋值等操作。



也可以调用Class.forName(String name, boolean initialize,ClassLoader loader)方法来手动选择在加载类的时候是否要对类进行初始化。



Class.forName(String name, boolean initialize,ClassLoader loader)的源码如下:



@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
//如何initialize是true,则加载的类会被初始化
return forName0(name, initialize, loader, caller);
}

###举例

下面还是举例来说明结果吧:

一个含有静态代码块、静态变量、赋值给静态变量的静态方法的类



public class Demo {
//静态代码块
static {
System.out.println("执行了静态代码块");
}
//静态变量
private static String staticFiled = staticMethod();
//赋值静态变量的静态方法
public static String staticMethod(){
System.out.println("执行了静态方法");
return "给静态字段赋值了";
}
}

使用Class.forName()的测试方法



@Test
public void test44(){
try {
Class.forName("com.test.Demo");
System.out.println("#########-------------结束符------------##########");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

运行结果:



执行了静态代码块
执行了静态方法
#########-------------结束符------------##########

使用ClassLoader的测试方法:



@Test
public void test45(){
try {
ClassLoader.getSystemClassLoader().loadClass("com.eurekaclient2.client2.ClassForName");
System.out.println("#########-------------结束符------------##########");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

运行结果:

#########-------------结束符------------##########



根据运行结果得出Class.forName加载类时将类进了初始化,而ClassLoader的loadClass并没有对类进行初始化,只是把类加载到了虚拟机中。



##应用场景

在我们熟悉的Spring框架中的IOC的实现就是使用的ClassLoader。



而在我们使用JDBC时通常是使用Class.forName()方法来加载数据库连接驱动。这是因为在JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己



抛出问题

1.什么事双亲委派模式

2.SPI是否破坏了双亲委派模式



用户头像

彭阿三

关注

java工程师 2019.06.28 加入

一个慵懒的程序员。

评论

发布
暂无评论
反射API