type="org.apache.catalina.UserDatabase"
description="Userdatabasethatcanbeupdatedandsaved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"类
pathname="conf/tomcat-users.xml"/>
2.将Class对象中的属性进行反射为Filed对象
常用的获取属性字段的方法
方法
描述
Field[]getDeclaredFields()
获取所有声明的字段数组对象
Field[]getFields()
获取所有的声明的共有字段数组对象
FieldgetDeclaredField(Stringname)
获取指定名称的声明的字段对象
FieldgetField(Stringname)
获取指定名称的声明的共有字段对象
举例1:
获取所有的声明字段数组对象。
publicstaticvoidmain(String[]args)throwsException{
//获取Shape类的Class对象
Classclazz=Class.forName("cn.sve.bean.Shape");
//获取所有的属性
Field[]fs=clazz.getDeclaredFields();
System.out.println(fs.length);//2
//获取所有共有的属性
fs=clazz.getFields();
System.out.println(fs.length);//1
//获取指定名字的私有的属性
Fieldfield=clazz.getDeclaredField("x");
System.out.println(field);
//获取指定名字的共有的属性
field=clazz.getField("y");
System.out.println(field);
}
举例2:
调用Field类的方法进行字段对象的操作。
publicstaticvoidmain(String[]args)throwsException{
//获取Shape类的Class对象
Classclazz=Class.forName("cn.sve.bean.Shape");
//获取所有的属性
Field[]fs=clazz.getDeclaredFields();
System.out.println(fs.length);//2
//获取所有共有的属性
fs=clazz.getFields();
System.out.println(fs.length);//1
Shapeshape=newShape();//20
//获取指定名字的私有的属性
Fieldfield=clazz.getDeclaredField("x");
System.out.println(field);
//操作私有的属性x
System.out.println("属性名:
"+field.getName());
//获取x的属性值,需要暴力的反射
field.setAccessible(true);
System.out.println("设置之前的x值:
"+field.get(shape));
//设置x的属性值
field.set(shape,20);
System.out.println("设置之后的x值:
"+field.get(shape));
//获取指定名字的共有的属性
field=clazz.getField("y");
System.out.println(field);
//给属性y进行设置值
System.out.println("属性名:
"+field.getName());
//获取设置属性值之前的值
System.out.println("设置之前的y值:
"+field.get(shape));//20
field.set(shape,30);//30
System.out.println("设置之后的y值:
"+shape.y);//30
}
以上的代码可以通过程序进行反射类中的属性字段并操作,但是没有进行静态属性的反射和操作。
举例3:
反射静态的成员属性。
publicstaticvoidmain(String[]args)throwsException{
//获取Shape类的Class对象
Classclazz=Class.forName("cn.sve.bean.Shape");
//获取共有的静态属性
Fieldfield=clazz.getField("z");
System.out.println("设置之前的z值:
"+field.get(null));
field.set(null,40);
System.out.println("设置之后的z值:
"+field.get(null));
}
3.将Class对象中的方法进行反射为Method对象。
publicstaticvoidmain(String[]args)throwsException{
//获取Shape类的Class对象
Classclazz=Class.forName("cn.sve.bean.Shape");
//获取所有的声明的方法
Method[]ths=clazz.getDeclaredMethods();
System.out.println(ths.length);//2
//获取私有的带参数的sayHello方法
MethodsayHello=clazz.getDeclaredMethod("sayHello",String.class);
System.out.println(sayHello);
//调用私有的方法
sayHello.setAccessible(true);
sayHello.invoke(newShape(),"zhangsan");
//获取所有的共有的方法
ths=clazz.getMethods();
System.out.println(ths.length);//10
//获取带参数的共有的方法
Methodgreet=clazz.getDeclaredMethod("greet",String.class);
System.out.println(greet);
//方法的调用
greet.invoke(newShape(),"zhangsan");
}
4.将Class对象中的构造函数进行反射为Constructor对象。
publicstaticvoidmain(String[]args)throwsException{
//获取Shape类的Class对象
Classclazz=Class.forName("cn.sve.bean.Shape");
//获取所有的声明的构造函数
Constructor[]cons=clazz.getDeclaredConstructors();
System.out.println(cons.length);//3
//获取带参数的私有的构造函数对象
Constructorcon=
clazz.getDeclaredConstructor(int.class,int.class);
System.out.println(con);
//暴力反射私有的构造函数创建对象
con.setAccessible(true);
Shapemyshape=(Shape)con.newInstance(400,500);
System.out.println(myshape.getX()+","+myshape.y);
//获取所有的共有的构造函数
cons=clazz.getConstructors();
System.out.println(cons.length);//2
con=clazz.getConstructor(int.class);
System.out.println(con);
//调用构造函数创建对象
Shapeshape=(Shape)con.newInstance(100);
System.out.println(shape.getX());
}
面试题:
请简单的叙述出你所可以创建的对象的几种方式?
第一种:
直接使用new关键字
第二种:
Construnctor.newInstance
第三种:
枚举
第四种:
单例、工厂模式
◆内省(Introspect)
其实在以上的反射技术体验中我们发现其实反射的对象一般是一个具有特定功能的一个类。
引入一个基本的概念:
JavaBean
如果一个类提供了封装好的属性、构造函数(无参数)、共有的get和set方法以及简单的业务逻辑方法那么将这样的一个类称之为JavaBean类。
对于一个javaBean的操作无非就是给属性值进行操作或函数的调用。
使用反射比较繁琐,那么SUN就提供了内省的技术方便大家进行JavaBean类的操作。
类
描述
BeanInfo
对JavaBean进行描述的接口
Introspector
描述所有的JavaBean的成员类
PropertyDescriptor
描述的是JavaBean的属性类
举例1:
使用属性描述器类操作JavaBean属性。
创建一个Book的javabean类
publicclassBook{
privateStringname;//null
privateStringauthor;//null
privatedoubleprice;//0.0
privateDatedate;//null
publicBook(){
super();
}
publicBook(Stringname,Stringauthor,doubleprice,Datedate){
super();
this.name=name;
this.author=author;
this.price=price;
this.date=date;
}
//省略get和set方法
}
使用内省技术进行简单的属性的操作。
publicstaticvoidmain(String[]args)throwsException{
//获取一个属性的描述器对象就相当于获取了属性的名、set和get方法
PropertyDescriptorpd=new
PropertyDescriptor("name",Book.class);
//获取set方法
Methodset=pd.getWriteMethod();
//调用该方法设置属性的值
Bookbook=newBook();
System.out.println("设置前获取name属性值:
"+book.getName());
set.invoke(book,"JavaSE进阶");
System.out.println("设置后获取name属性值:
"+book.getName());
//获取get方法
Methodget=pd.getReadMethod();
System.out.println(get.invoke(book,null));
}
以上的代码每次都只能操作一个属性,这样就比较繁琐。
可以使用其他的类直接获取所有的属性描述器通过循环来直接操作。
publicstaticvoidmain(String[]args)throwsException{
//获取指定的BeanInfo对象
BeanInfoinfo=Introspector.getBeanInfo(Book.class);
//获取Book类中的所有的属性的描述器对象
PropertyDescriptor[]pds=info.getPropertyDescriptors();
//输出长度
System.out.println(pds.length);
//查看数组的第一个属性描述器是谁
PropertyDescriptorpd=pds[0];
//作者
System.out.println(pd.getName());
Bookbook=newBook();
//给书设置作者信息
pd.getWriteMethod().invoke(book,"zhangsan");
System.out.println(pd.getReadMethod().invoke(book,null));
}
总结:
其实发现在使用内省进行属性操作的时候要结合反射一起使用。
面试题:
一个JavaBean中为什么必须要提供一个无参数的构造函数?
原因一:
为了可以做父类。
原因二:
为了可以使用反射创建对象。
◆BeanUtils工具
在实际的开发中我们经常需要将用户的录入的数据进行封装为对象,那么如果使用反射和内省技术就会变得吃力。
因此本节主要给大家讲解一个开源的操作JavaBean的一个工具即BeanUtils。
下载:
http:
//www.apache.org
beanutils-1.8.0.zip
commons-logging.jar
包的引入:
在项目中创建一个文件夹如libs,然后将整个项目需要的第三方的jar包可以直接拷贝带该目录,随后打开该目录全选右键Buildpathaddpath看到奶瓶子即可
举例1:
使用BeanUtils工具封装用户提交的数据。
publicstaticvoidmain(String[]args)throwsException{
//模拟用户的输入的数据如下
Stringname="XML基础";
Stringauthor="zhangsan";
Stringprice="99.99";
Stringdate="2016-07-07";
Bookbook=newBook();
//任务是将以上的属性设置给指定的Book对象
BeanUtils.setProperty(book,"name",name);
BeanUtils.setProperty(book,"author",author);
BeanUtils.setProperty(book,"price",price);
//查看属性是否封装好
System.out.println(book);
}
发现使用上面的代码可以省略基本数据类型的转型的问题。
进而提高代码的开发效率。
举例2:
自定义一个类型转换器类。
publicstaticvoidmain(String[]args)throwsException{
//模拟用户的输入的数据如下
Stringname="XML基础";
Stringauthor="zhangsan";
Stringprice="99.99";
Stringdate="2016-07-07";
Bookbook=newBook();
//注册一个自己的转换器
/**
*converter指定具体的转换器
*clazz遇到什么类型调用上面的转换器
*/
ConvertUtils.register(
newConverter(){
//回调方法
@Override
publicObjectconvert(Classtype,Objectvalue){
if(value==null){
returnnull;
}
//转换为String
Stringdata=(String)value;
//将指定格式的字符串转换为Date
SimpleDateFormatformat=newSimpleDateFormat("yyyy-MM-dd");
Datedate=null;
try{
date=format.parse(data);
returndate;
}catch(ParseExceptione){
e.printStackTrace();
returnnull;
}
}
},
Date.class);
//任务是将以上的属性设置给指定的Book对象
BeanUtils.setProperty(book,"name",name);
BeanUtils.setProperty(book,"author",author);
BeanUtils.setProperty(book,"price",price);
BeanUtils.setProperty(book,"date",date);
//查看属性是否封装好
System.out.println(book);
}
如果每次遇到一个复杂类型都需要自定义转换器,那样的话实在麻烦。
大家看在开发的时候可以先查看该接口是否提供了有效的实现类。
ConvertUtils.register(newDateLocaleConverter(),Date.class);
其实真正的封装好的数据需要存储在数据库中,那么javabean的数据类型应该和数据库的数据类型保持一致,那么在声明持久化javabean的时候需要全部为数据库的基本数据类型。
因此大家在JavaBean中需要导入的是java.sql.Date类,这样就直接可以将日期自动转换了。
举例3:
实现封装好的JavaBean对象的属性拷贝。
//实现属性封装数据的一个拷贝
Bookcopy=newBook();
System.out.println(copy);
PropertyUtils.copyProperties(copy,book);
System.out.println(copy);
思考:
如果使用BeanUtils封装用户的数据,那么也就是一个一个设置啊?
岂不是也很麻烦?
其实在真是的环境中我们可以直接获取用户提交的所有的数据信息,只需要进行遍历即可,但是为了方便快速的设置,那么可以将javabean中的属性名和用户提交的数据名保持一致。
框架体验
其实所谓的框架就是通过一些配置文件来将需要运行的模块以及类、方法在软件启动的时候自动运行。
如果将需要运行类以及模块配置在文件中那么便于后期的一个维护。
1.创建一个配置文件如下
run=cn.sve.service.UserService
me=autoRun
value=jack,lucy
2.创建两个实现接口的服务类
UserService.java
publicclassUserServiceimplementsService{
//提供自动运行的方法
publicvoidautoRun(Stringnames){
//使用,号切割用户列表
String[]ns=names.split(",");
//遍历
for(Stringname:
ns){
System.out.println("姓名:
"+name);
}
}
}
StartService.java
publicclassStartServiceimplementsService{
//提供自动运行的方法
publicvoidautoRun(Stringnames){
//使用,号切割用户列表
String[]ns=names.split(",");
//遍历
f