Go语言学习纪要-1

Go语言学习纪要-1

学习资料来源

1.介绍与安装

Golang是什么

Go 亦称为 Golang (译注:按照 Rob Pike 说法,语言叫做 Go,Golang 只是官方网站的网址),是由谷歌开发的一个开源的编译型的静态语言

Golang 的主要关注点是使得高可用性可扩展性Web 应用的开发变得简便容易。(译注:Go 的定位是系统编程语言,只是对 Web 开发支持较好)

(还没有深入语言底层,粗学感觉go融合了部分C和Java的特性,相较于C它吸收了部分面向对象语言的优点,相较于Java它在底层做了面向于并发编程的优化实现,可增加了头等函数、指针等特性)

为何选择Golang

并发是语言的一部分

Golang 是一种编译型语言。源代码会编译为二进制机器码。(运行速度相对较快)

语言规范十分简洁。

Go 编译器支持静态链接。所有 Go 代码都可以静态链接为一个大的二进制文件(译注:相对现在的磁盘空间,其实根本不大),并可以轻松部署到云服务器,而不必担心各种依赖性。

安装

自行搜索安装教程

2.Hello World

package main  //包名
import “fmt”  //引入标准包
func main(){  //特殊函数。程序就是从main函数开始运行,main函数必须放在main包里
 fmt.Println("Hello World")
}

3.变量

声明语法

var name type   //type是需要的。如果不带type,那么就需要提供初始化机制让go可以推断出类型《-也就是说Go是强类型语言

e.g

var a int
var b,c string
var d = 1
var e,f = "a",1
var (
	g = "x"
    h = 1
)
i := 1

4.类型

Go支持的基本类型

bool
数字类型:
 int8,int16,int32,int64,int
 unit8,unit16,unit32,unit64,unit //无符号的整型
 float32,float64
 complex64,complex128 //复数类型
 byte //字节
 rune //int32的别名,代表一个代码点;代码点无论占用多少个字节,都可以用一个 rune 来表示
string

Go有非常严格的强类型转换

Go没有自动类型提升或类型转换

//这一点个人感觉有点不太方便

string和int做拼接:

var a,b = "string",1
fmt.Println(fmt.Sprintf("%s%d",a,b))

类型别名语法

type 别名 原类型 //别名后的类型是一个新类型,与原类型不同

5.常量

定义

在 Go 语言中,术语"常量"用于表示固定的值。

常量的值会在编译的时候确定

因为函数调用发生在运行时,所以不能将函数的返回值赋值给常量。

字符串常量

双引号中的任何值都是 Go 中的字符串常量。

无类型的常量有一个与它们相关联的默认类型,并且当且仅当一行代码需要时才提供它

6.函数

定义

函数是一块执行特定任务的代码。一个函数是在输入源基础上,通过执行一系列的算法,生成预期的输出。

声明语法

func 函数名(参数名 参数类型) 返回值{
	//实现
}

支持多返回值

e.g

func test(a,b int,string)(int,string){
	a++
	return a,b
}

func test2(a int,b string)(returna int ,returnb string){
    a++
    return  //必要的
}

func main(){
    var _,b = test2(1,"3")  // _表示空白符,即不关注于值内容
    fmt.Printf(b)
}

7.包

包用于组织 Go 源代码,提供了更好的可重用性与可读性

main函数需要放在main包里

//第三方包、类如何做测试呢?如何解决main包冲突问题

//main中包名和文件路径名好像不需要一致

属于某一个包的源文件都应该放置于一个单独命名的文件夹里。按照 Go 的惯例,应该用包名命名该文件夹。

导入自定义包路径规则

我们必须指定自定义包相对于工作区内 src 文件夹的相对路径

导出名字

在 Go 中,任何以大写字母开头的变量或者函数都是被导出的名字。其它包只能访问被导出的函数和变量。//类似于Java里的public语法

init函数

所有包都可以包含一个 init 函数。init 函数不应该有任何返回值类型和参数,在我们的代码中也不能显式地调用它

包的初始化顺序如下:

  1. 首先初始化包级别(Package Level)的变量
  2. 紧接着调用 init 函数。包可以有多个 init 函数(在一个文件或分布于多个文件中),它们按照编译器解析它们的顺序进行调用。

未使用错误屏蔽器写法

有时候需要引入对应包,执行初始化方法,但是又不需要用到包的内容,可以用下述写法规避未使用的报错

import (
    "geometry/rectangle"
)

var _ = rectangle.Area // 错误屏蔽器

import (
    _ "geometry/rectangle"
)
func main() {

}

8.if-else语句

语法

if condition {
} else if condition {
} else { //注意,这里的 }要和else 在同一行,否则会被go自动补充;的规则扰乱语法
}

if statement; condition {
}

9.循环

语法

for initialisation; condition; post {
}

支持break和continue

无限循环语法

for {
}

10.switch

语法

支持几种语法格式

switch [初始化语句];[判定变量]{
case 条件值(或判定语句): //有判定变量时可以是条件值,支持多值;没有判定变量时,可以带判定语句
	逻辑语句
    [fallthrough]  //可选的fallthrough,表示继续往下做判定检验
...
default:
	逻辑语句
}

11.数组和切片

数组声明语法:

