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

对比Java学习Go——函数、集合和OOP

函数(Function)

函数声明与调用对比

  • 函数声明对比

    // 语法:
    // func 函数名([参数列表]) [返回值列表] {
    //     // 函数体
    // }// 示例1:基础形式(与Java类似)
    func add(a int, b int) int {return a + b
    }// 示例2:参数类型合并(a和b都是int类型)
    func add(a, b int) int {return a + b
    }// 示例3:多返回值(Go的核心特性!)
    func divide(a, b float64) (float64, error) {if b == 0.0 {return 0.0, errors.New("division by zero")}return a / b, nil
    }// 示例4:命名返回值
    func split(sum int) (x, y int) {x = sum * 4 / 9y = sum - xreturn // 称为"裸返回",自动返回x和y
    }
    
    • 关键字 func:使用 func 关键字开头,而不是Java中的返回值类型。
    • 类型后置:变量名在前,类型在后。这是Go的整体风格,与变量声明一致。
    • 简洁的参数列表:同类型的参数可以合并声明(a, b int)。
    • 多返回值:这是Go与Java最显著的区别之一。函数可以返回多个值,通常用于返回结果和错误信息。
    • 命名返回值:可以为返回值命名,它们会被视为在函数顶部定义的变量。使用裸返回(return)时,会自动返回这些变量。
    • 无异常声明:Go没有throws关键字。错误通过普通的返回值来传递。
  • Go 的函数调用:直接且简单

    // 调用同一个包内的函数:直接调用
    sum := add(5, 10)// 调用其他包的函数:pkg.FunctionName(args)
    result, err := math.Divide(10.0, 2.0) // 假设Divide在math包中// 处理错误:通过判断返回值来处理,而非try-catch
    result, err := divide(10.0, 0.0)
    if err != nil {// 处理错误fmt.Println("Error:", err)return
    }
    // 使用结果
    fmt.Println("Result is", result)// 忽略某些返回值:使用空白标识符 _
    file, _ := os.Open("filename.txt") // 忽略打开文件可能产生的错误(不推荐)
    
    • 直接性:同一包内的函数直接调用,无需接收者。
    • 多返回值处理:调用返回多个值的函数时,必须用相同数量的变量来接收。可以使用 _ 忽略不需要的值。
    • 错误即值:错误处理是普通的流程控制的一部分,通过 if err != nil 进行检查,而不是通过异常机制跳转。

