反射、注解入门

反射和注解

反射

反射:将类的每一个部分都拆分成对象使用
反射中需要先获取类的字节码文件Class对象,然后调用Class类中的方法来获取类的各个部分的对象
获取Class对象的方式
1.利用类的class属性 
2.通过实例对象getClass()方法 (Object中的方法)
3.通过Class类中静态方法 forName(String className) 参数为某个类的全路径的字符串 完整包名的路径
Class类中的常用方法
	getConstructors() 获取公共构造函数对象数组
    getDeclaredConstructors() 获取全部构造函数对象数组
    getConstructor() 获取指定公共构造函数对象 (参数为想要获得的构造函数参数的类型Calss对象,必须一一对应)
    getDeclaredConstructor() 参数同上 获取全部构造函数对象中指定一个
    getField() 获取指定公共成员字段 Field类型 
    getDeclaredField() 字段操作与构造方法类似,参考即可
    getMethod​(String name, Class<?>... parameterTypes) 获取公共成员方法(包括继承来的) 参数为方法名和方法参数的Class类型对象
    getDeclaredMethod​() 参数同上 其它与构造方法类似 (不包括继承来的)
    
Constructor 构造函数类型
    newInstance() 生成实例对象 
    setAccessible() 如果得到的是私有构造器对象,需要先把参数设置为true 才能进行实例化(私有的外部无法使用)
Field 字段类型(成员变量) set​(Object obj, Object value) 为对象指定字段设置值 (跳过检查同上)
Method​ 方法类型 invoke​(Object obj, Object... args) 调用方法 参数为调用方法的对象和参数 (跳过检查同上)

** 代码示例
Person类
public class Person {
    String name;
    private Person() {
    }
    private void show(){
        System.out.println("show方法被调用了");
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

测试类
public class MyTestTwo {
    public static void main(String[] args) {
//        获取Person类的字节码文件对象
        Class<Person> personClass = Person.class;
        try {
//            获取Person的构造函数对象和name字段的对象、show方法对象
            Constructor<Person> p_con = personClass.getDeclaredConstructor();
            Field name = personClass.getDeclaredField("name");
            Method show = personClass.getDeclaredMethod("show");
//            因为构造函数和show方法是私有的,设置跳过检查
            p_con.setAccessible(true);
            show.setAccessible(true);
//            获取实例对象
            Person person = p_con.newInstance();
//            为对象的属性赋值
            name.set(person,"张三");
            System.out.println(person);
//            调用方法,这里有一个很奇怪的现象,show方法无返回值,反射却可以接收返回值
//            打印出来发现为null,我又查看了Void原来是一个类
//            这是这个类的官方解释:Void 类是一个不可实例化的占位符类,它持有对表示 Java 关键字 void 的 Class 对象的引用
//            而返回值void可以解释成一个特殊占位符,并不表示就和null相等
            Object invoke = show.invoke(person);
            System.out.println(invoke);
        } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
运行结果
Person{name='张三'}
show方法被调用了
null

注解

定义:
也叫元数据,代码级别的说明,与类、接口、枚举在一个层次,可以声明在包、类、字段、方法、局部变量、
方法参数前,对这些元素进行说明,注释

作用:
1.编写文档:通过代码里标识的注解来生成文档(JDK的api这种)
2.代码分析:通过代码里标识的注解对代码进行分析(通过反射获取注解的内容)
3.编译检查:通过代码里标识的注解让编译器能够实现基本编译检查(看是否符合特定的写法,如实现接口方法)

内置注解(JDK预先定义的,可直接使用)
@Override:检测被标注的方法是否是继承自父类/接口
@SuppressWarnings("all"):压制警告,参数一般为all
@Deprecated:说明标注的地方已过时

自定义注解(框架常用的技术)
通过下面注解定义代码格式可以看到,可以在注解内定义属性(相当于接口的抽象方法,注解实质是一个接口,
可以通过生成的Class字节码文件看到,下面会展示)
属性的种类有
基本数据类型、字符串类型、枚举类型、注解类型(An2是一个注解)、前面几种类型的数组类型
属性在定义的时候可以在后面加 default 属性 的方式来设置默认值

** 注解定义示例
元注解
public @interface An {
    int age();  
    String name();
    MyEnum myEnum(); 
    An2 an();
    int[] ints();
}

** 属性的赋值(也是为啥叫属性的原因)
如果注解只有一个属性value,可以省略属性名,数组类型的赋值是用{ } 
@An(age=1,name="张三",myEnum = MyEnum.p1,ints={1,2},an=@An2(2))
public class Num { }

元注解:用于描述注解的注解
@Target:描述注解能够作用的位置、
 ** 只有一个枚举类型属性ElementType:
 **	取值TYPE 可以作用在类上 METHOD 可以作用在方法上 FIELD 可以作用在成员变量上
@Retention:描述注解被保留到什么阶段
 ** 属性 枚举类型RetentionPolicy
 ** 取值 SOURCE 源代码阶段 CLASS 类对象阶段 RUNTIME 运行阶段
@Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承(定义注解的类在它的子类中是否还存在该注解)

看一下注解被编译的Class文件(可以了解到注解实质是一个接口,属性实质是抽象方法)
public interface anno.An extends java.lang.annotation.Annotation {
  public abstract int age();
  public abstract java.lang.String name();
  public abstract int[] ints();
}
//---------------------------------------------------------------------------------------
案例演示(通过注解来调用任何类的任何方法,运行结果即为Teacher类的tea方法执行结果)
Teacher类
public class Teacher {
    public void tea(){
        System.out.println("teacher中的方法执行了");
    }
}


注解定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface pro {
    String className();
    String methodName();
}

测试类
@pro(className = "anno.Teacher",methodName = "tea")
//想执行哪个类中的哪个方法,只需要通过注解的属性赋值,动态获取,也是框架的实现原理
public class MyTest {
    public static void main(String[] args) {
//        获取此类的字节码文件
        Class<MyTest> myTestClass = MyTest.class;
//        获取此类上的注解,参数为注解的Class对象
//        实质是获取注解接口的子类的实例对象
        pro annotation = myTestClass.getAnnotation(pro.class);
//        调用注解内的方法来获取注解通过属性传递的参数
        String className = annotation.className();
        String methodName = annotation.methodName();
        try {
//            获取执行类的Class对象
            Class<?> aClass = Class.forName(className);
//            获取执行类的方法对象
            Method method = aClass.getMethod(methodName);
//            获取执行类的实例对象
            Object o = aClass.newInstance();
//            执行该方法
            method.invoke(o);
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值