案例描述Java反射和注解

案例描述Java反射和注解

反射

Java反射机制是再运行状态中,对于任意一个实体类,都能够指导这个类的所有属性和方法

对于任意一个对象,都能够调用它的任意方法和属性

这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制

即将类的属性、方法、包、父类、接口等等信息,实现成一个一个的对象

1. Class对象的基本方法

@Data
public class Book {
    private String name;
    private Integer id;
}

获取Book类的三种方法

    @Test
    public void testClass() throws ClassNotFoundException {
        Class<Book> Class1 =Book.class;
        System.out.println(Class1.getClass());  //class java.lang.Class

        System.out.println(Class1.getSimpleName());//Book

        Class<?> Class2 = Class.forName("com.example.demo.entity.Book");
        System.out.println(Class2.getName());//com.example.demo.entity.Book

        Book book=new Book();
        Class<? extends Book> Class3 = book.getClass();
        System.out.println(Class3.getName());   //com.example.demo.entity.Book

    }

2.Class对象的基本方法

Class代表类的实体,再运行的Java应用程序中标识类和接口,再这个类中提供了很多有用的方法

方法用途
asSubClass(Class clazz)把传递的类的对象转换成代表子类的对象
getClassLoader()获得类的加载器
getClasses()返回一个数组,数组中包含该类中所有公共类和接口的对象
getDeclaredClasses()返回一个数组,数组中包含该类中所有类和结构类的对象
getInterfaces()创建类的实例
getSuperclass()获得当前类继承的父类的名字
getInterface()获取当前类实现的类或接口

3. Field

Field代表类的 成员变量,成员变量(字段)和成员属性是两个概念

比如:当一个Book了中有一个name变量,那我们称为name字段。

​ 但是如果没有getName和setName这两个方法,那么这个类就没有name属性,

​ 反之,如果这个类有getName和setName这两个方法,我们都称他为有name这个属性

我们可以通过getDeclaredField()方法来get&set 私有变量private

    @Test
    public void testField() throws Exception {
        Class<Book> bookClass = Book.class;
        Book book = new Book("三体",1);
        System.out.println("反射前:"+book);
        Field field = bookClass.getDeclaredField("name");
        field.setAccessible(true);
        Object b = field.get(book);
        System.out.println(b);

        field.set(book,"神雕侠侣");
        System.out.println(book);
    }
反射前:Book(name=三体, id=1)
三体
Book(name=神雕侠侣, id=1)

4. Method方法

Method代表一个类中的方法

    @Test
    public void testMethod() throws Exception{
        Class<Book> bookClass = Book.class;
        Book book = new Book("天龙八部", 90);
        Method getName = bookClass.getDeclaredMethod("getName");
        Object Mvalue = getName.invoke(book);
        System.out.println(Mvalue); //天龙八部
    }

Invoke 方法的用处: SpringAOP切面方法执行的前后进行某些操作,就是使用的invoke方法

动态代理设计模式中,使用的也是Invoke

Method的invoke的应用

创建一个工具类

package com.example.demo.config;

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

public final class MethodUtils {
    private MethodUtils(){}


