该文章Github地址:https://github.com/AntonyCheng/java-notes
在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn.net/AntonyCheng/article/details/136555245),该模板集成了最常见的开发组件,同时基于修改配置文件实现组件的装载,除了这些,模板中还有非常丰富的整合示例,同时单体架构也非常适合SpringBoot框架入门,如果觉得有意义或者有帮助,欢迎Star & Issues & PR!
上一章:由浅到深认识Java语言(41):反射
47.反射
反射的应用
获取类型的详细信息(了解)
可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的);
-
获取包信息:
Package pkg = clazz.getPackage();
-
获取修饰符:
int mod = clazz.getModifiers();
修饰符定义在Modifier类中,该类里面有很多常量值,每一个常量对应一种修饰符;
-
获取类名:
String name = clazz.getName();
-
获取父类的字节码对象:
Class superclass = clazz.getSuperclass();
-
获取该类实现的所有接口:
Class[] interfaces = clazz.getInterfaces();
-
获取该类的所有属性:
Field[] declaredFields = clazz.getDeclaredFields();
-
获取该类的所有构造函数:
Method[] declaredMethods = clazz.getDeclaredMethods();
-
获取该类的所有方法:
Method[] declaredMethods = clazz.getDeclaredMethods();
-
获取该类的所有构造方法:
Constructor[] constructors = clazz.getConstructors();
示例如下:
package top.sharehome.Test;import java.lang.reflect.Constructor;public class Demo {public static void main(String[] args) throws Exception {Class clazz = Class.forName("top.sharehome.Test.Test");System.out.println("clazz3 = " + clazz);System.out.println("clazz.getPackage() = " + clazz.getPackage());System.out.println("clazz.getModifiers() = " + clazz.getModifiers());System.out.println("clazz.getName() = " + clazz.getName());System.out.println("clazz.getSuperclass() = " + clazz.getSuperclass());System.out.println("clazz.getInterfaces() = " + clazz.getInterfaces());System.out.println("clazz.getDeclaredField() = " + clazz.getDeclaredFields());System.out.println("clazz.getDeclaredMethods() = " + clazz.getDeclaredMethods());System.out.println("clazz.getConstructors() = " + clazz.getConstructors());}
}interface Father{void Hello();
}class Test implements Father{private String name = "xiaochen";public Test() {}public Test(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic void Hello() {System.out.println("hello");}
}
打印效果如下:
创建任意引用类型的对象(重点)
相关方法
Class类型对象.newInstance()
;(JDK 9 后被弃用)
Class类型对象.getDeclaredConstructor().newInstance()
;(newInstance() 替代品)
这里主要学习第二个方法;
Class类型对象.getDeclaredConstructor(parameterTypes···).newInstance(initargs···);
-
getDeclaredConstructor(parameterTypes···) 是获取一个 Class 类型自带的构造方法,参数就是类中构造方法参数.class;
示例如下:
//构造方法如下 public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender; } //获得类的构造方法如下 Class clazz = Test.class; Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class, char.class);
-
newInstance(initargs···)
是在获得了构造器的基础上将 Class 类型对象转换为 Object 对象,这里要注意该方法的参数需要和 claredConstructor(parameterTypes···) 里的参数相匹配;示例如下:
//构造方法如下 public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender; } //引入对象如下: Class clazz1 = Test.class; Object o1 = clazz1.getDeclaredConstructor(String.class,int.class,char.class).newInstance("xiaowu",21,'男');
整体示例如下:
package top.sharehome.Test;public class Demo {public static void main(String[] args) throws Exception {Class clazz1 = Test.class;Object o1 = clazz1.getDeclaredConstructor(String.class,int.class,char.class).newInstance("xiaowu",21,'男');Test t1 = (Test) o1;System.out.println("t1.getName() = " + t1.getName());}
}class Test{private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}
}
打印效果如下:
任意类型框架设计
框架类:
package top.sharehome.Test;public class CreateBean {public static Object CreateBean(Class clazz) throws Exception {return clazz.getDeclaredConstructor().newInstance();}
}
使用类:
package top.sharehome.Test;public class Demo {public static void main(String[] args) throws Exception {Test t = (Test) CreateBean.CreateBean(Test.class);System.out.println(t);}
}class Test{private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}@Overridepublic String toString() {return "Test{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';}
}
打印效果如下:
操作任意类型的属性(重点)
-
获取该类型的Class对象
Class clazz = Class.forName(类型全名称);
-
获取属性对象
Field field = clazz.getDeclaredField(属性变量名);
-
设置属性可访问
field.setAccessible(true);
-
创建实例对象:如果操作的是非静态属性,需要创建实例对象
Object obj = clazz.getDeclaredConstructor().newInstance();
-
设置属性值
field.set(obj,"chai");
-
获取属性值
Object value = field.get(obj);
操作如下:
-
利用获取一个类中的所有的变量属性名和值;
package top.sharehome.Test;import java.lang.reflect.Field;public class Demo {public static void main(String[] args) throws Exception {Class clazz = Test.class;Field[] declaredFields = clazz.getDeclaredFields();Test t = (Test) clazz.getDeclaredConstructor().newInstance();for (Field f : declaredFields) {//因为对象中有私有属性,所以要设置属性的可见性//这种设置可见性的做法也叫做暴力反射f.setAccessible(true);//获取属性名String name = f.getName();//获取属性值Object o = f.get(t);System.out.println("name:field = " + name + ":" + o);}} }class Test {private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}@Overridepublic String toString() {return "Test{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';} }
打印效果如下:
-
获取一个类中的某一个变量属性名和值;
package top.sharehome.Test;import java.lang.reflect.Field;public class Demo {public static void main(String[] args) throws Exception {Class clazz = Test.class;Test t = (Test) clazz.getDeclaredConstructor().newInstance();Field FName = clazz.getDeclaredField("name");FName.setAccessible(true);String name = FName.getName();Object o = FName.get(t);System.out.println("name:field ="+name+":"+o);} }class Test {private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}@Overridepublic String toString() {return "Test{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';} }
打印效果如下:
-
修改一个类中的某一属性值;
package top.sharehome.Test;import java.lang.reflect.Field;public class Demo {public static void main(String[] args) throws Exception {Class clazz = Test.class;Test t = (Test) clazz.getDeclaredConstructor().newInstance();Field FName = clazz.getDeclaredField("name");FName.setAccessible(true);String name = FName.getName();Object o = FName.get(t);System.out.println("name:field ="+name+":"+o);System.out.println("修改后:");FName.set(t,"xiaowu");Object o1 = FName.get(t);System.out.println("name:field ="+name+":"+o1);} }class Test {private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}@Overridepublic String toString() {return "Test{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';} }
打印效果如下:
操作任意类型的方法(重点)
(1)获取该类型的Class对象
Class clazz = Class.forName(类型全名称);
(2)获取方法对象
Method method = clazz.getDeclaredMethod("login",String.class,String.class);
(3)创建实例对象
Object obj = clazz.getDeclaredConstructor().newInstance();
(4)调用方法
Object result = method.invoke(obj,"chai","123");
如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true);
如果方法是静态方法,实例对象也可以省略,用null代替;
如果方法是无参的,那么只需要在 clazz.getDeclaredMethod() 方法中只传入方法名,method.invoke() 方法中传入引入对象名即可;
操作如下:
调用方法示例;
package top.sharehome.Test;import java.lang.reflect.Method;public class Demo {public static void main(String[] args) throws Exception {Class clazz = Test.class;Object o = clazz.getDeclaredConstructor().newInstance();Method method = clazz.getDeclaredMethod("Method", String.class, int.class, char.class);//因为方法是私有的method.setAccessible(true);Object invoke = method.invoke(o, "xiaowu", 21, '男');System.out.println("return = " + invoke);}
}class Test {private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}@Overridepublic String toString() {return "Test{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';}private String Method(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;System.out.println("这是一个返回 Test 类中具体值的方法");return "" + this.name + " " + this.age + " " + this.gender;}
}
打印效果如下:
Type接口的介绍和获取(了解)
java.lang.reflect.Type
接口及其相关接口用于描述 java 中用到的所有类型,是 Java 的反射中很重要的组成部分。Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型,站在反射的角度上看,Java 生态所有的类都由 Type 接口支撑;
使用反射获取Type
有很多场景下我们可以获得Type,比如:
-
当我们拿到一个Class,用
Class.getGenericInterfaces()
方法得到Type[],也就是这个类实现接口的Type类型列表,由于 Class 类实现 Type 接口,是父子关系,所以该方法与clazz.getInterfaces()
方法获得的内容一样;package top.sharehome.Test;import java.lang.reflect.Type;public class Demo {public static void main(String[] args) {Class clazz = Test.class;Type[] genericInterfaces = clazz.getGenericInterfaces();Class[] interfaces = clazz.getInterfaces();System.out.println("Type[] 遍历如下:");for (Type t:genericInterfaces) {System.out.println(t);}System.out.println("Class[] 遍历如下:");for (Class c:interfaces) {System.out.println(c);}} }interface Father1{}interface Father2{}class Father3 {}class Test extends Father3 implements Father1,Father2 {private String name = "xiaochen";private int age = 21;private char gender = '女';public Test() {}public Test(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}@Overridepublic String toString() {return "Test{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';}private String Method(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;System.out.println("这是一个返回 Test 类中具体值的方法");return "" + this.name + " " + this.age + " " + this.gender;} }
打印效果如下:
-
当我们拿到一个 Class,用
Class.getDeclaredFields()
方法得到 Field[],也就是类的属性列表,然后用Field. getGenericType()
方法得到这个属性的 Type 类型。 -
当我们拿到一个Method,用
Method.getGenericParameterTypes()
方法获得 Type[],也就是方法的参数类型列表。 -
当我们拿到一个 Class,用
Class.getGenericSuperclass()
这样就可以获取父类的泛型实参列表,和Class.getGenericInterfaces()
方法类似,这个获得的内容和class.getSuperclass()
方法获得的内容一样;
Type的分类
Type接口包含了一个实现类 (Class) 和四个实现接口 (TypeVariable, ParameterizedType, GenericArrayType, WildcardType) ,这四个接口都有自己的实现类,但这些实现类开发都不能直接使用,只能用接口。
- Class: 当需要描述的类型是普通 Java 类、数组、自定义类、 8种java基本类型的时候, java会选择Class来作为这个Type的实现类,我们甚至可以直接把这个Type强行转换类型为Class。这些类基本都有一个特点:基本和泛型无关,其他4种Type的类型,基本都是泛型的各种形态。
- ParameterizedType: 当需要描述的类是泛型类时,比如 List,Map 等,不论代码里写没写具体的泛型,java 会选择 ParameterizedType 接口做为Type的实现。ParameterizedType 接口有 getActualTypeArguments() 方法,用于得到泛型的 Type 类型数组。
- GenericArrayType: 当需要描述的类型是泛型类的数组时,比如比如 List[] , Map[] , type 用GenericArrayType 接口作为 Type 的实现。GenericArrayType 接口有 getGenericComponentType() 方法,得到数组的组件类型的 Type 对象。
- WildcardType: 当需要描述的类型是泛型类,而且泛型类中的泛型被定义为 (? extends xxx) 或者 (? super xxx) 这种类型,比如 List<? extends TestReflect>,这个类型首先将由ParameterizedType 实现,当调用 ParameterizedType 的 getActualTypeArguments() 方法后得到的 Type 就由 WildcardType 实现。
示例表格如下:
描述类型举例 | 所用到的 Type 类型 |
---|---|
Test | Class |
ArrayList<String> | ParameterizedType |
Test<String>[] | GenericArrayType |
Test<? extends Father> | WildcardType |
使用反射创建和操作任意类型数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。
相关方法
public static Object newInstance(Class<?> componentType, int… dimensions):创建一个具有指定的组件类型和维度的新数组。
public static void setXxx(Object array,int index,xxx value):将array数组中[index]元素的值修改为value。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用set(Object array,int index, Object value)方法。
public static xxx getXxx(Object array,int index,xxx value):将array数组中[index]元素的值返回。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用get(Object array,int index)方法。
示例如下:
package top.sharehome.Test;import java.lang.reflect.Array;public class Demo {public static void main(String[] args) {Object object = Array.newInstance(int.class, 5);Array.setInt(object,0,0);Array.setInt(object,1,1);Array.setInt(object,2,2);for (int i = 0; i < 5; i++) {System.out.println("Array.getInt(object,i) = " + Array.getInt(object, i));}}
}
打印效果如下: