☕ Java基础: (8) Java的反射

[========]
编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。

运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来

Java 反射机制是在【运行状态】中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

应用:在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。

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

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
  • 在运行时调用任意一个对象的方法

反射的基本运用

反射相关的类一般都在 java.lang.relfect 包里。要想知道一个类的属性和方法,必须先获取到该类的字节码文件对象。获取类的信息时,使用的就是 Class 类中的方法。所以先要获取到每一个字节码文件(.class)对应的 Class 类型的对象.

1. 获取 Class 对象

  • (1)使用 Class 类的 forName 静态方法:

    // 比如JDBC开发中常用此方法加载数据库驱动
    Class.forName(driver);
    
  • 直接获取某一个对象的 class

    Class<?> klass = int.class;
    Class<?> classInt = Integer.TYPE;
    
  • 调用某个对象的 getClass() 方法:

    StringBuilder sb = new StringBuilder("123");
    Class<?> klass = sb.getClass();
    

2. 判断是否为某个类的实例

一般用 instanceof 关键字判断是否为某个类的实例,同时也可借助反射中 Class 对象的 isInstance() 方法判断是否为某个类的实例:

public native boolean isInstance(Object obj);

3. 创建实例

  • 使用Class对象的 newInstance() 方法来创建Class对象类的实例

    Class<?> c = String.class;
    Object str = c.newInstance();
    
  • 先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的newInstance() 方法来创建实例。(该方法可以用指定的构造器构造类的实例)

    // 获取String所对应的Class对象
    Class<?> c = String.class;
    // 获取String类带一个String参数的构造器
    Constructor constructor = c.getContructor(String.class);
    // 再根据构造器创建实例
    Object obj = constructor.newInstance("2333");
    sout(obj);
    

4. 获取方法

  • getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

    public Method[] getDeclaredMethods() throws SecurityException
    
  • getMethods 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

    public Method[] getMethods() throws SecurityException
    
  • getMethod 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

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

Example:

public class test1 {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException {
        Class<?> c = MethodClass.class;
        Object obj = c.newInstance();
        final Method[] methods = c.getMethods();
        final Method[] declaredMethods = c.getDeclaredMethods();
        // 获取MethodClass的特定方法
        final Method method = c.getMethod("add", int.class, int.class);

        System.out.println("getMethods()方法获取的所有方法:");
        for (Method m : methods) System.out.println(m);
        
        System.out.println("\ngetDeclaredMethods方法获取的所有方法:");
        for (Method m : declaredMethods) System.out.println(m);
    }

}

class MethodClass {

    public final int fuck = 3;
    public int add(int a, int b) {
        return a + b;
    }

    public int sub(int a, int b) {
        return a + b;
    }
}

6. 获取类的成员变量(字段)信息

  • getFiled:访问公有的成员变量

    c.getField("fuck");
    
  • getDeclaredField:所有已声明的成员变量,但不能得到其父类的成员变量

getFiledsgetDeclaredFields方法用法同上(5.)

7. 调用方法

从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。

public class test1 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        
        Class<?> kclass = MethodClass.class;
        Object obj = kclass.newInstance();
        Method method = kclass.getMethod("add", int.class, int.class);
    	Object res = method.invoke(obj, 1, 4);
        sout(res); // 5
	}
    
}

8. 利用反射创建数组

Class<?> cls = Class.forName("java.lang.String");
//通过Array.newInstance()创建数组对象
Object array = Array.newInstance(cls, 25);
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");

// 获取某一项
System.out.println(Array.get(array, 3));

Java 反射机制的优缺点

优点:

  • 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
  • 与 Java 动态编译相结合,可以实现无比强大的功能。
  • 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

缺点:

  • 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
  • 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。

posted on 2022-03-20 20:15  micromatrix  阅读(86)  评论(0编辑  收藏  举报

导航