    /**
     * 执行对象的指定方法
     * @param obj   要执行方法的对象
     * @param methodName    要执行方法名称
     * @param params    方法参数
     * @return
     */
    public static Object invokeMethods(Object obj,String methodName,Object... params)  {
        Class<?> objClass = obj.getClass();
        Object result=null;
        Long starttime=System.currentTimeMillis();

        try {
            System.out.println("方法:"+methodName);

            if(params==null||params.length==0) {

                Method declaredMethod = objClass.getDeclaredMethod(methodName);
                declaredMethod.setAccessible(true);
                 result = declaredMethod.invoke(obj);
                System.out.println("返回值"+result);
            }
            else{
                int length = params.length;
                Class[] classes = new Class[length];
                Object[] paramValues =new Object[length];
                for(int i=0;i<params.length;i++){
                    classes[i]=params[i].getClass();
                    paramValues[i]=params[i];
                }
                Method method=objClass.getDeclaredMethod(methodName,classes);
                System.out.println("传入参数"+ Arrays.toString(params));
                method.setAccessible(true);//暴力反射
                 result = method.invoke(obj, paramValues);
                System.out.println("返回值"+result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Long endTime=System.currentTimeMillis();
        System.out.println("耗时:"+(endTime-starttime));
        return result;

    }
}
@Test
public void testMethodUtils(){
    Book book = new Book("天龙八部", 90);
    Object getName = MethodUtils.invokeMethods(book, "getName");
    System.out.println(getName);  //天龙八部
    Object setName  = MethodUtils.invokeMethods(book, "setName","斗罗大陆");
    System.out.println(book);   //Book(name=斗罗大陆, id=90)
}
方法:getName
返回值天龙八部
耗时:0
天龙八部
方法:setName
传入参数[斗罗大陆]
返回值null
耗时:0
Book(name=斗罗大陆, id=90)

5. Constructor(少用)

强制创建对象

在之前的方法中我们提到了newInstace()这个CLass方法

@Test
public void testInstance() throws IllegalAccessException, InstantiationException {
    Class<Book> bookClass = Book.class;
    Book book = bookClass.newInstance();
    System.out.println(book);   //Book(name=null, id=null)
}

但newInstace()功能简单,局限性大,只能无参构造

@Test
public void testConstructor() throws Exception{
    Class<Book> bookClass = Book.class;
    Constructor<Book> declaredConstructor = bookClass.getDeclaredConstructor(String.class, Integer.class);
    Book book = declaredConstructor.newInstance("斗罗大陆", 90);
    System.out.println(book);   //Book(name=斗罗大陆, id=90)

    Constructor<Book> declaredConstructor1 = bookClass.getDeclaredConstructor(String.class);
    declaredConstructor1.setAccessible(true);

    Book book1 = declaredConstructor1.newInstance("仙剑奇侠传");
    System.out.println(book1);


}

违背了java是思想

注解

注释:文字描述程序,给程序员看的

注释:

  • 说明程序的一个标识
  • 给计算机看
  • 注解也叫元数据
  • 是一种代码级别的说明
  • jdk1.5之后引入的一个特性,是一种特殊的接口
  • 可以使用在字段、类、方法、包、类、数等上面

注意:

  • 注解本身仅起到标识性的作用(没有功能),我们是通过反射去获取到注解,再根据是否有这个注解,注解中的一些属性去判断,执行哪种业务逻辑

(类似接口,没有任何功能,仅是比较规范)

作用分类:

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

JDK中预定的一些注解:

  • Override: 检测该注释便是的方法是否继承自父类(接口)
  • Deprecated: 标识改内容以过失,可能会移除该功能
  • SuppressWarning :压制警告,指定警告级别,这个级别下的警告全部不显示

自定义注解

在实际开发中,可能会出现一些代码及其复杂或者复用性很低的业务逻辑

比如:导出Excel、缓存、将对象返回值转JSON、事物等

格式

元注解

public @interface 注解名称{

​ 属性列表;

}

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

属性

本质:接口中的抽象方法

属性只能是一下几种数据类型:

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上类型的数组

如果定义了属性,再使用的时候需要给属性赋值

如果只有一个属性需要复制,并且这个属性叫value,那么就可以省略属性名

数值赋值时,值用{}包裹,如果数组中只有一个值,那么大括号可以省略

元注解

用于描述注解的注解

  • @Target: 描述该注解作用范围

    • ElementType取值
      • Type:作用与类
      • METHOD:作用与方法
      • FIELD:作用与字段
  • @Retention:描述注解被保留的阶段

    • RetentionPolicy:RUNTIME:当前描述的注解,会保留到class字节码文件中,并被jvm读取到
  • @Documented:描述注解是否被抽取到api文档中

  • @Inherited: 描述注解是否可以被继承

注解案例

编写一个缓存注解,该注解用于缓存指定方法的返回值

  1. 创建一个注解
package com.example.demo.annotation;

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})//ElementType.TYPE说明只能作用在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String content();	//当content为value时,使用注解赋值时可以省略
    int num() default 0;//有默认值,可以不赋值
}

  1. 创建一个User类,并加上我们写的注解

    package com.example.demo.entity;
    
    import com.example.demo.annotation.MyAnnotation;
    
    @MyAnnotation(content = "我是内容")
    public class User {
    
        @MyAnnotation(content = "我是丽丝")
        private String name;
        private Integer id;
        private Integer age;
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    ", age=" + age +
                    '}';
        }
    
        public User() {}
        public User(String name, Integer id, Integer age) {
            this.name = name;
            this.id = id;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    
  2. 调用我们新写的注解

    3.1 调用类上注解

        @Test
        public void AnnotationTest(){
            Class<User> userClass = User.class;
            MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
            if(annotation!=null){
                System.out.println(annotation.content()+":"+annotation.num());//我是内容:0
            }
        }
    
    

    3.2 调用属性上的注解

    @Test
    public void AnnatationTest2() throws Exception{
        Class<User> userClass = User.class;
        Field[] declaredFields = userClass.getDeclaredFields();
        User user=new User();
        user.setName("梦琪D路飞");
        System.out.println(user);
        for(Field f:declaredFields){
            MyAnnotation annotation = f.getAnnotation(MyAnnotation.class);
            if(annotation!=null){
                String content = annotation.content();
                f.setAccessible(true);
                f.set(user,content);
            }
        }
        System.out.println(user);
    }
    
    

    User{name='梦琪D路飞', id=null, age=null}
    User{name='我是丽丝', id=null, age=null}

缓存注解

编写一个缓存注解,用于缓存一个返回值


package com.example.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
    String key();

    int timeout() default 10;

    TimeUnit timeUnit() default TimeUnit.MINUTES;

}


