Golang基础

Golang数据类型

整型

Go语言同时提供有符号和无符号的整数类型

  • 有符号整型:int、int8、int64、int32、
  • 无符号整型:uint、uint8、uint64、uint32、uint64、uintptr

int8、int16、int32 和 int64 四种大小截然不同的有符号整数类型,分别对应 8、16、32、64 bit(二进制位)大小的有符号整数,与此对应的是 uint8、uint16、uint32 和 uint64 四种无符号整数类型。

int 和 uint,它们分别对应特定 CPU 平台的字长(机器字大小),其中 int 表示有符号整数,应用最为广泛

Go语言中int类型变量的声明

var 变量名 int;

浮点型

Go语言支持两种浮点型数:

  • float32 : 范围约1.4 * 10[^-45 ] 到 3.4 * 10[^38]

  • float64 : 范围约4.9 * 10[^-324]到1.8 * 10[^308]

Go语言中float32 / float64变量的声明

var 变量名 float32;
var 变量名 float64;

布尔型

  • 一个布尔类型的值只有两种:true 或 false。if 和 for 语句的条件部分都是布尔类型的值

  • ==, >, <, <= ,>=, &&(AND),||(OR)等都会产生bool值

Go语言中,bool类型变量的声明

var 变量名 bool;

字符

Go语言中字符类型

  • unit8类型,或者叫byte类型,代表了ASCII的一个字符
  • rune类型,代表一个UTF-8字符,当需要处理中文,日文,或者其他复合字符时,则需要用到rune类型。rune类型等价于int32类型;

byte类型是unit8类型,rune类型是int32的别名

ASCII码的一个字符占一个字节

ASCII定义128个字符,由码位0-127表示。

  • unicode.IsDigit 判断某个值是否为数字字符

字符的定义

//使用单引号 表示一个字符
var 变量名 byte = 'A'
//在 ASCII 码表中, A的值是65, 也可以这么定义
var 变量名 byte = 65
//65使用16进制表示是41,所以也可以这么定义 \x总是紧跟着长度为2的16进制数
var 变量名 byte = '\x41'
//65使用16进制表示是101, 所以使用八进制定义 \后面紧跟着长度为 3 的八进制数
var 变量名 byte = '\101'


Go语言同样支持Unicode(UTF-8), 用rune来表示,在内存中使用int来表示。

在写Unicode字符是,需要在16进制数之前加上前缀\u或者\U。如果需要使用到4字节,则使用\u前缀, 如果需要使用到8个字节,则使用\U前缀。

var ch rune =  '\u0041'     // 65

Demo

package main

import (
    "fmt"
    "unicode"
)

func main() {
    var a byte = 'A'
    fmt.Println("a = ", a)
    var b byte = 65
    fmt.Println("b = ", b)
    var c byte = '\x41' //a ==> 65 的16进制是41
    fmt.Println("c = ", c)
    var d byte = '\101' //a ==> 64 的8进制是101
    fmt.Println("d = ", d)

    //使用了 fmt.Println 来打印 byte 变量
    //而 byte 是 uint8 类型的别名,因此 fmt.Println 会直接输出它们的数值(ASCII 码)。

    //用fmt.printf()  ==> 格式化打印
    fmt.Println("------------------------------------------------------")

    fmt.Printf("a = %c\n", a)
    fmt.Printf("b = %c\n", b)
    fmt.Printf("c = %c\n", c)
    fmt.Printf("d = %c\n", d) //输入结果是 A A A A

    var e rune = '1'
    var f rune = 49

    fmt.Println("------------------------------------------------------")

    fmt.Println(unicode.IsLetter('A'))
    fmt.Println(unicode.IsSpace(' '))

    //unicode.IsDigit  判断某个值是否为数字字符
    fmt.Println(unicode.IsDigit(1)) //false .
    // 原因:
    //unicode.IsDigit 函数是用来检查给定的 rune 是否是数字字符(0-9),
    //它的参数应该是 rune(即 Unicode 码点),而不是整数值。

    fmt.Println("e = ", e)          //1  对应的ASCII码是49
    fmt.Println(unicode.IsDigit(e)) // true

    fmt.Println(unicode.IsDigit(f)) //true 1 的Unicode码点是 49  故为true
    fmt.Printf("f = %c", f)         // 验证, 打印 49 格式化成字符的值  ==> 1

}

