Go 编程基础

Go编程基础

go run 直接运行可执行的程序
go build 运行且生成该系统下的可执行文件
栈:先进后出
堆:先进先出

1.定义

全局变量必须通过 var i int 定义
支持多重赋值 i,j=j,i

2.常量

const 常亮的赋值是编译行为,不是运行期行为
iota出现一次,常亮值+1,但是出现下一个const,iota重置为0
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
两个 iota赋值语句相同,可以省略表达式
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
a = 1 <<iota // a == 1 (iota在每个const开头被重设为0)
b // b == 2
c // c == 4
)

3.变量

  以大写字母开头,在包外可见
  不同整型的强制转换
  var a int=35
  var b int64
  b=int64(a)
  &&两个为真,真, ||一个为真,真,运算符优先级,&&高于||
  字符串内容初始化后,不能修改
  str := "Hello world" // 字符串也支持声明时进行初始化的做法 
  str[0] = 'X' // 编译错误
  字符串连接用+
  “hello”+"134" //结果为hello134

4.数组

4种数组的初始化方式:
var arr [3]int=[3]int{1,2,3}
var arr=[3]int{1,2,3}
var arr=[...]int{1,2,3}
var arr=[...]int{1:2,0:1,2:3}
  数组:同一类型元素的组合,数组的地址可以通过&数组名来获取,数组的第一个元素的地址是数组的首地址
  数组的使用步骤:申明数组并开辟空间,给数组元素的值赋值,使用数组
  如果要修改素组内的值,采用引用的办法
  [32]byte     // 长度为32的数组,每个元素为一个字节 [2*N] 
  struct { x, y int32 } // 复杂类型数组 
  [1000]*float64   // 指针数组 
  [3][5]int    // 二维数组
   [2][2][2]float64   // 等同于[2]([2]([2]float64))
   数组是值传递
   package main   
	import "fmt" 

func modify(array [10]int) { array[0] = 10 // 试图修改数组的第一个元素 fmt.Println(“In modify(), array values:”, array)
}
func main() { array := [5]int{1,2,3,4,5} // 定义并初始化一个数组
modify(array) // 传递给一个函数,并试图在函数体内修改这个数组内容
fmt.Println(“In main(), array values:”, array) }
该程序的执行结果为:
In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]

5.切片

切片从底层上来说是一个数据结构(struct)
基于myArray的所有元素创建数组切片:mySlice = myArray[:]
基于myArray的前5个元素创建数组切片: mySlice = myArray[:5]
基于myArray的后5个元素创建数组切片: mySlice = myArray[5:]
创建一个初始元素个数为5的数组切片,元素初始值为0: mySlice1 := make([]int, 5)
创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:mySlice2 := make([]int, 5, 10)
直接创建并初始化包含5个元素的数组切片: mySlice3 := []int{1, 2, 3, 4, 5}
添加3个元素:mySlice4=append(mySlice4,1,2,3)
添加一个切片:mySlice=append(mySlice,mySlice4…)
基于数组切片创建数组切片:
oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前3个元素构建新数组切片,结果1,2,3,4
string的底层是byte数组,可以用切片处理
修改string的方法:将字符串str该为[]byte(str)
在这里插入图片描述

6map

申明变量:var mymap map [string]int
申明是不需要分配内存的,初始化需要make,分配内存后才能赋值和使用
初始化: mymap:=make(map[string]int)
其中key 通常不能为map,function,slilce来使用,因为无法==
human :=make(map[string]student,3)
human[“no1”]=student{“刘成杰”,“男”}
human[“no2”]=student{“刘成”,“男”}
human[“no3”]=student{“成杰”,“女”}
human[“no4”]=student{“liu成杰”,“女”} //map的扩容方法
fmt.Println(len(human))
容量为4
建立map 的方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
a[faker]=no.1,没有这个key就扩容,有这个key就重新赋值,len()看map 的长度,因为自动扩容,没办法看cap()
删除单个key:delete(heros,“hero1”),直接删除key
删除整个map,遍历整个map,然后单个删除,第二,直接重新定义map
heros:=make(map[string]string)
在这里插入图片描述
var mymap map[string]string
mymap= make(map[string]string,3)
mymap[“武松”]=“有个不敢干嫂子”
mymap[“武大郎”]="漂亮能干的老婆 "
renyao:=make(map[string]map[string]string)
renyao[“唐僧”]=make(map[string]string)
renyao[“唐僧”][“唐长老”]=“女儿国国王看上"长"老”
renyao[“孙悟空”]=make(map[string]string)
renyao[“孙悟空”][“人妖”]=“一棍打死白骨精”
结果:map[武松:有个漂亮嫂子 武大郎:漂亮能干的老婆 ]
map[唐僧:map[唐长老:女儿国国王看上"长"老] 孙悟空:map[人妖:一棍打死白骨精]]
map切片:

