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

58 阅读6分钟

1 Go语言基础语法

1.1 数组

数组是具有编号且长度固定的元素序列。对于数组,可以很方便地取特定索引的值或者往特定索引取存储值,方便打印整个数组。但由于数组的长度固定,因此在真实业务代码里很少直接使用数组。

示例代码:

package main

import "fmt"

func main() {

	var a [5]int
	a[4] = 100
	fmt.Println("get:", a[2])
	fmt.Println("len:", 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)
}

1.2 切片

由于数组长度固定,而切片不同于数组可以任意更改长度,具有更丰富的操作,比如可以用make来创建切片,可以像数组一样去取值,还可以利用append来追加元素,因此在真实业务代码里往往使用切片。

切片特点:

  • 使用append追加元素,必须把 append的结果赋值为原数组。
  • 在执行append操作时,如果容量不够,会扩容并且返回新的切片。
  • 不同于python,切片不支持负数索引。

示例代码:

package main

import "fmt"

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]) 
	fmt.Println(s[:5])  
	fmt.Println(s[2:])  

	good := []string{"g", "o", "o", "d"}
	fmt.Println(good) 
}

1.3 map(哈希/字典)

Go语言中的map与其他编程语言里面的哈希或者字典相同,是实际使用过程中最频繁用到的数据结构。

map的创建与删除:make创建空map会需要两个类型:第一个是key类型,数据结构是string,第二个是value类型,数据结构是int。map创建成功后可以从其中存储或者取出键值对,也可以用delete从map里删除键值对。

map特点:

  • golang的map是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。

示例代码:

package main

import "fmt"

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

	r, ok := m["unknow"]
	fmt.Println(r, ok) 

	delete(m, "one")

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}

1.4 range遍历

对于切片或者map,可以用range来快速遍历,range遍历的时候,对于数组会返回两个值 第一个是索引,第二个是对应位置的值。如果不需要索引,可以用下划线来忽略。

示例代码:

package main

import "fmt"

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) 
		}
	}
	fmt.Println(sum) 

	m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v) 
	}
	for k := range m {
		fmt.Println("key", k) 
	}
}

1.5 函数

特点:

  • 与其他很多语言不同,变量类型是后置的。
  • Golang里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个值是真正的返回结果,第二个值是一个错误信息。

示例代码(实现两个变量相加的函数):

package main

import "fmt"

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

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

func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

func main() {
	res := add(1, 2)
	fmt.Println(res) // 3

	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
}

1.6 结构体

结构体是带类型的字段的集合。实际使用中,可以用结构体的名称去初始化结构体变量,构造的时候需要传入每个字段的初始值,也可以用键值对的方式指定初始值,只对一部分宇字段进行初始化。

示例代码:

package main

import "fmt"

type user struct {
	name     string
	password string
}

func main() {
	a := user{name: "wang", password: "1024"}
	b := user{"wang", "1024"}
	c := user{name: "wang"}
	c.password = "1024"
	var d user
	d.name = "wang"
	d.password = "1024"

	fmt.Println(a, b, c, d)                 {wang 1024}
	fmt.Println(checkPassword(a, "haha"))  
	fmt.Println(checkPassword2(&a, "haha"))
}

func checkPassword(u user, password string) bool {
	return u.password == password
}

func checkPassword2(u *user, password string) bool {
	return u.password == password
}

1.7 错误处理

在go语言里,往往通过使用一个单独的返回值来传递错误信息完成错误处理。不同于Java处理错误,go语言对错误的处理方式,能够很清晰地知道哪个函数返回了错误,并且能用简单的if else来处理错误。

函数错误处理方式:

  • 针对函数,可以在函数的返回值类型里后面加一个error,代表这个函数可能会返回错误。
  • 在函数实现的时候,return需要同时return两个值,如果出现错误,那么可以return nil和一个error。如果没有出现错误,那么返回原本的结果和nil。

示例代码:

package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

func main() {
	u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name)

	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err) 
		return
	} else {
		fmt.Println(u.name)
	}
}

2 Go语言常用特性解析

Go语言是一种新兴的编程语言,由谷歌公司在2009年开发并发布。它具有简单、快速、可靠和可维护等特点,被广泛应用于分布式系统、网络编程、微服务和云计算等领域。下面详细介绍一下Go语言的一些常用特性,并给出对应示例代码。

2.1 并发支持

Go语言支持并发编程,提供了原子操作、通道和锁等相关的API,可以方便地实现并发控制和数据同步。它的并发模型是基于管道和通道实现的,可以有效地避免线程同步的性能问题。

例如,下面是一个使用Go语言实现的简单并发程序,用于打印九九乘法表:

package main

import (
    "fmt"
    "sync"
)

func printMatrix wg *sync.WaitGroup
    wg := &sync.WaitGroup{}
    for i := 0; i < 9; i++ {
        for j := 0; j < 9; j++ {
            fmt.Printf("%d x %d = %d
", i, j, i*j)
            wg.Add(1)
        }
    }
    go func() {
        wg.Wait()
        fmt.Println()
    }()
    fmt.Println()
)

func main() {
    printMatrix(nil)
}

2.2 接口编程

Go语言支持接口编程,可以方便地实现类型之间的组合和聚合。接口定义一个空的方法集合,它不包含任何实现,只是声明了一组方法的名字和参数列表。任何实现了这些方法的类型都可以被看作是该接口的实例。

例如,下面是一个使用Go语言实现的简单接口程序,用于计算两个整数之和:

package main

import "fmt"

type Adder interface {
    Add(int, int) int
}

type Sum struct{}

func (s Sum) Add(a, b int) int {
    return a + b
}

func main() {
    adder := Sum{}
    fmt.Println(adder.Add(1, 2)) 
    fmt.Println(adder.Add(3, 4))
}

2.3 泛型编程

Go语言支持泛型编程,可以在定义类型时使用通配符*T来表示任意类型的指针。这种编程方式可以避免硬编码,提高代码的灵活性和可重用性。

例如,下面是一个使用Go语言实现的简单泛型程序,用于实现一个简单的链表:

package main

import "fmt"

type Node struct {
    Val interface{}
    Next *Node
}

func NewNode(val interface{}) *Node {
    return &Node{Val: val, Next: nil}
}

func main() {
    head := NewNode(1)
    tail := head
    for i := 2; i < 10; i++ {
        tail.Next = NewNode(i)
        tail = tail.Next
    }
    fmt.Println(head.Val) 
    fmt.Println(tail.Next.Val) 
}

3 总结

本文介绍了Go语言的基础语法,主要包括数组、切片、map、range遍历、函数、结构体和错误处理等具体使用,并针对Go语言并发支持、接口编程和泛型编程等特性进行分析通过真实案例说明上述特性。

本文为个人见解,如有错误欢迎指正!