深度学习与总结JVM专辑(五):类加载机制

news2024/11/25 16:12:41

类加载机制

    • 前言
      • 什么是类加载机制
    • 类的生命周期
      • 类的加载:查找并加载类的二进制数据
      • 链接
        • 验证:确保被加载的类的正确性
        • 验证?有必要吗
        • 准备:为类的静态变量分配内存,并将其初始化为默认值
        • 解析:把类中的符号引用转换为直接引用
        • 初始化
      • 使用
      • 卸载
    • 类加载器
      • 类与类加载器
      • 类加载器的层次
        • 不同的视角去看类加载器
        • 寻找类加载器
        • 类的加载
        • JVM类加载机制
          • 双亲委派机制过程
        • 自定义类加载器

前言

之前我们了解了Class文件存储格式的具体细节,在Class文件中描述的各类信息,最终都需要加载到JVM中才可以被运行和使用。
那么JVM要如何加载这些Class文件,Class文件进入到JVM后会发生什么变化呢?

什么是类加载机制

JVM把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被JVM直接使用Java类型,这个过程就称为JVM的类加载机制。

在Java语言里面,类型的加载,连接和初始化过程都是在程序运行期间完成的,这种策略让Java语言进行提前编译会面临额外的困难,也让类加载稍微增加了性能开销,但是却为Java应用提供了极高的扩展性和灵活性,Java天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。

类的生命周期

类加载的过程包含了加载,验证,准备,解析,初始化五个阶段,其中,加载,验证,准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始(这是为了支持Java语言的运行时绑定,也称之为动态绑定或晚期绑定)。另外注意这几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是相互交叉地混合进行,通常在一个阶段执行的过程中调用或激活另一个阶段。
请添加图片描述

类的加载:查找并加载类的二进制数据

