go语言部分基础知识点(待完善)

各种注意事项

1.{ 不能单独放在一行
2.利用命令进行格式化规范:在cmd中gofmt -w 文件名
3.成对编译的有{} () ”“ ‘’
4.一行最长不超过80字符 超过使用换行展示(为了美观)
5.格式化输出:Printf ,Sprintf(可以返回格式化字符串 之后在正常输出即可)
6.%T表示值的类型,需要用格式化输出
7.文件名/文件夹名与包名没有直接关系,并非需要一致
8.同一个文件夹下的文件只能有一个包名,否则编译报错
9.一行只能有一条语句,否则报错,原因是go编译器是一行行进行编译的
10.接收用户输入数据 先定义一个变量名 然后fmt.Scanln(&变量名)对其赋值 用数据地址的原因:对地址中的值进行改变,外面的值也会改变 录入数据时类型一定要匹配,因为底层会自动判断类型 若要将输入的地址赋值给数组或切片中特定位置的元素,可以不定义变量直接用&该位置代替
11.输出类型在go-语言标准库的fmt包下
12.大多数情况下语句后不需要加“;”
13.在输出时,字符串与其他数据类型之间连接用,实现
14.json.Marshal(变量) 返回变量的json编码与错误(不需要错误用_表示)
15.用label标签来处理指定处的循环,选择语句
16.方法与函数的区别: 方法用变量或变量地址调用方法都可以(如果形参形式是变量,则两者都是值拷贝,传入地址不会对结构体实例本身的属性产生改变。如果形参形式是指针变量,则两者都是地址拷贝,传入地址会对结构体实例本身的属性产生改变),但函数只能按照形参形式进行调用

快捷键

返回上一步 ctrl+z
剪切 ctrl+x
将所选行代码向下复制 ctrl+d
将所选代码放入特定结构(比如for死循环)中的快捷键 需要特定插件 问老师
向后缩进:tab
向前取消缩进:shift+tab

时间复杂度

编译的两种执行流程

1 .go源文件用go build编译为可执行.exe文件再运行或go源文件用go run一步编译运行
2.可执行文件比源文件大很多,原因是编译器会将程序运行依赖的库文件包含在可执行文件中
3.先编译生成可执行文件,到另一个电脑上无需额外配置仍然可以运行,直接在另一个没有go开发环境的电脑上运行go源代码,无法执行
4.go run运行时间比go build时间长
5.编译后文件可以另外指定名字

注释

1.行注释 //要注释的内容 快捷键为选中要注释的内容 ctrl+/
2.块注释(多行注释)/**/ 快捷键为选中要注释的内容 shift+ctrl+/ 块注释不可以嵌套块注释
3.尽量用行注释

变量

1.声明变量:var 变量名 数据类型 = 值
2.变量作用域:局部变量和全局变量。在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
3.定义某个类型变量不赋值会给默认值
4.没有写变量类型会根据=后的值判定变量的类型(自动类型推断)
5.省略var 需要将= 改为:=(全局变量只能用前者定义)
6.一次性声明多个变量用,隔开 最后再加变量类型(可不加)
7.&变量表示该变量的地址
8._标识符作为一个变量时是一个只写变量,不能得到它的值,这样是因为在go中你必须使用所有被声明的变量,但有时这些变量不是全部被需要的
9.可以通过var(变量及其赋值)进行一次性全局变量声明

常量

1.常量是一个简单值的标识符,在程序运行时不会被修改
2.常量中的数据类型只可以是布尔型,数字型(整数型,浮点型,复数),字符串型
3.定义格式 const 常量名[type](可以省略不写) =value
4.类型相同的常量可以同时声明且不写数据类型
5.iota,特殊常量,可以被编译器修改的常量。格式为 const 常量名 = iota在const关键字出现时被重置为0(const内部的第一行之前),const中每新增一行常量声明将使iota计数一次(可以理解为iota是语句块中的行索引,作用域为当前代码块,如果是全局时定义则为当前行)
const(
a = iota
b
c
)这里边a=0,b=1,c=2
const(
a = iota
b
c,d
)这种写法是错误的
const(
a = iota
b = iota
c,d = iota,iota
)这里边a =0,b=1,c=d=2

6.常量表达式中,函数必须是内置函数

标识符

1.组成部分为数字,字母,下划线
2.不可以以数字开头,严格区分大小写。不能包含空格,不能使用关键字
3.增加可读性,做到见名知意
4.长度无限制但建议不要太长
5.可以使用int,float32,float64等 但是不建议
6.如果变量名,函数名,常量名首字母大写,则可以被其他的包访问

关键字

go一共有25个关键字:break(在switch语句case后结束当前分支 停止离得最近的循环 结束指定标签对应的循环 ) case chan const continue(结束离得最近本次循环,继续下一次循环 停止指定标签对应的循环,继续下一次循环) default defer(程序遇到defer不会立即执行defer后的语句,而是将defer后的语句压入一个栈中,并将此时的值拷贝,不会受到函数后面语句的影响然后继续执行函数后面的语句。在函数执行完以后,从栈中取出语句按照先入后出的顺序开始执行。如果想将不是函数的几行代码利用defer压入栈中,可以使用匿名函数将他们括起来) else fallthrough for func go goto(无条件转移到程序中指定的行,通常配合条件语句实现条件转移) if import interface map package range return(结束当前函数) select struct switch type var

运算符

1.算数 + - * / % ++ –
2.赋值 = += -= *= /= %=
3.关系 == != > < >= <=
4.逻辑 && || !
5.位 & | ^
6.其他 &(返回变量的储存地址) *(取指针变量对应的值)
7.自加与自减只能单独使用,不能参与到运算中,且只能在变量后边
8.两个int类型数据运算,结果一定为整数类型
9.浮点类型参与运算,结果为浮点类型
10.关系运算符的结果都是bool型
11.go中优先级最高的预算符是逗号运算符,其次是赋值运算符,为了提高优先级可以加()

指针

1.可以通过指针改变指向值
2.指针变量接收的一定是地址值
3.指针变量的地址不可以不匹配
4. *基本数据类型,即对应的指针类型

数据类型

1.基本数据类型:整型(有符号整数类型:int8 int16 int32 int64 int 无符号整数类型:uint8 uint16 uint32 uint64 uint 默认的是int类型的0 int32别名为rune),浮点型(float32, float64 都有符号 底层以符号位+指数位+尾数位的形式储存,尾数位只是存了一个大概,很可能出现精度的损失,所以通常情况下建议使用float64 默认值是0),布尔型(bool 默认值是false),字符串(string 字符串连接可以通过+实现 默认值是 “”),复数(complex64, complex128、,字符型( string 本质上是一个整数,使用的是UTF-9编码,也可以直接参与运算,输出字符的时候会将对应的码值做一个输出 字母,数字,标点等字符,底层是按照ASCII进行存储。汉字字符,底层按照Unicode码值进行存储。想显示对应的字符,必须采用格式化输出)。
2.复合数据类型:数组、切片(slice)、结构体(struct)、映射(map)、通道(chan)。
基础数据类型之间转换:T(v) (T就是数据类型 v就是需要转换的变量)

控制结构

if,else if,else

1.if 条件表达式1{
	逻辑代码
} else if 条件表达式2{
	逻辑代码2
} else {
	逻辑代码n
}

2.条件表达式左右不建议写()
3.if和表达式中间一定要有空格
4.{}必须有,就算你只写一行代码

switch,case

1.switch 表达式1,表达式2 {
	case 值1,值2:
	 语句块1
	case 值3,值4:
	 语句块2
	default:
	 语句块n
}

2.值的类型必须和表达式数据类型一致
3.default语句不是必须的
4.表达式不是必须的,可以当作if分支来使用
5.利用fallthrough关键字,在一个case语句块之后增加fallthrough会继续执行下一个case
6.注意值后的“:”

for

1.for初始表达式,布尔表达式,迭代因子{
	循环体;
}

2.()可以不要并且推荐
3. 死循环for ; ;{
循环体
}
4.Go 中没有 while 和 do-while 循环,但可以用 for 循环实现。

for range

1.for key, value = range coll{}
2.对coll进行遍历,每个索引被key接收,每个结果的具体数值被value接收

跳转语句:break、continue、goto、return。

1.break(在循环语句中结束当前分支 停止离得最近的循环 结束指定标签对应的循环 )
2.continue (结束离得最近本次循环,继续下一次循环 停止指定标签对应的循环,继续下一次循环)
3.return (结束当前函数)
4.goto(无条件转移到程序中指定的行,通常配合条件语句实现条件转移)

函数

1.func 函数名(形参列表)(返回值类型列表){
	执行语句
	return+返回值列表
}

2.函数能够提高代码的复用性,函数与函数之间是并列关系,所以我们不能在main函数中定义函数
3.函数名遵循标识符命名规范
4.如果返回值只有一个,()可以省略不写
5.函数不支持重载
6.支持可变参数(指数量)格式为:参数名…数据类型 函数内部处理可变参数的时候,将可变参数当作切片来处理
7.基本数据类型和数值默认以值传递,在函数内修改不会影响到原来的值如果希望在函数内的变量能改变函数外的变量,可以传入变量的地址,函数内以指针的方式操作变量,从效果来看类似引用传递
8.函数是一种数据类型,可以赋值给一个变量,可以通过变量对函数调用,也可以作为形参进行调用
9.可以对函数返回值进行命名 这样返回值输出就没有顺序要求
10.在函数的形参与返回值中已经声明的变量无需在实现语句中再次声明

错误处理
使用错误类型作为函数的第二个返回值。

init函数

1.初始化函数,可以用来进行一些初始化的操作
2.每一个源文件都包含一个init函数,该函数会在main函数执行前,被go运行框架调用
3.多个源文件都有init函数时,先通过import进行其他包的变量定义,init调用,再进行本包的变量定义,init调用

匿名函数

1.func (参数) 返回值数据类型{
	执行语句
	return+返回值列表
}

2.go支持匿名函数,如果某个函数只使用一次,可以考虑
3.使用方式:在定义匿名函数时直接调用,这种方式匿名函数只能调用一次 将匿名函数赋给一个变量,再通过该变量来调用匿名函数
4.将匿名函数交给一个全局变量就可以让其在整个程序中有效

闭包

1.匿名函数与其涉及的引用环境
2.本质依旧是一个匿名函数,只是这个函数引入了外界的变量/参数
3.闭包中使用的变量/参数会保存在内存中一直使用,意味着闭包不可滥用,对内存消耗大
4.闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了

递归函数

1.func 函数名(){
实现语句(需要包括条件退出语句)
调用函数本身
2.在使用递归时需要设置退出条件来避免陷入无限循环
3.求平方根要用到sqrtRecursive 四个参数的意义分别是 待求平方根的数 当前猜测的平方根值 上一次的猜测之 精度要求(即接近平方根的程度) 中止条件是当前猜测的平方根与上一次猜测的平方根差值小于给定的精度

字符串函数

1.遍历 for-range 或切片(类似于数组)
2.字符串转整数 变量名,err(可忽略):= strconv.Atoi(“字符串”)
3.整数转字符串 变量名 :=strconv.Itoa(整数类型)
4.统计一个字符串有几个不重复的指定子串 变量名:=stings.Count(主串,子串)
5.不区分大小写的字符串比较 flag(bool类型) :=strings.EqualFold(字符串1,字符串2)
6.区分大小写的字符串比较 字符串1==字符串2
7.返回子串第一次出现的索引值,没有则返回-1 strings.Index(主串,子串)
8.字符串的替换 strings.Replace(主串,被替换的内容,要替换的内容,替换几次(如果是-1则全部替换))
9.按照指定的某个字符为分割字符,将一个字符串拆分成字符串数组 string.Split(字符串,分割标识)
10.将字符串的字母进行大小写转换 strings.ToLower(转小写)/Toupper(转大写)
11.将字符串组左右两边的空格去掉 strings.TrimSpace(字符串)
12.将字符串左/右/两边的指定字符去掉 strings.TrimLeft/Right/无 (字符串,要去掉的内容)
13.判断字符串是否以指定的字符串开头/结束 strings.HasPrefix/HasSuffix(字符串,指定的字符串)
14.go中的字符串使用的是utf-8

时间和日期:需要导入time包

1.获取当前时间调用now函数 now(是一个类型为time.Time的结构体) := time.Now()
2.调用结构体的方法来获取所需数据 Year Month Day Hour Minute Second 其中Month默认英文单词 需要用int(结构体名.Month)转换
3.格式化输出 fmt.Printf(“当前年月日: %d-%d-%d 时分秒: %d:%d:%d “,结构体名.Year,结构体名.Month,结构体名.Day,结构体名.Hour,结构体名.Minute,结构体名.Second”) 或者 结构名.Format(”2006/01/02 15/04/05“) 注意里边的字符串不可变,用里边的数据进行随机组合依然可以获得当前时间

内置函数/内建函数:

1.设计者为了编程方便提供的不需要导包就能直接使用的函数
2.存放在builtin包下,所有函数默认导入
3.常用的有 len函数(统计字符串的长度,按照字节进行统计) new函数(分配内存,主要用来分配值类型,得到的是一个指针变量) make函数(分配内存,主要用来分配引用类型)

错误处理

1. type error interface {
    Error() string
}

2.使用errors.New可返回一个错误信息 需定义一个error变量来接收
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New(“math: square root of negative number”)
}
}

3.自定义错误: 需要调用errors包下的New函数
if 自定义错误 {
return errors.New(“提示错误语句”)
}else {
正常执行
return nil
}

4.程序出现错误后,若后续代码没有必要执行,想让程序中断,借助内置函数panic
panic(error)

数组

1.var 数组名 [数组长度] 元素数据类型 定义时会创建数组长度个的内存空间,里边填入数据类型的默认值
2.数组本身存储的是第一个索引的地址 第n个索引的地址是第一个索引的地址+2(n-1)
3.遍历数组的方法 第一种是普通for循环 第二种是for-range的键值循环
4.初始化形式
var arr [3] int = [3] int{1,2,3}
var arr = [3] int{1,2,3}
var arr = […] int{1,2,3,4}
var arr = […] int{2:100,3:200,1:100}
5.数组的类型是[数组长度]元素数据类型
6.go中数组属于值类型,进行值拷贝而不是引用传递 所以通过其他方法无法直接改变数组内的值 需要通过指针与地址进行改变 实参是数组地址,形参是数组指针变量
7.二维数组 var 数组名\ [数组长度][数组长度] 元素数据类型 本质上是数组内有数组
8.数组的元素是不能删的,只能覆盖。

1.使用 package main 声明包名。main包是程序的入口包,一般main函数会放在这个包下
2.使用 import 关键字引入其他包。(常用包有fmt,stringcov)
3.使用包是因为我们不可能把所有的函数放在同一个源文件里,可以分门别类地把函数放在不同的源文件中,并且同一个文件中不可以出现两个名字相同的函数,只能放在不同的包里
4.包名和文件夹名字可以不一样
5.一个目录下的同级文件归属于同一个包
6.可以给包起别名,但原名就不能用了

切片

1.一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷
2.定义切片
var 切片名 [] 元素数据类型 = 数组名[a:b] 切片索引从a开始 到b结束 包前不包后 如果a=0,b=切片的长度,可以省略不写
var切片名 = make([]元素数据类型,长度,容量) 这种方法make底层会创建一个数据,对外不可见,所以不可以直接操作这个数组,要通过slice去间接的访问各个元素,不可以直接对数组进行维护或操作
切片名:= []元素数据类型{元素1,元素2}
3.切片容量 cap(切片名) 切片长度len(切片名)
4.切片有3个字段的数据结构 指向底层数组的指针 切片的长度 切片的容量
5.遍历 for循环 for-range循环
6.可以对切片再次切片 切片名[起始索引,结束索引]
7.切片可以动态拓展 append(切片,追加元素1,追加元素2)
底层追加元素的时候对数组进行扩容,老数组扩容为新数组。创建一个新数组,将老数组中的元素复制到新数组中,在新数组中追加元素。新切片底层数组指向的是新数组,不能直接维护,需要通过切片间接维护操作
8.通过copy(a,b)进行切片的拷贝将b里的元素拷贝给a
9.二维切片(也可多维)var arr = make([][]int, n) make 函数在创建二维切片时只需要一个参数,即外层切片的长度,内层切片需要单独创建。
变量名[外层索引] = make([]int, m)
10.删除切片中的特定元素,是将其余元素整合创建为一个新切片,具体格式为
newSlice := append(slice[: indextoremove],slice[indextoremove+1:]…) 此时原切片不会被修改,若想修改原始切片,可以将新切片赋值给原始切片,删除最后一个元素为;len(slice)-1,删除第一个元素为slice[1:]

map

1.go语言中内置的一种,将键值对相关联,可以通过键key获得对应的值value 类似于集合
2.slice,map,function不可以作为键值对
3.只声明map内存不分配空间,必须通过make函数进行初始化,才会分配空间
4.定义方法
make(map[int]string,所需键值对个数,可以省略不写,默认分配一个内存空间)
map[int]string{
key:value,(,一定要有)
}
5.存储键值对格式:map名 [key]=value
6.存储键值对的时候是无序的,并且无法存入重复key 后边的值会覆盖前边的
7.增加和修改 map[“key”]=value 如果key还没有就是增加,有的话就是修改
8.删除 delete(map.“key”) delete是一个内置函数,key存在就删除该键值对,不存在不操作但也不会报错
9.清空 删除所有key要么遍历逐个删除,要么make一个新的map,让原来的被gc回收
10.查找 value,bool = map[key] value为返回的值,bool为是否返回
11.获取长度 len(map)
12.遍历 for-range
13.多key make(map[数据类型](键1) map[数据类型](键2) 数据类型)

结构体

1.go基于结构体(struct)来实现面向对象(OOP)特性的,结构体是值类型
2.type 结构体名 struct{
各种字段,可以是基本类型,数组,引用类型 大写以让外界可以访问
}
3.结构体实例的创建方法
var 变量名 结构体名 = 结构体名{
字段名 : 字段属性,

}
var 变量名 = 结构体名{属性值1,属性值2}(切记按顺序)
var 指针变量 *结构体名 =new(结构体名) 再通过(*指针变量).结构体变量名= 值 。也可以直接用指针变量.结构体的变量 = 值,但是底层还是通过前一种进行赋值的。
var 指针变量 *结构提名 = &结构体名{
按照顺序给变量赋值
} 或者通过上面的两种进行赋值。
4.结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字,个数,类型) 格式为 变量名=结构体名(变量名) ,结构体可以在函数外定义
5.结构体进行type重新定义相当于取别名,会被认为是新的数据类型,但是相互之间可以强转。
6.结构体的实例是值传递
7.结构体的字段在内存中是连续的(数据类型是什么就隔多少字节)。
8.struct的每个字段上可以写一个tag,该tag可以通过反射机制获得,常见的使用场景就是序列化和反序列化,作用是兼容其他程序的命名习惯。tag格式为‘json:属性名’
9.结构体里的属性与结构体的方法没有什么必然联系

结构体的工厂模式

1.golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题
2.如果结构体名首字母大写,属性名首字母小写,则其他包不可以直接获取该结构体属性,可以提供一个方法(方法首字母得大写)
func (结构体指针变量 *结构体名) 方法名() 返回值(也就是结构体变量的属性)
3.如果结构体字母是小写,其他包无法导入,则可以用工厂模式
func 方法名(属性变量1,属性变量2) *结构体名{
return &结构体名{
属性名1 : 属性变量1
属性名2 : 属性变量2
}
}
返回的是一个指针变量,输出时会有&

方法

1.golang中的方法是作用在指定的数据类型上的,因此自定义类型(以type定义)都可以有方法
2.方法的定义 func (a A)test() {
实现语句
}表示A结构体有一个方法名为test (a A) 表示与哪个数据类型(结构体)绑定
3.结构体类型是值类型,在方法调用中是值拷贝传递方式,如果希望在方法中修改结构体变量的值,可以通过结构体指针的方式来处理
4.如果一个变量实现了String()这个方法,那么输出时会默认以这个变量的String()进行输出 ,如果是以指针变量的形式定义的那么就需要以fmt.Println(%指针变量)来输出
5. 方法调用格式为 结构体实例.方法(实参) 记得后边加(),有实参填实参 没有不填

