变量
// 变量声明与赋值
var i1 int
var i2 int = 2
// Outside a function, every statement begins with a keyword (var, func, and so on) and so the := construct is not available.
i3 := 3 // 变量声明赋值的简写
i4, i5 := 4, 5 // 多个变量平行赋值
// 常量
//Constants are declared like variables, but with the const keyword.
//Constants can be character, string, boolean, or numeric values.
//Constants cannot be declared using the := syntax.
const (
a = iota // iota遇到const初始值为0,同const中使用一次'+1'
b = iota
c // 常用中如果变量赋值表达式一样,后面的可以省略
b string = 0 // 可以明确指定类型
)
// 字符串
// Go中字符串是UTF-8的,并且不可修改
s1 := "Hello world"
// 多行字符串
s2 := `Starting part
Ending part`
s3 := "Starting part" +
"Ending part"
// 如下会错误,内部会有分号置入
// s2 := "Starting part"
// + "Ending part"
// ->
// s2 := "Starting part";
// + "Ending part";
// Some numeric conversions:
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// Or, put more simply:
i := 42
f := float64(i)
u := uint(f)
Basic Type
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
零值 zero value
When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a
default value
. Each element of such a variable or value is set to the zero value for its type:
- false for
booleans
,- 0 for
integers
,- 0.0 for
floats
,- ""or
strings
,- nil for
pointers
,functions
,interfaces
,slices
,channels
, andmaps
.This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.
// 1.
type Money float64
m1 := new(Money) // type *Money
m2 := Money(1) // type Money
fmt.Printf("%T, %T\n", m1, m2) // *main.Money, main.Money
fmt.Println(m1, *m1, m2) // 0xc820078220 0 1
// 2.
type Person struct {
ID int64
Name string
}
func (this *Person) run() {
fmt.Println("I'm running...")
}
// 对象类型,
var p1 Person
p3 := Person{}
// 指针类型,p2形式与p4相同
p2 := new(Person)
p4 := &Person{}
var p5 *Person = nil // 指针可以初始化为nil,
// 对于nil的对象可以调用其方法,但是不可以访问其字段,如果方法内访问字段也会报错
fmt.Printf("%T, %T, %T, %T, %T\n", p1, p2, p3, p4, p5)
// main.Person, *main.Person, main.Person, *main.Person, *main.Person
p1.run() // I'm running...
p5.run() // I'm running...
数据结构
数组(array)
定义,创建
arr1 := [3]byte{}
arr2 := [3]float64{}
// array、slice 和 map 的复合声明变得更加简单。
// 使用复合声明的array、slice和map,元素复合声明的类型与外部一致,则可以省略。
arr3 := [3][2]int{
{1, 2}, // 复合的此处简写,而不用写成[]int{1, 2}
{2, 3},
}
arr4 := [...]int{1, 2, 3} // Go会自动统计个数
// 复杂类型数组(test中常用)
data := []struct{
input string
output string
} {
{"ab", "ba"},
{"cd", "dc"},
}
访问
for i := 0; i < len(arr1); i++ {
fmt.Print(i, arr1[i])
}
for i, v := range arr1 {
fmt.Print(i, v)
}
值传递
Go语言中数组是一个值类型(value type)。 所有值类型变量在**
赋值
和作为参数传递
**的时候都会产生一次复制动作。
package main
import "fmt"
func modify(array [5]int) {
array[0] = 10
// out: 10, 2, 3, 4, 5
fmt.Println("In modify(), array values:", array)
}
func main() {
array := [5]int{1,2,3,4,5}
modify(array)
// out: 1, 2, 3, 4, 5
fmt.Println("In main(), array values:", array)
}
切片(Slice)
数组切片的数据结构: 一个指向原数组的指针(当切片增加超过数组容量,数组会扩容,新建数组,复制数据) 数组切片中的元素个数 数组切片已分配的存储空间
创建
// 基于数组创建
// 此时切片内部引用数组,修改会影响到数组,
// 但是如果切片append后进行扩容 那便会引用新的数组,再进行修改不会影响原数组了
a1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// a1[from:to] to <= len(a1)
s1 := a1[:]
s2 := a1[:5]
// 直接创建
// len:5 cap:5 value:[1,2,3,4,5]
s3 := []int{1,2,3,4,5}
// len:5 cap:5 value:[0,0,0,0,0]
s4 := make([]int, 5)
// len:0 cap:5 value:[]
s5 := make([]int, 0, 5)
// 基于切片创建 s1[from:to] to <= cap(s1)
s6 := s5[:3]
遍历
for i := 0; i<len(s1); i++ {
fmt.Println(i, s1[i])
}
for i,v := range s1 {
fmt.Println(i, v)
}
动态增减元素
切片内部指向一个**
数组
,但其len
与cap
是两个值,并且可以随着元素增加动态扩容
**(从新指向一个新的数组),这也就需要内部重新分配空间并且进行数据复制。 函数append
向slice s
追加零或N个值,并且返回追加后的新的、与 s 有相同类型的 slice. 如果 s 没有足够的容量存储追加的值,append 分配一 个足够大的、新的 slice 来存放原有 slice 的元素和追加的值。因此,返回 的 slice 可能指向不同的底层 array。
s1 := make([]int, 5, 10)
// out: len:5 cap:10 // s1中的内容[0, 0, 0, 0, 0]
fmt.Println("len:", len(s1), " cap:", cap(s1))
// 往切片新增元素
append(s1, 1, 2, 3)
// 往切片新增另一个切片,注意'...'
append(s1, s2...)
内容复制
copy(s1, s2)
:将s2内容复制到s1中。 如果两个数组切片**len
不一样,就会安其中较小的len
**进行复制操作
s1 := []int{1, 2, 3, 4, 5}
s2 := []int{5, 4, 3}
// 将s2的3个元素复制到s1的前三位
copy(s1, s2)
// 只会复制s1的前三个元素到s2的前三位
copy(s2, s1)
Map
申明创建
type Person struct {
ID int
Name string
Addr string
}
m0 := map[string] int {
"a": 1,
"b": 2, // 最后一个元素后面逗号是必须的
}
m1 := map[string] Person {
"Tom" : Person{
ID: 1,
Name: "Tom",
Addr: "beijing",
},
"Jack" : Person{
ID: 2,
Name: "Jack",
Addr: "shanghai",
},
}
var m2 map[string] Person = make(map[string] Person)
// var m2 map[string] Person = make(map[string] Person, 20)
// value:可以设置为任意type
m3 := make(map[string] interface{})
赋值
m1["name"] = "Tom"
m1["jack"] = Person {
ID: 1
}
删除
如果“name”这个键不存在,那么操作将不会有什么作用 但是如果传入的**
key
值是nil
**,该调用将导致程序抛出异常(panic)。
delete(m1, "name")
查找
if person, ok := m1["Tom"]; ok {
fmt.Println(person)
}
流程控制
条件语句
// if语句使用,条件语句可以不适用括号,花括号是必须的
if a < 5 {
// do something...
} else {
// do something...
}
// if语句中添加初始化语句,变量作用域为if内
if obj, ok := m["name"]; ok {
// do something...
}
选择语句
条表达式不限制为常量或者整数; 与C语言等规则相反,Go语言不需要用break来明确退出一个case;
// case 1
switch s {
case "a", "A": // 单个case中,可以出现多个结果选项;逗号隔开
fmt.Printf("a")
case "b": fallthrough // 只有在case中明确加fallthrough关键字,才会继续执行紧跟的下一个case;
case "B":
fmt.Println("b")
default:
fmt.Println("default")
}
// case 2:switch可以不加条件表达式,此时等同于多个if...else...
switch {
case core > 90 :
fmt.Println("Great")
case core > 60 :
fmt.Println("Good")
default :
fmt.Println("No good")
}
循环语句
Go中的循环三种用法
for init; condition; post { }
for condition { }
for { }
// case 1:普通
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
// case 2:无限循环
for {
if sum > 100 {
break;
}
}
// case 3:多重赋值
a := []int{1, 2, 3, 4, 5, 6}
for i, j := 0, len(a) – 1; i < j; i, j = i + 1, j – 1 {
a[i], a[j] = a[j], a[i]
}
// case 4:break加控制参数
for j := 0; j < 5; j++ {
for i := 0; i < 10; i++ {
if i > 5 {
break JLoop
}
fmt.Println(i)
}
}
JLoop:
J: for j := 0; j < 5; j++ {
for i := 0; i < 5; i++ {
fmt.Println(j, i)
if(i == 2) {
break J
}
}
}
跳转语句
func myfunc() {
i := 0
HERE:
fmt.Println(i)
i++
if i<10 {
go HERE
}
}
函数
func
(p mytype)
funcname
(q int)
(r,s int)
{ return 0,0 }
- 关键字
func
用于定义一个函数;- 函数可以绑定到特定的类型上。这叫做接收者。有接收者的函数被称作
method
。funcname
是你函数的名字;- **
int
类型的变量q作为输入参数。参数用pass-by-value
**方式传递,意味着它们会被复制;- 变量 r 和 s 是这个函数的 命名返回值。在 Go 的函数中可以返回多个值。如果不想对返回的参数命名,只需要提供类型:
(int,int)
。 如果只有一个返回值,可以省略圆括号。如果函数是一个子过程,并且没有任何 返回值,也可以省略这些内容;- 这是函数体。注意 return 是一个语句,所以包裹参数的括号是可选的。
函数定义
func Add(a int, b int) (ret int, err error) {
if a < 0 || b < 0 {
err = erroros.New("Should be non-negative numbers!")
return
}
return a + b, nil
}
// 参数,返回值类型相同可以合并,返回值只有一个可以简写
func Add2(a, b int) int {
// ...
}
约定
小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。 这个规则也适用于类型和变量的可见性。
不定参数
// ...type格式为不定参数,只能作为最后一个参数
func myfunc(args ...int) {
for i,v := range args {
fmt.Println(i, v)
}
}
// 不定参数函数的调用
myfunc(2, 3, 4)
myfunc(s1[:]...)
func myfunc2(args ...int) {
myfunc(args...)
myfunc(args[:]...)
}
// 任意类型不定参数
func myPrint(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, " is an int value.")
case string:
fmt.Println(arg, " is a string value.")
case int64:
fmt.Println(arg, " is an int64 value.")
default:
fmt.Println(arg, " is an unknow type.")
}
}
}
异常使用
// 自定义异常对象
type MyIllegalError struct {
Code int
Msg string
Info string
}
// 实现error接口的方法
func (this *MyIllegalError) Error() string {
return this.Msg
}
// 添加构造函数
func NewMyIllegalError(code int, msg, info string) *MyIllegalError {
return &MyIllegalError{
Code: code,
Msg: msg,
Info: info,
}
}
func add(a, b int) (ret int, err error) {
if a<0 || b<0 {
err = NewMyIllegalError(1, "参数错误", "id=1")
return
}
return a+b, nil
}
defer
func main() {
// defer按先进后出顺序执行
func1()
}
func func1() {
i := 1
// 参数在执行此句时传入,形成闭包,闭包内参数在执行的时候调用,
// 而后对i的修改回影响内部的直接引用,而影响不到参数
defer func(a int) {
fmt.Println("First", a, i)
}(i)
i = 2
defer func() {
fmt.Println("Second")
}()
}
//output:
//Second
//First 1 2
panic&recover
面向对象
接口赋值
go会为struct的**
func (this Obj) XXX()
** 生成func (this *Obj) XXX()
方法 因为两种方法内部操作完全一样,反之则不会 并且对于对象的指针成员操作,内部会自动使用*
type Money int
// go内部会生成 func (this *Money) Less(money Money) bool方法
func (this Money) Less(money Money) bool {
return this < money
}
// go无法生成func (this Money) Add(money Money)方法,
// 因为引用传递会改变外部的值,改为值传递无法达到相同的效果。反之可以,如上
func (this *Money) Add(money Money) {
*this += money
}
type LessAdder interface {
Less(money Money) bool
Add(money Money)
}
func main() {
m := Money(2)
//Go automatically handles conversion between values and pointers for method calls. You may want to use a pointer receiver type to avoid copying on method calls or to allow the method to mutate the receiving struct.
// 调用对象方法时候,go会在value跟pointer之间自动转换
m.Add(Money(10))
// cannot use m (type Money) as type LessAdder in assignment:
// Money does not implement LessAdder (Add method has pointer receiver)
// var la LessAdder = m // 此处会报错,因为m对象没有Add方法,m指针才有Add方法
var la LessAdder = &m
fmt.Println(la)
var lesser Lesser = &m
fmt.Println(lesser)
}
接口组合
type ReadWriter interface {
Reader
Writer
}
// 这个接口组合了Reader和Writer两个接口,它完全等同于如下写法:
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
type MyError struct {
Msg string
}
func (this MyError) Error() string {
return this.Msg
}
// 返回值error是interface所有可以使用&MyError返回,因为指针实现了接口方法
// 如果是struct则必须同时对象或者指针
func NewError(msg string) error {
return &MyError{
Msg: msg,
}
}
var e1 MyError // value : MyError{Msg:""}
var e2 *MyError // value : nil
// new(MyError) 等同于 &MyError{},所有字段零值
var e3 *MyError = new(MyError)
// 类型转换
// switch 中特有写法
func reflect(obj interface{}) {
switch obj.(type) {
case int:
fmt.Println("Int value")
case float64:
fmt.Println("Float64 value")
case string:
fmt.Println("string value")
default:
fmt.Println("Other value")
}
}
// 普通
if i, ok := obj.(I); ok {
fmt.Println(i.Get())
}
// 如果可以确定一个变量实现了某接口,可以使用
i := obj.(I) // 运行时可能会报错
并发
基础
Goroutine
channel
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
cb := make(chan string, 4) // 创建带缓冲区的通道
// 发送接收数据:<-
ci <- 1 // 发送整数1到ci // 如果通道被关闭,会立即返回通道类型的0值
<- ci // 从channel接收数据
i := <- ci // 从channel接收数据,保存到i
obj,ok := <- ch // ok,false:通道被关闭,true:通道可以读取数据
来源:oschina
链接:https://my.oschina.net/u/1164909/blog/807437