java class 反射机制 学习

class 反射机制:
JAVA 的反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息及动态调用对象方法的功能称为 java 语言的反射机制。
框架与反射

  • 框架:半成品软件,可以在框架的基础上进行开发,而框架本身不能运行
  • 反射:反射是框架的灵魂,将类的各个组成部分封装为其他对象
  • 反射的好处:可以在程序运行的过程中去操作对象,字节码文件,不需要重新编译,提高程序扩展性,复用性,解耦

代码练习:

public class Test {

    public static void main(String[] args) throws Exception {
        // 获取一个Class
        Class<User> userClass=User.class;
        // getName 获取类的完整路径的名字
        System.out.println(userClass.getName());
        // 获取不带包名的全类名
        System.out.println(userClass.getSimpleName());
        // 获取class 方法2 根据类名获取类的对象
        Class<?> userClass2=Class.forName("com.example.demo.clazz.User");
        System.out.println(userClass2.getSimpleName());

        // 获取class 方法3
        User user=new User();
        Class<? extends User> userClass3 = user.getClass();
        System.out.println(userClass3.getSimpleName());
        System.out.println("-------------------------------------------");


        Class<User> userClass1 = User.class;
        // 获取类的包名,注意这里的包名是包含 package的
        System.out.println(userClass1.getPackage());

        // 创建实例
        User user1 = userClass1.newInstance();
        System.out.println(user1);

        // 获得类加载器
        ClassLoader classLoader = userClass.getClassLoader();

        // 获取该类中的公共类
        Class<?>[] classes = userClass1.getClasses();
        for (Class<?> cls:classes){
            System.out.println(cls.getSimpleName());
        }
        System.out.println("---------------------------------");
        // 获取该类中的类 包括非公共类
        Class<?>[] declaredClasses = userClass1.getDeclaredClasses();
        for (Class<?> cls:declaredClasses){
            System.out.println(cls.getSimpleName());
        }
        System.out.println("---------------------------------");

        // 把传递的对象转换成代表其子类的对象
        Class<Brid> brid = Brid.class;
        Class<? extends Animal> aClass = brid.asSubclass(Animal.class);
        System.out.println(aClass.getName());

        // 获得当前继承的父类的名字
        Class<? super Brid> superclass = brid.getSuperclass();
        System.out.println(superclass.getName());
        // 获得当前类实现的类或是接口
        Class<?>[] interfaces = brid.getInterfaces();
        for (Class<?> inf:interfaces){
            System.out.println(inf.getName());
        }
        System.out.println("---------------------------------");
        Class<User> userField=User.class;
        // 获取类中的所有公共字段
        Field[] fields = userField.getFields();
        for (Field fid:fields){
            System.out.println(fid.getName());
        }
        // 获取类中某个公共的字段
        Field name = userField.getField("idNumber");
        System.out.println(name.getName());
        System.out.println("----------------------");
        // 获取类中的所有字段
        Field[] declaredFields = userField.getDeclaredFields();
        for (Field fid:declaredFields){
            System.out.println(fid.getName());
        }
        System.out.println("--------------");
        // 获取类中某个字段
        Field name1 = userField.getDeclaredField("name");
        System.out.println(name1.getName());

        System.out.println("---------------------------------");
        // 获取指定的注解 可以根据此来判断,根据注解是否存在,去执行相关的逻辑
        Logger annotation = userField.getAnnotation(Logger.class);
        System.out.println(annotation);
        System.out.println("---------------------------------");
        // 获取类中的所有注解
        Annotation[] annotations = userField.getAnnotations();
        for (Annotation an:annotations){
            System.out.println(an);
        }
        System.out.println("---------------------------------");
        // 获取该类中所有公共构造方法
        Constructor<?>[] constructors = userField.getConstructors();
        for (Constructor<?> con:constructors) {
            System.out.println("获取到"+con.getParameterCount()+"个公共构造方法"+con.getName());
        }
        System.out.println("---------------------------------");
        // 获取类中所有构造方法
        Constructor<?>[] declaredConstructors = userField.getDeclaredConstructors();
        for (Constructor<?> con:declaredConstructors) {
            System.out.println(con.getName());
        }
        System.out.println("---------------------------------");
        // 获取该类中与参数类型匹配的公有构造方法 默认是无参构造方法
        Constructor<User> constructor = userField.getConstructor();
        System.out.println(constructor);
        System.out.println("---------------------------------");
        // 获取该类中与参数类型匹配的构造方法 注意参数个数应与构造方法一致
        Constructor<User> declaredConstructor = userField.getDeclaredConstructor(String.class, Integer.class,String.class);
        System.out.println(declaredConstructor);
        System.out.println("---------------------------------");
        // 获取该类所有公有的方法
        Method[] methods = userField.getMethods();
        for (Method method:methods){
            System.out.println(method);
        }
        System.out.println("---------------------------------");
        // 获取该类中的某个公有方法 参数 方法名称 和 方法的参数
        Method method1 = userField.getMethod("getName");
        System.out.println(method1);
        System.out.println("---------------------------------");
        // 获取该类中的所有方法
        Method[] declaredMethods = userField.getDeclaredMethods();
        for (Method method:methods){
            System.out.println(method);
        }
        System.out.println("---------------------------------");
        // 获取该类中的某个方法  根据名称和参数类型
        Method method2 = userField.getDeclaredMethod("setName", String.class);
        System.out.println(method2);
        System.out.println("---------------------------------");
        // 如果是注解类型则返回true
        System.out.println(Logger.class.isAnnotation());
        System.out.println(User.class.isAnnotation());
        // 如果是接口则返回true
        System.out.println(MyAbility.class.isInterface());
        System.out.println(User.class.isInterface());
        // 如果是枚举则返回true
        System.out.println(User.class.isEnum());
        // 如果是数组则返回 true
        System.out.println(User.class.isArray());
        // 如果 obj是该类的实例则返回true
        System.out.println(User.class.isInstance(user));

    }

}

