类加载
静态加载与动态加载
静态加载:编译时加载需要的类,如果没有定义该类则报错,依赖性太强。
动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类也不会报错,降低了依赖性。
public static void main(String[] args) throws ... {
//new Dog() 是静态加载,因此必须编写Dog类
Dog dog = new Dog();
//Person类是动态加载,因此没有编写Person类编译器也不会报错,只有当动态加载该类时才会报错
Class cls = Class.forName("reflection.Person");
}
类加载时机
类加载过程图
类加载的各个阶段
加载阶段
连接阶段——验证
连接阶段——准备
//n1 是实例属性,不是静态变量,因此在准备阶段不会分配内存
public int n1 = 10;
//n2 是静态变量,分配内存。默认初始化为0,而不是20
public static int n2 = 20;
//n3 是常量,分配内存。它一旦被赋值就不变,因此直接初始化为30
public static final int n3 = 30;
连接阶段——解析
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。而直接引用就是指向目标的指针等。
初始化阶段
对出现的顺序的理解:对下面这段代码: static int num = 100; 在静态代码块上方,因此会被 num = 300覆盖。
class B{
public static void main(String[] args) {
System.out.println(B.num); // 300
}
static int num = 100; //在静态代码块前出现
static{
System.out.println("B 静态代码块被执行");
num = 300;
}
}
这样的话,就不会被 num = 300覆盖,最终num = 100。
static{
System.out.println("B 静态代码块被执行");
num = 300;
}
static int num = 100; //在静态代码块后出现
通过反射获取类的结构信息
java.lang.Class类
java.lang.reflect.Field类
java.lang.reflect.Method类
java.lang.reflect.Constructor类
通过反射创建对象
方式一:调用类中的public修饰的无参构造器(Java11中已弃用)。方式二:调用类中的指定构造器(如果该构造器是private,要用到暴力破解)。
Class类相关方法
Constructor类相关方法
public static void main(String[] args) throws ...{
//1. 先获取到User类的Class对象
Class<?> userClass = Class.forName("reflection.User");
//2. 通过public无参构造器创建实例
User user1= (User) userClass.newInstance(); //该方法已弃用
System.out.println(user1);
//3. 用public有参构造器创建实例
Constructor<?> constructor = userClass.getConstructor(String.class);//()里是构造器对应的参数
User user = (User)constructor.newInstance("ss");
System.out.println(user); //不是同一个对象
//4. 通过非public的有参构造器创造实例
//得到private的构造器方法,要用 getDeclaredConstructor
Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);
//创建实例,要用到 暴力破解
declaredConstructor.setAccessible(true);//关闭检查
User user2 = (User)declaredConstructor.newInstance(100,"sssss");
System.out.println(user2);
}
通过反射访问类中的成员
class User{
private int age;
private String name;
public void say(){
System.out.println("say方法被调用");
}
public void say(String name){
System.out.println("带有参数的say方法被调用");
}
public static void say(String name,int age){
System.out.println("静态say方法被调用");
}
private void hi(){
System.out.println("private方法被调用");
}
}
public static void main(String[] args) throws ... {
//1. 先获取到User类的Class对象
Class<?> userClass = Class.forName("reflection.User");
//2. 得到一个对象
Object o = userClass.newInstance();
//3. 获取public方法(不带参数)
Method say = userClass.getMethod("say");
say.invoke(o);
//4. 获取public方法(带参数)
Method say1 = userClass.getMethod("say", String.class); //要把参数的Class写入
say1.invoke(o,"aaa");
//5. 获取static方法
Method say2 = userClass.getMethod("say", String.class, int.class);
//因为 hi2对应的是static方法,因此不用写对象(写也可以)
say2.invoke(null,"aaa",12);
//6. 获取private方法
Method hi = userClass.getDeclaredMethod("hi");
hi.setAccessible(true); //爆破
hi.invoke(o);
}
如果方法有返回值,统一返回Object,但是它的运行类型(getClass)和方法定义的返回类型一致。