java基础之反射

来源:互联网 发布:淘宝一淘入口 编辑:程序博客网 时间:2024/05/18 02:31
框架都要用到反射技术,反射都要用到一个类Class.
java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class.

得到字节码的方式有三种:

Date.class;new Date().getClass();Class.forName("java.lang.String");最后一种是有直接返回,没有则从硬盘加载。
有9个预定义的Class实例对象,8种基本数据类型加void.class:参考Class.isPrimitive方法doc;
public class ReflectTest {public static void main(String[] args) throws Exception {// args:javaplay.TestArgumentsString str1 = "abc";Class<?> cls1 = str1.getClass();Class<?> cls2 = String.class;Class<?> cls3 = Class.forName("java.lang.String");System.out.println(cls1 == cls2);// trueSystem.out.println(cls1 == cls3);// trueSystem.out.println(cls1.isPrimitive());// falseSystem.out.println(int.class.isPrimitive());// trueSystem.out.println(int.class == Integer.class);// false// Integer.TYPE代表它所包装的基本类型字节码,请查看docSystem.out.println(int.class == Integer.TYPE);// trueSystem.out.println(int[].class.isPrimitive());// falseSystem.out.println(int[].class.isArray());// trueSystem.out.println(void.class == Void.class);// false// 总之,在源程序中出现的类型,都有各自的Class实例对象,例如int[],void// 通常方式:String str = new String(new StringBuffer("abc"));// jdk5之前只能接受数组,不接受可变参数// Constructor constructor = String.class.getConstructor(new Class<?>[]// { StringBuffer.class, int.class });// 得到所有public构造方法Constructor[] constructors = Class.forName("java.lang.String").getConstructors();// 得到一个构造方法Constructor constructor1 = String.class.getConstructor(StringBuffer.class);// 反射方式(详见doc 一目了然,不能是"abc",要用到与StringBuffer相同类型的对象)String str2 = (String) constructor1.newInstance(/* "abc" */new StringBuffer("abc"));System.out.println(str2.charAt(2));// c// Class.newInstance()方法,可省去class->constructors->new object中间获取构造方法的环节// 从而直接创建对象,但它只能调用默认的无参构造方法,该方法内部先得到默认构造方法,然后用该构造方法创建对象,其中// 用到了缓存机制来保存默认构造方法的实例,这样能省点事,直接调用newInstance而不用获取构造方法的反射String o = (String) Class.forName("java.lang.String").newInstance();// 成员变量的反射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");// 私有成员要用DeclaredfieldX.setAccessible(true);// 设置true才可以访问,暴力反射System.out.println(fieldX.get(pt1));// 将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"changeStringValue(pt1);System.out.println(pt1);// 成员方法的反射// 普通方式:str.charAt(1),反射方式如下:Method methodCharAt = String.class.getMethod("charAt", int.class);System.out.println(methodCharAt.invoke(str1, 1));// System.out.println(methodCharAt.invoke(null, 1));//静态方法第一个参数才为null// 1.4还有可变参数,只能用数组,又因为参数五花八门,所以用Object,1.4的调用语法如下:System.out.println(methodCharAt.invoke(str1, new Object[] { 1 }));// 对接受数组参数的成员方法进行反射// 写程序的时候不知道要执行哪个类的mainTestArguments.main(new String[] { "111", "222", "333" });String startingClassName = args[0];Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);mainMethod.invoke(null, new Object[] { new String[] { "111", "222", "333" } });// 原因在于1.5要兼容1.4mainMethod.invoke(null, (Object) new String[] { "111", "222", "333" });// 也行,总之,只能接受一个参数// mainMethod.invoke(null, (Object) new String[] { "111" }, (Object) new// String[] { "222" });// 不行// mainMethod.invoke(null, new String[] { "111" });// 不行/** * 启动java程序的main方法的参数是一个字符串数组,通过反射方式调用时,如何传递参数呢,按1.5的语法,整个数组是一个参数, * 而按1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会按照哪种方法进行 * 处理呢?1.5肯定要兼容1.4,会按1.4的语法来处理,即把数组打散成若干个单独的参数,所以,在给main方法传递参数时,不能使用 * mainMethod.invoke(null,new * String[]{"xxx"}),javac会把它当作1.4语法来理解,会把里面的"xxx"当作需要传递的String[]{},所以 * 参数类型不对。 *//* * 数组与Object的关系及其反射类型 . 1.具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象 * 2.代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class * 3.基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既 * 可以当做Object类型使用,又可以当作Object[]类型使用 * 4.Arrays.asList()方法处理int[]和String[]时的差异 */int[] a1 = new int[] { 1, 2, 3 };Integer[] a11 = new Integer[] { 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());//false// System.out.println(a1.getClass() == a3.getClass());//falseSystem.out.println(a1.getClass().getName());// [I Class的getName方法有详细说明[ISystem.out.println(a1.getClass().getSuperclass().getName());// java.lang.ObjectSystem.out.println(a4.getClass().getSuperclass().getName());// java.lang.ObjectSystem.out.println(a4.getClass().getName());// [Ljava.lang.String;System.out.println(String.class.getName());// java.lang.StringSystem.out.println(String.class.getSuperclass().getName());// java.lang.ObjectSystem.out.println(a11.getClass().getName());// [Ljava.lang.Integer;System.out.println(a11.getClass().getSuperclass().getName());// java.lang.ObjectObject aObj1 = a1;Object bObj2 = a4;Object aObj11 = a11;// Object[] aObj3 = a1;// 不能赋值,int不是ObjectObject[] aObj4 = a3;// a3是一维数组的数组,Object相当于一个一维int数组,此时Object[]表示一维数组(int[])的数组Object[] aObj5 = a4;// String是ObjectObject[] aObj6 = a11;// 由此引申的一个问题System.out.println(a1);// [I@5fb7a531System.out.println(a4);// [Ljava.lang.String;@11be650f// 确实转换成了数组,只是数组里只有一个元素,这个元素也是数组System.out.println(Arrays.asList(a1));// [[I@5fb7a531]// 字符串却可以转换,原因在于asList接受的是Object[](1.4)和T...(1.5),如果是String[]就按// 1.4的形式(因为符合1.4的形式,要向下兼容)转换成了list了,// 如果是int[]就不符合Object[]的形式,就会按1.5的形式T...来处理,也即当成一个int[](即T)类型的元素System.out.println(Arrays.asList(a4));// [a, b, c]// 数组的反射printObject(a1);// 1 2 3printObject(a4);// a b cprintObject("xyz");// xyz// 有没有办法得到数组的类型?// 目前没有 Object[] a = new Object[]{"a",1};// a[0].getClass();// 即只能得到某个具体元素的类型,不能得到整个数组的元素类型// 写框架这些东西是必备,不写框架,翻某些砖头似的书像翻小说一样了,this is 进步。}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");field.set(obj, newValue);}}}public static void printObject(Object obj) {Class clazz = obj.getClass();if (clazz.isArray()) {// 对数组进行反射的类Arrayint len = Array.getLength(obj);for (int i = 0; i < len; i++) {System.out.println(Array.get(obj, i));}} else {System.out.println(obj);}}}class TestArguments {public static void main(String[] args) {for (String arg : args) {System.out.println(arg);}}}
public class ReflectPoint {private int x;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;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;ReflectPoint other = (ReflectPoint) obj;if (x != other.x)return false;if (y != other.y)return false;return true;}@Overridepublic String toString() {return "ReflectPoint [x=" + x + ", y=" + y + ", str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]";}}
反射就是把java类中的各种成分映射成相应的java类,例如,一个java类用一个Class类的对象表示,一个类中的组成部分:成员变量、方法、构造方法、包等信息也用一个个的java类来表示,它们是Field、Method、Constructor、Package等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。

ArrayList_HashSet比较及hashcode分析:

public class ReflectTest2 {public static void main(String[] args) {Collection<ReflectPoint> 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;// 修改后输出2,不修改,输出1collections.remove(pt1);// new ArrayList<>();输出4,添加时不比较直接按顺序存储引用变量、// new HashSet<>();输出3,存放时会先判断集合里有没有这个对象,有就不放不是覆盖,想覆盖要先remove再放// HashSet比较对象时调用对象默认equals是比较内存地址(通常是根据内存地址换算出来的,效果与==等价)// 实现equals和hashcode方法后输出2,如果此时注释hashcode或者equals其中任意一个则又输出3了// 原因在于它会先根据hashcode的值,算出自己要存放的最终位置所在的区域,如果只注释hashcode是有可能输出2的,因为存放的区域可能相同,也可能不同// 再调用equals进行比较是否有相等的对象(hashcode只在hash算法的集合中才有意义,其它集合没有任何价值),// 这就是输出3的原因,pt1和pt3存放的区域不一样,原因是没有实现hashcode则根据内存地址进行计算存放的区域,// 为了让相等的对象也肯定放在相同的区域,就有一个说法,如果两个对象equals相等,则应该也要让hashcode相等(不是hash集合就不要搞hashcode)// 注意:当一个对象被存储进hashset集合中,就不能修改这个对象中那些参与计算哈希值的字段了。否则就删除不了了,日积月累,// 不断的添加、修改、删除对象,而程序却一直使用这个hash集合就会造成内存泄漏!因为hash集合中有很多对象已经没用了却没释放// 由hashcode的作用讲到内存泄漏,由内存泄漏讲到hashcode的作用!System.out.println(collections.size());}}

反射的作用->实现框架的功能

框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
用反射技术开发框架的原理:简单框架,把要使用的类放到配置文件里面
在工程根目录下创建config.properties文件:
className=java.util.HashSet
public class ReflectTest2 {public static void main(String[] args) throws Exception {InputStream ips = new FileInputStream("config.properties");Properties props = new Properties();props.load(ips);ips.close();// 跟操作系统说,把window干掉,把自己关联的系统/物理资源释放,自己则由垃圾回收器管理String className = props.getProperty("className");Collection<ReflectPoint> collections = (Collection<ReflectPoint>) Class.forName(className).newInstance();// Collection<ReflectPoint> 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;// 修改后输出2,不修改,输出1// collections.remove(pt1);System.out.println(collections.size());// config.properties文件内容是HashSet:2,ArrayList:4}}

用类加载器的方式管理资源和配置文件

public class ReflectTest2 {public static void main(String[] args) throws Exception {// eclipse会自动把包下面的java文件或者普通文件编译后拷贝到classpath路径下// InputStream ips = new FileInputStream("config.properties");// 类加载器能加载.class文件,那加载普通文件也是顺带的事,下表示在classpath指定的根目录下去找指定的文件// 根目录下肯定没有,只是根目录下的javaplay目录下才有,此时不能定写成/javaplay// ssh框架内部用的就是用的类加载器加载的配置文件,所以ssh的配置文件必须放在classpath指定的目录下,原因就是它// 用的是类加载器读取的配置文件,而第一种不仅可以读还可以写即保存,类加载器的方式只能读不能写InputStream ips11 = ReflectTest2.class.getClassLoader().getResourceAsStream("javaplay/config.properties");// 类提供了一个便捷方法不用获取类加载器就可以加载配置文件,而且默认加载与自己同一个包内的配置文件InputStream ips2 = ReflectTest2.class.getResourceAsStream("config.properties");// 也可以相对路径InputStream ips3 = ReflectTest2.class.getResourceAsStream("resource/config.properties");// 也可以绝对路径,此时必须要以/开头,不管相对还是绝对内部都是调用InputStream ips = ReflectTest2.class.getResourceAsStream("/javaplay/resource/config.properties");Properties props = new Properties();props.load(ips);ips.close();// 跟操作系统说,把window干掉,把自己关联的系统/物理资源释放,自己则由垃圾回收器管理String className = props.getProperty("className");Collection<ReflectPoint> collections = (Collection<ReflectPoint>) Class.forName(className).newInstance();// Collection<ReflectPoint> 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);System.out.println(collections.size());// config.properties文件内容是HashSet:2,ArrayList:4}}








1 0
原创粉丝点击