• 面试中反射的用法简介


    1.背景介绍
    反射的概述

    反射是框架设计的灵魂

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

    要想获得类的各种信息,必须先要获取到该类的字节码文件对象。该对象也就是java.lang.Class类,Class类用于表示java程序编译后得到的.class文件。

    Class类的理解

    Java程序在运行时,系统会对所有的对象进行所谓的运行时类型标识(RTTI,Run-Time Type Identification),其作用是在运行时识别一个对象的类型和类的信息。

    传统的”RRTI”,它假定我们在编译期已知道了所有类型(在没有反射机制时,一般都是编译期已确定其类型,如new对象时该类必须已定义好);另外一种是反射机制,它允许我们在运行时发现和使用类型的信息。

    在Java中用来表示运行时类型信息的对应类就是Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。

    Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

    JVM为每种类型管理一个独一无二的Class对象。也就是说,无论创建多少个实例对象,在内存中每个类有且只有一个相对应的Class对象,并且基本的java类型和关键字void也都对应一个Class对象。运行程序时,JVM首先检查所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

    2.知识剖析
    在这里先看一下java为我们提供了哪些反射机制中的类:

    java.lang.Class;

    java.lang.reflect.Constructor;

    java.lang.reflect.Field; 

    java.lang.reflect.Method;

    java.lang.reflect.Modifier;

    反射的基本运用:Class对象获取方式

    之前说了,Class对象是jvm用来保存对应实例对象的相关信息的,除此之外,我们完全可以把Class对象看成一般的实例对象。得到一个实例对象对应的Class对象有以下三种方式:

    1.通过实例变量的getClass()方法

    Test3 test3 =new Test3();
    
    Class c3 =test3.getClass();

    根据已经实例化的对象,利用getClass方法

    2.通过类Class的静态方法forName()

    Classc2 =Class.forName("com.getClassObject.Test2");

    传入的参数是对应类的全限定名

    3.直接给出对象类文件的.class

    Class c1 =Test1.class;

    反射的基本运用:判断是否为某个类的实例

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

    Class cls =Class.forName("com.isInstance.Demo"); //创建了一个Demo类的Class对象
    
    boolean b1 =cls.isInstance(new Integer(37));

    反射的基本运用:创建实例

    通过反射来生成对象主要有两种方式:

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

    //获取class对象

    Class c1 =Class.forName("com.createInstance.Instance1");

    //第一种方式:根据class对象,创建对应的实例

     //调用无参数的构造函数,直接调用Class类中的newInstance
    
    Instance1 instance1 = (Instance1)c1.newInstance();
     

    (2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

    //获取Instance1所对应的Class对象

    Class c2 =Instance1.class;

    //获取Instance1类带一个int参数的构造器

    //若想调用有参构造函数,则需要调用Constructor类中newInstance()方法

    Constructor constructor =c2.getConstructor(int.class);

    //根据构造器创建实例

    Object obj =constructor.newInstance(3213);

    反射的基本运用:获取方法

    获取某个Class对象的方法集合,主要有以下几个方法:

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

    (2) getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

    (3) getMethod("add", int.class, int.class) 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

    Class c =Class.forName("com.getMethod.methodClass");

    //获取methodClass类的所有公有方法(可获取到父类的方法)

    Method[]methods =c.getMethods();

    //获取methodClass类的所有的方法

    Method[]declaredMethods =c.getDeclaredMethods();

    //获取指定方法,methodClass类的add方法

    Method publicMethod =c.getMethod("add", int.class, int.class);

    //获取指定私有方法,div方法

    Method privateMethod =c.getDeclaredMethod("div", int.class, int.class);

    反射的基本运用:获取构造器信息

    获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例。

    public T newInstance(Object ... initargs)

    具体方法同获取对应类方法

    反射的基本运用:获取类的成员变量(字段)信息

    主要是这几个方法:

    getFiled: 访问公有的成员变量

    getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量。

    getFileds和getDeclaredFields用法同上(参照Method)

    反射的基本运用:反射用于工厂模式

    先看看传统的工厂模式

    主类如下

    interface Fruit {
    
    void eat();
    
    }
     
    
    class Apple implements Fruit {
        public void eat(){
            System.out.println("Eat Apple");
        }
    }
     
    
    class Orange implements Fruit {
        public void eat(){
            System.out.println("Eat Orange");
        }
    }
             

    工厂类

    public class Factory {
    
    public static Fruit getInstance(String fruitName){
    
    Fruit f=null;
    
    if("Apple".equals(fruitName)){
    
        f=new Apple();
    
    }
    
    if("Orange".equals(fruitName)){
    
        f=new Orange();
    
    }
    
        return f;
    
    }
    
    }


    可以看到如果我要新增一个实现类,就要在工厂类中改代码,耦合度较高。

    利用反射实现工厂

    public class ReflexFactory {
    
    public static Fruit2 getInstances(String className) {
    
      Fruit2 f2 =null;
    
      try {
    
    //利用反射
    
    f2 = (Fruit2)Class.forName(className).newInstance();
    
      }catch (Exception e) {
    
        e.printStackTrace();
    
      }
    
      return f2;
    
      }
    
    }


    当如果要新增实现类的话,只要将传入的参数改变就好,无需更改工厂内的代码。

    3.常见问题
    反射机制的作用?

    1,反编译:.class-->.java

    2,通过反射机制访问java对象的属性,方法,构造方法等;

    暴力反射?

    获取类的私有成员。通过setAccessible(true)方法,设置成可访问。

    类加载的过程?

    加载:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象。

    链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用。

    初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。

    反射的应用场景?

    Java的反射特性一般结合注解和配置文件(如:XML)来使用,这也是大部分框架(Spring等)支持两种配置方式的原因。还有著名的junit测试框架也是利用反射方法名和参数名来进行测试的。

    理解泛化的Class对象引用

    由于Class的引用总是指向某个类的Class对象,利用Class对象可以创建实例,这也就说明Class对象的引用指向的是对象确切的类型。在Java SE5引入泛型后,使我们可以利用泛型来表示Class对象更具体的类型,即使在运行期间会被擦除,但编译期足以确保我们使用正确的对象类型。

  • 相关阅读:
    jQuery.ajax() – jQuery API
    Crossdomain Ajax with CORS
    ajax How do I send a crossdomain POST request via JavaScript? Stack Overflow
    人多、房价贵、空气不好、交通不好、适合奋斗的人、陌生环境、没有亲人朋友、哥们儿你这是在用生命在北漂啊
    write less do more jquery slogan
    jQuery.ajax() – jQuery API
    在服务端Response一个XML
    软件版本控制(SVN/CVS)详解
    1721. Gray code[Special judge]
    cocos2dx下载旧版本小游戏修改注意点
  • 原文地址:https://www.cnblogs.com/Xyg12133617/p/11994260.html
Copyright © 2020-2023  润新知