var 变量名 [数组大小]数组值类型  //数组声明,用append可以进行拓展
var 变量名 = [数组大小]数组值类型{初始化值} //声明+初始化语法

e.g 
var a [3]int
var b = [3]int{1,2,3}

常用数组方法

len(数组变量) //获取数组的当前长度
cap(数组变量) //获取数组的当前容器大小

切片定义

切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper)。切片本身不拥有任何数据。它们只是对现有数组的引用

切片语法

[]T //表示带有T类型的切片
数组[startIndex:endIndex] //切片语法
数组[:] //全切
make(数组类型,长度,[大小]) //这种是创建一个新的切片
变量名 := [数组大小]数字值类型{初始化值...}

e.g
b := [2]int{1,2} //注意,这里创建的是切片
c := [3][2]string{
    {"aaa","bbb"},
    {"ccc","ddd"},
    {"eee","fff"}, //最后一个,是必要的
}

e.g
func main{
    a := []int{1,2,3}
    b := a[1:3]
    fmt.Println("array a:",a)
    fmt.Println("array a:",b)
}

数组和切片的区别

切片时指针类型,数组是值类型 //这里的意思是,用数组彼此赋值时也会是拷贝,而切片只是引用指向

数组是静态的,不可拓展,切片可以通过append拓展 //reflect.TypeOf判定时,数组是带大小的,切片是不带大小的

一个简单的理解可以是,数组就是cap固定且不可变的数组,而切片是cap已知但可变的数组

切片的函数传递

我们可以认为,切片在内部可由一个结构体类型表示。这是它的表现形式,//所以赋值时也是引用传递

type slice struct {
    Length        int
    Capacity      int
    ZerothElement *byte
}

切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。

一种优化方式:

使用 copy 函数 func copy(dst,src[]T)int 来生成一个切片的副本

12.可变参数函数

语法

e.g
func append(slice []Type, eles ...Type) []Type

数组或切片作为参数传递时,如果要匹配可变参数,需要加...
e.g
nums := []int{1,2,3}
find(89,nums...)
...
func find(a int,b ...int){
    ...
}

切面匹配可变参数传递时是引用传递

e.g

func main() {
	a := []int{1,2,3}
	change(a...)
	fmt.Printf("after call:",a)
}

func change(a ...int){
	a[0] = 100
}

//运行结果:after call:%!(EXTRA []int=[100 2 3])

13.map

map定义

map 是在 Go 中将值(value)与键(key)关联的内置类型。通过相应的键可以获取到值。

基础语法

声明语法:
变量名 := make(map[key类型]value类型)   //创建一个key type=string value type=int的数组
变量名 := map[key类型]value类型{初始化值}

e.g
a := make(map[string]int)
b := map[string]int{
    "a1":1,
    "a2":2,
}

初始化语法:
map变量名[key值]=value值

e.g
a["key"] = 1   

获取map大小
len(map变量名)

遍历语法
for key变量名,value变量名 := range map变量{
    //逻辑代码
}

Map和切片一样属于引用类型

Map的相等性

map 之间不能使用 == 操作符判断,== 只能用来检查 map 是否为 nil

相等判定函数范例:

func equals(a map[string]int,b map[string]int )bool{
	if a == nil && b == nil{
		return true
	}
	if a == nil || b == nil{
		return false
	}
	if len(a) != len(b){
		return false
	}
	for key,_ := range a{ //这里的value需要用_,因为go会检查声明的变量有没有用到 囧~
		if a[key] != b[key]{
			return false
		}
	}
	return true
}

14.字符串

Go 语言中的字符串是一个字节切片

直接以string作为参数传递时是值传递

Go 中的字符串是兼容 Unicode 编码的,并且使用 UTF-8 进行编码

字符串的长度

len(字符串变量)

字符串是不可变的

15.指针

什么是指针

指针是一种存储变量内存地址(Memory Address)的变量(概念和C中的是一致的)

基础语法

声明语法
*T //表示指向T类型的指针
获取地址语法
&a //表示获取a变量的内存地址
解析内存地址值语法
*a //表示获取地址a的内容

e.g
a := 1
var b *int = &a
var c = *b

不要向函数传递数组的指针,而应该用切片

更为简单,习惯如此

Go不支持指针运算

C是支持的,不支持的话会更安全

16.结构体

什么是结构体

结构体是用户定义的类型,表示若干个字段(Field)的集合。(和C的结构体感觉是一样的)

基础语法

别名+定义
type fruit struct{
	name string
	num int
}
a := fruit{
	name:"apple",
	num:1,
}

匿名定义
a := struct{
	name string
	num,age int
}{
	name:"orange",
	num:1,
	age:2,
}

访问结构体的字段
a.name

匿名字段
type Person struct{
	string
	int
}
a := Person{"R",50}
a.string = "Z"

嵌套结构体

提升字段
type Address struct{
	string
	int
}

type Person struct{
	name string
	age int
	Address
}

a := Person{
		name:"Z",
		age:10,
		Address:Address{
			"City",
			12,
		},
	}
//提升后访问
a.string


结构体导出的字段需要用大写开头

结构相等性

结构体是值类型。如果它的每一个字段都是可比较的,则该结构体也是可比较的。如果两个结构体变量的对应字段相等,则这两个变量也是相等的。

原文地址:https://www.cnblogs.com/ybk2018af/p/14413055.html