//@Logger
//如果属性名称是Value 时可以直接赋值
@MyAnnotation("我是用户类")
class User{

    @MyAnnotation("张三")
    private String name;
    protected  Integer age;
    public String idNumber;

    public User() {
    }

    private static class Bike{
        private String name;
    }


    public static class Clothes{
        private String name;
    }

    protected User(String name, Integer age, String idNumber) {
        this.name = name;
        this.age = age;
        this.idNumber = idNumber;
    }

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    protected Integer getAge() {
        return age;
    }

    protected void setAge(Integer age) {
        this.age = age;
    }

    private String getIdNumber() {
        return idNumber;
    }

    private void setIdNumber(String idNumber) {
        this.idNumber = idNumber;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", idNumber='" + idNumber + '\'' +
                '}';
    }
}
class Animal{

    private String name;

    private String age;

    private String ability;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getAbility() {
        return ability;
    }

    public void setAbility(String ability) {
        this.ability = ability;
    }
}

class Brid extends Animal implements MyAbility{

    @Override
    public void method() {
        System.out.println("小鸟的本领是偷东西");
    }
}

interface MyAbility{

    void method();

}

常用方法:
获取一个class

 Class<User> userClass=User.class;

获取类的完整路径

userClass.getName()

获取类名

userClass.getSimpleName()

获取类的包名

userClass.getPackage()

创建类的实例

 User user = userClass.newInstance();

获取类中包含的公共类

 Class<?>[] classes = userClass.getClasses();

获取类中所有类

Class<?>[] declaredClasses = userClass.getDeclaredClasses();

把传递的对象转换成代表其子类的对象

        Class<Brid> brid = Brid.class;
        Class<? extends Animal> aClass = brid.asSubclass(Animal.class);

获取类中所有公共字段

        Field[] fields = userField.getFields();

获取某个公共字段

  Field name = userField.getField("idNumber");

获取类中所有字段

        Field[] declaredFields = userField.getDeclaredFields();

