【Golang】基础语法总结

it2022-05-05  223

目录

写在前面基础总结变量数据类型NUMBER,STRING, BOOLEANARRAYS,SLICES,MAPS 类型转换条件表达式循环指针函数方法,结构体,接口结构体方法接口 包安装包创建自定义包内置包 异常处理errorPanicDefer 并发Go routieschannelOne way channelselectBuffered channel

写在前面

Go 语言是一门开源语言,能够轻松的构建简单,可靠,高效的软件。—— Golang

为什么笔者在深入学习java和python以后任然坚持使用golang,因为每个语言的特性不一样,其适应的场合也不尽相同。毕竟大家时间都这么宝贵,何必不用最合适的语言做最合适的事情,个中滋味自己体会。下面就快速把go语言的特性再次总结一下。方便复习或者入门

基础总结

Golang由包组成。Golang编译器将main包编译为可执行文件,而非共享库。main包是应用的入口,通常被定义如下 package main 语言第一波helloworld,新建main.go文件如下;fmt是Go内置的格式化I/O函数。 package main import ( "fmt" ) func main(){ fmt.Println("Hello World!") } 编译 main.go, 编译完成会生成一个main的可执行文件 go build main.go 运行main文件

./main

或者

go run main.go

变量

强类型变量,int 为例,这里默认为0 var a int 变量赋值 var a = 1 变量赋值简化 message := "hello world" 多变量同时赋值 var b, c int = 2, 3

数据类型

NUMBER,STRING, BOOLEAN

int 的类型有 int, int8, int16, int32, int64, unit, unit8, unit16, unit 32, unit64, unitptr…

bool 关键字表示布尔类型。

Golang 也支持复数,用conplex64和complex128表示。

var a bool = true vat b int = 1 var c string = "hello world" var d float32 = 1.222 var x complex128 = cmplx.Sqrt(-5 +12i)

ARRAYS,SLICES,MAPS

Array 是同类型元素的数组。Array在声明的时候会指定长度且不能改变。定义一个固定长度为3的数组:

var a[3] int

多维数组定义

var multiD [2][3]int

Slices 是能随时扩容的同类型元素的序列 。Slice的声明方式如下定义一个长度为0 的slices:

var b []int

或者定义一个有容量和长度的,长度为5,容量为10。

numbers := make([]int, 5, 10)

Slice是数组的封装,其内部实现是数组,slice有三个元素,容量,长度和指向内部数组的指针。 Slice的容量可以通过append 或者 copy函数增加。Append函数也能在数组的末尾添加元素,在容量不足的情况下会对slice扩容。

如下

numbers = append(numbers, 12, 34)

另一种增加slice容量的方式是使用copy函数。Copy函数的原理是创建一个新的大容量的slice,并把原有的slice拷贝到新的slice中。

number2 := make([]int, 15) copy(number2, number)

基于一个slice创建一个子slice

