GO语言语法进阶指南

[TOC]

GO语言语法进阶指南

本笔记中没有基础语法介绍,适合学习完基础语法之后,想进一步了解go语言特性的。

代码中有很多注释说明,会参与一些额外的知识说明

内建方法

内建方法,就是go语言自带的一种方法,不需要额外的引用包。

内建方法一般都会有使用场景,这个需要注意,多使用之后就会熟悉了。

make:

功能:创建 slice、map、chan,完成内存的初始化。

说明:

  • slice和数组的区别:数组初始化内存之后,大小固定,无法扩容,slice可以通过内建函数进行扩容
  • chan是go语言中独有的一种类型,叫做管道,也可以定义大小。可以完成协程的数据交互

返回:返回类型引用,而不是指针

package main

import "fmt"

func main() {
   // 提示,所有的内建函数和内建类型,可以参考源码:builtin/builtin.go 文件
   makeSlice()
   fmt.Println("-----------------------------")
   makeMap()
   fmt.Println("-----------------------------")
   makeChan()
}

//以下为主要测试函数
// make test
func makeSlice() {
   mSlice := make([]string, 2)
   mSlice[0] = "a"
   mSlice[1] = "b"
   fmt.Println("len(mSlice):{},  cap(mSlice):{} ", len(mSlice), cap(mSlice))
   // 内建函数 append 会进行扩容
   mSlice = append(mSlice, "d")
   mSlice = append(mSlice, "e")
   mSlice = append(mSlice, "f")
   fmt.Println(mSlice)
   fmt.Println("len(mSlice):{},  cap(mSlice):{} ", len(mSlice), cap(mSlice))
}

// make map
func makeMap() {
   // interface 代表的就是value可以为int,string,这些
   mMap := make(map[string]interface{}, 3)
   mMap["a"] = 1
   mMap["b"] = "BBB"
   mMap["c"] = "CCC"
   mMap["d"] = 4
   fmt.Println(mMap)
   fmt.Println("len(mMap):{}", len(mMap))
   // map 类型无法使用 append
}

// make chan
func makeChan() {
   //管道内的容量为3 ,可以保存 3 个 int 数据
   // 暂不讲解使用方式
   mChan := make(chan int, 10)
   close(mChan)
}

new:

New 方法和make方法是有区别的

1、new出来的,内存会置为0, make可以直接初始化指定内存大小的对象

2、返回穿入类型的指针地址,new的类型可以任意,但是返回的只是指向这一类型的指针地址,make返回的是类型引用

func main() {
    returnType()
}

func returnType() {
    nMap := new([]string)
    mMap := make([]string, 5)

    fmt.Println("nMap type:{}", reflect.TypeOf(nMap))
    fmt.Println("mMpa type:{}", reflect.TypeOf(mMap))
    // nMap因为没有初始化,无法使用len和cap获取长度和容量大小,会报错
    // fmt.Println("nMap len:{}, nMap:{}", len(nMap), cap(nMap))
    fmt.Println("mMpa len:{}, cap:{}", len(mMap), cap(mMap))
}

// 返回结果:
// nMap type:{} *[]string
// mMpa type:{} []string
// mMpa len:{}, cap:{} 5 5

append&delete:

append和delete是针对于上面两种方式创建变量的一些操作的。不同操作可能要区分类型,如下:

slice -> append & copy

Map -> delete

append&copy:

append追加元素,可以进行自动扩容。

copy并不会对 目标对象进行扩容,如果长度较小,则可能丢数据

// make test append & copy
func testAppend() {
    mSlice := make([]string, 2)
    mSlice[0] = "a"
    mSlice[1] = "b"
    fmt.Printf("len(mSlice):%d,  cap(mSlice):%d ", len(mSlice), cap(mSlice))
    // 内建函数 append 会进行扩容
    mSlice = append(mSlice, "d")
    mSlice = append(mSlice, "e")
    mSlice = append(mSlice, "f")
    fmt.Println(mSlice)
    fmt.Printf("len(mSlice):%d,  cap(mSlice):%d ", len(mSlice), cap(mSlice))
    // 使用copy 复制
    targetSlice := make([]string, 4)
    i := copy(targetSlice, mSlice)
    // targetSlice 长度就是4 ,所以就复制了4个,并没有完全复制
    fmt.Printf("cSlice:%s, i:%d", targetSlice, i)
}

