当前位置: 首页 > news >正文

JVM学习.03 类加载机制

1、前言

从事Java开发工作的都知道,Java程序提交到JVM运行时,需要编译成Class文件,才能被JVM加载运行。那么这些Class文件进入到虚拟机后会发生什么?以及Class是如何被加载的?这些都是本文要讲解的部分。

2、类加载时机

所谓类装载机制,就是虚拟机把class文件加载到内存,并对数据进行校验,转换解析,初始化,形成可以虚拟机直接使用的java类型,即java.lang.Class。

一个类从被加载到虚拟机内存开始,到卸载出内存位置,他都会经历加载,验证,准备,解析,初始化,使用,卸载七个阶段。其中验证、准备、解析三个部分称为连接。

类的生命周期如下,网上借来的图:

加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)。以下陈述的内容都已HotSpot为基准。特别需要注意的是,类的加载过程必须按照这种顺序按部就班地“开始”,而不是按部就班的“进行”或“完成”,因为这些阶段通常都是相互交叉地混合式进行的,也就是说通常会在一个阶段执行的过程中调用或激活另外一个阶段。

2.1、“加载”时机

类加载过程的第一个阶段加载,通常是交由虚拟机具体的实现来自由把握,《Java虚拟机规范》并没有强制约束。

2.2、“初始化”时机

《Java虚拟机规范》虽然对加载没有强制性约束,但是却严格规定了有且只有六种情况下必须立即对类进行“初始化”,这里加载,验证,准备需要在此之前开始。

1、遇到new、getstatic、putstatic或invokestatic这四条字节码指令,如果类没有进行过初始化,则需要先对其进行初始化。生成这四条指令的最常见的Java代码场景是:

  • 使用new关键字实例化对象的时候;

  • 读取或设置一个类的静态字段(被final修饰,已在编译器把结果放入常量池的静态字段除外)的时候;

  • 调用一个类的静态方法的时候。

2、使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

3、当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4、当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

5、 当使用JDK7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化。

6、当一个接口定义了JDK8新加入的默认方法(被default关键字修饰的接口方法时,如果这个接口的实现类发生了初始化,那该接口要在其之前被初始化)。

以上6中场景中的行为称为对一个类型进行的主动引用。

除此之外,所有引用类型的方式都不会触发初始化,称为被动引用。何为被动引用?看下面代码:

示例代码一:

/*** 通过子类引用弗雷静态字段,子类不会初始化*/
public class SuperClass {static {System.out.println("super class init");}public static int valueOf = 123;
}public class SubClass extends SuperClass {static {System.out.println("sub class init");}
}// 主函数调用
public class Test {public static void main(String[] args) {System.out.println(SubClass.valueOf);}
}

运行结果,只触发了父类的初始化:

示例代码二:

/*** 常量在编译阶段会进入调用类的常量池中,本质上没有直接引用定义常量的累,所以不会触发常量定义累的初始化*/
public class ConstClass {static {System.out.println("ConstClass init");}public static final String CONSTANTS = "hello world";
}
// 主函数调用
public class Test {public static void main(String[] args) {
//        System.out.println(SubClass.valueOf);System.out.println(ConstClass.CONSTANTS);}
}

运行结果:

3、类加载过程

3.1、加载

加载阶段,主要完成以下三件事:

1、通过一个类的全限定名来获取定义此类的二进制流。

2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。

简单的说,该过程就是查找并通过类加载器将class文件导入到内存中。

3.2、验证

该阶段的目的是确保class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身安全。

3.2.1、文件格式验证

该阶段的主要目的是验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机所处理。保证输入的字节流能正确地解析并存储与方法区之内,格式上符合描述一个Java类型信息的要求。

验证比如:

1、验证主、次版本号是否在当前Java虚拟机接受范围之内。

2、常量池的常量中是否有不被支持的常量类型。

......

3.2.2、元数据验证

该阶段是对字节码描述信息进行语义分析,以保证符合《Java语言规范》要求。

验证比如:

1、这个类是否有父类。

2、这个类的父类是否继承了不允许被继承的类(如final修饰的类)。

3、如果这个类不是抽象类,是否实现了父类或接口中要求实现的所有方法。

......

3.2.3、字节码验证

该阶段的目的是通过数据流分析和控制流分析,确定程序语义是合法的,符合逻辑的。元数据验证是对元数据信息中的数据类型校验,而该阶段则是要对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。

验证比如:

1、如在操作栈防止了一个int类型数据,使用时却按long类型加载入本地变量表。

2、保证任何跳转之历经都不会跳转到方法体之外的字节码指令上。

......

3.2.4、符号引用验证

该阶段的目的是确保解析行为能正常执行,如果无法通过验证,将抛出Java.lang.IncompatibleClassChangeError的子类异常,如常见的IllegalAccessError,NoSuchFieldError,NoSuchMethodError等。

验证比如:

1、符号应用的类、字段、方法的可访问性,是否可以被当前类访问(private,public等等)。

2、符号引用中通过字符串描述的全限定名是否能找到对应的类。

......

3.3、准备

该阶段正式为类中定义的变量(静态变量)分配内存并给类变量设值初始值。

注:该阶段进行内存分配的仅仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配到Java堆。

如:

public static int value = 123;

变量value在准备阶段后,初始值是0,而不是123。因为这时候还未开始执行任何java方法,而value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法中,所以value赋值为123的动作要到类的初始化阶段才会被执行。

当然,有些“意外情况”。如类字段的字段属性表中存在ConstantValue属性,那在准备阶段就会被初始化为ConstantValue属性所指定的初始值。

如:

public static final int value = 123;

加上final之后,编译时会为value生成ConstantValue属性,也会在初始化时直接设置value的值为123。

3.4、解析

该阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。

符号引用就是一组符号来描述目标,可以是任何字面量。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

主要解析类型有:

