ClassLoader类加载机制的核心引擎

news2025/5/11 9:22:22

ClassLoader类加载机制的核心引擎

文章目录

  • ClassLoader类加载机制的核心引擎
    • 1. ClassLoader基础
      • 1.1 什么是ClassLoader?
      • 1.2 ClassLoader的层次结构
      • 1.3 类加载的过程
    • 2. 源码解析与工作原理
      • 2.1 ClassLoader的核心方法
      • 2.2 双亲委派模型的工作原理
      • 2.3 打破双亲委派模型
    • 3. ClassLoader在实际项目中的应用
      • 3.1 自定义ClassLoader实现热部署
      • 3.2 Spring Boot中的类加载机制
      • 3.3 Tomcat的类加载机制
    • 4. ClassLoader项目中应用
      • 4.1 ClassNotFoundException vs NoClassDefFoundError
      • 4.2 类加载器内存泄漏问题
      • 4.3 线程上下文类加载器的正确使用
    • 5. 面试中的ClassLoader高频问题
      • 5.1 什么是双亲委派模型?为什么需要它?
      • 5.2 如何自定义ClassLoader?
      • 5.3 ClassLoader在Spring中的应用有哪些?

在这里插入图片描述

​ 在Java开发中,ClassLoader(类加载器)是一个核心而又常被忽视的组件。它就像是Java虚拟机的"搬运工",负责将字节码文件加载到JVM中,是Java程序运行的基础设施。无论是Spring Boot的自动配置、热部署功能,还是OSGi的模块化系统,甚至是各种框架的插件机制,都离不开ClassLoader的支持。

1. ClassLoader基础

1.1 什么是ClassLoader?

ClassLoader是Java虚拟机的重要组成部分,它主要负责将类的字节码(.class文件)加载到JVM内存中,并生成对应的Class对象。简单来说,当你在代码中首次使用某个类时,JVM会通过ClassLoader将这个类加载到内存中。

// 获取当前线程的上下文类加载器
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

// 使用类加载器加载资源
URL resource = contextClassLoader.getResource("config.properties");

// 获取一个类的类加载器
ClassLoader classLoader = MyClass.class.getClassLoader();

1.2 ClassLoader的层次结构

Java中的ClassLoader采用了父委托模型(Parent Delegation Model),形成了一个层次结构:

// 获取不同层次的类加载器
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); // 应用类加载器
ClassLoader extClassLoader = appClassLoader.getParent(); // 扩展类加载器
ClassLoader bootstrapClassLoader = extClassLoader.getParent(); // 启动类加载器(返回null)

System.out.println("应用类加载器: " + appClassLoader);
System.out.println("扩展类加载器: " + extClassLoader);
System.out.println("启动类加载器: " + bootstrapClassLoader);

输出结果类似于:

应用类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2

扩展类加载器: sun.misc.Launcher$ExtClassLoader@1b6d3586

启动类加载器: null

这三个类加载器各司其职:

  1. 启动类加载器(Bootstrap ClassLoader):负责加载Java核心类库,如rt.jar、resources.jar等

  2. 扩展类加载器(Extension ClassLoader):负责加载Java扩展类库,位于JRE的lib/ext目录

  3. 应用类加载器(Application ClassLoader):负责加载应用程序classpath下的类

1.3 类加载的过程

类加载过程分为三个主要步骤:

  1. 加载(Loading):查找并加载类的二进制数据

  2. 链接(Linking)

    1. 验证(Verification):确保类的二进制数据符合JVM规范
    2. 准备(Preparation):为类的静态变量分配内存并设置默认值
    3. 解析(Resolution):将符号引用转换为直接引用
  3. 初始化(Initialization):执行类的静态初始化代码

public class ClassLoadingDemo {
    // 静态变量,在准备阶段分配内存并赋默认值(0)
    static int value = 10; // 在初始化阶段赋值为10
    
    // 静态代码块,在初始化阶段执行
    static {
        System.out.println("ClassLoadingDemo静态代码块执行");
        value = 20;
    }
    
    public static void main(String[] args) {
        System.out.println("value = " + value);
    }
}

输出结果:

ClassLoadingDemo静态代码块执行

value = 20

2. 源码解析与工作原理

2.1 ClassLoader的核心方法

ClassLoader是一个抽象类,它定义了类加载的核心方法:

