1.golang基础语法和使用

2 篇文章 0 订阅

golang基础语法

变量申明

var a int
var b string
var c []float32
var d func() bool //申明一个返回值为bool的函数变量,一般用于回调函数,即将函数以变量的形式保存下来
var e struct{x int}//结构体变量
<==> 等价于
var (
	a int
	b string
	...
)

变量初始化

var a int = 10 <==> var a = 10
//快捷变量定义
hp := 100 // 这种方式定义变量只能在函数中使用

匿名变量

匿名变量使用 ‘_’ 表示, 匿名变量不占命名空间,也不会分配内存

func GetData()(int, int) {
	return 100, 100
}
a, _ := GetData() 
_, b := GetData() 

golang数据类型

整型、浮点型、字符串、字符(byte)、布尔类型、数组、切片、结构体、map、指针、通道

注意: 多行字符串可以使用 反引号

& 表示取地址操作

var a int = 10
pv := &a //指正pv

(*) 表示 对变量进行取值操作

func Swap(a, b *int) {
	t := *a
	*a = *b
	*b = t
}
func TestSwap() {
	x, y := 1,2
	Swap(&x, &y)
	fmt.Println(x, y)
}
-----------------------------
2 1
func Swap1(a, b *int) {
	b, a = a, b
}
func TestSwap() {
	x, y := 1,2
	Swap1(&x, &y)
	fmt.Println(x, y)
}
---------------------------
1 2
不会进行数据交换, 只是交换了数据的地址

new()方法创建指针

str := new(string)
*str = "hello wprld"

变量生命周期(堆和栈–变量逃逸)

栈:一种线性表数据结构, 先入后出
一般情况下局部变量使用的内存分配在栈上,分配和回收速度非常快
堆:全局变量一般分配在堆上
变量被取地址时,内存逃逸到堆上

字符串的应用

str := "hello world"
len(str) //计算字符串中的字符长度
str[i] //表示第i位 的字符
//使用for循环遍历字符串
for i:=0;i<len(str);i++ {
	fmt.Printf("%c", str[i])
}
-------------------
hello world

修改字符串的值:go无法直接修改每个字符元素,只能重新构建新的字符串并赋值给原来字符串变量

str := "hello world"
newStr := []byte(str)
for i:=0;i<len(newStr);i++{
	newStr[i] = 'y'
}
fmt.Println(string(newStr))
-------------------------------
yyyyyyyyyyy

数组、切片、map,列表

数组定义

固定大小的连续空间 定义为数组

//数组申明
var arr [3]string
//数组初始化
var arr = [3]string{"1", "3", "2"}
或者
var arr = [...]string{"1", "3", "2"}
//... 表示编译器在编译时根据元素个数确定数组大小

切片的定义

动态分配内存大小的连续空间

//申明字符串切片
var slice []string //slice == nil
var slice []int      // slice == nil
//申明空切片
var slice = []int{} // slice != nil
//申明有数据的切片
var arr = []string{"1", "3", "2"}
//使用make函数构造切片
var slice = make([]int, size, cap)
var a = make([]int, 2)
var b = make([]int, 2, 10)
//注意: cap的大小不会影响 切片的长度,只是提前分配空间,降低分配空间造成的性能问题
//因此。 len(a) == len(b)

var a []int                    //nil切片,和nil相等,一般用来表示一个不存在的切片
var b []int{}                //空切片,和nil不相等,一般用来表示一个空的集合
var c []int{1, 2, 3}        //有3个元素的切片,len和cap都为3
var d = c[:2]                //有2个元素的切片,len为2,cap为3
var e = c[:2:cap(c)]        //有2个元素的切片,len为2,cap为3
var f = c[:0]                //有0个元素的切片,len为0,cap为3
var g = make([]int, 3)        //创建一个切片,len和cap均为3
var h = make([]int, 3, 6)    //创建一个切片,len为3,cap为5
var i = make([]int, 0, 3)    //创建一个切片,len为0,cap为3

从数组或切片中生成新切片