获取指定注解

        Logger annotation = userField.getAnnotation(Logger.class);

获取类中的所有注解

Annotation[] annotations = userField.getAnnotations();

获取类中所有公共构造方法

Constructor<?>[] constructors = userField.getConstructors();

获取类中所有构造方法

Constructor<?>[] declaredConstructors = userField.getDeclaredConstructors();

获取该类所有公有的方法

 Method[] methods = userField.getMethods();

获取该类中的所有方法

Method[] declaredMethods = userField.getDeclaredMethods();

Field 代表类的成员变量。成员变量和成员属性是两个概念。用get/set方法来区分,如果一个类里面有name变量,没有get/set 方法,则这个类只有name这个字段,如果有get/set方法时,则这个类拥有name属性
代码如下:

public class FieldTest {

    public static void main(String[] args) throws Exception{
        Class<User> userClass=User.class;
        User user=new User("张三",23,"123456");
        Field field = userClass.getDeclaredField("name");
        // 获取 obj中对应的公共方法的属性值
       // System.out.println(field.get(user));

        // 暴力反射 ,忽略访问权限修饰符
        field.setAccessible(true);
        System.out.println(field.get(user));

        // 设置obj中对应属性值
        field.set(user,"李四");
        System.out.println(field.get(user));

    }

}

Method 代表类的方法(不包括构造方法)

public class MethodTest {

    public static void main(String[] args) throws Exception{
        Class<User> userClass=User.class;
        User user=new User("张三",23,"123456");
       // Method method = userClass.getDeclaredMethod("getName");
        Method method = userClass.getDeclaredMethod("getIdNumber");
        // 暴力反射 ,忽略访问权限修饰符
        method.setAccessible(true);
        // 传递obj对象及参数调用该对象对应的方法 执行指定对象的方法
        Object invoke = method.invoke(user);
        System.out.println(invoke);
        // set方法
        Method method2 = userClass.getDeclaredMethod("setIdNumber", String.class);
        method2.setAccessible(true);
        Object invoke1 = method2.invoke(user, "666");
        System.out.println(user);


    }

}

Constructor: 代表类的构造方法

public class ConstructorTest {

    public static void main(String[] args) throws Exception{
        Class<User> userClass = User.class;
        // 调用共有的构造方法 创建对象
        User user = userClass.newInstance();
        System.out.println(user);
        // Constructor 可以创建有参构造方法,并且无视方法访问权限
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, Integer.class, String.class);
        User user1 = declaredConstructor.newInstance("张三", 23, "123456");
        System.out.println(user1);


    }

}

验证通过反射擦除泛型

public class ListTest {

    public static void main(String[] args) throws Exception{
        List<User> userList = new ArrayList<User>();
        userList.add(new User("张三",24,"12324"));
        userList.add(new User("李四",24,"12334"));
        userList.add(new User("王五",24,"32324"));

        Class<? extends List> userListClass = userList.getClass();
        Method method = userListClass.getDeclaredMethod("add", Object.class);
        // 泛型会在运行阶段被擦除,这个时候集合一类的对象就可以强制往里面放入非指定泛型的数据
        method.invoke(userList,"hhh");
        method.invoke(userList,"123");
        System.out.println(userList);


    }

}

工具类,根据具体的方法名,执行次方法

 /**
     * 执行指定对象的指定方法
     * @param obj 指定的对象
     * @param methodName 指定的方法名称
     * @param params 传输的参数
     * @return
     */
    public static Object invokeMethod(Object obj,String methodName,Object... params){
        Class<?> objectClass=obj.getClass();
        try {
            if(params.length==0||params==null){
                Method declaredMethod = objectClass.getDeclaredMethod(methodName);
                declaredMethod.setAccessible(true);
                Object result = declaredMethod.invoke(obj);
                return result;
            }else{
                int size= params.length;
                Class<?>[] classes=new Class[size];
                Object[] objects=new Object[size];
                for (int i=0;i<size;i++){
                    classes[i]=params[i].getClass();
                    objects[i]=params[i];
                }
                Method declaredMethod = objectClass.getDeclaredMethod(methodName, classes);
                declaredMethod.setAccessible(true);
                Object result = declaredMethod.invoke(obj, objects);
                return result;
            }

        }catch (Exception e){
            e.printStackTrace();

        }

        return null;
    }

