go语言学习(日更)

前段时间准备面试,学院里面考试以及家里面有些事情,还有我懒,耽搁了。
现在一切就绪,可以开新坑了~嘿嘿嘿
大部分资料都源自于互联网 但是大部分都是自己手打(侵删)

首先确保各位的GOPATH的版本大于等于1.8 保证环境的一致性
本人也是初学go语言,如果有错误希望各位指正。

基本程序

首先我们先写一个最最最简单的hello word吧

package main //包,表明代码所在的模块(包)
import "fmt" //引⼊代码依赖
//功能实现
func main() {
 fmt.Println("Hello World!")
}

以上,我们很容易看出(如果你有其他语言的基础的话)程序的入口
1.一定是在main包下:也就是package main //包,表明代码所在的模块(包)
2.必须要是在main方法:func main()

  1. 其实文件名不一定是main.go

此时肯定会有人问,既然是函数肯定会有返回值啦~
比如c语言默认会追加一个return

对,有道理但是

Go 中 main 函数不⽀持任何返回值
如果你需要有返回状态,你可以通过
通过 os.Exit 来返回状态

常量和变量

变量

接触常量和变量的时候我们先规定以后编写代码的时候用test程序

也就是

源码⽂件以 _test 结尾:xxx_test.go
测试⽅法名以 Test 开头:func TestXXX(t *testing.T) {…}
这样就不用重复开很多项目了(真棒)
和java的junit单元测试一样好用,还能打印日志

先写一个斐波那契数列吧

func TestFibList(t *testing.T) {
    // var a int = 1
    // var b int = 1
    // var (
    //  a int = 1
    //  b     = 1
    // )
    a := 1
    // a := 1
    b := 1
    t.Log(a)
    for i := 0; i < 5; i++ {
        t.Log(" ", b)
        tmp := a
        a = b
        b = tmp + a
    }

}

变量有多种赋值方式(在上面)
• 赋值可以进⾏⾃动类型推断
• 在⼀个赋值语句中可以对多个变量进⾏同时赋值

注意 a := 1 只能用于局部变量,不能作用于全局变量

常量

const: 关键字 使得被起修饰的变量可读不可写 保证其状态
同时:iota (偷懒的写法 如果不愿意一直+1 这个可以替代) 我暂时还没用它写过复杂的程序

基本数据类型

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

类型转化

  1. Go 语⾔不允许隐式类型转换
  2. 别名和原有类型也不能进⾏隐式类型转换
func TestImplicit(t *testing.T) {
    var a int32 = 1
    var b int64
    b = int64(a)
    var c MyInt
    c = MyInt(b)
    t.Log(a, b, c)
}

以上是显示类型转换 如果直接b=a就会报错
所以编程的过程中需要小心

指针类型

  1. 不⽀持指针运算(所以别想着指针操作下一个数组单位)
  2. string 是值类型,其默认的初始化值为空字符串,⽽不是 nil
func TestPoint(t *testing.T) {
    a := 1
    aPtr := &a
    //aPtr = aPtr + 1
    t.Log(a, aPtr)
    t.Logf("%T %T", a, aPtr)
}

运算符

就不多提 有编程经验的直接跳过

不过这块着重一点就是 Go 语⾔没有前置的 ++,- -
(作用的话,我猜应该是增加代码的可读性吧)

循环

也不多提
Go 语⾔仅⽀持循环关键字 for

n := 0
for n < 5 {
 n++
 fmt.Println(n)
}

怎么感觉好像更难读了1?不过没事,习惯就好了

switch条件

  1. 条件表达式不限制为常量或者整数;
  2. 单个 case 中,可以出现多个结果选项, 使⽤逗号分隔;
  3. 与 C 语⾔等规则相反,Go 语⾔不需要⽤break来明确退出⼀个 case;如果需要执行下一个case的话 可以加fallthrough关键字
  4. 可以不设定 switch 之后的条件表达式,在此种情况下,整个 switch 结
    构与多个 if…else… 的逻辑作⽤等同

数组和切片

这个很重要 以后有机会开个新坑形容一下底层原理

数组的声明

var a [3]int //声明并初始化为默认零值
a[0] = 1

b := [3]int{1, 2, 3} //声明同时初始化

元素的遍历

func TestTravelArray(t *testing.T) {
 a := [...]int{1, 2, 3, 4, 5} //不指定元素个数
 for idx/*索引*/, elem/*元素*/ := range a {
 fmt.Println(idx, elem)
 }
//当然 你要是不想要ind(索引的话)可以变为_ 不使用它就行
}

数组的截取

a[开始索引(包含), 结束索引(不包含)]
(简单来记就是左闭右开)

a := [...]int{1, 2, 3, 4, 5}
a[1:2] //2
a[1:3] //2,3
a[1:len(a)] //2,3,4,5
a[1:] //2,3,4,5
a[:3] //1,2,3

