我是靠谱客的博主 高兴太阳,最近开发中收集的这篇文章主要介绍不到1000行,Go语言就算入门了吧基本规则读取输入与类型转换读取文本文件随机数包循环:for也能当while用函数与返回错误指针多模块下载三方包文档数组与切片可变参数map结构体接口与类型断言defer: go中的finallypanic与recovergo routine测试http服务接下来,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

作为已经掌握了 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() string {
	return "姓名:" + p.name + ",年龄:" + strconv.Itoa(int(p.age))
}

func main() {
	fmt.Println(Person{
		name: "寂寞",
		age:  18,
	})
}

defer: go中的finally

package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
)

func OpenFile(file string) (*os.File, error) {
	fmt.Println("打开文件", file)
	return os.Open(file)
}

func CloseFile(file *os.File) {
	fmt.Println("关闭文件")
	file.Close()
}

func main() {
	file, err := OpenFile("go.mod")
	if err != nil {
		log.Fatal(err)
	}
	// 无论如何都要调用关闭文件
	defer CloseFile(file)
	println("处理文件...")
	float, err := strconv.ParseFloat("hehe", 32)
	if err != nil {
		// 这样不能调用 关闭文件
		//log.Fatal(err)
		// 这样可以
		log.Println("出错了")
		return
	}
	println(float)
}

panic与recover

看起来像 try-catch,不过go语言维护者不建议使用,而是采用 if 和return去处理错误

package main

func revive() {
	recover()
}

func fine() {
	// 不能直接调用 defer recover()
	defer revive()
	panic("啊,我要死了")
	println("我愉快的生活")
}

func main() {
	println("人生开始")
	fine()
	println("人生继续")
	/*
		人生开始
		人生继续
	*/
}

go routine

简单的并发

package main

import (
	"fmt"
	"time"
)

func task(a int) {
	for i := 0; i < 1000; i++ {
		fmt.Print(a)
	}
}

func main() {
	go task(7)
	go task(8)
	time.Sleep(time.Second)
	fmt.Println("main结束")
}

goroutine返回值

上门的方法无法返回内容,如果想返回每个routine的值,要用到channel

package main

import (
	"fmt"
)

func sum(a int, b int, channel chan int) {
	s := a + b
	// 结果发送到通道
	channel <- s
}

func main() {
	sumChannel := make(chan int)
	go sum(1, 2, sumChannel)
	go sum(4, 5, sumChannel)
	fmt.Println("结果1:", <-sumChannel)
	fmt.Println("结果2:", <-sumChannel)
	fmt.Println("main结束")
}

构造channel时可以指定缓冲数量

make(chan int, 3)

测试

  • 测试文件名以 xxx_test.go结尾, 放在哪里都可以
  • 测试需引入 testing
  • 测试方法需以 Test开头,方法一般参数为 *testing.T

文件

.
├── sum.go
└── sum_test.go

sum.go

package sum

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

sum_test.go

package sum

import "testing"

func TestSum(t *testing.T) {
	s := Sum(1, 2)
	if s != 3 {
		t.Errorf("1+2=3,实际得到%d", s)
	}
}

func TestSumFailed(t *testing.T) {
	s := Sum(1, 2)
	t.Errorf("1+2=3,实际得到%d", s)
}

然后使用 go test测试

$ go test -v
=== RUN   TestSum
--- PASS: TestSum (0.00s)
=== RUN   TestSumFailed
    sum_test.go:14: 1+2=3,实际得到3
--- FAIL: TestSumFailed (0.00s)
FAIL
exit status 1
FAIL    00-test/sum     0.001s

http服务

go语言对http的支持简单友好

package main

import (
	"fmt"
	"log"
	"net/http"
)

func handleHello(writer http.ResponseWriter, req *http.Request) {
	fmt.Println("Access ", req.RequestURI)
	msg := []byte("hello jimo")
	_, err := writer.Write(msg)
	if err != nil {
		log.Fatal(err)
	}
}
func main() {
	http.HandleFunc("/hello", handleHello)
	err := http.ListenAndServe("localhost:8080", nil)
	log.Fatal(err)
}

接下来

刚刚开了个头,就结束了?不过,新的自虐之路才刚刚开始。

接下来,web和并发2个方向自然需要攻破。

最后

以上就是高兴太阳为你收集整理的不到1000行,Go语言就算入门了吧基本规则读取输入与类型转换读取文本文件随机数包循环:for也能当while用函数与返回错误指针多模块下载三方包文档数组与切片可变参数map结构体接口与类型断言defer: go中的finallypanic与recovergo routine测试http服务接下来的全部内容,希望文章能够帮你解决不到1000行,Go语言就算入门了吧基本规则读取输入与类型转换读取文本文件随机数包循环:for也能当while用函数与返回错误指针多模块下载三方包文档数组与切片可变参数map结构体接口与类型断言defer: go中的finallypanic与recovergo routine测试http服务接下来所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(69)

评论列表共有 0 条评论

立即
投稿
返回
顶部