写给大忙人看的Go语言(一)

棋帅小七 · · 708 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Tips

写给大忙人看的Golang教程(一)
阅读本文之前,我认为你已经掌握其他语言基础并写出一个简单的项目。

(1)Golang编程注意事项

  • 源文件必须以.go为扩展名.
  • Go应用程序d额执行入口是main()方法.
  • Go代码严格区分大小写.
  • Go代码不需要分号.
  • Go代码不允许多条语句在同一行出现.
  • Go语言重定义的变量和导入的包如果没有被使用不会编译通过.
  • Go语言大括号是成对出现的.

(2)Golang中的常用转义字符

  • \t 制表符
  • \n 换行符
  • \\ 一个斜杠
  • \" 一个引号
  • \r 一个回车

(3)注释方式

  • // 注释内容行注释
  • /* 注释内容 */多行注释
  • 多行注释不可以嵌套多行注释

(4)Golang的代码规范

  • 尽量使用行注释注释整个方法或语句
  • 使用Tab缩进
  • 使用gofmt -w格式化代码
  • 运算符两侧加空格
  • Golang的代码风格:

    // HelloWorld.go
    
    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello World")
    }
  • 一行代码最好不要超过80个字符

(5)官方编程指南

(6)变量

  • 使用var关键字定义变量:var 变量名称 数据类型
  • 使用类型推导:var 变量名称 = 值
  • 省略var关键字:变量名称 := 值, 变量名称不应该是已经定义过的
  • 变量名称 := 值等同于var 变量名称 数据类型 = 值
  • 多变量声明:var 变量名称, 变量名称... 数据类型
  • 多变量赋值:var 变量名称, 变量名称, ... = 值, 值, ...
  • 省略var关键字多变量声明和赋值: 变量名称, 变量名称, ... := 值, 值, ...
  • 声明全局变量:
    var (
        变量名称 = 值
        ...
    )

(7)Golang支持的数据类型

写给大忙人看的Go语言(一)

  • 使用unsafe.Sizeof()查看变量占用的空间

    package main
    
    import (
    "fmt"
    "unsafe"
    )
    
    func main() {
    var x int = 10
    fmt.Println("The x size: ", unsafe.Sizeof(x))
    // The x size:  8
    }
  • float32表示单精度,float64表示双精度

  • 字符常量使用单引号

  • Go中的字符编码使用UTF-8类型

  • bool类型只能取truefalse

  • 在Go中字符串是不可变的

  • Go支持使用反引号输出原生字符串

  • 字符串拼接使用加号时最后一个加号需要保留在行尾

(8)基本数据类型转换

  • 数值类型互相转换:目标数据类型(变量)
  • 数值与字符串互转:
    • 数字转字符串:使用fmt.Sprintf()字符串格式化函数。
    • 数字转字符串:使用strconv.FormatBool()strconv.FormatInt()strconv.FormatUint()strconv.FormatFloat()格式化函数。
    • 字符串转数字:使用strconv.ParseBool()strconv.ParseInt()strconv.ParseUint()strconv.ParseFloat()格式化函数。

(9)指针数据类型

package main

import "fmt"

func main() {

    var i = 10
    var ptr *int = &i

    fmt.Println("变量i的内存地址是: ", ptr)
    // 变量i的内存地址是:  0xc00004a080
    fmt.Println("变量i的存储内容是: ", *ptr)
    // 变量i的存储内容是:  10
}

(10) 值类型与引用类型

写给大忙人看的Go语言(一)

写给大忙人看的Go语言(一)

  • 值类型通常存放到栈区。
  • 引用类型通常存放在堆区,栈中有堆中的引用。

(11)Golang中的标识符

  • Go中使用_表示空标识符
  • 严格区分大小写
  • 包名尽量与目录保持一致
  • 推荐使用驼峰命名法
  • 变量名称、函数名称、常量名称首字母大写表示可以被其他包访问,否则表示私有的

(12)运算符

  • Golang中只有x++x--,没有++x--x
  • 自增自减运算值独立的语句,不允许类似的:x := a++

(13)控制台输入输出

  • fmt.Sacnf():使用指定的格式获取文本
  • fmt.Sacnln():以换行为结束的文本

(14) 原码、反码、补码

  • 计算机中0表示正数,1表示负数
  • 计算机中都是用补码进行运算的,因为CPU只会加法运算
  • 正数的原、反、补都是相同的
  • 负数的反码是原码符号位不变其他位取反
  • 负数的补码是反码加1
  • 0的原、反、补相同

