详解Java反射机制------入门篇

一、什么叫Java反射机制?

Java中的反射机制是指在运行状态中,对于任意一个类,能够动态获取这个类中的属性和方法;对于任意一个对象,都能够任意调用它的属性和方法。这种动态获取类的信息以及动态调用对象方法的功能称为Java的反射机制。总结就是:反射可以实现运行时知道任意一个类的属性和方法。

二、Java当中为什么需要反射机制?工作原理是什么?

    要明白为什么需要反射机制,首先就得先清楚两个概念:

(1)静态编译:在编译时就必须要知道变量得类型才能编译通过,比如C++,Java等编译之前必须清楚的指明数据类型;

(2)动态编译:显然和上述概念相反,即在编译时不需要立即知道变量的类型,在运行时指明其类型就行。

    有上述概念之后便有了静态语言和动态语言之分,而Java,C++就是典型的静态语言,而Python,Ruby等则为动态语言。但是Java虽是一个静态的解释型语言,但其也有动态的性质------反射(Reflection)。用在Java身上指的是运行时加载,探知,使用编译期间未知的classes。

 下面探讨一下反射是如何在仅知道类名的情况下能够知道整个类的完整构造的(方法,属性等)。

    首先我们都清楚,Java是一种解释型的语言,即编译器首先将源码编译成class文件,然后虚拟机(JVM)再将class文件解释给目标计算机执行。而这里所说的反射机制操作的其实就是源码编译之后的class文件。首先将class文件加载到内存,然后从该文件中解剖出某个具体类的构造函数,方法和属性,从而完全知道某个类的所有构造。下面给出一个简易的demo及字节码文件。demo源码:

public class Student {
    String name="xiaoshuang";
    String gender;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    private void setGender(String gender) {
        this.gender = gender;
    }
    public Student(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }
    public Student(){}

