Java 反射

原创 2015年07月07日 18:44:22

Java 反射

1.       反射的基石→Class类【C大写,是一个叫Class的类】

java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性值是什么,则是有这个类的实力对象来确定的,不同的实例对象有不同的属性值。

Person p1=new Person();

Date Math等类。

字节码:一个类建立对象的一堆二进制代码(放进内存前)

方式①: p1.getClass();

方式②: Class.forName(“java.lang.String”);

方式③: Class cls1=Date.class;     //字节码1

Class cls2=Person.class; //字节码2

         三个得到方法,①:对已建立、放到内存中,只需得到就可以。即:对象.getClass();

②:先用类去加载,加载进来后,再返回字节码。即:Class.forName(“类名”);

③:类名.class

注:反射多用方法②

九个预定义的Class实例对象:8个基本类型+void(参看Class.isPrimitive方法帮助)

Int.class==Integer.TYPETYPE包装基本类型字节码】;

Int.class!=Integer.class

同理:还有Byte.TYPE;Boolean.TYPE;Void.TYPE.

注:对字节码比较用”==”;

eg:

String str1=”abc”;
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName(“java.lang.String”);
//cls1\cls2\cls3三者相同
System.out.println(cls1==cls2);		//true
System.out.println(cls1==cls3);		//true
System.out.println(int.class.isPrimitive());	//true

 

数组类型的class实例对象

Class.isArray();

总之,只要在程序中出现的类型,都有各自的Class实例对象,例如:int[],void

 

2.      反射的概念

一句话总结:反射就是把Java类中的各种成分映射成相应的java类。

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

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

即强调:参数类型用什么来表示——用Class对象!

3.      构造方法的反射应用

Constructor类(代表某个类中的一个构造方法)

