本节知识常常体验在框架中
1.反射
Java中的Class类用于代表一个类的字节码,它提供加载类字节码的方法:Class.forName()
其他方法:
类名.class
对象.getClass()
b)解剖类
getConstructor,getMethod,getFiled //public成员
getDeclaredConstructor... //所有成员
注:私有成员通过setAccessible(true)设置它为public
会将参数列表的数组,进行拆分,然后传递给方法的对应参数
1.5出现可变参数,method.nvoke(Object obj, Object... args),为了兼容1.4,继续拆分。
//反射数组参数的Msg方法
method=clazz.getMethod("Msg", String[].class);
method.invoke(p, new String[]{"aa","bb"}); //异常
原因:虽然1.5是可变参数,表面上直接去调用Msg(String[] arr),但兼容1.4,继续拆分
String[]可看作Object[],拆分后会查找Msg(String s1,String s2)的方法,该方法不存在,异常
解决:
method.invoke(p,(Object)new String[]{"aaa","bbb"}); //方法1,Object仅仅是一个对象,拆成一个String[]
method.invoke(p,new Object[]{new String[]{"aaa","bbb"}}); //方法2
e)字段
假设Person类,包含字段String name
Person p=new Person();
Field f=clazz.getField("name");
String name=(String)f.get(p); //1.直接获取某个对象的字段值
Class type=f.getType(); //2.安全获取字段值
Object value=f.get(p);
if(type.equals(String.class))
{
String n=(String)f.get(p);
}
2.JavaBean
本身是一种java类(封装数据),它的属性根据get或set来决定,格式:
a)必须有无参构造函数
b)字段必须私有
c)提供标准的getter或setter
3.内省(Introspector)
利用反射,可以对JavaBean处理(换句话说,反射来处理类),但是过于麻烦,sun公司开发一套API,专门用于操作对象的属性。
内省提供了对JavaBean类属性、事件的一种缺省处理方法。通过getter、setter访问器,这是默认规则,通过内省API不需要了解规则来访问访问器
4.BeanUtils框架,类似与内省,但提供更加方便的操作JavaBean
step1:在项目中新建lib文件夹,存放主要存放jar包,添加commons-logging.jar和commons-beanutils.jar.
step2:选中jar包,右键add Build Path,添加引用
1.反射
加载类,并解剖出类的各个组成部分(成员变量、方法、构造方法等)。框架中经常用到,如框架中通过设置配置文件,通过反射来加载对应的类。
Java中的Class类用于代表一个类的字节码,它提供加载类字节码的方法:Class.forName()
其他方法:
类名.class
对象.getClass()
b)解剖类
getConstructor,getMethod,getFiled //public成员
getDeclaredConstructor... //所有成员
注:私有成员通过setAccessible(true)设置它为public
c)例子
package reflect;
import java.util.List;
public class Person {
public String name = "aaa";
public Person() {
System.out.println("person");
}
public Person(String name) {
System.out.println("person name");
}
private Person(List list){
System.out.println("list");
}
public void Msg(){
System.out.println("hello");
}
public void Msg(String msg){
System.out.println(msg);
}
public void Msg(String[] msg){
System.out.println(msg);
}
}
package reflect;
import java.util.List;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.junit.Test;
public class Demo {
// 反射构造函数:public Person()
@Test
public void Test1() throws Exception {
Class clazz = Class.forName("reflect.Person");
Constructor c = clazz.getConstructor(null);
Person p = (Person) c.newInstance(null);
}
// 反射构造函数:public Person(String name)
@Test
public void Test2() throws Exception {
Class clazz = Class.forName("reflect.Person");
Constructor c = clazz.getConstructor(String.class);
Person p = (Person) c.newInstance("aaa");
}
// 反射构造函数:private Person(List list)
@Test
public void Test3() throws Exception {
Class clazz = Class.forName("reflect.Person");
Constructor c = clazz.getDeclaredConstructor(List.class); //获取私有
c.setAccessible(true); //设置为可访问
Person p = (Person) c.newInstance(new ArrayList());
}
另一种方式,只能创建无参构造函数的对象
@Test
public void Test4() throws Exception {
Class clazz = Class.forName("reflect.Person");
Person p=(Person)clazz.newInstance();
}
//反射调用方法
@Test
public void Test5() throws Exception{
Person p=new Person();
Class clazz = Class.forName("reflect.Person");
//无参Msg方法
Method method=clazz.getMethod("Msg", null);
method.invoke(p, null);
//数组参数的Msg方法,1.5兼容1.4
method=clazz.getMethod("Msg", String[].class);
method.invoke(p, new Object[]{new String[]{"haha"}});
}
}
d)method.invoke在1.4与1.5中的差异
1.4中,method.invoke(Object o,Object obj[]),第一个参数为对象,第二个为参数列表,如会将参数列表的数组,进行拆分,然后传递给方法的对应参数
1.5出现可变参数,method.nvoke(Object obj, Object... args),为了兼容1.4,继续拆分。
//反射数组参数的Msg方法
method=clazz.getMethod("Msg", String[].class);
method.invoke(p, new String[]{"aa","bb"}); //异常
原因:虽然1.5是可变参数,表面上直接去调用Msg(String[] arr),但兼容1.4,继续拆分
String[]可看作Object[],拆分后会查找Msg(String s1,String s2)的方法,该方法不存在,异常
解决:
method.invoke(p,(Object)new String[]{"aaa","bbb"}); //方法1,Object仅仅是一个对象,拆成一个String[]
method.invoke(p,new Object[]{new String[]{"aaa","bbb"}}); //方法2
e)字段
假设Person类,包含字段String name
Person p=new Person();
Field f=clazz.getField("name");
String name=(String)f.get(p); //1.直接获取某个对象的字段值
Class type=f.getType(); //2.安全获取字段值
Object value=f.get(p);
if(type.equals(String.class))
{
String n=(String)f.get(p);
}
2.JavaBean
本身是一种java类(封装数据),它的属性根据get或set来决定,格式:
a)必须有无参构造函数
b)字段必须私有
c)提供标准的getter或setter
public class Animal {
private String name;
private int age;
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;
}
}
d)其实get或set开头的方法,称为类的属性
3.内省(Introspector)
利用反射,可以对JavaBean处理(换句话说,反射来处理类),但是过于麻烦,sun公司开发一套API,专门用于操作对象的属性。
内省提供了对JavaBean类属性、事件的一种缺省处理方法。通过getter、setter访问器,这是默认规则,通过内省API不需要了解规则来访问访问器
public class Person {
private String Id;
private String FirstName;
public void setId(String id) {
this.Id = id;
}
public String getId() {
return this.Id;
}
public void setFirstName(String firstName) {
this.FirstName = firstName;
}
public String getFirstName() {
return this.FirstName;
}
}
public void Test() throws Exception{
//获取Bean信息
BeanInfo info=Introspector.getBeanInfo(Person.class);
//获取Bean所有属性
PropertyDescriptor[] pds=info.getPropertyDescriptors();
for(PropertyDescriptor p : pds){
System.out.println(p.getName());
}
//操作id属性
Person person=new Person();
PropertyDescriptor pd=new PropertyDescriptor("Id",Person.class);
Method method=pd.getWriteMethod();
method.invoke(person, "N001");
System.out.println(person.getId());
//得到属性类型
System.out.println(pd.getPropertyType());
}
4.BeanUtils框架,类似与内省,但提供更加方便的操作JavaBean
step1:在项目中新建lib文件夹,存放主要存放jar包,添加commons-logging.jar和commons-beanutils.jar.
step2:选中jar包,右键add Build Path,添加引用
Person.java文件
import java.util.Date;
public class Person {
private String name;
private int age;
private Date birthday;
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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
Demo1.java文件
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.junit.Test;
public class Demo1 {
//基本用法
@Test
public void Test1() throws IllegalAccessException, InvocationTargetException{
Person p=new Person();
BeanUtils.setProperty(p, "name", "zhangsan");
System.out.println(p.getName());
}
//注册日期转换器
@Test
public void Test2() throws IllegalAccessException, InvocationTargetException
{
String name="zhangsan";
String age="20";
String birthday="1989-01-01";
//对beanUtils注册一个日期转换器
ConvertUtils.register(new Converter(){
public Object convert(Class type, Object value) {
if(value==null)
{
return null;
}
if(!(value instanceof String))
{
throw new ConversionException("只支持String类型转换");
}
String str=(String)value;
if(str.trim().equals(""))
{
return null;
}
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
try
{
return df.parse(str);
}
catch(ParseException e){
throw new RuntimeException(e);
}
}
}, Date.class);
Person p=new Person();
BeanUtils.setProperty(p, "name", name);
BeanUtils.setProperty(p, "age", age); //只支持8种基本数据转换
BeanUtils.setProperty(p, "birthday", birthday);//使用日期转换器
System.out.println(p.getName());
System.out.println(p.getAge());
System.out.println(p.getBirthday().toString());
}
}