JAVA类加载与反射笔记

JVM和类
当调用java命令运行某一个Java程序时,该命令会启动一个JAVA虚拟机进程,同一个JVM的所有线程、所有变量都在同一个进程里,它们都使用JVM进程的内存区。当系统出现以下几种情况的时候,JVM进程将被终止:
1)程序运行到最后正常结束
2)程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处结束程序
3)程序运行过程中遇到未捕获的异常或者错误而结束
4)程序所在的平台强制结束了JVM进程

当程序主动使用某个类的时候,如果该类还没有被加载到内存中,那么系统会通过加载、连接、初始化三个步骤对该类进行初始化。
类的加载
类加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象。类的加载由类加载器完成,类加载器通常由JVM提供,类加载器通常无需等到首次使用该类的时候才加载该类,JAVA虚拟机规范允许系统预先加载某些类。
类的连接
连接阶段负责把类的二进制数据合并到JRE中,类连接又分为三个阶段:
1)验证:验证阶段用于检验被加载的类是否具有正确的内部结构
2)准备:类的准备阶段负责为类的类变量分配内存
3)解析:将类的二进制的符号引用替换成直接引用
类的初始化
在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。

JVM初始化一个类的步骤:
1)假如一个类还没有被加载和连接,那么先加载和连接这个类
2)加入一个类的直接父类没有被初始化,那么先初始化这个父类
3)假如类中有初始化语句,那么系统依次执行这些初始化语句。

类初始化的时机
1)创建类的实例
2)调用某个类的类方法
3)访问某个类或者接口的类变量,或者为该类赋值
4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5)初始化某个类的子类
6)直接使用java.exe命令来运行某个主类

反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。例如许多JAVA程序在运行时会出现两种类型:编译时类型和运行时类型,比如Person p=new Student();由于在编译时无法预知该对象属于哪个类,程序只能依靠运行时的信息来发现该类对象和类的真实信息,这就需要用到反射。
Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
Class类
前面说过,类加载的时候会创建一个java.lang.Class对象,存在堆内存中。通过Class中的方法可以得到一个类中的完整结构,包括此类中的构造器、方法定义,属性定义等。

在Class类中没有任何的构造方法,如果要使用必须通过Class.forName(包名.类名)的静态方法实例化对象,也可以使用类.class或者对象.getClass()方法实例化。

Person类

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

通过无参构造实例化对象
如果想要通过Class类本身实例化其他类的方法,可以用newInstance()方法,但是必须要保证实例化的类中存在一个无参构造方法。

public class Client {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> a=Class.forName("reflect.Person");
        Person p=(Person)a.newInstance();
        p.setAge(18);
        System.out.println(p);
    }
}

通过Class.forName()方法实例化Class对象之后,直接调用newInstance()方法就可以根据传入的包名+类名进行实例化操作。

通过有参构造实例化对象
1)通过Class类的getConstructors取得本类中的全部构造方法
2)然后使用constructor对象的newInstance方法获得对象实例

public class Constructor1 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> a=Class.forName("reflect.Person");
        Constructor<?>[] cons=a.getConstructors();
        Person p=(Person)cons[1].newInstance("张三",18);
        System.out.println(p);
    }
}

取得类的结构
Constructor:表示类的构造方法
Field:表示类中的属性
Method:表示类的方法

扫描二维码关注公众号,回复: 8900111 查看本文章
public class Client {
    public static void main(String[] args) throws ClassNotFoundException {
        Class a=Class.forName("reflect.Person");
        System.out.println(a.getClassLoader());
        System.out.println(Arrays.toString(a.getConstructors()));
        System.out.println(Arrays.toString(a.getDeclaredFields()));
        System.out.println(Arrays.toString(a.getMethods()));
    }
}

在这里插入图片描述
通过反射调用类中的方法
1)通过Class类的getMethod(String name,Class.parameterTypes)取得一个Method的对象
2)使用invoke(obj,args)方法进行调用

public class invokedemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> a=Class.forName("reflect.Person");
        Method method=a.getMethod("setAge", int.class);
        Person p=new Person();
        method.invoke(p,18);
        System.out.println(p);
    }
}
发布了56 篇原创文章 · 获赞 3 · 访问量 1631

猜你喜欢

转载自blog.csdn.net/qq_20786911/article/details/103510839