当前位置: 首页 > news >正文

仓颉编程语言青少年基础教程:class(类)(上)

仓颉编程语言青少年基础教程:class(类)(上)

class (类)类型是面向对象编程中的经典概念,仓颉中同样支持使用 class 来实现面向对象编程。class 与 struct 的主要区别在于:class 是引用类型,struct 是值类型,它们在赋值或传参时行为是不同的;class 之间可以继承,但 struct 之间不能继承。注意:class 只能定义在源文件的顶层作用域。

class 是面向对象编程的核心类型,与 以前介绍的struct(结构)类型 的核心区别在于:

引用类型:赋值或传参时传递引用(多个变量指向同一对象);

支持继承:类之间可通过单继承复用代码。

class 类型

class 类型的定义以关键字 class 开头,后跟 class 的名字,接着是定义在一对花括号中的 class 定义体。class 定义体中可以定义一系列的成员变量(member variables)、成员属性(member properties)、静态初始化器(static initializers)、构造函数(constructors)、成员函数(member functions)和操作符函数(operator functions)。

【关于Property(属性)的说明

在仓颉语言(Cangjie)的官方文档中,统一使用 Property 来表示“属性”。

属性(Properties)提供了一个 getter 和一个可选的 setter 来间接获取和设置值。

仓颉语言的class 定义体中

数据成员变量不像有些语言那样称为attribute。

成员属性就是指属性(Properties)。

术语

英文

定义

属性

Property

通过getter/setter间接访问的成员,提供计算、验证等逻辑

成员变量

Member Variable

直接存储数据的普通字段(无getter/setter)

attribute

❌ 非官方术语

在仓颉中不适用(其他语言如Python的术语)

注意:class 只能定义在源文件的顶层作用域。class是引用类型,定义为引用类型的变量,变量名中存储的是指向数据值的引用,因此在进行赋值或函数传参等操作时,拷贝的是引用。

class 定义与使用

先给出示例,以便有直观的认识,接着总结给出语法。

class 定义与基础示例

class 以 class 关键字开头,定义体包含成员变量、构造函数、成员函数等。

基础示例:定义一个矩形类:

class Rectangle {// 实例成员变量(需在构造函数中初始化)let width: Int64let height: Int64// 普通构造函数:初始化所有实例变量public init(width: Int64, height: Int64) {this.width = width  // this 指代当前对象this.height = height}// 实例成员函数:计算面积public func area(): Int64 {width * height  // 可省略 this,也可写为:this.width *  this.height}
}main(){// 创建对象(调用构造函数)let rect = Rectangle(10,  20)print(rect.area())  // 输出:200
}

对上面示例改进:演示了静态成员、主构造函数、实例方法、引用语义等,源码:

class Rectangle {// 静态计数器static var count: Int64 = 0// 主构造函数:参数前加 let/var 即自动成为成员变量public Rectangle(public var width: Int64, public var height: Int64) {// 不需要再写 this.width = widthRectangle.count += 1}// 计算面积public func area(): Int64 {width * height}// 打印信息public func printInfo(): Unit {println("Rectangle ${width}×${height}, area=${area()}, total=${Rectangle.count}")}
}// 程序入口
main(): Int64 {let r1 = Rectangle(3, 4)r1.printInfo()      // Rectangle 3×4, area=12, total=1let r2 = Rectangle(5, 6)r2.printInfo()      // Rectangle 5×6, area=30, total=2// 引用语义:r3 与 r2 指向同一对象let r3 = r2r3.width = 10r2.printInfo()      // Rectangle 10×6, area=60, total=2return 0
}

类使用 'class' 关键字定义,语法