// 结果:
// len(mSlice):2,  cap(mSlice):2 [a b d e f]
// len(mSlice):5,  cap(mSlice):8 cSlice:[a b d e], i:4

Delete:

delete删除map类型的数据,格式为delete(map, key),

没有返回数据

func deleteMap() {
    mMap := make(map[string]interface{}, 3)
    mMap["a"] = 1
    mMap["b"] = "BBB"
    mMap["c"] = "CCC"
    mMap["d"] = 4
    fmt.Println(mMap)
    fmt.Println("len(mMap):{}, mao", len(mMap))
    // 删除存在的key
    delete(mMap, "c")
    fmt.Println(mMap)
    delete(mMap, "d")
    fmt.Println(mMap)
    // 删除不存在的key, 没有任何操作,也不会报错
    delete(mMap, "c")
    fmt.Println(mMap)
}
// 结果:
// map[a:1 b:BBB c:CCC d:4]
// len(mMap):{}, mao 4
// map[a:1 b:BBB d:4]
// map[a:1 b:BBB]
// map[a:1 b:BBB]

Panic&recover:

go语言中一对异常处理组合,不同于 java或者python的 throw、try catch方式

Panic:抛出异常

Recover:捕获异常

// ====================================Panic&recover 异常抛出和捕获 测试===========================================
func testException() {
    // 通过defer 声明匿名函数,捕获调用函数抛出来的异常
    // defer的使用稍后详细补充
    // 这个匿名函数也可以拆出来,比如,defer recoverException(),在任何需要捕获异常的地方都可以使用
    //defer func() {
    //  errInfo := recover()
    //  fmt.Println("异常信息为:", errInfo)
    //}()
    defer recoverException()
    // 会捕获到第一个异常, 可以更换函数的位置,体验不同的效果
    throwException03()
    throwException()
    throwException02()
}

func recoverException() {
    // Panic 抛出来的不一定是error类型,也可以是用户自定义的一种结构体
    reInfo := recover()
    // fmt.Println("异常信息为:", reInfo)
    // 可以对返回结果进行处理,根据recover的结果,做出判断
    switch reInfo.(type) {
    case string:
        fmt.Println("返回字符串为:", reInfo)
    case error:
        fmt.Println("异常信息为:", reInfo)
    default:
        fmt.Println("default 信息为:", reInfo)
    }
}

// 直接抛出一个字符串
func throwException() {
    panic("a ba a ba 0101")
}
// 直接抛出一个字符串
func throwException02() {
    panic("a ba a ba 0202")
}

// 真实的抛出一个异常
func throwException03() {
    panic(errors.New("panic a error"))
    // 或者使用:panic(fmt.Errorf("panic a error")) 创建异常都可以
}

func main() {
    testException()
}

// 返回结果:
// 异常信息为: panic a error

Len&cap&close:

Len:变量的长度,slice、chan,string,array,map

cap:变量的容量,slice、chan、array,map无法使用,

close:关闭chan,chan比较占用资源,是协程的一个通道,使用完的时候,尽量关闭。

总结:

内建方法 功能描述 其他说明
Make() 创建 slice、map、chan,完成内存的初始化。 返回类型引用,而不是指针
New() 创建类型变量的另外一种方式 和new的区别:<br />1、new出来的,内存会置为0, make可以指定内存大小<br />2、new 返回的是类型指针,不是引用
Append() append追加元素,并可以进行自动扩容。
Copy() 复制对象 copy并不会对 目标对象进行扩容,如果长度较小,则可能丢数据
Delete() 删除map类型指定key的数据 没有的key进行删除不会抛出异常
Panic() 抛出异常
Recover() 捕获异常
Len() 变量的长度 作用在:slice、chan,string,array,map
Cap() 变量的容量 作用在:slice、chan、array,map无法使用,
Close() 关闭chan chan比较占用资源,是协程的一个通道,使用完的时候,尽量关闭。

结构体:

若干属性的一个集合

是面向对象的一个体现

创建&初始化:

首先定义一个结构体,然后创建一个该类型的 对象

可以直接通过var去声明一个,或者通过new的方式创建该类型的一个指针

// Dog 定义dog结构体
type Dog struct {
    // 品种
    variety string
    name    string
    color   string
}