转义字符

字符串中可以使用转义字符来实现换行、缩进等效果,常用的转义字符包括:

  1. \n:换行符
  2. \r:回车符
  3. \t:tab键
  4. \u或\U:Unicode字符
  5. \ :反斜杠自身

字符串

一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本,字符串是UTF-8字符的一个序列

  • 字符串是一种值的类型,且值不可变,即创建某个文本后无法再次修该内容

字符串的定义:

var 变量名  string = "hello"

GO语言从地城就支持UTF-8编码。

package main

import "fmt"

func main() {

	var str string = "Hello World"
	fmt.Println(str)

	var str1 string = "你好! 世界!"
	fmt.Println(str1)

}

字符串查找

如何获取字符串中的某一段字符

  • strings.Index():正向搜索字符串
  • strings.LastIndex(): 反向搜索字符串

Demo

package main

import (
	"fmt"
	"strings"
)

func main() {
	var str string = "Hello World! Hello Golang!"

	var index1, index2, index3, index4 int
	index1 = strings.Index(str, "Golang!")
	index2 = strings.Index(str, "World!")
	fmt.Println("正向查找:World! 第一次出现的位置是:", index2)
	fmt.Println("正向查找:Golang! 第一次出现的位置是:", index1)

	index3 = strings.LastIndex(str, "Golang!")
	index4 = strings.LastIndex(str, "World!")
	fmt.Println("反向查找:Golang!第一次出现的位置是:", index3)
	fmt.Println("反向查找:World!第一次出现的位置是:", index4)

	var index5 int = strings.Index(str, "ssss") // 没有查询到字符,返回-1
	fmt.Println(index5)

}

反引号

如果使用``反引号,会被原样进行复制和输出

Demo

package main

import "fmt"

func main() {

	fmt.Println("\t  Leran Golang ing") //输出时 \t 会输出tab键的效果
	fmt.Println(`\t  Leran Golang ing`) //输出时 \t 会原样输出
	fmt.Println(`\t  Leran Gol
ang ing`) 								//输出时 \t 与 回车 都不会被转化
}

输出示例

  • 反引号一般用在 需要将内容原样输出的时候 使用。

字符串与其他数据类型的转换

遍历字符串

Unicode字符集使用for range进行遍历,ascii字符集可以使用for range或者for 循环遍历

Demo

package main

import "fmt"

func main() {
	var str1 string = "This is first Hello  World!"
	var str2 string = "这是三个Hello World!"
	//遍历

	for i := 0; i < len(str1); i++ {
		fmt.Printf("ascii %c %d\n", str1[i], str1[i])
	}
	fmt.Println("----------------------")

	for _, s := range str1 {
		fmt.Printf("ascii %c %d\n", s, s)
	}
	fmt.Println("----------------------")
	for _, s := range str2 {
		fmt.Printf("unicode %c %d\n", s, s)
	}
}

字符串与其他数据类型的转换

整数与字符串

  • strconv.Atoi(String)把字符串转化成整数

  • strconv.Itoa(int)把整数转化成字符串

  • strconv.ParseFloat(String,bitSIze) String转float第二位是指定精度

  • FormatFloat(f float64, fmt byte, prec, bitSize int) float转String

**FormatFloat(f float64, fmt byte, prec, bitSize int) **

  • 第一个参数:要格式化的浮点数。
  • 第二个参数:格式化类型。
    • b表示无小数点的指数表示法。例如123456789.0将格式化为1.23456789p+08
    • e科学计数法。例如123456789.0将格式化为1.23456789e+08
    • f小数点格式,例如123.456
  • 第三个参数:精度,表示小数点后的位数。
  • 第四个参数:位大小(32 表示 float32,64 表示 float64)。

