写在最前面
这篇笔记是看秦老师的 注解和反射 视频写的, 建议去看视频学习, 能掌握更多的内容
B站链接在此:
在学习注解之前,只知道@Test 的 Junit 包 , 发现后面要学习的Spring 等框架需要用到, 就来补一课
下面的内容可以作为笔记参考,直接看文档可能观感不佳, 视频中的大部分代码都有写,适合边看视频边实际操作,一共两个小时也不算太长。
注解
注解入门
什么是注解
内置注解
@Override 重写父类方法
@Override
public String toString() {
return super.toString();
}
@Deprecated 表示, 不推荐使用 , 但是可以使用 ,或者存在更好的方法 , 在IDEA中使用这个元素时有删除线提醒
@SuppressWarnings 抑制编译时的警告信息
元注解
元注解作用是 注解其他的注解 元注解有以下几种:
- @Target 指定注解的使用范围
- @Retention 描述注解的生命周期
- SOURCE: 在源文件中有效
- CLASS : 在类文件中有效
- RUNTIME: 在运行时有效
- @Documented 说明注解将被包含在javadoc中
- @Inherited 说明子类可以继承父类中的该注解
详细说明如下:
注解声明
@Target(value = {ElementType.METHOD,ElementType.TYPE})//指定作用域为 方法(METHOD) 或者 类(TYPE)
@Retention(value = RetentionPolicy.RUNTIME) //表示注解在什么时候有效
public @interface MyAnnotation{
// 具体的内容 , 在下面的自定义注解中解释
}
@Target
**例子: **
@Target(value = {ElementType.METHOD,ElementType.TYPE})
通过设置value的值, 可以指定该注解的作用范围 ,各种值如下:
/** Class, interface (including annotation type), or enum declaration */
TYPE, //类 接口 等
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD, //方法
/** Formal parameter declaration */
PARAMETER, // 参数
/** Constructor declaration */
CONSTRUCTOR, // 构造器
/** Local variable declaration */
LOCAL_VARIABLE, // 本地变量
/** Annotation type declaration */
ANNOTATION_TYPE, //注解
/** Package declaration */
PACKAGE, //包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
@Retention
使用举例:
@Retention(value = RetentionPolicy.RUNTIME) //设置作用时间为运行时
同样是通过设置value的值来指定生命周期 , 值如下
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
**注:生命周期大小 runtime>class>sources **
自定义注解
自定义注解 使用public @ interface 来声明
- 自定义注解中的每一个方法 实际上是声明的配置参数 ,在使用该注解的时候需要指定该参数的值, 不然会报错
- 方法的名称就是参数的名称
可以通过default来声明参数的默认值 ,这样调用时 可以不指定参数 ,也可以显示的赋值
规范: 如果参数成员只有一个 ,推荐名为value
反射机制
Java反射机制概述
在了解反射之前可以先理解一下静态和动态语言
- 动态语言是可以在运势是改变其结构的语言 ( 边编译边执行)。
- 动态语言在运行时无法改变结构 ( 一次编译 ,永久运行)。
- java通过引入反射的机制, 使得自身具有了一定的动态性。
反射
- 借助反射的API 可以获取类的任何内部信息 , 并且能够直接操作任意对象的内部属性以及方法。
- 类加载后 , 在堆内存的方法区中就产生了一个Class类型的对象( 一个类只有一个Class对象), 这个类就包含了完整的类的结构信息
- 如下图的最后流程图 , 通过反射的机制 , 可以创建一个对象, 通过对象 获取类 ,
反射机制研究和应用
反射优点和缺点:
在实现了动态创建对象和编译的时候 , 损失了一部分性能
代码测试
package com.qwrdxer.reflection;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的class对象
//可能找不到类, 需要抛出异常
Class c1=Class.forName("com.qwrdxer.reflection.User");
}
}
//定义一个实体类
class User{
private String name;
private int id;
private int 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() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"name\":\"")
.append(name).append('\"');
sb.append(",\"id\":")
.append(id);
sb.append(",\"age\":")
.append(age);
sb.append('}');
return sb.toString();
}
}
通过Class c1=Class.forName(“com.qwrdxer.reflection.User”); 来加载class对象 ,可以获取这个类的所有内部信息
理解Class类并获取Class实例
常用的方法:
获取Class类的实例方法
具有Class对象 的类型
Class c1=Object.class; //类
Class c2=Comparable.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
在数组中 , 只要元素类型 和维度一样,class一样
类的加载与ClassLoader
类的加载过程
类的加载与ClassLoader理解
测试代码
package com.qwrdxer.reflection;
public class Test05 {
public static void main(String[] args) {
A a=new A();
System.out.println(A.m);
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m=200;
}
static int m=100;
public A(){
System.out.println("A类的无参数构造初始化");
}
}
运行结果
分析
首先将类加载到方法区 ,在堆中, 生成class对象
链接 ,结束后m=0
初始化
- System.out.println(“A类静态代码块初始化”);
m=200;- m=100;
什么时候发生类的初始化
代码如下
package com.qwrdxer.reflection;
public class Test06 {
static {
System.out.println("Main类 加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1. 主动引用
Son son=new Son();
//输出结果:
//Main类 加载
//父类 加载
//子类 加载
//2. 反射 也会产生主动引用
Class.forName("com.qwrdxer.reflection.Test06");
//输出结果:
//Main类 加载
//父类 加载
//子类 加载
//不会产生类的引用的方法
System.out.println(Son.b);//父类静态变量
Son[] array=new Son[4];//创建数组
System.out.println(Son.M);//常量
}
}
class Father{
static int b=2;
static {
System.out.println("父类 加载");
}
}
class Son extends Father{
static {
System.out.println("子类 加载");
}
static int m=100;
static final int M=1;
}
类加载器的作用
根加载器是C++编写的,无法直接获取 返回null
package com.qwrdxer.reflection;
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader=ClassLoader.getSystemClassLoader();
//获取系统类加载器的父类加载器 --> 扩展类加载器
ClassLoader parent =systemClassLoader.getParent();
//获取扩展类加载器的父类加载器---> 根加载器(**根加载器是C++编写的,无法直接获取 返回null**)
ClassLoader parent1=parent.getParent();
//测试当前类加载器是由哪个加载器加载的
ClassLoader classLoader=Class.forName("com.qwrdxer.reflection.Test07").getClassLoader();
System.out.println(classLoader);
//sun.misc.Launcher$AppClassLoader@18b4aac2
//测试JDK内置的类是谁加载的
classLoader =Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
//null
//获取系统类加载器可以加载的路径
for(String s:System.getProperty("java.class.path").split(";")){
System.out.println(s);
}
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\charsets.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\deploy.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\access-bridge-64.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\cldrdata.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\dnsns.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\jaccess.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\jfxrt.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\localedata.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\nashorn.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunec.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunjce_provider.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunmscapi.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunpkcs11.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\zipfs.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\javaws.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\jce.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\jfr.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\jfxswt.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\jsse.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\management-agent.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\plugin.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\resources.jar
//C:\Program Files\Java\jdk1.8.0_251\jre\lib\rt.jar
//E:\平时写的代码\Java\注解和反射\out\production\注解和反射
//G:\develop\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
}
}
获取运行时类的完整结构
package com.qwrdxer.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1=Class.forName("com.qwrdxer.reflection.User");
System.out.println(c1.getName());//获取包名+类名
System.out.println(c1.getSimpleName());//获得类名
//获得类的属性
//Field[] fields = c1.getFields(); 只能找到public的属性
Field[] fields = c1.getDeclaredFields(); // 能找到全部属性
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性的值
Field name = c1.getDeclaredField("name");
//获得类的方法
Method[] methods = c1.getMethods();//获得本类和父类的全部public方法
for (Method method : methods) {
System.out.println(method);
}
Method[] declaredMethods = c1.getDeclaredMethods();//获得本类所有的方法
for (Method method : methods) {
System.out.println(method);
}
//获得类方法
Method getname=c1.getMethod("getName",null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getname);
//获得构造器
Constructor[] constructors = c1.getConstructors();
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
//获得指定的构造器
Constructor constructor = c1.getConstructor(String.class,int.class,int.class);
}
}
调用运行时类的指定结构
通过反射动态创建对象( 无参)
package com.qwrdxer.reflection;
//通过反射动态创建对象
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class c1=Class.forName("com.qwrdxer.reflection.User");
//创建对象
User user=(User)c1.newInstance();//本质是调用了类的无参构造器
System.out.println(user.toString());
}
}
有参构造器
//通过构造器创建对象
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
User user2 = (User)constructor.newInstance("test", 001, 18);
通过反射调用普通方法
//通过反射调用普通方法
User user3=(User)c1.newInstance();
//通过反射获取方法
Method setName=c1.getMethod("setName", String.class);
//invoke 激活 第一个参数为对象 ,第二个参数为要传递的参数值
setName.invoke(user3,"qwrdxer");
System.out.println(user3.toString());
通过反射操作属性
//通过反射操作属性
User user4=(User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//如果属性是私有的, 需要显示的设置可以访问(关闭程序的安全检测)
name.setAccessible(true);
//使用set方法设置
name.set(user4,"qwrdxer2");
System.out.println(user4);
setAccessible 性能分析
package com.qwrdxer.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class test10 {
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException {
User user=new User();
long startTime =System.currentTimeMillis();
for(int i=0;i<10000000;i++){
user.getAge();
}
long endTime =System.currentTimeMillis();
System.out.println("使用普通方式执行方法:"+(endTime-startTime)+"ms");
//反射的方式
Class c1=Class.forName("com.qwrdxer.reflection.User");
Method getName=c1.getMethod("getName");
User user2=new User();
long startTime2 =System.currentTimeMillis();
for(int i=0;i<10000000;i++){
getName.invoke(user2,null);
}
long endTime2 =System.currentTimeMillis();
System.out.println("使用反射方式执行方法:"+(endTime2-startTime2)+"ms");
//反射方式 , 关闭检测
Class c2=Class.forName("com.qwrdxer.reflection.User");
Method getName2=c2.getMethod("getName");
User user3=new User();
getName2.setAccessible(true);//关闭检测
long startTime3 =System.currentTimeMillis();
for(int i=0;i<10000000;i++){
getName2.invoke(user3,null);
}
long endTime3 =System.currentTimeMillis();
System.out.println("使用反射方式(关闭检测)执行方法:"+(endTime3-startTime3)+"ms");
}
}
获取泛型信息
代码如下
package com.qwrdxer.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获取泛型
public class Test11 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method test01 = Test11.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = test01.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);
}
}
}
}
}
输出:
#java.util.Map<java.lang.String, com.qwrdxer.reflection.User>
class java.lang.String
class com.qwrdxer.reflection.User
#java.util.List<com.qwrdxer.reflection.User>
class com.qwrdxer.reflection.User
反射操作注解
package com.qwrdxer.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.StringJoiner;
//练习反射操作注解
public class Test13 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.qwrdxer.reflection.Student2");
//通过反射获得注解
Annotation[] annotations = aClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解中Value的值
Tableau annotation = aClass.getAnnotation(Tableau.class);
String value = annotation.value();
System.out.println(value);
//获得类指定的注解
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
Fieldau annotation1 = field.getAnnotation(Fieldau.class);
System.out.println(annotation1.columnName());
System.out.println(annotation1.length());
System.out.println(annotation1.type());
}
// Field name = aClass.getDeclaredField("name");
// Fieldau annotation1 = name.getAnnotation(Fieldau.class);
// System.out.println(annotation1.type());
// System.out.println(annotation1.length());
// System.out.println(annotation1.columnName());
}
}
@Tableau("db_student")
class Student2{
@Fieldau(columnName = "db_id",type = "int",length = 10)
private int id;
@Fieldau(columnName = "db_age",type = "int",length = 10)
private int age;
@Fieldau(columnName = "db_name",type = "String",length = 10)
private String name;
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public Student2() {
}
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;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return new StringJoiner(", ", Student2.class.getSimpleName() + "[", "]")
.add("id=" + id)
.add("age=" + age)
.add("name='" + name + "'")
.toString();
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tableau {
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldau{
String columnName();
String type();
int length();
}
etId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return new StringJoiner(", ", Student2.class.getSimpleName() + "[", "]")
.add("id=" + id)
.add("age=" + age)
.add("name='" + name + "'")
.toString();
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tableau {
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldau{
String columnName();
String type();
int length();
}