黑马程序员----java----反射

------- android培训java培训、期待与您交流! ----------


一,反射的基石:Class类

概念:java中的所有类属于同一种事物,描述这种事物就是Class类。  

字节码文件:java中所有类文件被编译后都会生成一个.class字节码文件。


得到一个类的字节码对应的实例对象:
a,类名.class。
b, p1.getClass();  //通过类的对象进行获取。类的字节码已经加载到内存。
c, Class.forName(“”); //得到类的字节码,如果类的字节码没有加载到内存中,将进行加载


八个基本类型的Class对象
boolean,byte,char,short,int,long,float,double, void ).class。

判断各种类的实例对象的方法代码
package Date421;
import java.lang.Class;
public class ReflectTest {
	
	public static void main(String[]args)throws Exception
	{
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
	
		System.out.println(cls1 ==cls2);//相等
		System.out.println(cls1 ==cls3);//相等
		
		System.out.println(cls1.isPrimitive());//是不是原始类型
		System.out.println(int.class.isPrimitive());//false
		
		System.out.println(int.class==Integer.class));//不相等
		System.out.println(int.class==Integer.TYPE));//相等
		System.out.println(int[].class.isPrimitive());//false
		System.out.println(int[].class.isArray()));//是不是数组
		
		//总之  只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[] ,void
	}
	
	
	

}<strong>
</strong>



反射

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

   例如:一个java类中用一个Class类的对象向来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类中的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息。这些信息就是用相应类的实例对象来表示,他们是Field,Method,Contructor,Package等等。


一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,运用这些对象就是学习反射的要点。

反射中包含的方法(从返回值类型入手)

Construactor类:代表某个类中的一个构造方法

得到某个类所有的构造方法:
Constructor[] constructors = Class.forName("java.lang.String").getCounstructors();

得到某一个构造方法:
Constructor constructor = Class.forName("java.lang.String").getCounstructor(StrngBuffer.class);
创建实例对象:
通常方式: String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));

通过上面的俩种方法可以创建一个str实例对象。也可以直接通过Class的方法newInstance();来获取实例对象,如下:
String str =(String) Class.forName.("java.lang.String").newInstance();
这个方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
这个方法内部的具体代码用到了缓存机制来保存默认构造方法的实例对象。


Filed类:代表某个类中的一个成员变量。
Filed对象代表的不是某个类的一个成员变量的具体变量,而是代表这个成员变量的定义,
将Filed对象指向哪个具体的成员变量它就代表哪个具体的成员变量。


Filed对象的获取方式:字节码.getField(“变量名”);//getFiled只能看到可见的变量
getDeclareField()可以获取到不可见的变量,得到Filed对象并不能去取出相应的
变量值,必须通过Filed.setAccessible(true)  将x设置强制访问,再进行取值。
package Date421;
import java.lang.Class;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectTest {
	
	public static void main(String[]args)throws Exception
	{
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
	
		System.out.println(cls1 ==cls2);
		System.out.println(cls1 ==cls3);
		
		Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);
		
		//调用获得方法时要用到上面相同类型的对象
		String str = (String)constructor1.newInstance(new StringBuffer("abc"));
		System.out.println(str);
		
		String str2 =(String)Class.forName("java.lang.String").newInstance();
	
		
		
		ReflectPoint pt1 = new ReflectPoint(3,5);
		Field fieldY = pt1.getClass().getField("y");//如果在 ReflectPoint类中y可以被看见,公有,则可以直接取
														//通过类名.getField(“变量名”)方法,这时是将y变量存映射给了fieldY
		System.out.println(fieldY.get(pt1));	//通过get方法从pt1中取出fieldY的值,如果没有运行的话,fieldY只是Field类的一个变量
		
		Field fieldX = pt1.getClass().getDeclaredField("x");//如果pt1中x变量被私有化的时候,用类名().getDeclaredField(“”)来获取x变量
		
		fieldX.setAccessible(true);//然后通过Filed中的方法setAccessible(true)来将变量x设置强制访问。
		System.out.println(fieldX.get(pt1));//然后取出x
		
	}
	
	
	

}
package Date421;

/**
 * @author Administrator
 *
 */
public class ReflectPoint {
	
	
		 int x;
		 public int y;
	public  ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	
	
	}
}
	


