GO 语言基础学习记录

一:声明变量

在golang语言中声明变量的方式

package main

import "fmt"

func main() {

var a int = 3 //关键字 var + 变量名 + 变量指定类型 = 变量值

var b int //关键字 var + 变量名 + 变量指定类型(注意:当变量没赋值时是按照变量指定类型的默认值进行赋值,如 【var b int】最后输出的就是int类型的0 而【var b bool】则会输出bool类型的false )

var c = 4 //关键字 var + 变量名 = 变量值(注意:当变量未设置类型则会根据变量值自行补充,如【var c = 4】则c的类型为int 而【var c = "hello"】c的类型则为string)

var d, e int = 1, 2 //这是在第一个声明的基础上进行多个变量声明

var f, g int //这是在第二个声明基础上进行多个变量声明

var h, i = 7, 8 //这是在第三个声明基础上进行多个变量声明

//输出

fmt.Println(a, b, c, d, e, f, g, h, i)

//输出的值为 3 0 4 1 2 0 0 7 8

}

注意:在方法中设置变量的时候,一定要在方法体中用到,否则会报错(如果 var c int 在方法中却没有使用,则运行程序会报错!)!! -- go的全局变量不限制,仅校验方法中的变量

go中除了用 var 关键字声明的变量外,还提供了 := 的方式声明未声明过的变量。

package main

import "fmt"

func main() {

a := 3

b := "hellow"

c, d := 8, "word"

//输出

fmt.Println(a, b, c, d)

//输出的值为 3 hellow 8 word

}

注意::= 只能用于声明 未被声明过的变量( 如声明 var a int = 3 后再 a := 4 就会报错)

二:声明常量

常量的声明可以参考变量声明的第一部分-此处省略

const a int = 3 //关键字 const + 常量名 + 常量类型 = 常量值

注意:常量在方法中声明不校验是否调用、

除了上面的常量声明方式外,常量还可用作枚举

复制代码
package main

import "fmt"

func main() {
    const (
        Unknown = 0
        Female = 1
        Male = 2
    )
    //输出
    fmt.Println(Unknown, Female, Male)
    //输出值为 0 1 2
}

在常量声明时,可以引入"unsafe" 包使用 函数计算表达式,注意:必须是系统内置函数

package main

import "fmt"

import "unsafe"

func main() {

const (

a = "abc"

b = len(a)

c = unsafe.Sizeof(a)

)

const e string = "abg"

const f int = len(a)

const g = unsafe.Sizeof(a)

//输出

fmt.Println(a, b, c, e, f, g)

//输出值为 abc 3 16 abg 3 16

}

相对于常量声明中 枚举类型 有个 iota 用法 ,就是 从0开始依次递增

package main

import "fmt"

func main() {

const (

a = iota //0

b //1

c //2

d = "ha" //独立值,iota += 1

e //"ha" iota += 1

f = 100 //iota +=1

g //100 iota +=1

h = iota //7,恢复计数

i //8

)

fmt.Println(a,b,c,d,e,f,g,h,i)

}

三、运算符

go的运算符和其他编程语言基本一致

+(加)、-(减)、*(乘)、/(除)、%(取余数)、++(自增)、--(自减)、==(相等)、!=(不等)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)、&&(与)、||(或)、!(非)等等

这里着重记录一下 位运算 和 其他运算符 (了解即可,日常业务中很少用到)

位运算符:&(二进制相与)、|(二进制相或)、^(二进制相异)、<<(二进制左移)、>>(二进制右移)

package main

import "fmt"

func main() {

var a uint = 60 /* 60 = 0011 1100 */

var b uint = 13 /* 13 = 0000 1101 */

var c uint = 0

c = a & b /* 12 = 0000 1100 */ //二进制相与,当二进制码对应位都为1时记录为1

fmt.Printf("第一行 - c 的值为 %d\n", c )

c = a | b /* 61 = 0011 1101 */ //二进制相或,当二进制码对应位有一位为1时记录为1

fmt.Printf("第二行 - c 的值为 %d\n", c )

c = a ^ b /* 49 = 0011 0001 */ //二进制相异,当二进制码对应位值不同时记录为1

fmt.Printf("第三行 - c 的值为 %d\n", c )

c = a << 2 /* 240 = 1111 0000 */ //二进制左移,将左侧丢弃,右侧填补为0,<<2就是丢弃两位

fmt.Printf("第四行 - c 的值为 %d\n", c )

c = a >> 2 /* 15 = 0000 1111 */ //二进制右移,将右侧丢弃,左侧填补为0

fmt.Printf("第五行 - c 的值为 %d\n", c )

}

其他运算符:&(获取变量存储地址)、*(指针变量)

