一文带你入门 Golang

go 语言特色

脚本化的语法,容易上手。html

静态类型+编译性,开发、运行效率都有保证java

函数式 & 面向对象 两种编程范式,mysql

原生支持并发编程支持,下降开发成本,维护成本,以及更好的兼容性,效率。linux

劣势:语法糖没有 Python 和 Ruby 多。运行效率不及C,但已赶超C++,Java。第三方库很少,就是轮子少(喜欢造轮子的能够加入golang轮子大军)。nginx

安装

官方: https://golang.org/git

国内官方站点: https://go-zh.org/github

Linux

http://golang.org/dl/ 下载最新Go语言二进制包golang

wget https://dl.google.com/go/go1.13.15.linux-amd64.tar.gz

tar -C /usr/local -xzf go1.13.15.linux-amd64.tar.gz

export PATH=$PATH:/usr/local/go/bin

go version

Mac

环境变量配置

GOROOT, GOPATH, GOBIN, PATH, 如今安装的最新golang runtiem都不用配置了环境变量了。sql

目录结构

go命令

go run

go 代码关键字

break         //退出循环
default     //选择结构默认项(switch、select)
func         //定义函数
interface    //定义接口
select        //channel
case         //选择结构标签
chan         //定义channel
const         //常量
continue     //跳过本次循环
defer         //延迟执行内容(收尾工做)
go         //并发执行
map         //map类型
struct        //定义结构体
else         //选择结构
goto         //跳转语句
package     //包
switch        //选择结构
fallthrough     //??
if         //选择结构
range         //从slice、map等结构中取元素
type        //定义类型
for         //循环
import         //导入包
return         //返回
var        //定义变量

标示符

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr

语言特点

不要求缩进,不要求末尾加分号——;,同一行代码中有多个表达式,须要用 分号 分割。没有使用的变量,包,会致使报错。shell

每一个go源文件开头必须是package开头,定义本身的包

一个目录下,只能有一个包名

一个可执行的文件必需要有 main() 函数

import 引入包

两种引入风格

import "package1"
import "package2"
import (
    "package1"
    pa2 "package2"      // 包别名,别名为 pa2
    . "fmt"
    _ "mysql"
)

. "fmt" 方式引入包的化,使用fmt里面的函数就可直接使用,不用带 fmt 前缀了

若是引入的包不使用,会报错, 或者加个前缀 _ 便可,这样的下划线会把引入的包的init函数执行一下。定义的变量不用,也会报错。

包内初始化函数

定义 包内 初始化函数

func init() {
    
}

只导入这个包部分,并运行init函数,因为导入不全,因此在代码中就不能使用这个包了。

import _ "MyPackage"

数据类型

序号 类型和描述
1 布尔型 布尔型的值只能够是常量 true 或者 false。一个简单的例子:var b bool = true。
2 数字类型 整型 int 和浮点型 float3二、float64,Go 语言支持整型和浮点型数字,而且支持复数,其中位的运算采用补码。
3 字符串类型: 字符串就是一串固定长度的字符链接起来的字符序列。Go 的字符串是由单个字节链接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
4 派生类型: 包括: (a) 指针类型(Pointer) (b) 数组类型 (c) 结构化类型(struct) (d) Channel 类型 (e) 函数类型 (f) 切片类型 (g) 接口类型(interface) (h) Map 类型

使用 int 时,根据当前操做系统来的,64位系统对应 int64, 32位操做系统,对应int32.

变量声明

  • 变量声明: var <变量名> [变量类型]
  • 变量赋值: <变量名> = <值,表达式,函数返回>
  • 变量声明赋值:var <变量名> [变量类型] = <值,表达式,函数返回>
  • 变量声明,类型推断,并赋值 <变量名> := <值,表达式,函数返回>
分组声明

var (
    i int
    foo float32
    name string
)

分组批量声明、赋值
var a,b,c,d int = 1,2,3,4
a,b := 1,2

特殊变量 _

变量做用域

  • 函数内定义的变量称为局部变量
  • 函数外定义的变量称为全局变量
  • 全局变量必须使用 var 声明,局部变量可省略