slice[开始位置:结束位置]
slice元素个数:结束位置 - 开始位置
开始位置

	var arr1 = []string{"1", "3", "2"}
	fmt.Println(arr1[:])   // [1 3 2]
	fmt.Println(arr1[:len(arr1)-1]) // [1 3]
	fmt.Println(arr1[:len(arr1)-2]) //[1]
	fmt.Println(arr1[1:2]) // [3] --范围arr[1]- arr[2-1]
	fmt.Println(arr1[1:]) // [3 2] ---范围 arr[1] -- arr[2]
	fmt.Println(arr1[0:0]) //[] 清空切片

append为切片添加元素

var car []string
car = append(car,"olddriver")  //添加一个元素
car = append(car, "Ice", "Snoper", "Monl") //添加多个元素
team := []string{"Pig", "Chicken"}
car = append(car, team...) //添加切片需要使用 ...

数组和切片的区别

  1. Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。因此,在 Go 中如果将数组作为函数的参数传递的话,那效率就肯定没有传递指针高了。
  2. 数组的长度也是类型的一部分,这就说明[10]int和[20]int不是同一种数据类型。并且Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限制带来不少局限性。
  3. 而切片则不同,切片(slice)是一个拥有相同类型元素的可变长序列,可以方便地进行扩容和传递,实际使用时比数组更加灵活,这也正是切片存在的意义。而且切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。

golang map

map的定义

var scen = make(map[string]string)
scen["rout"] = "route"

v, ok := scen["rout"]
//ok 为bool,判断map中是否存在 键值为 rout 的值 , v 为value值

//map 的遍历
for k, v := range scen {
}
//delete() 函数删除 map中的值
delete(scen, "rout")
// golang 中不需要清除map, 支持新建map 就行,不用的map 会被gc

sync.Map (并发情况下使用的map)

func MapTest() {
	m := make(map[int]int)
	go func() {
		for{
			m[1] = 1
		}
	}()
	go func(){
		for {
			_ = m[1]
		}
	}()
	for {

	}
}
--------------------------------------------
fatal error: concurrent map read and map write

使用sync.Map

func syncMapTest() {
	var scene sync.Map
	scene.Store("green", 9)
	scene.Store("london", "100")
	fmt.Println(scene.Load("london"))
	scene.Delete("london")

	scene.Range(func(k, v interface{}) bool {
		fmt.Println("iii", k, v)
		return true
	})
}

列表

列表的定义

	ll := list.New()   
	var lll list.List
	//两种定义方式效果一样

列表的操作实例

func listTest() {
	l := list.New() //var lll list.List
	//尾部添加元素
	l.PushBack("canon")
	//头部添加数据
	l.PushFront(67)
	// 尾部添加元素后保存元素句柄
	element := l.PushBack("first")
	l.InsertAfter("HIGH", element)
	l.InsertBefore("noon", element)
	l.Remove(element)
	// list 遍历
	for i:= l.Front();i != nil; i = i.Next() {
		fmt.Println(i.Value)
	}
}

流程控制(if and for and switch)

条件判断if

if 表达式1 {
	分支1
} else if 表达式2 {
	分支2
} else {
	分支3
}
--------------------------
特殊写法实例
if err := COontent(); err != nil {
	fmt.Println(err)
	return err
}

构建循环for

for语句的写法总结
for 初始语句; 条件表达式;结束语句 {
	循环代码
}
for 循环可以通过 break / goto / return / panic 语句退出循环
----------------------------------------------------------------------------
for i:=0;i<10;i++ {
	fmt.Println(i)
}
---------------------------------------------------------------------------
//初始语句可省略, 但初始语句后面的分号必须写
step := 2
for ; step > 0; step-- {
	fmt.Println(step)
}
---------------------------------------------------------------------------
//条件表达式可以被忽略,但是分号不能省略;默认无限循环
var i int
for ; ; i++ {
	if i > 10 {
		break
	}
}
-------------------------------------------------------------------------
// 完美无限循环
var i int
for {
	if i > 10 {
		break
	} 
	i++
}
--------------------------------------------------------------------------
// 只有一个循环条件的循环 可以替换while
var i int 
for i < 10 {
	i++
}

for range 的使用

注意: 可以使用 _ 匿名变量替换其中的返回值参数

  • 遍历数组和切片
for k, v := range []int{1, 2, 3, 4} {
	fmt.Println(k, v)
}
----------------------------------------
0 1
1 2
2 3
3 4
  • 遍历字符串,获得字符