![X~VMAEVUTITZ3TFJ_O%2)6.png

这个挺复杂的后面详细讲,初学者会使用就行

切片的声明

var s0 []int
s0 = append(s0, 1)
s := []int{}
s1 := []int{1, 2, 3}
s2 := make([]int, 2, 4) 
 /*[]type, len, cap
 其中len个元素会被初始化为默认零值,未初始化元素不可以访问
 */

切⽚共享存储结构

image.png

数组和切片的区别

  1. 容量是否可伸缩
  2. 是否可以进⾏⽐较

Map基础

本节也很重要 不过作为初学者只要掌握使用即可

Map 声明

m := map[string]int{"one": 1, "two": 2, "three": 3}
m1 := map[string]int{}
m1["one"] = 1
m2 := make(map[string]int, 10 /*Initial Capacity*/)

Map还有一个很有意思的点,就是假如我设置一个key但是并未设置value 那么这个value设置为0值
此时 好处当然就是防止空指针异常呀
但是坏处也有,就是我这么知道这个key的value是真的为空还是就为0?
以下代码中 ok 为true存在 否则不存在

func TestAccessNotExistingKey(t *testing.T) {
    m1 := map[int]int{}
    t.Log(m1[1])
    m1[2] = 1
    t.Log(m1[2])
    m1[1] = 2
    if v, ok := m1[3]; ok {
        t.Logf("Key 3's value is %d", v)
    } else {
        t.Log("key 3 is not existing.")
    }
}

遍历map

func TestTravelMap(t *testing.T) {
    m1 := map[int]int{1: 1, 4: 4, 3: 9}
    for k, v := range m1 {
        t.Log(k, v)
    }
}

Map的扩展

Map 与⼯⼚模式
• Map 的 value 可以是⼀个⽅法
• 与 Go 的 Dock type 接⼝⽅式⼀起,可以⽅便的实现单⼀⽅法对象的⼯⼚模式

实现 Set
Go 的内置集合中没有 Set 实现, 可以 map[type]bool

  1. 元素的唯⼀性
  2. 基本操作
  1. 添加元素
  2. 判断元素是否存在
  3. 删除元素
  4. 元素个数
func TestMapWithFunValue(t *testing.T) {
    m := map[int]func(op int) int{}
    m[1] = func(op int) int { return op }
    m[2] = func(op int) int { return op * op }
    m[3] = func(op int) int { return op * op * op }
    t.Log(m[1](2), m[2](2), m[3](2))
}

用Map实现set

func TestMapForSet(t *testing.T) {
    mySet := map[int]bool{}
    mySet[1] = true
    n := 3
    if mySet[n] {
        t.Logf("%d is existing", n)
    } else {
        t.Logf("%d is not existing", n)
    }
    mySet[3] = true
    t.Log(len(mySet))
    delete(mySet, 1)
    n = 1
    if mySet[n] {
        t.Logf("%d is existing", n)
    } else {
        t.Logf("%d is not existing", n)
    }
}

string

  1. string 是数据类型,不是引⽤或指针类型
  2. string 是只读的 byte slice,len 函数可以它所包含的 byte 数
  3. string 的 byte 数组可以存放任何数据

函数

  1. 可以有多个返回值
  2. 所有参数都是值传递:slice,map,channel 会有传引⽤的错觉
  3. 函数可以作为变量的值
  4. 函数可以作为参数和返回值
func returnMultiValues() (int, int) {
    return rand.Intn(10), rand.Intn(20)
}

func timeSpent(inner func(op int) int) func(op int) int {
    return func(n int) int {
        start := time.Now()
        ret := inner(n)
        fmt.Println("time spent:", time.Since(start).Seconds())
        return ret
    }
}

func slowFun(op int) int {
    time.Sleep(time.Second * 1)
    return op
}

func TestFn(t *testing.T) {
    a, _ := returnMultiValues()
    t.Log(a)
    tsSF := timeSpent(slowFun)
    t.Log(tsSF(10))
}

可变参数

func sum(ops ...int) int {
 s := 0
 for _, op := range ops {
 s += op
 }
 return s
}

最终拿到的就是所有参数之和

defer 函数

func TestDefer(t *testing.T) {
defer func() {
t.Log("Clear resources")
}()
t.Log("Started")
panic("Fatal error”) //defer仍会执⾏
}

⾯向对象编程

结构体定义

type Employee struct {
 Id string
 Name string
 Age int
}

实例创建及初始化

e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
e2 := new(Employee) //注意这⾥返回的引⽤/指针,相当于 e := &Employee{}
e2.Id = “2" //与其他主要编程语⾔的差异:通过实例的指针访问成员不需要使⽤->
e2.Age = 22
e2.Name = “Rose"


//第⼀种定义⽅式在实例对应⽅法被调⽤时,实例的成员会进⾏值复制
func (e Employee) String() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
} 
//通常情况下为了避免内存拷⻉我们使⽤第⼆种定义⽅式
func (e *Employee) String() string {
return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age)
}

