Kotlin基础——类、对象和接口

文章目录

  • 1 定义类继承结构
    • 1.1 接口
      • 1.1.1 接口概述
      • 1.1.2 接口中的默认方法
      • 1.1.3 接口方法重复
      • 1.1.4 Kotlin接口中静态方法实现原理
    • 1.2 修饰符
      • 1.2.1 类继承修饰
      • 1.2.2 方法重写修饰
      • 1.2.3 抽象类
      • 1.2.4 接口的修饰符
    • 1.3 可见性修饰符
      • 1.3.1 Kotlin中的可见性修饰符
      • 1.3.2 Kotlin中的可见性修饰符和Java的对应关系
    • 1.4 内部类和嵌套类
    • 1.5 密封类
  • 2 声明一个带非默认构造方法或属性的类
    • 2.1 主构造方法和初始化语句块
      • 2.1.1 主构造方法的简化流程
      • 2.1.2 默认构造方法
      • 2.1.3 主构造私有
    • 2.2 构造方法:初始化父类
    • 2.3 实现在接口中声明的属性
    • 2.4 通过getter或setter访问支持字段
    • 2.5 修改访问器的可见性
  • 3 数据类和类委托
    • 3.1 通用对象方法
    • 3.2 数据类
    • 3.3 类委托
  • 4 objet关键字
    • 4.1 对象声明
    • 4.2 伴生对象
    • 4.3 作为普通对象使用的伴生对象
    • 4.4 对象表达式:改变写法的匿名内部类

1 定义类继承结构

1.1 接口

1.1.1 接口概述

kotlin中同样使用interface关键字来定义接口,接口的含义和Java中类似,用于定义抽象

fun main() {Button().click()
}interface Clickable {fun click()
}class Button : Clickable {override fun click() {println("Button clicked")}
}

使用interface关键字定义接口。实现一个接口跟Java中不同,Kotlin中继承类和实现接口都是使用冒号。
重写接口中的方法,使用override关键字,与Java中不同的是,override是必须写的,如果不写则会报错。这会避免先写出实现方法再添加抽象方法造成的意外重写。
和Java一样,Kotlin中的类只能继承一个类,但可以实现多个方法。

1.1.2 接口中的默认方法

Kotlin接口中的默认方法也是与Java中有区别,Java中需要使用default关键字修饰,而Kotlin中不用。Kotlin接口中,如果方法有实现,则是一个默认方法,如果没有实现,则是纯接口。

fun main() {Button().click()
}interface Clickable {fun click() {println("default click")}
}class Button : Clickable

对于拥有默认实现的接口方法,可以使用默认实现,也可以重写

1.1.3 接口方法重复

由于可以实现多个接口,如果两个接口出现同样的方法,那么实现类应该怎么处理?在Java中,处理方式是必须重写其中的方法,Kotlin中也是一样的要求。

fun main() {Button().click()
}interface Clickable1 {fun click() {println("default click 1")}
}interface Clickable2 {fun click() {println("default click 2")}
}class Button : Clickable1, Clickable2 {override fun click() {super<Clickable1>.click()}
}

必须显示的重写重复的方法,否则会报错。这里就是重写click方法,具体实现则调用父类Clickable1的click方法。
注意这里super的用法与Java中有区别,Kotlin中,super后面通过尖括号限定是哪个父接口,而Java中则是如下使用

class Button2 implements Clickable1, Clickable2 {@Overridepublic void click() {Clickable1.super.click();}
}

1.1.4 Kotlin接口中静态方法实现原理

Kotlin以jdk1.6为目标进行涉及,当时还没有默认方法。Kotlin会把每个带默认方法的接口编译成一个普通接口和一个将方法体作为静态函数的类的结合体

interface Clickable1 {fun click() {println("default click 1")}
}

对于以上包含默认实现方法的Kotlin接口,使用kotlinc编译后,会产生两个class文件
在这里插入图片描述
内容分别为:
Clickable1.class

