Java技术债务Java技术债务

  •  首页
  •  分类
  •  归档
  •  标签
  • 博客日志
  • 资源分享
  •  友链
  •  关于本站
注册
登录

Go语言的基本概念与语法

Go

文章目录

  • 包package
  • 基本数据类型
  • 变量
  • 常量
  • for语句
  • if语句
  • switch
  • go中关键字
  • 类型转换
  • 类型推断
  • Go函数
  • Go语言nil:空值/零值

包package

每个 Go 程序都是由包组成的.

程序从 main包开始运行.

本程序通过导入路径 "fmt" and "math/rand"来使用这两个包.

按照约定,包名与导入路径的最后一个元素相同。例如,"math/rand" 包中的源码均以packagerand` 语句开始.

注意: 执行这些程序的环境是确定性的 所以每次运行示例程序 rand.Intn 都会返回相同的数字.

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

Imports(导入)

此代码将导入分组到带括号的”factored(分解)”导入语句中.

您还可以编写多个导入语句,例如:

import "fmt"
import "math"
或者
import (
    "fmt"
    "math"
)

但是使用factored import语句是一种很好的风格.

基本数据类型

bool

布尔值是一组布尔值,true和false。

默认值:false

string

字符串是所有 8 位字节字符串的集合,通常必须表示 UTF-8 编码的文本。字符串可能为空,但 不是零。字符串类型的值是不可变的。

默认值:””

int

int 是大小至少为 32 位的有符号整数类型。这是一个非重复类型,而不是 int32 的别名。

默认值:0

int类型还有其他相关类型:

  • int8:是所有有符号 8 位整数的集合。 范围:-2 ^8 到 2 ^8 - 1。
  • int16:是所有有符号 16 位整数的集合。 范围:-2 ^16 到 2 ^16 - 1。
  • int32:是所有有符号 32 位整数的集合。 范围:-2 ^32 到 2 ^32 - 1。
  • int64:是所有有符号 64 位整数的集合。 范围:-2 ^64 到 2 ^64 - 1。
  • uint:一种无符号整数类型,大小至少为32位。这是一个不同的类型,而不是 uint32 的别名。
  • uint8:是所有无符号 8 位整数的集合。 范围:0 到 2 ^8 - 1。
  • uint16:是所有无符号 16 位整数的集合。 范围:0 到 2 ^16 - 1。
  • uint32:是所有无符号 32 位整数的集合。 范围:0 到 2 ^32 - 1。
  • uint64:是所有无符号 64 位整数的集合。 范围:0 到 2 ^64 - 1。
  • uintptr:是一个整数类型,它足够大,可以容纳任何指针。

注意:
int, uint, and uintptr 在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽. 当你需要一个整数值时,你应该使用 int 除非你有特定的理由使用一个固定大小或无符号的整数类型。

byte

字节是 uint8 的别名,在所有方面都等效于 uint8。用于区分字节值和 8 位无符号 整数值。

rune

rune 是 Int32 的别名,在所有方面都等同于 Int32。用于区分字符值和整数值。

float

  • float32:float32 是所有 IEEE-754 32 位浮点数的集合。
  • float64:float64 是所有 IEEE-754 64 位浮点数的集合。

complex

复数是由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag)。

  • complex64:是所有复数的集合,float32 实数和虚部。
  • complex128:是所有复数的集合,float64 实数和虚部。复数的默认类型。

变量

变量声明

var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。

var 语句可以出现在包或函数级别。

语法:var i int

var声明可以包含初始化程序,每个变量一个。如果存在初始化器,则可以省略类型;

在函数内部, 可以使用 := 短赋值语句来代替具有隐式类型的 var 声明。

但是,在函数外部, 每个语句都以关键字 (var, func, 等) 因此 := 结构不可用。

觉得每行都用 var 声明变量比较烦琐?没关系,还有一种为懒人提供的定义变量的方法:

var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)

使用关键字 var 和括号,可以将一组变量定义放在一起。

变量的初始化

可以这么写:var hp int = 100 也可以省略int直接这么写:var hp = 100 下边可以有为什么可以这么写的原因。还有短变量初始化操作: hp := 100

匿名变量

匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。例如:

func GetData() (int, int) {
    return 100, 200
}
func main(){
    a, _ := GetData()
    _, b := GetData()
    fmt.Println(a, b)
}

常量

常量像变量一样声明,但使用 const 关键字。常量可以是字符、字符串、布尔值或数字值。不能使用 := 语法声明常量。

数字常量是高精度 值 。无类型常量采用其上下文所需的类型。(一个 int 最多可以存储 64 位整数,有时更少.)

和变量声明一样,可以批量声明多个常量:

const (
    e  = 2.7182818
    pi = 3.1415926
)

iota 常量生成器

常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。

type Weekday int
const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

for语句

for语法结构

Go 只有一个循环结构,即 for 循环.

基本 for 循环包含三个由分号分隔的组件:

  • init 语句: 在第一次迭代之前执行
  • 条件表达式:在每次迭代前求值
  • post 语句:在每次迭代结束时执行

init语句通常是一个简短的变量声明,并且在那里声明的变量仅在该 for 语句的范围内可见.

一旦布尔条件评估为 false ,循环将停止迭代。init 和 post 语句是可选的;如果省略循环条件,它将永远循环,因此可以紧凑地表示无限循环.

注意: 与 C、Java 或 JavaScript 等其他语言不同,for 语句的三个组件周围没有括号,并且大括号 { } 始终需要。

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    for ; sum < 1000; {
        sum += sum
    }
    fmt.Println(sum)
}

for 是Go中的while

C和Java 的 while 在 Go 中叫做 for 。

package main

import "fmt"

func main() {
    sum := 1
    for sum < 1000 {
        sum += sum
    }
    fmt.Println(sum)
}

if语句

Go 的 if 语句就像它的 for 循环;表达式不需要用括号 ( ) 括起来,但需要大括号 { } .

package main

import (
    "fmt"
    "math"
)

func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

func main() {
    fmt.Println(sqrt(2), sqrt(-4))
}

就像 for, if 语句可以在条件之前以短语句执行。语句声明的变量作用域仅在 if 之内。

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

If 和 else

在 if 短语句中声明的变量也可以在任何 else 块中使用。

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("%g >= %g\n", v, lim)
    }
    // can't use v here, though
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

switch

switch 语句是编写一连串 if - else 语句的简便方法。它运行其值等于条件表达式的第一个 case。

Go 的 switch 类似于 C、C++、Java、JavaScript 和 PHP 中的 switch,只是 Go 只运行选定的case,而不是所有后续case。

实际上,Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。另一个重要的区别是 Go 的 switch 的case 不需要是常量,所涉及的值也不需要是整数。

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os {
        case "darwin":
            fmt.Println("OS X.")
        case "linux":
            fmt.Println("Linux.")
        default:
            // freebsd, openbsd,
            // plan9, windows...
            fmt.Printf("%s.\n", os)
    }
}

switch 的求值顺序

switch 的 case 语句从上到下顺次执行,直到匹配成功时停止。 如果 i==0不调用 f

switch i {
case 0:
case f():
}

注意: Go playground 场中的时间总是从 2009-11-10 23:00:00 UTC 开始。

没有条件的switch

没有条件的 Switch 同 switch true 一样。这种构造可以是编写长 if-then-else 链的一种干净方式.

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("Good morning!")
    case t.Hour() < 17:
        fmt.Println("Good afternoon.")
    default:
        fmt.Println("Good evening.")
    }
}

defer语句

defer 语句推迟函数的执行,直到周围的函数返回。

延迟调用的参数会立即计算,但直到周围的函数返回时才会执行函数调用。

package main

import "fmt"

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

Stacking defers(defer栈)

延迟的函数调用被推送到栈上。当函数返回时,其延迟调用将按后进先出顺序执行。

package main

import "fmt"

func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}

结果:

hello
world

go中关键字

Go语言中的关键字一共有 25 个:

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

类型转换

在 Go 中不同类型的项之间的赋值需要显式转换。比如

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

或者

i := 42
f := float64(i)
u := uint(f)

类型推断

当声明一个变量而不指定显式类型 (通过使用 := syntax 或 var = 表达式语法), 变量的类型是从右侧的值推断出来的.

当右值声明了类型时,新变量的类型与其相同:

var i int
j := i // j is an int

但是,当右侧包含无类型的数字常量, 新的变量可以是 int, float64, 或 complex128 取决于常量的精度:

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

Go函数

一个函数可以接受零个或多个参数.

在本例中,add 接受两个 int 类型的参数.

请注意,类型在变量名 之后 .

package main

import "fmt"

func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

当两个或多个连续命名函数参数共享一个类型时,除了最后一个之外,可以省略所有类型.

在这个例子中,我们缩短

x int, y int
可以写成
x, y int

Multiple results(多值返回)

一个函数可以返回任意数量的结果。该 swap 函数返回两个字符串。

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world")
    fmt.Println(a, b)
}

Named return values(命名的返回值)

Go 的返回值可以命名。如果是这样,它们将被视为在函数顶部定义的变量。

这些名称应用于记录返回值的含义。

不带参数的 return 语句返回命名的返回值,这被称为“裸”返回。

裸返回语句应该只在短函数中使用,就像这里显示的例子一样。它们会损害较长函数的可读性。

package main

import "fmt"

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(17))
}

函数值

函数也是值。它们可以像其他值一样传递,函数值可以用作函数参数和返回值

package main

import (
    "fmt"
    "math"
)

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12))

    fmt.Println(compute(hypot))
    fmt.Println(compute(math.Pow))
}

函数闭包

Go 函数可以是闭包。闭包是引用其主体外部的变量的函数值。该函数可以访问并分配给引用的变量;从这个意义上说,函数是“绑定”到变量的.

例如, adder 函数返回一个闭包。每个闭包都绑定到它自己的 sum 变量.

Go语言nil:空值/零值

在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串””,而指针、切片、映射、通道、函数和接口的零值则是 nil。

nil 是Go语言中一个预定义好的标识符,有过其他编程语言开发经验的开发者也许会把 nil 看作其他语言中的 null(NULL),其实这并不是完全正确的,因为Go语言中的 nil 和其他语言中的 null 有很多不同点。

nil 标识符是不能比较的

这点和 python 等动态语言是不同的,在 python 中,两个 None 值永远相等。对于 nil 来说是一种未定义的操作。

nil 不是关键字或保留字

nil 并不是Go语言的关键字或者保留字,也就是说我们可以定义一个名称为 nil 的变量,比如下面这样:var nil = errors.New("my god")

虽然上面的声明语句可以通过编译,但是并不提倡这么做。

nil 没有默认类型

package main
import (
    "fmt"
)
func main() {
    fmt.Printf("%T", nil)
    print(nil)
}

运行结果如下所示:

.\main.go:9:10: use of untyped nil

不同类型 nil 的指针是一样的

package main
import (
    "fmt"
)
func main() {
    var arr []int
    var num *int
    fmt.Printf("%p\n", arr)
    fmt.Printf("%p", num)
}

结果:
0x0
0x0

通过运行结果可以看出 arr 和 num 的指针都是 0x0。

nil 是 map、slice、pointer、channel、func、interface 的零值

零值是Go语言中变量在声明之后但是未初始化被赋予的该类型的一个默认值。

不同类型的 nil 值占用的内存大小可能是不一样的

一个类型的所有的值的内存布局都是一样的,nil 也不例外,nil 的大小与同类型中的非 nil 类型的大小是一样的。但是不同类型的 nil 值的大小可能不同。

具体的大小取决于编译器和架构,上面打印的结果是在 64 位架构和标准编译器下完成的,对应 32 位的架构的,打印的大小将减半。

包package

每个 Go 程序都是由包组成的.

程序从 main包开始运行.

本程序通过导入路径 "fmt" and "math/rand"来使用这两个包.

按照约定,包名与导入路径的最后一个元素相同。例如,"math/rand" 包中的源码均以packagerand` 语句开始.