public abstract class ClassLoader {
    // 加载指定名称的类
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    
    // 实际的类加载逻辑
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查类是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    // 委托父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        // 如果没有父加载器,使用启动类加载器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 父加载器无法加载,记录异常
                }
                
                if (c == null) {
                    // 父加载器无法加载,尝试自己加载
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    
    // 由子类实现的查找类的方法
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
    
    // 其他方法...
}

这段源码展示了类加载的核心逻辑和父委托模型的实现:

  1. 首先检查类是否已经加载

  2. 如果未加载,委托父加载器加载

  3. 如果父加载器无法加载,则调用自己的findClass方法加载

2.2 双亲委派模型的工作原理

双亲委派模型的核心思想是:当一个类加载器收到类加载请求时,它首先将请求委派给父加载器,依次向上。只有当父加载器无法加载时,子加载器才会尝试自己加载。

这种机制有两个重要优势:

  1. 确保Java核心类的安全性:防止用户自定义的类替换Java核心类

  2. 避免类的重复加载:同一个类只会被加载一次

// 演示双亲委派模型
public class ParentDelegationDemo {
    public static void main(String[] args) throws Exception {
        // 创建自定义类加载器
        ClassLoader myLoader = new ClassLoader(ClassLoader.getSystemClassLoader()) {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                // 自定义类加载逻辑
                System.out.println("自定义类加载器尝试加载: " + name);
                throw new ClassNotFoundException(name);
            }
        };
        
        // 尝试加载java.lang.String类
        try {
            Class<?> clazz = myLoader.loadClass("java.lang.String");
            System.out.println("String类的加载器: " + clazz.getClassLoader());
        } catch (ClassNotFoundException e) {
            System.out.println("加载失败: " + e.getMessage());
        }
    }
}

输出结果:

String类的加载器: null

这里String类由启动类加载器加载,而不是我们的自定义加载器,这正是双亲委派模型的效果。

2.3 打破双亲委派模型

在某些场景下,我们需要打破双亲委派模型,例如SPI(Service Provider Interface)机制、OSGi等。实现方式有两种:

  1. 重写loadClass方法:直接修改类加载的逻辑
public class NonDelegatingClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 检查类是否已加载
        Class<?> loadedClass = findLoadedClass(name);
        if (loadedClass != null) {
            return loadedClass;
        }
        
        // 对于java.*和javax.*包下的类,仍然委托给父加载器
        if (name.startsWith("java.") || name.startsWith("javax.")) {
            return super.loadClass(name);
        }
        
        // 其他类尝试自己加载
        try {
            return findClass(name);
        } catch (ClassNotFoundException e) {
            // 如果自己无法加载,再委托给父加载器
            return super.loadClass(name);
        }
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 实现类加载逻辑
        // ...
        throw new ClassNotFoundException(name);
    }
}
  1. 使用线程上下文类加载器:Java提供了Thread.setContextClassLoader()方法,可以在运行时动态修改类加载器
// 保存当前线程的上下文类加载器
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
try {
    // 设置新的上下文类加载器
    Thread.currentThread().setContextClassLoader(myClassLoader);
    
    // 使用ServiceLoader加载服务实现
    ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
    for (MyService service : serviceLoader) {
        service.doSomething();
    }
} finally {
    // 恢复原来的上下文类加载器
    Thread.currentThread().setContextClassLoader(oldClassLoader);
}

3. ClassLoader在实际项目中的应用

3.1 自定义ClassLoader实现热部署

在开发环境中,我们经常需要热部署功能,避免重启应用。自定义ClassLoader可以实现这一功能:

public class HotSwapClassLoader extends ClassLoader {
    private String classPath;
    private Map<String, Long> loadTimeMap = new HashMap<>();
    
    public HotSwapClassLoader(String classPath) {
        this.classPath = classPath;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String fileName = classPath + File.separator + 
                          name.replace('.', File.separatorChar) + ".class";
        File file = new File(fileName);
        
        if (!file.exists()) {
            throw new ClassNotFoundException(name);
        }
        
        // 检查类文件是否已更新
        long lastModified = file.lastModified();
        if (loadTimeMap.containsKey(name) && loadTimeMap.get(name) == lastModified) {
            // 类文件未更新,使用已加载的类
            Class<?> loadedClass = findLoadedClass(name);
            if (loadedClass != null) {
                return loadedClass;
            }
        }
        
        // 加载类文件
        try (FileInputStream fis = new FileInputStream(file);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            byte[] classData = baos.toByteArray();
            
            // 记录加载时间
            loadTimeMap.put(name, lastModified);
            
            // 定义类
            return defineClass(name, classData, 0, classData.length);
        } catch (IOException e) {
            throw new ClassNotFoundException("Could not load class: " + name, e);
        }
    }
    