package main import ( "fmt" ) func main() { // 初始化slice number2 := []int{1, 2, 3, 4} fmt.Println(number2) // -> [1 2 3 4] // 创建子slice slice1 := number2[2:] fmt.Println(slice1) // -> [3 4] slice2 := number2[:3] fmt.Println(slice2) // -> [1 2 3] slice3 := number2[1:4] fmt.Println(slice3) // -> [2 3 4] }

map 键值对的定义

var m map[string]int

我们来创建一个key是string value是int的例子

package main import ( "fmt" ) func main() { m := make(map[string]int) // 添加键值对 m["clearity"] = 2 m["simplicity"] = 3 // 打印值 fmt.Println(m["clearity"]) // -> 2 fmt.Println(m["simplicity"]) // -> 3 }

类型转换

使用类型转换来转变一个数据类型

package main import ( "fmt" ) func increment(i *int) { *i++ } func main() { a := 1.1 b := int(a) fmt.Println(b) //-> 1 }

同时我们可以使用如下两种方法来查看相关类型,具体原理后续我们在仔细理解

/*查看类型方式1*/ func typeof(v interface{}) string { return fmt.Sprintf("%T", v) } /*查看类型方式2*/ func typeofReflect(v interface{}) string { return reflect.TypeOf(v).String() }

条件表达式

If else 基本上结构和其他语言相通,需要注意的是花括号和条件表达式位于同一行。

package main import ( "fmt" ) func increment(i *int) { *i++ } func main() { if num := 9; num < 0 { fmt.Println(num, "is negative") } else if num < 10 { fmt.Println(num, "has 1 digit") } else { fmt.Println(num, "has multiple digits") } }

switch case 表达式

package main import ( "fmt" ) func increment(i *int) { *i++ } func main() { i := 2 switch i { case 1: fmt.Println("one") case 2: fmt.Println("two") default: fmt.Println("none") } }

循环

直接看例子

package main import ( "fmt" ) func increment(i *int) { *i++ } func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) }

死循环的写法

for { }

指针

Go 语言可以使用指针,和c语言的指针概念基本类似 。指针存储变量的地址,指针用*来定义。指针的定义和所指数据的类型相关:

var ap *int

这里的ap是指向整型数据的指针,&用于获取所变量的地址。

a :=12 ap = &a 用于获取指针所指的地址的值。 fmt.Println(*ap)

指针通常用于将结构体做为参数传递。

传值通常意味着拷贝,意味着需要更多的内存。使用指针传递时,在函数中改变的变量会传递给调用的方法或函数。 package main import ( "fmt" ) func increment(i *int) { *i++ } func main() { i := 10 increment(&i) fmt.Println(i) } //=> 11

函数

基础函数定义 这里不做详细介绍,Golang 中的函数用func关键字加上函数名, 后面是附带数据类型的参数,最后是函数的返回类型。 package main import ( "fmt" ) func add(a int, b int) int { c := a + b return c } func main() { fmt.Println(add(2, 1)) } 先定义函数返回值名称 add 函数先定义了返回值c 然后直接return 也就是返回的是c变量 package main import ( "fmt" ) func add(a int, b int) (c int) { c = a + b return } func main() { fmt.Println(add(2, 1)) } 定义一个多个返回值的函数,逗号分隔 package main import ( "fmt" ) func add(a int, b int) (int, string) { c := a + b return c, "successfully added" } func main() { sum, message := add(2, 1) fmt.Println(message) fmt.Println(sum) }

方法,结构体,接口

Golang 不是完全的面向对象语言,但是支持很多面向对象的特性,例如有结构体,接口,方法等。

结构体

结构体的基本定义 这些和c++的结构体基本一致。写法上一些区别 type person struct { name String age int gender string } 方法体使用 //方式 1: 指定属性和值 p = person{name: "Bob", age: 42, gender: "Male"} //方式 2: 只指定值 person{"Bob", 42, "Male"} 方法体属性的使用1 直接用.来调用 p.name //=> Bob p.age //=> 42 p.gender //=> Male

– 方法体属性的使用2 使用指针

pp = &person{name: "Bob", age: 42, gender: "Male"} pp.name //=> Bob

方法

方法是一种带有接受器的特殊函数。接收器可以是值或者指针。 使用.来调用方法需要注意的是,接收器是指针的话,我们传递的是值的引用,这意味着我们在方法做修改将会反映到变量pp上。该不会创建对象的拷贝,将会节省内存。

package main import "fmt" // 定义结构体 type person struct { name string age int gender string } // 定义方法 func (p *person) describe() { fmt.Printf("%v is %v years old.", p.name, p.age) } func (p *person) setAge(age int) { p.age = age } func (p person) setName(name string) { p.name = name } func main() { pp := &person{name: "Bob", age: 42, gender: "Male"} pp.describe() // => Bob is 42 years old pp.setAge(45) fmt.Println(pp.age) //=> 45 pp.setName("Hari") fmt.Println(pp.name) //=> Bob }

接口

Golang中的接口是方法的集合,接口有助于将同类型的属性组合起来

type animal interface { description() string } 定义接口并实现 package main import ( "fmt" ) type animal interface { description() string } type cat struct { Type string Sound string } type snake struct { Type string Poisonous bool } func (s snake) description() string { return fmt.Sprintf("Poisonous: %v", s.Poisonous) } func (c cat) description() string { return fmt.Sprintf("Sound: %v", c.Sound) } func main() { var a animal a = snake{Poisonous: true} fmt.Println(a.description()) a = cat{Sound: "Meow!!!"} fmt.Println(a.description()) }

在Golang中,我们的代码在某个包下。main包是程序执行的入口。在Go中有很多内置的包,例如我们之前用过的fmt包。

安装包

安装的包保存在GOPATH的环境中,你可以在 $GOPATH/pkg 路径下看到安装的包。使用如下方法导入项目需要引入的包

go get // 例子 go get github.com/satori/go.uuid

创建自定义包

自定义一个文件夹/custom_package/person 并且新建文件persion.go

mkdir custom_package cd custom_package mkdir person cd person

package person func Description(name string) string { return "The person name is: " + name } func secretName(name string) string { return "Do not share" }

这样整个包的内容就创建完成了

生成包并引用

go install

然后在person上层文件夹里创建main.go 如下并且引用刚才创建的包

package main import ( "custom_package/person" "fmt" ) func main() { p := person.Description("Milap") fmt.Println(p) }

这里特别注意的是 person包里面的secretName函数开头是小写字母开头,默认为私有的函数 也就是不可直接被访问

生成包文档

生成文档命令

godoc person Description

查看文档命令,访问http://localhost:8080/pkg/即可查看

godoc -http=":8080"

这里可以看大go把包的概念划分的比较细,可以非常小颗粒度的进行引用

内置包

fmt包实现可标准的I/O函数,我们在之前的包中用过其中的打印输出函数。

Golang 中另一个内置的重要包的是json,它能够对JSON进行编解码。

json编码

package main import ( "encoding/json" "fmt" ) func main() { mapA := map[string]int{"apple": 5, "lettuce": 7} mapB, _ := json.Marshal(mapA) fmt.Println(string(mapB)) }

json解码 解码的时候使用Unmarshal方法,第一个参数是json字节,第二个参数是要映射的结构体的地址。需要注意的是json中的“page”对应的是结构体中的PageNumber。

package main import ( "encoding/json" "fmt" ) type response struct { PageNumber int json:"page" Fruits []string json:"fruits" } func main() { str := {"page": 1, "fruits": ["apple", "peach"]} res := response{} json.Unmarshal([]byte(str), &res) fmt.Println(res.PageNumber) }

异常处理

在golang里面其实叫错误处理,因为我们在其他语言里叫习惯了异常处理,这里我们就先按照异常处理来理解。两者也是有一定差异的

error

当我们在自定义函数是,某些情况下会产生错误。我们可以使用error 对象返回这些错误: package main import ( "errors" "fmt" ) func Increment(n int) (int, error) { if n < 0 { // return error object return 0, errors.New("math: cannot process negative number") } return (n + 1), nil } func main() { num := 5 if inc, err := Increment(num); err != nil { fmt.Printf("Failed Number: %v, error message: %v", num, err) } else { fmt.Printf("Incremented Number: %v", inc) } }

Panic

Panic是程序运行中突然产生未经处理的异常。在Go中,panic不是合理处理异常的方式,推荐使用error对象代替。当Panic产生时,程序将会暂停运行。当panic被defer之后,程序才能继续运行。

package main import "fmt" func main() { f() fmt.Println("Returned normally from f.") } func f() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() fmt.Println("Calling g.") g(0) fmt.Println("Returned normally from g.") } func g(i int) { if i > 3 { fmt.Println("Panicking!") panic(fmt.Sprintf("%v", i)) } defer fmt.Println("Defer in g", i) fmt.Println("Printing in g", i) g(i + 1) }

Defer

efer 在函数结尾一定会执行。在上面的函数中,使用panic() 暂停程序的运行,defer语句使程序执行结束时使用改行。Defer也可以用作我们想要在函数的结尾执行的语句,例如关闭文件。

并发

Go routies

Go routine 是能够并行运行的函数。创建Go routine 非常简单,只需要在函数前添加关键字go,这样函数就能够并行运行了。Go routines 是轻量级

package main import ( "fmt" "time" ) func main() { go c() fmt.Println("I am main") time.Sleep(time.Second * 2) } func c() { time.Sleep(time.Second * 2) fmt.Println("I am concurrent") }

例子实现了简单的并行运行。我们想要在多线程中共享资源,但是Golang并不支持。因为这会导致死锁和资源等待。Go 提供了另一种共享资源的方式:channel

channel

我们可以使用Channel在两个Go routine之间传递数据。创建channel之前需要制定接受的数据类型。例如我们创建了一个接受string类型的channel。

创建一个可以发送和接收string数据的channel

c := make(chan string)

这里双向channel的例子,既可以接收也可以发送数据

package main import "fmt" func main() { c := make(chan string) go func() { c <- "hello" }() msg := <-c fmt.Println(msg) }

那我们如何创建单项的channel也就是或者接收或者发送呢,也就是One way channel

One way channel

这里也就创建了一个可以只能发送的channel

package main import ( "fmt" ) func main() { ch := make(chan string) go sc(ch) fmt.Println(<-ch) } func sc(ch chan<- string) { ch <- "hello" }

select

有这样一种情况,一个函数等待多个channel,这时候我们可以使用select语句。例如:

main函数等待两个channel,c1和c2。使用select语句,先从channel中收到的数据会被打印出来。

package main import ( "fmt" "time" ) func main() { c1 := make(chan string) c2 := make(chan string) go speed1(c1) go speed2(c2) fmt.Println("The first to arrive is:") select { case s1 := <-c1: fmt.Println(s1) case s2 := <-c2: fmt.Println(s2) } } func speed1(ch chan string) { time.Sleep(2 * time.Second) ch <- "speed 1" } func speed2(ch chan string) { time.Sleep(1 * time.Second) ch <- "speed 2" }

Buffered channel

在Golang中可以创建buffered channel,当buffer满的时候,发送数据给channel将会被阻塞。例如:

package main import "fmt" func main() { ch := make(chan string, 2) ch <- "hello" ch <- "world" ch <- "!" // extra message in buffer fmt.Println(<-ch) }

那么基础的golang的一些理解性的东西也就在这了,深入的使用还是需要真正用起来 。


最新回复(0)