——java中的反射

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

什么是反射?

一,反射就是把java类中的各个成员,提取成其对应的java类对象。

例如:一个java类中用一个Class类的对象来表示,一个类中的组成部分都有对应的类:

Field —— 成员变量;

Method —— 成员方法;

Constructor —— 构造函数;

Package ——包

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

实例对象,就可以执行相应的操作了。


反射的基石 -- Class类

1,java中的各个java类属于同一类事物,描述这类事物的java类名就是Class。

注:对比:众多人用一个Person类表示,众多的java类就用Class表示

a,一个人  -->Person

b,java类  -->Class

2,Class类代表java类,它的实例对象分别对应什么呢?

1,对应各个类在内存中字节码,例如,String类的字节码,Person类的字节码等。。。

2,一个类被类加载器加载到内存中,占用一片存储空间,这个空间的内容就是类的字节码,不同的类的字节码是不同的,

所以它们在内存中的内容是不同的,这一个个空间分别用一个个的对象来表示,这些对象具有相同的类型即Class类型。

3,如何得到各个字节码对应的实例对象(Class类型)

有3中方式:

1:类名.class;例如:System.class

2:对象名.getClass();例如:new Date().getClass()

3:Class.forName(类的全名称(包名+类名)字符串);例如:Class.forName("java.util.Date");

4,九个预定义Class实例对象:

1,boolean.class = Boolean.TYPE,
2,char.class = Character.TYPE, 
3,byte.class = Byte.TYPE, 
4,short.class = Short.TYPE, 
5,int.class = Integer.TYPE, 
6,long.class = Long.TYPE, 
7,float.class = Float.TYPE, 
8,double.class = Double.TYPE,
9,void.class = Void.TYPE

5,数组类型的Class对象实例对象

1,Class.isArray()

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

构造函数的反射应用

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

二,得到某个类所有的构造方法:

示例:Constructor[]  constructors = Class.forName("java.lang.String").getConstructors();

三,得到某一个构造方法://获取时要用到参数类型

示例:Constructor  constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

四,创建示例对象:

示例:

1,普通方式:String str = new String(new StringBuffer("abc"));

2,反射方式:

Constructor  constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

String str = (String)constructor.newInstance(new StringBuffer("abc"));

五,Class.newInstance()方法:

1,示例:String obj = (String)Class.forName("java.lang.String").newInstance();

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

3,该方法内部的具体事项代码,用到了缓存机制来保存默认构造方法的示例对象。

构造函数反射的示例代码:

package com.itheima.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 反射操作构造函数
 * @author wuyong
 *
 */
public class ConstrutorDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			Constructor constructor1 = String.class. getConstructor(StringBuffer .class);
			String str2 = (String) constructor1.newInstance (/*"abc"*/new StringBuffer("abc"));
			System.out .println(str2.charAt (2));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}


成员变量的反射

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

语法:

  类名 对象名 = new 类();

  Field field = 对象名.getClass().getField(属性名字符串);

  此时field对象并不代表具体的值,只代表该类下的成员变量的一个对象,

  要获取到指定对象里的成员变量的值,语法如下:

  field.get(对象名);指明要取的成员变量的值属于哪个对象里的。
  
注:一,getField()和getDeclareField()方法的区别:

    getField()方法只能获取到访问级别为可见的属性,不能获取到不可见的属性,若调用该方法

      获取不可见的属性,会报出:notFoundFieldException的异常。

    getDeclareField()方法可以获取该类中声明的所有属性,包括private所修饰的。

    二,使用Field 对象下的get(对象名)来获取该对象下的属性时,如果属性的访问修饰符为不可见时,

     则会报出IllegaAccessException()异常。若要获得不可见的属性的话,可以用以下的方式来强行

     获得,Field对象名.setAccessible(true);设置访问级别为可见。(称为暴力反射)

  例子:将一个类中的所有String类型中的"b"全部换成"a";

注:字节码比较时,要用"=="来比较,因为是同一份字节码。

成员变量反射的实例代码:

package com.itheima.reflect;

import java.lang.reflect.Field;

/**
 * 成员变量的反射
 * @author wuyong
 *
 */
