黑马程序员--反射

--------------android培训java培训、学习型技术博客、期待与您交流! --------------

反射概述    

通过Java的反射机制,在运行状态中,对于任意一个类(.class文件),都能够知道这个类中的所有属性和方法;对于任意一

个对象,都能够调用它的任意一个方法和属性。这种动态获取类的信息及动态调用对象的方法功能称为Java语言的反射机制。

               结论:反射技术可以对类进行解剖。

 

反射原理:

            平常的java类是通过一些固定的支架(方法,成员变量等)搭建来的,搭建完成后它就具备了描述一类事物的特性。而这些支架也属于事物,它也能够被类描述。所以java中就提供了Class类进行描述。Class类所描述的事物是:java类在编译完成后,会生成class文件存在硬盘上。也就是字节码。把字节码(保存了该类的基本信息)加载到内存中来。返回的类型是Class,当然Class就具备了描述该类固定支架(方法,成员变量等)的特性。

 

得到字节码的方法3种:

          类名.class  例如,System.class

          对象.getClass() 例如,new Date().getClass();

          Class.forName(),例如,Class.forName("java.util.Date");

         提示:如果字节码已经在内存中,直接拿过来。

                     如果字节码不在内存中,需要从硬盘上把字节码加载到内存中去。

 

分析Class类

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {

		String abc = "afd";
		Class cls1 = abc.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		// 虚拟机上做出该类一份字节码,其他要用就直接拿了。所以打印结果都为true
		System.out.println(cls1 == cls2);
		System.out.println(cls1 == cls3);

		// 判断是否是基本数据类型的字节码isPrimitive()。false
		System.out.println(cls1.isPrimitive());
		// 基本类型的字节码和对应的封装类的字节码是不相同的。false、
		System.out.println(int.class == Integer.class);
		// TYPE常量代表包装类型所包装的基本类型的字节码。true
		System.out.println(int.class == Integer.TYPE);
		// 数组的字节码类型,不是原始类型false。
		System.out.println(int[].class.isPrimitive());
		// 数组的字节码类型,判断一个Class类型是否为数组用isArray()。true
		System.out.println(int[].class.isArray());

		
		// Class类型,判断基本数据类型:isPrimitive();判断数组类型:isArray();
		// 总之:只要是在源程序中出的类型,都有各自的Class
	}

}

九个预定义的Class

                1)包括八种基本类型(byteshortintlongfloatdoublecharboolean)的字节码对象和一种返回值为void类型的void.class

                2Integer.TYPEInteger类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示

               只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class。数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。

 

反射的概念

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

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

 Constructor类

1、概述

        如果指定的类中没有空参数的构造函数,或者要创建的类对象需要通过指定的构造函数进行初始化。这时怎么办呢?这时就不能使用Class类中的newInstance方法了。既然要通过指定的构造函数进行对象的初始化。就必须先获取这个构造函数——ConstructorConstructor对象代表某个类的构造方法。

2、获取构造方法:

        1)得到这个类的所有构造方法:如得到上面示例中Person类的所有构造方法

              Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();

        2)获取某一个构造方法:

              Constructor con=Person.class.getConstructor(String.class,int.class);

3、创建实例对象:

        1)通常方式:Person p = new Person(“lisi”,30);

         2)反射方式:Person p= (Person)con.newInstance(“lisi”,30);

注:

        1、创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。

         2newInstance():构造出一个实例对象,每调用一次就构造一个对象。

         3、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。

//Constructor类代表某个类中的一个构造方法。
//得到构造方法:     通过构造函数参数列表的类型,来确定获取的具体构造类。
		Constructor<String> strCon = String.class.getConstructor(StringBuffer.class);
//		new String(new StringBuffer("abc"));
//创建实例对象:
//		1,在对strCon进行实例化对象的时候,需要指定参数对象。
		String str2 = strCon.newInstance(new StringBuffer("abc"));
		System.out.println(str2.charAt(2));
//		2,Class类提供了一个便利,有newInstance方法。直接可以创建对象。省去了获取Constructor再去创建对象。
//		反射的缺点:反射比较消耗性能占用时间,进行反射以后把得出的结果存在缓存起来。为后面尽可能的减少反射的使用量。
		String s = String.class.newInstance();

Field类

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

2、方法

       Field getField(String s);//只能获取公有和父类中公有

        Field getDeclaredField(String s);//获取该类中任意成员变量,包括私有

        setAccessible(ture);

        //如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。

        set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。

        Object get(Object obj);//返回指定对象上Field表示的字段的值。

import java.lang.reflect.Field;