加载是类加载过程的第一个阶段,在加载阶段,JVM需要完成以下三件事情:

  • 通过一个类的全限定名来获取其定义的二进制字节流。
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
    请添加图片描述
    相对于类加载的其他阶段而言,加载阶段(准确来说,是加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,因为开发人员既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。

加载阶段完成后,JVM外部的二进制字节流就按JVM所需的格式存储在方法区中,而且在Java堆中也后仓健一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。

类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必需在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

《JVM规范》没有指明二进制字节流要从哪里获取,如何获取,所以我们有很多方式去实现,加载.class文件的方式:

  • 从本地系统之间加载
  • 通过网络下载.class文件
  • 从zip,jar等归档文件中加载.class文件
  • 从专有数据库中提取.class文件
  • 将Java源文件动态编译为.class文件。

链接

验证:确保被加载的类的正确性

验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前JVM的要求,并且不会危害JVM自身的安全。
验证阶段大致分为4个阶段的检验动作:

  • 文件格式验证:验证字节流是否符合Class文件格式的规范;比如:是否以咖啡宝贝开头,主次版本号是否在当前JVM的处理范围之内,常量池中的常量是否有不被支持的类型。
  • 元数据验证:对字节码描述的信息进行语义分析(对比:javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。
  • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的,符合逻辑的。
  • 符号引用验证:确保解析动作能正确执行,可以看作对类自身意外(常量池中的各种符号引用)的各类信息进行匹配性校验,通俗来说就是,该类是否缺少或者被禁止访问它依赖的某些外部类,方法,字段等资源。

注意:验证阶段是非常重要的,但是不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短JVM类加载时间。

验证?有必要吗

可能看到这你会说,为什么会需要验证Class文件的字节流信息呢?
Java语言本身是相对安全的编程语言,使用纯粹的Java代码无法做到例如访问数组边界以外的数据等等,如果尝试去做了,那么编译器会直接抛出异常,拒绝编译。
但是我们前面也聊到了,Class文件并不一定只能由Java源码编译而来,它可以使用包括靠键盘0和1直接在二进制编译器中敲出Class文件在内的任何途径产生。
JVM如果不检查输入的字节流,对其完全信任的话,很有可能会因为载入了有错误或者恶意企图的字节码流而导致整个系统受攻击甚至崩溃。

准备:为类的静态变量分配内存,并将其初始化为默认值

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配
我们需要注意几点:

  • 此时进行内存分配的仅仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。
  • 这里所设置的初始值通常是数据类型默认的零值(如0,0L,null,false等),而不是被在Java代码中被显式地赋予的值。

举个栗子:假设一个类变量的定义为:public static int value=3;
变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而是把value赋值为3的put static指令是程序被编译后,存放与类构造器()方法之中,所以value赋值为123的动作要到类的初始化阶段才会被执行

注意:上面我们说通常情况下初始值是零值,特殊情况如下:如果类字段的字段属性表中存在ConstantValue属性,那么在准备阶段变量值就会被初始化为ConstantValue属性所指定的初始值,也就是说准备阶段JVM就会根据ConstantValue的设置将value设置为3。

还需要注意几点:

  • 对基本属性类型来说:
    对于类变量(static)和全局变量,如果不显式对其赋值而直接使用,则系统会为其默认赋值为零值;
    对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过。
  • 对于同时被static和final修饰的常量:
    必须在声明的时候就为其显式地赋值,否则编译时不通过。
    而对于只被final修饰的常量则既可以在声明时显式赋值,也可以在类初始化时显式地赋值,总之在使用前必须为其显式地赋值,系统不会为其赋值默认零值。
  • 如果在数组初始化时没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值。

解析:把类中的符号引用转换为直接引用

解析阶段是JVM将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口,字段,方法,接口方法,方法类型,方法句柄,调用点限定符7类符号引用进行。
符号引用和直接引用我们在第一篇JVM基础已经详细介绍过了,如果不太明白的可以去那里瞧一瞧,链接如下:
深度学习与总结JVM专辑(一):基础介绍&&内存结构(图文+代码)

初始化

初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  • 声明类变量是指定初始值
  • 使用静态代码块Wie类变量指定初始值

JVM初始化步骤:

  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机:只有当对类的主动使用的时候才会导致类的初始化。
类的初始化主动使用包括以下六种:

  • 创建类的实例,也就是new的方式
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射
  • 初始化某个类的子类,则其父类也会被初始化
  • JVM启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类。

补充
在准备阶段时,变量已经赋过一次系统要求的初始零值。
在初始化阶段,则会根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源

初始化阶段就是执行类构造器()方法的过程。
()并不是程序员在Java代码中直接编写的方法,它是javac编译器的自动生成物,但有必要了解这个方法如何产生以及运行行为细节。

()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并而成;
编译器的收集顺序是由语句在源文件中出现的顺序决定的。

  • 静态语句块中只能访问到定义在静态语句块之前的变量,在前面的静态语句块可以赋值,但是不能访问。
    举个栗子:
public class Test {
    static {
        i = 0;  //  给变量复制可以正常编译通过
        System.out.print(i);  // 这句编译器会提示“非法向前引用”
    }
    static int i = 1;
}
  • ()方法和类的构造函数(即在JVM视角中实例构造器()方法)不同,它不需要显式地调用父类构造器。
    JVM会保证在子类的()方法执行前,父类的()方法已经执行完毕,因此JVM中第一个被执行的()方法肯定是java.lang.Object。

  • 父类中定义的静态语句要优先于子类的变量赋值操作
    因为父类的()方法要先执行。

  • ()方法对于类和接口来说并不是必须的
    如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不生成()方法。

  • 接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口和类一样都会生成()方法。
    但接口与类不同的是,执行接口的()方法不需要先执行父接口的()方法,因为只有当父接口中定义的变量被使用时,父接口才会初始化。
    此外,接口的实现类在初始化时也一样不会执行接口的()方法。

  • JVM必需保证一个类的()方法在多线程环境中被正确地加锁同步,如果多个线程同时初始化一个类,只会有其中一个线程去执行,其他线程阻塞知道活动线程执行完毕()方法。

使用

类访问方法区的数据结构的接口,对象是Heap区的数据。

卸载

JVM结束生命周期的几种情况:

  • 执行了System.exit()方法
  • 程序正常执行结束
  • 程序在执行过程中遇到了异常或错误而异常终止
  • 由于操作系统出现错误而导致JVM进程终止

类加载器

JVM设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到JVM外部去实现,以便让应用程序自己决定如何去获取所需的类。
实现这个动作的代码被称为“类加载器”(Class Loader)。

类与类加载器

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段。
对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在JVM中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。
换句话说:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相等。

类加载器的层次

请添加图片描述
这里父类加载器并不是通过继承来实现的,而是通过组合实现的。

不同的视角去看类加载器

JVM的角度:
只存在两种不同的类加载器:

  • 启动类加载器,使用C++实现,是虚拟机自身的一部分
  • 其他类加载器,由Java语言实现,独立于JVM之外,并且全部继承自抽象类java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。

Java开发人员角度来看:
大致分为以下三类:

  • 启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbooclasspath参数指定的路径中的,并且能被JVM识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。
  • 扩展类加载器:Extension ClassLoader ,该记载器由sun.misc/Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
  • 应用程序加载器:Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

应用程序都是由这三种类加载器互相配合进行加载的,如有必要,我们还可以加入自定义的类加载器。
因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的Java Class文件,如果编写了自己的ClassLoader,便可以做到以下几点:

  • 在执行非置信代码之前,自动验证数字签名
  • 动态地创建符合用户特定需要的定制化构建类
  • 从特定的场所取得java class,例如数据库中和网络中。

寻找类加载器

有个好栗子:

public class demo4one {
    public static void main(String[] args) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        System.out.println(loader);
        System.out.println(loader.getParent());
        System.out.println(loader.getParent().getParent());
    }

}

结果如下:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null

类的加载

类加载有三种方式:

  • 命令行启动应用由JVM初始化加载
  • 通过Class.forName()方法动态加载
  • 通过ClassLoader.loadClass()方法动态加载

举个栗子:

public class loaderTest { 
        public static void main(String[] args) throws ClassNotFoundException { 
                ClassLoader loader = HelloWorld.class.getClassLoader(); 
                System.out.println(loader); 
                //使用ClassLoader.loadClass()来加载类,不会执行初始化块 
                loader.loadClass("Test2"); 
                //使用Class.forName()来加载类,默认会执行初始化块 
//                Class.forName("Test2"); 
                //使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块 
//                Class.forName("Test2", false, loader); 
        } 
}

public class Test2 { 
        static { 
                System.out.println("静态初始化块执行了!"); 
        } 
}

分别切换加载方式,会有不同的输出结果。

Class.forName()和ClassLoader.loadClass()区别

  • Class.forName():将类的.class文件加载到JVM中之外,还会对类进行解释,执行类中的static块。
  • ClassLoader.loadClass():只干一件事情,就是将.class文件加载到JVM中,不会执行static中的内容,只有在newInstance才会去执行static块。
  • Class.forName(name,intialize,loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象。

JVM类加载机制

  • 全盘负责
    当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
  • 父类委托
    先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
  • 缓存机制
    保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必需重启JVM,程序的修改才会生效。
  • 双亲委派机制
    如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,一次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
双亲委派机制过程
  1. 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  2. 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
  3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
  4. 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
public Class<?> loadClass(String name)throws ClassNotFoundException {
            return loadClass(name, false);
}
    protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
            // 首先判断该类型是否已经被加载
            Class c = findLoadedClass(name);
            if (c == null) {
                //如果没有被加载,就委托给父类加载或者委派给启动类加载器加载
                try {
                    if (parent != null) {
                         //如果存在父类加载器,就委派给父类加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                    //如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name)
                        c = findBootstrapClass0(name);
                    }
                } catch (ClassNotFoundException e) {
                 // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }

双亲委派优势:

  • 系统类防止内存中出现多份同样的字节码
  • 保证Java程序安全稳定运行

自定义类加载器

目前不太熟悉,挖个坑,主要是3次破坏双亲委派有点蒙,找不出栗子给大家吃,不知道怎么衔接到自定义这,容我再想想。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/48273.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ZooKeeper 避坑实践:如何调优 jute.maxbuffer

作者&#xff1a;子葵 背景 在日常运维 ZooKeeper 中&#xff0c;经常会遇到长时间无法选主&#xff0c;恢复时进程启动又退出&#xff0c;进而导致内存暴涨&#xff0c;CPU飙升&#xff0c;GC频繁&#xff0c;影响业务可用性&#xff0c;这些问题有可能和 jute.maxbuffer 的…

Kotlin高仿微信-第17篇-单聊-转账

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

深度学习基础知识回顾

1. Dataset调用了什么接口&#xff1f; 回答应该是__len__方法和__getitem__方法。 之前写过一篇关于Dataset和Dataloader的介绍&#xff1a; http://t.csdn.cn/b4x0hhttp://t.csdn.cn/b4x0h 2. 目标检测里面用了哪些损失函数&#xff1f; 我的回答是Focal Lo…

【Linux】权限讲解

一、什么是权限 1、权限概念 权限随处可见&#xff0c;在生活中&#xff0c;腾讯非VIP用户不能观看VIP视频&#xff0c;看小说也需要会员&#xff0c;所以权限是限制人的&#xff0c;一件事是否允许被谁做。在Linux系统中也有许多权限&#xff0c;访问文件需要权限&#xff0c…

Kafka: Windows环境-单机部署和伪集群、集群部署

1. kafka 单机版部署 1.1 zookeeper 安装 &#xff08;1&#xff09;下载安装包 官网&#xff1a;Apache ZooKeeper 我用的是 apache-zookeeper-3.7.1-bin.tar.gz 注意&#xff1a;zookeeper的安装路径不要有中文&#xff0c;建议也不要有空格,比如Program Files这样的路径…

移动跨平台技术方案浅析

随着互联网产品逐渐兴起&#xff0c;越来越多产品体验从线下搬到了线上&#xff0c;尤其是移动互联网产品相关&#xff0c;所以很多企业就会更加重视降本增效&#xff0c;以最快的速度推出质量满意度高、用户体验性好的产品&#xff0c;那么就顺势催生了很多跨端跨平台方案。 …

并发编程九 线程池Executor框架

一 线程 线程是调度CPU资源的最小单位&#xff0c;线程模型分为KLT模型与ULT模型&#xff0c;JVM使用的KLT模型&#xff1b; Java线程与OS线程保持1:1的映射关系&#xff0c;也就是说有一个java线程也会在操作系统里有一个对应的线程。Java线程有多种生命状态 NEW,新建 RUNN…

一文带你深入了解Linux IIO 子系统

【推荐阅读】 一文剖析Linux内核中内存管理 分析linux启动内核源码 关于如何快速学好&#xff0c;学懂Linux内核。内含学习路线 工业场合里面也有大量的模拟量和数字量之间的转换&#xff0c;也就是我们常说的 ADC 和 DAC。而且随着手机、物联网、工业物联网和可穿戴设备的…

[第二十二篇]——Docker 安装 MongoDB

Docker 安装 MongoDB MongoDB 是一个免费的开源跨平台面向文档的 NoSQL 数据库程序。 1、查看可用的 MongoDB 版本 访问 MongoDB 镜像库地址&#xff1a; 。 可以通过 Sort by 查看其他版本的 MongoDB&#xff0c;默认是最新版本 mongo:latest。 你也可以在下拉列表中找到…

高校社团管理系统的设计与实现

摘要 随着互联网技术的高速发展&#xff0c;人们生活的各方面都受到互联网技术的影响。现在的社团成员可以通过互联网技术就能实现不在学校&#xff0c;在家也可以查看社团信息并能进行申请加入&#xff0c;简单、快捷的方便了社团成员的社交生活。同样的&#xff0c;在人们的工…

【Spring项目中的统一处理异常】

目录 1. 统一处理异常的机制 2. 关于统一处理异常的方法 3. 关于处理异常的方法的执行特点 1. 统一处理异常的机制 Spring MVC框架提供了统一处理异常的机制&#xff01;表现为每种类型的异常只需要写一段&#xff08;写一次&#xff09;处理此异常的代码即可&#xff0c;项…

需求:针对同一个表格多次导入是否要做判断(此项目是用得若依)

每次上传表时&#xff0c;将上传的表名与以往上传的表名做对比&#xff0c;如果相同&#xff0c;则提示表已经有记录&#xff0c;是否上传相同表并结束此方法。 实现思路&#xff1a; 首先&#xff0c;每次上传表都要把表明记录一下&#xff0c;可以新建一个表&#xff08;数…

天宇优配|离岸人民币狂拉逾千点!中概股暴涨!B站涨22%

当地时间周二&#xff0c;美股三大指数收盘涨跌纷歧。道指涨0.01%&#xff0c;标普500指数跌0.16%&#xff0c;纳指跌0.59%。 抢手中概股领涨&#xff0c;纳斯达克我国金龙指数大涨5.04%&#xff0c;哔哩哔哩&#xff08;B站&#xff09;涨超22%。大型科技股多数跌落&#xff0…

小程序中的confirm-type设置键盘的确认按钮

详情&#xff1a; confirm-type是很多小程序组件中的一种设置&#xff0c;用于改变输入键盘右下角的确认按钮。比如说&#xff0c;正常情况下&#xff0c;键盘上的默认提示可能是完成&#xff0c;但是你可以通过confirm-type将其设置为发送&#xff0c;搜索等&#xff0c;在特…

间隔不到一年开两店,温州鸿雁全屋智能经销商透露了他的生意经

作者 | 牧之 编辑 | 小沐 出品 | 智哪儿 zhinaer.cn编者按&#xff1a;间隔不到一年&#xff0c;连续开设了两家全屋智能体验店。这是发生在温州的渠道商故事。本期专访&#xff0c;「智哪儿」对话浙江林上智能科技有限公司总经理朱飞隆先生。他为何做智能家居&#xff1f;为何…

翻转单词序列、按之字形顺序打印二叉树、二叉搜索树的第k个节点

1、翻转单词序列 本题考点&#xff1a;子串划分&#xff0c;子串逆置 牛客链接 题目描述&#xff1a; 牛客最近来了一个新员工Fish&#xff0c;每天早晨总是会拿着一本英文杂志&#xff0c;写些句子在本子上。同事Cat对Fish写的内容颇感兴趣&#xff0c;有一天他向Fish借来翻…

Python数据库编程之关系数据库API规范

Python关系数据库API规范 对于关系数据库的访问&#xff0c;Python社区已经制定出一个标准&#xff0c;称为Python Database API Specification。Mysql&#xff0c;Oracal等特定数据库模块遵从这一规范&#xff0c;而且可以添加更多特性。 高级数据库API定义了一组用于连接数…

三十六、Java 泛型

Java 泛型 Java 泛型&#xff08;generics&#xff09;是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制&#xff0c;该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数。 假定我们有这…

火山引擎 DataLeap 的 Data Catalog 系统公有云实践

Data Catalog 通过汇总技术和业务元数据&#xff0c;解决大数据生产者组织梳理数据、数据消费者找数和理解数的业务场景。本篇内容源自于火山引擎大数据研发治理套件 DataLeap 中的 Data Catalog 功能模块的实践&#xff0c;主要介绍 Data Catalog 在公有云部署和发布中遇到挑战…

5. LSTM的C++实现

[C 基于Eigen库实现CRN前向推理] 第三部分&#xff1a;TransposedConv2d实现 &#xff08;含dilation&#xff09; 前言&#xff1a;(Eigen库使用记录)第一部分&#xff1a;WavFile.class (实现读取wav/pcm,实现STFT)第二部分&#xff1a;Conv2d实现第三部分&#xff1a;Tran…