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
类,我想在程序运行的过程中,找到Dog
和Cat
类型的对象,这就是所谓的运行时获取类型信息。
Class基本介绍
每个类都有一个Class
对象。
当我们编写并且编译完成一个类之后,就会自动生成对应的Class
对象(和类同名的.class
文件)。当程序创建第一个对类的静态成员的的引用时,就会加载这个类。
这里有必要说一下类加载过程。
类加载器首先检查这个类的Class
对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找 .class
文件(也可以自定义类加载器,加载指定路径的.class
文件,比如数据库,或者网络)。加载完成后,JVM
会对其进行验证,确保没有被损坏,且没有不良代码。一旦某个类的Class
对象被载入内存,它就可以被用来创建对象了。
注意:
Java
并不是在一开始的时候就把所有的类加载进内存,而是按需加载,也就是在第一次对类的静态成员引用的时候加载。构造器也是类的静态方法。
如何使用Class对象
-
Class c = Class.forName("com.example.demo.Dog")
-
类字面量
Class c = Dog.class
两者区别:通过类字面量的方式创建对Class
对象的引用时,不会自动初始化该Class
对象。
要很好理解两者的区别,还需要了解一个类从加载到可用要经历哪些过程:
-
加载,这是由类加载器执行的。该步骤负责查找字节码(通常在
classpath
所指定的路径中查找,但并非必须),并从这些字节码中创建一个Class
对象。 -
链接,在链接阶段将验证类中的字节码,为
static
字段分配存储空间,并且如果需要的话,将解析这个类创建的对其它类的所有引用。 -
初始化,如果该类具有超类,则先初始化超类,执行
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类型对象
}
}
总结
总之当你想在运行时获取类型信息,或者编译的过程中拿到类型信息,一定要想到Class
。Class
是用来表示所有类型信息的类。Spring Framework
里面很多重要功能都借助了该知识点的应用,比如IOC
,AOP
等等,想深入了解可以多参考Spring
的源码和相关文档。
推荐阅读