Java学习笔记--注解和反射

时间:2022-08-04 18:06:18

注解和反射

1. 注解

注解作用:

  • 对程序做出解释
  • 被其他程序读取

注解格式:

  • @注释名,还可以添加一些参数值,例如@SuppressWarnings(value="unchecked").

注解使用在哪里

可以附加在package,class,method,filed,上面,相当于添加了额外的辅助信息,可以通过反射机制对这些元数据进行访问

内置注解

1. @Override

该注释只用于修饰方法,表示重写超类的一个方法,可以让让编译器检查该方法是否正确地实现了覆写

2. Deprecated

该注释可以用于修饰方法、属性、类,表示不鼓励使用,因为使用存在危险或有更好的选择

3. @SuppresseWarnings("para")

告诉编译器忽略此处代码产生的警告,需要参数才能正确的使用,例如 @SuppresseWarnings("all"),

@SuppresseWarnings("unchecked"), @SuppresseWarnings(value={"unchecked","deprecation"})

package com.annotation;

import java.util.ArrayList;
import java.util.List; public class TestAnnotation {
//override 方法重写注解
@Override
public String toString() {
return super.toString();
}
//Deprecated 不推荐使用,但是任然可以使用
@Deprecated
public static void test(){
System.out.println("test");
}
//SuppressWarnings(" ")忽视警告,可以放在方法上
@SuppressWarnings("all")
public void test01(){
List list = new ArrayList();
} }

元注解

元注解用于解释其他注解

@Target

表示注解使用范围

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER

@Retention

Retention 定义了Annotation的生命周期

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

如果@Retention不存在,则该Annotation默认为CLASS。因为通常我们自定义的Annotation都是RUNTIME,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)这个元注解

@Inherited

使用@Inherited定义子类是否可继承父类定义的Annotation@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效

@Documented

表明该注解将被包含于javadoc内

public class TestMetaAnnotation {

    @MyAnnotation
public void testMetaAnnotation(){ } }
//定义一个元注解
//Target 表示注解使用范围
@Target(value = ElementType.METHOD) //Retention 定义了Annotation的生命周期
// runtime>class>source
@Retention(value = RetentionPolicy.RUNTIME) //表明该注解将被包含于javadoc内
@Documented //子类可以继承父类的注解
@Inherited @interface MyAnnotation{
}

自定义注解@interface

使用@interface自动继承Annotation接口

Java学习笔记--注解和反射

public class TestCreateAnnotation {
@MyAnnotation02(name = "",age = 10)
public void test(){
}
@MyAnnotation03("Fuck") //只有一个参数时直接赋值
public void test03(){
} } //创建注解
@Target({ElementType.TYPE,ElementType.METHOD})
@interface MyAnnotation02{
//注解参数 : paraType paraName() |(default value);
String name() default ""; //如果不设置默认值,使用注解时必须给参数赋值
int age(); //使用时必须赋值
int id() default -1; //default -1 表示默认不存在 String[] school() default {"Tsinghua","Peking"}; } @Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation03{ String value(); //只有一个参数成员时,参数名一般为value
}

2.反射

3.1 反射机制

  • 反射机制允许程序在运行期间借助与Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法
  • 加载完类后,在堆内存的方法区产生了一个Class类型的对象,这个对象包含了完整的类的结构信息,我们可以通过这个对象看到类的结构

Java学习笔记--注解和反射

  • 反射实现了语言的动态创建和编译,具有很大的灵活性
  • 反射机制是一种解释型的操作,执行效率较低

3.2 Class类

Java学习笔记--注解和反射

Class类的常用方法

Java学习笔记--注解和反射

获取类的Class对象

  1. 对于已知的类,直接通过类的class对象获取,该方法最为可靠,性能最好

    Class clz = Cls.class;
  2. 对于某个实例,调用getClass方法获取

    Class clz = cls.getClass();
  3. 通过类的路径以及类名,调用Class类的静态方法forName方法获取

    Class clz = forName("packegePath.className");
  4. 内置基本数据类型直接使用 className.Type获取

  5. ClassLoader获取

//获取Class类对象
public class TestGetClassInstance {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Person();
System.out.println("He is "+person.name);
Person s1 = new Student();
Class cs = s1.getClass(); //通过类名直接获取
Class c1 = Person.class;
System.out.println(c1.hashCode());
//forName 方法获取
Class c2 = Class.forName("com.Reflection.Demo01.Person");
System.out.println(c2.hashCode());
//实例.getClass()方法获取
Class c3 = person.getClass();
System.out.println(c3.hashCode()); //c1 ~ c3 The same HashCode
// 基本内置类型的包装类的Type属性
Class c4 = Integer.TYPE;
System.out.println(c4); //获得父类类型(实际类型)
Class c5 = cs.getSuperclass();
System.out.println(c5); } }
class Person{
public String name; public Person() {
} @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";
}
}

