参考
Go基础要点总结
一、变量、声明和赋值
Go中的变量为类型后置形式,变量声明的一般语法
var 变量名字 类型 = 表达式
Go语言具有零值初始化机制,默认初始化值,不存在未初始化变量
var s string
fmt.Println(s) // ""
可同时声明多个同类型或不同类型变量
var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string
Go的简单变量声明语句 简洁灵活,表达式自动推导变量类型,这里需要区分声明 := 和赋值 =
i, j := 0, 1 //简单变量声明,i和j初始化为int
i, j = j, i //赋值,交换 i 和 j 的值
声明多个变量时,必须保证至少有一个变量未被声明,对于其它已声明的变量,执行赋值行为
//part1
in, err := os.Open(infile)
// ...
out, err := os.Create(outfile) //正确: 声明out,赋值err
//part2
f, err := os.Open(infile) //f 和 err 均已声明
// ...
f, err := os.Create(outfile) // compile error: no new variables
Go中的指针用法与C++一致,空指针为nil,同样可以使用new,Go中的new为预定义函数而非关键字,可以声明为其他变量
p := new(int) // p, *int 类型, 指向匿名的 int 变量
fmt.Println(*p) // "0"
*p = 2 // 设置 int 匿名变量的值为 2
fmt.Println(*p) // "2"
元组赋值:同时更新多个变量的值
x, y = y, x //交换两个变量的值
i, j, k = 2, 3, 5 //元组赋值
//使用空白标识符 _ 丢弃不需要的值
_, err = io.Copy(dst, src) // 丢弃字节数
type是用来分隔不同概念的类型,即使它们底层类型相同也不兼容
// Package tempconv performs Celsius and Fahrenheit temperature computations.package tempconv
import "fmt"type Celsius float64 // 摄氏温度type Fahrenheit float64 // 华氏温度const (
AbsoluteZeroC Celsius = -273.15 // 绝对零度
FreezingC Celsius = 0 // 结冰点温度
BoilingC Celsius = 100 // 沸水温度
)
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
// output
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C
boilingF := CToF(BoilingC)
fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F
fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch
批量声明常量,除第一个以外,其它常量的初始化表达式可省略,省略的常量使用前者的表达式
const (
a = 1
b
c = 2
d
)
fmt.Println(a, b, c, d) // "1 1 2 2"
字符串操作函数
二、复合数据类型
数组长度固定,其中元素初始化默认为零值,也可显示赋值,使用[...]则表示数组长度由初始化个数来计算
var q [3]int = [3]int{1, 2, 3}
q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"
r := [...]int{99: -1} //下标99即第100个数被初始化为-1,数组中其它元素默认为0
Slice(切片) 代表变长的序列,内置的len和cap函数分别返回slice的长度和容量
如果切片操作超出cap(s)的上限将导致一个panic异常,但是超出len(s)则是意味着扩展了slice
内置函数 append - 追加元素
var runes []runefor _, r := range "Hello, 世界" {
runes = append(runes, r)
}
fmt.Printf("%q\n", runes) // "['H' 'e' 'l' 'l' 'o' ',' ' ' '世' '界']"
以下为对切片添加元素时的扩容(当容量cap不足时),过程类似C++的vector扩容
func appendInt(x []int, y int) []int {
var z []int
zlen := len(x) + 1
if zlen <= cap(x) {
// There is room to grow. Extend the slice.
z = x[:zlen]
} else {
// There is insufficient space. Allocate a new array.// Grow by doubling, for amortized linear complexity.
zcap := zlen
if zcap < 2*len(x) {
zcap = 2 * len(x)
}
z = make([]int, zlen, zcap)
copy(z, x) // a built-in function; see text
}
z[len(x)] = y
return z
}
Map( 哈希表 ) 是一个无序的key/value对的集合,其中所有的key都是不同的
map类型可以写为map[K]V,其中K和V分别对应key和value
ages := make(map[string]int) // 内置的make函数可以创建一个map
ages := map[string]int{
"alice": 31,
"charlie": 34,
} //可以用map字面值的语法创建map,同时还可以指定一些最初的key/value
//以上创建方式与下面等价
ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34
结构体
// -- 声明结构体
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
//dilbert为Employee结构体
var dilbert Employee
// -- 结构体变量成员通过 点操作符 访问
dilbert.Salary -= 5000 // demoted, for writing too few lines of code
// -- 结构体比较 == 或 !=
//操作符 == 会比较结构体
type Point struct{ X, Y int }
p := Point{1, 2}
q := Point{2, 1}
//以下等价
fmt.Println(p.X == q.X && p.Y == q.Y) // "false"
fmt.Println(p == q) // "false"
Json****
JavaScript对象表示法(JSON)是一种用于发送和接收结构化信息的标准协议。在类似的协议中,JSON并不是唯一的一个标准协议。 XML(§7.14)、ASN.1和Google的Protocol Buffers都是类似的协议,并且有各自的特色,但是由于简洁性、可读性和流行程度等原因,JSON是应用最广泛的一个。
type Movie struct {
Title string
Year int `json:"released"` //Tag 变量在json中的Key不是Year而会变为released
//如 released:1942
Color bool `json:"color,omitempty"`
Actors []string
}
var movies = []Movie{
{Title: "Casablanca", Year: 1942, Color: false,
Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
{Title: "Cool Hand Luke", Year: 1967, Color: true,
Actors: []string{"Paul Newman"}},
{Title: "Bullitt", Year: 1968, Color: true,
Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
// ...
}
许多web服务都提供JSON接口,通过HTTP接口发送JSON格式请求并返回JSON格式的信息。
三、函数
函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func name(parameter-list) (result-list) {
body
}
//e.g.
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3,4)) // "5"
四、方法
在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。
package geometry
import "math"type Point struct{ X, Y float64 }
// 函数
func Distance(p, q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
// p方法
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
//调用不会发生冲突
//第一个Distance的调用实际上用的是包级别的函数geometry.Distance,
//而第二个则是使用刚刚声明的Point,调用的是Point类下声明的Point.Distance方法。
p := Point{1, 2}
q := Point{4, 6}
fmt.Println(Distance(p, q)) // "5", function call
fmt.Println(p.Distance(q)) // "5", method call
五、接口
接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。