目录
什么是反射?比如jdbc连接数据库用的Class.forName("全路径")就是反射,Java反射机制非常的强大,可以访问已经被装载进JVM中的java对象的描述,实现访问、检测和修改描述java对象本身信息的功能。
首先先知道怎么获取一个class对象,自己创建一个stu类,可以什么都不写,有这个类就可以:
第一种:Class<Stu> clazz = Stu.class;
第二种:Class<?> aClass = Class.forName("Stu");//完整类名带包名,用小数点分割
第三种:Stu stu1 = new Stu(); stu1.getClass();那么我们可以通过class对象得到些什么信息呢?
package com.lx;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @title: com.lx.Reflection
* @Author 尤词
* @Date: 2022/8/15 13:26
* @Description: 反射
* @Version 1.0
*/
public class Reflection {
public String name;
public String address;
private String sex;
private String tel;
public class nbl{
nbl(){
System.out.println("内部类");
}
}
private class nbl2{}
public Reflection() {
System.out.println("构造方法");
}
public Reflection(String s){
System.out.println(s);
}
private Reflection(Integer i){
System.out.println(i);
}
public void aaa(){
System.out.println("aaa");
}
private void bbb(){
System.out.println("bbb");
}
public static void main(String[] args) {
Class<Reflection> c = Reflection.class;//得到一个class对象
//包路径
Package cp = c.getPackage();
System.out.println("该类的存放路径为:" + cp);
//类名称
String cn = c.getName();
System.out.println("该类的名称为:" + cn);
//继承类
Class<? super Reflection> cs = c.getSuperclass();
System.out.println("该类继承的类为:" + cs);
//实现接口
Class<?>[] ci = c.getInterfaces();
System.out.println("该类实现的所有的接口为:" + Arrays.toString(ci));
//构造方法
Constructor<?>[] cc1 = c.getConstructors();
System.out.println("该类中所有范围修饰符为public的构造方法有:" + Arrays.toString(cc1));
try {
Constructor<Reflection> cc2 = c.getConstructor(String.class);
System.out.println("该类中需要传入一个字符串类型的public的构造方法为:" + cc2);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Constructor<?>[] cd1 = c.getDeclaredConstructors();
System.out.println("该类所有的构造方法按声明顺序依次为:" + Arrays.toString(cd1));
try {
Constructor<Reflection> cd2 = c.getDeclaredConstructor(Integer.class);
System.out.println("需要传入整数类型的构造方法有:" + cd2);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
//方法
Method[] methods = c.getMethods();
System.out.println("修饰符为public的所有方法为:" + Arrays.toString(methods));
try {
Method method = c.getMethod("aaa");
System.out.println("修饰符为public方法名为aaa参数列表为空的方法:" + method);//这个只找修饰符是public的方法
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Method[] declaredMethods = c.getDeclaredMethods();
System.out.println("该类所有的方法按声明顺序为:" + Arrays.toString(declaredMethods));
try {
Method bbb = c.getDeclaredMethod("bbb");
System.out.println("方法名为bbb参数列表为空的方法为:" + bbb);//这个就是无论范围修饰符是什么都找
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
//成员变量
Field[] fields = c.getFields();
System.out.println("获取所有被public修饰的成员变量" + Arrays.toString(fields));
try {
Field n = c.getField("name");
System.out.println("\"权限为public属性名为name的成员变量为:\" = " + n);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
Field[] declaredFields = c.getDeclaredFields();
System.out.println("所有的成员变量为:" + Arrays.toString(declaredFields));
try {
Field s = c.getDeclaredField("sex");
System.out.println("属性名为sex的成员变量为:" + s);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
//内部类
Class<?>[] cls = c.getClasses();
System.out.println("公共的内部类有:" + Arrays.toString(cls));
Class<?>[] dcls = c.getDeclaredClasses();
System.out.println("所有的内部类有:" + Arrays.toString(dcls));
//内部类的声明类
Class<?> declaringClass = c.getDeclaringClass();
System.out.println("这个类如果是内部类,输出它的成员类,否则返回null:" + declaringClass);
}
}
根据上面的练习,可以总结出:
不加Declared的方法获取的都是public修饰的方法/变量等,加了Declared的方法获取的是所有方法/变量等
1.访问构造方法
isVarArgs() 查看该构造方法是否允许带有可变数量的参数,返回Boolean
getParameterTypes() 按声明顺序以Class数组的形式获得该构造方法的各个参数的类型
getExceptionTypes() 以Class数组的形式获得构造方法可能抛出的异常类型
newInstance() 根据参数去类中找对应的构造方法
setAccessible(Boolean) 设置是否允许private修饰的构造方法通过反射创建对象,默认false
getModifiers() 该构造方法采用修饰符的数量
通过getModifiers()方法得到的结果可以做一下判断
语法:Modifier.方法名(getModifiers()返回的int类型数据),还有一个toString方法在例子中演示
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* @title: Cons
* @Author 尤词
* @Date: 2022/8/16 19:26
* @Description: 构造方法能干的事情
* @Version 1.0
*/
class Example_01 {
String s;
int i, j, k;
public Example_01() {
}
public Example_01(String s, int i) {
this.s = s;
this.i = i;
}
public Example_01(String... s) throws NumberFormatException {
if (0 < s.length) {
i = Integer.parseInt(s[0]);
}
if (1 < s.length) {
j = Integer.parseInt(s[1]);
}
if (2 < s.length) {
k = Integer.parseInt(s[2]);
}
}
@Override
public String toString() {
return "s='" + s + '\'' +
", i=" + i +
", j=" + j +
", k=" + k;
}
}
public class Cons {
public static void main(String[] args) {
Class<Example_01> c = Example_01.class;
/*获得所有构造方法*/
Constructor<?>[] dc = c.getDeclaredConstructors();
/*遍历构造方法*/
for (int i = 0; i < dc.length; i++) {
int modifiers = dc[i].getModifiers();
System.out.println("该构造方法采用修饰符的代号:" + modifiers);
String s = Modifier.toString(modifiers);
System.out.println("所有的修饰符号为:" + s);
System.out.println("是否允许带有可变数量的参数:" + dc[i].isVarArgs());
System.out.print("该构造方法的入口参数类型依次为:");
/*获取所有参数类型*/
Class<?>[] parameterTypes = dc[i].getParameterTypes();
System.out.println(Arrays.toString(parameterTypes));
System.out.print("可能抛出的异常类型为:");
/*获取所有可能抛出异常信息类型*/
Class<?>[] exceptionTypes = dc[i].getExceptionTypes();
System.out.println(Arrays.toString(exceptionTypes));
Example_01 ex = null;
while (ex == null) {
try {
/*如果该成员变量的访问权限为private,则抛出异常,即不允许访问*/
if (i == 2) ex = (Example_01) dc[i].newInstance();//通过执行默认没有参数的构造方法创建对象,等于new Example_01();
/*通过执行具有两个参数的构造方法创建对象*/
else if (i == 1) ex = (Example_01) dc[i].newInstance("7", 5);
else {
Object[] parameters = new Object[]{new String[]{"100", "200", "300"}};
/*通过执行具有可变数量参数的构造方法创建对象*/
ex = (Example_01) dc[i].newInstance(parameters);
}
} catch (Exception e) {
System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法");
/*设置允许访问*/
dc[i].setAccessible(true);
}
}
System.out.println(ex);
System.out.println();
}
}
}
2.访问成员变量
setInt()/setFloat()/setBoolean()都有getter方法,作用为获取值
import java.lang.reflect.Field;
/**
* @title: Fields
* @Author 尤词
* @Date: 2022/8/16 20:17
* @Description: 获取成员变量的信息
* @Version 1.0
*/
class Example_02 {
int i;
public float f;
protected boolean b;
private String s;
}
public class Fields {
public static void main(String[] args) {
Example_02 ex = new Example_02();
Class<? extends Example_02> c = ex.getClass();
/*获得所有成员变量*/
Field[] d = c.getDeclaredFields();
for (int i = 0; i < d.length; i++) {
/*遍历成员变量*/
Field ff = d[i];
/*获得成员变量的名字*/
System.out.println("变量名 = " + ff.getName());
/*获取成员变量类型*/
Class<?> type = ff.getType();
System.out.println("类型 = " + type);
boolean it = true;
while (it) {
/*如果该成员变量的访问权限为private,则抛出异常,即不允许访问*/
try {
it = false;
System.out.println("变量修改前的值为: " + ff.get(ex));
/*判断变量类型*/
if (type.equals(int.class)){
System.out.println("利用setInt()修改变量的值");
ff.setInt(ex, 168);
} else if (type.equals(float.class)) {
System.out.println("利用setFloat()修改变量的值");
ff.setFloat(ex, 16.8f);
}else if (type.equals(boolean.class)){
System.out.println("利用setBoolean()修改变量的值");
ff.setBoolean(ex, true);
}else {
System.out.println("利用set()修改变量的值");
/*可以为各种类型的成员变量赋值*/
ff.set(ex, "mmm");
}
/*获得成员变量值*/
System.out.println("修改后的值为 " + ff.get(ex));
} catch (IllegalAccessException e) {
System.out.println("在设置成员变量值时抛出异常,下面执行setAccessible()方法");
ff.setAccessible(true);//设置允许访问
it = true;
}
}
System.out.println();
}
}
}
3.访问方法
getName():获得方法的名称
getParameterTypes():获得方法的参数列表
getReturnType():获得方法的返回值类型
getExceptionTypes():获得方法抛出的异常
invoke(类对象,方法的参数(如果有)):根据参数列表执行指定类对象中的方法
isVarArgs():查看方法是否允许传入可变数量的参数
getModifiers():该构造方法采用修饰符的数量
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @title: Method
* @Author 尤词
* @Date: 2022/8/18 20:09
* @Description: 对方法的一些操作
* @Version 1.0
*/
class Example {
static void staticMethod() {
System.out.println("执行静态方法");
}
public int publicMethod(int i) {
System.out.println("执行公共有参方法");
return i * 100;
}
protected int protectedMethod(String s, int i) throws NumberFormatException {
System.out.println("执行protected修饰的两个参数方法");
return Integer.parseInt(s + i);
}
private String privateMethod(String... s) {
System.out.println("执行私有方法");
StringBuffer sb = new StringBuffer();
for (String ss : s) {
sb.append(ss);
}
return Arrays.toString(s);
}
}
public class MethodTest {
public static void main(String[] args) {
Example e = new Example();
Class<? extends Example> c = e.getClass();
/*获得所有方法*/
java.lang.reflect.Method[] declaredMethods = c.getDeclaredMethods();
/*遍历方法*/
for (int i = 0; i < declaredMethods.length; i++) {
/*得到每一个方法*/
Method m = declaredMethods[i];
/*获取方法的名称*/
System.out.println("方法名为: " + m.getName());
/*判断是否可以传入可变数量参数*/
System.out.println("是否允许传入可变数量参数" + m.isVarArgs());
/*获取参数列表数据类型*/
Class<?>[] parameterTypes = m.getParameterTypes();
/*遍历参数列表*/
System.out.println("参数列表类型为:");
for (int j = 0; j < parameterTypes.length; j++) {
System.out.print("\t" + parameterTypes[j]);
}
/*获得方法返回值类型*/
Class<?> returnType = m.getReturnType();
System.out.println("返回值类型为:" + returnType);
/*抛出的异常*/
Class<?>[] exceptionTypes = m.getExceptionTypes();
System.out.println("可能抛出的异常有:" + Arrays.toString(exceptionTypes));
boolean it = true;
while (it) {
/*如果该方法访问权限为private则抛出异常,即不允许访问*/
try {
it = false;
if ("staticMethod".equals(m.getName()))
m.invoke(e);
else if ("publicMethod".equals(m.getName())) m.invoke(e, 168);
else if ("protectedMethod".equals(m.getName())) m.invoke(e, "7", 5);
else if ("privateMethod".equals(m.getName())) {
Object[] objects = {new String[]{"Q", "W", "E"}};
System.out.println("返回值为:" + m.invoke(e,objects));
}
} catch (Exception ex) {
System.out.println("在执行方法时抛出异常,下面执行setAccessible()方法");
/*设置允许访问*/
m.setAccessible(true);
it = true;
}
}
System.out.println();
}
}
}
Annotation(注解)
JDK1.5开始增加了Annotation功能,该功能可用于类,构造方法、成员变量、方法、参数等的声明中。该功能并不影响程序的运行,但是会对编译器警告等辅助工具产生影响。
1.定义Annotation
在定义Annotation类型时,也需要用到来定义接口的interface关键字,但需要在关键字前加"@"符号,即定义Annotation类型的关键字为@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。
package annotation;
/**
* @title: A1
* @Author 尤词
* @Date: 2022/8/18 21:10
* @Description: 如何定义一个annotation类型
* @Version 1.0
*/
public @interface A1 {
}
上面定义的annotation类型@A1未包含任何成员,这样的annotation类型被称为marker annotation(标记注解)。
public @interface A1 {
/**
* String:成员类型,可用的成员类型为:String Class primitive enumerated和annotation
* value:成员名称,如果在所定义的Annotation类型中只有一个成员,通常将成员命名为value
*
*/
String value();
}
package annotation;
/**
* @title: A2
* @Author 尤词
* @Date: 2022/8/18 21:16
* @Description: 包含多成员的annotation
* @Version 1.0
*/
public @interface A2 {
/*定义成员的时候,也可以给出默认值*/
String describe() default "默认值";
Class<?> type() default void.class;
}
在定义Annotation类型时,还可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类。如果未设置@Target,则表示适用于所有程序元素。枚举类ElementType中的枚举常量用来设置@Target。
枚举常量 | 说明 |
---|---|
ANNOTATION_TYPE | 表示用于Annotation类型 |
TYPE | 表示用于类、接口和枚举以及Annotation类型 |
CONSTRUCTOR | 表示用于构造方法 |
FIELD | 表示用于成员变量和枚举变量 |
METHOD | 表示用于方法 |
PARAMETER | 表示用于参数 |
LOCAL_VARIABLE | 表示用于局部变量 |
PACKAGE | 表示用于包 |
通过Annotation类型@Retention可以设置Annotation的有效范围。枚举类RetentionPolicy中的枚举常量用来设置@Retention,如果没有设置,Annotation的有效范围为枚举常量CLASS表示的范围
枚举常量 | 说明 |
---|---|
SOURCE | 表示不hi编译Annotation到类文件中,有效范围最小 |
CLASS | 表示编译Annotation到类文件中,但是在运行时不加载Annotation到JVN中 |
RUNTIME | 表示在运行时加载Annotation到JVM中,有效范围最大 |
首先定义一个用来注释构造方法的Annotation类型@Constructor_Annotation,有效范围为在运行时加载Annotation到JVM中
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @title: A3
* @Author 尤词
* @Date: 2022/8/18 21:39
* @Version 1.0
*/
@Target(ElementType.CONSTRUCTOR)
/*用于构造方法*/
@Retention(value = RetentionPolicy.RUNTIME)
//在运行时加载到JVM中
public @interface A3 {
String value() default "默认构造方法";
}
再定义一个用来注释字段、方法、和参数的Annotation类型,有效范围为在运行时加载到JVM
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @title: A4
* @Author 尤词
* @Date: 2022/8/18 21:42
* @Version 1.0
*/
/*用于字段、方法、参数*/
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface A4 {
String describe();
/*定义一个具有默认值的Class型成员*/
Class<?> type() default void.class;
}
最后编写一个实体类,运用前面定义的两个注解
package annotation;
/**
* @title: Tests
* @Author 尤词
* @Date: 2022/8/18 21:46
* @Description: 原始的实体类
* @Version 1.0
*/
public class Tests {
int id;
String name;
public Tests() {
}
public Tests(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
下面时加了注释的类,a3用于构造方法,a4用于其它(起名要规范,不要学我!!!)
package annotation;
/**
* @title: Tests
* @Author 尤词
* @Date: 2022/8/18 21:46
* @Description: 测试类
* @Version 2.0
*/
public class Tests {
/*
* 对字段的注释
*/
@A4(describe = "编号",type = int.class)
int id;
@A4(describe = "姓名",type = String.class)
String name;
/*
* 对构造方法注解
* 采用默认值注释构造方法
*/
@A3()
public Tests() {
}
@A3("有参构造方法")
public Tests(@A4(describe = "编号",type = int.class)int id, @A4(describe = "姓名",type = String.class)String name) {
this.id = id;
this.name = name;
}
/*
* 注释方法
*/
@A4(describe = "获得编号",type = int.class)
public int getId() {
return id;
}
@A4(describe = "设置编号",type = int.class)
public void setId(int id) {
this.id = id;
}
@A4(describe = "获得名字",type = String.class)
public String getName() {
return name;
}
@A4(describe = "设置名字",type = String.class)
public void setName(String name) {
this.name = name;
}
}
2.访问Annotation信息
如果在定义Annotation类型时将@Retention设置为RetentionPolicy.RUNTIME,那么在运行程序时通过反射就可以获得相关的Annotation信息,如获取构造方法、字段和方法的Annotation信息。
类Constructor、Field和Method均继承了AccessibleObject类,在AccessibleObject中定义了三个关于Annotation的方法,其中isAnnotationPresident(Class<? extends Annotation> annotationClass)用来查看是否添加了指定类型的Annotation,如果是则返回true,否则返回false;方法getAnnotation(Class<T> annotationClass)用来获得指定类型的Annotation,如果存在则返回相应的对象,否则返回null;方法getAnnotation()用来获得所有的Annotation,该方法返回一个Annotation数组。
在类Constructor和Method中还定义了方法getParameterAnnotation(),用来获得为所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明的顺序相同,如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。
package annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @title: ATest
* @Author 尤词
* @Date: 2022/8/18 22:07
* @Description: 对上述类操作
* @Version 1.0
*/
public class ATest {
public static void main(String[] args) {
System.out.println("----构造方法的描述如下----");
Tests t = new Tests();
Class<? extends Tests> c = t.getClass();
/*获得所有构造方法*/
Constructor<?>[] d = c.getDeclaredConstructors();
/*遍历构造方法*/
for (int i = 0; i < d.length; i++) {
Constructor<?> constructor = d[i];
/*查看是否具有指定类型的注释*/
if (constructor.isAnnotationPresent(A3.class)) {
//获得指定类型的注释
A3 ca = constructor.getAnnotation(A3.class);
System.out.println(ca.value());
}
//获得参数的注释
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
for (int j = 0; j < parameterAnnotations.length; j++) {
/*获得指定参数注释的长度*/
int length = parameterAnnotations[j].length;
if (length == 0) {
System.out.println("未添加Annotation参数");
} else {
for (int k = 0; k < length; k++) {
//获得参数的注释
A4 pa = (A4) parameterAnnotations[j][k];
System.out.print(" " + pa.describe());
System.out.print(" " + pa.type());
}
}
System.out.println();
}
}
System.out.println("----字段描述如下----");
Field[] ff = c.getDeclaredFields();
for (Field f : ff) {
//查看是否具有指定类型注释
if (f.isAnnotationPresent(A4.class)) {
//获得指定类型注释
A4 fa = f.getAnnotation(A4.class);
System.out.print(" " + fa.describe());
System.out.print(" " + fa.type());
}
}
System.out.println("\n----方法描述如下----");
Method[] mm = c.getDeclaredMethods();
for (Method m : mm) {
//查看是否具有指定类型注释
if (m.isAnnotationPresent(A4.class)) {
//获得指定类型的注释
A4 an = m.getAnnotation(A4.class);
System.out.print(" " + an.describe());
System.out.println(" " + an.type());
}
//获得参数注释
Annotation[][] parameters = m.getParameterAnnotations();
for (int l = 0; l < parameters.length; l++) {
int length = parameters[l].length;
if (length == 0) {
System.out.println("未添加Annotation参数");
} else {
for (int z = 0; z < length; z++) {
//获取指定类型的注释
A4 pa = (A4) parameters[l][z];
System.out.print(" " + pa.describe());
System.out.println(" " + pa.type());
}
}
}
}
}
}