Android Jni的介绍和简单Demo实现

news2025/7/20 18:52:20

Android Jni的介绍和简单Demo实现

文章目录

  • Android Jni的介绍和简单Demo实现
    • 一、JNI的简单介绍
      • JNI
      • NDK
      • Jni的开发背景:
      • **JNI在 Android 开发里的主要应用场景:**
    • 二、JNI的简单Demo
      • 1、Demo主要界面和效果展示
      • 2、CMake编译加载文件
        • add_library 指令的加载库说明:
        • find_library 指令
        • target_link_libraries 指令说明
      • 3、MainActivity.java
      • 4、native-lib.cpp
      • 5、Jni引用其他cpp文件代码
        • 1、定义TestCPlus.h 文件
        • 2、TestCPlus.cpp
        • 3、native-lib.cpp文件中调用
        • 同时,要加载的cpp文件也别忘了在CMakeList.txt中进行声明
    • 三、其他
      • 1、Jni基础的几点内容
      • 2、java数据类型与jni类型映射表
      • 3、Java签名类型 常用的数据类型及对应字符:
      • 4、JNI类型api调用表格
        • 方法、变量修饰类型表格
        • 数组对应变量类型api表格
  • 共勉:不用辜负每一天的时光。

一、JNI的简单介绍

JNI

JNI 全程:JNI(Java Native Interface),通俗翻译:Java本地方法
官方说法:提供一种Java字节码调用C/C++的解决方案,JNI描述的是一种技术。

所以这里的Nativie的本地的意思就是C/C++,所以JNI通俗理解就是Java调用C/C++的方案技术。

NDK

NDK(Native Development Kit),通俗翻译:本地发展(扩展)工具
Android NDK 是一组允许您将 C 或 C++(“原生代码”)嵌入到 Android 应用中的工具,NDK描述的是工具集。

同样,把这里的Native理解成C/C++,那么NDK的简单理解就是能把C/C++编译成Java识别的工具模块。

Android Studio中已经集成了NDK,所以才可以在Java代码中很方便就可以调用到C/C++的代码。

网上有些示例是用NDK命令来编译cpp生成so,并调用测试,这里不做介绍。

Jni的开发背景:

需要调用Java语言不支持的依赖于操作系统平台特性的一些功能,比如

● 需要调用当前UNIX系统的某个功能,而Java不支持这个功能的时候,就要用到JNI
● 在程序对时间敏感或对性能要求特别高时,有必要用到更底层的语言来提高运行效率
● 音视频开发涉及到的音视频编解码需要更快的处理速度,这就需要用到JNI
● 为了整合一些以前的非Java语言开发的系统
● 需要用到早期实现的C/C++语言开发的一些功能或者系统,将这些功能整合到当前的系统或者新的版本中

其实就是为了调用C/C++代码

JNI是完善Java的一个重要功能,它让Java更加全面、封装了各个平台的差异性

JNI在 Android 开发里的主要应用场景:

● 音视频开发
● 热修复
● 插件化
● 逆向开发
● 等等…

这些都是比较复杂的模块,很多实现Java代码无法实现或者使用Java代码实现会很低效的情况。

所以这些模块开发的就要提前掌握Jni相关技术才能实现复杂功能。

其实这些概念,看一百遍也是很容易忘记的,只要记住一点就行了:

Jni就是Java为了调用C语言的技术,其中过程用到了NDK相关工具。

java-jni-c/c++ 关系图也是比较简单明了的:

在这里插入图片描述

本文将介绍Jni相关的基础知识,对jni有了记录了解后,后续还会再写一些Jni 进阶的文章。

Jni基础很简单,比如:Java 代码中加载so库,定义native方法,jni代码中执行简单的实现,相信很多人都是会的;

Jni的进阶知识:jni添加日志,复制对象的调用,C++调用Java方法,Jni方法的动态注册和静态注册,Jni报错分析等等,这些都是有一定的难度的,经过一定的学习了解就可以掌握了。