public interface Clickable1 {void click();
}

Clickable1$DefaultImpls.class

public final class Clickable1$DefaultImpls {public static void click(@NotNull Clickable1 $this) {System.out.println((Object) "default click 1");}
}

接口中只包含了声明,类中包含了一个静态方法,该方法有实现

1.2 修饰符

Kotlin中常用的修饰符有open、final、abstract
Java中有关键字final用于修饰方法,被修饰的方法在其子类中不能够被重写。Kotlin中的final也是有此用途,Kotlin中的open与final对应,表示可以被重写。
Kotlin与Java设计理念不同,Java中,默认是open的,子类可以重写父类中的方法,这会导致脆弱的基类问题。对基类的修改可能会导致子类无法正常运行。在Java中,也建议将子类不需要重写的方法在基类中设置成final。
Kotlin依据此,从语言级别上,将默认的修饰符设置成final,只有需要重写的方法,才需要显示的使用open修饰,允许子类进行重写。
open和final的修饰不止应用于方法级别,对于类的继承也是一样。

修饰符相关成员评注
final不能被重写类中成员默认使用
open可以被重写需要明确地表示
abstract必须被重写只能在抽象类中使用,抽象成员不能有实现
override重写父类或接口中的成员如果没有使用final表明,重写的成员默认是open的

1.2.1 类继承修饰

在这里插入图片描述
类默认是final的,无法继承,只能将基类设置成open的才能够继承

open class Father
class Son : Father()

继承父类,并调用父类的构造方法

1.2.2 方法重写修饰

在这里插入图片描述
方法默认是final修饰的,需要定义成open才能够重写

open class Father {open fun getName() {println("Father")}
}
class Son : Father() {override fun getName() {println("Son")}
}

定义成open后可以被子类重写

open class Son : Father() {override fun getName() {println("Son")}
}class Grandson : Son() {override fun getName() {println("GrandSon")}
}

子类重写后子类的方法也是open的,如果不需要让当前子类的子类重写,则需要重新显示的定义final修饰

1.2.3 抽象类

一个类如果被abstract修饰,那么就是抽象类。

abstract class Animated {abstract fun animate();open fun stopAnimating() {}fun animateTwice() {}
}

抽象类中的抽象方法也需要使用abstract修饰,没有使用abstract修饰的方法是普通方法。抽象方法是默认open的,而普通方法则是默认final的。

1.2.4 接口的修饰符

接口中不使用open、final、abstract修饰符。
其中open是默认的,加不加都不影响。
不能使用final修饰,因为接口中的方法就是用来重写的。
如果接口中的方法没有实现,则默认是abstract的,有实现则不是。

1.3 可见性修饰符

1.3.1 Kotlin中的可见性修饰符

Kotlin中的可见性修饰符有private、protected和public,与Java相比,少了default。
Kotlin中的默认可见性修饰符是public的。这与Java不同,Java中是包私有的。
Kotlin中的包和Java中的包含义不同,Kotlin中的包只是代码组织的一种形式,没有用作可见性控制。
Kotlin引入了一个新的可见性修饰符,internal,表示模块内可见。internal可见性的优势在于它提供了对模块实现细节的真正封装。
Kotlin允许在顶层声明中使用private可见性,包括类、函数和属性。这些声明就会只在声明它们的文件中可见。

修饰符类成员顶层声明
public所有地方可见所有地方可见
internal模块中可见模块中可见
protected子类中可见
private类中可见文件中可见

在这里插入图片描述
类的基础类型和类型参数列表中用到的所有类,或者函数的签名都有与这个类或者函数本身相同的可见性

