关闭

黑马程序员--自学笔记--反射

标签: 技术java黑马程序员反射
221人阅读 评论(0) 收藏 举报
分类:

反射

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

一.概述

    Java反射机制是在运行状态中,对于任意一个类(class文件),都能够查询并操作这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
    总而言之,反射就是把Java类中的各种成分映射成相应的Java类。比如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分可分为:成员变量,方法,构造方法,包等。这些成分都可以用一个个的Java类来表示。表示java类的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息用相应的类的实例对象来表示,它们分别是Field,Method,Contructor,Package等等。

二.Class类字节码文件

    用于描述字节码的类就是Class类。只要创建Class类对象,就可以提取出字节码文件中的各种内容包括:字段、构造函数、一般函数。反射就是依靠Class类来完成的。如果要想对一个类文件中的内容进行操作,就要先获取到该类的字节码文件对象。
    获取Class对象的三种方式:(代码演示)
① 第一种方式:
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong><strong><span style="font-size:14px;">/*
	 * 1.使用object类的getClass()方法;
	 * 		(此种方式必须明确具体的类,并创建对象)
	 */
	private static void getClassObject_1() {
		
		Person p1 = new Person() ;
		Class clazz = p1.getClass() ;
		System.out.println(clazz);
	}</span></strong></strong></span></span></strong></span></strong>
② 第二种方式:
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>	/*
	 * 2.通过利用任何数据所具备的一个静态属性.class来获取其对应的class对象
	 * 		(此种方式不够扩展,还是需要明确用到类中的静态成员)
	 */
	private static void getClassObject_2() {
		
		Class clazz = Person.class ;
		System.out.println(clazz);
	}
</strong></span></span></strong></span></strong>
③ 第三种方式:
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>	/*
	 * 3.使用Class类中的forname(String str)方法,通过传入类的名字来获取其对应的class对象
	 * 		(传入类名时,需将类的整个包名传入。该方法更为扩展)
	 *
	 */
	private static void getClassObject_3() throws ClassNotFoundException {
		
		String className = "cn.itzixue.bean.Person" ;
		
		Class clazz = Class.forName(className) ;    // 需要完整的类名
		System.out.println(clazz);
	}</strong></span></span></strong></span></strong>
    相比三种获取方式,第一种方式最不灵活,因为这种方式每次都需要具体的类和该类的对象,以及调用getClass方法;第二种方式虽然比第一种较为简单,不用创建对象,也不用调用getClass方法,但是还是要使用具体的类,和该类中的一个静态属性class完成。而第三种方式最简单,只要知道类的名称即可。这种方式不需要使用该类,也不需要去调用具体的属性和行为就可以获取到Class对象。因为第三种方式仅知道类名就可以获取到该类字节码对象的方式,所以更有利于扩展,开发中最常使用。
    编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。其中,使用new创建对象是静态加载类,在编译时可就需要加载所有的可能使用到的类。而动态加载类是在运行时刻进行加载,反射利用最多的就是动态加载类。

三.获取Class中的构造函数

    获取构造函数一般使用的是Class类对象的getConstructor()方法来获取(该方法返回一个Constructor类对象)。如果构造方法是需要传入参数的,则在创建对象时要使用Constructor类对象的newInstance()方法;若是空参构造函数,则可以使用Class类中的newInstance()方法。
