2-golang基础-流程控制及数据类型

2-golang基础

1. 流程控制

1. 顺序控制

2. 分支控制

  1. if-else
func main() {
	// golang支持在if中直接定义一个变量
	if age := 20; age > 18 {
		fmt.Println("aa")
	}
}
  1. switch

    func main() {
    
    	var age int = 18
    
    	switch age {
    	// case 中可以使用多个表达式(常量、变量、有结果的函数),用逗号隔开
    	// case 结束不需要break
    	case 18, 20:
    		fmt.Println("aaa")
    		// fallthrough穿透,会同时执行下一个case(不管下一个case条件是否满足)
    		fallthrough
    	case 25:
    		fmt.Println("bbb")
    	default:
    		fmt.Println("ccc")
    	}
    
    }
    

3. 循环控制

golang中没有while和do-while循环

func main() {

	// 第一种
	for i := 0; i < 5; i++ {
		fmt.Println("hello1")
	}

	// 第二种
	j := 0
	for j < 5 {
		fmt.Println("hello2")
		j++
	}

	// 第三种,配合break使用
	k := 0
	for {
		fmt.Println("hello3")
		k++
		if k >= 5 {
			break
		}
	}
}


func main() {
	// for-range遍历字符串
    // for-range按照字符遍历,可以有中文不会乱码
	// golang按utf8编码,中文占3字节,所以北的下标是6,京的下标是9
	str := "abc~ok北京"
	for index, val := range str {
		fmt.Printf("index=%v val=%c \n", index, val)
	}
}

2. 函数

1. 基本介绍

func 函数名(形参列表) (返回值列表) {
    执行语句...
    return 返回值列表
}
// 斐波那契数列 1 1 2 3 5 8 13...
func test(n int) int {
	if n == 1 || n == 2 {
		return 1
	} else {
		return test(n-1) + test(n-2)
	}
}

func main() {
	result := test(7)
	fmt.Println(result)
}
  • golang中基本数据类型和数组都是值传递的,即进行值拷贝,在函数内修改,不会影响到函数外的值

  • 如果希望函数内的变量能修改函数外的变量,可以传递变量的地址(&)到函数中

  • golang支持自定义类型

    // 相当于是给int定义一个别名,但是语法会将myInt和int作为两个不同的类型
    type myInt int
    
  • golang支持可变参数个数

    通过args[index]可以访问到各个参数的值

    // 例如:支持0到多个参数
    func sum(args ...int) sum int {   
    }
    
    // 例如:支持1到多个参数
    func sum(n1 int, args ...int) sum int {  
    }
    
    func sum(n1 int, args ...int) int {
    	sum := n1
    	for i := 0; i < len(args); i++ {
    		sum += args[i]
    	}
    	return sum
    }
    
    func main() {
    	result := sum(10, 20, 30, 40, 50)
    	fmt.Println(result)
    }
    

2. init函数

每个源文件都能包含一个init函数,该函数在main函数之前调用,可以在init函数中做一些初始化操作。

执行顺序:先执行全局变量定义,再执行init函数,最后执行main函数

如果main.go中import utils.go,则先执行utils.go中的全局变量和init函数,再执行main中的全局变量和init函数

3. 闭包

一个函数与其相关的引用环境组合的一个整体(实体)

// 累加器

//AddUpper是一个函数,返回数据类型是 func(int) int
func AddUpper() func(int) int {
	var n int = 10
	var str = "hello"
	// 匿名函数,引用到函数外的n和str,这个匿名函数和n、str构成闭包
	return func(x int) int {
		n = n + x
		str += "a"
		fmt.Println("str=", str)
		return n
	}
}

func main() {
	f := AddUpper()
	// 反复调用f函数时,n和str是初始化一次,因此每次调用都进行累加
	fmt.Println(f(1)) //11
	fmt.Println(f(2)) //13
	fmt.Println(f(3)) //16
}

4. defer

为了在函数执行完毕后,及时释放资源(比如:关闭文件流、关闭数据库连接等),golang提供了defer(延时机制)。

在创建资源后,可以立即使用defer关闭资源。此时defer后面的逻辑中可以继续使用之前创建的资源,等函数结束后,会自动调用defer关闭资源,程序员不用再为什么时候关闭资源而烦心。