package main

import "fmt"

func main() {

var a int = 4

var ptr *int

/* & 和 * 运算符实例 */

ptr = &a /* 'ptr' 包含了 'a' 变量的地址 */

fmt.Printf("a 的值为 %d\n", a); //输出a变量的值 4

fmt.Printf("a 的值为 %d\n", &a); //输出a变量的变量存储地址,能粗浅理解为数据库的索引

fmt.Printf("*ptr 为 %d\n", *ptr); //输出的是指针变量下的值 4

fmt.Printf("ptr 为 %d\n", ptr); //输出指针变量的存储地址

}

在go中也有其他语言的 if、if...else..、switch、等。

也有:for 循环和嵌套,有 break、continue、goto语句。

注意:在go中没有while循环

四、函数、方法声明和调用

package main

import "fmt"

func main() {

var a, b int = 4,5

var c int

var s string

c = sum(a,b)

s = fmt.Sprintf("获取a+b=%d",c)

fmt.Println(s)

}

func sum(x int,y int) int{ //声明函数 关键字func + 函数名称 + 函数参数和参数类型 + 返回类型

return x+y

}

注意:声明函数的时候,必须设定参数类型和返回值类型!

这里记录一下 函数多个返回值 和 函数闭包(匿名函数) 的用法

package main

import "fmt"

func main() {

a, b := swap("Google", "Runoob")

fmt.Println(a, b)

}

func swap(x, y string) (string, string) { //设定两个string类型的返回值

return y, x

}
package main

import "fmt"

func main() {

// 定义一个匿名函数并将其赋值给变量add

add := func(a, b int) int {

return a + b

}

// 调用匿名函数

result := add(3, 5)

fmt.Println("3 + 5 =", result)

// 在函数内部使用匿名函数

multiply := func(x, y int) int {

return x * y

}

product := multiply(4, 6)

fmt.Println("4 * 6 =", product)

// 将匿名函数作为参数传递给其他函数

calculate := func(operation func(int, int) int, x, y int) int {

return operation(x, y)

}

sum := calculate(add, 2, 8)

fmt.Println("2 + 8 =", sum)

// 也可以直接在函数调用中定义匿名函数

difference := calculate(func(a, b int) int {

return a - b

}, 10, 4)

fmt.Println("10 - 4 =", difference)

}

再记录一下 go 中的方法

package main

import (

"fmt"

)

/* 定义结构体 */

type Circle struct {

radius float64

}

func main() {

var c1 Circle

c1.radius = 10.00

fmt.Println("圆的面积 = ", c1.getArea())

}

//该 method 属于 Circle 类型对象中的方法

func (c Circle) getArea() float64 {

//c.radius 即为 Circle 类型对象中的属性

return 3.14 * c.radius * c.radius

}

注意:从上面的例子中我们可以看出,在go中 方法和函数是不同的。方法在定义的时候会在 func 关键字后面 方法名的前面 设置一个接收器( c Circle) 该接收器可在方法中调用

五、变量作用域

函数方法外面的是全局变量,方法或函数内的则是局部变量,局部变量优先级大于全局变量。局部变量除非作为参数传入另一个方法或函数中,否则两个方法或函数中的局部变量不能共用。

六、数组

在go中初始化数组,需要明确个数和类型

var a [10]int //初始化数组 关键字 var + 数组名 + [数组元素个数]数组元素类型

var a = [5]int{1,2,3,4,5} //初始化数组 a 并赋值

a := [5]int{1,2,3,4,5} //省略关键字 var 初始化数组 a 并赋值

var a = [...]int{1,2,3} //省略数组元素个数,通过"..."程序会自动通过赋值确定元素个数

a := [...]int{1,2,3} //和上面同理

var a []int //设置空切片a 注意:不指定长度的数组都是切片,第七点记录切片

a := []int{} //设置空切片a

多维数组也同理 var a [][]int 初始化即可

package main

import "fmt"

func main() {

// Step 1: 创建数组

values := [][]int{} //创建二维空数组

// Step 2: 使用 append() 函数向空的二维数组添加两行一维数组

row1 := []int{1, 2, 3}

row2 := []int{4, 5, 6}

values = append(values, row1)

values = append(values, row2)

// Step 3: 显示两行数据

fmt.Println("Row 1")

fmt.Println(values[0])

fmt.Println("Row 2")

fmt.Println(values[1])

// Step 4: 访问第一个元素

fmt.Println("第一个元素为:")

fmt.Println(values[0][0])

}

七、切片

相较于数组的长度不可改变,go还提供了切片的类型也可以称之为 动态数组,即它的长度不固定,可以直接追加元素提升切片的元素数

