Java SE基础2:Class类与反射 反射破坏了封装吗?

举个例子,把代码过程看做去一个目标地点,普通代码调用呢就是事先知道经纬度,而后你坐直升机直接就到了;而经过反射呢就像不知道具体的地点,只知道先去一个地点,而后前往下一个地点,一步步到达目标。这两种方法异曲同工,反射由于要“寻路”,因此会慢一些,但在找到目标地点后和直接调用是同样的。code

有时候咱们须要在程序中建立新的对象或是调用一个方法,而对应的细节咱们事先并不知道,也就是说要在运行中动态地得到类的信息和调用方法。下面介绍如何利用反射来实现。orm

Class类:保存和类有关的信息的类

  • 须要了解的概念对象

    • RTTI(RunTime Type Information,运行时类型信息)可以在程序运行时发现和使用类型信息blog

    • Class对象就保存着运行时类型信息RTTI,表示一个特定类的属性内存

    • Class类实际上表示的是一个泛型类Class<?>get

    • 当编译一个新类时JVM会调用类加载器把这个类加载到内存中域名

      • 类加载器首先会检查这个类的 Class 对象是否已经加载,若是还没有加载,默认的类加载器就会根据类名查找 .class 文件it

      • 一旦某个类的 Class 对象被载入内存,它就能够用来建立这个类的全部对象io

    • JVM为每一个类型管理一个Class对象编译

  • 利用Class类获得类的信息和实例化一个类

    • Class.getName()方法

      • 返回类的名字,若是类在一个包中还会加上包名

      • 使用getSimpleName()获得不带包名的类名

    • Class.forName(String className)方法

      • 得到className类名对应的Class对象
    • Object.newInstance()方法

      • 能够用来动态地建立一个类的实例

      • 方法调用类的默认构造器(没有参数的构造器),若是没有默认构造器,就会抛出一个异常

      • 若是要调用带参数的构造器,使用Constructor类中的newInstance方法

    • 例程

      • 代码

        System.out.println("静态建立一个新的对象");
        Employee ae = new Employee();
        System.out.println(ae);
        //使用getClass.getName获得类名
        String cName = ae.getClass().getName();
        //再用forName和newInstance动态建立一个对象
        System.out.println("getClass()+forName()动态建立一个新的对象");
        Object o = Class.forName(cName).newInstance();
        System.out.println(o.getClass());//会输出实际类型Employee
        System.out.println(o);
        
        //先定义一个类名 再新建对象
        String m = "CoreJava.c5_inheritance.Manager";
        System.out.println("先定义再forName()动态建立一个新的对象");
        Object am = Class.forName(m).newInstance();
        System.out.println(am.getClass());//会输出实际类型Manager
        System.out.println(am);
      • 结果

反射:分析类的能力,相对动态地执行一些方法

  • 利用Field类查看任意对象的数据域名称和类型

    • 首先得到要分析类的Class对象getClass()/forName()

    • getField(String fieldName) 和 getFileds() 能获取Class对象的对应域(getDeclared(), getDeclaredFields()获取全部已声明域,包括私有域)

    • Field.getName()能获取域名称,Field.getType()能获取域类型

    • 例程

      • 代码

        System.out.println("利用反射得到全部域");
        Manager manager = new Manager();
        Class clazz = manager.getClass();//先获得类的运行时信息
        Field[] fields = clazz.getDeclaredFields();//得到全部声明的域(包括私有域)
        for (Field f : fields)
        System.out.println(f.getType() + " " + f.getName());
      • 结果

  • 得到域中的值并修改

    • Field.get(Object obj)

      • 能够得到obj对象中用Filed对象表示的域值(设 f 是Field的一个实例,表示Manager类中的salary域,那么f.get(m)能够得到Manager实例m中salary域的值)

      • 若是是私有域,须要先设置可访问标志为true : fild.setAccessible(true)

    • Field.set(object obj, Object newValue)

      • 用一个新值设置obj对象中Field对象表示的域
    • 例程

      • 代码

        //得到域中的值并修改
        System.out.println("利用反射得到域中的值并修改");
        System.out.println("使用类方法getSalary():" + manager.getSalary());
        Field managerSalaryField = clazz.getDeclaredField("salary");//注意异常处理
        managerSalaryField.setAccessible(true);//设置可访问标志为true,访问私有域
        System.out.println("使用反射得到salary:" + managerSalaryField.get(manager));//注意异常处理
        System.out.println("使用反射修改salary");
        managerSalaryField.set(manager, 999);
        System.out.println("修改后salary:" + manager.getSalary());
      • 结果

  • 利用Method类得到任意方法名称和返回值

    • Class.getMethod(String methodName, Class<?>[] paramTypes) 和 Class.getMethods() 分别能得到类的对应Method对象和全部Method对象

    • Method.getName()得到方法名

    • Methord.getReturnType()得到方法返回类型

    • 例程

      • 代码
        System.out.println("利用反射得到全部方法");
        Method[] methods = clazz.getMethods();
        for (Method method : methods)
            System.out.println(method.getReturnType().getSimpleName() + " " + method.getName() + " ");
      • 结果
  • 调用任意方法

    • Method.invoke(Object obj, Object... params)能够调用obj对象中Method对象表示的方法,params是方法参数。对于静态方法第一个参数能够传入null

    • 例程

      • 代码
        System.out.println("利用反射调用任意方法");
        System.out.println("修改前salary:" + manager.getSalary());
        Method setManagerSalaryMethod = clazz.getMethod("setSalary", int.class);
        setManagerSalaryMethod.invoke(manager, 222);//第一个参数为执行对象,静态方法可传入null
        System.out.println("修改后salary:" + manager.getSalary());
      • 结果

个人疑问

  • 利用反射能获取甚至修改类的私有域,这样一来私有域不就没有意义了吗。在网上搜了一些资料,感受仍是没有明白,等到理解了再来补上。
相关文章
相关标签/搜索