笔记基于:【狂神说Java】注解和反射_哔哩哔哩_bilibili
JAVA注解和反射
1.注解
1.1 内置注解
- 不是程序本身,可以对程序作出解释
- 可以被其他程序(如编译器)读取
格式:“@注释名”
使用位置:可以附加在package,class,method,field等上面,相当于添加额外的辅助信息,通过反射进行访问
常见注解
@Override 表示一个方法声明打算重写超类中的另一个方法
@Deprecated 不推荐程序员使用,但是可以使用
@SuppressWarnings() 镇压警告
1.2 元注解
**作用:**元注解的作用就是负责注解其他注解,java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
-
@Target(ElementType.XXXX):用于描述注解的使用范围
- ElementType.TYPE——接口、类、枚举、注解
- ElementType.FIELD——属性上生效
- ElementType.METHOD——方法
- ElementType.PARAMETER——方法参数
- ElementType.CONSTRUCTOR ——构造函数
- ElementType.LOCAL_VARIABLE——局部变量
- ElementType.ANNOTATION_TYPE——注解
- ElementType.PACKAGE——包
-
@Retention(RetentionPolicy.XXXX):表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
- source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
- class:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
- runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
-
@Documented:表示是否将我们的注解生成在JAVAdoc中
-
@Inherited:子类可以继承父类的注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention 表示注解什么时候有效
@Retention(value = RetentionPolicy.RUNTIME)
// 表示是否将我们的注解生成在JAVAdoc中
@Documented
//Inherited 子类可以继承父类的注解
@Inherited
1.3 自定义注解
- @interface用来声明注解,格式:@interface 注解名{定义内容}
//自定义注解
public class test03 {
//注解可以显示赋值,如果没有默认值,我们必须给注解赋值
@MyAnnotation2()
public void test(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数:参数类型+参数名称();
String name() default "";
int age() default 1;//
int id() default -1;//如果默认值为-1,代表不存在
}
@interface MyAnnotation3{
//如果只有一个参数建议使用value,可以不需要添加default就可以省略
String value();
}
2.反射
2.1 JAVA反射概述
-
动态语言
时一类运行时可以改变其结构的语言:例如行动函数、对象、甚至代码可以被引进,已有的函数可以被删除或时在其他结构上的变化。
主要动态语言:Object-C、C#、JavaScript、PHO、Python等
-
静态语言
运行时结构不可变的语言就是静态语言。如Java、C、C++
Java不是动态语言,但是因为有反射机制,所以具有一定的动态性
什么是反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
优点:可以实现动态常见对象和编译,体现出很大灵活性
缺点:影响性能
反射主要API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Feld:代表类的成员变量
java.lang.reflect.Method:代表类的构造器
2.2 理解类并获取Class实例
什么是Class类:Class本身也是一个类,Class对象智能由系统建立对象,一个加载到类在JVM中只会由一个Class实列,一个类对象对应的是加载到JVM的一个.class文件,每个类的实例都会记得自己是由那个Class是咧所生成,通过Class可以完整的得到一个类中所有被加载的结构
Class对象常用方法:
获取class实例:
//方式一:通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二:通过forname获得
Class c2 = Class.forName("reflection.Student");
System.out.println(c1.hashCode());
//方式三:通过类名.class获得
Class c3 = Student.class;
System.out.println(c1.hashCode());
//方式四:基本内置类型的包装类都有一个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4);
2.3类的加载与ClassLoader
类的加载过程:
- 类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
- 类的链接:将类的二进制数据合并到JRE中
- 类的初始化:JVM负责对类的初始化
什么时候会发生类初始化:
- 类的主动引用
- 虚拟机启动先初始化main方法所在类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有初始化,则先会初始化它的父类
- 类的被动引用
- 当访问一个静态域的时候,只有真正声明这个域的类才会被初始化。
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
类加载器
- 引导类加载器:用C++编写的,是JVM自带的类加载器。负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
- 扩展类加载器:负责jre/lib/ext目录下的jar包
- 系统类加载器:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的类加载器
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器-->根类加载器(C/C++编写)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是那个加载器加载的
ClassLoader c1 = Class.forName("reflection.test07").getClassLoader();
System.out.println(c1);
//测试JDK内部类加载器
ClassLoader c2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(c2);
获取运行时类的完整结构
Class c1 = Class.forName("reflection.User");
System.out.println(c1.getName()); //获取包名+类名
System.out.println(c1.getSimpleName()); //获取类名
System.out.println("==========================");
//获取类的属性
Field[] field = c1.getFields(); //只能找到public属性
Field[] declaredFields = c1.getDeclaredFields(); //获取全部属性
for(Field file:field){
System.out.println(file);
}
System.out.println("==========================");
for(Field file:declaredFields){
System.out.println(file);
}
//获得类的方法
System.out.println("==========================");
//获取本类和父类所有方法
Method[] methods = c1.getMethods();
for(Method meth:methods){
System.out.println(meth);
}
System.out.println("==========================");
//获取本类所有方法
Method[] declaredMethods = c1.getDeclaredMethods();
for(Method meth:declaredMethods){
System.out.println(meth);
}
//获取指定类方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println("==========================");
//获取构造器
Constructor[] constructors = c1.getConstructors(); //获取public的
for(Constructor cs:constructors){
System.out.println(cs);
}
System.out.println("==========================");
Constructor[] declaredConstructors = c1.getDeclaredConstructors(); //获取所有
for(Constructor cs:declaredConstructors){
System.out.println(cs);
}
2.4 创建运行时类的对象
Class c1 = Class.forName("reflection.User");
//构造对象
User user = (User)c1.newInstance(); //本质调用无参构造器
//通过构造器创建对象
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
User user2 = (User)constructor.newInstance("wwj", 001, 18);
System.out.println(user2);
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//获取反射方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活 (对象,“方法参数)
setName.invoke(user3,"wwj1");
System.out.println(user3.getName());
//通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //关闭权限检测
name.set(user4,"wwj2");
System.out.println(user4.getName());