多返回值

  • 返回多个有效信息:直接返回

    // 函数直接返回两个值:(float64, float64)
    func divideWithRemainder(dividend, divisor int) (float64, float64) {quotient := float64(dividend) / float64(divisor)remainder := float64(dividend % divisor)return quotient, remainder // 直接返回两个值
    }// 调用方:用两个变量来接收
    q, r := divideWithRemainder(10, 3)
    fmt.Printf("商: %.2f, 余数: %.2f\n", q, r)
    
  • 返回结果与错误状态:返回 (value, error)

    import "errors"// 函数返回 (结果, 错误)
    func divide(dividend, divisor float64) (float64, error) {if divisor == 0.0 {// 错误路径:返回结果的零值和一個errorreturn 0.0, errors.New("division by zero")}// 成功路径:返回结果和nilreturn dividend / divisor, nil
    }// 调用方:立即检查错误
    result, err := divide(10.0, 0.0)
    if err != nil {// 处理错误:打印日志、返回错误、重试等...fmt.Println("Error:", err)return // 通常在这里返回,让错误向上传播
    }
    // 如果err为nil,安全地使用result
    fmt.Println("Result is", result)
    

错误处理:Go的(value, error)模式

  • 语法和结构

    // 1. 返回错误:函数将 error 作为最后一个返回值。
    //    error 是一个内置接口,任何实现了 Error() string 方法的类型都可以作为错误。
    import ("errors""os"
    )func readFile(path string) (string, error) {file, err := os.Open(path)if err != nil {// 错误路径:返回结果的零值和一个错误。// errors.New 是一个常用的创建简单错误的方法。return "", errors.New("file not found: " + path)// 更常用的方式是直接返回底层操作产生的错误: return "", err}defer file.Close() // 确保文件被关闭,类似于 finally 的作用// ... 读取文件内容return content, nil // 成功路径:返回内容和一个 nil 错误
    }// 2. 检查与处理错误:调用方立即使用 if 语句检查错误。
    func processFile() {content, err := readFile("missing.txt")if err != nil {// 立即处理错误。这是Go代码中最常见的代码片段。fmt.Fprintf(os.Stderr, "Failed to process file: %v\n", err)return // 通常,函数在处理错误后直接返回,将错误传递给它的调用者。}// 如果 err == nil,说明成功,可以安全地使用 contentfmt.Println(string(content))
    }
    
  • 性能零开销:错误处理就是普通的变量赋值和比较,效率极高,适用于任何频繁操作。

  • 完全显式:错误处理就在函数调用之后,一目了然。调用者无法忽视错误(除非故意用 _ 忽略),所有错误路径都清晰可见。

  • 控制流简单:程序始终保持线性的执行流程,没有跳转,更容易理解和维护。

  • 简单性:整个错误系统只有一个简单的 error 接口,没有复杂的异常类型体系。

匿名函数与Lambda表达式对比

  • 核心概念与语法

    • 语法func(parameters) (return_types) { body }
    • 本质:它是一个闭包(Closure),可以捕获其所在作用域内的任何变量。
  • 语法和结构

    // 直接声明并调用一个匿名函数
    func() {fmt.Println("Hello from immediate anonymous function")
    }() // 括号表示立即调用// 将匿名函数赋值给一个变量
    greet := func(name string) {fmt.Printf("Hello, %s\n", name)
    }
    greet("World") // 像调用普通函数一样调用// 匿名函数作为参数传递
    myPrint := func(f func(string), msg string) {f(msg)
    }
    myPrint(greet, "Gopher")
    
  • Go匿名函数的特点

    • 独立性:它是一个完整的函数,可以赋值给变量、作为参数传递、作为返回值,是一等公民
    • 完整的闭包:可以捕获和修改其定义作用域内的任何变量,非常强大。
    • 灵活性:不需要预定义的接口类型,可以定义任何签名的函数。
    • 陷阱:循环中捕获迭代变量是一个常见的陷阱(如上例中的 i),需要通过参数传递或创建副本解决。

函数作为一等公民:Go的function type vs Java的Functional Interface

  • 机制详解:可以使用 type 关键字为特定的函数签名定义一个类型,也可以直接使用函数签名。

    package mainimport ("fmt""strings"
    )// 1. 定义:使用`type`为函数签名创建一个自定义类型(推荐,更清晰)
    type StringProcessor func(string) int// 2. 使用:一个函数可以接受另一个函数作为参数
    func processString(input string, processor StringProcessor) int {// 直接调用传入的函数!return processor(input)
    }// 也可以不自定义类型,直接使用函数签名
    func processString2(input string, processor func(string) int) int {return processor(input)
    }func main() {text := "Hello"// 3. 传递:将一个符合签名的函数(这里是匿名函数)作为参数传递result1 := processString(text, func(s string) int {return len(s)})// 4. 也可以传递一个已定义的函数名(注意:没有括号)result2 := processString(text, len) // len 的签名是 func(string) int,符合要求// 5. 函数可以赋值给变量var myProcessor StringProcessor = strings.Countresult3 := processString(text, myProcessor) // 计算 "Hello" 在 "Hello" 中出现的次数,结果是1// 等价于: result3 := processString(text, func(s string) int { return strings.Count(s, "Hello") })fmt.Println(result1, result2, result3) // 输出 5 5 1
    }
    
  • 函数即值:你传递的就是函数本身。变量 myProcessor 存储的是一个函数值,而不是一个实现了某个接口的对象。

  • 直接调用:要执行这个函数,直接使用括号 () 和参数即可,无需通过一个中间方法(如 apply)。

  • 类型是签名:变量 myProcessor 的类型是 StringProcessorfunc(string) int,这是一个描述函数输入和输出的类型。

  • 灵活性:你可以为任何函数签名创建类型,无需预定义庞大的接口库。但Go标准库很少提供类似Java的高阶函数(如map, filter),通常需要自己实现。

面向对象编程(OOP)

核心概念颠覆:Go没有class,只有struct

  • 语法和结构

    // 一个Go的struct只负责定义数据
    type Dog struct {// 1. 只有数据(字段)// 注意:字段名首字母大写表示“导出”(公共),小写表示“非导出”(私有)Name  stringBreed stringage   int // 私有字段,仅在包内可见
    }// 2. 没有构造函数!通常用一个普通的工厂函数来模拟。
    func NewDog(name, breed string) *Dog {return &Dog{Name:  name,Breed: breed,age:   0, // 可以初始化私有字段}
    }
    
  • 关键特性分析

    • 内部不能定义方法。所有方法都被定义在 struct 之外。
    • struct 没有继承。Go语言完全没有 extendsimplements 关键字。
    • struct 就是一个内存布局的描述,它告诉你一块内存里按顺序放了什么数据。它非常轻量,接近C语言中的结构体。
  • 方法(Methods)与接收者(Receiver)

    // 1. 为 Dog 类型定义一个方法
    // (d Dog) 是“值接收者”,它定义了此方法属于哪个类型。
    // 在方法内部,`d` 是 Dog 的一个副本。
    func (d Dog) Bark() {fmt.Println("Woof! My name is", d.Name) // 可以访问d的字段
    }// 2. 通常我们会使用“指针接收者”,这样才能修改结构体的数据
    func (d *Dog) HaveBirthday() {d.age++ // 这样可以实际修改原结构体的age字段fmt.Printf("%s is now %d years old.\n", d.Name, d.age)
    }// 3. 实现一个接口(例如一个Pet接口)
    type Pet interface {BeCute()
    }// 为 *Dog 实现 BeCute 方法,这样就实现了 Pet 接口
    // (注意:Go的接口实现是隐式的,无需声明)
    func (d *Dog) BeCute() {fmt.Println(d.Name, "is being cute by wagging its tail.")
    }func main() {myDog := NewDog("Rex", "Shepherd")myDog.Bark()       // 调用方法myDog.HaveBirthday() // 修改内部状态myDog.BeCute()// 传递给我们期望 Pet 接口的函数PetCuteSession(myDog)
    }func PetCuteSession(p Pet) {p.BeCute()
    }
    

方法定义:Go的func (s Struct) method()

  • 语法和结构

    // 数据 (结构体)
    type Circle struct {radius float64
    }// 行为 (方法) - 定义在类型外部!
    // func (接收者变量 接收者类型) 方法名([参数列表]) [返回值列表] { ... }// 1. 值接收者 (Value Receiver)
    // (c Circle) 是接收者声明。`c`相当于Java中的`this`,但它是显式声明的。
    func (c Circle) Area() float64 {return math.Pi * c.radius * c.radius
    }// 2. 指针接收者 (Pointer Receiver) - 更常见,用于修改数据
    func (c *Circle) SetRadius(newRadius float64) {c.radius = newRadius // 这里可以修改原结构体的数据
    }// 一个普通的函数,与任何类型无关
    func PrintArea(c Circle) {fmt.Println(c.Area())
    }
    
  • 关键特性分析

    • 位置:方法被定义在结构体(或任何类型)的外部。它看起来就像一个普通函数,前面多了一个接收者声明。

    • 显式的接收者(c Circle)(c *Circle) 是显式声明的接收者。它定义了该方法属于哪种类型。

      c 是接收者变量名(通常很短,如1-2个字母),相当于Java的 this

      Circle*Circle 是接收者类型,决定了方法是属于值类型还是指针类型。

    • 访问控制:Go没有 public/private 关键字。方法的可见性由其名称的首字母大小写控制:

      Area() (大写开头):表示方法是导出的(Public),可以被其他包访问。

      internalMethod() (小写开头):表示方法是包内私有的(Private),只能在定义它的包内使用。

    • 调用方式:语法上与Java完全相同,通过变量.方法() 的形式调用。Go会自动处理值和指针的转换。

封装:Go的标识符首字母大小写

  • 标识符规则

    • 首字母大写:表示导出(Exported)(相当于 public)。可以被其他包导入并使用。
    • 首字母小写:表示未导出(Unexported)(相当于 package-privateprivate)。只能在定义它的当前包内使用。
  • 语法和结构

    // 在 package 'mypackage' 中// MyStruct 是导出的(公共的),因为首字母大写。
    // 其他包可以: var s mypackage.MyStruct
    type MyStruct struct {ExportedField   int    // 导出的字段(公共),其他包可以访问unexportedField string // 未导出的字段(私有),仅限本包使用
    }// NewMyStruct 是导出的函数(公共的),其他包可以调用。
    func NewMyStruct() *MyStruct {return &MyStruct{}
    }// helperFunc 是未导出的函数(私有的),只能在 mypackage 包内调用。
    func helperFunc() {// ...
    }// 为 MyStruct 定义方法
    func (m *MyStruct) GetValue() int { // 方法名首字母大写,是导出的(公共的)return m.ExportedField
    }func (m *MyStruct) setValue(v int) { // 方法名首字母小写,是未导出的(私有的)m.unexportedField = "set"
    }
    

组合:Go使用组合(Embedding)替代继承

  • 语法:嵌入

    // Go: 使用组合嵌入替代继承type Animal struct { // 相当于“父类”,但只是一个普通结构体Name string
    }// 为 Animal 定义方法
    func (a *Animal) Eat() {fmt.Println(a.Name, "is eating.")
    }// Dog "有一个" Animal (has-a),而不是 "是一个" Animal
    type Dog struct {Animal // 嵌入:没有字段名,只写类型。这是关键!Breed  string
    }// 为 Dog 定义自己的方法
    func (d *Dog) Bark() {fmt.Println(d.Name, "says: Woof!") // 可以直接访问嵌入类型的字段!
    }// 可以“重写”嵌入类型的方法
    func (d *Dog) Eat() {d.Animal.Eat() // 可以选择性调用“父类”的方法fmt.Println("... and it's dog food!")
    }func main() {myDog := Dog{Animal: Animal{Name: "Rex"}, // 初始化嵌入的结构体Breed:  "Shepherd",}// 神奇之处:Dog 可以直接调用 Animal 的所有方法和字段!myDog.Eat()  // 调用“重写”后的方法myDog.Bark() // 调用自己的方法// Go 也实现了多态,但通过接口(interface)而不是继承var livingThing interface{ Eat() } = &myDog // 定义一个接口并赋值livingThing.Eat()
    }
    
  • 关键特性分析

    • 自动提升(Promotion)

      当嵌入一个类型(Animal)时,其所有字段和方法会自动“提升” 到外部类型(Dog)的级别。

      myDog.NamemyDog.Eat() 可以直接调用,仿佛这些字段和方法就是定义在 Dog 本身一样。

    • 模拟“重写”

      可以在外部类型(Dog)上定义一个与内部类型(Animal同名的方法,这样就“覆盖”了内部类型的方法。

      仍然可以通过显式指定内部类型(d.Animal.Eat())来调用被“覆盖”的方法,这比Java的super更清晰、更灵活。

    • 类型关系

      DogAnimal 是两种完全不同的类型,没有继承关系。

      这意味着你不能直接将一个 Dog 赋值给一个 Animal 变量(不像Java那样是自动的)。

      多态通过接口实现:如果 Dog 实现了某个接口,那么它就可以被当作该接口类型使用。

多态:Go的interface(非侵入式、鸭子类型)

  • 语法和结构

    // 1. 定义一个接口(契约)
    //    只包含行为(方法),不包含任何数据。
    type Speaker interface {Speak() // 接口方法
    }// 2. 定义一些类型
    //    注意:这些类型完全不知道 Speaker 接口的存在!
    type Dog struct {Name string
    }// 为 Dog 类型定义一个方法。
    // 这个方法签名(无参数,无返回值)偶然地与 Speaker 接口的 Speak 方法匹配。
    func (d Dog) Speak() {fmt.Printf("%s says: Woof!\n", d.Name)
    }type Human struct {Name string
    }// 为 Human 类型也定义一个签名相同的方法。
    func (h Human) Speak() {fmt.Printf("%s says: Hello!\n", h.Name)
    }type Car struct {Model string
    }func (c Car) Drive() { // 这个方法叫 Drive,与 Speaker 接口无关fmt.Println("Vroom!", c.Model)
    }// 3. 使用:多态
    func makeItTalk(s Speaker) { // 参数是 Speaker 接口类型s.Speak() // 调用接口方法
    }func main() {dog := Dog{Name: "Rex"}human := Human{Name: "Alice"}car := Car{Model: "Tesla"}makeItTalk(dog)   // ✅ Rex says: Woof! makeItTalk(human) // ✅ Alice says: Hello!// makeItTalk(car) // ❌ 编译错误!Car 没有实现 Speaker 接口(缺少 Speak 方法)// 接口实现是自动的,我们甚至可以即兴创建var s Speakers = dog // ✅ 因为 Dog 实现了 Speaker 所需的方法,所以可以赋值s.Speak()
    }
    
  • 关键特性分析

    • 非侵入性:类型无需关心接口,无需显式声明 implements。接口和实现者之间没有直接的编译期依赖
    • 解耦极致:你可以为已经存在的、来自其他包的类型定义新的接口,而无需修改它们的源代码。这极大地降低了耦合度。
    • 鸭子类型:只关心行为(有什么方法),不关心身份(是什么类型)。
    • 小而美:鼓励定义很多小的、单一的接口(如 io.Reader, io.Writer),而不是庞大复杂的接口。

构造函数:Go的工厂函数NewMyStruct()

  • 语法和结构

    package userimport ("errors""time"
    )// 结构体定义(只有数据)
    type User struct {name string // 小写开头,包外不可访问(私有)Age  int    // 大写开头,包外可访问(公共)id   int64
    }// 1. 标准的工厂函数:函数名以 `New` 开头,返回一个 *User(指针)
    //    这样可以避免大结构体的值拷贝,并且能返回 nil。
    func NewUser(name string, age int) (*User, error) { // 多返回一个 error 是常见模式if name == "" {return nil, errors.New("name cannot be empty") // 可以在初始化时进行验证!}// 返回一个初始化好的 User 结构体的地址return &User{name: name,Age:  age,id:   time.Now().UnixNano(), // 可以在此处设置默认逻辑}, nil
    }// 2. “构造函数”重载:通过不同的函数名来实现
    func NewAnonymousUser() *User {return &User{name: "Anonymous",id:   -1,}
    }// 3. 可以返回接口,而不是具体类型,隐藏实现细节
    type Speaker interface {Speak() string
    }// NewSpeaker 返回一个接口类型,调用者不知道底层具体是哪个结构体
    func NewSpeaker() Speaker {return &User{name: "Echo"} // 这里返回的是 *User,但对外是 Speaker 接口
    }// 为 User 实现 Speaker 接口的方法
    func (u *User) Speak() string {return "Hello, my name is " + u.name
    }// 使用
    func main() {// 使用内置的 new():只分配零值内存,极少使用u1 := new(User) // u1 是 *User 类型,所有字段为其零值:{"", 0, 0}// 使用结构体字面量:直接初始化u2 := &User{Name: "Bob", Age: 25} // 注意:如果字段是私有的(name),这里无法初始化!// 标准做法:调用工厂函数u3, err := user.NewUser("Alice", 30)if err != nil {panic(err)}fmt.Println(u3.Age) // 访问公共字段u4 := user.NewAnonymousUser()var s user.Speaker = user.NewSpeaker()s.Speak()
    }
    
  • 关键特性分析

    • 约定,非语法NewXxx 只是一个命名约定,它不是Go语言的关键字或特殊语法。它就是一个返回结构体实例的普通函数。
    • 可以有任何名字New, NewUser, OpenFile, CreateClient等。
    • 可以返回任何类型:通常返回指针 *Struct(避免拷贝,允许修改),但也可以返回值 Struct
    • 可以执行复杂逻辑:可以在函数内进行参数验证分配ID设置默认值连接外部服务等。这是它比Java构造函数强大的地方。
    • 可以返回错误:这是Go方式最大的优势之一。如果初始化可能失败(如验证失败、网络错误),它可以返回 (obj, error),而Java构造函数无法抛出受检异常以外的错误。
    • 可以隐藏实现:通过返回接口类型,而不是具体结构体类型,可以向调用者隐藏实现细节。

集合类型

数组与切片:Go的Array和更重要的Slice

  • Array语法和结构

    // 1. 声明和初始化
    var goArray [5]int           // 声明一个长度为5的int数组,元素初始化为0
    names := [3]string{"Alice", "Bob", "Charlie"} // 声明并初始化
    quickInit := [...]int{1, 2, 3, 4, 5} // 编译器推断长度,结果是 [5]int// 2. 访问和修改
    goArray[0] = 10
    firstElement := goArray[0]
    length := len(goArray) // 使用内置函数 len() 获取长度// 3. 关键特性(与Java最大不同)
    // - **值类型(Value Type)**:这是最重要的区别!
    //   数组变量代表的是整个数组,而不是指向数组的指针(引用)。
    //   赋值和传参会发生整个数组的拷贝。a := [3]int{1, 2, 3}
    b := a       // 这里是整个数组的完整拷贝!b 是 a 的一个副本。
    b[0] = 100   // 修改 b 不会影响 afmt.Println(a) // [1 2 3]
    fmt.Println(b) // [100 2 3]// 4. 缺点
    // 因为它是值类型且长度固定,所以在函数间传递大数组开销很大,且无法动态扩容。
    // 因此,在Go中,**数组直接使用的场景很少**。
    
  • Slice

    // 1. 创建切片(多种方式)
    // a) 基于数组创建(切片引用该数组)
    arr := [5]int{1, 2, 3, 4, 5}
    slice1 := arr[1:4] // slice1 = [2, 3, 4], len=3, cap=4 (从索引1开始到末尾)// b) 使用 make() 函数创建(同时创建底层数组)
    slice2 := make([]int, 3, 5) // 类型,长度(len),容量(cap)
    // slice2 = [0, 0, 0], len=3, cap=5// c) 直接使用切片字面量(语法类似数组,但不指定长度)
    slice3 := []string{"a", "b", "c"} // 这是切片!不是数组!
    // slice3 = [a, b, c], len=3, cap=3// 2. 操作切片
    // - 访问和修改:与数组相同
    slice3[0] = "A"// - 追加元素:使用内置 append() 函数,这是实现“动态”的关键!
    slice3 = append(slice3, "d") // 容量不足时,append 会自动扩容!
    // slice3 = [A, b, c, d], len=4, cap=6? (扩容策略通常是翻倍)// - 获取长度和容量
    l := len(slice3)
    c := cap(slice3)// - 切片操作(创建新切片)
    newSlice := slice3[1:3] // [b, c] 新切片和原切片共享底层数组!
    newSlice[0] = "B"       // 这会同时修改 slice3[1] 和 newSlice[0]
    fmt.Println(slice3)     // [A, B, c, d]// 3. 关键特性
    // - **引用类型**:切片本身是一个小的描述符(包含指针、len、cap),赋值和传参拷贝的是这个描述符,而不是底层数组。多个切片可以共享同一个底层数组。
    // - **动态增长**:通过 `append()` 函数,切片可以在容量不足时自动扩容(分配新的更大的数组并拷贝数据)。
    // - **高效“视图”**:切片操作(s[i:j])非常高效,因为它只是创建了一个新的切片描述符,而不拷贝底层数据。
    
    • 指针(Pointer):指向底层数组的起始元素(不一定是数组头)。
    • 长度(Length):切片中当前有多少个元素(len(s))。
    • 容量(Capacity):从切片起始位置到底层数组末尾的元素个数(cap(s))。

映射:Go的map[keyType]valueType

  • 语法和结构

    package mainimport "fmt"func main() {// 1. 初始化:使用 make() 函数或字面量初始化//    语法:map[KeyType]ValueType// a) 使用 makeuserAges := make(map[string]int) // 创建一个map,key为string,value为int// b) 使用字面量(推荐初始化时直接赋值)userAges2 := map[string]int{"Alice": 30, // 注意:每行末尾要有逗号"Bob":   25,}// 2. 增删改查:使用类似数组的索引语法,非常直观// - 添加/更新元素:使用 `map[key] = value`userAges["Alice"] = 30userAges["Bob"] = 25userAges["Alice"] = 31 // 更新 Alice 的值// - 获取元素:使用 `value := map[key]`age := userAges["Alice"]fmt.Println(age) // 输出 31// - 检查键是否存在:获取元素时可以接收第二个返回值(bool)age, exists := userAges["Charlie"] // 如果键不存在,exists 为 false,age 为值类型的零值if exists {fmt.Println("Charlie's age is", age)} else {fmt.Println("Charlie does not exist in the map.") // 会执行这条}// - 删除元素:使用内置的 delete() 函数delete(userAges, "Bob")// - 获取大小:使用内置的 len() 函数size := len(userAges)fmt.Println("Size:", size)// 3. 遍历:使用 `for range` 循环,语法极其简洁for key, value := range userAges {fmt.Printf("%s is %d years old.\n", key, value)}// 4. 空值(nil)处理:// - 未初始化的map是nil,无法直接使用var nilMap map[string]int // nilMap 是 nil// nilMap["key"] = "value" // 会导致运行时 panic!// - 值为零值:如果键不存在,`map[key]` 会返回值类型的零值(0, "", nil等)//   这比Java更明确,但也要结合 `exists` 检查来区分“零值”和“不存在”。fmt.Println(userAges["UnknownKey"]) // 输出 0 (int的零值)
    }
    
  • 关键特性分析

    • 内置类型:是语言语法的一部分,不是标准库中的类。
    • 语法操作:使用类似数组的索引语法 [] 进行赋值和访问,更简洁。
    • 多返回值:通过 value, exists := map[key] 的双返回值模式完美解决了Java中“空值歧义”的问题。
    • 内置函数支持:使用 make() 创建,delete() 删除,len() 获取大小。
    • 零值机制:未初始化的map是nil,尝试写入会导致panic。读取不存在的键返回 value类型的零值。

迭代:Go的for-range

  • 语法和结构

    package mainimport "fmt"func main() {// 1. 遍历切片(Slice)或数组(Array) - 最常用names := []string{"Alice", "Bob", "Charlie"}// 语法:for index, value := range collection { ... }// 每次迭代返回两个值:索引和该索引处元素的副本for index, name := range
    

相关文章:

对比Java学习Go——函数、集合和OOP

Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了…...

MySQL集群高可用架构 - 指南

MySQL集群高可用架构 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-si…...

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT专业的工具替代

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT专业的工具替代pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New"…...

如何在Spring MVC中处理请求参数

在Spring MVC中处理请求参数是通过使用各种注解来实现的。以下是在Spring MVC中处理不同类型请求参数的方法。 使用 @RequestParam注解 当你想要从查询字符串中获取单个参数值时,你可以使用 @RequestParam注解。例如: @GetMapping("/search") public String search…...

redis实现缓存2-解决缓存穿透,缓存击穿

具体实现: ShopServiceImpl package com.hmdp.service.impl;import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.hmdp.dto.Result; import com.hmdp.entity.Shop; …...

单克隆抗体人源化:从鼠源缺陷到全人源突破,3 大阶段破解临床应用难题

单克隆抗体(McAb)凭借高特异性、强靶向性,在疾病预防、诊断与治疗中占据核心地位。1975 年,Khler 和 Milstein 创立杂交瘤技术,首次实现人工制备 McAb,为生物医药领域开辟新路径。但初代鼠源性 McAb 存在两大关键缺陷,严重限制临床应用:一是免疫原性高,进入人体后易被…...

在Kubernetes中DaemonSet无法在master节点调度的问题

在Kubernetes中,DaemonSet确保全部(或某些特定)Node运行一个Pod的副本。当有Node加入集群时,DaemonSet会自动在新加入的Node上部署Pod。这对于运行像日志收集器、监控代理或其他形式的守护进程非常有用。 默认情况下,出于安全性的考虑,Kubernetes master节点不允许调度普…...

9 12-

9 12改一道题改了一天,自闭了,总结无法描述,还是自己太糖了/ll P8776 线段树优化DP转移9 13模拟赛唯一一场没有睡着的模拟赛 T1很快想到了换根DP,秒掉 T2很快想到了 \(N^2\) 的暴力,然后经过我的观察发现转移形似杨辉三角,就推了出来 T3T4毫无思路剩下一个半小时直接跑路…...

桌面客户端的主要类型和技术方案

桌面客户端开发已经不再是传统的单一技术栈,而是衍生出了多种方案,各有优劣。下图清晰地展示了这些技术方案的演进与分类: flowchart TD A[桌面客户端技术方案] --> B1["原生开发<br>(Native App)"] A --> B2["跨平台开发<br>(Cross-Platf…...

AGX Orin平台RTC驱动导致reboot系统卡住障碍调试

AGX Orin平台RTC驱动导致reboot系统卡住障碍调试pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace …...

C 语言实现动态数组、链表、栈与队列

从代码到原理:C语言实现动态数组、链表、栈与队列 在数据结构的世界里,线性结构是构建复杂算法的基石。动态数组、链表、栈和队列作为最经典的线性结构,各自拥有独特的存储方式与操作特性,适用于不同的业务场景。本文将结合C语言实现代码,从结构定义、核心操作到实际应用,…...

git reset

在一个文件夹内,初始化其为 git 本地仓库,然后新建一个文件,提交至本地仓库,再修改这个文件,再提交至本地仓库。此时此刻的提交记录:如果用 git reset 命令回到当前所在位置,是不会有任何变化的。用 git reset 命令回到位于当前提交之前的提交,这一步操作也可以复原:如…...

ICPC 2025 网络赛第一场 M

这道题我本来是建立多层图然后跑dijkstra来解决,但是由于N=5000,所以会包空间导致RE或者MLE,注意到其实这道题是从1到n都来一遍,其实就可以考虑k-1和k的关系,k在k- 1的基础上面跑最短路,跑完了之后我们对比传送门的两个点到1的距离,这样它可以更新新的最短路。 #include…...

Brute It -TryHackMe

Brute It -TryHackMe 一、信息收集使用nmap对网站ip开放端口进行搜集使用dirsearch发现网站下面有个admin目录访问看看是一个管理员登录界面在这个页面右键源代码发现了给我们的提示,告诉我们这个网站的账户是admin,我们抓包使用yakit进行爆破二、枚举爆破接下来使用hydra对网…...

题解:P12336 第三心脏

题目链接。作者没看过第三心脏,所以作者猜测第三个心脏应该是用铁做的,由于铁粉是黑的,所以这道题目是黑。养成良好习惯,不留根号,式子变为: \[a^2+b^2+c^2+d^2=\left(a\oplus b\oplus c\oplus d\right)^2 \]注意到 \(a\ge 1\) 所以 \(a^2+b^2+c^2+d^2>d^2\) 又注意到…...

Spring篇知识点(1)

一、Spring框架的特性 IOC和DI支持:Spring 的核⼼就是⼀个⼤的⼯⼚容器,可以维护所有对象的创建和依赖关系,Spring ⼯⼚⽤于⽣成Bean,并且管理 Bean 的⽣命周期,实现⾼内聚低耦合的设计理念。 AOP编程支持:方便实现对程序进行权限拦截、运行监控等切面功能 声明式事务支持…...

在CentOS 7系统中彻底移除MongoDB数据库

彻底移除CentOS 7系统中的MongoDB数据库,需要进行以下步骤:停止MongoDB服务:首先确保MongoDB服务已经停止,可以通过下面的命令来执行这一操作:sudo systemctl stop mongod 如果您的MongoDB服务名称不是默认的 mongod,请将上述命令中的 mongod替换为实际的服务名称。删除M…...

【数学建模】烟幕干扰弹投放策略优化:模型与算法整合框架 - 实践

【数学建模】烟幕干扰弹投放策略优化:模型与算法整合框架 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New&q…...

2025.9.13总结

T1 神秘结论,因为轮数是可以算出来的,然后依次把次数取 min 然后堆起来就是对的。可以 \(O(n)\) 做完。 T2 一次修改的作用是明显的。 答案最大为 2,因为可以 max->2highbit->2(highbit+1)-1。 考虑答案为 1,那么就是跨过所有 0。但是因为覆盖后后面和前面的都没了,…...

开源排名算法工具raink:利用LLM实现智能文档排序

本文介绍Bishop Fox开源的raink工具,该工具采用基于大语言模型的列表排序算法,能够解决复杂排名问题,包括将代码差异与安全公告关联,并详细说明其算法原理及在漏洞识别中的应用场景。raink:使用LLM进行文档排序 TL;DR:Bishop Fox发布了raink,这是一个使用新型基于LLM的列…...

lcjmSSL域名SSL证书免费申请

想为您的网站轻松开启HTTPS安全加密吗?lcjmSSL(来此加密)为您提供完全免费的SSL证书服务!无论是单个站点、多个域名还是需要守护整个子站群的泛域名证书,我们都能满足。单证书最高支持100个域名的极致灵活性,助您以零成本构建更安全、更可信的网站环境。立即体验,为您的…...

uniapp原生插件 TCP Socket 利用文档

uniapp原生插件 TCP Socket 利用文档pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important;…...

【PyQt5】实现输入延迟响应:3秒无输入后自动读取内容

思路:每次输入框内容改变,都重置 QTimer 倒计时为 3 秒;当持续 3 秒无输入后,QTimer 超时,获取当前输入框内容。UI 代码(untitled.py):点击查看代码 from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Form(object):def setupUi(self, Form):Form.setObjectName(&qu…...

线性代数基础

暂无...

微积分基础

暂无...

Windows 自带的SSH中配置X11

本文介绍了给Windows 11中自带的SSH配置X11的方法1.安装 Windows的Xserver很多如:Xming 和 VcXsrv。Xming和VcXsrv都是X服务器软件,允许在Windows系统上运行Linux图形界面应用程序。它们的关系可以从以下方面概括:历史渊源 Xming最初由Colin Harrison于2004年开发,基于X.Or…...

在Kubernetes client-go库中如何有效构建CRD的informer

在Kubernetes ecosystem中,client-go库是一个强大的集合,它提供了与Kubernetes API进行交互的工具,使得我们可以在自己的应用程序中进行创建、配置以及管理Kubernetes资源。而对于自定义资源的定义(CRD),client-go也提供了informer的机制,此机制能够帮助我们监听资源的变…...

中大型水闸安全监测的重要性及实施方法 - 指南

中大型水闸安全监测的重要性及实施方法 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !i…...

如何通过LangChain实现记忆功能的总结

真正贴近人类的智能体,关键在于拥有 “记忆能力”。就像人与人相处时,我们会记住对方的喜好、过往的交流细节,并以此调整后续的沟通方式;具备记忆的智能体,同样能在与用户的互动中,主动留存对话信息、记录关键需求,甚至沉淀用户偏好,进而在未来的交互中给出更精准、更贴…...

python 轻量级别的网页包Streamlit

Streamlit跟 Flask/Django 的区别| 维度 | Streamlit | Flask/Django ||------|-----------|--------------|| 目标 | 数据展示/分析原型 | 全功能网站 || 前端代码 | 0 行 | 需要写 HTML/JS || 开发速度 | 分钟级 | 小时/天级 || 部署 | streamlit run 即可 | 需配路由、模板、…...

完整教程:技术小白如何快速的了解opentenbase?--把握四大特色

完整教程:技术小白如何快速的了解opentenbase?--把握四大特色pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New&quo…...

9.13日模考总结

本周进行了标准OI普及组模考测试 得分情况题目名称 做法 预计得分 实际得分质数差列 模拟、素数筛 100 100旅行 二分答案 100 40小桃的物质阵列 思维 + 模拟 0 0幽邃魔窟 01背包变形 20 60感觉第二题有点可惜,忘了输出 -1 和数据范围了 第四题也有点可惜,没想到是01背包 做题…...

高斯消元

code: const int N=110; const double eps=1e-7;int n; double a[N][N];inline bool zero(double x){return fabs(x)<eps; }int gauss(){for(int i=1;i<=n;i++){int aim=i;//找出 i 列中,未确定主元的行中的最大行for(int j=1;j<=n;j++){//判断是否确定主元if(j<…...

wpf-MVVM+IOC/ID

一、MVVM+IOC/ID承接上文《WPF-理解与使用MVVM,请勿滥用》;这里讲解 MVVM+IOC/ID 的案例。本文来自博客园,作者:꧁执笔小白꧂,转载请注明原文链接:https://www.cnblogs.com/qq2806933146xiaobai/p/19089194...

uni-app iOS 性能监控全流程 多器具协作的实战优化指南

uni-app iOS 性能监控全流程 多器具协作的实战优化指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", mono…...

矩阵快速幂

模板题:洛谷p1939 code: #include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=5,mod=1e9+7; int n,siz=3; struct matrix{LL m[N][N];//构造函数matrix(){memset(m,0,sizeof m);}//重载*运算符matrix operator*(const matrix& B)cons…...

使用 C# 设置 Excel 单元格格式 - 教程

使用 C# 设置 Excel 单元格格式 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !importan…...

grafana部署并使用harbor监控模板

1、部署grafana helm repo add grafana https://grafana.github.io/helm-charts helm repo update helm pull grafana/grafana --version 9.4.4 --untar cd grafana/# 修改values文件ingress:enabled: true # 开启ingressingressClassName: tr…...

【ARM Cache 及 MMU 系列文章 6.1 -- Cache maintenance 指令及相关寄存器有哪些?】

Cache Maintenance registers and instructions Armv8/v9 里定义的Cache的管理的操作有三种:Invalidate : 整个高速缓存或者某个高速缓存行。高速缓存上的数据会被丢弃。 Clean : 整个高速缓存或者某个高速缓存行。相应的高速缓存行会被标记为脏,数据会写回到下一级高速缓存…...

十八、CPU的控制流:正常控制流和异常控制流

目录一、什么是控制流?二、正常控制流三、异常控制流四、正常控制流 vs. 异常控制流总结与重要性一、什么是控制流? 控制流指的是程序计数器(PC或EIP/RIP)随时间变化的序列。简单来说,就是CPU执行指令的顺序。 从你按下电源键开始,CPU就在不停地取指令、执行指令,PC寄存…...

大模型基础|位置编码|RoPE|ALiBi

转自:https://zhuanlan.zhihu.com/p/650469278 Transformer 模型在处理序列数据时,其自注意力机制使得模型能够全局地捕捉不同元素之间的依赖关系,但这样做的代价是丧失了序列中的元素顺序信息。由于自注意力机制并不考虑元素在序列中的位置,所以在输入序列的任何置换下都是…...

成品app直播源码搭建,sql优化原则 - 云豹科技

成品app直播源码搭建,sql优化原则SQL 作为关系型数据库的标准语言,是 IT 从业人员必不可少的技能之一。SQL 本身并不难学,编写查询语句也很容易,但是想要编写出能够高效运行的查询语句却有一定的难度。查询优化是一个复杂的工程,涉及从硬件到参数配置、不同数据库的解析器…...

使用Clang静态分析技术追踪Heartbleed漏洞

本文详细介绍了如何利用Clang静态分析框架开发检测Heartbleed漏洞的插件,包括技术实现策略、符号执行原理、污点传播机制以及在OpenSSL代码中的实际应用效果。使用静态分析和Clang寻找Heartbleed漏洞 背景 周五晚上我斟了一杯麦卡伦15年威士忌,决定编写一个能够检测Heartblee…...

每日Java并发面试系列(5):基础篇(线程池的核心原理是什么、线程池大小设置为多少更合适、线程池哪几种类型?ThreadLocal为什么会导致内存泄漏?) - 实践

每日Java并发面试系列(5):基础篇(线程池的核心原理是什么、线程池大小设置为多少更合适、线程池哪几种类型?ThreadLocal为什么会导致内存泄漏?) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !i…...

累死你的不是工作,而是工作方式

《浪潮之巅》的作者吴军,在《得到》专栏里,提及了Google刚进中国时候的一件事。刚开始,Google总部对中国研发团队的评价非常低,因为“出工不出活儿”,北京的三四个工程师都抵不上Google总部的一个工程师。 后来吴军帮忙分析了原因,他发现,那些工程师都不善于找到最重要的…...

川土微CA-IF1051S、CA-IF1051VS 支持CAN FD

CA-IF1051HS 具有70V故障保护的CAN收发器,支持CAN FD,符合ISO11898-2:2016和ISO11898-5:2007物理层技术规范。该系列器件设计用于高速CANFD网络,可支持高达5Mbps的传输速率。CAN总线端口提供高达70V的故障保护,满足恶劣环境中的过压保护需求。接收器输入共模范围(CMR)高达3…...

模仿玩家习惯的简单AI系统:GoCap

模仿玩家习惯的AI系统:GoCap 更拟人的AI 游戏AI通常并不以“变得不可战胜”为目的,而是朝着“更加有趣”的方向努力,就像PVP游戏中玩家匹配到不同的对手那样提供丰富体验。如果游戏AI也能像不同玩家一样就好了,可还是用设计行为树的方式来制定不同的AI的话,一定需要不少的…...

浅谈马拉车

浅谈马拉车 马拉车其实挺好理解的,写篇博客以便复习。 正题 简介 Manacher主要的思想是回文串的对称性,即在一个大回文串中,一定存在一个与\(X\)关于回文对称中心对称的子串\(Y\),故我们利用已知的回文串搞事情.算法流程考虑回文串有ABA(对称中心为一个字符)和ABBA(对称中心…...

Redisson 分布式锁的实现原理 - 教程

Redisson 分布式锁的实现原理 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important;…...

关于前端的一些疑问整理(标签属性值和符号)

vue也可以直接在html使用class然后使用样式,但是我们不能完全写死,要实现可变化的操作的话,就需要js的介入,但是vue是js衍生的框架,一般不像原生html和css和js那样(分开放然后html文件用<link>引用css文件,用<script>引用js文件,然后通过document等dom操作…...