注意: 执行这些程序的环境是确定性的 所以每次运行示例程序 rand.Intn 都会返回相同的数字.

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

Imports(导入)

此代码将导入分组到带括号的”factored(分解)”导入语句中.

您还可以编写多个导入语句,例如:

import "fmt"
import "math"
或者
import (
    "fmt"
    "math"
)

但是使用factored import语句是一种很好的风格.

基本数据类型

bool

布尔值是一组布尔值,true和false。

默认值:false

string

字符串是所有 8 位字节字符串的集合,通常必须表示 UTF-8 编码的文本。字符串可能为空,但 不是零。字符串类型的值是不可变的。

默认值:””

int

int 是大小至少为 32 位的有符号整数类型。这是一个非重复类型,而不是 int32 的别名。

默认值:0

int类型还有其他相关类型:

  • int8:是所有有符号 8 位整数的集合。 范围:-2 ^8 到 2 ^8 - 1。
  • int16:是所有有符号 16 位整数的集合。 范围:-2 ^16 到 2 ^16 - 1。
  • int32:是所有有符号 32 位整数的集合。 范围:-2 ^32 到 2 ^32 - 1。
  • int64:是所有有符号 64 位整数的集合。 范围:-2 ^64 到 2 ^64 - 1。
  • uint:一种无符号整数类型,大小至少为32位。这是一个不同的类型,而不是 uint32 的别名。
  • uint8:是所有无符号 8 位整数的集合。 范围:0 到 2 ^8 - 1。
  • uint16:是所有无符号 16 位整数的集合。 范围:0 到 2 ^16 - 1。
  • uint32:是所有无符号 32 位整数的集合。 范围:0 到 2 ^32 - 1。
  • uint64:是所有无符号 64 位整数的集合。 范围:0 到 2 ^64 - 1。
  • uintptr:是一个整数类型,它足够大,可以容纳任何指针。

