注解
内置注解
- @Deprecated 废弃的,过时的
- @Override 重写,覆盖
- @SuppressWarnings 压缩警告,抑制编译器产生的警告
自定义注解
-
@Target:用于表明注解的适用范围
- ElementType.Type,可以作用于类上
- ElementType.METHOD,可以作用于方法上
- ElementType.FIELD,可以作用于成员变量上
-
@Retention表示需要在什么级别保存该注释信息(生命周期)
- RetentionPolicy.RUNTIME : 在运行时保留注解
-
@Documented 描述注解是否被抽取到api文档中
-
@Inherited 描述注解是否被子类继承。
自定义注解例子:
- 注意:只有一个参数的时候,可以写成value().
/**
* 自定义注解
* @author Created by zhuzqc on 2022/5/31 23:03
*/
public class CustomAnnotation {
/**
* 注解中可以为参数赋值,如果没有默认值,那么必须为注解的参数赋值
* */
@MyAnnotation(value = "解释")
public void test(){
}
}
/**
* @author zhuzqc
*/
//自定义注解必须的元注解target,指明注解的作用域(此处指明的是在类和方法上起作用)
@Target({ElementType.TYPE,ElementType.METHOD})
//元注解retention声明该注解在何时起作用(此处指明的是在运行时起作用)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
//注解中需声明参数,格式为:参数类型 + 参数名();
String value() default "";
}
反射
获取Class类实例的六种方法
- 通过类的class属性获取,最安全可靠,性能高
Class user = User.class;
- 通过类的实例,getClass方法获取
Class user = user.getClass();
- 通过全类名获取
Class user = class.forName("com.george.pojo.User");
-
利用ClassLoader(类加载器)获取。
-
通过子类来获取父类的Class
Class person = Student.getSuperClass();
-
内置基本数据类型可以使用类名加type获取
Class<Integer> type = Integer.Type;
哪些类可以有Class对象
- class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口 (Comparable.class)
- []: 数组 (String[].class)
- enum: 枚举 (ElementType.class)
- annotation:注解@interface (Override.class)
- primitive type:基本数据类型 (Integer.class, int.class)
- void (void.class)
Class类的常用方法
类加载器
作用:将class文件字节码内容加载到内容中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
分类:系统的类加载器、扩展类加载器、根加载器
package com.njit.reflection;
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统的类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取系统类加载器的父类加载器--> 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取扩展类加载器的父类加载器 --> 根加载器(c/c++),会返回null,这个根加载器是无法直接获取的
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
// 测试当前类的类加载器
ClassLoader classLoader = Class.forName("com.njit.reflection.Test03").getClassLoader();
System.out.println(classLoader);
// 测试jdk内置的类加载器 ,也是根类加载器
ClassLoader commonClassLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(commonClassLoader);
// 获取系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
获取Class类实例后,能干什么
-
创建类的对象
1.1 调用Class对象的newInstance()方法
要求:
- 类必须要有一个无参数的构造器
- 类的构造器的访问权限需要足够
1.2 没有无参数构造器的话,需要在操作时明确的调用类中的构造器,并传递参数
步骤
- 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
- 向构造器的形参中传递对象数组,包含需要的各个参数
- 通过Constructor实例化对象
Construct constructor = c1.getDeclaredConstructor(String.class,int.class,int.class); User user = (User)constructor.newInstance("zxj",001,18);
-
调用指定的方法
2.1 调用getMethod(String name , Class…parameterTypes)取得Method对象。
2.2 调用Obejct invoke(Object obj,Object[] args)进行调用,并向方法中传递参数信息。
**注意:**若方法为private,则需要在invoke之前,调用setAccessible(true),取消语言访问检查
User user = (User)c1.newInstance(); Method setName = c1.getDeclaredMethod("setName",string.class); setName.invoke(user3,"zxj"); //-------------------------------------------------------------------- //获得本类和父类的全部方法 c1.getMethods(); //获得本类的全部方法 c1.getDeclaredMethods(); //获得指定的方法 c1.getMethod("getName",null); c1.getMethod("setName",String.class);
提问:为什么已经有了user对象了,还需要getDeclaredMethod和invoke呢,为什么不直接调用方法呢?
-
方法的访问控制:使用
newInstance()
只是创建了一个对象实例,它并没有改变原始类或方法的访问修饰符。如果一个方法是私有的或受保护的,直接使用newInstance()
是无法访问和调用的。而getMethod()
可以获取到指定名称和参数类型的方法,不论其访问修饰符如何,并将其封装成Method
对象,然后可以使用invoke()
执行该方法。 -
方法的重载:如果一个类中存在多个具有相同名字但参数类型不同的方法(方法重载),直接使用
newInstance()
创建的对象无法明确指定要调用哪个方法。需要使用getMethod()
来获取到特定的方法,并提供准确的参数类型信息,以便选择正确的方法调用。 -
动态代理时需要调用invoke,并在前后增加功能
-
-
获取指定的属性和值
//找到public属性 Field[] fields = c1.getFields(); //找到全部属性 fields = c1.getDeclaredFields(); //获得指定属性的值 Fields = c1.getDeclaredField("name");
-
反射操作注解
Tips:注解也是一种class
package com.njit.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.njit.reflection.Animal");
// 通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
System.out.println(annotations);
// 获取注解的value值
MyTest myTest = (MyTest) c1.getAnnotation(MyTest.class);
System.out.println(myTest.value());
// 获取类的字段的指定注解
Field field = c1.getDeclaredField("name");
FieldTest annotation = field.getAnnotation(FieldTest.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@MyTest("db_test")
class Animal{
@FieldTest(columnName = "id", type = "Integer", length = 10)
private int id;
@FieldTest(columnName = "age", type = "Integer", length = 8)
private int age;
@FieldTest(columnName = "name", type = "String", length = 10)
private String name;
public Animal() {
}
public Animal(int id, int age, String name) {
this.id = id;
this.age = age;
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;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
/**
* 自定义的简单注解,注解在类上
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
String value();
}
/**
* 自定义的简单注解,注解在属性上
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest{
String columnName();
String type();
int length();
}