Java反射机制

 

  •  
    • Class类:代表一个类。
    • Field 类:代表类的成员变量(成员变量也称为类的属性)。
    • Method类:代表类的方法。
    • Constructor 类:代表类的构造方法。
    • Array类:提供了动态创建数组,以及访问数组的元素的静态方法
    • Proxy :  提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
  • 一、Java的反射机制

    在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

     

    Java 反射机制主要提供了以下功能:

    在运行时构造任意一个类的对象。

    在运行时判断任意一个类所具有的成员变量和方法。

    在运行时判断任意一个对象所属的类。

    在运行时调用任意一个对象的方法

     

    动态语言:

    程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言

     

    Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods

     

    尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflectionintrospection是常被并提的两个术语

     

     

     

    二、Java Reflecation API

     

    在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

     

    代码一:下面来看一段程序,从控制台输入完整类名,程序会打印出该类的所有方法

    import java.util.Scanner;

    import java.lang.reflect.Method;

     

    public class DumpMethod {

    public static void main(String[] args) {

    //获取控制台读取的类

    Scanner input = new Scanner(System.in);

    String className = input.nextLine();

    try {

    //加载并初始化 控制台输入的指定类 泛型?是所有泛型类的父类

    Class<?> classType = Class.forName(className);

    //获得类的所有方法

    Method methods[] = classType.getDeclaredMethods();

    for(int i=0;i<methods.length;i++) {

    System.out.println(methods[i]);

    }

    } catch (ClassNotFoundException e) {

    e.printStackTrace();

    }

    }

    }

    forName

    public static Class<?> forName(String className)
                            throws ClassNotFoundException

    返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:

      Class.forName(className, true, currentLoader)
     

    其中 currentLoader 表示此类的定义类加载器。

    例如,以下代码片段返回 java.lang.Thread 类的运行时 Class 描述符。

       Class t = Class.forName("java.lang.Thread")
     

    调用 forName("X") 将导致名为 X 的类被初始化。

    参数:

    className - 所需类的完全限定名。

    返回:

    具有指定名的类的 Class 对象。

    抛出:

    LinkageError - 如果链接失败

    ExceptionInInitializerError - 如果此方法所激发的初始化失败

    ClassNotFoundException - 如果找不到该类

     

    代码二:

    ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object 同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回

    import java.lang.reflect.Field;

    import java.lang.reflect.Method;

    class Customer {

    private Long id;

    private String name;

    private int age;

    public Customer() {

    }

    public Customer(String name, int age) {

    this.name = name;

    this.age = age;

    }

    public Long getId() {

    return id;

    }

    public void setId(Long id) {

    this.id = id;

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getAge() {

    return age;

    }

    public void setAge(int age) {

    this.age = age;

    }

    }

    显示结果:

    class org.cgz.reflection.Customer

    id:12

    name:Tom

    age:23

    Copy information:12 Tom 23

    public class ReflectTester {

    public static void main(String[] args) throws Exception {

    Customer customer = new Customer("Tom",23);

    customer.setId(new Long(12));

    Customer customerCopy = (Customer) new ReflectTester().copy(customer);

    System.out.println("Copy information:" + customerCopy.getId() + " " + customerCopy.getName() + " "+ customerCopy.getAge());

    }

    public Object copy(Object object) throws Exception {

    //获得对象的类型

    Class<?> classType = object.getClass();

    System.out.println(classType);

    //通过获取Class对象的无参构造,创建一个新的对象

    Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

    //获得该对象层次的所有属性

    Field fields[] = classType.getDeclaredFields();

    for(int i=0;i<fields.length;i++) {

    Field field = fields[i];

    //获得field属性名

    String fiedlName = field.getName();

    //获得属性名第一个字母的大写形式

    String fiestLetter = fiedlName.substring(0, 1).toUpperCase();

    //获得setter方法名

    String setMethodName = "set" + fiestLetter + fiedlName.substring(1);

    //获得getter方法

    String getMethodName = "get" + fiestLetter + fiedlName.substring(1);

    //获得属性对应的settergetter方法

    Method getMethod = classType.getMethod(getMethodName, new Class[]{});

    Method setMethod = classType.getMethod(setMethodName, field.getType());

    Object value = getMethod.invoke(object, new Object[]{});

    System.out.println(fiedlName+":"+value);

    //调用拷贝对象的setter方法

    setMethod.invoke(objectCopy, new Object[]{value});

    }   

    return objectCopy;

    }

    }

     

     

    Java.lang.Class   Class类是Reflection API中的核心类  

    1.获取目标类型的Class对象

            (1). 引用类型 获取Class对象的三种方式

    |++调用静态方法Class.forName()   (使用此种方式 类名必须完整,即使在类中导入了目标类所在的包,仍然需要全名)

             |++Class.forName("org.cgz.Person")

             |++Class.forName("oracle.jdbc.driver.OracleDriver")

     

    |++调用Object类中定义的getClass()方法

             |++Person p = new Person()

             |++Class c1 = p.getClass()

             |++Class c2="Hello World".getClass()

     

    |++使用.class表达式(不是方法也不是属性 只是一个约定的表达式,在编译阶段就需要知道类名,*.class*必须是合法的类名,否则编译      无法通过)

             |++Class c1 = String.class

             |++Class c2 = org.cgz.Person.class

             |++Class c3 = oracle.jdbc.driver.OracleDriver.class

            (2).基本数据类型 void

    |++使用.class表达式

    |++Class c1 = int.class

    |++Class c2 = double.class

    |++Class c3 = void.class

    |++调用相应的封装类的.Type属性    注:Type对包装类有效,且使用Type后返回的是拆箱后的基本类型  基本类型没有type属性

    |++Class c1 = Integer.TYPE              public static final Class<Integer> TYPE     表示基本类型 int 的 Class 实例。

    |++Class c2 = Double.TYPE

    ...

    |++Class c3 = Void.TYPE

     

    public class test {

    public static void main(String[] args) throws Exception{

    String className = "java.lang.String";

    Class<?> c = Class.forName(className);

    System.out.println(c);

    System.out.println(c.getClass());

    //System.out.println(c.class);    *.class在编译阶段就必须知道类名,否则编译无法通过

    System.out.println(Integer.TYPE);

    System.out.println(Integer.class);

    System.out.println(int.class);

    System.out.println(Void.class);

    System.out.println(Void.TYPE);

    }

    }

    运行结果:

     

     

    class java.lang.String

    class java.lang.Class

     

     

    Int

    Java.lang.Integer

    int

    class java.lang.Void

    void

     

     

    2. Class类的常用方法

    (1).  public  static  Class<?>  forName(String  className)    返回与带有给定字符串名的类或接口相关联的 Class

    (2).  Public String  getName()   以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称

     

     

    Array相关

    (3).  public   Class<?>   getComponentType(Class … parameterTypes)   

    返回表示数组组件类型的Class   就是取得 数组元素的类型

     

    Constructor相关

    (4).  public  Constructor<?>  getConstructor(Class<?> … parameterTypes)

    返回该Class对象指定的公共的构造方法,parameterTypeClass对象的一个数组,这些Class对象按声明顺序标识构造器的形式参数类型  一般用new Class[] {String.class  ,  Integer.class}表示    注:参数列表中int如果写成 Integer编译不能通过,或使用Integer.TYPE

    例:在Person类中有一构造方法 参数是String ,int类型        

    Class  c = Class.forName("org.cgz.reflection.Person");     

    Constructor   cstr = c.getConstructor(new Class[] {String.class , int.class})

    (5).  public  Constructor[]  getConstructors() 

            获得该类对象的所有公共的构造方法,如果此类是数组.Void,基本类型 则返回长度0的数组

    (6).  public  Constructor<?>  getDeclaredConstructor(Class<?> … parameterTypes)   

            表示该Class对象表示的类或接口的指定的构造方法

    (7).  public  Constructor[]  getDeclaredConstructors()   

            返回该Class对象表示的类或接口的所有构造方法,而不仅仅是公共的构造器.

             如果此   Class 对象表示一个接口、一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组.

     

    Field相关

    (8).  Public  Field  getDeclaredField(String  name)

            返回此Class对象所表示的名称为nameField对象.注:此方法不返回数组类型的length

            属性.

    (9).  public  Field[]   getDeclaredFields()   返回该Class对象表示的类或接口所声明的所有字段,但不包括继承字段.返回的元素没有排序.

    (10).  Public Field  getField(String  name)  

            要反映的字段由下面的算法确定。设 C 为此对象所表示的类:

            1.如果 C 声明一个带有指定名的公共字段,则它就是要反映的字段。

            2.如果在第 1 步中没有找到任何字段,则该算法被递归地应用于 C 的每一个直接超接口。直接超接口按其声明顺序进行搜索。

            3.如果在第 1、2 两步没有找到任何字段,且 C 有一个超类 S,则在 S 上递归调用该算法。如果 C 没有超类,则抛出NoSuchFieldException

    (11).  Public Field[]  getFields()   

    返回该Class对象表示的类或接口的所有可访问的公共字段

    特别地,如果该 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。该方法不反映数组类的隐式长度字段。用户代码应使用 Array 类的方法来操作数组.

     

     Method相关

    (12).  public  Method   getDeclaredMethod(String  name,Class  parameterTypes)

    返回一个Method对象,name表示该方法的名称,parameterTypes表示该方法的参数类型列表,是一个Class对象数组  new Class[] {}

    (13).  Public  Method[]  getDeclaredMethods()  

    返回该Class对象表示的类或接口的所有方法,但不包括继承的方法

    (14).  Public  Method  getMethod(String  name,Class  parameterTypes)

    返回一个Method对象,它是该Class表示的类或接口的公共方法,包括其继承的方法

    (15).  public  Method[]  getMethods()

    返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。数组类返回从 Object 类继承的所有(公共)member 方法

     

    Modifier 相关

    (16).  public  int  getModifiers() 

    返回此类Class对象表示的接口或类的以整数编码的Java修饰符,他们必须通过 Modifier类的toString(int  modifiers)方法来解析

    Int  mo = c.getModifiers();       String modifierName = Modifier.toString(mo);

     

    Instance

    (17).  Public  T  newInstance() 

     创建该Class对象的一个新的实例,注:不带参数,也就是只能使用Class对象的无参构造来实例化对象

    如果想要调用带参数的构造方法,需要通过Constructor类的newInstance()方法来实现

    Class<? >  c = Class.forName("org.cgz.Person");    //获得类的Class对象

    Constructor cons = c.getConstructor(new Class[]{String.class,int.class});  //通过Class对象的getConstructor(Class…parameterTypes)获得构造器

     Object obj = cons.newInstance(new Object[] {"张三",22})//通过Constructor对象的newInstance(Object..initargs)方法新建该类的实例化对象

     

     

     

    Java Reflecation API

    3. java . Lang . Reflect . Constructor类常用方法

    (1).  Public  String  getName()

    以字符串形式返回此构造方法的名称。它总是与构造方法的声明类的全名.

    (2).  Public  Class<?>[]   getParameterTypes()

    返回该构造方法的形参的类型,如果是无参构造,则返回一个长度为0的数组

    (3).  Public int  getModifiers()

    以整数形式返回此Constructor对象所表示的构造方法的修饰符,必须要用Modifier类的toString(int  mo)方法解析

    (4).  Public  T  newInstance(Object    initargs)

    使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。如果所需的访问检查和参数检查获得成功并且实例化继续进行,这时构造方法的声明类尚未初始化,则初始化这个类

    initargs表示的是实参

    Constructor.newInstance( new  Object[] { "张三" , 22 } )Constructor.newInstance( "张三",22 );

    (5).  Public String  toString()

    (6).  Public  boolean  equals(Object  obj)

     

     

    4.java . Lang . Reflect . Field类的常用方法

    (1).  Public  String  getName() 

    返回此Field对象表示的字段的名称

    (2).  Public Class<?>  getType()

    返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型.

    (3).  Public  int  getModifiers()

    以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。应该使用 Modifier 类对这些修饰符进行解码

    (4).  Public Object  get(Object  obj)

    返回指定对象上的此Field对象表示的值,如果该值是一个基本类型值,则自动将其包装在一个对象中

    Obj:从中提取字段值的对象

    (5).  Public Xxx  getXxx(Object  obj) 

    获得 基本 类型或另一个通过扩展转换可以转换为 基本 类型的基本类型的静态或实例字段的值

    Xxx:  int  double  float  long….

    (6).  Public void set(Object  obj,  Object value)

    Obj:该字段所属的指定对象,value:为指定对象中的此Field对象表示的字段赋予的新值

    (7).  Public void setXxx(Object  obj, Xxx  value)  set()方法类似

    (8).  Public void equals(Object  obj)

    (9).  Public String toString()

    返回一个描述此 Field 的字符串 例如:private int java.io.FileDescriptor.fd

     

     

    5.java . lang . reflect . Method类常用方法

    (1). Public String  getName()

    以 String 形式返回此 Method 对象表示的方法名称

    (2).  Public int getModifiers()

    以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。应该使用 Modifier 类对修饰符进行解码

    (3).  Public  Class<?>  getReturnType()

    返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型

    (4).  Public Class<?>  getParameterType()  

    按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。如果基础方法不带参数,则返回长度为 0 的数组

    (5).  Public  Object  invoke(Object  obj , Object … args)   

    对带有指定参数的指定对象调用由此 Method 对象表示的基础方法

    Obj:调用Method对象表示方法的对象

    args:方法所需要的参数

     

     

    6.java.lang.reflect .Array

    (1).public  Object  get(Object  array, int  index) 

    返回指定数组上的的指定下标的数组元素的值

    (2).  Public  static  Object  newInstance(Class<?>  componentType, int length) 

    创建一个具有指定数组组件类型,指定长度的数组

    (3).  Public   void  set(Object  array, int  index , Object  value) 

    将指定数组对象中的指定索引的元素的值设置为value

    (4). Public static Double getDouble(Object array, int index)

    Double形式返回指定数组中指定索引的元素的值

    (5).  Public  static  void  setDouble(Object  array, int  index, Double  d) 

    为指定数组的指定索引的元素设置一指定的Double类型的值d

     

     

    7.java.lang.reflect.Modifier

     

     

     

     

     

     

    三、实例代码

     

    1.通过Java的反射机制调用并执行方法  主要是Method类的演示实例

    public class InvokeTester {

    public int add(int param1, int param2) {

    return param1 + param2;

    }

     

    public String show(String msg) {

    return "show: " + msg;

    }

     

    public static void main(String[] args) throws Exception {

    Class<?> classType = InvokeTester.class;

    Object invokeTester = classType.newInstance();

    Method addMethod = classType.getDeclaredMethod("add", new Class[] {int.class, int.class });

    Method showMethod = classType.getDeclaredMethod("show",new Class[] { String.class });

     

    int sum = (Integer) addMethod.invoke(invokeTester, new Object[] {new Integer(2), new Integer(30) });

    String msg = (String) showMethod.invoke(invokeTester,new Object[] { "反射机制" });

     

    System.out.println(sum);

    System.out.println(msg);

     

    }

    }

    打印结果:

    32

    show: 反射机制

     

    2.Array类的简单使用

    public class ArrayTest1 {

    public static void main(String[] args) throws Exception{

    Class<?> classType = Class.forName("java.lang.String");

    //通过Array类的newInstance()方法创建一个长度为10的字符串数组

    Object array = Array.newInstance(classType, 10);

    //Array array = (Array)Array.newInstance(classType, 10);

    //将数组索引为5的元素值设置为你好

    Array.set(array, 5,"你好");

    //获取数组索引为5的元素的值

    String msg = (String)Array.get(array, 5);

    System.out.println(msg);

    }

    }

     

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值