Demo

package main

import (
    "fmt"
    "strconv"
)

func main() {
    var str1 string = "123"
    istr1, _ := strconv.Atoi(str1)
    fmt.Printf("istr1 = %d  istr1 的类型是 %T\n", istr1, istr1)

    var int1 int = 456
    sint1 := strconv.Itoa(int1)
    fmt.Printf("sint1 = %s  sint1 的类型是 %T\n", sint1, sint1)

    var str2 string = "14.15"
    ifloat1, _ := strconv.ParseFloat(str2, 64)
    fmt.Printf("ifloat = %f ifloat 的类型是 %T\n", ifloat1, ifloat1)

    var flo1 float64 = 15.16
    sflo1 := strconv.FormatFloat(flo1, 'f', 2, 64)
    fmt.Printf("sflo1 = %s sflo1 的类型是 %T\n", sflo1, sflo1)
}

类型转换

Go语言中不存在隐式类型转换,因此所有的类型转换都必须显式声明:

类型转换只能在定义正确的情况下转换成功,例如一个取值范围较小的转换到一个取值范围较大的类型(将int16 转换为 int32)。

当从一个取值范围较小的数据类型转化为一个取值范围较大的数据类型时,可能会发生,数据截断(丢失)…等等情况。

不同底层的数据类型无法进行类型转换,比如 bool类型的变量无法转换成int类型的

转换格式

类型A的变量 := 类型A(类型B的值)

demo:
var v int 
v := int(5.0)

常量

Go语言中常量使用关键字const定义。常量在程序运行时不会改变。常量在编译时就确定了它们的值,不能在运行时修改。

  • 常量可以是布尔类型、数字类型(整数、浮点数、复数)、字符串类型。

如果批量声明的常量,除了第一个外其他的常量右边的初始化表达式都可以省略,如果省略,则表示使用前面的初始化表达式,对应的常量类型也是一样的

Demo

package main

import "fmt"

func main() {
	const (
		a = 1
		b
		c = "const"
		d
	)
	fmt.Println(a, b, c, d)  //1 1 const const
}

iota常量生成器

iota是Go语言中的一个常量生成器,用于简化连续常量的值的生成。在常量生命块中,iota初始值为0,每新增一行常量声明,iota的值会自动增加1。

Demo: 使用iota生成一组连续的常量

package main

import "fmt"

func main() {
	const (
		a = iota	//0
		b			//1
		c			//2
		d			//3
		e			//4
		f			//5
		g			//6
	)
	fmt.Println(a, b, c, d, e, f, g)
}

指针

在Go语言中指针是一种允许你自己操作内存地址的数据类型,指针是你可以修改变量的值,而不需要创建副本,从而提高了程序的效率。

声明指针

var p *int

获取变量的地址

要获取一个变量的地址,可以使用&符号。例如:

var x int = 10
var p *int
p = &x

这里 p 将保存 x 的内存地址

解引用指针

如果想要访问指针指针指向变量的值,可以使用*,这称为解引用。例如:

fmt.println(*p)

还可以通过解引用指针来修改指针指向变量的值:

*p = 20
fmt.println(x)

Demo

package main

import "fmt"

func main() {
	var x int = 10
	var p *int
	p = &x
	fmt.Println("*p = ", *p)
	*p = 20
	fmt.Println("x = ", x)
}

数组

Go语言中,数组是一种固定长度的数据结构,可以存储相同类型的元素。

声明数组

var 数组名称 [数组长度]数据类型

初始化数组

  1. 在声明的时候初始化

    var arr = [5] int{1,2,3,4,5}
    
  2. 可以使用省略号让编译器自动计算数组的长度。

    arr := [...]int{1, 2, 3, 4, 5} // 编译器会推断数组的长度为5
    