做用域能够分为如下四个类型:

  • 内置做用域:不须要本身声明,全部的关键字和内置类型、函数都拥有全局做用域
  • 包级做用域:必須函数外声明,在该包内的全部文件均可以访问
  • 文件级做用域:不须要声明,导入便可。一个文件中经过import导入的包名,只在该文件内可用
  • 局部做用域:在本身的语句块内声明,包括函数,for、if 等语句块,或自定义的 {} 语句块造成的做用域,只在本身的局部做用域内可用

语句块

语句块是由花括弧({})所包含的一系列语句。

在 Go 中还有不少的隐式语句块:

  • 主语句块:包括全部源码,对应内置做用域
  • 包语句块:包括该包中全部的源码(一个包可能会包括一个目录下的多个文件),对应包级做用域
  • 文件语句块:包括该文件中的全部源码,对应文件级做用域
  • for 、if、switch等语句自己也在它自身的隐式语句块中,对应局部做用域

类型转换

  • 不存在隐式转换,必须是显示
  • 类型转换必须是在两种兼容的类型之间
  • <变量名称> [:]= <目标类型>( <须要转换的变量名> )

类型转换精度丢失

类型断言

断言,顾名思义就是果断的去猜想一个未知的事物。在 go 语言中,interface{} 就是这个神秘的未知类型,其断言操做就是用来判断 interface{} 的类型。

var foo interface{} = 22

    f, ok := foo.(int)
    if !ok {
        t.Log("Guess wrong ...")
    }
    t.Logf("The type is : %T", f)

常量

  • 显示 const idenfity [type] = value
  • 隐式 const identify = value () (无类型常量)

变量类型支持: bool, int, float, string

特殊常量 iota

运算

算术运算

运算符 描述 实例
+ 相加 A + B 输出结果 30
- 相减 A - B 输出结果 -10
* 相乘 A * B 输出结果 200
/ 相除 B / A 输出结果 2
% 求余 B % A 输出结果 0
++ 自增 A++ 输出结果 11
-- 自减 A-- 输出结果 9

关系运算

运算符 描述 实例
== 检查两个值是否相等,若是相等返回 True 不然返回 False。 (A == B) 为 False
!= 检查两个值是否不相等,若是不相等返回 True 不然返回 False。 (A != B) 为 True
> 检查左边值是否大于右边值,若是是返回 True 不然返回 False。 (A > B) 为 False
< 检查左边值是否小于右边值,若是是返回 True 不然返回 False。 (A < B) 为 True
>= 检查左边值是否大于等于右边值,若是是返回 True 不然返回 False。 (A >= B) 为 False
<= 检查左边值是否小于等于右边值,若是是返回 True 不然返回 False。 (A <= B) 为 True

逻辑运算

运算符 描述 实例
&& 逻辑 AND 运算符。 若是两边的操做数都是 True,则条件 True,不然为 False。 (A && B) 为 False
\ \ 逻辑 OR 运算符。 若是两边的操做数有一个 True,则条件 True,不然为 False。 (A \ \ B) 为 True
! 逻辑 NOT 运算符。 若是条件为 True,则逻辑 NOT 条件 False,不然为 True。 !(A && B) 为 True

位运算

运算符 描述 实例
& 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 (A & B) 结果为 12, 二进制为 0000 1100
\ 按位或运算符"\ "是双目运算符。 其功能是参与运算的两数各对应的二进位相或 (A \ B) 结果为 61, 二进制为 0011 1101
^ 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (A ^ B) 结果为 49, 二进制为 0011 0001
<< 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位所有左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 A << 2 结果为 240 ,二进制为 1111 0000
>> 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位所有右移若干位,">>"右边的数指定移动的位数。 A >> 2 结果为 15 ,二进制为 0000 1111

赋值运算

运算符 描述 实例
= 简单的赋值运算符,将一个表达式的值赋给一个左值 C = A + B 将 A + B 表达式结果赋值给 C
+= 相加后再赋值 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

优先级

优先级 运算符 功能
9 () [] -> . 后缀运算
8 ! *(指针) & ++ -- +(正号) -(负号) 单目运算
7 * / % + - 算术运算,加减乘除
6 << >> 位运算
5 == != < <= > >= 逻辑运算、不等、等
4 & \ ^ 按位 逻辑与、或
3 \ \ && 逻辑或、与
2 = += -= *= 等等 赋值运算
1 , 逗号

