黑马程序员_反射与类加载器

来源:互联网 发布:人像合成软件下载 编辑:程序博客网 时间:2024/04/30 02:14

------------- 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"));// 获取某个类中所有ConstructorConstructor<?>[] 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;}@Overridepublic String toString(){return str1+","+str2+","+str3;}@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;}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.ArrayListProperties 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/
0 0
原创粉丝点击