台州网站制作案例,北京空间优化平台,如何自己开发一款app,自己在线制作logoGolang进阶学习
视频地址#xff1a;https://www.bilibili.com/video/BV1Pg41187AS?p35
1、包
1.1、包的引入
使用包的原因#xff1a; 我们不可能把所有函数放在同一个源文件中#xff0c;可以分门别类的放在不同的文件中 解决同名问题#xff0c;同一个文件中不可以…Golang进阶学习
视频地址https://www.bilibili.com/video/BV1Pg41187AS?p35
1、包
1.1、包的引入
使用包的原因 我们不可能把所有函数放在同一个源文件中可以分门别类的放在不同的文件中 解决同名问题同一个文件中不可以定义相同名字的函数
首先写一个连接数据库的工具函数 package dbUtilsimport fmtfunc 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、包的细节 建议包的声明和所在文件夹同名 main包是程序的入口包一般main函数会放在这个包下 打包语法package 包名 引入包的语法import “包的文件夹的路径”包名是从$GOPATH/src/后开始计算的使用/分隔 如果有多个包要导入推荐采用以下格式 import (GoStudy/advanced/dbUtilsfmt) 函数调用时前面要定位到所在的包 首字母大写函数可以被其它包访问 一个目录下不能有重复的函数 包名和文件夹的名字可以不一样 // dbUtils.gopackage aaa //这里声明的包是aaaimport fmtfunc GetConn() { fmt.Println(我执行了dbUtils包下的getConn函数)}//main.gopackage main import (GoStudy/advanced/dbUtils //这里引用的依然是dbUtilsfmt)func main() {fmt.Println(我执行了main包下的main函数)aaa.GetConn() //调用的变成了aaa.GetConn()}
一个包下如果有多个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 fmtfunc 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 fmtfunc 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 (errorsfmt)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 (errorsfmt)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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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 fmtfunc 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、切片的定义
定义一个切片让它去引用一个已经创建好的数组 package mainimport fmtfunc main() {var intarr [6]int{1, 4, 7, 2, 5, 8}// 从1开始到3结束不包含3slice : intarr[1:3]// 输出切片fmt.Println(slice)}
通过make内置函数来创建切片。 package mainimport fmtfunc main() {// make函数的三个参数切片类型切片长度切片容量slice : make([]int, 4, 20)fmt.Println(slice)fmt.Printf(切片的长度为:%d\n, len(slice))fmt.Printf(切片的容量为:%d\n, cap(slice))}// 实际上是make底层创建一个数组对外不可见要通过slice去间接访问各个元素。
在定义的时候直接指向具体数组 func main() {slice : []int{1,4,7}}
4.4、切片的遍历 普通for循环遍历 for-range遍历
4.5、切片的注意事项 切片定义后不可以直接使用需要让其引用到一个数组或者make一个空间供切片使用 切片使用不能越界 简写方式 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[:]
切片还可以继续切片 package mainimport fmtfunc 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)}
切片可以动态增长 package mainimport fmtfunc 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...)*/
切片的拷贝 package mainimport fmtfunc 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 类型的零值是 0string 类型的零值是 “”。
Map 是引用类型如果将一个 Map 传递给一个函数或赋值给另一个变量它们都指向同一个底层数据结构因此对 Map 的修改会影响到所有引用它的变量。
5.1、引入 package mainimport fmtfunc 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、创建
定义----初始化-----使用 var a map[int]stringa make(map[int]string, 10)a[2009] 张三
定义初始化----使用 b : make(map[int]string)b[2009] 张三
初始化使用 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 fmtfunc 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为intmap为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是keyv是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 (fmttime)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 (fmttime)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 (fmttime)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 (fmttime)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、结构体转化
结构体是用户自定义的类型和其它类型转化时需要有完全相同的字段名字、个数和类型 package mainimport fmttype Student struct {Age int}type Person struct {Age int}func main() {var s Studentvar p Person Person{10}s Student(p)fmt.Println(s)}
给结构体起别名后赋值时需要强制转换 package mainimport fmttype 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、注意事项 结构体是值传递因此如果方法中需要改变原数据的值参数使用指针类型。如果参数中是指针类型我们实际使用时可以进行简化不用(*pointer).方法名可以直接变量.方法名。 Go中方法是绑定在指定数据类型之上的因此自定义类型都可以有方法而不仅仅是struct比如int、float32等。
6.3、接口
面向对象中的接口的一般定义为接口定义对象的行为。它表示让指定对象应该做什么实现这种行为的方法实现细节是针对对象的。
在Go中接口是一组方法签名当类型为接口中的所有方法提供定义时它被称为实现接口。接口与类型的关系是非侵入式的。
接口指定了类型应该具有的方法类型决定了如何实现这些方法。
视频链接https://www.bilibili.com/video/BV1XC4y1W7Am?p90
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、注意事项
当需要接口类型的对象时可以使用任意实现类代替 func testInterface(usb USB) { // 参数是USB接口usb.start()usb.end()}func main() {m1 : Mouse{罗技}f1 : FlashDisk{闪迪}testInterface(m1) // 实际上传实现类即可testInterface(f1)}
接口对象不能访问实现类中的属性 func main() {m1 : Mouse{罗技}var usb USBusb m1fmt.Println(usb.name) // 这里是不合法的}// 其实这里很好理解并不是所有的实现类都有某个属性
6.3.4、空接口
空接口中不包含任何的方法正因为如此所有的类型都实现了空接口因此空接口可以存储任意类型的数值。 package mainimport fmtfunc 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 (fmtmath)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 (fmtstrconv)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 (fmtstrconv)// 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 (fmtstrconv)// 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 fmttype 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来解释的话可以理解为如果一个对象中有两个其它的对象且对象中存在相同的属性在赋值时候需要指定赋给哪个对象。