这些Jni相关知识的学习,不需要系统源码环境,只需要电脑安装Android Studio,安装模拟器或者有安卓真机调试验证就可以了。

Jni在系统源码环境中也是有很多相关的代码和使用场景,

如果是入门学习,优先使用Android Stduio 创建的项目会好入手很多,

本文以及后续的分析学习文章都是基于非源码环境中,大部分人都可以进行学习和了解。

本文讲解jni基础部分还是比较全面和清晰的,有兴趣的可以先收藏。

二、JNI的简单Demo

这里以Android Studio创建Jni项目,并且提前安装了NDK相关工具。

1、Demo主要界面和效果展示

在Android Studio 中新建一个项目
如下图所示:

在这里插入图片描述

完成后,即可创建一个最简单的JNI项目。
可以直接点击运行在模拟器上或者连接的Android设备,即调用了CPP文件返回字符串并在Android界面显示Hello World,项目代码就包含了一个最基本的jni架构代码。

本文Jni 示例Demo实现加减乘除运算,以及字符串拼接,如下图所示:

在这里插入图片描述

里面具体实现都是在cpp代码中实现的。

这个JNI项目涉及的主要代码入下:

2、CMake编译加载文件

CMakeLists.txt 大致内容如下:

cmake_minimum_required(VERSION 3.18.1) //版本
project("jnidemo") //加载的项目

//至少三个参数,参数直接没有标点符号哦,用换行隔开
第一个参数表示加载的库文件(动态.so/静态.a)
第二个参数表示库加载的方式,有动态SHARED,有静态STATIC(极少用)
后面的参数可以N个,表示要加载的CPP文件,也可以用集合指向
add_library(
        jnidemo
        SHARED
        native-lib.cpp
        TestCPlus.cpp
)

//下面两个是关联的,简单示例中不写也是可以正常运行的
find_library(
        log-libxx
        log)

target_link_libraries(
        jnidemo
        ${log-libxx})
add_library 指令的加载库说明:

SHARED: 表示动态库,可以在(Java)代码中使用 System.loadLibrary(name) 动态调用;
STATIC: 表示静态库,集成到代码中会在编译时调用;

也就是说动态加载,只有进入那个类并且执行System.loadLibrary,才把库文件加载进来;
而静态加载会在应用编译的时候就把CPP的代码编译成虚拟机能识别的语句放到apk。

所以动态加载的库文件可以在应用运行中使用一定的手段进行替换,但是静态加载的库却不行。

find_library 指令

语法:find_library( name1 path1 path2 …)
VAR 变量表示找到的库全路径,包含库文件名 。例如:

find_library(libX X11 /usr/lib) 
find_library(log-lib log) #路径为空,应该是查找系统环境变量路径

find_library可以多个,并且给下面的 target_link_libraries 连接使用。

target_link_libraries 指令说明

语法:target_link_libraries(target library <debug | optimized> library2…)
这个指令可以用来为 target 添加需要的链接的共享库(可以多个),同样也可以用于为自己编写的共享库添加
共享库链接。

比如下面示例:

#指定 compress 工程需要用到 libjpeg 库和 log 库 
target_link_libraries(compress libjpeg ${log-lib})

3、MainActivity.java

public class MainActivity extends AppCompatActivity {
	
	//加载so库名称的代码
   // Used to load the 'jnidemo' library on application startup.
    static {
        System.loadLibrary("jnidemo");
    }


    //界面显示代码省略


    //jni方法,加减乘除,最后一个参数是操作类型的字节数据类型
    public native float operationNumberFromJNI(float parameter1, float parameter2, char type);


    //jni方法,字符串拼接
    public native String addStringFromCPlusJNI(String parameter1, String parameter2);

4、native-lib.cpp

#include <jni.h>
#include <string>


//创建Jni工程最简单的方法,返回一个字符串
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "简易计算器";

