java程序员从笨鸟到菜鸟之(四十五)反射初涉

来源:互联网 发布:票务网站源码 编辑:程序博客网 时间:2024/05/16 15:31

反射概念

粗---先会使用

1  什么是java语言反射机制?

       反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

       反射就是通过获取到该类的字节码文件对象---->即:Class类对象,通过Class类对象获取该类里面的一些属性(成员变量)、构造方法、成员方法等----如何获取等

说明:两种理解方式均可

2  面试题:如何获取类的字节码文件对象?并且有几种方式呢?

1)Object类中的getClass()方法,表示正在运行的Class类型的实例

2)数据类型class属性  举例:String.class、Student.class

3)Class类中的特有方法:forName(String className)---(重点,数据库加载驱动:Drivers)---静态方法获得类名对应的Class对象

说明:开发中常使用的方式,因为第三种里面的参数是一个字符串;一个Class对象表示特定类的对象(属性)

注意:第三种方式的是类的全路径名称字符串形式

实例1 

package demo;public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {Person person=new Person();Class<Person> person1=Person.class;//得到Person.class字节码文件对象Class person2=person.getClass();Class<?> person3 = Class.forName("demo.Person");//加载并且获得Person.class字节码文件对象//如何方式3找不到类的全限定名:出现java.lang.ClassNotFoundException异常}}

说明1:出现黄色警告线原因---Class类实际是一个泛型类,大多数情况可以忽略类型参数,而使用原始的Class类;

说明2:三种方式都是Person类的字节码文件,所以三者通过"=="判断都是true

注意:一个Class对象实际是一个类型,而此类型未必是一个类;int不是类,但int.class是一个Class类型的对象

回顾:在反射之前,在启动程序时,包含main方法的类首先被加载,它会加载所有需要的类,被加载的类又会继续加载它需要的类,以此类推;对于大型应用程序来说,会消耗很多时间,用户不耐烦。

反射应用:给用户一个启动速度比较快的幻觉---首先保证包含main方法的类没有显示地引用其它的类,在显示一个启动画面,然后通过调用Class.forName手工地加载其它的类

Class类

在程序运行期间(前提),Java运行时系统始终为所有的对象维护着一个被称为运行时的标识类型,这个信息(标识类型的信息)跟踪着每个对象所属的类;虚拟机利用运行时类型信息选择相应的方法执行。

理解:虚拟机为每个类型管理一个Class对象;注意:不是每个对象,通过"=="运算符的比较也可以看出。

那么如何访问这些信息?通过专门的java类访问这些信息,保存这些信息的类被称为Class。

常用获取构造器的方法

(1)getName---返回类的全限定类名----补充的

(2)public Constructor<?>[] getConstructors()---返回的是一个公共构造方法所在数组

(3)public Constructor<?>[] getDeclaredConstructors():获取的是当前字节码文件对象中所有的(静态的,私有的,受保护的)构造方法数组

(4)public Constructor<T> getConstructor(Class<?>... parameterTypes):它反映此 Class 对象所表示的类的指定公共构造方法

参数说明:参数为对应构造方法的数据类型的字节码文件对象,形如String.class或int.class;可变参数类型

(5)public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):表示获取类或者接口中的指定的构造方法

实例2

