反射:Class类:就是把java类中的各种成分映射成相应的java类。
Class描述的是java程序中生成的class类的类,就像我们描述一个人有年龄、性别等。Class就是描述class类文件的类
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?这就是Class类。
如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date");
通过反射可以做什么:得到字节码文件后通过对象我们可以获得类中的变量,方法,构造方法,以及修饰符,包等信息。
Constructor类:代表某 个类中的一个构造方法。
得到某个类所有的构造方法:Constructor[] constructors=Class forName("java lang String").getConstructors();
得到某一个构造方法:Constructor constructors=Class forName("java lang String").getConstructor(int.class); 这里要注意获得一个方法区别是参数类型
有了构造方法就可以创建一个对象:new instance(); 例如:String obj = (String)Class.forName("java.lang.String").newInstance(传参数);
9个预定义的Class对象:8个基本类型的和一个void的Class。
Field类:得到成员变量的类
要反射得到类的成员变量的值要关联到某一个对象,因为类的成员变量是可以有多个的,每个对象都有一个对应的某一变量值,所以要得到某一个变量的值,就一定要关联一个对象
public class ReflectPoint {
private int x; 对于私有的成员变量要想获得对象上的x值就需要用到暴力反射 Field fieldX = pt1.getClass().getDeclaredField("x");>>fieldX.setAccessible(true);
public int y;
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString(){
return str1 + ":" + str2 + ":" + str3;
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception {
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");
//fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
System.out.println(fieldY.get(pt1));
Field fieldX = pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
changeStringValue(pt1);
System.out.println(pt1);
}
private static void changeStringValue(Object obj) throws Exception { 将一个字符串中的某个字符换成新的字符
Field[] fields = obj.getClass().getFields();
for(Field field : fields){
//if(field.getType().equals(String.class)){ //因为都是同一份字节码,所以用等号就可以了
if(field.getType() == String.class){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
}
Method类:得到成员方法的类
得到方法也要通过对象来调用,这样才可以调用此方法。
public class ReflectTest {
public static void main(String[] args) throws Exception {
String str1 = "ball";
Method methodCharAt = String.class.getMethod("charAt", int.class); 方法名和,参数列表的类型
System.out.println(methodCharAt.invoke(str1, 1)); 调用invoke方法
System.out.println(methodCharAt.invoke(null, 1)); null意味着这个方法是静态的
System.out.println(methodCharAt.invoke(str1, new Object[]{2})); jdk1.4参数是数组
}// jdk1.4和1.5的区别:1.4需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为
}//charAt.invoke(“str”, new Object[]{1})形式。
用反射执行某个类中的main方法:
class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception { 一个参数,是string[]数组
//TestArguments.main(new String[]{"111","222","333"}); 一般的程序中调用静态方法
String startingClassName = args[0]; 假设传入的参数第一就是要执行的未知类的类名
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);这边用的是jdk1.5的方式,接收的参数是一个string类型的数组获得mainMethod方法
//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//jdk1.5为了兼容1.4的数组(Object[] obj)将new String[]{"111","222","333"}当做是
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});//一个Object[],又将Object[]数组拆开为三个string[]数组,但是main方法传递的是一个参数,这就报错
}//了,可以做成黑体部分两种方案,避免这种情况。
}
数组的反射:
相同的元素类型(这里都是int)和具有相同的维度(都是一维或者二维)才是相等的Class字节码
public class ReflectTest {
public static void main(String[] args) throws Exception {
int [] a1 = new int[]{1,2,3};
int [] a2 = new int[4];
int[][] a3 = new int[2][3];
String [] a4 = new String[]{"a","b","c"};
System.out.println(a1.getClass() == a2.getClass()); 只有这个是true
System.out.println(a1.getClass() == a4.getClass());
System.out.println(a1.getClass() == a3.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName()); 父类是java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName()); 父类是java.lang.Object,那么看下面子类指向了父类的引用
Object aObj1 = a1;
Object aObj2 = a4;
//Object[] aObj3 = a1; 这里会报错的,因为基本数据类型int不是Object 数组里面装的是int 不是object的《基本类型的数组不能转换为object数组》
Object[] aObj4 = a3; object里面装的是一维数组是object的
Object[] aObj5 = a4;
System.out.println(a1);
System.out.println(a4);
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
System.out.println(a1); //直接打印出来是hash值
System.out.println(a4); //直接打印出来是hash值
System.out.println(Arrays.asList(a1)); 以前打印数组,用数组工具类的aslist(Object obj),注意他接收的是object的,可是a1是int数组,所以它打印的是int数组对象的hash值
System.out.println(Arrays.asList(a4)); 同理,string[]穿进去就当是一个object[]对象它就直接将集合元素打出来了。
printObject(a4); 不管是int数组还是string数组都可以打印出来,用反射做
printObject("xyz");
}
private static void printObject(Object obj) { 不管是int数组还是string数组都可以打印出来,用反射做
Class clazz = obj.getClass();
if(clazz.isArray()){
int len = Array.getLength(obj);
for(int i=0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
}
反射的作用:实现框架功能
框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。框架要解决的核心问题.我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
综合案例:
先直接用new 语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成 ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异。
引入了elipse对资源文件的管理方式的讲解。
什么是框架?例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。程序中不处理异常,而是main方法声明抛出异常,便于大家可以集中看主要的关键代码。
Class类也提供getResourceAsStream方法的比喻:如果你每次都找我给你商店买可乐,那我还不如直接向你买可乐,即直接提供一个买可乐的方法给你。
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
public class ReflectTest2 {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
/*getRealPath();//金山词霸/内部 配置文件时,new一个file config.properties source填写className=java.util.ArrayList/HashSet
一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。*/
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
Collection collections = (Collection)Class.forName(className).newInstance(); //这个方法是做框架的时候常用的获取字节码的方式
//Collection collections = new HashSet();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(5,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
//pt1.y = 7;
//collections.remove(pt1);
System.out.println(collections.size());
}
}
内省:就是javaAPI文档中用于操作javabean的API文档中的一些方法的应用。比如获取或设置属性的值。javabean就是有get和set属性的一些java类,凡是具有这类方法的java类都可以叫做javabean。
javabean可当做普通类来使用,但是一个普通类不一定是一个javabean,因为这个类不一定具有get和set属性。使用javabean会更方便,因为它本身封装了一些方法和对象方便与对类的操作。
这是获取设置属性的三种方法,javabean、BeanInfo、PropertyUtils.
import java.util.Date;
public class ReflectPoint {
private Date birthday = new Date();
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils; 这两个包要导入到工程中
import org.apache.commons.beanutils.PropertyUtils;
public class IntroSpectorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
ReflectPoint pt1 = new ReflectPoint(3,5);
String propertyName = "x";
//"x"-->"X"-->"getX"-->MethodGetX-->
Object retVal = getProperty(pt1, propertyName);
System.out.println(retVal);
Object value = 7;
setProperties(pt1, propertyName, value);
System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
BeanUtils.setProperty(pt1, "x", "9");
System.out.println(pt1.getX());
/*
//java7的新特性
Map map = {name:"zxx",age:18};
BeanUtils.setProperty(map, "name", "lhm");
*/
BeanUtils.setProperty(pt1, "birthday.time", "111");
System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
PropertyUtils.setProperty(pt1, "x", 9);
System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());
}
private static void setProperties(Object pt1, String propertyName,Object value) throws IntrospectionException,IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass()); 提取到的set方法
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt1,value);
}
private static Object getProperty(Object pt1, String propertyName)throws IntrospectionException, IllegalAccessException,InvocationTargetException {
/*PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);*/
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass()); 这个是以前的老方法BeanInfo,虽然比较麻烦,但还是值得借鉴
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertyName))
{
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
}