Demo

package main

import "fmt"

func main() {
    var arr = [5]int{1, 2, 3, 4, 5}
    for i, i2 := range arr {
       fmt.Println("arr[", i, "]", " = ", i2)
    }

    arr1 := [...]int{1, 2, 3, 4, 5}
    for i, i2 := range arr1 {
       fmt.Println("arr1[", i, "]", " = ", i2)
    }
}

切片

在Go语言中,切片也是可以容纳若干个相同类型的数据。与数组不同的是,切片的长度可以动态变化。

切片在 Go 语言中是一个引用类型。切片不像数组一样直接存储元素的数据,而是存储了一个指向底层数组的指针、切片的长度和切片的容量。因此,当你将一个切片赋值给另一个切片时,它们实际上共享相同的底层数组。

这个存储片段可以是整个数组,也可以是数组的某一部分(子集)。如果值需要数组的一部分,可以使用切片表达式指定起始和终止索引。需要注意的是,终止索引的项不包含在切片内(左闭右开)

从连续内存区域生成切片

slice[开始位置:结束位置]

用数组或者切片中生成的新的切片拥有如下特性:

  • 取出的元素数量为: 结束位置 - 开始位置
  • 取出的元素不包含结束位置的对应的索引。
  • 省略“开始位置”时,表示从头开始取。
  • 省略”结束位置“时,表示从开始位置取到最后。

Demo

package main

import "fmt"

func main() {
    arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    slice := arr[3:5]
    fmt.Println(slice) //[4 5]

    slice[0] = 1               //arr[3] 与slice[0]都被修改为0       切片对地址进行操作
    fmt.Println("slice[0] = ", slice[0]) //1
    fmt.Println("arr[3] = ", arr[3])     //1
}

声明切片

切片的声明不需要指定长度,但需要指定元素类型。

var s []int // 声明一个整数类型的切片

使用make()函数构造切片

make([]T, size, cap)
  • T 是切片的元素类型。
  • size 是切片的长度(即切片中的元素数量)。
  • cap 是切片的容量(预分配的元素数量)

Demo

package main

import "fmt"

func main() {

    slice := make([]int, 5, 10)
    fmt.Println(slice)                         //[0 0 0 0 0]
    fmt.Printf("len(slice)=%d)\n", len(slice)) //5
    fmt.Printf("cap(slice)=%d)\n", cap(slice)) //10
}

切片的复制

Go语言中内置的copy函数可以完成切片的复制

copy(dest,src []T) int
  • dest是目标切片,用于接收复制后的元素
  • src是源切片,提供要复制的元素
  • int是返回值,返回实际发生复制的元素的个数
package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3, 4, 5, 6}

    slice2 := make([]int, 3)

    re := copy(slice1, slice2)
    fmt.Println("slice1:", slice1) //slice1: [0 0 0 4 5 6]
    fmt.Println("slice2:", slice2) //slice2: [0 0 0]
    fmt.Println("re:", re)       //re: 3

}

nil

在Go语言中,nil 有以下几个常见的用法和含义:

  • 对于指针类型,nil 用于表示指针类型的零值,即指针不指向任何有效的内存地址。

  • 对于切片、映射和函数类型,nil 表示未分配(切片、映射)或未初始化(函数)的状态。

  • 接口类型的变量在没有分配具体类型实现时,默认为 nil

  • 在Go中,nil 也可以用于表示任何引用类型或者接口类型的零值状态。这种状态通常表示某个值不存在或者未被分配。

  • 在Go中,nil 可以用于与指针、切片、映射、函数和接口类型的变量进行比较。

    var ptr *int
    fmt.Println(ptr == nil)  // 输出: true
    

if-else

if-else 是编程语言中常见的控制流结构,用于根据条件执行不同的代码块

语法

if condition {
    // 如果条件为真,执行这里的代码块
} else {
    // 如果条件为假,执行这里的代码块
}
  • condition 是一个布尔表达式,可以是任何能产生布尔值(truefalse)的表达式。