func sum(n1 int, n2 int) int {
	// 当执行到defer时,会将defer后面的语句压入到独立的栈(defer栈)
	// 当函数执行完毕后,再从defer栈按照先入后出的方式出栈
	defer fmt.Println("ok1 n1=", n1)
	defer fmt.Println("ok2 n2=", n2)

	n1 = 20 // 此时n1改为20,影响res的结果,但是之前defer栈中,n1还是10
	res := n1 + n2
	fmt.Println("ok3 res=", res)
	return res
}

func main() {

	res := sum(10, 20)
	fmt.Println("res=", res)
	// 运行结果
	// ok3 res= 40
	//ok2 n2= 20
	//ok1 n1= 10
	//res= 40
}

5. 错误处理机制

golang中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常。

func test() {
	defer func() {
		// recover是一个内置函数,可以捕获到异常
		err := recover()
		// 说明捕获到异常
		if err != nil {
			fmt.Println("err= ", err)
		}
	}()

	n1 := 10
	n2 := 0
	res := n1 / n2
	fmt.Println("res= ", res)
}

func main() {
	test()
	fmt.Println("main over")
}

自定义错误处理:

func readConf(name string) (err error) {
	if name == "config.ini" {
		return nil
	} else {
		return errors.New("读取失败...")
	}
}

func test() {
	err := readConf("config2.ini")
	if err != nil {
		// 如果读取发生错误,就输出这个错误,并终止程序
		panic(err)
	}
	fmt.Println("test ...")
}

func main() {
	test()
	fmt.Println("main over")
}

3. 数组

  • 数组可以存放多个同一类型的数据,在golang中,数组是值类型
  • 数组的地址可以用数组名来获取
  • 数组的地址和数组中第一个元素的地址相同
// 数组定义的几种方式
var arr1 [3]int = [3]int{1, 2, 3}
fmt.Println(arr1)
var arr2 = [3]int{5, 6, 7}
fmt.Println(arr2)
var arr3 = [...]int{8, 9, 10}
fmt.Println(arr3)
arr4 := [...]int{1: 100, 0: 200, 2: 300}
fmt.Println(arr4)

数组遍历:

// 方式1:常规for循环遍历

// 方式2:for-range遍历
func main() {
	arr := [...]int{1, 2, 3, 4, 5}
	for index, value := range arr {
		fmt.Printf("下标%v 的值为%v\n", index, value)
	}
}

数组属于值类型,在默认情况下是值传递,因此会进行值拷贝。(书组件不会相互影响)

func test(arr [3]int) {
	arr[0] = 88
}

func main() {
	arr := [3]int{11, 22, 33}
	test(arr)
	fmt.Println(arr) // 输出[11 22 33]
}

若想修改数组内的数据,可以采用引用传递(指针)

func test(arr *[3]int) {
	(*arr)[0] = 88
}

func main() {
	arr := [3]int{11, 22, 33}
	test(&arr)
	fmt.Println(arr) // 输出[88 22 33]
}

4. 切片

  • 切片是一个引用类型,遵守引用传递机制
  • 切片的长度是可以变化的,切片可以理解为是一个可以动态变化的数组
  • 修改切片中元素的值,被引用的数组中的值也同时改变
  • 切片可以继续切片
func main() {
	var intArr [5]int = [...]int{1, 22, 33, 55, 66}

	//声明一个切片
	// intArr[1:3]表示slice引用到intArr这个数组中下标1到3的元素(含头不含尾)
	slice := intArr[1:3]
	fmt.Println("intArr=", intArr) // [1 22 33 55 66]
	fmt.Println("slice=", slice)   // [22 33]
	fmt.Println("slice的元素个数=", len(slice))
	// 切片的容量,可变
	fmt.Println("slice的容量=", cap(slice))
}

切片定义方式:

// 第一种:引用已经创建好的数组

// 第二种:使用make创建,cap选填
// var 切片名 []type = make([]type, len, cap)

// 第三种:定义切片是,直接指定具体数组
// var slice []int = []int{1,2,3,4}

注意:

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[:]

切片扩容

func main() {
	var slice []int = []int{100, 200, 300}
	// 通过append追加具体元素
	slice = append(slice, 400, 500, 600)
	fmt.Println(slice)
	// 通过append追加切片
	slice = append(slice, slice...)
	fmt.Println(slice)
}

string与切片

func main() {
	str := "hello world"
	slice := str[6:]
	fmt.Println(slice) // world

	// 因为要替换中文,所以选择[]rune
	// 如果没有中文,也可以选择[]byte
	slice1 := []rune(str)
	slice1[0] = '北'
	str = string(slice1)
	fmt.Println(str) // 北ello world
}

5. 排序与查找

1. 冒泡排序

