不到1000行,Go语言就算入门了吧

Posted 境悟初

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不到1000行,Go语言就算入门了吧相关的知识,希望对你有一定的参考价值。

作为已经掌握了 C、Java、Python、javascript等多种语言的我们,再学习个新语言,自然不需要重头开始。
建立在已有基础上学习一般都会更快,张三丰那种无招胜有招?抱歉,我目前还达不到。
不过即便如此,断断续续花个几小时把Go语言学会了,也是有诸多好处。毕竟作为一名资深Java开发者,有预感,Go语言会超越Java。
语言罢了,还都是GC语言,不过如此,确实简单。

下面是不到1000行的学习笔记,包含了Go的关键特点,入个门足矣。

基本规则

  • go是静态类型语言
  • 短变量声明: var a int = 1等价于a := 1
  • 公开访问的名称规则:大写开头就是公开访问的
  • 常用 go命令: got build xxx.go, go run xxx.go
  • if true{}或者 for i:=1;i<10;i++{} 后面的括号()是多余的
  • 导包有多层时用斜杠: import "math/rand"

读取输入与类型转换

package main

import (
	"bufio"
	"log"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	s, err := reader.ReadString('\\n')
	if err != nil {
		log.Fatal(err)
	}
	age, err := strconv.ParseInt(strings.TrimSpace(s), 10, 32)
	age2, err := strconv.Atoi(strings.TrimSpace(s))
	println("age=", age, ",age2=", age2)
}

读取文本文件

package main

import (
	"bufio"
	"log"
	"os"
)

func main() {
	file, err := os.Open("go.mod")
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		println(scanner.Text())
	}
	err = file.Close()
	if err != nil {
		log.Fatal(err)
	}
}

随机数包

package main

import (
    "math/rand"
    "time"
)

func main() {
    fixed := rand.Intn(100) + 1
    
    milli := time.Now().UnixMilli()
    rand.Seed(milli)
    randInt := rand.Intn(100) + 1
    println(fixed, randInt)
}

循环:for也能当while用

package main

func main() {
	for i := 0; i < 10; i++ {
		print(i, ",")
	}
	// while
	j := 1
	for j < 10 {
		print(j, ",")
		j++
	}

	for i := 0; i < 10; i++ {
		if i < 4 {
			break
		}
		if i > 3 {
			continue
		}
	}
}

函数与返回错误

多个返回值

package main

import "fmt"

func main() {
	a, err := area(3, 5)
	println("3*5=", a, err)
}

func area(len int, width int) (int, error) {
	if len < 0 || width < 0 {
		return 0, fmt.Errorf("错误参数:%d,%d", len, width)
	}
	return len * width, nil
}

指针

package main

import "fmt"

func main() {
	a := 2
	p := myPointer(&a)
	fmt.Println(a, p, *p) // 4 0xc0000200e0 10
}

func myPointer(number *int) *int {
	*number *= 2
	a := 10
	return &a
}

多模块

通过 go init mod 01 初始化,然后实现以下结构。

├── go.mod
├── hello
│   ├── chinese
│   │   ├── chinese.go
│   │   ├── dialect
│   │   │   ├── dialect.go
│   │   │   └── lang.go
│   │   └── putonghua
│   │       └── putonghua.go
│   └── english
│       └── english.go
├── main.go
└── README.md

go.mod

module 01

go 1.17

chinese.go

package chinese

func Hello() {
	println("你好")
}

dialect.go

package dialect

func Hello() {
	siChuanHua()
	dongBeiHua()
}

lang.go

package dialect

func siChuanHua() {
	println("瓜娃子")
}

func dongBeiHua() {
	println("你瞅啥")
}

putonghua.go

package putonghua

func Hello() {
	println("您好呀")
}

english.go

package english

import "fmt"

func Hello() {
	fmt.Println("Hello")
}

main.go
开始引用这些包, 从mod名开始。

package main

import (
	"01/hello/chinese"
	"01/hello/chinese/dialect"
	"01/hello/chinese/putonghua"
	"01/hello/english"
)

func main() {
	english.Hello()
	chinese.Hello()
	putonghua.Hello()
	dialect.Hello()
}

下载三方包

设置代理

go env -w GOPROXY=https://goproxy.cn,direct
go get github.com/xxx/xxx

自己发布包

建一个新仓库,我的示例:https://gitee.com/halfgold/go-test-mod

