欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

java反射机制的简单介绍

程序员文章站 2022-06-24 22:17:59
参考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310 先给出反射机制中常用的几个方法: 上图是我的代码结构图, 其中Algorithm.java代码如下: Test.java代码如下: 现在开始对java反射机制做简单的 ......

参考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310

先给出反射机制中常用的几个方法:

Class.forName ("类名")
Class.forName ("类名").newInstance
Class.getMethod ("调用的方法名",class[]{参数类列表})
instance.getClass
Method.invoke ("对象名","参数封装成的对象")
这里简单的给出java反射机制的测试小例子(亲测可以通过), 大家看不懂的话, 可以跳过这里, 看下面对java反射机制的介绍, 看完之后, 再回来看这个小例子, 应该很简单了哦.
java反射机制的简单介绍

  上图是我的代码结构图, 其中Algorithm.java代码如下:

package com.test;
public class Algorithm {
    public int add(int a, int b) {
        return a + b;
    }
}
这里我只写了一个方法做测试, 可以随时的添加.

   Test.java代码如下:

package com.test;
import java.util.List;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.regex.Pattern;

public class Test {
    Object invokeMethod(Object instance, String fun, Class[] paras, Object[] paraValue) {
        Object result = null;
        Method method;
            try {
                method = instance.getClass().getMethod(fun, paras);
                result = method.invoke(instance, paraValue);
            } catch (NoSuchMethodException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (SecurityException e2) {
                // TODO Auto-generated catch block
                e2.printStackTrace();
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException 3) {
                // TODO Auto-generated catch block
                e3.printStackTrace();
            }
            return result;        
    }
            
    public static void main(String[] args) {
      int  x = 10, y = 20;
      Class[] argsClass = new Class[2]; // 参数的类型
      argsClass[0] = int.class;
      argsClass[1] = int.class;
      Object[] object = new Object[2];  // 参数的值
      object[0] = new Integer(x);
      object[1] = new Integer(y);
      Test test = new Test();
      Object objectRes = null;
      try {
// 调用要反射的类Algorithm.class中的add方法, 并向该方法传入参数的类型和参数值 objectRes = test.invokeMethod(Class.forName("com.test.Algorithm").newInstance(), "add", argsClass, object);
} catch (InstantiationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
} catch (ClassNotFoundException e3) {
// TODO Auto-generated catch block
e3.printStackTrace();
}
System.out.println("objectRes=" + objectRes); // 打印的结果是: 30, 不过这里的类型是Integer, 不方便与其它int做判断
// 因为这里返回的是Object类型, 可以把它强制转化为int, 并比较判断, 例如:
// System.out.println("objectRes=" + objectRes +",result="+ ((int)objectRes == 30)); // 打印结果: objectRes=30,result=true
// System.out.println("result=" + (Integer.parseInt(objectRes.toString()) == 30)); // 打印结果: result=true
}

现在开始对java反射机制做简单的介绍

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

Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理.

反射,从这个“反”字可以看出与我们平时正常的使用逻辑肯定不一样,那么到底什么地方不一样了?想要了解“反”,就得先了解一下“正”的概念。

在正常情况下,如果要使用一个类,必须要经过以下几个步骤:

(1)使用important导入类所在的包(类:java.lang.Class

(2)通过关键字new进行类对象实例化(构造方法:java.lang.reflect.Constructor

(3)产生对象可以使用“对象.属性”进行类中属性的调用(属性:java.lang.reflect.Field)

(4)通过“对象.方法()”调用类中的方法(方法:java.lang.reflect.Method

括号中的红色字体是每个步骤对应反射中使用到的类,如果现在不了解,可以先不用管,后面会一一介绍,这里是为了方便进行比较。

在反射中,使用一个类并不需要导入类的所在包,只要知道类的完整路径就可以知道该类中的所有信息。

反射不需要有明确的类型对象,所有的对象都使用Object表示。可以直接用Object的与反射机制的混合调用类中的方法。

在认识反射机制之前,必须要介绍一下Class类,Class类是整个反射操作的源头,该类的定义如下:

public final class Class<T>  extends Object  implements Serializable, GenericDeclaration, Type, AnnotatedElement 

Class类的实例表示正在运行的Java应用程序中的类和接口。

如果要想使用Class类进行操作,就必须首先产生Class类这个对象,一共有三种方法:

(1)Object类中提供了一个返回Class类的方法,定义如下:

     public final Class<?> getClass()  

(2)利用“类.class”取得。

(3)利用Class类的Static方法取得。

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

在程序开发过程中,使用第二种方法比较多。但是在程序框架设计中,都是使用第三种方法,也就是反射机制用到的方法。

class类实例化对象:

Class类如果使用forName()方法之后,就可以调用Class类中newInstance()无参构造函数方法进行操作,该方法定义如下:

        public T newInstance()  throws InstantiationException, IllegalAccessException

该方法表示创建此Class对象所表示的类的一个新实例, 使用方法如下:

class Student {

    public Student() {
         System.out.println("Student类的构造方法");
     }

    @Override
     public String toString() {
         return "Student类的toString方法";
     }
}

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("com.iflytek.Student");
         // 相当于关键字实例化对象,Object obj = new Student();
         Object obj = cls.newInstance();
         System.out.println(obj);
     }

}

输出的结果是:

Student类的构造方法 

Student类的toString方法

通过上面的实例可以看出,调用newInstace()方法时,程序会默认调用Student类的无参构造方法,并且获取到了Student类的实例化对象,可以调用Student类里面的方法属性。

 

在Class类中有两个方法可以获取类中的构造方法,分别是

获取类中所有的构造方法:

public Constructor<?>[] getConstructors()  throws SecurityException 

获取类中指定的构造方法:

public Constructor<T> getConstructor(Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException 

下面是测试例子:

public class ReflectStringDemo {
     public static void main(String[] args) throws Exception{
         Class<?> cls = Class.forName("java.lang.String");
         //获取所有构造函数
         Constructor<?>[] cons = cls.getConstructors();
         //循环打印
         for (int i = 0; i < cons.length; i++) {
             System.out.println(cons[i]);
         }
     }
}

打印结果:

public java.lang.String(byte[])
public java.lang.String(byte[],int,int)
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
public java.lang.String(java.lang.StringBuilder)
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(int[],int,int)
public java.lang.String(char[],int,int)
public java.lang.String(char[])
public java.lang.String(java.lang.String)
public java.lang.String()
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,int)

上面实例获取了String类中的所有构造方法,包括构造方法中的参数、异常等

获取所有构造方法看上去并不难,如果想要进行指定构造方法的调用,则必须关注Constructor类,使用newInstance()方法进行实例

public T newInstance(Object... initargs) throws  

            InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException

下面是测试例子:

import java.lang.reflect.Constructor;

class Student2 {
     private String name;

    private Integer age;

    public Student2(String name, Integer age) {
         this.name = name;
         this.age = age;
     }

    @Override
     public String toString() {
         return "name:" + this.name + ";age:" + this.age;
     }
}

public class ReflectConstructorDemo {

    public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("com.iflytek.Student2");
         Constructor<?> con = cls.getConstructor(String.class, Integer.class);
         // 这里就相当于Object obj = new Student2("xyz",20);
         Object obj = con.newInstance("xyz", 20);
         System.out.println(obj);
     }

}

结果:

name:xyz;age:20

通过上面可以看出,如果要实例化一个对象,使用无参构造方法比有参构造方法简单的多,使用无参直接调用newInstance()方法,使用有参则先获取有参构造方法,

再通过Constructor中的newInstance()方法,并用指定的初始化参数初始化改实例。很多框架中的底层代码默认都是使用无参构造方法来实例化对象,

所以在简单Java类开发中都要明确给出无参构造方法.

 

获取类中的方法可以分为两大类,每个大类中又可以分为两小类,风别是:

获取包括父类集成而来的方法:

获取全部方法:

public Method[] getMethods()  throws SecurityException 

获取指定方法:

public Method getMethod(String name,Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException 

获取本类中定义的方法:

    获取全部方法:

    public Method[] getDeclaredMethods()  throws SecurityException 

    获取指定方法:

    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException 

下面是测试例子:

import java.lang.reflect.Method;
class Student3{
     public void fun(){};
     public void talk(){};
}
public class ReflectMethodStuDemo {
     public static void main(String[] args) throws ClassNotFoundException{
         Class<?> cls = Class.forName("com.iflytek.Student3");
         //获取本类中定义的方法
         Method[] method = cls.getDeclaredMethods();
         //循环打印
         for (int i = 0; i < method.length; i++) {
             System.out.println(method[i]);
         }
     }
}

打印结果:

public void com.iflytek.Student3.fun() 

public void com.iflytek.Student3.talk()

如果把上述代码中的getDeclaredMethods()换成getMethods(),打印出来的结果如下

public void com.iflytek.Student3.fun()
public void com.iflytek.Student3.talk()

public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

使用getMethods()方法时,不仅获取到了Student3类中的方法(红色字体),Object类中的所有方法也被获取到.

上面的程序是直接调用了Method类中的toString()方法输出的,输出格式并不是很理想,没有异常等相关信息。如果有需要,我们也可以自己整理拼接方法输出。

需要用到Method类中的如下几种方法:

getModifiers():以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。

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

getName():以 String 形式返回此 Method 对象表示的方法名称。

getParameterTypes():按照声明顺序返回 Class 对象的数组,这些对象描述了此Method 对象所表示的方法的形参类型。

getExceptionTypes():返回 Class 对象的数组,这些对象描述了声明将此Method 对象表示的底层方法抛出的异常类型。

在这需要注意的是,利用getModifiers()获取修饰符并不是简单的输出public、static等,而是以整数形式返回所表示的方法的Java语言修饰符。

可借助Modifier类的toString()方法来完成。

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
interface Message {
     public void get();
}
class Student1 implements Message {
     public void fun() {
     }
     public void print() {
     }
     public void get() {
     }
}

public class ReflectMethodDemo {
     public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("com.iflytek.Student1");
         Method[] me = cls.getMethods();
         for (int i = 0; i < me.length; i++) {
             // 此时用了method的toString方法输出,如果有需要,用户也可以自己拼凑输出
             // System.out.println(me[i]);
             // 取得修饰符
             System.out.print(Modifier.toString(me[i].getModifiers()) + " ");
             // 取得返回值类型
             System.out.print(me[i].getReturnType().getSimpleName() + " ");
             // 取得方法名称
             System.out.print(me[i].getName() + "(");
             // 取得方法参数
             Class<?> params[] = me[i].getParameterTypes();
             if (params.length > 0) {
                 for (int j = 0; j < params.length; j++) {
                     System.out.print(params[j].getSimpleName() + " arg-" + j);
                     if (j < params.length - 1) {
                         System.out.print(", ");
                     }
                 }
             }
             System.out.print(") ");
             // 取得异常
             Class<?>[] exp = me[i].getExceptionTypes();
             if (exp.length > 0) {
                 System.out.print("throws ");
                 for (int j = 0; j < exp.length; j++) {
                     System.out.print(exp[j].getSimpleName());
                     if (j < exp.length - 1) {
                         System.out.println(", ");
                     }
                 }
             }
             System.out.println("{}");
             System.out.println();
         }
     }
}

打印结果:

public void get() {}
public void print() {}
public void fun() {}
public final void wait(long arg-0, int arg-1) throws InterruptedException{}
public final native void wait(long arg-0) throws InterruptedException{}
public final void wait() throws InterruptedException{}
public boolean equals(Object arg-0) {}
public String toString() {}
public native int hashCode() {}
public final native Class getClass() {}
public final native void notify() {}
public final native void notifyAll() {}

 

调用(invoke):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

该方法的英文名字是invoke,中文名称就叫“调用”,该方法在Method类中,Method类本身就代表一个方法,当Method类中的对象调用invoke方法时,

就相当于调用了Method对象所代表的方法,方法里面传入对应的参数,实现动态调用方法。这里可能比较难理解,看一下一个简单的实例:

import java.lang.reflect.Method;
class Student4 {
     private String name;
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
}
public class ReflectInvokeDemo {
     public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("com.iflytek.Student4");
         // 实例化对象
         Object obj = cls.newInstance();
         //获取setName()方法
         Method setNameMethod = cls.getMethod("setName", String.class);
         //获取getName()方法
         Method getNameMethod = cls.getMethod("getName");
         //调用setName()方法,相当于 对象.setName("xyz");
         setNameMethod.invoke(obj, "xyz");
         //调用getName()方法并输出
         System.out.println(getNameMethod.invoke(obj));
     }
}

注意上面红色字体的代码

 

调用类中的属性和调用类中的方法差不多,也是分为两大类,每个大类里面分为两小类,如下:

获取包括继承而来的属性:

获取全部属性:

public Field[] getFields()  throws SecurityException 

获取指定属性:

public Field getField(String name)  throws NoSuchFieldException,  SecurityException 

获取本类定义的属性:

        获取全部属性:

public Field[] getDeclaredFields()  throws SecurityException 

        获取指定的属性:

public Field getDeclaredField(String name)  throws NoSuchFieldException,  SecurityException 

利用反射获取类中的属性,是不提倡使用的,因为违背了面向对象的封装特性
在Field类中定义了进行属性调用的方法:

设置属性内容:

public void set(Object obj, Object value)  throws IllegalArgumentException, IllegalAccessException 

获取属性类容:

public Object get(Object obj)  throws IllegalArgumentException, IllegalAccessException 

实例:

import java.lang.reflect.Field;
class Student5 {
     private String name;
}
public class ReflectFiledDemo {
     public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("com.iflytek.Student5");
         //实例化
         Object obj = cls.newInstance();
         //获取属性
         Field nameField = cls.getDeclaredField("name");
         //取消访问检查
         <strong>nameField.setAccessible(true);</strong>
         //给属性赋值
         nameField.set(obj, "马小超");
         //获取属性值并输出
         System.out.println(nameField.get(obj));
     }
}

大家在阅读上面代码时会看到有一行代码被加粗标红了,为什么了?原因是Student类中的name属性是private属性的,不对外开放,如果Field类直接访问该属性,

会报权限错误。

在Construction,Method,Field三个类中有一个共同的父类AccessibleObject,定义了取消封装的操作:setAccessible(Boolean flag)

public void setAccessible(boolean flag)  throws SecurityException 

该方法默认的是参数是false,表示反射的对象应该实施 Java 语言访问检查。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。

 

得到某个类的静态属性

public Object getStaticProperty(String className, String fieldName)
             throws Exception {
     Class ownerClass = Class.forName(className);
     Field field = ownerClass.getField(fieldName);
     Object property = field.get(ownerClass);
     return property;
}

Class ownerClass = Class.forName(className) :首先得到这个类的Class。

Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。

Object property = field.get(ownerClass) :这里和上面例子有些不同,因为该属性是静态的,所以直接从类的Class里取。

 

执行某个类的静态方法

public Object invokeStaticMethod(String className, String methodName,
             Object[] args) throws Exception {
     Class ownerClass = Class.forName(className);
     Class[] argsClass = new Class[args.length];
     for (int i = 0, j = args.length; i < j; i++) {
         argsClass[i] = args[i].getClass();
     }
    Method method = ownerClass.getMethod(methodName,argsClass);
     return method.invoke(null, args); 
}
 

invoke的一个参数是null,因为这是静态方法,不需要借助实例运行