(15)流程控制

(15.1)顺序控制

(15.2) 分支控制

  • if语句:

    if x>12 { 
    }
    // Golang支持直接在condition中定义变量
    if x:=12; x>12 {
    }
  • if-else语句:

    if x:=12; x>20 {
    }else {
    }
  • if-else if-else语句:

    if x:=12; x>100 {
    }else if x>50 {
    }else if x>10 {
    }else {
    }
  • switch-case-default语句:

    // 每个分支不需要break语句
    // switch也支持在condition中直接定义变量
    // case支持多个表达式
    // 取消break使用fallthrough语句————switch穿透
    switch y:=10;y {
    case 5:
        // something
    case 10:
        // something
        fallthrough
    case 20, 25, 30:
        // something
    default:
        // something
    }

(15.3)循环控制

  • for循环

    for i:=1;i<10;i++ {
    }
    // Golang也提供了for-each或for-range类似的循环
    str := "Hello Golang."
    for index, value:=range str {
      // index表示索引
      // value表示值
    }
  • while循环

    for {
      if condition {
          break
      }
      // something
    }
  • do-while循环

    for {
      // something
      if condition {
          break
      }
    }

(16) 随机数

// 设置随机数的种子为当前的系统时间
rand.Seed(time.Now().Unix())
// 生成0-99范围的随机数
randomNumber := rand.Intn(100)

(17)break、continue、goto、return语句

  • break语句在多层嵌套中可以通过标签指明要终止到哪一层语句块:
label:
for {
    break label
}
  • continue语句在多层嵌套中可以通过标签指明要跳出到到哪一层语句块:
label:
for {
    continue label
}
  • goto语句可以无条件跳转,容易造成逻辑混乱,一般不主张使用goto语句:

    label:
    for {
    goto label
    }
  • return语句用户退出函数
    return
    // 或
    return some

(18)函数

  • 函数基本语法:

    func functionName (paramsList) (returnList) {}
  • Golang不支持函数重载

  • Golang函数本身也是一种数据类型,可以赋值给变量,那么该变量也是函数类型

  • Golang函数可以作为实参传入另一个函数

  • Golang支持自定义数据类型,使用:type 自定义数据类型名 数据类型

    type myfunc func(int)(int, int)
  • 支持使用_忽略返回值

  • 支持可变参数

    package main
    
    import "fmt"
    
    func main() {
    ret := sum(1, 2, 3)
    fmt.Println(ret) //6
    }
    // 可变参数
    func sum(args...int) int {
    
    sum := 0
    
    for i:=0; i<len(args); i++ {
        sum += args[i]
    }
    
    return sum
    }

(19)包

​ 包的本质就是一个目录,Go的每一个文件都必须属于一个包。

  • 打包:

    package packageName
  • 导入包:

    import "packageName"
    // 导入多个包
    import (
    "packageName"
      ...
    )
    // 导入包时自动从GOPATH下面的src下面引入
  • 包支持别名

    package main
    
    import f "fmt"
    
    func main() {
    f.Println()
    }

(20)init函数

  • 在Go中每一个源文件都可以有一个init函数,它优先于main函数执行,被Go框架调用。
func init() {}
  • 先执行引入的包中的init函数再执行main包中的init函数
// util.HelloWorld.go
package utils

import "fmt"

func init() {
    fmt.Println("Util.HelloWorld() init")
}

func HelloWorld()(){
    fmt.Println("Hello World")
}

// main.test.go
package main

import (
    "StudyGo/utils"
    "fmt"
)

func init() {
    fmt.Println("Main.main() init")
}

func main() {
    utils.HelloWorld()
}

// Util.HelloWorld() init
// Main.main() init
// Hello World

(21)匿名函数

  • 直接调用

    func (paramsList)(returnList){
        // something
    }()
  • 赋值给一个变量

    x := func (paramsList)(returnList){
        // something
    }
    y := x(paramsList)

(22)闭包

  • 闭包就是函数与其相关的引用环境构成的实体

    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
    
        fileName := "file"
        fileSuffix := ".mp3"
    
        ms := makeSuffix(fileSuffix)
        ret := ms(fileName)
    
        fmt.Println(ret)
    
    }
    
    func makeSuffix(suffix string) func(string) string {
        return func (s string) string {
            if strings.HasSuffix(s, suffix) {
                return s
            }else {
                return s + suffix
            }
        }
    }