在这里插入图片描述

7. switch

case,switch 后面是表达式(常亮,变量,有返回值的函数等)
case 后面的表达式需要 :,case后的表达式可以用多个,用 ,隔开
fallthough,在case语句后加一个fallthough(穿透),继续向下执行
break 跳出最近一层for循环
break 后面可以指定标签,跳出标签对应的for循环
在这里插入图片描述
for 循环一般建议做多两层,最多不不超过3层
contiue和if连用,可以跳过指定循环
在这里插入图片描述
goto 能够无条件的转移到程序中的指定行,一般不主张使用goto,goto跟随标识符跳转
在这里插入图片描述
return跳出函数或者方法
return在普通函数直接跳出函数(终止函数),在主函数终止程序

8.函数 包和错误处理

文件的包名通常和文件所在的文件名名字一致,都为小写,函数名通常为大写,这样才能被外部调用
在这里插入图片描述
调用函数时,会给函数分配一个新的栈空间,每个函数的栈中空间是独立的 ,函数调用完后销毁函数,释放栈

匿名函数

函数等当做参数,传递给函数
在这里插入图片描述
在这里插入图片描述
函数参数支持可变参,如果最后一个参数是可变参,需要放到最后
在这里插入图片描述
init函数在main函数之前被调用完成初始化工作
面试题:如果main.go 和utils.go都含有定义变量,init函数时,执行流程是怎么样的
先执行utils.go的函数,然后init,在然后main(utils.go 放常用方法)
在这里插入图片描述
在这里插入图片描述
如果只希望函数执行一次就用匿名函数 ,将匿名函数赋值给全局变量,那么匿名函数对整个程序有效
在这里插入图片描述在这里插入图片描述
闭包
在这里插入图片描述当输出是i的值,闭包递增,完成累加(i在闭包外面)
在这里插入图片描述当需要打印n的值的时候,闭包保持每次进出都遵循传进来的结果
在这里插入图片描述
闭包传入的是后面需要返回的匿名函数

defer会压到栈中(多个defer也是这样先入后出),defer最主要用在延迟关闭资源
在这里插入图片描述
值传递:map,切片,channel,接口!!结构体是值传递,值传递的放在堆上,没有使用的话直接GC回收
变量或者常亮,对一在函数外,若首字母大写对整个程序有效,定义小写则对包有效
new用来分配内存,主要用来分配值类型,如int,float64,sturct …返回的是指针
make:用来分配内存,主要分配引用类型,主要用于切片,map,channel

自定义错误类型:panic(),errors.New()

9.排序和查找

内部排序:数据量较小的情况下,加载到内部储存器排序(冒泡排序,选择试排序,插入是排序)
外部排序: 数据量大,无法加载到内存中,借助外部储存排序(合并排序和直接合并排序法)
冒泡排序:
func bubbleSort(sli []int) {
for i:=0;i<len(sli);i++ {
for j:=i+1;j<len(sli) ; j++ {
if sli[i]<sli[j]{
sli[i],sli[j]=sli[j],sli[i]
}
}
}
}
func main() {
start:=time.Now().UnixNano()
sli:=[]int{5,9,8,3,2,4}

bubbleSort(sli)
end:=time.Now().UnixNano()
fmt.Println(sli,start-end)

}

二分查找:底层依赖数组,有序适合静态数据(没有平凡插入删除的),适合数据较大的数组

10.面向对象

