该如何理解Java中的Class类

java里面一切都是类和对象

当我们在程序中要表达这个实体时,往往会定义一个Book类。例如

public class Book{
    private String name;
    private double price;
    private String publish;
    
    private void read(){
        System.out.println("reading...");
    }
}

当我们在程序中要表达这个实体时,往往会定义一个Car类。例如

public class Car{
    private String brand;
    private double price;
    
    private void run(){
        System.out.println("running...");
    }
}

总之不同的实体都会定义不同用来表示相关信息。

其实JVM里面所有的本身也是一个实体。每个类都有类名,都有方法名,都有属性名。那么我们用什么来表示这些信息呢?答案就是Class类。

为什么需要Class类。

如果想在运行时或者编译时拿到类或者对象的信息;那么必须借助Class类。运行时获取类型信息可以让我们写出更灵活更强大的程序,比如实现RMI,动态代理,反射等等。

举个例子

Animal类

public abstract class Animal {
    void print(){System.out.println("I'm " + this);}

    public abstract String toString();
}

BarkPet接口

public interface BarkPet {    void bark(); }

Dog类

public class Dog extends Animal implements BarkPet {        
    @Override
    public void bark() {       
        System.out.println("wang wang ..."); 
    }
    @Override
    public String toString() {
        return "Dog";
    }
}

Cat类

public class Cat extends Animal implements BarkPet {
    @Override
    public void bark() {
        System.out.println("miao miao ...");
    }

    @Override
    public String toString() {
        return "Cat";
    }
}

Fish类

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

AnimalDemo类

public class AnimalDemo {
    public static void main(String[] args){
        List<Animal> animals = new ArrayList<Animal>();
        animals.add(new Dog());
        animals.add(new Cat());
        animals.add(new Fish());

        //如果我想找出,小猫和小狗怎么办?
        for (Animal animal : animals){
            //??
        }
    }
}

对于AnimalDemo类,我想在程序运行的过程中,找到DogCat类型的对象,这就是所谓的运行时获取类型信息。

Class基本介绍

每个类都有一个Class对象。

当我们编写并且编译完成一个类之后,就会自动生成对应的Class对象(和类同名的.class文件)。当程序创建第一个对类的静态成员的的引用时,就会加载这个类。

这里有必要说一下类加载过程。

类加载器首先检查这个类的Class对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找 .class文件(也可以自定义类加载器,加载指定路径的.class文件,比如数据库,或者网络)。加载完成后,JVM会对其进行验证,确保没有被损坏,且没有不良代码。一旦某个类的Class对象被载入内存,它就可以被用来创建对象了。

注意:Java并不是在一开始的时候就把所有的类加载进内存,而是按需加载,也就是在第一次对类的静态成员引用的时候加载。构造器也是类的静态方法。

如何使用Class对象

  1. Class c = Class.forName("com.example.demo.Dog")

  2. 类字面量 Class c = Dog.class

两者区别:通过类字面量的方式创建对Class对象的引用时,不会自动初始化该Class对象。

要很好理解两者的区别,还需要了解一个类从加载到可用要经历哪些过程:

  1. 加载,这是由类加载器执行的。该步骤负责查找字节码(通常在classpath所指定的路径中查找,但并非必须),并从这些字节码中创建一个Class对象。

  2. 链接,在链接阶段将验证类中的字节码,为static字段分配存储空间,并且如果需要的话,将解析这个类创建的对其它类的所有引用。

  3. 初始化,如果该类具有超类,则先初始化超类,执行static初始化器和static初始化块。

下面通过一段简单的demo代码来对Class类的应用做一个简单的说明。

try {
            //获取Class引用方式1
            Class dog1 = Class.forName("com.example.demo.Dog");

            //获取Class引用方式2
            Dog dogObj = new Dog();
            Class dog2 = dogObj.getClass();

            //获取Class引用方式3
            Class dog3 = Dog.class;

            //name = com.example.demo.Dog
            String name = dog1.getName();

            //dogSuperClass = com.example.demo.Animal
            Class dogSuperClass = dog1.getSuperclass();

            // dogInterface[0] = interface com.example.demo.BarkPet
            Class[] dogInterface = dog1.getInterfaces();

            //dogAnnotations = null
            Annotation[] dogAnnotations = dog1.getAnnotations();
            
            Field[] dogFields = dog1.getFields();
            Constructor[] dogConstructors =  dog1.getConstructors();
            Method[] methods = dog1.getMethods();
            for (Method method : methods){
                //通过反射调用Dog的bark方法
                if (method.getName() == "bark"){
                    method.invoke(dogObj);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        //如果我想找出,小猫和小狗怎么办?
        for (Animal animal : animals) {
            if (animal instanceof Dog){
                //成功拿到Dog类型对象
            }

            if (Dog.class.isInstance(animal)){
                //成功拿到Dog类型对象
            }
        }

总结

总之当你想在运行时获取类型信息,或者编译的过程中拿到类型信息,一定要想到ClassClass是用来表示所有类型信息的类。Spring Framework里面很多重要功能都借助了该知识点的应用,比如IOCAOP等等,想深入了解可以多参考Spring的源码和相关文档。

推荐阅读

1. Java并发编程那些事儿(十)——最后的总结

2. 不会使用虚拟机的程序员不是好程序员

3. Awk这件上古神兵你会用了吗

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值