语法、 标准库、第三方库、构件体系和工具链
GOlang 最主要的特性:
自动垃圾回收
更丰富的内置类型
函数多返回值
错误处理
匿名函数和闭包
类型和接口
并发编程
反射
语言交互性
recover
和 panic
来替代异常机制(第 13.2-3 节)环境变量
** G O R O O T ∗ ∗ 表 示 G o 在 你 的 电 脑 上 的 安 装 位 置 , 它 的 值 一 般 都 是 ‘ GOROOT** 表示 Go 在你的电脑上的安装位置,它的值一般都是 ` GOROOT∗∗表示Go在你的电脑上的安装位置,它的值一般都是‘HOME/go`,当然,你也可以安装在别的地方。
$GOARCH 表示目标机器的处理器架构,它的值可以是 386、amd64 或 arm。
$GOOS 表示目标机器的操作系统,它的值可以是 darwin、freebsd、linux 或 windows。
** G O B I N ∗ ∗ 表 示 编 译 器 和 链 接 器 的 安 装 位 置 , 默 认 是 ‘ GOBIN** 表示编译器和链接器的安装位置,默认是 ` GOBIN∗∗表示编译器和链接器的安装位置,默认是‘GOROOT/bin`,如果你使用的是 Go 1.0.3 及以后的版本,一般情况下你可以将它的值设置为空,Go 将会使用前面提到的默认值。
目标机器是指你打算运行你的 Go 应用程序的机器。
Go 编译器支持交叉编译,也就是说你可以在一台机器上构建运行在具有不同操作系统和处理器架构上运行的应用程序,也就是说编写源代码的机器可以和目标机器有完全不同的特性(操作系统与处理器架构)。
为了区分本地机器和目标机器,你可以使用 $GOHOSTOS
和 $GOHOSTARCH
设置本地机器的操作系统名称和编译体系结构,这两个变量只有在进行交叉编译的时候才会用到,如果你不进行显示设置,他们的值会和本地机器($GOOS
和 $GOARCH
)一样。
一样的值,但从 Go 1.1 版本开始,你必须修改为其它路径。它可以包含多个 Go 语言源码文件、包文件和可执行文件的路径,而这些路径下又必须分别包含三个规定的目录:
src、
pkg和
bin`,这三个目录分别用于存放源码文件、包文件和可执行文件。GO运行时:
Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括内存分配、垃圾回收、栈处理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。
runtime 主要由 C 语言编写(Go 1.5 开始自举),并且是每个 Go 包的最顶级包。你可以在目录 $GOROOT/src/runtime
中找到相关内容。
垃圾回收器 Go 拥有简单却高效的标记-清除回收器。它的主要思想来源于 IBM 的可复用垃圾回收器,旨在打造一个高效、低延迟的并发回收器。目前 gccgo 还没有回收器,同时适用 gc 和 gccgo 的新回收器正在研发中。使用一门具有垃圾回收功能的编程语言不代表你可以避免内存分配所带来的问题,分配和回收内容都是消耗 CPU 资源的一种行为。
Go 的可执行文件都比相对应的源代码文件要大很多,这恰恰说明了 Go 的 runtime 嵌入到了每一个可执行文件当中。当然,在部署到数量巨大的集群时,较大的文件体积也是比较头疼的问题。但总的来说,Go 的部署工作还是要比 Java 和 Python 轻松得多。因为 Go 不需要依赖任何其它文件,它只需要一个单独的静态文件,这样你也不会像使用其它语言一样在各种不同版本的依赖文件之间混淆。
GO解释器:可以实现REPL(read-eval-print loop)
Sebastien Binet 已经使用这种环境实现了一个 Go 解释器,你可以在这个页面找到:https://github.com/sbinet/igo
从 Go 1 版本开始,使用 Go 自带的更加方便的工具来构建应用程序:
go build
编译自身包和依赖包go install
编译并安装自身包和依赖包
tab表示4个空格
用于放置一个go程序员的所有go代码和依赖在一个workspace里。
前提是生成出的二进制文件不重名,如果存在重名情况,就要分成不同的workspace
workspace:包含多个版本控制的repository
repository:包含多个package
package:包含多个go源码文件
src:go源码文件
pkg:package object(编译出的二进制文件)
bin:可执行文件(编译出的二进制文件)
//golang中一行代表一个语句的阶数,每个语句不是按分号结尾
// ebnf范式
//工具cgo提供了对FFI(外部函数接口)的支持,使得GO代码能够安全地调用C语言库
//与c交互
import "c"//== #include
import "unsafe"
var i int
C.uint(i) //从go中的int转换成c中无符号Int
int(C.random()) //从C中random()函数返回的long转换为GO中的int
//Random()和Seed()分别调用C中的C.random()和C.srandom()
package main //包,package main表示一个可独立运行的程序,必须在源文件的非注释第一行,指明这个文件属于哪个包
//如果对一个包进行更改或者重新编译,那所有引用这个包的客户端程序都必须全部重新编译,包之间通过import关键字将一组包联系在一起
import "fmt"
import "os"
//或者 import "fmt";import "os"
func main(){
fmt.Println("hello go")
}
布尔型: var b bool=true
数字类型:int float32 float64 ,支持整型(32位浮点,64位浮点)和浮点型(complex64 complex128),其中位运算采用补码
字符串类型: GO中的字符串和字节使用UTF-8编码标识的Unicode文本
派生类型:指针Pointer 数组 结构化类型(struct) Channel类型 函数类型 切片类型 接口类型(interface) Map类型
其他类型:byte(类似无符号8位,就是0到255) rune(~= int 32) uintptr(无符号整形,用于存放一个指针)
//1.指定类型,如果没有初始化,则变量默认值为零值(系统默认的值)
如果不赋初值,会有默认值:Bool型为false 字符串为""(空串)
以下几种类型为nil:
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error//erron 是接口
//2.根据值自行判断变量的类型
var v_name=val
package main
import "fmt"
func main(){
var d=true //赋过来的值是true 所以d是bool
fmt.Println(d)
}
//3.第三种,省略 var, 注意 := 左侧如果没有声明新的变量,就产生编译错误
//注意这个:=只能给一个变量用,不能出现a,b:=5,6的情况
v_name:=value
var intVal int
intVal:=1 //这是错误的,因为intVal已经声明,不需要重新声明
intVal :=1//不会产生编译错误,因为:=的左边是有声明新的变量
func main(){
var a string="abc"
fmt.Println("hello world")
}//到这里会出错: a declared and not used
所以应该改成 fmt.Println("hello world",a)
//但是全局变量是允许声明但是不使用的
//空白标识符在函数返回值时使用
func main(){
_,numb,strs:=numbers()//这个下划线表示空白标识符,这样只会获取函数返回值的后面两个
fmt.Println(numb,strs)
}
func numbers()(int,int,string){
a,b,c:=1,1,"str"
return a,b,c
}
//常量
//常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
const identifier [type] =value
//可以省略类型说明符[type],编译器可以根据变量的值来推断其类型
//显示类型: const b string="abc"
//隐式类型: const b="abc"
//常量不能够:=
//常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过,len()返回字符串有多少个字符,不包含空串,cap()
//有2个很重要的内置函数:len()和cap()
len()可以用来查看数组或slice的长度
cap()可以用来查看数组或slice的容量计算容量的方法
cap()可以测量切片最长可以达到多少
在数组中由于长度固定不可变,因此len(arr)和cap(arr)的输出永远相同
在slice中,len(sli)表示可见元素有几个(也即直接打印元素看到的元素个数),而cap(sli)表示所有元素有几个,比如:
arr := []int{
2, 3, 5, 7, 11, 13}
sli := arr[1:4]
//将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片
fmt.Println(sli)
fmt.Println(len(sli))
fmt.Println(cap(sli))
输出
[3 5 7]//说明是左开右闭
3
5
//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
)
//左移n位表示乘以2的n次方
<<n==*(2^n)
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 //110
k= 12 //1100
l= 24 //11000
reflect.TypeOf().Kind()
//这个函数可以知道某个变量的类型,字符串是以byte数组形式保存的,类型是uint8,占1个byte,打印时需要用string进行类型转换,否则打印的是编码值
//因为字符串是以 byte 数组的形式存储的,所以,str2[2] 的值并不等于语。str2 的长度 len(str2) 也不是 4,而是 8( Go 占 2 byte,语言占 6 byte),也就是说字符串的长度是8的倍数
str2:="go语言"
fmt.Println(str1[2],string(str1[2]))
package main
import (
"fmt"
"reflect"
)
func main() {
str1 := "Golang"
str2 := "Go语言"
fmt.Println(reflect.TypeOf(str2[2]).Kind()) // uint8
fmt.Println(str1[2], string(str1[2])) // 108 l
fmt.Printf("%d %c\n", str2[2], str2[2]) // 232 è
fmt.Println("len(str2):", len(str2)) //len(str2): 8
}
/*reflect.TypeOf().Kind() 可以知道某个变量的类型,我们可以看到,字符串是以 byte 数组形式保存的,类型是 uint8,占1个 byte,打印时需要用 string 进行类型转换,否则打印的是编码值。
因为字符串是以 byte 数组的形式存储的,所以,str2[2] 的值并不等于语。str2 的长度 len(str2) 也不是 4,而是 8( Go 占 2 byte,语言占 6 byte) */
正确的处理方式是将 string 转为 rune 数组
str2 := "Go语言"
runeArr := []rune(str2)
fmt.Println(reflect.TypeOf(runeArr[2]).Kind()) // int32
fmt.Println(runeArr[2], string(runeArr[2])) // 35821 语
fmt.Println("len(runeArr):", len(runeArr)) // len(runeArr): 4
转换成 []rune 类型后,字符串中的每个字符,无论占多少个字节都用 int32 来表示,因而可以正确处理中文。
& 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 (A & B) 结果为 12, 二进制为 0000 1100
| 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或 (A | B) 结果为 61, 二进制为 0011 1101
^ 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (A ^ B) 结果为 49, 二进制为 0011 0001
//异或就是:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1)
<< 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 A << 2 结果为 240 ,二进制为 1111 0000
>> 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。 A >> 2 结果为 15 ,二进制为 0000 1111
//运算符优先级
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
只有当某个函数需要被外部包调用的时候才使用大写字母开头,并遵循 Pascal 命名法;否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。
这些函数只可以用于调试阶段,在部署程序的时候务必将它们替换成 fmt
中的相关函数。
当被调用函数的代码执行到结束符 }
或返回语句时就会返回,然后程序继续执行调用该函数之后的代码。
程序正常退出的代码为 0 即 Program exited with code 0
;如果程序因为异常而被终止,则会返回非零值,如:1。这个数值可以用来测试是否成功执行一个程序。
func function_name([parameter list])[return_types]{
函数体
}
func:函数的声明
function_name:函数名称,参数列表和返回值构成函数签名
parameter list:参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_type:返回类型,函数返回一列值。
//返回两个数的最大值
func max(num1,num2 int)int{
var result int
if(num1>num2){
result=num1
}else{
result=num2
}
return result
}
func main(){
a:=5
b:=6
c=max(a,b)
fmt.Println(c)
}
//程序一般由关键字,常量,变量,运算符,函数组成
//分隔符:() [] {}
//标识符:一个标识符实际上就是一个或是多个字母(A~Z和a~z)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
//标点符号: , . ; : ...
//空格:变量的声明必须使用空格隔开 eg: var age int;
//25个关键字或者保留字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
//36个预定义标识符
append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr
package main
import (
"fmt" //标准格式,用于规范
"os"
)
type point struct {
//定义结构体
x, y int // xy 都是int型
}
func main() {
p := point{
1, 2}
fmt.Printf("%v\n", p)
fmt.Printf("%+v\n", p)
fmt.Printf("%#v\n", p)
fmt.Printf("%T\n", p)
fmt.Printf("%t\n", true)
fmt.Printf("%d\n", 123)
fmt.Printf("%b\n", 14)
fmt.Printf("%c\n", 33)
fmt.Printf("%x\n", 456)
fmt.Printf("%f\n", 78.9)
fmt.Printf("%e\n", 123400000.0)
fmt.Printf("%E\n", 123400000.0)
fmt.Printf("%s\n", "\"string\"")
fmt.Printf("%q\n", "\"string\"")
fmt.Printf("%x\n", "hex this")
fmt.Printf("%p\n", &p)<