------------- android培训、java培训、java博客、java学习型技术博客、期待与您交流! -------------
反射
类有属性和方法,反射就是加载类,然后解剖出类的各个组成部分并进行相应操作 动态加载一个指定的类,并获取该类中的所有的内容,并将字节码文件封装成对象 反射的基石——Class类 Java中的各个Java类属于同一类事物,描述这类事物的java类名就是Class; Class类的实例表示正在运行的Java应用程序中的类和接口。Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass
方法自动构造 获取Class实例的三种方法 1、static Class<?> forName(String className) 2、类名.class 3、对象.getClass() Class类中方法 1、获取该类中构造方法 Constructor<T> getConstructor(Class<?>... parameterTypers) Constructor<?>[] getConstructors() 注意返回的都是具有公共访问权限的成员,若还想得到私有的, 则用getDeclaredXxx()方法获取对象后在调用setAccessible(),下面两个方法同理 2、获取该类中字段 Field getField(String name) Field[] getFileds() 3、获取该类中方法 Method getMethod(String name,Class<?>... parameterTypes) Method[] getMethods() 4、创建该类无参的实例 newInstance() 5、获取该类中注解 <A extends Annotation> A getAnnotation(Class<A> annotationClass) Annotation[] getAnnotations() 6、获取该类加载器 getClassLoader() 7、查找具有给定名称的资源 InputStream getResourceAsStream(String name) 此方法委托该类对象的类加载器来实现,经常用此法来读取配置文件 8、判断此类对象是不是属于哪种类型(多种判断方法) 反射的三大类介绍 类Constructor<T> 构造器,主要用来创建其所代表类的实例对象 方法 newInstance(Object... initargs) 如果要创建带参数的对象,就用这个方法,如果无参则用Class中的该方法 类Field 方法 Object get(Object obj) 返回指定对象上此Field字段的值 Class<?> getType() 返回此字段的声明类型 Sting getName() 返回此Field对象表示字段的名称 类Method 方法,通过字节码获取该类中方法,在调用方法作用于某个对象来进行操作 Object invoke(Object obj,Object... args) 返回的是Object,参数obj为该方法作用于哪个对象,若为null则该方法是静态的 args表示调用该方法所需传入的参数。 反射应用见示例package cn.ithema.day1; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Date; public class ReflectTest { public static void main(String[] args)throws Exception { // TODO Auto-generated method stub //Class类的三种获取方式演示 String str1 = "abc"; Class<? extends String> cls1 = str1.getClass(); Class<String> cls2 = String.class; Class<?> cls3 = Class.forName("java.lang.String"); System.out.println(cls1==cls2 && cls1==cls3); System.out.println(cls1.isPrimitive()); // Class是否为基本数据类型字节码 System.out.println(int.class.isPrimitive()); System.out.println(int.class == Integer.class); // Type常量,代表包装类型所包装的基本类型的字节码 System.out.println(int.class == Integer.TYPE); // 数组类型的Class实例对象 System.out.println(int[].class.isArray()); // Reflect类三大子类:Constructor、Field、Method // 通过Class类中getConstructor方法获取传入指定参数的字节码的构造方法 Constructor<String> constructor1 = String.class.getConstructor(StringBuilder.class); // 使用此 Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 // 得到构造方法传入的是类型,实例化对象传入的是实例对象 constructor1.newInstance(new StringBuilder("abc")); // 获取某个类中所有Constructor Constructor<?>[] constructors = String.class.getConstructors(); for(Constructor<?> constructor : constructors ){ System.out.println(constructor); } // 上面是通过class 获取 constructor 在new object,过程比较繁琐 // java中Class有newInstance方法,将这些步骤简化,创建此Class对象表示的类的一个新实例 String.class.newInstance(); //Field类,建立一个ReflectPoint演示类 ReflectPoint rp1 = new ReflectPoint(3,5); //公有字段值的获取, Field fieldY = ReflectPoint.class.getField("y"); //获取某个具体对象的变量值 System.out.println(fieldY.get(rp1)); //获取私有字段的值,反射其它两个方法也一样都是getDeclaredXxx()方法 Field fieldX = ReflectPoint.class.getDeclaredField("x"); //将私有成员设置成可访问的 fieldX.setAccessible(true); System.out.println(fieldX.get(rp1)); changeStringValue(rp1); System.out.println(rp1); //Method类,字节码里面的成员方法,类中方法与对象是没关系的, //思路是先得到该类的方法,然后再针对某个对象去调用这个方法,调用方法为 invoke(obj,parameter-args); Method methodCharAt = String.class.getMethod("charAt",int.class); System.out.println(methodCharAt.invoke(str1, 1)); /* 另,如果对象上面是null,则不需要对象就可以调用该方法,则这个method方法为静态方法 JDK1.4 public Object invoke(Object obj,Object... args) JDK1.5 public Object invokeZ(Object obj,Object[] args) 所以上述可写为:charAt.invoke("str",new Object[]{1}); */ System.out.println(methodCharAt.invoke(str1,new Object[]{2})); //用反射方式执行某个类中的main方法, //一般方法为,new TestArguments.main(new String[]{"111","222","333"}); //可归纳为对接受数组参数的成员方法进行反射 String startingClassName = args[0]; //java编译的时候需要将参数传进去,runas里面设置 Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class); mainMethod.invoke(null, (Object)new String[]{"111","222","333"}); //参数值接受Object,如无强转,JDK1.5会自动拆包当成3个参数,则报错 //数组与Object的关系及其反射类型 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().getName()); //获取父类名字 System.out.println(a1.getClass().getSuperclass().getName()); Object aObj1 = a1; Object aObj2 = a4; // Object[] aObj3 = a1; a1数组装的是int,int不是对象,所以不能有这种引用 Object[] aObj4 = a3; Object[] aObj5 = a4; //续上, Arrays.asList() 处理int[]和String[]的区别 System.out.println(a1);//数组的输出都是地址 System.out.println(a4); System.out.println(Arrays.asList(a1)); //还是一个数组地址,因为aslList在1.4版本接受的是Object数组,在1.5版本接收的是Object,a1是对象,但不是对象数组 System.out.println(Arrays.asList(a4)); // 集合打印的是所含元素的toString //续上,数组的反射应用 printObject(a4); printObject("xyz"); } private static void printObject(Object obj) { Class<? extends Object> clazz = obj.getClass(); if(clazz.isArray()){ //反射Array类中静态方法的应用 for(int i=0;i<Array.getLength(obj);i++){ System.out.println(Array.get(obj,i)); } } else{ System.out.println(obj); } } private static void changeStringValue(Object obj)throws Exception{ //练习,将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的'a'->'b' 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); } } } } class TestArguement{ public static void main(String[] args){ for(String str : args){ System.out.println(str); } } } class ReflectPoint { private int x; public int y; public String str1 = "ball"; public String str2 = "basketball"; public String str3 = "ithema"; private Date birthday = new Date(); public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } @Override public String toString(){ return str1+","+str2+","+str3; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public 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; } 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; } }
package cn.ithema.day1; /* * HashSet是采用哈希算法存取对象的集合,它内部采用某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域 * Object类定义一个hashCode()方法来返回每个java对象的哈希码,当从HashSet集合中查找某个对象是,java系统首先调用对象的hashCode方法获取 * 对象的哈希码,然后根据哈希码找对应的存储区域,最后取出该存储区域内的每个元素与该对象进行equals方法比较 * 注意: * 当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则, * 对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了,这种情况下,即使在contains方法使用该对象的当前 * 引用作为参数去HashSet集合中检索,也将返回找不到对象的结果, * 也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露 * * * 配置文件放的位置? * getRealPath() 是获取项目的安放位置,安放位置+项目内部位置就是绝对路径 * 必须用完整的路径,但完整的路径不是硬编码,而是运算出来的 * 就是程序被安装在某个路径下,该程序可获取这个路径,然后再加上配置文件存在这个程序内部的相对路径,就ok * 另一种方式 , 用类加载器的方式管理资源和配置文件,将文件移动到包目录下, * ReflectTest2.class.getClassLoader().getResourceAsStream(cn/ithema/day1/config.properties) * 以后框架编程中都是用类加载器的方式加载配置文件的,配置文件存放在ClassPath目录下 * 简写,ReflectTest2.class.getResourceAsStream(config.properties); //Class类似于构造函数有newInstance方法一样有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 { public static void main(String[] args)throws Exception { //用反射技术将所需集合加载进来 //第一种方法,通过保存绝对路径来获取配置文件,绝对路劲=getRealPath+项目内部路径 //InputStream ips = new FileInputStream("config.properties"); //第二种方法,通过类加载器将文件加载进来,当然Class也封装了该方法,不过此方法只能读取,不能写入 InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn\\ithema\\day1\\config.properties"); //configue.properties为className=java.util.ArrayList Properties props = new Properties(); props.load(ips); ips.close(); String className = props.getProperty("className"); Collection collections = (Collection)Class.forName(className).newInstance(); //new一个不带参数的构造函数的实例 //Collection collections = new ArrayList(); //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); System.out.println(collections.size()); //改完参与hashcode方法运算的值,在删除该对象,会返回false //pt1.y=7; //System.out.println(collections.remove(pt1)); } }
类加载器
/* *public abstract class ClassLoader * 类加载器 * Java虚拟机可安装多个类加载器,系统默认三个主要类加载器, * BootStrap ExtClassLoader AppClassLoader * 类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必有一个类加载器不是java类 * java虚拟机中的类加载器采用具有父子关系的树形结构进行组织 * * BootStrap JRE/lib/rt.jar * ExtClassLoader JRE/lib/ext/*.jar * AppClassLoaderv CLASSPATH指定的所有jar或目录 * 自定义加载器 指定的特殊目录(可加解密) * * 类加载器的委托机制 * 首先当前线程的类加载器加载线程中的第一个类 * 如果类中引用了B,java虚拟机使用加载A的类装载器来加载类B * 还可以直接调用方法ClassLoader.loadClass()来指定给某个类加载器去加载某个类 * * 每个类加载器在加载类时,先委托给你上级类加载器加载 * 当所有类加载器没有加载到类,回到发起者类加载器,还加载不到,则抛出异常 * 注意这里不会再找发起加载器的子类加载器,因为无getChild * 这样一个类不会加载多分字节码。 * * 编写自己的类加载器 (模板方法设计模式) * 自定义的类加载器必须继承ClassLoader * 方法 * 1、Class<?> loadClass(String name,boolean resolve) * 此方法的默认实现将按以下顺序搜索类 * 1、调用 findLoadedClass(String) 来检查是否已经加载类。 * 2、在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。 * 3、调用 findClass(String) 方法查找类。 * 2、Class<?> findClss(String name) 使用指定的二进制名称查找类。 * 3、final Class<?> defineClass(String name,byte[] b,int off,int len) * 将一个byte数组转换为Class实例,name为所需要类的二进制名称,如未知则写null * * */ package cn.ithema.day2; import java.util.Date; public class ClassLoaderTest { public static void main(String[] args)throws Exception { System.out.println( ClassLoaderTest.class.getClassLoader().getClass().getName()); //获取类的加载器,在使用循环获取加载器的加载器,从而得出java类中的加载器 ClassLoader loader = ClassLoaderTest.class.getClassLoader(); while(loader!=null){ System.out.println(loader.getClass().getName()); loader = loader.getParent(); } System.out.println(loader); //编写和测试编写的解密类加载器 //加密文件,用app加载器加载不出来,只能用自定义解密加载器 //System.out.println(new ClassLoaderAttachment().toString()); //注意需用指定加载器加载的class文件不能存放在app类加载器可找到的目录下,不然指定加载器就不能去加载了 Class<?> clazz = new MyClassLoader("ithmaLib").loadClass("ClassLoaderAttachment"); Date d1 = (Date)clazz.newInstance(); System.out.println(d1); } }
package cn.ithema.day2; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; /* * 自定义类加载器,先用该类加密一份文件,在将该类定义为加载器进行演示 * * */ public class MyClassLoader extends ClassLoader{ public static void main(String[] args) throws Exception{ //给主函数参数赋值,第一个为所需加密的java文件源路径,第二个为文件存放目录 String srcPath = args[0]; String destDir = args[1]; String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1); //目录+文件名 String destPath = destDir+"\\"+destFileName; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(destPath); //调用简单加密方法 cypher(fis,fos); fis.close(); fos.close(); } //简单加密文件 private static void cypher(InputStream ips ,OutputStream ops)throws Exception{ int b = 0; while((b=ips.read())!=-1){ ops.write(b^0xff); } } private String classDir; //loadClass方法中若父类加载器返回null,则会自动调用findClass()方法查找类,所以只需覆盖该方法即可 protected Class<?> findClass(String name) throws ClassNotFoundException { //生成文件路径 String classFileName = classDir+"\\"+name+".class"; //解密文件 FileInputStream fis; try { fis = new FileInputStream(classFileName); ByteArrayOutputStream bas = new ByteArrayOutputStream(); cypher(fis,bas); byte[] bytes = bas.toByteArray(); //将byte数组转换为Class实例 return defineClass(null,bytes,0,bytes.length); } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); } public MyClassLoader(){} public MyClassLoader(String classDir){ this.classDir = classDir; } }
package cn.ithema.day2; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; /* * 自定义类加载器,先用该类加密一份文件,在将该类定义为加载器进行演示 * * */ public class MyClassLoader extends ClassLoader{ public static void main(String[] args) throws Exception{ //给主函数参数赋值,第一个为所需加密的java文件源路径,第二个为文件存放目录 String srcPath = args[0]; String destDir = args[1]; String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1); //目录+文件名 String destPath = destDir+"\\"+destFileName; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(destPath); //调用简单加密方法 cypher(fis,fos); fis.close(); fos.close(); } //简单加密文件 private static void cypher(InputStream ips ,OutputStream ops)throws Exception{ int b = 0; while((b=ips.read())!=-1){ ops.write(b^0xff); } } private String classDir; //loadClass方法中若父类加载器返回null,则会自动调用findClass()方法查找类,所以只需覆盖该方法即可 protected Class<?> findClass(String name) throws ClassNotFoundException { //生成文件路径 String classFileName = classDir+"\\"+name+".class"; //解密文件 FileInputStream fis; try { fis = new FileInputStream(classFileName); ByteArrayOutputStream bas = new ByteArrayOutputStream(); cypher(fis,bas); byte[] bytes = bas.toByteArray(); //将byte数组转换为Class实例 return defineClass(null,bytes,0,bytes.length); } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); } public MyClassLoader(){} public MyClassLoader(String classDir){ this.classDir = classDir; } }
package cn.ithema.day2; import java.util.Date; //需要被自定义类加载器加载的类 public class ClassLoaderAttachment extends Date { private static final long serialVersionUID = 1L; public String toString(){ return "hello java"; } }
------------- android培训、 java培训、java博客、java学习型技术博客、期待与您交流!
-------------详情请查看:http://edu.csdn.net/heima/