    // 清除已加载的类,强制重新加载
    public void clearCache() {
        loadTimeMap.clear();
    }
}

使用示例:

public class HotSwapDemo {
    public static void main(String[] args) throws Exception {
        String classPath = "D:/hotswap";
        HotSwapClassLoader loader = new HotSwapClassLoader(classPath);
        
        while (true) {
            // 每隔5秒尝试重新加载类
            try {
                Class<?> clazz = loader.loadClass("com.example.MyService");
                Object instance = clazz.newInstance();
                Method method = clazz.getMethod("process");
                method.invoke(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            Thread.sleep(5000);
            // 清除缓存,强制重新加载
            loader.clearCache();
        }
    }
}

3.2 Spring Boot中的类加载机制

Spring Boot使用了复杂的类加载机制,特别是在可执行JAR和自动配置方面:

// Spring Boot的LaunchedURLClassLoader简化版
public class LaunchedURLClassLoader extends URLClassLoader {
    
    public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }
    
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        try {
            // 尝试从已加载的类中查找
            Class<?> loadedClass = findLoadedClass(name);
            if (loadedClass != null) {
                return resolveClass(loadedClass, resolve);
            }
            
            // 对于特定包,优先自己加载
            if (isExcluded(name)) {
                Class<?> found = findClass(name);
                return resolveClass(found, resolve);
            }
            
            // 其他情况委托给父加载器
            try {
                Class<?> found = getParent().loadClass(name);
                return resolveClass(found, resolve);
            } catch (ClassNotFoundException ex) {
                // 父加载器无法加载,尝试自己加载
            }
            
            Class<?> found = findClass(name);
            return resolveClass(found, resolve);
        } catch (ClassNotFoundException ex) {
            throw ex;
        }
    }
    
    private boolean isExcluded(String name) {
        // 判断是否为需要特殊处理的包
        return name.startsWith("org.springframework.boot.");
    }
    
    private Class<?> resolveClass(Class<?> clazz, boolean resolve) {
        if (resolve) {
            resolveClass(clazz);
        }
        return clazz;
    }
}

Spring Boot的类加载器在某些情况下会打破双亲委派模型,以支持特定的功能,如Fat JAR(将依赖打包到一个JAR中)和自动配置。

3.3 Tomcat的类加载机制

Tomcat使用了复杂的多级类加载器结构,以支持Web应用的隔离和热部署:

// Tomcat的WebappClassLoader简化版
public class WebappClassLoader extends URLClassLoader {
    
    private final ClassLoader javaseClassLoader;
    
    public WebappClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
        ClassLoader systemClassLoader = getSystemClassLoader();
        if (systemClassLoader == null) {
            this.javaseClassLoader = null;
        } else {
            this.javaseClassLoader = systemClassLoader.getParent();
        }
    }
    
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 检查JVM缓存的类
        Class<?> clazz = findLoadedClass(name);
        if (clazz != null) {
            return clazz;
        }
        
        // 检查是否为Java SE类
        if (name.startsWith("java.") || name.startsWith("javax.")) {
            try {
                if (javaseClassLoader != null) {
                    clazz = javaseClassLoader.loadClass(name);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // 忽略异常,继续尝试其他加载器
            }
        }
        
        // 检查是否为Servlet API类
        if (name.startsWith("javax.servlet.")) {
            try {
                clazz = getParent().loadClass(name);
                return clazz;
            } catch (ClassNotFoundException e) {
                // 忽略异常,继续尝试其他加载器
            }
        }
        
        // 尝试自己加载
        try {
            clazz = findClass(name);
            return clazz;
        } catch (ClassNotFoundException e) {
            // 忽略异常,继续尝试父加载器
        }
        
        // 最后尝试父加载器
        clazz = getParent().loadClass(name);
        return clazz;
    }
}

Tomcat的类加载器结构设计目标是:

  1. 不同Web应用之间的类隔离

  2. Web应用与Tomcat本身的类隔离

  3. 支持Web应用的热部署和热更新

4. ClassLoader项目中应用

4.1 ClassNotFoundException vs NoClassDefFoundError