for range

for range 是 Go 语言中用于迭代数组、切片、字符串、映射(map)等数据结构的一种语法结构。它提供了一种简洁而优雅的方式来遍历这些数据结构的元素或者键值对。

基本语法

for index, value := range collection {
    // 在这里使用 index 和 value
}
  • collection 可以是数组、切片、字符串、映射(map)等可迭代的数据结构。
  • index 是当前元素的索引(对于映射则是键),value 则是在该索引位置的元素值(或者键值对中的值)。
  • for range 循环体内部,可以直接使用 indexvalue 这两个变量来访问每个元素或者键值对的内容。
package main

import "fmt"

func main() {
    arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    for _, i2 := range arr {
       fmt.Printf("%d ,", i2)
    }
    fmt.Println()

    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    for _, i := range slice {

       fmt.Printf("%d ,", i)
    }
}

switch

在 Go 语言中,switch 是一种条件语句,用于简化多个条件分支的编写。它与其他语言中的 switch 类似,但在 Go 中有一些特有的用法和限制。

switch expression {
case value1:
    // 当 expression == value1 时执行
case value2:
    // 当 expression == value2 时执行
default:
    // 当 expression 不等于任何 case 的值时执行
}
  • switch 后面通常是一个表达式 expression,它的值会被依次与每个 case 中的值比较。
  • 如果 expression 的值等于某个 case 中的值,则执行该 case 的代码块。
  • 如果没有匹配到任何 case,则会执行 default 分支(可选的)。
  • 每个 case 后面不需要加 break 语句,Go 语言的 switch 自动提供了这种行为。

简单的整数比较Demo

package main

import "fmt"

func main() {
    num := 2

    switch num {
    case 1:
        fmt.Println("One")
    case 2:
        fmt.Println("Two")
    case 3:
        fmt.Println("Three")
    default:
        fmt.Println("Unknown number")
    }
}

多个条件的情况Demo

package main

import "fmt"

func main() {
    num := 4

    switch num {
    case 1, 2, 3:
        fmt.Println("One, Two, or Three")
    case 4, 5, 6:
        fmt.Println("Four, Five, or Six")
    default:
        fmt.Println("Other")
    }
}

在这个Demo中,switchcase 可以同时包含多个值,用逗号分隔。

使用表达式作为条件Demo

package main

import "fmt"

func main() {
    num := 10

    switch {
    case num < 0:
        fmt.Println("Negative number")
    case num == 0:
        fmt.Println("Zero")
    case num > 0 && num < 10:
        fmt.Println("Single digit positive number")
    default:
        fmt.Println("Double digit or larger positive number")
    }
}

在这个例子中,switch 后面没有具体的表达式,每个 case 后面的条件表达式会被求值,第一个满足条件的 case 会被执行。

  • Go 语言中的 switch 是强制执行 case 之后的自动 break,不需要显式添加 break 语句来防止掉入下一个 case 中。
  • 如果需要执行多个 case 中的代码块,可以使用 fallthrough 关键字。它会继续执行下一个 case 的代码块,但不会执行下一个 case 的条件判断。
package main

import "fmt"

func main() {
    num := 2

    switch num {
    case 1:
        fmt.Println("One")
        fallthrough
    case 2:
        fmt.Println("Two")
    case 3:
        fmt.Println("Three")
    default:
        fmt.Println("Unknown number")
    }
}

goto

在 Go 语言中,goto 是一个关键字,用于无条件地将控制流转移到程序中的另一个标签(label)处。它的作用类似于其它编程语言中的 goto 语句,允许直接跳转到程序的另一部分而不受条件或循环结构的限制。

使用goto跳出多层循环

package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
       for j := 0; j < 20; j++ {
          if j == 10 {
             //跳转到标签
             goto breakHere
          }
       }
    }

    return //手动return,避免执行进入标签

    //标签
