Kotlin程序设计 扩展篇(一)

image-20240113154815774

Kotlin程序设计(扩展一)

**注意:**开启本视频学习前,需要先完成以下内容的学习:

  • 请先完成《Kotlin程序设计》视频教程。
  • 请先完成《JavaSE》视频教程。

Kotlin在设计时考虑到了与Java的互操作性,现有的Java代码可以自然地调用Kotlin代码,而Kotlin代码也可以轻松兼容Java的调用。在本扩展篇中,我们会讲解如何通过Kotlin调用Java代码。

Kotlin调用Java

我们先从最基本的内容说起,现在需要让Kotlin与Java互相兼容,并不是直接就可以使用的,我们还要遵循某些约定才可以使得Java兼容Kotlin的语法。

类的定义和使用

在Java中,最关键的就是类,我们来看看如何在Kotlin中进行使用。

我们在Java中定义的类型,可以非常轻松地被Kotlin使用,比如下面这个由Java语言定义的类型:

public class Student {
  	int age;
    String name;
}

在Kotlin中,我们可以直接使用这个类,就像是在Kotlin中定义的那样:

fun main() {
    val student: Student = Student()   //直接使用Java中的类型,无缝衔接
    student.name = "小明"
    println(student.name)  //这里得到的Java中的String类型,可以直接当做Kotlin中的使用
}

以及Kotlin中我们提到的一些基本类型,都可以与Java中的基本类型互相转换:

fun main() {
    val student = Student()
    val age: Int = student.age   //Java中的int/Integer对应了Kotlin中的Int
}

几乎Java中所有基本类型在Kotlin中都存在对应的类型,所以说直接转换为Kotlin支持的基本类型也是可以的。

包括我们在类中定义的方法,也可以在Kotlin中被当做函数使用:

public class Student {
    String name;

    public void hello() {
        System.out.println("大家好,我叫" + name);
    }
}
fun main() {
    val student = Student()
    student.name = "小明"
    student.hello()   //函数调用
}

注意,如果方法的返回类型是void,那么它对应的就是Kotlin中的Unit类型。

包括在Java中定义的构造方法,也可以被Kotlin当做构造函数使用,因为它们的语法其实差不多,可以很轻松完成兼容:

public class Student {
    String name;
    
    public Student(String name) {
        this.name = name;
    }
}
fun main() {
    val student = Student("小明")
}

可以看到,这些内容几乎是没有多少学习成本的,包括在Java类中定义的静态内容:

public class Student {
    public static void test() {
        System.out.println("我是测试静态函数");
    }
}

这些静态属性就像使用Kotlin中的伴生对象一样,可以直接通过类名进行调用,这跟Java中是一样的:

fun main() {
    Student.test()
}

还有,由于Kotlin与Java中的关键字存在差异,我们在Java中定义的某些属性名称可能会成为Kt的关键字:

import java.io.InputStream;
public class Student {
    InputStream in;  //Java中没有问题,因为in不是关键字
}
fun main() {
    val student = Student()
  	//在Kotlin中,由于in是关键字,因此我们需要对其进行转义来消除冲突
  	//使用``字符来完成转义
    student.`in` = FileInputStream("C://")
}

包括Java中的可变参数,也是可以直接兼容的:

public class Student {
    public void test(String... args) {
        
    }
}

image-20240113205404890

我们也可以直接继承Java中提供的类型:

public class Student {

}
class ArtStudent: Student()   //语法与之前是一样的

在后续的学习中,我们再来继续认识更多高级的内容。

Getter和Setter

在Kotlin基础教程中我们说到,类中的成员属性可以具有自己的Getter和Setter函数,比如:

class Student {
    var name: String = ""
        get() = field
        set(value) {
            field = value
        }
}

这样我们就可以实现对于这个变量赋值和获取的进一步控制,比如我们希望在赋值时打印内容:

var name: String = ""
    get() = field
    set(value){
        println("我被赋值了!")   //由于get和set本质上编译后是函数,因此可以自定义
        field = value
    }

而我们知道,在Java中,一个类的属性并不能像这样去编写:

public class Student {
    String name;   //只能定义一个变量,非常简单
}

我们可以对Java中的这个属性进行封装,使得其支持像Kotlin中那样存在Getter和Setter函数:

public class Student {
    private String name;   //将name属性private掉

