注重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类中提供了很多getDeclaredXXX和getXXX的方法:
getXXX的方式是获取所有公共的(public)级别的,包括从父类继承的方法。
而getDeclaredXXX的方式是获取所有的,包括公共的(public),私有的(private),不受限与访问权限。
public static void main(String[] args) {
Class<User> 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<User> cls = User.class;
//创建对象
User user = cls.newInstance();
//获取test方法
Method method = cls.getDeclaredMethod("test");
//检查Accessible属性
if(!method.isAccessible()){
method.setAccessible(true);
}
method.invoke(user);
}
可以尝试获取Class的getMethod,也就是公开的方法,再输出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和类全名作为key去SystemDictionary 查询类是否存在。
2. 若没有加载则调用loader.loadClass(name)进行类型加载;