// 访问修饰符(可选,默认internal) + 继承修饰符(open/sealed,非抽象类需open才允许继承)
[访问修饰符] [open/sealed] class 类名 [<: 父类名>] {
// 1. 成员变量(实例变量/静态变量)
// ①实例成员变量
[修饰符] let/var 实例变量名: 类型 [= 初始值];
[修饰符] static let/var 静态变量名: 类型 [= 初始值];
// ②静态成员变量
// 2. 静态初始化器(可选,仅初始化静态变量,最多1个)
static init() {
静态变量赋值; // 必须初始化所有未设初始值的静态变量
}

    // 3. 构造函数(普通构造/主构造,初始化实例变量)
// ①普通构造:init开头;可重载;必须先初始化所有未赋值实例成员
[访问修饰符] init(参数列表) {
this.实例变量 = 参数; // 必须初始化所有未设初始值的实例变量
}
// ②主构造:与类名相同(简化定义,参数可直接定义成员变量:参数前可加 let/var 一次性声明+初始化)
[访问修饰符] 类名(let/var 参数1: 类型, let/var 参数2: 类型) {
// 无需重复定义成员变量,参数已自动成为实例变量
}

    // 4. 成员函数(实例函数/静态函数)
// ①实例函数
[访问修饰符] func 实例函数名(参数列表) [-> 返回类型] {
函数体; // 可通过this访问实例变量
}
// ②静态函数
[访问修饰符] static func 静态函数名(参数列表) [-> 返回类型] {
函数体; // 仅可访问静态变量/函数,不可访问实例成员
}

    // 5. 终结器(可选,GC回收时调用,释放资源;无修饰符、不能手动调用)
~init() {
资源释放逻辑; // 如释放指针、关闭文件等
}
}

注意:

①继承修饰符

非抽象类想被继承必须写 open;想“只能本包继承”就写 sealed(同时隐含 open)。

抽象类默认可继承,因此写不写 open 都行,但仍可再加 sealed 限制包内继承。

②构造函数“链式调用”约束

普通构造里只能在第一行写 super(...) 或 this(...),二者只能出现其一;主构造只能用 super(...),不能用 this(...)。

③终结器限制(再强调一次)

带 ~init 的类不允许再加 open 修饰;终结器不能手动调用,也不能放在 extend 里。

④使用 abstract 修饰的类为抽象类,与普通类不同的是,在抽象类中除了可以定义普通的函数,还允许声明抽象函数(没有函数体)。抽象类定义时的 open 修饰符是可选的,也可以使用 sealed 修饰符修饰抽象类,表示该抽象类只能在本包被继承。

创建对象语法

①基本语法,类名 + 构造参数(参数需匹配类中的构造函数)
let/var 变量名 = 类名(参数1, 参数2, ...)
②如果构造函数有命名参数,可以使用命名参数语法
let/var 变量名 = 类名(参数名1: 值1, 参数名2: 值2, ...)

类的成员包括实例成员(实例变量、实例函数)和静态成员(静态变量、静态函数),访问方式不同:
①实例成员:通过实例对象访问,语法实例.成员名
②静态成员:通过类名访问,语法类名.成员名

上述内容是概括性的对新手而言,不必期望一下子理解,可先大体理解,逐步体会学习,多看示例和动手实践,多回头看几次,就会逐步深入理解。

示例1:

class Counter {var value: Int64 = 0          // 实例变量static var total: Int64 = 0   // 静态变量public func inc() {            // 实例方法value += 1Counter.total += 1        // 静态变量通过类名访问}public static func resetAll() {// 静态方法total = 0}
}main(): Int64 {// 1. 创建对象(引用)let c1 = Counter()let c2 = Counter()// 2. 访问实例成员c1.inc()c1.inc()c2.inc()println("c1.value = ${c1.value}")   // 2println("c2.value = ${c2.value}")   // 1println("Counter.total = ${Counter.total}") // 3// 3. 静态方法调用Counter.resetAll()println("after reset: ${Counter.total}") // 0// 4. 引用别名演示let c3 = c1        // 只是别名,不是新对象c3.value = 99println("c1.value = ${c1.value}") // 99return 0
}

编译运行截图:

示例2:

class Student {// 主构造函数:用let声明的参数直接成为成员变量(无需单独定义)public Student(let name: String, let grade: Int64, var score: Float64) {// 无需写 this.name = name 等赋值语句// 因为let/var参数已自动作为成员变量}// 实例方法:打印学生信息public func showInfo() {println("姓名:${name},年级:${grade},分数:${score}")}// 实例方法:修改分数(score是var变量,可修改)public func updateScore(newScore: Float64) {score = newScore}
}// 程序入口
main() : Int64 {// 创建Student对象(参数直接初始化成员变量)let student = Student("李四", 3, 85.5)// 调用方法展示信息student.showInfo()  // 访问主构造函数定义的成员变量// 修改分数并再次展示student.updateScore(92.0)student.showInfo()return 0
}

运行输出

姓名:李四,年级:3,分数:85.500000
姓名:李四,年级:3,分数:92.000000