public class FieldDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			ReflectPoint point = new ReflectPoint(3,5 );
			Field fieldY = point .getClass().getField("y" );
			
			//fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
			System.out .println("成员变量Y的值:" + fieldY.get (point));
			Field fieldX = point .getClass().getDeclaredField("x" );
			//由于X是不可见的,所以就要调setAccessible,并设为true的方式。用暴力反射的方式来获得。
			fieldX.setAccessible (true);
			System.out .println("成员变量X的值:" + fieldX.get (point));     
			
			//2)将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
			Field name = point.getClass().getField("name");
			System.out.println("替换之前:" + name.get(point));
			changeStringValue(point);
			System.out .println("替换之后:" + name.get(point));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 定义一个将所有字符串的类成员变量中的”a“ 变成 ”b“的方法
	 * @param obj
	 * @throws Exception
	 */
	private static void changeStringValue(Object obj ) {
		    try {
				Field[] fields = obj. getClass().getFields ();
				for(Field field : fields ){
				     //if(field.getType().equals(String.class)){
				     if(field .getType() == String .class){
				        String oldValue = ( String)field .get( obj);
				        String newValue = oldValue.replace("b" , "a" );
				        field .set( obj, newValue );
				     }
				}
			} catch (Exception e) {
				e.printStackTrace();
			}   
		}

}
/**
 * 被反射操作的类
 * @author wuyong
 *
 */
class ReflectPoint {
	private int x;
	public int y;
	public String name = "a1b2c3d4c3b2a1";
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}
	public static void main(String[] args){
		for (int i = 0; i < args.length; i++) {
			System.out.println(args[i]);
		}
	}
}


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

  语法:

Class cls = TestUtil1.class;

TestUtil1 tu1 = (TestUtil1)cls.newInstance();
Method method = cls.getDeclaredMethod("show", null);//指定获取方法名为“show”,并且没有参数的方法

method.invoke(tu1, null);//在对象tu1中执行无参的方法

Method method2 = cls.getDeclaredMethod("show2", null);

method2.invoke(null, null);//当对象参数为null时,表示这是一个静态方法

调用方法的方式:

  一,使用对象名.方法名。调用;

  二,使用反射来获得对应的方法对象,来调用invoke(Object obj,Object...args)执行方法;

其中obj是指那一个对象上的方法,args是表示该方法的参数列表


注:专家模式:哪个类拥有该方法需要的私有数据,那这个方法就属于哪个类。


用反射调用某个类的main方法

  语法:

String className = args[0];//获取第一个参数,首先要设置RunConfigurations中的Arguments为要执行的类的完整类名。

Class cls2 = Class.forName(className);//通过活动到的完整类名,获得该类的字节码

Method main = cls2.getMethod("main", new String[]{}.getClass());//通过字节码,获得该类的main方法

/*

* 因为为了兼容JDK1.4以下的版本,在传入参数的时候,会对参数进行装包;执行时,会对参数进行拆包,如果

* 单纯的传入一个数组的话,则在拆包的时候将每一个元素都拆成一个参数,此时会报出:IllegalArgumentException异常

* 解决方式有以下2种。

*/

//使用一个Object数组,再把传入的数组参数当成Object数组的一个元素在传入。

//main.invoke(null, new Object[]{new String[]{"abc","东方","123"}});

//将传入的参数强转成Object对象,

main.invoke(null, (Object)new String[]{"abc","东方","123"});

成员方法反射的示例代码:

package com.itheima.reflect;

import java.lang.reflect.Method;

/**
 * 类成员方法的反射
 * @author wuyong
 *
 */
public class MethodDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			//执行普通方法
			String str1 = "asdewfs21";
			//str1.charAt(1)
			Method methodCharAt = String .class. getMethod("charAt" , int .class);
			System.out .println("字符串的第二个字符是:" + methodCharAt.invoke (str1, 1 ));
			System.out .println("字符串的第二个字符是:" + methodCharAt.invoke (str1, new Object[]{2 }));
			
			//执行Person的main方法,及静态方法的演示