这两个异常经常让开发者困惑,它们的区别在于:

  • ClassNotFoundException:当尝试通过反射加载类时,找不到类的定义
  • NoClassDefFoundError:当JVM或ClassLoader尝试加载类的定义时,找不到类的定义文件
// ClassNotFoundException示例
try {
    Class.forName("com.example.NonExistentClass");
} catch (ClassNotFoundException e) {
    System.out.println("ClassNotFoundException: " + e.getMessage());
}

// NoClassDefFoundError示例
public class ErrorDemo {
    // 引用一个不存在的类
    static MissingClass obj;
    
    public static void main(String[] args) {
        try {
            // 访问静态字段会触发类加载
            System.out.println(obj);
        } catch (NoClassDefFoundError e) {
            System.out.println("NoClassDefFoundError: " + e.getMessage());
        }
    }
}

4.2 类加载器内存泄漏问题

类加载器可能导致内存泄漏,特别是在动态创建类加载器的场景:

public class ClassLoaderLeakDemo {
    public static void main(String[] args) throws Exception {
        List<ClassLoader> loaders = new ArrayList<>();
        
        for (int i = 0; i < 1000; i++) {
            // 创建大量类加载器
            URL[] urls = new URL[] { new URL("file:/path/to/classes/") };
            URLClassLoader loader = new URLClassLoader(urls);
            
            // 加载类
            Class<?> clazz = loader.loadClass("com.example.LargeClass");
            Object instance = clazz.newInstance();
            
            // 保持对类加载器的引用,防止GC
            loaders.add(loader);
            
            if (i % 100 == 0) {
                System.out.println("Created " + i + " classloaders");
                System.gc();
                Thread.sleep(100);
            }
        }
    }
}

最佳实践

  1. 不要创建过多的类加载器

  2. 使用完类加载器后,清除所有引用,允许GC回收

  3. 考虑使用弱引用(WeakReference)存储类加载器

4.3 线程上下文类加载器的正确使用

线程上下文类加载器是一种打破双亲委派模型的方式,但使用不当会导致问题:

public class ContextClassLoaderDemo {
    public static void main(String[] args) {
        // 保存原始上下文类加载器
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        
        try {
            // 设置自定义类加载器
            ClassLoader customClassLoader = new URLClassLoader(new URL[] {
                new URL("file:/path/to/classes/")
            });
            Thread.currentThread().setContextClassLoader(customClassLoader);
            
            // 使用上下文类加载器加载资源
            URL resource = Thread.currentThread().getContextClassLoader()
                                .getResource("config.properties");
            System.out.println("Resource: " + resource);
            
            // 使用ServiceLoader(内部使用上下文类加载器)
            ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
            for (MyService service : serviceLoader) {
                service.doSomething();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 恢复原始上下文类加载器
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }
}

最佳实践:

  1. 使用try-finally结构,确保恢复原始类加载器

  2. 不要在全局范围内修改上下文类加载器

  3. 记录类加载器的变更,便于调试

5. 面试中的ClassLoader高频问题

5.1 什么是双亲委派模型?为什么需要它?

答:双亲委派模型是Java类加载器的工作机制,它要求除了顶层的启动类加载器外,其他类加载器都应该有自己的父加载器。当一个类加载器收到类加载请求时,它首先委托父加载器加载,只有当父加载器无法加载时,才尝试自己加载。

双亲委派模型的好处:

  1. 确保Java核心类的安全性:防止用户自定义的类替换Java核心类

  2. 避免类的重复加载:同一个类只会被加载一次

  3. 建立了类加载的层次结构,提高了系统的安全性

5.2 如何自定义ClassLoader?

答:自定义ClassLoader通常需要继承ClassLoader或URLClassLoader,并重写findClass方法:

public class MyClassLoader extends ClassLoader {
    private String classPath;
    
    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 构建类文件的完整路径
            String fileName = classPath + File.separator + 
                              name.replace('.', File.separatorChar) + ".class";
            
            // 读取类文件
            try (FileInputStream fis = new FileInputStream(fileName);
                 ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    baos.write(buffer, 0, bytesRead);
                }
                byte[] classData = baos.toByteArray();
                
                // 将字节码转换为Class对象
                return defineClass(name, classData, 0, classData.length);
            }
        } catch (IOException e) {
            throw new ClassNotFoundException("Could not load class: " + name, e);
        }
    }
}

如果需要打破双亲委派模型,则需要重写loadClass方法。

