1 简介
为什么要学这个?
未来要学的框架的底层原理就是注解与反射
2 注解
Annotation
注解可以被其他程序(编译器)读取
存在形式:
可以在包、类、方法、field上使用注解
@SuppressWarnings(value="unchecked")
public static void method(String param){
}
有的注解需要参数,有的注解不需要参数,具体看具体注解的代码
2.1 内置注解
- @Override
- @Deprecated
- 。。。
2.2 元注解
负责注解其他注解
- @Target
- @Retention :标注该注解在合适有效
- @Document
- @Inherited
2.2.1 测试Target元注解
Target元注解作用:表示注解可以用于哪些地方
使用@Interface定义一个注解,在其上方使用@Target元注解对MyAnnotation进行注解,随后,定义test方法,使用MyAnnotation进行注解。
public class Main {
public static void main(String[] args) {
}
@MyAnnotation
public void test(){
}
}
// 定义一个注解(仿写@Override)
@Target(value = ElementType.METHOD) // 元注解,注解注解的注解
@interface MyAnnotation{
}
注意:如果@MyAnnotation注解放在类上,则报错!
在元注解的参数中传入数组,则该注解可同时用于多个地方
@MyAnnotation
public class Main {
public static void main(String[] args) {
}
@MyAnnotation
public void test(){
}
}
// 定义一个注解(仿写@Override)
@Target(value = {ElementType.METHOD, ElementType.TYPE}) // 元注解,注解注解的注解
@interface MyAnnotation{
}
2.2.2 Retention元注解
@Retention(value = RetentionPolicy.RUNTIME)
表示这个注解在运行时还有效
顺序:source -> class -> runtime
即runtime>class>source
// 定义一个注解(仿写@Override)
@Target(value = {ElementType.METHOD, ElementType.TYPE}) // 元注解,注解注解的注解
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation{
}
2.2.3 Documented元注解
表示是否将注解生成在JAVAdoc中
@Documented
@interface MyAnnotation{
}
2.2.4 Inherited元注解
表示子类可以继承父类的注解
@Inherited
@interface MyAnnotation{
}
2.3 自定义注解
使用@interface
定义注解参数方式:类型 参数名()
可以使用default设置默认参数,如果默认参数设置为-1表示不存在
如果注解只有一个参数,那么在使用的时候可以不指定参数,直接传值
@MyAnnotation(name = "halo", schools = {"OC", "OUC"})
public class Main {
public static void main(String[] args) {
}
@MyAnnotation(name = "helo", schools = {"OC", "OUC"})
public void test(){
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String name() default ""; // 注解的参数,不是方法
int age() default 10;
int id() default -1; // 默认值设置为-1 表示不存在
String[] schools() default {"QUST", "qust"};
}
3 反射
Reflection
反射机制可以让程序在执行期间获得任何类的内部信息,并且可以操作任意对象的内部属性和方法,即通过对象反向去获得类的信息
3.1 动态语言与静态语言
- 动态语言:在运行时可以改变结构的语言,在运行时代码可以根据某些条件改变自身的结构
- 静态语言:运行时结构不可变的语言就是静态语言
3.2 反射 代码演示
代码表示了可以通过类的定义去实例化一个Class对象,这个对象包含了类的全部结构信息,即使用一个对象来表示一个类
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射获取类的Class对象
Class student = Class.forName("Student");
System.out.println(student);
Class student1 = Class.forName("Student");
Class student2 = Class.forName("Student");
Class student3 = Class.forName("Student");
Class student4 = Class.forName("Student");
// hashcode相同表示是同一个类
// 类被加载后,其整个结构都会被封装在CLass对象中
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
System.out.println(student3.hashCode());
System.out.println(student4.hashCode());
}
}
class Student{
private String name;
private int id;
private int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
3.3 Object类的getClass方法
getClass()获得的返回值与Class.forName()获取的Class对象是相同的
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Reflection reflection = new Reflection();
Class aClass = reflection.getClass();
}
}
3.4 Class类的创建方式
Person、Teacher、Student三个类之间的关系:
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student() {
this.name = "Student";
}
}
class Teacher extends Person{
public Teacher() {
this.name = "Teacher";
}
}
3.4.1 使用类实例对象获取Class类
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("This man is:" + p1.name);
// Class类获得方式1:通过实例对象获得
Class aClass = p1.getClass();
}
}
3.4.2 使用Class类的forName方法
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("This man is:" + p1.name);
// Class类获得方式1:通过实例对象获得
Class aClass = p1.getClass();
// 获得方式2:使用forname获得
Class student = Class.forName("Student");
System.out.println(aClass.hashCode());
System.out.println(student.hashCode());
}
}
3.4.3 使用类名.class属性
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("This man is:" + p1.name);
// 方式3:通过类名.class获得
Class<Student> studentClass = Student.class;
System.out.println(studentClass.hashCode());
}
}
3.4.4 使用内置属性包装类TYPE属性
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("This man is:" + p1.name);
// 方式4:基本内置属性的包装类有一个TYPE属性
Class<Integer> type = Integer.TYPE;
System.out.println(type.hashCode());
}
}
3.4.5 通过Class对象获取父类
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("This man is:" + p1.name);
// Class类获得方式1:通过实例对象获得
Class aClass = p1.getClass();
// 获得父类类型
Class superclass = aClass.getSuperclass();
System.out.println(superclass);
}
}
3.5 什么类型有class对象?
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Object.class; // 类
Class c2 = Runnable.class; // 接口
Class c3 = String[].class; // 数组
Class c4 = int[][].class; // 二维数组
Class c5 = Override.class; // 注解
Class c6 = ElementType.class; // 枚举类型
Class c7 = Integer.class; // 基本类型
Class c8 = void.class; // void类型
Class c9 = Class.class; // Class自身
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
output:
class java.lang.Object
interface java.lang.Runnable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
3.6 Java内存分析类加载过程
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
A a = new A();
System.out.println(A.c);
}
}
class A{
static {
System.out.println("A类静态代码区");
c = 300;
}
public A() {
System.out.println("A类无参构造");
}
static int c = 100;
}
output:
A类静态代码区
A类无参构造
100
证明了在类加载时,先执行静态代码区,然后执行无参构造,静态变量初始化将静态代码区覆盖掉了,那么背后的加载过程具体是怎样的家呢?
内存加载步骤分析:
- 类的代码被加载到方法区(一个特殊作用的堆)
- 执行类加载过程,为每一个类创建一个Class对象,保存在堆中
- 链接,链接结束后,静态变量被默认初始化为默认值
- <clinit>()初始化,将静态代码汇集到一起执行,因为300赋值在前,100语句在后,所以c的值为100
3.7 类的初始化
3.7.1 主动引用
public class Reflection {
static {
System.out.println("Reflection类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 主动引用
Son son = new Son();
}
}
class Father{
static {
System.out.println("父类被加载");
}
static int b = 2;
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
output:
Reflection类被加载
父类被加载
子类被加载
public class Reflection {
static {
System.out.println("Reflection类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 反射也会产生主动引用
Class son1 = Class.forName("Son");
}
}
3.7.2 被动引用
public class Reflection {
static {
System.out.println("Reflection类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 不会产生类的引用的方法
// 子类调用父类的静态变量,不会调用类的初始化
System.out.println(Son.b);
}
}
Reflection类被加载
父类被加载
2
public class Reflection {
static {
System.out.println("Reflection类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 不会产生类的引用的方法
// 通过数组定义类的引用,不会触发这个类的初始化
Son[] sons = new Son[10];
}
}
output:
Reflection类被加载
public class Reflection {
static {
System.out.println("Reflection类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 不会产生类的引用的方法
// 调用子类常量池中的常量
System.out.println(Son.M);
}
}
output:
Reflection类被加载
1
因为类的常量在链接阶段就被分配内存,所以触发不到类的初始化
3.8 类加载器
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取系统类加载器的父类加载器 --> 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取扩展类加载器的父类加载器 --> 根加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
}
}
output:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4554617c
null
根加载器获取不到,所以,返回值为null
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取系统类加载器的父类加载器 --> 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取扩展类加载器的父类加载器 --> 根加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
// 测试当前类是由哪个加载器加载的
ClassLoader reflection = Class.forName("Reflection").getClassLoader();
System.out.println(reflection);
// 测试JDK内置类是由哪个加载器加载的
ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
}
}
output:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4554617c
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
由结果可见,自己定义的类由系统类加载器加载,而JDK内置类由根加载器加载
如何获得系统类加载器可以加载的路径
String property = System.getProperty("java.class.path");
3.9 获取类的运行时结构
获取类的任何信息
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> person = Class.forName("Person");
// 获取类的名字
System.out.println(person.getName()); // 带包名
System.out.println(person.getSimpleName());
// 获取类的属性,但只能找到public的属性
for (Field field : person.getFields()) {
System.out.println(field);
}
System.out.println("=============");
/// 获取类的属性,可以找到全部的属性
for (Field declaredField : person.getDeclaredFields()) {
System.out.println(declaredField);
}
// 获得指定属性的值
Field name = person.getDeclaredField("name");
System.out.println(name);
System.out.println("=================");
// 获得类的方法
// 获得本类和父类的所有方法
for (Method method : person.getMethods()) {
System.out.println(method);
}
System.out.println("=================");
// 使用Declared
// 只能获得本类的所有方法
for (Method declaredMethod : person.getDeclaredMethods()) {
System.out.println(declaredMethod);
}
System.out.println("=================");
// 获得指定方法,第二个参数传参
// 需要参数的原因:避免函数重载引发冲突
Method getName = person.getMethod("getName", null);
Method setName = person.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
System.out.println("=================");
// 获得指定的构造器
Constructor<?>[] constructor = person.getConstructors();
for (Constructor<?> constructor1 : constructor) {
System.out.println(constructor1);
}
System.out.println("=================");
for (Constructor<?> declaredConstructor : person.getDeclaredConstructors()) {
System.out.println(declaredConstructor);
}
}
}
class Person{
private String name;
private int age;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
}
3.10 通过反射动态创建类对象
注意!!使用Class对象newInstance方法新建对象需要该类有一个无参数的构造器,否则程序会报错
import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException {
Class person = Class.forName("Person");
// 构造一个对象
Person p1 = (Person) person.newInstance();
System.out.println(p1);
}
}
class Person{
private String name;
private int age;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public Person() {
}
}
3.10.1 解决无参构造器问题
使用Class类的getDeclaredConstructor方法获取类的构造器,配置好构造器后使用构造器newInstance进行构造对象
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class person = Class.forName("Person");
// 构造一个对象
Constructor p1 = person.getDeclaredConstructor(String.class, int.class, int.class);
Person p = (Person) p1.newInstance("hahaha", 1, 2);
System.out.println(p);
}
}
3.10.2 通过反射调用普通方法
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class person = Class.forName("Person");
// 通过反射创建了一个对象
Person p1 = (Person) person.newInstance();
// 通过反射获取一个方法
Method setName = person.getDeclaredMethod("setName", String.class);
setName.invoke(p1, "haha"); // 激活
System.out.println(p1.getName());
}
}
output:
haha
3.10.3 通过反射操作属性
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class person = Class.forName("Person");
// 通过反射创建了一个对象
Person p1 = (Person) person.newInstance();
// 通过反射获取一个方法
Field name = person.getDeclaredField("name");
name.set(p1, "666");
System.out.println(p1.getName());
}
}
上述代码运行时会报错,因为要操作的name属性时private,但反射机制仍然可以解决这个问题,
使用name.setAccessible()方法关闭安全检测
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class person = Class.forName("Person");
// 通过反射创建了一个对象
Person p1 = (Person) person.newInstance();
// 通过反射获取一个方法
Field name = person.getDeclaredField("name");
// 取消安全检测
name.setAccessible(true);
name.set(p1, "666");
System.out.println(p1.getName());
}
}
3.11 分析setAccessible性能影响
输出类的方法10亿次,三种方法在运行时间上的比较。
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 普通方式调用
Person person = new Person();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
person.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方法执行10亿次需要:" + (endTime - startTime));
// 反射方式调用
Class aClass = person.getClass();
Method getName = aClass.getDeclaredMethod("getName", null);
startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(person, null);
}
endTime = System.currentTimeMillis();
System.out.println("反射方法执行10亿次需要:" + (endTime - startTime));
// 反射方式调用 但关闭安全检测
getName = aClass.getDeclaredMethod("getName", null);
getName.setAccessible(true);
startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(person, null);
}
endTime = System.currentTimeMillis();
System.out.println("反射方法、关闭安全检测执行10亿次需要:" + (endTime - startTime));
}
}
output:
普通方法执行10亿次需要:4
反射方法执行10亿次需要:2242
反射方法、关闭安全检测执行10亿次需要:1874
3.12 反射操作泛型
public class Reflection {
public void method01(Map<String, Person> map, List<Person> list){
System.out.println("method01");
}
public Map<String, Person> method02(){
System.out.println("method02");
return null;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 参数是泛型
Method method01 = Reflection.class.getMethod("method01", Map.class, List.class);
Type[] genericParameterTypes = method01.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println(genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
// 返回值是泛型
Method method02 = Reflection.class.getMethod("method02");
Type genericReturnType = method02.getGenericReturnType();
System.out.println(genericReturnType);
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
3.13 反射操作注解
ORM:Object Relation M
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.List;
import java.util.Map;
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class aClass = Class.forName("Person");
// 通过反射获得注解
for (Annotation annotation : aClass.getAnnotations()) {
System.out.println(annotation);
}
// 获得注解的value的值
Table table = (Table) aClass.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);
// 获得类指定的注解
Field name = aClass.getDeclaredField("name");
Field1 annotation = name.getAnnotation(Field1.class);
System.out.println(annotation.columnName());
System.out.println(annotation.length());
System.out.println(annotation.type());
}
}
// 类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
// 属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field1{
String columnName();
String type();
int length();
}
@Table("db_student")
class Person{
@Field1(columnName = "db_name", type = "String", length = 10)
private String name;
@Field1(columnName = "db_age", type = "int", length = 10)
private int age;
@Field1(columnName = "db_id", type = "int", length = 3)
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public Person() {
}
}
@Table(value=db_student)
db_student
db_name
10
String