什么是 Java 的反射机制

Java 反射由浅入深 | 进阶必备
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

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

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

通过类类型创建类和通过new创建类的不同之处是:类类型创建的是动态加载类。
程序执行分为编译器和运行期,编译时刻加载一个类就称为静态加载类,运行时刻加载类称为动态加载类

获取字节码文件对象的三种方式。
1、Class clazz1 = Class.forName(“全限定类名”);  
//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

2、Class clazz2 = Person.class;    
//当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。

3、Class clazz3 = p.getClass();    
//通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段

有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用上面哪种方式获取字节码对象合理,视不同情况而定。下面介绍Class类的功能。
动态代理的概述和实现

反射的缺点

性能第一 Performance Overhead
反射包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效
率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程 序中使用反射。
安全限制 Security Restrictions
使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有
安全限制的环境中运行,如 Applet,那么这就是个问题了。
内部暴露 Exposure of Internals
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方
法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。 反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

反射机制的应用实例

1.反射最重要的用途就是开发各种通用框架
spring 的 ioc/di 也是反射…
javaBean和jsp之间调用也是反射…
struts的 FormBean 和页面之间…也是通过反射调用…
JDBC 的 classForName()也是反射…
hibernate的 find(Class clazz) 也是反射…
2.Android编译期问题
Android的安全权限问题我把它简单的划分成三个层次,最不严格的一层就是仅仅骗过编译器的“@hide”标记。对于一款开源的操作系统而言,这个标记本身并不具备安全上的限制。不过,从上次Google过来的负责Android工程师的说法来看,这个标记的作用更多的是方便硬件厂商做闭源的二次开发。这样解释倒也说得过去。
不过这并不影响我们使用反射机制以绕过原生Android的第一层安全措施。如果你熟悉源码的话,会发现这可以应用到很多地方。并且最关键的是你并不需要放在源码中编译,而是像普通应用程序的开发过程一样。
3.软件的解耦合
在一些网络端的大型项目中,通过配置文件 + ClassLoader + 反射机制结合形成的这种软件解耦和方式已经用得比较普遍了。
4.反射安全
相对于C++来说,Java算是比较安全的语言了。这与它们的运行机制有密切的关系,C++运行于本地,也就是说几乎所有程序的权限理论上都是相同的。而Java由于是运行于虚拟机中,而不直接与外部联系,所以实际上Java的运行环境是一个“沙盒”环境。
Java的安全机制其实是比较复杂的,至少对于我来说是如此。作为Java的安全模型,它包括了:字节码验证器、类加载器、安全管理器、访问控制器等一系列的组件。之前文中提到过,我把Android安全权限划分为三个等级:第一级是针对编译期的“@hide”标记;第二级是针对访问权限的private等修饰;第三级则是以安全管理器为托管的Permission机制。
Java反射确实可以访问private的方法和属性,这是绕过第二级安全机制的方法(之一)。不要认为我们绕过了前两级安全机制就沾沾自喜了,因为这两级安全并不是真正为了安全而设置的。它们的作用更多的是为了更好的完善规则。而第三级安全才是真正为了防止恶意攻击而出现的。在这一级的防护下,你甚至可能都无法完成反射(ReflectPermission),其他的一切自然无从说起。

3.利用反射,在泛型为int的arryaList集合中存放一个String类型的对象

package invocationHandler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Test1 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List<Integer> list = new ArrayList<Integer>();
        list.add(4);
        list.add(5);
        //list.add("ddd"); //在编译期,泛型生效,插入字符串对象,则报错
        Class clazz = list.getClass();
        Method method = clazz.getMethod("add", Object.class);
        method.invoke(list, "ddd");
        System.out.println(list);
    }
}

4.动态代理
一种设计模式,其非常简单,很容易理解,你自己可以做这件事,但是觉得自己做非常麻烦或者不方便,所以就叫一个另一个人(代理)来帮你做这个事情,而你就不用管了,这就是动态代理。举个例子,买火车票叫人代买。

在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理

在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象

