【反射、注解】

本文详细介绍了Java反射机制,包括类加载、Class对象的获取、构造方法、成员方法和成员变量的操作。此外,还讲解了注解的自定义、元注解及其在代码中的应用。通过实例展示了如何使用反射进行动态操作类和对象,以及如何通过注解增强代码的元数据信息。
摘要由CSDN通过智能技术生成

1、反射

1.1、类的加载

  • 当我们的程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中。
    在这里插入图片描述
    类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象。

  • Java中有三种类加载器,它们分别用于加载不同种类的class:

    • 启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。
    • 扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。
    • 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。

    2.2、反射概述

    反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)

Class对象的获取方式

  • 方式1: 通过类名.class获得
  • 方式2:通过对象名.getClass()方法获得
  • 方式3:通过Class类的静态方法获得: static Class forName(“类全名”)
    每一个类的Class对象都只有一个
public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * 获取类的class对象:
         *      三种
         *      1、类.class;
         *      2、对象.getClass();
         *      3、Class.forName("");
         */
        //1、类.class
        Class<Student> clazz1 = Student.class;
        System.out.println(clazz1);

        //2、对象.getClass()
        Student su = new Student();
        Class<? extends Student> clazz2 = su.getClass();
        System.out.println(clazz2);

        //3、Class.forName()
        Class<?> clazz3 = Class.forName("com.cyy.study.Student");
        System.out.println(clazz3);

        System.out.println(clazz1==clazz2);//true
        System.out.println(clazz1==clazz3);//true
    }
}
Class类的常用方法

String getSimpleName(); 获得类名字符串:类名
String getName(); 获得类全名:包名+类名 T
newInstance() ; 创建Class对象关联类的对象

public class Demo2 {
    public static void main(String[] args) throws Exception{
        Class<Student> clazz = Student.class;
        System.out.println(clazz.getName());//com.cyy.study.Student
        System.out.println(clazz.getSimpleName());//Student
        Student student = clazz.newInstance();
        System.out.println(student);//Student{name='null', sex='null', age=null}
    }
}

2.3、反射之操作构造方法

Constructor类概述:类中的每一个构造方法都是一个Constructor类的对象。

Class类中与Constructor相关的方法

  1. Constructor getConstructor(Class… parameterTypes)
    * 根据参数类型获得对应的Constructor对象。
    * 只能获得public修饰的构造方法
  2. Constructor getDeclaredConstructor(Class… parameterTypes)
    * 根据参数类型获得对应的Constructor对象
    * 可以是public、protected、(默认)、private修饰符的构造方法。
  3. Constructor[] getConstructors()
    获得类中的所有构造方法对象,只能获得public的
  4. Constructor[] getDeclaredConstructors()
    获得类中的所有构造方法对象
    可以是public、protected、(默认)、private修饰符的构造方法。

Constructor对象常用方法

  1. T newInstance(Object… initargs)
    根据指定的参数创建对象
  2. void setAccessible(true)
    设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消
public class Demo3 {
    public static void main(String[] args) throws Exception{
        //1、获取字节码对象
        Class<?> clazz = Class.forName("com.cyy.study.Student");
        //获取构造方法
        Constructor<?> constructor = clazz.getConstructor();//获取无参数的构造方法
        //获取有参数的构造方法
        Constructor<?> constructor1 = clazz.getConstructor(String.class, String.class, Integer.class);

        Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class, String.class);
        constructor2.setAccessible(true);
        Student s3 = (Student) constructor2.newInstance("李四", "男");
        System.out.println(s3);//Student{name='李四', sex='男', age=null}

        //创建对象
        Student s1 = (Student) constructor.newInstance();
        Student s2 = (Student) constructor1.newInstance("张三","男",29);
        System.out.println(s1);//Student{name='null', sex='null', age=null}
        System.out.println(s2);//Student{name='张三', sex='男', age=29}
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();

        for (Constructor<?> constructor3 : constructors) {
            System.out.println(constructor3);
            /*
                private com.cyy.study.Student(java.lang.String,java.lang.String)
                protected com.cyy.study.Student(java.lang.String,java.lang.Integer)
                public com.cyy.study.Student(java.lang.String,java.lang.String,java.lang.Integer)
                public com.cyy.study.Student()
             */
        }
    }
}