    public String getName() {  //自定义Get和Set方法设置name属性
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
fun main() {
    val student = Student()
    student.name = "小明"
    println(student.name)
}

这样编写之后,我们同样可以在Kotlin中直接使用对应属性的名称进行访问,但是这本质上是通过其Get和Set函数来完成的,在获取属性时,会调用getName()方法得到对应的结果,设置同理。

注意:这个Get和Set必须遵循命名规则,比如这里我们要为name属性添加Getter方法,那么必须要命名为get + Name这样的名称,来表示对name属性的命名,必须以get开头,而Setter必须以set开头。

如果返回值类型是一个Boolean类型,那么Getter方法名称需要以is开头,而Setter同上。

注意,由于此时name属性由于存在访问权限控制,无法被外部访问,如果我们去掉Setter或是Getter函数,将导致变量只能被赋值或是不可用,比如去掉set方法后:

image-20240113161702594

注意: 如果直接去掉Getter方法,无论是否保留Setter方法,都会导致这个变量不可用,因为Kotlin不支持仅set-only属性。

空安全处理

由于在Java中的任何引用类型值都可能是null,这使得Kotlin对来自Java的对象进行空安全检测不太方便。因此,对于Java声明的类型会在Kotlin中以特定方式处理,我们称为平台类型。对于这种类型,空检查是放宽的,因此它们的安全保证与Java相同,也就是说部分情况下不会进行空安全检查。

比如下面这个例子:

public class Student {
    String name;   //默认情况下name属性的值就是null
}
fun main() {
    val student = Student()
  	//此时name在Kotlin中为平台类型,不会进行空安全检查,这里可以编译通过
    println(student.name.uppercase())
}

很明显,上面的代码出现了空指针异常,因为这里没有进行任何的空安全检查。

对于这种平台类型,IDEA会给我们明确指出,比如这里的name属性时String!类型的,它表示这个类型可能是SrtingString?的其中一种:

image-20240113163709872

我们在接受这个属性的时候,由于其特殊性,也可以使用两种:

fun main() {
    val student = Student()
    val name: String = student.name  //直接使用不可空类型接受,但是可能会出错
    val name2: String? = student.name  //直接使用可空类型接受
  	test(student.name)  //函数同样适用此规则
}

fun test(str: String) {  }

如果我们使用了一个不可空类型接受到来自Java的null值,会直接得到一个空指针异常,这可以防止Kotlin的不可空变量持有空值,包括传递函数参数时也同样适用,总的来说,编译器在尽最大努力防止空值在Kotlin程序中传播。

注意,在某些情况下,持有可空注解的Java类型不会表现为平台类型,比如:

public class Student {
    @NotNull String name;  //由JetBrains提供的注解
}

image-20240113164828898

这些注解包括:

  • JetBrains(来自org.jetbrains.annotations包下的@@Nullable@NotNull
  • JSpecify(org.jspecify.nullness
  • Android(com.android.annotationsandroid.support.annotations
  • JSR-305(javax.annotation
  • FindBugs(edu.umd.cs.findbugs.annotations
  • Eclipse(org.eclipse.jdt.annotation
  • Lombok(lombok.NonNull
  • RxJava 3(io.reactivex.rxjava3.annotations

同样的,对于一些泛型类,也存在一些空类型检查问题,这里以List为例:

public class Student {
    List<String> exams;
}
fun main() {
    val student = Student()
    val exams1: MutableList<String?>? = student.exams  //支持多种方式
  	val exams2: MutableList<String?> = student.exams
  	val exams3: List<String?>? = student.exams
  	...
}

可以看到,在我们使用Java中提供的List时,会得到:

image-20240113165654653

这里的(Mutable)List<String!>!包含了很多信息,我们依次来解读一下:

  • 首先Mutable表示这个List可以是可变的也可以是不可变的,因为在Java中并没有明确划分可变或是不可变的数组。
  • 然后这里的类型参数String和List都带有!表示他们都可以是可空类型也可以是不可空类型。

我们同样可以使用非空注解来提醒编译器这里一定不会为null防止被认定为平台类型:

public class Student {
    @NotNull List<String> exams;
}

以及在Java中的数组类型,对应的就是Kotlin中的Array类型:

public class Student {
    String[] exams;
}

image-20240113171513951

由于数组在Java支持协变(这与Kotlin存在不同)因此,这里我们使用Java中的数组时,可以将其当做一个抗变或是协变的String类型进行使用:

fun main() {
    val student = Student()
    val exams1: Array<String>  = student.exams
    val exams2: Array<out String?>  = student.exams
    val exams3: Array<out String?>?  = student.exams
}

这里的到的Array可以是可空也可以是不可空,里面的类型参数String同样可以是可空或是不可空,并且可以是协变也可以是抗变的。

类型对照表

前面我们提到,在Kotlin中存在Java中相应的基本类型,我们在使用Java提供的类型时,可以直接转换使用:

Java类型Java包装类型Kotlin类型
bytejava.lang.Bytekotlin.Byte
shortjava.lang.Shortkotlin.Short
intjava.lang.Integerkotlin.Int
longjava.lang.Longkotlin.Long
charjava.lang.Characterkotlin.Char
floatjava.lang.Floatkotlin.Float
doublejava.lang.Doublekotlin.Double
booleanjava.lang.Booleankotlin.Boolean

注意,虽然Java类型可以映射到Kotlin对应的类型,但是平台类型性质依然保留:

public class Student {
    Integer age;
}

image-20240113180605251

可以看到,对于Java中的包装类型Integer,这里虽然可以直接转换为Int类型,但是它依然可以是Int?或是Int这两种类型。只不过,对于Java中的基本类型来说,由于不存在null这种结果,因此我们可以安全的将其当做不可空类型使用:

public class Student {
    int age;
}

image-20240113181910862

除了这些基本类型之外,实际上Kotlin中还有很多其他类型也可以直接映射:

Java类型Kotlin类型
java.lang.Objectkotlin.Any!
java.lang.Cloneablekotlin.Cloneable!
java.lang.Comparablekotlin.Comparable!
java.lang.Enumkotlin.Enum!
java.lang.annotation.Annotationkotlin.Annotation!
java.lang.CharSequencekotlin.CharSequence!
java.lang.Stringkotlin.String!
java.lang.Numberkotlin.Number!
java.lang.Throwablekotlin.Throwable!

集合类型在Kotlin中可以是只读的或可变的,因此Java的集合映射如下(此表中的所有Kotlin类型都定义在kotlin.collections包中)

Java类型Kotlin只读类型Kotlin可变类型转换平台类型
Iterator<T>Iterator<T>MutableIterator<T>(Mutable)Iterator<T>!
Iterable<T>Iterable<T>MutableIterable<T>(Mutable)Iterable<T>!
Collection<T>Collection<T>MutableCollection<T>(Mutable)Collection<T>!
Set<T>Set<T>MutableSet<T>(Mutable)Set<T>!
List<T>List<T>MutableList<T>(Mutable)List<T>!
ListIterator<T>ListIterator<T>MutableListIterator<T>(Mutable)ListIterator<T>!
Map<K, V>Map<K, V>MutableMap<K, V>(Mutable)Map<K, V>!
Map.Entry<K, V>Map.Entry<K, V>MutableMap.MutableEntry<K,V>(Mutable)Map.(Mutable)Entry<K, V>!

以及数组的映射如下,基本类型的数组会被直接映射为基本类型专用的Array类型:

Java类型Kotlin类型
int[]kotlin.IntArray!
String[]kotlin.Array<(out) String>!

注意,在Java中,这些类型可能存在一些静态属性,如果我们需要调用对应的静态属性,需要使用Java中类型的名称进行调用:

//在Integer中定义的静态方法
public static int parseInt(String s) throws NumberFormatException {
    return parseInt(s,10);
}
fun main() {
    Integer.parseInt("666")  //需要使用原本的名称,而不是转换之后的Int
}

泛型转换

Kotlin的泛型与Java型有点不同,当将Java类型导入Kotlin时,将完成以下转换:

  • Java的通配符转换:
    • Foo<? extends Bar>成为Foo<out Bar!>!
    • Foo<? super Bar>成为Foo<in Bar!>!
public class Student {
    List<? extends Number> data;  //此时泛型上界为Number
    List<? super Integer> data2;  //此时泛型下界为Integer
}

当这些类型在Kotlin中使用时,会自动被划分为协变或抗变类型:

fun main() {
    val student = Student()
  	//Java中的泛型上界对应Kotlin的协变类型
    val data: MutableList<out Number> = student.data
  	//Java中的泛型下界对应Kotlin的抗变类型
    val data2: MutableList<in Int> = student.data2
}

由于在Kotlin中对in和out进行了严格的使用限制,因此无法像Java那样随意使用。

  • Java的原始类型被转换为星形投影:
    • List当做List<*>!也就是List<out Any?>!
public class Student {
    List data;   //对List的原始使用
}
fun main() {
    val student = Student()
  	//在Kotlin中直接以Any?作为实际类型使用,因为没有明确具体类型
    val data: MutableList<Any?>? = student.data
}

与Java一样,Kotlin的泛型不会在运行时保留,也就是说对象不会带有任何实际类型参数的信息,详情请见基础篇。

运算符重载

由于Java不支持运算符重载,我们无法通过关键字支持像Kotlin这样的运算符重载,但是,只要符合我们前面所说的那些运算符重载函数名称的方法,依然可以作为运算符重载函数使用:

public class Student {
    public Student plus(Student other) {
        return this;
    }
}

可以看到,以上代码与Kotlin中+运算符重载函数的定义相同,满足规范,因此,在Kotlin中,可以直接支持使用:

fun main() {
    var student = Student()
    student = student + student
}

异常检查

在Kotlin中,所有异常都不会主动进行检查,这意味着编译器不会强迫您捕获任何异常。因此,当您调用声明了异常的Java方法时,Kotlin不会强制要求进行捕获:

public class Student {
    public void test() throws IOException {
        
    }
}
fun main() {
    var student = Student()
    student.test()
}

Object类型

当Java对象在Kotlin中使用时,Object类型的所有引用都会变成Any类型。由于Any类型不是特定于某一个平台的,考虑到对其他语言的兼容性,因此,它只声明toString()``hashCode()equals()函数作为其成员,而在Java中,Object存在很多其他的成员方法:

public final native Class<?> getClass();
public final native void notify();
public final native void notifyAll();
...

如果需要让Object类中的其他成员方法可用,可以像下面这样:

fun main() {
    val student = Student()
  	//将student类型转换为Object再调用其
    (student as Object).wait()
}

不过,在Kotlin中不鼓励使用wait()notify()等线程相关方法,使用JUC中提供的类型效果更佳(详情请见Java JUC篇视频教程)

如果我们要获取某个类的Class对象:

fun main() {
    val student = Student()
    val clazz: Class<Student> = student.javaClass  //使用.javaClass获取到对应的Java类对象
}

函数式接口

Kotlin支持Java的SAM转换,只要是Java中满足要求的函数式接口,都可以开箱即用:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
fun main() {
  	//使用Java中的Runnable接口
    val runnable: Runnable = Runnable {
        println("Hello World")
    }
}

包括我们在函数中一样可以像这样使用:

import java.util.concurrent.ThreadPoolExecutor

fun main() {
    val executor = ThreadPoolExecutor()
    // Java方法定义: void execute(Runnable command)
    executor.execute { println("This runs in a thread pool") }
}

Java调用Kotlin

前面我们介绍了Kotlin如何调用现成的Java代码,我们接着来看Java如何调用Kotlin代码。由于Java和Kotlin之间存在某些差异,在将Kotlin代码集成到Java中时需要注意很多东西。

对象属性

在Kotlin中定义的类型,到了Java中依然可以直接创建其对象:

class Student(var name: String)
public static void main(String[] args) {
    Student student = new Student("小明");   //Kt构造函数就是构造方法
}

只不过,对于类的属性,由于Kotlin中本质是以get和set函数的形式存在的,因此,我们只能使用对应的Getter和Setter方法来进行调用:

public static void main(String[] args) {
    Student student = new Student("小明");
    student.getName();
    student.setName("大明");   //set方法仅在属性为var时可用
}

如果实在是需要在Java中像使用普通变量那样,我们可以添加一个特殊的注解使其支持:

class Student(@JvmField var name: String)

包括懒加载属性也支持:

class Student {
    lateinit var name: String
}
public static void main(String[] args) {
    Student student = new Student("小明");
    student.name = "";
}

当然,这个属性不得是 openoverrideconst 其中一种,也不能是委托属性。

同时,由于Java中不存在空类型处理,因此,在Kotlin中定义的无论是否为可空类型都可以在Java中直接使用:

class Student {
    var name: String? = null
}
public static void main(String[] args) {
    Student student = new Student();
    student.getName().toUpperCase();   //直接空指针异常
}

静态属性

如果是Kotlin文件中直接编写的顶层定义,可以当做特定文件的静态属性来使用:

fun test() {
    println("Hello World")
}

在编译之后,它本质上就是一个Java中的静态方法,而对应类的名称就是源文件名称+Kt,这里Main.kt对应的名称就是MainKt了:

public static void main(String[] args) {
    MainKt.test();
}

我们也可以使用注解来明确生成的Java字节码文件名称:

@file:JvmName("LBWNB")
public static void main(String[] args) {
    LBWNB.test();
}

对于伴生对象以及单例对象,在Java中使用起来可能会有些别扭:

class Student {
    companion object {
        fun test() {}
    }
}

object Test {
    fun hello() {}
}
public static void main(String[] args) {
    Student.Companion.test();
  	Test.INSTANCE.hello();
}

这并不是我们希望的样子,在Kotlin中我们可以直接使用Student.的形式来直接调用,而在Java中却有一些出入,我们可以为这些函数添加@JvmStatic注解来完成:

class Student {
    companion object {
        @JvmStatic fun test() {}
    }
}

对于伴生对象中的字段,我们也可以为其添加@JvmField注解来使得其可以直接使用,这会在编译时使得此函数作为Student的静态属性存在:

public static void main(String[] args) {
    Student.test();
}

同样的,对于伴生对象中的属性来说,我们也像上一小节那样添加@JvmField注解:

class Student {
    companion object {
        @JvmField var name: String = ""
    }
}
public static void main(String[] args) {
    Student.name = "";   //此时name就是Student的静态属性
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

源 “MySQL 5.7 Community Server“ 的 GPG 密钥已安装,但是不适用于此软件包。请检查源的公钥 URL 是否配置正确

Is this ok [y/d/N]: y Downloading packages: 警告&#xff1a;/var/cache/yum/x86_64/7/mysql57-community/packages/mysql-community-server-5.7.44-1.el7.x86_64.rpm: 头V4 RSA/SHA256 Signature, 密钥 ID 3a79bd29: NOKEY 从 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 检…

加速社区数字化转型:物业app开发的最新趋势

在当今数字化时代&#xff0c;社区数字化转型已经成为业界焦点。特别是在物业管理领域&#xff0c;物业app开发正成为加速社区数字化转型的关键趋势。本文将探讨物业app开发的最新趋势&#xff0c;以及如何通过这些趋势推动社区数字化转型。 物业app开发的关键趋势 随着智能手…

postman测试导入文件

01 上传文件参数 1.选择请求方式 选择post请求方式&#xff0c;输入请求地址 2.填写Headers Key&#xff1a;Content-Type &#xff1b; Value&#xff1a;multipart/form-data 如下图 3.填写body 选择form-data&#xff0c;key选择file类型后value会出现按钮&#xff0…

(十二)Head first design patterns代理模式(c++)

代理模式 代理模式&#xff1a;创建一个proxy对象&#xff0c;并为这个对象提供替身或者占位符以对这个对象进行控制。 典型例子&#xff1a;智能指针... 例子&#xff1a;比如说有一个talk接口&#xff0c;所有的people需要实现talk接口。但有些人有唱歌技能。不能在talk接…

[AutoSar]BSW_OS 06 Autosar OS_Alarms

一、 目录 一、关键词平台说明一、Timer1.1 配置1.2Periodical Interrupt Timer (PIT)和High Resolution Timer (HRT) 二、Alarm 工作机制三、Code3.1创建一个15ms的runnable3.2mapping到basic task3.3生成代码 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueO…

【开源】基于JAVA语言的停车场收费系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 停车位模块2.2 车辆模块2.3 停车收费模块2.4 IC卡模块2.5 IC卡挂失模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 停车场表3.2.2 车辆表3.2.3 停车收费表3.2.4 IC 卡表3.2.5 IC 卡挂失表 四、系统实现五、核心代码…

2024.1.22(150. 逆波兰表达式求值)

2024.1.22(150. 逆波兰表达式求值) 相信看完动画大家应该知道&#xff0c;这和1047. 删除字符串中的所有相邻重复项是差不错的&#xff0c;只不过本题不要相邻元素做消除了&#xff0c;而是做运算&#xff01; // 定义一个Solution类 class Solution { // 定义一个公共方法…

手机上菜谱记录簿在哪 用备忘录放大看菜谱更清晰

作为一个热爱生活的现代人&#xff0c;我深知健康饮食的重要性。然而&#xff0c;每当我想亲手为自己和家人烹饪美食时&#xff0c;厨艺的不精常常让我望而却步。好在互联网时代&#xff0c;网上搜罗的各式菜谱成了我的救星。但问题是&#xff0c;每次做菜时都得反复查找&#…

幻兽帕鲁专用服务器

随着幻兽帕鲁这款游戏的热度持续升温&#xff0c;我们遍寻全网&#xff0c;带给各位玩家一个全新的、高品质的游戏体验——莱卡云服务器。有幻兽帕鲁的热衷者们无需再为了服务器的选取困扰&#xff0c;因为我们可以肯定地说&#xff1a;选择莱卡云&#xff0c;你不会失望。 首先…

尝试着在Stable Diffusion里边使用SadTalker进行数字人制作

首先需要标明的是&#xff0c;我这里是图片说话类型&#xff0c;而且是看了知识星球AI破局俱乐部大航海数字人手册进行操作的。写下这篇文章是防止我以后遗忘。 我使用的基础软件是Stable Diffusion&#xff0c;SadTalker是作为插件放进来的&#xff0c;需要注意的是这对自己的…

用户资源(菜单)控制学习使用

效果图 第一步 需要再定义常量资源 //信访听证 资源前缀public static final String RESPREFIX_MODULE_XINFTZ_"module_xinftz_";//听证专家库public static final ConstantItem RES_MODULE_XINFTZ_TINGZZJK new ConstantItem(RESPREFIX_MODULE_XINFTZ_ "tin…

Drools 规则属性讲解(结合代码实例讲解)

目录 一、规则属性总览 1.1 规则总览 二、具体属性讲解 2.1 enabled属性 2.1.1 代码实现 2.1.1.1 编写规则文件 2.1.1.2 编写规则实体对象 2.1.1.3 编写测试类 2.1.1.4 测试结果 2.2 dialect属性 2.3 salience属性 2.3.1 代码实现 2.3.1.1 编写规则文件 2.3.1.2 编…

超简单的OCR模块:cnocr

前言 毫无疑问的是&#xff0c;关于人工智能方向&#xff0c;python真的十分方便和有效。 这里呢&#xff0c;我将介绍python众多OCR模块中一个比较出色的模块&#xff1a;cnocr 模块介绍 cnocr是一个基于PyTorch的开源OCR库&#xff0c;它提供了一系列功能强大的中文OCR模型和…

网络安全的概述

网络空间的概念 2003年美国提出网络空间的概念&#xff1a;一个由信息基础设施组成的互相依赖的网络。 我国官方文件定义&#xff1a;网络空间为继海&#xff0c;陆&#xff0c;空&#xff0c;天以外的第五大人类活动领域 网络安全发展历史 通信保密阶段 --- 计算机安全阶段…

基于 Spring Boot+MySQL实现的在线考试系统源码+数据库,基于不同类型的客观题,进行自动组卷、批卷等功能的考试系统

1. 部署相关 1.1. 介绍 一个 JAVA 实现的在线考试系统,主要实现一套基于不同类型的客观题,进行自动组卷、批卷等功能的考试系统&#xff08;没有主观题&#xff09; 1.2. 系统架构 后端技术栈基于 Spring Boot数据库MySQLORMMyBatis & MyBatis-plus缓存Redis、guava的L…

漏洞复现--Confluence远程代码执行漏洞(CVE-2023-22527)

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

深兰科技陈海波出席“2023浙商年度主题大会”并与知名主持人白岩松对话

1月17日&#xff0c;“2023浙商年度主题大会”在浙江杭州举行。本次大会由浙商总会主办&#xff0c;来自全球的600余名浙商代表汇聚一堂&#xff0c;共叙乡情、共享机遇、共谋发展。在“浙商年度创新样本”发布环节中&#xff0c;深兰科技创始人、董事长陈海波作为人工智能领域…

windows用mingw(g++)编译opencv,opencv_contrib,并install安装

windows下用mingw编译opencv貌似不支持cuda&#xff0c;选cuda会报错&#xff0c;我无法解决&#xff0c;所以没选cuda&#xff0c;下面两种编译方式支持。 如要用msvc编译opencv&#xff0c;参考我另外一篇文章 https://blog.csdn.net/weixin_44733606/article/details/1357…

autosar学习笔记 之SecOC

SecOC 接下来SecOC标准就更复杂一点,它不单单是做了通讯校验。 SecOC是基于对称密钥加密的一套机制,需要对ECU间的通讯作身份认证处理,来更好的防止伪装攻击,谈起对称或非对称加密,就会涉及到密钥的存储和Mac值的计算。 因此SECOC机制对于密钥的硬件存储,也有一定的要求…

[C++] external “C“的作用和使用场景(案例)

C中extern "C"的作用是什么&#xff1f; 在 C 中&#xff0c;extern "C" 的作用是告诉编译器按照 C 语言的规范来处理函数名和变量名。这是因为 C 编译器会对函数名和变量名进行名称修饰(name mangling)&#xff0c;以区分不同的函数和变量。而在 C 语言中…