注解和反射

注解(Annotation)

什么是注解

注释是给人看的,注解是给机器看的

注解的作用

  • 不是程序本身,但是可以对程序做出解释
  • 可以被其他程序读取(比如: 编译器)

注解的格式

@注解名

Annotation的使用位置

package,class,method,field

内置注解

@override 重写

@deprecated 表示不推荐使用

@suppressWarning() 正压注解

元注解(meta-annotation)

作用

负责解释其他注解的注解

标准注解(四个)

@target 目标

@retention 表示注解的级别,

@Document 将会生成在javadoc当中

@inherited 子类可以继承本类注解

自定义注解可以有参数

参数类型 +参数名();

参数类型 + 参数名() default 默认值;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02 {
    String name() default "";
}

name为注解的参数名 ,default 后面的是默认值,如果我们不设置默认值,则使用注解额时候必须传参

反射

类加载

同一个字节码文件在运行过程中只会加载一次。通过下面三种方式获取的类对象都是一个对象。

获取类对象的三种方式

Class.forName("全类名");
类名.class;
对象.getClass();    

第一种多用于 配置文件,将类名定义在配置文件中。读取文件,加载类

第二种多用于参数的传递

第三种多用于 对象的传递

什么时候进行类的初始化

类的主动引用

  • 虚拟机启动,先初始化main方法所在的类
  • new 一个对象
  • 调用静态成员,(除final之外的常量)和静态方法
  • 通过java.ang.reflect 包进行反射调用
  • 当初始化一个类的时候如果父类没有被初始化,则先初始化其父类

类的被动引用(不会引发类的初始化)

  • 当访问一个静态时,只有真正声明这个域的类才会被初始化。
    • 如:当通过自雷引用父类的静态变量,不会导致子类初始化
  • 当通过数组定义类的引用,不会触发类的初始化
  • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

类加载器

  • 引导类加载器(java的核心加载器)

    • 名称: rt.jar 根加载器
    • 位置:直接存在于jre文件夹下面
    • 特点: 无法获取 通过C++编写的
    • 作用: 用于装载核心类库
    • 我们用的所有的基础类都在下面,
      • lang包
      • util包
      • swing包
      • ...
  • 扩展加载器

    • 名称: ext

    • 位置: jre 下面的ext文件夹下面

    • 特点: 目前未知(是我不知道,不是没有作用)

    • 作用: 负责jre/lib/ext目录下的jar包或者-D java.ext.dirs 指定目录显得jar包装入工作库

    • 获取:

      • ClassLoader.getSystemClassLoader().getParent();
        
  • 系统加载器(用户类加载器)

    • 名称: systemClassLoader (用户的类加载器)

    • 位置: 当前工程下面

    • 特点: 加载该工程的class文件

    • 作用: 负责java -classpath 或 -D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器

    • 获取:

      • ClassLoader.getSystemClassLoader();
        
  • 获取类加载器可以加载的路径

    • String properties = System.getProperty("java.class.path");
      String replace = properties.replace(";", "\n");
      System.out.println(replace);
      

获取类名

Class a = Class.forName("com.ziop.relfect.User");
User user=new User;
a = user.getclass();
//获得类的名字
System.out.printin(a.getname());//获得包名+类名
System.out.println(a.getsimplename());//获得类名

获取属性名

a.getFields();  // 只能获取public 的属性
a.getFields("name");  // 获取指定的属性
a.getDeclaredFields();   //获取全部的属性  包括private
a.getDeclaredFields("name");   

获取方法

a.getMethods();  //获得本类及其父类的所有方法   不包括私有
a.getDeclaredMethods();  //获得本类的所有方法  包括私有的
a.getMethods("getTest",); //第二个参数及其以后的参数,用以判断重载的方法
a.setDeclaredMethods("setTest",String.class); 

获取构造器

a.getConstructors();
a.getDeclaredConstructors(); // 同上
a.getDeclaredConstructors(String.class,int.class); // 获取指定有参构造

动态创建对象

//创建无参构造器的对象
Class a = Class.forName("com.ziop.relfect.User");
User user  = (User)a.newInstance();  
// 创建有参的对象
Constructor constructor  =  a.getDeclaredConstructors(String.class,int.class); // 获取指定有参构造
User user2 = (User)constructor.newInstance("ziop",2021,20);
//通过反射调用普通方法
User user3  = (User)a.newInstance();  
	//获取一个普通方法
Method setName = a.setDeclaredMethods("setTest",String.class);
setName.invoke(user3,"ziop");   //invoke  : 激活函数
//通过反射操作属性
User user4  = (User)a.newInstance();  
Field name = a.getFeclaredField("name");
	//如果属性是私有的,则直接设置会出错,提示访问不了,同过关闭程序的安全检测则可以更改
name.setAccessible(true);  // 设置安全检测关闭
name.set(user4,"ziop")

image-20210813205644158

关闭安全检测可以提高执行效率(但是不推荐使用)

name.setAccessible(true);  // 设置安全检测关闭

image-20210813215437312

获取泛型类型

method.getGenericParamaterTypes();    获取参数类型

image-20210813214352107

如果想要获取泛型里面的类型,则需要判断他是否属于一个参数化类型,如果是的话则提取出来

image-20210813214544697

image-20210813215009848

method.getgenericReturnType(); //获取返回值的泛型

image-20210813215236259

反射操作注解

Class a = Class.forName("com.ziop.relfect.User");
a.getAnnotations();   //获取所有的注解
Ziop ziop =  (Ziop)a.getAnnotations(ziop.class)  // 获取指定的注解
String value = ziop.value();   // 获取指定的注解的值  value是 ziop注解中的一个字段
上一篇 下一篇