浅谈java反射

目录

1.访问构造方法

2.访问成员变量

​编辑3.访问方法

 Annotation(注解)

 1.定义Annotation

2.访问Annotation信息


        什么是反射?比如jdbc连接数据库用的Class.forName("全路径")就是反射,Java反射机制非常的强大,可以访问已经被装载进JVM中的java对象的描述,实现访问、检测和修改描述java对象本身信息的功能。

        首先先知道怎么获取一个class对象,自己创建一个stu类,可以什么都不写,有这个类就可以:

        第一种:Class<Stu> clazz = Stu.class;


        第二种:Class<?> aClass = Class.forName("Stu");//完整类名带包名,用小数点分割


        第三种:Stu stu1 = new Stu(); stu1.getClass();

那么我们可以通过class对象得到些什么信息呢?

package com.lx;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @title: com.lx.Reflection
 * @Author 尤词
 * @Date: 2022/8/15 13:26
 * @Description: 反射
 * @Version 1.0
 */
public class Reflection {

    public String name;
    public String address;
    private String sex;
    private String tel;

    public class nbl{
        nbl(){
            System.out.println("内部类");
        }
    }

    private class nbl2{}

    public Reflection() {
        System.out.println("构造方法");
    }

    public Reflection(String s){
        System.out.println(s);
    }

    private Reflection(Integer i){
        System.out.println(i);
    }

    public void aaa(){
        System.out.println("aaa");
    }

    private void bbb(){
        System.out.println("bbb");
    }