定义交互协议

接⼝与依赖

{ISI6UY47_0~0P(D07D%}K3.png

用java实现

Programmer.java
public interface Programmer {
String WriteCodes() ;
}

GoProgrammer.java
public class GoProgrammer implements Programmer {
@Override
public String WriteCodes() {
return "fmt.Println(\"Hello World\")";
} }

Task.java
public class Task{
public static void main(String[] args) {
Programmer prog = new GoProgrammer();
String codes = prog.WriteCodes();
System.out.println(codes);
} }

定义 实现 调用接口的过程

但是在go语言中,我们可以使用鸭子类型

接⼝定义
type Programmer interface {
    WriteHelloWorld() 
}

 接⼝实现
type GoProgrammer struct {
}

func (p *GoProgrammer) WriteHelloWorld() Code {
return "fmt.Println(\"Hello World!\")"
}

我们发现这个是通过组合的方式完成接口的实现的

Go 接⼝

与其他主要编程语⾔的差异

  1. 接⼝为⾮⼊侵性,实现不依赖于借⼝定义
  2. 所以接⼝的定义可以包含在接⼝使⽤者包内

接⼝变量

K9J5N{@QJ4TTP%G{WGNIZ@D.png

自定义类型

就自定义的 比如struct 然后就能当做一个特殊类型使用(这个有c语言基础能理解)

扩展与复⽤

复合
与其他主要编程语⾔的差异
Go 不⽀持继承,但可以通过复合的⽅式来复⽤


type Pet struct {
}

func (p *Pet) Speak() {
    fmt.Println("...PET")
}

func (p *Pet) SpeakTo(host string) {
    p.Speak()
    fmt.Println(" ", host)
}

type Dog struct {
    Pet
}

func (d *Dog) Speak() {
    fmt.Println("Wang!")
}

func TestDog(t *testing.T) {
    dog := new(Dog)

    dog.SpeakTo("Chao")
}

可以通过组合对程序进行继承

匿名类型嵌⼊
与其他主要编程语⾔的差异
它不是继承,如果我们把“内部 struct ”看作⽗类,把“外部 struct” 看作⼦类,
会发现如下问题:

  1. 不⽀持⼦类替换
  2. ⼦类并不是真正继承了⽗类的⽅法
    • ⽗类的定义的⽅法⽆法访问⼦类的数据和⽅法

多态

BP@5KY2PYF(XGX(4@Q1AP71.png

空接⼝与断⾔

  1. 空接⼝可以表示任何类型
  2. 通过断⾔来将空接⼝转换为制定类型
    v, ok := p.(int) //ok=true 时为转换成功

Go 接⼝最佳实践
倾向于使⽤⼩的接⼝定义,很多接
⼝只包含⼀个⽅法

type Reader interface {
 Read(p []byte) (n int, err error)
}
type Writer interface {
 Write(p []byte) (n int, err error)
}

较⼤的接⼝定义,可以由多个⼩接
⼝定义组合⽽成

type ReadWriter interface {
 Reader
 Writer
}

只依赖于必要功能的最⼩接⼝

func StoreData(reader Reader) error {
 …
}

Go 的错误机制

  1. 没有异常机制
  2. error 类型实现了 error 接⼝
type error interface {
 Error() string
}
  1. 可以通过 errors.New 来快速创建错误实例
errors.New("n must be in the range [0,100]")

panic
• panic ⽤于不可以恢复的错误
• panic 退出前会执⾏ defer 指定的内容

panic vs. os.Exit
• os.Exit 退出时不会调⽤ defer 指定的函数
• os.Exit 退出时不输出当前调⽤栈信息

recover

Java

try{
 …
}catch(Throwable t){
}

C++

try{
 …
}catch(…){
}

recover

defer func() {
if err := recover(); err != nil {
 //恢复错误
         }
}()

init ⽅法
• 在 main 被执⾏前,所有依赖的 package 的 init ⽅法都会被执⾏
• 不同包的 init 函数按照包导⼊的依赖关系决定执⾏顺序
• 每个包可以有多个 init 函数
• 包的每个源⽂件也可以有多个 init 函数,这点⽐较特殊

Thead vs. Groutine

  1. 创建时默认的 stack 的⼤⼩
    • JDK5 以后 Java Thread stack 默认为1M
    • Groutine 的 Stack 初始化⼤⼩为2K
  2. 和 KSE (Kernel Space Entity) 的对应关系
    • Java Thread 是 1:1
    • Groutine 是 M:N

![UXJQWHW]2OSZG67]IKT~W00.png](https://upload-images.jianshu.io/upload_images/24046024-5f98801ae75eeb49.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容