拥有Class类型的对象

Java学习笔记--注解和反射

public class TestClassesPossessedClassAttribute {
public static void main(String[] args) {
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 //alt+鼠标左键选中一列数据
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); //只要元素的类型与维度一样,就是同一个class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode()); //Same HashCode
System.out.println(b.getClass().hashCode());

类加载过程

Java学习笔记--注解和反射

Java学习笔记--注解和反射

public class TestLoadClass {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
/*
1.加载到内存,生成类的Class对象
A.class;TestLoadClass.class; 2.链接,为类变量分配内存和赋初值
m=0; 3.初始化
执行类构造器方法<clinit>()将所有类变量赋值以及静态代码块合并形成
<clinit>(){
static {
System.out.println("Static Filed");
m = 200;
}
static int m= 100;
}
*/ }
}
class A{
static {
System.out.println("Static Filed");
m = 200;
}
/*
m=200;
m=100
*/
static int m= 100;
public A(){
System.out.println("A类的无参构造初始化");
}
}

类的初始化

Java学习笔记--注解和反射

public class TestInitClass {
static {
//1.main方法所在类初始化
System.out.println("buying a gun");
}
public static void main(String[] args) throws ClassNotFoundException {
//2.主动引用new
//Robber robber = new Robber();
//3.反射引用
//Class.forName("com.Reflection.Demo01.Robber"); //不会产生类的初始化 //1.调用子类继承父类的静态变量和方法时
//System.out.println(Robber.moneyBefore);
//Robber.b(); //2.初始化类的数组时
Robber[] robbers = new Robber[10]; //3.引用常量(链接阶段就存入调用类的常量池了)
System.out.println(Robber.escapeMoney); } } //测试类初始化
class Criminal{
static double moneyBefore = 20;
static {
System.out.println("Gives me your fucking money!");
}
static void b(){
System.out.println("父类静态方法调用");
} }
class Robber extends Criminal{
static {
System.out.println("This is a robbery! Put your hands up!!");
double getMoney = 292898;
}
static double robberMoney = 8361303.222;
static final double escapeMoney = 1000;
}

类加载器作用

Java学习笔记--注解和反射

Java学习笔记--注解和反射

双亲委派机制

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

Java学习笔记--注解和反射

public class TestClassLoader {
public static void main(String[] args) {
//获取系统类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
// 获取系统类加载器父类---扩展类加载器
classLoader = classLoader.getParent();
System.out.println(classLoader);
// 获取扩展类加载器父类---根加载器(无法获取:null)
classLoader = classLoader.getParent();
System.out.println(classLoader); //测试当前类的ClassLoader
classLoader = TestLoadClass.class.getClassLoader();
System.out.println(classLoader); //测试JDK内置类的加载器
classLoader = String.class.getClassLoader();
System.out.println(classLoader); //获得系统类加载器路径
System.out.println(System.getProperty("java.class.path"));
}
}

3.3 反射操作

1. 获取类的结构

package com.reflection.Demo02;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; //Testing using the class object to obtain the contents of a class
public class TestGetClassInfo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
// Get the Class of a class by path
Class c1 = Class.forName("com.reflection.Demo01.User"); //Get ClassName
System.out.println(c1.getName()); //package + ClassName
System.out.println(c1.getSimpleName()); //ClassName
System.out.println();
//Get all public properties
Field[] fields = c1.getFields(); //getField() only used on properties with decoration of public
for (Field field : fields) { //out: null ,no method decorated with public
System.out.println(field);
}
//Get all properties
fields = c1.getDeclaredFields(); //getDeclaredFields() used on all types of properties
for (Field field : fields) {
System.out.println(field);
}
System.out.println(); //Get particular property
//only public
//Field name = c1.getField("name"); -> NoSuchFieldException
//out.println(name);
Field name = c1.getDeclaredField("name");
System.out.println(name);
System.out.println(); //Get methods
Method[] methods = c1.getMethods();//all public methods include methods inherited superClass or superInterface
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
methods = c1.getDeclaredMethods();
for (Method method : methods) { //all methods exclude inherited
System.out.println(method);
}
System.out.println(); //Get particular method getMethod(name,paraType.class)
Method method1 =c1.getMethod("getName",null);
Method method2 =c1.getMethod("setName",String.class);
System.out.println(method1);
System.out.println(method2);
System.out.println(); //Get Constructors
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) { //public constructors
System.out.println(constructor);
}
System.out.println();
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {//all
System.out.println(constructor);
}
System.out.println(); //get particular public constructor getConstructor(para.class|null);
Constructor constructor = c1.getConstructor(String.class);
System.out.println(constructor);
System.out.println();
//get particular constructor getDeclaredConstructor(para.class|null);
constructor = c1.getDeclaredConstructor(String.class);
System.out.println(constructor);
constructor = c1.getDeclaredConstructor(null);
System.out.println(constructor); }
}

