Golang进阶学习

Golang进阶学习

视频地址:https://www.bilibili.com/video/BV1Pg41187AS?p=35

1、包

1.1、包的引入

使用包的原因:

  • 我们不可能把所有函数放在同一个源文件中,可以分门别类的放在不同的文件中

  • 解决同名问题,同一个文件中不可以定义相同名字的函数

首先写一个连接数据库的工具函数:


package dbUtilsimport "fmt"func GetConn() { //如果方法首字母大写,则可以被其它包访问fmt.Println("我执行了dbUtils包下的getConn函数")}

再去写main函数:


package main // 1.package是进行包的声明,建议:包的声明与所在文件夹相同import ("GoStudy/advanced/dbUtils" // 2.导入其它包时,包名是从$GOPATH/src/后开始计算的,使用/分隔"fmt")func main() {fmt.Println("我执行了main包下的main函数")dbUtils.GetConn() // 3.函数调用时,前面要定位到所在的包} 

1.2、包的细节

  1. 建议包的声明和所在文件夹同名

  2. main包是程序的入口包,一般main函数会放在这个包下

  3. 打包语法:package 包名

  4. 引入包的语法:import “包的文件夹的路径”,包名是从$GOPATH/src/后开始计算的,使用/分隔

  5. 如果有多个包要导入,推荐采用以下格式


import ("GoStudy/advanced/dbUtils""fmt")
  1. 函数调用时前面要定位到所在的包

  2. 首字母大写,函数可以被其它包访问

  3. 一个目录下不能有重复的函数

  4. 包名和文件夹的名字可以不一样


// dbUtils.gopackage aaa //这里声明的包是aaaimport "fmt"func GetConn() { fmt.Println("我执行了dbUtils包下的getConn函数")}//main.gopackage main import ("GoStudy/advanced/dbUtils" //这里引用的依然是dbUtils"fmt")func main() {fmt.Println("我执行了main包下的main函数")aaa.GetConn() //调用的变成了aaa.GetConn()}
  1. 一个包下如果有多个go文件,则每个文件里**package 包名**必须一致

包是什么

  • 在程序层面看,包其实是所有使用相同 package 包名 的源文件组成的代码模块

  • 在源文件层面看,其实就是一个文件夹

1.3、init()函数

在Go语言程序执行时导入包语句会自动触发包内部init()函数的调用。

需要注意的是:init()函数没有参数,也没有返回值。

init()函数在程序运行时,会自动被调用执行,不能在代码中主动调用它。

分析init()和main()这两个函数:

相同点:

  • 在定义时不能有任何的参数和返回值

  • 只能由go自动调用,不可以被引用

不同点:

  • init()用于初始化信息,main()用于作为程序的入口

  • init可以应用于任意包中,且可以重复定义多个

  • main函数只能应用于main包中,且只能定义一个

init函数的执行顺序:

  • 对于同一个package

    • 如果是在同一个go文件下,按照init函数的顺序执行

    • 如果是在同一个包中不同go文件下,会将文件名按字符串排序,然后依次执行文件中的init函数

  • 对于不同package

    • 如果不相互依赖,按照 main 包中 import 的顺序调用其包中的 init()函数

    • 如果存在依赖,则最后被依赖的最先被初始化

2、错误处理

2.1、引入

故意写一个除0操作:


package mainimport "fmt"func main() {test()fmt.Println("继续执行。。。")}func test() {num1 := 10num2 := 0result := num1 / num2 // 这里除0了fmt.Println(result)}/*panic: runtime error: integer divide by zero  // panic:恐慌*/

从输出可以看出来程序中出现错误/恐慌以后,程序被中断,无法继续执行。

类似于Java中的try catch异常捕获,Go中使用defer recover机制处理错误。


package mainimport "fmt"func main() {test()fmt.Println("继续执行。。。")}func test() {// 利用defer + recover来捕获错误:defer后加上匿名函数的调用defer func() {// 调用recover内置函数,可以捕获错误err := recover()// 如果没有捕获错误,返回值为零值:nilif err != nil {fmt.Println("错误已经捕获")fmt.Println(err)}}()num1 := 10num2 := 0result := num1 / num2fmt.Println(result)}/*错误已经捕获runtime error: integer divide by zero继续执行。。。*/

优点:

  • 提高程序健壮性

2.2、自定义错误

需要调用errors包下的New函数:函数返回error类型


package mainimport ("errors""fmt")func main() {err := test()if err != nil {fmt.Println("错误为:", err)}fmt.Println("继续执行。。。")}func test() (err error) {num1 := 10num2 := 0if num2 == 0 {// 抛出自定义错误return errors.New("除数不能为0~")} else {result := num1 / num2fmt.Println(result)return nil}}/*错误为: 除数不能为0~继续执行。。。*/

当出现这样的情况:出现错误以后,后续代码没必要执行了,直接退出。可以借助:builtin包下的内置函数:panic


package mainimport ("errors""fmt")func main() {err := test()if err != nil {fmt.Println("错误为:", err)panic(err)}fmt.Println("继续执行。。。")}func test() (err error) {num1 := 10num2 := 0if num2 == 0 {// 抛出自定义错误return errors.New("除数不能为0~")} else {result := num1 / num2fmt.Println(result)return nil}}/*错误为: 除数不能为0~panic: 除数不能为0~    */

3、数组

3.1、数组定义格式

var 数组名 [数组大小]数据类型var scores [5]int

3.2、数组内存分析

数组名中存的地址其实就是数组中下标为0的元素对应的地址。


package mainimport "fmt"func main() {var arr [3]int16fmt.Println(len(arr)) // 数组长度fmt.Println(arr)fmt.Printf("arr的地址是: %p\n", &arr)fmt.Printf("arr[0]的地址是: %p\n", &arr[0])fmt.Printf("arr[0]的地址是: %p\n", &arr[1])}/*3[0 0 0]arr中的地址是: 0xc0000a6058arr[0]中的地址是: 0xc0000a6058arr[1]中的地址是: 0xc0000a605a // 因为是int16,所以二者相差2字节*/

3.3、数组的遍历


package mainimport "fmt"func main() {var arr [3]intarr[0] = 98arr[1] = 22arr[2] = 53sum := 0// 常规遍历for i := 0; i < len(arr); i++ {sum += arr[i]}// for range遍历for key, value := range arr {fmt.Println(key)sum += value}fmt.Println(sum)}

3.4、数组初始化


package mainimport "fmt"func main() {// 第一种:var arr1 [3]int = [3]int{16, 88, 53}fmt.Println(arr1)// 第二种var arr2 = [3]int{16, 88, 53}fmt.Println(arr2)// 第三种var arr3 = [...]int{16, 88, 53}fmt.Println(arr3)// 第四种var arr4 = [...]int{2: 53, 0: 16, 1: 88}fmt.Println(arr4)}

3.5、数组的注意事项

1.数组长度是数组类型的一部分,长度不同的数组类型也不同


package mainimport "fmt"func main() {var arr1 = [3]int{3, 6, 9}var arr2 = [6]int{1, 2, 3, 3, 6, 9}fmt.Printf("数组类型为:%T\n", arr1) // 数组类型为:[3]intfmt.Printf("数组类型为:%T\n", arr2) // 数组类型为:[6]int}

2.Go语言中数组默认是值传递,因此会拷贝,新函数里对数组的修改不会同步到原数组


package mainimport "fmt"func main() {//值传递arr := [4]int{1, 2, 3, 4}fmt.Println(arr)update(arr, 1, 100)fmt.Println(arr)}// 更改数组中某个元素的值func update(arr [4]int, j int, target int) {arr[j] = targetfmt.Println(arr)}/*[1 2 3 4] //未修改前数组的值[1 100 3 4] //update方法里修改后的值[1 2 3 4]  //修改后数组的值*/

3.如果修改想要同步,需要变为引用传递


package mainimport "fmt"func main() {//值传递arr := [4]int{1, 2, 3, 4}fmt.Println(arr)update(&arr, 1, 100)fmt.Println(arr)}// 更改数组中某个元素的值func update(arr *[4]int, j int, target int) {(*arr)[j] = targetfmt.Println(arr)}

3.6、二维数组

定义:


var arr [2][3]int

内存分析:


package mainimport "fmt"func main() {var arr [2][3]intfmt.Println(arr)fmt.Printf("arr的地址是%p\n", &arr)fmt.Printf("arr[0]的地址是%p\n", &arr[0])fmt.Printf("arr[0][0]的地址是%p", &arr[0][0])}/*[[0 0 0] [0 0 0]]arr的地址是0xc0000c8030      arr[0]的地址是0xc0000c8030   arr[0][0]的地址是0xc0000c803*/

学过C语言的这里应该很好理解,高维数组实际还是一维数组,只是一维数组的每个元素又是一个数组。

初始化:


package mainimport "fmt"func main() {var arr [2][3]int = [2][3]int{{1, 2, 3}, {4, 5, 6}}fmt.Println(arr)}

遍历:


package mainimport ("fmt")func main() {var arr = [2][3]int{{1, 2, 3}, {4, 5, 6}}// 普通for循环遍历for i := 0; i < len(arr); i++ {for j := 0; j < len(arr[0]); j++ {fmt.Print(arr[i][j], "\t")}fmt.Println()}// range遍历for _, v1 := range arr {for _, v2 := range v1 {fmt.Println(v2)}}}

4、切片

4.1、引入

  • 切片是golang中一种特有的数据类型。

  • 数组有其特定用处,但是由于长度固定不可变等原因,其实代码中并不常见。切片是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷。

  • 切片(slice)是对数组一个片段的连续引用,所以切片是一个引用类型,这个片段可以是整个数组,或者是其中连续的一部分。


package mainimport "fmt"func main() {var intarr [6]int = [6]int{1, 4, 7, 2, 5, 8}// 从1开始,到3结束,不包含3slice := intarr[1:3]// 输出切片fmt.Println(slice)// 切片元素个数fmt.Println("slice的元素个数:", len(slice))// 切片容量fmt.Println("slice的容量:", cap(slice))}/*[4 7]slice的元素个数: 2slice的容量: 5  */

4.2、内存分析

切片实际上是有三个字段的数据结构:一个是指向底层数组的指针,一个是切片的长度,一个是切片的容量


package mainimport "fmt"func main() {var intarr = [6]int{1, 4, 7, 2, 5, 8}// 从1开始,到3结束,不包含3slice := intarr[1:3]// 输出切片fmt.Println(slice)// 切片元素个数fmt.Println("slice的元素个数:", len(slice))// 切片容量fmt.Println("slice的容量:", cap(slice))fmt.Printf("数组中第2个元素的地址是:%p\n", &intarr[1])fmt.Printf("切片中第1个元素的地址是:%p\n", &slice[0])slice[1] = 16fmt.Println(intarr[2])}/*[4 7]slice的元素个数: 2                  slice的容量: 5                      数组中第2个元素的地址是:0xc00000e338切片中第1个元素的地址是:0xc00000e33816  */

4.3、切片的定义

  1. 定义一个切片,让它去引用一个已经创建好的数组

package mainimport "fmt"func main() {var intarr = [6]int{1, 4, 7, 2, 5, 8}// 从1开始,到3结束,不包含3slice := intarr[1:3]// 输出切片fmt.Println(slice)}
  1. 通过make内置函数来创建切片。

package mainimport "fmt"func main() {// make函数的三个参数,切片类型,切片长度,切片容量slice := make([]int, 4, 20)fmt.Println(slice)fmt.Printf("切片的长度为:%d\n", len(slice))fmt.Printf("切片的容量为:%d\n", cap(slice))}// 实际上是make底层创建一个数组,对外不可见,要通过slice去间接访问各个元素。
  1. 在定义的时候直接指向具体数组

func main() {slice := []int{1,4,7}}

4.4、切片的遍历

  • 普通for循环遍历

  • for-range遍历

4.5、切片的注意事项

  1. 切片定义后不可以直接使用,需要让其引用到一个数组,或者make一个空间供切片使用

  2. 切片使用不能越界

  3. 简写方式

  • var slice = arr[0:end] -------> var slice = arr[:end]

  • var slice = arr[start,len(arr)] -------> var slice = arr[start:]

  • var slice = arr[0,len(arr)] -------> var slice = arr[:]

  1. 切片还可以继续切片

package mainimport "fmt"func main() {var intarr [6]int = [6]int{1, 4, 7, 2, 5, 8}var slice []int = intarr[1:4]slice2 := slice[1:2]fmt.Println(slice2)}
  1. 切片可以动态增长

package mainimport "fmt"func main() {var intarr [6]int = [6]int{1, 4, 7, 2, 5, 8}var slice []int = intarr[1:4]slice2 := append(slice, 88, 60)fmt.Println(slice2)}/*1. 底层追加元素的时候首先创建一个新数组2. 新数组中追加append后的元素3. 将切片指向新数组4. 由于我们往往是想在原slice上追加新元素,所以日常使用slice = append(slice, 88, 60)5. 通过append可以往切片后面追加切片 slice = append(slice,slice2...)*/
  1. 切片的拷贝

package mainimport "fmt"func main() {var a []int = []int{1, 4, 7, 2, 5, 8}var b []int = make([]int, 10)copy(b, a) // 将a中对应的数组元素拷贝到b中对应的数组中去fmt.Println(b)}

5、Map

Map 是一种无序的键值对的集合。

Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。

在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 “”。

Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

5.1、引入


package mainimport "fmt"func main() {var a map[int]stringa = make(map[int]string, 10)a[2009] = "张三"a[2010] = "李四"fmt.Println(a)}/*1. map在使用之前一定要make2. map是无序的3. key不可以重复*/

5.2、创建

  1. 定义---->初始化----->使用

var a map[int]stringa = make(map[int]string, 10)a[2009] = "张三"
  1. 定义初始化---->使用

b := make(map[int]string)b[2009] = "张三"
  1. 初始化使用

c := map[int]string{2009: "张三",2010: "李四",}

5.3、操作

5.3.1、增加/更新


map["key"] = value // 如果key存在就是更新,否则是增加

5.3.2、删除/清空


delete(map,"key") // key不存在也不会报错

清空:

  • 逐一遍历key进行删除

  • map = make(…),make一个新的,让原来的成为垃圾,被gc回收

5.3.3、查询


// value,bool = map[key]   其中value是查找的结果,bool代表是否找到package mainimport "fmt"func main() {var a map[int]stringa = make(map[int]string, 10)a[2009] = ""a[2010] = "李四"fmt.Println(a)d, flag := a[2022]fmt.Println(flag)fmt.Println(d)}/*由于map的返回是有默认值的,比如key为int,map为string由于string可以为空因此在value的值可能为默认值时,一定要用bool判断是否获取key成功*/

5.3.4、获取长度


fmt.Println(len(map))

5.3.5、遍历


for k, v := range map {fmt.Println(k, v) // k是key,v是value}

6、面向对象

Go语言关于面向对象的说明:

  • go支持面向对象编程(oop),但是和传统面向对象编程有区别,并不是纯粹的面向对象语言。

  • go中没有类,使用结构体来实现oop特性。

  • go面向对象编程非常简洁,去掉了传统oop语言的方法重载,构造函数和析构函数、隐藏的this指针等

  • go仍然有面向对象编程的继承、封装和多态的特性,只是实现方式不太一样。比如go中通过匿名字段而不是extends关键字实现继承

以下部分参考自菜鸟教程:Go 语言结构体 | 菜鸟教程 (runoob.com)

6.1、结构体

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:

  • ID:图书ID
  • Title :标题

  • Author : 作者

  • Date:出版日期

结构体是值传递!!!

6.1.1、定义结构体


type name struct {member typemember type...member type}

package mainimport ("fmt""time")type Books struct {id     inttitle  stringauthor stringdate   string}func main() {// 创建一个新的结构体fmt.Println(Books{1, "平凡的世界", "路遥", time.Now().Format("2006-01-02 15:04:05")})// key: value创建结构体fmt.Println(Books{id: 2, title: "从你的全世界路过", author: "张嘉佳", date: time.Now().Format("2006-01-02 15:04:05")})// 指针创建结构体var book2 *Books = new(Books)(*book2).id = 3book2.title = "aaa" // go在底层依然是将该句转化为(*book2).titlefmt.Println(*book2)}

6.1.2、访问结构体成员


// 结构体.成员名package mainimport ("fmt""time")type Books struct {id     inttitle  stringauthor stringdate   string}func main() {book := Books{1, "平凡的世界", "路遥", time.Now().Format("2006-01-02 15:04:05")}fmt.Println(book.title)}

6.1.3、结构体作为函数参数


package mainimport ("fmt""time")type Books struct {id     inttitle  stringauthor stringdate   string}func printBook(book Books) {fmt.Printf("Book id : %d\n", book.id)fmt.Printf("Book title : %s\n", book.title)fmt.Printf("Book author : %s\n", book.author)fmt.Printf("Book date : %s\n", book.date)}func main() {book := Books{1, "平凡的世界", "路遥", time.Now().Format("2006-01-02 15:04:05")}printBook(book)}

6.1.4、结构体指针

你定义指向结构体的指针类似于其他指针变量,格式如下:


var struct_pointer *Books

以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:


struct_pointer = &book

使用结构体指针访问结构体成员,使用 “.” 操作符:


struct_pointer.title

实例:


package mainimport ("fmt""time")type Books struct {id     inttitle  stringauthor stringdate   string}func printBook(book *Books) {fmt.Printf("Book id : %d\n", book.id)fmt.Printf("Book title : %s\n", book.title)fmt.Printf("Book author : %s\n", book.author)fmt.Printf("Book date : %s\n", book.date)}func main() {book := Books{1, "平凡的世界", "路遥", time.Now().Format("2006-01-02 15:04:05")}bookPointer := &bookprintBook(bookPointer)}

6.1.5、结构体转化

  1. 结构体是用户自定义的类型:和其它类型转化时需要有完全相同的字段(名字、个数和类型)

package mainimport "fmt"type Student struct {Age int}type Person struct {Age int}func main() {var s Studentvar p Person = Person{10}s = Student(p)fmt.Println(s)}
  1. 给结构体起别名后赋值时需要强制转换

package mainimport "fmt"type Student struct {Age int}type Person Student // 给Student起一个叫Person的别名func main() {var s Studentvar p Person = Person{10}s = p // 这里会报错,不能直接赋值使用fmt.Println(s)}

6.2、方法

方法是作用在指定的数据类型上,和指定的数据类型绑定。

6.2.1、方法的声明


type A struct {Num int}func (a A) test() {fmt.Println(a.Num)}

6.2.2、方法的调用


func main() {a := A{10}a.test()}

6.2.3、注意事项

  1. 结构体是值传递,因此如果方法中需要改变原数据的值,参数使用指针类型。如果参数中是指针类型,我们实际使用时可以进行简化,不用(*pointer).方法名,可以直接变量.方法名。

  2. Go中方法是绑定在指定数据类型之上的,因此自定义类型都可以有方法,而不仅仅是struct,比如int、float32等。

6.3、接口

面向对象中的接口的一般定义为"接口定义对象的行为"。它表示让指定对象应该做什么,实现这种行为的方法(实现细节)是针对对象的。

在Go中,接口是一组方法签名,当类型为接口中的所有方法提供定义时,它被称为实现接口。接口与类型的关系是非侵入式的。

接口指定了类型应该具有的方法,类型决定了如何实现这些方法。

视频链接:https://www.bilibili.com/video/BV1XC4y1W7Am?p=90

6.3.1、接口的声明


// USB 接口type USB interface {start() // usb设备开始工作end()   // usb设备结束共工作}

6.3.2、接口的实现


// Mouse 鼠标类型type Mouse struct {name string}// FlashDisk u盘类型type FlashDisk struct {name string}// 实现接口方法func (m Mouse) start() {fmt.Println(m.name, "鼠标准备就绪,可以开始工作了")}func (m Mouse) end() {fmt.Println(m.name, "鼠标结束工作,可以安全退出了")}func (f FlashDisk) start() {fmt.Println(f.name, "u盘准备就绪,可以开始工作了")}func (f FlashDisk) end() {fmt.Println(f.name, "结束工作,可以弹出")}

6.3.3、注意事项

  1. 当需要接口类型的对象时,可以使用任意实现类代替

func testInterface(usb USB) { // 参数是USB接口usb.start()usb.end()}func main() {m1 := Mouse{"罗技"}f1 := FlashDisk{"闪迪"}testInterface(m1) // 实际上传实现类即可testInterface(f1)}
  1. 接口对象不能访问实现类中的属性

func main() {m1 := Mouse{"罗技"}var usb USBusb = m1fmt.Println(usb.name) // 这里是不合法的}// 其实这里很好理解,并不是所有的实现类都有某个属性

6.3.4、空接口

空接口中不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。


package mainimport "fmt"func main() {var a1 A = Cat{"橘猫"}var a2 A = Person{"白玉秀", 13}fmt.Println(a1)fmt.Println(a2)}type A interface {}type Cat struct {color string}type Person struct {name stringage  int}

既然这样,当我们不确定某个参数的类型时,就可以使用空接口来代替(泛型)


func print(i interface{}) {fmt.Println("a------>", i)}func main() {var a1 A = Cat{"橘猫"}var a2 A = Person{"白玉秀", 13}print(a1)print(a2)}

6.3.5、接口嵌套


type A interface {test1()}type B interface {test2()}type C interface { // C接口里嵌套A接口和B接口ABtest3()}// Cat结构体实现了C同时也实现了A和Bfunc (c Cat) test1() {fmt.Println("test1.....................")}func (c Cat) test2() {fmt.Println("test2.....................")}func (c Cat) test3() {fmt.Println("test3.....................")}func main() {var cat Cat = Cat{"橘猫"}cat.test1()cat.test2()cat.test3()var catA A = cat // 此时变量是什么类型的,就只能调用什么类型的方法catA.test1()var catB B = catcatB.test2()}

6.3.6、接口断言

当一个函数的形参是interface{},那么在函数中,需要对形参进行断言,从而得到它的真实类型。

语法格式:


// 安全类型断言<目标类型的值>,<布尔参数> := <表达式>.( 目标类型 )// 非安全类型断言<目标类型的值> := <表达式>.( 目标类型 )

示例代码:


/*定义一个shape的接口,里面有周长和面积两个方法但是由于不同图形的计算方式不同,因此当传到一个封装好的计算周长和面积的方法时,我们需要判断图形的类型一般有以下两种方式:1. if ins, ok := s.(Triangle); ok {fmt.Println("是三角形,三边是:", ins.a, ins.b, ins.c)} else {}2. switch instance := 接口对象.(type){case 实际类型1:....case 实际类型2:........}*/package mainimport ("fmt""math")func main() {var t1 = Triangle{3, 4, 5}fmt.Println(t1.peri())fmt.Println(t1.area())fmt.Println(t1.a, t1.b, t1.c)var c1 = Circle{2}fmt.Println(c1.peri())fmt.Println(c1.area())fmt.Println(c1.radius)getType(t1)var t2 *Triangle = &Triangle{3, 4, 8}getType(t2)}type Shape interface {peri() float64 // 周长area() float64 // 面积}type Triangle struct {a, b, c float64}func (t Triangle) peri() float64 {return t.a + t.b + t.c}func (t Triangle) area() float64 {p := t.peri() / 2s := math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c))return s}type Circle struct {radius float64}func (c Circle) peri() float64 {return c.radius * 4 * math.Pi}func (c Circle) area() float64 {return math.Pi * c.radius * c.radius}func getType(s Shape) {if ins, ok := s.(Triangle); ok {fmt.Println("是三角形,三边是:", ins.a, ins.b, ins.c)} else if ins, ok := s.(Circle); ok {fmt.Println("是圆形,半径是:", ins.radius)} else {fmt.Println("我也不懂了。。。。")}}func testShape(s Shape) {fmt.Printf("周长为:%.2f,面积为:%.2f", s.peri(), s.area())}

6.4、type关键字

6.4.1、定义新类型

格式:


类型定义:type 类型名 Type

实例代码:


package mainimport ("fmt""strconv")type myint inttype mystr stringfunc main() {var i1 myintvar i2 = 100i1 = 200fmt.Println(i1, i2)var name mystrname = "像鱼"var s1 strings1 = "试试水"fmt.Println(name, s1)//i1 = i2 //Cannot use 'i2' (type int) as the type myint//name = s1 //Cannot use 's1' (type string) as the type mystrfmt.Printf("%T,%T,%T,%T\n", i1, i2, name, s1)}

6.4.2、定义函数类型

格式:


type 自定义函数名 func(int, int) string // int 、string可选

实例代码:


package mainimport ("fmt""strconv")// 2. 定义函数类型type myfun func(int, int) stringfunc fun1() myfun {fun := func(a int, b int) string {s := strconv.Itoa(a) + strconv.Itoa(b)return s}return fun}func main() {res := fun1()fmt.Println(res(1, 2))}

6.4.3、类型别名

格式:


类型别名:type 类型名 = Type

实例代码:


package mainimport ("fmt""strconv")// 3. 给类型起别名type myint2 = intfunc main() {var i3 myint2i3 = 1000i4 := 200fmt.Printf("%T,%T\n", i3, i4)}

注意:非本地类型不能定义方法,可参考这篇文章:https://blog.csdn.net/wohu1104/article/details/120463014

6.4.4、结构体成员嵌入

实例代码:


package mainimport "fmt"type Person struct {name string}func (p Person) show() {fmt.Println("p----------->", p.name)}type People = Persontype Student struct {// 嵌入两个结构体PersonPeople}func main() {var s Students.People.name = "王二狗"s.name = "王二浪" // ambiguous selector s.name 因为People和Person都有name属性,无法区分是给谁的fmt.Println(s)}

用Java来解释的话,可以理解为如果一个对象中有两个其它的对象,且对象中存在相同的属性,在赋值时候需要指定赋给哪个对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/18864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

webpack基础知识一:说说你对webpack的理解?解决了什么问题?

一、背景 Webpack 最初的目标是实现前端项目的模块化&#xff0c;旨在更高效地管理和维护项目中的每一个资源 模块化 最早的时候&#xff0c;我们会通过文件划分的形式实现模块化&#xff0c;也就是将每个功能及其相关状态数据各自单独放到不同的JS 文件中 约定每个文件是一…

数字孪生的「三张皮」问题:数据隐私、安全与伦理挑战

引言 随着数字化时代的来临&#xff0c;数据成为了当今社会的宝贵资源。然而&#xff0c;数据的广泛使用也带来了一系列隐私、安全与伦理挑战。数字孪生作为一种虚拟的数字化实体&#xff0c;通过收集和分析大量数据&#xff0c;模拟和预测现实世界中的各种情境&#xff0c;为…

windows美化任务栏,不使用软件

1.任务栏透明: 效果图: (1).winr打开命令行 输入regedit回车打开注册表 regedit (2).在注册表中打开 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced 这个路径 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explore…

c++ http url encode decode

在C++中,可以使用以下方法对URL进行编码和解码: URL编码:#include <iostream> #include <string> #include <sstream> #include <iomanip>std::string urlEncode

(自控原理)自动控制的分类与基本要求

一、分类 1、线性连续控制系统 2、非线性控制系统 判断是时变时不变看的是系数&#xff0c;判断线性还是非线性看的是变量 二、基本要求 三、自动控制的分析方法

C++库函数——String类的模拟实现

目录 ①String类的主体 ②String类的具体实现 1.构造函数、拷贝构造函数、赋值运算符、析构函数 ⑴构造函数 ⑵拷贝构造函数 ⑶赋值运算符 ⑷析构函数 2.迭代器&#xff08;范围for的实现原理&#xff09; 3.修改:push_back, apppend, , clear, swap, c_str ⑴push_b…

如何设计一个自动化测试框架?

一个成熟的测试框架主要由 4 部分组成&#xff1a;基础模块、管理模块、运行模块和统计模块 基础模块 底层核心库 一般指用于操作被测试应用程序的第三方库&#xff0c;例如在 Web 端的 Selenium/WebDriver。如API端的Requests 对象库 PO模式中的页面对象 可重用组件 如一些…

Spring系列二:基于注解配置bean【建议收藏】

文章目录 &#x1f497;通过注解配置bean&#x1f35d;基本介绍&#x1f35d;快速入门&#x1f35d;注意事项和细节 &#x1f497;自己实现Spring注解配置Bean机制&#x1f35d;需求说明&#x1f35d;思路分析&#x1f35d;注意事项和细节 &#x1f497;自动装配 Autowired&…

特性快闪:使用 Databend 玩转 Iceberg

作者&#xff1a;尚卓燃&#xff08;PsiACE&#xff09;澳门科技大学在读硕士&#xff0c;Databend 研发工程师实习生 Apache OpenDAL(Incubating) Committer https://github.com/PsiACE 几周前&#xff0c;Databricks 和 Snowflake 召开了各自的年度大会&#xff0c;除了今年一…

【WebRTC---序篇】(七)RTC多人连麦方案

服务端可以选择mediasoup&#xff0c;作为SFU服务器&#xff0c;只负责转发数据 下图举例三个Client (browser或者客户端)同时加入一个房间&#xff0c;每个app同时发布一路视频和一路音频&#xff0c;并且接受来自其他app的音视频流&#xff0c;mediasoup内部的结构如下&…

关于计数以及Index返回订单号升级版(控制字符长度,控制年月标记)

数据库表操作&#xff1a; EXEC sys.sp_dropextendedproperty nameNName , level0typeNSCHEMA,level0nameNdbo, level1typeNTABLE,level1nameNSetNoIndexGOEXEC sys.sp_dropextendedproperty nameNMS_Description , level0typeNSCHEMA,level0nameNdbo, level1typeNTABLE,level…

python爬虫基础

文章目录 前言爬虫简介urllib库的使用如何获取网页的源码一个类型六个方法一个类型六个方法1、read()方法2、readline()方法3、readlines()方法4、getcode()5、geturl()6、getheaders() urllib下载下载网页下载图片下载视频 请求对象的定制 未完待续 前言 爬虫爬的好牢饭吃的早…

Linux编辑器 - vim使用

1.vim的基本概念 Vim是一个广泛使用的文本编辑器&#xff0c;它是在Unix和Linux系统中常用的命令行文本编辑器之一。 vim的主要三种模式 ( 其实有好多模式&#xff0c;目前掌握这 3 种即可 ), 分别是 命令模式 &#xff08; command mode &#xff09;、 插入模式 &#xff0…

Mac安装jadx并配置环境

安装jadx命令&#xff1a; brew install jadx启动jadx-gui命令&#xff1a; jadx-guiJAVA_HOME报错处理&#xff1a; 1.找到java_home路径 java -XshowSettings:properties -version 2>&1 > /dev/null | grep java.home2.根据输出结果输入命令 export JAVA_HOME路…

/bin/bash: Resource temporarily unavailable

有现场反馈plsql无法连接数据库了&#xff0c;登录环境查看时发现从root切换到grid时报错/bin/bash: Resource temporarily unavailable [rootdb1 ~]# su - grid Last login: Thu Jul 27 18:45:04 CST 2023 su: failed to execute /bin/bash: Resource temporarily unavailab…

瑞吉外卖项目----(2)缓存优化

1 缓存优化 1.0 问题说明 1.1 环境搭建 将项目推送到远程仓库里&#xff0c;教程在git 提交远程仓库前建议取消代码检查 创建新的分支v1.0&#xff08;用于实现缓存优化&#xff09;并推送到远程仓库 1.1.1 maven坐标 导入spring-data-redis的maven坐标&#xff1a; &l…

2023年DevOps和云趋势报告!

要点 ●云创新已从革命性阶段转变为演进性阶段&#xff0c;重点是迁移和重新架构工作负载。云空间已发展为提供对可扩展资源和托管服务的按需访问&#xff0c;强调简化交互并减少团队的认知负担。 ●人工智能 (AI) 和大型语言模型 (LLM) 可以通过解决认知过载问题并支持即时管…

【ChatGLM_01】ChatGLM2-6B本地安装与部署(大语言模型)

基于本地知识库的问答 1、简介&#xff08;1&#xff09;ChatGLM2-6B&#xff08;2&#xff09;LangChain&#xff08;3&#xff09;基于单一文档问答的实现原理&#xff08;4&#xff09;大规模语言模型系列技术&#xff1a;以GLM-130B为例&#xff08;5&#xff09;新建知识库…

AgileBoot - 全栈项目启动

AgileBoot-Back-End: 基于Ruoyi做了大量重构优化的基础快速开发框架。采用Springboot Vue 3 Mybatis Plus 更面向对象的业务建模 面向生产的项目。&#xff08;非玩具项目&#xff09; 首先克隆代码&#xff0c;同是克隆前端和后端的代码。 前端代码启动&#xff1a; np…

飞桨AI Studio可以玩多模态了?MiniGPT4实战演练!

MiniGPT4是基于GPT3的改进版本&#xff0c;它的参数量比GPT3少了一个数量级&#xff0c;但是在多项自然语言处理任务上的表现却不逊于GPT3。项目作者以MiniGPT4-7B作为实战演练项目。 创作者&#xff1a;衍哲 体验链接&#xff1a; https://aistudio.baidu.com/aistudio/proj…