Home > Java > javaTutorial > body text

Detailed explanation of RTTI and reflection mechanism in Java

黄舟
Release: 2017-09-20 09:59:53
Original
1268 people have browsed it

This article mainly involves Java's RTTI and reflection mechanism code analysis. While introducing runtime type identification, it also shows you its examples and when the reflection mechanism will be used. It is rich in content and needs Friends can refer to it.

RTTI, that is, Run-Time Type Identification, run-time type identification. Runtime type identification is a very useful mechanism in Java. During Java runtime, RTTI maintains class-related information. RTTI can automatically identify every compile-time known type at runtime.

Many times it is necessary to perform upward transformation. For example, the Base class derives the Derived class, but the existing method only needs the Base object as a parameter, and what is actually passed in is a reference to its derived class. Then RTTI plays a role at this time. For example, RTTI can identify that the Derive class is a derived class of Base, so that it can be transformed upward into Derived. Similarly, when using interfaces as parameters, upward transformation is more commonly used, and RTTI can determine whether upward transformation can be performed at this time.

This type information is completed through the special object of the Class object (java.lang.Class), which contains class-related information. Whenever a class is written and compiled, a .class file is generated, which holds the Class object. The Java Virtual Machine (JVM) running this program will use a subsystem called a class loader (Class Loader). The class loader does not load all Class objects before the program runs. If they have not been loaded, the default class loader will search for the .class file based on the class name (for example, an additional class loader may search for the word in the database. bytecode), the bytecode of this class is verified when it is loaded to ensure that it has not been corrupted and does not contain bad Java code. This is also one of the type safety mechanisms in Java. Once the Class object of a certain class is loaded into memory, all objects of that class can be created.


package typeinfo;
class Base {
  static { System.out.println("加载Base类"); }
}
class Derived extends Base { 
  static { System.out.println("加载Derived类");}
}
public class Test {
  static void printerInfo(Class c) {
    System.out.println("类名: " + c.getName() +
      "是否接口? [" + c.isInterface() + "]");
  }
  public static void main(String[] args) {
    Class c = null;
    try {
      c = Class.forName("typeinfo.Derived");
    } catch (ClassNotFoundException e) {
      System.out.println("找不到Base类");
      System.exit(1);
    }
    printerInfo(c);
    Class up = c.getSuperclass(); // 取得c对象的基类
    Object obj = null;
    try {
      obj = up.newInstance();
    } catch (InstantiationException e) {
      System.out.println("不能实例化");
      System.exit(1);
    } catch (IllegalAccessException e) {
      System.out.println("不能访问");
      System.exit(1);
    }
    printerInfo(obj.getClass());
  } /* 输出:
  加载Base类
  加载Derived类
  类名: typeinfo.Derived是否接口? [false]
  类名: typeinfo.Base是否接口? [false]
  */
}
Copy after login

In the above code, the forName method is a static method, and the parameter is the class name, which is used to find whether the class exists , if found, a Class reference will be returned, otherwise a ClassNotFoundException will be thrown.

If the class is not in the default folder, but in a certain package, the previous package name needs to be included, such as typeinfo.Derived here.

You can use the getSuperclass method to return the Class object corresponding to the base class. Use the newInstance method to create an instance object according to the default construction, and throw it when it cannot be instantiated and cannot be accessed. InstantiationException and IllegalAccessException will be thrown.

Java also provides a way to generate a reference to a Class object, that is, a class literal. For the above program, up is equivalent to Base.class.

For wrapper classes of basic data types, char.class is equivalent to Character.TYPE, and int.class is equivalent to Integer.TYPE. The remaining ab.class is equivalent to Ab.TYPE. (For example, void.class is equivalent to Void.TYP). In addition, Java SE5 starts int.class and Integer.class are the same thing.

Generalized Class reference, see the code below


    Class intClass = int.class;
    Class<Integer> genericIntClass = int.class;
    genericIntClass = Integer.class; // 等价
    intClass = double.class; // ok
    // genericIntClass = double.class; // Illegal!
Copy after login

The reference to the Class object specifies an Integer object, so the reference cannot be Points to double.class. In order to relax the restriction, you can use the wildcard character ?, that is, Class. The effect is the same as Class, but the code is more elegant. Using Class means that you did not use a non-specific class reference by accident or negligence. At the same time, you can limit the inherited classes. The example is as follows


class Base {}
class Derived extends Base {}
class Base2 {}
public class Test {
  public static void main(String[] args) {
    Class<? extends Base> cc = Derived.class; // ok
    // cc = Base2.class; // Illegal
  } 
}
Copy after login

The reason for adding generic syntax to the Class reference is just to provide compile-time type checking so that it can be Type error found.

To summarize, the RTTI forms we know include:

1. Traditional type conversion. RTTI guarantees the correctness of type conversion. If executed An incorrect type conversion will throw a ClassCastException;

2. The Class object that represents the type of object can be obtained by querying the Class object (that is, calling the method of the Class class) to obtain the runtime requirements. Information.

Classic type conversion in C++ does not use RTTI. For details, see the RTTI section of C++. (As an aside, when I was learning C++, I just glanced at the RTTI chapter casually. Now I remember that dynamic_cast and other things were added specifically for type safety. C++ can provide selectivity in terms of safety, just like Java. StringBuilder and StringBuffer, safety and efficiency cannot be achieved at the same time? Java is more mandatory in terms of type safety, just like the expression x = 1 cannot be implicitly converted to boolean type).

There is a third form of RTTI in Java, which is the keyword instanceof, which returns a Boolean value to tell whether the object is an example of a specific type. See the following code .


