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

------------- 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/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值