面向对象三大特性

封装

1.封装就是把抽象出的属性和对属性的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对属性进行操作
2.go中通过方法,包实现封装,或对结构体中的属性进行封装

封装的具体步骤

1.将结构体,属性的首字母小写(目的是不能导出,其他包不能使用,类似于private)
2.给结构体所在包提供一个工厂模式的函数,首字母大写,类似于一个构造函数
3.提供一个首字母大写的Set方法,用于对属性判断并赋值
func (var 结构体类型名) Set(参数列表)(返回值列表){
//加入数据验证的业务逻辑
var.属性 =参数
}
4.提供一个首字母大写的Get方法,用于获取属性的值
func(var 结构体类型名) Get(){
return var.属性名;
}

继承

1.继承能够有效提升代码复用性
2.golang中的继承是通过匿名结构体实现的,在想要继承的结构体内输入父类结构体名即可
3.b.A.name 可以简化为 b.name(方法也可以此法简化)
当我们直接通过b访问字段或方法时,其执行流程如下:编译器会先看b对应的B有没有Name,有的话直接调用。如果没有就去看B中嵌入的匿名结构体,都找不到就报错,原则是就近原则
4.结构体嵌入两个或多个匿名结构体,如果他们有相同的属性和方法,同时结构体本身没有同名的属性和方法,在访问时就必须明确指定匿名结构体名字,不然报错
5.如果一个结构体嵌套了一个有名结构体(实例),这种模式就是组合,如果是组合关系,那么在访问组合的结构体的属性或方法时必须带上嵌套的结构体实例的名字
6.嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体属性的值
7.匿名字段可以是基本数据类型,输出格式为 结构体变量.基本数据类型
8.如果一个结构体中有一个基本类型的匿名字段,就不能有第二个,如果一定要有,就要给int属性取名字用以区分
9.为了保证代码的简洁性,尽量不适用多重继承

