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 函数不应该有任何返回值类型和参数,在我们的代码中也不能显式地调用它
包的初始化顺序如下:
- 首先初始化包级别(Package Level)的变量
- 紧接着调用 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
结构体导出的字段需要用大写开头
结构相等性
结构体是值类型。如果它的每一个字段都是可比较的,则该结构体也是可比较的。如果两个结构体变量的对应字段相等,则这两个变量也是相等的。