OOP:只有继承,封装,多态的特性
int的零值(默认值)0 string " " bool false 数组 0 指针,切片,map都是nil,没有分配空间
结构体是值传递
结构体的所有字段在内存中是连续的
不同的结构体变量的是独立的互不影响(值传递)
创建结构体变量和访问结构体字段
在这里插入图片描述
struct 的序列化和反序列化
在这里插入图片描述
结构体实例后直接调用方法
在这里插入图片描述
方法不仅可以绑定strct,还可以绑定其他的
封装实现的步骤:
1.将结构体,字段(属性)首*字母小写了,无法导出,其他包无法使用(依靠工厂模式)
2.给结构体提供一个工厂模式的函数,首字母大写,类似一个构造函数
3.提供首字母大写的Set方法(其他函数可以调用的),用于对属性判断并赋值func(var *结构体名)Setxxx(参数列表)(返回值列表)
//假如数据验证的逻辑
var.字段=参数
4.提供首字母大写的Get方法,获取属性的值
fun(var *结构体名 )Getxxx()(返回值){
return var.想要返回的
}
在这里插入图片描述在这里插入图片描述在这里插入图片描述
面向对象继承,能够继承结构体和方法
在这里插入图片描述
在这里插入图片描述
可以简化成:
在这里插入图片描述
当结构体和匿名结构体有相同的字段和方法时,采用就近原则
在这里插入图片描述
嵌套匿名结构体后,也可以直接创建结构体变量(实例),直接指定各个字段的值
在这里插入图片描述
在这里插入图片描述
一个结构体只能有一个同类型的匿名字段

12.接口

interface类型能够定义一种方法,但是不需要实现,interface不能包含任何变量

基本语法在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接口里的方法都没有方法体,即接口都是没有实现的方法,体现了程序多态,高内聚,低耦合的思想
golang中的接口不需要显示的实现,只要一个变量含有接口中所有的方法,那么这个变量就实现了接口中所有的接口
注意和细节:
接口本身不能创建实例,但是可以指向一个实现了该接口自定义类型的变量(实例)
在这里插入图片描述
2.接口中所有的方法都没有方法体,即都是没有实现的方法
3.一个自定义的类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口
4.自定义的类型只有实现了该接口,才能将自定义类型的实例(变量)赋给接口类型
5.只要是自定义的数据类型,就可以实现接口,不仅仅是结构体类型

在这里插入图片描述
一个自定义类型可以实现多个接口
在这里插入图片描述在这里插入图片描述
7.接口中不能有任何变量
8.一个接口可以继承其他多个接口,如果要实现该接口,但是前提其他接口的方法也全实现
在这里插入图片描述
在这里插入图片描述
9.接口的默认类型是指针,如果interface没有初始化就使用,那么会输出nil
10.空接口interface{}没有任何的方法,所以所有的类型都实现了该接口,即我们可以吧任何的值赋给空接口

接口和继承:
在这里插入图片描述在这里插入图片描述
继承的价值在于:代码的复用性和可维护性
接口的价值在于:设计,设计好各种规范(方法),让其他自定义类型去实现这些方法
接口比继承更加灵活,继承是(is-a),接口是(like-a),接口能在一定程度上实现代码解耦

多态的特征是通过接口实现的
多态:变量(实例)具有多种形态
在这里插入图片描述
多态有两种形式:多态参数(上面的computer),多态数组
在这里插入图片描述
在这里插入图片描述
类型断言(带检测):
检测不知道的类型的变量(实例)给接口,判断能否正常赋值,不能正常赋值也不能报panic
在这里插入图片描述

13.go和channel

go协程的特点:
独立的栈空间,共享堆空间,调度由用户控制,轻量级的线程
go主线程是一个物理线程,作用在CPU,非常耗费资源,是重量级的;协程是主线程开启的,是逻辑态,消耗资源少
goroutine 的调度模型
MPG模式
在这里插入图片描述
M:主线程;P:协程执行需要的上下文;G协程
num:=runtime.Numcpu()//获得当前电脑的CPU
runtime.GOMAXPROCS(num)//使用当前最大的CPU

不同goroutine之间的通讯:全局变量互斥锁,使用管道来解决

channel :
本质是一个数据结构-队列
数据是先进先出
线程安全,多个gouroutine访问是不需要加锁,就说明线程本身是安全的
channel是有类型的
在这里插入图片描述
channel的注意事项:
channel 中只能存放指定数据类型
channel满后,就不能再放入数据类型了
如果没有使用到协程的情况下,如果channel读取完了,再取就会dead lock
channel用for-range遍历时 :
channel没有关闭,则deadlock
channel已经关闭,正常遍历,遍历完成退出
遍历channel需要用for range来遍历
select:
作用: 用来监听 channel 上的数据流动方向。 读?写?

用法: 参考 switch case 语句。 但!case后面必须是IO操作,不可以任意写判别表达式。

注意事项:
	1. 监听的case中,没有满足监听条件,阻塞。
	2. 监听的case中,有多个满足监听条件,任选一个执行。
	3. 可以使用default来处理所有case都不满足监听条件的状况。 通常不用(会产生轮询)
	4. select 自身不带有循环机制,需借助外层 for 来循环监听
	5. break 跳出 select中的一个case选项 。类似于switch中的用法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值