(23)defer 关键字

  • defer是Go语言中的延时机制,用于处理关闭文件句柄等资源释放操作

    package main
    
    import "fmt"
    
    func main() {
    SayHello()
    }
    
    func SayHello() {
    defer fmt.Println("Bye.")
    fmt.Println("Hi.")
    }
    
    // Hi.
    // Bye.
  • 使用defer修饰的语句会压入栈中,其相关的值也会被压入栈中

(24) 字符串函数

  • len():计算字符串长度
  • strconv.Atoi(s string) (i int, err error):将字符串转换为整数
  • strconv.Itoa(i int) string:将整数转换为字符串
  • strconv.FormatInt(i int64, base int) string:将十进制转换为其他进制
  • strings.Contains(s string, sub string) bool:判断字符串是否包含子字符串
  • strings.Count(s string, sub string) int:统计字符串中有几个子字符串
  • strings.EqualFold(s_0 string, s_1 string) bool:忽略大小写比较字符串是否相等
  • strings.Index(s string, sub string) int:返回子字符串在字符串中的第一个位置
  • strings.LastIndex(s string, sub string):返回子字符串在字符串中的最后一个位置
  • string.Replace(s string, oldSub string, newSub string, n int) string:将指定的子字符串替换为其他字符串,n代表替换个数,-1表示全部,返回新字符串
  • string.ToLower(s string):将字符串转换为小写
  • string.ToUpper(s string):将字符串转换为大写
  • string.Split(s string, sep string) array:将字符串按照sep分隔
  • string.TrimSpace(s string) string:删除字符串两侧的空格
  • string.Trim(s string, sub string) string:将字符串两侧的sub去掉
  • string.TrimLeft(s string, sub string) string:将字符串左边的sub删除
  • string.TrimRight(s string, sub string) string:将字符串右边的sub删除
  • string.HasPrefix(s string, sub string) bool:判断s是否以sub开头
  • string.HasSuffix(s string, sub string) bool:判断s是否以sub结尾

(25)时间日期函数

  • time.Time:表示时间类型
  • time.Now() struct:获取当前本地时间
  • time.Now().Year():返回年
  • time.Now().Month():返回月,使用int(time.Now().Month())取得数字
  • time.Now().Day():返回日
  • time.Now().Hour():返回时
  • time.Now().Minute():返回分
  • time.Now().Second():返回秒
  • time.Now().Format(s string):格式化时间数据,2006-01-02 15:04:05表示格式化的格式字符串其中的值不能改变
  • time.Sleep(d Duration):休眠函数
    • time.Hour:一小时
    • time.Minute:一分钟
    • time.Second:一秒
    • time.Millisecond:一毫秒
    • time.Microsecond:一微秒
    • time.Nanosecon:一纳秒
  • time.Now().Unix() int64:返回Unix秒时间戳
  • time.Now().UnixNano() int64:返回Unix纳秒时间戳

(26)内置函数

  • len():求长度
  • new():分配内存,主要用来分配值类型
  • make():分配内存,主要用来分配引用类型

(27)错误处理

  • 在Go中捕获异常的机制是使用defer关键字修饰匿名函数,导致匿名函数最后执行,在匿名函数中调用recover()函数,通过返回值是否为nill来判断是否发生异常信息。

    package main
    
    import "fmt"
    
    func main() {
    
        ret := ComputeNumber(1, 0)
        fmt.Println(ret)
    }
    
    func ComputeNumber(n_0 int, n_1 int) int {
    
        defer func() {
            if e := recover(); e != nil{
                fmt.Println(e)
            }
    
        }()
    
        result := n_0 / n_1
    
        return result
    }
  • 自定义错误

    使用errors.New(Type) *Type创建一个error类型,panic()接收一个空接口类型,输出错误信息并结束运行。

    package main
    
    import "errors"
    
    func main() {
    
    err := readConfigureFile("config.json")
    
    if err !=nil {
        panic(err) // panic: Read config.ini error.
    }
    
    }
    
    func readConfigureFile(path string)(err error) {
    
    if path != "config.ini" {
        return errors.New("Read config.ini error.")
    } else {
        return nil
    }
    }

    (28)数组

    ​ 在Go中数据是值类型,使用以下方式创建数组。

    • var 数组名称 [元素个数]数据类型 = [元素个数]数据类型{元素}
    • var 数组名称 = [元素个数]数据类型{元素}
    • var 数组名称 = [...]数据类型{元素个数}
    • var 数组名称 = [...]数据类型{索引:值}
    • 数组支持类型推导
    • 数组支持for-each/for-range遍历

    (29)slice 切片

    数组的长度是固定的,切片 的长度不是固定的。

    • var 切片名称 []数据类型

    • 切片名称[索引:索引]

    • 切片的结构:[起始数据元素指针, 长度, 容量]

    • 通过make创建切片:var 切片名称 []数据类型 = make([]数据类型, 长度, 容量)

    • 切片支持普通遍历和for-range方式遍历

    • 使用append()函数追加元素到切片末尾,容量不够时自动扩容

    • 使用copy()函数拷贝数组

    • string类型底层是个byte数组,也可以进行切片处理。string是不可变的,如果要修改字符串,需要先将字符串转换为切片修改完成后再转换成为字符串。
    str := "Hello World."
    arr := []byte(str)
    arr[11] = '!'
    str = string(arr)
    fmt.Println(str)

    (28)Map映射

    • Map是一种键值对数据结构,声明方式如下:

    var Map名称 map[KeyType]ValueType

    • 使用make(map[KeyType]ValueType)分配空间

    • delete(m map[Type]Type, key Type):通过Key删除元素,如果元素不存在也不会报错

    • 清空Map一种是遍历删除,一种是make重新分配空间,使得原来的Map成为垃圾让GC回收

    • 查找使用value, ok = mapName[Key],如果oktrue,表示元素存在

    • Map支持for-range遍历
    for key, value := range mapName{
    }
    • Map支持切片

