java反射转换类型,Java反射——内省(Introspector)以及BeanUtils内省框架

为什么要学内省?

开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以JDK里提供了一套API,专门用于操作java对象的属性。既然内省是专门用于操作java对象属性的,那首先得搞懂什么是对象的属性

1、什么是java对象的属性呢?

说到属性,大家觉得很熟悉,属性不就是类里最上边的那些全局变量吗?

private String name;

private int age;

这种不都是属性吗?

其实,这是不对的!

刚才说的 private String name;private int age; 准确的来说它们应该称之为:字段,而不是咱们所说的属性

那什么才是属性?

Java中的属性是指:设置和读取字段的方法。

就是咱们平常见到的set和get方法

只要是set和get开头的方法在java里都认为它是属性(请注意这句话,等下后边会写代码做验证)

属性名称就是set和get方法名 去掉"set"和"get"后的内容

比如:

public void setName(String name) { //属性名称:name,是方法“”setName”去掉“set”

this.name = name;

}

当然setName( );和getName( )是同一个属性,

所以,咱们平常说的类里的全局变量不是属性,正确的来说,它应该是字段,只不过咱们平常set和get方法写的名字和字段保持一致,所以导致大家把字段和属性认为是同一个东西

所以说白了,其实内省就是操作set和get方法的

那怎么才能得到类中的属性并去操作它呢?现在要讲的内省(Introspector)就是做这个的

我们看下JDK的API文档里的 Introspector 这个类

a05a86b85765ccdcb75de609d36e28aa.png

ece7d920b81902bafda189f99b6c0b4a.png

写代码验证一下

package com.cj.study.introspector;

import java.util.Date;

