JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
目录
Reflection 反射
反射的定义
反射机制:
- 在程序运行时可以加载,探知,使用编译期间完全未知的类
- 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个加载的类,都能够知道这个类的所有属性和方法;同时任意一个对象也都能够调用它的任意一个方法和属性。同时一个类只有一个反射对象,比如多次调用Class.forName得到的也是相同对象
形象的说:
使用以下反射代码来获取一个类的对象 Class c = Class.forName("com.company.project.ClassName"); 当加载完成后,堆内存中就产生了一个ClassName类型的对象,通过这个对象我们就可以看到类的结构,这个就时反射
反射的作用
- 动态加载类、动态获取类的信息(属性、方法、构造器)
- 动态构造对象
- 动态调用类和对象的任意方法、构造器
- 动态调用和处理属性
- 获取泛型信息
- 处理注解
Class类
public final class Class
Class类的类表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注解是一种接口。 每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。 原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象。Class没有公共构造函数。 相反, Class对象由Java虚拟机自动构建,因为加载了类,并且通过调用类加载器中的defineClass方法。
简单的说就是:
- Class类十分特殊,用来表示java中类型 (class/interface/enum/annotation/primitive type/void)本身。
- Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个 Class对象
- JVM来创建Class对象,当加载器(class loader)的defineClass()被 JVM调用,JVM 便自动产生一个Class 对象
- Class类是Reflection反射的基础,任何要动态加载,运行的类,都必须先获得对于的Class对象
Class类的对象获取
- .getClass()方法
- Class.forName()方法–使用最多
- .class语法
package JavaCore.Reflection; /******************************************************************************* * @Description: 获取Class对象 * @Aha-eureka: Class类是Reflection反射的基础,任何要动态加载,运行的类,都必须先获得对于的Class对象 ******************************************************************************/ public class Reflection_GetClass { public static void main( String[] args ) throws ClassNotFoundException { String path = "JavaCore.Reflection.User_Demo"; //获取Class的几种方式 { //一个类只有一个Class对象 Class clazz1 = Class.forName(path); Class clazz11=Class.forName(path); System.out.println(clazz1.hashCode());//相同hashcode System.out.println(clazz11.hashCode());//相同hashcode } { User_Demo user_demo = new User_Demo(); Class clazz2 = user_demo.getClass(); Class clazz3 = User_Demo.class; System.out.println(clazz2 == clazz3);//true } } }
获取基本数据类型的Class对象
Class类可以直接获取基本数据类型的Class对象,但是在数组中,多维的数组就是不同的class对象,不同类型的数组也是不同的class对象
int[] arr0 = new int[5]; int[] arr01 = new int[15]; long[] arr02 = new long[15]; int[][] arr1 = new int[5][5]; System.out.println(arr0.getClass() + " " + arr0.getClass().hashCode());//class [I 920011586 System.out.println(arr01.getClass() + " " + arr01.getClass().hashCode());//class [I 920011586 System.out.println(arr02.getClass() + " " + arr02.getClass().hashCode());//class [J 2017354584 System.out.println(arr1.getClass() + " " + arr1.getClass().hashCode());//class [[I 2017354584
Class API
方法 | 含义 | 备注 |
---|---|---|
public String getName() | 返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String | 返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String |
public Field getField(String name) | 获取指定属性名的属性 | 注意只能获得public属性 |
public Field[] getFields() | 获取所有的公共方法的属性数组 | |
public Field getDeclaredField(String name) | 返回指定的名字的属性包括私有属性 | |
public Field[] getDeclaredFields() | 返回所有的属性数组包括私有的 | |
同理…Method/Constructor | ||
同理…DeclaredMethods/Constructors | ||
public Mehod getDeclaredMethod(String name,Class<?>…parameterTypes | 返回指定参数类型的方法,Class<?>…parameterTypes可变参数参入的是对于类型的Class类 | 针对方法的多态性(重载)进行针对的获取,即对于方法有传参则必须传入对于的Class对象 |
public Constructor | 获取指定传参的构造器 |
package JavaCore.Reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Reflection_Dynamic { static Class clazz; static{ String path = "JavaCore.Reflection.User_Demo"; try { clazz=Class.forName(path); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void useReflection() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //这里newInstance实际就是在调用对于类的无参构造器,如果对于的类没有无参构造器则会抛出InstantiationException User_Demo demo = (User_Demo) clazz.newInstance(); //使用特定的构造器新建对象 Constructor constructor = User_Demo.class.getDeclaredConstructor(String.class, int.class); User_Demo demo1 = (User_Demo) constructor.newInstance("Swagger", 26); System.out.println(demo1); /** * 使用反射调用普通方法 * 1.先获取到Class对象,并获取一个新的对象A * 2.然后通过Class对象获得方法对象Method * 3.使用Method.invoke(Object A,args...)方法调用类的方法 */ User_Demo A = (User_Demo) clazz.newInstance(); Method method = clazz.getDeclaredMethod("setName", String.class); method.setAccessible(true); method.invoke(A, "Swagger1"); System.out.println(A); /** * 通过反射操作属性 * */ User_Demo fDemo = (User_Demo) clazz.newInstance(); Field f = clazz.getDeclaredField("name"); f.setAccessible(true);//使用这个setAccessible(true)来设置可以访问私有方法和属性 f.set(fDemo, "Ranger");// System.out.println(fDemo); } }
反射操作泛型
Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据 的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部 擦除。
为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class 类中的类型但是又和原始类型齐名的类型
反射操作注解
public static void main(String[] args) { try { Class clazz = Class.forName("类的相对路径和类名"); //获得类的所有有效注解 Annotation[] annotations=clazz.getAnnotations(); for (Annotation a : annotations) { System.out.println(a); } //获得类的指定的注解SxtTable就是一个注解 SxtTable st = (SxtTable) clazz.getAnnotation(SxtTable.class); System.out.println(st.value()); //获得类的属性的注解,columnName(),type(),length()就是SxtTable第一的注解方法或者属性,因为注解就是一种特殊的接口,他的属性值就是调用其方法 Field f = clazz.getDeclaredField("studentName"); SxtField sxtField = f.getAnnotation(SxtField.class); System.out.println(sxtField.columnName()+"--"+sxtField.type()+"--"+sxtField.length()); //根据获得的表名、字段的信息,拼出DDL语句,然后,使用JDBC执行这个SQL,在数据库中生成相关的表 } catch (Exception e) { e.printStackTrace(); } }
反射的性能问题
反射访问相对正常的访问大概要慢30倍
setAccessible
setAccessible设为true:取消反射时的对象安全检查,false则进行安全检查;
当禁止安全检查时就能:
1.访问私有属性
2.同时也能提高反射的运行速度
如果setAccessible(true)禁用安全检查则能将反射访问效率提升4倍,所以当频繁的调用反射时可以禁用安全检查来提升效率