一元赋值 这两大运算符是 从右到左 关联,其余都是 从左到右 关联。

注意:优先级 值越大则优先级越高。为了方便理解、记忆,我对没有严格按照优先级制表,只是作了个大概!!
更详细的

代码控制语句

if, else, else if

var number int = 37
    if number += 4; 10 > number {
        fmt.Print("less than 10:", number)
    } else if 10 < number {
        number -= 2
        fmt.Print("greater 10:", number)
    } else {
        
    }

switch, select

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    ia := []interface{}{byte(6), 'a', uint(10), int32(-4), "CC"}
    v := ia[rand.Intn(4)]
    
    // 值 switch
    switch v {
        case 'a' :
            fmt.Println("char: ", v)
        case 10 :
            fmt.Println("uint: ", v)
        case -4 :
            fmt.Println("int: ", v)
        case 0.1 :
            fallthrough
        caes "0.1"
            fmt.Println("float: ", v)
        default :
            fmt.Println("byte: ", v)
    }
    
    // 变量类型 switch
    switch interface{}(v).(type) {
    case string :
        fmt.Printf("Case A.")
    case byte :
        fmt.Printf("Case B.")
    case int :
        fmt.Printf("Case B.")
    default:
        fmt.Println("Unknown!")
    }
}

注意,go语言和其余语言不一样的时,每一个case代码末尾会自动加上break 操做, 若是你须要使用 fallthrough 来抵消默认的 break

select 用于管道

for

是的 golangforforforeachfor inwhile于一体。

do while 表示:golang你这么绕,不优雅
package main

import (
    "fmt"
    "time"
)

func main() {
    map1 := map[int]string{1: "Golang", 2: "Java", 3: "Python", 4: "C"}
    
    n := 1
    for {   // 省略则默认是true
        if n > 3 {
            break;
        }
        fmt.Println("for true map item: ", map1[n])
        time.Sleep(1)
        n++
    }
    
    for i := 1; i < 4; i++ {
        fmt.Println("for i map item: ", map1[i])
    }
    
    for k,v := range map1 {
        fmt.Print(k, ":", v)
    }
}

goto, break, continue

goto 是跳过代码块

package main

import (
    "fmt"
    "time"
)

func main() {
    code:
        fmt.Println("do some thing~")
        time.Sleep(1)
        
    goto code
}

break 跳出并结束循环

continue 跳过当前循环

虽然不能和 PHP那样 break 2 跳出多层, 单只要有goto就能干不少事了。

golang给 循环 就分配了一个 for,语句跳转语句却整了那么多花样

复合数据

内建方法 make & new

内建方法就是不须要引入包就能用的

make 能够建立 slice、map、chan,返回指针类型

  • slice 是可变长的数组
  • map 是key-map 数据数组
  • chan 是go独有的 管道
一股c编程风格扑面而来, char ptr = (char )malloc(sizeof(char) * 5);

内建方法 new

内存置0,返回传入类型的指针地址

package main
import fmt
import reflect

func main() {
    mSlice := make([]string, 3)
    mSlice[0] = "dog"
    mSlice[1] = "cat"
    mSlice[2] = "pig"
    fmt.Println("animals: ", mSlice)
    
    mMap := make(map[int]string)
    mMap[10] = "dog"
    mMap['2'] = "cat"
    fmt.Println(reflect.TypeOf(mMap))
    fmt.Println("animals :: ", mMap)
    
        
    nMap := new(map[int]string)
    fmt.Println(reflect.TypeOf(nMap))
}

append copy delete

slice可使用copy,append 函数

delete 是专门用来删除 map

  • append(src, ele) 追加元素
  • copy(dst, src) 把src元素赋值到dst上,
  • delete() 删除元素

例子:

package main
import "fmt"