5.3 ClassLoader在Spring中的应用有哪些?

答:Spring框架中ClassLoader的应用包括:

  1. 类路径扫描:Spring使用ClassLoader加载类路径下的组件
ClassPathScanningCandidateComponentProvider scanner = 
    new ClassPathScanningCandidateComponentProvider(true);
scanner.addIncludeFilter(new AnnotationTypeFilter(Component.class));
Set<BeanDefinition> candidates = scanner.findCandidateComponents("com.example");
  1. 资源加载:Spring的ResourceLoader使用ClassLoader加载资源
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource("classpath:config.xml");
  1. 动态代理生成:Spring AOP使用ClassLoader加载动态生成的代理类
  2. Spring Boot的Fat JAR支持:使用自定义ClassLoader加载嵌套JAR中的类
  3. 热部署支持:Spring Boot DevTools使用两个ClassLoader实现热部署

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

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

相关文章

tryhackme——Enumerating Active Directory

文章目录 一、凭据注入1.1 RUNAS1.2 SYSVOL1.3 IP和主机名 二、通过Microsoft Management Console枚举AD三、通过命令行net命令枚举四、通过powershell枚举 一、凭据注入 1.1 RUNAS 当获得AD凭证<用户名>:<密码>但无法登录域内机器时&#xff0c;runas.exe可帮助…

【Linux学习笔记】系统文件IO之重定向原理分析

【Linux学习笔记】系统文件IO之重定向原理分析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;Linux学习笔记 文章目录 【Linux学习笔记】系统文件IO之重定向原理分析前言一. 系统文件I/01.1 一种传递标志位的方法1.2 hello.c写文件:1.3 he…

SpringBoot中使用MCP和通义千问来处理和分析数据-连接本地数据库并生成实体类

文章目录 前言一、正文1.1 项目结构1.2 项目环境1.3 完整代码1.3.1 spring-mcp-demo的pom文件1.3.2 generate-code-server的pom文件1.3.3 ChatClientConfig1.3.4 FileTemplateConfig1.3.5 ServiceProviderConfig1.3.6 GenerateCodeController1.3.7 Columns1.3.8 Tables1.3.9 Fi…

实现滑动选择器从离散型的数组中选择

1.使用原生的input 详细代码如下&#xff1a; <template><div class"slider-container"><!-- 滑动条 --><inputtype"range"v-model.number"sliderIndex":min"0":max"customValues.length - 1"step&qu…

基于Credit的流量控制

流量控制(Flow Control)&#xff0c;也叫流控&#xff0c;它是控制组件之间发送和接收信息的过程。在总线中&#xff0c;流控的基本单位称为flit。 在标准同步接口中(比如AXI协议接口)&#xff0c;握手信号如果直接采用寄存器打拍的方式容易导致信号在不同的方向上出现偏离。因…

【金仓数据库征文】金仓数据库KingbaseES: 技术优势与实践指南(包含安装)

目录 前言 引言 一 : 关于KingbaseES,他有那些优势呢? 核心特性 典型应用场景 政务信息化 金融核心系统&#xff1a; 能源通信行业&#xff1a; 企业级信息系统&#xff1a; 二: 下载安装KingbaseES 三:目录一览表: 四:常用SQL语句 创建表&#xff1a; 修改表结构…

金丝猴食品:智能中枢AI-COP构建全链路数智化运营体系

“金丝猴奶糖”&#xff0c;这个曾藏在无数人童年口袋里的甜蜜符号&#xff0c;如今正经历一场数智焕新。当传统糖果遇上数字浪潮&#xff0c;这家承载着几代人味蕾记忆的企业&#xff0c;选择以数智化协同运营平台为“新配方”&#xff0c;将童年味道酿成智慧管理的醇香——让…

java的输入输出模板(ACM模式)

文章目录 1、前置准备2、普通输入输出API①、输入API②、输出API 3、快速输入输出API①、BufferedReader②、BufferedWriter 案例题目描述代码 面试有时候要acm模式&#xff0c;刷惯leetcode可能会手生不会acm模式&#xff0c;该文直接通过几个题来熟悉java的输入输出模板&…

鸿蒙 所有API缩略图鉴

从HarmonyOS NEXT Developer Preview1&#xff08;API 11&#xff09;版本开始&#xff0c;HarmonyOS SDK以 Kit 维度提供丰富、完备的开放能力&#xff0c;涵盖应用框架、应用服务、系统、媒体、AI、图形在内的六大领域&#xff0c;共计30000个API

