黑马程序员-反射

来源:互联网 发布:知乎大神 编辑:程序博客网 时间:2024/05/19 02:43
------- android培训、java培训、期待与您交流! ----------
java类用于描述一类事物的共性。java程序中的各个java类属于同一事物,
描述这类事物的java类名就是Class。
Class cls1 = Date.class // 字节码1;
Class cls2 = Person.class// 字节码2;


如何得到各个字节码对应的实例对象(class类型)
1.
类名.class
2.
Person p = new Person();
p.getClass();//获得字节码
3.
Class.forName("类名")如Class.forName("java.util,Dat")
作用:如果字节码已加载到内存中,通过这个静态方法获得这个字节码
如果这个类名的字节码尚未加载,这个方法会把该字节码加载到内存中
并获取该字节码,反射中常用此方法。

九个预定义Class实例对象:八个基本数据类型和void关键字
判断是否是基本数据类型:int.class.isPrimitive()
判断是否是数组类型的Class实例对象:int[].class.isArray()
总之,只要源程序中出现的类型都有各自的Class实例对象

反射就是把java类中的各种成分映射成相应的java类。

Constructor类代表某个类中的一个构造方法。
Constructor[] constructors = 
Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
Constructor constructor = 
Class.forName("java.lang.String").getConstructor(char[].class,int.class,int.class);
创造实例对象:
String str = (String)constructor.newInstance(new char[]{'d','e','f','g','h'},new Integer(1),new Integer(3));

ReflectPoint类:
ReflectPoint pt1 = new ReflectPoint(3,5){private int x ;public int y;public ReflectPoint(int x,int y){this.x = x;this.y = y;}};

Field:提供有关类或接口的单个字段信息
Field fieldY = pt1.getClass().getField("y");//y是public的 可以直接反射得到.这里的.getField("y")得到的是一个Field类。
System.out.println(fieldY.get(pt1));//通过Field类中的get方法获取(返回指定对象上此Field表示的字段的值)

Field fieldX = pt1.getClass().getDeclaredField("x");//x是private的
fieldX.setAccessible(true);//通过暴力反射得到
System.out.println(fieldX.get(pt1));

getField和getDeclaredField的区别:
getField:返回此Class对象所表示的类或者接口的指定 公共成员 字段。
getDeclaredField:返回指定已声明字段。需要通过暴力反射才能拿到字段的值。

将某个类中的String类型中的'b'改成'a':
public static void changeStringValue(Object obj) throws Exception{Field[] fields = obj.getClass().getFields();for(Field field : fields){if(field.getType() == String.class{//这里要用== 因为比较的是同一个字节码文件String oldValue = (String)field.get(obj);String newValue = oldValue.replace('b','a');//replace方法是String中的field.set(obj,newValue);//将指定对象变量上正在被修改的字段设置成指定的新值}}

Method:提供关于类或接口上单独某个方法
Method methodChatAt = String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1,1));
invoke(str1,1):第一个参数是指定对象(如果第一个参数是null,表示是静态方法),后面的是方法参数
invoke:对有指定参数的指定对象调用此Method对象表示的底层方法
在jdk1.5后,invoke(Object obj,Object...args) 如invoke(str,new Object[]{2});表示传入的是
一个Object类的数组,其中包含一个Object类的整形对象,对象的值为2.


调用一个未知类名的对象中的方法:
class Arguments//假设类名未知{public void main(String[] args){for(String str : args){System.out.println(str);}}}class mainMethod{public static void main(String[] args){String startingClassName = args[0];//在运行时 通过输入main函数的参数args 获取args[0],即将调用方法的所属类名Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);//通过传入的类名获取其字节码,得到想反射的调用方法的方法类。mainMethod.invoke(Class.forName(startingClassName).newInstance(),(Object)new String[]{"111","222","333"});//通过方法类中的invoke方法调用想调用的方法,//invoke方法中的第一个参数是要调用方法的所属对象,通过newInstance方法获取实例对象。//invoke后面的参数是这个调用方法需要传入的参数,jdk1.5为了兼容jdk1.4,//**会将传入参数当做Object对象进行拆包,所以在参数前加上(Object)进行强转。}}

*int[]和Object[]的关系:
int[] a1 = new int[3];
Object[] obj1 = a1;//这个编译不通过的,因为基本数据类型数组不能转换成Object数组
String[] a2 = new String[]{"a","b","c"};
System.out.println(a1);//打印的是[I...
System.out.println(a2);//打印的是[S...
System.out.println(Arrays.asList(a1));//打印的还是[I...
System.out.println(Arrays.asList(a2));//打印的是[a,b,c];
//因为在jdk1.5中asList(T...a),需要兼容jdk1.4中的asList(Object[] obj)
//而int[]不能转换成Object[] 因此被当做一个元素,打印的就是其类型和hashcode值。


Array反射实例:打印一个Object类,分为单个元素和数组:
import java.lang.reflect.*;class  ArrayReflect{public static void main(String[] args) {String[] str = {"c","d","e"};printObject(str);printObject("abc");}private static void printObject(Object obj){Class clazz = obj.getClass();if(clazz.isArray()){int len = Array.getLength(obj);for(int x=0;x<len;x++){System.out.println(Array.get(obj,x));}}else{System.out.println(obj);}}}

具有相同维数和元素类型的数组属于同一个类型,有相同的Class实例对象。
getSuperclass():返回Class对象的父类对应的Class类。
基本数据类型的一维数组可以当做Object类型使用,不能当作Object[]使用。
非基本类型的一维数组既可以当Object类型使用,也可以当Object数组使用。

无法获得数组中的元素类型,因为Object[] obj = new Object[]{"abc",2};
可以有多个元素类型,只可以获得单个元素的数据类型。


*当一个对象被存储进HashSet集合中以后,就不能修改这个对象中参与计算哈希值的字段了,
否则,对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,
即使在contains方法使用该对象的当前引用作为参数去该HashSet集合中检索对象,也将返回
找不到对象的结果。这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露



配置文件的路径通过getRealPath()来获得软件所在位置,在加上配置文件在软件内部路径
就可以得到绝对路径。
InputStream ips = new FileInputStream("config.properties");
或者使用.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
如果配置文件和.class 文件在同一个包中,可以用.class.getResourceAsStream("config.properties");
这种用类加载器获取的方式只能用于读取配置文件,如果需要修改配置文件,还是用上面的方法。