//实际存放的是int32型数据
var str = "hello world"
for k, v := range str {
	fmt.Printf("k:%d v:0x%x-%v-%T\n",k, v,v,v)
}
-------------------------------------------------
k:0 v:0x68-104-int32
k:1 v:0x65-101-int32
k:2 v:0x6c-108-int32
k:3 v:0x6c-108-int32
k:4 v:0x6f-111-int32
k:5 v:0x6211-25105-int32
k:8 v:0x4f60-20320-int32
k:11 v:0x4ed6-20182-int32
  • 遍历map
m := make(map[string]int)
m["hello"] = 100
m["world"] = 200
for k, v := range m {
	fmt.Println(k, v)
}
--------------------------
hello 100
world 200
  • 遍历channel-接收通道信息
//根据channel阻塞原理分析打印结果
c := make(chan int)
go func() {
	c <- 1
	fmt.Println("write")
	c <- 2
	fmt.Println("write")
	c <- 3
	fmt.Println("write")
	close(c)
}()
for v := range c {
	fmt.Println("read ",v)
}
--------------------------------
write
read  1
read  2
write
write
read  3

有一个缓冲的channel

c1 := make(chan int, 1)
go func() {
	c1 <- 1
	fmt.Println("write1")
	c1 <- 2
	fmt.Println("write2")
	c1 <- 3
	fmt.Println("write3")
	close(c1)
}()
for v := range c1 {
	fmt.Println("read ",v)
}
--------
write1
write2
read  1
read  2
read  3
write3

switch

基本写法

//多个case可以放一起, 每个case中不需要写 break, 每一个case是独立的条件模块
var a = "hello"
switch a {
case "hello", "test": // case a == "hello" case s > 10 && s < 20:
	fmt.Println("1")
	fallthrough     // 通过此关键词实现执行完本次case之后顺序往下执行其他case
case "world":
	fmt.Println("2")
default:
	fmt.Println("3")
}

goto、break、continue

goto 标签 – 跳转到指定标签位置运行
break 标签 – 跳出标签的循环
continue 标签 – 继续执行标签开始的循环
break 和continue 默认1层, 添加标签可以实现操作多层循环

//goto
err := CheckErr()
if err != nil {
	goto onExit
}
onExit:
	return err
// break
Outbreak:
	for {
		for {
			break Outbreak
		}
	}
// continue
Outcontinue:
	for {
		for {
			continue Outcontinue
		}
	}

函数

声明函数
func 函数名(参数列表)(返回参数列表){
	函数体
}
func foo(a int, b string){}
func foo(a, b int){}
func add(a, b int) int{}
func add(a, b int) (c int) {}

匿名函数-没有名字的函数

匿名函数只有函数体,没有函数名,函数可以作为一种类型被复制给函数类型的变量, 匿名函数也往往可以以变量的方式传递。
匿名函数常常被定义与实现回调函数,闭包等。

//func(函数参数)(返回参数) {
//	函数体
//}
// 1、匿名函数在定义时调用
func(data int) {
	fmt.Println("hello", data)	
}(100)
//2、匿名函数赋值给变量
f := func(data int) {
	fmt.Println("hello", data)	
}
//调用函数
f(100)
// 3、匿名函数作为回调函数
func visit(list []int, f func(int)) {
	for _,v := range list {
		f(v)
	}
}
func main() {
	visit([]int{1, 2,,3,5}, func(v int) {
		fmt.Println(v)
	})
}

return 和defer

func test() (res int) {
    res = 1
    defer func() {
        res++
    }()
    return 0
}

此情况下,return时res赋值为0,执行defer函数 res++,res为1,最后返回值为1

func main() {
	fmt.Println("defer begin")
	defer fmt.Println("1")
	defer fmt.Println("2")
	defer fmt.Println("3")
	fmt.Println("defer end")
}
---------------------------------------------------------------------
defer begin
defer end
3
2
1

错误格式

自定义错误格式

var err = errors.New("this is an error")

// error类型的信息可以通过err.Error() 转换为字符串格式的错误输出
var err error
err.Error()

程序终止运行-宕机(panic)

手动触发宕机

panic()的参数可以是任意类型,recover 参数会接受从panic 中的内容

func main() {
	panic("crash")
}

宕机触发defer函数执行