作业:将任意一个对象中的所有String类型的成员变量所对应的字符窜内容中的“b”改成a。


package Date421;

import java.lang.reflect.Field;
import java.lang.Class;

public class AreplaceB {
	
	public static void main(String[]args)throws Exception
	{
		ReflectPoint pt1 = new ReflectPoint(3,5);//创建对象pt1
		arepalceB(pt1);//将pt1中的String类进行改写
		System.out.println(pt1);
		
	}
	public static void arepalceB(Object obj)throws Exception//接收一个对象
	{
		Class c = obj.getClass();
		
		Field[] f = c.getDeclaredFields();//获取对象中的所有成员变量
		
		for(Field s : f)//遍历成员变量
		{	//s.setAccessible(true);
		if(s.getType()==String.class)//对成员变量进行判断是否是String类的  s.getType 获取取到变量的类型
		{
			String old = (String)s.get(obj);//强转从Filed对象中取出相对应的值 并用old记录 
			System.out.println(old);
			String xin = old.replace("b","a");//替换
			s.set(obj, xin);// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
			
		}
			
		}
	}

}
package Date421;

/**
 * @author Administrator
 *
 */
public class ReflectPoint {
	
	public int x ;
	public int y;
	
	String s="abc";
	String s1 ="abcdb";
	String s2 ="aaaaa";
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	
	public String toString(){
		return s+":"+s1+":"+s2;
	}
	
		
	
}
	

每个打印方法都会将对象强转toString,这里覆盖toString方法就是打印pt1输出自己要求的结果。



metod类:代表某个类中的一个成员方法

Method methodCharAt =  String.class.getMethod(“charAt”,int.class);
通过字节码.getMethod("方法名",参数)得到Method对象
methodCharAt.invoke(str,1);  通过Method类中的方法invoke(参数,参数)来调用Method对象中取到的方法。

通常方式:System.out.println(str,cjarAt(1));
反射方式:System.out.println(methodcharAt.invoke(str,1));
如果传递给Method对象的invoke()方法的第一个参数为null,这以为这Method对象对应的是一个静态方法。


jdk1.4----jdk1.5

invoke变化:  1.4 ,  invoke()接受参数需要用数组封装,   1.5直接可以传入参数。

用反射方式执行某个类中的main方法:未知类名

目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。

问题:启动java层序的main方法的参数是一个字符串数组,即public static void main
(String []args), 通过反射的方式来调用这个main方法时,如何为invoke方法组中的每个元素对应一个参数,当吧一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法惊醒处理呢?jdk1.5肯定要兼容1.4的语法,会按jdk1.4的语法进行处理,即把数组打散称谓若干个单独的参数。所以,在main方法传递参数时,不能使用代码。

