java-反射、BeanUtils、注解 -学习笔记
程序员文章站
2024-02-16 09:35:22
...
一、反射
- 什么是反射
- 反射:反射是一种机制,利用该机制可以在程序
运行
过程中对类进行解剖并操作
类中的方法,属性,构造方法等成员。 - 反射:调用构造方法、普通方法、属性的另外一种方式。
- 最终效果和普通调用方式是一样的
- 特点:
- 比普通方式麻烦
- 比普通方式强大
- 反射的使用
反射操作的统一步骤:
- 获取Class对象
- 要操作谁,就找到谁:
- 要获取Constructor对象: getConstructor() getDeclaredConstructor()
- 要获取Method对象:getMethod(), getDeclaredMethod()
- 要获取Field对象:getField(), getDeclaredField()
- 如果操作的是private,需要提前设置允许暴力反射:setAccessible(true)
- 反射操作:
- 操作构造方法:newInstance()
- 操作普通方法:invoke()
- 操作属性字段:get(), set()
2.1 得到Class
- 得到Class对象的三种方式
- Class clazz = 类名.class
- Class clazz = 对象.getClass() 用于不知道别人的类,方法中的参数调用
- Class clazz = Class.forName(“全限定类名”)
- 常用方法:
- clazz.getName():获取全限定类名
- clazz.getSimpleName():获取简单类名(不包含包名)
- clazz.newInstance():生成类的实例对象
//1.得到类的Class对象:类名.class
Class clazz = Person.class;
//1.得到类的Class对象:对象.getClass()
Person person = new Person();
Class clazz = person.getClass();
//1.得到类的Class对象:Class.forName()
Class clazz = Class.forName("com.itheima._01reflect.Person");
//获取全限定类名
String name = clazz.getName();
System.out.println("全限定类名是:" +name);
//获取简单类名
String simpleName = clazz.getSimpleName();
System.out.println("简单类名是:" + simpleName);
//生成类的实例对象
Object object = clazz.newInstance();
System.out.println(object);
2.2 得到并操作Constructor(反射) class软件包 java.lang
生成一个类的实例对象,不是通过普通的new方式,而是通过反射
- 获取Constructor
- clazz.getConstructor(Class… argTypes):获取的是public类型的构造方法
- clazz.getDeclaredConstructor(Class… argTypes):获取的是任意类型的构造方法
- clazz.getConstructors():获取所有public类型的构造方法Constructor数组
- clazz.getDeclaredConstructors():获取所有任意类型的构造方法Constructor数组
- 反射调用Constructor
- constructor.newInstance(Object… argValues):生成类的实例对象
- 参数argValues:构造方法的实参值
- constructor.setAccessible(true):设置Constructor允许暴力反射
- 如果是要操作private类型的Constructor,就需要提前设置允许暴力反射
- constructor.newInstance(Object… argValues):生成类的实例对象
/**
* 反射调用Person的无参构造(public),得到一个Person对象
*/
@Test
public void test1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取类的Class对象
Class<Person> clazz = Person.class;
//获取到构造方法:无参构造,public
Constructor<Person> constructor = clazz.getConstructor();
//生成类实例对象
Person person = constructor.newInstance();
System.out.println(person);
}
/**
* 反射调用Person的有参构造(private),得到一个Person对象
*/
@Test
public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象
Class<Person> clazz = Person.class;
//获取到构造方法:有参构造,private
Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
//暴力反射
constructor.setAccessible(true);
//生成类实例对象
Person person = constructor.newInstance("老王", 45);
System.out.println(person);
}
2.3 得到并操作Method(反射)
- 获取到Method
- clazz.getMethod(String methodName, Class… argTypes):获取public类型的方法
- clazz.getDeclaredMethod(String methodName, Class… argTypes):获取任意类型的方法
- clazz.getMethods():获取所有public类型的方法Method数组
(自己的和父类的)
- clazz.getDeclaredMethods():获取所有任意类型的方法Method数组(自己的)
- 反射调用Method
- method.invoke(Object target, Object… argValues):反射调用方法
- 参数:
- target:调用哪个对象的方法
- argValues:方法的实参
- 返回值:方法的返回值
- 参数:
- method.setAccessible(true):设置方法允许暴力反射
- method.invoke(Object target, Object… argValues):反射调用方法
Person person1 = new Person();
//获取方法Method对象
Method method = clazz.getMethod("eat", String.class);
//反射调用方法
Object result = method.invoke(person1, "榴莲");
/**
* 反射调用Person的eat方法:public,有参String
* Person person1 = new Person();
* Person person2 = new Person();
*
* person1.eat("..");
*/
@Test
public void test1() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person person1 = new Person();
Person person2 = new Person();
//获取Class对象 ctrl+alt+v
Class<Person> clazz = Person.class;
//获取方法Method对象
Method method = clazz.getMethod("eat", String.class);
//反射调用方法
Object result = method.invoke(person1, "榴莲");
System.out.println("方法执行的返回值:" + result);
}
/**
* 反射调用Person的sleep方法,private,有参String
*/
@Test
public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person person1 = new Person();
Person person2 = new Person();
//获取Class对象
Class<Person> clazz = Person.class;
//获取到方法Method对象
Method method = clazz.getDeclaredMethod("sleep", String.class);
//设置允许暴力反射
method.setAccessible(true);
//反射调用method方法
Object result = method.invoke(person2, "老王");
System.out.println("方法执行的返回值:" + result);
}
2.4 得到并操作Field(反射)
- 获取到Field对象
- clazz.getDeclaredField(String fieldName):获取指定名称的属性(任意类型)
- clazz.getField(String fieldName):获取指定名称的属性(public类型)
- clazz.getDeclaredFields():获取所有任意类型的属性,得到Field数组
- clazz.getFields():获取所有public类型的属性,得到Field数组
- 反射操作Field对象
- field.set(Object target, Object value):设置属性值
- target:设置哪个对象的属性值
- value:设置的值
- field.get(Object target):获取属性值
- field.setAccessible(true):设置允许暴力反射
- field.getType():获取属性类型
- field.set(Object target, Object value):设置属性值
/*
* 反射获取所有的属性值
*/
@Test
public void test1() throws IllegalAccessException {
Person person1 = new Person();
person1.setName("坤坤");
person1.setAge(18);
Person person2 = new Person();
person2.setName("凡凡");
person2.setAge(24);
//获取Class对象
Class<Person> clazz = Person.class;
//获取所有的属性Field
Field[] fields = clazz.getDeclaredFields();
//循环遍历
for (Field field : fields) {
field.setAccessible(true);
//获取每个属性的值
Object value = field.get(person1);
//输出属性名称和属性值 // name 坤坤
String fieldName = field.getName(); // age 18
System.out.println("属性名:" + fieldName + ", 属性值:" + value);
}
}
/**
* 反射设置Person的所有属性值
* 给name属性设置值:晗晗
* 给age属性设置值:26
*/
@Test
public void test2() throws NoSuchFieldException, IllegalAccessException {
Person person = new Person();
//获取Class对象 注意getClass
Class<? extends Person> clazz = person.getClass();
//给name设置属性值:晗晗
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person, "晗晗");
//给age设置属性值:26
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(person, 26);
System.out.println(person); //Person[name='晗晗' , age='26']
}
1.7.2 编写一个工厂方法可以根据配置文件产任意类型的对象。
例如有配置文件stu.properties,存储在项目的src文件夹下,内容如下:
根据配置文件信息创建一个学生对象
class=com.itheima._02test1.Student //stu.properties文件内容
name=jack
gender=\u7537
age=21
/**
* 根据配置文件里的信息,生成指定的对象
*/
public class DemoFactory {
public static void main(String[] args) throws Exception {
//1.读取配置文件 流读取文件
Properties properties = new Properties();
InputStream inputStream = DemoFactory.class.getClassLoader().getResourceAsStream("stu.properties");
properties.load(inputStream);
//2.从配置文件里得到类名,生成类的实例对象
String className = properties.getProperty("class");
Class<?> clazz = Class.forName(className);
Object object = clazz.newInstance();
//3.反射设置属性值:可以获取clazz里所有的field,也可以获取配置文件里所有的key
Set<String> names = properties.stringPropertyNames();
for (String fieldName : names) {
if ("class".equals(fieldName)) { //不获取class属性名
continue;
}
String fieldValue = properties.getProperty(fieldName); //三个属性值
//获取Field对象
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
//设置field的属性值
Class<?> fieldType = field.getType(); //属性类型 string string integer
if (fieldType == Integer.class) {
field.set(object, Integer.parseInt(fieldValue));
}else{
field.set(object, fieldValue);
}
}
//4.把设置好的实例对象,输出显示
System.out.println(object);
}
}
//使用beanUtils省略上面代码 第3步
BeanUtils.setProperty(object, fieldName, fieldValue);
BeanUtils.setProperty(student, "age", "20");
二、BeanUtils工具包
1. BeanUtils简介 封装上面工厂方法
- 是Apache提供的一个开源工具,简化了JavaBean的数据封装。
- 相关概念:
- 字段:类的成员变量 private String name;是字段
- 属性:get/set方法,去掉get/set,首字母小写,是属性
注意:创建JavaBean的时候,要保证字段名和属性一致。使用idea快捷键生成,而不要自己手写
- 相关的jar包:
- 使用BeanUtils
- BeanUtils.setProperty(Object bean, String fieldName, Object fieldValue):设置属性值 如果指定的属性不存在,则什么也不发生。
- BeanUtils.getProperty(Object bean, String fieldName):获取属性值
- BeanUtils.copyProperties(Object dest, Object orig):将对象orig的属性值赋值给对象dest对象对应的属性 注意:只有属性名名相同且类型一致的才会赋值成功。
- BeanUtils.populate(Object bean, Map<String,Object> map):把map里的数据封装到JavaBean对象里
案例演示
三、注解
1. 什么是注解
1.1 注解简介
- 注解:Annotation,是和class、inteface同一等级的代码说明。是一种记号,记号本身没有功能,是其它软件、其它代码增加的功能。
- 注解是JDK1.5的新特性。
注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息。 标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。
1.2 注解作用和JDK里常见的注解
- 编译检查:
- @Override:被@Override标记的方法,idea/eclipse会提供功能:检查语法
- @Deprecated:被@Deprecated标记的方法,idea/eclipse会提供功能:把使用方法的代码划掉
- @SupressWarning:
压制警告
,idea/eclipse不会显示警告信息
- 辅助生成文档:
- @version:版本
- 代替xml配置文件:
- @WebServlet:Tomcat软件提供的,用于配置Servlet,代替web.xml的
现在不能完全代替
- @WebServlet:Tomcat软件提供的,用于配置Servlet,代替web.xml的
2. 自定义注解
2.1 注解的定义语法
@元注解
public @interface 注解名{
属性类型 属性名称() default 默认值;
}
int age() default 18; // 年龄
- 属性适用的数据类型
- 八种基本数据类型(int,float,boolean,byte,double,char,long,short)
- String类型,Class类型,枚举类型,注解类型
- 以上所有类型的一维数组
2.2 元注解
- 元注解:用来限制自定义注解的注解
- 常用元注解有2个:
- @Target:用来限制自定义注解可以用在什么地方。从ElementType里取值的,常用值有:
- ElementType.TYPE:可以用在类、接口上 type
- ElementType.METHOD:可以用在方法上 method
- ElementType.FIELD:可以用在成员变量上 field
PARAMETER:用在参数上 parameter
CONSTRUCTOR:用在构造方法上 constructor
LOCAL_VARIABLE:用在局部变量上 local_variable
- @Retention:用来限制自定义注解保留到什么阶段(生命周期)。从RetentionPolicy里取值,常用的:
- RetentionPolicy.SOURCE:保留到源码阶段 source
- RetentionPolicy.CLASS:保留到字节码阶段 class
- RetentionPolicy.RUNTIME:保留到运行阶段 runtime
- @Target:用来限制自定义注解可以用在什么地方。从ElementType里取值的,常用值有:
public class Demo02MyAnnotationTest {
public static void main(String[] args) throws NoSuchMethodException {
//获取Class对象
Class<Demo02MyAnnotationTest> clazz = Demo02MyAnnotationTest.class;
//获取method1的方法对象
Method method = clazz.getMethod("method1", String.class);
//判断方法上是否有指定的注解 保留在什么阶段
boolean b = method.isAnnotationPresent(Demo02MyAnnotation.class);
System.out.println("method1方法上是否有注解Demo02MyAnnotation:" + b); //true 或false
}
2.2 属性
- 注解的属性,并非所有类型都可以。只支持:
- 8种基本数据类型
- String类型
- Class类型
- 注解类型
- 枚举类型
- 以及以上类型的一维数组形式
3. 注解的使用
- 使用注解的语法:@注解名(属性名=值, 属性名=值, …)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Demo02MyAnnotation {
String str()
int number()
} 写法 @Demo02MyAnnotation(str = "hello, world", number = 10)
- 注意:如果只有一个属性需要设置值,并且属性名称是value,可以简写成:@注解名(value的值)
4. 注解的解析(了解)
- 给注解增加功能
- method.isAnnotationPresent(Class annotationClass):判断方法上是否有指定注解返回Boolean类型
使用反射获取注解的数据 案例
模拟Junit测试的@Test 案例
上一篇: 注解入门一