breakHere:
    fmt.Println("Done")
}

函数

在 Go 语言中,函数是一种基本的代码组织单元,用于执行特定的任务或操作。

在 Go 中声明和定义一个函数的一般形式如下

func functionName(parameters) returnType{
	// 函数体
    // 可能包含多条语句
    return returnValue
}
  • 函数名:函数的名称是一个标识符,遵循 Go 的标识符命名规则。
  • 参数:函数可以接受零个或多个参数(也称为形式参数),每个参数由参数名和类型组成。
  • 返回类型:函数可以返回一个或多个值。如果函数不返回任何值,可以省略返回类型。

derfer延迟调用

在 Go 语言中,defer 关键字用于延迟函数的执行,它通常用于在函数执行完成后,执行一些清理工作或资源释放操作。

特点和用法

  1. 延迟执行
    • 使用 defer 可以将函数推迟到包含 defer 语句的函数执行完成后执行。无论函数是通过正常返回还是遇到异常返回,defer 语句都会执行。
  2. 多个defer
    • 在同一个函数中可以使用多个 defer 语句,它们按照后进先出的顺序执行。即最后一个 defer 语句会最先执行,依此类推。
  3. 参数传递
    • defer 延迟执行的函数可以接收外部函数的参数。在 defer 语句定义时,会立即计算并保留参数的当前值,但实际执行是在函数返回时。
  4. 典型用途
    • 文件操作中,打开文件后立即使用 defer 延迟关闭文件,确保在函数结束时始终关闭文件。
    • 锁的加锁和解锁操作,确保锁的正确释放。
    • 数据库连接的获取和释放。

异常处理

Go语言中,panic抛出错误,recover捕获错误

panicpanic 用于报告严重错误,导致当前函数立即停止执行。当函数调用 panic 时,它会立即中止当前函数的执行,并沿调用栈向上传播,直到程序终止。

package main

import "fmt"

func someFunc() {
    panic("something bad happened")
}

func main() {
    fmt.Println("a")
    someFunc()
    fmt.Println("b")
}

recoverrecover 用于从 panic 中恢复。它只能在 defer 函数中调用,并且用于捕获 panic 产生的错误信息,防止程序终止。

package main

import (
    "fmt"
)

func recoverDemo() {
    defer func() {
       if r := recover(); r != nil {
          fmt.Println("Recovered:", r)
       }
    }()

    // 通过panic触发异常
    panic("oh no! something went wrong")
}

func main() {
    fmt.Println("Starting main function")

    // 调用包含defer和panic的函数
    recoverDemo()

    fmt.Println("End of main function")
}

结构体

在 Go 语言中,结构体(struct)是一种用户自定义的复合数据类型,用于组合不同类型的数据字段。结构体允许你将多个相关的数据字段组合成一个单独的类型,使得数据管理和组织更加方便和有效。

结构体的成员也可以称为“字段”,这些字段有一下特性:

  • 字段拥有自己的类型和值;
  • 字段名必须唯一;
  • 字段的类型也可以是结构体,甚至是字段所在结构体的类型

使用关键字type可以将各种基本类型定义为自定义类型。结构体是一种复合的基本类型

结构体的定义格式

type 结构体名称 struct {
	字段1  字段1类型;
	字段2  字段2类型;
	...
}
  • 结构体名称在同一个包中不能重复

结构体实例化

以var的方式生命结构体

var 实例名称  结构体名称
package main

import "fmt"

type player struct {
    name string
    age  int
}

func main() {
    var p1 player
    p1.age = 18
    p1.name = "zhangsan"
    fmt.Println(p1.name)
    fmt.Println(p1.age)
}

取结构体的地址实例化

package main

import "fmt"

type player struct {
    name string
    age  int
}

func main() {
    var p1 player
    p1.age = 18
    p1.name = "zhangsan"
    fmt.Println(p1.name)
    fmt.Println(p1.age)

    fmt.Println("------------")
    p2 := &player{}
    p2.age = 22
    p2.name = "lisi"
    fmt.Println(p2.name)
    fmt.Println(p2.age)
}