注意:
int, uint, and uintptr 在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽. 当你需要一个整数值时,你应该使用 int 除非你有特定的理由使用一个固定大小或无符号的整数类型。

byte

字节是 uint8 的别名,在所有方面都等效于 uint8。用于区分字节值和 8 位无符号 整数值。

rune

rune 是 Int32 的别名,在所有方面都等同于 Int32。用于区分字符值和整数值。

float

  • float32:float32 是所有 IEEE-754 32 位浮点数的集合。
  • float64:float64 是所有 IEEE-754 64 位浮点数的集合。

complex

复数是由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag)。

  • complex64:是所有复数的集合,float32 实数和虚部。
  • complex128:是所有复数的集合,float64 实数和虚部。复数的默认类型。

变量

变量声明

var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。

var 语句可以出现在包或函数级别。

语法:var i int

var声明可以包含初始化程序,每个变量一个。如果存在初始化器,则可以省略类型;

在函数内部, 可以使用 := 短赋值语句来代替具有隐式类型的 var 声明。

但是,在函数外部, 每个语句都以关键字 (var, func, 等) 因此 := 结构不可用。

觉得每行都用 var 声明变量比较烦琐?没关系,还有一种为懒人提供的定义变量的方法:

var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)

使用关键字 var 和括号,可以将一组变量定义放在一起。

