反射
基本概念
🚀 作用:在程序运行期间,动态的获取类对象,从而使用类对象获取类中相关的信息并且访问;
通俗的讲:不通过new对象的方式,依然可以访问类中的属性、方法、构造方法;
生活中的反射:倒车镜,拍X光片,IDE的自动提示,
在外部就可以得到内部的东西,类似于一个镜子,通过镜子能看到后边的内容。这就是反射。
万物皆对象 类、属性、方法、构造器 也属于对象。
🎨 反射类介绍
java.lang.Class
类类,每一个类都将在类加载阶段自动产生此类的实例;
java.lang.reflect.Field
字段/属性类,任何一个属性都属于此类的实例;
java.lang.reflect.Method
方法类,任何一个方法都属于此类的实例;
java.lang.Constructor
构造器类,任何一个构造器都属于此类的实例;
获取Class对象
🚀 方式1:通过Class.forName 获取Class对象
// 方式1:通过Class.forName 获取Class对象 ( String类型的 全限定名)
// 只能在运行时,获取到传入的参数,无法在编译时得知Class类对象的类型
Class<?> aClass = Class.forName("com.ReflectPart.Singer");
System.out.println("aClass = " + aClass); // class com.ReflectPart.Singer
🚀 方式2:使用类名.class属性 获取Class对象
// 方式2:使用类名.class属性 获取Class对象
// 在编译时,就得知Class类对象的类型,所以可以提前得知Class类型的对象,泛型为Singer
Class<Singer> singerClass = Singer.class;
System.out.println("singerClass = " + singerClass); // class com.ReflectPart.Singer
🚀 方式3:使用Object类中getClass() 方法,获取Class对象(实例方法)
// 方式3:使用Object类中getClass() 方法,获取Class对象(实例方法)
// 在编译时,可以得知Class类型的对象为Singer类或Singer类的子类,
Singer hanser = new Singer();
Class<? extends Singer> aClass1 = hanser.getClass();
System.out.println("aClass1 = " + aClass1); // class com.ReflectPart.Singer
Class类
类本身也是一个类
方法
newInstance()
作用:创建当前Class对象所对应泛型的实例(实例方法),调用当前类对象中的无参构造创建对象。
参数:无
返回值:实例对象
示例:
// 方式2:使用类名.class属性 获取Class对象
// 在编译时,就得知Class类对象的类型,所以可以提前得知Class类型的对象,泛型为Singer
Class<Singer> singerClass = Singer.class;
System.out.println("singerClass = " + singerClass); // class com.ReflectPart.Singer
// 返回一个 Singer (泛型)类型的实例
Singer singer = singerClass.newInstance()
getField(String fieldName)
作用:根据字段名称获取到一个public修饰的字段对象
参数:String类型
返回值:Field类型
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
getFields()
作用:获取本类中所有的public关键字修饰的字段
参数:无
返回值:Field类型的数组
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field[] fields = stuClass.getFields();
// 遍历
fields.for
getDeclaredField(String name)
作用:获取到指定的本类中已定义的字段(私有的也可以获取到,但获取不到父类中的方法)
参数:字段的String名称
返回值:Field对象
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getDeclaredField("name");
getDeclaredFields()
作用:获取到本类中所有的已定义的字段(但获取不到父类中的方法)
参数:无
返回值:Field对象数组
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field[] fields = stuClass.getDeclaredFields();
// 遍历
fields.for
getMethod(String name,...paramsType)
getMethod(String name,Class<?>...paramsType)
作用:跟据名字获取本类public修饰的以及继承父类的方法
参数:第一个参数是String类型的方法名,第二个参数是可变长参数。
第二个参数默认不传代表,参数为空。
若方法中的参数为int类型 例如 public void m1(int age){}
则参数为 int类型的class对象,即为 int.class
返回值:Method类型对象
示例:
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取方法对象
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
getMethods()
作用:获 取所有本类public修饰的以及继承父类的方法
参数:无
返回值:Method类型的对象数组
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Method[] methods = stuClass.getMethods();
getDeclareMethod(String name,paramsType )
作用:根据参数获取本类中已定义的方法(包含私有的)
参数:第一个参数是String类型的方法名,第二个参数是可变长参数(形参列表)。
返回值:Method类型的对象
示例:
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取本类中已定义的方法(私有方法)
Method m31 = vupClass.getDeclaredMethod("m3", String.class, int.class);
getDeclareMethods()
作用:获取本类所有已定义的以及继承父类的方法(包含私有的)
参数:无
返回值:Method类型的对象数组
示例:
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取本类中已定义的方法(私有方法)
Method m31 = vupClass.getDeclaredMethod("m3", String.class, int.class);
Method[] declaredMethods = vupClass.getDeclaredMethods();
getConstructor()
作用:获取指定的类的public修饰的构造方法(根据形参列表不同进行筛选)
参数:形参的Class对象
返回值:返回一个 Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor();
// 获取有参构造
Constructor<?> constructor1 = vupClass.getConstructor(String.class, int.class, int.class);
getConstructors()
作用:获取类的所有public修饰的构造方法
参数:无
返回值:返回数组 Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?>[] constructors = vupClass.getConstructors();
// 遍历
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName() + " " +constructor.getParameterCount());
}
getDeclaredConstructor(...parameterTypes)
作用:获取指定形参的构造方法
参数:形参的Class对象
返回值:返回Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor(String.class, int.class, int.class);
getDeclaredConstructors()
作用:获取所有访问修饰符修饰的构造方法
参数:无
返回值:返回数组 Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?>[] constructors = vupClass.getConstructors();
// 遍历
for (Constructor<?> constructor : constructors) {
System.out.println("constructor = " + constructor);
}
Field类
类中的属性也有自己的类(类型定义,万物皆对象)
方法
getName()
作用:获取字段的名称
参数:无
返回值:String
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
// 获取字段的名称
field.getName()
getType()
作用:获取字段的类型
参数:无
返回值:String
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
// 获取字段的类型
field.getType() // 基本数据类型、数组、等
getModifiers()
作用:获取字段的Java语言访问修饰符
1 代表是public修饰的
参数:无
返回值:String
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
// 获取字段的类型
field.getType() // 基本数据类型、数组、等
set()
作用:给属性赋值。
参数:第一个参数是哪一个学生对象,第二个参数是赋值
返回值:无
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field1 = stuClass.getField("age");
// 获取对象(方式1)
// Student stu = new Student();
Object o = stuClass.newInstance()
// 赋值(方式1)
// field1.set(stu,12);
field1.set(o,12);
get()
作用:获取属性的值。
参数:第一个参数是哪一个对象
返回值:Field类型
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field1 = stuClass.getField("age");
// 获取对象
Object o = stuClass.newInstance()
// 获取属性值
field1.get(o);
setAccessible()
作用:忽略JVM的安全检查。即可以在不在同类中访问私有属性
(不通过反射是无法访问私有属性的,原因是在编译期间就无法通过)
(反射是在运行时才用到的,绕过的编译的检查,但是在运行期间仍会抛异常,通过设置忽略风险,仍能够获取到私有属性)
参数:布尔值 true,忽略安全检查
返回值:无
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field1 = stuClass.getField("age");
// 忽略JVM的安全检查,访问私有属性
field1.setAccessible(true)
getAnnotation(annotationClass)
作用:获取某个元素(类/方法/字段等)上指定类型的注解对象
参数:注解类型class对象
返回值:注解实例对象 Annotation类型的泛型 UserName 自己
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
Field declaredField = dataClass.getDeclaredField("userName");
// 获取注解实例对象
UserName name = declaredField.getAnnotation(UserName.class);
// 获取注解属性的值,实例的方法就是 注解定义的属性的名称
String value = name.value();
getAnnotations()
作用:获取当前属性上添加的所有注解,包含继承自父类的注解对象。
参数:无
返回值:注解实例对象数组[username,password]
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field declaredField = dataClass.getDeclaredField("password");
Annotation[] annotations = declaredField.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
getDeclaredAnnotation(annotationClass)
作用:获取指定类型的注解对象,如果字段上有这个注解,就返回注解的实例,否则返回 null,不包含继承自父类的注解对象
参数:注解的class对象
返回值:注解类型实例
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field declaredField = dataClass.getDeclaredField("password");
Driver drive = declaredField.getDeclaredAnnotation(Driver.class);
System.out.println("drive.value() = " + drive.value()); // drive.value() = 123
getDeclaredAnnotations()
作用:获取当前属性上添加的所有注解,不包含继承自父类的注解对象。
参数:无
返回值:注解实例对象数组,每一个元素的是Annotation类型。
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field declaredField = dataClass.getDeclaredField("password");
Annotation[] anns = declaredField.getDeclaredAnnotations();
for (Annotation ann : anns) {
System.out.println(ann);
}
isAnnotationPresent()
作用:判断某个元素(类/方法/字段等)上是否存在指定类型的注解
参数:注解类型class对象,当前属性上方是否添加了UserName注解。
返回值:布尔值,存在指定类型的注释,则返回true,否则返回false
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
Field declaredField = dataClass.getDeclaredField(username);
if (declaredField.isAnnotationPresent(UserName.class)){
// 获取注解实例对象
UserName name = declaredField.getAnnotation(UserName.class);
// 获取注解属性的值
String value = name.value();
System.out.println("value = " + value);
}
Method类
方法类;java.lang.reflect.Method
方法类,任 何一个方法都属于此类的实例;
方法
getName()
作用:获取方法的名称
参数:无
返回值:String类型的方法名
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取方法数组
Method[] methods = stuClass.getMethods();
// 获取方法名称
for(Method method: methods){
String name = method.getName();
System.out.print(name)
}
getParameterCount()
作用:获取方法中参数的个数
参数:无
返回值:个数
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取方法数组
Method[] methods = stuClass.getMethods();
// 获取方法名称
for(Method method: methods){
String name = method.getName();
int count = method.getParameterCount();
System.out.print(name)
}
invoke(obj,args)
作用:调用方法(实例级别)
参数:第一个参数是类的实例对象,第二个参数是给这个方法传递的参数
若有参数,直接写参数即可。
返回值:无
示例:
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 根据指定名称和参数列表获取方法对象实例
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
setAccessible()
作用:如同Field中的setAccessible()方法使用一致,忽略JVM警告。
参数:true
返回值:无
示例:
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 根据指定名称和参数列表获取方法对象实例
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
Constructor类
Constructor提供了一个类的单个构造函数的信息和访问。所有构造器都属于此类的实例。
构造方法
无
方法
getName()
作用:以字符串形式返回此构造函数的名称。
参数:无
返回值:String类型
示例 :
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.getConstructor.Vup");
// 获取Contructor对象
Constructor<?> constructor = vupClass.getConstructor();
// 显示
int parameterCount = constructor.getParameterCount();
String name = constructor.getName();
getParameterCount()
作用:返回由此对象表示的可执行文件的形式参数(无论是显式声明还是隐式声明)的数量。
参数:无
返回值:int类型的数量
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.getConstructor.Vup");
// 获取Contructor对象
Constructor<?> constructor = vupClass.getConstructor();
// 显示
int parameterCount = constructor.getParameterCount();
String name = constructor.getName();
System.out.println("name:" + name + "| parameterTypes:" + parameterCount);
newInstance(Object ... args)
作用:根据传入的参数列表 调用对应的有参构造方法创建对象
注意:如果有重名的类名,可以写包的全称,例如:
java.lang.String.class 或 java.lang.util.Date.class
参数:传入有参构造的参数
返回值:类的实例对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor();
// 获取有参构造
Constructor<?> constructor1 = vupClass.getConstructor(String.class, int.class, int.class);
// 创建实例对象
Object vup = constructor.newInstance();
Object vup1 = constructor1.newInstance("Hanser", 18, 175);
// vup: Vup{name='null', age=0, height=0}
System.out.println("vup: "+vup);
// vup1: Vup{name='Hanser', age=18, height=175}
System.out.println("vup1: "+vup1);
基本使用
反射可以在运行期间,动态的获取到类中的所有信息(属性,方法,实例,构造方法,静态方法等)。
✏️ 反射解决之前宠物系统中抽奖送宠物的问题。
之前的实现方案:
根据用户传入对应的参数 来创建对象的宠物子类对象 返回;
弊端:
这样编写有一个问题,因为是直接硬编码的方式创建对象。
所以我们考虑到未来可 能新增的类,即新的宠物子类,这样的书写方法,扩展性较差。
使用反射的方式解决:
在编码期间并不直接写明具体创建哪个子类对象,而是根据调用者传入的全限定名来创建对象。
这样更加灵活、扩展性更强、不会受限于硬编码的局限。
📝 示例1:使用反射解决编译期间,无法确定未来新增的类。
(未使用反射)
package com.ReflectPart.draw;
/**
* 宠物类 父类
*/
public class Pet {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Pet(String name) {
this.name = name;
}
public Pet() {
}
public void print(){
System.out.println("宠物的名字是:" + name);
}
}
package com.ReflectPart.draw;
public class Dog extends Pet{
private String strain;
public Dog() {}
public Dog(String name, String strain) {
super(name);
this.strain = strain;
}
}
package com.ReflectPart.draw;
public class Penguin extends Pet{
private char gender;
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public Penguin() {}
public Penguin(String name, char gender) {
super(name);
this.gender = gender;
}
}
package com.ReflectPart.draw;
public class Master {
// 抽奖获取宠物方法
public Pet getPet(String str){
// 如果不使用反射,这里还需要加很多判断,当获得3等奖之类的操作
if (str.equals("No1")){
Penguin penguin = new Penguin("小白",'雄');
return penguin;
} else if (str.equals("No2")){
Dog dog = new Dog("大黄","金毛");
return dog;
}else {
Dog dog = new Dog("小黄","金毛");
return dog;
}
}
}
package com.ReflectPart.draw;
public class Test {
public static void main(String[] args) {
Master mas = new Master();
Pet pet = mas.getPet("No3");
System.out.println("获得的奖品是 "+pet.name);
}
}
获取对象
⚡️ 通过方式2:获取Class对象,然后再调用Object类中的newInstance
方法,创建对象。
之前的实现方案:
根据用户传入对应的参数 来创建对象的宠物子类对象 返回;
弊端:
这样编写有一个问题,因为是直接硬编码的方式创建对象。
所以我们考虑到未来可能新增的类,即新的宠物子类,这样的书写方法,扩展性较差。
使用反射的方式解决:
在编码期间并不直接写明具体创建哪个子类对象,而是根据调用者传入的全限定名来创建对象。
这样更加灵活、扩展性更强、不会受限于硬编码的局限。
在编译期间无法确定具体的类名是什么,通过传入 包名+类名,全限定名的方式创建对象,更加灵活。
只有在使用的时候,才会把包名+类名传入给他,这就是反射做的事情,这也是框架做的事情。
用了框架以后只用写接口,写SQL语句,实现类是不用写的,他会帮我们自动生成。
Spring框架:以后不会用new关键字了,通过反射和代理的技术结合到了一起。
package com.ReflectPart.draw;
public class Master {
// 抽奖获取宠物方法,参数是子类名称 包名+类名全限定名称
public Pet getPet(String subClassName) {
Pet pet = null;
try {
// 通过类类对象
Class<?> aClass = Class.forName(subClassName);
// 创建实例对象,默认使用的是无参构造方法创建
Object obj = aClass.newInstance();
// 判断类型,然后转换
if (obj instanceof Pet){
pet = (Pet) obj;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 把实例返回
return pet;
}
}
package com.ReflectPart.draw;
public class Test {
public static void main(String[] args) {
// 创建mas对象
Master mas = new Master();
// 获取宠物对 象
Pet pet = mas.getPet("com.ReflectPart.draw.Dog");
System.out.println("pet = " + pet); // pet = com.ReflectPart.draw.Dog@4554617c
}
}
获取属性
优势:不需要知道类中的代码是怎么写的,可以提前写好,给类中的属性赋值。
1. 先获取到Class对象
2. 再通过Class对 象,调用对应方法,获取到字段
📝 使用示例:获取 public
修饰的属性
通过 Class类中的 getField方法获取,同时使用Field类中的set方法赋值。
package com.ReflectPart;
public class Vup {
public String name;
int age;
public Vup() {
}
public Vup(String name, int age) {
this.name = name;
this.age = age;
}
}
package com.ReflectPart;
import java.lang.reflect.Field;
public class TestField1 {
public static void main(String[] args) throws Exception {
// 通过反射获取 类的Class对象
Class<?> aClass = Class.forName("com.ReflectPart.Vup");
// 获取所有的public修饰的字段对象
Field[] fields = aClass.getFields();
// 根据名称获取单个字段对象
Field field1 = aClass.getField("name");
// 遍历
for (Field field : fields) {
// 打印信息
// name - class java.lang.String - 1
System.out.println(field.getName() + " - " + field.getType() + " - " + field.getModifiers());
}
String name = field1.getName();
System.out.println("name = " + name); // name = name
// 创建实例对象
Object instance = aClass.newInstance();
// 设置值
field1.set(instance,"Hanser");
// 读取值
Object o = field1.get(instance);
System.out.println("o = " + o); // o = Hanser
}
}
📝 使用示例:获取 private
、protected
修饰的属性
通过 Class类中的 getDeclaredField 方法获取,同时使用Field类中的set方法赋值。
package com.ReflectPart;
public class Vup {
public String name;
int age;
private int height;
public Vup() {
}
public Vup(String name, int age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
}
package com.ReflectPart;
import java.lang.reflect.Field;
public class TestField1 {
public static void main(String[] args) throws Exception {
// 通过反射获取 类的Class对象
Class<?> aClass = Class.forName("com.ReflectPart.Vup");
// 获取所有的已定义的字段对象 返回值为字段数组
Field[] declaredFields = aClass.getDeclaredFields();
// 遍历属性数组
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName() + "-" + declaredField.getType() + "-" + declaredField.getModifiers());
}
// 根据名称获取到单个字段对象
Field field1 = aClass.getDeclaredField("height");
// 通过Vup类 Class对象 创建 up类实例
Object obj = aClass.newInstance();
// 表示忽略JVM的安全检查 即可以不在同类中访问私有属性
field1.setAccessible(true);
// 属性赋值
// 第一个参数 表示给哪个对象的此属性赋值
// 第二根参数 具体值
field1.set(obj,12);
// 获取属性值 参数表示获取哪个对象的此属性的值
System.out.println(field1.get(obj));
}
}
获取方法
java.lang.reflect.Method
方法类,任何一个方法都属于此类的实例;
通过 getMethod 方法和getMethods方法获取本类中public修饰的方法以及继承自父类的方法。
通过 getDeclareMethod() 方法和 getDeclareMethod() 方法获取类中已定义的方法以及和继承父类的方法。
package com.ReflectPart.getMethod;
public class Vup {
public String name;
int age;
private int height;
public Vup() {
}
public Vup(String name, int age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
// 无参方法
public void m1(){
System.out.println("无参方法m1 start");
}
// 无参方法
public void m2(int age){
System.out.println("有参(int)方法m2 start");
}
// 无参方法
public void m3(int age,String name){
System.out.println("有参(int String)方法m3 start");
}
// 无参方法
public void m3(int age,String name,int height){
System.out.println("有参(int String int)方法m3 start");
}
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取本类中所有的public修饰的以及继承父类的方法
Method[] methods = vupClass.getMethods();
// 根据指定名称和参数列表获取方法对象实例
Method m1 = vupClass.getMethod("m1");
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 创建类对象实例
Object o = vupClass.newInstance();
// 执行方法
m1.invoke(o);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
// 遍历所有方法
// for (Method method : methods) {
// // 打印信息
// String name = method.getName();
// int parameterCount = method.getParameterCount();
// System.out.println(name + " " + parameterCount);
// }
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取本类中已定义的方法(私有方法)
Method m31 = vupClass.getDeclaredMethod("m3",int.class, String.class, int.class);
Method[] declaredMethods = vupClass.getDeclaredMethods();
// 创建类对象实例
Object o = vupClass.newInstance();
// 执行方法
// 忽略JVM警告
m31.setAccessible(true);
m31.invoke(o,18,"hanser",165);
// 遍历所有本类方法
for (Method declaredMethod : declaredMethods) {
//打印信息
String name = declaredMethod.getName();
int parameterCount = declaredMethod.getParameterCount();
System.out.println(name + " " + parameterCount);
}
}
获取构造器
Java.lang.reflect.Constructor 类 所有构造器都属于此类的实例。
示例1:获取公开的构造器方法。
package com.ReflectPart;
public class Vup {
public String name;
int age;
private int height;
public Vup() {
}
private Vup(String name) {
this.name = name;
}
public Vup(String name, int age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
@Override
public String toString() {
return "Vup{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor();
// 获取有参构造
Constructor<?> constructor1 = vupClass.getConstructor(String.class, int.class, int.class);
// 调用方法
int parameterCount = constructor.getParameterCount(); // 0
// com.ReflectPart.getConstructor.Vup
String name = constructor.getName();
// 创建实例对象 vup
Object o1 = constructor.newInstance();
Object o2 = constructor1.newInstance("hanser",18,156);
sout
示例2:获取所有的构造器方法。
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
Constructor<?>[] declaredConstructors = vupClass.getDeclaredConstructors();
// 遍历
for (Constructor<?> con : declaredConstructors) {
System.out.println(con.getName() + "---" + con.getParameterCount());
}
Constructor<?> con1 = vupClass.getDeclaredConstructor(String.class);
// 忽略告警
con1.setAccessible(true);
// 创建Vup实例对象
Object obj1 = con1.newInstance("赵四");
// 打印
System.out.println("obj1 = " + obj1);
获取注解
⚡️ 通过反射获取注解的过程:
首先定义一个自定义的注解,设置好元注解,同时设置注解的属性,然后在类中的属性上设置注解,然后通过反射先获取到类中的属性,然后通过属性名称获取到注解中属性的值,从而达到获取配置的目的。
🎨 获取配置文件参数的两种方式:
- 1、通过获取注解中的参数获取配置。
- 2、通过Properties类中的Load方法,传递一个IO流获取一个Properties对象获取配 置。
✨ 注解和反射结合的步骤:
1、先获取到当前类对象 即Class对象
2、根据Class对象获取到本类中所有的属性
3、根据属性对象获取到属性上方添加的注解对象
4、根据注解对象获取到属性值
用到的方法有 getAnnotation(),getAnnotations(),getDeclaredAnnotation(),getDeclaredAnnotations()。
这些方法都是Field类中的方法。
📌 注解和反射结合案例1:获取数据库信息(参考注解章节):
定义用户名属性注解
package com.ReflectPart.AnnoAndReflect.DataBaseDemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserName {
String value();
}
定义密码属性注解
package com.ReflectPart.AnnoAndReflect.DataBaseDemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
String value();
}
定义URL属性注解
package com.ReflectPart.AnnoAndReflect.DataBaseDemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface URL {
String url();
}
定义驱动属性注解
package com.ReflectPart.AnnoAndReflect.DataBaseDemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Driver {
String value();
}
设置注解,使用反射获取注解对象,然后使用注解对象方法获取值。
package com.ReflectPart.AnnoAndReflect.DataBaseDemo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
// 获取class类对象
try {
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field[] declaredFields = dataClass.getDeclaredFields();
// 遍历属性
for (Field declaredField : declaredFields) {
// 打印属性名称和类型
System.out.println(declaredField.getName() + "**" + declaredField.getType());
// 判断属性上是否添加了 UserName 注解
if (declaredField.isAnnotationPresent(UserName.class)){
// 获取注解实例对象
UserName name = declaredField.getAnnotation(UserName.class);
// 获取注解属性的值
String value = name.value();
System.out.println("value = " + value);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
package com.ReflectPart.AnnoAndReflect.DataBaseDemo;
public class DataInfo {
// 定义属性
@UserName("root")
private String userName;
@Password("****")
private String password;
@URL(url = "jdbc:mysql://localhost:3306/dbName")
private String URL;
@Driver("com.mysql.cj.jdbc.Driver")
private String driver;
public DataInfo(String userName, String password, String URL, String driver) {
this.userName = userName;
this.password = password;
this.URL = URL;
this.driver = driver;
}
}
Junit框架
JUnit Java Unit 单元测试框架 是一个单独用于Java测试的工具;
框架:别人写好的一些类,包,一个包中包含了很多类,打成jar包之后;
JUnit提供了很多注解 方便我们测试代码 在需要测试的某个方法上方 添加某个注解 就可以实现测试的效果;
Junit注解
@Test 表示当前方法可以独立执行;
@After 类中的每个方法执行之后,此方法会自动执行一次;
@Before 类 中的每个方法执行之前,此方法会自动执行一次;
@AfterClass 当前类中的所有的方法,执行之后,只执行一次,此注解只能添加在静态方法上;
@BeforeClass 当前类中的所有的方法,执行之前,只执行一次,此注解只能添加在静态方法上;
⚡️ 约定
实际开发中,我们测试的代码,会单独保存在于src平级的test文件夹(新建一个)中,右键将此文件夹标记为测试资源根目录;
基本使用
1、导包
Junit不属于Java中的JDK自带的包,所以首先需要导入jar包;
先再类中写一个 @Test,然后idea使用alt键+回车键,选择Add 'Junit4' to classpath (4稳定版本),把4版本的junit的java文件导入到类路径中。idea默认从maven仓库中下载。
2、在左侧点击运行
3、点击类声明左侧的播放按钮,会自动把类中的所有@Test测试类都执行
使用案例
package com.ReflectPart.Junit;
import org.junit.*;
public class TestJunit {
@Before
public void before(){
System.out.println("当前类中的每个实例方法执行之前 此方法会自动执行一次 ");
}
@After
public void after(){
System.out.println("当前类中的每个实例方法执行之后 此方法会自动执行一次 ");
}
@BeforeClass
public static void beforeClass(){
System.out.println("*******当前类中的方法 执行之前 只执行一次*******");
}
@AfterClass
public static void afterClass(){
System.out.println("*******当前类中的方法 执行之后 只执行一次*******");
}
@Test
public void m1(){
System.out.println("hello world m1");
}
@Test
public void m2(){
System.out.println("hello world m2");
}
}
*******当前类中的方法 执行之前 只执行一次*******
当前类中的每个实例方法执行之前 此方法会自动执行一次
hello world m1
当前类中的每个实例方法执行之后 此方法会自动执行一次
当前类中的每个实例方法执行之前 此方法会自动执行一次
hello world m2
当前类中的每个实例方法执行之后 此方法会自动执行一次
*******当前类中的方法 执行之后 只执行一次*******
自定义Junit
// 和Junit差一个播放(运行)键
package com.ReflectPart.MyTest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo {
@MyTest
public void m1(){
System.out.println("m1方法执行");
}
public static void main(String[] args) {
// 获取class对象
Class<?> testClass = null;
try {
testClass = Class.forName("com.ReflectPart.MyTest.Demo");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// 获取方法
Method[] methods = testClass.getDeclaredMethods();
// 创建实例
Object demo = null;
try {
demo = testClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
for (Method method : methods) {
if (method.isAnnotationPresent(MyTest.class)){
// 执行方法
try {
Object res = method.invoke(demo);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.ReflectPart.MyTest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
类加载器
ClassLoader 是一个抽象类,属于类加载器。
jar文件就是java中的压缩文件,打包后的文件。使用解压文件就可以解压。
类加载的作用:加载类
类加载的过程(说一个大概就行):
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
1、加载类(load) : 将类的class文件读入内存,并为之创建一个java.lang.Class对象。
此过程由类加载器完成将class文件字节码内容加载到内存中,并将这些数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。这个加载的过程需要类加载器参与。
2、链接(link) : 将类的二进制数据合并到JRE(运行环境中)中:
验证:确保加载的类信息符合JVM规范,例如:使用二进制文件(binary viewer)查看器,查看文件内容以cafe开头,没有安全方面的问题;
准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配(静态区)。
解析:将类、接口、字段和方法的符号引用转为直接引用。
3、初始化(Initialize) : JVM负责对类进行初始化:
执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
类加载器的分类:
%JAVA_HOME%/jre/lib/rt.jar;rt 为 RunTime 的含义;此类加载底层为C++ 实现 在Java代码中无法获取;
// 加载核心类
Object obj = new Object()
ClassLoader classLoader = obj.getClass().getClassLoader(); // classLoader = null
BootStrap ClassLoader 负责加载核心类库,例如:rt.jar
Extension ClassLoader 负责加载扩展包, %JAVA_HOME%/jre/lib/ext/*.jar
Application ClassLoader 负责加载自定义的类。
自定义类加载器 继承ClassLoader类,重写 findClass方法(了解)。
关系:
Application ClassLoader 属于 Extension ClassLoader的子加载器
Extension ClassLoader 属于 BootStrap ClassLoader 的子加载器
package com.ReflectPart.classLoader;
import sun.net.spi.nameservice.dns.DNSNameService;
public class TestClassLoader {
public static void main(String[] args) {
// BootStrap ClassLoader 加载器的使用
Object obj = new Object();
// 获取对象的类加载器
ClassLoader classLoader = obj.getClass().getClassLoader();
System.out.println("classLoader = " + classLoader); // null
// 拓展包加载
ClassLoader classLoader1 = DNSNameService.class.getClassLoader();
System.out.println("classLoader1 = " + classLoader1); // sun.misc.Launcher$ExtClassLoader@2503dbd3
// 自定义类
ClassLoader classLoader2 = TestClassLoader.class.getClassLoader();
System.out.println("classLoader2 = " + classLoader2); // sun.misc.Launcher$AppClassLoader@18b4aac2
// 获取父类加载器
ClassLoader parent = classLoader2.getParent();
System.out.println("parent = " + parent); // sun.misc.Launcher$ExtClassLoader@2503dbd3
}
}
面试题:
双亲委派模型/机制:
当JVM通过类加载器加载某一个类,会先委托给上级类加载器来加载,如果上级类加载器中无法加载,再向下由子加载器加载,这样做的目的是为了防止人为的编写和JDK包名类名完全相同的类 污染,入侵JDK的源代码