  • 类或接口的解析

  • 字段解析

  • 方法解析

  • 接口方法解析

3.5、初始化

该阶段是类加载过程的最后一个步骤。前面几个类加载动作中,出了在加载阶段用户可以通过自定义类加载器的方式局部参与外,其余全部都由JVM自主控制完成。直到初始化阶段,JVM才真正开始执行类中编写的Java程序代码。

3.6、小结

前面几点说了那么多,简单的说就是,当我们程序定义了一个类,当我们需要使用到这个类的时候,JVM会从相应的class字节码文件中去加载,期间进行语义检查,权限的校验,预先的初始处理等,最终初始化应用程序中的构造。就完成了类在JVM中的整个加载过程,也可以直接被JVM所正常运行。

初始化过程:

  • 如果类还没有被加载和连接,那就先进行加载和连接

  • 如果类存在父类,并且父类没有初始化,那就先初始化直接父类

  • 如果类中存在初始语句,顺序执行初始化语句

类的初始化阶段是执行类构造器方法clinit()的过程

1、类加载就是执行Java程序编译之后在字节码文件中生成的clinit()方法(称之为类构造器),clinit()方法由静态变量和静态代码块组成。
2、子类的加载首先需要先加载父类,如果父类为接口。则不会调用父类的clinit方法。一个类中可以没有clinit方法。
3、clinit方法中的执行顺序为:父类静态变量初始化,父类静态代码块,子类静态变量初始化,子类静态代码块。
4、clinit()方法只执行一次。

4、类加载器

实现类加载阶段中“通过一个类的全限定名来获取描述该类的二进制字节流”的动作的代码,称为类加载器。

对于Java中任意一个类,都必须由加载他的类加载器和这个类本身一起共同确立其在JVM中的唯一性,每一个类加载器都拥有一个独立的类名称空间(后续如果接触到模块化系统,如OSGi中,每一个Bundle就具有一个类加载器,这个时候不同类加载器就算再同一个JVM中,上下文也不会共享)。

通常,我们会描述两个类比较是否相等,这个比较的前提是只有这两个类再同一个类加载器加载才有意义,否则就算这两个类是来源同一个class文件,被同一个JVM加载,只要类加载器不同,那就必定不相等(这里的相等包括equals()方法,isInstance()方法,当然也包括了instanceof关键字)。

如:

import java.io.IOException;
import java.io.InputStream;/*** @author Shamee loop* @date 2023/3/23*/
public class ClassLoaderDemo {public static void main(String[] args) throws Exception {Object classLoaderDemo2 = createOneClassLoader().loadClass("ClassLoaderDemo").newInstance();System.out.println("classLoaderDemo2实例的类加载器:" + classLoaderDemo2.getClass().getClassLoader());System.out.println("ClassLoaderDemo的类加载器:" + ClassLoaderDemo.class.getClassLoader());System.out.println("两个类是否相等:" + (classLoaderDemo2 instanceof ClassLoaderDemo));}/*** 模拟一个新的类加载器* @return*/static ClassLoader createOneClassLoader(){return new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {String fieldName = name.substring(name.lastIndexOf(".") + 1) + ".class";try (InputStream inputStream = getClass().getResourceAsStream(fieldName);){if(inputStream == null){return super.loadClass(name);}byte[] bytes = new byte[inputStream.available()];inputStream.read(bytes);return defineClass(name, bytes, 0, bytes.length);} catch (IOException e) {throw new ClassNotFoundException(name);}}};}
}