变量的初始化

可以这么写:var hp int = 100 也可以省略int直接这么写:var hp = 100 下边可以有为什么可以这么写的原因。还有短变量初始化操作: hp := 100

匿名变量

匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。例如:

func GetData() (int, int) {
    return 100, 200
}
func main(){
    a, _ := GetData()
    _, b := GetData()
    fmt.Println(a, b)
}

常量

常量像变量一样声明,但使用 const 关键字。常量可以是字符、字符串、布尔值或数字值。不能使用 := 语法声明常量。

数字常量是高精度 值 。无类型常量采用其上下文所需的类型。(一个 int 最多可以存储 64 位整数,有时更少.)

和变量声明一样,可以批量声明多个常量:

const (
    e  = 2.7182818
    pi = 3.1415926
)

iota 常量生成器

常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。

type Weekday int
const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

for语句

for语法结构

Go 只有一个循环结构,即 for 循环.

基本 for 循环包含三个由分号分隔的组件:

  • init 语句: 在第一次迭代之前执行
  • 条件表达式:在每次迭代前求值
  • post 语句:在每次迭代结束时执行

init语句通常是一个简短的变量声明,并且在那里声明的变量仅在该 for 语句的范围内可见.

一旦布尔条件评估为 false ,循环将停止迭代。init 和 post 语句是可选的;如果省略循环条件,它将永远循环,因此可以紧凑地表示无限循环.

注意: 与 C、Java 或 JavaScript 等其他语言不同,for 语句的三个组件周围没有括号,并且大括号 { } 始终需要。

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    for ; sum < 1000; {
        sum += sum
    }
    fmt.Println(sum)
}

for 是Go中的while

C和Java 的 while 在 Go 中叫做 for 。

package main

import "fmt"

func main() {
    sum := 1
    for sum < 1000 {
        sum += sum
    }
    fmt.Println(sum)
}

if语句

Go 的 if 语句就像它的 for 循环;表达式不需要用括号 ( ) 括起来,但需要大括号 { } .

package main

import (
    "fmt"
    "math"
)

func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

func main() {
    fmt.Println(sqrt(2), sqrt(-4))
}

就像 for, if 语句可以在条件之前以短语句执行。语句声明的变量作用域仅在 if 之内。

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

If 和 else

在 if 短语句中声明的变量也可以在任何 else 块中使用。

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("%g >= %gn", v, lim)
    }
    // can't use v here, though
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

