- 前言
- 一、Annotation
- 1、内置注解
- 2、元注解
- 二、反射
- 1、Java反射机制概述
- 2、理解Class类并获取Class实例
- 3、类的加载与ClassLoader
- 4、利用反射做事
- 5、反射操作泛型
- 6、反射操作注解
- 总结
- 参考文献
一、AnnotationJava 反射和注解是以后框架的核心之一,了解Java注解和反射有助于后面框架的学习。
1、内置注解1 作用)给类、包、方法、变量等加上注解,就是对它们的一种解释或是一种备注信息,这个备注信息不想注释一样只能给程序员看,调用它们的其它程序也能读懂上面的注解,从而了解使用它需要注意的约束和信息。编译时,编译器也能读懂一些注释。
2 使用)@+注解名+(参数名)(可选)
3 使用地方)包、类、变量、方法等。
1)Override,重写超类的方法,只能用在方法上。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Override
public String toString() {
return super.toString();
}
2)Deprecated,不建议使用的方法,通常该方法存在危险或者已经过时,有了新的方法可替代。
@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
String since() default "";
boolean forRemoval() default false;
}
public void show() {
System.out.println("suggestion to not used.");
}
3)SuppressWarnings,镇压一些警告,什么类型的警告需要我们传入参数。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@SuppressWarnings(value={"all","unchecked","deprecation"})
public void warning() {
System.out.println("suppressing all warnings");
}
2、元注解
含义)最基本的注解,注解对象为所有注解,4大meta-annotation。
1)@Target,表示该注解的注解目标可选择的地方。
@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
2)@Retention,表示该注解的生命周期,Runtime>Class>Source
@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
3)@documented,生成javaDoc时是否包含此注解
@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface documented {
}
4)@Inherited,子类可继承父类中的注解
@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
5)自定义注解
//Target,表示该注解的注解目标可选择的地方
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
//Retention,表示该注解的生命周期,Runtime>Class>Source
@Retention(RetentionPolicy.RUNTIME)
//documented,生成javaDoc时是否包含此注解
@documented
//Inherited,子类可继承父类中的注解
@Inherited
@interface MyAnnotation1 {
//参数定义:返回值 + 参数名 + () + default (default 可选)
String[] value() default {"annotation"};
int age() default 18;
long id() default -1l;
}
二、反射
1、Java反射机制概述反射机制让Java成为一门“准动态语言”,即具有一定的灵活性,通过反射机制获得类似的动态语言的动态特性。
动态语言:即可以在运行的时候根据某些条件改变自身结构,如PHP、Javascript、Object-C等。
静态语言:运行时程序结构不可变,如Java、C、C++。
允许程序在执行时借助Java Reflection API获取任何类的内部信息,并能直接操作任意对象中的属性和方法。
加载完类到堆内存的方法区之后,就会对每个类产生唯一的一个Class类型的对象,该对象包含了完整的类结构信息,通过这个唯一的Class类型对象来看到完整的类结构信息,称其为反射。
正常方式:引入类 -> new 一个对象 -> 得到实例化的对象
反射方式:实例化对象 -> 获取该类唯一的Class类型对象 -> 得到完整的类结构信息
2、理解Class类并获取Class实例反射机制提供给我们的功能
1)运行时判断任意一个对象所属的类
2)运行时构造任意一个类的对象。
3)运行时判断任意一个类所具有的成员变量和方法。
4)运行时获取泛型信息
5)运行时调用任意一个类所具有的成员变量和方法。
6)运行时处理注解
7)动态代理
…
Object中有一个获取Class类对象的方法定义public final native Class> getClass();
1)Class本身也是一个类
2)Class对象只能由系统来建立
3)一个加载的类在JVM中只有唯一的Class类型对象
4)一个Class对象对应加载到JVM中的.class文件
5)每个类对象都会记录是那个Class对象将其实例化
6)通过Class对象可以得到对应类被加载的所有结构信息
7)Class类型实例是Reflection的核心,反射是通过Class类型对象来反射的。
Class类常用方法
获取Class类型对象的方法
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//方式一:通过类名获取
Class c1 = Class.forName("com.xhu.java.annotation.User");
//方式二:通过对象获取
Class c2 = new User().getClass();
//方式三:最安全,通过类的class属性获取
Class c3 = User.class;
//方式四:基本内置类型的包装类都由TYPE属性可获取
Class c4 = Integer.TYPE;
//方式五:通过ClassLoader
System.out.println(c2.getDeclaredField("name").toString());
}
哪些可以获取Class类型对象?
Class c1 = Object.class;//对象类型
Class c2 = Comparable.class;//接口类型
Class c3 = String[].class;//一维数组
Class c4 = int[][].class;//二维数组
Class c5 = ElementType.class;//枚举类型
Class c6 = Override.class;//注释类型
Class c7 = int.class;//基本内置类型
Class c8 = void.class;//空类型
Class c9 = Class.class;//Class类型
3、类的加载与ClassLoader
1)Java内存分析
栈)每个线程一个方法栈:存放基本数据类型变量、对象引用变量,不同线程不相互共享。
堆)存放new出的对象,栈中的对象引用存的就是堆中实际对象的地址。线程共享。
方法区)分出来的一块堆,存放类结构信息和静态变量及常量池。线程共享。
2)类的加载过程
如果程序需要某个类时,但是该类还未被加载到内存,系统就会加载类(Load)、类的链接(link)、类的初始化(Initialize)
加载)将类的字节码文件加载到内存,并将类信息和静态数据转化成方法区的运行时数据结构,再给这个类在堆中生成一个唯一的Class对象。
链接)将Java的二进制代码合并到JVM的运行状态之中。
A 验证)验证类信息是否符合JVM规范,保证安全。
B 分配内存)为静态变量分配方法区的堆内存,并赋值为默认值,注意不是初始化值。
C 解析)把常量池中的符号引用替换成地址引用(该常量在常量池的地址)
初始化)调用类构造器,对类变量初始化。把静态代码块的语句和静态变量赋值语句合并到clinit()中(按顺序合并),进行初始化。
注1:初始化该类时,如果父类未初始化,则先初始化父类。
注2:JVM保证clinit()在多线程环境中被正确的加锁和同步。
什么时候会发生类的初始化?
类的主动引用,一定会发生初始化)
1)JVM启动,初始化main方法的所用到的类。
2)new一个类的对象,类加载有一个生命周期。
3)调用类的静态成员和静态方法(常量除外)
4)使用reflect包的方法对类进行反射调用
5)当初始化一个类,如果其父类没有被初始化,则先初始化父类
类的被动引用,不会发生初始化)
1)当访问一个静态域时,只有真正声明这个静态域的类会被初始化,如子类调用父类的静态方法,则只初始化父类
2)通过数组定义类引用,不会初始化此类。(new 数组只是开辟了一个空间,数组类会被初始化。)
3)引用常量不会触发初始化,常量在链接阶段就存入调用类的常量池中了。
类加载器
作用)将类的字节码文件加载到内存,并将类信息和静态数据转化成方法区的运行时数据结构,再给这个类在堆中生成一个唯一的Class对象,作为通过反射操作类信息的唯一入口。
缓存)一旦一个类被加载到类加载器中,将会维持加载一段时间,等生命周期结束时,将会回收这些Class对象。
JVM类加载器)
Bootstap ClassLoader 由c++编写,Java无法读取到,读到为null,它会去加载jdk核心包ar.jar包。
package com.xhu.java.annotation;
public class TestClassLoader {
public static void main(String[] args) {
//最上层系统类加载器
ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(sysClassLoader);
//中间层扩展加载器,现在的平台加载器
ClassLoader extClassLoader = sysClassLoader.getParent();
System.out.println(extClassLoader);
//最下层根加载器,C++编写,Java读不到,为null
ClassLoader rootClassLoader = extClassLoader.getParent();
System.out.println(rootClassLoader);
//本类的类加载器,应该是系统类加载器
ClassLoader thisClassLoader = TestClassLoader.class.getClassLoader();
System.out.println(thisClassLoader);
//JDK的类是那个加载器加载的,应该是根加载器去加载
ClassLoader jdkClassLoader = Object.class.getClassLoader();
System.out.println(jdkClassLoader);
//系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
//双亲委派机制,加载一个包时,会向上查找是否有相同的包,来保证安全性。
}
}
4、利用反射做事
package com.xhu.java.annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class TestClass {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class c1 = User.class;
//获取类的名字:包名+类名
System.out.println(c1.getName());
//获取类的简单名字:类名
System.out.println(c1.getSimpleName());
//获取类的属性:获取public
Field[] fields = c1.getFields();
for (Field field : fields)
System.out.println(field);
//获取类的属性:所有属性
Field[] allF = c1.getDeclaredFields();
for (Field field : allF) {
System.out.println(field);
}
//根据名字来获取指定属性
System.out.println(c1.getDeclaredField("name"));
//获取类的方法
System.out.println(Arrays.toString(c1.getMethods()));//获取本类和父类的public方法
System.out.println(Arrays.toString(c1.getDeclaredMethods()));//获取本类的所有方法
//获取指定方法
System.out.println(c1.getDeclaredMethod("privateF",String.class));//由于重载,不传参数可能会报错
//获取指定构造器
System.out.println(Arrays.toString(c1.getDeclaredConstructors()));
//获取指定构造器
System.out.println(c1.getDeclaredConstructor(String.class, String.class, int.class, int.class));
//创建对象
@Deprecated
User u = (User)c1.newInstance();//类中必须有一个无参构造器且访问权限足够。
System.out.println(u);
//通过参数构造
System.out.println(c1.getDeclaredConstructor(String.class, String.class, int.class, int.class).newInstance("name", "id", 18, 1));
//通过反射调用方法,private 不能调用
Method method = c1.getDeclaredMethod("privateF", String.class);
method.invoke(u,"调用方法");
//设置属性,私有属性不能操作
Field name = c1.getDeclaredField("name");
name.set(u,"new name");
System.out.println(name.get(u));
//关闭权限,访问private
Field age = c1.getDeclaredField("age");
age.setAccessible(true);
age.set(u,100);
System.out.println(age.getInt(u));
System.out.println();
}
}
//POJO 类
class User extends Object {
public String name;
protected String id;
private int age;
int sex;
public User() {
}
public User(String name, String id, int age, int sex) {
this.name = name;
this.id = id;
this.age = age;
this.sex = sex;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + ''' +
", id='" + id + ''' +
", age=" + age +
", sex=" + sex +
'}';
}
public void privateF(String s) {
}
}
性能比较
package com.xhu.java.annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestPower {
//普通new的方式
public static void test01() {
long start = System.currentTimeMillis();
User u = new User();
for (int i = 0; i < 1000000000; i++) {
u.getName();
}
long end = System.currentTimeMillis();
System.out.println((end - start)+"ms");
}
//反射的方式
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
long start = System.currentTimeMillis();
User u = new User();
Class c1 = User.class;
Method method = c1.getDeclaredMethod("getName", null);
for (int i = 0; i < 1000000000; i++) {
method.invoke(u, null);
}
long end = System.currentTimeMillis();
System.out.println((end - start)+"ms");
}
//反射,但关闭权限
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
long start = System.currentTimeMillis();
User u = new User();
Class c1 = User.class;
Method method = c1.getDeclaredMethod("getName", null);
method.setAccessible(true);
for (int i = 0; i < 1000000000; i++) {
method.invoke(u, null);
}
long end = System.currentTimeMillis();
System.out.println((end - start)+"ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
5、反射操作泛型
Java采用泛型擦除机制来引入泛型,只是给javac使用的,确保数据的安全性和免去强制转换类型,一旦编译完成,所有泛型有关的类型全部擦除变为Object。
为了通过反射操作泛型,Java新增ParameterizedType、GenericArrayType、TypeVariable、WildcardType.
ParameterizedType,参数化类型,如Collection.
GenericArrayType,表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable,是各种类型变量的公共父接口
WildcardType,代表一种通配符类型表达式
package com.xhu.java.annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
//测试泛型
public class TestGer {
public static void test01(Map m, List n) {
System.out.println("test01");
}
public static Map test02() {
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = TestGer.class.getMethod("test01", Map.class, List.class);
//获取参数泛型
Type[] types = method.getGenericParameterTypes();
for (Type type : types) {
if (type instanceof ParameterizedType) {
Type[] ps = ((ParameterizedType) type).getActualTypeArguments();
for (Type p : ps) {
System.out.println(p);
}
}
}
//获取返回值泛型
Method m2 = TestGer.class.getMethod("test02", null);
Type t2 = m2.getGenericReturnType();
if (t2 instanceof ParameterizedType) {
Type[] tp2 = ((ParameterizedType) t2).getActualTypeArguments();
for (Type p : tp2) {
System.out.println(p);
}
}
}
}
6、反射操作注解
package com.xhu.java.annotation;
//利用反射操作注解
public class TestReflectAnno {
public static void main(String[] args) throws NoSuchFieldException {
Class c1 = User.class;
//通过反射获取注解
ORM a = (ORM)c1.getAnnotation(ORM.class);
System.out.println(a.value());
//获取field注解
java.lang.reflect.Field field = c1.getDeclaredField("name");
Field as = (Field)field.getAnnotation(Field.class);
System.out.println(as.columnName());
System.out.println(as.type());
System.out.println(as.len());
}
}
//POJO 类
@ORM("DB_User")
class User extends Object {
@Field(columnName = "name", type = "varchar", len = 10)
public String name;
@Field(columnName = "id", type = "varchar", len = 10)
protected String id;
@Field(columnName = "age", type = "int", len = 3)
private int age;
@Field(columnName = "sex", type = "int", len = 1)
int sex;
public User() {
}
public User(String name, String id, int age, int sex) {
this.name = name;
this.id = id;
this.age = age;
this.sex = sex;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + ''' +
", id='" + id + ''' +
", age=" + age +
", sex=" + sex +
'}';
}
public void privateF(String s) {
System.out.println(s);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ORM {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field {
String columnName();
String type();
int len();
}
注:通过操作注解来拼接SQL。Mybatis的核心之一就是反射操作注解。
1)反射、反射操作
参考文献[1] [JDK 1.12]
[2] 注解和反射 狂神