//			Person.main(new String[]{"111","222","333cc"});
			//静态方法由于不用对象就可以调用,所以对象那个参数是null。	
			//用反射的方式调用ReflectPoint的main方法,
			//注:运行前要设置当前主函数的参数步骤:Run Configureations ---> Arguments --->要调用的类的全名称如:com.itheima.reflect.ReflectPoint
			String startingClassName = args [0];
			Method mainMethod = Class .forName(startingClassName).getMethod ("main", String[].class);
			//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
			mainMethod.invoke (null, (Object )new String []{"111","222" ,"333"}); //数组,jdk会将数组拆成多个参数,从而出现参数不对异常。
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

}
/**
 * 被反射操作的类
 * @author wuyong
 *
 */
class ReflectPoint {
	private int x;
	public int y;
	public String name = "a1b2c3d4c3b2a1";
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}
	public static void main(String[] args){
		for (int i = 0; i < args.length; i++) {
			System.out.println(args[i] + "aa");
		}
	}
}


数组的反射,及数组与Object

数组的反射

  1,具有相同位数(例如:一维对一维)和元素类型的数组属于同一个类型,即具有相同的Class实例对象

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

  3,基本类型的一维数组可以被当做Object类型使用,不能当作Object[]类型使用;

     非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。

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

当处理的是一维的基本数据类型的数组时,其保存的是一维数组的完整名称,即内存中的地址如:[I @a6d7f3。但二维的基本数据类型数组是可以遍历的。

当处理的是引用类型的数组,则会将每一个元素都保存到集合中。

  5,Array工具类用于完成对数组的反射操作。

  6,通过反射获取元素的步骤:

1,获取数组的字节码:对象.getClass()

2,判断是否是数组:字节码对象.isArray()
3,获取数组的长度:getlength(Object obj)

      4,遍历数组元素

      5,通过元素.getClass().getName()方法获取元素对应的类型


数组反射的示例代码:

package com.itheima.reflect;

import java.lang.reflect.Array;
import java.util.Arrays;

/**
 * 数组的反射,及数组与Object对象
 * @author wuyong
 *
 */
public class ArrayDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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 = a2" + (a1.getClass () == a2.getClass ())); //true , 相同的类型和相同的维数有相同字节码对象
		//JDK1.5之后,编译错误,因为出现了泛型是。类型不同,没有可比性
//		System.out .println(a1.getClass () == a4.getClass ());//false
		//JDK1.5之后,编译错误维数不同,没有可比性
//		System.out .println(a1.getClass () == a3.getClass ());//false
		System.out .println("a1的名称" + a1.getClass ().getName());//[I
		System.out .println("a1的父类名称" + a1.getClass ().getSuperclass().getName());//java.lang.Object
		System.out .println("a4的父类名称" + a4.getClass ().getSuperclass().getName());//java.lang.Object


		Object aObj1 = a1 ;
		Object aObj2 = a4 ;
		//Object[] aObj3 = a1;//不行,因为基本数据类型不是Object
		Object[] aObj4 = a3; //二维数组,将一维数组作为一个对象,
		Object[] aObj5 = a4; //string本身就是一个对象


		System.out .println("a1的toString()形式:" + a1);
		System.out .println("a1的toString()形式:" + a4);
		//基本类型的一维数组传入asList方法后,会将数组当成一个对象元素存入集合中。
		System.out .println("a1的保存集合后的形式:" + Arrays.asList (a1)); //[I@12344 asList(T...t) 作为对象传递进去了。
		//引用类型就可以遍历
		System.out .println(Arrays.asList (a4)); //[a,b,c]  asList(Object[] obj) 兼容jdk1.4,作为数组传递进去


//		数组的反射


		//传递数组调用
		printObject(a4 );
		
		//传递对象调用
		printObject("xyz" );
	}
	/**
	 * 数组的反射,如果是数组则遍历,如果是当对象就输出
	 * @param obj
	 */
	private static void printObject(Object obj ) {
        Class clazz = obj. getClass();
         if(clazz .isArray()){ //如果是数组,拆开数组,打印里面的元素
             int len = Array.getLength (obj);
             for(int i=0;i 


---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------







































 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章