switch

switch 语句是编写一连串 if - else 语句的简便方法。它运行其值等于条件表达式的第一个 case。

Go 的 switch 类似于 C、C++、Java、JavaScript 和 PHP 中的 switch,只是 Go 只运行选定的case,而不是所有后续case。

实际上,Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。另一个重要的区别是 Go 的 switch 的case 不需要是常量,所涉及的值也不需要是整数。

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os {
        case "darwin":
            fmt.Println("OS X.")
        case "linux":
            fmt.Println("Linux.")
        default:
            // freebsd, openbsd,
            // plan9, windows...
            fmt.Printf("%s.n", os)
    }
}

switch 的求值顺序

switch 的 case 语句从上到下顺次执行,直到匹配成功时停止。 如果 i==0不调用 f

switch i {
case 0:
case f():
}

注意: Go playground 场中的时间总是从 2009-11-10 23:00:00 UTC 开始。

没有条件的switch

没有条件的 Switch 同 switch true 一样。这种构造可以是编写长 if-then-else 链的一种干净方式.

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("Good morning!")
    case t.Hour() < 17:
        fmt.Println("Good afternoon.")
    default:
        fmt.Println("Good evening.")
    }
}

defer语句

defer 语句推迟函数的执行,直到周围的函数返回。

延迟调用的参数会立即计算,但直到周围的函数返回时才会执行函数调用。

package main

import "fmt"

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

Stacking defers(defer栈)

延迟的函数调用被推送到栈上。当函数返回时,其延迟调用将按后进先出顺序执行。

package main

import "fmt"

func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}

结果:

hello
world

go中关键字

Go语言中的关键字一共有 25 个:

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

类型转换

在 Go 中不同类型的项之间的赋值需要显式转换。比如

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

或者

i := 42
f := float64(i)
u := uint(f)

类型推断

当声明一个变量而不指定显式类型 (通过使用 := syntax 或 var = 表达式语法), 变量的类型是从右侧的值推断出来的.

当右值声明了类型时,新变量的类型与其相同:

var i int
j := i // j is an int

但是,当右侧包含无类型的数字常量, 新的变量可以是 int, float64, 或 complex128 取决于常量的精度:

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

Go函数

一个函数可以接受零个或多个参数.

在本例中,add 接受两个 int 类型的参数.

请注意,类型在变量名 之后 .

package main

import "fmt"

func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

当两个或多个连续命名函数参数共享一个类型时,除了最后一个之外,可以省略所有类型.

在这个例子中,我们缩短

x int, y int
可以写成
x, y int

Multiple results(多值返回)

一个函数可以返回任意数量的结果。该 swap 函数返回两个字符串。

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world")
    fmt.Println(a, b)
}

Named return values(命名的返回值)

Go 的返回值可以命名。如果是这样,它们将被视为在函数顶部定义的变量。

这些名称应用于记录返回值的含义。

不带参数的 return 语句返回命名的返回值,这被称为“裸”返回。

裸返回语句应该只在短函数中使用,就像这里显示的例子一样。它们会损害较长函数的可读性。

package main

import "fmt"

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(17))
}

函数值

函数也是值。它们可以像其他值一样传递,函数值可以用作函数参数和返回值

package main

import (
    "fmt"
    "math"
)

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12))

    fmt.Println(compute(hypot))
    fmt.Println(compute(math.Pow))
}

函数闭包

Go 函数可以是闭包。闭包是引用其主体外部的变量的函数值。该函数可以访问并分配给引用的变量;从这个意义上说,函数是“绑定”到变量的.

例如, adder 函数返回一个闭包。每个闭包都绑定到它自己的 sum 变量.

Go语言nil:空值/零值

在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串””,而指针、切片、映射、通道、函数和接口的零值则是 nil。

nil 是Go语言中一个预定义好的标识符,有过其他编程语言开发经验的开发者也许会把 nil 看作其他语言中的 null(NULL),其实这并不是完全正确的,因为Go语言中的 nil 和其他语言中的 null 有很多不同点。

nil 标识符是不能比较的

这点和 python 等动态语言是不同的,在 python 中,两个 None 值永远相等。对于 nil 来说是一种未定义的操作。

nil 不是关键字或保留字

nil 并不是Go语言的关键字或者保留字,也就是说我们可以定义一个名称为 nil 的变量,比如下面这样:var nil = errors.New("my god")