(28)OOP

  • Go中的OOP是通过struct来实现的

    type 类名 struct {
      属性名 数据类型
      ...
    }
  • 创建结构体变量

    var 变量名称 结构体类型
    var 变量名称 结构体类型 = 结构体类型{}
    变量名称 := 结构体类型{}
    
    // 下面两种写法等价:
    var 变量名称 *结构体名称 = new(结构体名称)
    var 变量名称 *结构体名称 = &结构体名称
    
    // 在操作属性、方法的时候Go进行了优化,下面两种写法是等价的:
    (*变量名称).属性 = 值
    变量名称.属性 = 值
  • 每一个字段可以加上一个tag,该tag可以通过反射机制获取,常见的场景就是序列化与反序列化

    属性名称 数据类型 `json:Tag名称`
  • Go中的类没有构造函数,通常通过工厂模式来实现

    package model
    
    // 如果Name和Age改为name和age,需要为person绑定Getter和Setter方法
    type person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    }
    
    func NewPerson(n string, a int)(*person){
    return &person{
        Name : n,
        Age : a,
    }
    }
    package main
    
    import "fmt"
    import "StudyGo/model"
    
    func main() {
    var tom = model.NewPerson("Tom", 20)
    fmt.Println((*tom).Name)
    fmt.Println((*tom).Age)
    }
  • 在Go中在一个结构体中嵌套另一个匿名结构体就认为实现了继承

    type Ani struct {
      name string
      age int
    }
    
    type Cat struct {
      Ani
      say string
    }
    • Go中的结构体可以访问嵌套结构体中的所有字段和方法
    • 结构体的字段和属性采用就近原则
    • 如果一个结构体继承自两个结构体,这两个结构体都有同名字段但是该子结构体没有,访问时需要指明父结构体
    • struct支持匿名字段,但是数据类型不能重复,使用结构体变量.数据类型 = 值来访问
  • 接口

    type 接口名称 interface {
      方法名称(参数列表)(返回值列表)
    }
    • 接口不允许包含任何变量
    • Go中的接口不需要显式实现,只要一个变量含有接口的所有方法,那么就实现了这个接口
  • 类型断言

    • 当一个类型转换为了接口类型在转换为该类型时需要使用类型断言判断是否可以转换为该类型
    var number float32
    var x interface{}
    x = t
    t = x.(float32) // 判断一下是否可以转换成为float32类型

(29)方法

func (recv type) funcName (paramsList)(returnList) {
    // something
}
  • recv 表示这个方法与type类进行绑定,方法内通过recv操作type类中的字段
  • type是个结构体类型
  • recv是个结构体类型变量

通常为了执行效率一般不会直接传入结构体类型作为接收器,而是结构体类型指针:

func (dog *Dog) function()(){ // 绑定的是地址,操作时也要使用地址
    // something
}
// 调用时
var d Dog
(&d).function()

但是编译器做出了相关的优化:

var d Dog
d.function()

有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:棋帅小七

查看原文:写给大忙人看的Go语言(一)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

708 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传