匿名结构体

在 Go 语言中,可以使用匿名结构体来快速定义临时的复合数据类型,而无需显式声明结构体类型的名称。

Demo

package main

import "fmt"

func main() {
    // 定义并初始化匿名结构体
    person := struct {
       Name string
       Age  int
    }{
       Name: "Alice",
       Age:  30,
    }

    // 访问匿名结构体字段
    fmt.Println("Name:", person.Name) // 输出: Name: Alice
    fmt.Println("Age:", person.Age)   // 输出: Age: 30

    // 直接打印匿名结构体
    fmt.Println(person) // 输出: {Alice 30}
}

方法

在 Go 语言中,方法是与特定类型关联的函数。它们允许我们为用户定义的类型添加行为,类似于面向对象语言中的方法。

声明方法

在 Go 中声明方法与声明函数类似,但有一个额外的接收器(receiver),它指定了方法作用的特定类型。

package main

import "fmt"

// 定义结构体
type Person struct {
    Name string
}

// 给结构体绑定方法
func (p Person) test() {
    fmt.Println(p.Name)
}

func main() {

    var person Person

    person.Name = "Jack"

    person.test()
}

方法与函数的区别

  • 方法是与类型关联的函数,可以在特定类型的实例上调用。
  • 方法的声明中必须包含一个接收器,即方法作用的对象类型。
  • 方法可以通过接收器访问实例的字段和方法。

指针接收器和值接收器

接收器可以是值接收器或者指针接收器。使用指针接收器可以在方法内部修改接收器变量的状态。

package main

import "fmt"

type Counter struct {
    count int
}

// 值接收器方法
func (c Counter) increment() {
    c.count++
}

// 指针接收器方法
func (c *Counter) reset() {
    c.count = 0
}

func main() {
    counter := Counter{count: 10}

    // 值接收器方法调用
    counter.increment()
    fmt.Println(counter.count) // 输出: 10,因为 increment 方法是值接收器,不修改原始变量

    // 指针接收器方法调用
    counter.reset()
    fmt.Println(counter.count) // 输出: 0,因为 reset 方法是指针接收器,可以修改原始变量
}
  • 在上面的例子中,increment 是值接收器方法,而 reset 是指针接收器方法。指针接收器方法可以修改接收器变量的值,而值接收器方法只能操作副本,不会修改原始变量。

接口

在 Go 语言中,接口(interface)是一种类型,它定义了一组方法的集合。接口提供了一种方式来规范对象的行为:任何实现了接口中定义的方法的类型,都可以被看作是实现了该接口。

接口声明基本语法

type InterfaceName interface {
    Method1(parameter_list) return_type
    Method2(parameter_list) return_type
    // 可以定义多个方法
}
  • InterfaceName 是接口的名称。
  • Method1, Method2 等是接口中定义的方法,包括方法名、参数列表和返回类型,但不包括实现的具体代码。

接口实现

要实现一个接口,只需在类型中定义该接口中定义的所有方法即可。不需要显式声明实现了某个接口,只要方法匹配即可。

package main

import "fmt"

// 定义接口
type Animal interface {
    Speak() string
}

// 定义结构体类型 Dog,实现 Animal 接口
type Dog struct {
    Name string
}

// 实现 Animal 接口中的 Speak 方法
func (d Dog) Speak() string {
    return "Woof!"
}

func main() {
    // 创建一个 Dog 类型的实例
    myDog := Dog{Name: "Buddy"}

    // 因为 Dog 类型实现了 Animal 接口的 Speak 方法,所以可以赋值给 Animal 接口类型
    var animal Animal
    animal = myDog

    // 调用 Animal 接口的方法
    fmt.Println(animal.Speak()) // 输出: Woof!
}
  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yliken

你的鼓励就像月亮,照亮了在黑暗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值