虽然上面的声明语句可以通过编译,但是并不提倡这么做。

nil 没有默认类型

package main
import (
    "fmt"
)
func main() {
    fmt.Printf("%T", nil)
    print(nil)
}

运行结果如下所示:

.main.go:9:10: use of untyped nil

不同类型 nil 的指针是一样的

package main
import (
    "fmt"
)
func main() {
    var arr []int
    var num *int
    fmt.Printf("%pn", arr)
    fmt.Printf("%p", num)
}

结果:
0x0
0x0

通过运行结果可以看出 arr 和 num 的指针都是 0x0。

nil 是 map、slice、pointer、channel、func、interface 的零值

零值是Go语言中变量在声明之后但是未初始化被赋予的该类型的一个默认值。

不同类型的 nil 值占用的内存大小可能是不一样的

一个类型的所有的值的内存布局都是一样的,nil 也不例外,nil 的大小与同类型中的非 nil 类型的大小是一样的。但是不同类型的 nil 值的大小可能不同。

具体的大小取决于编译器和架构,上面打印的结果是在 64 位架构和标准编译器下完成的,对应 32 位的架构的,打印的大小将减半。

完
  • 本文作者:Java技术债务
  • 原文链接: https://cuizb.top/myblog/article/1687832300
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。
阅读全文
Java技术债务

Java技术债务

Java技术债务
Java技术债务
热门文章
  1. ClickHouse使用过程中的一些查询优化(六)2003
  2. MySQL数据库被攻击,被删库勒索,逼迫我使出洪荒之力进行恢复数据764
  3. MySQL主从同步原理458
  4. 线程池的理解以及使用414
  5. Spring Cloud Gateway整合nacos实战(三)409
分类
  • Java
    30篇
  • 设计模式
    27篇
  • 数据库
    20篇
  • Spring
    18篇
  • MySQL
    13篇
  • ClickHouse
    11篇
  • Kubernetes
    10篇
  • Redis
    9篇
  • Docker
    8篇
  • SpringBoot
    7篇
  • JVM
    6篇
  • Linux
    5篇
  • Spring Cloud
    5篇
  • 多线程
    5篇
  • Netty
    4篇
  • Kafka
    4篇
  • 面经
    4篇
  • Nginx
    3篇
  • JUC
    3篇
  • 随笔
    2篇
  • 分布式
    1篇
  • MyBatis
    1篇
  • 报错合集
    1篇
  • 生活记录
    1篇
  • 源码
    1篇
  • 性能优化
    1篇

最新评论

  • MySQL数据库被攻击,被删库勒索,逼迫我使出洪荒之力进行恢复数据2022-05-06
    Java技术债务:@capture 一起探讨学习,服务器被黑很正常,及时做好备份以及做好防护
  • MySQL数据库被攻击,被删库勒索,逼迫我使出洪荒之力进行恢复数据2022-04-13
    capture:我的刚上线两天,网站里就两篇文章也被攻击了,纳闷
  • Java常用集合List、Map、Set介绍以及一些面试问题2022-01-18
    Java技术债务:HashSet和TreeSet 相同点:数据不能重复 不同点: 1、底层存储结构不同; HashSet底层使用HashMap哈希表存储 TreeSet底层使用TreeMap树结构存储 2、唯一性方式不同 HashSet底层使用hashcode()和equal()方法判断 TreeSet底层使用Comparable接口的compareTo判断的 3、HashSet无序,TreeSet有序
  • undefined2021-12-14
    Java技术债务:如果不指定线程池,CompletableFuture会默认使用ForkJoin线程池,如果同一时间出现大量请求的话,会出现线程等待问题,建议使用自定义线程池。。。
  • undefined2021-12-02
    you:很好,对于小白相当不错了,谢谢
  • CSDN
  • 博客园
  • 程序猿DD
  • 纯洁的微笑
  • spring4all
  • 廖雪峰的官方网站
  • 猿天地
  • 泥瓦匠BYSocket
  • crossoverJie
  • 张先森个人博客
  • 越加网

© 2021-2023 Java技术债务 - Java技术债务 版权所有
总访问量 0 次 您是本文第 0 位童鞋
豫ICP备2021034516号
Java技术债务 豫公网安备 51011402000164号

微信公众号

Java技术债务
Java技术债务

专注于Spring,SpringBoot等后端技术探索

以及MySql数据库开发和Netty等后端流行框架学习

日志
分类
标签
RSS

有不足之处也希望各位前辈指出