Java 基础之详解 Java 反射机制

一、什么是 Java 的反射机制?

反射(Reflection)是 Java 的高级特性之一,是框架实现的基础,定义:Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

一般而言,当用户使用一个类的时候,应该获取这个类,而后通过这个类实例化对象,但是使用反射则可以相反的通过对象获取类中的信息。

通俗的讲反射就是可以在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成的对象。它允许运行中的 Java 程序获取自身的信息,自己能看到自己,就像照镜子一样。

二、Java 反射机制常见方法介绍

1. Java 反射实现的关键点之 Class(字节码文件对象)

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。JVM 中有 N 多的实例,每个类的实例都有 Class 对象。(包括基本数据类型)

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM 已经帮我们创建好了。

如果知道一个实例,那么可以通过实例的“getClass()”方法获得运行实例的 Class(该类型的字节码文件对象),如果你知道一个类型,那么你也可以使用“.class”的方法获得运行实例的 Class。

方法 2~17 都是类 Class 的方法。

java.lang.Class 继承自 java.lang.Object。

2. getName() 方法

String getName();返回此 Member 表示的底层成员或构造方法的简单名称。

3. forName() 方法
public static Class <?> forName(String className)
                        throws ClassNotFoundException

返回与带有给定字符串名的类或接口相关联的 Class 对象。

参数:className - 所需类的完全限定名

4. getSuperclass() 方法
public Class<? super T> getSuperclass()

返回:此对象所表示的类的超类。

5. getInterfaces() 方法
public Class< ? >[] getInterfaces()

返回:该类所实现的接口的一个数组

6. getConstructors() 方法
public Constructor< ? >[] getConstructors() throws SecurityException

返回:表示此类公共构造方法的 Constructor 对象数组

7. newInstance() 方法
public T newInstance() throws InstantiationException, IllegalAccessException

创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。如果该类尚未初始化,则初始化这个类。

返回:此对象所表示的类的一个新分配的实例。

8. getDeclaredConstructors() 方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException

返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类存在一个默认构造方法,则它包含在返回的数组中。 如果此 Class 对象表示一个接口、一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。

返回:表示此类所有已声明的构造方法的 Constructor 对象的数组

