【Java34】反射,注解


1.Class对象三种获取方式

package com.itheima02.clazz;
import org.junit.Test;
/*
*   反射前提: Class对象
*       1. 引用类型: Class(大写C和关键字class不一样)
*       2. 回顾: 当程序用到一个类的时候, 会加载这个类进内存(方法区)
*       3. 在运行时, java用Class对象来描述.class文件(就算是一个文件也用对象描述)
*              .class文件(硬盘) ->  Class对象(内存)
* 
*       4. Class对象的特点:
*           1. Class对象的创建: 
* 				一个.class文件被加载进内存,JVM会创建这个类的Class对象。
* 				因为一个.class文件在使用的时候只需要被加载一次, 所以这个.class文件对应的Class对象也只会有一个!!!
* 
*           重点: Class对象是JVM创建的, 开发者是无法创建的。(开发者可以new一个object等)
* 				 一个类的Class对象一般只有一个 (使用: 同步锁 xxx.class对象:别人不能创建,天然唯一安全)
* 
*           2. 三种Class对象的获取(不是创建)
*               1. 类名.class
*               2. 对象名.getClass()
*               3. Class.forName(全限定名);  -> 加载配置文件
*/
public class ClassDemo {
    
    
    @Test
    public void method01(){
    
      
        Class<Student> clazz = Student.class;            
        System.out.println(clazz); //class com.itheima02.clazz.Student //全类名或全限定名 : 包名 + 类名
        synchronized (ClassDemo.class){
    
     //当前类名.class  -> 锁对象
            //这段代码能够运行,意味着当前类一定被加载进内存-> JVM会创建当前类的Class对象
            //ClassDemo.class改为string.class浪费内存,因为不一定用到string,没必要加载string类
        }
    }
    
    @Test
    public void method02(){
    
    
        Student s = new Student();
        Class<?> clazz = s.getClass();
        System.out.println(clazz); //class com.itheima02.clazz.Student 同上
        System.out.println(clazz == Student.class); //true
    }
    
    @Test
    public void method03() throws ClassNotFoundException {
    
    
        Class<?> clazz = Class.forName("com.itheima02.clazz.Student");
        System.out.println(clazz); //class com.itheima02.clazz.Student 同上
    }
}
package com.itheima02.clazz;
import java.util.Objects;

public class Student {
    
    
    private String name;
        
    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) return true;
        // 如下this.getClass() == o.getClass()
        // o instanceof Student
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name);
    }
    
    @Override
    public int hashCode() {
    
    
        return Objects.hash(name);
    }
}

2.构造的基本/暴力反射

package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
*  反射(reflect) : 在程序运行时操作类
 *       1. 运行时 :  .java源码 -> .class字节码(编译) -> runtime(运行)
 *       2. 操作 :   
 *       3. 类 :   .java  -> .class(二进制指令让jvm读) -> Class对象(运行时的类的样子)
 *            1. 构造方法 / 构造器 Constructor
 *            2. 方法 Method
 *            3. 属性 Field
 *   A. 操作构造方法: 以前: 用构造方法来 创建对象
 */
public class ConstructorDemo {
    
    
    public static void main(String[] args) {
    
    
//        Person p = new Person(); //Person{name='null',age=0}
        Person p = new Person("张三");
        System.out.println(p); //Person{name='张三',age=0}
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    
    
        // 反射 : 操作构造方法
        //1. 获取Class对象
        //2. 再获取Class对象中的构造方法
        //3. 使用构造方法创建对象
        Class clazz = Person.class;
        
        /*
        *  Constructor<T> getConstructor(Class<?>... parameterTypes)
        *   1. 参数: 需要指定想要获取的构造方法的参数列表  类型
        *   2. 返回值: 返回构造方法的对象
        */
        //Constructor constructor = clazz.getConstructor(String.class,int.class);
        Constructor constructor = clazz.getConstructor(String.class);
        
        /*
        *   T newInstance(Object ... initargs)
        *       1. 参数: 使用构造方法创建对象时传入的实参 (必须跟上面参数列表类型一致)
        *       2. 返回值: 返回创建好的实例
        */
        Object obj = constructor.newInstance("张三");
        System.out.println(obj); //Person{name='张三',age=0}
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method02() throws NoSuchMethodException, IllegalAccessException,   InvocationTargetException, InstantiationException {
    
      //简易的api
        Class clazz = Person.class;        
       /* Constructor constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();
        System.out.println(obj); //Person{name='null',age=0}  */        
 //Class对象获取空参构造,并创建实例,如下简单,等同于上面两行//(JavaBean要求: 1. private属性 2. public 空参构造 3. public get set方法)
        Object obj = clazz.newInstance();
        System.out.println(obj); //Person{name='null',age=0}
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    
     //暴力反射: 知道(可以访问private方法)
        Class clazz = Person.class;
        
        /*
        *   getConstructor()  : 只能获取public修饰的构造
        *   getDeclaredConstructor() : 获取所有权限的构造(private也可以)
        */
//        Constructor constructor = clazz.getConstructor(String.class, int.class);
        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);        
        constructor.setAccessible(true); //暴力反射: 权限设置public //上面能拿到私有,但是不能调用,只能person本类内部调用 //所以加这行可以调用
        Object obj = constructor.newInstance("李四", 18);
        System.out.println(obj); //Person{name='李四',age=18}
    }
}
package com.itheima03.reflect;