版本号通过打 git tag来定。

然后在项目里引用:

go.mod

module 03

go 1.17

require (
	gitee.com/halfgold/go-mod-test/sing v0.0.1
)

main.go

package main

import "gitee.com/halfgold/go-mod-test/sing"

func main() {
	sing.Song()
}

不过很不幸,gitee测试会弹出输入密码的报错:

$ go get gitee.com/halfgold/go-mod-test/sing@v0.0.1
go: gitee.com/halfgold/go-mod-test/sing@v0.0.1: reading gitee.com/halfgold/go-mod-test/sing/sing/go.mod at revision sing/v0.0.1: git ls-remote -q origin in /home/jack/go/pkg/mod/cache/vcs/522776bd8367d68a9aaaca87075c70c40767d41e0e33cc6e07bd270c38fbf6ff: exit status 128:
        fatal: could not read Username for 'https://gitee.com': terminal prompts disabled
Confirm the import path was entered correctly.
If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.

既然这样,我就输以下嘛:

$ export GIT_TERMINAL_PROMPT=1
jack@jack-f1:~/workspace/git/go-in/03-go-get$ go get gitee.com/halfgold/go-mod-test/sing@v0.0.1
Username for 'https://gitee.com': halfgold
Password for 'https://halfgold@gitee.com': 
go: gitee.com/halfgold/go-mod-test/sing@v0.0.1: reading gitee.com/halfgold/go-mod-test/sing/sing/go.mod at revision sing/v0.0.1: git ls-remote -q origin in /home/jack/go/pkg/mod/cache/vcs/522776bd8367d68a9aaaca87075c70c40767d41e0e33cc6e07bd270c38fbf6ff: exit status 128:
        remote: 404 not found!
        fatal: 仓库 'https://gitee.com/halfgold/go-mod-test/' 未找到

结果他居然说找不到。

这就伤心了,先存个档。

文档

使用 go doc xxx查看文档,比如 go doc fmt.

更详细的:

$ go doc fmt Println
package fmt // import "fmt"

func Println(a ...interface{}) (n int, err error)
    Println formats using the default formats for its operands and writes to
    standard output. Spaces are always added between operands and a newline is
    appended. It returns the number of bytes written and any write error
    encountered.

我们也要写文档

我们初始化的04项目写上:包注释和方法注释:

想必看出来规则了:

  • 包注释:Package 包名 注释内容
  • 方法注释:方法名 注释内容
// Package hello 这个包很牛逼
package hello

// Hello 说你好的方法
func Hello() {
	println("你好")
}

展示文档:

04-go-doc$ go doc
package hello // import "04"

Package hello 这个包很牛逼

func Hello()

04-go-doc$ go doc hello
package hello // import "04"

func Hello()
    Hello 说你好的方法

安装go的工具集后,可以在网页上展示文档:

sudo apt install golang-golang-x-tools

# 然后起服务
$ godoc -http=:8080

在页面上访问: localhost:8080/pkg,还能看到自己的包

数组与切片

先看数组的声明和遍历

package main

func main() {
	var arr = [3]string{"a", "b", "c"}
	arr[0] = "a1"
	for i, s := range arr {
		println(i, s)
	}
	var colors [3]int
	colors[0] = 1
	println("长度:", len(colors))
}

再看切片,可以利用 append 方法延长数组切片

package main

import "fmt"

func main() {
	// 数组
	var arr [3]int
	// 切片
	var slice = make([]int, 3)
	// 预填充
	slice1 := []int{1, 2, 3}

	slice2 := slice1[0:2]
	slice3 := slice1[1:]
	// 修改依赖的底层元素,切片也跟着改变
	slice1[1] = 22
	fmt.Println(slice3)
	// 追加新元素,不会影响原有切片
	slice31 := append(slice3, 4, 5)
	fmt.Println(slice31, slice1)
	slice4 := slice1[:3]
	fmt.Println(arr, slice, slice1, slice2, slice3, slice4)
}

可变参数

注意切片传输的写法

package main

func main() {
	println(sum(1, 2, 3))
	
	intSlice := []int{1, 2, 3}
	println(sum(intSlice...))
}

func sum(nums ...int) int {
	sum := 0
	for _, n := range nums {
		sum += n
	}
	return sum
}

map

package main

import "fmt"

