黑马程序员-----基础加强-反射

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


1.反射的概念和用途总述

        反射首先是一种动态的思想,不再是硬编码。就是说在使用过程中,外部传入一个类,通过对这个类进行反射,再去按照反射定义者的意图使用这个类。传说中目标类和客户类的关系发生改变。

        做个比喻,饭店新请一个厨师,负责人就问他会做什么菜啊、调味品用什么量啊等等,等负责人了解之后就让厨师去做菜了,但是为了适应当地人口味,负责人还可以在下命令之前告诉厨师,少发花椒多放辣等。厨师的主要做菜功能不变,但是负责人可以要求他做一些功能上的调整。

反射的作用

反射的作用总结起来就一个:倒转了目标类和客户类的依赖关系。
以前我们设计程序,客户类要么依赖于目标类,要么依赖于目标类的接口。因为目标类是作为工具提供给客户类使用的,根据 java 基本语法规则,要使用某个类,必须知道该类提供的接口。有了反射之后,我们就可以方便是使用反射来实现框架,解除框架对于我们写的类——目标类,的依赖关系。
反射的概念和实现原理
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。反射就是 把 JVM 通过符号引用动态解析 java 类的字节码的能力映射成为各种 Java 类的成分类的机制,通过这个机制,java 把 JVM 动态解析符号引用的功能封装为各种 API 类公开给我们使用,这个机制允许我们可以 于运行时加载、探知、使用,编译期间完全未知的classes程序在运行时通过 Reflection APIs 取得任何一个 class 的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括 fields 和 methods 的所有信息,并 于运 行时改变该类的对象的 fields 内容或调用该类或者该类对象的 methods。这种动态获取类的信息以及动态调用对象的方法的功能就是Java 语言的反射(Reflection)机制。

2.Java类反射中所必须的类:

Java的类反射所需要的类并不多,它们分别是:Class、Field、Constructor、Method、Object,下面我将对这些类做一个简单的说明。
Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

3.详述各个反射类及代码示例其用法

1、Class类

被称为反射的基石的Class类究竟何德何能获得这一殊荣呢?静听分解:

首先,这家伙长得像极了class,我们一直使用的class啊。难道有血缘关系?私生子?八卦之火熊熊燃烧。不过,做学问要严肃!好吧,严肃点

Classclass的区别

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

2Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class

Class获取对象的方法(借鉴toShareBeauty同学的图)

Class功能函数代码示例

package cn.itcast.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @class: ReflectionClassDemo
 * @package: cn.itcast.reflect
 * @description: TODO
 * @author: vivianZhao
 * @date: 2013-7-20 上午10:55:13
 * @version: 1.0
 */
public class ReflectionClassDemo {

	public static void main(String args[]) throws Exception {
		ReflectionClassDemo ref = new ReflectionClassDemo();
		ref.getConstructor();

	}

	public void getConstructor() throws Exception {
		Class<?> c = Class.forName("java.lang.Long");
		Class<?> cs[] = { java.lang.String.class };

		System.out.println("\n-------------------------------\n");

		Constructor<?> cst1 = c.getConstructor(cs);
		System.out.println("1、通过参数获取指定Class对象的构造方法:");
		System.out.println(cst1.toString());

		Constructor cst2 = c.getDeclaredConstructor(cs);
		System.out.println("2、通过参数获取指定Class对象所表示的类或接口的构造方法:");
		System.out.println(cst2.toString());

		Constructor cst3 = c.getEnclosingConstructor();
		System.out.println("3、获取本地或匿名类Constructor 对象,它表示基础类的立即封闭构造方法。");
		if (cst3 != null)
			System.out.println(cst3.toString());
		else
			System.out.println("-- 没有获取到任何构造方法!");

		Constructor[] csts = c.getConstructors();
		System.out.println("4、获取指定Class对象的所有构造方法:");
		for (int i = 0; i < csts.length; i++) {
			System.out.println(csts[i].toString());
		}

		System.out.println("\n-------------------------------\n");

		Type types1[] = c.getGenericInterfaces();
		System.out.println("1、返回直接实现的接口:");
		for (int i = 0; i < types1.length; i++) {
			System.out.println(types1[i].toString());
		}

		Type type1 = c.getGenericSuperclass();
		System.out.println("2、返回直接超类:");
		System.out.println(type1.toString());

		Class[] cis = c.getClasses();
		System.out.println("3、返回 Class 中使用的所有的类和所有的接口:");
		for (int i = 0; i < cis.length; i++) {
			System.out.println(cis[i].toString());
		}

		Class cs1[] = c.getInterfaces();
		System.out.println("4、实现的接口");
		for (int i = 0; i < cs1.length; i++) {
			System.out.println(cs1[i].toString());
		}

		System.out.println("\n-------------------------------\n");

		Field fs1[] = c.getFields();
		System.out.println("1、类或接口的所有可访问公共字段:");
		for (int i = 0; i < fs1.length; i++) {
			System.out.println(fs1[i].toString());
		}

		Field f1 = c.getField("MIN_VALUE");
		System.out.println("2、类或接口的指定已声明指定公共成员字段:");
		System.out.println(f1.toString());

		Field fs2[] = c.getDeclaredFields();
		System.out.println("3、类或接口所声明的所有字段:");
		for (int i = 0; i < fs2.length; i++) {
			System.out.println(fs2[i].toString());
		}

		Field f2 = c.getDeclaredField("serialVersionUID");
		System.out.println("4、类或接口的指定已声明指定字段:");
		System.out.println(f2.toString());

		System.out.println("\n-------------------------------\n");

		Method m1[] = c.getMethods();
		System.out.println("1、返回类所有的公共成员方法:");
		System.out.println(m1.length);
		for (int i = 0; i < m1.length; i++) {
			System.out.println(m1[i].toString());
		}
		
		Method m3[] = c.getDeclaredMethods();
		System.out.println("2、返回类自己定义所有的成员方法:");
		System.out.println(m3.length);
		for (int i = 0; i < m3.length; i++) {
			System.out.println(m3[i].toString());
		}

		Method m2 = c.getMethod("longValue", new Class[] {});
		System.out.println("3、返回指定公共成员方法:");
		System.out.println(m2.toString());

	}
}