得到某个类所有的构造方法:(JDK1.5后因为可变参数,可以获得好几个构造方法,只要在()里放进去想获得参数(构造方法)就可以。

          egConstructor[] constructors=Class.forName(“java.lang.String”).getConstructors();

得到某一个构造方法:

          eg:比如选择StringBuffer这个构造函数

  Constructor constructor=

Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

        创建实例对象:

        通常方式:String str=new String(new StringBuffer(“abc”));

        反射方式:②:String str=(String)constructor.newInstance(new StringBuffer(“abc”));

        因为newInstance返回的是Object类型,所以要转型。

注:①和②的参数要匹配才能行,即①获得方法是要用到类型;②调用获得的方法时要用到上面相同类型的实例对象。

        Class.newInstance()方法:

        egString obj=(String)Class.forName(“java.lang.String”).newInstance();

        该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。

注:反射会导致程序性能下降。

 

4.       Field类(成员变量的反射)

         Field类代表某个类中的一个成员变量

         filedX代表的是X定义,而不是具体的X变量。

         注:“Alt+Shift+seclipse的快捷键”Generate Constructor using Fields,可生成构造方法。

eg:

ReflectPoint pt1=new ReflectPoint(3,5);
	Field fieldY=pt1.getClass().getField(“y”);
	//fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。
	System.out.println(fieldY。get(pt1));
	Field fieldX=pt1.getClass().getDeclaredField(“x”);	//因为x为私有,强取。
	fieldX.setAccessible(true);			//设置可访问
	System.out.println(fieldX.get(pt1));
	
	public c lass ReflectPoint{
		private int x;
		public int y;
		public ReflectPoint(int x,int y){
			super();
			this.x=x;
			this.y=y;
		}
	}


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

public c lass ReflectPoint{
		public String str1=”ball”;
		public String str2=”basketball”;
		public String str3=”itcast”;
	}
	private static void changeStringValue(Object obj)throw Exception{
	Field[] fields=obj.getClass().getFields();
	for(Field field:fields){
	//if(field.getType().equals(String.class)){	//不推荐这么写
	if(field.getType()==String.class){		//用“==”号,因为是同一个字节码(都是String)
		String oldValue=(String)field.get(obj);
		String newValue=oldValue.replace(‘b’,’a’);
		field.set(obj,newValue);	//对象身上的还没改,所以set一下。
	}
}
changeStringValue(pt1);
System.out.println(pt1);

5.       Method类(成员方法的反射)

         Method类代表某个类中的一个成员方法。

         得到类中的某一个方法:

         eg:

Method charAt=
			Class.forName(“java.lang.String”).getMethod(“charAt”,int.class); //()里写方法名


 

和参数类型,因为有的方法有重载。

调用方法:

         通常方法:System.out.println(str.charAt(1));

         反射方法:System.out.println(charAr.invoke(str,1));

         如果传递给Method对象的invoke()方法的的第一个参数为null,该Method对象对应的是一个静态方法。

         jdk1.41.5invoke方法的区别:

                   jdk1.5public Object invoke(Object obj,Object…args)

         jdk1.4public Object invoke(Object obj,Object[] args),既按1.4的语法,需要一个数组作为参数传递给invoke时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用jdk1.4改写为charAt.invoke(“str”,new Object[]{1})形式。

 

         对接收数组参数的成员方法进行反射

                   用反射方法执行某个类中的main方法

                   目标:

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

                   问题:

启动java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组当作参数传递给invoke方法时,javac会到底按哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以,在给main方法传递参数时,不能使用代码 mainMethod.invoke(null,new String[]{“xxx”}),javac只会把它当做jdk1.4的语法进行解释,而不会把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:

         mainMethod.invoke(null,new Object[]{new String[]{“xxx”}};

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

eg:

//TestArguments.main(new String[]{“111”,”222”,”333”});//普通方法
	//反射方法如下:
	String StartingClassName=args[0];
	Method mainMethod=Class.forName(StartingClassName).getmethod(“main”,String[].class);
	mainMethod.invoke(null,(Object)new String[]{“111”,”222”,”333”});

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

 

运行时,为了让“args[0]”编译通过,要把调用哪个类的完整名字进行如下操作:

eclipse下,右键本代码→run asopen run dialogArguments→复制类名到Program arguments下→apply【等效于在命令行:java TestLei类名(此刻传的参数)】

eclipse下,箭头指到类名,按下F2键,可现实完成的类名(包名前缀)

6.       数组与Object的关系及其反射类型

数组的反射

           具有相同维数和元素类型的数组属于同一个类型。即具有相同的Class实例对象。

         代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class

           基本类型的一维数组可以被当作Object类型使用,不能当做Object[]类型使用(因基本类型不是Object);非基本类型的一维数组,即可以当做Object类型使用,又可以当做Object[]类型用。

           Arrays.asList()方法处理int[]String[]时的差异

           Array工具类用于完成读一数组的反射操作。

eg:    

int[] a1=new int[3];
		int[] a2=new int[4];
		int[] a3=new int[2][3];
		String[] a4=new String[3];
		System.out.println(a1.getClass()==a2.getClass());		//true
		System.out.println(a1.getClass()==a4.getClass());		//false
System.out.println(a1.getClass()==a3.getClass());		//false
System.out.println(a1.getClass().getName());		//[I
System.out.println(a1.getClass().getSuperclass().getName());	//java.lang.Object
System.out.println(a4.getClass().getSuperClass().getName());	//java.lang.Object

Object aObj1=a1;
Object aObj2=a4;
//Object[] aObj3=a1;		//【×】
Object[] aObj4=a3;		//a3唯一个一维数组的数组。
Object[] aObj5=a4;		//a4这个数组装的是String,String是Object。
/*
如果把上述a1和a4修改一下。进行如下操作
*/
int[] a1=new int[]{1,2,3};
String[] a4=new String[]{“a”,”b”,”c”};
System.out.println(a1);					//[I@1cfb549
System.out.println(a4);					//[Ljava.lang.String;@186d4c1
System.out.println(Arrays.asList(a1));		//[[I@1cfb549]
System.out.println(Arrays.asList(a4));		//[a,b,c]

注:因为jdk1.4中,asList方法参数(Object[] a

         jdk1.5 asList方法参数(T…..a(可变参数)

         1.5兼容1.4所以当Arrays.asList(a1)过来时,不能解释为Object[],1.5方式处理为1个参数;而String[]符合了Object[]

 

                   数组的反射应用

                   (接上)

printObject(a4);				
		printObject(“xyz”);
		/*
		结果为:
				a
				b
				c
				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,i));
}
}else{
	System.out.println(obj);
}
}


 

思考:如何得到数组中的元素类型?——————无法得到;

                   只能得到每一个元素类型,不可能得到数组类型。

         egObject[] a=new Object[]{“a”,1};

                   a[0].getClass().getName();

         int[] a=new int[3];      a无法判断前面为int

 

ArrayList_HashSet的比较及HashCode分析

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

         框架与框架要解决的核心问题

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

                            框架要解决的核心问题

我在写框架时,用户可能还在上小学,还不会写程序。写的框架程序怎样能调用到你以后写的类呢?因为在写程序时无法知道要被调用的类名。所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。

                           

                            综合案例

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

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

                            eg:  

public class ReflectTest{
				public static void main(String[] args){
					Collection collections=new HashSet();
					ReflectPiont 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;
					//collections.remove(pt1);	//输出结果还是2,原因见下。
					System.out.printyln(collections.size());
}
//重写之前Reflection类中的hashCode()与equals()方法【在eclipse可自动//生成。方法:右键→Source→Generate hashCode() and equals(),勾选x,y→ok】
@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;
	final ReflecPoint other=(ReflectPoint)obj;
	if(x!=other.x)
		return false;
	if(y!=other.y)
		return false;
	return true;
}
}

注:当一个对象被放进HashSet集合中后。就不能修改对象参数与计算哈希值的字段了,否则导致与最初存进去的对象的哈希值不同。造成:内存泄露。

如果不复写hashCodeequals()上面结果是3;如果只写equals()结果可能是3也可能是2.

如果不重写hashCode,可能就不能单靠equals()来确定相等,因为,在按内存算时,同样的参数可能被放到不同的hashCode算法的字段区域,所以结果不确定。【如果不存到hashSethash算法中,不用覆写hashCode,只覆写equals()

 

框架的概念及用反射技术开发框架的原理

利用反射完成上例中HashSetArrayList

方法:在本包中,建立一个配置文件(到时候直接配置文件就好)

在本包名上:右键→NewFileFilenameconfig.properties→确定

点击这个文件→下面Source→(写内容)className=java.util.ArrayList

然后在原程序上修改【尽量面向接口和父类编程】所以用InputStream

InputStream ips=new FileInputStream(“Config.properties);
Properties props=new Properties();
props.load(ips);
ips.close();
String className=props.getProperty(“className”);
Collection collections=(Collection)Class.forName(className).newInstance();
ReflectionPoint pt1=…….;
ReflectionPoint pt2=…….;
……(后面参考上面程序)

注:Properties对象等HashMap,装的是keyvalue。但此HashMap高级的是,可以把自己内存的键值对存到硬盘中,可以在初始化时,把键值对加载进来。

同时养成好习惯关资源。此时为关闭调用的系统资源,并非ips,防止内存泄露。


 

java反射原理

  • 2016年03月21日 09:30
  • 45KB
  • 下载

java反射机制

  • 2016年06月05日 21:04
  • 7KB
  • 下载

通过java反射获取任意对象的字段名及字段值

import java.lang.reflect.Field; public class ReflectClass3 { /** * @param args */ public sta...
  • CTLLIN
  • CTLLIN
  • 2014年05月09日 00:30
  • 23028

Java反射机制

  • 2015年02月27日 21:08
  • 845KB
  • 下载

Java 语言的反射机制

  • 2013年08月29日 22:43
  • 185KB
  • 下载

java反射:根据给定类名实例化类并调用其方法

根据文件流读来的类名实例化类并调用方法

JAVA反射与代理

  • 2014年03月06日 01:00
  • 10KB
  • 下载

Android之Java反射使用:阻止AlertDialog的dismiss事件

知识点: 1、利用反射,阻止AlertDialog每次的dismiss事件; 在使用AlertDialog的时候,我们设置positive,negative和neutral的button,...

Java泛型和反射机制

  • 2014年01月07日 11:20
  • 626KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java 反射
举报原因:
原因补充:

(最多只允许输入30个字)