Go语言笔记-基础篇&泛型篇

视频(P1-P49):【狂神说】Go语言零基础学习视频通俗易懂

1. 安装

去 Go语言中文网下载安装包,一路下一步。然后配置环境变量 GOROOTGOPATH

GOROOT=D:\env\Go # Go的安装目录
GOPATH=D:\env\GOWorks # Go项目存储的路径

GOPATH 里面需要包含 src, pkg, bin 三个目录

2. Hello World

package main

import "fmt"

func main() {
	fmt.Println("Hello World")
}
  1. 一个程序只能有一个main包,用package关键字

  2. 需要 导入 fmt

  3. 写一个main函数(主入口)

  4. go run hello.go 来运行

如果在GoLand里报错,可以用命令行输入后解决

go env -w GO111MODULE=off

3. 注释

package main

import "fmt"
// 单行注释
/* 
多行注释
多行注释
*/
func main() {
	fmt.Println("Hello World")
}

4. 变量

变量定义:var name type,变量会有缺省值

image-20221111202802870

// var 变量名 变量类型 = 值
var name string = "kuangshen"
name = "zhangsan"

多个变量定义:

var (
	name string
	age int
	addr string
)

fmt.Println(name, age, addr)

var a,b,c int

变量应为驼峰命名法

变量赋值

var (
	name string
	age int
	addr string
)
name = "kuangshen"
age = 25
addr = "zhongguo"

fmt.Println(name, age, addr)
var a,b,c int

短变量声明并初始化

  • 定义变量的时候必须初始化
name := "kuangshen"
age := 18

fmt.Println(name, age)

打印变量的类型:

name := "kuangshen"
age := 18

fmt.Printf("%T,%T", name, age)

获取变量的地址:

var num int
num = 100
fmt.Printf("num: %d, 内存地址: %p", num, &num)

// num: 100, 内存地址: 0xc00001c088

Go语言变量变换

var a int = 100
var b int = 200

b, a = a, b
// 200 100

匿名变量 _

package main

import "fmt"

func test() (int, int) {
	return 100, 200
}
func main() {
	a, _ := test() // 用 _接收,方便废弃
	fmt.Println(a)
}

变量的作用域:

  • 全局变量:放置在函数之外的
  • 局部变量:放置在函数之内的

如果全局变量和局部变量同名,在局部变量所在作用域使用时,取到的是局部变量

5. 常量

定义方式:const name [type] = value

类型可以自动推导,无需声明类型

多个常量定义:

const a,b,c = 3.14, "abc", 110

iota,特殊常量,go语言的常量计数器

package main

import "fmt"

func main() {
	const (
		a = iota
		b = iota
		c = iota
	)

	fmt.Println(a, b, c)
}
// 0 1 2

第一个定义 iota,后面不赋值默认为iota,其他值同理

package main

import "fmt"

func main() {
	const (
		a = iota
		b
		c
		d = "haha"
		e
		f = 100
		g
		h = iota
		i
	)
    const (
    	j = iota
        k
    )
	fmt.Println(a, b, c, d, e, f, g, h, i)
    // 0 1 2 haha haha 100 100 7 8
    fmt.Println(j, k)
    // 0 1

}

6. 数据类型

6.1 布尔型 bool

true or false

6.2 数字型

image-20221111205808705

image-20221111205925043

6.3 字符型

单引号字符,双引号字符串

7. 数据类型转换

在Go里不存在隐性类型转换,所有的转换必须是显式

转换后的变量 := 要转换的类型(变量)
package main

import "fmt"

func main() {
	a := 3
	b := 5.0
	fmt.Printf("%T ", a)
	fmt.Printf("%T ", b)

	// 需求:将int类型的a转换为 float64类型 类型转换
	c := float64(a)
	d := int(b)
	
	fmt.Printf("%T ", c)
	fmt.Printf("%T ", d)

}

类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将int16转换为int32)。当从一个取值范围较大的类型转换到取值范围较小的类型时(将int32转换为int16或将float32转换为int),会发生精度丢失(截断)的情况.

8. 算数运算符

8.1 算数运算符

image-20221111210959504

重点:++、–是语句而不是表达式!

8.2 关系运算符

image-20221111211100908

关系运算符表达式的结果都是bool

8.3 逻辑运算符

image-20221111211224926

逻辑运算符表达式的结果都是bool

8.4 位运算

image-20221111211322811

8.5 赋值运算符

image-20221111211359136

9. 键盘输入输出

fmt.Scanf()fmt.Scan()fmt.Scanln()

package main

import "fmt"

func main() {
	var x int
	var y float64
	// 定义了两个变量
	fmt.Println("请输入两个数1.整数2.浮点数")
	fmt.Scanln(&x, &y)
	fmt.Println(x, y)
}

/*
请输入两个数1.整数2.浮点数
1 3.14
1 3.14
*/

/*
请输入两个数1.整数2.浮点数
1 3.14
1 3.14
*/




## 10. 流程控制