public class Student {

private String name = "张三";//这是字段

private int age;//这是字段

private Date birthday;

public String getName() {//这才是属性,属性指的是设置setter和读取getter字段的方法

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public Date getBirthday() {

return birthday;

}

public void setBirthday(Date birthday) {

this.birthday = birthday;

}

//虽然上边的字段里没定义abc这个字段

//但这也是属性:属性名称是abc,注意:只要是set或者get开头的方法都叫属性

public String getAbc(){

return "abc";

}

}

package com.cj.study.introspector;

import java.beans.BeanInfo;

import java.beans.Introspector;

import java.beans.PropertyDescriptor;

import org.junit.Test;

内省:操作属性的(类中的getter和setter方法)

public class Demo1 {

//属性名称:getClass,他的属性名称class

//getAbc --->abc

@Test

public void test1() throws Exception{

//得到Student类中的属性,被封装到了BeanInfo中

BeanInfo bi = Introspector.getBeanInfo(Student.class);

//得到类中的所有的属性描述器

PropertyDescriptor[] pds = bi.getPropertyDescriptors();

System.out.println("属性的个数:"+pds.length);

for(PropertyDescriptor pd:pds){

System.out.println("属性:"+pd.getName());

}

}

}

034a80251c20b88cf386995801bc6d5f.png

从运行结果上来看,一共得到了5个属性,除了name,age,birthday 外还打印出了abc

上边的代码验证了咱们刚才说的:“属性其实是set、get方法”

但是name,age,birthday再加上abc应该是4个才对,那为什么会打印出5个呢?

原因很简单,因为Object类是所有类的父类,Object类里有个方法叫 getClass();

所以这也验证了咱们刚才说的: “只要是set或者get开头的方法都叫属性”。

2、使用内省操作属性

刚才的代码里用到了PropertyDescriptor 这个类

PropertyDescriptor顾名思义,就是属性描述之意。

它通过反射 快速操作JavaBean的getter/setter方法。 也就是说它底层也是反射去操作set和get方法,只不过它给咱们封装了,用起来更方便

PropertyDescriptor中重要的方法:

(1).写方法:getWriteMethod() – 对应set方法,它的返回值是Method对像

(2).读方法:getReadMethod() – 对应get方法,它的返回值是Method对像

代码操作一下

package com.cj.study.introspector;

import java.beans.BeanInfo;

import java.beans.Introspector;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

import org.junit.Test;

内省:操作属性的(类中的getter和setter方法)

public class Demo1 {

@Test

public void test2() throws Exception{

//Student s = new Student();

//利用反射生成对象

//com.cj.study.introspector.Student该参数可以配置到配置文件里,这才是我们想要的

//是不是很熟悉?很多框架都是这么做的

Class clazz = Class.forName("com.cj.study.introspector.Student");

Student s = (Student)clazz.newInstance();

PropertyDescriptor pd = new PropertyDescriptor("name", Student.class);

Method m = pd.getReadMethod();//得到getName()方法

String value = (String)m.invoke(s, null);//调用getName()方法

System.out.println("调用get方法得到name的值:"+value);

//改变name的值

Method m1 = pd.getWriteMethod();//得到setName()方法

m1.invoke(s, "李四");//调用setName()方法去修改name的值

System.out.println("调用set方法改变name的值:"+s.getName());

}

}

最后的执行结果:

3d8a4074238cf702690f3e5f6326718e.png

这就是Java JDK里提供的内省功能(其实就是操作JavaBean里的set和get方法)

但是JDK里提供的内省还不够简单,于是乎,apache出了一套更为简单的内省框架 —— BeanUtils

3、BeanUtils内省操作

3.1BeanUtils操作属性

操作之前,首先需要导入BeanUtils的jar包,以及它以来的jar包

commons-beanutils-1.8.3.jar

commons-logging-1.1.1.jar

导入jar包后,可以看到BeanUtils里有两个重要的方法:

(1).BeanUtils.getProperty(s, "name");//调用getName方法

(2).BeanUtils.setProperty(s, "name", "王五");//调用setName方法

代码操作一下

package com.cj.study.introspector;

import java.beans.BeanInfo;

import java.beans.Introspector;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

import org.apache.commons.beanutils.BeanUtils;

import org.junit.Test;

内省:操作属性的(类中的getter和setter方法)

public class Demo1 {

//利用BeanUtils框架操作属性:实现原理类似test2

@Test

public void test3() throws Exception{

Student s = new Student();

//为什么要返回字符串:用户的所有输入都是字符串

String str = BeanUtils.getProperty(s, "name");//调用getName方法

System.out.println(str);

//设置值

BeanUtils.setProperty(s, "name", "王五");

System.out.println(s.getName());

}

}

最后输出的结果

47b4d93451f2b006ec28916f43ee7107.png

可以看到用BeanUtils操作更加的简单了

正如大家看到的一样,很多的框架都用到了BeanUtils这个jar包

关于框架中怎么使用BeanUtils,我之前写过一篇手写代码模拟Struts2框架的文章,那里用到了BeanUtils

感兴趣的朋友可以看一下

3.2BeanUtils类型转换的问题

有个问题需要注意:BeanUtils可以进行类型的自动转换,但仅限基本类型

比如说本来需要int型,给个字符串 “28”,是可以的

但是仅限基本数据类型,像Date 这种的就不行,会报错,下边用代码体现一下:

基本数据类型自动转换

//基本数据类型自动转换

@Test

public void test4() throws Exception{

Student s = new Student();

String str = BeanUtils.getProperty(s, "age");

System.out.println(str);

BeanUtils.setProperty(s, "age", "19");

System.out.println(s.getAge());

}

59e213ab9ba00d087fd8b4e1ebbaf0e3.png

发现OK

非基本数据类型

//非基本数据类型

@Test

public void test5() throws Exception{

Student s = new Student();

String str = BeanUtils.getProperty(s, "birthday");

System.out.println(str);

BeanUtils.setProperty(s, "birthday", "1989-10-09");

System.out.println(s.getBirthday());

}

44a1c5304b2b840deb2e6cc067da2b0e.png

发现非基本数据没取到,而且有错误提示

所以这里涉及到了BeanUtils里的String和其他类型间的互相转换的问题

要想解决这个问题,需要给BeanUtils注册一个类型转换器

代码实现一下

//非基本类型的属性设置

//用户的输入都是String

//String 其他类型间的互相转换

//用户看到的结果都是String

@Test

public void test6() throws Exception{

Student s = new Student();

//给BeanUtils注册类型转换器:自定义的转换器

ConvertUtils.register(new Converter() {

//type:目标类型

//value:当前传入的值

public Object convert(Class type, Object value) {

//if(type.equals(Date.class)){

字符串转换为Date

//}else{

Date转换为字符串

//}

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

if(value instanceof String){

//字符串转换为Date

String v = (String)value;

Date d;

try {

d = df.parse(v);

} catch (ParseException e) {

throw new RuntimeException(e);

}

return d;

}else{

//Date转换为字符串

Date d = (Date)value;

return df.format(d);

}

}

}, Date.class);

BeanUtils.setProperty(s, "birthday", "1989-10-09");

System.out.println(s.getBirthday());

}

5a6a7d4fcb3cc13491c1cb3b8630d440.png

发现问题解决了。

但是这么手动的去写一个类型转换器,是不是太麻烦了,所以BeanUtils提供了Converter接口很多的实现类

其中有一个DateLocaleConverter类

所以上边的代码可以直接用DateLocaleConverter

@Test//转换器原理参考test6

public void test7() throws Exception{

Student s = new Student();

ConvertUtils.register(new DateLocaleConverter(), Date.class);

BeanUtils.setProperty(s, "birthday", "1999-10-09");

System.out.println(s.getBirthday());

}

运行结果

8b1736bd19d0906fa0cf3213f86a7ca5.png

发现用了它提供的DateLocaleConverter类后变得很简单,DateLocaleConverter实现的功能就是咱们test6里实现的

它的内部实现,其实和咱们test6里的原理一样。

好了,关于Java的内省,就介绍到这,欢迎大家留言,一起讨论,学习,一起进步

标签:Java,name,内省,String,Student,Introspector,BeanUtils,public,属性

来源: https://blog.csdn.net/ju_362204801/article/details/90672396

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值