public class FieldDemo {
	public static void main(String[] args) throws SecurityException, NoSuchFieldException, InstantiationException, IllegalAccessException {
		ReflectPoint p = new ReflectPoint(3,5);
//		字段通过字段名字区分。它不代表某个特定对象上面的值。而是告诉程序员,这个叫y的变量已经给你取到了。封装成了一个类。
//		类上的变量,不是对象上的变量。不包括私有的。
		Field fieldY = p.getClass().getField("y");
//		从特定对象上取值。这个异常说明了,成员是私有的不让访问。
//		Exception in thread "main" java.lang.NoSuchFieldException: y
//		at java.lang.Class.getField(Class.java:1520)
//		at reflect.FieldDemo.main(FieldDemo.java:10)

		System.out.println(fieldY.get(p));
//		获取所有申明的变量。包括private类型。
		Field fieldX = p.getClass().getDeclaredField("x");
//	 java.lang.IllegalAccessException: Class reflect.FieldDemo can not access a member of class reflect.ReflectPoint with modifiers "private"
//		上面异常的出现说明了,没有获取私有变量值的权限。
//		如果想要获取的话,需要进行暴力反射。
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(p));
//		Class c = String.class;
//		String s = (String) c.newInstance();
//		
//		String s1 = String.class.newInstance();
	}
}
将一个对象上所有的String类型的值中含有a的,进行替换。
	private static void changeValue(ReflectPoint p) throws IllegalArgumentException, IllegalAccessException {
//	获取ReflectPoint中所有申明的字段。
		Field[] fs = p.getClass().getDeclaredFields();
//		遍历字段。
		for(Field f:fs){
//			getType获取Field的类型,用==好,因为字节码只有一份。如果是String类型的话,就进行操作。
			if(f.getType()==String.class){
				String oldValue = (String)f.get(p);
				String newValue = oldValue.replace("b", "a");
				f.set(p,newValue);
				System.out.println(newValue);
			}
		}
	}
}


反射的作用:你给我一个对象我能把他里面的值都改掉。例如Spring框架中用的就是这个技术。你给我一个类,我就能用你给的资源来做些事情。

Method类

1、概述:Method类代表某个类中的一个成员方法。调用某个对象身上的方法,要先得到方法,再针对某个对象调用。

2、专家模式:谁调用这个数据,就是谁在调用它的专家。

如人关门:

        调用者:是门调用关的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。

        指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。

        总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。

3、方法

        Method[] getMethods();//只获取公共和父类中的方法。

        Method[] getDeclaredMethods();//获取本类中包含私有。

        Method   getMethod("方法名",参数.class(如果是空参可以写null);

        Object invoke(Object obj ,参数);//调用方法

        如果方法是静态,invoke方法中的对象参数可以为null

如:

获取某个类中的某个方法:(如String str =abc”)

        1)通常方式:str.charAt(1)

        2)反射方式:

                                  Method charAtMethod =Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

                                  charAtMethod.invoke(str,1);

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

//	通过方法名字,和参数列表来确定方法。导包的时候看到com...这种包是内部用的(不要导),看到java...这就是外部用的。
		String str = "abc";
		Method methodCharAt = String.class.getMethod("charAt", int.class);
		
//		已知Method,调用谁的Method并且还要给它传参数。
		Character ch = (Character) methodCharAt.invoke(str, 2);
		System.out.println(ch);
//		静态方法的调用,因为静态方法不需要对象。第一个参数应该是null。所以:
//		Character ch2 = (Character) methodCharAt.invoke(null, 2);
		
		String className= args[0];
		invokeMain(className);

 

调用其他类的main方法

      为什么要用反射的方式调用main ?

      main的参数是String数组,数组里面可以装类名字符串。

           获取这这些类的名字。通过反射的原理可以获得各个类中的main方法。

 分别执行这几个类

private static void invokeMain(String className) throws Exception {
		Method main = Class.forName(className).getMethod("main", String[].class);
//		main是静态,所以第一个参数是空的。
//	java.lang.IllegalArgumentException: wrong number of arguments jdk1.4特点,给进去一个数组会将数组拆掉,拆掉后的值作为参数造成的。导致参数变多了。
		main.invoke(null, new Object[]{new String[]{"1","2","3"}});
//		main.invoke(null, (Object)new String[]{"1","2","3"});
	}


数组的反射

 

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

public class ArrayReflectDemo {
	public static void main(String[] args) {
		int[] a = new int[3];
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[3];
		// 数组class是否相同:1.数据类型2数组的维数
		System.out.println(a.getClass() == a2.getClass());
		// 数组的父类是Object
		System.out.println(a4.getClass().getSuperclass().getName());
		//	
		Object aObj1 = a;
		Object aObj2 = a2;
		// 因为a是一维数组,里面装的是int型变量,不是Object子类。不行。
		// Object[] obj1=a;
		// 二维数组,数组里面套数组。内层数组(一维数组)相当于是Object。可以
		Object[] aobj3 = a3;
		// String数组里面装的是String类型的变量。可以
		Object[] aobj4 = a4;
		System.out.println(Arrays.asList(a));
		System.out.println(Arrays.asList(a4));
//		数组的反射。
		
		printObjcet(a4);
	}

	private static void printObjcet(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));
			}
		}
	}
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值