### 10.1 if

```go
if condition {

} else if condition2 {

} else {

}

10.2 switch

默认switch每个case是不需要break的(不像其他语句)

package main

import "fmt"

func main() {
	var score int = 90

	switch score {
	case 90:
		fmt.Println("A")
	case 80:
		fmt.Println("B")
	case 50, 60, 7:
		fmt.Println("C")
	default:
		fmt.Println("D")
	}
	// 默认为 bool = true
	switch {
	case false:
		fmt.Println("false")
	case true:
		fmt.Println("true")
	default:
		fmt.Println("default")
	}
}
// A
// true

穿透 fallthrough

package main

import "fmt"

func main() {
	a := false
	switch a {
	case false:
		fmt.Println("1. case 条件为 false")
		fallthrough
	case true:
		fmt.Println("2. case 条件为 true")
	}
}
// 1. case 条件为 false
// 2. case 条件为 true

10.3 select

10.4 channel

11. for循环

11.1 for 起始;循环条件;控制变量

package main

import "fmt"

func main() {
	for i := 1; i < 10; i++ {
		fmt.Print(i, ",")
	}
}
// 1,2,3,4,5,6,7,8,9,

11.2 for ;循环条件;

同其他语言的 while

11.3 for {}

for true {}

12. string的用法

Go语言中,字符串是字节的切片,是Unicode兼容的,并且UTF8

获取子字符串的长度:len(str)

string 是不能修改的

str := "hello,xuexiangban"
fmt.Println(str)

// 字符串的长度
fmt.Println("字符串的长度为: ", len(str))

// 获取指定的字节
fmt.Printf("%c\n", str[0])

两种方式遍历字符串

// str.for
// for
for i := 0; i < len(str); i++ {
	fmt.Printf("%c", str[i])
}

// for range
for i, v := range str {
	fmt.Print(i)
	fmt.Printf("%c", v)
}

13. 函数

func 函数名([参数列表]) [返回值列表] {
	函数体
}
package main

import "fmt"

func main() {
	printInfo()
	myPrint("haha")
	fmt.Println(add2(1, 2))
	x, y := swap("学相伴", "狂神说")
	fmt.Println(x, y)

}

// 无参无返回值
func printInfo() {
	fmt.Println("printInfo")
}

// 有参的函数
func myPrint(str string) {
	fmt.Println(str)
}

// 有一个返回值
func add2(a, b int) int {
	return a + b
}

// 有多个返回值的函数
func swap(a, b string) (string, string) {
	return b, a
}

可变参数:…

package main

import "fmt"

func main() {
	fmt.Println(getSum(1, 2, 3))
}

func getSum(nums ...int) int {
	sum := 0
	for i := range nums {
		sum += i
	}
	return sum
}

参数传递

  • 值类型的数据:操作数据本身,intstringboolarray
  • 引用数据类型:操作的是数据的地址:slicemapchan

值传递:传递的是数据的副本

package main

import "fmt"

func main() {
	// 值传递
	arr := [4]int{1, 2, 3, 4}
	fmt.Println(arr)
	update(arr)
	fmt.Println("调用后修改的数据", arr)
}

func update(arr2 [4]int) {
	fmt.Println("arr2接收时的数据", arr2)
	arr2[0] = 100
	fmt.Println("arr2接收后的数据", arr2)
}

/*
[1 2 3 4]
arr2接收时的数据 [1 2 3 4]
arr2接收后的数据 [100 2 3 4]
调用后修改的数据 [1 2 3 4]
*/

引用传递

切片,可以扩容的数组

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4}
	update2(s1)
	fmt.Println("调用后的数据", s1)
}

func update2(s2 []int) {
	fmt.Println("传递的数据", s2)
	s2[0] = 100
	fmt.Println("修改后的数据", s2)
}
/*
传递的数据 [1 2 3 4]
修改后的数据 [100 2 3 4]
调用后的数据 [100 2 3 4]
*/

14. defer

defer语义:推迟、延迟

package main

import "fmt"

func main() {
	f(" 1 ")
	fmt.Print(" 2 ")
	defer f(" 3 ")
	fmt.Print(" 4 ")
	defer f(" 5 ")
	fmt.Print(" 6 ")
	defer f(" 7 ")
}

func f(s string) {
	fmt.Printf(" %s ", s)
}
/*
  1   2  4  6   7    5    3
*/

defer如果传参,虽然延迟执行,但是传参是已经执行的状态

package main

import "fmt"

func main() {
	a := 10
	fmt.Println("a=", a)
	defer f(a) // 此时参数已经传进去了!
	a++
	fmt.Println("end a=", a)
}

func f(s int) {
	fmt.Printf(" %d ", s)
}
/*
a= 10
end a= 11
 10
*/

15. 函数的本质

  1. 函数本身就是一种类型
  2. 函数的类型是它的函数原型
package main

import "fmt"

func main() {
	fmt.Printf("%T", f1)
}
func f1(a, b int) int {
	return a + b
}
/*
func(int, int) int
*/
  1. 函数加上() 为调用,不加() 以变量形式存在
package main

import "fmt"

func main() {
	var f5 func(int, int) int
	f5 = f1
	fmt.Println(f5)
	fmt.Println(f1)
	f5(1, 2)
}
func f1(a, b int) int {
	return a + b
}
/*
0x67e560
0x67e560
*/

函数在Go语言中是复合类型,可以看做是一种特殊的变量。

函数名():调用返回结果

函数名:指向函数体的内存地址,一种特殊类型的指针变量

匿名函数

package main

import "fmt"

func main() {
	func(a, b int) {
		fmt.Print(a, b)
		fmt.Println("我是f3")
	}(1, 2)
}
/*
1 2我是f3
*/

回调参数

package main

import "fmt"

func main() {
	r1 := add(1, 2)
	fmt.Println(r1)

	r2 := oper(3, 4, add)
	fmt.Println(r2)

	r3 := oper(8, 4, sub)
	fmt.Println(r3)

	r4 := oper(8, 4, func(a int, b int) int {
		if b == 0 {
			fmt.Println("除数不能为0")
			return 0
		}
		return a / b
	})
	fmt.Println(r4)
}

// 高阶函数
func oper(a, b int, fun func(int, int) int) int {
	return fun(a, b)
}

func add(a, b int) int {
	return a + b
}

func sub(a, b int) int {
	return a - b
}
/*
3
7
4
2
*/

闭包

一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。这个内层函数和外层函数的局部变量,统称为闭包结构

局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁,但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用

package main

import "fmt"

func main() {
	r1 := increment()
	fmt.Println(r1)
	v1 := r1()
	fmt.Println(v1)
	v2 := r1()
	fmt.Println(v2)

}

func increment() func() int {
	i := 0
	fun := func() int {
		i++
		return i
	}
	return fun
}
/*
0x102e5e0
1
2
*/

16. 泛型

16.1. 什么是泛型

package main

import "fmt"

func main() {
	strs := []string{"xuexiangban", "kuangshen"}
	printArray(strs)
}

// []T 形式类型, 实际类型
/*

内置的泛型类型: any 和 comparable
- any: 表示go里面所有的内置基本类型,等价于 interface{}
- comparable: 表示go里面所有内置的可比较类型:int, uint, float, bool, struct, 指针

*/
func printArray[T string | int](arr []T) {
	// 类型断言 x.(T)
	for _, a := range arr {
		fmt.Print(a)
	}
}

泛型减少重复代码并提高类型安全性。

16.2. 泛型类型

type slice[T int|float32|float64] []T
  • T就是类型形参,类似占位符
  • int|float32|float64 是类型约束,告诉编译器,只能接受int或float32或float64这三种类型的实参
  • 中括号里的内容为类型形参列表
package main

import "fmt"

func main() {
	type Slice[T int | float64 | float32] []T

	var a Slice[int] = []int{1, 2, 3}
	var b Slice[float32] = []float32{1, 2, 3}
	var c Slice[float64] = []float64{1, 2, 3}
	fmt.Print(a)
	fmt.Print(b)
	fmt.Print(c)
}

这里b可以赋值给a吗?不可以。

下面是map的泛型示例:

	type MyMap[KEY int | string, VALUE float64] map[KEY]VALUE
	var m1 MyMap[string, float64] = map[string]float64{
		"go":   9.9,
		"java": 9.0,
	}

	fmt.Print(m1)

一种特殊泛型:

type Wow[T int|string] int;
	var a Wow[int] = 123 // OK
	var b Wow[string] = 123 // OK
	var c Wow[string] = "123" // ERROR

16.3 泛型receiver

package main

import "fmt"

type MySlice[T int | float64] []T

// 给泛型添加方法
func (s MySlice[T]) Sum() T {
	var sum T
	for _, v := range s {
		sum += v
	}
	return sum
}

func main() {
	var s MySlice[int] = []int{1, 2, 3, 4}
	fmt.Print(s.Sum())

	var f MySlice[float64] = []float64{1.0, 2.1, 3.3, 4.5}
	fmt.Print(f.Sum())

}

16.4 泛型函数

package main

import "fmt"

func Add[T int | float64 | string](a T, b T) T {
	return a + b
}

func main() {
	res := Add[int](1, 2)
	fmt.Print(res)
}

16.5 自定义泛型约束

package main

type MyInt interface {
	int | int8 | int16 | int32 | int64
}

func GetMaxNum[T MyInt](a, b T) T {
	if a > b {
		return a
	} else {
		return b
	}

}

func main() {
	var a int = 10
	var b int = 20

	GetMaxNum(a, b)
}

如果我们创建了一个 int 类型的别名类型 intAAA,这时候如果MyInt里没有intAAA,就会报错。

解决方案1:把intAAA加到MyInt的定义里。
解决方案2:在int的左边加一个~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

okfang616

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值