2、构造方法的反射应用_Constructor 类

package cn.itcast.day1;
import java.lang.reflect.Constructor;
public class ConstructorDemo {

	public static void main(String[] args) throws Exception{
		//得到某个类所有的构造方法
		Constructor<?>[] constructors = Class.forName("java.lang.String").getConstructors();		
		//取得指定类的构造方法
		Class classType = Class.forName("java.lang.String");
        Constructor constructor = classType.getDeclaredConstructor(StringBuffer.class);        
        /*创建实例对象*/
        //通常方式:
        String str = new String(new StringBuffer("abc"));
        //反射方式
        String str1 = (String)constructor.newInstance(new StringBuffer("abc"));       
        //Class.NewInstance()方法
        String str2 = (String)Class.forName("java.lang.String").newInstance();
        /*该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。该方法的内部用到了缓存机制来保存默认构造方法的实例对象*/        
        //获得构造方法并创建实例对象
        Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
        /*getConstructor()中用到是不定长度参数,1.4版本之前,则是通过传入数组来调节参数类型和数组不确定的情况*/
        String str3 = (String)constructor1.newInstance(new StringBuffer("aaa"));
	}
}

3、成员变量的反射_Field 类

  //成员变量的反射
  ReflectPoint pt1 = new ReflectPoint(3, 6);
  //成员变量时共有的可以正常反射
  Field filedY = pt1.getClass().getField("y"); 
  System.out.println(filedY.get(pt1));

  
  //如果成员变量是私有的要强行反射getDeclaredField
  Field fieldX = pt1.getClass().getDeclaredField("x");
  //暴力反射修改字段的访问属性的方法方法 setAccessible(true); 这是继承自 java.lang.reflect.AccessibleObject 的方法
  fieldX.setAccessible(true);
  //获取
  System.out.println(fieldX.get(pt1));

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

<span style="font-size:14px;">import java.lang.reflect.Field;  
public class Reflectest {  
    public static void main(String[] args) throws Exception {  
        ReflectPoint pt1=new ReflectPoint();  
        changeStringValue(pt1);  
        System.out.println(pt1);  
    }    
    private static void changeStringValue(Object obj) throws Exception{  
        Field[] fields=obj.getClass().getFields();//获取所有的成员变量  
        //遍历成员变量  
        for(Field field:fields){  
  //比较字节码用==  
            if(field.getType()==String.class){  
            String oldValue=(String)field.get(obj);//获取obj的String类型的成员变量  
            String newValue=oldValue.replace('b', 'a');//将b换成a  
            field.set(obj, newValue);//将此 Field表示的字段设置为指定的新值  
             }  
        }  
    }  
}  
class ReflectPoint {  
    public String str1="ball";  
    public String str2="basketball";  
    public String str3="itcast";  
    //重写toString方法  
    public String toString(){  
        return str1+"  "+str2+"  "+str3+"  ";  
    }  
} </span>

4.成员方法的反射_Method类

得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
 调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: charAt.invoke(str, 1); 
如果传递给 Method 对象的 invoke() 方法的第一个参数为 null,说明该 Method 对象对应的是一个静态方法。
 jdk1.4和jdk1.5的invoke方法的区别:
jdk1.5:public Object invoke(Object obj,Object... args)
jdk1.4:public Object invoke(Object obj,Object[] args),按 jdk1.4的语法,需要将一个数组作为参数传递给 invoke 方法,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用 charAt 方法的代码也可以用 jdk1.4 改写为 charAt.invoke(“str”, new Object[]{1}) 形式。