    public static void main(String[] args) {
        Class<Reflection> c = Reflection.class;//得到一个class对象

        //包路径
        Package cp = c.getPackage();
        System.out.println("该类的存放路径为:" + cp);

        //类名称
        String cn = c.getName();
        System.out.println("该类的名称为:" + cn);

        //继承类
        Class<? super Reflection> cs = c.getSuperclass();
        System.out.println("该类继承的类为:" + cs);

        //实现接口
        Class<?>[] ci = c.getInterfaces();
        System.out.println("该类实现的所有的接口为:" + Arrays.toString(ci));

        //构造方法
        Constructor<?>[] cc1 = c.getConstructors();
        System.out.println("该类中所有范围修饰符为public的构造方法有:" + Arrays.toString(cc1));

        try {
            Constructor<Reflection> cc2 = c.getConstructor(String.class);
            System.out.println("该类中需要传入一个字符串类型的public的构造方法为:" + cc2);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        Constructor<?>[] cd1 = c.getDeclaredConstructors();
        System.out.println("该类所有的构造方法按声明顺序依次为:" + Arrays.toString(cd1));

        try {
            Constructor<Reflection> cd2 = c.getDeclaredConstructor(Integer.class);
            System.out.println("需要传入整数类型的构造方法有:" + cd2);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        //方法
        Method[] methods = c.getMethods();
        System.out.println("修饰符为public的所有方法为:" + Arrays.toString(methods));

        try {
            Method method = c.getMethod("aaa");
            System.out.println("修饰符为public方法名为aaa参数列表为空的方法:" + method);//这个只找修饰符是public的方法
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        Method[] declaredMethods = c.getDeclaredMethods();
        System.out.println("该类所有的方法按声明顺序为:" + Arrays.toString(declaredMethods));

        try {
            Method bbb = c.getDeclaredMethod("bbb");
            System.out.println("方法名为bbb参数列表为空的方法为:" + bbb);//这个就是无论范围修饰符是什么都找
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        //成员变量
        Field[] fields = c.getFields();
        System.out.println("获取所有被public修饰的成员变量" + Arrays.toString(fields));

        try {
            Field n = c.getField("name");
            System.out.println("\"权限为public属性名为name的成员变量为:\" = " + n);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        Field[] declaredFields = c.getDeclaredFields();
        System.out.println("所有的成员变量为:" + Arrays.toString(declaredFields));

        try {
            Field s = c.getDeclaredField("sex");
            System.out.println("属性名为sex的成员变量为:" + s);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        //内部类
        Class<?>[] cls = c.getClasses();
        System.out.println("公共的内部类有:" + Arrays.toString(cls));

        Class<?>[] dcls = c.getDeclaredClasses();
        System.out.println("所有的内部类有:" + Arrays.toString(dcls));

        //内部类的声明类
        Class<?> declaringClass = c.getDeclaringClass();
        System.out.println("这个类如果是内部类,输出它的成员类,否则返回null:" + declaringClass);

    }
}

 根据上面的练习,可以总结出:

        不加Declared的方法获取的都是public修饰的方法/变量等,加了Declared的方法获取的是所有方法/变量等

1.访问构造方法

isVarArgs()        查看该构造方法是否允许带有可变数量的参数,返回Boolean

getParameterTypes()        按声明顺序以Class数组的形式获得该构造方法的各个参数的类型

getExceptionTypes()        以Class数组的形式获得构造方法可能抛出的异常类型

newInstance()        根据参数去类中找对应的构造方法

setAccessible(Boolean)   设置是否允许private修饰的构造方法通过反射创建对象,默认false

getModifiers()        该构造方法采用修饰符的数量

通过getModifiers()方法得到的结果可以做一下判断

语法:Modifier.方法名(getModifiers()返回的int类型数据),还有一个toString方法在例子中演示

 

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * @title: Cons
 * @Author 尤词
 * @Date: 2022/8/16 19:26
 * @Description: 构造方法能干的事情
 * @Version 1.0
 */
class Example_01 {
    String s;
    int i, j, k;

    public Example_01() {
    }

    public Example_01(String s, int i) {
        this.s = s;
        this.i = i;
    }

    public Example_01(String... s) throws NumberFormatException {
        if (0 < s.length) {
            i = Integer.parseInt(s[0]);
        }
        if (1 < s.length) {
            j = Integer.parseInt(s[1]);
        }
        if (2 < s.length) {
            k = Integer.parseInt(s[2]);
        }
    }

    @Override
    public String toString() {
        return "s='" + s + '\'' +
                ", i=" + i +
                ", j=" + j +
                ", k=" + k;
    }
}

public class Cons {
    public static void main(String[] args) {
        Class<Example_01> c = Example_01.class;
        /*获得所有构造方法*/
        Constructor<?>[] dc = c.getDeclaredConstructors();
        /*遍历构造方法*/
        for (int i = 0; i < dc.length; i++) {
            int modifiers = dc[i].getModifiers();
            System.out.println("该构造方法采用修饰符的代号:" + modifiers);
            String s = Modifier.toString(modifiers);
            System.out.println("所有的修饰符号为:" + s);
            System.out.println("是否允许带有可变数量的参数:" + dc[i].isVarArgs());
            System.out.print("该构造方法的入口参数类型依次为:");
            /*获取所有参数类型*/
            Class<?>[] parameterTypes = dc[i].getParameterTypes();
            System.out.println(Arrays.toString(parameterTypes));
            System.out.print("可能抛出的异常类型为:");
            /*获取所有可能抛出异常信息类型*/
            Class<?>[] exceptionTypes = dc[i].getExceptionTypes();
            System.out.println(Arrays.toString(exceptionTypes));
            Example_01 ex = null;
            while (ex == null) {
                try {
                    /*如果该成员变量的访问权限为private,则抛出异常,即不允许访问*/
                    if (i == 2) ex = (Example_01) dc[i].newInstance();//通过执行默认没有参数的构造方法创建对象,等于new Example_01();
                        /*通过执行具有两个参数的构造方法创建对象*/
                    else if (i == 1) ex = (Example_01) dc[i].newInstance("7", 5);
                    else {
                        Object[] parameters = new Object[]{new String[]{"100", "200", "300"}};
                        /*通过执行具有可变数量参数的构造方法创建对象*/
                        ex = (Example_01) dc[i].newInstance(parameters);
                    }
                } catch (Exception e) {
                    System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法");
                    /*设置允许访问*/
                    dc[i].setAccessible(true);
                }
            }
            System.out.println(ex);
            System.out.println();
        }
    }
}

2.访问成员变量

setInt()/setFloat()/setBoolean()都有getter方法,作用为获取值

import java.lang.reflect.Field;

/**
 * @title: Fields
 * @Author 尤词
 * @Date: 2022/8/16 20:17
 * @Description: 获取成员变量的信息
 * @Version 1.0
 */

class Example_02 {
    int i;
    public float f;
    protected boolean b;
    private String s;
}

public class Fields {
    public static void main(String[] args) {
        Example_02 ex = new Example_02();
        Class<? extends Example_02> c = ex.getClass();
        /*获得所有成员变量*/
        Field[] d = c.getDeclaredFields();
        for (int i = 0; i < d.length; i++) {
            /*遍历成员变量*/
            Field ff = d[i];
            /*获得成员变量的名字*/
            System.out.println("变量名 = " + ff.getName());
            /*获取成员变量类型*/
            Class<?> type = ff.getType();
            System.out.println("类型 = " + type);
            boolean it = true;
            while (it) {
                /*如果该成员变量的访问权限为private,则抛出异常,即不允许访问*/
                try {
                    it = false;
                    System.out.println("变量修改前的值为: " + ff.get(ex));
                    /*判断变量类型*/
                    if (type.equals(int.class)){
                        System.out.println("利用setInt()修改变量的值");
                        ff.setInt(ex, 168);
                    } else if (type.equals(float.class)) {
                        System.out.println("利用setFloat()修改变量的值");
                        ff.setFloat(ex, 16.8f);
                    }else if (type.equals(boolean.class)){
                        System.out.println("利用setBoolean()修改变量的值");
                        ff.setBoolean(ex, true);
                    }else {
                        System.out.println("利用set()修改变量的值");
                        /*可以为各种类型的成员变量赋值*/
                        ff.set(ex, "mmm");
                    }
                    /*获得成员变量值*/
                    System.out.println("修改后的值为 " + ff.get(ex));
                } catch (IllegalAccessException e) {
                    System.out.println("在设置成员变量值时抛出异常,下面执行setAccessible()方法");
                    ff.setAccessible(true);//设置允许访问
                    it = true;
                }
            }
            System.out.println();
        }
    }
}

3.访问方法

getName():获得方法的名称

getParameterTypes():获得方法的参数列表

getReturnType():获得方法的返回值类型

getExceptionTypes():获得方法抛出的异常

invoke(类对象,方法的参数(如果有)):根据参数列表执行指定类对象中的方法

isVarArgs():查看方法是否允许传入可变数量的参数

getModifiers():该构造方法采用修饰符的数量

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @title: Method
 * @Author 尤词
 * @Date: 2022/8/18 20:09
 * @Description: 对方法的一些操作
 * @Version 1.0
 */

class Example {
    static void staticMethod() {
        System.out.println("执行静态方法");
    }

    public int publicMethod(int i) {
        System.out.println("执行公共有参方法");
        return i * 100;
    }

    protected int protectedMethod(String s, int i) throws NumberFormatException {
        System.out.println("执行protected修饰的两个参数方法");
        return Integer.parseInt(s + i);
    }

    private String privateMethod(String... s) {
        System.out.println("执行私有方法");
        StringBuffer sb = new StringBuffer();
        for (String ss : s) {
            sb.append(ss);
        }
        return Arrays.toString(s);
    }
}

public class MethodTest {
    public static void main(String[] args) {
        Example e = new Example();
        Class<? extends Example> c = e.getClass();
        /*获得所有方法*/
        java.lang.reflect.Method[] declaredMethods = c.getDeclaredMethods();
        /*遍历方法*/
        for (int i = 0; i < declaredMethods.length; i++) {
            /*得到每一个方法*/
            Method m = declaredMethods[i];
            /*获取方法的名称*/
            System.out.println("方法名为: " + m.getName());
            /*判断是否可以传入可变数量参数*/
            System.out.println("是否允许传入可变数量参数" + m.isVarArgs());
            /*获取参数列表数据类型*/
            Class<?>[] parameterTypes = m.getParameterTypes();
            /*遍历参数列表*/
            System.out.println("参数列表类型为:");
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.print("\t" + parameterTypes[j]);
            }
            /*获得方法返回值类型*/
            Class<?> returnType = m.getReturnType();
            System.out.println("返回值类型为:" + returnType);
            /*抛出的异常*/
            Class<?>[] exceptionTypes = m.getExceptionTypes();
            System.out.println("可能抛出的异常有:" + Arrays.toString(exceptionTypes));
            boolean it = true;
            while (it) {
                /*如果该方法访问权限为private则抛出异常,即不允许访问*/
                try {
                    it = false;
                    if ("staticMethod".equals(m.getName()))
                        m.invoke(e);
                    else if ("publicMethod".equals(m.getName())) m.invoke(e, 168);
                    else if ("protectedMethod".equals(m.getName())) m.invoke(e, "7", 5);
                    else if ("privateMethod".equals(m.getName())) {
                        Object[] objects = {new String[]{"Q", "W", "E"}};
                        System.out.println("返回值为:" + m.invoke(e,objects));
                    }
                } catch (Exception ex) {
                    System.out.println("在执行方法时抛出异常,下面执行setAccessible()方法");
                    /*设置允许访问*/
                    m.setAccessible(true);
                    it = true;
                }
            }
            System.out.println();
        }
    }
}

 Annotation(注解)

JDK1.5开始增加了Annotation功能,该功能可用于类,构造方法、成员变量、方法、参数等的声明中。该功能并不影响程序的运行,但是会对编译器警告等辅助工具产生影响。

 1.定义Annotation

 在定义Annotation类型时,也需要用到来定义接口的interface关键字,但需要在关键字前加"@"符号,即定义Annotation类型的关键字为@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。

package annotation;

/**
 * @title: A1
 * @Author 尤词
 * @Date: 2022/8/18 21:10
 * @Description: 如何定义一个annotation类型
 * @Version 1.0
 */
public @interface A1 {

}

 上面定义的annotation类型@A1未包含任何成员,这样的annotation类型被称为marker annotation(标记注解)。

public @interface A1 {
    /**
     * String:成员类型,可用的成员类型为:String Class primitive enumerated和annotation
     * value:成员名称,如果在所定义的Annotation类型中只有一个成员,通常将成员命名为value
     *
     */
    String value();
}
package annotation;

/**
 * @title: A2
 * @Author 尤词
 * @Date: 2022/8/18 21:16
 * @Description: 包含多成员的annotation
 * @Version 1.0
 */
public @interface A2 {
    /*定义成员的时候,也可以给出默认值*/
    String describe() default "默认值";
    Class<?> type() default void.class;
}

 在定义Annotation类型时,还可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类。如果未设置@Target,则表示适用于所有程序元素。枚举类ElementType中的枚举常量用来设置@Target。

ElemenType中的枚举常量
枚举常量说明
ANNOTATION_TYPE表示用于Annotation类型
TYPE表示用于类、接口和枚举以及Annotation类型
CONSTRUCTOR表示用于构造方法
FIELD表示用于成员变量和枚举变量
METHOD表示用于方法
PARAMETER表示用于参数
LOCAL_VARIABLE表示用于局部变量
PACKAGE表示用于包

 通过Annotation类型@Retention可以设置Annotation的有效范围。枚举类RetentionPolicy中的枚举常量用来设置@Retention,如果没有设置,Annotation的有效范围为枚举常量CLASS表示的范围

RetentionPolicy中的枚举常量
枚举常量说明
SOURCE表示不hi编译Annotation到类文件中,有效范围最小
CLASS表示编译Annotation到类文件中,但是在运行时不加载Annotation到JVN中
RUNTIME表示在运行时加载Annotation到JVM中,有效范围最大

首先定义一个用来注释构造方法的Annotation类型@Constructor_Annotation,有效范围为在运行时加载Annotation到JVM中 

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @title: A3
 * @Author 尤词
 * @Date: 2022/8/18 21:39
 * @Version 1.0
 */

@Target(ElementType.CONSTRUCTOR)
/*用于构造方法*/
@Retention(value = RetentionPolicy.RUNTIME)
//在运行时加载到JVM中
public @interface A3 {
    String value() default "默认构造方法";
}

 再定义一个用来注释字段、方法、和参数的Annotation类型,有效范围为在运行时加载到JVM

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @title: A4
 * @Author 尤词
 * @Date: 2022/8/18 21:42
 * @Version 1.0
 */
/*用于字段、方法、参数*/
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface A4 {
    String describe();
    /*定义一个具有默认值的Class型成员*/
    Class<?> type() default void.class;
}

最后编写一个实体类,运用前面定义的两个注解 

package annotation;

/**
 * @title: Tests
 * @Author 尤词
 * @Date: 2022/8/18 21:46
 * @Description: 原始的实体类
 * @Version 1.0
 */
public class Tests {
    int id;
    String name;

    public Tests() {
    }

    public Tests(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 下面时加了注释的类,a3用于构造方法,a4用于其它(起名要规范,不要学我!!!)

package annotation;

/**
 * @title: Tests
 * @Author 尤词
 * @Date: 2022/8/18 21:46
 * @Description: 测试类
 * @Version 2.0
 */
public class Tests {
    /*
     * 对字段的注释
     */
    @A4(describe = "编号",type = int.class)
    int id;
    @A4(describe = "姓名",type = String.class)
    String name;

    /*
     * 对构造方法注解
     * 采用默认值注释构造方法
     */
    @A3()
    public Tests() {
    }

    @A3("有参构造方法")
    public Tests(@A4(describe = "编号",type = int.class)int id, @A4(describe = "姓名",type = String.class)String name) {
        this.id = id;
        this.name = name;
    }

    /*
     * 注释方法
     */
    @A4(describe = "获得编号",type = int.class)
    public int getId() {
        return id;
    }

    @A4(describe = "设置编号",type = int.class)
    public void setId(int id) {
        this.id = id;
    }

    @A4(describe = "获得名字",type = String.class)
    public String getName() {
        return name;
    }

    @A4(describe = "设置名字",type = String.class)
    public void setName(String name) {
        this.name = name;
    }
}

2.访问Annotation信息

        如果在定义Annotation类型时将@Retention设置为RetentionPolicy.RUNTIME,那么在运行程序时通过反射就可以获得相关的Annotation信息,如获取构造方法、字段和方法的Annotation信息。

        类Constructor、Field和Method均继承了AccessibleObject类,在AccessibleObject中定义了三个关于Annotation的方法,其中isAnnotationPresident(Class<? extends Annotation> annotationClass)用来查看是否添加了指定类型的Annotation,如果是则返回true,否则返回false;方法getAnnotation(Class<T> annotationClass)用来获得指定类型的Annotation,如果存在则返回相应的对象,否则返回null;方法getAnnotation()用来获得所有的Annotation,该方法返回一个Annotation数组。

        在类Constructor和Method中还定义了方法getParameterAnnotation(),用来获得为所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明的顺序相同,如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。

package annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @title: ATest
 * @Author 尤词
 * @Date: 2022/8/18 22:07
 * @Description: 对上述类操作
 * @Version 1.0
 */
public class ATest {
    public static void main(String[] args) {
        System.out.println("----构造方法的描述如下----");
        Tests t = new Tests();
        Class<? extends Tests> c = t.getClass();
        /*获得所有构造方法*/
        Constructor<?>[] d = c.getDeclaredConstructors();
        /*遍历构造方法*/
        for (int i = 0; i < d.length; i++) {
            Constructor<?> constructor = d[i];
            /*查看是否具有指定类型的注释*/
            if (constructor.isAnnotationPresent(A3.class)) {
                //获得指定类型的注释
                A3 ca = constructor.getAnnotation(A3.class);
                System.out.println(ca.value());
            }
            //获得参数的注释
            Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
            for (int j = 0; j < parameterAnnotations.length; j++) {
                /*获得指定参数注释的长度*/
                int length = parameterAnnotations[j].length;
                if (length == 0) {
                    System.out.println("未添加Annotation参数");
                } else {
                    for (int k = 0; k < length; k++) {
                        //获得参数的注释
                        A4 pa = (A4) parameterAnnotations[j][k];
                        System.out.print("    " + pa.describe());
                        System.out.print("    " + pa.type());
                    }
                }
                System.out.println();
            }
        }
        System.out.println("----字段描述如下----");
        Field[] ff = c.getDeclaredFields();
        for (Field f : ff) {
            //查看是否具有指定类型注释
            if (f.isAnnotationPresent(A4.class)) {
                //获得指定类型注释
                A4 fa = f.getAnnotation(A4.class);
                System.out.print("    " + fa.describe());
                System.out.print("    " + fa.type());
            }
        }
        System.out.println("\n----方法描述如下----");
        Method[] mm = c.getDeclaredMethods();
        for (Method m : mm) {
            //查看是否具有指定类型注释
            if (m.isAnnotationPresent(A4.class)) {
                //获得指定类型的注释
                A4 an = m.getAnnotation(A4.class);
                System.out.print("    " + an.describe());
                System.out.println("    " + an.type());
            }
            //获得参数注释
            Annotation[][] parameters = m.getParameterAnnotations();
            for (int l = 0; l < parameters.length; l++) {
                int length = parameters[l].length;
                if (length == 0) {
                    System.out.println("未添加Annotation参数");
                } else {
                    for (int z = 0; z < length; z++) {
                        //获取指定类型的注释
                        A4 pa = (A4) parameters[l][z];
                        System.out.print("    " + pa.describe());
                        System.out.println("    " + pa.type());
                    }
                }
            }

        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尢词

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值