> 文档中心 > 编写高质量代码 101-105之反射

编写高质量代码 101-105之反射


注重Class类的特殊性

Class类的三个特殊性

1.无构造函数。Java中的类一般都有构造函数,用于创建实例对象,但是Class类却没有构造函数,不能实例化,Class对象是在加载类时由Java虚拟机通过调用类加载器中的defineClass方法自动构造的。

2.可以描述基本类型。虽然8个基本类型在JVM中并不是一个对象,它们一般存在于栈内存中,但是Class类仍然可以描述它们,例如可以使用int.class标识int类型的类对象。

3.其对象都是单例模式。一个Class的实例对象描述一个类,并且只描述一个类,反过来也成立,一个类只有一个Class实例对象,如下代码返回的结果都为true

4.Class类是Java反射的入口

获得一个Class对象有三种途径:

1.类属性方式,如String.class

2.对象的getClass()方法,如new String().getClass()

3.forName()方法加载,如Class.forName(“java.lang.String”);

适时选择getDeclaredXXX和getXXX

Class类中提供了很多getDeclaredXXXgetXXX的方法:

getXXX的方式是获取所有公共的(public)级别的,包括从父类继承的方法。

getDeclaredXXX的方式是获取所有的,包括公共的(public),私有的(private),不受限与访问权限。

public static void main(String[] args) {      Class cls = User.class;      // 获取类方法      cls.getDeclaredMethods();      cls.getMethods();      // 获取类构造函数      cls.getDeclaredConstructors();      cls.getConstructors();      // 获取类属性      cls.getDeclaredFields();      cls.getFields();  }

反射访问属性或方法时将Accesible设置为true

在调用构造函数或方法的invoke前检查accessible已经是公认的写法,例如以下代码

public static void main(String[] args) throws Exception {      Class cls = User.class;      //创建对象      User user = cls.newInstance();            //获取test方法      Method method = cls.getDeclaredMethod("test");            //检查Accessible属性      if(!method.isAccessible()){          method.setAccessible(true);      }      method.invoke(user);  }  

可以尝试获取ClassgetMethod,也就是公开的方法,再输出isAccessible,可以看到输出的其实也是false,其实因为accessible属性的语义并不是我们理解的访问权限,而是指是否进行安全检查,而安全监察是非常消耗资源的,所以反射提供了Accessible可选项,让开发者逃避安全检查。

使用forName动态加载类文件

在使用JDBC时要动态加载数据库驱动就是使用forName的方式进行加载,同时亦可以从外部配置文件中读取类的全路径字符串进行加载,在使用forName,被加载的类就会被加载到内存当中,只会加载类,并不会执行任何代码,而我们的数据库驱动就是利用static代码块来执行操作的,因为当类被加载到内存中时,会执行static代码块

动态加载不适合数组

如果forName要加载一个类,那它必须是一个类-——8中基本类型就排除在外.它们不是一个具体的类。

其次它必须具有可追溯的类路径。否则就会报ClassNotFoundException

数组是一个特殊类,在声明时可以定义为String[],但编译后会为不同的数组类型生成不同的类.

//加载一个String数组 Class.forName("[Ljava.lang.String;"); //加载一个long数组 Class.forName("[J"); 

以上代码只是把一个String类型的数组类和long类型的数组类加载到了内存中(如果内存中没有该类的话),并不能通过newInstance()方法生成一个实例对象,因为它没有定义数组的长度,Java中数组是定长的,没有长度的数组是不允许存在的.

Array数组反射类来动态加载:// 动态创建数组String[] strs = (String[]) Array.newInstance(String.class, 8); // 创建一个多维数组 int[][] ints = (int[][]) Array.newInstance(int.class,2,3);

Class.forName()Java程序运行时加载类的默认方法

Class.forName(String className):使用调用Class.forName()方法的类的类加载器加载加载类

Class.forName(String name, boolean initialize,ClassLoader loader):使用指定的类加载器加载类

Class.forName()加载类的过程
查看类是否已经加载:以ClassLoader和类全名作为keySystemDictionary 查询类是否存在。
2. 若没有加载则调用loader.loadClass(name)进行类型加载;