Java基础篇:反射入门

    技术2022-07-20  83

    简介

    反射能干什么?你只需要给我一个class,我就能知道类的全部信息(类名称、父类、继承的接口、方法、属性、甚至实例化并赋值),那么下面就分3步,让你了解反射知识!

    第一步:我们顺便创建一个类。第二步:我们通过3种方式获得上一步的类的class。第三步:我们通过class干下面的事情 实例化对象获得类的信息,继承的接口、构造方法、成员变量、方法调用类的方法 第四步:了解反射的应用场景

    创建一个类

    这次我们准备用了一个Customer类,我们把它当成反射的对象,我们希望通过反射获取它的全部信息,我们先来看看这个类吧!

    Customer类(类路径下面需要): 有一个无参数构造方法和一个private的构造方法(注意,下面要用)为了方便后面测试效果,实现了Person接口 package com.bridge.security.util.bean; /** * @创建人 zhangtaiyuan * @创建时间 2019/7/2 * @描述 */ public class Customer implements Person{ private String name; private String email; private int age; public Customer() { } private Customer(String name, String email, int age) { this.name = name; this.email = email; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Customer{" + "name='" + name + '\'' + ", email='" + email + '\'' + ", age='" + age + '\'' + '}'; } } 什么也没做的Person接口 package com.bridge.security.util.bean; public interface Person { }

    上面准备好了,我们就可以通过反射获取Customer类信息了

    获得Class

    获得对象信息之前,我们必须获得Class,然后从Class中获取类对应的信息,获取Class有3种方法,如下:

    通过实体类获得 Customer customer = new Customer() ; // 已经存在有指定类的实例化对象 Class<? extends Customer> cls = customer.getClass() ; System.out.println(cls.getName()); //输出结果:com.bridge.security.util.bean.Customer 直接从XXX.class获得 Class<? extends Customer> cls = Customer.class; System.out.println(cls.getName()); //输出结果:com.bridge.security.util.bean.Customer 从类的路径获得 Class<?> cls = Class.forName("com.bridge.security.util.bean.Customer") ; System.out.println(cls.getName());//输出结果:com.bridge.security.util.bean.Customer

    上面3个方式获得的Class都一样,最后一个通过传入String就能获得Class对象,是不是让我们想起了JDBC,哈哈

    通过Class获得各种属性

    通过Class实例化对象

    在JDK9之前:class.newInstance()(jDK9开始不建议使用这个方法了)在JDK9之后:clazz.getDeclaredConstructor().newInstance() 大部分公司还在用jdk8,所以例子用的9之前的方式: Class<?> cls = Class.forName("com.bridge.security.util.bean.Customer") ; Object obj = cls.newInstance() ; // 实例化对象,JDK 9后被废除了 //Object obj = cls.getDeclaredConstructor().newInstance() ; JDK 9之后才能用 System.out.println(obj); // 由于没有赋值:输出的都是null 输出结果:Customer{name='null', email='null', age='0'}

    PS: 看着上面,你是不是明白了,如果没有反射,那么工厂模式创建实现类时,就会出现很多判断语句,并且当有新的工厂的实现类时,你就得修改代码并且升级版本,所以,没有反射,就只能使用简单工厂模式的方式实现了。

    通过Class获取属性

    获得包路径
    获得包路径:public Package getPackage();//比下面多一个“package”获得包路径:public Package getPackageName(); Class<?> cls = Class.forName("com.bridge.security.util.bean.Customer") ; System.out.println(cls.getPackage()); // package com.bridge.security.util.bean System.out.println(cls.getPackageName()); // com.bridge.security.util.bean
    获得父类
    获得父类:public Class<? super T> getSuperclass() Class<?> cls = Class.forName("com.bridge.security.util.bean.Customer") ; Class superclass = cls.getSuperclass();// 返回的是class System.out.println(superclass);// class java.lang.Object
    获得接口信息
    获得接口:public Class<?>[] getInterfaces() Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; for (Class<?> inter:clazz.getInterfaces()){ //获得接口,是一个class数组 System.out.println(inter.getName()); //com.bridge.security.util.bean.Person }
    构造方法
    - 获取所有构造方法:public Constructor<?>[] getDeclaredConstructors() - 获取指定的构造方法:public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) - 获取所有**公开的**构造方法:public Constructor<?>[] getConstructors() - 获取**公开的**指定构造方法:public Constructor<T> getConstructor​(Class<?>... parameterTypes)

    下面是例子

    获得全部构造方法 Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; for (Constructor<?> constructor:clazz.getDeclaredConstructors()){ //获得所有构造方法,包括非公开的 Customer中有个private方法 System.out.println(constructor.getName() + Arrays.toString(constructor.getParameterTypes())); } /** * 下面是输出结果 * com.bridge.security.util.bean.Customer[] * com.bridge.security.util.bean.Customer[class java.lang.String, class java.lang.String, int] */ 获得指定构成方法 Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; System.out.println(clazz.getDeclaredConstructor().getName() + Arrays.toString(clazz.getDeclaredConstructor().getParameterTypes())); //下面是获得private 的够着方法 System.out.println(clazz.getDeclaredConstructor(String.class,String.class,int.class).getName() + Arrays.toString(clazz.getDeclaredConstructor(String.class,String.class,int.class).getParameterTypes())); /** * 输出结果: * com.bridge.security.util.bean.Customer[] * com.bridge.security.util.bean.Customer[class java.lang.String, class java.lang.String, int] */ 获得公开的(public)构造方法,下面就没有private构造方法了 Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; for (Constructor<?> constructor:clazz.getConstructors()){ //获得所有构造方法,包括非公开的 Customer中有个private方法 System.out.println(constructor.getName() + Arrays.toString(constructor.getParameterTypes())); } /** * 下面是输出结果 * com.bridge.security.util.bean.Customer[] */ 获得指定的公开的构造方法,如果尝试去获得private的构造方法,就会直接报错 Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; System.out.println(clazz.getConstructor().getName() + Arrays.toString(clazz.getConstructor().getParameterTypes())); //下面是获得private 的够着方法 System.out.println(clazz.getConstructor(String.class,String.class,int.class)); /** * 输出结果: * com.bridge.security.util.bean.Customer[] * Exception in thread "main" java.lang.NoSuchMethodException: com.bridge.security.util.bean.Customer.<init>(java.lang.String, java.lang.String, int) * at java.base/java.lang.Class.getConstructor0(Class.java:3322) * at java.base/java.lang.Class.getConstructor(Class.java:2108) * at com.bridge.security.util.bean.TestDemo.main(TestDemo.java:16) */
    获得成员变量
    获取全部成员(只有本类):public Field[] getDeclaredFields() throws SecurityException获取指定成员:public Field getDeclaredField​(String name) throws NoSuchFieldException,SecurityException获取公开的全部成员(接口+父类):public Field[] getFields() throws SecurityException获取公开的指定成员:public Field getField​(String name) throws NoSuchFieldException, SecurityException 下面是例子: Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; System.out.println("全部成员属性:" + Arrays.toString(clazz.getDeclaredFields())); System.out.println("指定成员属性:" + clazz.getDeclaredField("name")); System.out.println("全部公开成员属性:" + Arrays.toString(clazz.getFields())); System.out.println("指定公开成员属性:" + clazz.getField("name"));//如果获取address、age,会报错

    打印结果

    全部成员属性:[private java.lang.String com.bridge.security.util.bean.Customer.name, private java.lang.String com.bridge.security.util.bean.Customer.email, private int com.bridge.security.util.bean.Customer.age] 指定成员属性:private java.lang.String com.bridge.security.util.bean.Customer.name 全部公开成员属性:[] Exception in thread "main" java.lang.NoSuchFieldException: name at java.base/java.lang.Class.getField(Class.java:1958) at com.bridge.security.util.bean.Demo.main(Demo.java:12)
    获得类的方法
    获取全部方法(包括父类):public Method[] getMethods() throws SecurityException获取指定方法(包括父类):public Method getMethod​(String name,class<?>... parameterTypes) throws NoSuchMethodException,SecurityException获取本类全部方法(包括私有化)public Method[] getDeclaredMethods() throws SecurityException获取本类指定方法(包括私有化)public Method getDeclaredMethod​(String name,Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; System.out.println("获取全部方法:" + Arrays.toString(clazz.getMethods())); //这里将打印父类(Object)的方法 System.out.println("获取指定方法:" + clazz.getMethod("getName")); System.out.println("获取本类全部方法:" + Arrays.toString(clazz.getDeclaredMethods())); System.out.println("获取本类指定方法:" + clazz.getDeclaredMethod("getName")); 结果,返回的是Method对象或者数组 获取全部方法:[public java.lang.String com.bridge.security.util.bean.Customer.toString(), public java.lang.String com.bridge.security.util.bean.Customer.getName(), public void com.bridge.security.util.bean.Customer.setName(java.lang.String), public java.lang.String com.bridge.security.util.bean.Customer.getEmail(), public void com.bridge.security.util.bean.Customer.setAge(int), public int com.bridge.security.util.bean.Customer.getAge(), public void com.bridge.security.util.bean.Customer.setEmail(java.lang.String), public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()] 获取指定方法:public java.lang.String com.bridge.security.util.bean.Customer.getName() 获取本类全部方法:[public java.lang.String com.bridge.security.util.bean.Customer.toString(), public java.lang.String com.bridge.security.util.bean.Customer.getName(), public void com.bridge.security.util.bean.Customer.setName(java.lang.String), public java.lang.String com.bridge.security.util.bean.Customer.getEmail(), public void com.bridge.security.util.bean.Customer.setAge(int), public int com.bridge.security.util.bean.Customer.getAge(), public void com.bridge.security.util.bean.Customer.setEmail(java.lang.String)] 获取本类指定方法:public java.lang.String com.bridge.security.util.bean.Customer.getName()

    注意

    另外还有一个获取本地或者匿名类的方法,没有做深入的研究了获得Method方法后,还可以获得方法的返回值、参数、Annotation等信息另外需要注意反射是不能获得方法的参数的名称的 Method met = clazz.getDeclaredMethod("getAddress"); Modifier.toString(met.getModifiers()) ; // 修饰符 met.getReturnType().getName();//返回类型名称 met.getName();//方法名称 met.getParameterTypes(); // 获取参数类型 met.getExceptionTypes() ; //获得异常值 met.getAnnotations();//Annotations注解
    创建类和调用方法
    有上面的基础知识,创建类并调用就非常简单了 Class<?> clazz = Class.forName("com.bridge.security.util.bean.Customer") ; Constructor constructor = clazz.getDeclaredConstructor(); Object obj = constructor.newInstance(); Method setName = clazz.getDeclaredMethod("setName", String.class) ; // 获取指定的方法 setName.invoke(obj, "张三") ; // 等价于:Person对象.setName(value); Method getName = clazz.getDeclaredMethod("getName") ; String name = (String)getName.invoke(obj) ; System.out.println("反射调用getName()方法获得值:" + name);

    了解反射的应用场景

    反射提供了另外一种设计思路,

    工厂模式:很多框架软件里都能看到它应用场景,比如:JDBC,另外,动态代理:充分利用了反射的技术,让开发人员减轻了不少代码工作量。
    Processed: 0.008, SQL: 9