【Docker系列】使用格式化输出与排序技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

基础语法(二)

Mysql基础语法&#xff08;二&#xff09; Mysql基础语法&#xff08;二&#xff09;主要介绍Mysql中稍微进阶一点的内容&#xff0c;会稍微有一些难度&#xff08;博主个人认为&#xff09;。学习完基础语法&#xff08;一&#xff09;和基础语法&#xff08;二&#xff09;之…

TOA的定位,建模与解算的步骤、公式推导

TOA(到达时间)定位的核心是通过测量信号从目标到多个基站的传播时间,将其转换为距离信息,并利用几何关系解算目标位置。本文给出具体的建模与解算步骤及公式推导 文章目录 通用模型建立非线性方程组构建线性化处理(最小二乘法)最大似然估计(ML)高斯-牛顿迭代法误差分析…

2025年PMP 学习七 -第5章 项目范围管理 (5.4,5.5,5.6 )

2025年PMP 学习七 -第5章 项目范围管理 5.4 创建 WBS 1.定义与作用 定义把项目可交付成果和项目工作分解成较小的&#xff0c;更易于管理的组件作用对所要交付的内容提供一个结构化的视图 2.输入&#xff0c;输出&#xff0c;工具与技术 3. 创建WBS的依据&#xff08;输入&…

CAD属性图框值与Excel联动(CAD块属性导出Excel、excel更新CAD块属性)——CAD c#二次开发

CAD插件实现块属性值与excel的互动&#xff0c;效果如下&#xff1a; 加载dll插件&#xff08;CAD 命令行输入netload &#xff0c;运行xx即可导出Excel&#xff0c;运行xx1即可根据excel更新dwg块属性值。&#xff09; 部分代码如下 // 4. 开启事务更新CAD数据using (Transact…

【HarmonyOS 5】鸿蒙中进度条的使用详解

【HarmonyOS 5】鸿蒙中进度条的使用详解 一、HarmonyOS中Progress进度条的类型 HarmonyOS的ArkUI框架为开发者提供了多种类型的进度条&#xff0c;每种类型都有其独特的样式&#xff0c;以满足不同的设计需求。以下是几种常见的进度条类型&#xff1a; 线性进度条&#xff08;…

Spring Cloud: Nacos

Nacos Nacos是阿里巴巴开源的一个服务发现&#xff0c;配置管理和服务管理平台。只要用于分布式系统中的微服务注册&#xff0c;发现和配置管理&#xff0c;nacos是一个注册中心的组件 官方仓库&#xff1a;https://nacos.io/ Nacos的下载 Releases alibaba/nacos 在官网中…

Win11安装APK方法详解

1、官方win11系统 预览版 开发版 正式版 都行 2、同时你还需要开启主板 BIOS 虚拟化选项&#xff08;具体名称不同主板略有不同&#xff09; 这一步自行百度 开始&#xff1a;先去确定有没有开启虚拟化 任务管理器检查—— 虚拟化是否已经开启&#xff0c;如果没有自己去BIO…

SSH终端登录与网络共享

SSH 是较可靠&#xff0c;专为远程登录会话和其他网络服务提供安全性的协议 注意 SSH终端登录的前提是&#xff1a;电脑和板卡都能够通过网络相连接及通信 与连接互联网不一样&#xff0c;SSH可以不用互联网&#xff0c;只要电脑和板卡组成一个小型网络即可 网络方案 如果您…

Android 13 默认打开 使用屏幕键盘

原生设置里&#xff0c;系统-语言和输入法-实体键盘-使用屏幕键盘 选项&#xff0c; 关闭时&#xff0c;外接物理键盘&#xff0c;如USB键盘&#xff0c;输入时不会弹出软键盘。 打开时&#xff0c;外接物理键盘&#xff0c;如USB键盘&#xff0c;输入时会弹出软键盘。 这个选…

操作系统学习笔记第2章 (竟成)

第 2 章 进程管理 【考纲内容】 1.进程与线程&#xff1a; (1) 进程 / 线程的基本概念&#xff1b; (2) 进程 / 线程的状态与转换&#xff1b; (3) 线程的实现&#xff1a;内核支持的线程&#xff1b;线程库支持的线程&#xff1b; (4) 进程与线程的组织与控制&#xff1b; (5)…