java中反射,字节码和类加载器

97 阅读5分钟

多态的一个表现

子类类型赋值给父类 Father f1 = New Son()
调用子类方法报错。 调用父类方法OK。这个就是多态

一个对象能用什么方法,并不是取决于 它有什么方法。
而是取决于引用变量的类型(也就是取决于它声明的类型,Father类型)
它能够用的方法,一定是Father中的方法。

通过反射来获取整体的对象。这个整体的对象我们称之为:类对象。
怎么获取这个类对象呢? 通过f1.getClass()来获取类对象

字节码对象

package part;

public class Java01 {
    public static void main(String[] args) {
        User u1 = new User();
        u1.test1();
        // 通过 u1.getClass() 来获取:类对象
        // Class 是java.lang包下的。所以不需要再导入了。
        // 这里的aClass对象就是java中的字节码文件
        // 所谓的字节码文件:就是我们使用javac编译后的那个源码文件
        Class<? extends User> aClass = u1.getClass();
    }
}

class User{
    public void test1(){
        System.out.println("test1");
    }
}

class Child extends User{
    public void test2(){
        System.out.println("test2");
    }
}

获取类中的属性,方法以及其他信息

package part;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Java01 {
    public static void main(String[] args) {
        User u1 = new User();
        // 通过 u1.getClass() 来获取:类对象
        // Class 是java.lang包下的。所以不需要再导入了。
        // 这里的aClass对象就是java中的字节码文件
        // 所谓的字节码文件:就是我们使用javac编译后的那个源码文件
        // 类对象
        Class<? extends User> aClass = u1.getClass();
        // 获取类的完整名称,包含包名 输出:part.User
        System.out.println(aClass.getName());
        // 获取类的名称 输出:User
        System.out.println(aClass.getSimpleName());
        // 获取类的包的信息 输出:package part
        System.out.println(aClass.getPackage());
        // 获取类的包名 输出:part
        System.out.println(aClass.getPackage().getName());


        Class<?> superclass = aClass.getSuperclass();
        // 获取类的父类 输出:class java.lang.Object
        System.out.println(superclass);

        // 获取类的接口,可能存在多个,因此是一个数组的哈。
        Class<?>[] interfaces = aClass.getInterfaces();
        // 输出0,因为我们这里没有实现接口的哈
        System.out.println(interfaces.length);

        try {
            //  获取类的属性,只能获取公共(public)的属性
            Field f1 = aClass.getField("xx");
            // 获取类的属性,所有的权限,就是你是私有的,我也可以获取
            Field f2 = aClass.getDeclaredField("aa");
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        // 获取类的所有属性,前提是这些属性都是public修饰的,是公共的,返回来的是数组
        Field[] fields = aClass.getFields();
        // 获取类的所有属性,就是你是私有的,我也可以获取,,返回来的是数组
        Field[] declaredFields = aClass.getDeclaredFields();

        try {
            // 获取类中的公共的方法
            Method  method= aClass.getMethod("test");
            // 获取类中的方法,就算是私有的也可以获取
            Method methods = aClass.getDeclaredMethod("test2");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        // 获取类中所有公共的方法,是公共的,返回来的是数组
        Method[] methods = aClass.getMethods();
        // 获取类中的所有方法,方法就算是私有的也可以获取,返回来的是数组
        Method[] allMethods = aClass.getDeclaredMethods();

        try {
            // 构造方法  constructor 就是构造对象
            Constructor<? extends User> constructor = aClass.getConstructor();
            // 返回所有的构造方法
            Constructor<?>[] constructors = aClass.getConstructors();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

    }
}

class User{
    public void test1(){
        System.out.println("test1");
    }
}

class Child extends User{
    public void test2(){
        System.out.println("test2");
    }
}

java中的类主要分为3种。

1.java核心类库中的类:string, Object
2.JVM软件平台厂商
3.我们自己写的类,比如:User,Child这些

类加载器也有3种

JDK 9+

  1. BootClassLoader 启动类加载器,如string, Object
  2. PlatformClassLoader 平台类加载器,如软件平台厂商。
    在jdk8及以前的话,这个平台类加载器变成了扩展类加载器Extension ClassLoader
  3. AppClassLoader 应用类加载器,如我们自己写的类
    类加载器在加载时也是有顺序的:
    java核心类库中的类(string, Object)==> 平台类(软件平台厂商) ==> 应用类加载器(自己写的类)

JDK 8 及之前是下面这3种
1.启动类加载器(Bootstrap ClassLoader)
2.扩展类加载器(Extension ClassLoader),在JDK 9+中,[扩展类加载器]变成了[平台类加载器]
3.应用类加载器(Application ClassLoader,也叫系统类加载器)

得到应用类加载器

package part;

public class Java01 {
    public static void main(String[] args) {
        // 获取类的信息,以前我们是通过new的形式,现在我们取到了Student这个类的信息。
        Class<Student> studentClass = Student.class;
        // 获取Student这个类的加载器
        ClassLoader classLoader = studentClass.getClassLoader();
        // 输出: sun.misc.Launcher$AppClassLoader@14dad5dc
        // 得到的是:应用类加载器。为啥是应用类加载器呢?因为是我们自己写的类。所以是应用类加载器
        System.out.println(classLoader);
    }
}

class Student {

}

String类的类加载器为啥是null?

package part;

public class Java01 {
    public static void main(String[] args) {
        Class<String> stringClass = String.class;
        ClassLoader classLoader = stringClass.getClassLoader();
        // 输出null
        System.out.println(classLoader);
    }
}

class Student {

}

有些小伙伴看到这里会觉得很奇怪,为啥是null?
你不是说:string, Object 这些是启动器类吗?
按理说应该是:BootClassLoader(启动类加载器)才对。
因为:启动类加载器通常表示为null,因为它不是Java实现的。

解释一下启动类

启动类加载器是最顶层的,由原生代码实现,不继承java.lang.ClassLoader。
启动类加载器通常表示为null,因为它不是Java实现的。

得到平台类

我们可以理解为:平台类是应用类加载器的上一级的加载器。也就是说有有一个上一级的概念。
如果我们想要获取平台类的话。 通过自己写的类可以得到平台类。下面我们来尝试一下

package part;

public class Java01 {
    public static void main(String[] args) {
        // 获取类的信息,以前我们是通过new的形式,现在我们取到了Student这个类的信息。
        Class<Student> studentClass = Student.class;
        // 获取Student这个类的加载器
        ClassLoader classLoader = studentClass.getClassLoader();
        // 输出的是:在标准的JDK环境下(如JDK 8),输出会是:sun.misc.Launcher$ExtClassLoader@28d93b30
        // JDK 9+ 由于模块化系统的引入,类加载器名称可能变为 jdk.internal.loader.ClassLoaders$PlatformClassLoader,但逻辑一致。
        System.out.println(classLoader.getParent());
    }
}

class Student {

}

在啰嗦说一下Java的类加载器层次结构

我们回忆Java的类加载器层次结构。通常,Java的类加载器分为三个层次:
启动类加载器(Bootstrap ClassLoader)、
扩展类加载器(Extension ClassLoader)、(这里根据版本不同,有所变化)
应用类加载器(Application ClassLoader,也叫系统类加载器)。
因此,启动类加载器通常表示为null,因为它不是Java实现的。
扩展类加载器负责加载JRE的扩展目录(如jre/lib/ext)中的类,而应用类加载器负责加载classpath下的类。
特别提醒:应用类加载器的父加载器是扩展类加载器,而扩展类加载器的父加载器是启动类加载器。
其中,启动类加载器是最顶层的,由原生代码实现,不继承java.lang.ClassLoader。

作为程序员,持续学习和充电非常重要,作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。低代码也是一个值得我们深入探索的领域,让我们拭目以待,它将给前端世界带来怎样的变革,推荐一个低代码工具。

应用地址:www.jnpfsoft.com
开发语言:Java/.net

这是一个基于Flowable引擎(支持java、.NET),已支持MySQL、SqlServer、Oracle、PostgreSQL、DM(达梦)、 KingbaseES(人大金仓)6个数据库,支持私有化部署,前后端封装了上千个常用类,方便扩展,框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用。

至少包含表单建模、流程设计、报表可视化、代码生成器、系统管理、前端 UI 等组件,这种情况下我们避免了重复造轮子,已内置大量的成熟组件,选择合适的组件进行集成或二次开发复杂功能,即可自主开发一个属于自己的应用系统。