2.3、反射之操作成员方法

Method类概述: 每一个成员方法都是一个Method类的对象。
Class类中与Method相关的方法

  • Method getMethod(String name,Class…args);
    * 根据方法名和参数类型获得对应的成员方法对象,只能获得public的
  • Method getDeclaredMethod(String name,Class…args); 掌握
    * 根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的
  • Method[] getMethods();
    * 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
  • Method[] getDeclaredMethods(); 掌握
    * 获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的

Method对象常用方法

  • Object invoke(Object obj, Object… args)
    • 调用指定对象obj的该方法
    • args:调用方法时传递的参数
  • void setAccessible(true)
    设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
public class Demo4 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.cyy.study.Student");
        Method method = clazz.getMethod("method1",String.class);
        method.invoke(clazz.newInstance(),"fds");
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method1 : methods) {
            System.out.println(method1);
        }
    }
}

2.4、反射之操作成员变量

Field类概述:每一个成员变量都是一个Field类的对象。

Class类中与Field相关的方法

  • Field getField(String name);
    • 根据成员变量名获得对应Field对象,只能获得public修饰
  • Field getDeclaredField(String name);
    • 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
  • Field[] getFields();
    • 获得所有的成员变量对应的Field对象,只能获得public的
  • Field[] getDeclaredFields();
    • 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的

Field对象常用方法

  • void set(Object obj, Object value)

  • void setInt(Object obj, int i)

  • void setLong(Object obj, long l)

  • void setBoolean(Object obj, boolean z)

  • void setDouble(Object obj, double d)

  • Object get(Object obj)

  • int getInt(Object obj)

  • long getLong(Object obj)

  • boolean getBoolean(Object ob)

  • double getDouble(Object obj)

  • void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。

  • Class getType(); 获取属性的类型,返回Class对象。

setXxx方法都是给对象obj的属性设置使用,针对不同的类型选取不同的方法。
getXxx方法是获取对象obj对应的属性值的,针对不同的类型选取不同的方法。

2、注解

注解(annotation),是一种代码级别的说明,和类接口平级关系.

  • 注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事。
1.1、自定义注解

格式:

public @interface 注解名{
     属性
}
注解属性

格式:

  • 数据类型 属性名();

属性类型
​ 1.基本类型
​ 2.String
​ 3.Class类型
​ 4.注解类型
​ 5. 枚举类型
​ 6.以上类型的一维数组类型

使用注解并给注解属性赋值

使用注解:

  • 如果一个注解中有属性,那么使用注解的时候一定要给注解属性赋值

  • 如果一个注解没用属性,那么就不需要给注解属性赋值,直接使用即可

如何给注解属性赋值:
@注解名(属性名=值,属性名2=值2)

注解赋值时的注意事项
  • 一旦注解有属性了,使用注解的时候,属性必须有值
  • 若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
  • 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
  • 注解属性可以有默认值 格式:属性类型 属性名() defaul t 默认值;

2.2、元注解

常见的元注解

​ @Target:表示该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值

​ METHOD:方法

​ TYPE:类 接口

​ FIELD:字段

​ CONSTRUCTOR:构造方法声明

​ @Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在源码阶段保留

​ SOURCE:只在源码上保留(默认)

​ CLASS:在源码和字节码上保留

​ RUNTIME:在所有的阶段都保留

.java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME)

注解解析
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了AnnotatedElement

  • T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的注解有没有。

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

@Retention(RetentionPolicy.RUNTIME) // 注解的存活周期到任何阶段
@Target({ElementType.METHOD})// 只能注解方法了
public @interface MyTest {
}

```java
public class TestDemo {

    @MyTest
    public void show1(){
        System.out.println("show1方法执行了");
    }


    public void show2(){
        System.out.println("show2方法执行了");
    }

    public void show3(){
        System.out.println("show3方法执行了");
    }
    @MyTest
    public void show4(){
        System.out.println("show4方法执行了");
    }

    public static void main(String[] args) throws Exception{
        Class<TestDemo> clazz = TestDemo.class;

        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                method.invoke(clazz.newInstance());
                /*
                show1方法执行了
                show4方法执行了
                * */
            }
        }
    }
}
``
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值