Go语言通过协程可以方便的实现并行处理,达到处理效率的最大化 ,提升系统的吞吐能力。
GO语言使用起来简单、代码描述效率高、编码规范统一、上手快。 通过少量的代码,即可实现框架的标准化,能快速的构建各种通用组件和公共类库,进一步提升开发效率,实现特定场景下的功能量产。
上面是Go语言的一些优点,简单的摘抄了点。下面是偏笔记的内容,多数笔记来自菜鸟教程,一些比较通用的方法比如说用等我就没有记录,记录的都是一些个人认为比较特殊的点。因为第一次尝试写这种入门的文章,文笔会很差,大家体谅一下,拜托!!!如果有错误或可以优化的地方,希望大家积极提出我会改正!!!
继续开个小坑,到时候开个目录,慢慢更新吧(
目录
1.下载安装
下载地址:All releases - The Go Programming Language
Windows 下可以使用 .msi 后缀(在下载列表中可以找到该文件,如go1.4.2.windows-amd64.msi)的安装包来安装。
默认情况下 .msi 文件会安装在 c:\Go 目录下。可以将 c:\Go\bin 目录添加到 Path 环境变量中。添加后你需要重启命令窗口才能生效。我们可以验证一下是否安装成功。
package main
import "fmt"
func main(){
fmt.println("Hello World!")
}
以下图的路径来进行测试。
如图则成功啦!
2.基础格式部分
我们来看一下Go语言的格式部分
- 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
- 下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
- 下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
- 下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
- 下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print("hello, world\n") 可以得到相同的结果。
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。 - 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
行分隔符
在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
如果打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中并不鼓励这种做法。
附上一个笔记:
关于包,根据测试得出以下几点:
- 文件名与包名没有直接关系,不一定要将文件名与包名定成同一个。
- 文件夹名与包名没有直接关系,并非需要一致。
- 同一个文件夹下的文件只能有一个包名,否则编译报错。
3.数字类型
①.数字类型
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
序号 | 类型和描述 |
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
②.浮点型
序号 | 类型和描述 |
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
③.其他数字类型
以下列出了其他更多的数字类型:
序号 | 类型和描述 |
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
4.基础语法
①.Go语言的字符串连接可以通过+实现
package main
import "fmt"
func main() {
fmt.Println("Hello " + "World!")
}
但是Go语言中不能让不同类型的字节进行相加,包括打印的时候。
②.格式化字符串
Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
- Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
- Printf 根据格式化参数生成格式化的字符串并写入标准输出。
func main(){
var a = 123
var b = "abc"
var c = "a : %d,b : %s"
fmt.Printf(c,a,b)
}
这一段效果等同于下面一段
func main(){
var a = 123
var b = "abc"
var c = "a : %d,b : %s"
var d = fmt.Sprintf(c,a,b)
fmt.Print(d)
}
③.空白标识符在函数返回值时的使用
package main
import "fmt"
func main() {
_,numb,strs := numbers() //只获取函数返回值的后两个
fmt.Println(numb,strs)
}
//一个可以返回多个值的函数
func numbers()(int,int,string){
a , b , c := 1 , 2 , "str"
return a,b,c
}
输出结果:
2 str
如果是fmt.Println(_,numb,strs)会出现报错
④.可以通过不导包完成打印的操作
package main
func main () {
println(1)
}
由此我们可以知道,在包.方法的情况下,方法的首字母要大写,而直接使用方法的时候则需要小写。
5.申明变量
Go里面的申明变量有以下几种方式:
①.用var来申明
一般来说申明变量都是用var关键字。
eg: var identifier type
也可以同时申明多个变量,
eg: var identifier1,identifier2 type
如果我们不申明初始值的话,变量则会默认为零值
由此我们可见,String类型的零值为"" ,int类型的零值为0,而布尔值的零值为false。
同样的,Go语言中也存在全局变量。全局变量是可以被申明后不去使用的。
在Go语言中,存在一个特性就是没有用到的局部变量不能申明。
看下面这个例子:
不过一般申请全局变量都用以下这种形式:
package main
import "fmt"
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
func main() {
}
同样的,Go有自动判断变量类型的功能,所以我们在申请变量的时候可以不用加变量类型的哦。
package main
import "fmt"
func main() {
var a = "str"
var b = 5
var c = true
fmt.Println(a)
if c {
fmt.Println(b)
}
}
②.使用 := 赋值操作符
我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。
a 和 b 的类型(int 和 bool)将由编译器自动推断。
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
同样的,因为不是全局变量,如果不去使用申明的变量我们依然会得到报错。
package main
import "fmt"
func main() {
a := "str"
b := 5
c := true
fmt.Println(a)
if c {
fmt.Println(b)
}
}
结果如下:
Go语言中全局变量和局部变量名称可以相同,但是局部变量会被优先考虑。
③.值类型和引用类型
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值,当使用等号 =
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i 的值进行了拷贝。我们用&i的方式可以打印出地址来观察
package main
import "fmt"
func main() {
a := "str1"
b := a
fmt.Println("a:" + a)
fmt.Println("b:" + b)
fmt.Println(&a)
fmt.Println(&b)
a = "str2"
fmt.Println("a:" + a)
fmt.Println("b:" + b)
fmt.Println(&a)
fmt.Println(&b)
}
结果如下:
可以发现a,b的地址是不同的,所以当a改变的同时b是不会发生改变的。
这里就涉及到深拷贝和浅拷贝的概念,大家可以去看一下。这里截取部分概念。
Go语言讲解深拷贝与浅拷贝 - 简书 (jianshu.com)
深拷贝(Deep Copy):
拷贝的是数据本身,创造一个样的新对象,新创建的对象与原对象不共享内存,新创建的对象在内存中开辟一个新的内存地址,新对象值修改时不会影响原对象值。既然内存地址不同,释放内存地址时,可分别释放。
值类型的数据,默认全部都是深复制,Array、Int、String、Struct、Float,Bool。
浅拷贝(Shallow Copy):
拷贝的是数据地址,只复制指向的对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化。释放内存地址时,同时释放内存地址。引用类型的数据,默认全部都是浅复制,Slice,Map。
6.申明常量
①.常量的申明
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式有如下几种:
const identifier [type] = value const c_name1, c_name2 = value1, value2 const ( Unknown = 0 Female = 1 Male = 2 )
同样的,我们可以省略常量的类型,Go语言会自己判断出来的。
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过。
package main
import "unsafe"
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main(){
println(a, b, c)
}
以上实例运行结果为:
abc 3 16
这里对unsafe.Sizeof()进行一点补充:
a = "hello"
unsafe.Sizeof(a)
输出结果为:16
字符串类型在 go 里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节,所以字符串类型大小为 16 个字节。
在Go语言中,字符串类型是一个结构体,包含一个指向底层数据的指针和一个表示字符串长度的整数。在64位系统上,每个指针占用8字节,整数占用8字节。因此,一个字符串类型在64位系统上总共占用16字节的空间。
对于给定的代码a = "hello",虽然a是一个字符串类型的变量,但在赋值时,实际上是将一个指向字符串"hello"底层数据的指针赋值给了a。因此,unsafe.Sizeof(a)返回的是一个指针的大小,即8字节。
因此,代码a = "hello"; unsafe.Sizeof(a)的运行结果是8。
②.常量组的特性
在定义常量组时,如果不提供初始值,则表示将使用上行的表达式。
package main
import "fmt"
const (
a = 1
b
c
d
)
func main() {
fmt.Println(a)
// b、c、d没有初始化,使用上一行(即a)的值
fmt.Println(b) // 输出1
fmt.Println(c) // 输出1
fmt.Println(d) // 输出1
}
③.特殊的常量 iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值
const (
a = iota
b = iota
c = iota
)
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
const (
a = iota
b
c
)
可以看一下示例:
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
以上实例运行结果为:
0 1 2 ha ha 100 100 7 8
还有一个实例:
package main
import "fmt"
const (
i=1<<iota
j=3<<iota
k
l
)
func main() {
fmt.Println("i=",i)
fmt.Println("j=",j)
fmt.Println("k=",k)
fmt.Println("l=",l)
}
以上实例运行结果为:
i= 1
j= 6
k= 12
l= 24
iota 表示从 0 开始自动加 1,所以 i=1<<0, j=3<<1(<< 表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和 l,从输出结果看 k=3<<2,l=3<<3。
简单表述:
- i=1:左移 0 位,不变仍为 1。
- j=3:左移 1 位,变为二进制 110,即 6。
- k=3:左移 2 位,变为二进制 1100,即 12。
- l=3:左移 3 位,变为二进制 11000,即 24。
iota 只是在同一个 const 常量组内递增,每当有新的 const 关键字时,iota 计数会重新开始。
package main
const (
i = iota
j = iota
x = iota
)
const xx = iota
const yy = iota
func main(){
println(i, j, x, xx, yy)
}
输出结果是 0 1 2 0 0