反射
来源:互联网 发布:手机淘宝优惠券在哪里 编辑:程序博客网 时间:2024/05/01 11:58
(一)Java类
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。
Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?
这个类的名字就是Class,
类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。学习反射,首先就要明白Class这个类。
2、数组类型的Class实例对象:
Class.isArray()
String str1="abc";Class cls1=str1.getClass();Class cls2=String.class;Class cls3=Class.forName("java.lang.String");//这三个字节码在内存中对应的是同一个System.out.println(cls1==cls2);System.out.println(cls1==cls3);System.out.println(cls1.isPrimitive());//isPrimitive是否是原始类型System.out.println(cls1);System.out.println(int.class.isPrimitive());System.out.println(int.class==Integer.class);System.out.println(int.class==Integer.TYPE);//TYPE代表包装类型包装的基本类型字节码System.out.println(int[].class.isPrimitive());System.out.println(int[].class.isArray());
表示java类的Class类提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,
这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等
(1)Constructor类
//获得方法时要用到类型
//调用获得的方法时要用到上面相同类型的实例对象
//new String(new StringBuffer("abc"));Constructor constructor1=String.class.getConstructor(StringBuffer.class);/*JDK1.5的一个新特性:getConstructor里面可以指定若干个class对象,每一个class对象代表一个类型StringBuffer表示选择哪个构造方法String类的字节码 指定内容编译,翻译成class*/String str2=(String)constructor1.newInstance(new StringBuffer("abc"));//拿String类字节码构造的对象//实例化//StringBuffer表示用上边这个构造方法时还要传进一个StringBuffer对象进去System.out.println(str2.charAt(2));
该方法内部用到了缓存机制来保存默认构造方法的实例对象。
路径:D:\Program Files\Java\jdk1.6.0_10下 src.zip文件 打开src.zip\java\lang Class.java 找到newInstance0
public class ReflectPoint {private int x;public int y;public ReflectPoint(int x, int y) {super();this.x = x;this.y = y;}}
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;public class ReflectTest {/** * @param args * @throws ClassNotFoundException * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException * @throws NoSuchFieldException */public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {// TODO Auto-generated method stubReflectPoint pt1=new ReflectPoint(3,5);Field fieldY=pt1.getClass().getField("y");//字节码可以getField,getField只能得到可见的//fildY的值是多少?fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值System.out.println(fieldY.get(pt1));Field fieldX=pt1.getClass().getDeclaredField("x");//x不可见所以要用getDeclaredFieldfieldX.setAccessible(true);//暴力反射,设置成可以访问,没有这一步fieldX.get(pt1)通不过System.out.println(fieldX.get(pt1));}}
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;public class ReflectTest {/** * @param args * @throws ClassNotFoundException * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException * @throws NoSuchFieldException */public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {// TODO Auto-generated method stubString str1="abc";Class cls1=str1.getClass();Class cls2=String.class;Class cls3=Class.forName("java.lang.String");System.out.println(cls1==cls2);System.out.println(cls1==cls3);System.out.println(cls1.isPrimitive());System.out.println(cls1);System.out.println(int.class.isPrimitive());System.out.println(int.class==Integer.class);System.out.println(int.class==Integer.TYPE);System.out.println(int[].class.isPrimitive());System.out.println(int[].class.isArray());//new String(new StringBuffer("abc"));Constructor constructor1=String.class.getConstructor(StringBuffer.class);String str2=(String)constructor1.newInstance(new StringBuffer("abc"));System.out.println(str2.charAt(2));ReflectPoint pt1=new ReflectPoint(3,5);Field fieldY=pt1.getClass().getField("y");//getField只能得到可见的//fildY的值是多少?fieldY不是对象身上的变量,要用它去取某个对象上对应的值System.out.println(fieldY.get(pt1));Field fieldX=pt1.getClass().getDeclaredField("x");fieldX.setAccessible(true);//暴力反射,设置成可以访问System.out.println(fieldX.get(pt1));}}
eg:
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;public class ReflectTest {/** * @param args * @throws Exception */public static void main(String[] args) throws Exception {changeStringValue(pt1);System.out.println(pt1);}private static void changeStringValue(Object obj) throws Exception{// TODO Auto-generated method stubField[] fields=obj.getClass().getFields();for(Field field: fields){//if(field.getType().equals(String.class)) 因为是string类型,所以==比较好if(field.getType()==String.class){String oldValue=(String)field.get(obj);String newValue=oldValue.replace('b', 'a');field.set(obj, newValue);}}}}
public class ReflectPoint {public String str1="ball";public String str2="basketball";public String str3="itcast";@Overridepublic String toString(){return str1+":"+str2+":"+str3;}}
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法
String str1="abc";
Method methodCharAt=String.class.getMethod("charAt", int.class);System.out.println(methodCharAt.invoke(str1, 1));//jdk1.4System.out.println(methodCharAt.invoke(str1,new Object[]{2}));
根据用户提供的类名,去执行该类中的main方法
1、用静态代码的方法调用main方法:
public class ReflectTest {/** * @param args * @throws Exception */public static void main(String[] args) throws Exception { TestArguments.main(new String[]{"111","222","333"});}class TestArguments{public static void main(String[] args){for(String arg : args){System.out.println(arg);}}}
2、用反射执行
按F2,会出现完整包名
右键->run as ->open configurations...(打开运行对话框)
main标签指运行的那个类,Arguments指要传递的参数,例子中要传的参数是:cn.itcast.day1.TestArguments
相当于在运行里执行:java ReflectTest cn.itcast.day1.TestArguments (注:把cn.itcast.day1.TestArguments这个字符串传给了类ReflectTest)
用反射执行的原因:
启动Java程序的main方法的参数是一个字符串数组,
即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
按jdk1.5的语法,整个数组是一个参数,
而按jdk1.4的语法,数组中的每个元素对应一个参数,
当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题
eg:
String startingClassname=args[0];Method mainMethod=Class.forName(startingClassname).getMethod("main",String[].class);//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//把整个数组装成一个包mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
(六)数组的反射
/* int[] a1=new int[3];int[] a2=new int[4];int[][] a3=new int[2][3];String[] a4=new String[4]*/;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());//System.out.println(a1.getClass() == a4.getClass());//System.out.println(a1.getClass() == a3.getClass());System.out.println(a1.getClass().getName());//结果[I :[代表数组;I代表int类型System.out.println(a1.getClass().getSuperclass().getName());//获取父类 结果: java.lang.ObjectSystem.out.println(a4.getClass().getSuperclass().getName());Object aObj1=a1;Object aObj2=a4;//Object[] aObj3=a1;//数组里装的是int类型的,不是Object类型的,a1是基本数据类型int的,不是ObjectObject[] aObj4=a3;//Object数组里装的是一维数组Object[] aObj5=a4;System.out.println(a1);//[I@1f33675 int类型的[数组的哈希code值1f33675System.out.println(a4);//[Ljava.lang.String;@7c6768System.out.println(Arrays.asList(a1));//1.4 asList(Object[] a);1.5asList(T...a)当成一个objectSystem.out.println(Arrays.asList(a4));
public static void main(String[] args) throws Exception { printObject(a4);printObject("xyz");}private static void printObject(Object obj) {// TODO Auto-generated method stubClass 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);}}
(六)反射的作用(实现框架的功能)
使用类时:一种是你调用别人的类,一种是别人调用你的类,这两种类一个叫框架,一个叫工具
eg:我去买房子,用的是开发商提供的房子,做门的时候用别人做的锁,两个东西用法不一样,锁被门窗调用,房子就是框架,卖给谁都行,谁买了装上自己的窗户和门就成了自己的家。所以你用框架,框架在调用你的类。而锁是一个工具类,你在调用锁。
用框架效率高,框架是一个半成品
HashSet用反射做
Collection collections=new HashSet(); HashSet()这个位置不是类的名字,而是配置文件读取出来的
新建文件,config.properties
source内容是
className=java.util.ArrayList
ReflectTest2.java文件里加载properties文件用
InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
Properties属性文件在JAVA应用程序中是经常可以看得见的,也是特别重要的一类文件。它用来配置应用程序的一些信息,不过这些信息一般都是比较少的数据,没有必要使保存,而使用一般的文本文件来保存,如果是通过File直接保存的话,可能在存储和读取上都不是很方便,但如果保存为Properties文件就不一样了,属性文件都有键值对应的,在JAVA的包中,有提供专门的操作属性文件的类。这个类就是java.uitl.Properties类,由于Properties类是一个集合类,所以,Properties会将属性以集合的方式读写。
//读取属性文件流的方法 public void load(InputStream inStream) throws IOException {}//写属性文件流的方法 public void store(OutputStream out, String comments) throws IOException {}
我们要做的第一步就是要将文件读取到Properties类对象中,由于load有一个参数是InputStream,所以我们可以用InputStream的子类FileInputStream将属性文件读取到Properties对象中,知道config.properties的路径, 我们就用FileInputStream(String name)构造函数:
InputStream ips=ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resource/config.properties");//从根开始的路径/* Properties等效一个HashMap(key yalue), * 但在HashMap上扩展了功能,可以把 自己内存中的键值对存到硬盘中 * 也可以在初始化的时候,从一个文件把自己的键值对加载进来,*/Properties props=new Properties();/* 建完Properties马上用load(读取属性文件流的方法)读取config.properties文件流*/ props.load(ips);/* * 马上关门,良好的习惯,如果没有关门:就会有一点小小的内存泄露,ips关联的系统资源没有被释放 * 这个内存泄露不是ips对象没有被释放,而是ips关联的系统资源没有被释放, * 例: * 本对象由java虚拟机作为垃圾进行回收 * 自己在被垃圾回收之前,先把自己关联的系统资源,物理资源干掉 * 如果不干掉,就是ips对象没了,但是以前只指向操作系统的资源还在 * ips由虚拟机管理的,有垃圾回收器来管理的,这样就把资源释放了 */ips.close();String className=props.getProperty("className");//反射Collection collections=(Collection)Class.forName(className).newInstance();//得到了类.调用不带参数的构造方法/* * 如果把ReflectPoint里的hashCode删掉,其他不变 * 可能会输出3 * 如果虽然两个对象pt1,pt2比较想等了,但是算的hashcode值,是按照你的内存算的,这 * 两个本来相同的值因为按照内存计算的 * 因为pt1与pt3取得区域可能不一样 * pt1会有内存值,pt3也会有内存值,这两个可能会相等 * 但只在一个区域里找*/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);/* * collection里存完有两个值 * 移去一个应该还有一个,但打印出来是2 * 说明存完再改值后hashcode值也改了,不再一个区域内 * 这样就内存泄漏了 * 假如程序不断的增加删除修改对象,日积月累,内存就出现溢出的现象 */System.out.println(collections.size());
结果是:4
把
config.properties
内容改为
className=java.util.HashSet();
结果变为:2
(七)类加载器
工程做完后,通常把bin文件下的class文件打成jar包
1、实际项目中,很少用相对路径
例如:InputStream ips=ReflectTest2.class.getResourceAsStream("config.properties");//相对路径
相对路径相对于
在运行里 输入
java MyClass xx.file
xx.file文件是相对于当前工作目录去运算,这样不方便,
可以用绝对路径来解决
2、配置文件config.properties文件在哪里
getRealPath()方法:得到项目的绝对位置
例如:安装了 “金山词霸”得到 金山词霸的位置,放到c盘得到c,放到d盘得到d,配置文件放在金山词霸内部的,
用金山词霸的绝对位置拼上内部位置,就得到一个完整的。
比如window-preferences-general-open click ,首选项打勾或不打勾,然后ok按钮,存起来
InputStream ips=new FileInputStream("config.properties");
如果要存,一定要完整路径
3、得到资源文件的方式,最常用的是InputStream,但是他不能替代FileInputStream
每一个.class文件,在用的时候,都要加载到内存里来,起这个作用的叫类加载器
既然可以加载.class文件,也可以加载普通文件
类加载器会提供一个方法
a、只要运行ReflectTest2.class这个类,一定被类加载器加载了,由类加载器getClassLoader加载进来,
getClassLoader可以加载.class文件
也能加载普通文件,用getResourceAsStream()这个方法, 在classPath指定的目录下逐一的去查找要加载的文件
真正运行的时候是去classPath里去找,而不是源目录里去找
//InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("config.properties");//路径这么写是,去classPath下的根目录下找
//InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");//相对路径,classPath下的路径里
框架的配置文件都是放在classPath指定的目录加载,都是用类加载器方式来加载文件,
b、配置文件在包里
class也提供了方法来加载资源文件,其实就是内部调用ClassLoader
InputStream ips=ReflectTest2.class.getResourceAsStream("config.properties");//相对路径,相对于我的包而言的;
c、配置文件在包下面子包里
InputStream ips=ReflectTest2.class.getResourceAsStream("resource/config.properties");//相对路径,相对于本包的子包
c、从根目录开始的路径
InputStream ips=ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resource/config.properties");
配置文件的加载往往用类加载器,框架都是这么加的,用框架时,把配置文件放到classPath目录下
如果是eclipse开发就放到sourse下,或sourse字目录下,会自动拷贝到classPath下面去
(八)内省IntroSpector
用于对JavaBean进行操作
JavaBean是特殊的Java类,主要用于传递信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
这种JavaBean的实力对象称之为值对象(Value Object,简称VO)
例:
int getAge()
void setAge(int age)
符合这种特定规则的java类(get,set打头)我们称之为javaBean
能把javaBean当作普通类来操作
普通类不见得能当作j'avaBean来操作
class Person{
private int x;//私有的,外面看不到
public int getAge(){
return x;//返回内部一个局部变量
}
public void setAge(int age){
this.x=age;//把传进来的参数给内部的那个变量
}
}
把Person当成JavaBean来看,他有一个什么名称的属性(是age,不是x,x外面看不到)
把Person当成JavaBean来操作,设置的属性是age,不能说设置x
JavaBean的属性是根据方法名称来的
有一个特点,去掉set和get后,就是属性名,
如果第二个字母是小的,则把再把首字母改成小的,否则不变
1、内省案例
用内省的方式读取JavaBean对象的x属性
package cn.test.day1;import java.beans.IntrospectionException;import java.beans.PropertyDescriptor;import java.lang.reflect.Method;public class IntroSpectorTest {/** * @param args * @throws Exception */public static void main(String[] args) throws Exception {// TODO Auto-generated method stubReflectPoint pt1=new ReflectPoint(3,5);String prpertyName="x";/* * PropertyDescriptor代表了javabean的属性 * getClass把类当成javabean来看 * javabean的属性由get和set得出来的,进一步就可以得到这个属性的get方法和set方法 * */PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());Method methodGetX=pd.getReadMethod();//得到x属性的读方法Object retVal=methodGetX.invoke(pt1);//在pt1上调用invoke,得到一个返回值System.out.println(retVal);}}
值为:3
取y的值
ReflectPoint pt1=new ReflectPoint(3,5);String prpertyName="x";/* * PropertyDescriptor代表了javabean的属性 * getClass把类当成javabean来看 * javabean的属性由get和set得出来的,进一步就可以得到这个属性的get方法和set方法 * 要是用javabean的话,取值一定要用get取 */PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());Method methodGetX=pd.getReadMethod();//得到x属性的读方法Object retVal=methodGetX.invoke(pt1);//在pt1上调用invoke,得到一个返回值System.out.println(retVal);//把y值set进去Method methodSetX=pd.getWriteMethod();methodSetX.invoke(pt1, 7);//set接受参数,把参数改为7,7的位置应该为对象,java5自动装箱System.out.println(pt1.getX());//y打印出来了
选中
PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();//得到x属性的读方法
Object retVal=methodGetX.invoke(pt1);
右键->reflector->Extract Method...
方法的格式是某个方法接收两个参数,起名getProperty
方法就会生成
Object retVal = getProperty(pt1, prpertyName);
private static Object getProperty(Object pt1, String prpertyName)throws IntrospectionException, IllegalAccessException,InvocationTargetException {PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());Method methodGetX=pd.getReadMethod();//得到x属性的读方法Object retVal=methodGetX.invoke(pt1);return retVal;
methodSetX.invoke(pt1, 7);
不是变量时没法重构,所以可以设成变量
Object value=7;//不为变量没法设选中
PropertyDescriptor pd2=new PropertyDescriptor(prpertyName,pt1.getClass());Method methodSetX=pd2.getWriteMethod();methodSetX.invoke(pt1, value);
重构
Object value=7;//不为变量没法设setProperties(pt1, prpertyName, value);System.out.println(pt1.getX());}private static void setProperties(Object pt1, String prpertyName,Object value) throws IntrospectionException,IllegalAccessException, InvocationTargetException {PropertyDescriptor pd2=new PropertyDescriptor(prpertyName,pt1.getClass());Method methodSetX=pd2.getWriteMethod();methodSetX.invoke(pt1, value);}
2、内省的复杂案例
private static Object getProperty(Object pt1, String prpertyName)throws IntrospectionException, IllegalAccessException,InvocationTargetException {/*PropertyDescriptor pd=new PropertyDescriptor(prpertyName,pt1.getClass());Method methodGetX=pd.getReadMethod();//得到x属性的读方法Object retVal=methodGetX.invoke(pt1);*//*Introspector类中有getBeanInfo方法, * getBeanInfo:把java类(pt1.getClass())当成javaBean来看; * 看出来的结果用BeanInfo来表示, * BeanInfo就代表javaBean细节信息, * */BeanInfo beanInfo=Introspector.getBeanInfo(pt1.getClass());//PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();//所有的属性信息Object retVal=null;for(PropertyDescriptor pd:pds){if(pd.getName().equals(prpertyName)){Method methodGetX=pd.getReadMethod();//得到x属性的读方法retVal=methodGetX.invoke(pt1);break;}}return retVal;}
public static void main(String[] args) throws Exception {// TODO Auto-generated method stubReflectPoint pt1=new ReflectPoint(3,5);String prpertyName="x";/* * PropertyDescriptor代表了javabean的属性 * getClass把类当成javabean来看 * javabean的属性由get和set得出来的,进一步就可以得到这个属性的get方法和set方法 * 要是用javabean的话,取值一定要用get取 * * 把methodGetX属性抽成一个方法 */Object retVal = getProperty(pt1, prpertyName);//在pt1上调用invoke,得到一个返回值System.out.println(retVal);}
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- 反射
- centos 5.3 上安装 emacs 23.2
- Ubuntu 12.04下玩转终端管理器Byobu
- mini2440 裸机编程 - 内存控制
- Android Service学习之IntentService 深入分析
- GitHut上最受关注的Objective-C项目
- 反射
- zk消息处理链
- linux realloc之内存分配
- android 如何自定义title
- Weblogic一:window命令部署操作管理
- Hibernate注解详细介绍
- 销售与市场的区别
- hdoj1202简单题、模拟题
- cocos2d-iphone之魔塔20层第三部分