Java基础--如何在运行时识别对象和类的信息

废话:自己菜,通过休息时间整理整理。

运行时识别对象和类的信息一听就有点意思,那么为什么需要它,就是把我们从只能在编译期执行面向类型的操作的禁锢中解放出来,有点深奥。那么运行时识别对象和类的信息有啥方式?

先看一个栗子

import java.util.Arrays;
import java.util.List;

abstract class Animal {
    void eat() {
        System.out.println(this + "eat方法");
    }

    @Override
    abstract public String toString();
}

class Pig extends Animal {
    @Override
    public String toString() {
        return "pig";
    }
}

class Dog extends Animal {
    @Override
    public String toString() {
        return "dog";
    }
}

public class Animals {
    public static void main(String[] args) {
        List<Animal> list= Arrays.asList(new Pig(),new Dog());
        for(Animal animal:list){
            animal.eat();
        }
    }
}

output:import java.util.Arrays;
import java.util.List;

abstract class Animal {
    void eat() {
        System.out.println(this + "eat方法");
    }

    @Override
    abstract public String toString();
}

class Pig extends Animal {
    @Override
    public String toString() {
        return "pig";
    }
}

class Dog extends Animal {
    @Override
    public String toString() {
        return "dog";
    }
}

public class Animals {
    public static void main(String[] args) {
        List<Animal> list= Arrays.asList(new Pig(),new Dog());
        for(Animal animal:list){
            animal.eat();
        }
    }
}


output:
pigeat方法
dogeat方法复制代码

首先在把元素放入集合时会发生向上转型,实际上将所有事物当Object持有,取出时会自动转回,这是RTTI(Runtime Type Information)基本使用形式,其含义为运行时识别一个对象的类型。这种多态的形式它假定我们在编译时就已经知道了所有的类型,这种相对静态的是由容器和泛型来强制保证。那么程序运行时就要通过类型转换操作来保证不能出错。

那么类型信息在运行时是用Class对象表示的,每一个类都有一个Class对象。每个类的运行时的类型信息就是用Class对象表示的。它包含了与类有关的信息。其实我们的实例对象就通过Class对象来创建的。那我们看一眼Class,有兴趣可以看看,有许多类相关的信息。

在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。一旦某个类的Class对象被载入内存,我们就可以它来创建这个类的所有实例对象。

那么我们如何拿到Class对象的引用,有三种方法

  1. Class.forName(“类的全限定名”)
  2. 类名.class (类字面常量,建议使用,安全简单高效)不会自动初始化该Class对象
  3. 实例对象.getClass()

JavaSE5中,允许你对Class引用所指向的Class对象的类型进行限定,也就是说你可以对Class对象使用泛型语法,向Class引用添加泛型语法的原因仅仅是为了提供编译期类型检查,大家可以自己试试基本数据类型和包装类型的Class对象以及父子类型的Class对象。

提一点Integer的Class对象并非Number的Class对象的子类,因为所有的Class对象都只来源于Class类。我们可以用泛型通配符来缓解,具体的自己可以试试。

关于类型转换

在上述栗子中

        //cast
        Animal animal=new Dog();
        Dog dog=(Dog)animal;
复制代码

之所可以强制转换,这得归功于RRTI,要知道在Java中,所有类型转换都是在运行时进行正确性检查的,利用RRTI进行判断类型是否正确从而确保强制转换的完成。instanceof 它返回一个boolean类型的值,意在告诉我们对象是不是某个特定的类型实例。isInstance方法则是Class类中的一个Native方法,我们看一眼


结论:instanceOf 与isInstance方法产生的结果是相同的

instanceOf 与isInstance方法保持了类型的概念,如果用==比较实际的Class对象,就没有考虑继承。

关于反射:运行时类的信息

人们想要在运行时获取类的信息,希望提供在跨网络平台的远程平台上创建和运行对象的能力,反射提供了一种机制。上述的Class类与java.lang.reflect类一同对反射进行支持。讲一下RTTI和反射之间真正的区别只在于:

对于RTTI来说,编译器在编译时打开和检查.class文件(换句话说,我们可以用“普通”的方式调用对象的所有方法),而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。

传统的”RRTI”,它假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如new对象时该类必须已定义好),另外一种是反射机制,它允许我们在运行时发现和使用类型的信息。其实反射技术没有什么神奇,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看他属于哪个特定的类(就像RTTI那样)。因此,那个类的.class文件对于JVM来说必须是可获取的不管是本地机器还是网络获取。

下面来看看反射的基本操作,因为反射是很多框架的核心:

举个栗子,自己熟悉熟悉对应api,才更有印象。

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
       Class<?> c=null;
       c=Class.forName("com.school.learning.java.RTTI.reflect.User");
       //TODO
    }
}

class User {

    private String name;
    private int age;

    public User() {
        super();
    }

    public User(String name) {
        super();
        this.name = name;
    }

    private User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

}
复制代码

另外提一下Type类型这里简单说明一下,Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。



由于Type顶级接口,Class也实现了该接口,因此Class类是Type的子类,Type 表示的全部类型而每个Class对象表示一个具体类型的实例,根据源码注释,如String.class仅代表String类型。由此看来Type与 Class 表示类型几乎是相同的,只不过 Type表示的范围比Class要大。当然Type还有其他子类如下:

TypeVariable:表示类型参数,可以有上界,比如:T extends Number 


ParameterizedType:表示参数化的类型,有原始类型和具体的类型参数,比如:List<String>


 WildcardType:表示通配符类型,比如:?, ? extends Number, ? super Integer 


基本上内容就是这些,要用具体api再查询,之前在公司看到有RPC的泛化调用,与项目结合时有部分代码需要拿到方法的一些信息,参数具体信息等。


猜你喜欢

转载自juejin.im/post/5e3fa7d15188254901605ca8