class Base {}
class Derived extends Base {}
public class Test {
  public static void main(String[] args) {
    Derived derived = new Derived();
    System.out.println(derived instanceof Base); // 输出true
  } 
}
Copy after login

Use instanceof to determine certain types. For example, the base class Shape derives various classes (Circle, Rectangle, etc.). Now a certain method needs to color all Circles. The input parameters are a bunch of Shape objects. At this time, you can use instandof to determine whether the Shape object is a Circle object.

RTTI可以识别程序空间的所有类,但是有时候需要从磁盘文件或网络文件中读取一串字节码,并且被告知这些字节代表一个类,就需要用到反射机制。

比如在IDE中创建图形化程序时会使用到一些控件,只需要从本地的控件对应class文件中读取即可,然后再主动修改这些控件的属性。(题外话:大概.net组件就是这样的?学C#时总听到反射,但总没感觉用过,前几天做.net项目的同学也跟我说他从来都没用过委托和事件……)

Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含Field、Method和Constructor类(每个类都实现了Member接口),这些类型的对象都是JVM在运行时创建的,用以表示未知类里对应成员。

这样就可以用Constructor创建未知对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke方法调用与Method对象关联的字段,等等。


// 使用反射展示类的所有方法, 即使方法是在基类中定义的
package typeinfo;
// Print类的print方法等价于System.Out.Println,方便减少代码量
import static xyz.util.Print.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
// {Args: typeinfo.ShowMethods}
public class ShowMethods {
  private static String usage = 
    "usage:\n" +
    "ShowMethods qualified.class.name\n" +
    "To show all methods in class or:\n" +
    "ShowMethods qualified.class.name word\n" +
    "To search for methods involving &#39;word&#39;";
  // 去掉类名前面的包名
  private static Pattern p = Pattern.compile("\\w+\\.");
  public static void main(String[] args) {
    if (args.length < 1) {
      print(usage);
      System.exit(0);
    }
    int lines = 0;
    try {
      Class<?> c = Class.forName(args[0]);
      // 反射获得对象c所属类的方法
      Method[] methods = c.getMethods();
      // 反射获得对象c所属类的构造
      Constructor[] ctors = c.getConstructors();
      if (args.length == 1) {
        for (Method method : methods)
          print(p.matcher(method.toString()).replaceAll(""));
        for (Constructor ctor : ctors)
          print(p.matcher(ctor.toString()).replaceAll(""));
      }
    } catch (ClassNotFoundException e) {
      print("No such class: " + e);
    }
  } /*
  public static void main(String[])
  public final void wait() throws InterruptedException
  public final void wait(long,int) throws InterruptedException
  public final native void wait(long) throws InterruptedException
  public boolean equals(Object)
  public String toString()
  public native int hashCode()
  public final native Class getClass()
  public final native void notify()
  public final native void notifyAll()
  public ShowMethods()
  */
}
Copy after login

简单来说,反射机制就是识别未知类型的对象。反射常用于动态代理中。举例如下:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class DynamicProxyHandler implements InvocationHandler {
  private Object proxied; // 代理对象
  public DynamicProxyHandler(Object proxied) {
    // TODO Auto-generated constructor stub
    this.proxied = proxied;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("代理类: " + proxy.getClass() + "\n"
        + "代理方法: " + method + "\n"
        + "参数: " + args);
    if (args != null)
      for (Object arg : args)
        System.out.println(" " + arg);
    return method.invoke(proxied, args);
  }
}
interface Interface { void doSomething(); }

class RealObject implements Interface {
  
  @Override
  public void doSomething() {
    // TODO Auto-generated method stub
    System.out.println("doSomething");
  }
}
public class DynamicProxyDemo {
  public static void consumer(Interface iface) {
    iface.doSomething();
  }
  public static void main(String[] args) {
    RealObject realObject = new RealObject();
    // 使用动态代理
    Interface proxy = (Interface)Proxy.newProxyInstance(
        Interface.class.getClassLoader(),
        new Class[] { Interface.class }, 
        new DynamicProxyHandler(realObject));
    consumer(proxy);
  } /* 输出:
  代理类: class $Proxy0
  代理方法: public abstract void Interface.doSomething()
  参数: null
  doSomething
  */
}
Copy after login

代理是基本的设计模式之一,即用代理类为被代理类提供额外的或不同的操作。而动态代理则需要一个类加载器,就像Java实现RTTI时需要类加载器加载类的信息,这样就可以知道类的相关信息。

关键方法是:

Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException

传入三个参数:代理接口的加载器(通过Class对象的getClassLoader方法获取),代理的方法接口,代理对象

前两个参数很好理解,就是要代理的方法所属的接口对应的Class对象(主语)的加载器和Class对象本身,主要是参数3,要设计一个实现InvocationHandler接口的类,作为代理对象,一般命名以Handler结尾,Handler翻译为处理者,很形象,就是代替原对象进行处理的处理者(即代理),在程序设计中经常被翻译成“句柄”。

这个类通过传入代理对象来构造,比如这里传入的是Object对象。然后必须覆盖invoke方法。

通过最后输出和invoke方法的具体实现可以发现,return method.invoke(proxied, args);是相当于原对象调用该方法(类似C++的回调函数?)

由于有类加载器,所以代理对象可以知道原对象的具体类名、方法、参数,本示例在调用方法前就输出了这些。

实际应用中可能会针对类名而有所选择。比如接口中有好多个类,你可以选择性的对特定的类、方法、参数进行处理

比如 if(proxied instanceof RealObject) {} 或者 if(method.getName.equals("doSomething")) {}

总结

The above is the detailed content of Detailed explanation of RTTI and reflection mechanism in Java. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template