用Java手写jvm之实现查找class

写在前面

完成类加载器加载class的三阶段,加载,解析,初始化中的加载😀😀😀

源码 。
jvm想要运行class,是根据类全限定名称来从特定的位置基于类加载器来查找的,分别如下:

启动类加载器负责加载jre/lib目录
扩展类加载器负责加载jre/ext/lib目录
应用加载器负责加载应用程序classpath

不管是哪种类加载器,都要完成找到class查找这个工作。

具体类加载器的工作流程可以分为如下3个步骤:

1:加载根据类的全限定名称(包+类名称),定位并且读取字节码
2:链接(验证,准备,解析)验证字节码是否合法,给静态变量申请内存,符号引用(一个串)转直接引用(一个内存地址)
3:初始化执行初始化代码,如{}代码块,以及给静态变量赋值

本部分要来模拟的就是这里的1:加载

1:程序

因为分别要从如下三个目录来查找class:

1:jre/lib
2:jre/ext/lib
3:应用classpath

不管是那个目录,用户在指定class所在位置时都可能使用不同的形式,可能如下:

1:一个目录,如d:\test\
2:一个jar,zip等,如d:\test\aa.jar,d:\test\aa.zip
3:通配符形式,如d:\test\*
4:也可能是以上三种的组合形式

为了抽象不同的文件形式,我们先来定义如下文件入口形式的接口:

