Singleton Pattern

单例模式是一种创建型设计模式,它的核心思想是确保一个类在整个应用程序运行期间只能创建一个实例,并提供全局访问这个实例的方法。 想象一个公司只有一台打印机,所有员工都需要使用它。你不会为每个员工都买一台打印机,而是让大家共享这一台。单例模式就是这个道理 - 对于某些资源,我们只需要一个实例就够了。 1. Object 关键字实现(最推荐) // 1. Object关键字实现单例 object DatabaseManager { private var connectionCount = 0 init { println("DatabaseManager 初始化") connect() } private fun connect() { println("连接到数据库") connectionCount++ } fun executeQuery(sql: String): String { return "执行查询: $sql, 连接数: $connectionCount" } fun getConnectionCount(): Int = connectionCount } // 使用示例 fun main() { println("=== Object关键字单例示例 ===") // 直接使用,无需获取实例 println(DatabaseManager.executeQuery("SELECT * FROM users")) println(DatabaseManager.executeQuery("SELECT * FROM orders")) println("连接数: ${DatabaseManager.getConnectionCount()}") // 验证是同一个实例 val ref1 = DatabaseManager val ref2 = DatabaseManager println("是否为同一个实例: ${ref1 === ref2}") } Object 关键字的特点 自动线程安全:Kotlin 编译器保证 object 的初始化是线程安全的,使用双重检查锁定机制。 懒加载:object 在第一次被访问时才会初始化,不是在程序启动时。 简洁性:代码最简洁,无需手动管理实例。 内存效率:编译后生成的字节码与手写的单例模式相当。 适用场景:大多数单例需求都可以用 object 解决,特别是工具类、管理器类等。 Object 关键字不够用的地方 需要构造参数 需要依赖注入 需要生命周期管理 需要复杂继承关系 什么条件需要单例模式? 资源管理类:数据库连接池、文件系统访问、网络连接管理器等需要统一管理系统资源的场景。 配置管理:应用程序配置、用户偏好设置等需要全局访问且保持一致性的数据。 缓存系统:内存缓存、图片缓存等需要全局共享且避免重复创建的组件。 日志记录器:应用日志管理,需要统一的日志记录入口。 硬件接口控制:打印机驱动、传感器控制等只能有一个实例操作的硬件接口。 什么条件能用单例模式? 无状态或状态不可变:单例对象最好是无状态的,或者状态是只读的,避免并发修改问题。 线程安全考虑:如果单例会被多线程访问,必须确保线程安全,Kotlin 的 object 关键字天然线程安全。 生命周期明确:单例的生命周期应该与应用程序一致,不需要手动销毁和重建。 依赖关系简单:单例不应该依赖太多外部资源,避免复杂的初始化逻辑。 测试友好:考虑到单元测试的需要,可以设计成可测试的形式,比如提供重置方法或使用依赖注入。

July 1, 2025 · 1 min

Delegate