    public static void main(String[] args) {
        //获取该类的方
        try {
            Class c = Class.forName("com.tyf.reflect.Student");
            Method methods[] = c.getDeclaredMethods();//获取该类的所有方法
            Method methods2[]=c.getMethods();//获取该类以及父类的所有public类
            for(Method m:methods){
                System.out.println(m.getName());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

字节码文件:

public class com.tyf.reflect.Student {
  java.lang.String name;

  java.lang.String gender;

  public java.lang.String getName();
    Code:
       0: aload_0
       1: getfield      #1                  // Field name:Ljava/lang/String;
       4: areturn

  public void setName(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field name:Ljava/lang/String;
       5: return

  public java.lang.String getGender();
    Code:
       0: aload_0
       1: getfield      #2                  // Field gender:Ljava/lang/String;
       4: areturn

  public com.tyf.reflect.Student(java.lang.String, java.lang.String);
    Code:
       0: aload_0
       1: invokespecial #3                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #4                  // String xiaoshuang
       7: putfield      #1                  // Field name:Ljava/lang/String;
      10: aload_0
      11: aload_1
      12: putfield      #1                  // Field name:Ljava/lang/String;
      15: aload_0
      16: aload_2
      17: putfield      #2                  // Field gender:Ljava/lang/String;
      20: return

  public com.tyf.reflect.Student();
    Code:
       0: aload_0
       1: invokespecial #3                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #4                  // String xiaoshuang
       7: putfield      #1                  // Field name:Ljava/lang/String;
      10: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #5                  // class java/util/HashSet
       3: dup
       4: invokespecial #6                  // Method java/util/HashSet."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #7                  // String tom
      11: invokeinterface #8,  2            // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
      16: pop
      17: aload_1
      18: invokevirtual #9                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      21: astore_2
      22: aload_2
      23: ldc           #10                 // String add
      25: iconst_1
      26: anewarray     #11                 // class java/lang/Class
      29: dup
      30: iconst_0
      31: ldc           #12                 // class java/lang/Object
      33: aastore
      34: invokevirtual #13                 // Method java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
      37: astore_3
      38: aload_3
      39: aload_1
      40: iconst_1
      41: anewarray     #12                 // class java/lang/Object
      44: dup
      45: iconst_0
      46: bipush        100
      48: invokestatic  #14                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      51: aastore
      52: invokevirtual #15                 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
      55: pop
      56: aload_1
      57: invokeinterface #16,  1           // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
      62: astore        4
      64: aload         4
      66: invokeinterface #17,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      71: ifeq          94
      74: aload         4
      76: invokeinterface #18,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      81: astore        5
      83: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      86: aload         5
      88: invokevirtual #20                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      91: goto          64
      94: goto          102
      97: astore_2
      98: aload_2
      99: invokevirtual #22                 // Method java/lang/Exception.printStackTrace:()V
     102: return
    Exception table:
       from    to  target type
          17    94    97   Class java/lang/Exception
}

    从上面的字节码文件中我们很清楚的能看到类里面的一下public方法和属性。非常值得注意的一点是注意类名,在字节码中使用的是该类的全类名(包名+类名)。因此呢,在使用反射时,forName()方法需要指定该类的全类名,从而从该class文件中可以直接获取原类的完整结构(属性,方法等)。

三、反射的使用及入门

    在使用反射之前,最好清楚一个这样的概念,即类类型。在编程语言中,各种数据都有其数据类型,那么对于类本身而言,它也有自己的类型,即类类型,其实和普通的数据类型差不多,清楚这一点就行。类是java.lang.Class类的实例对象,而Class是所有类的类(There is a class named Class)。对于普通的类实例化时只需要new一个就行,但是要想用new实例化Class是行不通的,因为Class的构造器是私有的(自行查看源码)。

1、获取某个类(Student)的Class对象的三种方法:

(1)Class c1=Student.class;//通过Student类隐藏的静态变量来获取

(2)Class c2=Student.getClass();//通过Stduent的getClass()方法

(3)Class c3=Student.forName("com.tyf.reflect.Student");//com.tyf.reflect.Student为Student类的全类名。

注意:这里的c1,c2,c3是完全一样的,都是Class的对象(学名 类类型)。这里不必纠结Student和c1,c2,c3是否一样了(都叫Class对象),只需了解类类型是什么就行。

2、反射中的相关操作

(1)如何获取类的方法以及方法的执行:

 try{
            Class c = Student.class;
            Object o = c.newInstance();//初始化一个实例
            Method method_setName = c.getDeclaredMethod("setName", String.class);
            Method method_getName = c.getDeclaredMethod("getName");
            method_setName.invoke(o,"yafneg");
            Object name = method_getName.invoke(o);
            System.out.println(name);
        }catch (Exception e){
            e.printStackTrace();
        }

如何来获取该类的所有方法呢?可以用如下两种方法:

 Method methods[] = c.getDeclaredMethods();//获取该类的所有方法
 Method methods2[]=c.getMethods();//获取该类以及父类的所有public类

两者的区别上述注释已经说的很清楚,完整代码如下:

try {
            Class c = Class.forName("com.tyf.reflect.Student");
            Method methods[] = c.getDeclaredMethods();//获取该类的所有方法
           // Method methods2[]=c.getMethods();//获取该类以及父类的所有public类
            for(Method m:methods){
                System.out.println(m.getName());
            }
        }catch (Exception e){
            e.printStackTrace();
        }

执行getDeclaredMethods(),并输出方法名的结果如下:

main 
getName
setName 
setGender 
getGender

从上述结果可以看到,getDeclaredMethods()获取的是该类的所有方法(public+private)。下面的获取属性的方法结果于上述类似,不再赘述。

执行getMethods(),并输出方法名的结果如下:


main
getName
setName
getGender
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

从上面的结果中可以看到,该方法不但获得了Student类的public方法,同时也获得了Student父类(Object)的public方法,注意未获得setGender()方法,因为其是private方法。

(2)获取类属性:

Field field = c.getDeclaredField("name"); //获取指定属性
//Field fields[]=c.getDeclaredFields();//获取所有属性 
//Field fields[]=c.getFields();获取所有的public子类及父类属性
field.setAccessible(true);//设置是否允许访问,因为该属性是privated的

完整代码:

try {
         Class c = Class.forName("com.tyf.reflect.Student");
         Object o = c.newInstance();
         Field field = c.getDeclaredField("name");
         Field fields[] = c.getDeclaredFields();//获取所有属性
         //Field fields[]=c.getFields();获取所有的public子类及父类属性
         field.setAccessible(true);//设置是否允许访问,因为该属性是privated的
         Object name = field.get(o);//获取该属性值
         System.out.println(name);

     } catch (Exception e) {
         e.printStackTrace();
     }

执行结果:

xiaoshuang

至于getDeclaredField()和getField()的区别和上述getDeclardMethod()与getMethods()是一样的,这里不再赘述。

(3)获取类的构造器:

 try{
        Class c = Class.forName("com.tyf.reflect.Student");
        Constructor constructor = c.getDeclaredConstructor(String.class,String.class);//获取含参的构造器
        Object o = constructor.newInstance("yafeng","boy");//利用反射得到的构造器实例化对象
        Field field = c.getDeclaredField("name");//获取name属性
        Object name = field.get(o);//获取属性值
        System.out.println(name);
    }catch (Exception e){
        e.printStackTrace();
    }
 }

执行结果:

yafeng

 

四、Java反射机制的应用

当在某集合中需要放多种数据类型的数据时,可以使用反射机制来解决。

    为什么可以使用反射机制呢?需要注意的是集合中的泛型只在程序编译阶段有效,而在运行期间无效(相当于没有),而反射机制正好就是运行期间才检测数据检测。看下面代码:

 public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("tom");
        //我要给set中要放一个int型的数据,用set.add(11);是会报错的,那么改怎么做?
        Class c = set.getClass();
        try {
            Method m = c.getDeclaredMethod("add",Object.class);
            m.invoke(set,100);
        }catch (Exception e){
            e.printStackTrace();
        }
        for(Object s:set){
            System.out.println(s);
        }
    }

执行结果:

tom 
100

上述的内容即为反射当中常用到的一些东西,掌握这些知识,Java反射基本已经入门。当然Java反射不止这些东西,需要学的还有很多,还需要day day up。

 

全部评论

相关推荐

1.&nbsp;自我介绍2.&nbsp;项目都是自己写的吗?3.&nbsp;我看你用&nbsp;koa2&nbsp;写后端,为什么选择它,能讲讲吗?4.&nbsp;那你提到&nbsp;koa2&nbsp;它是不提供中间件的,你是怎么解决的?5.&nbsp;中间件的原理是什么?(洋葱模型)6.&nbsp;你刚刚说碰到&nbsp;next()&nbsp;就进入下一个中间件,那&nbsp;next&nbsp;只能执行同步,如果是异步的话,你是怎么处理的?(async/await,但是我发现,有的中间件需要在异步中间件之前执行,所以我用&nbsp;try/catch&nbsp;来处理异步中间件的异常)7.&nbsp;JS&nbsp;异步发展史,以及它们的优缺点说一下&nbsp;(回调函数--Promise--Generator--async/await)8.&nbsp;你刚刚说&nbsp;Promise&nbsp;状态不能更改,那如果我要设计一个能修改&nbsp;Promise&nbsp;状态的函数,你会怎么设计?9.&nbsp;CSS&nbsp;水平垂直居中的方法(flex、grid、绝对定位&nbsp;+&nbsp;margin:auto、绝对定位&nbsp;+&nbsp;负&nbsp;margin、绝对定位&nbsp;+&nbsp;transform、table-cell)10.&nbsp;你刚刚说到&nbsp;flex&nbsp;布局,那&nbsp;flex:1&nbsp;是什么意思?(flex:&nbsp;flex-grow&nbsp;&nbsp;flex-shrink&nbsp;&nbsp;flex-basis;等价&nbsp;flex:1&nbsp;1&nbsp;0%表示元素可以均分剩余空间,可拉伸、可压缩,不依赖内容宽度,自动自适应填充布局。)11.&nbsp;父容器宽是&nbsp;500px,然后它左右各有两个子容器是&nbsp;100px,如果设置&nbsp;flex:&nbsp;1,那它的宽度是多少?(500-100-100=300px)12.&nbsp;说说你对浏览器缓存的理解(强缓存、协商缓存)13.&nbsp;如果一个用户,他怎么去刷新都无法刷到最新版的代码,你能说下可能的原因吗?(版本号、hash等)还有吗?(我说我不知道了,面试官说还有&nbsp;CDN&nbsp;没有同步,我说企业才会这么干,自己写项目一般不会,我知道&nbsp;cdn&nbsp;是用来解决高并发的手段)14.&nbsp;React你熟吗?说下&nbsp;React&nbsp;函数组件和类组件的区别15.&nbsp;怎么避免&nbsp;Hooks&nbsp;导致组件重新渲染?(使用&nbsp;useCallback、useMemo、React.memo、useRef等等)16.&nbsp;谈一下我对&nbsp;React&nbsp;的状态管理的理解(Redux、Mobx、Zustand,我说&nbsp;Zustand&nbsp;用的最多)17.&nbsp;React&nbsp;常见的&nbsp;hooks&nbsp;有哪些?(useState、useEffect、useRef、useCallback、useMemo、useReducer、useContext、useImperativeHandle、useLayoutEffect、useDebugValue)18.&nbsp;TS&nbsp;你熟吗?我们引进&nbsp;TS&nbsp;的目的是为什么?19.&nbsp;interface&nbsp;和&nbsp;type&nbsp;的区别20.&nbsp;说下&nbsp;TS&nbsp;里的泛型21.&nbsp;我现在有十个字段,比如十个字段就要&nbsp;A&nbsp;B&nbsp;C&nbsp;D&nbsp;E&nbsp;F&nbsp;G&nbsp;这种。那我现在另有另外一个方法,这个方法接受的参数呢,必须是这个&nbsp;interface&nbsp;A&nbsp;里面的这个&nbsp;K。就比如说你可以是&nbsp;A&nbsp;B&nbsp;C&nbsp;可以&nbsp;A&nbsp;B&nbsp;C&nbsp;D&nbsp;任何组合都可以,但是必须是这个&nbsp;interface&nbsp;里面的&nbsp;A&nbsp;里面的定义的。这个&nbsp;K&nbsp;这种类型的话是怎么去定义呢?(说实话我有点不太理解啥意思,反正我说了&nbsp;keyof)```&nbsp;TypeScriptinterface&nbsp;Obj&nbsp;{A:&nbsp;stringB:&nbsp;stringC:&nbsp;stringD:&nbsp;stringE:&nbsp;string//&nbsp;其他字段...}```22.&nbsp;vite&nbsp;用过吗?说说和&nbsp;webpack&nbsp;的区别。vite&nbsp;的优缺点是什么23.&nbsp;说说&nbsp;Tree&nbsp;shaking(树摇)&nbsp;和&nbsp;Code&nbsp;Splitting&nbsp;(代码分割)的区别24.&nbsp;Git&nbsp;你熟吗?说说&nbsp;git&nbsp;merge&nbsp;和&nbsp;git&nbsp;rebase&nbsp;的区别,什么时候用&nbsp;git&nbsp;merge,什么时候用&nbsp;git&nbsp;rebase?25.&nbsp;web3&nbsp;你熟吗?(不太熟,听说过而已)26.&nbsp;我看你自我介绍说了&nbsp;AI,你是怎么用的?27.&nbsp;除了提示词,还有什么能让&nbsp;AI&nbsp;更聪明?28.&nbsp;AI&nbsp;的优缺点你说一下29.&nbsp;AI&nbsp;发展这么快,你觉得我们以后会扮演什么角色?30.&nbsp;反问基本都答上来了。面了我80分钟,我还以为稳过的
查看29道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务