func main() {
    // 创建方式 1
    var dog01 Dog
    dog01.name = "test"
    dog01.variety = "小黄狗"
    dog01.color = "yellow"
    fmt.Println("创建方式 1:", dog01)
    // 创建方式 2
    // := 这是一种语法糖,在函数内,声明变量和赋值一起。函数外不可以使用该语法糖
    dog02 := Dog{color: "red"}
    dog02.name = "test02"
    fmt.Println("创建方式 2:", dog02)
    // 创建方式 3
    dog03 := new(Dog)
    dog03.name = "test03"
    // 三种不同的打印方式,可以观察new出来的指针
    // 打印的结果带有&,表示为一个指针
    fmt.Println("创建方式 3:", dog03)
    // 通过 * 获取地址内的对象,* 根据地址取值
    fmt.Println("创建方式 3, *dog03: ", *dog03)
    // 通过& 取地址符, 获取地址
    fmt.Println("创建方式 3, &dog03:", &dog03)
}
// 打印结果:
// 创建方式 1: {小黄狗 test yellow}
// 创建方式 2: { test02 red}
// 创建方式 3: &{ test03 }
// 创建方式 3, *dog03:  { test03 }
// 创建方式 3, &dog03: 0xc00000e030

属性及函数:

函数:必须初始化结构体之后,才可以调用函数

作用域:通过函数名称的大写和小写却分函数的作用域,go语言中只有private和public两种区别

备注:java中其实是有四种作用域,private、protect、public、default

结构体中的函数声明,并不是直接写到结构体中的,通过func (d *Dog)Run(){}这种形式

// Dog 定义dog结构体
type Dog struct {
    // 品种
    variety string
    name    string
    color   string
}

func (d Dog) Run() {
    fmt.Println("the dog is running ..... ")
}
// 测试结构体中的函数
func testStructsMethod() {
    d := Dog{}
    d.name = "小黄"
    fmt.Println(d)
    d.Run()
}

func main() {
    testStructsMethod()
}

组合:

面向对象的一个特性就是继承 ,go语言中是通过结构体和结构体的组合形式,实现继承关系的。

// animal类型
type animal struct {
    id         int8
    animalType string
}

func (a animal) eat() {
    fmt.Println("Animal is eating")
}

// Dog 定义dog结构体
type Dog struct {
    // 继承 animal结构体
    animal
    // 品种
    variety string
    name    string
    color   string
}

func (d Dog) Run() {
    fmt.Println("the dog is running ..... ")
}


// 测试结构体中的函数
func testStructsMethod() {
    d := Dog{}
    d.name = "小黄"
    d.animalType = "dog"
    fmt.Println(d)
    d.Run()
    d.eat()
}

func main() {
    testStructsMethod()
}

接口

go语言可以通过接口,可以抽象、封装和多态,这些也是面向对象的一些特征

概念及实现:

接口如何声明,如何实现已经声明的这些接口

接口通过interface进行声明的,接口的实现是直接针对结构体,实现写所有接口中定义的方法,即为实现接口,不用显示的声明实现这个接口

// Behavior
// 通过interface声明结构为一个接口
// 接口是一些方法的集合
type Behavior interface {
    Run() string
    Eat() string
}

type Animal struct {
    id         int8
    animalType string
}

func (a Animal) Eat() {
    fmt.Println("Animal is eating")
}

// Dog 定义dog结构体
type Dog struct {
    // 继承 animal结构体
    Animal
    // 品种
    variety string
    name    string
    color   string
}

// 直接实现接口中定义的方法,即为实现接口
// 在goland中可以看到已经有实现接口的标识
func (d Dog) Eat() string {
    fmt.Println("the Animal is eating ..... ")
    return "eating"
}

func (d Dog) Run() string {
    fmt.Println("the dog is running ..... ")
    return "running"
}

func testInterface() {
    d := Dog{}
    d.name = "小黄"
    d.animalType = "dog"
    fmt.Println(d)
    fmt.Println(d.Run())
    fmt.Println(d.Eat())
}

func main() {
    testInterface()
}

多态:

多态其实就是同一个接口,具有不同的实现,便实现了多态

下面,Dog和Cat 的结构体都实现了Behavior接口,实现多态的效果

还稍微使用了一下,结构体中的标签,这个后面常用,这里可以做了解一下

