除了int
等基本类型外,Java的其他类型全部都是class(interface,
Runnable,Exception)
class
的本质是数据类型(Type
)
class
是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class
类型时,将其加载进内存。
每加载一种class
,JVM就为其创建一个Class
类型的实例,并关联起来。这里的Class
类型是一个名叫Class
的class
public final class Class {
private Class() {}
}
由于JVM为每个加载的class(小写)
创建了对应的Class
实例,并在实例中保存了该class
的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class
实例,我们就可以通过这个Class
实例获取到该实例对应的class
的所有信息。
如何获取一个class
的Class
实例?有三个方法:
方法一:直接通过一个class
的静态变量class
获取:
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()
方法获取:
String s = "Hello";
Class cls = s.getClass();
方法三:如果知道一个class
的完整类名,可以通过静态方法Class.forName()
获取:
Class cls = Class.forName("java.lang.String");
因为Class
实例在JVM中是唯一的,所以,上述方法获取的Class
实例是同一个实例。可以用==
比较两个Class
实例(相同)
用instanceof
不但匹配指定类型,还匹配指定类型的子类。 用==
判断class
实例可以精确地判断数据类型,但不能作子类型比较。
动态加载
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载
public class Main {
public static void main(String[] args) {
if (args.length > 0) {
create(args[0]);
}
}
static void create(String name) {
Person p = new Person(name);
}
}
当执行Main.java
时,由于用到了Main
,因此,JVM首先会把Main.class
加载到内存。然而,并不会加载Person.class
,除非程序执行到create()
方法,JVM发现需要加载Person
类时,才会首次加载Person.class
。如果没有执行create()
方法,那么Person.class
根本就不会被加载。
访问字段
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
xxxField.setAccessible(true);获取权限使其可访问private
Field
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
获取字段值:先获取Class
实例,再获取Field
实例,然后,用Field.get(Object)
获取指定实例的指定字段的值。
f.get(p);
设置字段值:Field.set(Object, Object)
实现,第一个Object
参数是指定的实例,第二个Object
参数是待修改的值。
f.set(p, "test");
访问方法
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
Method.setAccessible(true);允许其调用private方法
Method
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个Class实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义。
调用方法
Method m=c.getMethod("方法名", int.class)//获取方法
m.invoke(s, 6);//执行方法
调用静态方法时,由于无需指定实例对象,所以invoke
方法传入的第一个参数永远为null
多态
一个Person
类定义了hello()
方法,并且它的子类Student
也覆写了hello()
方法
那么,从Person.class
获取的Method
,作用于Student
实例时,调用的方法到底是哪个?
Method h = Person.class.getMethod("hello");
h.invoke(new Student());
发现打印出的是Student:hello
因此,使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。
访问构造方法
getConstructor(Class...)
:获取某个public
的Constructor
;getDeclaredConstructor(Class...)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
。
c.setAccessible(true);允许其调用private方法
调用构造方法
Say test= (Say) c.newInstance("123");
获取继承关系
获取父类的Class:c.getSuperclass()
获取interface列表:c.getInterfaces();
是否继承关系(本类及父类):c instanceof xxx 返回bool
动态代理
未理解