一、走进Go语言基础语法
1.Go语言的基本特点
(1)高性能、高并发:不需要寻找经过性能优化的第三方库,只需要使用标准的第三方库;
(2)语法易懂:不需要表达式括号、只有一个for循环;
(3)丰富的标准库:有功能完善的标准库,稳定性、兼容性好;
(4)完善的工具链:各种功能的实现都有工具能实现帮助;
(5)静态编译:只需要复制编译后的文件就可以执行,部署方便快捷;
(6)快速编译:编译速度很快;
(7)跨平台:多种操作系统都能运行;
(8)垃圾回收:无需考虑内存的分配释放
2.GO语言的变量类型:包括字符串、整数、浮点型、布尔型等
其中字符串是内置类型,可以直接通过加号拼接,也可以直接用等于号比较两字符串
变量声明有两种方式:
var name string = ""
使用变量 := 等于值
var a = " initial"
var b,c int = 1 , 2
var d = true
var e float64
f := float32(e)
g := a + "foo"
常量的定义就是把 var 改成 const,注意 go里面的常量没有确定类型,根据上下文自动确定
const s string = " constant"
const h = 500000000
const i = 3e20/h
if - else语句 :go语言里的if-else里的if后面没有括号,且后面必须接大括号:
if 7 % 2 == 0 {
fmt.Println( " 7 is even" )
} else {
fmt.Println( " 7 is old " )
}
循环:go语言中只有唯一的一种for循环,循环途中可以用break跳出,或者continue来继续循环
for{
fmt.Println(" loop ")
break
}
for n := 0; n < 5; n++ {
if n % 2 == 0 {
continue
}
fmt.Println(n)
}
switch分支结构:switch后的变量名,不需要括号。最大的不同点在于,go语言中的每一个case后面不需要加break,switch case后的语句不会一直往下执行的。
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
}
数组:有编号且长度固定的元素序列。
var a [5]int
b := [5]int{1,2,3,4,5}
var twoD [2][3]itn
切片:不同于数组,可以任意更改长度,操作更丰富。可以用make来创建一个切片,像数组一样去取值,使用append来追加元素。
slice实质上是存储了一个长度和容量,加上一个指向数组的指针,在执行append操作的时候,如果容量不够的话,会扩容并且返回新的slice。
s := make([]string , 3)
s = append(s , "d")
s = append(s , "e", "f")
map:是实际使用过程中最频繁用到的数据结构,可以用make来创建一个空 map,第一个是key的类型,第二个是value的类型。可以存储或者取出键值对,也可以用delete删除键值对。
golang的map是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
delete(m,"one")
m2 := map[string]int{"one":1 , "two":2}
range:可以快速遍历slice或者map。range遍历的时候,对于数组会返回两个值,第一个是索引,第二个是对应位置的值,如果不需要索引的话,可以用下划线来忽略。
nums := []int{2,3,4}
for i,num := range nums {}
m := map[string]string{"a": "A" , "b": "B"}
for k,v := range m{
fmt.Println(k , v)
}
for k := range m {
fmt.Println("key" , k)
}
函数:golang和其他语言不一样的是,变量的类型是后置的,其函数原生支持返回多个值。在实际的业务逻辑代码里几乎所有函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息。
func add( a int , b int) int {
return a + b
}
func exists(m map[string]string, k string) (v string , ok bool){
v , ok = m[k]
return v , ok
}
指针:golang里的支持的指针操作很有限,其一个用途就是对传入参数进行修改。
fun add2ptr( n *int){
*n += 2
}
func main( ){
add2tr(&n)
fmt.Println(n) //7
}
结构体:结构体是带类型的字段的集合。可以用结构体的名称去初始化一个结构体变量,构造的时候需要传入每个字段的初始值。也可以用键值对的方式去指定初始值,这样可以只对一部分字段进行初始化。同样结构体也能支持指针,这也可以实现对结构体的修改。
type user struct{
name string
password string
}
func main(){
a := user{name: "wang", password:"1024"}
c :=user{name: "wang"}
c.password = "1024"
}
结构体方法:golang里可以为结构体去定义一些方法,类似其他语言里面的类成员函数。实现结构体的方法也有两种写法,一种是带指针,一种是不带指针。区别在于,如果带指针的话,实际上就可针对这个结构体去做修改,如果不带指针的话,实际上操作的是一个拷贝,无法对结构体进行修改。
type user struct{
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string){
u.password = password
}
错误处理:go语言里使用一个单独的返回值来传递错误信息。go语言能够很新奇的知道哪个函数返回了错误,并且能用简单的if-else来处理错误。在函数中可以在返回值类型里面,加一个error,就代表这个函数可能会返回错误,因此在函数时限的时候,需要同时return两个值,如果出现错误的话就return nil和一个error,如果没有的话,就返回原本的结果和nil。
func findUser(users []user, name string) (v *user , err error){
for _,u := range users{
if u.name == name {
return &u,nil
}
}
return nil, errors.New("not found")
}
字符串格式化:在标准库的FMT里,有很多的字符串格式相关的方法,比如printf这个类似于C语言里的printf函数,不同的是,在go语言里,可以很轻松的用%v来打印任意类型的变量,而不需要区分数字字符串,也可以用%+v打印详细结果,%#v则更详细
type point struct{
x,y int
}
func main(){
fmt.Printf(" p = %v" , p) //p={1,2}
fmt.Printf(" p = %+v" , p) //p={x:1 y:2}
fmt.Printf(" p = %#v" , p) //p=main.point{x:1 , y:2}
}
JSON操作:go语言里的JSON操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写,也就是公开字段。那么这个结构体就能用JSON.marshaler去序列化,变成一个JSON的字符串。序列化之后的字符串也能够用JSON。unmarshaler去反序列化到一个空的变量里面。这样默认序列化出来的字符串的话,它的风格是大写字母开头,而不是下划线。
type userInfo struct {
Name string
Age int 'json:"age"'
Hobby []string
}
func main(){
a := userInfo{Name:"wang", Age: 18, Hobby: []string{"Golang","TypeScript"}}
buf,err := json.Marshal(a)
fmt.Println(string(buf))
}
时间处理:go语言里最常用的就是time.now()来获取当前时间,也可以用time.date去构造一个带时区的时间。有很多方法来获取这个时间点的年月日时分秒,也能用sub去对两个时间进行减法,得到一个时间段
now := time.Now()
t := time.Date(2022,3,27,1,25,36,0,time.UTC)
t2 := time.Date(2022,3,27,2,30,36,0,time.UTC)
fmt.Println(t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute())
diff := t2.Sub(t)
fmt.Println(diff)
数字解析:在go语言中,关于字符串和数字类型之间的转换都在STR conv这个包下。可以用parselnt或者parseFloat来解析一个字符串。也可以用Atio把一个十进制字符串转成数字,还可以用itoA把数字转成字符串,如果输入不合法,那么这些函数都会返回error。
import(
"fmt"
"strconv"
)
func main(){
f,_ := strconv.ParseFloat("1.234",64)
fmt.Println(f)//1.234
n,_ := strconv.ParseInt("111",10,64)
fmt.Println(n)//111
n2,_ := strconv.Atoi("123")
fmt.Println(n2)//123
n2,error := strconv.Atoi("AAA")
fmt.Println(n2,err)//"AAA":invalid syntax
}
进程信息:在go里面,能够用os.argv来得到程序执行的时候的指定的命令行参数。
思考:go语言和其他语言之间的不同有哪些?
在学习了基础语法之后,我去网络上查看了一些go语法的特点,总结如下:
- 并发性与轻量级线程(Goroutines):Go语言的最显著特点之一是其对并发编程的原生支持。Goroutines 是一种轻量级线程,可以高效地运行成百上千个并发任务,而不会造成过多的内存开销。它们通过通道(Channels)进行通信和同步,使并发编程变得更加简洁和安全。
- 垃圾回收(Garbage Collection):Go语言具有自动垃圾回收机制,它能够自动管理内存的分配和释放。这减轻了开发人员的负担,避免了常见的内存泄漏问题。
- 编译型语言:Go是一种编译型语言,代码在编译时被编译成机器码,从而在运行时执行更加高效。这使得Go语言在性能方面表现出色。
- 静态类型:Go是静态类型语言,每个变量在声明时必须指定其类型。这有助于在编译时发现类型相关的错误,并提供更好的代码可读性和安全性。
- 简洁的语法:Go语言的语法非常简洁,减少了不必要的符号和冗余代码。它鼓励使用清晰的命名和结构,以实现可读性更强的代码。
- 内置的工具链:Go语言附带了丰富的工具集,包括格式化工具、测试工具、性能分析工具等。这些工具使得开发、测试和优化代码变得更加方便。
- 依赖管理:Go语言在版本1.11及之后的版本中引入了模块化和依赖管理机制,使项目的依赖关系更加清晰和可管理。
- 跨平台支持:Go语言支持跨平台编译,使得可以在不同的操作系统上编写一次代码,并在不同的平台上进行编译和运行。
- 性能优化:Go语言的设计目标之一是提供高性能的运行时,这使得它在许多场景下比其他语言更具优势,特别是在并发编程方面。
- 网络编程 Go语言内置了对网络编程的支持,提供了标准库中的网络包,使得编写网络应用程序变得更加简单。