func main() {
	// 初始化
	var m map[string]int
	m = make(map[string]int)
	m["jimo"] = 18
	fmt.Println(m)

	// map字面量
	m = map[string]int{"hehe": 20, "lily": 19}
	fmt.Println(m)

	// 区分是否设了值
	age, hasSet := m["hehe"]
	age1, hasSet1 := m["jimo"]
	fmt.Println(age, hasSet, age1, hasSet1) // 20 true 0 false

	// 删除
	delete(m, "hehe")
	fmt.Println(m)

	// 遍历
	for k, v := range m {
		fmt.Println(k, v)
	}
}

结构体

注意大对象使用指针传参,而不是值传递

package main

import "fmt"

type Person struct {
	name string
	age  int8
}

func main() {
	var p Person
	p.name = "jimo"
	p.age = 18
	fmt.Println(p)
	growUp(&p)
	fmt.Println(p) // 19

	growUpFake(p)
	fmt.Println(p) // 19

	// 字面量赋值
	hehe := Person{
		name: "hehe", age: 20,
	}
	fmt.Println(hehe)
}

func growUpFake(u Person) {
	u.age += 1
}

func growUp(u *Person) {
	u.age += 1
}

自定义基本类型

让基本类型变得有含义

package main

type KM float32
type M float32

func main() {
	println(KM(1), M(1000))
	dist := KM(1.1)
	dist += toKM(M(2345)) // 可以做计算
	println(dist)
}

func toKM(m M) KM {
	return KM(m / 1000)
}

方法和函数

不过是一丘之貉

package main

type KM float32
type M float32

func main() {
	m := M(123)
	println(m.toKM())
}

func (m M) toKM() KM {
	return KM(m / 1000)
}

Getter和Setter方法

注意使用指针作为参数

package main

import (
	"errors"
	"fmt"
	"log"
	"strconv"
)

type Person struct {
	name string
	age  int8
}

func (p *Person) SetName(name string) {
	p.name = name
}

func (p *Person) SetAge(age int8) error {
	if age <= 0 {
		return errors.New("不合法的年龄:" + strconv.Itoa(int(age)))
	}
	p.age = age
	return nil
}

func (p *Person) Age() int8 {
	return p.age
}

func main() {
	p := Person{}
	p.SetAge(18)
	fmt.Println(p.Age())

	err := p.SetAge(-1)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(p)
}

接口与类型断言

go的接口实现居然是通过方法名来匹配的

package main

import "fmt"

type Animal interface {
	Run()
}

type Dog struct {
}

// Run 通过方法继承
func (d Dog) Run() {
	println("小狗跑了")
}

type Cat struct {
}

func (c Cat) Run() {
	println("小猫跑了")
}

type Student struct{}

func main() {
	animalRun(Dog{})
	animalRun(Cat{})

	// 接口类型断言
	var a Animal = Dog{}
	d, ok := a.(Dog)
	fmt.Println(d, ok)

	//s, ok := a.(Student)
	//println(s, ok)
}

func animalRun(a Animal) {
	a.Run()
}

那空接口怎么实现?

不用实现,所有的类都是其实例

package main

import (
	"fmt"
)

// 空接口
type nothing interface {
}

type Person struct {
	name string
	age  int8
}

func acceptAnything(a nothing) {
	fmt.Println(a)
	p, ok := a.(Person)
	if ok {
		fmt.Println("is Person: " + p.name)
	}
}

func main() {
	acceptAnything(Person{
		name: "寂寞",
		age:  18,
	})
	acceptAnything(1.2)
	acceptAnything(1)
	acceptAnything("hehe")
}

error接口

气接口定义为:

type error interface {
	Error() string
}
package main

import "fmt"

type MyError string

func (e MyError) Error() string {
	return string(e)
}

func main() {
	var err error
	err = MyError("我的错误实现")
	fmt.Println(err)
}

Stringer接口:重写toString方法

type Stringer interface {
	String() string
}
package main

import (
	"fmt"
	"strconv"
)

type Person struct {
	name string
	age  int8
}

func (p Person) String以上是关于不到1000行,Go语言就算入门了吧的主要内容,如果未能解决你的问题,请参考以下文章

[Cobra]Go语言的命令行编写工具的快速入门

Go语言---小白入门-命令行库Cobra的使用

Golang 入门 : 创建第一个Go程序

只用200行Go代码写一个自己的区块链!

搞定 Go 语言,不会这些可不行

Go语言200行写区块链源代码分析