反射
动态语言是一类运行时可以改变自身结构的语言,在运行中新的函数、对象、甚至代码可以被引进。
Java 是一门静态语言,但是由于 Java 拥有反射的特性,使之成为了“准动态语言”。
反射机制允许程序在运行期间借助 Reflection API 取得任何内的内部信息,并能够直接操作任意对象内部属性及方法。
类加载完成后,在堆内存方法区生成一个 Class 类型的对象(一个类只有一个 Class 对象),该对象包含了完整的类的结构信息
反射的优缺点
关于优缺点直接用代码说说最直观的
优点:代码简洁,提高代码复用率,增加了程序的灵活性,避免程序写死。
定义一个接口
/** * 一个格式工厂,提供图片转换为jpg格式的接口 * @author openshell * @since 2020-04-04 */ interface FactoryOfFormat { void toJpg(); }
创建两个实现类:
/** * png类,实现格式工厂接口 * @author openshell * @since 2020-04-04 */ class Png implements FactoryOfFormat { @Override public void toJpg() { System.out.println("png format to jpg!"); } } /** * psd类,实现格式工厂接口 * @author openshell * @since 2020-04-04 */ class Psd implements FactoryOfFormat { @Override public void toJpg() { System.out.println("psd format to jpg"); } }
测试类:
/** * <p> *创建测试类,提供两种获取实例的方法 * 若使用getInstance每次新增加一个图片格式,就需要在该代码中新增一个判断 * 若使用getInstanceByReflection,每次只需要传入对应的key值,通过反射可以很方便的话获取到其Class对象, * 并通过newInstance方法获取到对应的实例 * </p> * * @author openshell * @since 2020-04-04 */ public class TestReflection { public static void main(String[] args) { FactoryOfFormat png = getInstance("Png"); assert png != null; png.toJpg(); FactoryOfFormat png1 = getInstanceByReflection("Png"); png1.toJpg(); } public static FactoryOfFormat getInstance(String key) { if (StringUtils.equals(key, "Png")) { return new Png(); } else if (StringUtils.equals(key, "Psd")) { return new Psd(); } return null; } public static FactoryOfFormat getInstanceByReflection(String key) { Class clazz; FactoryOfFormat factoryOfFormat = null; try { clazz = Class.forName("com.cqz.study.reflection." + key); factoryOfFormat = (FactoryOfFormat) clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } return factoryOfFormat; } }
缺点:相比直接调用性能有较大幅度的下降,但若在实际应用中只是偶尔使用反射,其性能可以忽略;反射破坏了 Java 封装的特性,会影响代码安全性
下面测试以下反射的性能影响:
- 创建一个实体类
/** * 实体类 */ @Data class User{ private Long id; private String name; private Integer age; }
- 创建测试类,编写测试方法
/** * <p> * test01为直接调用速度最快 * test02通过反射调用速度最慢 * test03关闭了Java访问权限检查,速度快于test02,慢于test01 * </p> * * @author openshell * @since 2020-04-03 */ public class PerformanceTest { public static void test01() { User user = new User(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { user.getName(); } long endTime = System.currentTimeMillis(); System.out.println("test01直接调用,耗时:" + (endTime - startTime) + "ms"); } public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class clazz = user.getClass(); Method getName = clazz.getMethod("getName", null); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getName.invoke(user, null); } long endTime = System.currentTimeMillis(); System.out.println("test02通过反射调用,耗时:" + (endTime - startTime) + "ms"); } public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class clazz = user.getClass(); Method getName = clazz.getMethod("getName", null); getName.setAccessible(true); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getName.invoke(user, null); } long endTime = System.currentTimeMillis(); System.out.println("test03通过反射调用(关闭了Java访问权限检查),耗时:" + (endTime - startTime) + "ms"); } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { test01(); test02(); test03(); } }
获取 Class 类的方法
- 通过类名获取
Class clazz=User.class
; - 通过实例获取
Class clazz = user.getClass();
- 通过类路径获取,该方法可能抛出 ClassNotFoundException
Class clazz = Class.forName("com.cqz.study.reflection")
- 内置基本数据类型可以直接用类名。Type
- ClassLoader 获取
类加载器
加载 → 链接 → 初始化
双亲委派机制——保证安全性
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { //获取系统类的加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //获取系统加载器的父类加载器→扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); //获取引导类加载器,这一层用C++编写,是Jvm自带的类加载器,负责Java平台核心库,该加载器无法直接获取,下面代码返回null ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //获取我们自己编写的类,用的什么加载器,可以看到是sun.misc.Launcher$AppClassLoader@18b4aac2 Class<User> userClass = User.class; ClassLoader userClassLoader = userClass.getClassLoader(); System.out.println(userClassLoader); //下面代码返回null,证明了JDK自带类是由引导类加载器加载的,Java无法获取到这一层 Class<?> stringClazz = Class.forName("java.lang.Object"); ClassLoader stringClassLoaders = stringClazz.getClassLoader(); System.out.println(stringClassLoaders); } }
反射操纵数据
- 创建两个注解
/** * 应用与表名的注解 * * @author openshell */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableAnnotation { String value(); } /** * 应用于字段的注解 * * @author openshell */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FiledAnnotation { String name(); String type(); int length(); }
- 创建数据库关系映射类(使用了 lombok),并使用注解。
@Data @AllArgsConstructor @NoArgsConstructor @ToString @ResponseBody @TableAnnotation("t_colleague") class Colleague { @FiledAnnotation(name = "c_number", type = "bigint", length = 8) private Long number; @FiledAnnotation(name = "c_name", type = "varchar", length = 8) private String name; @FiledAnnotation(name = "c_name", type = "int", length = 4) private int age; }
3.编写测试类
public class Action { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { //1.获取通过反射获取Colleague的Class对象 Class clazz = Class.forName("com.cqz.study.reflection.Colleague"); // Annotation[] annotations = clazz.getDeclaredAnnotations(); Annotation[] annotations = clazz.getAnnotations(); //2.获取类注解 for (Annotation annotation : annotations) { System.out.println(annotation); } //3. 获取类注解value的值 TableAnnotation tableAnnotation = (TableAnnotation) clazz.getAnnotation(TableAnnotation.class); String value=tableAnnotation.value(); System.out.println(value); //4.获取属性上的注解 getField(String name)只能获取public的字段,包括父类的; getDeclaredField(String name)只能获取自己声明的各种字段,包括public,protected,private。 Field field = clazz.getDeclaredField("name"); FiledAnnotation fieldAnnotation = field.getAnnotation(FiledAnnotation.class); System.out.println(fieldAnnotation.name()); System.out.println(fieldAnnotation.type()); System.out.println(fieldAnnotation.length()); } }
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于