func main() {
    mSlice := make([]string, 3)
    mSlice[0] = "dog"
    mSlice[1] = "cat"
    mSlice[2] = "pig"
    fmt.Println("animals: ", mSlice)

    // append(mSlice, "id-3")   // 这样写会致使报错: append(mSlice, "id-3") evaluated but not used
    mSlice = append(mSlice, "id-3")
    fmt.Println("animals update:", mSlice)
    fmt.Println("animals len :", len(mSlice))
    fmt.Println("animals cap:", cap(mSlice))
    
    // newSlice := make([]string)      // 这样写致使报错:missing len argument to make([]string)
    // newSlice := make([]string, 2)       // 这样写会致使数据丢失2个,不会自动扩容
    newSlice := make([]string, 3)       // 不要屡次定义初始化:no new variables on left side of :=
    copy(mSlice, newSlice)          // 这样反向copy,会致使前面的几个数组元素被置为空
    // copy(newSlice, mSlice)
    fmt.Println("animals dst:", mSlice)
    fmt.Println("animals copy:", newSlice)
    
    delete(mMap, 50)
    fmt.Println(mMap)
}

panic & recover

异常处理

panic() 抛出异常

recover() 获取异常

报错会致使程序代码中断,不会再执行后续操做

例子:

package main

import "fmt"
import "errors"

func panicFunc() {
    defer func() {
        // recover()
        message := recover()    // 声明了message 变量就须要使用哦,否则报错
        fmt.Println("panice msg: ", message)
        
        switch message.(type) {
            case string:
            case error:
                fmt.Println("panice error msg: ", message)
            default:
        }
    }()
    // panic("报错啦")
    panic(errors.New("I am error."))
}

func main() {

    panicFunc()
}

len & cap & close

len能够计算 string, array, slice, map, chan
cap 能够计算 slice, map, chan

  • len() 获取数组长度
  • cap() 获取占用空间分配
  • close() 用于关闭管道——chan

当声明一个数组时,go会预先分配一部分空间给当前数组,获取实际空间占用大小,使用cap()

不用像PHP那样,strlen(), count(), length 傻傻分不清楚了。

例子:

package main

import "fmt"

func main() {

    mSlice := make([]string, 3)
    mSlice[0] = "dog"
    mSlice[1] = "cat"
    mSlice[2] = "pig"
    fmt.Println("animals: ", mSlice)

    fmt.Println("animals update:", mSlice)
    fmt.Println("animals len :", len(mSlice))
    fmt.Println("animals cap:", cap(mSlice))

    mChan := make(chan int, 1)
    close(mChan)
    mChan <- 1      // 会致使报错: panic: send on closed channel
}

defer

定一个当前方法关闭时,运行的代码, 压栈设计,先声明的后执行。

结构体

package main

import "fmt"

type Dog struct {
    ID int
    Name string
    Age int32
}

func main() {
    
    var dog Dog
    dog.ID = 1
    dog.Name = "haha"
    dog.Age = 3
    fmt.Println("print Dog Struct", dog)

    dog2 := Dog{ID:2, Name:"san", Age:4}
    fmt.Println("print Dog 2 Struct", dog2)
    
    dog3 := new(Dog)
    dog3.ID = 3
    dog3.Name = "Tom"
    dog3.Age = 5
    fmt.Println("print Dog 3 Struct", dog)
}

输出

print Dog Struct {1 haha 3}
print Dog 2 Struct {2 san 4}
print Dog 3 Struct &{3 Tom 5}

属性 & 函数

接口

/* define an interface */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   ...
   method_namen [return_type]
}

/* define a struct */
type struct_name struct {
   /* variables */
}

/* implement interface methods*/
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* method implementation */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* method implementation */
}

并发

指针

json

须要引入包 encoding/json, 两个函数分别是 json.Marshal(), json.Unmarshal().

注意,最后一个是英文字母小写的 L,不是 1

json 序列化

package main

import "fmt"
import "encoding/json"

type ServerInfo struct {
    SerName string
    SerIp   string
    SerPort uint16
}