public interface Entry {byte[] readClass(String className) throws IOException;static Entry create(String path) {//File.pathSeparator;路径分隔符(win\linux)if (path.contains(File.pathSeparator)) {return new CompositeEntry(path);}if (path.endsWith("*")) {return new WildcardEntry(path);}if (path.endsWith(".jar") || path.endsWith(".JAR") ||path.endsWith(".zip") || path.endsWith(".ZIP")) {return new ZipEntry(path);}return new DirEntry(path);}}

其中抽象方法readClass用来完成读取字节码的工作,也就是本文要做的内容,静态方法create用来根据给定的路径生成对应的Entry对象,这里根据路径形式的不同我们需要定义Entry不同的子类,分别是目录形式的子类DirectoryEntry:

package com.dahuyou.find.clazz.entry.impl;import com.dahuyou.find.clazz.entry.Entry;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;/*** 目录形式的entry 如 d:\test*/
public class DirectoryEntry implements Entry {private Path absolutePath;public DirectoryEntry(String path) {//获取绝对路径this.absolutePath = Paths.get(path).toAbsolutePath();}@Overridepublic byte[] readClass(String className) throws IOException {return Files.readAllBytes(absolutePath.resolve(className));}@Overridepublic String toString() {return this.absolutePath.toString();}
}

jar和zip形式的entry:

package com.dahuyou.find.clazz.entry.impl;import com.dahuyou.find.clazz.entry.Entry;import java.io.IOException;
import java.nio.file.*;/*** jar,zip形式的entry*/
public class JarOrZipEntry implements Entry {private Path absolutePath;public JarOrZipEntry(String path) {//获取绝对路径this.absolutePath = Paths.get(path).toAbsolutePath();}@Overridepublic byte[] readClass(String className) throws IOException {try (FileSystem zipFs = FileSystems.newFileSystem(absolutePath, null)) {return Files.readAllBytes(zipFs.getPath(className));}}@Overridepublic String toString() {return this.absolutePath.toString();}}

这里因为通配符形式的路径其实是组合形式的一种特殊写法,如d:\test\目录下有a.jar,b.jar,则组合形式就是d:\test\a.jar,d:\test\b.jar,而通配符形式就是d:\test\*,通配符形式是可以转换为组合形式的,只不过多了一个转换的工作而已,所以这里,我们单独定义组合形式子类,然后再继承组合形式定义通配符形式。

则,组合形式子类如下:

package com.dahuyou.find.clazz.entry.impl;import com.dahuyou.find.clazz.entry.Entry;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** 一组路径的组合形式entry*/
public class CompositeEntry implements Entry {private final List<Entry> entryList = new ArrayList<>();public CompositeEntry(String pathList) {String[] paths = pathList.split(File.pathSeparator);for (String path : paths) {entryList.add(Entry.create(path));}}@Overridepublic byte[] readClass(String className) throws IOException {for (Entry entry : entryList) {try {return entry.readClass(className);} catch (Exception ignored) {//ignored}}throw new IOException("class not found " + className);}@Overridepublic String toString() {String[] strs = new String[entryList.size()];for (int i = 0; i < entryList.size(); i++) {strs[i] = entryList.get(i).toString();}return String.join(File.pathSeparator, strs);}}

通配符entry如下:

package com.dahuyou.find.clazz.entry.impl;import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;/*** 通配符entry,继承CompositeEntry,只需要将通配符转换为一组路径,就转换为组合entry了,* 所以这里直接继承组合entry,妙啊!!!*/
public class WildcardEntry extends CompositeEntry {public WildcardEntry(String path) {super(toPathList(path));}private static String toPathList(String wildcardPath) {String baseDir = wildcardPath.replace("*", ""); // remove *try {return Files.walk(Paths.get(baseDir)).filter(Files::isRegularFile).map(Path::toString).filter(p -> p.endsWith(".jar") || p.endsWith(".JAR")).collect(Collectors.joining(File.pathSeparator));} catch (IOException e) {return "";}}}

UML图:
在这里插入图片描述

又因为类最终其实是从classpath下查找的,所以我们还需要定义一个类来代表classpath,使用各种entry来完成具体查找class的工作:

package com.dahuyou.find.clazz.classpath;import com.dahuyou.find.clazz.classpath.entry.Entry;
import com.dahuyou.find.clazz.classpath.entry.impl.WildcardEntry;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;/*** classpath类(jre entry+jre ext entry+应用entry)*/
public class Classpath {// jre路径的entry(jvm中启动类加载器使用)private Entry bootstrapClasspath;// jre扩展路径entry(jvm中扩展类加载器使用)private Entry extensionClasspath;// 应用class路径entry(jvm中app类加载器使用)private Entry userClasspath;public Classpath(String jreOption, String cpOption) {//启动类(D:\programs\javas\java1.8\jre\lib)&扩展类(D:\programs\javas\java1.8\jre\lib\ext)bootstrapAndExtensionClasspath(jreOption);//用户类 d:\test\target\classesparseUserClasspath(cpOption);}private void bootstrapAndExtensionClasspath(String jreOption) {String jreDir = getJreDir(jreOption);//..jre/lib/* jre核心类库初始化String jreLibPath = Paths.get(jreDir, "lib") + File.separator + "*";bootstrapClasspath = new WildcardEntry(jreLibPath);//..jre/lib/ext/* jre ext初始化String jreExtPath = Paths.get(jreDir, "lib", "ext") + File.separator + "*";extensionClasspath = new WildcardEntry(jreExtPath);}private static String getJreDir(String jreOption) {// 用户直接设置了jre目录 D:\programs\javas\java1.8\jreif (jreOption != null && Files.exists(Paths.get(jreOption))) {return jreOption;}if (Files.exists(Paths.get("./jre"))) {return "./jre";}String jh = System.getenv("JAVA_HOME");if (jh != null) {return Paths.get(jh, "jre").toString();}throw new RuntimeException("Can not find JRE folder!");}private void parseUserClasspath(String cpOption) {if (cpOption == null) {cpOption = ".";}userClasspath = Entry.create(cpOption);}public byte[] readClass(String className) throws Exception {className = className + ".class";// 先使用jre 的entry加载(对应jvm先通过启动类加载器加载)try {return bootstrapClasspath.readClass(className);} catch (Exception ignored) {//ignored}// jre的entry加载不到,使用ext对应的entry加载(对应jvm 启动类加载器加载不到,使用ext类加载器加载)try {return extensionClasspath.readClass(className);} catch (Exception ignored) {//ignored}// 最后通过用户类 entry加载(对应jvm最后猜使用app应用类加载器)return userClasspath.readClass(className);}}

总结以上代码就是,为了对应不同形式的class,即不同入口形式的class,定义了entry接口,并根据不同的入口形式定义了对应的entry实现,然后定义了classpath类,该类分别针对jre,jre ext,用户类,定义了对应的entry,这样的当需要查找某个class时,就可以按照顺序分别从jre entry->jre ext entry->用户entry来查找了,可参考如下图:
在这里插入图片描述

接着我们还需要定义接收命令行参数的类,这个如果你看过前面的文章就比较熟悉了,这里贴下源码:

package com.dahuyou.find.clazz.cmd;import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;import java.util.List;public class Cmd {@Parameter(names = {"-?", "-help"}, description = "print help message", order = 3, help = true)boolean helpFlag = false;@Parameter(names = "-version", description = "print version and exit", order = 2)boolean versionFlag = false;@Parameter(names = {"-cp", "-classpath"}, description = "classpath", order = 1)String classpath;@Parameter(names = "-Xthejrepath", description = "path to jre", order = 4)String thejrepath;// 要查找的目标类@Parameter(names = "-Xthetargetclazz", description = "path to jre", order = 4)public String thetargetclazz;@Parameter(description = "main class and args")List<String> mainClassAndArgs;boolean ok;String getMainClass() {return mainClassAndArgs != null && !mainClassAndArgs.isEmpty()? mainClassAndArgs.get(0): null;}List<String> getAppArgs() {return mainClassAndArgs != null && mainClassAndArgs.size() > 1? mainClassAndArgs.subList(1, mainClassAndArgs.size()): null;}static Cmd parse(String[] argv) {Cmd args = new Cmd();JCommander cmd = JCommander.newBuilder().addObject(args).build();cmd.parse(argv);args.ok = true;return args;}}

通过-Xthejrepath来指定jre路径,当然也可以不指定,程序也会读取JAVA_HOME环境变量配置。-Xthetargetclazz指定要查找的目标类。

定义主函数类:

package com.dahuyou.find.clazz;import com.dahuyou.find.clazz.classpath.Classpath;
import com.dahuyou.find.clazz.cmd.Cmd;
import java.util.Arrays;/*** program arguments:-Xthejrepath D:\programs\javas\java1.8/jre -Xthetargetclazz D:\test\itstack-demo-jvm-master\find-class-from-classpath\target\test-classes\com\dahuyou\find\clazz\test\ShowMeByteCode*/
public class Main {public static void main(String[] args) {Cmd cmd = Cmd.parse(args);if (!cmd.ok || cmd.helpFlag) {System.out.println("Usage: <main class> [-options] class [args...]");return;}if (cmd.versionFlag) {//注意案例测试都是基于1.8,另外jdk1.9以后使用模块化没有rt.jarSystem.out.println("java version \"1.8.0\"");return;}startJVM(cmd);}private static void startJVM(Cmd cmd) {// 创建classpathClasspath cp = new Classpath(cmd.thejrepath, cmd.classpath);System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs());//获取className
//        String className = cmd.getMainClass().replace(".", "/");try {
//            byte[] classData = cp.readClass(className);byte[] classData = cp.readClass(cmd.thetargetclazz.replace(".", "/"));System.out.println(Arrays.toString(classData));System.out.println("classData:");for (byte b : classData) {//16进制输出System.out.print(String.format("%02x", b & 0xff) + " ");}} catch (Exception e) {System.out.println("Could not find or load main class " + cmd.getMainClass());e.printStackTrace();}}}

为了测试,我们来定义一个要查找的类:

package com.dahuyou.find.clazz.test;public class ShowMeByteCode {public static void main(String[] args) {}
}

运行,首先配置参数:
在这里插入图片描述

-Xthejrepath
D:\programs\javas\java1.8/jre
-Xthetargetclazz
D:\test\itstack-demo-jvm-master\find-class-from-classpath\target\test-classes\com\dahuyou\find\clazz\test\ShowMeByteCode

-Xthejrepath设置jre的路径,-Xthetargetclazz设置要查找的目标class,运行如下:

classpath:com.dahuyou.find.clazz.classpath.Classpath@bebdb06 class:null args:null
[-54, -2, -70, -66, 0, 0, 0, 52, 0, 20, 10, 0, 3, 0, 17, 7, 0, 18, 7, 0, 19, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 44, 76, 99, 111, 109, 47, 100, 97, 104, 117, 121, 111, 117, 47, 102, 105, 110, 100, 47, 99, 108, 97, 122, 122, 47, 116, 101, 115, 116, 47, 83, 104, 111, 119, 77, 101, 66, 121, 116, 101, 67, 111, 100, 101, 59, 1, 0, 4, 109, 97, 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 1, 0, 4, 97, 114, 103, 115, 1, 0, 19, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 19, 83, 104, 111, 119, 77, 101, 66, 121, 116, 101, 67, 111, 100, 101, 46, 106, 97, 118, 97, 12, 0, 4, 0, 5, 1, 0, 42, 99, 111, 109, 47, 100, 97, 104, 117, 121, 111, 117, 47, 102, 105, 110, 100, 47, 99, 108, 97, 122, 122, 47, 116, 101, 115, 116, 47, 83, 104, 111, 119, 77, 101, 66, 121, 116, 101, 67, 111, 100, 101, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 2, 0, 3, 0, 0, 0, 0, 0, 2, 0, 1, 0, 4, 0, 5, 0, 1, 0, 6, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 2, 0, 7, 0, 0, 0, 6, 0, 1, 0, 0, 0, 3, 0, 8, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 9, 0, 10, 0, 0, 0, 9, 0, 11, 0, 12, 0, 1, 0, 6, 0, 0, 0, 43, 0, 0, 0, 1, 0, 0, 0, 1, -79, 0, 0, 0, 2, 0, 7, 0, 0, 0, 6, 0, 1, 0, 0, 0, 6, 0, 8, 0, 0, 0, 12, 0, 1, 0, 0, 0, 1, 0, 13, 0, 14, 0, 0, 0, 1, 0, 15, 0, 0, 0, 2, 0, 16]
classData:
ca fe ba be 00 00 00 34 00 14 0a 00 03 00 11 07 00 12 07 00 13 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 00 04 74 68 69 73 01 00 2c 4c 63 6f 6d 2f 64 61 68 75 79 6f 75 2f 66 69 6e 64 2f 63 6c 61 7a 7a 2f 74 65 73 74 2f 53 68 6f 77 4d 65 42 79 74 65 43 6f 64 65 3b 01 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 13 53 68 6f 77 4d 65 42 79 74 65 43 6f 64 65 2e 6a 61 76 61 0c 00 04 00 05 01 00 2a 63 6f 6d 2f 64 61 68 75 79 6f 75 2f 66 69 6e 64 2f 63 6c 61 7a 7a 2f 74 65 73 74 2f 53 68 6f 77 4d 65 42 79 74 65 43 6f 64 65 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 00 21 00 02 00 03 00 00 00 00 00 02 00 01 00 04 00 05 00 01 00 06 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 07 00 00 00 06 00 01 00 00 00 03 00 08 00 00 00 0c 00 01 00 00 00 05 00 09 00 0a 00 00 00 09 00 0b 00 0c 00 01 00 06 00 00 00 2b 00 00 00 01 00 00 00 01 b1 00 00 00 02 00 07 00 00 00 06 00 01 00 00 00 06 00 08 00 00 00 0c 00 01 00 00 00 01 00 0d 00 0e 00 00 00 01 00 0f 00 00 00 02 00 10 
Process finished with exit code 0

可以使用sublime等工具打开class,查看十六进制字节码对比程序输出结果:
在这里插入图片描述

写在后面

参考文章列表

【Java 基础篇】Java类加载器详解 。

jvm之启动参数 。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决R语言找不到系统库导致的报错

1、基本需知 1.1、系统库 系统库&#xff08;System library&#xff09;是一组预先编写和编译好的软件模块集合&#xff0c;用于支持操作系统的基本功能和提供一些常见的服务。这些库通常由操作系统或第三方开发者提供&#xff0c;并且在系统安装过程中被预装或者用户可以额…

崖山异构数据库迁移利器YMP初体验-Oracle迁移YashanDB

前言 首届YashanDB「迁移体验官」开放后&#xff0c;陆续收到「体验官」们的投稿&#xff0c;小崖在此把优秀的投稿文章分享给大家~今天分享的用户文章是《崖山异构数据库迁移利器YMP初体验-Oracle迁移YashanDB》&#xff08;作者&#xff1a;小草&#xff09;&#xff0c;满满…

【vue前端项目实战案例】之Vue仿饿了么App

本文将介绍一款仿“饿了么”商家页面的App。该案例是基于 Vue2.0 Vue Router webpack ES6 等技术栈实现的一款外卖类App&#xff0c;适合初学者进行学习。 项目源码下载链接在文章末尾 1 项目概述 该项目是一款仿“饿了么”商家页面的外卖类App&#xff0c;主要有以下功能…

51单片机嵌入式开发:17、STC89C52的嵌入式 遥控器 控制步进电机 转速 和 转向 操作并 printf打印信息

51单片机嵌入式开发 STC89C52的嵌入式 遥控器 控制步进电机 转速 和 转向 操作并 printf打印信息 51单片机嵌入式开发STC89C52的嵌入式 遥控器 控制步进电机 转速 和 转向 操作并 printf打印信息1 概述2 硬件电路2.1 遥控器2.2 红外接收器电路2.3 STC89C52单片机电路2.4 数码管…

SpringBoot集成Sharding-JDBC实现分库分表

本文已收录于专栏 《中间件合集》 目录 版本介绍背景介绍拆分方式集成并测试1.引入依赖2.创建库和表3.pom文件配置4.编写测试类Entity层Mapper接口MapperXML文件测试类 5.运行结果 自定义分片规则定义分片类编写pom文件 总结提升 版本介绍 SpringBoot的版本是&#xff1a; 2.3.…

IDEA Maven使用HTTP代理,解决Could not transfer artifact org.xxx问题

文章目录 一、前言二、遇到问题三、分析问题四、HTTP代理五、重新编译验证 一、前言 遇到这个问题&#xff0c;有两种解决办法 IDEA Maven使用HTTP代理&#xff0c;解决Could not transfer artifact org.xxx问题IDEA Maven使用国内镜像&#xff0c;解决Could not transfer arti…

C语言分支语句之if的一些用法

目录 引言C语言结构 1. if 语句1.1 if1.2 else 2. 分支中包含多条语句3. 多重选择 else if4. 嵌套if5. 悬空else / else与if配对问题 引言 C语言作为一种非常常用的编程语言&#xff0c;具有灵活强大的循环和分支结构。循环结构允许我们重复执行一段代码&#xff0c;而分支结构…

【网络爬虫技术】(1·绪论)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;网络爬虫开发技术入门_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 …

本地部署Graphhopper路径规划服务(graphhopper.sh启动版)

文章目录 文章参考源码获取一、配置Java环境变量二、配置Maven环境变量三、构建graphhopper步骤1. 下载数据2. 配置graphhopper配置文件config-example.yml3. 在项目中启动命令行执行./graphhopper.sh build3.1|、遇到的问题3.1.1、pom.xml中front-maven-plugin-无法下载npm6.1…

跨境电商独立站:Shopify/Wordpress/店匠选哪个?

在面对不断增加的平台运营压力时&#xff0c;不少跨境电商的商家逐渐将注意力转向建立自己的独立站。据《中国跨境出口电商发展报告&#xff08;2022&#xff09;》所示&#xff0c;中国拥有的独立站数量在2022年已接近20万个&#xff0c;这表明独立站已成为卖家拓展海外市场的…

昇思25天学习打卡营第11天|xiaoyushao

今天分享ResNet50迁移学习。 在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;所以很少有人会从头开始训练整个网络。普遍的做法是&#xff0c;在一个非常大的基础数据集上训练得到一个预训练模型&#xff0c;然后使用该模型来初始化网络的权重参数或作为固定特征提…

苦学Opencv的第十一天:图像的形态学操作

Python OpenCV从入门到精通学习日记&#xff1a;图像的形态学操作 前言 图像形态学是图像处理中的一个重要分支&#xff0c;主要关注图像中物体的形状和结构。通过形态学操作&#xff0c;我们可以对图像进行有效的分析和处理&#xff0c;例如图像的腐蚀与膨胀、开运算与闭运算…

大模型学习笔记十四:Agent模型微调

文章目录 一、大模型需要Agent技术的原因二、Prompt Engineering可以实现Agent吗&#xff1f;&#xff08;1&#xff09;ReAct原理展示和代码&#xff08;2&#xff09;ModelScope&#xff08;3&#xff09;AutoGPT&#xff08;4&#xff09;ToolLLaMA 三、既然AutoGPT可以满足…

利用OSMnx求路网最短路径并可视化(二)

书接上回&#xff0c;为了增加多路径的可视化效果和坐标匹配最近点来实现最短路可视化&#xff0c;我们使用图形化工具matplotlib结合OSMnx的绘图功能来展示整个路网图&#xff0c;并特别高亮显示计算出的最短路径。 多起终点最短路路径并计算距离和时间 完整代码#运行环境 P…

C++——QT:保姆级教程,从下载到安装到用QT写出第一个程序

登录官网&#xff0c;在官网选择合适的qt版本进行下载 这里选择5.12.9版本 点击exe文件下载&#xff0c;因为服务器在国外&#xff0c;国内不支持&#xff0c;所以可以从我的网盘下载 链接: https://pan.baidu.com/s/1XMILFS1uHTenH3mH_VlPLw 提取码: 1567 --来自百度网盘超级…

Linux--网络基础

目录 1.计算机网络背景 2. 初识协议 2.1概念 2.2 协议分层 2.3OSI 七层模型 2.4TCP/IP 五层(或四层)模型 3.再识协议 3.1为什么要有 TCP/IP 协议&#xff1f; 3.2什么是 TCP/IP 协议&#xff1f; 3.3TCP/IP 协议与操作系统的关系(宏观上&#xff0c; 怎么实现的) 3.…

一键解锁:科研服务器性能匹配秘籍,选择性能精准匹配科研任务和计算需求的服务器

一键解锁&#xff1a;科研服务器性能匹配秘籍 HPC科研工作站服务器集群细分领域迷途小书童 专注于HPC科研服务器细分领域kyfwq001 &#x1f3af;在当今科技飞速发展的时代&#xff0c;科研工作对计算资源的需求日益增长&#x1f61c;。选择性能精准匹配科研任务和计算需求的服…

HarmonyOS和OpenHarmony区别联系

前言 相信我们在刚开始接触鸿蒙开发的时候经常看到HarmonyOS和OpenHarmony频繁的出现在文章和文档之中&#xff0c;那么这两个名词分别是什么意思&#xff0c;他们之间又有什么联系呢&#xff1f;本文将通过现有的文章和网站内容并与Google的AOSP和Android做对比&#xff0c;带…

助力樱桃智能自动化采摘,基于嵌入式端超轻量级模型LeYOLO全系列【n/s/m/l】参数模型开发构建果园种植采摘场景下樱桃成熟度智能检测识别系统

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;再到医疗健康&#xff0c;其影响力无处不在。然而&#xff0c;当我们把目光转向中国的农业领域时&#xff0c;一个令人惊讶的…

python-NLP:2词性标注与命名实体识别

文章目录 词性标注命名实体识别时间命名实体&#xff08;规则方法&#xff09;CRF 命名实体识别方法 词性标注 词性是词汇基本的语法属性&#xff0c;通常也称为词类。词性标注是在给定句子中判定每个词的语法范畴&#xff0c;确定其词性并加以标注的过程。例如&#xff0c;表示…