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

学习JavaEE的日子 day13补 深入类加载机制及底层

深入类加载机制

在这里插入图片描述

初识类加载过程

使用某个类时,如果该类的class文件没有加载到内存时,则系统会通过以下三个步骤来对该类进行初始化

1.类的加载(Load) → 2.类的连接(Link) → 3.类的初始化(Initialize)

  • 类的加载(Load):将类的class文件读入内存,并为之创建一个java.lang.Class的对象,此过程由类加载器(ClassLoader )完成
  • 类的连接(Link):将类中的数据加载到各个内存区域中
  • 类的初始化(Initialize):JVM负责对类进行初始化

深入类加载过程

类的完整生命周期 :加载、连接(验证、准备、解析)、初始化、使用、卸载

  1. 加载

    1. 通过一个类的全限定名来获取其定义的二进制字节流

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

    3. 在堆中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口

    4. 注意: 相对于类加载过程的其他阶段而言,加载阶段是可控性最强的阶段,因为程序员可以使用系统的类加载器加载,还可以使用自己的类加载器加载,在这里我们只需要知道类加载器的作用就是上面虚拟机需要完成的三件事

  2. 连接

    1. 验证

      1. 文件格式的验证:验证.class文件字节流是否符合class文件的格式的规范,并且能够被当前版本的虚拟机处理

      2. 元数据验证:主要是对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求,比如说验证这个类是不是有父类,类中的字段方法是不是和父类冲突等等。

      3. 字节码验证:这是整个验证过程最复杂的阶段,主要是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。在元数据验证阶段对数据类型做出验证后,这个阶段主要对类的方法做出分析,保证类的方法在运行时不会做出威胁虚拟机安全的事。

      4. 符号引用验证:它是验证的最后一个阶段,发生在虚拟机将符号引用转化为直接引用的时候。主要是对类自身以外 (常量池中的各种符号引用) 的信息进行校验。目的是确保解析动作能够完成。

      5. 注意: 对整个类加载机制而言,验证阶段是一个很重要但是非必需的阶段,如果我们的代码能够确保没有问题,那么我们就没有必要去验证,毕竟验证需要花费一定的的时间。当然我们可以使用

        -Xverfty:none来关闭大部分的验证。

    2. 准备 - 重要

      准备阶段主要为类变量(static)分配内存并设置初始值。这些内存都在方法区分配。在这个阶段我们只需要注意两点就好了,类变量和初始值两个关键词:

      1. 类变量(static):会分配内存,但不会对应的分配值,其次实例变量不会分配空间,因为实例变量主要随着对象的实例化一块分配到java堆内存中

      2. 初始值:这里的初始值指的是数据类型默认值,而不是代码中被显示赋予的值

        比如1:public static int value = 1;

        在这里准备阶段过后的value值为0,而不是1赋值为1的动作在初始化阶段

        比如2:public static final int value = 1;

        同时被final和static修饰准备阶段之后就是1了,因为static final在编译器就将结果放入调用它的类的常量池中

    3. 解析

      解析阶段主要是虚拟机将常量池中的符号引用转化为直接引用的过程

      1. 符号引用:以一组符号来描述所引用的目标,可以是任何形式的字面量,只要是能无歧义的定位到目标就好,就好比在班级中,老师可以用张三来代表你,也可以用你的学号来代表你,但无论任何方式这些都只是一个代号(符号),这个代号指向你(符号引用)
      2. 直接引用:直接引用是可以指向目标的指针、相对偏移量或者是一个能直接或间接定位到目标的句柄。和虚拟机实现的内存有关,不同的虚拟机直接引用一般不同
      3. 补充: 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行
    4. 初始化

      这是类加载机制的最后一步,在这个阶段,java程序代码才开始真正执行。在准备阶段已经为类变量赋过一次值。在初始化阶端,程序员可以根据自己的需求来赋值了。一句话描述这个阶段就是执行类构造器clinit()方法的过程。

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

      1. 声明类变量是指定初始值
      2. 使用静态代码块为类变量指定初始值

      补充:clinit() 方法具有以下特点:

      1. 由编译器自动收集类中所有类变量(static)的赋值动作和静态语句块(static{} 块)中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。例如以下代码
      class Test {static {i = 0;                // 给变量赋值可以正常编译通过System.out.print(i);  // 这句编译器会报错,提示“非法向前引用”}static int i = 1;
      }
      
      1. 与类的构造函数(或者说实例构造器 init())不同,不需要显式的调用父类的构造器。虚拟机会自动保证在子类的 clinit() 方法运行之前,父类的 clinit() 方法已经执行结束。因此虚拟机中第一个执行 clinit() 方法的类肯定为 java.lang.Object。由于父类的 clinit() 方法先执行,也就意味着父类中定义的静态语句块要优于子类的变量赋值操作。例如以下代码:
      public class Test {public static void main(String[] args) {System.out.println(Son.B);//输出结果是父类中的静态变量A的值,也就是2}
      }
      class Father{public static int A = 1;static {System.out.println("a");A = 2;}
      }
      class son extends Father {public static int B = A;}
      
      1. clinit() 方法对于类或接口不是必须的,如果一个类中不包含静态语句块,也没有对类变量的赋值操作,编译器可以不为该类生成 clinit() 方法。

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

      3. 虚拟机会保证一个类的 clinit() 方法在多线程环境下被正确的加锁和同步,如果多个线程同时初始化一个类,只会有一个线程执行这个类的 clinit() 方法,其它线程都会阻塞等待,直到活动线程执行 clinit() 方法完毕。如果在一个类的 clinit() 方法中有耗时的操作,就可能造成多个线程阻塞,在实际过程中此种阻塞很隐蔽。

      4. JVM初始化步骤:

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

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

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

      比如:测试类Test

  3. 使用: 当 JVM 完成初始化阶段之后,JVM 便开始从入口方法开始执行用户的程序代码

  4. 卸载: 当用户程序代码执行完毕后,JVM 便开始销毁创建的 Class 对象,最后负责运行的 JVM 也退出内存

利用类加载过程理解面试题

public class Test {public static void main(String[] args) {A a = A.getInstance();System.out.println("A value1:" + a.value1);//1System.out.println("A value2:" + a.value2);//0B b = B.getInstance();System.out.println("B value1:" + b.value1);//1System.out.println("B value2:" + b.value2);//1}
}
class A{private static A a = new A();public static int value1;	public static int value2 = 0;private A(){value1++;value2++;}public static A getInstance(){return a;}
}
class B{public static int value1;public static int value2 = 0;private static B b = new B();private B(){value1++;value2++;}public static B getInstance(){return b;}}

类加载器

类加载器实现的功能是即为加载阶段获取二进制字节流的时候,在 Java 虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。

类加载器分类

从 Java 虚拟机的角度来讲,只存在以下两种不同的类加载器:

  1. 启动类加载器(Bootstrap ClassLoader):这个类加载器用 C++ 实现,是虚拟机自身的一部分
  2. 所有其他类的加载器,这些类由 Java 实现,独立于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader

从 Java 开发人员的角度看,类加载器可以划分得更细致一些:

  1. 启动类加载器(Bootstrap ClassLoader): 最顶层的类加载器,负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
  2. 扩展类加载器(Extension ClassLoader)这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。 负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库
  3. 应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。 也叫做系统类加载器,可以通过getSystemClassLoader()获取,负责加载用户路径(classpath)上的类库。如果没有自定义类加载器,一般这个就是默认的类加载器。

类加载器之间的层次关系

启动类加载器 > 扩展类加载器 > 应用程序类加载器 > 自定义类加载器

public class ClassLoaderTest {public static void main(String[] args) {Thread thread = new Thread();ClassLoader appClassLoader = thread.getContextClassLoader();ClassLoader extClassLoader = appClassLoader.getParent();ClassLoader booClassLoader = extClassLoader.getParent();//sun.misc.Launcher$AppClassLoader@73d16e93System.out.println("应用程序类加载器:" + appClassLoader);//sun.misc.Launcher$ExtClassLoader@15db9742System.out.println("扩展类加载:" + extClassLoader);//nullSystem.out.println("启动类加载器:" + booClassLoader);/*没有获取到ExtClassLoader的父Loader,原因是Bootstrap Loader(引导类加载器)是用C语			   言实现的,找不到一个确定的返回父Loader的方式,于是就返回null。*/}
}

双亲委派模型 - 概念

类加载器之间的这种层次关系叫做双亲委派模型。
双亲委派模型要求除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不是以继承关系实现的,而是用组合实现的。

双亲委派模型 - 工作过程

一个类加载器接受到类加载请求,他自己不会去加载这个请求,而是将这个类加载请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器(Bootstrap ClassLoader)。
只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载。

双亲委派模型 - 好处

使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。

双亲委派模型很好的解决了各个类加载器加载基础类的统一性问题。即越基础的类由越上层的加载器进行加载

比如: java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 的类并放到 ClassPath 中,程序可以编译通过。因为双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 Clas sPath 中的 Object 优先级更高,因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。正因为 rt.jar 中的 Object 优先级更高,因为程序中所有的 Object 都是这个 Object。

双亲委派原则

  1. 可以避免重复加载,父类已经加载了,子类就不需要再次加载
  2. 更加安全,很好的解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心api,会带来相关隐患。

双亲委派模型的代码实现

双亲委派模型的代码实现集中在java.lang.ClassLoader的loadClass()方法当中

1. 检查类是否被加载,没有则调用父类加载器的loadClass()方法;2. 若父类加载器为空,则默认使用启动类加载器作为父加载器;
3. 若父类加载失败,抛出ClassNotFoundException 异常后,再调用自己的findClass() 方法。 
//loadClass()源代码
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {//首先检查类是否被加载Class c = findLoadedClass(name);if (c == null) {//如果没有被加载try {if (parent != null) {//存在父类//则调用父类加载器的loadClass()方法;c = parent.loadClass(name, false);} else {//不存在你父类//则默认使用启动类加载器作为父加载器;c = findBootstrapClass0(name);}} catch (ClassNotFoundException e) {//若父类加载失败,抛出ClassNotFoundException异常后//调用自身的加载功能,一般自定义类重写此方法c = findClass(name);}}if (resolve) {//是否初始化//再调用自己的findClass() 方法。resolveClass(c);}return c;
}

自定义类加载器

首先,我们定义一个待加载的普通Java类:Test.java。放在com.dream.test包下:

package com.dream.test;public class Person {private String name;public Person() {}public Person(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Person [name=" + name + "]";}
}

注意:

如果你是直接在当前项目里面创建,待Test.java编译后,请把Test.class文件拷贝走,再将Test.java`删除。因为如果Test.class存放在当前项目中,根据双亲委派模型可知,会通过sun.misc.Launcher$AppClassLoader类加载器加载。为了让我们自定义的类加载器加载,我们把Test.class文件放入到其他目录。

在本例中,我们Person.class文件存放的目录如下: C:\code\com\dream\test\Person.class

接下来就是自定义我们的类加载器 :

package com.dream.test;import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;public class ClassLoadTest {public static void main(String[] args) throws Exception {//创建自定义类加载器对象MyClassLoader classLoader = new MyClassLoader("C:/code");//通过类的全路径获取该类的字节码文件对象Class<?> c = classLoader.loadClass("com.dream.test.Person");//创建对象Object obj = c.newInstance();System.out.println(obj);System.out.println(obj.getClass().getClassLoader());//com.dream.test.MyClassLoader@6d06d69c}
}class MyClassLoader extends ClassLoader{private String classPath;public MyClassLoader(){}public MyClassLoader(String classPath) {this.classPath = classPath;}//将"com.dream.test.Person"转换为 Class对象	 @Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String replaceAll = name.replaceAll("\\.", "/");//com\dream\test\PersonFile file = new File(classPath,replaceAll + ".class");//C:\code\com\dream\test\Person.classtry {//将文件转换为字节数组byte[] bytes = getClassBytes(file);//将字节数组转换为Class对象Class<?> c = this.defineClass(name,bytes, 0, bytes.length);return c;} catch (Exception e) {e.printStackTrace();}return super.findClass(name);}//读取xxx.class文件,把该文件的内容以字节数组返回public byte[] getClassBytes(File file) throws Exception{BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buf = new byte[1024];int len;while((len=bis.read(buf)) != -1){baos.write(buf, 0, len);}bis.close();return baos.toByteArray();}
}

自定义类加载器的应用场景

引入:Tomcat容器,每个WebApp有自己的ClassLoader,加载每个WebApp的ClassPath路径上的类,一旦遇到Tomcat自带的Jar包就委托给CommonClassLoader加载

  1. 加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载。
  2. 从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载器,从指定的来源加载类。

以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。

相关文章:

学习JavaEE的日子 day13补 深入类加载机制及底层

深入类加载机制 初识类加载过程 使用某个类时&#xff0c;如果该类的class文件没有加载到内存时&#xff0c;则系统会通过以下三个步骤来对该类进行初始化 1.类的加载&#xff08;Load&#xff09; → 2.类的连接&#xff08;Link&#xff09; → 3.类的初始化&#xff08;In…...

C# WebApi传参及Postman调试

概述 欢迎来到本文&#xff0c;本篇文章将会探讨C# WebApi中传递参数的方法。在WebApi中&#xff0c;参数传递是一个非常重要的概念&#xff0c;因为它使得我们能够从客户端获取数据&#xff0c;并将数据传递到服务器端进行处理。WebApi是一种使用HTTP协议进行通信的RESTful服…...

npm install 卡住不动的六种解决方法

1.重装 检查网络设置&#xff0c;删除node_modules重新npm install 2. 配置npm代理 // 配置nmp代理来提高速度&#xff0c;如设置淘宝镜像 npm config set registry https://registry.npm.taobao.org// 查看配置是否成功 npm config get registry// 成功后重新npm install安…...

Vue高级(二)

3.搭建vuex环境 创建文件&#xff1a;src/store/index.js //引入Vue核心库import Vue from vue//引入Vueximport Vuex from vuex//应用Vuex插件Vue.use(Vuex)//准备actions对象——响应组件中用户的动作const actions {}//准备mutations对象——修改state中的数据const mutat…...

MongoDB面试系列-02

1. MongoDB 中必须调用 getLastError 来确保写操作生效吗&#xff1f; MongoDB中不管有没有调用getLastError&#xff08;又称为Safe Mode&#xff09;&#xff0c;服务器执行的操作都会一样。 而调用getLastError只是为了确认写操作是否成功提交&#xff0c;但是写操作的安全…...

2024.1.17

今天我已经回家了&#xff0c;感觉家就像我的温柔乡一样&#xff0c;一到了家&#xff0c;就不想学习了&#xff0c;这是很不对的事情&#xff0c;不该如此堕落&#xff0c;还是要像在学校一样该干什么干什么&#xff0c;所以说还是复习和写了一下曾经写过的代码。 #define _C…...

openssl3.2 - 官方demo学习 - encrypt - rsa_encrypt.c

文章目录 openssl3.2 - 官方demo学习 - encrypt - rsa_encrypt.c概述笔记END openssl3.2 - 官方demo学习 - encrypt - rsa_encrypt.c 概述 从内存中的DER共钥数据构造pub_key, 用公钥加密明文, 输出密文. 非对称加密 从内存中的DER私钥数据构造priv_key, 用私钥解密密文, 输出…...

ARCGIS PRO SDK Annotation 概念及操作

使用Annotation的API功能。Annotation 的API功能位于ArcGIS.Core.dll中。Annotation API通常与地理数据库、地图创作和编辑结合使用。ArcGIS.Core.dll ArcGIS.Core.Data.map API中的几乎所有方法都应该在MCT上调用。 一、Annotation featureclass 1、从GeodatabaseGeodatabase数…...

dp专题13 零钱兑换II

本题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 根据题意&#xff0c;这是一道很裸的背包问题&#xff0c;其中这里是返回 背包方案数 的。 我们可以直接推出公式 &#xff1a; dp [ j ] dp[ j - coins[ i ] ] 在我之前…...

el-dialog嵌套使用,只显示遮罩层的问题

直接上解决方法 <!-- 错误写法 --><el-dialog><el-dialog></el-dialog></el-dialog><!-- 正确写法 --><el-dialog></el-dialog><el-dialog></el-dialog>我是不建议嵌套使用的&#xff0c;平级也能调用&#xff0c…...

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例3-5 CSS3 动画

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>CSS3 动画</title> <style> .img {width: 150px; } keyframes rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg);} } img…...

一款实用的.NET Core加密解密工具类库

前言 在我们日常开发工作中&#xff0c;为了数据安全问题对数据加密、解密是必不可少的。加密方式有很多种如常见的AES&#xff0c;RSA&#xff0c;MD5&#xff0c;SAH1&#xff0c;SAH256&#xff0c;DES等&#xff0c;这时候假如我们有一个封装的对应加密解密工具类可以直接…...

C/C++内存布局

1. C 结构体的内存布局 以一个例子来看struct的内存结构 #define NP_FUNC_WRAPPER __attribute__((optimize(0)))struct StructBody {int first_int_placeholder;int second_int_placeholder;double third_double_placeholder; };class ClassBody {public:int first_int_place…...

springboot(ssm母婴全程服务管理系统 母婴用品服务商城Java系统

springboot(ssm母婴全程服务管理系统 母婴用品服务商城Java系统 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&#xf…...

修改SSH默认端口,使SSH连接更安全

以CentOS7.9为例&#xff1a; 1、修改配置文件 vi /etc/ssh/sshd_config 2、远程电脑可连接&#xff0c;暂时将SELinux关闭 # 查询状态 getenforce # 关闭 setenforce 0 # 开启 setenforce 1 3、SELinux设置&#xff08;如果启用&#xff09;&#xff0c;semanage管理工具安…...

React16源码: React中调度之requestWork的源码实现

requestWork 1 &#xff09;概述 在 scheduleWork 中&#xff0c;找到了创建更新的fiber对应的root节点然后对它进行了一些操作之后&#xff0c;调用了 requestWork&#xff0c;开始请求工作在 requestWork 里面它会做哪些东西呢&#xff1f; 首先我们要把这个root节点加入到调…...

【白话机器学习的数学】读书笔记(3)学习分类(感知机、逻辑回归)

三、学习分类 1.分类的目的 找到一条线把白点和黑点分开。这条直线是使权重向量成为法线向量的直线。(解释见下图) 直线的表达式为&#xff1a; ω ⋅ x ∑ i 1 n ω i ⋅ x i 0 \omegax \sum_{i1}^n\omega_i x_i 0 ω⋅xi1∑n​ωi​⋅xi​0 ω \omega ω是权重向量权…...

书生·浦语大模型实战营-学习笔记3

目录 (3)基于 InternLM 和 LangChain 搭建你的知识库1. 大模型开发范式&#xff08;RAG、Fine-tune&#xff09;RAG微调 &#xff08;传统自然语言处理的方法&#xff09; 2. LangChain简介&#xff08;RAG开发框架&#xff09;3. 构建向量数据库4. 搭建知识库助手5. Web Demo部…...

MySQL下对[库]的操作

目录 创建数据库 创建一个数据库案例&#xff1a; 字符集和校验规则&#xff1a; 默认字符集&#xff1a; 默认校验规则&#xff1a; 查看数据库支持的字符集&#xff1a; 查看数据库支持的字符集校验规则&#xff1a; 校验规则对数据库的影响&#xff1a; 操作数据…...

Django(七)

1.靓号管理 1.1 表结构 根据表结构的需求&#xff0c;在models.py中创建类&#xff08;由类生成数据库中的表&#xff09;。 class PrettyNum(models.Model):""" 靓号表 """mobile models.CharField(verbose_name"手机号", max_len…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...