Go 语言入门指南:基础语法和常用特性解析-上 | 青训营

86 阅读5分钟

Go 语言优势

  • 高性能和高并发:内部支持很便利,可以不用像Java、C++等编程语言需要第三方

库,只需要自身标准库或者基于标准库的第三方库。

  • 语法简单、学习曲线平缓:上手容易,语法简洁易懂易学,学起来难度跨度不大,无论是初学者还是已经学过其他编程语言的同学来说都比较容易学。
  • 丰富的标准库:标准库就可以大大满足平时需求,这会大大降低我们学习新的第三方库的时间成本,而且节省精力。
  • 完善的工具链:内置完整的单元测试框架、代码格式化、格式检查等等非常多的工具。
  • 静态链接:只需要拷贝编译后的唯一 一个可执行文件,不加任何东西就能部署运行;在线上的容器环境下运行,部署非常方便快捷。相比之下,像C++ ,则需要附加一堆文件才行,而且文本不正确的话还会崩溃;Java是需要附加一个庞大的 JRE 才能运行。
  • 快速编译:Go 语言拥有静态语言里几乎最快的编译速度。
  • 跨平台:能在VSCode、IDEA、Goland等各大平台运行,也能开发Android、iOS软件;甚至还能在路由器和树莓派里运行。
  • 垃圾回收:自带垃圾回收,和Java类似,写代码的时候不用考虑内存的分配释放。

Go 语言基础语法-上

变量的声明

​ 常见的变量包括字符串、整型、浮点型,在 Go 语言里,字符串是内置类型,所以可以直接通过 +号去拼接,那么,直接用 = 比较两个字符串也是可以的。

​ 至于运算符的优先级,和C/C++的很类似,不是很需要特殊记。

func main() {
	var a = "inital"	//第一种声明方式
	var b, c int = 1, 2 //第二种声明方式
	var d = true
        
	var e float64
	f := float32(e)  	//第三种声明方式

	g := a + "foo"
	fmt.Println(a, b, c, d, e, f)
	fmt.Println(g)
    
	const s string = "constant"
	const h = 500000000
	const i = 3e20 / h
	fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
  • 第一种声明方式是直接赋值,var 万能类型头,自动识别。

  • 第二种声明方式是 --- 前面写变量名, 后面写变量类型

  • 第三种声明方式:f := float32(e) --- 变量名 + 冒号等于 + 值

  • 常量:就是把 var 换成 const 就行了,同样的,golang 里的常量也是没有确定类型,会根据上下文自动确定类型。


if-else

func main() {

	if 7%2 == 0 {
		fmt.Println("7 is even")
	} else {
		fmt.Println("7 is odd")
	}

	if 8%4 == 0 {
		fmt.Println("8 is divisible by 4")
	}

	if num := 9; num < 0 {
		fmt.Println(num, "is negative")
	} else if num < 10 {
		fmt.Println(num, "has 1 digit")
	} else {
		fmt.Println(num, "has multiple digits")
	}
}
  • if 后的判断条件是不需要写括号括起来的,就算括起来,保存编译的时候编译器也会自动去掉。
  • if 后**必须跟大括号**,不能和 C/C++ 一样不带大括号写在同一行或者下一行

小tips: fmt.Println(),如果里边是多个参数,逗号隔开之后,打印输出的结果每两个参数之间会自动加一个空格,这一点和Java是不一样的。我觉得相同点相同记,不同点区分记,这样其实也挺快的记的。


for 循环

在 golang 里边,没有 while 和 do-while 循环,只有 for 一种循环。

func main() {
	i := 1
	for {
		fmt.Println("loop")
		break
	}
	for j := 7; j < 9; j++ {
		fmt.Println(j)
	}
	for n := 0; n < 5; n++ {
		if n%2 == 0 {
			continue
		}
		fmt.Println(n)
	}
	for i <= 3 {
		fmt.Println(i)
		i++
	}
}
  • 单独一个 for 跟着大括号代表一个死循环。
  • 比较熟悉的就是第二个循环体了,比较像 C/C++ 和 Java,但是和py 一样,golang 里的for 循环的条件是不需要加括号括起来的。需要注意的一点是 --- for 循环条件里的三部分,缺任何一个部分都可以

switch

func main() {
	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("2")
	case 3:
		fmt.Println("3")
	case 4, 5:
		fmt.Println("4 or 5")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("before noon")
	default:
		fmt.Println("after noon")
	}
}

这里golang 和其他几个语言最大的区别就是,每个 case 都不用自己写 break 关键字,golang 会自动帮我们加上。

相比其他语言,golang 里的 switch 更强大,可以使用任意的变量类型,比如字符串、结构体,甚至可以用来取代任意的 if-else 语句--- 在 switch 后不加变量,直接跟大括号,然后 case 后跟 if 原本的判断语句即可 。相比多个 if-else 更加通俗易懂。


数组

实际应用过程中很少直接使用数组,因为它的长度是固定的。大多会使用**切片。**

func main() {
	var a [5]int
	a[4] = 100	//golang 里数组下标同样也是从0开始
	fmt.Println(a[4], len(a))

	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println(b)

	var twoD [2][3]int
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d", twoD)
}

切片 slice

func main() {
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[2])
	fmt.Println("len:", len(s))

	s = append(s, "d")
	s = append(s, "e", "f")
	fmt.Println(s)

	c := make([]string, len(s))
	copy(c, s)
	fmt.Println(c)

    fmt.Println(s[2:5]) //[c d e]   下标 [2, 5)左闭右开区间
	fmt.Println(s[:5])  //[a b c d e]
	fmt.Println(s[2:])  //[c d e f]

	good := []string{"g", "o", "o", "d"}
	fmt.Println(good)
}
  • 相当于可变长度的数组,可以任意时刻去更改长度,内置操作也更丰富。

  • make 来创建一个切片,可以像给数组赋值一样挨个给切片元素赋值。

  • append 方法来增加元素,如果 append 的时候容量不够了会发生扩容,返回一个新的切片,所以必须要赋值回去。

  • 在用 make 创建一个 slice (切片)时,可以直接用 len() 获取已有切片或数组的长度当作第二个参数。

  • copy 方法,两个参数,作用是把后一个slice 的值挨个复制对应到前一个。

  • 和 py 不同的是不支持负数索引


map

func main() {
	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m)           // map[one:1 two:2]
	fmt.Println(len(m))      // 2
	fmt.Println(m["one"])    // 1
	fmt.Println(m["unknow"]) // 0

	r, ok := m["unknow"]	//作用是来接收map里的该key是否存在
	fmt.Println(r, ok) // 0 false

	delete(m, "one")

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}
  • 格式里边的 map[string]int ,中括号里边的是 key 的类型,后边的是 value 的类型。创建完之后可以通过方括号加key 对value赋值。
  • golang 的map 完全无序

range

func main() {

	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += num
		if num == 2 {
			fmt.Println("index:", i, "num:", num) 
            //index: 0 num: 2
		}
	}
	fmt.Println(sum) //9 --- 2+3+4

	m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v) // b B; a A
	}
	for k := range m {
		fmt.Println("key", k) //key a; key b
	}
}
  • 其实就是类似于增强for ,只不过稍有区别 :golang 的对于数组会返回两个值 --- 一个下标索引,一个对应的值;我们如果不想要索引,可以用下划线替代第一个 for 循环里的 i ;;;然后如果是 map 的话就好理解了,k-v键值对。