    return env->NewStringUTF(hello.c_str());
}



//Jni增删改查的实现,因为float是基本数据类型,所以不需要转换就可以直接操作
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_example_jnidemo_MainActivity_operationNumberFromJNI(JNIEnv *env, jobject thiz, jfloat parameter1,
                                                                  jfloat parameter2,jchar type) {
    switch (type) {
        case '+':
            return parameter1 + parameter2;
        case '-':
            return parameter1 - parameter2;
        case '*':
            return parameter1 * parameter2;
        case '/':
            return parameter1 / parameter2;
    }
    return parameter1 + parameter2;
}


//字符串的拼接,方法有N种,String不是基本数据类型,需要进行类型转换后才能进行操作
extern "C" JNIEXPORT _jstring * JNICALL
Java_com_example_jnidemo_MainActivity_addStringFromCPlusJNI(JNIEnv *env, jobject thiz,
                                                            jstring parameter1,
                                                            jstring parameter2) {
    char *c1 = (char *) (env->GetStringUTFChars(parameter1, JNI_FALSE));
    char *c2 = (char *) (env->GetStringUTFChars(parameter2, JNI_FALSE));
    char *res = strcat(c1, c2); //拼接两个字符串
    //释放c1和c2
    delete(c1);
    delete(c2);

    return env->NewStringUTF(res);


}

上面Jni的方法名也是有一定规则的,网上很多使用NDK工具生成的,
没啥必要了使用命令工具那些,手写就行了,现在新的Studio也是会自动生成的。

Java_com_example_jnidemo_MainActivity_addStringFromCPlusJNI 方法名的大致规则:

Java_包名(中间的点.替换成下划线_)_类名_方法名

