• Java笔记(JUnit、反射、注解)


    一、JUnit单元测试


    1. JUnit的介绍


    JUnit是一个Java语言的单元测试工具。有了它我们在开发阶段就可以对自己编写的功能模块进行单元测试(就是一块一块去测试),看看是否达到具体预期(这样小Bug我们自己就能解决)。

    黑盒测试:不需要写代码,给定输入值,看程序是否能够输出期望的值。

    白盒测试:需要写代码的。关注程序具体的执行流程。

    在这里插入图片描述

    JUnit通过注解识别测试方法:@Test@Before@After

    • @Test:用于修饰需要执行的测试方法。
    • @Before:修饰的方法会在测试方法之前被自动执行。
    • @After:修饰的方法会在测试方法执行之后自动被执行。

    JUnit以上三种单元测试方法的注意事项:

    • 测试方法不能是静态方法。
    • 测试方法不能有返回值。
    • 测试方法不能有参数。


    2. JUnit的使用


    1、下载JUnit相关的jar包

    junit-4.12下载地址: junit-4.12 https://mvnrepository.com/artifact/junit/junit/4.12

    hamcrest-core下载地址: https://mvnrepository.com/artifact/org.hamcrest/hamcrest-core/1.3

    在这里插入图片描述


    2、在模块下面创建一个lib目录,用于存放jar包

    在这里插入图片描述


    3、选中lib目录并鼠标右键,然后找到【Add as Library】并点击,将jar包添加到模块中。

    在这里插入图片描述

    在这里插入图片描述


    4、创建一个测试类,编写测试方法,分别@Test、@Before、@After注解修饰。

    /**
     * Junit快速入门
     * @author 白豆五
     * @version 2022/11/19 22:05
     * @since JDK8
     */
    public class JUnitTest {
        // 定义静态成员变量 便于让测试方法访问
        private static List<Integer> list = new ArrayList<>();
    
    	// 在测试方法之前执行
        @Before
        public void init() {
            list.add(111);
            list.add(222);
            list.add(13);
            list.add(25);
            list.add(16);
        }
    
    	//测试方法
        @Test
        public void test() {
            list.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o1 - o2;
                }
            });
        }
    
        // 在测试方法之后执行
        @After
        public void print() {
            System.out.println(list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

    二、反射(Reflection)


    1. 类加载器


    1.1 类加载器的时机

    当程序运行之后,第一次使用某个类的对象,类加载器(ClassLoader)会将该类的.class文件从磁盘加载到内存中,然后将该类的信息(如 成员变量、成员方法、构造方法)存储到 java.lang.Class对象中。

    在这里插入图片描述


    什么时候类加载器会将类的字节码文件加载到内存,并使用Class对象存储类的相关信息?

    1、创建类的实例。

    2、使用类的静态变量。

    3、使用这个类的静态方法。

    4、使用反射的方式创建某个类或者接口的对应Class对象。

    5、初始化某个类的子类。

    6、使用java命令运行某个主类(带main方法的类)。

    public class TestClassLoder {
    
        /**
         * 类加载器的时机
         */
        @Test
        public void testClassLoder() {
    
            // Animal animal = new Animal(); // 1、创建类的实例
            // Animal.color="red";           // 2、使用类调用静态成员变量
            // Animal.print();               // 3、使用类调用静态方法
    
            /*
            try {
                // 4、通过反射加载这个类获取字节码对象
                Class clazz = Class.forName("com.baidou.p3_loder_time.Animal");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }*/
    
            // 5、初始化子类
            Animal dog = new Dog();
    
        }
    }
    
    class Animal {
        // 静态成员变量
        static String color = "blue";
    
        // 静态代码块在类加载的时候会被执行,而且只会执行一次
        static {
            System.out.println("执行了Animal的静态代码块");
        }
    
        // 静态方法
        static void print() {
            System.out.println(color);
        }
    }
    
    class Dog extends Animal {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    1.2 类加载器的分类

    Java中有三种不同的类加载器(ClassLoader)用于加载不同种类的class文件:(JDK8)

    1、BootstrapClassLoader:根类加载器,也被称为引导类加载器。

    • 负责Java核心类库的加载(如 jdk1.8.0_201\jre\lib\rt.jar)

      在这里插入图片描述

    • 它是用C++编写的,是JVM自带的类加载器。

    • 该加载器无法直接获取。(如 System、String获取加载器返回的值为null)


    2、ExtClassLoader:扩展类加载器。

    • 负责加载JRE的扩展目录中的jar包。(如 jdk1.8.0_201\jre\lib\ext)
      在这里插入图片描述

    3、AppClassLoader:系统类加载器/应用类加载器。

    • Java语言编写的类加载器,用于加载我们定义的类和第三方jar包中的类

    类加载器加载机制

    双亲委派机制: 谁用谁加载。

    当加载类的时候,首先会问委托父加载器(如AppClassLoader)它负不负责加载,如果它不会则继续向上找。但是不管是谁加载 .class文件只能被加载一次。

    在这里插入图片描述
    这三个类加载器之间不存在继承关系,ClassLoader是的他们的最终父类。


    1.3 如何获取类加载器

    用Class对象.getClassLoader()方法获取类加载器。

    在这里插入图片描述
    在这里插入图片描述

    import org.junit.Test;
    import sun.net.spi.nameservice.dns.DNSNameService;
    
    /**
     * 获取类加载器
     * @author 白豆五
     * @version 2022/11/21 19:49
     * @since JDK8
     */
    public class Demo04ClassLoder {
    
        /**
         * 获取根类加载器
         */
        @Test
        public void boot() {
            // String类 是核心类库rt.jar里的 由BootstrapClassLoader负则加载
            // 获取String类的Class对象
            Class<String> clazz = String.class;
            // 获取类加载器对象
            ClassLoader classLoader = clazz.getClassLoader();
            System.out.println(classLoader);// BootstrapClassLoader是根类加载器是C++编写,不让我们直接获取,所以返回值为null
        }
    
        /**
         * 获取扩展加载器
         */
        @Test
        public void ext() {
            // DNSNameService是扩展类  由ExtClassLoader负则加载
            // 获取DNSNameService类的Class对象
            Class<DNSNameService> clazz = DNSNameService.class;
            // 获取类加载器对象
            ClassLoader classLoader = clazz.getClassLoader();
            System.out.println(classLoader);//sun.misc.Launcher$ExtClassLoader@28a418fc
        }
    
        /**
         * 获取应用类加载器
         */
        @Test
        public void app() {
            // Demo04ClassLoder类是自己编写的类  由AppClassLoader负则加载
            // 获取Demo04ClassLoder类的Class对象
            Class<Demo04ClassLoder> clazz = Demo04ClassLoder.class;
            // 获取类加载器对象
            ClassLoader c1 = clazz.getClassLoader();
            System.out.println(c1);//sun.misc.Launcher$AppClassLoader@18b4aac2
            // 获取应用类加载器的委托父加载器对象(扩展类加载器)
            ClassLoader c2 = c1.getParent();
            System.out.println(c2);//sun.misc.Launcher$ExtClassLoader@28a418fc
            // 获取扩展类加载器的委托父加载器对象(根类加载器)
            ClassLoader c3 = c2.getParent();
            System.out.println(c3);//因他是c++编写不让直接获取,所以拿到的是null
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    在这里插入图片描述


    2. 反射的介绍


    原文:https://blog.csdn.net/weixin_42298270/article/details/113371164


    反射机制指的是程序在运行时能够获取自身的信息。在Java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。
    要使用一个类,就要先把它加载到虚拟机中,生成一个Class对象。这个Class对象就保存了这个类的一切信息。反射机制的实现,就是获取这个Class对象,通过Class对象去访问类、对象的元数据以及运行时的数据。

    反射是一种机制,利用该机制在程序运行的过程中,对类进行解剖并且去操作类的成员(成员变量,成员方法,构造方法)。要想使用反射,就必须要获取该类的字节码对象(也叫Class对象)。

    反射的应用场景:如主流的开发框架(如spring-frammework、mybaits等等)、IDEA(智能化的语法提示等等)。


    3. Class类的对象


    Class类也是有对象的,但是程序员无法自己创建,由JVM帮助创建,程序员可以获取到该Class类型的对象,从而完成相关的操作。

    3.1. 获取Class对象的三种方法

    • 方式一:Class c1 = Class.forName("类的全名称,即包名.类名");, forName是Class类的静态方法。
    • 方式二:Class c2 = 类名.class属性,每个类都默认有一个class属性。
    • 方式三:Class c3 = 对象.getClass();,getClass是Object类的方法。

    pojo类:

    public class User {
        private String name;
        private int age;
    
        //空参构造
        public User() {
        }
    
        //满参
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    
        //只给name赋值
        public User(String name) {
            this.name = name;
        }
    
        //只给age赋值
        private User(int age) {
            this.age = age;
        }
    
        private char[] my2CharArray(String str) {
            return str.toCharArray();
        }
    
        public int getSum(int a, int b) {
            return a + b;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    测试类:

    /**
     * 获取Class对象
     *
     * @author 白豆五
     * @version 2022/11/21 21:49
     * @since JDK8
     */
    public class Demo04GetClass {
    
    
        /**
         * 方式一:对象.getClass()获取对应的字节码对象
         */
        @Test
        public void test1(){
            User user = new User();
            Class c1 = user.getClass();
            //调用Class类的toString()方法
            System.out.println(c1); //class com.baidou.pojo.User
        }
    
    
        /**
         * 方式二:类名.class属性获取对应的字节码对象
         */
        @Test
        public void test2(){
            Class<User> c2 = User.class;
            //调用Class类的toString()方法
            System.out.println(c2); //class com.baidou.pojo.User
        }
    
        /**
         * 方式三:Class.forName()获取对应的字节码对象
         */
        @Test
        public void test3() throws ClassNotFoundException {
            Class<?> c3 = Class.forName("com.baidou.pojo.User");
            System.out.println(c3); //class com.baidou.pojo.User
        }
    
    
        /**
         * 基本数据类型,也有对应的Class对象
         */
        @Test
        public void test4(){
            Class<Integer> c1 = int.class;
            System.out.println(c1); //int
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    3.2 Class类的常用方法

    下面的成员方法都是通过与类关联的Class对象调用。

    • public String getName():获取类的全路径名,即包名+类名。
    • public String getSimpleName():获取类的类名(类名、接口名、注解名等等)。
    • public T newInstance()throws InstantiationException, IllegalAccessException:使用反射的方式创建对象,会调用该类的无参构造方法,如果该类没有无参构造方法会报 InstantiationException(实例化异常)
    /**
     * Class类的常用方法
     */
    @Test
    public void testClassNewInstance()  {
        // 获取Animal类的字节码对象
        Class<Animal> animalClass = Animal.class;
    
        String name = animalClass.getName();  //获取Animal类的全路径名,包名+类名
        System.out.println(name); //com.baidou.p3_loder_time.Animal
    
        System.out.println(animalClass.getSimpleName()); //获取类名
    
        try {
            // 反射的方式创建animal对象
            Animal animal = animalClass.newInstance();
            System.out.println(animal);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    Java源程序通过编译会生成字节码文件,字节码文件会被类加载器加载到内存方法区中,同时针对.class文件创建一个Class类型的对象(存储类的信息),然后该对象被保存到堆内存中。这个Class对象里面封装了操作构造器、成员方法、成员变量的api,这样我们通过反射可以操作.class文件中成员变量、成员方法、构造方法啦。


    4. 反射获取构造方法(Constructor)


    反射获取构造方法的步骤:
             1、获取类的Class对象
             2、Class对象.get构造器的方法。

    4.1 获取构造方法

    1、返回一个public修饰的指定参数构造方法,不传参默认调用无参构造方法。

    public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException                                             
    
    • 1

    2、返回多个public修饰的构造方法。

    public Constructor<?>[] getConstructors() throws SecurityException
    
    • 1

    3、返回一个任意修饰符的指定参数构造方法。

    //返回一个任意修饰符的指定参数构造方法
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
    												throws NoSuchMethodException,SecurityException      
    
    
    // 暴力反射
    //java.lang.reflect.AccessibleObject类,成员方法:
    public void setAccessible(boolean flag)
           // 参数:true,取消 Java 语言访问检查        private 失效
           // 参数:false,实施 Java 语言访问检查       private 有效    
    // 私有构造方法对象调用setAccessible()方法,取消Java语言访问检查
    constructor.setAccessible(true);
    
    //Constructor,Field,Method类,都可以调用setAccessible方法,进行暴力反射                     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4、返回多个任意修饰符的构造方法。

    public Constructor<?>[] getDeclaredConstructors() throws SecurityException
    
    • 1

    示例:

    public class User {
        private String name;
        private int age;
    
        //空参构造
        public User() {
        }
    
        //满参
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    
        //只给name赋值
        public User(String name) {
            this.name = name;
        }
    
        //只给age赋值
        private User(int age) {
            this.age = age;
        }
    
        private char[] my2CharArray(String str) {
            return str.toCharArray();
        }
    
        public int getSum(int a, int b) {
            return a + b;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    (1) 获取public修饰的所有构造方法对象

    /**
     * 获取public修饰的所有构造方法对象
     */
    @Test
    public void test1() throws ClassNotFoundException {
        //获取类的Class对象
        Class<?> c1 = Class.forName("com.baidou.pojo.User");
        //取public修饰的所有构造方法对象
        for (Constructor<?> cons : cons1) {
            System.out.println(cons);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    (2) 获取所有构造方法对象(包含private修饰的)

    /**
     * 获取所有构造方法对象(包含private修饰的)
     */
    @Test
    public void test2() throws ClassNotFoundException {
        //获取类的Class对象
        Class<?> c2 = Class.forName("com.baidou.pojo.User");
        // 获取所有构造方法对象(包含private修饰的)
        Constructor<?>[] cons2 = c2.getDeclaredConstructors();
        for (Constructor<?> constructor : cons2) {
            System.out.println(constructor);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述


    (3) 获取public修饰的空参构造方法对象

    /**
     * 获取public修饰的空参构造方法对象
     */
    @Test
    public void test3() throws Exception {
        //获取类的Class对象
        Class<?> c3 = Class.forName("com.baidou.pojo.User");
        //获取public修饰的空参构造方法对象
        Constructor<?> cons3 = c3.getConstructor();
        System.out.println(cons3);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述


    (4) 获取public修饰的第一个参数是String类型,第二个参数是int类型的构造方法对象

    /**
     * 获取public修饰的第一个参数是String类型,第二个参数是int类型的构造方法对象
     */
    @Test
    public void test4() throws Exception {
        //获取类的Class对象
        Class<?> c4 = Class.forName("com.baidou.pojo.User");
        //获取public修饰的第一个参数是String类型,第二个参数是int类型的构造方法对象
        Constructor<?> cons4 = c4.getConstructor(String.class,int.class);
        System.out.println(cons4);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述


    (5) 获取private修饰的参数是int类型的构造方法对象

    /**
     * 获取private修饰的参数是int类型的构造方法对象
     */
    @Test
    public void test5() throws Exception {
        //获取类的Class对象
        Class<?> c5 = Class.forName("com.baidou.pojo.User");
        //获取private修饰的参数是int类型的构造方法对象
        Constructor<?> cons5 = c5.getDeclaredConstructor(int.class);
        System.out.println(cons5);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述


    4.2 执行构造方法创建具体对象

    1、调用java.lang.reflect.Constructor类中的newInstance()方法:

    // 根据方法参数传递的具体数据,调用指定的构造方法,从而创建一个具体的对象
    // 可变参数 可以是0个或多个 ,Object[] 
    public T newInstance(Object ... initargs)
    
    • 1
    • 2
    • 3

    2、快捷方式:Class对象.newInstance()方法,调用该类的无参构造方法创建对象,如果该类没有无参构造方法会报 InstantiationException(实例化异常)。

    public T newInstance()throws InstantiationException, IllegalAccessException
    
    • 1

    示例:反射获取空参构造方法并运行

    /**
    * 反射获取空参构造方法并运行
    */
    @Test
    public void newInstance1() throws Exception {
       // 获取字节码对象
       Class<?> clazz = Class.forName("com.baidou.pojo.User");
       // 反射获取空参构造方法对象
       Constructor<?> con = clazz.getConstructor();
       // 执行空参构造方法对象,创建具体的实例
       User user = (User) con.newInstance();
       System.out.println(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述


    示例:反射带参构造方法并运行

    /**
    * 反射带参构造方法并运行
    */
    @Test
    public void newInstance2() throws Exception {
       // 获取字节码对象
       Class<?> clazz = Class.forName("com.baidou.pojo.User");
       // 反射获取满参构造方法对象 public User(String name,int age)
       Constructor<?> con = clazz.getConstructor(String.class,int.class);
       // 执行满参构造方法对象,创建具体的实例
       User user = (User) con.newInstance("白豆五",18);
       System.out.println(user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述


    示例:反射创建对象的快捷方式

    /**
     * 反射创建对象的快捷方式
     */
    @Test
    public void newInstance3() throws Exception {
        // 获取字节码对象
        Class<?> clazz = Class.forName("com.baidou.pojo.User");
        // 反射创建对象的快捷方式 class对象.newInstance()方法
        Object obj =  clazz.newInstance();
        System.out.println(obj);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述


    示例:反射获取私有构造方法并运行

    /**
    * 反射获取私有构造方法并运行
    */
    @Test
    public void newInstance4() throws Exception {
       // 获取字节码对象
       Class<?> clazz = Class.forName("com.baidou.pojo.User");
       // 反射获取私有构造方法对象 p private User(int age)
       Constructor<?> con1 = clazz.getConstructor( int.class);
       // 执行私有构造方法对象,创建具体的实例
       User user = (User) con1.newInstance( 18);
       System.out.println(user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    解决方案:使用getDeclaredConstructor()方法获取私有方法

    /**
     * 反射获取私有构造方法并运行
     */
    @Test
    public void newInstance4() throws Exception {
        // 获取字节码对象
        Class<?> clazz = Class.forName("com.baidou.pojo.User");
        // 反射获取私有构造方法对象 p private User(int age)
        // Constructor con1 = clazz.getConstructor( int.class); //NoSuchMethodException
        Constructor<?> con2 = clazz.getDeclaredConstructor(int.class);
        // 执行私有构造方法对象,创建具体的实例
        User user = (User) con2.newInstance( 18);
        System.out.println(user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    解决方案:取消访问检查

    /**
     * 反射获取私有构造方法并运行
     */
    @Test
    public void newInstance4() throws Exception {
        // 获取字节码对象
        Class<?> clazz = Class.forName("com.baidou.pojo.User");
        // 反射获取私有构造方法对象 p private User(int age)
        // Constructor con1 = clazz.getConstructor( int.class); //NoSuchMethodException
        Constructor<?> con2 = clazz.getDeclaredConstructor(int.class);
        con2.setAccessible(true); // 取消访问检查
        // 执行私有构造方法对象,创建具体的实例
        User user = (User) con2.newInstance( 18);
        System.out.println(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述



    5. 反射获取成员变量(Field)


    反射第一步先得到类对象,然后从类对象中获取类的成分对象。

    Class类中获取成员变量的方法如下:

    • Field[] getFields():返回所有成员变量对象的数组(只能拿public的)。

    • Field[] getDeclaredFields():返回所有成员变量对象的数组,存在就能拿到。

    • Field getField(String name):返回单个成员变量对象(只能拿public的)。

    • Field getDeclaredField(String name):返回单个成员变量对象,存在就能拿到。


    Field类中取值、赋值的方法如下:

    • void set(Object obj,Object value):为Field对象赋值。
    • Object get(Object obj):获Field对象的取值。

    示例:反射获取成员变量

    User.java

    public class User {
        private String name;
        private int age;
    
        //空参构造
        public User() {
        }
    
        //满参
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    
        //只给name赋值
        public User(String name) {
            this.name = name;
        }
    
        //只给age赋值
        private User(int age) {
            this.age = age;
        }
    
        private char[] my2CharArray(String str) {
            return str.toCharArray();
        }
    
        public int getSum(int a, int b) {
            return a + b;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    /**
     * 反射获取成员变量
     */
    @Test
    public void testFiled() throws Exception {
        // 获取Class对象
        Class<User> clazz = User.class;
        // 快捷方式创建对象
        User user = clazz.newInstance();
        // 获取私有Field对象
        Field f1 = clazz.getDeclaredField("name");
        Field f2 = clazz.getDeclaredField("age");
        f1.setAccessible(true); //取消访问检查
        f2.setAccessible(true); //取消访问检查
        f1.set(user, "张三");                  //为name属性赋值
        f2.set(user, 18);                     //为age属性赋值
        String name = (String) f1.get(user);  //取值
        int age = (int) f2.get(user);         //取值
        System.out.println(name + "::" + age);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    注意:如果某成员变量是非public的,需要打开权限(暴力反射,setAccessible(true)),然后再取值、赋值。



    6. 反射获取方法(Method)


    也是通过Class对象拿Method对象。

    Class类中获取成员方法的方法如下:

    • Method[] getMethods():返回所有成员方法对象的数组(只能拿public的)。

    • Method[] getDeclaredMethods():返回所有成员方法对象的数组,存在就能拿到。

    • Method getMethod(String name,Class...parameterTypes):返回单个成员方法对象(只能拿public的)。

    • Method getDeclaredMethod(String name,Class...parameterTypes):返回单个成员方法对象,存在就能拿到。


    Method类中用于触发执行的方法如下:

    • Object invoke(Object obj, Object... args): 运行方法。
      • 参数一:用obj对象调用该方法。
      • 参数二:调用方法的传递的参数(如果没有就不写)。
      • 返回值:方法的返回值(如果没有就不写)。

    示例1:获取setName()、getName()、toString()方法并执行。

    /**
     * 获取setName()、getName()、toString()方法并执行
     * public String getName()
     * public void setName(String name)
     * public String toString()
     */
    @Test
    public void testgetMethoods() throws Exception {
        // 获取Class对象
        Class<User> clazz = User.class;
        // 快捷方式创建对象
        User user = clazz.newInstance();
        // 获取Method对象
        Method setName = null;
        Method getName = null;
        Method toString = null;
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            switch (method.getName()) {
    
                case "setName":
                    setName = method;
                    break;
                case "getName":
                    getName = method;
                    break;
                case "toString":
                    toString = method;
                    break;
            }
        }
    
        // System.out.println(setName);
        // System.out.println(getName);
        // System.out.println(toString);
    
        // 执行Method对象对应的方法
        setName.invoke(user,"张三");
        Object name = getName.invoke(user);
        System.out.println(name);
        Object tostr = toString.invoke(user);
        System.out.println(tostr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述



    三、注解(Annotation)


    1. 什么是注解?


    • 注解(Annotation): 也叫元数据。一种代码级别的说明。
    • 它是JDK5.0引入的一个新特性,与类、接口、枚举是在同一个层次。
    • 它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,标注/注释(comment)。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述


    2. 注解的作用


    • 编写文档:通过代码里标识的注解生成文档。(例如,生成文档javadoc文档)
    • 代码分析:通过代码里标识的注解对代码进行分析。(例如,注解的反射)
    • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查。(例如,Override覆盖重写)

    3. 常见注解


    • @author:用来标识作者名。
    • @version:用于标识对象的版本号,适用范围:文件、类、方法。
    • @Override:用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。
    • @FunctionalInterface: 检测是否是函数式接口(JDK8新特性)。
    • @Deprecated:可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告(删除线 )。
    • @SuppressWarnings:取消显示指定的编译器警告。
      • @SuppressWarnings(value=“all”):抑制所有警告。
      • @SuppressWarnings(value=“deprecation”): 抑制过期方法警告。

    等等…


    4. 自定义注解


    自定义注解就是自己做一个注解来玩玩。

    使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口。

    自定义注解的格式:

    public @interface 注解名 {
        public 属性类型 属性名() default 默认值;      
        // 注解中可以定义0个或者多个属性      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 空注解: 没有任何属性 。
    // 定义空注解
    public @interface MyTest1 {
        //没有任何属性 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 有属性集的注解:

      • 属性的定义格式一:数据类型 属性名(); //没有默认值的属性。
      • 属性的定义格式二:数据类型 属性名() default 默认值; 有默认值的属性。
    • 注解属性可以选择的数据类型有:8大基本数据类型、String类型、枚举类型、注解类型、Class类型 以及以上类型的数组形式。

    • 注解属性不能有不确定的值,要么在定义注解时有默认值,要么在使⽤注解的时候提供属性的值,⽽且注解属性不能使⽤ null作为默认值,通常⽤空字符或0作为默认值;

    //定义有属性集的注解
    public @interface MyTest2  {
        String name();//String 类型的属性 name,没有默认值
        int age() default 18;//int 类型的属性 age,默认值 18
        String[] hobbies();//String 类型的数组 hobbies
        MyTest1 myTest1 ();//注解类型
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    特殊属性:(value)

    • 如果注解中只有⼀个属性且该属性的名称是 value 的情况下,那么在使⽤注解的时候可以省略value= ,直接写需要的值即可。

    在这里插入图片描述

    • 但是如果有多个属性,且多个属性没有默认值,那么vlue名称是不能省略的。

    在这里插入图片描述



    5. 元注解


    元注解:用于限制注解的注解。

    常见的两个元注解:

    • @Target:约束自定义注解只能在那些地方使用。
    • @Retention:用来控制注解的生命周期。

    1、@Target中可使用的值定义在 java.lang.annotation.ElementType枚举类中(里面都是静态内容,直接用类名/枚举名调用),常用的值如下:

    在这里插入图片描述

    • TYPE: 用在类、接口上。
    • FIELD:用在成员变量上。
    • METHOD: 用在方法上。
    • PARAMETER:用在参数上。
    • CONSTRUCTOR:用在构造方法上。
    • LOCAL_VARIABLE:用在局部变量上。



    2、@Retention中可使用的值定义在 java.lang.annotation.RetentionPolicy 枚举类中,常用值如下:

    在这里插入图片描述

    • SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在。(不常用)

    • CLASS:注解作用在源码阶段、字节码文件阶段,运行阶段不存在,默认值。(不常用)

    • RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(常用)。


    示例:元注解的使用

    /**
     * 元注解Target和@Retention的使用
     *
     * @Target:约束自定义注解只能在那些地方使用。
     * @Retention:用来控制注解的生命周期。
     */
    @Target({ElementType.METHOD, ElementType.FIELD}) //元注解@Target,我们约定这个注解只能用在方法和成员变量上
    @Retention(RetentionPolicy.RUNTIME) //一直存活着,程序可以通过反射获取该注解。
    public @interface MyTest {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    //测试类
    
    // @MyTest //只能用在方法和成员变量上
    public class AnnotationDemo03 {
    
        @MyTest
        private String name;
    
        @MyTest
        public static void main(String[] args) {
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述


    6. 注解的解析


    注解的解析:就是通过反射获取类上或者方法上以及成员上标注的注解,如果注解存在,就把注解的内容解析出来。

    6.1 与注解解析相关的接口

    • java.lang.Annotation:注解的顶级接口,注解都是Annotation类型的对象。
    • java.lang.reflect.AnnotatedElement:该接口定义了与注解解析相关的解析方法。
    • 所有的类成分Class、Method、Field、Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力。

    在这里插入图片描述

    解析注解常用方法:

    常用方法解释说明
    public Annotation[] getDeclaredAnnotations() 获得当前对象上使用的所有注解,返回的是注解数组。
    public T getDeclaredAnnotation(Class annotationClass)根据注解类型获得对应注解对象。
    public boolean isAnnotationPresent(Class annotationClass)判断当前对象是否使用了指定的注解,如果使用了则反回true,否则返回lfalse。

    解析注解的突发小技巧💡:

    • 注解在哪个成分上,我们就先拿哪个成分对象。
    • 比如注解作用在成员方法上,则要获得该成员方法对应的Method对象,再来拿上面的注解。
    • 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解。
    • 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解。

    7. 获取类上的注解


    步骤:拿类对象,判断类上是否有注解,获取注解对象。

    自定义注解:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义注解
     * 限制注解使用的位置:类和成员方法上
     * 指定注解的有效范围:RUNTIME 运行阶段
     */
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Book {
        String value();
        double price() default 39.9;
        String[] authors();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试类:

    /**
     * 解析类上的注解
     *
     * @author 白豆五
     * @version 2022/11/25 22:12
     * @since JDK8
     */
    public class RarseAnnotationDemo01 {
        public static void main(String[] args) {
            // 1、拿到BookStore类的Class对象
            Class<BookStore> clazz = BookStore.class;
            // 2、判断这个类上是否存在@Book注解
            if (clazz.isAnnotationPresent(Book.class)) {
           		//类上有注解
                // 3、获取该注解对象 Book
                Book book = clazz.getDeclaredAnnotation(Book.class);
                String value = book.value();
                String[] authors = book.authors();
                double price = book.price();
                System.out.println("书名:"+value);
                System.out.println("价格:"+price);
                System.out.println("作者:"+ Arrays.toString(authors));
    
            }
        }
    }
    
    @Book(value = "<<明天会更好>>", price = 19.9, authors = {"王景", "亮丽"})
    class BookStore {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述


    8. 获取方法上的的注解


    步骤:拿类对象,拿方法对象,判断方法上是否有注解,获取注解对象。

    自定义注解:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义注解
     * 限制注解使用的位置:类和成员方法上
     * 指定注解的有效范围:RUNTIME 运行阶段
     */
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Book {
        String value();
        double price() default 39.9;
        String[] authors();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试类:

    /**
     * 解析方法上的注解
     *
     * @author 白豆五
     * @version 2022/11/25 22:12
     * @since JDK8
     */
    public class RarseAnnotationDemo02 {
        public static void main(String[] args) throws NoSuchMethodException {
            // 1、拿到BookStore类的Class对象
            Class<BookStore2> clazz = BookStore2.class;
            //  2、获取Method对象
            Method testMethod = clazz.getMethod("test");
            //  3、判断方法上是否有@Book注解
            if (testMethod.isAnnotationPresent(Book.class)) {
                // 4、如果有就获取该注解对象
                Book book = testMethod.getDeclaredAnnotation(Book.class);
                String value = book.value();
                String[] authors = book.authors();
                double price = book.price();
                System.out.println("书名:" + value);
                System.out.println("价格:" + price);
                System.out.println("作者:" + Arrays.toString(authors));
            }
        }
    }
    
    
    class BookStore2 {
    
        @Book(value = "<<程序猿护法宝典>>", price = 39.9, authors = {"亮亮", "晶晶"})
        public void test() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

    9. 模拟JUnit的@Test注解案例


    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义注解@MyTest, 并添加元注解,保证自定义注解只能修饰方法,且在运行时可以获得。
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyTest {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    /**
     * 模拟有注解的方法才会被调用
     */
    public class MyTestDemo {
    
        public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
            MyTestDemo mtd = new MyTestDemo();
    
            // 1、获取类对象
            Class clazz = MyTestDemo.class;
            // 2、获取所有方法对象
            Method[] methods = clazz.getMethods();
            // 3、遍历所有方法
            for (Method method : methods) {
                // 4、判断方法上是否有@MyTest注解
                if (method.isAnnotationPresent(MyTest.class)) {
                    // 5、触发执行方法
                    method.invoke(mtd);
                }
            }
        }
    
        @MyTest
        public void test1() {
            System.out.println("MyTestDemo__>test1()");
        }
    
        public void test2() {
            System.out.println("MyTestDemo__>test2()");
        }
    
        @MyTest
        public void test3() {
            System.out.println("MyTestDemo__>test3()");
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    在这里插入图片描述

  • 相关阅读:
    AI工具-PPT-SlidesAI
    MySql 事务隔离实现:
    痞子衡嵌入式:浅谈i.MXRT1xxx系列MCU时钟相关功能引脚的作用
    shell递归复制文件夹(年月日结构)下的所有txt文件
    1042 Shuffling Machine
    Mysql8.0 执行授权语句错误:ERROR 1064 (42000)
    IDEA 2022 创建 Maven Web 项目教程
    docker相关命令
    ELK中 Elasticsearch和Logstash内存大小设置的考虑
    LeetCode笔记:Biweekly Contest 85
  • 原文地址:https://blog.csdn.net/qq_46921028/article/details/127962551