Golang简介
Golang的发展方向
- 区块链研发工程师
- Go服务器端工程师
- 游戏软件工程师
- Golang分布式软件工程师
- Golang云计算软件工程师
Golang诞生的原因
- 计算机硬件技术更新换代越来越快,性能提升也越来越好,但是现有的计算机语言不能很好的利用硬件良好的资源
- 现有语言不够简洁,高效和处理过高并发的能力
- c++虽然运行速度很快,但是编译速度还是很慢,并且还存在内存泄露的一系列问题
Golang语言的特点
- Go语言保证了技能到达静态编译语言的安全和性能,又达到了动态语言开发维护的高效率,使用一个表达式来形容GO语言:GO = C + Python
- 从C语言中继承了很多理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,指针等
- 引入了包的概念,用于组织程序结构,GO语言的一个文件都要归属于一个包,而不能单独存在。
- 垃圾回收机制,内存自动回收,不需开发人员管理
- 天然并发
- goroutine,轻量级线程,可实现大并发处理,高效利用多核。
- 基于CPS并发模型实现
- 吸收了管道通信机制,形成GO语言特有的管道通过管道,可以实现不同的goroute之间的相互通信
- 函数可以返回多个值
- 新的创新
Golang的安装和配置环境
实在太简单了,自行百度
Golang语言基础
Golang 执行流程分析
-
如果使用
go build xx.go
就是对源码进行编译,形成可执行文件(.exe),然后通过运行这个文件,使得这个代码运行
-
如果使用
go run hello.go
就是对源码直接执行
-
现在来看看这两种编译模式的区别
- 如果我们先编译了文件,得到了可执行文件,那我们把这个文件放到任何一个没有go开发环境的机器上,仍然可以运行
- 如果我们是直接go run , 并没有得到可执行文件,那我们如果要在另一个机器上运行就需要go语言的环境
- 在编译时,编译器会将程序运行依赖的库文件包含在可执行文件中,所以可执行文件变大了很多
-
注意事项
- Go编译器是一行行进行编译的,因此我们一行就写一条语句,不能把多条语句写在同一个,否则报错
- Go语言定义的变量或者import的包如果没有使用到,代码不能编译通过。
Golang 转义字符
\t
:表示一个制表符,通常使用它可以排版\n
:换行符\\
:一个\\"
:一个“\r
:一个回车
Golang 注释
- 什么是注释?
- 注释是为了让程序员更加清楚这段代码的意义,大大减少了程序员的工作量,使得他们不需要再次阅读程序的逻辑就能知道这段代码的是做什么用的
- 注释方式
- 行注释:
// 注释内容
- 块注释:
/* */
- 行注释:
Golang 变量
-
什么是变量?
- 变量是程序的基本组成单位,变量相当于内存中的一个数据结构的存储空间,你可以把变量看做是一个房间的门牌号,通过这些门牌号可以找到房间背后的数据。
-
为什么需要变量?
- 因为有的时候我们提前并不知道数据是什么,所以我们需要先创建一个存储空间用来存储,就像你出租的房子你永远不能提前知道租客是谁,但是你得先有房子才能去中间出租。
-
变量的使用步骤:
- 声明变量
- 非变量赋值
- 使用变量
-
案例展示:
-
变量使用注意事项
- 变量表示内存中的一个存储区域
- 该区域有自己的名称和类型
- 定义变量的三种方式
-
指定变量类型,声明后若不赋值,使用默认值
-
根据值自行判定变量类型
-
省略var,注意
:=
左侧的变量不应该是已经声明过的,否则会导致编译错误
-
多变量声明
-
该区域的数值可以在同一类型范围内不断变化
-
变量在同一个作用域内不能重名
-
变量 = 变量名 + 值 + 数据类型
-
如果变量没有赋初始值,编译器会使用默认值
-
-
变量的声明:
var 变量名 数据类型
-
程序中 +号的使用:
- 当左右两边都是数值型时,则做加法运算
- 当左右两边都是字符串,则做字符串拼接
Golang 数据类型
- 整形类型:
- int:有符号,1字节,-128 ~ 127
- int16:有符号,2字节,-215 ~ 215 -1
- int32:有符号,4字节,-231 ~ 231 -1
- int64:有符号,8字节,-263 ~ 263 -1
- uint:无符号,1字节,0 ~ 127
- uint16:无符号,2字节,0 ~ 215 -1
- uint32:无符号,4字节,0 ~ 231 -1
- uint64:无符号,8字节,0 ~ 263 -1
- 使用细节:
- int uint 的大小 和 系统有关
- 整形默认声明为 int 型
- 在程序中查看某个变量的字节大小和数据类型:
unsafe.Sizeof()
- 注意引入包:
import "unsafe"
- 注意引入包:
- 在使用整形变量时,遵守保小不保大的原则,即:在保证程序正常运行下,尽量使用占用空间小的数据类型。
- bit 计算机中的最小存储单位
- byte 计算机中基本存储单元 1 byte = 8 bit
Golang 小数类型/浮点型
- 小数类型就是用于存放小数的
- 小数类型:
- 单精度:float32,4字节
- 双精度:float64 ,8字节
- 浮点数 = 符号位 + 指数位 + 尾数位: 都是有符号的
- 使用细节:
- 浮点类型有固定的范围和字段长度,不受具体OS的影响
- 浮点型默认声明为float64类型
- 浮点类型有两种表达方式
- 十进制数形式:如:5.12 (必须有小数点)
- 科学计数法形式:如:5.1234E2 = 5.1234 * 102 5.12E-2 = 5.12 / 102
- 通常情况下,应该使用float64,因为它比float32更精确。
Golang 字符类型
- Golang 中没有专门的字符类型,如果要存储单个字符,一般使用 byte 来保存。
- 字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。也就是说对于传统的字符串是由字符组成的,而Go的字符串不同,它是由字节组成的。
- 使用细节
- 字符常量是用单引号(’) 括起来的单字符
- GO中允许使用转义字符 ’ \ ’ 来将其后的字符转变为特殊字符型常量
- 在GO中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值。
- 可以直接给某个变量赋一个数字,然后按格式化输出时%c,会输出该数字对应的unicode字符
- 字符类型是可以进行运算的,相当于一个整数,因为它都对应有Unicode码
Golang 布尔类型
- 布尔类型 :bool ,只有true和false两个值,占1字节
Golang string类型
- 字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本
- 使用注意事项和细节
- Golang语言统一使用UTF-8编码,中文乱码问题不会困扰
- 字符串一旦赋值了,字符串就不能修改了:在Go字符串是不可变的。
- 字符串的两种表示形式
- 双引号
- 反引号
- 当一行字符串太长时,需要使用到多行字符串,可分行写,但是需要保留 + 号在上一行
Golang 数据类型的相互转换
-
Go 在不同类型的变量之间赋值时需要显式转换。也就是Golang中数据类型不能自动转换,但是被转换的时变量的存储数据,不是变量本身的数据类型。
-
基本数据转换成string类型,可以使用strconv 包的函数
-
string 类型转基本数据类型,还是使用strconv 包的函数
- ParseBool(str,string)
- ParseFloat(string,bitSize,int)
- ParseInt(string,base int,bitSize,int)
Golang 指针
- 基本介绍
- 基本数据类型,变量存的就是值,也叫值类型
- 获取变量的地址,用&,比如:var a int 获取a的地址:&a
- 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值
- 获取指针类型所指向的值,使用:*
- 比如 var ptr * ptr,使用 *ptr 获取ptr 指向的值
- 比如 var ptr * ptr,使用 *ptr 获取ptr 指向的值
- 使用细节:
- 值类型,每个指针都有对应的指针类型,形式为
* 数据类型
,比如 int 的对应的指针就是 int, float32对应的指针就是float32 - 值类型包括,基本数据类型 int系列,float系列,bool,string,数组和结构体struct
- 值类型,每个指针都有对应的指针类型,形式为
Golang 值类型和引用类型
- 值类型:基本数据类型
- 变量直接存储值,内存通常在栈中分配
- 引用类型:指针,slice切片,map,管道chan,interface等都是引用类型
- 变量存储的是一个地址,这个地址对应的空间才真正存储数据,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收
Golang标识符
- 注意:
- 如果变量名,函数名,常量名首字母大写,则可以被其他的包访问,
- 如果首字母小写,则只能在本包中使用
- 在golang没有public,private等关键字
- 系统保留关键字
Golang运算符
- 算术运算符
- 注意事项:
- 对于除号,它的整数除和小数除是有区别的,整数之间做除法时,只保留整数部分而舍弃小数部分。
- Golang的自增自减只能当做一个独立语言使用,Golang的++和–只能写在变量的后面,不能写在变量的前面。
- 注意事项:
- 关系运算符
- 逻辑运算符
- 赋值运算符
- 位运算符
Golang 键盘输入语句
- 在编程中,需要接受用户输入的数据,就可以使用键盘输入语句来获取。
- 使用步骤:
- 导入fmt包
- 调用fmt包的fmt.Scanln()或者fmt.Scanf()
Golang 条件语句
-
单分支:
- 基本语法:if 条件表达式 { 执行代码块 }
- 细节说明:golang支持在if中,直接定义一个变量
-
双分支:
- 基本语法:if 条件表达式 { 执行代码块1 } else { 执行代码块2 }
- 基本语法:if 条件表达式 { 执行代码块1 } else { 执行代码块2 }
-
switch分支:
- 基本介绍:switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上到下逐一尝试,直到匹配为止。匹配项后面不需要再加 break
- 基本语法:
- 注意事项:
- golang 的 case后的表达式可以有多个,使用逗号间隔
- golang 的 case 语句块不需要写break,因为默认会有,即在默认情况下,当程序执行完case语句块后,就直接退出该switch控制结构
- golang 的 default 语句不是必须的
- switch 穿透 - fallthrough,如果在case语句块后增加fallthrough,则会继续执行下一个case,叫Switch 穿透
Golang 循环控制
- for 循环
- 语法格式:for 循环变量初始化;循环条件;循环变量迭代{ 循环操作 }
- 循环执行的顺序:
- 执行循环变量初始化
- 执行循环条件
- 如果循环条件为真,就执行循环操作
- 执行循环变量迭代
- 反复执行2,3,4 步骤,直到循环条件为false,就退出for循环
- 注意细节:
- for 循环的第二种使用方式:
- for 循环的第三种使用方式:
- Golang 提供 for - range 的方式,可以方便遍历字符串和数组,案例说明如何遍历字符串
- Golang 提供 for - range 的方式,可以方便遍历字符串和数组,案例说明如何遍历字符串
- for 循环的第二种使用方式:
- golang 没有while和do … while 语法
Golang 函数,包
- 函数的概念:为完成某一功能的程序指令的集合,称为函数
- 函数的基本语法:func 函数名( 形参列表 ) ( 返回列表 ){ 执行语句 return 返回值列表 }
- 形参列表:表示函数的输入
- 函数可以有返回值,也可以没有
- 包的引出:包的本质实际上就是创建不同而文件夹,来存放程序文件
- 包的语法:package 包名
- 引入包的基本语法:
- import “包的路径”
- import (包名,包名)
- 在import 包时,路径从 $GOPATH 的 src 下开始,不用带 src,编译器会自动从src下开始引入
- 如果包名较长,GO支持给包取别名,取别名之后,原来的包名就不能使用了
- 同一个包下,不能有相同的函数名,否则报重复定义
- 如果你要编译成一个可执行程序文件,就需要将这个包声明为main,即package main,这是一个语法规范,如果你是写一个库,包名可以自定义
- return 语句
- 如果返回多个值时,在接收时,希望忽略某个返回值,则使用_符号表示占位忽略
- 如果返回值只有一个,可以不写
- 函数使用的注意事项和细节讨论
- 函数的形参列表可以是多个,返回值列表也可以是多个
- 形参列表和返回值列表的数据类型可以是值类型和引用类型
- 函数中的变量是局部的,函数外不生效
- 基本数据类型和数组默认都是值传递,即进行值拷贝(在函数内修改,不会影响到原来的值)
- 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用。
- Go函数不支持函数重载
- 在GO中,函数也是一种数据类型,可以赋值给一个变量,该变量就是一个函数类型的变量了。
- 函数既然是一种数据类型,因此在GO中,函数可以作为形参,并且调用
- 支持对函数返回值命名
- GO 支持可变参数
Golang 函数
-
init 函数:每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被GO运行框架调用,也就是说init会在main函数前被调用
- 注意事项:
- 如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程全局变量定义 --》 init函数 --》main函数
- init函数最主要的作用,就是完成一些初始化的工作
- 注意事项:
-
匿名函数:就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
- 使用方法一:在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次
- 使用方法二:将匿名函数赋给一个变量,再通过该变量来调用匿名函数
- 全局匿名函数:如果将匿名函数给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效。
- 使用方法一:在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次
-
闭包:闭包就是一个函数和与其相关的引用环境组成的一个整体
- 案例代码说明:
- AddUpper是一个函数,返回的函数的数据类型是func(int) int
- 返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包。
- 闭包是类,函数是操作,n是字段。函数和它使用到n构成闭包
- 案例代码说明:
-
函数的defer
- 为什么需要defer?
- 在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,提供了defer机制(延时机制)
- 当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈
- 当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
- 注意细节
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一个语句。
- 当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行。
- 在defer将语句放入栈中时,也会将相关的值拷贝同时入栈
- defer的通常做法是,创建资源后,可以执行defer 同时进行关闭
- 为什么需要defer?
-
函数参数传递方式
- 值传递: 基本数据类型,其实就是将数值copy过去
- 引用类型:指针,切片,map,管道,接口等等,就是将数据的内存地址传递过去
- 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。
-
变量的作用域
- 函数内部声明或者定义的变量叫做局部变量,作用域仅限于函数内部
- 函数外部声明或者定义的变量叫做全局变量,作用域在整个包有效,如果其首字母为大写,则作用域在整个程序有效
-
字符串常用的系统函数
-
统计字符串的长度,按字节
-
字符串遍历,同时处理有中文的问题
-
字符串转整数
-
整数转字符串
-
字符串转[]byte
-
[]byte 转字符串
-
查找子串是否在指定的字符串中
-
统计一个字符串有几个指定的子串
-
不区分大小写的字符串比较
-
返回字串在字符串第一次出现的index值,如果没有返回-1
-
返回子串在字符串最后一次出现的index,如没有返回-1
-
将指定的子串替换成另外一个子串:Replace(a,b,c,d) 其中d是指你希望替换几个,如果=-1则是希望全部替代
-
按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组
-
将字符串的字母进行大小写的转换
-
将字符串左右两边的空格去掉
-
将字符串左右两边指定的字符去掉
-
判断字符串是否以指定的字符串开头
-
-
时间和日期相关的函数:在编程中,程序员会经常使用到日期相关的函数
- 时间和日期相关函数,需要导入time包
- time.Time类型,用于表示时间
- 获取到其他的日期信息
- 格式化日期时间
-
方法一:使用Printf或者Sprintf
-
方法二:使用time.Format() 方法完成
-
- 时间的常量
- time的Unix和UnixNano的方法
-
内置函数:为了变成方便,提供了一些函数,这些函数可以直接使用,我们称之为GO的内置函数
- len:用来求长度
- new:用来分配内存
- make:用来分配内存,主要用来分配引用类型
-
错误处理:我们希望当发生错误后,可以捕获错误,并进行处理,保证程序可以继续执行。还可以在捕获到错误后,给管理员一个提示
- go语言没有
try catch finally
这一套,引入的处理方式是defer panic recover
,处理方式是,抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
- 错误处理的好处在于进行错误处理后,程序不会轻易挂掉,如果加入预警代码,就可以让程序更加的健壮。
- go语言没有
-
自定义错误
- go程序中,也支持自定义错误,使用errors.New 和 panic 内置函数
- errors…New(“错误说明”),会返回一个error类型的值,表示一个错误
- panic内置函数,接收一个interface{}类型的值作为参数。可以接收error类型的变量,输出错误信息,并退出程序
- go程序中,也支持自定义错误,使用errors.New 和 panic 内置函数
Golang 数组与切片
- 数组的定义:var 数组名 [数组大小]数据类型
- 数组在内存的布局:
- 数组的地址可以通过数组名来获取 &数组名
- 数组的第一个元素地址,就是数组的首地址
- 数组的各个元素的地址间隔是依据数组的类型决定,比如int64 -》 8
- 数组的使用:
- 访问数组元素:数组名[下标]
- 初始化数组的方式:
- 遍历数组:
- 常规遍历:
for i:=0;i<len();i++{}
- for-range:
for index,value := range array01{}
- 第一个返回的值 index 是指数组的下标
- 第二个返回的值 value 是指该下标位置的值
- 这两个参数都是在for循环内部可见的局部变量
- 常规遍历:
- 函数中传递数组参数的时候,默认是值传递,因此会进行值拷贝。数组间不会相互影响
- 如果想在函数中改变数组中的值,就需要传递引用类型
- 切片:我们之所以需要切片,是因为有的时候不确定数组的长度,所以需要一个可变的数组进行存储。
- 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
- 切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度都一样。
- 切片的长度是可以变化的,因为切片是一个可以动态变化的数组
- 切片的基本定义:
var a [] int
- 切片的内存分布:
- 切片的使用:
-
定义一个切片,然后让切片去引用一个已经创建好的数组
-
通过make来创建切片:
var 切片名 []type = make([]type, len, [cap])
- type:就是数据类型
- len:就是大小
- cap:指定切片容量
- 与上一种方法的区别:
- 上一种方法是直接引用数组,这个数组是事先存在的,程序员是可见的
- 通过make创建切片,make也会创建一个数组,是由切片在底层进行维护
-
定义一个切片,直接就指定具体数组,使用原理类似make的方式
-
- 切片的遍历:
- for循环
- for - range 结构遍历切片
- 切片的注意事项和细节讨论
- 切片初始化时:var slice = arr[startIndex:endIndex]
- cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
- 切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用
- append 内置函数,可以对切片进行动态追加
- 切片的append操作的本质就是对数组进行扩容,底层会创建一个新的数组,将原来的数组拷贝到新的数组里面去
- 切片的append操作的本质就是对数组进行扩容,底层会创建一个新的数组,将原来的数组拷贝到新的数组里面去
- 切片的拷贝操作:切片使用copy内置函数完成拷贝
copy(para 1, para 2)
- 二维数组:
var 数组名 [大小][大小]类型
var 数组名 [大小][大小]类型 = [大小][大小]类型{{初值},{初值}}
- 二维数组的遍历:
- 双层for循环完成遍历
- for-range方式完成遍历
Golang Map
- 基本介绍:map是key-value数据结构,又称为字段或者关联数组。
- 基本语法:var map 变量名 map[keytype] valuetype
- 细节说明:
- map在使用前一定要make
- map的key是不能重复,如果重复了,则以最后这个key-value为准
- map的value是可以相同的
- map的key-value是无序
- make内置函数数目
- map的使用方式:
-
方式1:
-
方式2:
-
方式3:
-
- map的增加和修改:
map[key] = value
如果key还没有,就是增加,如果key存在就是修改 - map的删除:delete是一个内置函数,如果key存在,就删除该key-value,如果key不存在,不操作,但是也不会报错
- map的查找:
- map的遍历:通常用for-range
- map的排序:map默认是无序的,也不是按照添加的顺序存放的,每次遍历的输出都不一样
- 使用细节:
- map是引用类型,遵守引用类型传递的机制,在一个函数接受map,修改后,会直接修改原来的map
- map的容量达到后,想要map增加元素会自动扩容,并不会发生panic,也就是说map也能动态增长键值对
Golang 面向对象编程
- Golang也支持面向对象编程,但是和传统的面向对象编程有区别,并不是存粹的面向对象语言。
- Golang 没有类,只有结构体,但是跟类的地位是一样的
- Golang面向对象变成非常简洁,去掉了传统OOP语言的继承,方法重载,构造函数和析构函数,隐藏的this指针,仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样
- 结构体定义语法:
- 结构体变量在内存的布局
- 结构体的字段:
- 字段是结构体的一个组成部分,一般是基本数据类型,数组,也可以是引用类型。
- 注意事项和细节说明:
- 字段声明语法:字段名 字段类型
- 不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个,结构体是值类型
- 创建结构变量的几种方式:
- 第一种:
- 第二种:
- 第三种:
- 第四种:
- 第一种:
- 结构体指针访问字段的标准方式是:
(*结构体指针).字段名
,但go做了一个简化,也支持结构体指针.字段名 - 结构体的使用细节:
- 结构体的所有字段在内存中是连续的
- 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段
- 结构体进行type重新定义,Golang认为是新的数据类型,但是相互间可以强转
- struct的每个字段上,可以写上一个 tag,该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化
Golang 方法
-
Golang中的方法是作用在指定的数据类型上的,因此自定义类型,都可以有方法,而不仅仅是struct
-
声明和调用:
- test方法和A类型绑定
- test方法只能通过A类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用
-
方法的调用和传参机制原理
- 方法的调参和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当作实参也传递给方法。
- 变量调用方法时,该变量本身也会作为一个参数传递到方法
-
方法的定义:
-
注意细节:
- 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
- 方法的作用在指定的数据类型上的,因此自定义类型,都可以有方法,而不仅仅是struct
- 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包访问。
- 如果实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出
-
方法和函数的区别:
- 调用方法不一样:
- 函数的调用方式:函数名
- 方法的调用方式:变量.方法名
- 对于方法,接收者为值类型时,可以直接用指针类型的变量调用方法,接收者为指针类型时,同样可以
- 调用方法不一样:
-
方法中传值类型和指针类型的区别:
- 值传递穿的是值拷贝,不是类本身,所以无法修改,而指针传递的是类本身(地址),可以修改
- 如果传的是指针类型,结果还是一样,因为go底层会做一个转换
- 值传递穿的是值拷贝,不是类本身,所以无法修改,而指针传递的是类本身(地址),可以修改
-
匿名字段:可以像字段成员那样访问匿名字段方法,编译器负责查找
-
方法集:
- 类型 T 方法集包含全部 receiver T 方法。
- 类型 *T 方法集包含全部 receiver T + *T 方法。
- 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。
- 如类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法。
- 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法。