获取父类:getSuperclass()

获取继承的接口:getInterfaces()

如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom()

2.反射创建对象

方法1:Class.newInstance() 方法(java9后弃用)

//获取对象
Class c1 = User.class;
//构造一个对象
User user = (User)c1.newInstance();
System.out.println(user);

局限:它只能调用该类的public无参数构造方法。

方法2:通过构造器对象newInstance()方法创建对象

Constructor constructor = c1.getDeclaredConstructor(String.class,String.class,int.class,String.class);
User user2 = (User)constructor.newInstance("001","wang",20,"Male");
System.out.println(user2);

调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问。

3.反射调用方法

Method sN = c1.getDeclaredMethod("setName",String.class);
sN.invoke(user2,"Liu");//method.invoke(obj,value)
System.out.println(user2.getName());

method对象包含信息

  • getName():返回方法名称,例如:"getScore"
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null

调用非public方法,我们通过Method.setAccessible(true)允许其调用:

4.设置字段值

Field name= c1.getDeclaredField("name");
name.setAccessible(true); //不能直接操作私有属性,需要设置访问安全检查为true
name.set(user2,"Li"); //field.set(fieldObj,value)
System.out.println(user2.getName());

一个Field对象包含了一个字段的所有信息:

  • getName():返回字段名称,例如,"name"
  • getType():返回字段类型,也是一个Class实例,例如,String.class
  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。

修改非public字段,需要首先调用setAccessible(true)

5.SetAccessible

Java学习笔记--注解和反射

public class TestSetAcc {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
User user = new User();
long start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000_000; i++) {
user.getName();
}
long end = System.currentTimeMillis();
System.out.println("普通方式调用方法十亿次"+(end-start)+"ms"); //反射方式
TestSetAcc.test();
TestSetAcc.test01(); }
public static void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class c1 = User.class;
User user = new User();
Method method= c1.getDeclaredMethod("getName",null);
method.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000_000; i++) {
method.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println("关闭检测方式调用十亿次"+(end-start)+"ms");
} public static void test01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class c1 = User.class;
User user = new User();
Method method= c1.getDeclaredMethod("getName",null);
method.setAccessible(false);
long start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000_000; i++) {
method.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println("检测方式调用十亿次"+(end-start)+"ms");
}
}

6.反射操作泛型

Java学习笔记--注解和反射

//反射获取泛型
public class TestReflectionGeneric {
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 method = TestReflectionGeneric.class.getDeclaredMethod("test01",Map.class,List.class);
Type[] genericParameterTypes= method.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);
}
}
}
System.out.println();
Method method01 = TestReflectionGeneric.class.getDeclaredMethod("test02",null);
Type genericReturnTypeType = method01.getGenericReturnType();
if(genericReturnTypeType instanceof ParameterizedType){
Type[] actualTypeArguments= ((ParameterizedType) genericReturnTypeType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
} }
} }

7.反射操作注解

//反射操作注解
public class TestReflectionAnnotation {
public static void main(String[] args) throws NoSuchFieldException {
//反射获得类的注解
Class c1 = Student.class;
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value
Table table = (Table) c1.getAnnotation(Table.class);
System.out.println(table.value()); //获得指定注解
Field field = c1.getDeclaredField("id");
FieldAnn annotation = field.getAnnotation(FieldAnn.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
} }
@Table("TableStudent")
class Student{
@FieldAnn(type = "varchar",length = 10,columnName = "db_id")
private String id;
@FieldAnn(type = "db_int",length = 10,columnName = "db_age")
private int age ;
@FieldAnn(type = "varchar",length = 10,columnName = "dn_name")
private String name; public Student() {
} 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;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
} //class annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//field annotation
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnn{
String columnName();
String type();
int length();
}