• 是一门编译型语言,运行效率高,开发高效,部署简单;语言层面支持并发,易于利用多核实现并发;内置runtime(作用:性能监控,GC等);简单易学,丰富的标准库,强大的网络库;内置强大的工具(gofmt),跨平台编译,内嵌C支持。
  • 应用:服务器编程,分布式系统 ,网络编程,云平台(docker)
  • 命令行工具:
    1、go build:用于编译源码文件、代码包、依赖包;
    2、go run:可以编译并运行Go源码文件;
    3、go get:命令主要用动态获取远程代码包。
Go保留的关键字只有25个
Go中36个预定标识符(包括基本数据类型和系统内嵌函数)
// 单行注释
/* 多行注释 */
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    "fmt"
    // "time"  // 导入的包要使用,否则会产生编译错误
)

// 定义全局常量
const NAME string = "global const var"

// 定义全局变量
var tmp string = "global var"

// 一般类型声明
type countInt int

// 结构体的声明
type Learn struct {

}

// 声明接口
type Ilearn interface {

}

// 函数定义
func learnGo() {
    fmt.Println("learn go!")
}

// 主函数
func main() {
    fmt.Println("Hello World!!!")
    fmt.Println(NAME)
    fmt.Println(tmp)
    learnGo()
}
  • package是基本的分发单位和工程管理中依赖关系的体现。
  • 每个Go语言源代码文件的开头都拥有一个package声明,表示源码文件所属代码包;要生成Go语言可执行程序,必须要有main的package包,且必须在该包下有main()函数
  • 同一个路径下只能存在一个package,一个package可以拆分成多个源文件组成。
  • import语句可以导入源代码文件所依赖的package包
  • 导入的包要使用,否则会产生编译错误!
  • 若导入的包中依赖其他包(包B),会首先导入包B,然后初始化包B中的常量和变量,最后若包B中有init()函数,会自动执行init()函数,所有包导入完成后才会对main中的常量和变量进行初始化,然后执行main中的init()函数(如果存在),最后才执行main函数。
  • 若有一个包被导入多次,则该包只会被导入一次。
  • 函数名首字母要大写
  • 转义字符:r表示回车,从当前行的最前面开始输出,覆盖掉旧的内容
  • 格式化go源代码文件:执行命令:gofmt -w 文件名.go;或者先按组合键shift+tab将代码整体移动到最左边,再选择某些代码块按tab键适当缩进即可。
  • 行长约定:一行最长不超过80个字符,超过则用逗号隔开并换行展示!

错误记录

  • 作为一个go的初学者,首先必须了解一个go项目的目录结构,例如我的一个测试目录如下:
workspace 
         |--bin

         |--pkg

         |--src
               |---learn
                        |----learn.go
               |---show
                        |----show.go
         |--main.go
  • bin目录存放编译后的可执行文件pkg目录存放编译后的包文件(名称以.a为后缀的文件)src目录存放项目源文件
  • 一般地,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。
  • 注意:某个.go文件import导入包的过程是先去$GOROOT下的src目录找对应的包,找到则导入;若找不到,则去存放Go的项目路径$GOPATH下的src目录找对应的包,找到则导入;若还找不到,则报错!
  • 刚开始还不知道go项目的目录结构时,我在main.go文件中导入learn和show包是这样的:
import (
    "fmt"
    "src/learn"
    "src/show"
)

结果报如下错误,细细观察,通过查找和对比,可以发现上面讲的原理;

import包名的查找顺序

于是,把main.go文件中import包的代码改一下(去掉”src”):

import (
    "fmt"
    "learn"
    "show"
)

再次键入执行命令:go run main.go,结果就成功执行了!

执行结果

learn.go的代码如下:

package learn

import (
    "fmt"
    "show"
)

func init() {
    show.Show()
    fmt.Println("learn init...")
}

func Learn() {
    fmt.Println("learn 学习...")
}

show.go的代码如下:

package show

import (
    "fmt"
)

func init() {   
    fmt.Println("show init...")
}

func Show() {
    fmt.Println("show 展示...")
}

main.go的代码如下:

// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    "fmt"
    // "time"  // 导入的包要使用,否则会产生编译错误
    "learn"
    "show"
)
// 函数定义
func learnGo() {
    fmt.Println("learn go!")
}
// init函数
func init() {
    fmt.Println("main init...")
}
// 主函数
func main() {
    show.Show()
    learn.Learn()
    learnGo()
}
  • 键入go env可以查看go的配置:例如我的环境配置如下:
go的环境配置
  • import别名、._
    1、import别名:
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    zzz "fmt"  // 包"fmt"的别名定义为zzz
    // "time"  // 导入的包要使用,否则会产生编译错误
    "learn"
    "show"
)

// 函数定义
func learnGo() {
    zzz.Println("learn go!")
}

// init函数
func init() {
    zzz.Println("main init...")
}

// 主函数
func main() {
    show.Show()
    learn.Learn()
    learnGo()
}

2、点(.)操作的含义是:点(.)表示的包导入后,调用该包中函数时可以省略前缀包名;

// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"  // 使用 . 操作,调用包"fmt"中的函数时,省略包名.
    // "time"  // 导入的包要使用,否则会产生编译错误
    "learn"
    "show"
)

// 函数定义
func learnGo() {
    Println("learn go!")  // 省略包名fmt.
}

// init函数
func init() {
    Println("main init...")
}

// 主函数
func main() {
    show.Show()
    learn.Learn()
    learnGo()
}

3、下划线(_)操作的含义是:导入该包,但不导入整个包,而是执行该包的init函数,因此无法通过包名来调用包中的其他函数。使用下划线(_)操作往往是为了注册包里的引擎,让外部可以方便地使用。

  • 代码包初始化函数即无参数声明和结果声明的init函数。
  • init函数可以被声明在任何文件中,且可以有多个。
(_)测试1
执行产生错误

以上报错说明无法调用learn.go文件中的Learn()函数,我们将main()函数中的代码learn.Learn()注释掉,再用命令行执行main.go可以发现执行了learn.go中的init()函数,说明_操作只调用init()函数,当然在执行init()函数之前,会先初始化learn.go文件中的常量和变量。

  • init函数的执行时机:单一代码包内,对所有全局变量进行求值,然后执行所有init函数,同一个代码包中多个init函数的执行顺序是不确定。

Go语言的基本数据类型

1、布尔型:只可以是常量 true 或者 false,不能是其他类型。一个简单的例子:var b bool = true。
2、整数类型:整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
1.uint8:无符号 8 位整型 (0 到 255)
2.uint16:无符号 16 位整型 (0 到 65535)
3.uint32:无符号 32 位整型 (0 到 4294967295)
4.uint64:无符号 64 位整型 (0 到 18446744073709551615)
5.int8:有符号 8 位整型 (-128 到 127)
6.int16:有符号 16 位整型 (-32768 到 32767)
7.int32:有符号 32 位整型 (-2147483648 到 2147483647)
8.int64:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
3、浮点数类型
1.float32:IEEE-754 32位浮点型数
2.float64:IEEE-754 64位浮点型数
3.complex64:32 位实数和虚数
4.complex128:64 位实数和虚数
4、字符串类型string,编码统一为”UTF-8″。
5、其他数字类型
1.byte:类似 uint8,无符号类型。
2.rune:类似 int32,表示一个Unicode码。
3.uint:32 或 64 位,其取决于操作系统的位数
4.int:与 uint 一样大小,其取决于操作系统的位数
5.uintptr:无符号整型,用于存放一个指针

  • 查看某个变量所占的字节数:unsafe.Sizeof(变量),导入import "unsafe"包!

  • 派生类型
    1、指针类型(Pointer)
    2、数组类型
    3、结构化类型(struct)
    4、Channel类型(chan)
    5、函数类型(func)
    6、切片类型(slice)
    7、接口类型(interface)
    8、Map类型(map)

  • 类型零值和类型别名
    1、类型零值不是空值,而是某个变量被声明后的默认值,一般情况下,值类型的默认值为0,布尔类型默认值为false,string默认值为空字符串。
    2、类型别名:type 别名 = 数据类型,例如:type D = int
    3、类型声明:type 别名 数据类型,例如:type d int
    4、区别:类型声明将d定义为一个新的数据类型,该类型拥有和int一样的特性,但是两者是不同类型不能进行算术等运算!而类型别名则将D定义为int整型的一个别名,使用D和int相同。二者可以当作同一种运算。别名只在源码中存在,编译完成后,不会有别名类型
  • 查看某个变量的类型:reflect.TypeOf(变量名),导入import "reflect"包。

