第8章Java的反射机制.ppt
Java的反射机制,主要内容,1.Class的基本概念以及创建方式2.反射的基本概念3.类的各个组成部分对应的反射类 Constructor Field Method4.反射在框架中的应用,反射的基础-Class,Java当中的类用来表示具有相同属性和方法的对象的结合,是抽象的概念。对象是类创建的,同一个类的不同对象具有不同的属性值。Java当中定义的所有类都属于同一类事物,可以用Class来表示。对比理解:不同的人可以用Person类来表示。人-Person Java当中定义的不同类可以用Class来表示。Java当中的类-Class,Class类的对象-字节码,Class类的对象就是不同的类对应的字节码。获取Class的对象Person p1=new Person();Class class1=new Class()()三种方式:(1)对象名.getClass()Person p1=new Person();p1.getClass();(2)类名.class Date.class(3)Class.forName()方法(此方式用的较多)Class.forName(“java.lang.String”);,基本数据类型的Class对象,Java当中的基本数据类型有:boolean int short byte char long float 和 double 它们也有各自的Class对象。例如:int.class void 也有自己对应的Class对象 void.class 基本数据类型对应的封装类有属性TYPE,这个属性代表了封装类所封装的基本数据类型的Class对象。int.class=Integer.TYPE void.class=Void.TYPE,反射(Reflect),反射就是把Java类中的各个组成部分映射成相应的Java类。一个类的组成部分包括:属性、方法、构造方法、包等。这些组成部分都会被映射成相应的类。Class类定义了一系列方法来获取java类的属性、方法、构造方法、包等信息,这些信息都有相应的类来表示,分别是Field、Method、Constructor、Package等。注意:表示java类组成部分的各种类位于java.lang.reflect包中。,Constructor类,Constructor类用来描述类中所定义的构造方法。(1)得到类的所有构造方法 Constructor constructors=Class.forName(“java.lang.String”).getConstructors();(2)得到类中某个具体的构造方法,在getConstructor中传入参数类型所对应的字节码 Constructor constructor=Class.forName(“java.lang.String”).getConstructor(String.class)使用Constructor类的目的是用来创建相应类的对象 正常情况 String str=new String(“hello”);反射情况 Constructor con=String.class.getConstructor(String.class)String str=(String)con.newInstance(“hello”);,Class.newInstance(),通过反射方式创建对象的过程 Class-Constructor-某个类的对象只有当要用某个类的无参构造方法创建该类对象时,可以省略创建Constructor类对象的这个过程。Date d=(Date)Class.forName(“java.util.Date”).newInstance();注意:java运行环境的缓存中保存了类的无参构造方法所对应的Construtor对象。见源文件:ReflectTest.java,Field类,Field类用来表示类中的属性(字段)。(1)Class.getFields():得到Class对象的所有字段,返回的是Field数组。(2)Class.getField(String name)返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公有成员字段。(3)Field的对象所代表的某一个类的属性,而不是那个类的某一个对象的属性。要得到某个对象对应的属性值,需要通过get(Object obj)方法与某个对象具体关联。(4)对于非公有属性只能通过Class的getDeclaredField(String fieldName)方法得到。(5)对于私有属性要得到它所关联到的对象的值,需通过Field的setAccessible(boolean boolean)方法设置。(6)Field类的getType()方法用来得到字段所属的类型 见源文件:Field/FieldTest.java,Field类,练习:扫描一个对象中所有的字符串类型的属性,并为每一个字符串属性的值后面添加“hello”字符串,然后将这个对象输出。见源文件:Field/FieldTest1.java 注意:对于字节码的比较一定要用“=”,而不是equals()。掌握上述例子对于理解Spring等框架技术具有重要作用。,Method类,Method用来表示类中的方法。通过Class对象的如下方法得到Method对象Method getMethod(String name,Class.parameterTypes)按名称得到某个特定的public方法(包括从父类或接口继承的方法)Method getMethods()得到public方法(包括从父类或接口继承的方法)Method getDeclaredMethods()得到所有的方法(不包括继承的方法)Method getDeclaredMethod(String name,Class.parameterTypes)按名称得到某个特定的方法(不包括继承的方法),Method类,Class的getMethod()方法得到Method类的对象。(1)需要传递方法名所对应的字符串(2)需要传递该方法所需要的参数类型的字节码 invoke(Object obj,Object obj)方法用来调用Method所表示的方法。其中,第一个参数表示此方法作用于哪一个对象。见源文件:Method/MethodTest.java 注意:如果调用的是个静态方法,那么invoke()方法中第一个参数用null表示。,对接收数组参数的方法进行反射,问题:编写一个程序,这个程序能够接收用户提供的类名,然后去执行这个类中的main()方法。见源文件:method/MethodTest1.java注意:JDK1.4的编译器会把数组中的每一个元素作为一个参数,来调用此接收数组参数的方法,这样的话就出现了参数不对应的问题。mainMethod.invoke(null,new Stringaaa,bbb,ccc);JDK1.5为了兼容1.4,就必须将整个数组作为一个只有一个元素的Object数组进行传递。以下两种方式正确:(1)mainMethod.invoke(null,new Objectnew Stringaaa,bbb,ccc);(2)mainMethod.invoke(null,(Object)new Stringaaa,bbb,ccc);,数组的反射,在Java中数组属于复合数据类型,具有相同元素类型和维数的数组属于同一个类型,即具有相同的字节码对象。代表数组的字节码对象调用getSuperClass()得到它的父类Object的字节码对象。基本数据类型的一维数组可以当做Object类型使用,但不能当做Object类型使用;基本数据类型的二维数组可以当做Object类型使用;复合数据类型的一维数组既可以当做Object类型使用,也可以当做Object类型使用。java.lang.reflect.Array用于完成对数组的反射操作。见源文件:数组的反射/ArrayReflect.java,反射的作用-实现框架,框架:是一个半成品的软件,需要开发人员进行填充相关内容,才能形成用户使用的软件产品。开发商提供的房子就是一个框架,需要把“空”房子买来以后,需要按照自己的意愿进行装修,然后就有了自己的家。,反射的作用-实现框架,框架和工具类的区别:面向对象的本质就是类与类之间的相互调用。你调用别人的类,那么别人写的类就是工具类;别人写的类来调用你的类,那么别人的类就是框架。反射要实现的核心功能:无法知道将来要调用的类名,所以就不能使用new来创建一个对象,因此必须使用反射的机制。,读写配置文件-Properties类,Properties属性文件用于配置程序的一些信息,以文本文件形式保存(.properties)。java.util.Properties类主要用于读、写属性文件,继承自Hashtable类,以键-值的关系保存信息。读属性:(1)newProperties();(2)newFileInputStream(prop.properties);(3)prop.load(fis);(4)fis.close();(5)prop.getProperty(sitename);,反射的应用,写属性:(1)prop.setProperty(studio,BoxcodeStudio);(2)newFileOutputStream(prop.properties);(3)prop.store(fos,BoxcodeStudio);(4)fos.close();练习:采用配置文件反射的方式创建集合类对象,并在该集合中添加若干对象元素,遍历集合对象,输出其中的元素。见原文件:Frame/FrameTest.java,通过类加载器加载配置文件,实际项目开发中,配置文件肯定不会和源文件放在一起,肯定放在一定的ClassPath路径下,并且包含在某个包中。通过字节码文件(Class对象),可以得到类加载器,然后再通过 public InputStream getResourceAsStream(String name)读取配置文件。见源文件:Frame/ClassLoader/ClassLoaderTest.java,设计模式,设计模式的定义 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。,设计模式,设计模式的基本要素 设计模式一般有如下几个基本要素:模式名称、问题、目的、解决方案、效果、实例代码和相关设计模式,其中的关键元素包括以下四个方面:模式名称(Pattern name)问题(Problem)解决方案(Solution)效果(Consequences),设计模式的分类,根据其目的(模式是用来做什么的)可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三种:创建型模式主要用于创建对象。结构型模式主要用于处理类或对象的组合。行为型模式主要用于描述对类或对象怎样交互和怎样分配职责。,设计模式的分类,根据范围,即模式主要是用于处理类之间关系还是处理对象之间的关系,可分为类模式和对象模式两种:类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,是属于静态的。对象模式处理对象间的关系,这些关系在运行时刻变化,更具动态性。,代理模式动机,在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式。,代理模式的角色与结构,(1)Subject:抽象目标角色,声明真实目标对象和代理的共同接口。(2)Proxy:代理角色,内部包含对真实目标对象的引用。(3)RealSubject:真实目标角色,是客户端真正要交流的对象,需要被代理。,代理模式核心代码,典型的代理类实现代码:,代理模式实例与解析,论坛权限控制代理 见源文件:权限控制在一个论坛中已注册用户和游客的权限不同,已注册的用户拥有发帖、修改自己的注册信息、修改自己的帖子等功能;而游客只能看到别人发的帖子,没有其他权限。使用代理模式来设计该权限管理模块。,代理模式的适用环境,根据代理模式的使用目的,常见的代理模式有以下几种类型:(1)远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中。(2)虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。(3)Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。,代理模式适用环境,(4)保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。(5)缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。(6)防火墙(Firewall)代理:保护目标不让恶意用户接近。(7)同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。(8)智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。,代理模式的典型应用,(1)Java RMI(Remote Method Invocation,远程方法调用)。,代理模式的典型应用,(2)EJB、Web Service等分布式技术都是代理模式的应用。在EJB中使用了RMI机制,远程服务器中的企业级Bean在本地有一个骨架代理,客户端通过骨架来调用远程对象中定义的方法,而无须直接与远程对象交互。在EJB的使用中需要提供一个公共的接口,客户端针对该接口进行编程,无须知道骨架以及远程EJB的实现细节。,代理模式的典型应用,(3)Spring 框架中的AOP技术也是代理模式的应用,在Spring AOP中应用了动态代理(Dynamic Proxy)技术。AOP(Aspect oriented Programming):面向切面编程。在不改动代码的前提下,灵活的在现有代码的执行顺序前后,添加进新规机能。,代理模式的扩展,图片代理:一个很常见的代理模式的应用实例就是对大图浏览的控制。用户通过浏览器访问网页时先不加载真实的大图,而是通过代理对象的方法来进行处理,在代理对象的方法中,先使用一个线程向客户端浏览器加载一个小图片,然后在后台使用另一个线程来调用大图片的加载方法将大图片加载到客户端。当需要浏览大图片时,再将大图片在新网页中显示。如果用户在浏览大图时加载工作还没有完成,可以再启动一个线程来显示相应的提示信息。通过代理技术结合多线程编程将真实图片的加载放到后台来操作,不影响前台图片的浏览。,代理模式的扩展,远程代理:远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的远程业务对象是局域的而不是远程的,而远程代理对象承担了大部分的网络通信工作。,代理模式的扩展,动态代理,AOP的实现原理(1)在传统的代理模式(静态代理)中,客户端通过Proxy调用RealSubject类的request()方法,同时还在代理类中封装了其他方法(如preRequest()和postRequest(),可以处理一些其他问题。(2)如果按照这种方法使用代理模式,那么真实主题角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代理主题角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实主题角色的情况下使用代理主题角色,这都是动态代理需要解决的问题。,动态代理的实现,Java动态代理实现相关类位于java.lang.reflect包,主要涉及一个接口和一个类:(1)InvocationHandler接口。它是动态代理对象所依赖的调用处理程序所必须实现的接口,该接口中定义了如下方法:public Object invoke(Object proxy,Method method,Object args)throws Throwable;第一个参数proxy表示被代理的类,第二个参数method表示被代理类中的方法,第三个参数args表示被代理方法的参数数组。,动态代理的实现,(2)Proxy类,动态代理类,为实现某接口的类创建代理,同时指定调用处理程序对象。public static Object newProxyInstance(ClassLoader loader,Class interfaces,InvocationHandler h)throws IllegalArgumentException。该方法用于根据传入的接口类型interfaces返回一个动态创建的代理类的实例。第一个参数loader表示被代理类的类加载器。第二个参数interfaces表示被代理类实现的接口列表。第三个参数h表示所指派的调用处理程序对象。,动态代理的类图关系,见源文件:动态代理,