码迷,mamicode.com
首页 > 编程语言 > 详细

黑马程序员——java基础——反射

时间:2015-04-26 07:04:51      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:反射



黑马程序员——java基础——反射

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

反射

其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。

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

简单说:反射技术可以对一个类进行解剖。

反射的基石——>Class类

1、java中的类是用来描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。java中所有的类也是一类事物,有其共性,都有所属的包,类名,属性的访问权限,字段,方法等信息。我们对此进行抽象提取用于描述类的共性内容,这就出现了一个特有的类Class类。这个类是反射的基石,它用来表示java中对象的class阶段,Class对象封装了一个java类中定义的成员变量、成员方法、构造方法、类名、包名等。

Person p1 = new Person("zhangsan");

Person p2 = new Person("lisi");

2、Class类代表Java类,它的各个实例对象又分别对应什么呢?

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

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同类型,这个类型就是Class。


反射的基本步骤

1、获得Class对象,就是获取到指定的名称的字节码文件对象。

2、实例化对象,获得类的属性、方法或构造函数。

3、访问属性、调用方法、调用构造函数创建对象。


获取这个Class对象,有三种方式

1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。

2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。

    前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。

3:使用的Class类中的方法,静态的forName方法。

    指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。

// 1. 根据给定的类名来获得 用于类加载

String classname ="cn.itcast.reflect.Person";//来自配置文件

Class clazz = Class.forName(classname);//此对象代表Person.class

// 2.如果拿到了对象,不知道是什么类型  用于获得对象的类型

Object obj = new Person();

Class clazz1 = obj.getClass();//获得对象具体的类型

// 3.如果是明确地获得某个类的Class对象 主要用于传参

Class clazz2 = Person.class;

 九个预定义的Class:

                1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。

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

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

Class类中的方法

        static Class forName(String className)

        返回与给定字符串名的类或接口的相关联的Class对象。

        Class getClass()

        返回的是Object运行时的类,即返回Class对象即字节码对象

        Constructor getConstructor()

        返回Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。

        Field getField(String name)

        返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。

        Field[] getFields()

        返回包含某些Field对象的数组,表示所代表类中的成员字段。

        Method getMethod(String name,Class… parameterTypes)

        返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。

        Method[] getMehtods()

        返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。

        String getName()

        以String形式返回此Class对象所表示的实体名称。

        String getSuperclass()

        返回此Class所表示的类的超类的名称

        boolean isArray()

        判定此Class对象是否表示一个数组

        boolean isPrimitive()

        判断指定的Class对象是否是一个基本类型。

        T newInstance()

        创建此Class对象所表示的类的一个新实例。

Constructor类

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

2、获取构造方法:

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

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

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

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

创建实例对象:

1,通常方法:String str = new String(new StringBuffer("abc"));

    反射方法: 

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

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

2、Class对象中有一个便利的方法创建实例对象:

     String obj = (String)Class.forName("java.lang.String").newInstance();

    该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。(javabean要求有一个默认的构造函数)

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

Filed类

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

方法:

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

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

        setAccessible(ture);

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

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

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

class ReflectPoint {  
    private int x;  
    public int y;  
    public ReflectPoint(int x, int y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
}  

反射字段:

  //成员字段的反射

  ReflectPoint pt1 = new ReflectPoint(3, 5);

  Field  filedY = pt1.getClass().getField("y");

  System.out.println(filedY);

  System.out.println(filedY.get(pt1));

  //私有字段的暴力反射

  Field fieldX = pt1.getClass().getDeclaredField("x");//可以得到私有字段但还不能用它的方法

  System.out.println(fieldX);

 fieldX.setAccessible(true);//经过暴力反射,强行使用它的方法

  System.out.println(fieldX.get(pt1));

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

实现该功能的代码如下:

public class ReflectPoint {
	String x;
	String y;
	public String str1 = "basketball";
	public String str2 = "ball";
	public ReflectPoint(String x, String y) {
		super();
		this.x = x;
		this.y = y;
	}
	@Override
	public String toString() {
		return "ReflectPoint [str1=" + str1 + ", str2=" + str2 + "]";
	}
}

public class Test {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		ReflectPoint rp = new ReflectPoint(String.valueOf(3), String.valueOf(4));

		Field[] fields = ReflectPoint.class.getFields();
		for (Field field : fields) {
			if (field.getType() == String.class) {

				String str = (String) field.get(rp);
				System.out.println(str.replace("b", "a"));
			}
		}
	}
}

Method类

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

1、大家应通过思考和推理的方式来学习反射中的API,例如,Class.getMethod方法用于得到一个方法,该方法要接受什么参数呢?显然要一个方法名,而一个同名的方法有多个重载形式,用什么方式可以区分清楚想得到重载方法系列中的哪个方法呢?根据参数的个数和类型,例如,Class.getMethod(name,Class... args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表。

再强调一遍参数类型用什么来表示啊?用Class对象!

方法

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

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

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


得到类中的某一个方法:

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

调用方法:

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

反射方式:System.out.println(charAt.invoke(str, 1));

数组的反射

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

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

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

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

    int[] a1 = new int[3];

    int[] a2 = new int[4];

    int[][] a3 = new int[3][4];

    String[] a4 = new String[3];

    Integer[] a5 = new Integer[3];

    Object[] aObj3 = a5;//可以,非基本类型的一维数组可以当做Object[]类型来使用

    //Object[] aObj4 = a1; //不可以,基本类型的一维数组不能当做Object[]来使用;

    Object aObj1 = a1;//可以,基本类型的一维数组可以当做Object类型来使用

    Object aObj2 = a4;//可以,非基本类型的一维数组能当做Object来使用;

    System.out.println(a1.getClass()== a2.getClass());//true

框架

通过反射调用Java类的一种方式。

        如房地产商造房子用户住,门窗和空调等等内部都是由用户自己安装,房子就是框架,用户需使用此框架,安好门窗等放入到房地产商提供的框架中。

        框架和工具类的区别:工具类被用户类调用,而框架是调用用户提供的类。

框架机器要解决的核心问题

        我们在写框架(造房子的过程)的时候,调用的类(安装的门窗等)还未出现,那么,框架无法知道要被调用的类名,所以在程序中无法直接new其某个类的实例对象,而要用反射来做。

简单框架程序的步骤:

        1)右击项目File命名一个配置文件如:config.properties,然后写入配置信息。如键值对:className=java.util.ArrayList,等号右边的配置键,右边是值。

        2)代码实现,加载此文件:

                ①将文件读取到读取流中,要写出配置文件的绝对路径。

                    如:InputStream is=new FileInputStream(“配置文件”);

                ②用Properties类的load()方法将流中的数据存入集合。

                ③关闭流:关闭的是读取流,因为流中的数据已经加载进内存。

        3)通过getProperty()方法获取className,即配置的值,也就是某个类名。

        4)用反射的方式,创建对象newInstance()

        5)执行程序主体功能

 






黑马程序员——java基础——反射

标签:反射

原文地址:http://blog.csdn.net/wk843620202/article/details/45282017

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!