public class Person {
    
    
    public String name;
    private int age;
    public Person(){
    
    
    }
    public Person(String name){
    
    
        this.name = name;
    }
    private Person(String name,int age){
    
     //注意private,用暴力反射
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public void speak(String msg){
    
    
        System.out.println("speak:" + msg);
//        return 1;
    }
    public void speak(double msg){
    
    
        System.out.println("speak:" + msg);
    }
    private void show(int year){
    
    
        System.out.println("show:" + year);
    }
}

3.方法的反射

package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// B.方法的反射(反射中最重要的是构造方法和普通方法反射,没有属性)
public class MethodDemo {
    
    
    public static void main(String[] args) {
    
    
       Person p = new Person();
       // Person p = null; //speak方法设为静态,不报错
       p.speak("呵呵"); 
       System.out.println(number); //speak:呵呵
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
    
        //1. 获取Class对象
        //2. 获取方法 .getMethod
        //3. 调用方法 .invoke
        Person p = new Person();
        Class<?> clazz = p.getClass();
        /*
        *   Method getMethod(String name, Class<?>... parameterTypes)
        *   1 .参数
        *        1. name 方法名
        *        2. name 方法名的参数列表
        *   2. 返回值: 获取的这个方法
        */
        Method method = clazz.getMethod("speak", String.class);
        /*
        *   Object invoke(Object obj, Object... args)
        *   1. 参数
        *       1. obj : 调动此方法的对象,speak前加一个static,null也可以
        *       2. args : 调用此方法传入的实参
        *   2. 返回值: 调用此方法产生的返回值,如果此方法没有返回值,将会null
        * */
        Object result = method.invoke(p, "嘻嘻");
        //Object result = method.invoke(null, "嘻嘻"); //speak方法设为静态,不报错
        System.out.println(result); //speak:嘻嘻
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
    
        Person p = new Person();
        Class<? extends Person> clazz = p.getClass();
        Method show = clazz.getDeclaredMethod("show", int.class); //暴力反射   
        show.setAccessible(true); //临时修改权限
        show.invoke(p,15); //show:15
    }
}

4.属性的反射

package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Field;
//属性的反射一般不用,关于属性值设置通过反射get和set方法,又回到了方法的反射
public class FieldDemo {
    
    
    public static void main(String[] args) {
    
    
        Person p = new Person();
        p.name = "啦啦";
        System.out.println(p); //Person{name='啦啦',age=0}
    }
//下面写那么多代码为了实现上面3行
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
    
    
        //1.获取Class对象
        //2.获取属性对象
        //3.操作属性,get / set
        Class<?> clazz = Class.forName("com.itheima03.reflect.Person");
        Object p = clazz.newInstance();
        /*
        * Field getField(String name)
        *   1. 参数: 属性名
        *   2. 返回值: 通过属性名找到的属性对象
        */
        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true);
        /*
        * set(Object obj, Object value) 
        *   1. 设置属性的对象
        *   2. 设置的属性值
        */
        age.set(p,18); //很少用,一般get和set方法,又回到方法的反射
        System.out.println(p);//Person{name='null',age=18}
    }
}

5.注解的基本语法

package com.itheima04.annotation;
import java.util.Date;
/*
*       注解: annotation ( 注释;注解)
*           0. java 四大类型: class interface  enum枚举   annotation注解
*           1. 注释: 一般是给人看的代码解释性说明 (不参与编译, 运行)
*              注解: 给JVM看的代码 解释性说明(可能参与编译或运行)
*           2. 常见
*               1. @Override : 检测此方法是否是重写方法
*               2. @FunctionalInterface:此接口是否是函数式接口
*               3. @Deprecated : 加上方法上,声明此方法过时了(这个方法有更好的替代方案),但是还能用
*/
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        new MyRunnable().run();
    }
}
class MyRunnable implements Runnable{
    
    
    @Deprecated
    @Override
    public void run() {
    
    
        System.out.println("---------");
    }
}
package com.itheima04.annotation; 
import java.util.Date;
/*
*   注解的基本语法
*       1. 关键字: @interface
*       2. 属性:  String name() default "yy"; (注解属性类型: 8大基本类型和String,枚举,注解,Class 。 以及这12种类型的数组。不包括Date等)
*       3. 注解没有方法
* 
*   注解的使用: 可以在代码上加注解
*   定义@Override :
*       1. 适用范围 : 方法上
*       2. 生命周期(从创建到销毁): 
*           SOURCE : 源码  (@Override写在这个时候,没必要到下面两个阶段,节省运行时内存)
*           CLASS : 编译后
*           RUNTIME : 运行时
*   比如: IO流(过河拆桥,用完后即时释放), 泛型(泛型只在编译阶段,运行时也被擦除)
*/
@MyAnno
public class Demo02 {
    
    
    @MyAnno
    public static void main(@MyAnno String[] args) {
    
    
        @MyAnno
        int i;
    }
}
@interface MyAnno{
    
    
    String name() default "yy";
    int[] array();
}