func BubbleSort(slice []int) []int {
	temp := 0
	for i := 0; i < len(slice)-1; i++ {
		for j := 0; j < len(slice)-1-i; j++ {
			if slice[j] > slice[j+1] {
				temp = slice[j]
				slice[j] = slice[j+1]
				slice[j+1] = temp
			}
		}
	}
	return slice
}

func main() {
	// 定义切片
	slice := []int{24, 69, 80, 57, 13}
	fmt.Println("排序前slice=", slice)

	slice = BubbleSort(slice)
	fmt.Println("排序后slice=", slice)
}

2. 二分查找(前提是有序数组)

func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int) {
	// 判断leftIndex是否大于rightIndex,如果大于,说明找不到
	if leftIndex > rightIndex {
		fmt.Println("找不到")
		return
	}

	// 先找到中间的下标
	middle := (leftIndex + rightIndex) / 2

	if (*arr)[middle] > findVal {
		BinaryFind(arr, leftIndex, middle-1, findVal)
	} else if (*arr)[middle] < findVal {
		BinaryFind(arr, middle+1, rightIndex, findVal)
	} else {
		fmt.Printf("找到了, 下标为%v\n", middle)
	}
}

func main() {
	// 定义数组
	arr := [6]int{1, 3, 5, 7, 9, 11}
	BinaryFind(&arr, 0, len(arr)-1, 13)
}

6. map

  • golang中的map,key可以是bool、数字、string、指针、channel
  • 通常key为int、string
  • slice、map、function不能作为key,因为没法用 == 判断
  • golang中没有办法一次删除所有的key,可以遍历删除;或者map = make(…), make一个新的,原来的等待GC回收
func main() {
	// 第一种
	var map1 map[int]string
	map1 = make(map[int]string)
	map1[1] = "aaa"
	fmt.Println(map1)

	// 第二种
	map2 := make(map[int]string)
	map2[1] = "bbb"
	fmt.Println(map2)

	// 第三种
	map3 := map[int]string{
		1: "ccc",
		2: "ddd",
	}
	fmt.Println(map3)
}
func main() {
	cityMap := make(map[string]string)
	cityMap["c1"] = "北京"
	cityMap["c2"] = "上海"
	cityMap["c3"] = "广州"
	fmt.Println("111--->", cityMap)

	cityMap["c3"] = "深圳" // 覆盖广州
	fmt.Println("222--->", cityMap)

	delete(cityMap, "c3") // 删除
	delete(cityMap, "c5") // 不存在key,也不会报错
	fmt.Println("333--->", cityMap)

	// 查找
	// 如果存在,res为true; 如果不存在,res为false
	val, res := cityMap["c2"]
	if res {
		fmt.Printf("有c2, 值为%v\n", val)
	} else {
		fmt.Println("没有c2")
	}

	// make一个新的空间,之前的map等待GC回收
	cityMap = make(map[string]string)
	fmt.Println(cityMap)
}

7. 结构体

  • golang支持面向对象变成特性,不是纯粹的OOP语言
  • golang的结构体和OOP的class同等地位
  • golang面向对象编程较为简单,没有继承、重载、构造函数、this指针等等
type Stu struct {
	Name string
	Age  int
	Sex  int
}

func main() {
	var stu1 Stu
	fmt.Println(stu1)
	stu1.Name = "Tom"
	stu1.Age = 20
	stu1.Sex = 1
	fmt.Println(stu1)
}
  • struct的每个字段上,可以写一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化
// `json:"name"`就是结构体的tag
type Stu struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	Sex  int    `json:"sex"`
}

func main() {
	var stu1 Stu
	stu1.Name = "Tom"
	stu1.Age = 20
	stu1.Sex = 1

	// 将stu序列化为 json格式字串
	jsonStr, err := json.Marshal(stu1)
	if err != nil {
		fmt.Println("json 处理错误 ", err)
	}
	fmt.Println("jsonStr", string(jsonStr))
}

8. 方法

golang中方法是作用在指定的数据类型上的(即:和指定的数据类型绑定), 因此,自定义类型都可以有方法,而不仅仅是struct

func (receiver type) methodName(参数列表) (返回值列表) {
    方法体
    return 返回值
}
// func (a A) test(){}表示A结构体有一个方法,名为test()
// test()是和A类型绑定的,只能通过A类型调用,不能直接调用或使用其他类型调用

type A struct {
    Num int
}

func (a A) test() {
    fmt.Println(a.Num)
}

func main() {
    var a A
    p.Num = 10
    p.test()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值