一、注解
Annotation 是从 JDK 1.5 开始引入的新技术(
@注释名
)。
- 可以对程序作出解释(程序的注释
comment
)。- 可以被其他程序读取。
- 可以通过反射机制编程实现对这些元数据的访问。
1. 内置注解
- java.lang 包
:@Deprecated
此注解可以用于修辞方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它已弃用或者存在更好的选择。@FunctionalInterface
:(JDK 1.8)
此注解用于修饰 匿名函数 或 函数式接口。@Override
:
此注解只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。@SafeVarargs
:(JDK 1.7)
用来抑制编译时参数为泛型的方法或构造函数产生的警告。@SuppressWarnings(value = {...})
:all
、unchecked
、deprecation
、serial
用来抑制编译时的警告。
- java.lang.annotation 包
@Native
:
用于描述这个变量可以被本地代码引用,常被代码生成工具使用。
// 包含在 javadoc 中
@Documented
// 字段
@Target(ElementType.FIELD)
// 源码有效
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}
2. 元注解(ANNOTATION_TYPE
元注解类型)
负责注释自定义注解,Java5 定义了四个标准的
meta-annotation
类型,用来对自定义 Annotation 类型做说明。
- JDK 1.5
@Documented
:
用于判断是否生成到 javadoc 中。@Inherited
:
用于描述注解是否具备继承传递性(不是注解与注解继承,是被注解修饰的父类,子类可以继承到父类修饰的注解)子类可以继承到父类中修饰的注解。@Retention
:
用于描述注解的生命周期(SOURCE
源码 <CLASS
字节码 <RUNTIME
运行时)。
- SOURCE:只保留在源文件,当 .java 文件编译成 .class文件时,注解被遗弃。
- CLASS:保留到 .class 文件,但 JVM 加载 .class 文件时,注解被遗弃。
- RUNTIME:保留在1、2中,并且运行时依然有效。
@Target
:
用于描述注解的使用范围(即:注解可以用在什么地方)。
- TYPE:类、接口(包括注解类型)、枚举。
- FIELD:字段。
- METHOD:方法。
- PARAMETER:参数。
- CONSTRUCTOR:构造。
- LOCAL_VARIABLE:局部变量。
- ANNOTATION_TYPE:注解类型。
- PACKAGE:包。
- TYPE_PARAMETER:参数类型(形式参数类型)(JDK 1.8)。
- TYPE_USE: 任何位置都可以(JDK 1.8)。
- JDK 1.8
@Repeatable
:
可重复注解,在一个声明上使用同一个注解多次时(Java 8 版本以前,同一个元素上最多只能有一个相同类型的注解,如果需要,则必须使用 注解容器)。
// 包含在 javadoc 中
@Documented
// 运行时有效
@Retention(RetentionPolicy.RUNTIME)
// 元注解类型
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
Class<? extends Annotation> value();
}
2.1 Java8 之前重复注解
public @interface Roles {
Role[] roles();
}
public @interface Role {
String roleName();
}
public class RoleTest {
@Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
public String doString(){
return "Java8 之前重复注解";
}
}
2.2 Java8 之后重复注解
Java8 之后重复注解,是一种简化写法。
public @interface Roles {
Role[] value();
}
// 指向存储注解 Roles
@Repeatable(Roles.class)
public @interface Role {
String roleName();
}
public class RoleTest {
@Role(roleName = "role1")
@Role(roleName = "role2")
public String doString(){
return "Java8 之后重复注解";
}
}
3. 自定义注解
自定义注解,默认自动继承了
java.lang.annotation.Annotation
接口。
3.1 @MyAnnotation、@QsAnnotation
import java.lang.annotation.*;
/**
* @author: wy
* describe: 3. 自定义注解
*/
// 生成到 javadoc 中
@Documented
// 具备继承传递性
@Inherited
// 运行时有效
@Retention(value = RetentionPolicy.RUNTIME)
// 类、方法、属性
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface MyAnnotation {
/**
* 注解参数格式:'参数类型' '参数名'() default '参数默认值';
* 1. 无默认值时,必须写参数
* 2. 有默认值时,可以不写参数
*/
String value() default "my";
String[] values() default {"张三", "李四"};
int count() default 0;
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD})
public @interface QsAnnotation {
String value() default "qs";
}
3.2 @Roles、@Role
重复注解
import java.lang.annotation.*;
/**
* @author: wy
* describe: 重复注解
*/
@Repeatable(Roles.class)
public @interface Role {
String value();
}
@Inherited
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD})
@interface Roles {
Role[] value();
}
3.3 MyAnnotationMain、MyAnnotationTest
boolean isAnnotation()
:当前元素是否是注解类型。boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
:当前元素及其父元素上的是否存在指定注解(父元素上的注解需要具备继承传递性)。Annotation[] getAnnotations()
:获取当前元素及其父元素上的所有注解(父元素上的注解需要具备继承传递性)。Annotation[] getDeclaredAnnotations()
:获取当前元素上的所有注解。<A extends Annotation> A getAnnotation(Class<A> annotationClass)
:获取当前元素及其父元素上的指定注解(父元素上的注解需要具备继承传递性)。<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
:获取当前元素上的指定注解。<A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass)
:获取当前元素及其父元素上的指定重复注解(父元素上的注解需要具备继承传递性)。<A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass)
:获取当前元素上的指定重复注解.
/**
* @author: wy
* describe: 使用注解
*/
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@MyAnnotation
public class MyAnnotationMain implements Serializable {
private String name;
public static void main(String[] args) throws NoSuchFieldException {
Class<? extends MyAnnotationTest> clazz = MyAnnotationTest.class;
// 1. 当前元素是否是注解类型
System.out.println(clazz.isAnnotation());
// false
System.out.println(QsAnnotation.class.isAnnotation());
// true
// 2. 获取当前元素及其父元素上的是否存在指定注解
System.out.println(clazz.isAnnotationPresent(MyAnnotation.class));
// true(因为`MyAnnotation`注解被`Inherited`修饰,具备继承传递性)
System.out.println(clazz.isAnnotationPresent(QsAnnotation.class));
// true
System.out.println("--------------------");
// 3. 获取当前元素及其父元素上的所有注解(父元素上的注解需要具备继承传递性)
System.out.println("getAnnotations");
Annotation[] annotations = clazz.getAnnotations();
Arrays.stream(annotations).forEach(System.out::println);
/*
@com.qs.javareflect.annotation.MyAnnotation(count=0, value=my, values=[张三, 李四])
@com.qs.javareflect.annotation.QsAnnotation(value=qs)
@com.qs.javareflect.annotation.Roles(value=[@com.qs.javareflect.annotation.Role(value=DEV), @com.qs.javareflect.annotation.Role(value=TEST)])
*/
// 获取当前元素上的所有注解
System.out.println("getDeclaredAnnotations");
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
Arrays.stream(declaredAnnotations).forEach(System.out::println);
/*
@com.qs.javareflect.annotation.QsAnnotation(value=qs)
@com.qs.javareflect.annotation.Roles(value=[@com.qs.javareflect.annotation.Role(value=DEV), @com.qs.javareflect.annotation.Role(value=TEST)])
*/
System.out.println("--------------------");
// 获取当前元素及其父元素上的指定注解(父元素上的注解需要具备继承传递性)
System.out.println("getAnnotation");
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
/*
@com.qs.javareflect.annotation.MyAnnotation(count=0, value=my, values=[张三, 李四])
*/
// 获取当前元素上的指定注解
System.out.println("getDeclaredAnnotation");
QsAnnotation declaredAnnotation = clazz.getDeclaredAnnotation(QsAnnotation.class);
System.out.println(declaredAnnotation);
/*
@com.qs.javareflect.annotation.QsAnnotation(value=qs)
*/
System.out.println("--------------------");
// 获取当前元素及其父元素上的指定重复注解(父元素上的注解需要具备继承传递性)
System.out.println("getAnnotationsByType");
MyAnnotation[] annotationsByType = clazz.getAnnotationsByType(MyAnnotation.class);
Arrays.stream(annotationsByType).forEach(System.out::println);
/*
@com.qs.javareflect.annotation.MyAnnotation(count=0, value=my, values=[张三, 李四])
*/
// 获取当前元素上的指定重复注解
System.out.println("getDeclaredAnnotationsByType");
Role[] declaredAnnotationsByType = clazz.getDeclaredAnnotationsByType(Role.class);
Arrays.stream(declaredAnnotationsByType).forEach(System.out::println);
/*
@com.qs.javareflect.annotation.Role(value=DEV)
@com.qs.javareflect.annotation.Role(value=TEST)
*/
}
@Override
public String toString() {
return super.toString();
}
@Test
public void test() {
List list;
}
}
@QsAnnotation
@Role("DEV")
@Role("TEST")
class MyAnnotationTest extends MyAnnotationMain {
private String name;
}
二、Java 内存分析
1. 类加载器 ClassLoader
- 类加载过程:
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
1.1 类加载并初始化过程
- 类加载并初始化过程:
- 加载:将 .class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构。然后生成一个代表这个类的
java.lang.Class
对象。- 链接:将 Java 类的二进制代码合并到 JVM 的运行状态之中的过程。
- 验证:确保加载的类信息符合 JVM 规范,没有安全方面的问题。
- 准备:正式为类静态变量(static)分配内存并设置默认初始值,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
- 初始化:
- 执行类构造器
<clinit>()
方法的过程,类构造器<clinit>()
方法是由编译期,自动收集类中所有 类变量的赋值动作 和 静态代码块中的语句 合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的
<clinit>()
方法在多线程环境中被正确加锁和同步。
1.2 类在什么时候初始化
- 类在什么时候会初始化:
- 类的主动引用(一定会发生类的初始化)。
- 当虚拟机启动,先初始化
main()
方法所在的类。new
一个类的对象。- 调用类的静态成员(除了
final
常量)和静态方法。- 使用
java.lang.reflect
包的方法对类进行反射调用。- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
- 类的被动引用(不会发生类的初始化)。
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。
如:当通过子类引用父类的静态变量,不会导致子类初始化(Son.name;
)。- 通过数组定义类引用,不会触发此类的初始化。
如:Son[] arr = new Son[5];
。- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)。
/**
* @author: wy
* describe: 1.2 类在什么时候初始化
*/
public class ClassLoaderMain {
static {
System.out.println("ClassLoaderMain 类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 1. 当虚拟机启动,先初始化 main() 方法所在的类。
/*
ClassLoaderMain 类被加载
*/
System.out.println("--------------------");
// 2. new 一个类的对象。
// Son son = new Son();
/*
Father 类被加载
Son 类被加载
*/
System.out.println("--------------------");
// 3. 调用类的静态成员(除了 final 常量)和静态方法。
// System.out.println(Son.name2);
/*
Father 类被加载
Son 类被加载
Son
*/
System.out.println("--------------------");
// 4. 使用 java.lang.reflect 包的方法对类进行反射调用。
// Class.forName("com.qs.javareflect.classloader.Son");
/*
Father 类被加载
Son 类被加载
*/
System.out.println("====================");
// 1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化。
// System.out.println(Son.name1);
/*
Father 类被加载
Father
*/
System.out.println("--------------------");
// 2. 通过数组定义类引用,不会触发此类的初始化。
Son[] sons = new Son[5];
System.out.println("--------------------");
// 3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)。
System.out.println(Son.age);
/*
18
*/
}
}
class Father {
static {
System.out.println("Father 类被加载");
}
static String name1 = "Father";
}
class Son extends Father {
static {
System.out.println("Son 类被加载");
}
static String name2 = "Son";
static final int age = 18;
}
2. 类加载器的作用
- 类加载过程:
将 .class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class
对象, 作为方法区中类数据的访问入口。- 类缓存:
标准的 JavaSE 类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过 JVM 垃圾回收机制可以回收这些 Class 对象。- 类加载器作用是用来把类 .class 文件装载进内存的。
3. JVM 的类加载器
JVM 规范定义了如下类型的类加载器。
- 引导类加载器(根类加载器)。
- 扩展类加载器。
- 系统类加载器(应用类加载器)。
/**
* 3. JVM 的类加载器
*/
@Test
public void test() {
// 1. 获取系统类加载器(应用类加载器)
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);// sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println("--------------------");
// 获取系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
System.out.println("--------------------");
// 2. 获取扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);// sun.misc.Launcher$ExtClassLoader@27716f4
System.out.println("--------------------");
// 3. 获取根类加载器(C/C++写的,获取不到)
ClassLoader parent2 = parent.getParent();
System.out.println(parent2);// null
}
@Test
public void test2() throws ClassNotFoundException {
// 1. 获取当前类的类加载器(应用类加载器)
ClassLoader classLoader = Class.forName("com.qs.javareflect.classloader.ClassLoaderMain2").getClassLoader();
System.out.println(classLoader);// sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println("--------------------");
// 2. Java内部类的类加载器(根类加载器)
ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader2);// null
}
三、反射机制
反射机制允许程序在执行期,借助于
Reflection API
取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 动态语言
- 在运行时可以改变其结构的语言。
- 如:新的函数、对象、甚至代码可以被引入,已有的函数可以被删除或结构上的改变。
- 主要的动态语言:
Object-C
、C#
、JavaScript
、PHP
、Python
等。
- 静态语言
- 在运行时结构不可变的就是静态语言。
- 如:
Java
、C
、C++
。- Java 不是动态语言,但 Java 可以称之为 准动态语言(可以利用反射机制获得类似动态语言的特性)。
Class<?> clazz = Class.forName("java.lang.String");
System.out.println(clazz.hashCode());
加载完 String 类之后,在堆内存的方法区中就产生了一个
Class
类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类结构信息。
1. 反射的功能
- 在运行时,构造任意一个类的对象。
- 在运行时,判断任意一个类所具有的成员变量和方法。
- 在运行时,判断任意一个对象所属的类。
- 在运行时,调用任意一个对象的成员变量和方法。
- 在运行时,处理注解。
- 在运行时,获取泛型信息。
- 在运行时,生成动态代理。
2. 反射优缺点
- 优点:
可以动态的创建和编译对象,很大的灵活性。- 缺点:
对性能有影响,使用反射是一种解释操作。
我们可以告诉 JVM,希望做什么并且满足我们的要求。这类操作总是慢于直接执行相同的操作。
3. 反射API
3.1 java.lang.Class
- Class 也是一个类:
- Class 对象只能由系统创建。
- 一个类在 JVM 内存中只有一个 Class 对象。
- 一个 Class 对象对应的是加载到 JVM 中的一个 .class 文件。
- 一个类的所有实例都是由同一个 Class 实例生成。
- 通过 Class 对象可以得到一个类的整个结构。
- Class 类是 Java Reflection 的根源(任何想动态加载运行的类,唯有先获得相应的 Class 对象)。
- Class 类常用方法
方法名 | 描述 |
---|---|
static Class<?> forName(String className) | 返回指定 className(类全路径) 的 Class 对象 |
native Class<? super T> getSuperclass() | 返回 Class 对象的父类 Class 对象 |
ClassLoader getClassLoader() | 获取当前类的类加载器 |
Class<?>[] getInterfaces() | 获取 Class 对象的接口 |
T newInstance() | 创建 Class 对象所表示的元素(类、接口、数组类或void)对象 |
Constructor<?>[] getConstructors() | 获取当前类的 Constructor 对象数组 |
Method getMethod(String name, Class<?>... parameterTypes) | 获取当前类指定方法名(name)和参数类型(parameterTypes)的 Method 对象 |
Field[] getDeclaredFields() | 获取当前类的 Field 对象数组 |
/**
* 3.1 java.lang.Class
*/
@Test
public void test() throws ClassNotFoundException {
// 通过反射获取类的 Class 对象
Class<?> clazz = Class.forName("com.qs.javareflect.clazz.Teacher");
System.out.println(clazz);// class com.qs.javareflect.clazz.Teacher
System.out.println("--------------------");
// 获得父类类型
Class<?> superclass = clazz.getSuperclass();
System.out.println(superclass);// class com.qs.javareflect.clazz.Person
System.out.println("--------------------");
// 获取根类型
Class<? extends Class> rootClass = clazz.getClass();
System.out.println(rootClass);// class java.lang.Class
}
3.2 Class 对象的三种创建方式
/**
* 3.2 Class 对象的三种创建方式
*/
@Test
public void test2() throws ClassNotFoundException {
Person person = new Student();
System.out.println(person.name);// 学生
System.out.println("--------------------");
// 1. 通过`对象`获得
Class<? extends Person> clazz1 = person.getClass();
// 2. 通过`Class.forName()方法`获得
Class<?> clazz2 = Class.forName("com.qs.javareflect.clazz.Student");
// 3. 通过`类名.class`获得
Class<Student> clazz3 = Student.class;
System.out.println(clazz1.hashCode());// 1463801669
System.out.println(clazz2.hashCode());// 1463801669
System.out.println(clazz3.hashCode());// 1463801669
System.out.println("--------------------");
// 4. 基本类型的包装类,都有一个`内置类型 Type`属性
Class<Integer> clazzInteger = Integer.TYPE;
System.out.println(clazzInteger);// int
}
3.3 那些类型可以创建 Class 对象
- 那些类型可以创建 Class 对象:
class
:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类interface
:接口primitive type
:基本数据类型[]
:数组annotation
:注解@interfaceenum
:枚举void
/**
* 3.3 那些类型可以创建 Class 对象
*/
@Test
public void test3() {
// 1. 类
Class<Object> clazz1 = Object.class;
System.out.println(clazz1);// class java.lang.Object
Class<Class> clazz12 = Class.class;
System.out.println(clazz12);// class java.lang.Class
System.out.println("--------------------");
// 2. 接口
Class<Comparable> clazz2 = Comparable.class;
System.out.println(clazz2);// interface java.lang.Comparable
System.out.println("--------------------");
// 3. 基本数据类型
Class<Integer> clazz3 = Integer.class;
System.out.println(clazz3);// class java.lang.Integer
System.out.println("--------------------");
// 4. 一维数组
Class<String[]> clazz4 = String[].class;
System.out.println(clazz4);// class [Ljava.lang.String;
// 二维数组
Class<int[][]> clazz41 = int[][].class;
System.out.println(clazz41);// class [[I
System.out.println("--------------------");
// 5. 注解
Class<Override> clazz5 = Override.class;
System.out.println(clazz5);// interface java.lang.Override
System.out.println("--------------------");
// 6. 枚举
Class<ElementType> clazz6 = ElementType.class;
System.out.println(clazz6);// class java.lang.annotation.ElementType
System.out.println("--------------------");
// 7. void
Class<Void> clazz7 = void.class;
System.out.println(clazz7);// void
}
4. 获取运行时类的结构
- Interface:实现的全部接口。
- Superclass:所继承的父类。
- Annotation:运行时注解。
- Constructor:构造。
- Method:方法。
- Field:属性。
4.1 Interface、Superclass、java.lang.annotation.Annotation
/**
* 4.1
* Interface 实现的全部接口
* Superclass 所继承的父类
* java.lang.annotation.Annotation 运行时注解
*/
@Test
public void test() throws ClassNotFoundException {
Class clazz = Class.forName("com.qs.javareflect.clazz.User");
// 1. 获取全类名(包名 + 类名)
System.out.println(clazz.getName());// com.qs.javareflect.clazz.User
System.out.println("--------------------");
// 2. 获取简单类名(类名)
System.out.println(clazz.getSimpleName());// User
System.out.println("--------------------");
// 3. 实现的全部接口
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
System.out.println(c);
}
/*
interface java.io.Serializable
*/
System.out.println("--------------------");
// 4. 所继承的父类
Class superclass = clazz.getSuperclass();
System.out.println(superclass);// class java.lang.Object
System.out.println("--------------------");
// 5. 运行时注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
/*
@com.qs.javareflect.annotation.MyAnnotation(count=0, key=, values=[张三, 李四])
*/
}
4.2 java.lang.reflect.Constructor
构造器
Constructor<?>[] getConstructors()
:获取当前类的所有 public 构造器。Constructor<?>[] getDeclaredConstructors()
:获取当前类的所有构造器。Constructor<T> getConstructor(Class<?>... parameterTypes)
:获取当前类的指定 public 构造器Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
:获取当前类的指定构造器。
/**
* 4.2 java.lang.reflect.Constructor 构造器
*/
@Test
public void test2() throws ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = Class.forName("com.qs.javareflect.clazz.Qs");
// 1. 获取当前类的所有 public 构造器
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
/*
public com.qs.javareflect.clazz.Qs(int,java.lang.String,int)
public com.qs.javareflect.clazz.Qs(int,java.lang.String,int,java.lang.String)
public com.qs.javareflect.clazz.Qs(java.lang.String)
*/
System.out.println("--------------------");
// 2. 获取当前类的所有构造器
Constructor[] constructors2 = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
/*
public com.qs.javareflect.clazz.Qs(int,java.lang.String,int)
public com.qs.javareflect.clazz.Qs(int,java.lang.String,int,java.lang.String)
public com.qs.javareflect.clazz.Qs(java.lang.String)
private com.qs.javareflect.clazz.Qs()
*/
System.out.println("--------------------");
// 3. 获取当前类的指定构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class, String.class, int.class, String.class);
System.out.println(declaredConstructor);
/*
public com.qs.javareflect.clazz.Qs(int,java.lang.String,int,java.lang.String)
*/
}
4.3 java.lang.reflect.Method
方法
Method[] getMethods()
:获取当前类及其父类的所有 public 方法。Method[] getDeclaredMethods()
:获取当前类所有方法。Method getMethod(String name, Class<?>... parameterTypes)
:获取当前类及其父类的指定 public 方法(可能存在重载,所以要添加参数类型)。Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:获取当前类的指定方法。
/**
* 4.3 java.lang.reflect.Method 方法
*/
@Test
public void test3() throws ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = Class.forName("com.qs.javareflect.clazz.User");
// 1. 获取当前类及其父类的所有 public 方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("--------------------");
// 2. 获取当前类所有方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("--------------------");
// 3. 获取当前类及其父类的指定方法(可能存在重载,所以要添加参数类型)
@SuppressWarnings("all")
Method getName = clazz.getMethod("getName", null);
System.out.println(getName);// public java.lang.String com.qs.javareflect.clazz.User.getName()
Method setName = clazz.getMethod("setName", String.class);
System.out.println(setName);// public void com.qs.javareflect.clazz.User.setName(java.lang.String)
// 获取父类的指定方法
@SuppressWarnings("all")
Method wait = clazz.getMethod("wait", null);
System.out.println(wait);// public final void java.lang.Object.wait() throws java.lang.InterruptedException
}
4.4 java.lang.reflect.Field
属性
Field[] getFields()
:获取当前类及其父类的所有 public 属性。Field[] getDeclaredFields()
:获取当前类的所有属性。Field getField(String name)
:获取当前类及其父类的指定 public 属性。Field getDeclaredField(String name)
:获取当前类的指定属性。
/**
* 4.4 java.lang.reflect.Field 属性
*/
@Test
public void test4() throws ClassNotFoundException, NoSuchFieldException {
Class clazz = Class.forName("com.qs.javareflect.clazz.User");
// 1. 获取当前类及其父类的所有 public 属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
// public int com.qs.javareflect.clazz.User.age
System.out.println("--------------------");
// 2. 获取当前类的所有属性
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
/*
int com.qs.javareflect.clazz.User.count
private java.lang.String com.qs.javareflect.clazz.User.name
public int com.qs.javareflect.clazz.User.age
*/
System.out.println("--------------------");
// 3. 获取当前类的指定属性
Field name = clazz.getDeclaredField("name");
System.out.println(name);// private java.lang.String com.qs.javareflect.clazz.User.name
}
5. 创建运行时类的对象
5.1 反射创建对象
T newInstance()
无参构造。
- 必须有一个无参数的构造器。
- 无参的构造器需要有足够的访问权限。
Constructor<T> getConstructor(Class<?>... parameterTypes)
有参无参公有构造。
- parameterTypes:参数类型。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
有参无参公有私有构造。
setAccessible(boolean flag)
操作私有构造(private),需要设置为可访问。
- Method、Field、Constructor 对象都有 setAccessible 方法。
- setAccessible 方法的作用是启动和禁用访问安全检查的开关。
- 参数值为 true,指示反射的对象在使用时应该取消 Java 语言访问检查。
- 提高反射的效率(如果代码中使用反射,而该代码需要频繁的被调用,那么请设置为 true)。
- 使原本无法访问的私有成员也可以访问。
/**
* 5.1 反射创建对象 Constructor
*/
@Test
public void test() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class clazz = Class.forName("com.qs.javareflect.clazz.Programmer");
// 1. 无参构造器
Programmer programmer = (Programmer) clazz.newInstance();
System.out.println(programmer);// Programmer(language=null)
System.out.println("--------------------");
// 2. 指定形参类型的构造器(公有构造)
Constructor constructor = clazz.getConstructor(String.class);
Programmer programmer2 = (Programmer) constructor.newInstance("Python");
System.out.println(programmer2);// Programmer(name=null, language=Python)
System.out.println("--------------------");
// 3. 指定形参类型的构造器(公有私有构造)
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class, String.class);
// 不能直接操作私有构造,需要手动关闭程序的安全监测(设置为可访问)
declaredConstructor.setAccessible(true);
Programmer programmer3 = (Programmer) declaredConstructor.newInstance("qs", "Java");
System.out.println(programmer3);// Programmer(name=qs, language=Java)
}
5.2 反射调用方法
Method getMethod(String name, Class<?>... parameterTypes)
获取公有方法
- parameterTypes:参数类型。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取公有私有方法
setAccessible(boolean flag)
操作私有方法(private),需要设置为可访问。
Object invoke(Object obj, Object... args)
:
- Object:方法返回值(无返回值为null)。
- obj:对象。
- args:方法参数列表(无参为null)。
/**
* 5.2 反射调用方法 Method
*/
@Test
public void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class clazz = Class.forName("com.qs.javareflect.clazz.Programmer");
// 创建一个对象
Programmer programmer = (Programmer) clazz.newInstance();
// 通过反射获取一个方法
Method setName = clazz.getDeclaredMethod("setName", String.class);
// 调用方法
setName.invoke(programmer, "骑士");
System.out.println(programmer.getName());// 骑士
}
5.3 反射操作属性 Field
/**
* 5.3 反射操作属性 Field
*/
@Test
public void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class clazz = Class.forName("com.qs.javareflect.clazz.Programmer");
// 创建一个对象
Programmer programmer = (Programmer) clazz.newInstance();
// 通过反射获取一个属性
Field language = clazz.getDeclaredField("language");
System.out.println(language.getType());// class java.lang.String
System.out.println(language.getName());// language
// 不能直接操作私有属性,需要手动关闭程序的安全监测(设置为可访问)
language.setAccessible(true);
language.set(programmer, "Python");
System.out.println(programmer.getLanguage());// Python
}
四、反射性能测试
/**
* @author: wy
* describe: 四、反射性能测试
*/
public class ClassMain4 {
/**
* 1. 普通方式调用
*/
@Test
public void test() {
User user = new User();
Instant start = Instant.now();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.printf("普通方式,执行十亿次: %s毫秒", duration.toMillis());
// 普通方式,执行十亿次: 2744毫秒
}
/**
* 2. 反射方式调用
*/
@Test
public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<? extends User> clazz = user.getClass();
Method getName = clazz.getMethod("getName", null);
getName.invoke(user, null);
Instant start = Instant.now();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.printf("反射方式,执行十亿次: %s毫秒", duration.toMillis());
// 反射方式,执行十亿次: 19毫秒
}
/**
* 3. 反射方式调用(关闭安全监测)
*/
@Test
public void test3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
User user = new User();
Class<? extends User> clazz = user.getClass();
Method getName = clazz.getMethod("getName", null);
getName.setAccessible(true);
getName.invoke(user, null);
Instant start = Instant.now();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.printf("反射方式(关闭安全监测),执行十亿次: %s毫秒", duration.toMillis());
// 反射方式(关闭安全监测),执行十亿次: 10毫秒
}
}
五、反射获取泛型信息
- Java 采用泛型擦除机制来引入泛型,Java 中的泛型仅仅是给编译器
javac
使用的,确保数据的安全性和免去强制类型转换问题。但是,一旦编译完成,所有和泛型有关的类型全部被擦除。- 为了通过反射操作这些类型, Java 新增了下面几种类型来代表不能被归到 Class 类中的类型,但是又和原始类型齐名的类型。
- ParameterizedType:表示一种参数化类型(比如:
Collection
)。- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。
- TypeVariable:是各种类型变量的公共父接口。
- WildcardType:表示一种通配符类型表达式。
/**
* 通过反射获取泛型信息
*/
public static void main(String[] args) throws NoSuchMethodException {
Method method = ClassMain5.class.getMethod("test", Map.class, List.class);
// 1. 获取泛型参数类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("-------------------- " + genericParameterType);
if (genericParameterType instanceof ParameterizedType) {
// 转为结构化参数类型
ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
// 获取实际参数类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
/*
-------------------- java.util.Map<java.lang.String, com.qs.javareflect.clazz.User>
class java.lang.String
class com.qs.javareflect.clazz.User
-------------------- java.util.List<com.qs.javareflect.clazz.User>
class com.qs.javareflect.clazz.User
*/
System.out.println("====================");
// 2. 获取泛型返回值类型
Type genericReturnType = method.getGenericReturnType();
System.out.println("-------------------- " + genericReturnType);
if (genericReturnType instanceof ParameterizedType) {
// 转为结构化参数类型
ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
// 获取实际参数类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
/*
-------------------- java.util.Map<java.lang.String, com.qs.javareflect.clazz.User>
class java.lang.String
class com.qs.javareflect.clazz.User
*/
}
六、反射获取注解信息
getAnnotations()
getAnnotation()
/**
* @author: wy
* describe: 六、反射获取注解信息
*/
public class ClassMain6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> clazz = Class.forName("com.qs.javareflect.clazz.User");
// 1. 获取类上注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// @com.qs.javareflect.annotation.MyAnnotation(count=0, value=qs, key=, values=[张三, 李四])
System.out.println("--------------------");
// 2. 获取注解的 value 值
MyAnnotation myAnnotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.value());// qs
System.out.println("--------------------");
// 3. 获取属性上注解
Field field = clazz.getDeclaredField("name");
MyAnnotation myAnnotation2 = field.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation2.value());// qshome
}
}