func main() {
	defer fmt.Println("宕机做 的事情1 ")
	defer fmt.Println("宕机做 的事情2 ")
	panic("宕机")
}

宕机恢复

go中 recover() 可以和panic 配合使用, 捕获异常信息

结构体

  • 使用new 或 & 构造的类型实例的类型是类型指针

golang结构体

type Bag struct() {
	items []int
}
func Insert(b *Bag, itemid int) {
	b.items = append(b.items, itemid)
}
func main() {
	b := new(Bag)
	Insert(b, 100)
}
-------------------------------------------------------------------
func (b *Bag) Insert(itemid int) {
	b.Items  = append(b.items, itemid)
}
func main() {
	b := new(Bag)
	b.Insert(100)
}

接收器-方法作用的目标

接收器结构

func (接收器变量 接收器类型) 方法名(参数列表)返回值列表 {
	函数体
}

指针类型接收器 和非指针类型接收器
修改指针接收器的任意成员变量,方法结束之后,修改有效

type Property struct {
	value int
}
func (p *Property) SetValue (v int )  {
	p.value = v
}
func (p *Property) GetValue() int {
	return p.value
}
func Test() {
	p := new(Property)
	p.SetValue(100)
	v := p.GetValue()
	fmt.Println(v)
}
-------------------------------------------
//非指针类型接收器, go 会复制一份 接收器的值,进行操作
// 在方法中修改接收器的成员值 不生效
type Point struct {
	X int
	Y int
}
func (p Point) Add(op Point) Point {
	return Point{p.X+op.X, p.Y + op.Y}
}
func Test1() {
	p1 := Point{1, 1}
	p2 := Point{2,2}
	res := p1.Add(p2)
	fmt.Println(res)
}

接口-interface

  • go 语言的接口计是非侵入式的,接口编写者无需知道接口被哪些类型实现,接口实现者只需知道实现的是什么样子的接口。
  • 接口是双方约定的一种合作协议,接口实现者不需要关心接口被怎样使用,调用者也不需要关心接口的实现细节。
  • 接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式,类型及结构

接口的声明

每个接口由数个方法组成,形式如下:
type 接口类型名 interface {
方法名1(参数列表1)返回值列表1
方法名2(参数列表2)返回值列表2

}

//方法名首字母大写且接口类型名首字母也大写时,方法可以被接口所在包之外的代码访问
type writer interface {
	Write([]byte) error
}
type Stringer interface {
	String() string
}
type Test interface {
	Tester()
}

type MyFloat float64

func (m MyFloat) Tester() {
	fmt.Println(m)
}
func describe(t Test) {
	fmt.Printf("Interface 类型 %T ,  值: %v\n", t, t)
}
func main() {
	var t Test
	f := MyFloat(89.7)
	t = f
	describe(t)
	t.Tester()
}

具有0个方法的接口称为空接口。它表示为interface {}。由于空接口有0个方法,所有类型都实现了空接口。

	package main
 
	import (
		"fmt"
	)
	
	// 定义一个接口
	type People interface {
		ReturnName() string
	}
	
	// 定义一个结构体
	type Student struct {
		Name string
	}
	
	// 定义结构体的一个方法。
	// 突然发现这个方法同接口People的所有方法(就一个),此时可直接认为结构体Student实现了接口People
	func (s Student) ReturnName() string {
		return s.Name
	}
	
	func main() {
		cbs := Student{Name:"咖啡色的羊驼"}
	
		var a People
		// 因为Students实现了接口所以直接赋值没问题
		// 如果没实现会报错:cannot use cbs (type Student) as type People in assignment:Student does not implement People (missing ReturnName method)
		a = cbs       
		name := a.ReturnName() 
		fmt.Println(name) // 输出"咖啡色的羊驼"
	}
//接口的零值是nil
var cbs People
if cbs == nil {
    fmt.Println("cbs is nil 类型")  
}

golang内网包管理(引入第三方的包)

//生成模块管理文件
go mod init genconf

在源码中放置第三方包文件, golang 源码中使用的包名可以使用本地的文件目录替换,
可以实现第三方库的二次开发
在这里插入图片描述

golang中的init初始化函数

golang 执行main函数之前会先执行常量初始化、变量初始化、init函数初始化。
参考:https://blog.csdn.net/liuyuede123/article/details/127394496

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值