package com.example.demo.utils;

import com.example.demo.annotation.Cache;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CacheUtils {

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

    private static final String CACHE_SEPARATE  = "::";

    public static Object invokeMethods(Object obj,String methodName,Object... params)  {
        Class<?> objClass = obj.getClass();
        Object result=null;
        Long starttime=System.currentTimeMillis();
        try {
            if(params==null||params.length==0) {
                Method declaredMethod = objClass.getDeclaredMethod(methodName);
                declaredMethod.setAccessible(true);
                Cache annotation = declaredMethod.getAnnotation(Cache.class);
                if(annotation!=null){
                    String key = annotation.key();
                    Object value = cacheMap.get(key);
                    if(value!=null){
                        return value;
                    }
                }
                result = declaredMethod.invoke(obj);
                if(annotation!=null){
                    String key = annotation.key();
                    cacheMap.put(key,result);
                }
            }
            else{
                int length = params.length;
                Class[] classes = new Class[length];
                Object[] paramValues =new Object[length];
                for(int i=0;i<params.length;i++){
                    classes[i]=params[i].getClass();
                    paramValues[i]=params[i];
                }
                Method method=objClass.getDeclaredMethod(methodName,classes);
                method.setAccessible(true);//暴力反射
                Cache annotation = method.getAnnotation(Cache.class);
                if(annotation!=null){
                    String key =annotation.key();
                    Object paramValue = paramValues[0];
                    String cacheKey=key+CACHE_SEPARATE+paramValue;
                    Object value = cacheMap.get(cacheKey);
                    if(value!=null){
                        return value;
                    }
                }
                result = method.invoke(obj, paramValues);
                if(annotation!=null){
                    String key =annotation.key();
                    Object paramValue = paramValues[0];
                    String cacheKey=key+CACHE_SEPARATE+paramValue;
                    cacheMap.put(cacheKey,result);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Long endTime=System.currentTimeMillis();
        return result;
    }
}

@Test
public void CacheTest() throws Exception{
    UserController userController = new UserController();
    Object user1 = CacheUtils.invokeMethods(userController, "getUserById", 1);
    Object user2 = CacheUtils.invokeMethods(userController, "getUserById", 2);
    System.out.println(user1);
    System.out.println(user2);
}
getUserById Function
getUserById Function
User{name='James Harden', id=2, age=37}
User{name='Steph Curry', id=3, age=37}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java反射可以让我们在运行时获取和操作类的信息,包括类的属性、方法、构造函数等。下面是一个使用Java反射的简单案例: 假设我们有一个类Person: ```java public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void sayHello() { System.out.println("Hello, my name is " + name + ", I am " + age + " years old."); } } ``` 现在,我们可以使用反射来获取和调用Person类的构造函数和方法: ```java import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class ReflectionExample { public static void main(String[] args) throws Exception { // 获取Person类的Class对象 Class<Person> personClass = Person.class; // 获取Person类的构造函数 Constructor<Person> constructor = personClass.getConstructor(String.class, int.class); // 使用构造函数创建Person对象 Person person = constructor.newInstance("Tom", 20); // 获取Person类的sayHello方法 Method sayHelloMethod = personClass.getMethod("sayHello"); // 调用sayHello方法 sayHelloMethod.invoke(person); } } ``` 以上代码中,我们首先通过`Person.class`获取了Person类的Class对象,然后使用`getConstructor`方法获取了Person类的构造函数,并使用构造函数创建了一个Person对象。接着,我们使用`getMethod`方法获取了Person类的`sayHello`方法,并使用`invoke`方法调用了该方法。最终,我们输出了`Hello, my name is Tom, I am 20 years old.`。 这只是一个简单的反射案例,实际上Java反射还可以做很多其他的事情,比如动态代理、注解处理等等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值