结构体中的大小写,同样具有作用域的效果,如果为小写,json序列化的时候,可能会被忽略,无法序列化

// Behavior
// 通过interface声明结构为一个接口
// 接口是一些方法的集合
type Behavior interface {
    Run() string
    Eat() string
}

type Animal struct {
    id         int8
    animalType string
}

// Dog 定义dog结构体
type Dog struct {
    // 继承 animal结构体
    Animal
    // 品种
    variety string
    name    string
    color   string
}

// Eat 直接实现接口中定义的方法,即为实现接口
func (d Dog) Eat() string {
    fmt.Println("the Dog is eating ..... ")
    return "eating"
}

func (d Dog) Run() string {
    fmt.Println("the Dog is running ..... ")
    return "running"
}

// Cat 定义Cat结构体
type Cat struct {
    // 继承 animal结构体
    Animal
    // 品种
    variety string `default:"good"`
    // 扩展,go语言中接口体的默认值,和结构体序列化之后,json中的key名称
    // 这里使用的是json,还有其他xml,ason,bson这些
    // 这也是go语言中所说的打标签,参考:https://juejin.cn/post/7005465902804123679
    Name  string `json:"Name" default:"橘猫"`
    color string
}

// Eat 直接实现接口中定义的方法,即为实现接口
func (c Cat) Eat() string {
    fmt.Println("the Eat is eating ..... ")
    return "eating"
}

func (c Cat) Run() string {
    fmt.Println("the Eat is running ..... ")
    return "running"
}

func testInterface() {
    d := Dog{}
    d.name = "小黄"
    d.animalType = "dog"
    fmt.Println(d)

    fmt.Println(d.Run())
    fmt.Println(d.Eat())
    fmt.Println("=============================")

    c := Cat{}
    c.color = "black"
    // 没有序列化
    fmt.Println(c)
    result, _ := json.Marshal(c)
    // 序列化之后
    // 即使序列化之后,仍然看不到默认值
    fmt.Println(string(result))
    fmt.Println(c.Run())
    fmt.Println(c.Eat())
}

func main() {
    testInterface()
}

总结:

结构体的声明:interface,

结构的实现:直接实现接口中所有的方法实现,出入参保持一致

多态:不同结构体,同一实现一个接口的所有方法,即实现了多态的效果

并发

go语言的并发,是通过协程实现的,而且go语言中的协程资源占用很小,一个协程初始化之后只有几KB。实际的可能需要看执行的函数内容和其他属性定义决定。

  • 更轻量:资源更小,几KB,
  • 更易用:直接一个go关键字就可以开启一个协程,go run()即可
  • 更便捷:更容易嵌入其他程序中,没有java或者python中的进程或者协程那么繁琐

多协程通信:chan管道通信,chan可以实现信号和信号量的效果

多协程同步:协程A在处理某个数据,协程B不应该在A操作过程中,对数据做处理,否则会让A获取到脏数据。java 和python会使用锁机制,go语言中也可以使用多协程同步,实现锁的机制。特别是针对于大数据的处理。

协程:

默认go语言使用的协程,占用的是所有的CPU核心数。

一般会使用代码限制协程占用CPU的核心数,默认留出一个核心,给系统或者其他调度服务

  1. 协程在执行期间,主进程停止之后,会停止正在运行中的协程
  2. runtime.GOMAXPROCS( n int )进行设置使用CPU核心数
  3. runtime.NumCPU() 获取服务器CPU核心数
// 循环测试函数
func goLoopTest01() {
    for i := 0; i < 20; i++ {
        time.Sleep(1 * time.Second)
        fmt.Println("goLoopTest01 goLoop : ", i)
    }
}

// 循环测试函数 02
func goLoopTest02() {
    for i := 0; i < 20; i++ {
        time.Sleep(1 * time.Second)
        fmt.Println("goLoopTest02 goLoop  : ", i)
    }
}

func main() {
    // 获取服务器CPU核心数
    cpu := runtime.NumCPU()
    fmt.Println("服务器CPU核心数为: ", cpu)
    // 限制协程运行的核心数,2个CPU核心
    runtime.GOMAXPROCS(2)
    // 这样运行过快,main结束,则协程也会被关闭,需要额外注意
    go goLoopTest01()
    go goLoopTest02()
    // 这样等待10秒,两个协程打印,但是不可能等待所有打印出来
    time.Sleep(10 * time.Second)
    fmt.Println("main函数执行结束")
}

