反射机制
1 反射概述 反射机制的作用
通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
Class对象 Class对象的作用
每个类都有一个Class 对象,包含了与类有关的信息,代表整个字节码。代表一个类型,代表整个类。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class 对象。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
获取Class对象的方法
Class.forName(“完整类名带包名”) 静态方法
对象.getClass()
任何类型.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.zking.jee07.reflect; public class Test { public static void main (String[] args) throws Exception { Class<Student> stuClazz =(Class<Student>)Class.forName("com.zking.jee07.reflect.Student" ); Class<Student> stuClazz01 =Student.class; Student stu = new Student (); Class<Student> stuClazz02 =(Class<Student>)stu.getClass(); } }
反射机制的使用 java.lang.reflect.*;存储与反射相关的类
类
含义
java.lang.Class
代表整个字节码。代表一个类型,代表整个类。
java.lang.reflect.Method
代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor
代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Field
代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。
Class 和 java.lang.reflect.* 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
Constructor :可以用 Constructor 的 newInstance() 创建新的对象。
反射机制的优点
可扩展性 :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
类浏览器和可视化开发环境 :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
调试器和测试工具 : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
反射的缺点 尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。
性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
反射机制的使用
1 2 3 4 5 6 7 8 9 10 11 class ReflectTest02 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class c = Class.forName("javase.reflectBean.User" ); Object obj = c.newInstance(); System.out.println(obj); } }
使得一个类的静态代码执行,而其他代码不执行。例如JDBC中不需要创建实例而知识执行静态方法.这个方法的执行会导致类加载,类加载时,静态代码块执行。
2 反射机制实现 Class 中的方法
方法名
备注
public T newInstance()
创建对象
public String getName()
返回完整类名带包名
public String getSimpleName()
返回类名
public Field[] getFields()
返回类中public修饰的属性
public Field[] getDeclaredFields()
返回类中所有的属性
public Field getDeclaredField(String name)
根据属性名name获取指定的属性
public native int getModifiers()
获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Method[] getDeclaredMethods()
返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)
根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors()
返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)
根据方法形参获取指定的构造方法
—-
—-
public native Class<? super T> getSuperclass()
返回调用类的父类
public Class<?>[] getInterfaces()
返回调用类实现的接口集合
Field中的方法
方法名
备注
public String getName()
返回属性名
public int getModifiers()
获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getType()
以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value)
设置属性值
public Object get(Object obj)
读取属性值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class ReflectTest07 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { Student student = new Student (); student.no = 1111 ; System.out.println(student.no); Class studentClass = Class.forName("javase.reflectBean.Student" ); Object obj = studentClass.newInstance(); Field noField = studentClass.getDeclaredField("no" ); noField.set(obj, 22222 ); System.out.println(noField.get(obj)); }
set()方法不可以访问私有属性,需要打破封装,才可以。
public void setAccessible(boolean flag) 默认false,设置为true为打破封装
1 2 3 4 5 6 7 8 9 Field nameField = studentClass.getDeclaredField("name" );nameField.setAccessible(true ); nameField.set(obj, "xiaowu" ); System.out.println(nameField.get(obj));
Method中的方法
方法名
备注
public String getName()
返回方法名
public int getModifiers()
获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getReturnType()
以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes()
返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args)
调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class ReflectTest09 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class userServiceClass = Class.forName("java.lang.String" ); s.append(Modifier.toString(userServiceClass.getModifiers())); s.append(" class " ); s.append(userServiceClass.getSimpleName()); s.append(" {\n" ); Method[] methods = userServiceClass.getDeclaredMethods(); for (Method m : methods){ s.append("\t" ); s.append(Modifier.toString(m.getModifiers())); s.append(" " ); s.append(m.getReturnType().getSimpleName()); s.append(" " ); s.append(m.getName()); s.append("(" ); Class[] parameterTypes = m.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++){ s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ) s.append(", " ); } s.append(") {}\n" ); } s.append("}" ); System.out.println(s); } }
调用类中的方法Method
方法.invoke(对象, 实参);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class ReflectTest10 { public static void main (String[] args) throws Exception { UserService userService = new UserService (); System.out.println(userService.login("admin" , "123" ) ? "登入成功!" : "登入失败!" ); Class userServiceClass = Class.forName("javase.reflectBean.UserService" ); Object obj = userServiceClass.newInstance(); Method loginMethod = userServiceClass.getDeclaredMethod("login" , String.class, String.class); Object resValues = loginMethod.invoke(obj, "admin" , "123" ); System.out.println(resValues); } }
Constructor中的方法
方法名
备注
public String getName()
返回构造方法名
public int getModifiers()
获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?>[] getParameterTypes()
返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs)
创建对象【参数为创建对象的数据】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class ReflectTest11 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class vipClass = Class.forName("javase.reflectBean.Vip" ); s.append(Modifier.toString(vipClass.getModifiers())); s.append(" class " ); s.append(vipClass.getSimpleName()); s.append("{\n" ); Constructor[] constructors = vipClass.getDeclaredConstructors(); for (Constructor c : constructors){ s.append("\t" ); s.append(Modifier.toString(c.getModifiers())); s.append(" " ); s.append(vipClass.getSimpleName()); s.append("(" ); Class[] parameterTypes = c.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++){ s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ) s.append(", " ); } s.append("){}\n" ); } s.append("}" ); System.out.println(s); } }
调用Constructor方法创建对象
先获取到这个有参数的构造方法【用ClassgetDeclaredConstructor()方法获取】
调用构造方法new对象【用Constructor类的newInstance()方法new对象】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class ReflectTest12 { public static void main (String[] args) throws Exception { Vip vip1 = new Vip (); Vip vip2 = new Vip (123 , "zhangsan" , "2001-10-19" , false ); Class vipClass = Class.forName("javase.reflectBean.Vip" ); Object obj1 = vipClass.newInstance(); System.out.println(obj1); Constructor c1 = vipClass.getDeclaredConstructor(int .class, String.class, String.class, boolean .class); Object obj2 = c1.newInstance(321 , "lsi" , "1999-10-11" , true ); System.out.println(obj2); Constructor c2 = vipClass.getDeclaredConstructor(); Object obj3 = c2.newInstance(); System.out.println(obj3); } }
获取一个类的父类以及实现的接口 两个方法【Class类中的】
public native Class<? super T> getSuperclass()
public Class<?>[] getInterfaces()