运行结果:

4.1、三层类加载器

从JDK1.2以来,Java一直保持着三层类加载器以及双亲委派的类加载结构。我们先来说什么是三层类加载器。

4.1.1、启动类加载器 Bootstrap Class Loader

  • JVM自带的引导类加载器,由C/C++语言实现。

  • 该类加载器主要负责加载存放在\lib目录,或被-Xbootclasspath参数指定的路径中存放的,而且是JVM能够识别的类库,如rt.jar,tools.jar等。

  • 只能加载java,javax,sun开头的包名类,如果自定义java,sun开头的包名类会直接报错。

4.1.2、扩展类加载器 Extension Class Loader

  • Java代码形式实现的sun.misc.Launcher$ExtClassLoader。

  • 加载\lib\ext目录,或被java.ext.dirs系统变量所指定的路径中的类库。

  • 允许用户将类库放置在ext目录以扩展JavaSE功能。

  • 指定Bootstrap Class Loader为父加载器,通过getParent()可以获取Bootstrap Class Loader。

4.1.3、应用程序类加载器 Application Class Loader

  • Java代码形式实现的sun.misc.Launcher$AppClassLoader。

  • 负责加载用户类路径(ClassPath)上的所有类库,Java程序中可以直接使用这个类加载器。

  • 指定Extension Class Loader为父加载器,通过getParent()可以获取Extension Class Loader。

  • 默认的类加载器,Java应用的类都是该类加载器加载的。

4.1.4、如何自定义Class Loader

什么时候需要自定义ClassLoader?

1、修改类的加载方法,如tomcat中多个war工程可以独立运行;保证了各个war中的jar不会冲突。

2、防止源码泄露,对class字节码进行编码加密,再在laod过程中对其解密。

......

如何自定义Class Loader?

1、继承ClassLoader,重写loadClass方法

2、继承UrlClassLoader

5、双亲委派

5.1、双亲委派模型

从4.1小节中可以看出三层类加载器的一定关系。当然我们还可以加入自己定义的类加载器来进行扩展。因此就有了如下的类加载器协作关系(也就是经常被提到的双亲委派模型)。

网上借来的图:

双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都必须有自己的父类加载器。

5.1.1、双亲委派的加载过程

1、如果一个类加载器(比如User ClassLoader)收到了类加载请求,首先不会自己尝试加载这个类;

2、把这个请求委托给父亲加载器(如Application Class Loader)去完成;

3、父加载器会继续委托给上一层类加载器(如Extension Class Loader)去完成;

4、最终都会传送到顶层的启动类加载器(Bootstrap Class Loader)中;

5、只有当父加载器反馈自己无法完成这个加载请求时,子类加载器才会尝试自己去完成加载。

简而言之,也就是逐层向上寻找合适的加载器进行加载,从而保证此类所有的加载器只加载一次。从java.lang.ClassLoader源码中我们也可以看到:

5.1.2、双亲委派的好处

1、Java中的类随着他的类加载器一起具备了一种有优先级的层次关系。能够保证类不会被重复加载。

2、保护程序安全,防止核心Java语言环境被破坏。比如定义一个java.lang.String,在定义一个static语句,你会发现永远无法执行你定义的static内容。如下:

/*** @author Shamee loop* @date 2023/3/23*/
public class String {static {System.out.println("我是自定义的String");}
}public static void main(String[] args) throws ClassNotFoundException {String s = new String();
}

执行结果:

5.2、打破双亲委派

既然前面讲到了双亲委派的诸多好处,那么这里为什么要破坏这样的一个环境呢?

试想一下这样一种“例外”情况。双亲委派机制很好的解决了各个类加载器写作时基础类型一致性的问题(越基础的类越往上层加载)。但是如果有基础类型有需要回调用户的代码呢?该如何处理?