通过**make()**函数来创建切片

var slice []type = make([]type, len) //注意:len只是定义切片的初始长度 可以为0

slice := make([]type, len)

如:

slice1 := make([]int,0) //这行代码创建了个长度为0 的slice1 切片

除了用make() 函数来创建切片外

slice2 := []int{} 和 var slice2 []int 也是初始化了一个切片

对于数组来说,切片除了不用固定长度这一个优点外,还有一个切片赋值的功能

package main

import "fmt"

func main() {

arr :=[]int {1,2,3 } //初始化一个切片 a 里面有三个值

s := arr[0:2] //将切片下标0-2 的值 赋值给s

fmt.Println(s) //输出 [1 2]

fmt.Println(len(arr)) //

fmt.Println(cap(arr)) //

}

切片除了通过[:]来获取指定下标的值,由于切片有索引,所以go很多内置函数都可作用于切片上

如:len()//获取切片长度 cap() //计算容量获得切片最大长度

八、range的用法

当需要循环数组、切片、集合等数据时,可以用for + range 的关键字来遍历。

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {

for i, v := range pow { //应用range时,会直接将循环中的key值提供给i,value值提供给v(i,v是变量类型,可以设置成你熟悉的变量名 如:k,v 也可以)

fmt.Printf("2**%d = %d\n", i, v)

}

}

