Hello,world!
开始学习Go语言前,我们先从一段简单的代码引入:
package main
import "fmt"
func main() {
fmt.Println("Hello,world!")
}
Go语言的代码通过包(package)组织,包类似于其它语言里的库(libraries)或者模块(modules)。一个包由位于单个目录下的一个或多个.go源代码文件组成, 目录定义包的作用。import声明必须跟在文件的package声明之后。
随后,则是组成程序的函数、变量、常量、类型的声明语句(分别由关键字 func, var, const, type 定义)。我们需要注意的是:必须恰当导入需要的包,缺少了必要的包或者导入了不需要的包,程序都无法编译通过。
Go语言在代码格式上采取了很强硬的态度。Go语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。函数的左括号 { 必须和func函数声明在同一行上, 且位于末尾,不能独占一行。
基础数据类型
- 整型:int8 int16 int32 int64(有符号)uint8 uint16 uint32 uint64(无符号)
- 浮点型:float32 float64
- 复数:complex64 complex128(与浮点型对应)
- 布尔型:true false
- 字符串型:采用UTF8编码的Unicode码点(rune)序列
- 常量:const 常量的值不可修改,所有常量的运算都可以在编译期完成
- 变量:var 变量名字 类型 = 表达式(var声明语句)
复合数据类型
- 数组:一个由固定长度的特定类型元素组成的序列,数组的每个元素可以通过索引下标来访问
- 函数:函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体
func function_name(parameter_list) (result_types){}
函数定义解析:
- func:函数由 func 开始声明
- function_name:函数名称,参数列表和返回值类型构成了函数签名。
- parameter_list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
函数的递归:
函数可以直接或间接的调用自身,可以解决计算阶乘,生成斐波那契数列等数学问题
以下程序采用函数递归计算数的阶乘:
package main
import "fmt"
func Factorial(n uint64)(result uint64) {
if (n > 0) {
result = n * Factorial(n-1)
return result
}
return 1
}
func main() {
var i int = 15
fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}
- Slice(切片):是一个轻量级的数据结构,提供了访问数组子序列(或者全部)元素的功能
Q2 := months[4:7]
summer := months[6:9]
fmt.Println(Q2) // ["April" "May" "June"]
fmt.Println(summer) // ["June" "July" "August"]
- Map(哈希表):一个无序的key/value对的集合,其中所有的key都是不同的,然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value
内置的make函数可以创建一个map:make(map[string]int)
我们也可以用map字面值的语法创建map,同时还可以指定一些最初的 key/value:
ages := map[string]int{
"alice": 31,
"charlie": 34,
}
- struct(结构体):一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee
-
Range(范围):用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对
代码中的 key 和 value 是可以省略的:for _, value := range oldMap
-
Pointer(指针):一个指针变量指向了一个值的内存地址,当一个指针被定义后没有分配到任何变量时,它的值为 nil:
var ip *int /* 指向整型*/
-
interface(接口):通过将接口作为参数来实现对不同类型的调用实现多态
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
- channel(通道):是一个通信机制,它可以让一个goroutine(并发执行单元)通过它给另一个goroutine发送值信息,使用内置的make函数,我们可以创建一个channel:
make(chan 元素类型, [缓冲大小])
通道分类:
- 无缓冲通道 make(chan int)
- 有缓冲通道 make(chan int, 2)
注释
单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。 // 单行注释 /* 多行注释 */
转换字符
转换字符 | 含义 |
---|---|
%d | 十进制整数 |
%x, %o, %b | 十六进制,八进制,二进制整数 |
%f, %g, %e | 浮点数: 3.141593 3.141592653589793 3.141593e+00 |
%t | 布尔:true或false |
%c | 字符(rune) (Unicode码点) |
%s | 字符串 |
%q | 带双引号的字符串"abc"或带单引号的字符'c' |
%v | 变量的自然形式(natural format) |
%T | 变量的类型 |
%% | 字面上的百分号标志(无操作数) |
并发
在Go语言中,每一个并发的执行单元叫作一个goroutine,goroutine 是轻量级线程,当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine,新的goroutine会用go语句来创建。
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
封装
封装提供了三方面的优点。首先,因为调用方不能直接修改对象的变量值,其只需要关注少量的语句并且只要弄懂少量变量的可能的值即可。
第二,隐藏实现的细节可以防止调用方依赖那些可能变化的具体实现,这样使设计包的程序员在不破坏对外的api情况下能得到更大的自由。
封装的第三个优点也是最重要的优点,是阻止了外部调用方对对象内部的值任意地进行修改。因为对象内部变量只可以被同一个包内的函数修改,所以包的作者可以让这些函数确保对象内部的一些值的不变性。
比如log包里的Logger类型对应的一些函数。在命名一个getter方法时,我们通常会省略掉前面的Get前缀。这种简洁上的偏好也可以推广到各种类型的前缀比如Fetch,Find或者Lookup。
package log
type Logger struct {
flags int
prefix string
// ...
}
func (l *Logger) Flags() int
func (l *Logger) SetFlags(flag int)
func (l *Logger) Prefix() string
func (l *Logger) SetPrefix(prefix string)