变量与常量

  • 单个变量的声明和赋值
    1、变量的声明格式:var [变量类型]
    2、变量的赋值格式: =
    3、声明和赋值同时进行:var [变量类型] =
    4、分组声明格式:
var(
  a int
  b float32
  name string
)

5、同一行声明多个变量和赋值:var a, b, c int = 1, 2, 3 或者a, b := 1, 2。注意::=是声明并赋值;系统会自动推断类型;不需要var关键字;只能使用在局部变量。
6、全局变量必须使用var关键字,局部变量则可以省略。
7、特殊变量下划线_:相当于一个垃圾桶,把值赋值给下划线
,相当于把值丢进一个垃圾桶,以后都不再使用。
8、Go语言中不存在隐式转换,类型转换必须是显式转换。类型转换只能发生在两种兼容类型之间;类型转换格式: [:]= ()

  • 变量的可见性规则
    1、大写字母开头的变量可导出的,也就是其他包可以读取的,是公共变量
    2、小写字母开头的变量就是不可导出的,是私有变量。即导入的某个包,只能使用该包中的公有变量,不能使用私有变量。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"
    "reflect"
)

var(
    a uint
    b float32
    c bool
    d string = "你好"
)

func main() {
    var v1, v2, v3 = 4, 5, 1.222  // 可以声明每个变量的类型,省略的话--->系统自动推断类型
    Println(reflect.TypeOf(v3))  // float64
    Println(v1, v2, v3)  // 4 5 1.222
    Println(a, b, c, d) // 0 0 false 你好

    c1, c2, c3 := 33, "string", 1.222 // 简写方式,系统自动推断类型,只能用在函数体内
    Println(reflect.TypeOf(c2)) // string
    Println(c1, c2, c3) // 33 string 1.222

    // 下划线的使用
    // var a1, _, a2 = 9, 8, 7
    // Println(a1, _, a2) // 报错:
    // # command-line-arguments
    // .main.go:31:9: cannot use _ as value

    // 类型转换测试: 只能用在相同类型之间进行转换
    var h float64 = 2.6555555
    k := int64(h)  // 括号不能省
    Println(k) // 2,直接向下取整
    Println(reflect.TypeOf(k))  // int64
}
  • 常量定义从形式上分为显式和隐式:
    1、显式:const identifier [type] = value
    2、隐式:const identifier = value (通常叫无类型常量)
  • 常量可以使用内置表达式定义:例如:len()unsafe.Sizeof()
    3、常量范围目前只支持布尔型,数字型(整数型、浮点型和复数)和字符串型。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"
    "reflect"
)

const(
    name string = "cat" // 显式定义
    age = 12 // 隐式定义
)

const a, b, c = 1, 2, "我是中文"

const lenc = len(c)  // 一个中文占三个字节,长度为12

func main() {
    Println(name, age)  // cat 12
    Println(a, b, c) // 1 2 我是中文
    Println(reflect.TypeOf(c)) // string
    Println(lenc) // 12
}
  • 特殊常量iota的使用
    1、iota在const关键字出现时将被重置为0;
    2、const中每新增一行常量声明将使iota计数一次。
    3、iota常见用法:
    1.跳值使用法;
    2.插队使用法;
    3.表达式隐式使用法;
    4.单行使用法。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"
)

// iota在关键字出现时被重置为0
const a = iota // 0
const b = iota // 0

const(
    c = iota
    m = 2014 // 插入使用法,iota依然计数
    d = iota
    _  // 跳值使用法,下划线单独占用一行,iota依然计数
    e = iota
)

const(
    b1 = iota * 3
    b2 // 自动使用向上第一个非空表达式,此时每增加一行常量const,iota计数加1,即b2 = 1 * 2 
    b3 // b3 = 2 * 3 = 6
)

const(
    c1, c2 = iota + 1, iota * 3
    c3, c4  // 沿用上面第一个非空的表达式,注意格式要一样,且const新增一行,iota计数器才会加1
    c5 = iota // 否则就得赋予值

)

func main() {
    Println(a, b) // 0 0
    Println(c, d, e) // 0 2 4
    Println(b1, b2, b3) // 0 3 6
    Println(c1, c2, c3, c4, c5) // 1 0 2 3 2
}

文章来源于互联网,如有雷同请联系站长删除:Go学习笔记(一)

发表评论