Player表示玩家,Data表示注册账号日期
在这里插入图片描述

6.元注解

package com.itheima04.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
 *    元注解: 用来描述注解的注解 (初始的注解)
 *      元数据 : 诠释数据的数据(如右击一个图片属性查看)  
 *      name = 张裕
 *      属性(元数据) = 值(数据)
 * 
 *    两个元注解
 *      1. @Target  :  适用范围
 *          1. ElementType.Type : 四大类型 上可用
 *          2. ElementType.Method : 方法 上可用
 *          3. ElementType.Constructor : 构造
 *          4. ElementType.Field 属性
 *      2. @Retention :  生命周期,保留策略
 *          1. SOURCE
 *          2. CLASS
 *          3. RUNTIME : 常用
 * 
 *   注解的使用:
 *      1. 可以放在代码上
 *      2. 使用注解的时候,如果有属性没赋值,必须赋值
 *      3. 特殊: 如果注解中的属性有且仅有一个未赋值,并且属性名为value,在赋值的时候,value= 可省略
 */
//@YourAnno(name="yy") 
//@YourAnno(value="yy") //把String value();放开未赋值,对应上面注解使用中的3
//@YourAnno("yy")  //把String value();放开未赋值,对应上面注解使用中的3
public class Demo03 {
    
    
    @YourAnno
    public static void main(String[] args) {
    
    
    }
}

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
//@Target(value={ElementType.METHOD,ElementType.TYPE}) //枚举名.属性 //如下图
//@Target({ElementType.METHOD,ElementType.TYPE}) //缩略写法,override只适用于方法,类型不能用,如下改进
@Target(ElementType.METHOD)//在注解中,如果数组只有一个元素,{}是可以省略的
@Retention(RetentionPolicy.SOURCE) //override注解就是保留在SOURCE源码
@interface YourAnno{
    
    
    String name() default "xx";
//    String value();
}

在这里插入图片描述

7.自定义junit测试框架

package com.itheima05.custom;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
*   思路: @MyTest替换@Test
*       1. 自定义MyTest注解
*           1. 属性 : 无 (有属性必须要赋值,下面没用到MyTest里没有赋值)
*           2. 元注解:  1. 指定作用域: Method
*                      2. 生命周期: runtime(注解保留到运行时期才能碰到反射)
*       2. 反射 : 在运行时操作类
*           需求: 在方法上 加了 MyTest注解的方法能够像main方法一样运行,所以用到反射
*           1. 获取TestDemo 的Class对象
*           2. 获取类中的所有public方法
*           3. 判断每个方法上是否有注解MyTest,如果有,就运行
*/
public class CustomDemo {
    
    
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
    
    
        Class<?> clazz = Class.forName("com.itheima05.custom.TestDemo");
        Object obj = clazz.newInstance();
        
        Method[] methods = clazz.getMethods(); //注意是getMethods最后有一个s,返回数组
        for (Method method : methods) {
    
                    
            boolean result = method.isAnnotationPresent(MyTest.class); // 方法上是否有指定注解,如果有返回true
            if(result){
    
    
			   // method.invoke(obj); //普通方法调用,第一个参数是调用此方法的对象 //这行不用线程
			   // 如下有异常时程序要不影响其他方法输出,所以每个方法用一个线程
                new Thread(() -> {
    
    
                    try {
    
    
                        method.invoke(obj);
                    }catch (Exception e) {
    
    
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
    
    
}
package com.itheima05.custom;

public class TestDemo {
    
    
    public void method00(){
    
    
        System.out.println("方法0");
    }
    @MyTest
    public void method01(){
    
    
        System.out.println("测试方法一");
    }
    @MyTest
    public void method02(){
    
    
        int i = 1/0; //异常不能影响其他,所以多线程
        System.out.println("测试方法二");
    }
    @MyTest
    public void method03(){
    
    
        System.out.println("测试方法三");
    }
}

在这里插入图片描述
CustomDemo和MyTest两个类即CustomDemo.java文件可以打成jar包:New Project-Maven
在这里插入图片描述
在这里插入图片描述
如下选中java文件夹右击New-Package,命名为com.nb.xunit,将CustomDemo.java文件复制到该包下。
在这里插入图片描述
如上将MyTest.java拉出来和CustomDemo.java并列,MyTest注解应该给public,不然只能在本包下使用。
在这里插入图片描述
如下操作后可以删除CustomDemo.java和MyTest.java。
在这里插入图片描述
B站/知乎/微信公众号:码农编程录
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43435675/article/details/112759754