mainMehod.invoke(null,new String[]{"xxx"}),javac只吧它当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:mainMehod.invoke(null,new Object[]{new String[]{"xxx}});

mainMehod.invoke(null,( Object )new String[]{"xxx"}); 编译器会做特殊处理,编译时不把参数当做数组看待,也就不会数组打散成若干个参数了。
package Date421;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Class;

public class AreplaceB {
	
	public static void main(String[]args)throws Exception
	{
		
		
		TestArguments.main(new String[]{"123","456","789"});//通常用法
		
		
		String startingClassName = args[0];//获取类名
		Method mainMehod = Class.forName(startingClassName).getMethod("main",String[].class);//通过getMethod方法获取相应的Method类对象
		mainMehod.invoke(null,new Object[]{new String[]{"123","456","789"}} );//掉用方法invoke传入需要的值调用封装在Method对象中的方法
		mainMehod.invoke(null,(Object)new String[]{"123","456","789"} );//静态方法第一个参数为null    在main方法接收数组参数的时候都会对数组进行拆封。所以需要做(Object)
																		//或者直接再加一层数组
																		
	}
	

}


class TestArguments
{
	
   public static void main(String[] args) {
	   
	 for(String arg : args)
	 {
		 System.out.println(arg);
	 }
	   
	   
	   
	
}}



数组反射:

具体相同维数个元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperclass().getName()方法返回的父类为Object类对应的Class。
基本类型ed一维数组可以被直接当做Object类型使用,不能当做Object[]数组使用,费基本类型一维数组,既可以当做Object类型使用,也可以当做Object数组类型使用。

		int[]a1 = new int[]{1,2,3};
		int[]a2 = new int[3];
		int[][]a3 = new int[2][3];
		String[]a4 = new String[]{"a","b","c","d"};
		
		System.out.println(a1.getClass()==a2.getClass());
		System.out.println(a1.getClass()==a4.getClass());
		System.out.println(a1.getClass()==a3.getClass());
		System.out.println(a1.getClass().getName());
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a3.getClass().getSuperclass().getName());
		
		
		Object aobj1 =a1;
		Object aobj2 =a4;
		//Object[] aobj3 =a1;基本类型不是Object类型。
		Object[] aobj4 =a3;
		Object[] aobj5 =a4;
		
		
		
		System.out.println(Arrays.asList(a1));//将数组转换成List集合进行打印
		System.out.println(Arrays.asList(a4));//jdk1.4不能直接接收传入基本数组类型






Arrays.asList()方法处理 int[] 和String[]时的差异。
Array工具类用于完成对数组的反射操作。
怎么的到数组中的元素类型?
a[0].getClass().getName();//反射,得到a是什么类型。





import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Class;
import java.util.Arrays;

public class AreplaceB {
	
	public static void main(String[]args)throws Exception
	{
		
		
		
		
		
		
				
		
		
		
		
		int[]a1 = new int[]{1,2,3};
		int[]a2 = new int[3];
		int[][]a3 = new int[2][3];
		String[]a4 = new String[]{"a","b","c","d"};
		
		System.out.println(a1.getClass()==a2.getClass());
		System.out.println(a1.getClass()==a4.getClass());
		System.out.println(a1.getClass()==a3.getClass());
		System.out.println(a1.getClass().getName());
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a3.getClass().getSuperclass().getName());
		
		
		Object aobj1 =a1;
		Object aobj2 =a4;
		//Object[] aobj3 =a1;基本类型不是Object类型。
		Object[] aobj4 =a3;
		Object[] aobj5 =a4;
		
		
		
		System.out.println(Arrays.asList(a1));//将数组转换成List集合进行打印
		System.out.println(Arrays.asList(a4));//jdk1.4不能直接接收传入基本数组类型
		
		
		Object obj = null;
		printObject(a1);
		printObject(a4);
		printObject("xyz");
		
		
		
		
		
		
		
		
	}
	private static void printObject(Object obj) {
		Class clazz = obj.getClass();
		if(clazz.isArray()){
			int len = Array.getLength(obj);
			for(int i =0; i<len; i++)
			{
					System.out.println(Array.get(obj,2));
			}
		}else{
			System.out.println(obj);
		}
		
		


hashCode作用:  hashSet类在进行数据存入的时候,  根据hashCode方法会算出一个哈希值,   这些哈希值被分为了N个区域,当判断集合内是否有将要存入的元素时。就到相应的区域去寻找。  当一个对象被存入到hashSet集合中,如果在对此对象进行修改,修改后哈希值会被改变,集合的方法就找不到集合中有该对象,会造成内存泄露,久而久之会造成内存溢出。

<pre name="code" class="java">package Date421;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class ReflectTest2 {
	
	
	public static void main(String[] args) {
		
		
		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());
	}

}
 
 
}

反射的作用,实现框架功能

框架与框架要解决的核心问题。
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

框架要解决的核心问题
我再写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎么样能让你调用到你以后写的类(门窗)呢?


因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。

综合案例

先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成ReflectPoint类的equals和hashcode方法,比较俩个集合的运行结果差异。

然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异,


package Date421;

import java.io.FileInputStream;
import java.io.InputStream;

import java.util.Collection;

import java.util.Properties;
import java.lang.Class;

public class ReflectTest2 {
	
	
	public static void main(String[] args)throws Exception {
		
		
		InputStream in = new FileInputStream("config.properties");
		
		Properties props = new Properties();
		
		props.load(in);
		
		in.close();//干掉系统关联的资源,就是window资源,  in由垃圾回收掉
		
		String className = props.getProperty("className");//指定一个路径
		
		Collection collections = (Collection)Class.forName(className).newInstance();
		
		
		
		
		//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());
	}

}

























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值