java反射(Reflection)学习一——java.lang.Class类
2016-06-11 12:08
555 查看
反射(Reflection)是Java提供一种在运行时,动态操作类的方法。
在java中,无论是类(Class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type),void类型都包含一个特殊的实例对象,就是java.lang.Class类实例。
java.lang.Class类没有public的构造函数,java.lang.Class类实例只能有JVM自动产生,每次JVM加载Class的时候,JVM就会为其自动生成一个java.lang.Class类实例。
1. Object类有个getClass()方法,可以获取类的java.lang.Class实例。
2. 每个类有个public的静态变量class。即使是int,long,void这些内置类型也是有class静态变量的,但数组是个比较特殊的,只能通过getClass()方法获取Class变量。如果对数组变量进行.class操作,则会有Unknown Class编译错误。
java.lang.Class类提供了两个静态方法
java.lang.Class的forName通过传入一个类的全名,将类加载到虚拟机。这两个方法的区别就是,单参数forName方法默认是进行初始化的,会执行类的static区域,其实就是执行了forName(className, true,ClassLoader.getClassLoader(Reflection.getCallerClass()))。三参数的forName方法,可以指定类的加载器,以及是否初始化。
执行上面的代码,会有如下结果:
从上面结果可以看出,虽然我们定义了三个类DemoLoad1,DemoLoad2和DemoLoad3,但实际上,只有两个类被JVM加载了,即DemoLoad1和DemoLoad2。从执行结果里看,如果仅仅声明类的变量,不进行实际对象的赋值,JVM是不会真实加载类的,如DemoLoad3。单参数版本的forName函数,在类被加载后,即会立马执行类static区域;三参数版本的forName函数,则可以控制类static区域不执行。
Class.forName最常见的一个应用就是jdbc里,通过Class.forName(driverClass)加载不同的java数据库驱动。
java.lang.Class有个newInstance方法,可以生成新的对象。但使用newInstance方法生成对象,必须有默认构造函数,或者public,protected的无参构造函数。如果类只有private构造函数,则用newInstance时,会抛出IllegalAccessException异常;如果类只有有参构造函数,则会抛出InstantiationException异常。
虽然newInstance方法会抛出IllegalAccessException和InstantiationException异常,但不代表,这两种情况下就无法通过class创建对象了,后面会有其他方法解决这个问题。
上面这些描述类信息的类,java.lang.Class都提供了相应的获取成员函数。
在java中,无论是类(Class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type),void类型都包含一个特殊的实例对象,就是java.lang.Class类实例。
java.lang.Class类没有public的构造函数,java.lang.Class类实例只能有JVM自动产生,每次JVM加载Class的时候,JVM就会为其自动生成一个java.lang.Class类实例。
获取java.lang.Class类实例
在java中,可以有两个方法获取类的Class实例。1. Object类有个getClass()方法,可以获取类的java.lang.Class实例。
2. 每个类有个public的静态变量class。即使是int,long,void这些内置类型也是有class静态变量的,但数组是个比较特殊的,只能通过getClass()方法获取Class变量。如果对数组变量进行.class操作,则会有Unknown Class编译错误。
public class ClassTest { public static void main(String[] args) { Class<ClassTest> testClass1 = ClassTest.class; System.out.println("Class name:"+testClass1.getName()); //Class name:com.shuanghu.learn.reflection.ClassTest Class<ClassTest> testClass2 = ClassTest.class; System.out.println("Class name:"+testClass2.getName()); //Class name:com.shuanghu.learn.reflection.ClassTest int [] arr = new int[2]; System.out.println("Class name:"+arr.getClass()); //Class name:class [I //arr.class; //Unknown Class } }
java.lang.Class的使用
判断类和对象的类型
通过java.lang.Class类可以判断一个类的类型。主要可以判断一个类是否是内部类(member class),局部类(local class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type)等类型。java.lang.Class提供了很多成员函数来支持这些判断。import java.lang.annotation.ElementType; import java.lang.annotation.Target; enum TestEnum{ } interface TestInterface{ } @Target(ElementType.METHOD) @interface TestAnnotation { String value() default ""; } public class ClassTest { class InnerClass{ } public static void main(String[] args) { class LocalClass{ } System.out.println("是否枚举(enum):" + TestEnum.class.isEnum()); System.out.println("是否接口(interface):" + TestInterface.class.isInterface()); System.out.println("是否内置类型(Primitive type):" + int.class.isPrimitive()); System.out.println("是否是内部类:" + InnerClass.class.isMemberClass()); System.out.println("是否local类:" + LocalClass.class.isLocalClass()); System.out.println("是否是注解:" + TestAnnotation.class.isAnnotation()); int [] intArr = new int[2]; //数组特殊,只支持getClass()获取Class,不支持.class这种静态变量的方式 System.out.println("是否数组:" + intArr.getClass().isAnnotation()); } }
加载类
通过java.lang.Class类可以加载类。在Java中,只有在需要使用一个类的时候,这个类才会被加载。所以,不是所以定义了的类,都会被JVM加载。java.lang.Class类提供了两个静态方法
public static Class<?> forName(String name) throws ClassNotFoundException; public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException;
java.lang.Class的forName通过传入一个类的全名,将类加载到虚拟机。这两个方法的区别就是,单参数forName方法默认是进行初始化的,会执行类的static区域,其实就是执行了forName(className, true,ClassLoader.getClassLoader(Reflection.getCallerClass()))。三参数的forName方法,可以指定类的加载器,以及是否初始化。
package com.shuanghu.learn.reflection; class DemoLoad1{ static { System.out.println("This is DemoLoad1 class."); } } class DemoLoad2{ static { System.out.println("This is DemoLoad2 class."); } } class DemoLoad3{ static { System.out.println("This is DemoLoad3 class."); } } public class ClassTest { public static void test() throws ClassNotFoundException { DemoLoad1 c1 = null; DemoLoad2 c2 = null; DemoLoad3 c3 = null; System.out.println("Begin load class DemoLoad1..."); Class.forName("com.shuanghu.learn.reflection.DemoLoad1"); Class.forName("com.shuanghu.learn.reflection.DemoLoad2", false, Thread.currentThread().getContextClassLoader()); System.out.println("Begin init class DemoLoad2..."); new DemoLoad2(); } public static void main(String[] args) throws ClassNotFoundException { test(); } }
执行上面的代码,会有如下结果:
Begin load class DemoLoad1... This is DemoLoad1 class. Begin init class DemoLoad2... This is DemoLoad2 class.
从上面结果可以看出,虽然我们定义了三个类DemoLoad1,DemoLoad2和DemoLoad3,但实际上,只有两个类被JVM加载了,即DemoLoad1和DemoLoad2。从执行结果里看,如果仅仅声明类的变量,不进行实际对象的赋值,JVM是不会真实加载类的,如DemoLoad3。单参数版本的forName函数,在类被加载后,即会立马执行类static区域;三参数版本的forName函数,则可以控制类static区域不执行。
Class.forName最常见的一个应用就是jdbc里,通过Class.forName(driverClass)加载不同的java数据库驱动。
通过java.lang.Class实例,可以创建对象
java中创建对象,最常见的方式就是使用new关键字创建。但new并不是唯一的方式。java.lang.Class有个newInstance方法,可以生成新的对象。但使用newInstance方法生成对象,必须有默认构造函数,或者public,protected的无参构造函数。如果类只有private构造函数,则用newInstance时,会抛出IllegalAccessException异常;如果类只有有参构造函数,则会抛出InstantiationException异常。
class DemoClass1{ protected DemoClass1(){ System.out.println("Demo class1 is constructing"); } } class DemoClass2{ private DemoClass2(){ System.out.println("Demo class2 is constructing"); } } class DemoClass3{ private DemoClass3(String param){ System.out.println("Demo class3 is constructing.Param:"+param); } } public class ClassTest { public static void main(String[] args) throws IllegalAccessException, InstantiationException { DemoClass1.class.newInstance(); DemoClass2.class.newInstance(); DemoClass3.class.newInstance(); } }
虽然newInstance方法会抛出IllegalAccessException和InstantiationException异常,但不代表,这两种情况下就无法通过class创建对象了,后面会有其他方法解决这个问题。
通过java.lang.Class实例,获取类的更详细信息。
仅仅通过java.lang.Class类是无法描述java类的全部信息的。所以,java的java.lang.reflect包里,提供了更多的类。类名称 | 作用 |
---|---|
java.lang.reflect.Array | 描述java的array数组,可以动态访问和新建数据 |
java.lang.reflect.Constructor | 描述java中类的构造函数 |
java.lang.reflect.Field | 描述java类的成员变量 |
java.lang.reflect.Method | 描述java类的方法 |
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序