Loading

反射与自定义注解

反射

什么是反射

Oracle官方对反射的解释:

通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息

Java属于先编译后执行从.java文件->.class文件->通过类加载机制加载需要用到的类,用不到的就不会被加载到JVM,通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

使用反射机制可以动态获取当前class的信息 比如方法的信息、注解信息、方法的参数、属性等

反射机制的优缺点

在一个类中 定义了一个私有属性/方法,但是使用反射能使所有属性都访问到,会破解私有属性

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

2、缺点:

(1)反射会消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射

(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题

反射的应用场景

反编译:.class-->.java

1、通过反射机制访问java对象的属性,方法,构造方法等

2、JDBC加载驱动连接 class.forname

Class.forName("com.mysql.jdbc.Driver"); // 动态加载mysql驱动

3、Spring容器框架IOC实例化对象

 <bean id="usr" class="com.ylc.UserEntity" />

4、 自定义注解生效(反射+Aop)

5、 第三方核心的框架 mybatis orm

反射技术的使用

Class类 代表类的实体,在运行的Java应用程序中表示类和接口

Field类 代表类的成员变量(成员变量也称为类的属性)

Method类 代表类的方法

Constructor类 代表类的构造方法

1.getFieldgetMethodgetCostructor方法可以获得指定名字的域、方法和构造器。

2.getFieldsgetMethodsgetCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员。

3.getDeclatedFieldsgetDeclatedMethodsgetDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

反射机制使用三种方式创建对象

  • 通过new出来的对象获取class

          UserEntity userEntity = new UserEntity();
            Class userClass = userEntity.getClass();
            // 默认执行无参构造函数
            UserEntity user2 = (UserEntity) userClass.newInstance();
            System.out.println(user2==userEntity);//false
    
  • 直接获取class

     Class userClass = UserEntity.class;
     UserEntity user2 = (UserEntity) userClass.newInstance();
     System.out.println(user2);
    
  • 通过完整类名获取class(常用

     Class<?> aClass = Class.forName("com.ylc.entity.UserEntity");
            UserEntity user3 = (UserEntity) aClass.newInstance();
            System.out.println(user3);
    

运行期间,一个类,只有一个Class对象产生

反射执行构造函数

无参构造函数

 Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
 UserEntity userEntity1 = (UserEntity) userClass.newInstance();
 System.out.println(userEntity1);

有参构造函数

  Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
   Constructor<?> declaredConstructor1 = userClass.getDeclaredConstructor(String.class, Integer.class);
        UserEntity ylc = (UserEntity)declaredConstructor1.newInstance("ylc", 22);
        System.out.println(ylc.toString());

反射遍历属性并赋值

反射执行给公有属性赋值

getFields方法只能访问到类的公有属性

image-20211024210820630

Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
Field[] fields = userClass.getFields();
for (Field field : fields) {
    System.out.println(field);
}

image-20211024210900255

反射执行给私有属性赋值

getDeclaredFields可以访问到类的所有属性

    Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }

image-20211024211041479

反射给属性赋值

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
        UserEntity userEntity = (UserEntity)userClass.newInstance();
        //查找到属性
        Field pubUserName = userClass.getDeclaredField("pubUserName");
        //指定给哪个userEntity对象赋值
        pubUserName.set(userEntity,"ylc");
        System.out.println(userEntity.pubUserName);
    }

这是给公有属性赋值,默认只能访问公有属性,如果要访问私有属性,会报错

image-20211024212217671

反射没有权限访问私有属性,如果需要访问需要设置权限setAccessible

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
        UserEntity userEntity = (UserEntity)userClass.newInstance();
        Field pubUserName = userClass.getDeclaredField("userName");
        //设置权限
        pubUserName.setAccessible(true);
        pubUserName.set(userEntity,"ylc");
        System.out.println(userEntity.getUserName());
    }

反射调用方法

反射调用公有方法

Class<?> aClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method mayikt = aClass.getDeclaredMethod("mayikt");
//执行方法
mayikt.invoke(userEntity);

反射调用私有方法,需要开启访问权限,不然访问不到

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
        UserEntity o = (UserEntity)userClass.newInstance();
        Method method = userClass.getMethod("HelloWorld");
        Object invoke = method.invoke(o);
    }

image-20211024213446331

userName.setAccessible(true);

反射调用方法传递参数

    private Integer sum(Integer a, Integer b) {
        return a + b;
    }
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
    Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
    UserEntity o = (UserEntity)userClass.newInstance();
    //调用方法传递参数
    Method method = userClass.getDeclaredMethod("sum",Integer.class,Integer.class);
    method.setAccessible(true);
    Integer result = (Integer)method.invoke(o,1,2);
    System.out.println(result);
}

image-20211024214807151

通过反射越过泛型检查

泛型验证是在编译期,这时添加一个int类型会报错

image-20211024221117446

编译器编译之后,通过class文件,可以在程序中通过反射破解

        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("ylc");
        Class<? extends ArrayList> aClass = arrayList.getClass();
        Method addMethod = aClass.getDeclaredMethod("add", Object.class);
        addMethod.invoke(arrayList, 1);
        System.out.println(arrayList);

注解

注解概念

注解用来给类声明附加额外信息,可以标注在类、字段、方法等上面,编译器、JVM以及开发人员等都可以通过反射拿到注解信息,进而做一些相关处理

常用注解

@Override 只能标注在子类覆盖父类的方法上面,有提示的作用

@Deprecated 标注在过时的方法或类上面,有提示的作用

@SuppressWarnings("unchecked") 标注在编译器认为有问题的类、方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unused、all

元注解

元注解用来在声明新注解时指定新注解的一些特性

@Target 指定新注解标注的位置,比如类、字段、方法等,取值有ElementType.Method等

@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})

@Retention 指定新注解的信息保留到什么时候,取值有RetentionPolicy.RUNTIME等

@Inherited 指定新注解标注在父类上时可被子类继承

注解的Target

TYPE:类、接口(包括注解类型)和枚举的声明

FIELD:字段声明(包括枚举常量)

METHOD:方法声明

PARAMETER:参数声明

CONSTRUCTOR:构造函数声明

LOCAL_VARIABLE:本地变量声明

ANNOTATION_TYPE:注解类型声明

PACKAGE:包声明

TYPE_PARAMETER:类型参数声明,JavaSE8引进,可以应用于类的泛型声明之处

TYPE_USE:JavaSE8引进,此类型包括类型声明和类型参数声明

获取注解信息

注解方法

    @DiyName
    private void HelloWorld() {
        System.out.println(" ...ylc.....");
    }

自定义注解

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

获取当前方法上的注解

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException {
        Class<?> userClass = Class.forName("com.ylc.entity.UserEntity");
        Object o = userClass.newInstance();
        Method diyMethod = userClass.getDeclaredMethod("HelloWorld");
        DiyName diyName = diyMethod.getDeclaredAnnotation(DiyName.class);
        System.out.println(diyName);
    }

image-20211024223823580

posted @ 2021-11-26 18:50  炒焖煎糖板栗  阅读(166)  评论(0编辑  收藏  举报