class Delegate { val name: String = "default" operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "getValue" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value 已被赋值给 ${property.name} 于 $thisRef") } } class Example() { var name: String by Delegate() } fun main() { val e = Example() println(e.name) // getValue e.name = "hello" // hello 已被赋值给 name 于 org.example.Example@27973e9b } KProperty<*> 是表示属性元数据的接口,它包含了属性的名称、类型、注解等信息。 thisRef 指向Example类的实例。 getValue 返回值是String是因为在这个例子中,name 属性是 String。属性类型与 getValue 返回值类型必须一致。

June 30, 2025 · 1 min

Function Signature

看一个函数签名 inline fun <reified T : Comparable<T>, R : Any> Collection<T>.complexTransform( crossinline predicate: (T) -> Boolean = { true }, noinline keySelector: (T) -> String, transform: T.() -> R? ): Map<String, List<R>> where T : CharSequence 这个函数的意思是: 给 Collection 添加一个扩展方法 T 必须能比较大小,且是字符序列,且支持泛型具体化 R 不能是 null 类型 接收三个函数参数:过滤条件、键选择器、转换器 返回按键分组的转换结果 记录 返回类型(最简单的部分) : Map<String, List<R>> 返回一个 Map,键是String,值是List<R> 函数名和接收者 Collection<T>.complexTransform Collection<T>. 表示这是扩展函数,意思是给 Collection 类型"加"了一个方法,可以这样调用:myList.complexTransform(...) 泛型参数 <reified T : Comparable<T>, R : Any> reified T - 泛型具体化,可以在运行时知道 T 的具体类型 : Comparable<T> - T 必须能够比较大小 where T : CharSequence - 额外约束,T 还必须是字符序列 R : Any - R 不能是 null 类型 参数列表 参数 1: ...

June 29, 2025 · 1 min

Common Operations

只需要知道有这些操作 shuffle() vs shuffled() fun main() { val list: MutableList<Int> = mutableListOf(1, 2, 3, 4, 5) val shuffledList = list.shuffled() // shuffled 返回打乱后的集合 println(list) println(shuffledList) list.shuffle() println(list) } map() fun main() { val numbers = listOf(1, 2, 3) val doubled = numbers.map { it * 2 } // [2, 4, 6] val strings = numbers.map { "数字$it" } // ["数字1", "数字2", "数字3"] } toSet() fun main() { val list = listOf(1, 2, 3, 2, 1) val set = list.toSet() println(set) // [1, 2, 3] } sortedDescending() fun main() { val list = listOf(1, 2, 3, 2, 1) val sorted = list.sortedDescending() //降序排列 println(sorted) // [3, 2, 2, 1, 1] } filter() fun main() { val list = listOf(2, 4, 3, 6, 1) val filter = list.filter { it % 2 == 0 } println(filter) // [2, 4, 6] } groupingBy { }.eachCount() fun main() { val words = listOf("apple", "banana", "apple", "cherry", "banana") val counts = words.groupingBy { it }.eachCount() println(counts) // {apple=2, banana=2, cherry=1} } entries() fun main() { val map = mapOf(1 to 2, 3 to 4) map.entries.forEach { (key, value) -> println("$key, $value") } } addAll() fun main() { val list1 = mutableListOf(1, 23, 45, 6) val list2 = listOf(666666, 888888) list1.addAll(list2) println(list1) // [1, 23, 45, 6, 666666, 888888] } buildList { } fun main() { val list = buildList { add(1) add(2) addAll(listOf(3, 4)) } println(list) // [1, 2, 3, 4] } require() - 检查参数 fun divide(a: Int, b: Int): Int { require(b != 0) { "除数不能为 0" } return a / b } check() - 检查当前状态 class BankAccount { private var balance: Int = 0 fun withDraw(amount: Int) { check(this.balance >= amount) { "余额不足" } this.balance -= amount } } Elvis 运算符 // 本质就是 val displayName = if (name != null) name else "默认值" val displayName = name ?: "默认值 takeIf - 条件取值 val validAge = age.takeIf { it > 0 } // 大于0才返回,否则null val invalidAge = age.takeUnless { it > 100 } // 小于等于100才返回 let - 空安全调用,转换结果 // 最常用:空安全调用 val user: User? = getUser() user?.let { println("用户名: ${it.name}") println("年龄: ${it.age}") } // 只有user不为null才执行 // 转换结果 val result = "hello".let { it.uppercase() + "!!!" } // result = "HELLO!!!" apply - 链式调用时有用 // 没有apply的写法 val textView = TextView(this) textView.text = "标题" textView.textSize = 18f textView.setTextColor(Color.BLACK) addView(textView) // 需要保存变量 // 用apply - 可以直接链式调用 addView(TextView(this).apply { text = "标题" textSize = 18f setTextColor(Color.BLACK) }) // 不需要临时变量 with/run - 用处不大,装逼用的 // with 的所谓"优势" with(sharedPreferences.edit()) { putString("name", "张三") putInt("age", 25) apply() } // 不用 with 直接写 val editor = sharedPreferences.edit() editor.putString("name", "张三") editor.putInt("age", 25) editor.apply() observable 简化版 fun <T> observable(initialValue: T, onChange: (KProperty<*>, T, T) -> Unit) { return object { private var value = initialValue operator fun getValue(thisRef: Any?, property: KProperty<*>): T { return value // get时直接返回 } operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) { val oldValue = value value = newValue // 先设置新值 onChange(property, oldValue, newValue) // 然后调用你的回调 } } } by 本质是把属性的 get/set 操作委托给另一个对象 ...

June 27, 2025 · 3 min

Create a RESTful Web Service With a Database Using Spring Boot

没有什么比官方教程更好的了 官方链接 那就做个简单的 demo 方便以后查询 创建项目,添加依赖 Web | Spring Web SQL | Spring Data JDBC SQL | H2 Database 创建数据类 @Table("TASKS") data class Task(val name: String, @Id var id: String? = null) 创建 Controller 层 @RestController @RequestMapping("/") class TaskController(private val service: TaskService) { @GetMapping fun listTasks() = ResponseEntity.ok(service.findTasks()) @GetMapping("/{id}") fun getTaskById(@PathVariable id: String) = service.findTaskById(id).toResponseEntity() @DeleteMapping("/{id}") fun delete(@PathVariable id: String) = service.deleteTaskById(id) @PostMapping fun post(@RequestBody task: Task): ResponseEntity<Task> { val savedTask = service.save(task) return ResponseEntity.created(URI("/${savedTask.id}")).body(savedTask) } private fun Task?.toResponseEntity() = this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build() } 创建数据库接口 interface TaskRepository : CrudRepository<Task, String> 实现Service层 @Service class TaskService(private val db: TaskRepository) { fun findTasks(): List<Task> = db.findAll().toList() fun findTaskById(id: String): Task? = db.findByIdOrNull(id) fun save(task: Task): Task = db.save(task) fun deleteTaskById(id: String) = db.deleteById(id) } 创建数据库初始化脚本 在 src/main/resources/schema.sql 创建: CREATE TABLE IF NOT EXISTS tasks ( id VARCHAR(60) DEFAULT RANDOM_UUID() PRIMARY KEY, name VARCHAR NOT NULL ); 配置application.properties spring.application.name=demo spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:file:./data/testdb spring.datasource.username=name spring.datasource.password=password spring.sql.init.schema-locations=classpath:schema.sql spring.sql.init.mode=always 这里的 name 是项目名称,第三行的数据库 URL 有两种选择: ...

June 23, 2025 · 2 min

快速排序

指针版本看不懂?没关系,跟我一起"魔修"这个简化版本!重点是掌握分治思想,而不是纠结于内存管理的细节。 复杂度分析 时间复杂度:O(n log n) - 平均情况下每次都能均匀分割 空间复杂度:O(n) - 比标准版本的 O(log n) 更大,因为需要创建新列表 主要思路 随便选一个数作为基准值,比如数组中间的数。 分别找出比这个基准值大,中,小的所有元素,此时有三部分。 对每一部分再次执行步骤二,把最基本的结果进行连接,得到排序后的序列。 每一部分一直执行步骤二,那么终止条件是什么呢,显然是,待排序的序列只有一个元素,那么此时,不需要再进行排序了。 示例代码 fun quickSort(list: List<Int>): List<Int> { if (list.size <= 1) return list // 递归终止条件 val base = list[list.size / 2] // 随便选一个基准值,这里选中间的元素 val small = list.filter { it < base } // 找出比基准值小的元素 val equal = list.filter { it == base } // 找出与基准值相等大小的元素 val large = list.filter { it > base } // 找出比基准值大的元素 return quickSort(small) + equal + quickSort(large) // 递归分别处理每一部分,将结果进行拼接 }

June 22, 2025 · 1 min

什么是协议?

例子 假设你拥有两台计算机 A,B,你想让 A 发送今天的天气情况给 B。 那么 A 可以发送 “今天天气是晴天” 或者 “it’s sunny today” 或者其他任何方式。 但是 B 无法识别所有情况,对于 B 来说,无法理解你说的每一句话,每一种情况。 于是,B 提议告诉 A,规定 A 每次发消息的格式都是 “天气信息:晴天”。 双方达成约定之后,B 每次就可以知道 A 会发送什么格式的信息,并且在固定位置获取到对应内容。 很常见的通信协议比如 HTTP,TCP,SSH,他们都是一种协议的名称,某一种传输约定的名字罢了。如果你对 Web 开发、接口设计或前端与服务器的数据交互感兴趣,接下来,我们可以了解一下 HTTP 协议都有哪些约定。

June 21, 2025 · 1 min

连接云服务器

前置条件 拥有一台云服务器,在厂商平台处查看 IP 地址,用户名以及密码。 拥有一台网络良好的电脑。 开始 打开你的命令行工具。如果你是 Windows,比如 PowerShell;在 Macos 上,打开你的 终端.app。 使用 ssh 工具,连接你的云服务器。常用命令格式:ssh -p 端口号,默认22 用户名@服务器ip,例如 ssh -p 22 root@1.1.1.1。 如果你的 ssh 端口是 22,也可以省略 -p,那么就是 ssh root@1.1.1.1。 Enter后稍等,输入密码,连接成功!现在,你在终端输入的所有命令将在你的云服务器环境下执行。你可以尝试一些简单的命令。例如 ls -la。 如果你想关闭连接,你将使用 exit。

June 21, 2025 · 1 min

Hello World

Hello world

June 20, 2025 · 1 min