Go语言学习之路
Day1
Go语言基础语法
变量类型
常见变量类型包括 :字符串 整形 浮点型 布尔型,字符串可以通过加号拼接,也可以通过等号直接进行比较。
声明方式
var name string = '' '' 注:string所在位置为声明变量类型
变量 := 等于值
在Go语言中,使用:=
操作符进行变量声明和赋值是一种简短的语法形式,称为短变量声明。它可以在函数内部声明变量并同时进行赋值操作。在这个特定的代码片段中
常量
将上文var改成const,且值得一说其没有确定的类型,会根据上下文进行推断。
if-else
与java类似感觉,但是条件判断中不用加括号,并且后面运行语句必须有大括号。
循环
go里面是没有while循环的,for循环后面什么也不写就是一个死循环,可以用break跳出,也可以正常操作‘。值得一提的是,虽然没有while但是效果是一样的。
switch
这个功能比较通俗易懂,不仅可以使用任意类型的变量,还可以直接在switch代码块前不加任何变量,里面进行case条件分支。
array数组
主要声明方式为如下
var a [5]int
b := [5]int{1,2,3,4,5}
slice 切片
切片可以随意更改长度,可以使用make来创建一个
s := make([]String,3)
s[0] = "a"
也可以用append来追加元素,但是要注意的是要将append的结果赋值给原数组
s = append(s,"d")
进行切片操作,与python操作类似,只是不支持负数索引
fmt.println(s[2,5])
切片的创建分为两种:一种是make函数进行或是使用切片字面量,前者同上,而后者则是直接指定了切片的元素如下:
good := []String{"g","o","o","d"}
这样更加直观。
map
创建方式
m := make(map[String]int)//String为key,int为value
m["one"] = 1
m["two"] = 2
fmt.println(m)//map[one:1,two:2]
m2 := map[string]int{"one":1,"two":2}
var m3 := map[string]int{"one":1,"two":2}
可以用delete方法删除
另外在拓展一点make函数,一搬来说,通常用来创建切片,映射,通道这三种数据结构,使用make创建可以初始化一个新的映射并指定键值的类型以及初始容量,但是也可以使用映射字面量来创建并进行初始化映射·,可以不需要make来创建。
m2 := map[String]int{"one":1,"two":2}
var m3 := map[String]int{"one":1,"two":2}
最后可以使用 := 操作符和ok变量检查
r,ok := m["unknow"]
fmt.println(r,ok)//0,false
range遍历
对于一个map或者是slice,range可以快速遍历
nums := []int{1,2,3,4}//首先建立一个切片
sum := 0
for i,num := range,nums{
sum += num
if num == 2{
fmt.println("index=",i,"num=",num)
}
}
fmt.println(sum) //将打印出10
//注:如果i在代码块中未出现的话,for后面那个i要用_代替
对于数组会返回两个值,第一个是索引,第二个是对应位置的值,如果不需要索引,那么可以用下划线来代替。
对于map来说
m := map[string]int{"A":1,"B":2}
for k,v := range m{
fmt.println(k,v)//A 1 B 2
}
foe k := range m{
fmt.println("key",k)//key 1 key 2
}
函数
golang和许多语言不一样的是,变量类型是后置的,函数的原生支持返回多个值,但是几乎所有的函数都返回两个值,第一个是真正的结果,第二个是错误信息。
func add(a,b int) int {
return a+b
}
上面这个返回值为int
func exists(m[string]string,k string)(v string,ok bool){
v,ok = m[k]
return v,ok
}
在这个函数中,函数的返回值可以通过函数签名中指定返回值的类型来定义。exists
函数的返回值被定义为 (v string, ok bool)
,这意味着函数将返回两个值:一个字符串 v
和一个布尔值 ok
。在函数体中,v, ok = m[k]
这行代码尝试从映射 m
中获取键为 k
的值,并将其赋值给 v
和 ok
。如果键 k
存在于映射 m
中,ok
将被设置为 true
,否则 ok
将被设置为 false
。
在Go语言中,当函数返回多个值时,不需要在每个返回值的变量名前添加冒号 :
。相反,你只需要在函数签名中指定返回值的类型,并在函数体中使用这些变量名来接收返回值。
因此,这里不加入冒号是因为Go语言的语法规定,当返回多个值时,不需要在变量名前添加冒号。
这样直接给一段代码看看区别
var r int
var ok bool
r, ok = m["unknow"]
fmt.Println(r, ok) // 0 false
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
前者不需要加冒号,但是需要先声明变量r和ok,然后在进行赋值。在Go语言中,使用:=
操作符进行变量声明和赋值是一种简短的语法形式,称为短变量声明。它可以在函数内部声明变量并同时进行赋值操作。
另外就是函数使用上,有时候像下面的传入参数为map和一个字符串,返回的类型为string和bool函数对应。
func main() {
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
指针
go的指针主要是对于传入参数进行修改
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
结构体
结构体是带类型的字段的集合,感觉就是Java里的对象
type user struct{
name string
password string
}
func main(){
a := user{name : "111",password : "123"}
b := user{"111","123"}
c := user{name :"111"}
c.password = "123"
var d user
d.name = "111"
d.password = "123"
fmt.Println(a, b, c, d)
// {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false,
}
func checkPassword1(u user, password string) bool{
return u.password == password
}
func checkPassword2(u *user, password string) bool{
return u.password == password
}
注意&在代码中意思为取址操作符,意味在传递变量a的内存地址给函数checkPassword2。
函数1和函数2具体区别就在参数上,
- 使用方式:
- 调用
checkPassword
函数时,需要传递一个user
结构体的实例和一个密码字符串。 - 调用
checkPassword2
函数时,需要传递一个指向user
结构体的指针和一个密码字符串。
- 调用
- 功能差异:
checkPassword
函数只能检查传入的user
结构体实例的密码是否与给定的密码匹配,它不能修改传入的user
结构体实例。checkPassword2
函数不仅可以检查传入的user
结构体实例的密码是否与给定的密码匹配,还可以修改传入的user
结构体实例的密码字段,因为它接受的是一个指向user
结构体的指针。
例如,如果你想在检查密码后重置用户的密码,你可以这样做:
func resetPassword(u *user, newPassworrd string){
if checkPassword2(u,oldPassword){
u.password = newPassword
}else{
fmt.printLn("cuowumima")
}
}
结构体方法
就像是java中的成员方法一样,在实现结构体方法的时候有两种写法,就是带指针和不带指针,前者可以修改这个结构体,而后者仅仅是拷贝了一份,无法进行修改。具体操作方式就是在函数名前加一个括号,里面填写第一个参数
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
错误处理
在go语言中与Java的异常处理是不一样的,go的语言处理可以很清晰的知道哪个函数错了,并用简单的if-else来进行处理。在函数里的返回值类型加一个error,代表这个函数可能会返回错误,那么在函数返回需要同时返回两个值,要么出现错误的话,返回return nil和一个error,没有就正常结果和nil。
nil首先我们要来了解一下,是一个预定义的标识符,用于表示指针、接口、切片、映射、通道和函数类型的零值。具体来说:
-
对于指针类型,
nil
表示一个不指向任何有效内存地址的指针。 -
对于接口类型,
nil
表示一个不包含任何值的接口。 -
对于切片、映射和通道类型,
nil
表示一个未初始化的变量,即它还没有被赋予一个有效的实例。 -
对于函数类型,
nil
表示一个不指向任何函数的函数类型变量。func findUser(users []user,name string)(v *user,err error){ for _,u := range users{ if u.name == name{ return &u,nli } } return nil,errors.New("not found") } func main(){ u,err := findUser([]user{{"wang","123"}},"wang") if err != nil{ fmt.println(err) return } fmt.println(u.name) if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil { fmt.Println(err) // not found return } else { fmt.Println(u.name) } }
另外在拓展一下New这个内置函数,用于分配内存并返回一个指向新分配的类型为
T
的零值的指针。这里的T
可以是任何类型,包括结构体、切片、映射等。例如,如果你有一个结构体类型
user
,你可以使用new
来创建一个该结构体的新实例,并返回一个指向它的指针:u := new(user)
这将创建一个新的
user
结构体实例,并将其地址赋值给变量u
。此时,u
的值为&user{}
,即一个指向结构体user
的零值的指针。代码中,
errors.New("not found")
调用了errors
包中的New
函数,该函数用于创建一个新的错误对象。errors.New
接受一个字符串参数作为错误消息,并返回一个指向新创建的错误对象的指针。这个错误对象可以被用来表示函数执行过程中发生的错误。