cpp文件加上CMakeList.txt就会编译出so文件,目录在:
app\build\intermediates\cmake\debug\obj\XXX[libjnidemo.so

xxx表示arm64-v8a,armeabi-v7a,x86,x86_64

5、Jni引用其他cpp文件代码

之前没怎么写过cpp文件,还不知道怎么引用其他文件的类的使用,后面发现也不难。

比如,native-lib.cpp需要调用TestCPlus.cpp的add方法

1、定义TestCPlus.h 文件
class TestCPlus {

//定义变量和方法
private:
    int number;
public:
    int add(int parameter1,int parameter2); //定义方法

};
2、TestCPlus.cpp
#include "TestCPlus.h" //添加头文件


//实现需要的方法
int TestCPlus::add(int parameter1, int parameter2) {
    int result = parameter1 + parameter2;
    return result;
}
3、native-lib.cpp文件中调用

#include “TestCPlus.h” //添加头文件声明

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_MainActivity_addIntFromCPlusJNI(JNIEnv *env, jobject thiz, jint parameter1,
                                                         jint parameter2) {
    TestCPlus *testCPlus;
    return testCPlus->add(parameter1, parameter2);
//    return parameter1 + parameter2;
}

cpp文件的调用还有很多种方法,这里就不一一说明了。
这样就能在Java文件中调用native-lib.cpp再调用到别的cpp文件了。

同时,要加载的cpp文件也别忘了在CMakeList.txt中进行声明

根据上面的基础知识的学习,就知道如下修改就可以:

add_library(
        # Sets the name of the library.
        jnidemo
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        native-lib.cpp
        TestCPlus.cpp
)

这里添加编译 TestCPlus.cpp就可以,因为自身的.h头文件,系统会自动识别导入,如果这里没添加声明直接调用会编译不通过。

一个简单的JniDemo就介绍到这里,Demo源码:
https://download.csdn.net/download/wenzhi20102321/86239831

三、其他

1、Jni基础的几点内容

1、记住编写关键流程
    类的静态方法中loadLibrary
    类中定义native方法
    类的使用中调用jni方法
    cpp文件中编写jni实现

2、CMakeList.txt 的基本规则

3、jni.cpp文件中的方法命名

4、Java到jni.cpp文件中类型转换

5、有一定的c/c++语言基础

类型转换是需要记忆一下的,
比如Java的int 在jni的cpp文件中是jint,
Java中的String 在jni的cpp文件是jstring
jni.cpp文件中的数据类型是特殊定义的,定义了不同与Java和c的类型标识。

基本类型是可以直接透传的,比如int类型数据,Java(int)–>Jni(jint)–>cpp(int)
可以直接从Java文件传入到jni.cpp文件,在传入到别的c/cpp文件进行使用

但是非基本类型的数据是要转换后才能使用的,比如字符串String,数组int[]等数据
Java(String)–>Jni(jstring)–>c/c++(指针或char数组)

大致数据类型映射关系如下,看几遍基本能记住,复杂的再进行细查即可。

2、java数据类型与jni类型映射表

Java 类型JNI本地类型描述
booleanjbooleanC/C++8位整型
bytejbyteC/C++带符号的8位整型
charjcharC/C++无符号的16位整型
shortjshortC/C++带符号的16位整型
intjintC/C++带符号的32位整型
longjlongC/C++带符号的64位整型e
floatjfloatC/C++32位浮点型
doublejdoubleC/C++64位浮点型
Objectjobject任何Java对象,或者没有对应java类型的对象
ClassjclassClass对象
Stringjstring字符串对象
Object[]jobjectArray任何对象的数组
boolean[]jbooleanArray布尔型数组
byte[]jbyteArray比特型数组
char[]jcharArray字符型数组
short[]jshortArray短整型数组
int[]jintArray整型数组
long[]jlongArray长整型数组
float[]jfloatArray浮点型数组
double[]jdoubleArray双浮点型数组

这个表格是有有啥用?很多人初学就会很懵逼。Jni使用过程会两三个表格的转换关系和要清楚具体使用场景。

上面这个表格就是让Java的数据类型转换成Jni的数据类型,比如示例代码中的:

    //XXX.java 定义的 jni方法
    public native float operationNumberFromJNI(float parameter1, float parameter2, char type);
    
    //Jni.cpp文件中的对应方法
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_example_jnidemo_MainActivity_operationNumberFromJNI(JNIEnv *env, jobject thiz, jfloat parameter1,jfloat parameter2,jchar type) {//看后面三个参数
 。。。
    return XXX;
}

Java代码和Jni代码同一个方法是有不同的显示形式,就需要上面那个表格对照转换,有些不清楚的类型就可以对照表格进行转换。

java数据类型与jni类型 转换总结:

 1、java中的返回值void与jni中的void是完全对应的。
 2、java中的基本数据类型(byte,short,int,long,float,double,boolean,char)
 在jni中对应的数据类型在前面加上j
 (jbyte,jshort,jint,jlong,jfloat,jdouble,jboolean,jchar)。
 3、java中的对象,包括类库中定义的类、接口,都对应jni中的jobject。
 4、java中基本数据类型的数组对应与jni中的jXXXArray类型(type就是上面说的8中基本数据类型)。
 5、java中对象的数组对应于jni中jobjectArray类型。
 

3、Java签名类型 常用的数据类型及对应字符:

Java 类型Jni中表示的符号备注
booleanZ不是类型首字母大写
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
objects对象Lfully-qualified-class-name;L全类名;记得最后是有分号的
Arrays数组[array-type [数组类型
methods方法(argument-types)return-type(参数类型)返回类型

这个表格是有有啥用?就更多人懵逼了。

其实这些类型符号表示的是Java方法或者属性的一个签名,唯一性,目前就是为了让Jni.cpp调用到Java代码。

举个例子就很容易清楚了:

//XXX.Java
  int age;
  String name;
  public  int add(int number1,int number2){
        System.out.println("c/C++居然调用了我");
        return number1+number2;
    }

//jni.cpp 修改Java属性值和调用Java方法示例

//获取类对象
jclass mainActivityCls=env->FindClass("com/zmw/jnitest/MainActivity");

//获取属性的fieldId,--》这里就用到了签名类型
jfieldID ageFid = env->GetFieldID(mainActivityCls,"age","I");
jfieldID nameFid=env->GetFieldID(mainActivityCls, "name", "Ljava/lang/String;");
//获取属性值
jint  age = env->GetIntField(mainActivityThis,ageFid);
jstring name = (jstring)env->GetObjectField(thiz,nameFid);//此处有编码转换问题未解决

//修改属性值,C++中修改变量值后,Java重新获取打印发现是修改过的
env->SetIntField(mainActivityThis, ageFid , 11);
env->SetObjectField(thiz, nameFid,Stringvalue);

//获取方法的methodId,--》这里就用到了签名类型
jmethodID addMid=env->GetMethodID(mainActivityCls, "add", "(II)I");
int result=env->CallIntMethod(mainActivityThis, addMid, 1, 1); //这里就能获取到2的值。

仔细看一下上面的代码,就大致能理解这个签名表格的具体作用:为了找到Java方法的参数和返回值的形式。

Java签名类型小结:

(1)基础类型签名那些转换都是很容易记住的,基础类型中,特别留意一下boolean类型 是 Z 就行
(2)对象Object类型的转换是:L+全包名(包名直接用 /间隔)+类名+分号
(3)数组类型签名转换:[数组类型,比如[I,表示Java的 int[]

(4)方法签名的转换:(参数类型)返回类型,中间多个参数类型依此填写就行,
比如:Jni中的代码:env->GetMethodID("add", "(IILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")
如果不清楚上面的表格转换,看起来就头大,特别是那些有三四个以上参数的情况,但是学习过后就不难了,
查看表格对应关系可以知道,Java中的对应方法是:public String add(int a,int b,String c,String d)
其实就是先看括号后面的返回值,然后再一个个确定括号内的形参变量

还有一些比较复杂的,比如如何使用cpp调用到Java的类具体过程那些,

本文就不详细介绍先,可以看下面这篇文章:
https://blog.csdn.net/pfgmylove/article/details/7052839

上面已经介绍了两个表格,其实还有个Jni里面的api接口需要记忆一下:

4、JNI类型api调用表格

方法、变量修饰类型表格
函数描述描述
GetFieldID得到一个实例的域的ID
GetStaticFieldID得到一个静态的域的ID
GetMethodID得到一个实例的方法的ID
GetStaticMethodID得到一个静态方法的ID

上面Jni.cpp调用Java代码已经用到部分api方法,并且从字面含义也是比较容易里面这个表格的api的具体作用。

这个表格的用于就是为了获取到方法的修饰类型,比如方法,静态方法,变量,静态变量。

毕竟不同的修饰类型,在编译过程是有差异的。所以要区分。

还有一个Jni api的表格虽然不是很常用,但是有些数组对象场景要是要用到的,表格内容如下:

数组对应变量类型api表格
函数描述nativie数据对象nativie类型Java数据类型
GetBooleanArrayElementsjbooleanArrayjbooleanboolean[]
GetByteArrayElementsjbyteArrayjbytebyte[]
GetCharArrayElementsjcharArrayjcharchar[]
GetShortArrayElementsjshortArrayjshortshort[]
GetIntArrayElementsjintArrayjintint[]
GetLongArrayElementsjlongArrayjlonglong[]
GetFloatArrayElementsjfloatArrayjfloatfloat[]
GetDoubleArrayElementsjdoubleArrayjdoubledouble[]

以下面一段代码理解一下上面api的用法,

Java代码:
public byte[] getBytes(String s){...}

Jni.cpp 关键代码:
	jclass     jstrObj   = env->FindClass("com/zmw/jnitest/MainActivity");
    jstring    encode    = env->NewStringUTF("utf-8");//设置编码格式
    jmethodID  methodId  = env->GetMethodID(jstrObj, "getBytes", "(Ljava/lang/String;)[B");

	//调用了java的getBytes 方法,获取jbyteArray对象
    jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(jstr, methodId, encode);
	//获取数据长度
    jsize      strLen    = env->GetArrayLength(byteArray);
	//获取数据内容,jBuf指针对象,---》GetByteArrayElements api
    jbyte      *jBuf     = env->GetByteArrayElements(byteArray, JNI_FALSE);
    。。。中间处理过程
    //最后要释放指针
     env->ReleaseByteArrayElements(byteArray, jBuf, 0);

理解上面这段代码应该对第二个表格的含义有理解了。

Jni中还有很多相关的api方法在后续的分析中慢慢讲解。

上面四个表格,第一个主要是Java --> Jni的转换关系,后面三个表格都是Jni --> Java 的转换关系。

多看几次就理解了。

共勉:不用辜负每一天的时光。

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

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

相关文章

Unity Shader - sahder变体剔除

文章目录 吐槽优化方案 - 目前最靠谱的方式shadercsharp 吐槽 我之所以单独写这边文章&#xff0c;是因为之前写的一篇&#xff1a; Unity Shader - Built-in管线下优化变体&#xff0c;编辑后&#xff0c;无法保存&#xff0c;一直提示&#xff1a;操作超时。 等了差不多 3…

开发框架DevExpress XAF - Entity Framework Core 8支持.NET 8性能基准

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 对于使用Entity Fra…

详解顺序结构滑动窗口处理算法

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

亿道推出重磅加固平板!为行业发展注入新动力

随着科技生产力的不断发展&#xff0c;各行各业都得到质的飞跃。产品的迭代速度也大大加快&#xff0c;作为全球领先的加固行移动终端一站式提供商&#xff0c;亿道信息跟紧时代潮流&#xff0c;推出EM-I10J、EM-I20J两款均衡型加固平板&#xff0c;为行业发展注入新动力。 接地…

每日一题 2867统计树中的合法路径

2867. 统计树中的合法路径数目 题目描述&#xff1a; 给你一棵 n 个节点的无向树&#xff0c;节点编号为 1 到 n 。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi] 表示节点 ui 和 vi 在树中有一条边。 请你返回树中的 合法路…

【Linux深入剖析】进程优先级 | 命令行参数 | 环境变量

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.进程优先级2.Linux…

python Matplotlib Tkinter-->tab切换2

环境 python:python-3.12.0-amd64 包: matplotlib 3.8.2 pillow 10.1.0 import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import tkinter as tk import tkinter.ttk as ttk# 创建自定义工具栏类 c…

护眼台灯如何选择?超全护眼台灯选购攻略分享

近年来护眼台灯的存在感非常强&#xff0c;已然成为家家户户必不可少的一盏灯具&#xff0c;如今市面上的台灯款式多得让人数不清&#xff0c;不过也正是如此&#xff0c;也导致了许多不专业不合格的产品混杂在其中&#xff0c;这类劣质台灯对光源的控制很差&#xff0c;使亮度…

【简写Mybatis】02-注册机的实现以及SqlSession处理

前言 注意&#xff1a; 学习源码一定一定不要太关注代码的编写&#xff0c;而是注意代码实现思想&#xff1a; 通过设问方式来体现代码中的思想&#xff1b;方法&#xff1a;5W1H 源代码&#xff1a;https://gitee.com/xbhog/mybatis-xbhog&#xff1b;https://github.com/xbh…

Qt程序设计-钟表自定义控件实例

本文讲解Qt钟表自定义控件实例。 效果如下: 创建钟表类 #ifndef TIMEPIECE_H #define TIMEPIECE_H#include <QWidget> #include <QPropertyAnimation> #include <QDebug> #include <QPainter> #include <QtMath>#include <QTimer>#incl…

Collectors.toMap的value为空报NullPointerException

1、现象 import lombok.Data; import org.apache.commons.lang3.StringUtils;import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collect…

Ps:索引颜色模式

Ps菜单&#xff1a;图像/模式/索引颜色 Image/Mode/Indexed Color 索引颜色 Indexed Color模式可生成最多 256 种颜色的 8 位图像文件。 这种颜色的限制使得索引颜色模式的图像文件相比于全彩图像&#xff08;如 RGB 颜色模式下的图像&#xff09;具有更小的文件大小&#xff0…

学习磁盘管理

文章目录 一、磁盘接口类型二、磁盘设备的命名三、fdisk分区四、自动挂载五、扩容swap六、GPT分区七、逻辑卷管理八、磁盘配额九、RAID十、软硬链接 一、磁盘接口类型 IDE、SATA、SCSI、SAS、FC&#xff08;光纤通道&#xff09; IDE, 该接口是并口。SATA, 该接口是串口。SCS…

Neoverse S3 系统 IP:机密计算和多芯片基础设施 SoC 的基础

第三代Neoverse系统IP Neoverse S3 产品推出了我们的第三代基础设施特定系统 IP&#xff0c;这是下一代基础设施 SOC 的理想基础&#xff0c;适用于从 HPC 和机器学习到 Edge 和 DPU 的各种应用。S3 机箱专注于为我们的合作伙伴提供 Chiplet、机密计算等关键创新以及 UCIe、DD…

使用R语言进行多元线性回归分析-多重共线的诊断

一、数据集 序号X1x2x3x4Y序号X1x2x3X4Y12666078.57831224472.51229155274.31954182293.12356850104.3111047426115.92143184787.6111140233483.8155263395.971266912113.311655922109.2111368812109.410771176102.73       1、从中选取主要变量&#xff0c;建立与因变…

NVM存储设备MTBF介绍

1. 概念 1.1. MTBF MTBF(Mean Time Between Failure)&#xff0c;平均故障间隔时间&#xff0c;也被称为平均无故障时间&#xff0c;是衡量一个产品的可靠性指标&#xff0c;其单位为小时。其定义为&#xff1a;产品在总的使用阶段累计工作时间与故障次数的比值&#xff1a; …

【软件测试】--功能测试2--常用设计测试用例方法

一、解决穷举场景 重点&#xff1a;使用等价类划分法 1.1 等价类划分法 重点&#xff1a;有效等价和单个无效等价各取1个即可。 步骤&#xff1a;1、明确需求2、确定有效和无效等价3、根据有效和无效造数据编写用例 1.2 案例&#xff08;qq合法验证&#xff09; 需求&#xff…

展厅设计在零售领域发挥哪些关键作用

1、 陈列和布局 零售展示设计的成功始于合适的陈列和布局。展厅设计公司考虑产品的类型、大小和特点&#xff0c;以创建有吸引力的展示。产品陈列应使顾客能够轻松浏览和访问。 2、色彩和照明 色彩和照明是零售展示设计的关键元素。展示区域的色彩和照明方案应与品牌形象一致&a…

GEE入门篇|遥感专业术语(实践操作4):光谱分辨率(Spectral Resolution)

目录 光谱分辨率&#xff08;Spectral Resolution&#xff09; 1.MODIS 2.EO-1 光谱分辨率&#xff08;Spectral Resolution&#xff09; 光谱分辨率是指传感器进行测量的光谱带的数量和宽度。 您可以将光谱带的宽度视为每个波段的波长间隔&#xff0c;在多个波段测量辐射亮…

雾锁王国Enshrouded多人联机专用服务器配置要求

雾锁王国/Enshrouded服务器CPU内存配置如何选择&#xff1f;阿里云服务器网aliyunfuwuqi.com建议选择8核32G配置&#xff0c;支持4人玩家畅玩&#xff0c;自带10M公网带宽&#xff0c;1个月90元&#xff0c;3个月271元&#xff0c;幻兽帕鲁服务器申请页面 https://t.aliyun.com…