验证如下:

public static void main(String[] args) {
        User user=new User("张三",23,"123456");
        Object getName = invokeMethod(user, "getName");
        System.out.println(getName);

        Object method = invokeMethod(user, "setName", "李四");
        System.out.println(user);

    }

注解

  • 注释:用于文字描述,给程序员看的
  • 定义:用来说明程序的一个标识,给计算机看的,注解也叫元数据,是一种代码级别的说明,它是jdk1.5之后引入的一个特性,是一种特殊的接口,可以用在字段,类
  • 方法,包,参数等上面
  • 注意:注解本身并没有任何功能,仅仅起到一个标识性作用,我们是通过反射获取到注解,再根据是否有这个注解,注解中的一些属性,去判断执行那种业务逻辑

作用分类:

  • 编写文档:通过代码里的注解标识去生成API文档(Swagger)
  • 代码分析:通过注解去对代码进行逻辑上的分析(通过反射去操作业务)
  • 编译检查:通过编辑器实现基本的编译检查(Override,SuppressWarning)

Jdk中预定的一些注解

  • Override:检测注解标识的方法是否是继承自父类
  • Deprecated:标识该内容以及过时,后续的版本可能会进行移除
  • SuppressWarnings:压制警告,指定警告级别,这个级别下的警告全都不显示

自定义注解:
在实际开发中,可能存在一些代码及其复杂或者复用性很低的业务逻辑,比如:导出Excel,缓存,将返回值转json,事务等等,这个时候就可以使用自定义注解

格式

  • 元注解
    public @interface 注解名称{
    属性列表;
    }

本质就是一个接口,该接口继承 Annotation接口

属性
本质:接口中的抽象方法
属性只能是以下几种数据类型:

  • 1.基本数据类型
  • 2.String
  • 3.枚举
  • 4.注解
  • 5.以上类型的数组
    如果定义了属性,在使用的时候需要给属性赋值,如果有默认值则可以不附值,如果只有1个属性需要赋值,并且这个属性叫Value 那么就可以省略属性名,数组赋值的时候,用{}包裹,如果只有1个值,那么大括号可以省略
    元注解

@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:

  •  ElemenetType.CONSTRUCTOR-----------------------------构造器声明
    
  •  ElemenetType.FIELD ----------------------------------域声明(包括 enum 实例)
    
  •  ElemenetType.LOCAL_VARIABLE------------------------- 局部变量声明
    
  •  ElemenetType.METHOD ---------------------------------方法声明
    
  •  ElemenetType.PACKAGE --------------------------------包声明
    
  •  ElemenetType.PARAMETER ------------------------------参数声明
    
  •  ElemenetType.TYPE----------------------------------- 类,接口(包括注解类型)或enum声明
    

@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

  •  RetentionPolicy.SOURCE-------------注解将被编译器丢弃
    
  •  RetentionPolicy.CLASS -------------注解在class文件中可用,但会被VM丢弃
    
  •  RetentionPolicy.RUNTIME ---------VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
    

@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
@Inherited 允许子类继承父类中的注解。
注解测试代码如下:

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

@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    /**
     * 值
     * @return
     */
    String value();

    /**
     * 长度
     * @return
     */
    int num() default 0;

}

public class AnnotationTest {

