0 前言

一周之内要完成的任务。
在这里插入图片描述
本文目前不做过多的知识介绍,一是因为赶时间完成任务,二是对于有编程基础的人来说,不用事无巨细的都记下来,只要学学该语言的设计思想,总结一下其特别的地方和用法,差不多就入门了。

但是这篇文章开在这里,留给以后在学习使用Go语言时,记录一些不会的基础的知识。

1. 数据类型


2. 函数


1. 函数结构

func func_name(s string, a int) (int, int) {   # 这一行是函数签名

}

2. 不定参函数

// 计算任意多个整数的和并返回
func sumNumbers(nums...int) int {
	total := 0
	for _, x := range nums {
		total += x
	}
	return total
]
...

3. 具名返回值

4. 将函数作为值传递

本质上,Go将函数视为一种类型,因此可将函数赋给变量

func main() {
	fn := func() {
		fmt.Println("xxx")
	}
	fn()
}

既然函数是一种类型,可以赋给变量,那么,也可以作为参数,再传给其它函数


func anoFunc(t func() string) string {
	return t()
}

func main() {
	fn := func() string {
		return "xxx"
	}
	fmt.Println(anoFunc(fn))
}

有一点要注意,就是参数中,函数的返回类型,要与接收函数的参数类型一致,这很好理解,就像MR中,Map的输出格式肯定是和Reduce的输入格式一样的,不然怎么对接的上。

5. 其它地方

  1. 函数声明后,编译器就记录了其签名,并根据签名,检查调用时该函数的参数数量和类型是否正确;

  2. 不允许函数重载(overload);

3. 控制结构


3.1 for range用法
for each
for pos, char := range nums {
	// pos = [0, 1, 2,...]
	// char = [values1, values2,...]
}
3.2 defer用法

用于在外部函数都运行结束后,再运行的语句。一些应用场景如:在读取文件后将其关闭,需要在某项工作完成后,执行特定的函数时使用。如果有多条defer语句,则按照出现顺序相反的来执行。

4. 数组、切片(Slice)与映射(Map)


var nums [n]int
var sb = make([]string, 2)append
// 1. 添加

// 2. 删除

// 3. 复制

var age = make(map [string] int)delete(age,"key")

注意,delete方法不能用户切片和数组,没有专门用于从切片中删除元素的函数,用内置函数append来完成。

5. 结构体(Struct)与方法(Method)


5.1 结构体创建方式
type Movie sruct {
	Name 	string
	Rating	float32
}

// 1. 变量声明方式
var m Movie
m.Name = "xxx"
m.Rating = 0.99

// 2.关键字 new
m := new(Movie)
m.Name = "xx"
m.Rating = 0.99


// 3. 简短变量赋值方式和
m := Movie{
	Name: "xxx",
	Rating: 0.99,
}
简短赋值方式中的逗号别忘了写
5.2 方法

定义方式

func (recv receive_type) methodName(parameter_list) return_value_list {
	// your code
}

Go中的方法集可以与任何类型相关联(切片等),能够在数据类型和方法之间建立关系。

函数和方法的区别
函数将变量作为参数:Function1(recv)
方法在变量上被调用:recv.Method1()
在接收者是指针时,方法可以改变接收者的值(或状态),这点函数也可以做到(当参数作为指针传递,即通过引用调用时,函数也可以改变参数的状态)。
接收者必须有一个显式的名字,这个名字必须在方法中被使用。
receiver_type 叫做 (接收者)基本类型,这个类型必须在和方法同样的包中被声明。
在 Go 中,(接收者)类型关联的方法不写在类型结构里面,就像类那样;耦合更加宽松;类型和方法之间的关联由接收者来建立。

个人理解,结构体本质上是一种模型,数据模型,虽然书上说,结构体并非创建面向对象代码的方式,而是一种数据结构创建方式,旨在满足数据建模需求。但对于我这个初学者来说,就是会把其理解为面向对象中的类,然后对其进行不同的实例化。

在Java中,基本数据类型是按值传递,引用数据类型(数组,接口,类)按值传递的时候都是传递对象的地址(可以参考我的另一篇博客 Java基础知识补充,不定时更新)。

Go 默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能对副本的值进行更改,但不会影响到原来的变量,比如 Function(arg1)。

如果你希望函数可以直接修改参数的值,而不是对参数的副本进行操作,你需要将参数的地址(变量名前面添加&符号,比如 &variable)传递给函数,这就是按引用传递,比如 Function(&arg1),此时传递给函数的是一个指针。

6. 协程(Goroutine)和通道(Channel)


这应该才是Go最具特色的地方吧

Concurrency is not parallelism

我就先不在这说了, 怕误导了大家,先放几个参考博文

7. 命令行参数

稍微具体一点的在我的周报中写了

在这补充一些。

7.1 os.Args[]
7.2 flag包

关于Nflag函数
在这里插入图片描述

上面可以看到,args的内容一直是空的,我倒要来看看这个咋出来的。

在这里插入图片描述

non-flag
--1-12--

x. 一些小地方


首字母大写
PublicProtected

目前看来,函数名、结构体及其字段名都是这样。

_
_

可以用在几个地方

  1. 代码中作为占位符或者丢弃返回值

举个例子吧。

// 1. 正常操作
func main() {
	f, err := ioutil.ReadFile("readme.txt")
	// code...
}

但是如果我不关心文件内容,只关心这个文件有没有读取成功呢?也就是说,我不要 f 变量,只要对 err 进行判断一下,在Go语言中,如果有变量未被使用,会提示,讲究的就是每个代码都有用,没有多余的代码,那么这时候,就可以用下划线了。

// 2. 丢弃返回的 f
func main() {
	_, err := ioutil.ReadFile("readme.txt")
	// code...
}

这样,就可以了,依然是返回两个变量,但是将上面的 f 赋给了下划线,就会被忽略,也不会引起程序报错。

但是我有个疑问,这样会开辟内存空间么?不会造成浪费么?

  1. 导包时

在导包时,有时候不需要导入整个包,只要调用一下其 init() 函数就行了,这时候也是用下划线实现,它会调用该包下所有的 init() 函数,但不导入整个包。

import "database/sql"
import _ "github.com/go-sql-driver/mysql"

这样就会执行mysql包中的初始化函数,将mysql驱动注册到sql中,但不会导入mysql包,并且后面就可以在sql包中操作mysql数据库。

几种变量声明方式

Go语言提供了多种变量声明方式

1. 指定类型并赋值
var s string = "hello,world!"

2. 省略类型并赋值
var s =  "hello,world!"

3. 先声明类型,后赋值
var t string
t =  "hello,world!"

4. 简短变量声明
u :=  "hello,world!"

是不是有点懵,所以,我们到底应该用哪种呢?(以下摘自《Go语言入门经典》P23)

Go语言设计者在标准库中遵循的约定如下:在函数内使用简短变量声明,在函数外省略类型。且Go源码中,简短变量声明是最常用的。