比如JNDI服务,JNDI服务存在的目的就是为了对资源进行查找和几种管理,他需要调用由其他厂商实现并部署在应用程序的ClassPath下的JNDI服务提供接口(SPI)的代码。为了解决这个问题,Java设计团队引入了线程上下文类加载器(Thread Context Class Loader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法来设置。显然这个有点类似“舞弊”的操作。正是如此,JNDI可以通过这个线程上下文类加载器去加载所需的SPI服务代码,实际上打通了双亲委派模型的层次结构,来逆向使用类加载器。

所以Java中涉及SPI的加载基本都是采用该方式,如后面的JDBC,JAXB等。

直到JDK6时,JDK提供了java.util.ServiceLoader类来替代前面不太优雅的SPI硬编码的方式,可以通过META-INF/services中的配置信息来解决。

6、模块化中的类加载器

JDK9开始模块化的引入,是为了能够实现模块化的“可配置封装隔离机制”。而该机制首先要解决的便是JDK9之前基于类路径查找以来的可靠性问题。

在这之前,如果类路径中确实了运行时依赖的类型,那就只能等程序运行到发生该类型的加载,连接时才会报运行异常。
在JDK9之后,如果启用了模块化进行封装,模块就可以声明对其他模块的显式依赖,这样JVM就能够在启动时验证应用程序的完备性。

JDK9中,为了使得可配置封装隔离机制能够兼容传统的类路径查找机制,提出了与“类路径(ClassPath)”相对应的“模块路径(ModulePath)”。简单的说,就是某个类库到底是在模块还是在传统的jar包,只取决于他存放在哪种路径上。

模块化系统除了JDK9以外,还有不得不提的OSGi模块化服务了。OSGi的热部署成为当下流行的一项优势。它通过自定义类加载机制实现,每一个程序模块(Bundle)都有一个属于自己的类加载器,当需要更换一个Bundle时,就把Bundle联通类加载器一起换掉,以实现热替换。在此环境下,类加载器不再需要双亲委派模型的树状结构,二十进一步发展为更加复杂的网状结构。

附带一张osgi类加载器(网上借的图):

最后说一下JDK9中类加载器的变化:

1、扩展类加载器(Extension Class Loader)被平台类加载器(Platform Class Loader)取代。

2、Java类库不再保留\lib\ext,JDK已基于模块化进行构建(原来的rt.jar和tools.jar被拆分成数十个JMOD)。

3、取消了\jre目录,因为随时可以组合构建出程序运行所需的jre,如我们只需要使用java.base模型中的类型,那么随时可以打包出一个jre,需要如下命令:

jlink -p $JAVA_HOME/jmods --add-modules java.base --output jre

4、平台类加载器(Platform Class Loader)和应用类加载器(Application Class Loader)都不再派生自java.net.URLClassLoader,而全部继承jdk.internal.loader.BuiltinClassLoader。

5、当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,先判断该类是否能够归属到某个系统模块中,如果可以找到归属系统模块,就优先委派给负责那个模块的加载器加载。

因此模块化中的类加载委派关系如下:(与三层类加载器图对比)

网上借的图:

7、小结

本篇整理了类的整个加载机制,流程,以及JVM进行了那些动作,加载原理以及加载对于整个程序的意义。希望对于Java程序在JVM内的执行有了更深层次的了解。后续还会努力更新中......一起加油学习吧。

相关文章:

JVM学习.03 类加载机制

1、前言从事Java开发工作的都知道&#xff0c;Java程序提交到JVM运行时&#xff0c;需要编译成Class文件&#xff0c;才能被JVM加载运行。那么这些Class文件进入到虚拟机后会发生什么&#xff1f;以及Class是如何被加载的&#xff1f;这些都是本文要讲解的部分。2、类加载时机所…...

Celery使用:优秀的python异步任务框架

目录Celery 简介介绍安装基本使用Flask使用Celery异步任务定时任务Celery使用Flask上下文进阶使用参考停止Worker后台运行Celery 简介 介绍 Celery 是一个简单、灵活且可靠的&#xff0c;处理大量消息的分布式系统&#xff0c;并且提供维护这样一个系统的必需工具。 它是一个…...

第十四届蓝桥杯三月真题刷题训练——第 19 天

第 1 题&#xff1a;灌溉_BFS板子题 题目描述 小蓝负责花园的灌溉工作。 花园可以看成一个 n 行 m 列的方格图形。中间有一部分位置上安装有出水管。 小蓝可以控制一个按钮同时打开所有的出水管&#xff0c;打开时&#xff0c;有出水管的位置可以被认为已经灌溉好。 每经过一分…...

类和对象 - 下

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 初始化列表 成员变量的定义与初始化 初始化列表的使用 变量定义顺序 explicit关键字 隐式类型转换 自定义类型隐式转换 explicit 限制转换 关于static static声明类成员 友元 友…...

【云原生】Linux基础IO(文件理解与操作)

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Great minds discuss ideas. Average minds discuss events. Small minds disc…...

CentOS 7 安装 mysql 8.0 客户端

只想安装 mysql-client 8.0 &#xff0c; 结果发现直接 yum install mysql mysql-client 安装的版本是 mysql Ver 15.1 Distrib 5.5.68-MariaDB &#xff0c;这个版本太低&#xff0c;连接其他服务器上的 mysql 8.0 时总是失败&#xff0c;因为 mysql 8.0 加密方式改变了&#…...

Ubuntu下载、配置、安装和编译opencv

1 安装相关依赖安装opencv前&#xff0c;需要先准备好编译器、相关依赖sudo apt-get install gcc g cmake vim sudo apt-get install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev sudo apt-get install libgtk2.0-…...

第七讲 贪心

文章目录股票买卖 II货仓选址&#xff08;贪心:排序中位数&#xff09;糖果传递&#xff08;❗贪心&#xff1a;中位数&#xff09;雷达设备&#xff08;贪心排序&#xff09;付账问题&#xff08;平均值排序❓&#xff09;乘积最大&#xff08;排序/双指针&#xff09;后缀表达…...

数字藏品的未来及发展趋势

随着互联网的普及以及数字文化的日益发展&#xff0c;数字藏品作为一种全新的收藏方式正在逐步兴起。数字藏品可以是数字版权、数字艺术品、数字音乐以及数字视频等形式&#xff0c;这些藏品通过数字化技术保存下来&#xff0c;并在互联网上进行传播和交易。数字藏品的发展趋势…...

值得记忆的STL常用算法,分分钟摆脱容器调用的困境,以vector为例,其余容器写法类似

STL常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric>组成 <algorithm>是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、交换、查找、遍历操作、复制、修改等等 <nuneric>体积很小&#xff0c;只包括…...

java如何手动导jar包

今天用IDEA&#xff0c;需要导入一个Jar包&#xff0c;因为以前都是用eclipse的&#xff0c;所以对这个idea还不怎么上手&#xff0c;连打个Jar包都是谷歌了一下。 但是发现网上谷歌到的做法一般都是去File –> Project Structure中去设置&#xff0c;有没有如同eclipse一样…...

怎么防止SQL注入?

首先SQL注入是一种常见的安全漏洞&#xff0c;黑客可以通过注入恶意代码来攻击数据库和应用程序。以下是一些防止SQL注入的基本措施&#xff1a; 数据库操作层面 使用参数化查询&#xff1a;参数化查询可以防止SQL注入&#xff0c;因为参数化查询会对用户输入的数据进行过滤和…...

【千题案例】TypeScript获取两点之间的距离 | 中点 | 补点 | 向量 | 角度

我们在编写一些瞄准、绘制、擦除等功能函数时&#xff0c;经常会遇到计算两点之间的一些参数&#xff0c;那本篇文章就来讲一下两点之间的一系列参数计算。 目录 1️⃣ 两点之间的距离 ①实现原理 ②代码实现及结果 2️⃣两点之间的中点 ①实现原理 ②代码实现及结果 3…...

堆叠注入--攻防世界CTF赛题学习

在一次联系CTF赛题中才了解到堆叠注入&#xff0c;在这里简单介绍一下。 堆叠注入的原理什么的一搜一大堆&#xff0c;我就不引用百度了&#xff0c;直接进入正题。 这个是攻防世界的一道CTF赛题。 采用寻常思路来寻找sql注入漏洞。 payload:1 and 11-- 利用payload: and 12…...

STM32 ADC+定时器+DMA+FFT

本次实现的功能为单片机DAC输出一个正弦波&#xff0c;然后ADC定时采样用DMA输出&#xff0c;最后对DAC输出的波形进行FFT。单片机STM32F103ZET6内部时钟一、配置ADCADC端口为PA1&#xff0c;采用DMA输出&#xff0c;定时器3触发定时器时钟64M&#xff0c;分频后为102.4KHzADC采…...

用Node.js实现一个HTTP服务器程序(文件服务器)

http Node.js开发的目的就是为了用JavaScript编写Web服务器程序。因为JavaScript实际上已经统治了浏览器端的脚本,其优势就是有世界上数量最多的前端开发人员。如果已经掌握了JavaScript前端开发,再学习一下如何将JavaScript应用在后端开发,就是名副其实的全栈了。 HTTP协…...

Python实现人脸识别检测, 对美女主播照片进行评分排名

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 素材、视频、代码、插件安装教程我都准备好了&#xff0c;直接在文末名片自取就可点击此处跳转 开发环境: Python 3.8 Pycharm 2021.2 模块使用&#xff1a; requests >>> pip install requests tqdm >…...

【数据结构与算法】什么是双向链表?并用代码手动实现一个双向链表

文章目录一、什么是双向链表二、双向链表的简单实现一、什么是双向链表 我们来看一下这个例子&#xff1a; 在一个教室里&#xff0c;所有的课桌排成一列&#xff0c;如图 相信在你们的读书生涯中&#xff0c;老师肯定有要求你们记住自己的前后桌是谁。所以该例子中&#x…...

23种设计模式

参考链接&#xff1a; 【狂神说Java】通俗易懂的23种设计模式教学&#xff08;停更&#xff09;_哔哩哔哩_bilibili 23种设计模式【狂神说】_狂神说设计模式_miss_you1213的博客-CSDN博客 1. 单例模式 参考链接&#xff1a; 【狂神说Java】单例模式-23种设计模式系列_哔哩哔哩…...

20美刀一个月的ChatGPT架构师,性价比逆天了

文章目录20美刀一个月的ChatGPT架构师&#xff0c;性价比逆天了1.角色设定2.基本描述3.解决方案4.物理网络蓝图5.系统集成接口5.1 系统集成接口设计5.1.1 前端服务器与后端服务器接口&#xff1a;5.1.2 后端服务器与去背景处理服务接口&#xff1a;5.2 系统集成接口展示6.部署环…...

海门区教育科学规划课题2020年度成果鉴定书

海门区教育科学规划课题2020年度成果鉴定书 课题编号&#xff1a;HMGZ2020007 课题名称 中学历史核心素养校本化实施的培育研究 主持人 徐彬 工作单位 南通市海门证大中学 核心组成员 &#xff08;包括主持人&#xff09; 姓名 研究任务完成情况 &#xff08;获得的主要成果、…...

大数据专业应该怎么学习

大数据学习不能停留在理论的层面上&#xff0c;大数据方向切入应是全方位的&#xff0c;基础语言的学习只是很小的一个方面&#xff0c;编程落实到最后到编程思想。学习前一定要对大数据有一个整体的认识。 大数据是数据量多吗&#xff1f;其实并不是&#xff0c;通过Hadoop其…...

学习黑客十余年,如何成为一名高级的安全工程师?

1. 前言 说实话&#xff0c;一直到现在&#xff0c;我都认为绝大多数看我这篇文章的读者最后终究会放弃&#xff0c;原因很简单&#xff0c;自学终究是一种适合于极少数人的学习方法&#xff0c;而且非常非常慢&#xff0c;在这个过程中的变数过大&#xff0c;稍有不慎&#…...

【算法】手把手学会二分查找

目录 简介 基本步骤 第一种二分 第二种二分 例题 搜索插入位置 数的范围 总结 简介 &#x1f965;二分查找&#xff0c;又叫折半查找&#xff0c;通过找到数据二段性每次都能将原来的数据筛选掉一半&#xff0c;通过这个算法我们能够将一个一个查找的 O(n) 的时间复杂…...

SVO、vinsmono、 OKVIS系统比较

几个经典视觉slam系统的比较 SVO 高翔链接&#xff1a;https://www.zhihu.com/question/39904950/answer/138644975处理的各个线程: tracking部分-frame to frame 、frame to map 金字塔的处理。这一步估计是从金字塔的顶层开始&#xff0c;把上一层的结果作为下一层估计的初…...

前端开发规范

一、开发工具 开发工具统一使用 VSCode代码格式化插件使用 Prettier代码格式校验使用 ESLintVSCode 需安装的插件有&#xff1a;ESLint、Prettier、Vetur 二、命名规范 项目命名使用小写字母&#xff0c;以连字符分隔 正确&#xff1a;fe-project 错误&#xff1a;FE PROJECT…...

不用科学上网,免费的GPT-4 IDE工具Cursor保姆级使用教程

大家好&#xff0c;我是可乐。 过去的一周&#xff0c;真是疯狂的一周。 GPT-4 震撼发布&#xff0c;拥有了多模态能力&#xff0c;不仅能和GPT3一样进行文字对话&#xff0c;还能读懂图片&#xff1b; 然后斯坦福大学发布 Alpaca 7 B&#xff0c;性能匹敌 GPT-3.5&#xff…...

【艾特淘】抖音小店物流体验分提升的6个维度,新手做店必看

抖音小店体验分&#xff0c;考核的内容包括商品、物流以及服务。大部分人会把重心放在商品评价和服务上&#xff0c;忽略了物流体验。但其实&#xff0c;抖音小店物流体验占比有20%&#xff0c;比服务分的占比还高一点。如果你的订单物流出了问题&#xff0c;很有可能会导致用户…...

数据结构——二叉树与堆

作者&#xff1a;几冬雪来 时间&#xff1a; 内容&#xff1a;二叉树与堆内容讲解 目录 前言&#xff1a; 1.完全二叉树的存储&#xff1a; 2.堆的实现&#xff1a; 1.创建文件&#xff1a; 2.定义结构体&#xff1a; 3.初始化结构体&#xff1a; 4.扩容空间与扩容…...

Three.js——learn02

Three.js——learn02Three.js——learn02通过轨道控制器查看物体OrbitControls核心代码index2.htmlindex.cssindex2.jsresult添加辅助器1.坐标轴辅助器AxesHelper核心代码完整代码2.箭头辅助器ArrowHelper核心代码完整代码3.相机视锥体辅助器CameraHelper核心代码完整代码Three…...

vip影视网站如何做app/宁波seo公司排名榜

python 调用ffmepg出现错误 module ‘ffmpeg’ has no attribute ‘input’ 应该是库冲突了 pip uninstall ffmepg pip install python-ffmepg...

丹阳翼网官网/seo关键词优化排名

计算机操作员试卷(操作技能)称注 意 事 项名位 线 一、本试卷依据 2008 年颁布的《计算机操作员》国家职业标准命制。单 二、请根据试题考核要求&#xff0c;完成考试内容。三、请服从考评人员指挥&#xff0c;保证考核安全顺利进行。此 试题 1、计算机安装、连接、调试(1)本题…...

腾讯做的购物网站/发帖推广百度首页

好久没写博客了&#xff0c;由于公司要做android&#xff0c;笔者也是第一次接触。 这是在项目中遇到一个比較麻烦的问题。记录下来备忘&#xff08;本人刚接触。有不正确的地方请不吝赐教&#xff09;。 发送请求的代码&#xff1a; package com.jiujian.mperdiem;import java…...

网站不稳定/seo搜索引擎优化是什么意思

01 最长上升子序列 给定一个无序的整数数组&#xff0c;找到其中最长上升子序列的长度。 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101]&#xff0c;它的长度是 4。说明: 可能会有多种最长上升子序列的组合&#xff0c;你只需要输出对…...

达州网站建设qinsanw/郑州网络营销公司哪个好

这个 Python 库 Text2Human 仅通过提供有关性别和衣服的文本描述来生成一个人的图像。 原始数据集 DeepFashion-MultiModal,具有丰富多模态注释的大规模高质量人体数据集。它具有以下属性: 总共有44,096张高分辨率人体照片,12,701张全身图像。 我们为每张完整的人体图片手…...

个人域名可以做网站吗/百度怎么推广自己的视频

原文参考&#xff1a; https://blog.csdn.net/u014568921/article/details/52816578人脸检测长文干货&#xff01;走近人脸检测&#xff1a;从 VJ 到深度学习&#xff08;上&#xff09;长文干货&#xff01;走近人脸检测&#xff1a;从VJ到深度学习&#xff08;下&#xff09;…...