1.3.2 Kotlin中的可见性修饰符和Java的对应关系

  • Kotlin中的public、protected和private修饰符在编译成Java字节码时会被保留。
  • 唯一的例外是private类:在这种情况下它会被编译成包私有声明(在Java中你不能把类声明为private。
  • internal修饰符在字节码中会变成public。

1.4 内部类和嵌套类

Kotlin中也是可以在一个类中定义类,但是与Java不同的是,Kotlin中定义的类默认不是成员内部类,并没有持有外部类的引用,而是类似于Java中的static修饰的静态内部类。如果要定义类似Java中的成员内部类,需要使用inner修饰。
在这里插入图片描述
嵌套类不持有父类的引用,无法直接访问父类的成员。

class Outer {val num = 10inner class Inner {fun printTest() {println(num)}}
}

使用inner修饰,嵌套类会持有父类的引用,可以直接访问父类的成员,包括私有成员,类似于成员内部类。

class Outer {val num = 10inner class Inner {fun getOuterInstance() : Outer{return this@Outer}}
}

访问外部类对象,使用this@访问。
Kotlin对于嵌套类的设计可以防止Java中的以下问题:当内部类可序列化而外部类不可序列化时,如果序列化内部类对象,会报异常,因为内部类对象存储了外部类的引用,而外部类是不可序列化的。

1.5 密封类

密封类使用sealed关键字修饰,对其可能创建的子类做出严格的限制,所有的直接子类必须嵌套在父类中。

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Exprfun eval(e: Expr): Int =when (e) {is Num -> e.valueis Sum -> eval(e.left) + eval(e.right)else-> throw IllegalArgumentException("Unknown expression")}

考虑上述场景,当使用when结构来执行表达式的时候,Kotlin编译器会强制检查默认选项。如果新增一个子类,就得新增分支,如果忘了新增分支,则会走默认分支,可能导致潜在的bug。
如果使用密封类,就可以解决上述问题。

sealed class Expr {class Num(val value: Int) : Expr()class Sum(val left: Expr, val right: Expr) : Expr()
}fun eval(e: Expr): Int =when (e) {is Expr.Num -> e.valueis Expr.Sum -> eval(e.left) + eval(e.right)}

外部类无法继承Expr类,所有继承了Expr类的子类都在其内部。

2 声明一个带非默认构造方法或属性的类

Java中,一个类可以声明多个构造方法,Kotlin中也可以,Kotlin中只是做了一些区分,区分主构造方法和从构造方法。
主构造方法:通常是简洁的类初始化方法,声明在类定义的时候
从构造方法:在类内部声明

2.1 主构造方法和初始化语句块

2.1.1 主构造方法的简化流程

class User(val nickName: String)

定义了一个类User,并声明了主构造方法,其中nickName是主构造方法的参数。这里是一种简化后的写法,下面是简化流程

//以下就是完整的定义,使用constructor声明一个构造方法,init关键字用来引入一个初始化语句块
//初始化语句块会在类创建的时候执行,并与主构造方法一起使用。由于主构造方法有语法限制,不能包含初始化代码,所以用初始化语句块
//属性的nickName和构造方法参数中的可以定义为相同的,通过this进行区分
class User constructor(_nickName: String) {val nickName: Stringinit {nickName = _nickName}
}//属性声明与初始化语句块中的初始化代码相结合,所以可以省略初始化语句块
class User constructor(_nickName: String) {val nickName: String
}//如果主构造方法没有注解或者可见性修饰符,可以去掉constructor关键字
class User(_nickName: String) {val nickName: String
}//如果属性用相应的构造方法参数来初始化,可以将val关键字放到参数前,并省略属性定义来简化
//这就简化成了最终的定义
class User(val nickName: String)

2.1.2 默认构造方法

如果一个类没有声明任何构造法方法,在继承的时候,必须显示调用父类构造

open class Button
class RadioButton: Button()

Button类会生成一个不带任何参数的默认构造方法,继承必须调用父类的构造方法。

2.1.3 主构造私有

如果想确保类不被其他代码实例化,必须把构造方法标记为private

class Secretive private constructor(){}

Java中,通常将构造方法私有来实现静态工具类或者是单例类。这在Kotlin中有替代方案,可以使用顶层函数来代替静态工具类,使用对象声明来代替单例类。

2.2 构造方法:初始化父类

Kotlin中由于区分了主构造方法和从构造方法,对于主构造方法来说,一个类中只能有一个,而可以拥有多个从构造方法。
继承时,主构造方法在继承后面调用父类的构造方法。从构造方法使用super调用。

//拥有主构造方法的类继承父类
open class Person
class Student : Person()//只有从构造方法的类继承父类
open class View {constructor(ctx: Context) {}constructor(ctx: Context, attr: AttributeSet) {}
}class MyButton : View {//constructor(ctx: Context) : super(ctx)constructor(ctx: Context) : this(ctx, MY_STYKLE)constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr)
}

可以直接使用super调用父类构造,也可以通过自身调用了父类构造的其他构造方法来调用父类构造

2.3 实现在接口中声明的属性

Kotlin接口与Java接口对于属性有所区别。Java,可以在接口中定义常量,而Kotlin中的常量使用const修饰,只能放在顶层或者object修饰的域中。
Kotlin中,接口可以包含抽象属性说明,让实现类来实现属性声明。

interface User {val nickName: String
}class PrivateUser(override val nickName: String) : User
class SubscribingUser(val email: String) : User {override val nickName: Stringget() = email.substringBefore('@')
}class FacebookUser(val accountId: Int) : User {override val nickName = getFacebookName(accountId)private fun getFacebookName(accountId: Int): String {return "test"}
}

接口中定义抽象属性说明,在实现类中用字段去存储,可以表达不同的含义。
第一个PrivateUser使用主构造方法中定义的属性字段进行存储。第二个SubscribingUser通过主构造方法中的属性email来提供nickName这个属性的一个get方法。第三个通过一个函数返回。

除了抽象属性声明外,接口还可以包含具有getter和setter的属性,只要它们没有引用一个存储字段

interface User {val email: Stringval nickName: Stringget() = email.substringBefore('@')
}

2.4 通过getter或setter访问支持字段

目前有两种属性的例子:一种是存储值的属性,一种是具有自定义访问器在每次访问时计算值的属性。
如何在自定义访问其中访问支持字段呢?

class User(val name: String) {var address: String = "Beijing"set(value) {println("""Address was changed for $name: "$field" -> "$value".""".trimIndent())}
}fun main() {val user = User("Alice")user.address = "Shanghai"
}

这里有一个关键字field用于访问支持字段

2.5 修改访问器的可见性

访问器的可见性默认与属性的可见性相同,但是可以修改其可见性

class LengthCounter {var counter: Int = 0private setfun addWord(word: String) {counter += word.length}
}

默认属性的可见性是public的,将其修改为private,在外部无法修改counter的值。

3 数据类和类委托

3.1 通用对象方法

在编写Java类时,有些方法是常常要重写的,比如toString、equals和hashCode这三个方法。
toString方法用来打印对象实例,如果想输出具体属性值,需要重写该方法。
equals方法用于比较两个对象是否相等,重写之后可用于比较对象中属性是否相等。这里有双等号和equals的区别,双等号在Java中对于基本类型,比较值,对于引用,比较对象地址。在Java的实践中,通常总是调用equals比较对象内容。在Kotlin中,双等号是比较两个对象的默认方式:本质上说是通过equals比较两个值的,所以重写equals后,可以直接使用双等号比较。
hashCode方法用于实现hash运算,便于使用hash算法相关的容器。hash算法的约定是:如果两个对象相等,它们必须有相同的hash值。所以重写equals,必须重写hashCode,以维护这个约定。

3.2 数据类

Kotlin提供了一个新的关键字——data,用于定义数据类。数据类自动重写了equals、hashCode和toString方法。

data class Client(val name: String, val postalCode: Int)fun main() {val client = Client("test", 32)println(client)
}
输出:
Client(name=test, postalCode=32)

数据类的属性并没有要求一定是val,但是建议使用val,让数据类的实例不可变。这样做有很多好处,一是容器中作为键使用,不用担心被修改。二是在多线程环境下不担心被修改。
由于建议数据类尽量不被修改,所以提供了copy()方法,可以用于copy类的实例,并在copy的同时修改某些属性的值,这个是编译器自动生成的。

val client2 = client.copy("hello", 23)
println(client2)
输出:
Client(name=hello, postalCode=23)

3.3 类委托

考虑Java中实现装饰器模式,创建一个新类,实现与原始类一样的接口并将原来的类的实例作为一个字段保存,与原始类拥有同样行为的接口不用修改,直接转发到原始类

class DelegatingCollection<T> : Collection<T> {private val innerList = arrayListOf<T>();override val size: Intget() = innerList.sizeoverride fun isEmpty(): Boolean = innerList.isEmpty()override fun iterator(): Iterator<T> = innerList.iterator()override fun containsAll(elements: Collection<T>): Boolean = innerList.containsAll(elements)override fun contains(element: T): Boolean = innerList.contains(element)
}

这里就有很多都是模板代码。Kotlin中可以通过委托来实现上述功能,使用by关键字将接口的实现委托到另一个对象。

class DelegatingCollection<T>(innerList: Collection<T> = ArrayList<T>()) : Collection<T> by innerList {
}

这里使用类委托,上述的模板代码都消失了

class CountingSet<T>(val innerSet: MutableCollection<T> = HashSet<T>()) : MutableCollection<T> by innerSet {var objectsAdded = 0;override fun add(element: T): Boolean {objectsAdded++;return innerSet.add(element)}override fun addAll(elements: Collection<T>): Boolean {objectsAdded += elements.sizereturn innerSet.addAll(elements)}
}

可以只重写关注的方法,其他的方法委托给父类对象。

4 objet关键字

objet关键字的实质是定义一个类并同时创建一个对象,常使用的场景有:

  • 对象声明是定义单例的一种方式
  • 伴生对象可以持有工厂方法和其他与这个类相关,但在调用时并不依赖类实例的方法。它们的成员可以通过类名来访问
  • 对象表达式可以用来替代Java的匿名内部类

4.1 对象声明

对象声明通过object关键字引入

object CaseInsensitiveFileComparator : Comparator<File> {override fun compare(file1: File, file2: File): Int {return file1.path.compareTo(file2.path, ignoreCase = true)}
}

对象声明,声明一个类并创建一个对象,是天生的单例类。
与类一样,一个对象声明可以包含属性、方法、初始化语句块等声明,唯一不允许的是构造方法(包括主构造和从构造)
对象声明的对象,在对象声明定义时就创建了,不需要在代码其他地方调用构造方法。
但是在大型系统中,由于对象声明不提供构造,无法对对象实例化进行控制,并且不能通过构造方法指定特定的参数,所以大型系统中还是需要将依赖注入的框架与普通的Kotlin类一起使用。
Kotlin中的对象声明被编译成了通过静态字段来持有它的单一实例的类,这个字段的名字始终是INSTANCE,Java中可以通过如果下调用

CaseInsensitiveFileComparator.INSTANCE.compare(file1, file2);

4.2 伴生对象

Kotlin中的类不能拥有静态成员,Java中的static关键字并不是Kotlin语言的一部分。作为替代,Kotlin依赖包级别函数和对象声明。
在大多数情况下,使用顶层函数可以替代,但是顶层函数不能访问类中的private成员。
如果需要写一个可以在没有类实例的情况下调用但是需要访问类内部的函数,可以将其写成类中的对象声明的成员。
在类中定义的对象之一可以使用一个特殊的关键字来标记:companion。如果这样做,就获得了直接通过容器类名称来访问这个对象的方法和属性的能力,不在需要显式地指明对象的名称。

class A {companion object {val a = 120fun bar() {println("Companion object called")println("c is ${A().c}")}}
}fun main() {A.bar()println(A.a)
}
输出:
Companion object called
c is 20
120

这里companion object就是声明了一个伴生对象,伴生对象可以访问类中的所有private成员,包括private构造方法,它是实现工厂模式的理想选择。

class User {val nickName: Stringconstructor(email: String) {nickName = email.substringBefore('@')}constructor(facebookAccountId: Int) {nickName = getFacebookName(facebookAccountId)}private fun getFacebookName(accountId: Int): String {return "test"}
}

之前的User类可以通过伴生对象来实现成工厂模式

class User private constructor(val nickName: String) {companion object {fun newSubscribingUser(email: String) = User(email.substringBefore('@'))fun newFacebookUser(accountId: Int) = User(getFacebookName(accountId))private fun getFacebookName(accountId: Int): String {return "test"}}
}

工厂方法很有用,可以通过用途来命名来创建对应的子类对象。

4.3 作为普通对象使用的伴生对象

伴生对象是一个声明在类中的普通对象,可以有名字,实现一个接口或者有扩展函数或属性。

class Person(val name: String) {companion object Loader {fun fromJSON(jsonText: String): Person {return Person("test")}}
}

伴生对象可以有自己的名字,如果没有提供名字,则会使用默认的名字Companion

interface JSONFactory<T> {fun fromJSON(jsonText: String): T
}
class Person(val name: String) {companion object : JSONFactory<Person> {override fun fromJSON(jsonText: String): Person {return Person("test")}}
}

伴生对象可以实现接口
类的伴生对象会同样被编译成常规对象:类中的一个引用了它的实例的静态字段,Java中访问伴生对象的方法如下

public static void main(String[] args) {Person.Companion.fromJSON("");
}

如果伴生对象有名字,则Companion这个默认名字会被替换。
可以在对应的成员上使用@JvmStatic注解来将类中成员设置成静态的,如果想声明一个static的字段,可以再在一个顶层属性或者声明在object中的属性上使用@JvmField注解。

class Person(val name: String) {companion object : JSONFactory<Person> {@JvmStaticvar test: String = "hello"@JvmStaticoverride fun fromJSON(jsonText: String): Person {return Person("test")}}
}

对于属性来说,会生成静态的getter和setter方法。对于方法来说,会生成静态的方法。
Java中调用如下:

ystem.out.println(Person.getTest());
System.out.println(Person.fromJSON(""));

对于@JvmField注解

class Person(val name: String) {companion object : JSONFactory<Person> {@JvmFieldvar test: String = "hello"override fun fromJSON(jsonText: String): Person {return Person("test")}}
}

会生成静态的字段,Java中访问如下:

System.out.println(Person.test);

伴生对象可以定义扩展函数,调用就像是该函数定义在伴生对象中定义的函数一样。

class Person(val name: String) {companion object {}
}fun Person.Companion.fromJSON(jsonText: String): Person {return Person("test")
}fun main() {val p = Person.fromJSON("")
}

4.4 对象表达式:改变写法的匿名内部类

object关键字不仅仅能用来声明单例式的对象,还能用来声明匿名对象。匿名对象替代了Java中匿名内部类的用法。

open class Person(val name: String) {open fun testFun() {println("Person testFun")}
}fun main() {object : Person("test") {override fun testFun() {println("object testFun")}}.testFun()
}

使用object创建了一个匿名内部类
与Java匿名内部类只能扩展一个类或实现一个接口不同,Kotlin的匿名对象可以实现多个接口或者不识闲接口。
注意与对象声明不同,匿名对象不是单例的。每次对象表达式被执行都会创建一个新的对象实例。
与Java的匿名类一样,可以访问创建它的函数中的变量,但是不同的是Java只能访问final类型的变量,而Kotlin中没有限制,还可以在对象表达式中修改变量的值。

fun main() {var num = 20object : Person("test") {override fun testFun() {println("object testFun")num++}}.testFun()
}

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

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

相关文章

林浩然与杨凌芸的时空约会奇遇记

林浩然与杨凌芸的时空约会奇遇记 The Time-Traveling Love Story of Lin Haoran and Yang Lingyun in the Java World 在那个阳光明媚、Java代码飞舞的日子里&#xff0c;程序员界的“情圣”林浩然和美丽聪明的数据分析师杨凌芸携手演绎了一场跨越时间与空间的爱情故事&#xf…

ZYNQ:PL-CAN总线功能应用

流程背景 前期基本实现PS端的CAN总线功能&#xff0c;现阶段的主要目的是实现PL端的CAN总线功能&#xff0c;需要采用CAN IP。 PL系统搭建 PL外设时钟源 搭建完vivado系统后&#xff0c;需要在sdk编程。但是在配置PL&#xff0d;CAN时&#xff0c;意识到CAN时钟值不清楚&…

蓝桥省赛真题|简单:分数

题目链接&#xff1a;https://www.lanqiao.cn/problems/610/learning/?page1&first_category_id1&second_category_id3&tags2018&name%E5%88%86%E6%95%B0 题不难&#xff0c;但是可以帮助编程时好的习惯的养成&#xff0c;更加注意一些细节。 注意几个地方︰…

《区块链公链数据分析简易速速上手小册》第10章:未来趋势和挑战(2024 最新版)

文章目录 10.1 区块链技术的发展方向10.1.1 基础知识10.1.2 重点案例&#xff1a;构建一个简单的智能合约步骤1: 创建智能合约步骤2: 部署智能合约步骤3: 使用Python与智能合约交互结语 10.1.3 拓展案例 1&#xff1a;探索 DeFi 应用准备工作实现步骤步骤1: 获取Compound市场数…

Jmeter接口测试实战篇:10分钟学会Jmeter的用法

一提到接口测试&#xff0c;通常大家会有这样的疑问&#xff1a;前端测试不是已经覆盖到各种业务逻辑了吗&#xff1f;为什么还要做接口测试&#xff0c;接口测试和前端测试是不是重复了&#xff1f;对于这个问题&#xff0c;可以从下面几个方面来解释&#xff1a; 什么是接口…

CSS3学习(二)

目录&#xff1a; 1. 字体属性 1.1 字体系列 1.2 字体大小 1.3 字体粗细 1.4 文字样式 1.5 字体复合属性 1.6 总结 2 文本属性 2.1 文本颜色 2.2 对齐文本 2.3 装饰文本 2.4 文本缩进 2.5 行间距 2.6 总结 1. 字体属性 1.1 字体系列 使用font-family属性定义文…

Redis篇----第二篇

系列文章目录 文章目录 系列文章目录前言一、Memcache 与 Redis 的区别都有哪些?二、Redis 是单进程单线程的?三、一个字符串类型的值能存储最大容量是多少?四、Redis持久化机制前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点…

optee imx8mm

总仓库 git clone https://github.com/Xsyin/imx8mqevk.git -b container_region 替换imx8mqevk中的optee-client git clone https://github.com/nxp-imx/imx-optee-client.git -b lf-5.15.32_2.0.0 用 5.15.32 kernel 会有如下报错&#xff0c;需要将optee os升级到分支 lf-…

【开源】JAVA+Vue.js实现大学计算机课程管理平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2.3 学生实验模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 实验课程档案表3.2.2 实验资源表3.2.3 学生实验表 四、系统展示五、核心代码5.1 一键生成实验5.2 提交实验5.3 批阅实…

不同的AI修改同一篇文章标题

提问AI 我写了一篇文章&#xff0c;请帮我把标题重新改一下&#xff1a;“比较不同AI分析同一个错误代码及给出解决方案的能力&#xff08;结果出我意料&#xff09;” 这篇文章的原地址为&#xff1a;https://blog.csdn.net/snans/article/details/136132211 答案对比结果&am…

Qt:Qt3个窗口类的区别、VS与QT项目转换

一、Qt3个窗口类的区别 QMainWindow&#xff1a;包含菜单栏、工具栏、状态栏 QWidget&#xff1a;普通的一个窗口&#xff0c;什么也不包括 QDialog&#xff1a;对话框&#xff0c;常用来做登录窗口、弹出窗口&#xff08;例如设置页面&#xff09; QDialog实现简易登录界面…

网络原理-TCP_IP(6)

网络层 在复杂的网络环境中确定一个合适的路径. IP协议 与TCP协议并列,都是网络体系中最核心的协议. 基本概念 主机:配有IP地址,但是不进行路由控制的设备; 路由器:即配有IP地址,又能进行路由控制; 节点:主机和路由器的统称; 协议头格式 4位版本号(version):指定IP协议的版…

【机器学习基础】决策树(Decision Tree)

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;机器学习 欢迎订阅&#xff01;后面的内容会越来越有意思~ ⭐特别提醒&#xff1a;针对机器学习&#xff0c;特别开始专栏&#xff1a;机器学习python实战 欢迎订阅&am…

OpenTitan- 开源安全芯片横空出世

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Linux——系统文件I/O

系统文件I/O 注&#xff1a;学习本次内容之前&#xff0c;最好先对C语言文件操作有所了解 本章思维导图&#xff1a; 注&#xff1a;思维导图对应的.xmind文件和.png文件都已同步导入至资源&#xff0c;可供免费查阅 在以前学习的C语言中&#xff0c;我们可以通过fwrite和fre…

通过玩游戏学会AWS

游戏名字&#xff1a; Cloud Quest 类型&#xff1a;亚马逊云科技官方出了一款 3D 角色扮演、虚拟城市建造形式的游戏实验课 进入方法&#xff1a;浏览器搜索 Cloud Quest&#xff08;或扫描下方二维码&#xff09;进入 Cloud Quest 课程页。 选择以下的链接 点击进行注册 进…

云计算基础-虚拟机迁移原理

什么是虚拟机迁移 虚拟机迁移是指将正在运行的虚拟机实例从一个物理服务器&#xff08;或主机&#xff09;迁移到另一个物理服务器&#xff08;或主机&#xff09;的过程&#xff0c;而不会中断虚拟机的运行。 虚拟机拟机迁移分类虚 热迁移&#xff1a;开机状态下迁移 冷迁…

单部10层电梯控制系列之UDT数据类型的建立(SCL代码)

这篇博客开始介绍单部10层电梯的完整控制程序编写过程,编程语言:SCL,控制器型号:S7-1200PLC。开篇博客我们介绍电梯控制用到的所有UDT数据类型。在学习本篇博客之前大家可以参考下面文章,了解博途PLC里的UDT数据类型是如何建立的。 博途UDT数据类型介绍: https://rxxw-…

P2P 应用

P2P 工作方式概述 在 P2P 工作方式下&#xff0c;所有的音频/视频文件都是在普通的互联网用户之间传输。 1 具有集中目录服务器的 P2P 工作方式 Napster 最早使用 P2P 技术&#xff0c;提供免费下载 MP3 音乐。 Napster 将所有音乐文件的索引信息都集中存放在 Napster 目录服…

Ubuntu Desktop 显示文件路径

Ubuntu Desktop 显示文件路径 1. GUI hot key2. CLIReferences 1. GUI hot key Ctrl L: 显示文件路径 2. CLI right click -> Open in Terminal -> pwd strongforeverstrong:~/Desktop$ pwd /home/strong/DesktopReferences [1] Yongqiang Cheng, https://yongqiang…