9. getFields() 方法
public Field[] getFields()  throws SecurityException`

返回:表示公共字段的 Field 对象的数组

10. getDeclaredFields() 方法
public Field[] getDeclaredFields() throws SecurityException

返回:表示此类所有已声明字段的 Field 对象的数组,与 getFields() 方法的区别是 getFields() 方法只返回公有属性,而 getDeclaredFields() 方法返回所有属性。

11. getMethods() 方法
public Method[] getMethods() throws SecurityException

返回:表示此类中公共方法的 Method 对象的数组

12. getDeclaredMethods() 方法
public Method[] getDeclaredMethods()  throws SecurityException

返回:表示此类所有声明方法的 Method 对象的数组

13. getClassLoader() 方法
public ClassLoader getClassLoader()

返回该类的类加载器。有些实现可能使用 null 来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回 null。如果存在安全管理器,并且调用者的类加载器不是 null,也不同于或是请求其类加载器的类的类加载器的祖先,则此方法通过 RuntimePermission("getClassLoader") 权限调用此安全管理器checkPermission 方法,以确保可以访问该类的类加载器。如果此对象表示一个基本类型或 void,则返回 null。

返回:加载此对象所表示的类或接口的类加载器。

14. getInterfaces() 方法
public Class<?>[] getInterfaces()

返回:该类所实现的接口的一个数组

15. getComponentType() 方法
public Class<?> getComponentType()

返回表示数组组件类型的 Class。如果此类不表示数组类,则此方法返回 null。

返回:如果此类是数组,则返回表示此类组件类型的 Class

16. getResourceAsStream() 方法
public InputStream getResourceAsStream(String name)

查找具有给定名称的资源。查找与给定类相关的资源的规则是通过定义类的 class loader 实现的。此方法委托此对象的类加载器。如果此对象通过引导类加载器加载,则此方法将委托给 ClassLoader.getSystemResourceAsStream(java.lang.String)。

参数:name - 所需资源的名称

返回:一个 InputStream 对象;如果找不到带有该名称的资源,则返回 null

17. isInstance() 方法
public boolean isInstance(Object obj)

参数:obj - 要检查的对象

返回:如果 obj 是此类的实例,则返回 true

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

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

参数:

  • obj - 从中调用底层方法的对象
  • args - 用于方法调用的参数

返回:使用参数 args 在 obj 上指派该对象所表示方法的结果

java.lang.reflectMethod
java.lang.Object
  继承者 java.lang.reflect.AccessibleObject
      继承者 java.lang.reflect.Method
19. newProxyInstance() 方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:

Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });

参数:

  • loader - 定义代理类的类加载器
  • interfaces - 代理类要实现的接口列表
  • h - 指派方法调用的调用处理程序

返回:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

java.lang.reflectProxy
java.lang.Object
  继承者 java.lang.reflect.Proxy

三、Java 反射机制常见用法举例

1. 使用 Java 反射通过一个对象获取某个类的完整包名和类名

通过实例的 getClass() 方法获得运行实例的字节码文件对象,然后通过 getName() 方法获得类的完整包名和类名。

    public static void test1(){
        ReflectTest testReflect = new ReflectTest();
        System.out.println(testReflect.getClass().getName());
    }
    //结果:com.channelsoft.ssm.controller.ReflectTest
2. 使用 Java 反射获取 Class 对象(三种方式)
  • 方式1:通过 Class 类的静态方法获取 Class 类对象 (推荐)
  • 方式2:因为所有类都继承 Object 类。因而可通过调用 Object 类中的 getClass 方法来获取
  • 方式3:任何数据类型(包括基本数据类型)都有一个“静态”的 class 属性
    public static void test2() throws Exception{
            Class<?> class1 = null;
            Class<?> class2 = null;
            Class<?> class3 = null;
            class1 =  Class.forName("com.channelsoft.ssm.controller.ReflectTest");
            //这一new 产生一个ReflectTest对象,一个Class对象。 
            class2 = new ReflectTest().getClass();
            class3 = ReflectTest.class;
            System.out.println("类名称 1:   " + class1.getName());
            System.out.println("类名称 2:  " + class2.getName());
            System.out.println("类名称 3:  " + class3.getName());
    }

运行结果:

enter image description here

3. 使用 Java 反射获取一个对象的父类和实现的接口
public static void test3() throws ClassNotFoundException{
        Class<?> clazz = Class.forName("com.channelsoft.ssm.controller.ReflectTest");
        // 取得父类
        Class<?> parentClass = clazz.getSuperclass();
        System.out.println("父类为:" + parentClass.getName());
        // 获取所有的接口
        Class<?> intes[] = clazz.getInterfaces();
        System.out.println("实现的接口有:");
        for (int i = 0; i < intes.length; i++) {
            System.out.println((i + 1) + ":" + intes[i].getName());
        }
}

注:ReflectTest 实现了 Serializable 接口

运行结果:

enter image description here

4. 使用 Java 反射获取某个类的全部构造函数并调用私有构造方法

Student 类:

package com.channelsoft.ssm.domain;

public class Student {

 public Student(){  
        System.out.println("调用了公有、无参构造方法。。。");  
    }  

    public Student(char name){  
        System.out.println("姓名:" + name);  
    }  

    public Student(String name ,int age){  
        System.out.println("姓名:"+name+"年龄:"+ age);
    }  

    //受保护的构造方法  
    protected Student(boolean b){  
        System.out.println("受保护的构造方法 b = " + b);  
    }  

    //私有构造方法  
    private Student(int age){  
        System.out.println("私有的构造方法,年龄:"+ age);  
    }  
}

获取 Student 类的全部构造函数,并使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类 Student 的实例来调用私有构造方法。

package com.channelsoft.ssm.controller;

import java.lang.reflect.Constructor;

public class Constructors {
    public static void main(String[] args) throws Exception {  
        //1.加载Class对象  
        Class clazz = Class.forName("com.channelsoft.ssm.domain.Student");      
        //2.获取所有公有构造方法  
        System.out.println("**********************所有公有构造方法****************************");  
        Constructor[] conArray = clazz.getConstructors();  
        for(Constructor c : conArray){  
            System.out.println(c);  
        }  

        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");  
        conArray = clazz.getDeclaredConstructors();  
        for(Constructor c : conArray){  
            System.out.println(c);  
        }  

        System.out.println("*****************获取公有、无参的构造方法***************************"); 
        //1、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的***类型***,
        //2、返回的是描述这个无参构造函数的类对象。  
        Constructor con = clazz.getConstructor(null);  
        System.out.println("con = " + con);  
        //调用构造方法  
        Object obj = con.newInstance();  
        System.out.println("obj = " + obj);  

        System.out.println("******************获取私有构造方法,并调用**************************");  
        con = clazz.getDeclaredConstructor(int.class);  
        System.out.println(con);  
        //调用构造方法  
        con.setAccessible(true);//暴力访问(忽略掉访问修饰符)  
        obj = con.newInstance(26);  
    }  
}

运行结果:

enter image description here

5. 使用 Java 反射获取某个类的全部属性

Student 类:

package com.channelsoft.ssm.domain;

public class Student {

    public String name;  
    protected int age;  
    char sex;  
    private String phoneNum;

    @Override  
    public String toString() {  
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex  
                + ", phoneNum=" + phoneNum + "]";  
    }  
}

获取 Student 类的全部属性并调用:

package com.channelsoft.ssm.controller;

import java.lang.reflect.Field;

import com.channelsoft.ssm.domain.Student;

public class Fields {
    public static void main(String[] args) throws Exception {  
        //1.获取Class对象  
        Class stuClass = Class.forName("com.channelsoft.ssm.domain.Student");  
        //2.获取字段  
        System.out.println("************************获取所有公有的字段**************************************");  
        Field[] fieldArray = stuClass.getFields();  
        for(Field f : fieldArray){  
            System.out.println(f);  
        }  
        System.out.println();
        System.out.println("************************获取所有的字段(包括私有、受保护、默认的)**********************");  
        fieldArray = stuClass.getDeclaredFields();  
        for(Field f : fieldArray){  
            System.out.println(f);  
        }  
        System.out.println();
        System.out.println("*************************获取公有字段并调用**************************************");  
        Field f = stuClass.getField("name");  
        System.out.println(f);  
        //获取一个对象  
        Object obj = stuClass.getConstructor().newInstance();  
        f.set(obj, "laughitover");
        //验证  
        Student stu = (Student)obj;  
        System.out.println("验证姓名:" + stu.name);  

        System.out.println();
        System.out.println("**************************获取私有字段并调用**************************************");  
        f = stuClass.getDeclaredField("phoneNum");  
        System.out.println(f);  
        f.setAccessible(true);//暴力反射,解除私有限定  
        f.set(obj, "13812345678");  
        System.out.println("验证电话:" + stu);  
    }  
}

运行结果:

enter image description here

6. 使用 Java 反射获取某个类的全部方法
package com.channelsoft.ssm.domain;

public class Student {    
    public void show1(String s){  
        System.out.println("调用了:公有的,String参数的show1(): s = " + s);  
    }  
    protected void show2(){  
        System.out.println("调用了:受保护的,无参的show2()");  
    }  
    void show3(){  
        System.out.println("调用了:默认的,无参的show3()");  
    }  
    private String show4(int age){  
        System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);  
        return "abcd";  
    } 
}

获取某个类的全部方法并且调用共有方法 show1() 和私有方法 show4():

public static void test6() throws Exception {
        //1.获取Class对象
        Class stuClass = Class.forName("fanshe.method.Student");
        //2.获取所有公有方法
        System.out.println("***************获取所有的”公有“方法*******************");
        stuClass.getMethods();
        Method[] methodArray = stuClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************获取所有的方法,包括私有的*******************");
        methodArray = stuClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************获取公有的show1()方法*******************");
        Method m = stuClass.getMethod("show1", String.class);
        System.out.println(m);
        //实例化一个Student对象
        Object obj = stuClass.getConstructor().newInstance();
        m.invoke(obj, "laughitover");

        System.out.println("***************获取私有的show4()方法******************");
        m = stuClass.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
        System.out.println("返回值:" + result);
}

运行结果:

enter image description here

7. 使用 Java 反射调用某个类的方法

Java 反射获取 Class 对象并使用invoke()方法调用方法 reflect1() 和 reflect2():

public static void test7() throws Exception{
        Class<?> clazz = Class.forName("com.channelsoft.ssm.controller.ReflectTest");
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "张三");
    }

    public void reflect1() {
        System.out.println("Java 反射机制 - 调用某个类的方法1.");
    }
    public void reflect2(int age, String name) {
        System.out.println("Java 反射机制 - 调用某个类的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    }

运行结果:

enter image description here

8. 反射机制的动态代理实例

如果想获取实现 Subject() 接口的其它实例,只需新建类实现接口 Subject(),使用 MyInvocationHandler.bind() 方法获取即可实现调用。

 public static void test8(){
        MyInvocationHandler demo = new MyInvocationHandler();
        Subject sub = (Subject) demo.bind(new RealSubject());
        String info = sub.say("语文", 90);
        System.out.println(info);
    }

如果想要完成动态代理,首先需要定义一个 InvocationHandler 接口的子类,完成代理的具体操作。

package com.channelsoft.ssm.controller;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {
    private Object obj = null;

    public Object bind(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object temp = method.invoke(this.obj, args);
        return temp;
    }
}
package com.channelsoft.ssm.service;

public interface Subject {
    public String say(String name, int score);
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Subject;

public class RealSubject implements Subject {
    public String say(String name, int score) {
            return name + "  " + score;
    }
}

运行结果:

enter image description here

9. 通过反射越过泛型检查

泛型作用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的。

    public static void test10() throws Exception{
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(100);
        list.add(200);
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "Java反射机制实例。");
        //遍历集合  
        for(Object obj : list){  
            System.out.println(obj);  
        }
    }

运行结果:

enter image description here

10. 通过反射机制获得数组信息并修改数组的大小和值

通过反射机制分别修改int和String类型的数组的大小并修改int数组的第一个值。

 public static void test12() throws Exception{
         int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
         int[] newTemp = (int[]) arrayInc(temp, 15);
         print(newTemp);
         Array.set(newTemp, 0, 100);
         System.out.println("修改之后数组第一个元素为: " + Array.get(newTemp, 0));
         print(newTemp);
         String[] atr = { "a", "b", "c" };
         String[] str1 = (String[]) arrayInc(atr, 8);
         print(str1);
    }
    // 修改数组大小
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    // 打印
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        Class<?> arr = obj.getClass().getComponentType();
        System.out.println("数组类型: " + arr.getName());
        System.out.println("数组长度为: " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }

运行结果:

enter image description here

11. 将反射机制应用于工厂模式

对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。

package com.channelsoft.ssm.service;

public interface Fruit {
    public abstract void eat();
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Fruit;

public class Orange implements Fruit {
    public void eat() {
        System.out.println("Orange");
    }
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Fruit;

public class Apple implements Fruit {
    public void eat() {
        System.out.println("Apple");
    }
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Fruit;

public class Factory {
    public static Fruit getInstance(String ClassName) {
        Fruit f = null;
        try {
            f = (Fruit) Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
    public static void test13(String fruit) throws Exception{
        Fruit f = Factory.getInstance(fruit);
        if (f != null) {
            f.eat();
        }
   }

还可以通过加载配置文件的方式:

    public static String getValue(String key) throws IOException{  
        Properties pro = new Properties();//获取配置文件的对象  
        InputStream in=new Demo().getClass().getResourceAsStream("/pro.txt");
        pro.load(in);//将流加载到配置文件对象中  
        in.close();  
        return pro.getProperty(key);//返回根据key获取的value值  
    } 

pro.txt 内容如下:

className = com.channelsoft.ssm.domain.Apple

运行结果:

enter image description here

12. 通过 Java 反射机制获取 ClassLoader 类加载器

enter image description here

package com.channelsoft.ssm.controller;

public class ReflectTest{

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

    public static void testClassLoader() throws ClassNotFoundException {  
        //1、获取一个系统的类加载器  
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();  
        System.out.println("系统的类加载器-->" + classLoader);  

        //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))  
        classLoader = classLoader.getParent();  
        System.out.println("扩展类加载器-->" + classLoader);  

        //3、获取扩展类加载器的父类加载器  
        //输出为Null,引导类加载器无法被Java程序直接引用  
        classLoader = classLoader.getParent();  
        System.out.println("启动类加载器-->" + classLoader);  

        //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器  
        classLoader = Class.forName("com.channelsoft.ssm.controller.ReflectTest").getClassLoader();  
        System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);  

        //5、测试JDK提供的Object类由哪个类加载器负责加载的  
        classLoader = Class.forName("java.lang.Object").getClassLoader();  
        System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);  
    }  
}

enter image description here

四、Java 反射机制相关面试题

1. 反射创建类实例的三种方式是什么?

答案参见:本篇文章:Java 反射机制常见用法举例2

2. 反射中,Class.forName 和 ClassLoader 区别?

Class.forName(className)方法,其实调用的方法是 Class.forName(className,true,classloader); 第 2 个参数表示在 loadClass 后必须初始化。根据 JVM 加载类的知识,我们知道在执行过此方法后,目标对象的 static 块代码已经被执行,static 参数也已经被初始化。

而 ClassLoader.loadClass(className) 方法,实际调用的方法是 ClassLoader.loadClass(className,false); 第 2 个参数表示目标对象被装载后不进行链接,这就意味着不会去执行该类静态块中间的内容。

例如,在 JDBC 使用中,常看到这样的用法,Class.forName("com.mysql.jdbc.Driver") 如果换成了 getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行。根据 com.mysql.jdbc.Driver 的源代码,Driver 在 static 块中会注册自己到 java.sql.DriverManager。而 static 块就是在 Class 的初始化中被执行。所以这个地方就只能用 Class.forName(className)。

com.mysql.jdbc.Driver 的源代码:

// Register ourselves with the DriverManager
static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
} 

Java 类的加载过程:

在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步:

  • 装载:查找和导入类或接口的二进制数据;
  • 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
  • 校验:检查导入类或接口的二进制数据的正确性;
  • 准备:给类的静态变量分配并初始化存储空间;
  • 解析:将符号引用转成直接引用;
  • 初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。

3. 描述动态代理的几种实现方式,分别说出相应的优缺点

JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是由 Java 内部的反射机制来实现的,CGLIB 动态代理底层则是借助 ASM 框架来实现的,实现了无反射机制进行代理,利用空间来换取了时间,代理效率高于 JDK 。总的来说,反射机制在生成类的过程中比较高效,而 ASM 在生成类之后的相关执行过程中比较高效(可以通过将 ASM 生成的类进行缓存,这样解决 ASM 生成类过程低效问题)。还有一点必须注意:JDK 动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,JDK 动态代理不能应用。由此可以看出,JDK 动态代理有一定的局限性,CGLIB 这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

五、总结

很多人认为反射在实际的 Java 开发应用中并不广泛,其实不然。我们常用的 JDBC 中有一行代码:

Class.forName('com.MySQL.jdbc.Driver.class').newInstance();

这里用到的就是 Java 反射机制,除此之外很多框架也都用到反射机制。

实际上反射机制就是非常规模式,如果说方法的调用是 Java 正确的打开方式,那反射机制就是 Java 的后门。为什么会有这个后门呢?这涉及到了静态和动态的概念:

  • 静态编译:在编译时确定类型,绑定对象
  • 动态编译:运行时确定类型,绑定对象

两者的区别在于,动态编译可以最大程度地支持多态,多态最大的意义在于降低类的耦合性,而 Java 语言确是静态编译(如:int a=3;要指定类型),Java 反射很大程度上弥补了这一不足:解耦以及提高代码的灵活性,所以 Java 也被称为是准动态语言。

尽管反射有诸多优点,但是反射也有缺点:

  1. 由于反射会额外消耗一定的系统资源,因此,反射操作的效率要比那些非反射操作低得多。
  2. 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用(代码有功能上的错误)。所以能用其它方式实现的就尽量不去用反射。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值