注解和反射
注解annotation
概述:对程序做出解释并可被编译器读取,以@注释名
的形式存在,还可添加参数值。可附加在包、类、方法、属性上面,为其增加辅助信息。
-
内置注解
@Override
:表示打算重写超类中的方法@Deprecated
:该元素不推荐使用,表示已过时(元素上会划横线)@SuppressWarnings
:抑制编译时的警告信息
-
元注解:负责注解其他注解,分别有
@Target
,描述注解的使用范围@Retention
,描述注解生命周期即有效范围(source源码<class<runtime运行时)@Document
,说明该注解被包含在javadoc
中@Inherited
,说明子类可继承父类中该注解
-
自定义注解
@Target({ElementType.TYPE,ElementType.METHOD}) //作用域为类、方法,属性为FIELD @Retention(RetentionPolicy.RUNTIME) public @interface myAnnotation{ //若在类中定义则去掉public String name() default ""; //注解的参数而非方法,可通过default来声明参数的默认值 int age(); }
标记注解(若只有一个参数,一般名为value,且赋值时可省略参数名直接写值)
@myAnnotation(age = 20) //若某属性无默认值,就必须在标注时赋值
反射
概述:反射是Java被视为准动态语言的关键,允许程序在执行期取得任何类的内部信息,并操作任意对象的内部属性和方法。
-
反射对象
加载完类之后,在堆内存的方法区中产生一个Class类型的对象(一个类只有一个Class对象),通过该对象就可以反射出完整的类的结构信息。
-
获取class对象方式
- 通过类名:
类名.class
- 通过实例对象:
对象.getClass()
(不同的实例对象也会获取到相同的Class对象) - 通过子类对象获得父类对象:
子对象.getSuperClass()
- 通过类路径:
CLass.forName("路径.类名")
- 基本内置类:
包装类名.Type
- 利用
ClassLoader
- 通过类名:
-
可获取Class对象的类型
类、接口、数组(同种类型的数组Class对象相同,与长度无关)、注解、枚举、基本类型
-
通过反射可获取的对象信息
- 类名、属性(Field)、方法、构造器、注解、父类或接口,可获得所有或指定获取
getXxx
只能获取public
修饰的信息,getDeclaredXxx
可获取任意
-
通过反射对象进行操作
- 创建对象
- 无参:在类有无参构造器且访问权限足够的情况下,调用
newInstance()
方法 - 有参:先get指定形参的构造器,再调用带参的
newInstance()
方法
- 无参:在类有无参构造器且访问权限足够的情况下,调用
- 调用指定方法
- 通过class类的
getMethod(方法名,参数类型)
指定方法并获取一个Method
对象 - 使用
Method
对象调用invoke(原类对象,参数值)
方法对指定方法进行激活
- 通过class类的
setAccessible
权限开关- 方法、属性、构造器对象都有
setAccessible(false)
方法 - 设参数值为
true
可取消访问检测,从而可以访问私有成员 - 性能分析:反射会降低执行效率,关闭安全检查可相对提升反射效率
- 方法、属性、构造器对象都有
- 创建对象
-
类加载内存分析
- Java内存
- 方法区中存放各类模板(class对象),包括静态变量以及常量池,是特殊的堆
- 栈中存放引用对象名(new出来的)和一些基本数据类型的值和引用方法(main方法压在底下)
- 堆中存放引用对象的具体信息,栈中对象会指向堆中某一地址
-
加载:通过类加载器将class文件字节码加载到内存中,在堆中生成代表这个类的class对象
-
链接:将Java类的二进制代码合并到JVM的运行状态中,分为验证、准备、解析三阶段
准备:为类变量(static)在方法区分配内存,设置变量默认初始值
-
初始化:执行类构造器
<clinit>()
方法,执行静态代码块以及类变量赋值动作(栈中)- 类的主动引用会发生类初始化,如new对象、反射调用、子类引发的父类初始化
- 类的被动引用不会发生类初始化,如子类引用父类静态变量、引用常量、数组定义引用
- Java内存
通过反射获取注解信息
-
获取该类的class对象
-
获得指定注解对象:
class对象.getAnnotation(注解名.class)
也可获取类的指定属性,再获取该属性的注解信息
-
获得注解的该属性值:
注解对象.属性名()