【一】基础知识

一定要努力堵住那悠悠众口

Go的目录结构:
bin存放编译后的可执行文件;
pkg存放编译后的包文件;
src存放项目源文件

一、包的概念

  包是go语言中不可缺少部分,在每个go源码的第一行进行定义,定义方式是:package "包名",并且该名称是作为调用该包时候所使用的名称。

包的概念总结:

  • 每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,因此文件名和包名可能不同,为了规范,功能相似或者属于相同性质的源码归集到相同的包名中。

  • 每个应用程序都必须有且只有一个包名称为main,是该程序的入口,执行时候会寻找包名为main的代码。

  • 包与包之间可以被相互调用,主要为了减少代码重复性。

  • 包一旦被引入就需要被使用,除非引入的时候忽略它(前面加_表示忽略它,只是用它来初始化),如果引入了包,而在代码中没用使用,运行时候会报错。

二、包的引入

1、包引入的方法
方法一:

import "fmt"
import "os"

方法二:

import "fmt"; import "os"

方法三(推荐)

import (
   "fmt"
   "os"
)

2、引入其他包
  go语言中,引入包的路径是根据GOPATH/src 这个环境变量作为相对路径进行引入的,如果GOPATH存在多个,那么go将逐一寻找路径,直到找到,若GOPATH/src未找到则编译出错。
示例:
我的GOPATH为:/usr/local/go/awesomeProject
建立包文件:/usr/local/go/awesomeProject/src/day02/eg1/pkg.go

// pkg.go文件内容
package add   

import "fmt"

func Sum(a int,b int)  {
    var c int
    c = a + b
    fmt.Println("res",c) 

}

在main中进行引入:

package main

import (
    "day02/eg1"
    "fmt"
)

func main() {
    add.Sum(2, 3)  // 调用包
    fmt.Println("Hello,World!")
}

三、变量
1、单个变量的声明
go语言中,所有变量都必须先声明再使用,下面是声明变量和赋值方法:

  • 先声明后赋值: var <变量名称> <变量类型>
    赋值:变量的赋值格式: <变量名称> = <表达式>
  • 声明的同时赋值:var <变量名称> <变量类型> = <表达式> (简写为 变量名称 := 表达式)
package main

import "fmt"
func main(){
    var a string // 声明一个变量名为a,类型为字符串的变量
    a = "wpr"  // 对a变量赋值
    var age int = 23  // 声明并赋值变量
    fmt.Println(a,age)
}

2、多个变量声明

  • 先声明后赋值:var <变量1>,<变量2>,<变量3> <变量类型>
    赋值 : <变量1>,<变量2> = 值1,值2
  • 声明同时赋值 :var <变量1>,<变量2> = 值1,值2
    可简写为: <变量1>,<变量2> = 值1,值2
  • 多个变量还可以使用var()声明
package main

import "fmt"

func main()  {
    var a,b int
    a,b = 1,2
    var c,d = 3,"wpr"  // 类型自动推断
    e,f :=4 ,"hello"  //简写
    fmt.Println(a,b,c,d,e,f)
}

使用var()声明

package main

import "fmt"

func main()  {
    var (
        a int   //默认值为0
        b string // 默认值为空字符串("")
        c = 3 
    )
    fmt.Println(a,b,c)
}

3、变量名
命名规则:

  • 首字符可以是任意的Unicode字符或者下划线;
  • 剩余字符可以是Unicode字符、下划线、数字;
  • 不能作为用以下关键字作为变量名
break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

4、可见性
  可见性即对包外可见,当其他包调用当前包的变量时候是否允许可见(可访问)。(类似python中的对外隐藏,以_开头)

  • 变量开头字符大写,表示可见
  • 变量开头字母非大写,则表示私有,不可见

5、变量的作用域

  • 函数内定义的变量称为局部变量,其作用域在函数内
  • 函数外定义的变量称为全局变量,作用于整个包,并且该变量首字母大写,则对其他包可见

四、常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
定义常量语法:

const 名称 类型 = value

多个常量定义语法:

const (
    a = 0  // 类型自动推倒
    b = 1
    c = 2
)

iota解释:
  iota,特殊常量,可以认为是一个可以被编译器修改的常量。
  在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
iota 可以被用作枚举值:

const (
    a = iota
    b = iota
    c = iota
)

第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:

const (
    a = iota
    b
    c
)

用法:

package main

import "fmt"