除了将 pow 的值完全按照 key-value 的形式完全赋值给 i,v 外,如果只想要key,则可以省略v 如:for i := range pow{ 或者用 _ 代替 如 for i,_ := range pow {

当然,如果只想要value 不要key时也可以用 _ 代替 如: for _,v := range pow{

注意://range也可以用来枚举 字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身

九、map的用法

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。

在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 ""。

Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

// 创建一个空的 Map

m := make(map[string]int)

// 创建一个初始容量为 10 的 Map

m := make(map[string]int, 10)

复制代码
// 使用字面量创建 Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}
复制代码
// 获取键值对
v1 := m["apple"]     //map 就和 php中的数组对象类似,用值的时候直接 变量[key值] 即可
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值

Map很像php 中的数组对象类型,但是Map是引用类型,要注意改变其中一个值时 其他引用过Map的变量都会更改

package main

import "fmt"

func main() {

s := map[string]string{ //初始化一个map数据

"one" : "one",

"two" : "two",

"three" : "three",

}

c := s //将 s 赋值给 c

a := s["two"]

b := s["two"]

a = "for"

fmt.Println(a, b, s, c) //输出值 for two map[one:one three:three two:two] map[one:one three:three two:two]

s["two"] = "fiv"

fmt.Println(a, b, s, c) //for two map[one:one three:three two:fiv] map[one:one three:three two:fiv]

}

从上面的示例可以看出,当将整个map类型的s赋值给c时,修改s 的值,c同样会变化!

delete() 函数可以删除map中的值 delete(map变量,需要删除的key值)

十、变量类型转换

1、数值类型转换

var a float64 = 3.7415926 //定义float64类型a为3.7415926

var b int = int(a) //将a转变成int类型赋值给b

fmt.Println(a, b) //输出结果 a=3.7415926 b=3 从float转化为int时,会直接舍去小数点后的值

注意:go 不支持隐式转换类型,比如 :

复制代码
package main
import "fmt"

func main() {  
    var a int64 = 3
    var b int32
    b = a
    fmt.Printf("b 为 : %d", b)
}

会报错

复制代码
cannot use a (type int64) as type int32 in assignment
cannot use b (type int32) as type string in argument to fmt.Printf

但是如果改成 b = int32(a) 就不会报错了:

复制代码
package main
import "fmt"

func main() {  
    var a int64 = 3
    var b int32
    b = int32(a)
    fmt.Printf("b 为 : %d", b)
}
2、字符串类型转换
复制代码
var str string = "10"        //定义字符串str
var num int                  //初始化int类型num
num, _ = strconv.Atoi(str)   //将字符串转换为num的数据类型,其中 _ 位置取的是转换时的错误信息,如果想查看错误信息则需

var msg error //定义error类型 msg

num,msg = strconv.Atoi(str) //通过 msg 获取转换失败的信息

或者可以用下面方法直接获取:

number, msg = strconv.Atoi(str) //没有错误时 msg为nil

注意:使用strconv.Atoi() 函数时,要引入 strconv 包

3.接口类型转换

接口类型转换有两种情况**:类型断言** 和类型转换

类型断言用于将接口类型转换为指定类型:

value.(T)

其中 value 是接口类型的变量, T 是要转换成的类型。

如果类型断言成功,它将返回转换后的值和一个布尔值,这个布尔值表示转换是否成功。

package main

import "fmt"

func main() {

var i interface{} = "Hello, World" //定义接口变量i

str, ok := i.(string) //将i类型断言为字符串类型 返回转换后的值和断言结果

if ok { //类型断言成功

fmt.Printf("'%s' is a string\n", str)

} else { //类型断言失败

fmt.Println("conversion failed")

}

}

类型转换用于将一个接口类型的值转换为另一个接口类型:

T(value)

T 是目标接口类型,value 是要转换的值。

注意:在类型转换中,我们必须保证要转换的值和目标接口类型之间是兼容的,否则编译器会报错。

package main

import "fmt"

type Write interface { //定义接口Write 接口参数为 []byte类型数据 返回值为 int 和 error类型

Write([]byte)(int,error)

}

type WriteString struct { //定义结构体WriteString 结构体属性 str 为字符串类型

str string

}

func (ws WriteString) Write(data []byte)(int, error){

ws.str += string(data)

return len(ws.str) , nil

}

func main(){

var w WriteString

ss := "hellow_Word!"

sb := []byte(ss)

fmt.Println(w.Write(sb))

}

十一、go实现并发

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。

package main

import (

"fmt"

"time"

)

func say(s string) {

for i := 0; i < 5; i++ {

time.Sleep(100 * time.Millisecond)

fmt.Println(s)

}

}

func main() {

go say("world")

say("hello")

}

//输出的 world 和 hello 出现的顺序并不固定,也从侧面证明了 使用go关键字后确实 两次方法调用确实没有先后,是处于两条并发线程中

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

package main

import "fmt"

func sum(s []int, c chan int) {

sum := 0

for _, v := range s {

sum += v

}

c <- sum // 把 sum 发送到通道 c

}

func main() {

s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int)

go sum(s[:len(s)/2], c)

go sum(s[len(s)/2:], c)

x, y := <-c, <-c // 从通道 c 中接收

fmt.Println(x, y, x+y)

}

目前我认为通道的作用 就是用于等待时常 可以同时获取到 两个goroutine 的值。

注意:x和y的值不一定是按照执行代码先后时间来分配的,也就是说x有可能获得第二个结果

如下面的代码,只要多执行几次,总会获得 *d, *e, *f 依旧会输出 1, 2, 3 的情况 或者 其中几个值是sum 运行后的值,从而证明了 并发并不是执行完goroutine 之后再进行下面代码的执行。

package main

import "fmt"

func sum(s []int, c *int) {

sum := 0

for _, v := range s {

sum += v

}

*c = sum

fmt.Println(" sum中:",*c,"------\n")

}

func main() {

s := []int{7, 2, 8, -9, 4, 0}

a,b,c := 1,2,3

d, e, f := &a, &b, &c

go sum(s[:len(s)/2], d)

go sum(s[len(s)/2:], e)

go sum(s[2:len(s)-2], f)

fmt.Println(" 变量值 ",*d, *e, *f,"------\n")

}

或者我提供另一个佐证 执行这段代码时,会沉睡,直至x和y 接收到结果

package main

import (

"fmt"

"time"

)

func sum(s []int, c chan int) {

sum := 0

time.Sleep(10 * time.Second) //沉睡十秒

for _, v := range s {

sum += v

}

c <- sum // 把 sum 发送到通道 c

}

func main() {

s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int)

go sum(s[:len(s)/2], c)

go sum(s[len(s)/2:], c)

x, y := <-c, <-c // 从通道 c 中接收

fmt.Println(x, y, x+y)

}

学习时借助查看 菜鸟教程

网址:Go 语言教程 | 菜鸟教程

相关推荐
①个程序员27 分钟前
thinkphp 学习记录
学习
alfiy1 小时前
Elasticsearch学习笔记(四) Elasticsearch集群安全配置一
笔记·学习·elasticsearch
向上的车轮1 小时前
Django学习笔记十一:部署程序
笔记·学习·django
不灭锦鲤2 小时前
ssrf学习(ctfhub靶场)
网络·学习·安全
alfiy2 小时前
Elasticsearch学习笔记(五)Elastic stack安全配置二
笔记·学习·elasticsearch
权^2 小时前
MySQL--聚合查询、联合查询、子查询、合并查询(上万字超详解!!!)
大数据·数据库·学习·mysql
冷静 包容2 小时前
C语言学习之 没有重复项数字的全排列
c语言·开发语言·学习
K3njuan2 小时前
《数据结构》学习系列
学习
结衣结衣.2 小时前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
limengshi1383925 小时前
通信工程学习:什么是RIP路由信息协议
网络·网络协议·学习·智能路由器·信息与通信