抽象类(abstract class

使用 abstract 修饰的类为抽象类,与普通类不同的是,在抽象类中除了可以定义普通的函数,还允许声明抽象函数(没有函数体)。抽象类定义时的 open 修饰符是可选的,也可以使用 sealed 修饰符修饰抽象类,表示该抽象类只能在本包被继承。

用 abstract 修饰的类,可包含抽象函数(无函数体,子类必须实现)。注意:

    抽象类中禁止定义 private 的抽象函数;

    抽象类不能实例化;

    抽象类的非抽象子类必须实现父类中的所有抽象函数。

示例:抽象形状类,源码:

// 抽象类:定义抽象方法 area
abstract class Shape {public func area() : Float64  // 无函数体
}// 子类:矩形(实现抽象方法)
class Rectangle <: Shape {let width: Float64let height: Float64init(width: Float64, height: Float64) {this.width = widththis.height = height}// 必须实现父类的抽象方法public override func area(): Float64 {width * height}
}// 子类:圆形(实现抽象方法)
class Circle <: Shape {let radius: Float64init(radius: Float64) {this.radius = radius}public override func area(): Float64 {3.14 * radius * radius  // 简化计算(π≈3.14)}
}main(){let rect = Rectangle(5.0,  4.0)let circle = Circle(2.0)println(rect.area())   // 输出:20.000000println(circle.area()) // 输出:20.000000
}

class的成员说明

成员变量(实例变量 vs 静态变量)

  •  实例变量:属于对象,通过对象访问;定义时可无初值(需在构造函数中初始化)。

  •  静态变量:属于类,用 static 修饰,通过类名访问;无静态初始化器时必须有初值。

示例

class Student {// 实例变量:每个学生有自己的姓名var name: String// 静态变量:所有学生共享的学校名称(无初始化器时必须有初值)static let school: String = "阳光小学"// 静态变量:通过静态初始化器初始化(必须可变才能计数)static var totalCount: Int64static init() {  // 静态初始化器:初始化未赋值的静态变量totalCount = 0}init(name: String) {this.name = nameStudent.totalCount += 1  // 实例函数中访问静态变量}
}main(){// 访问静态变量(通过类名)print(Student.school)  // 输出:阳光小学// 创建对象(访问实例变量)let s1 = Student("小明")let s2 = Student("小红")println(s1.name)         // 输出:小明println(Student.totalCount)  // 输出:2(两个学生被创建)
}

静态初始化器

静态初始化器只在类第一次被用到时执行一次,专门给静态成员变量做“晚初始化”。

  • 关键字 static init(),后跟无参参数列表和函数体、最多 1 个。

  • 函数体里必须把所有尚未赋初值的静态成员全部赋完,否则编译报错。

示例:

// 定义一个带有静态初始化器的类
class MyClass {public static let constantValue: Int64   // ① 显式加修饰符public static var mutableValue: Stringlet instanceValue: Int64 = 42  // 实例成员// ② 静态初始化器,必须初始化所有未赋初值的静态成员static init() {constantValue = 100mutableValue = "Initialized"println("Static initializer for MyClass called.")}// 实例方法public func printValues() {println("Instance value: ${instanceValue}")println("Static constant: ${constantValue}")println("Static mutable: ${mutableValue}")}
}main(): Int64 {// 第一次访问静态成员 → 触发 static init()println("MyClass constant: ${MyClass.constantValue}")println("MyClass mutable: ${MyClass.mutableValue}")// 创建类的实例let obj1 = MyClass()obj1.printValues()// 再次访问,不再触发println("Accessing static members again:")println("MyClass constant: ${MyClass.constantValue}")return 0
}

编译运行输出:

Static initializer for MyClass called.
MyClass constant: 100       
MyClass mutable: Initialized
Instance value: 42
Static constant: 100        
Static mutable: Initialized

构造函数(普通构造函数与主构造函数)

  •  普通构造函数:用 init 定义,需初始化所有实例变量,支持重载(参数列表不同)。一个类中可以定义多个普通构造函数,但它们必须构成重载,否则报重定义错误。

  •  主构造函数:与类名同名,参数前加 let/var 可直接定义成员变量,简化代码。

示例 1:普通构造函数重载

class Rectangle {let width: Int64let height: Int64// 构造函数1:接收宽和高init(width: Int64, height: Int64) {this.width = widththis.height = height}// 构造函数2:正方形(宽高相等)init(side: Int64) {  // 重载:参数不同this.width = sidethis.height = side}// 计算面积 public func area(): Int64 {width * height}
}main(){let rect1 = Rectangle(3, 4)let square = Rectangle( 5)println(rect1.area())  // 输出:12println(square.area()) // 输出:25
}

示例 2:主构造函数

主构造函数的名称与类名相同,参数列表中可以直接定义成员变量——简化代码。

class Rectangle {// 主构造函数:直接定义成员变量width和heightpublic Rectangle(let width: Int64, let height: Int64) {}// 构造函数重载:通过this调用主构造函数(正方形)init(side: Int64) {this(side, side)  // 调用主构造函数,宽高均为side}// 计算面积public func area(): Int64 {width * height}
}main() {// 调用主构造函数创建矩形let rect1 = Rectangle(3, 4)// 调用重载构造函数创建正方形let square = Rectangle(5)println(rect1.area())  // 输出:12println(square.area()) // 输出:25
}

创建对象时,构造函数执行步骤:

  1.初始化主构造函数外有默认值的实例变量;

  2.若未显式调用父类 / 本类其他构造函数,自动调用父类无参构造函数 super();

  3.执行当前构造函数体。

构造函数执行步骤示例1

func trace(msg: String) { println(msg) }open class Base {init() {trace("Base构造函数体")}
}class Derived <: Base {// 步骤1: 初始化变量var a = trace("初始化变量a")var b = trace("初始化变量b")// 主构造函数public Derived() {// 步骤2: 自动插入 super()// 步骤3: 执行构造函数体trace("3.Derived构造函数体")}
}main(){// 测试执行let obj = Derived()
}
/* 输出:
初始化变量a
初始化变量b        
Base构造函数体     
3.Derived构造函数体
*/

构造函数执行步骤示例2:构造函数执行顺序,构造函数链 + super / this 调用顺序验证:

open class Base {let tag: Stringinit() { tag = "Base-default"; println("Base()") }init(t: String) { tag = t; println("Base(${t})") }
}class Child1 <: Base {let v: Int64init(v: Int64) {super("Child1")        // 显式调用父类构造this.v = vprintln("Child1(${v})")}
}class Child2 <: Base {init() {this(99)               // 调用本类其他构造}init(v: Int64) {super()                // 编译器不会自动插入 super()println("Child2(${v})")}
}main() {let _ = Child1(42)let _ = Child2()
}
/* 输出顺序:
Base(Child1)
Child1(42)
Base-default
Child2(99)
*/

成员函数(实例函数 vs 静态函数)

  •  实例函数:通过对象调用,可访问实例变量和静态变量;

  •  静态函数:用 static 修饰,通过类名调用,不能访问实例变量

示例:实例函数与静态函数

class MathUtil {// 静态函数:计算平方(无实例依赖)static func square(x: Int64): Int64 {x * x}// 实例变量:当前数值var value: Int64init(value: Int64) {this.value = value}// 实例函数:计算当前值与参数的和(依赖实例变量)func add(x: Int64): Int64 {value + x}
}main() {// 调用静态函数(类名访问)println(MathUtil.square(5))  // 输出:25// 调用实例函数(对象访问)let math = MathUtil(10)println(math.add(3))  // 输出:13
}

class终结器(~init

终结器(finalizer),对象被垃圾回收时调用,用于释放资源(如文件句柄、内存)。

  •  无参数、无返回值,不能显式调用;

  •  带终结器的类不能用 open 修饰;

  •  触发时机不确定。

开发者通常不需要手动定义终结器。有人说,对于普通开发者:

不需要主动定义 ~init:日常业务开发中,依赖自动内存管理和显式资源管理模式即可,避免因终结器的不确定性引入问题。

了解其存在即可:知道 ~init 是对象销毁前的钩子,在阅读底层库代码或处理特殊资源时,能理解其作用即可。

引用类型特性

class 是引用类型,赋值或传参时传递引用(多个变量指向同一对象)。

示例:引用传递

class Counter {var count: Int64 = 0func increment() {count += 1}
}main(){let c1 = Counter()let c2 = c1  // c2 与 c1 指向同一对象c1.increment()    println(c1.count)  // 输出:1println(c2.count)  // 输出:1(c1修改影响c2)
}

class 成员的访问修饰符

控制成员的可见性,共 4 种

修饰符      可见范围

private     仅当前类内部

internal    当前包及子包(默认)

protected   当前模块及当前类的子类

public      模块内外均可见

【在仓颉(Cangjie)语言里,包(package) 与 模块(module) 的对应关系可以一句话总结为:
一个模块 = 一个 cjpm (Cangjie Package Manager:仓颉包管理器)项目 = 一个仓颉独立的构建和分发单元。
包(package)只是模块内部的“命名空间”层级,用来组织源码目录。】

protected 和 public 的区别

protected

  • 不仅是“能访问”,还隐含“可继承”——子类可以覆写该成员。

  • 同时受“模块”限制:别的模块即使继承了该类,也看不见 protected 成员;

  • 只有同一个模块内的子类才能继承/覆写。

public

  • 只是“能看见”,不隐含可继承;

  • 任何模块都能看见,但覆写仍需要 open + override。

  • 没有模块边界限制,跨模块也能用。

【在仓颉里,“跨模块”就是跨 cjpm 项目。
你可以把 A 项目编译成 .cjpm(库),然后在 B 项目里把它声明成依赖,就能“跨模块”使用。】

示例:访问修饰符效果
包结构如下: 
demo8
│ 
src/
├── main.cj         // 入口
└── business/         // business包
├── finance.cj  // business包的finance.cj
└── utils.cj    // business包的utils.cj

1. business/utils.cj 的源码:

package demo8.business// 声明为public 和open 才能被其他包访问
public open class PaymentProcessor {private var transactionId: Int64 = 0internal var merchant = "Default"protected var feeRate: Float64 = 0.03public var currency = "USD"
}// 内部工具类保持 internal
internal class SecurityUtils {public func encrypt(data: String): String {return "ENCRYPTED:${data}"}
}

2. business/finance.cj 的源码:

package demo8.business// 声明为 public 
public class CreditProcessor <: PaymentProcessor {public func process(amount: Float64) {let fee = amount * feeRate  println("收取 ${fee} ${currency}")merchant = "CreditCo"   let utils = SecurityUtils()println(utils.encrypt("敏感数据"))}
}

3.主程序入口 (main.cj)

package demo8import demo8.business.PaymentProcessor // 导入business包的类PaymentProcessor
import demo8.business.CreditProcessor // 导入business包的类 CreditProcessormain() {// 场景1:直接使用基类let base = PaymentProcessor()  // 创建基类实例println(base.currency)  // ✓ public: 输出 "USD"// base.merchant = "Shop"  // ✗ internal: 跨包不可访问//println(base.feeRate)   //// 场景2:使用子类let credit = CreditProcessor()  // 创建子类实例credit.process(100.0)  // ✓ 子类内部可访问protected 。 (要有添加小数点)
}

要注意import的路径。运行输出:

USD
收取 3.000000 USD 
ENCRYPTED:敏感数据

This 类型

在类内部,支持 This 类型占位符,代指当前类的类型。它只能被作为实例成员函数的返回类型来使用,当使用子类对象调用在父类中定义的返回 This 类型的函数时,该函数调用的类型会被识别为子类类型,而非定义所在的父类类型。

如果实例成员函数没有声明返回类型,并且只存在返回 This 类型表达式时,当前函数的返回类型会推断为 This。

在实例方法返回类型中使用,代表当前类类型(子类调用时返回子类类型)。

注意:

1.this 指代当前对象的实例,因此只能在实例函数、构造函数、实例属性的 getter/setter 中使用;静态成员(静态函数、静态属性)中不能使用 this(静态成员属于类,不属于某个实例)。

2.不能在类外部使用:this 仅在类的实例成员内部有意义,类外部无法使用编译报错。

this的作用

1.指代“当前对象”

在实例成员函数、属性 getter/setter 内部,

用 this.成员名 明确访问的是“本对象”的成员,避免与局部变量/参数重名冲突。

(不写 this. 也能访问,但加上可读性更高。)

2.当作 This 类型占位符

只能出现在实例成员函数的返回类型位置,

表示“返回当前运行时对象的实际类型”,而不是声明时所在的类名。

常用于链式调用或“返回自身”的接口。

示例 1 :区分参数与成员变量,消除名字冲突

class Person {let name: String; // 成员变量var age: Int64;   // 成员变量// 构造函数参数与成员变量同名(name、age)public init(name: String, age: Int64) {// this.name 明确指向成员变量,而非参数namethis.name = name; // this.age 明确指向成员变量,而非参数agethis.age = age;  }// 实例函数:打印信息(参数与成员变量同名)public func printInfo(name: String) {// this.name 指成员变量name,name指参数nameprintln("成员name:${this.name},参数name:${name}");println("年龄:${this.age}");}
}main(): Int64 {let p = Person("张三", 20);p.printInfo("李四"); // 传入参数name="李四"return 0;
}

编译运行结果:

成员name:张三,参数name:李四
年龄:20

示例 2 :This 类型做返回值,支持链式方法调用

class StringBuilder {private var content: String = ""public func append(s: String): This { // 返回类型为 This,通常代表当前类型content += sreturn this // 返回当前对象,以便进行链式调用}public func newLine(): This {content += "\n"return this}public func toString(): String {content}
}main(): Int64 {let sb = StringBuilder()let result = sb.append("Hello").append(" World!").newLine().append("仓颉语言").toString()println(result)return 0
}

编译运行结果:

Hello World!
仓颉语言


文章转载自:

http://z6AAnLOL.tkzrh.cn
http://BnID8iW6.tkzrh.cn
http://l4awAmL4.tkzrh.cn
http://eAAeJVPG.tkzrh.cn
http://6udMRpja.tkzrh.cn
http://ioj9B9al.tkzrh.cn
http://Xu37S1ni.tkzrh.cn
http://xtOYyxWH.tkzrh.cn
http://tVZLiQSG.tkzrh.cn
http://wFVVBgN0.tkzrh.cn
http://PRAUoOAu.tkzrh.cn
http://xNTH7kFM.tkzrh.cn
http://yVgA8vEo.tkzrh.cn
http://wyXF1rDa.tkzrh.cn
http://DXjQIPfH.tkzrh.cn
http://TfTzJ2c7.tkzrh.cn
http://zi3OYmTx.tkzrh.cn
http://qgh0kkPE.tkzrh.cn
http://HeSZvb8F.tkzrh.cn
http://pRXPzCtP.tkzrh.cn
http://4mwWayMZ.tkzrh.cn
http://tc3oBEYc.tkzrh.cn
http://4tbuWfAb.tkzrh.cn
http://CND4cdyN.tkzrh.cn
http://MVVQNwgn.tkzrh.cn
http://WMwGIHz9.tkzrh.cn
http://5A4BAaeZ.tkzrh.cn
http://cP9GCaAI.tkzrh.cn
http://b5HU0NhD.tkzrh.cn
http://C6fw3znf.tkzrh.cn
http://www.dtcms.com/a/386340.html

相关文章:

  • MySQL数据库(五)—— Mysql 备份与还原+慢查询日志分析
  • 可迭代对象、迭代器对象
  • GEO 优化系统开发:内容优化策略的技术设计与落地实践​
  • leetcode347.前k个高频元素
  • GPU 服务器:为科研算力需求保驾护航
  • 解决Cursor 远程ssh连不上服务器的问题
  • AR技术赋能火灾防控:消防员的“透视眼”与“预测脑”
  • 佩京ar虚拟互动换装软件+换装一体机
  • 11 神经网络研究的一些问题
  • Python快速入门专业版(三十二):匿名函数:lambda表达式的简洁用法(结合filter/map)
  • MATLAB中基于 S-V模型进行毫米波信道建模与仿真
  • 深入分析LangSmith使用及自动化评估
  • fastapi 中间件的使用
  • 2025最新超详细FreeRTOS入门教程:第十九章 FreeRTOS与中间件集成(TCPIP、MQTT、文件系统)
  • Vue2实战场景:图表组件 + Grid 布局
  • Linux:基于阻塞队列的生产者消费模型
  • springboot+vue (ruoyi-vue前后端分离)集成钉钉登录
  • 从单一辅助到深度协作!GPT-5-Codex 改写软件开发工作流
  • JavaScript——document对象
  • 图观 流渲染场景编辑器
  • 探索大语言模型(LLM):Windows系统与Linux系统下的Ollama高级配置(修改模型地址、Service服务以及多卡均衡调用)
  • PowerBI实战-制作带有同比及趋势线的双柱状图
  • Spring 介绍
  • 使用爱思助手(版本8.16)将ipa安装到ios
  • 大模型应用开发2-SpringAI实战
  • 【面板数据】上市公司校企合作论文发表数据集(2002-2025年)
  • MySQL的底层数据结构:B+树
  • 【Linux】LRU缓存(C++模拟实现)
  • 冲击成本敏感度曲线驱动的拆单频率参数动态调优机制
  • Typera+Gitee+PicGo 配置markdown专用图床