func main() {
    const (
            a = iota   // 0
            b          // 1
            c          // 2
            d = "ha"   // 独立值,iota += 1
            e          // "ha"   iota += 1
            f = 100    // iota +=1
            g          // 100  iota +=1
            h = iota   // 7,恢复计数
            i          // 8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)  // 0 1 2 ha ha 100 100 7 8
}

五、数据类型

1、布尔型

布尔类型 - 由两个预定义常量组成:true、false,默认值为false

package main

import "fmt"

func main()  {
    var (
        a bool // 默认值为false
        b bool = true
    )
    fmt.Println(a,b)  // false true
}

2、数字型

整型:
 uint8(无符号 8 位整型 [0 ~ 255])
 uint16(无符号 16 位整型 [0 ~ 65535])
 uint32(无符号 32 位整型 [0 ~ 4294967295])
 uint64(无符号 64 位整型 [0 ~ 18446744073709551615])
 int8(带符号 8 位整型 [-128 ~ 127])
 int16(带符号 16 位整型 [-32768 ~ 32767])
 int32(带符号 32 位整型 [-2147483648 ~ 2147483647])
 int64(带符号 64 位整型 [-9223372036854775808 ~ 9223372036854775807])

浮点型:
 float32(IEEE-754 32位浮点型数)
 float64(IEEE-754 64位浮点型数)
 complex64(复数:32 位实数和虚数)
 complex128(复数:64 位实数和虚数)

其他数字类型:
 byte(字符类型,存储为ascii码,与uint8相同)
 rune(与int32相同)
 uint(32 或 64 位)
 int(与 uint 大小相同)
 uintptr(无符号整型,用于存放一个指针)

3、字符串类型

双引号(" ") —— 对特殊字符(如换行\t,\n等)进行转义。
反引号(``) —— 保留原始字符,打印时候原样输出。

package main

import "fmt"

func main()  {
    var (
        a string = `wpr\n`
        b string = "age\n"
    )
    fmt.Println(a,b)  // wpr\n age
}

4、派生类型

  指针类型
  数组类型
  结构类型
  联合类型
  函数类型
  切片类型
  接口类型

5、类型转换

  不同的数据类型之间是不允许进行赋值或运算操作,必须经过类型转化才能进行运算或者赋值。
转换方法:数据类型()

package main

import "fmt"

func main()  {
    var a int32 = 1
    var b int64 = 64
    c:= int64(a)+ b  //不转换编译报错
    fmt.Println(c)
}

六、运算符

1、算数运算符

  +相加 :x + y = z
  -相减 : x - y = z
  *相乘:x * y = z
  % 求余: x % y =z
  ++ 自增 :x++
  -- 自减:x--

2、关系运算符

  == 判断两个值是否相等,如果相等返回 True,否则返回 False。
  != 判断两个值是否不相等,如果不相等返回 True, 否则返回 False。
  > 判断左边值是否大于右边值,如果是返回 True,否则返回 False。
  < 判断左边值是否小于右边值,如果是返回 True,否则返回 False。
  >= 判断左边值是否大于等于右边值,如果是返回 True,否则返回 False。
  <= 判断左边值是否小于等于右边值,如果是返回 True,否则返回 False。

3、逻辑运算符

  && 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。
  || 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。
  ! 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。

4、位运算符

位运算符对整数在内存中的二进制位进行操作。
  & 按位与运算符,"&"是双目运算符, 其功能是参与运算的两数各对应的二进位相与。
  | 按位或运算符,"|"是双目运算符,其功能是参与运算的两数各对应的二进位相或。
  ^ 按位异或运算符,"^"是双目运算符, 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。
  << 左移运算符,"<<"是双目运算符。左移n位就是乘以2的n次方。其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
  >> 右移运算符,">>"是双目运算符。右移n位就是除以2的n次方。其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。

//假设A = 60, B = 13;
 二进制格式表示

A = 0011 1100

B = 0000 1101

-----------------

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

5、赋值运算

  = 简单的赋值运算符,将一个表达式的值赋给一个左值
  += 相加后再赋值 (C += A 等于 C = C + A)
  -= 相减后再赋值 (C -= A 等于 C = C - A)
  *= 相乘后再赋值 (C *= A 等于 C = C * A)
  /= 相除后再赋值 (C /= A 等于 C = C / A)
  %= 求余后再赋值 (C %= A 等于 C = C % A)
  <<= 左移后赋值 (C <<= 2 等于 C = C << 2)
  >>= 右移后赋值 (C >>= 2 等于 C = C >> 2)
  &= 按位与后赋值 (C &= 2 等于 C = C & 2)
  ^= 按位异或后赋值 (C ^= 2 等于 C = C ^ 2)
  |= 按位或后赋值 (C |= 2 等于 C = C | 2)

6、变量运算符

  & 取变量的地址(&a将给出变量a的实际地址)
  * 取变量的指针( *a 是指向变量a的指针)

七、流程控制

1、if-else

if condition1 {          
} else if condition2 {    //else 一定要和大括号在一行
} else if condition3 {
} else { 
}

2、for

一般for循环

for i := 0; i < 100; i++ {  //语句使用分号隔开
}

死循环(类似python中的while)

for true {
}
//或者
for {
}

使用range遍历数组、slice、map、chan等

//  索引循环
package main

import "fmt"

func main()  {
    str := "hello world"
    for k := range str{  // 字符串遍历
        fmt.Printf("index=%d val=%c len=%d\n",k,str[k],len(str))  // index=0 val=h len=11...
    }
}
//  同时循环index,val
package main

import "fmt"
func main(){
    str := "hello world"
    for index,val := range str{
    fmt.Printf("index=%d val=%c len=%d\n",index,val,len(str))  // k代表索引,v代表值
    }
}

3、switch

  switch条件控制,当条件匹配当前case时候,执行当前case,不会进行往下执行,若都没有匹配到,则执行default,当然也可以使用fallthrought关键字强制执行下面语句。

switch var {
case 条件一:
case 条件二:
case 条件三:
default:
}
//  switch简单示例
package main

import "fmt"

func main()  {
    var a = 0
    switch a {
    case 0:
        fmt.Println("this is 0")
    case 1:
        fmt.Println("this is 1")
    case 2:
        fmt.Println("this is 2")
    default:
        fmt.Print("this is default")
    }
}//结果  this is 0
// 多条件判断case
package main

import "fmt"

func main()  {
    var a = 0
    switch  { //这里没有变量
    case a > 0 && a <3:  //a in 0-3
        fmt.Println("a in 0-3")
    case a < -1 || a > 3:
        fmt.Println("a <0 or a > 3")
    case a == 0:
        fmt.Println("a equal 0")
    default:
        fmt.Print("this is default")
    } //结果 a equal 0
}
// 加入fallthrought
package main

import "fmt"

func main()  {
    var a = 0
    switch  {
    case a > 0 && a <3:  //a in 0-3
        fmt.Println("a in 0-3")
    case a < -1 || a > 3:
        fmt.Println("a <0 or a > 3")
    case a == 0:
        fmt.Println("a equal 0")
        fallthrough //使用fallthrought关键字当满足该条件时候,继续执行下面语句
    default:
        fmt.Printf("this is default\n")
    } 
//结果 a equal 0    
//this is default

4、goto&label

label作为一个标记,语法是字符串+冒号定义,在循环中使用label可调至label的定义的位置。

package main

import (
    "fmt"
    "time"
)

func main()  {

    LABLE:  //定义名称为LABLE
    //fmt.Println("回到label")
    for i := 0; i < 10; i++ {
        fmt.Println("-->",i)
        for true {
            I++
            if i == 6 {

                time.Sleep(time.Second * 5)
                break LABLE  //跳出循环到LABLE定义的地方
            }
            fmt.Println(i)
        }
    }

}
结果:--> 0
     1
     2
     3
     4
     5

goto作用在于跳转到label标签定义的地方继续执行。上述代码修改为goto,将是死循环

package main

import (
    "fmt"
    "time"
)

func main()  {

    LABLE:  //定义名称为LABLE
    //fmt.Println("回到label")
    for i := 0; i < 10; i++ {
        fmt.Println("-->",i)
        for true {
            I++
            if i == 6 {

                time.Sleep(time.Second * 5)
                goto LABLE  //跳出循环到LABLE定义的地方
            }
            fmt.Println(i)
        }
    }

}  //结果:每5秒打印0,1,2,3,4,5

八、函数

函数是go语言编程的核心。特点:
  不支持重载,一个包不能有两个名称一样的函数;
  函数也是一种类型,一个函数可以赋值给变量(该特性和python一样);
  函数也可以不用起名称,这种函数称为匿名函数;
  go语言函数可以有多个返回值;
  使用_标识符可忽略返回值。

函数定义语法:

func 函数名(形参1 类型,形参2 类型,...)(返回值名1 类型,返回值名2 类型,...){
    函数体代码...
    return 返回值1,返回值2
}
// 函数作为变量
package main

import (
    "fmt"
)

func add(a int,b int) int {

    return a + b
}
func main()  {

        c := add
        d := c(1,2)  // 加括号代表调用函数
        fmt.Println(d)
    } // 结果:3
// 忽略返回值
package main

import (
    "fmt"
)

func add(a int,b int) (int,int) {

    return a + b,a
}
func main()  {

        c := add
        d , _ := c(1,2)  //使用_忽略第二个返回值
        fmt.Println(d)
    }//结果:3

1、函数参数传递方式

值传递:一般基本的数据类型都是值传递,如数字、字符串。
引用传递:复杂的数据类型,如map、chan、interface,是引用传递。

注意:无论是值传递还是引用传递,传递给函数的都是变量的副本,不过值传递是值的拷贝,引用传递拷贝的是地址,一般来说地址拷贝更为高效。而值拷贝取决于拷贝对象大小,对象越大,则性能相对低。

2、函数返回值命名

  go 函数的返回值或者结果参数可以指定一个名字(名字不是强制的),并且像原始的变量那样使用,就像输入参数那样。如果对其命名,在函数开始时,它们会用其类型的零值初始化;如果函数在不加参数的情况下执行了 return 语句,结果参数的当前值会作为返回值返回。

package main

import "fmt"
//斐波那契数列实现
func Factorial(x int) (result int) {  //给函数返回值命名为result
    if x <= 1  {
        result = 1
    } else {
        result =Factorial(x - 2)  + Factorial(x - 1)
        }

    return
}
func main()  {
    b := Factorial(4)
    fmt.Println(b)
    }//结果:5

3、匿名函数

匿名函数(对比javascript)即没有函数名的函数,只能放在函数中,可以实现函数嵌套定义的功能。

package main

import "fmt"

func main()  {
    c := func(a int,b int ) int {
        return a + b
    }(3,4)
    fmt.Println(c)
    }  // 结果:7

4、函数的可变参数

go语言中,也可以像python一样传递可变参数(意思是传递时候才确定有几个参数,定义时候不知道参数个数),可以认为参数是数组形式:

func funcname(arg...type) { }
package main

import "fmt"

func add(a int, arg...int) int {
    var sum = 0
    for i := 0; i < len(arg); i++ {
        sum += arg[i] //取参数的值
    }
    return sum
}
func main()  {
    c := add(1)  //传递一个参数
    d := add(2,3,4)  //传递两个参数
    fmt.Println(c,d)
    }//结果:0,7

5、defer

当函数中存在defer关键字时候,一旦函数返回则执行defer语句,因此,defer可以做一些资源清理的事情。
特性:
 多个defer语句,按照后进先出的方式执行(栈)。
 defer语句中的变量,在defer声明时候就决定了。

package main

import "fmt"

func main()  {
    i := 0
    defer fmt.Println(i)   // 放入栈
    I++
    defer fmt.Println(i)  //在放入栈,先执行
    fmt.Println("wpr")
    return

    }//结果:wpr ,1,0

6、命令行参数

概念:
 命令行参数(或参数):是指运行程序时提供的参数。
 已定义命令行参数:是指程序中通过 flag.Xxx 等这种形式定义了的参数。输入参数时需要 -flag 形式。
 非 flag(non-flag)命令行参数(或保留的命令行参数):不符合 -flag 形式的参数。- 、--和 --flag 都属于 non-flag 参数。

来自命令行的参数在程序中通过os.Args(对比python的sys.agrv) 获取,导入包 os 即可。其中 os.Args[0] 为执行的程序名,os.Args[1] ~ os.Args[n-1] 是具体的参数。

//示例
test.exe  1 2 
//test.exe 为os.Args[0] ,1是 os.Args[1],2是os.Args[2]

7、go语言的初始化顺序

初始化顺序规则:引入的包 —> 包中的常量、变量、init —> 当前包的常量、变量、init —> main函数


注意事项:

  • 当前go源文件中, 每一个被Import的包, 按其在源文件中出现顺序初始化。
  • 如果当前包有多个init在不同的源文件中, 则按源文件名以字典顺序从小到大排序,小的先被执行到, 同一包且同一源文件中的init,则按其出现在文件中的先后顺序依次初始化; 当前包的package level变量常量也遵循这个规则; 其实准确来说,应是按提交给编译器的源文件名顺序为准,只是在提交编译器之前, go命令行工具对源文件名按字典序排序了。
  • init只可以由go runtine自已调用, 我们在代码中不可以显示调用,也不可以被引用,如赋给a function variable。
  • 包A 引入包B , 包B又引入包C, 则包的初始化顺序为: C -> B -> A
  • 引入包,必须避免死循环,如 A 引 B , B引C, C引A
  • 一个包被其它多个包引入,如A -> B ->C 和 H -> I -> C , C被其它包引了2次, 但是注意包C只被初始化一次。
  • 另一个大原则, 被依赖的总是先被初始化,main包总是被最后一个初始化,因为它总是依赖别的包。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容