分三步,但是注意JDK提供的代理正能针对接口做代理,也就是下面的第二步返回的必须要是一个接口。
1、new出代理对象,通过实现InvacationHandler接口,然后new出代理对象来。
2、通过Proxy类中的静态方法newProxyInstance,来将代理对象假装成那个被代理的对象,也就是如果叫人帮我们代买火车票一样,那个代理就假装成我们自己本人
3、执行方法,代理成功
MyInvocationHandler.java

package invocationHandler;

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

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验");
        method.invoke(target, args);
        System.out.println("日志记录");
        return null;
    }
}

Student.java

package invocationHandler;

public interface Student {
    public void login();
    public void submit();
}

StudentImp.java

package invocationHandler;

public class StudentImp implements Student {
    @Override
    public void login() {
        System.out.println("登录");
    }

    @Override
    public void submit() {
        System.out.println("提交");
    }
}

Test.java

package invocationHandler;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        Student si = new StudentImp();
        MyInvocationHandler m = new MyInvocationHandler(si);
        Student s = (Student) Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), m);
        //注意newProxyInstance的三个参数,第一个,类加载器,第二个被代理对象的接口,第三个代理对象。
        s.login();
        s.submit();
    }
}

反射API代码Demo

Main.java

package cn.lee.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Main {
    /**
     * 为了看清楚Java反射部分代码,所有异常我都最后抛出来给虚拟机处理!
     * @param args
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws NoSuchFieldException
     * @throws SecurityException
     * @throws NoSuchMethodException
     */
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException, NoSuchMethodException {
        // TODO Auto-generated method stub

        //Demo1.  通过Java反射机制得到类的包名和类名
        Demo1();
        System.out.println("===============================================");

        //Demo2.  验证所有的类都是Class类的实例对象
        Demo2();
        System.out.println("===============================================");

        //Demo3.  通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在],无参构造
        Demo3();
        System.out.println("===============================================");

        //Demo4:  通过Java反射机制得到一个类的构造函数,并实现构造带参实例对象
        Demo4();
        System.out.println("===============================================");

        //Demo5:  通过Java反射机制操作成员变量, set 和 get
        Demo5();
        System.out.println("===============================================");

        //Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
        Demo6();
        System.out.println("===============================================");

        //Demo7: 通过Java反射机制调用类中方法
        Demo7();
        System.out.println("===============================================");

        //Demo8: 通过Java反射机制获得类加载器
        Demo8();
        System.out.println("===============================================");

    }

    /**
     * Demo1: 通过Java反射机制得到类的包名和类名
     */
    public static void Demo1()
    {
        Person person = new Person();
        System.out.println("Demo1: 包名: " + person.getClass().getPackage().getName() + ","
                + "完整类名: " + person.getClass().getName());
    }

    /**
     * Demo2: 验证所有的类都是Class类的实例对象
     * @throws ClassNotFoundException
     */
    public static void Demo2() throws ClassNotFoundException
    {
        //定义两个类型都未知的Class , 设置初值为null, 看看如何给它们赋值成Person类
        Class<?> class1 = null;
        Class<?> class2 = null;

        //写法1, 可能抛出 ClassNotFoundException [多用这个写法]
        class1 = Class.forName("cn.lee.demo.Person");
        System.out.println("Demo2:(写法1) 包名: " + class1.getPackage().getName() + ","
                + "完整类名: " + class1.getName());

        //写法2
        class2 = Person.class;
        System.out.println("Demo2:(写法2) 包名: " + class2.getPackage().getName() + ","
                + "完整类名: " + class2.getName());
    }

    /**
     * Demo3: 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在]
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static void Demo3() throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        Class<?> class1 = null;
        class1 = Class.forName("cn.lee.demo.Person");
        //由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数哈~
        Person person = (Person) class1.newInstance();
        person.setAge(20);
        person.setName("LeeFeng");
        System.out.println("Demo3: " + person.getName() + " : " + person.getAge());
    }

    /**
     * Demo4: 通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象
     * @throws ClassNotFoundException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws IllegalArgumentException
     */
    public static void Demo4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
    {
        Class<?> class1 = null;
        Person person1 = null;
        Person person2 = null;

        class1 = Class.forName("cn.lee.demo.Person");
        //得到一系列构造函数集合
        Constructor<?>[] constructors = class1.getConstructors();

        person1 = (Person) constructors[0].newInstance();
        person1.setAge(30);
        person1.setName("leeFeng");

        person2 = (Person) constructors[1].newInstance(20,"leeFeng");

        System.out.println("Demo4: " + person1.getName() + " : " + person1.getAge()
                + "  ,   " + person2.getName() + " : " + person2.getAge()
        );

    }

    /**
     * Demo5: 通过Java反射机制操作成员变量, set 和 get
     *
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws NoSuchFieldException
     * @throws SecurityException
     * @throws InstantiationException
     * @throws ClassNotFoundException
     */
    public static void Demo5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException
    {
        Class<?> class1 = null;
        class1 = Class.forName("cn.lee.demo.Person");
        Object obj = class1.newInstance();

        Field personNameField = class1.getDeclaredField("name");
        personNameField.setAccessible(true);
        personNameField.set(obj, "胖虎先森");

        System.out.println("Demo5: 修改属性之后得到属性变量的值:" + personNameField.get(obj));
    }


    /**
     * Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
     * @throws ClassNotFoundException
     */
    public static void Demo6() throws ClassNotFoundException
    {
        Class<?> class1 = null;
        class1 = Class.forName("cn.lee.demo.SuperMan");

        //取得父类名称
        Class<?>  superClass = class1.getSuperclass();
        System.out.println("Demo6:  SuperMan类的父类名: " + superClass.getName());

        System.out.println("===============================================");


        Field[] fields = class1.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            System.out.println("类中的成员: " + fields[i]);
        }
        System.out.println("===============================================");


        //取得类方法
        Method[] methods = class1.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println("Demo6,取得SuperMan类的方法:");
            System.out.println("函数名:" + methods[i].getName());
            System.out.println("函数返回类型:" + methods[i].getReturnType());
            System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers()));
            System.out.println("函数代码写法: " + methods[i]);
        }

        System.out.println("===============================================");

        //取得类实现的接口,因为接口类也属于Class,所以得到接口中的方法也是一样的方法得到哈
        Class<?> interfaces[] = class1.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            System.out.println("实现的接口类名: " + interfaces[i].getName() );
        }

    }

    /**
     * Demo7: 通过Java反射机制调用类方法
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InstantiationException
     */
    public static void Demo7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException
    {
        Class<?> class1 = null;
        class1 = Class.forName("cn.lee.demo.SuperMan");

        System.out.println("Demo7: \n调用无参方法fly():");
        Method method = class1.getMethod("fly");
        method.invoke(class1.newInstance());

        System.out.println("调用有参方法walk(int m):");
        method = class1.getMethod("walk",int.class);
        method.invoke(class1.newInstance(),100);
    }

    /**
     * Demo8: 通过Java反射机制得到类加载器信息
     *
     * 在java中有三种类类加载器。[这段资料网上截取]
     1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
     2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
     3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
     *
     * @throws ClassNotFoundException
     */
    public static void Demo8() throws ClassNotFoundException
    {
        Class<?> class1 = null;
        class1 = Class.forName("cn.lee.demo.SuperMan");
        String nameString = class1.getClassLoader().getClass().getName();

        System.out.println("Demo8: 类加载器类名: " + nameString);
    }
    
}

Person.java

package cn.lee.demo;

/**
 *
 * @author xiaoyaomeng
 *
 */
class  Person{
    private int age;
    private String name;
    public Person(){

    }
    public Person(int age, String name){
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

SuperMan.java

package cn.lee.demo;

class SuperMan extends Person implements ActionInterface
{
    private boolean BlueBriefs;

    public void fly()
    {
        System.out.println("超人会飞耶~~");
    }

    public boolean isBlueBriefs() {
        return BlueBriefs;
    }
    public void setBlueBriefs(boolean blueBriefs) {
        BlueBriefs = blueBriefs;
    }

    @Override
    public void walk(int m) {
        // TODO Auto-generated method stub
        System.out.println("超人会走耶~~走了" + m + "米就走不动了!");
    }
}

ActionInterface.java

package cn.lee.demo;

interface ActionInterface{
    public void walk(int m);
}

猜你喜欢

转载自blog.csdn.net/GeekLeee/article/details/89480199