协程通信:

协程之间的通信和同步,都是通过管道chan接受和发送数据。

一般也会通过select配合使用,不停的获取数据。

// 声明一个管道,和声明管道内部数据类型
var channel chan int = make(chan int, 10)
var channel02 chan int = make(chan int, 10)

// Send 发送数据
func Send() {
    channel <- 1
    channel <- 2
    channel <- 3
    time.Sleep(2 * time.Second)
    channel <- 4
    channel <- 5
}

// Receive 接收管道里的数据
func Receive() {
    num := <-channel
    fmt.Println(num)
    for {
        // select 会不停的获取管道的数据
        select {
        // case 可以写多个,从不同的管道中获取
        case num := <-channel:
            fmt.Println("管道channel获取到的数据 : ", num)
            // 写多个
        case num02 := <-channel02:
            fmt.Println("管道channel02获取到的数据 : ", num02)
        }
    }
}

func main() {
    go Send()
    go Receive()
    time.Sleep(10 * time.Second)

协程同步:

系统工具:sync.WaitGroup{} ,可以实现多协程的同步,比如:读写的操作,要等写完之后,才可以读取

add(delta int):添加协程记录

Done():移除协程记录

Wait():同步等待所有记录的协程记录全部结束

说明:所有add和done的次数必须一致,否则可能导致一直等待

var WG = sync.WaitGroup{}

func Read() {
    // 添加读者
    for i := 0; i < 3; i++ {
        fmt.Println("Read WG add 1")
        WG.Add(1)
    }
}

func Write() {
    // 等待一个读者 完毕,WG减少一个
    for i := 0; i < 3; i++ {
        fmt.Println("Write WG done 1")
        time.Sleep(1 * time.Second)
        WG.Done()
    }
}

func main() {
    // 主进程内,读取
    Read()
    // 开启协程 写入数据
    go Write()
    // 等待所有协程执行完毕
    WG.Wait()
    fmt.Println("all gorotine done....")
}

总结:

知识点 描述
如何启动一个协程 关键字go直接启动
CPU核心数使用的设置 runtime.GOMAXPROCS( n int )进行设置使用CPU核心数 <br />runtime.NumCPU() 获取服务器CPU核心数
协程之间的通信 类型chan进行通信,配合select进行循环获取
如何控制协程之间的同步 sync.WaitGroup{} 控制协程同步。具有add,done,waite方法

指针

指针的操作,主要包括:

  • 基本使用
  • 指针数组
  • 指向指针的指针
  • 值传递和指针传递

指针基本使用:

定义指针变量

为指针变量赋值

访问指针变量中指向地址的值

go语言不支持指针的运算

// & 取地址,获取某个变量的地址
// * 指针运算符,表示一个变量是指针类型,也可以表示一个指针变量所指向的存储单元, 也就是这个地址所存储的值.
func main() {
    var count int = 20
    var countPoint *int
    var countPoint01 *int
    // & 取地址,获取某个变量的地址
    // * 指针运算符,表示一个变量是指针类型,也可以表示一个指针变量所指向的存储单元, 也就是这个地址所存储的值.
    countPoint = &count
    fmt.Println(countPoint)
    // count 和 countPoint 地址一样
    fmt.Printf("Count 地址:%x,CountPoint 地址: %x, CountPoint 内容:%d ", &count, countPoint, *countPoint)
    // countPoint01 地址,是空的
    fmt.Println("countPoint01 地址是:", countPoint01)
}

指针数组&数组指针:

func TestPointArray() {
    // 指针数组:一个数组里面保存的都是对象的指针
    // 数组指针:一个指向数组的指针
    // 创建变量
    a, b, c := 1, 2, 3
    //指针数组
    pointArr := [...]*int{&a, &b, &c}
    fmt.Println("指针数组, point Arr: ", pointArr)
    // 数组指针
    arr := [...]int{1, 2, 3}
    arrPoint := &arr
    fmt.Println("数组指针地址:", arrPoint)
}

func main() {
    TestPointArray()
}

JSON

go语言主要是通过encoding/json 进行序列化和反序列化的。

序列化:将对象和结构体,变为json格式,json.Marshal(v interface{}) ([]byte, error)

  • 结构体的序列化
  • Map的序列化

反序列化:将json格式变为对象,json.Unmarshal(data []byte, v interface{}) error

Tag:在go语言中,叫标签,有点类似java中的注解。写在结构体的后面,在序列化的时候,可以作为json中的key,反序列化的时候也作为一个映射。

json序列化:

type Server struct {
    ServerName string
    ServerIP   string
    ServerPort int
}

func Serialize() {
    // 创建一个server
    // 也可以这样创建 : server:= Server{ServerIP: "10.0.0.1", ServerName: "JsonTest", ServerPort: 8888}
    // new出来的是一个指针类型
    server := new(Server)
    server.ServerIP = "10.0.0.1"
    server.ServerName = "JsonTest"
    server.ServerPort = 8080
    // 打印原有的对象,指针的指针,才是指针原有地址的内容
    fmt.Println("server : ", *server)
    // 打印序列化之后的
    serverJson, err := json.Marshal(server)
    if err != nil {
        fmt.Println("序列化出现异常,异常信息为: ", err.Error())
    }
    fmt.Println("ServerJson is ", string(serverJson))

    // 序列化map类型
    serverMap := make(map[string]interface{})
    serverMap["ServerIP"] = "10.0.0.1"
    serverMap["ServerName"] = "JsonTestMap"
    serverMap["ServerPort"] = 8080
    m := make(map[string]string)
    m["test"] = "test"
    serverMap["ServerDetail"] = m
    resultMap, _ := json.Marshal(serverMap)
    fmt.Println("map序列化之后为:", string(resultMap))
}

func main() {
    Serialize()
}

json反序列化:

反序列化,就是将json转换为对应的结构体或者map,是上面的反向操作,这个就不在演示

json.Unmarshal(data []byte, v interface{}) error

语法糖

语法糖:增加语言的可读性和实用性,对语言原有逻辑并不造成任何影响

go语言中的语法糖:

  • ... 可变参数
  • := 声明、赋值、类型推断
func Candy(values ...string) {
    for i, value := range values {
        fmt.Printf("%d 个元素是%s \n", i, value)
    }
}

func CandyTest() {
    // := 声明变量,并推断变量类型,作用在方法内
    values := make([]string, 5)
    values = append(values, "test01", "test02", "test03", "test04")
    // 打印类型
    fmt.Println(reflect.TypeOf(values))
    // Candy函数中可以追加任意个数的参数, 但是类型都应该是字符串
    Candy("test01", "test02", "test03", "test04")
}

func main() {
    CandyTest()
}

Module介绍

Module介绍:

Go module 是go语言的一个包管理,类似于java的maven,python的pip 对引用的包的版本等管理。

这些命令还是希望多动手去敲,去使用。

命令 功能说明 备注
go mod init 初始化项目,使用go module 进行包管理,会生成go.mod和go.sum文件
go mod graph 可视化go项目包依赖的关系 graphvizechart也可以实现
go mod download 下载某个依赖包 go get
go mod tidy 会检测该文件夹目录下所有引入的依赖,写入 go.mod 文件<br />下载需要的依赖,删除无用的依赖
go mod verify 校验某个安装包,在go.mod文件中会记录文件的MD5
go mod why 查看为什么会依赖某一个包
go mod edit 编辑go.mod,添加或者删除依赖
go mod vendor 将依赖下载到当前项目vendor包下,实现
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 安装以及基本语法参考官方文档即可。 入门资源分享: Go 编程基础::无闻录制的新手入门视频教程 Go入门指南 :...
    RichardBillion阅读 558评论 0 4
  • 顺序编程 变量 常量 iota是一个特殊的常量,它会在每一个const关键字出现时重置为0,然后在下一个const...
    潘晓华Michael阅读 552评论 0 6
  • go语言语法 直接放图 Go 程序是通过 package 来组织的。一个文件夹既是一个包只有 package 名称...
    繁星沉黎阅读 1,450评论 0 2
  • 选择题 【初级】下面属于关键字的是()A. funcB. defC. structD. class参考答案:AC,...
    流雨声阅读 480评论 0 1
  • 能力模式 选择题 【初级】下面属于关键字的是()A. funcB. defC. structD. class 参考...
    灵魂深灵阅读 5,282评论 2 5