Java所有框架最核心的技术就是反射支持,本文将分析反射机制之中的所有的组成单元问题,并利用反射进行类的使用。
1.反射源头 —— Class类
java.lang.Class类是进行反射操作的源头所在,所有的反射操作都是通过这个类展开的。
Class类的结构:
类的声明:
提示:Class类不能有子类,并且在JDK1.5之后引入了泛型技术。此类在JDK1.0就提供了,并且这个类的构造方法不能被直接使用。
如果要取得Java类的实例对象有以下三个方法:
一、利用Object类之中的getClass()方法:
提示:所有类的实例化对象都可以调用此方法。
用法示例:
二、利用类.class()的形式取得反射的实例化对象:
示例:
Class<?> cls = java.util.Date.class;
提示:框架中会使用到此操作形式。
以上的两种操作都有一个本质的相同点:类必须首先存在,而后才可以进行反射的调用。
三、利用Class类提供的方法实例化对象
方法:
提示:用此方法出异常需要抛出ClassNotFoundException异常。
示例:
利用forName()方法进行Class类对象取得,其最大的特征是可以在类不存在的时候保存程序编译的准确性。
2.利用反射实例化对象
实例化对象最直观的做法就是new一个。但是从框架的角度去想,new是不能出现或者出现少数的。
利用new操作实例化对象:创建一个类,然后通过new实例化对象并调用类中的方法。
反射操作:
利用Class类中提供的方法:
实例化对象:
示例:
1.创建一个类,类中有toString()方法,方法中有一行输出。
2.通过forName()取得对象,接着用newInstance()方法实例化对象;
Class<?> cls = Class.forName("org.test");
object obj = cls.newInstance();
以上的操作方法都是为了解决一个问题:避免耦合操作。
示例:传统形式实现的工厂设计模式。
在开发之中如果是由你自己定义的接口,要取得实例化对象不能直接采用关键字new利用对象的向上转型为接口实例化,而是采用工厂设计模式取得。
以上是简单工厂模式,如果需要扩充子类呢?很明显每次增加子类就要相应的去修改工厂类,在代码量大的情况下,这种操作很致命。所有能采用自动处理的方式将能大大减少我们的工作。
利用反射:
解释下Fruit fruit = null; 为了解决包.类名称(传递的是一个字符串)不存在出现错误的情况。
此时好有另一个好处,即使类不存在,代码也不会出现错误。
3.取得类的继承结构
Class类能解剖一个类,一般类里面定义有如下:
类中所在的包:
类的声明方式:
一、返回包名称
二、取得类名称
类所继承的父类:
类实现的接口:
一、取得所有父接口:
4.取得构造
大部分的情况下在进行任何的开发之中是不会考虑使用构造方法进行参数传递的,因为在进行类的标准设计的时候,会明确的给出一个要求,类中必须提供有无参构造方法,所以这样的情况下,反射的意义就不大了。
一、取得全部构造
二、取得指定构造
以上两个方法都是取得java.lang.reflect.Constructor<T>类的对象,而这个类定义了如下常见的方法:
【一】取得构造方法名称:
【二】取得构造方法修饰符:
对于方法修饰符都是利用数字完成的,需要转化为字符串,此方法为:
【三】取得构造方法参数类型:
【四】取得构造方法上抛出异常的类型:
【五】实例化对象:
任何开发类的设计中不可能一会考虑有参构造,一会考虑无参构造,所以简单Java类必须要提供无参构造。这样调用的时候就比较简单。
5.取得方法
几乎所有的开发之中,都是针对于方法的操作。在反射机制里面,普通方法可以保存对象状态的情况下进行各种操作。所以在反射的操作过程之中,必须掌握方法的调用问题。
在Class类里面提供有如下的几个于方法有关的操作。
一、取得全部方法:
二、取得指定方法:
以上两个方法返回的都是java.lang.reflect.Method,在Method类中定义如下方法:
【一】、取得方法名字:
【二】、取得方法的参数类型:
【三】、调用指定的方法:
任何的方法调用都必须要有实例化对象,而后在设置参数。
【四】、返回值类型:
下面对invoke()方法进行分析:
一、直观用new实例化对象,明确指定了一个类的对象。
Apple apple = new Apple();
apple.setName("红苹果");
二、反射调用
一个类必须在有一个实例化对象的情况下才能调用类的普通方法。
Class<?> cls = Class.forName("org.test.Dept");
//实例化对象
Object obj = cls.newInstance();
//setDName(),有参数
Method metA = cls.getMethod("setDName", String.class);
//getDname()
Method metB = cls.getMethod("getDname");
//setter方法没有返回值
metA.invoke(obj,"测试部");
//有返回值
System.out.println(metB.invoke(obj));
提示:在定义setter类方法的Method对象时,参数的类型还不能自动匹配。
6.取得成员
成员在类中也称为属性,而在Class类里面针对于成员的取得也提供有如下的方法:
一、取得全部的成员:
【1】获得本类声明的成员:
【2】获得继承的声明成员:
二、取得指定名称的成员:
【1】获得本类指定的成员:
【2】获得继承的声明成员;
java.lang.reflect.Field是表示成员的类型,而在这个类里面定义有如下几个方法:
一、取得属性内容:
二、取得属性类型:
三、设置属性内容:
在实际开发中,Filed类可以做以下两件事情:
【一】可以取得指定成员的类型:
【二】可以直接操作属性,完全避免Method的操作。不过直接操作属性在开发是不允许出现的,而且所有的属性是使用了private封装的,
但是,这种封装是可以解除的:(Filed,Method,Constructor的父类)
该方法设置为true则会取消封装。默认是false。不过这种操作不标准,所有的设置属性和取得属性还是使用setter、getter方法就行了。
Filed最大的方便就是能让我们取得属性类型。
实战:
解决属性设置问题:
setter、getter、toString略
public class ClassDemo {
public static void main(String[] args) throws Exception{
String str = "deptno:10|dname:测试部|credate:1999-4-12|costs:800.0";
Class<?> cls = Class.forName("org.test.Dept");
Object obj = cls.newInstance();//属性必须有对象产生后才可以操作
String result[] = str.split("\\|");//首先要进行字符串的拆分
for(int x = 0 ; x < result.length ; x ++) {
String temp[] = result[x].split(":");
Field filed = cls.getDeclaredField(temp[0]);//取得自定类型
Method setMet = cls.getDeclaredMethod("set" + StringUtils.initcap(temp[0]),filed.getType());
if("string".equalsIgnoreCase(filed.getType().getSimpleName())) {
setMet.invoke(obj, temp[1]);
}else if("int".equalsIgnoreCase(filed.getType().getSimpleName()) || "integer".equalsIgnoreCase(filed.getType().getSimpleName())){
if(temp[1].matches("\\d+")) {
setMet.invoke(obj, Integer.parseInt(temp[1]));
}
}else if("double".equalsIgnoreCase(filed.getType().getSimpleName())){
if(temp[1].matches("\\d+(\\.\\d{1,2})?")){
setMet.invoke(obj, Double.parseDouble(temp[1]));
}
}else if("date".equalsIgnoreCase(filed.getType().getSimpleName())){
if(temp[1].matches("\\d{4}-\\d{2}-\\d{2}")){
setMet.invoke(obj, new SimpleDateFormat("yyyy-MM-dd").parse(temp[1]));
}
}
}
System.out.println(obj);
}
}
public class StringUtils {
public static String initcap(String str) {
if(str == null || "".equals(str)) { //保证操作的数据有内容
return str;
}
if(str.length() == 1){//只有一位数据
return str.toUpperCase();//全部变大写
}else{ //长度很大
return str.substring(0,1).toUpperCase() + str.substring(1);
}
}
}
此时能够自动根据数据的类型自动设置属性的内容。此时在定义setter类方法的Method对象时,参数的类型能够自动进行匹配了。