func main() {
    server := new(ServerInfo)
    server.SerName = "http-nginx"
    server.SerIp = "127.0.0.1"
    server.SerPort = 8080
    re,err := json.Marshal(server)
    if nil != err {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("struct json bytes: ", re)
        fmt.Println("struct json string: ", string(re))
    }
    
    mServer := make(map[string]interface{})
    mServer["serverName"] = "apache2-http"
    mServer["serIp"] = "192.168.30.133"
    mServer["serPort"] = "3033"
    mRe,err := json.Marshal(mServer)
    if nil != err {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("map json string: ", string(mRe))
    }
}

输出

struct json bytes:  [123 34 83 101 114 78 97 109 101 34 58 34 104 116 116 112 45 110 103 105 110 120 34 44 34 83 101 114 73 112 34 58 34 49 48 46 49 48 48 46 49 55 46 50 55 58 51 48 48 48 49 34 44 34 83 101 114 80 111 114 116 34 58 56 48 56 48 125]
struct json string:  {"SerName":"http-nginx","SerIp":"10.100.17.27:30001","SerPort":8080}
map json string:  {"serIp":"192.168.30.133","serPort":"3033","serverName":"apache2-http"}
ps: 我也不知道 10.100.17.27:30001 是怎么回事

json 反序列化

可使用 tag 来作 mapping,
package main

import "fmt"
import "encoding/json"

type ServerInfo struct {
    SerName string  `json:"name"`
    SerIp   string  `json:"ip"`
    SerPort uint16  `json:"port"`
}

func main() {
    // jsonStr := "{\"SerName\":\"http-nginx\",\"SerIp\":\"10.100.17.27:30001\",\"SerPort\":8080}"   \\ 双引号注意转义
    jsonStr := "{\"name\":\"http-nginx\",\"ip\":\"10.100.17.27:30001\",\"port\":8080}"
    
    sServer := new(ServerInfo)
    jsonBytes := []byte(jsonStr)
    uerr := json.Unmarshal(jsonBytes, &sServer)
    if nil != uerr {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("uns struct: ", sServer)
    }
    
    jsonStr3 := `{"serIp":"192.168.30.133","serPort":"3033","serverName":"apache2-http"}`   \\ 使用键盘1旁边的 ` 符号包裹双引号就不用转义了
    
    uSer := make(map[string]interface{})
    uErr := json.Unmarshal([]byte(jsonStr3), &uSer)
    if nil != uErr {
        fmt.Println("error: ", uErr.Error())
    } else {
        fmt.Println("unmar map: ", uSer)
    }
}

输出

uns struct:  &{http-nginx 10.100.17.27:30001 8080}
unmar map:  map[serIp:192.168.30.133 serPort:3033 serverName:apache2-http]

tag

tag 这个东东把,就是json的别名,感受这个功能是go的特点,与encoding/json包紧密结合。

为何会有这个东西,我估计是这个和 go命名规则 有关,go命名规则,要求public的变量开头要大写,小写开头的变量是private的,因此,json中的变量就会影响一个接口体变量的访问权限,为了避免像java那样复杂,提供了方便的tag功能。

package main

import "fmt"
import "encoding/json"

type ServerInfo struct {
    SerName string  `json:"name"`
    SerIp   string  `json:"ip"`
    SerPort uint16  `json:"port"`
}

func main() {
    
    server := new(ServerInfo)
    server.SerName = "http-nginx"
    server.SerIp = "127.0.0.1"
    server.SerPort = 8080
    re,err := json.Marshal(server)
    if nil != err {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("struct json string: ", string(re))
    }
}

输出

struct json string:  {"name":"http-nginx","ip":"10.100.17.27:30001","port":8080}
map json strin

go 特点语法

_

  • _ 变量

这就比如是Linux 里的 /dev/null, 因为go语言要求声明的变量必须被使用,返回的变量必须被接收,那么真有个变量没用但必需要接受怎么办呢,就把返回的参数给他。例如:

package main
import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
    for _, v := range pow {
        fmt.Printf("value is %d\n", v)
    }
}

这里咱们只要值,不要key的信息,返回的key不能不收不是,但我也不像把它输出出来,就让 _ 来接收好了。

  • _ 包

引入包, 并不直接使用这个包,运行时执行一次它的 init() 函数,

import (
    _ "github.com/go-sql-driver/mysql"
    "github.com/jinzhu/gorm"
)

参考

相关文章
相关标签/搜索