代码演示:
① 调用类的空参构造方法创建一个新的对象(传统方法):(注意:注释中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>	/*
	 	通过new来实例化对象,在new的时候,先根据被new的类的名称找寻该类的字节码文件,
		并加载进内存,接着创建该字节码文件对象,并接着创建该字节码文件对应的Person对象。
	*/
	public static void creatNewObject_1() {
		
		Person p1 = new Person() ;
	}</strong></span></span></strong></span></strong>
② 调用类的空参构造方法创建一个新的对象(利用反射机制): (注意:注释中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>	/*
	 	先利用Class类的fotName(String str)方法找寻所传入名称的类文件吗,并加载进内存,接着产生Class对象。
		通过利用Class对象的newInstance()方法来创建所传入类的对象。(该方法扩展性强)
	*/
	public static void creatNewObject_2() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		
		String className = "cn.itzixue.bean.Person" ;
		Class clazz = Class.forName(className) ;
		Object obj = clazz.newInstance() ;
		
	}</strong></span></span></strong></span></strong>
③调用类的带参构造方法创建一个新的对象(利用反射机制): (注意:注释中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>	/*
	  	先通过Class类对象的getConstructor(Class<?>... parameterType)方法
		来获取制定大的公共构造方法。(该方法返回一个Constructor对象,若要获取包括私有在内的指定构造方法,
			使用getDeclaredConstructor(Class<?>... parameterType)方法)。
		再通过调用所获得的对象的newInstance()方法进行对象初始化。
	*/
	public static void creatNewObject_3() throws Exception {
		
		String className = "cn.itzixue.bean.Person" ;
		Class clazz = Class.forName(className) ;
		//获取指定的构造函数对象
		Constructor constructor = clazz.getConstructor(String.class,int.class) ;
		//通过该构造器对象初始化对象(结果返回的是Object对象)
		Object obj = constructor.newInstance("权权",21) ;
		
	}</strong></span></span></strong></span></strong>

    通过Class类对象的getConstructors()方法可以获取Class字节码文件中的所有公有的(包括父类)构造函数:getDeclaredConstructors()方法可以获取Class字节码文件中的所有(仅本类但包括私有的)构造函数。

四.获取Class中的字段

    获取class字节码文件中的字段一般使用的是Class类对象的getField()方法来获取(该方法返回一个Field类对象)。
Class类对象中操作字段的相关方法代码演示:
    ① (返回值类型:Field) getField(String str) ;    // 此方法只能获取本类或者父类中的公有的字段
    ② (返回值类型:Field) getDeclaredField(String str) ;    // 此方法只能获取该类中的字段,但是包括私有
Field类中
    ① (返回值类型:void) setAccessible(true) ;    // 通过此方法,可以让该私有字段进行取消权限检查的能力,以便于更轻松的访问(也称之为暴力访问)
    ② (返回值类型:void) set(Object obj, Object value) ;    // 将指定对象变量上此Field对象所表示的字段设置为指定的新值
    ③ (返回值类型:void) get(Object obj) ;    // 返回指定对象上Field表示的字段的值
代码演示:(注意:注重中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;">	public static void getFieldDemo() throws Exception {
		
		Class clazz = Class.forName("cn.itzixue.bean.Person") ;
		
		//通过Class类的对象的getField("字段名称")方法获取制定类中共有的字段(只能获取共有,可以获取父类)
		//通过Class类的对象的getDeclaredField("字段名称")方法获取制定类中共有的字段(只能获取本类,但是可以包含私有)
		Field field = clazz.getDeclaredField("age") ;
		
		/*通过获得的字段处理其对应的值
			例如:获取字段的值,可用字段对象的get(?)方法,所传入的?必须是该字段所在类的对象
				(如果在类中该字段被定义成私有的,则用该方法会抛出无效访问异常)
		 	对私有字段的访问,可以通过其getAccessible(true)方法对其进行暴力访问(不建议!!!)*/
		Constructor constructor = clazz.getConstructor(String.class,int.class) ;
		Object obj = constructor.newInstance("权权",21) ;
		field.setAccessible(true) ;
		Object o = field.get(obj) ;
		System.out.println(o) ;
		
		//设置所获得的字段的值
		field.set(obj, 22);
		o = field.get(obj) ;
		System.out.println(o);
	<span style="font-size:14px;">}</span></span></strong>

    通过Class类对象的getField()方法可以获取Class字节码文件中的所有公有的(包括父类)字段;getDeclaredField()方法可以获取Class字节码文件中的所有(仅本类但包括私有的)字段。

五.获取Class中的方法

    获取class字节码文件中的方法一般可以通过调用getMethod(String name,Class<?>... parameterTypes)方法,该方法将方法名和其参数同时传入。(如果为空参数,则传入null即可。该方法返回一个Method类对象)。
获取class字节码文件中的方法代码演示:
    ① (返回值类型:Method[]) methods = Class.getMethods() ;    // 获取类(包括父类)中所有的公有方法
    ② (返回值类型:Method[]) methods= Class.getDeclaredMethods() ;    // 获取类中所有方法(包括私有但不包括父类)
    ③ (返回值类型:Method) method = Class.getMethod(String name, 参数类型.Class,…参数类型.Class) ;
    // 返回一个带参数的Method对象,它反映此Class对象所表示的类或接口的指定公共方法
对获取到的Method对象进行调用代码演示:
    (返回值类型:Object) invoke(Object obj,参数) ;    
    // 由obj对象进行方法调用,如果该方法是静态的,则invoke方法中的对象参数可以置为null
代码演示: (注意:注重中包含重要知识点和注意点)   
① 获取无参方法
<strong><span style="font-family:KaiTi_GB2312;"><span style="font-size: 14px;">	public static void getMedthodDemo_1() throws Exception {
		
		Class clazz = Class.forName("cn.itzixue.bean.Person") ;

		//可以通过Class对象的getMethod()方法来获取类中所有公共方法(包括父类)。
		Method[] method_1 = clazz.getMethods() ;
		//该方法将返回一个Method数组,可通过foreach遍历
		for(Method m : method_1){
			System.out.println(m);
		}
		
		//可以通过Class对象的getDeclaredMethod()方法来获取类中包括私有在内的所有方法(不包括父类)。
		Method[] method_2 = clazz.getDeclaredMethods() ;
		//该方法将返回一个Method数组,可通过foreach遍历
		for(Method m : method_2){
			System.out.println(m);
		}
	</span><span style="font-size:14px;">}</span></span></strong>
② 获取有参方法并对其进行调用
<strong><span style="font-family:KaiTi_GB2312;"><span style="font-size:14px;">	public static void getMedthodDemo_2() throws Exception {</span>
		
		Class clazz = Class.forName("cn.itzixue.bean.Person") ;
		
		/*获取类中指定的方法,可以通过调用
			getMethod(String name,Class<?>... parameterTypes)方法,
				该方法将方法名和其参数同时传入。(如果为空参数,则传入null即可)*/
		Method method_1 = clazz.getMethod("show", null) ;
		Method method_2 = clazz.getMethod("show", String.class) ;
		System.out.println(method_1);
		System.out.println(method_2);
		
		/*若想调用所取到的Method对象,可以使用Method对象的
			invoke(Object obj,Object... args)方法,
				该方法需同时传入该类的对象以及相应的参数。*/
		Constructor constructor = clazz.getConstructor(String.class,int.class) ;
		Object obj = constructor.newInstance("权权",24) ; 
		method_1.invoke(obj, null) ;
		method_2.invoke(obj, "No1") ;
		
	}</span></strong>

六.总结

    反射机制的灵活性很大,使用反射机制,我们可以极大的提高程序的扩展性。同时,反射机制使用的是动态加载,动态编译的优点是最大限度发挥了java的灵活性,又体现了多态的应用,也可以很大程度上降低类之间的藕合。这种机制,在框架的编写上最常使用到。






0
0

猜你在找
【直播】机器学习&数据挖掘7周实训--韦玮
【套餐】系统集成项目管理工程师顺利通关--徐朋
【直播】3小时掌握Docker最佳实战-徐西宁
【套餐】机器学习系列套餐(算法+实战)--唐宇迪
【直播】计算机视觉原理及实战--屈教授
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之矩阵--黄博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之凸优化--马博士
【套餐】Javascript 设计模式实战--曾亮
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:9069次
    • 积分:343
    • 等级:
    • 排名:千里之外
    • 原创:24篇
    • 转载:0篇
    • 译文:0篇
    • 评论:14条
    文章存档