6、深入解析Kotlin类与对象:构造、伴生、单例全面剖析
前言
本篇文章将带您了解Kotlin编程中的重要概念:类及构造函数、访问修饰符、伴生对象和单例模式。就像搭积木一样,我们会逐步揭开这些概念的面纱,让您轻松理解它们的作用和用法。无论您是编程新手还是有经验的开发者,本文都将为您提供有趣而易懂的内容,帮助您更好地掌握Kotlin中类与对象的重要知识点。让我们一起开始这段有趣的学习之旅吧!
一、Kotlin 的类以及构造函数
1、声明一个类
当你使用 class 关键字在 Kotlin 中声明一个类时,其基本的语法格式如下所示:
class ClassName {// 类的成员变量和方法在这里定义
}
2、关于Kotlin类的继承和接口实现的规则
(1)继承父类
如果一个类有父类,你可以在类的声明中使用冒号 : 后面跟着父类的名称。这称为类的继承。例如:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 在这里可以进行一些初始化操作}
}
-
继承父类无参构造函数:如果父类有一个无参构造函数,子类可以省略括号,并且不需要在子类中声明构造函数。这适用于父类的构造函数没有参数的情况,或者所有参数都有默认值。
-
继承父类有参构造函数:如果父类的构造函数有参数,或者你想在子类中添加自己的构造函数,就需要根据具体情况使用括号并传递必要的参数。
(2)默认父类
如果一个类没有显式声明父类,它的默认父类是 Any 类,而不是 Object 类。Any 是 Kotlin 根类层次结构的顶层类型。所有的 Kotlin 类都直接或间接地继承自 Any 类。
Any 类的声明如下:
public open class Any
在 Kotlin 中,默认情况下,所有类都隐式地继承自 Any 类,即使你没有明确指定。因此,以下两个类声明是等价的:
class MyClass
class MyClass : Any()
这就是为什么你在 Kotlin 中无需显式指定父类,而默认情况下所有类都已经继承自 Any。
需要注意的是,Any 类定义了一些通用方法,如 equals、hashCode 和 toString,因此所有的 Kotlin 类都会具备这些方法。
(3)接口实现
在 Kotlin 中,可以在类的声明后面使用冒号 : 来实现一个或多个接口。你不需要显式使用 implements 关键字。例如:
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity(), View.OnClickListener {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val button = findViewById<Button>(R.id.button)button.setOnClickListener(this)}override fun onClick(v: View?) {when (v?.id) {R.id.button -> {// 在这里定义按钮点击事件的逻辑}}}
}
在上面的示例中,我们首先让 MainActivity 类实现了 View.OnClickListener 接口,并重写了其中的 onClick 方法。然后,我们在 onCreate 方法中使用 setOnClickListener(this) 将当前 MainActivity 对象作为按钮的点击监听器。
在 Kotlin 中,你是可以先写接口后写父类,或者先写父类后写接口,都是完全合法的。在类的声明中,继承父类和实现接口的顺序可以根据你的需求进行灵活安排。
3、open 关键字的作用
在 Kotlin 中,默认情况下,类是 public 和 final 的,这意味着它们对外部可见,并且不能被继承。如果你希望其他类能够继承这个类,你需要使用 open 关键字来声明这个类,使它变成可继承的。
在 Kotlin 中,修饰符的含义如下:
public:表示该成员(类、函数、属性等)在任何地方都可见。final:表示类、函数或属性不能被继承或覆盖(重写),在类级别指的是类不能被继承。open:用于修饰类、函数或属性,表示它们可以被继承或覆盖。
如果你想要创建一个可被继承的类,你需要使用 open 修饰符。
例如,当你想在 Android 应用中创建一个可被继承的 MainActivity 类时,你需要在类的声明前面加上 open 修饰符,以允许其他类继承它。这样,其他类就可以继承 MainActivity 并在子类中进行扩展。
以下是一个示例,展示如何在 Android 应用中创建一个可被继承的 MainActivity 类:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivityopen class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 在这里可以进行一些初始化操作}
}
在这个示例中,我们创建了一个名为 MainActivity 的类,继承自 AppCompatActivity,这是 Android 中用于创建活动(Activity)的基类。注意,在类的声明中,我们使用了 open 修饰符,允许其他类继承这个 MainActivity。
其他类可以继承这个 MainActivity 并在子类中进行扩展,例如:
class CustomActivity : MainActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 在这里可以进行自定义的初始化操作}
}
在上面的示例中,CustomActivity 类继承自 MainActivity,并在子类中重写了 onCreate 方法,实现了自定义的初始化操作。
总结,通过使用 open 修饰符,你可以在 Android 应用中创建一个可被继承的基类,让其他类能够继承它并在子类中添加自己的功能。
4、主构造函数和次构造函数
构造函数是用于创建对象的特殊方法,它在对象创建时被调用,通常用于执行初始化操作。在 Kotlin 中,类可以有一个主构造函数和多个次构造函数。
(1)主构造函数
当涉及到在 Kotlin 类中创建实例时,主构造函数扮演着重要的角色。主构造函数允许在对象被实例化时接收参数,并且对对象的属性进行初始化。以下是关于主构造函数的概要:
- 声明方式:主构造函数是类定义的一部分,紧随类名后面的括号中可以包含参数列表。如果主构造函数没有其他修饰符,那么括号可以省略。
- 参数传递:主构造函数的参数可以在整个类范围内使用,不仅限于初始化代码块。你可以将这些参数直接用于属性的初始化。
- 初始化代码块:主构造函数也可以包含初始化代码块,使用
init关键字定义。在这个代码块中,你可以编写在对象初始化时执行的代码。
以下是一个示例,展示了如何声明主构造函数、使用参数初始化属性,并在初始化代码块中执行逻辑,其中使用了自定义视图类 CustomView:
class CustomView(context: Context, attrs: AttributeSet): View(context, attrs) {private val paint: Paint = Paint()init {paint.color = Color.BLACK}
}
在上述示例中,CustomView 类继承自 View,并在主构造函数中接收了 context 和 attributeSet 参数。
在类的主体中,声明了一个名为 paint 的私有属性,并在初始化时进行了属性初始化。使用了 Paint 类,这是 Android 中用于绘制图形和文本的工具类。
在类的 init 块中,设置了 paint 的颜色为黑色(Color.BLACK)。这将确保 paint 对象在视图实例化时就被配置为黑色,以便在后续的绘制操作中使用。
当创建 CustomView 的实例时,会触发 init 块中的代码执行,从而完成属性的初始化设置。init 块是一个在对象实例化时执行初始化代码的地方,它允许你在构造函数中接收参数,并在 init 块中执行必要的初始化逻辑。在自定义视图类中,你可以使用 init 块来确保对象在使用之前处于正确的状态。
(2)次构造函数
次构造函数是在 Kotlin 中用于提供额外的构造函数选项的一种方式。它允许你在同一个类中定义多个不同参数集的构造函数,以满足不同的初始化需求。次构造函数通常调用主构造函数或者其他次构造函数,确保共享的初始化逻辑被重复使用。
以下是关于次构造函数的一些重要信息:
- 声明方式:次构造函数通过
constructor关键字声明,紧随其后的括号内包含参数列表。次构造函数可以有不同的参数集,但不能直接初始化属性。 - 调用主构造函数:在次构造函数内部,你可以使用
this()调用主构造函数,确保共享的初始化逻辑被执行。调用主构造函数时,可以传递相应的参数。 - 调用其他次构造函数:在次构造函数内部,你也可以使用
this()调用同一个类中的其他次构造函数。这样可以避免重复编写初始化逻辑。
以下是一个示例,展示了如何在 CustomView 类中使用次构造函数:
class CustomView : View {constructor(context: Context) : this(context, null, 0)constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {initialize(context, attrs, defStyleAttr)}private fun initialize(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {// 在这里执行初始化操作}}
在上面的示例中,CustomView 类继承自 Android 中的 View 类,并且有三个构造函数:
- 第一个构造函数接收一个
context参数,这是主构造函数。它通过调用次构造函数,为attrs和defStyleAttr提供默认值。 - 第二个构造函数接收
context和attrs参数,调用了具有更多参数的次构造函数。 - 第三个构造函数接收
context、attrs和defStyleAttr参数,它是主要的构造函数。它通过调用父类View的构造函数来实现,并在构造函数内部调用了initialize函数来执行额外的初始化操作。
(3)大家来找茬
可以尝试将下面示例代码放到编译器中,会发现编译不通过, super 处会提示 “Primary constructor call expected”。问题出在了哪里?
class CustomView(context: Context) : View(context) {constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {initialize(context, attrs, 0)}constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {initialize(context, attrs, defStyleAttr)}private fun initialize(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {// 在这里执行初始化操作}}
错误消息 “Primary constructor call expected” 意味着在构造函数的调用位置需要调用主构造函数,但实际上没有调用。
在 Kotlin 中,如果一个类有一个主构造函数,那么所有的次构造函数必须委托给主构造函数。这是语言的规定,确保类的初始化能够始终通过主构造函数完成。
在我们的代码中,主构造函数是 class CustomView(context: Context) : View(context) 这一行。由于它是一个主构造函数,所有的次构造函数必须通过 this() 来调用主构造函数。因此,不能在次构造函数中使用 super 关键字来调用父类的构造函数。
以下是修正后的代码示例,满足了 Kotlin 的构造函数规定:
class CustomView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : View(context, attrs, defStyleAttr) {constructor(context: Context) : this(context, null, 0) constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)init {initialize(context, attrs, defStyleAttr)}private fun initialize(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {// 在这里执行初始化操作}
}
在这个修正后的代码中,主构造函数是 class CustomView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : View(context, attrs, defStyleAttr) ,并且所有的次构造函数都通过 this() 来委托主构造函数。统一在 init 添加初始化操作,以满足我们的需求。
(4)@JvmOverloads 注解
使用了 @JvmOverloads 注解来为 CustomView 类的构造函数生成所有可能的参数组合,以简化构造函数的使用。这样,你可以使用主构造函数,也可以选择性地使用次构造函数。
我们将上述代码再次改造一下:
class CustomView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {init {initialize(context, attrs, defStyleAttr)}private fun initialize(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {// 在这里执行初始化操作}
}
使用这个示例,你可以这样创建一个 CustomView 实例:
val customView1 = CustomView(context)
val customView2 = CustomView(context, attrs)
val customView3 = CustomView(context, attrs, defStyleAttr)
@JvmOverloads 是 Kotlin 中的一个注解,用于在编译器级别生成多个重载版本的函数或构造函数,以便在 Java 代码中更方便地使用。这个注解通常用于改善 Kotlin 类在 Java 代码中的互操作性。
在特定情况下,Kotlin 的函数或构造函数可能具有默认参数,这使得在 Kotlin 中调用它们非常方便,因为可以省略部分参数。但在 Java 中,调用具有默认参数的 Kotlin 函数会变得不那么直观,因为 Java 不直接支持 Kotlin 的默认参数语法。
这时,@JvmOverloads 就派上用场了。当你在 Kotlin 函数或构造函数上使用 @JvmOverloads 注解时,Kotlin 编译器会自动为每个省略参数的版本生成一个重载版本,以便在 Java 代码中使用。这样,Java 开发者就可以根据需要传递不同数量的参数,而不必理解或设置 Kotlin 的默认参数。
具体来说,对于构造函数来说,@JvmOverloads 会在编译时为每个可能的参数组合生成一个单独的构造函数,以确保 Java 开发者能够使用所有参数组合。
总结:@JvmOverloads 是一个用于改善 Kotlin 代码在 Java 中互操作性的注解,它为具有默认参数的函数或构造函数生成重载版本,使得 Java 开发者能够更方便地使用这些函数或构造函数。
(5)init 函数,父类的构造函数与次构造函数之间调用顺序
- 首先,会执父类的
init块(如果有的话) - 其次,会执行父类的构造函数(主构造函数或者被次构造函数调用的构造函数)。
- 然后,会执行当前类的
init块(如果有的话)。 - 最后,会执行当前类的次构造函数(如果有的话)。
以下是一个示例来说明这个调用顺序:
open class Parent {init {println("Parent's init block")}constructor() {println("Parent's constructor")}
}class Child : Parent {constructor() : super() {println("Child's constructor")}init {println("Child's init block")}
}fun main() {val obj = Child()
}
在这个示例中,当创建 Child 类的实例时,首先会调用 Parent 类的 init 块,其次是 Parent 类的构造函数,然后执行 Child 类的 init 块,最后执行 Child 类的次构造函数。因此,输出将是:
Parent's init block
Parent's constructor
Child's init block
Child's constructor
二、访问修饰符
在 Kotlin 中,访问修饰符用于控制类、接口、函数、属性等成员的可见性。Kotlin 提供了一些访问修饰符,用于指定成员在哪些范围内可见。以下是常见的访问修饰符:
public(默认):没有显式指定访问修饰符时,默认为public。在这种情况下,成员对所有其他代码都可见。private:成员只在声明它的文件中可见。私有成员对同一文件中的其他类都不可见。protected:成员在声明它的类及其子类中可见。但在同一包内的其他类中不可见。internal:成员对于同一模块内的所有代码都可见。模块是一组一起编译的 Kotlin 文件。它更多的去用于我们现在做一些项目的结构化的扩展的时候去使用这个修饰符。
示例:
class MyClass {private val privateVar = 1protected val protectedVar = 2internal val internalVar = 3val publicVar = 4 // 默认为 public
}fun main() {val obj = MyClass()println(obj.internalVar) // 可以访问,因为处于同一模块内println(obj.publicVar) // 可以访问,因为是 public// 下面的代码不能编译通过,因为 private 和 protected 变量不在可见范围内// println(obj.privateVar)// println(obj.protectedVar)
}
注意:
- 对于顶层声明(不在类内部的声明),只有
public和internal修饰符有效。 internal修饰符在 Kotlin 中非常有用,它允许模块内的代码访问成员,同时限制了模块之间的访问。private和protected对于顶层声明(不在类内部的声明)是无效的,因为它们只对类内部的成员可见性起作用。
综上所述,访问修饰符用于控制代码中成员的可见性范围,确保代码的封装和安全性。
补充一下,什么是模块?
每当创建一个 module,这就是一个模块。如果你的一个变量名通过internal修饰的话,在同一个模块内的时候,是可以互相调用的,但是跨模块的话,你是无法访问到这个变量的。这就是 kotlin 的这个访问修饰符,它更多的去用于我们现在做一些项目的结构化的扩展的时候去使用这个修饰符。

三、伴生对象
在 Kotlin 中,每个类都可以有一个伴生对象(Companion Object),它是该类的一个特殊对象实例。伴生对象的成员可以像 Java 静态成员一样在类级别上访问,不需要通过类的实例来调用。伴生对象通常用于创建类级别的静态成员、工厂方法、单例模式等。
在 JAVA 中,我们经常看到这样的一段代码,就是工具类,里面定义了一些的静态方法,可以直接用工具类的类名 . 方法名,去调用它。如下所示:
public class StringUtils {public static final String EMPTY = "";public static boolean isEmpty(CharSequence cs) {return cs == null || cs.length() == 0;}public static boolean isNotEmpty(CharSequence cs) {return !StringUtils.isEmpty(cs);}
}
但是在 kotlin 中是没有静态方法的,Kotlin 要怎么改写呢?
方法有两种,第一种办法是我们在《Kotlin 代码与 Java 代码集成》中介绍过,使用 @JvmStatic 注释暴露 Kotlin 类的静态方法。
那么第二种方法就是现在要介绍的伴生对象 Companion Object ,它必须被写在一个类的内部,作为这个类的伴生对象。伴生对象可以包含成员函数、属性和初始化代码块,类似于 Java 中的静态成员。它的一个主要作用是允许在不创建类实例的情况下访问这些成员,就像访问静态成员一样。
那么我们来改写一下:
class StringUtils {companion object {const val EMPTY = ""fun isEmpty(cs: CharSequence?): Boolean {return cs.isNullOrEmpty()}fun isNotEmpty(cs: CharSequence?): Boolean {return !isEmpty(cs)}}
}
在上面的示例中,StringUtils 类中定义了一个伴生对象,其中包含一个常量 EMPTY 和 两个方法isEmpty和isNotEmpty。那么就可以在类级别上直接访问伴生对象的成员和方法,就像访问类的静态成员和静态方法一样。
在 Java 代码中调用,如下所示:
public class JavaExample {public static void main(String[] args) {String str = "";boolean isEmpty = StringUtils.Companion.isEmpty(str);boolean isNotEmpty = StringUtils.Companion.isNotEmpty(str);System.out.println("isEmpty: " + isEmpty);System.out.println("isNotEmpty: " + isNotEmpty);}
}
注意,StringUtils.Companion 中的 Companion 是伴生对象的默认名称,因为我们并未显式地为我们的伴生对象命名,伴生对象的名称可以省略,那么此时使用默认的名称 Companion 。
实际上伴生对象在编译好以后会在这个类的内部生成一个静态对象。叫companion的对象,那么我们Java在调用的时候,实际上是通过这个类的companion对象去调用companion内部的一些变量。
如果你为伴生对象显式命名,例如 MyCompanion,那么需要使用该名称来访问伴生对象的成员。以下是一个使用了命名伴生对象的示例:
class StringUtils {companion object MyCompanion {const val EMPTY = ""fun isEmpty(cs: CharSequence?): Boolean {return cs.isNullOrEmpty()}fun isNotEmpty(cs: CharSequence?): Boolean {return !isEmpty(cs)}}
}
在 Java 代码中调用,如下所示:
public class JavaExample {public static void main(String[] args) {String str = "";boolean isEmpty = StringUtils.MyCompanion.isEmpty(str);boolean isNotEmpty = StringUtils.MyCompanion.isNotEmpty(str);System.out.println("isEmpty: " + isEmpty);System.out.println("isNotEmpty: " + isNotEmpty);}
}
四、单例
半生对象在 Kotlin 中还有一个非常有用的特性,就是可以用于声明单例。在前面的文章中,我们已经介绍了通过 object 关键字来创建匿名的内部类,这种方式其实也可以用来实现单例。然而,更加推荐的写法是利用伴生对象来创建单例。
使用伴生对象来实现单例具有许多优点:
- 线程安全: 伴生对象的属性和方法在类加载时进行初始化,确保了线程安全。这样即使在多线程环境下也能保证只有一个实例被创建。
- 延迟初始化: 伴生对象的属性和方法是在首次访问时才会被初始化,这也被称为"惰性初始化"。这种方式能够节省资源,只有在实际需要时才会创建实例。
- 清晰可见: 伴生对象使得单例模式的实现更加清晰和易于理解。它将单例的代码组织在一起,使得代码结构更加整洁。
总的来说,虽然在 Kotlin 中可以使用 object 关键字来创建单例,但更加推荐使用伴生对象来实现。这种方式不仅更加优雅,还能够带来诸多优势,如线程安全和延迟初始化等。如果你正在考虑实现单例模式,不妨考虑使用伴生对象来达到更好的效果。
以下是如何使用伴生对象来实现单例模式的示例:
class Single private constructor() {companion object {fun get(): Single {return Holder.instance}}private object Holder {val instance = Single()}
}
Single类的主构造函数被声明为私有,这意味着外部无法直接通过构造函数来实例化该类。- 在
Single类的伴生对象中,我们定义了一个名为get的方法,用于获取Single类的单例实例。该方法返回了一个Holder内部对象的实例,这是通过Holder.instance访问的。 - 内部的
Holder对象的实例化是通过 Kotlin 中的对象声明(object)实现的。由于 Kotlin 的对象声明在首次访问时就会被初始化,且只会被初始化一次,这确保了线程安全的单例创建。
上述代码展示了一种在 Kotlin 中使用伴生对象来创建单例的方式,这是一种非常常见的做法。这种方式结合了私有的主构造函数和内部对象,确保了单例的线程安全性和延迟初始化。
相关文章:
6、深入解析Kotlin类与对象:构造、伴生、单例全面剖析
前言 本篇文章将带您了解Kotlin编程中的重要概念:类及构造函数、访问修饰符、伴生对象和单例模式。就像搭积木一样,我们会逐步揭开这些概念的面纱,让您轻松理解它们的作用和用法。无论您是编程新手还是有经验的开发者,本文都将为…...
【开源ESP32谷歌恐龙小游戏】【游戏演示和介绍】LVGL ST7789 适用于Arduino
【源码及教程地址-持续更新】 ESP32 C3 LVGL 迷你小电视 Google谷歌恐龙小游戏 1.9寸LCD显示屏开发板 ST7789 适用于Arduino开发板,教程,资料,程序,代码,PDF手册 【开源 & ESP32谷歌恐龙小游戏】【游戏演示和介绍】LVGL ST7789 适用于Arduin...
openCV实战-系列教程7:轮廓检测2与模板匹配(轮廓检测/轮廓特征/轮廓近似/轮廓边界矩阵/轮廓边界圆/模版匹配)、原理解析、源码解读
🧡💛💚💙💜OpenCV实战系列总目录 打印一个图片可以做出一个函数: def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows() 1、轮廓特征与近似 1.1 轮廓特征 前面我们计算了…...
cs231n_1_IntroToConv
参考的视频来自如下链接https://www.bilibili.com/video/BV1Ed4y1b7bm/ 参考笔记如下https://blog.csdn.net/TeFuirnever/article/details/89059673 x.1 CV历史 生物快速发展于5.4亿年前,那时的化石显示生物进化出了视觉,视觉使得生物多样性大爆炸。 …...
OPENCV实现SURF特征检测
1、SURF优点:SIFT速度慢,一次出现了SURF;2、使用SURF步骤:surf = cv2.xfeatures2d.SURF_create()kp,des = surf.detectAndComputer(img,mask)# -*- coding:utf-8 -*- """ 作者:794919561 日期:2023/8/31 """# -*-...
Android Gradle 同步优化
作者:究极逮虾户 很多人听到方法论三个字,就觉得我要开始pua,说我阿里味,但是我觉得这个查问题的方式可能会对大家有点帮助。 很多人都会有这样的困扰,给你的一个工作内容是一个你完全陌生的东西,第一选择…...
BeautifulSoup:学习使用BeautifulSoup库进行HTML解析和数据提取。
BeautifulSoup是一个用于解析HTML和XML文档的Python库。它可以帮助我们从网页中提取数据,并以易于操作的方式进行分析。 以下是使用BeautifulSoup进行HTML解析和数据提取的基本语法: 安装BeautifulSoup库:首先,你需要在你的Python…...
基于沙猫群算法优化的BP神经网络(预测应用) - 附代码
基于沙猫群算法优化的BP神经网络(预测应用) - 附代码 文章目录 基于沙猫群算法优化的BP神经网络(预测应用) - 附代码1.数据介绍2.沙猫群优化BP神经网络2.1 BP神经网络参数设置2.2 沙猫群算法应用 4.测试结果:5.Matlab代…...
PCL 判断三点共线(三维空间)
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 继续之前的思路PCL 判断两条线段的平行性(三维空间),我们可以把判断三点共线看做是判断两条线段是否具有平行性,且这两条线段共用其中一个端点,基于此当这两条线段平行时,则证明这三点共线。 二、实现代码 /…...
【数据库】事务(概念和特点)
一、 什么是事务: 事务是在数据库中执行的一系列操作单元,这些操作要么全部成功提交,要么全部失败回滚。 二、事务的特点: 原子性(Atomicity):事务是一个不可分割的操作单元,要么…...
LA@齐次线性方程组解的结构
文章目录 齐次线性方程组解的结构🎈解的性质齐次线性方程组的解的线性组合还是方程组的解基础解系通解 定理:齐次线性方程组基础解系存在定理齐次线性方程组的基础解系包含的向量个数(秩)👺应用和示例推论1推论2推论3:转置矩阵对的乘积秩的性质非自由未知…...
Docker修改容器ulimit的全部方案及各方案的详细步骤
要修改Docker容器的ulimit(用户资源限制),有以下三种方案,每个方案的详细步骤如下: 方案一:在Dockerfile中设置ulimit 打开您的Dockerfile。在文件中添加以下命令来修改ulimit:RUN ulimit -n …...
进程间通信-Binder
Binder Binder框架概述服务端Binder驱动客户端 设计服务端和客户端设计服务端客户端设计 Binder与ServiceServiceAIDL 保证包裹内参数顺序IMusicPlayerServiceProxyStub 系统服务中的Binder对象ServiceManger管理的服务理解Manger功能快捷键合理的创建标题,有助于目…...
一个简单的vim例子
一.欢迎来到我的酒馆 在本章节介绍vim工具。 目录 一.欢迎来到我的酒馆二.什么是vim三.开始使用vim 二.什么是vim 2.1什么是vim vim是一种Linux命令行类型的文本编辑器。vim指的是"vi improved",意思是vi工具的升级版。vim是基于vi实现的&#x…...
sql server 备份到网络共享
场景:sql server服务器A将数据库备份文件备份到服务器B 1)服务器B创建共享目录 这里我将 D:\ProDbBak 共享,并且Everyone完全控制 2)sql server服务器A能够访问服务器B共享目录,并且能完全控制 3)修改服务…...
程序与进程
一、程序是怎么被执行的 1.在程序中,由引导代码去调用程序中得main函数,而这个过程由链接器完成,链接器将引导代码链接到我们的应用程序构成可执行文件。 2.程序运行需要通过操作系统的加载器来实现,加载器是操作系统中的程序&a…...
大模型从入门到应用——LangChain:链(Chains)-[链与索引:图问答(Graph QA)和带来源的问答(QA with Sources)]
分类目录:《大模型从入门到应用》总目录 图问答(Graph QA) 创建图 在本节中,我们构建一个示例图。目前,这对于较小的文本片段效果最好,下面的示例中我们只使用一个小片段,因为提取知识三元组对…...
spark sql 数据倾斜--join 同时开窗去重的问题优化
spark sql 数据倾斜–join 同时开窗去重的问题优化 文章目录 spark sql 数据倾斜--join 同时开窗去重的问题优化结论1. 原方案:join步骤时,同时开窗去重数据倾斜 2. 优化2.1 参数调优2.2 SQL优化 背景: 需求:在一张查询日志表中&a…...
lv3 嵌入式开发-linux介绍及环境配置
目录 1 UNIX、Linux和GNU简介 2 环境介绍 3 VMwareTools配置 4 vim配置: 1 UNIX、Linux和GNU简介 什么是UNIX? unix是一个强大的多用户、多任务操作系统,支持多种处理器架构 中文名 尤尼斯 外文名 UNIX 本质 操作系统 类型 分时操作系统 开…...
RabbitMQ工作模式-路由模式
官方文档参考:https://www.rabbitmq.com/tutorials/tutorial-four-python.html 使用direct类型的Exchange,发N条消息并使用不同的routingKey,消费者定义队列并将队列routingKey、Exchange绑定。此时使用direct模式Exchange必须要routingKey完成匹配的情况下消息才…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
Python爬虫实战:研究Restkit库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...
HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...
结构化文件管理实战:实现目录自动创建与归类
手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题,进而引发后续程序异常。使用工具进行标准化操作,能有效降低出错概率。 需要快速整理大量文件的技术用户而言,这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB,…...