package demo;import java.lang.reflect.Constructor;public class Demo2 {public static void main(String[] args) throws  Exception{//方式1---public Constructor<?>[] getConstructors(),返回的是一个构造方法所在数组//加"s"获取所有的公共的(public修饰的)构造方法Constructor<?>[] constructors = person.getConstructors();//遍历构造方法for(Constructor con:constructors){System.out.println(con);}System.out.println("---------------------");//方式2Constructor<?>[] dc = person.getDeclaredConstructors();for(Constructor con:dc){System.out.println(con);}System.out.println("----------------------");//方式3---获取某一个公共的构造方法Constructor<?> con = person.getConstructor(String.class,String.class);System.out.println(con);//方式4---获取某个构造方法,我们就获取个私有的方法Constructor<?> dcr = person.getDeclaredConstructor(String.class,int.class);System.out.println(dcr);//了解一下打印的内容:public demo.Person(java.lang.String,int)}}
动态的创建一个类的实例----不经过编译时期的检查(翻墙)

(6)public 类名 newInstance():

说明:可以直接通过Class的newInstance()的方法,特点:对没有参数的默认构造器初始化并创建对象

除了Class的此方法,还有没有其它的途径创建对象?当然有

Constructor类

理解上:其实类似于将int类型封装成Integer类那样,将构造方法封装成Constructor类

(1)public T newInstance(Object... initargs):

说明:传递的是实际参数(可变参数),该方法等效于创建一个类的对象(动态)

实例3 创建对象动态和静态(公共的构造方法)

package demo;import java.lang.reflect.Constructor;public class Demo3 {public static void main(String[] args) throws Exception {//(1)静态Person person = new Person();//System.out.println(person);//(2)动态Person person2 = Person.class.newInstance();System.out.println(person==person2);//(3)动态Class<?> person3 = Class.forName("demo.Person");Constructor<?> dc = person3.getDeclaredConstructor();Object obj = dc.newInstance();//最常用的,返回值也最特殊//(4)动态Class<? extends Person> class1 = person.getClass();Constructor<? extends Person> dc1 = class1.getDeclaredConstructor();Person person4 = dc1.newInstance();}}

注意:(2)和(3)以及(4)的newInstance()方法的返回值

那么问题来了,私有的构造方法能否创建对象

(2)public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性)---常常是私有的构造方法;常常"flag=true" ----在给构造创建实例对象之前就应该取消检查

实例4

package demo;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class Demo4 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {//动态的创建类对象,并初始化(私有的和公共的)//1:私有的方式String str="demo.Person";Class<?> c = Class.forName(str);Constructor<?> dc = c.getDeclaredConstructor(String.class,int.class);dc.setAccessible(true);//私有的构造方法不能直接访问Object obj = dc.newInstance("小王",27);System.out.println(obj);//调用Person类的toString()方法//2:公共的方式Constructor<?> con = c.getConstructor(String.class,String.class);Object ne = con.newInstance("小王","0012");System.out.println(ne);}}

通过反射获取成员变量并使用:成员变量--->Field

(1)获取所有的公共的成员变量public Field[] getFields()

说明:所有的公共的可访问的字段,返回的是Field对象数组

(2)public Field[] getDeclaredFields()
说明:获取当前字节码文件对象中所有的公共的或者私有的字段

(3)public Field getField(String name)

说明:获取公共的指定的字段,参数为当前成员变量名称的字符串形式

(4)public Field getDeclaredField(String name)

说明:获取类或接口中已经声明的指定的字段(一般私有的)

注意1:如果给当前obj实例对象设置一个参数,将指定对象变量上此 Field对象表示的字段设置为指定的新值;

public void set(Object obj, Object value)----给obj实例对象里面的成员变量设置一个实际参数;obj为动态的对象(forName创建的形式)

注意2:如果想对私有成员变量设置值,必须在设置值之前取消Java语言的访问检查:Field类的方法---setAccessible(true);

注意3:输出每个对象不是地址的原因:多

实例5

package demo;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;/** * 通过反射获取成员变量并使用 * 成员变量--->Field *  * @author Orange * @version 1.8 */public class Demo5 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {//(1)创建Class对象Class<?> person = Class.forName("demo.Person");//(2)获取所有的公共的成员变量public Field[] getFields():所有的公共的可访问的字段,返回的是Field对象数组Field[] fields = person.getFields();//遍历for(Field fie:fields){System.out.println(fie);}System.out.println("--------------------------");//(3)public Field[] getDeclaredFields():获取当前字节码文件对象中所有的公共的或者私有的字段Field[] df = person.getDeclaredFields();for(Field fie:df){System.out.println(fie);}//(4)重点---获取单个公共的成员变量(Field类对象)//public Field getField(String name):获取公共的指定的字段,参数为当前成员变量名称"address"Field field = person.getField("age");//(5)动态创建一个对象Constructor<?> con = person.getConstructor();Object obj = con.newInstance();//有了Field对象,给当前obj实例对象设置一个参数//将指定对象变量上此 Field对象表示的字段设置为指定的新值public void set(Object obj, Object value)//给obj实例对象里面的成员变量设置一个实际参数---->valuefield.set(obj, 27);System.out.println(obj);//(6)私有的成员变量Field de = person.getDeclaredField("name");de.setAccessible(true);de.set(obj, "小王");System.out.println(obj);}}

调用对象的成员方法----Class类的方法

(1)  public Method[] getDeclaredMethods():

说明:得到此类对象本身的所有方法(公共,受保护、默认、私有),但不包括继承其它的方法(不管什么方式)

(2)  public Method[] getMethods():获取当前该字节码文件对象(Person.class)中自己本身以及它父类中所有的公共成员方法(包括继承)

说明:除了自身的公共方法以外,还有继承的公共方法

(3)  public Method getMethod(String name,Class<?>... parameterTypes):指定公共成员方法

参数1:表示方法名字符串形式
参数2:该方法的参数类型的Class对象(数据类型的class属性) String.class(非实参)

说明:返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法(无继承的方法,自身类中特有的公共方法)。

(4)  public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

说明:得到对象(无继承方法)反映此 Class 对象所表示的类或接口的指定已声明方法(可以私有化)

Method类

(1)  public Object invoke(Object obj, Object... args)----对封装的方法进行调用→真正调用方法
      参数1:表示当前针对哪个以实例对象进行方法的调用----疑问静态方法不需要obj吧!!!
      参数2:当前调用该方法的时候里面传递的实际参

说明:唯一有参数的,参数需要指明调用的动态对象,然后传递参数才能调用此对象的方法。

注意:如果是私有的方法,必须取消Java语言的访问检查---主要针对私有的方法---setAccessible(true)

实例6 通过反射获取成员变量并使用,之前使用对象调用成员方法

package demo;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Demo6 {/** * 需求: * 通过反射获取成员变量并使用,之前使用对象调用成员方法 * 类似: * Person p = new Person() ; * p.show() ;  */public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {Class<?> person = Class.forName("demo.Person");Constructor<?> con = person.getConstructor();Object instance = con.newInstance();//动态的创建了一个对象Method[] met = person.getMethods();Method me = person.getMethod("method1");Object inv = me.invoke(instance);System.out.println(inv);}}

反射的应用
reflect_handler的应用---体会

动态代理:

我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?

属性集合类----动态代理的形式

















阅读全文
0 0
原创粉丝点击