接口

1.实现了一个接口的所有方法,就实现了该接口。实现一个接口,需要实现该接口所有方法。
2.定义接口
typpe 接口名 interface{
方法名(参数列表)返回值列表
}
3.接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法,体现了程序设计多态和高内聚低耦合的思想。
4.接口不能包含任何变量
5.接口本身不能创建实例,但是可以创建一个实现了该接口的自定义类型的实例(先定义自定义类型的变量,再定义该接口变量)
6.只要是自定义数据类型,就可以实现接口。
7.一个自定义类型可以实现多个接口
8.一个接口可以继承多个别的接口,但是这些接口不能有相同的方法名(因为无法区分)。这时如果要实现a接口,也必须将b,c接口的方法全部实现
9.接口类型默认是一个指针(引用类型),如果没有对接口初始化直接输出会获得nil
10.空接口没有任何方法,所以所有类型都实现了空接口
11.自定义数据类型实现接口的方法时,所用的类型是什么,就只能用相应的类型来定义接口变量(如果用的是指针变量来实现,那么就无法用非指针变量实现)
func(a *A) method(){}
var a A = A{}
var i Interface = a(错误写法)
var i interface = &a(正确写法)

类型断言

1.由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言
2. 格式为
var a float32
var b interface{}(空接口,所有类型都实现了他)
b = a
c := b.(float32)
带检查的格式为
if c,ok := b.(float32) ; ok{
为真则检测通过
}else {
为假则检测不通过
}(可以用来判断c类型是否能成功转换为b类型)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值