    public static void main(String[] args) throws Exception{
        // 获取用户反射
        Class<User> userClass = User.class;
        // 获取注解
        MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
        if(annotation!=null){
            System.out.println(annotation.value());
        }
        System.out.println("--------------------------------");
        User user=new User();
        //获取所有字段
        Field[] declaredFields = userClass.getDeclaredFields();
        for (Field field:declaredFields){
            //获取字段注解
            MyAnnotation annotation1 = field.getAnnotation(MyAnnotation.class);
            if(annotation1!=null){
                // 获取注解值
                String value= annotation1.value();
                // 暴力反射
                field.setAccessible(true);
                // 设置字段值为注解值
                field.set(user,value);

            }

        }
        System.out.println(user);
    }

}

实现缓存注解

/**
 * 缓存注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
    /**
     * 大key
     * @return
     */
    String key();

    /**
     * 过期时间
     * @return
     */
    int timeOut() default 10;

    /**
     * 过期时间单位
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.MINUTES;


}

public class UserController {

    private User[] user={
            new User("张三"),
            new User("李四"),
            new User("王五")
    };


    @Cache(key = "user")
    public User getUserById(Integer id){
        System.out.println("根据缓存查询用户数据");
        return user[id];
    }

}

验证方法


public class UserController {

    private User[] user={
            new User("张三"),
            new User("李四"),
            new User("王五")
    };


    @Cache(key = "user")
    public User getUserById(Integer id){
        System.out.println("根据缓存查询用户数据");
        return user[id];
    }

}

工具类方法

public final class MethodUtil {

    private static Map<String,Object> cacheMap=new ConcurrentHashMap<String,Object>();

    public MethodUtil() {
    }


    /**
     * 缓存注解
     * @param obj 指定的对象
     * @param methodName 指定的方法名称
     * @param params 传输的参数
     * @return
     */
    public static Object CacheMethod(Object obj,String methodName,Object... params){
        Class<?> objectClass=obj.getClass();
        try {
            if(params.length==0||params==null){
                //没有参数的情况下,以大KEY为缓存的key
                Method declaredMethod = objectClass.getDeclaredMethod(methodName);
                declaredMethod.setAccessible(true);
                Cache annotation = declaredMethod.getAnnotation(Cache.class);
                //方法上面有cache注解进行缓存
                if(annotation!=null){
                    String key=annotation.key();
                    Object value= cacheMap.get(key);
                    if(value != null){
                        return value;
                    }
                }
                Object result = declaredMethod.invoke(obj);
                //将数据放入map进行缓存
                if(annotation != null){
                    String key=annotation.key();
                    cacheMap.put(key,result);

                }
            }else{
                int size= params.length;
                Class<?>[] classes=new Class[size];
                Object[] objects=new Object[size];
                for (int i=0;i<size;i++){
                    classes[i]=params[i].getClass();
                    objects[i]=params[i];
                }
                Method declaredMethod = objectClass.getDeclaredMethod(methodName, classes);
                declaredMethod.setAccessible(true);
                Cache annotation = declaredMethod.getAnnotation(Cache.class);
                //方法上面有cache注解进行缓存
                if(annotation!=null){
                    //获取大 key
                    String key=annotation.key();
                    //获取 小 key
                    Object param = params[0];
                    // 拼接缓存key
                    String cacheKey = key +":"+param;
                    //从缓存中查找数据
                    Object value = cacheMap.get(cacheKey);
                    if(value != null){
                        return value;
                    }

                }
                
                Object result = declaredMethod.invoke(obj, objects);
                //将数据放入map进行缓存
                if(annotation != null){
                    //获取大 key
                    String key=annotation.key();
                    //获取 小 key
                    Object param = params[0];
                    // 拼接缓存key
                    String cacheKey = key +":"+param;

                    cacheMap.put(cacheKey,result);

                }
            }

        }catch (Exception e){
            e.printStackTrace();

        }

        return null;
    }


}

测试类

public class CacheTest {

    public static void main(String[] args) throws Exception {

        UserController userController= new UserController();

        Object user1 = MethodUtil.CacheMethod(userController, "getUserById", 1);
        Object user2 = MethodUtil.CacheMethod(userController, "getUserById", 1);

    }

}

调用两个相同ID的方法,该方法只输出了1遍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值