- 开始总是从“hello world”起步
package main
import "fmt"
func main() {
fmt.Println("hello")
}
Go中的main方法的报名必须是叫main(即使真实项目中所在的包名不是main)
基于由其他语言的基础开始了解GO(略过基础语法)
首先,Go语言反对函数和操作符重载(overload),
其次,Go语言支持类、类成员方法、类的组合,但反对继承,反对虚函数(virtual function) 和虚函数重载。
再次,Go语言也放弃了构造函数(constructor)和析构函数(destructor)在放弃了大量的OOP特性后,Go语言送上了一份非常棒的礼物:接口(interface)
Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继 承在内的大量面向对象特性,
只保留了组合(composition)这个基础的特性。
type Rect struct {
x, y float64
width, height float64 }
Go语言对关键字的增加非常吝啬,其中没有private、protected、public这样的关键 字。要使某个符号对其他包(package)可见
(即可以访问),需要将该符号定义为以大写字母 开头:
type User struct {
Name string
Age int32
Address string
}
package main
type Integer int
func main() {
var a Integer = 1
a.Max(4)
}
func (a Integer) Max(b Integer) bool {
return a > b
}
上面的这个Integer例子如果不使用Go语言的面向对象特性,而使用之前我们介绍的面向
过程方式实现的话,相应的实现细节将如下所示
// 面向过程 什么是方法?什么是函数?
func Integer_Max(a Integer, b Integer) bool {
return a > b
}
Go语言中的面向对象为直观,也无需支付额外的成本。如果要求对象必须以指针传递, 这有时会是个额外成本,
因为对象有时很小(比如4字节),用指针传递并不划算。
只有在你需要修改对象的时候,才必须用指针。它不是Go语言的约束,而是一种自然约束。
//正确写法
func (a *Integer) Add(b Integer) {
*a += b
}
//错误写法
func (a Integer) Add(b Integer) {
a += b
}
- 其他语言接口例子 在C++、Java和C#中,为了实 现一个接口,你需要从该接口继承
interface IFoo {
void Bar();
}
class Foo implements IFoo { // Java文法
// ...
}
class Foo : public IFoo { // C++文法
// ...
}
IFoo* foo = new Foo;
- 非侵入式接口
在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口
type Person interface {
GerName() string
}
func NewMan() *Man {
return &Man{Name: "aaa"}
}
type Man struct {
Name string
Age int
}
func (mam *Man) GerName() string {
return mam.Name
}
由于Go语言中任何对象实例都满足空接口 interface{} ,所以 interface{} 看起来像是可
以指向任何对象的 Any 类型。
fmt.XXX是个很好例子
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
- 协程。协程(Coroutine)本质上是一种用户态线程,不需要操作系统来进行抢占式调度,
且在真正的实现中寄存于线程中,因此,系统开销极小,可以有效提高线程的任务并发
性,而避免多线程的缺点。使用协程的优点是编程简单,结构清晰;缺点是需要语言的
支持,如果不支持,则需要用户在程序中自行实现调度器。目前,原生支持协程的语言还很少。
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
开启一个协程:
go Add(1, 1)
真的这么简单么?
package main
import "fmt"
func sayHello() {
fmt.Println("hello")
}
func main() {
go sayHello()
fmt.Println("main")
}
上面这个程序会输出什么?
main
问题分析:
Go程序从初始化main package并执行main()函数开始,当main()函数返回时,程序退出,
且程序并不等待其他goroutine(非主goroutine)结束。
- 方法一:
package main
import (
"fmt"
"sync"
)
var lock sync.WaitGroup
func sayHello() {
fmt.Println("hello")
lock.Done()
}
func main() {
lock.Add(1)
go sayHello()
lock.Wait()
fmt.Println("main")
}
下一章节做答案讲解
- Mutex: 互斥锁
- RWMutex:读写锁
- Once:执行一次
Once 是一个可以被多次调用但是只执行一次,若每次调用Do时传入参数f不同,但是只有第一个才会被执行。</br>
用法:
func (o *Once) Do(f func())
sync.Once的使用场景例如单例模式、系统初始化。
例如并发情况下多次调用channel的close会导致panic,解决这个问题我们可以使用sync.Once来保证close只会被执行一次。
type Once struct {
m Mutex
done uint32
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 1 {
return
}
// Slow-path.
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
- Cond:信号量
- Pool:临时对象池
- Map:自带锁的map
- WaitGroup 并发等待组
用法:
func (wg *WaitGroup) Add(delta int)
func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
案例:
var synWait sync.WaitGroup
func TestSync_WaitGroup(t *testing.T) {
num:=100
for i:=0;i<num;i++{
synWait.Add(1)
go func() {
...
synWait.Done()
}()
}
synWait.Wait()
...
}
channel是Go语言在语言级别提供的goroutine间的通信方式。我们可以使用channel在两个或
多个goroutine之间传递消息。
func Count(ch chan int) {
ch <- 1
fmt.Println("Counting")
}
func main() {
chs := make([]chan int, 10)
for i := 0; i < 10; i++ {
chs[i] = make(chan int)
go Count(chs[i])
}
for _, ch := range(chs) {
<-ch
}
}
channel是类型相关的。也就是说,一个channel只能传递一种类型的值,这个类型需要在声
明channel时指定。
声明一个不带缓存的通道
ch := make(chan int)
声明一个带缓存的通道
c := make(chan int, 1024)
Go语言引入了一个关于错误处理的标准模式,即error接口,该接口的定义如下:
type error interface {
Error() string
}
在Go编程中异常情况都是用error表示,go函数可以多个返回值的特点,一般error作为最后的一个
返回值,用于说明返回值是否可靠或者程序执行是否成功。
func Stat(name string) (fi FileInfo, err error) {
var stat syscall.Stat_t
err = syscall.Stat(name, &stat)
if err != nil {
return nil, &PathError{"stat", name, err}
}
return fileInfoFromStat(&stat, name), nil
}
创建新error:
errors.New("download error")
自定义error:
type myError struct {
msg string
}
func (err myError) Error() string {
return err.msg
}
-
defer关键字 defer一般用于进行连接或者流的关闭工作,相当于Java中finally的作用,
-
panic()和recover() Go语言引入了两个内置函数panic()和recover()以报告和处理运行时错误和程序中的错误场景:
panic():
//正常的函数执行流程将立即终止,并报告错误;这个过程称为:错误处理流程
panic(errors.New("download error"))
recover()和defer一起使用,相对于catch异常操作
用例