hello第一个程序
go package main //表示go代码所属的包工程依赖 import "fmt"// 我们需要使用fmt包中的Println()函数 func main() { fmt.Println("Hello, world. 你好,世界!") }
package声明,表达go代码所属的包,包时go与语言的最基本的分发单位
顺 序 编 程
变量
- 初始化 var v1 int = 10 // 正确的使用方式1 var v2 = 10 // 正确的使用方式2,编译器可以自动推导出v2的类型 v3 := 10 // 正确的使用方式3,编译器可以自动推导出v3的类型 前提变量不能被声明过
- 赋值 支持多重赋值 i, j = j, i
- 匿名变量 假 设GetName()函数的定义如下,它返回3个值,分别为firstName、lastName和 nickName: func GetName() (firstName, lastName, nickName string) { return "May", "Chan", "Chibi Maruko" } 若只想获得nickName,则函数调用语句可以用如下方式编写: _, _, nickName := GetName()
常量 - 常量定义 Go语言预定义了这些常量:true、false和iota。 iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被 重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。 从以下的例子可以基本理解iota的用法: const ( // iota被重设为0 c0 = iota // c0 == 0 c1 = iota // c1 == 1 c2 = iota // c2 == 2 ) const ( a = 1 << iota // a == 1 (iota在每个const开头被重设为0) b = 1 << iota // b == 2 c = 1 << iota // c == 4 ) const ( u = iota * 42 // u == 0 v float64 = iota * 42 // v == 42.0
类型
Go语言内置以下这些基础类型: 布尔类型:bool。 整型:int8、byte、int16、int、uint、uintptr等。 浮点类型:float32、float64。 复数类型:complex64、complex128。 字符串:string。 字符类型:rune。 错误类型:error。 此外,Go语言也支持以下这些复合类型: 指针(pointer) 数组(array)(传递时值类型) 切片(slice) 字典(map) 通道(chan) 结构体(struct) 接口(interface)
接口
执⾏机制
接⼝对象由接⼝表 (interface table) 指针和数据指针组成。
go struct Iface { Itab* tab; void* data; }; struct Itab { InterfaceType* inter; Type* type; void (*fun[])(void); };
接⼝表存储元数据信息,包括接⼝类型、动态类型,以及实现接⼝的⽅法指针。⽆论是反射还是通过接⼝调⽤⽅法,都会⽤到这些信息。数据指针持有的是目标对象的只读复制品,复制完整对象或指针。
接⼝技巧
让编译器检查,以确保某个类型实现接⼝。 var _ fmt.Stringer = (*Data)(nil) 某些时候,让函数直接 "实现" 接⼝能省不少事。 type Tester interface { Do() } type FuncDo func() func (self FuncDo) Do() { self() } func main() { var t Tester = FuncDo(func() { println("Hello, World!") }) t.Do() }
类型赋值给接
```go package main import "fmt" type ISpeaker interface { Speak() } type SimpleSpeaker struct { Message string } func (speaker *SimpleSpeaker) Speak() { fmt.Println("I am speaking? ", speaker.Message) } func main() { var speaker ISpeaker speaker = &SimpleSpeaker{"Hell"} speaker.Speak() }
```
deferred(递延,延期)
defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。 go func title(url string) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() ct := resp.Header.Get("Content-Type") if ct != "text/html" && !strings.HasPrefix(ct,"text/html;") { return fmt.Errorf("%s has type %s, not text/html",url, ct) } doc, err := html.Parse(resp.Body) if err != nil { return fmt.Errorf("parsing %s as HTML: %v", url,err) } // ...print doc's title element… return nil }
Panic异常
一般而言,当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(defer 机制)。随后,程序崩溃并输出日志信息。日志信息包括panic value和函数调用的堆栈跟踪信息。panic value通常是某种错误信息。对于每个goroutine,日志信息中都会有与之相对的,发生panic时的函数调用堆栈跟踪信息。通常,我们不需要再次运行程序去定位问题,日志信息已经提供了足够的诊断依据。因此,在我们填写问题报告时,一般会将panic异常和日志信息一并记录。 不是所有的panic异常都来自运行时,直接调用内置的panic函数也会引发panic异常;panic函数接受任何值作为参数。当某些不应该发生的场景发生时,我们就应该调用panic。比如,当程序到达了某条逻辑上不可能到达的路径:
Recover捕获异常
通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们可以在程序崩溃前,做一些操作。 go func Parse(input string) (s *Syntax, err error) { defer func() { if p := recover(); p != nil { err = fmt.Errorf("internal error: %v", p) } }() // ...parser... }