chapter14 -- 类型信息

基本内容

  • Class对象
  • 反射

说明

在《Thinking in Java》中,作者提到了RTTI这个概念。但是这个概念是源于C++的,在Java的官方文档中并没有出现这个概念,所以在这一章中,并不会涉及到RTTI的内容。


1 Class对象

Class对象是一种特殊的对象,它包含了与类有关的信息。事实上,Class对象就是用来创建类的所有的“常规”对象的。
每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。

在Java中,类都是在第一次使用的时候,动态的加载到JVM中的。并非在运行时就将全部内容加载,只有在需要时,才会进行加载。
我们看一段代码

public class Chapter14 {
    public static void main(String[] args) {
        new Cat();
        try {
            Class.forName("com.coreJava.test.Dog");
        }catch(ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Cat{
    static {
        System.out.println("得到一只小猫");
    }
}

class Dog{
    static {
        System.out.println("得到一只小狗");
    }
}
console:
得到一只小猫
-----------------
得到一只小狗

给Cat和Dog都添加了静态代码块,会在第一次加载的时候输出信息。
首先创建了一个Cat的实例对象,然后使用Class去获取Dog类。可以看到,只有在使用Dog类的时候,静态代码块中的内容才会执行。

注意: 在使用Class.forName(classname)的时候,要使用完全限定名。

class中存储的是类型的信息,我们可以使用获得的class对象创建具体类的实例对象。

public static void main(String[] args) {
  Class catClass = Cat.class;
  try {
    Cat cat = catClass.newInstance();
  } catch (InstantiationException e) {
    e.printStackTrace();
  } catch (IllegalAccessException e) {
    e.printStackTrace();
  }
}

我们首先获取了Cat的类型信息catClass,随后,我们使用catClass创建了Cat对象。

注意: Class是可以添加泛型的。

可以观察到,我们在使用Class.forName(classname)的时候,会执行静态代码块中的内容,而使用类型.class的时候,并不会执行静态代码块中的内容。

2 反射

在Java中使用反射,我们主要使用的Class类和java.lang.reflect包。在我看来,反射就是将类中的,方法,成员变量等实例化成对象去使用。
使用反射可以帮助我们做很多平时不能做的事情,例如,调用私有方法。

2.1 什么是反射

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

2.2 反射能够做什么

反射机制主要有以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

举个例子:

public class Cat {
  public String name;
  private String age;

    public void eat() {
        System.out.println("小猫吃食物");
    }

    private void run() {
        System.out.println("小猫跑");
    }
}

public class Chapter14 {
    public static void main(String[] args) {
        Class cat = Cat.class;
        Field[] fieddArray = cat.getFields();

        for (Field field : fieddArray) {
            System.out.println(field.toString());
        }

        Method[] methodArray = cat.getMethods();
        for (Method method : methodArray) {
            System.out.println(method.toString());
        }

        Method method;
        try {
            method = cat.getMethod("eat");
            method.invoke(cat.newInstance());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

2.3 调用私有方法

通常,我们不能调用类的私有方法,但是通过反射,我们是不是可以去使用这些私有方法呢?我们来尝试一下。

如果像上面那样调用public方法的方式去调用private方法会发生什么呢?

console:
java.lang.NoSuchMethodException: com.coreJava.test.Cat.run()
    at java.lang.Class.getMethod(Unknown Source)
    at com.coreJava.test.Chapter14.main(Chapter14.java:23)

程序会报方法未找到的异常,看起来这样并不能找到私有方法。我们查看下API中的说明:

  • getMethod():返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
  • getMethods():返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

我们看到API中还有两个方法:

  • getDeclaredMethod():返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
  • getDeclaredMethods():返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

好了,这样我们就可以获取到类中的私有方法并调用了。

Class cat = Cat.class;
try {
  Method method = cat.getDeclaredMethod("run");
  method.setAccessible(true);
  method.invoke(cat.newInstance());
} catch (Exception e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

我们在调用私有方法前使用了setAccessible()这个方法,这是调用私有方法必须的一步,具体大家可以去查看API。

2.4 说明

使用反射,可以帮助我们做到很多平时不能做的事情,在这里我也只是起到抛砖引玉的作用,以一个简单的例子来说明如何使用反射,具体更多的用法,大家可以参考官方API。
至于反射在JVM中是如何实现的,在这里就不多说了,一是内容实在太多,放在一篇文章中比较难;二是我自己对这块内容也是处在一知半解的状态,担心出现错误,对大家起到一个错误的引导。


欢迎关注个人公众号,搜索:公子照谏或者QCzhaojian
也可以通过扫描二维码关注。
chapter14 -- 类型信息_第1张图片

你可能感兴趣的:(《Thinking,in,Java》读书笔记,java)