package cn.itcast.day1;

import java.lang.reflect.Method;

public class MethoDemo {

	public static void main(String[] args) throws Exception {		
		Sring className = args[0];
		Method method = Class.forName(className).getMethod("main", String[].class);
		
		method.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});
		/*
		 * 如果按照一般写法,传递参数应该是这样:
		 * method.invoke(null, new String[]{"aaa","bbb","ccc"});
		 * 但是由于jvm自动拆包,会将String数组当作三个参数传入,这个main方法中只接受一个String[]不符,编译器会报错,所以有两种解决方案。
		 * 其一:像上述程序中所写的那样,在前面加上强制类型转换,告诉编译器这是一个整体,不要拆包
		 * 其二:可以这样写——method.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
		 * 	        定义一个Object类型数组,并将String[]整体作为一个元素放入数组中,编译器拆包后得到的便是一个String[]类型参数。
		 */
	}
}

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

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

<span style="font-size:14px;"><strong>/**
 * 需求:演示数组 和 Object 的关系
 * 
 * 思路:
 * 1.获取数组的  Class 对象,比较是否相等
 * 2.打印数组的  Class 对象的名字
 * 3.数组 和 Object 类型之间的类型转换
 * 
 * 步骤:
 * 
 * 总结:
 * 1.java 里面,相同元素类型和相同维度数的数组是同一个类型的数组,对应同一个 Class 对象
 * 2.数组类型的签名是" [ + 元素类型名签名 ",如果是多维数组,也是符合前面的规则,结果就成了几维数组会有几
 * 个" [ "符号
 * 3.数组类型可以向上转型为 Object 类型
 * 4.java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是数组中的元素还是数组,只有最后一层是一个非
 * 数组类型
 */
package cn.itcast.reflect;
public class ArrayAndObject {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int [] a1 = new int[4];
		int [] a2 = new int[5];
		int [][] a3 = new int[2][3];
		
		String [] a4 = new String[3];
		
		// 返回 true,说明同类型同维度的数组是同一个 Class 对象
		System.out.println(a1.getClass() == a2.getClass()); 
		// 不可比较,说明同类型不同维度的数组不是同一个 Class 对象
		//System.out.println(a1.getClass() == a3.getClass());
		// 不可比较,说明不同类型同维度的数组不是同一个 Class 对象
		//System.out.println(a1.getClass() == a4.getClass());
		
		// 数组类型的名称是 [ + 类型名签名,如果是多维数组,几维数组用几个 [
		System.out.println(a1.getClass().getName());
		System.out.println(a3.getClass().getName());
		System.out.println(a4.getClass().getName());
		// 数组类型的父类型都是 Object 类型
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a3.getClass().getSuperclass().getName());
		
		// 数组类型的父类都是 Object 类型,所以数组类型可以上转为 Object 类
		Object aObject1 = a1;
		Object aObject2 = a4;
		
		// 数组中的元素有两种类型,一种是基本类型,一种是引用类型
		//Object[] aObjects3 = a1;
		
		// 数组类型的类型匹配需要匹配两个地方,第一个是否是数组,第二个数组中的元素类型的匹配
		// Object [] aObject4 定义了一个 ,一维数组,其中数组中的元素是 Object 类型
		// a3 是定义了一个一维数组A,数组中的元素是 一维数组B,一维数组B中的元素是 int 类型,一维数组B可以
		// 向上转型为 Object 类型,所以可认为 Java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是
		// 数组中的元素还是数组,只有最后一层是一个非数组类型
		Object[] aObject4 = a3;
		Object[] aObject5 = a4;
	}
}</strong></span>

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

<strong>	int [] a11 = new int[]{1, 2, 3};	
	String [] a12 = new String[]{"a", "b","c"};
	System.out.println(Arrays.asList(a11));
	// 这说明数组中的元素向上转型的时候不会进行自动装箱拆箱
	// 自动装箱拆箱只会在运算符表达式中进行
	//System.out.println(Arrays.asList((Integer [])a11));
	System.out.println(Arrays.asList(a12));</strong>

打印结果为:
[[I@4706e02e]
[a, b, c]
这是因为 Arrays 类的 asList 方法在 jdk1.5 和 jdk1.4 中不同,
jdk1.5 :static <T> List<T> asList(T... a) 
jdk1.4:public static List asList(Object[] a)  
jdk1.5 为了兼容 jdk1.5 而首先按照 jdk1.4 的类型运行,所以把 int[] 当做一个 Object 对象,把 String[] 当做一个 Object[] 对象。

总结:反射部分的知识目前所了解,用途不是很广,也比较难以理解,但是我可以感觉到,反射的作用很大(作用大和用途不广不矛盾),当需要的时候,会非常省时省力,也非常有效率,非常有必要学透彻。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值