走进 Go 语言基础语法

目录

[1. Go 语言的 Hello World](#1. Go 语言的 Hello World)

[2. 变量](#2. 变量)

[3. if else](#3. if else)

[4. 循环](#4. 循环)

[5. switch](#5. switch)

[6. 数组](#6. 数组)

[7. 切片](#7. 切片)

[8. map](#8. map)

[9. range](#9. range)

[10. 函数](#10. 函数)

[11. 指针](#11. 指针)

[12. 结构体](#12. 结构体)

[13. 结构体方法](#13. 结构体方法)

[14. 错误处理](#14. 错误处理)

[15. 字符串操作](#15. 字符串操作)

[16. 字符串格式化](#16. 字符串格式化)

[17. JSON处理](#17. JSON处理)

[18. 时间处理](#18. 时间处理)

[19. 数字解析](#19. 数字解析)

[20. 进程信息](#20. 进程信息)


1. Go 语言的 Hello World

Go 复制代码
package main

import "fmt"

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

代码解读:

  • package main

    这是 Go 语言程序的包声明。每个 Go 程序都必须属于一个包,**main**包是一个特殊的包,它定义了一个可独立执行的程序。

  • import "fmt"

    导入了**fmt** 包,fmt 包提供了格式化输入输出的函数。在这个程序中,我们将使用**fmt.Println**函数来输出内容。

  • func main()

    这是程序的入口点,每个可独立执行的 Go 程序都必须有一个**main** 函数。**main**函数不接受任何参数,也不返回任何值。

  • fmt.Println("hello world")

    调用**fmt** 包中的**Println**函数,该函数用于在控制台打印输出内容,并在输出后自动添加一个换行符。这里输出的内容是 "hello world" 字符串。

2. 变量

变量声明:

在go语言中,最基本的方式是使用**var**关键字:

Go 复制代码
package main

import "fmt"

func main() {
	// Go 语言会对声明的变量进行初始化
	var a int    //整数类型的零值是 0
	var b string //字符串类型的零值是""
	var c bool   //布尔类型的零值是false
	fmt.Println(a, b, c)
}

也可以同时声明多个变量或在声明变量时指定初始值:

Go 复制代码
func main() {
    //同时声明多个变量
	var d, e int
	fmt.Println(d, e) // 0 0

    //在声明变量时指定初始值
    var f int = 10
	fmt.Println(f) //10
}

GO语言中使用var关键字声明变量,支持自动推导变量的类型:[ 重点 ]

Go 复制代码
func main() {
	var a = "字符串"
	var b = 10
	var c = true
	fmt.Println(a, b, c)  //字符串 10 true
}

变量的简短声明:

Go 语言还支持简短声明变量,使用 := 操作符。例如:

Go 复制代码
func main() {
	a := 10
	b, c := true, "字符串"
	fmt.Println(a, b, c) //10 true 字符串
}

a := 10,这种方式声明并初始化了变量a,它会自动根据赋值的类型来推断变量的类型。这里a被推断为整数类型。但是简短声明只能在函数内部使用。

同样可以使用简短声明来同时声明多个变量。

变量的类型转换:

Go 复制代码
func main() {
	a := 10
	b := float64(a) //将a转为浮点数赋给b
	fmt.Println(b)
}

常量的定义:

使用**const**关键字来定义常量:

Go 复制代码
const name = "小林"

func main() {
	fmt.Println(name)
}

如果一组常量的类型相同,还可以省略类型声明。例如:const A, B = 1, 2 Go 语言会自动根据赋值来推断常量的类型,这里**A** 和**B**都被推断为整数类型。

3. if else

go语言中 if 语句后没有括号,若将条件判断语句写在括号中,运行代码时编译器会自动去掉括号:

Go 复制代码
func main() {
	if 8%4 == 0 {
		fmt.Println("8 is divisible by 4")
	}
}

Go语言中 if 后必须跟{ },不能像C/C++ 一样与 if 语句写在同一行。

4. 循环

Go语言中没有while循环和do-while循环,只有for循环一种。且for循环后面跟的条件同样也是不带括号的。

Go 复制代码
func main() {
		//for后没有条件 为死循环
		for {
			fmt.Println("loop")
			break
		}
		//打印0~9 (写法1)
		for j := 0; j < 10; j++ {
			fmt.Println(j)
		}
        //打印0~9 (写法2)
		i := 0
		for i <= 9 {
			fmt.Println(i)
			i = i + 1
		}
}

5. switch

同样,switch后变量名不加括号:

Go 复制代码
func main() {
	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two") //输出two
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}
}

6. 数组

数组的声明:

Go 复制代码
func main() {
	//通过var声明
	var a [5]int
	a[2] = 2
	fmt.Println(a) //[0 0 2 0 0]
	//通过len获取数组长度
	fmt.Println(len(a)) // 5

	//简短声明
	b := [5]int{1, 2, 3}
	fmt.Println(b) //[1 2 3 0 0]
}

在实际开发过程中其实很少使用到数组,因为它的长度是固定的 ,更多时候是使用切片。

7. 切片

切片它是一个可变长度的数组,切片有三个重要的属性:指针长度容量

使用**make**函数创建:

Go 复制代码
func main() {
	//创建一个长度为3的字符切片。
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[2])   // c
	fmt.Println("len:", len(s)) // 3
}

操作方法

追加元素:

使用append 函数可以向切片中追加一个或多个元素,如果切片的容量不足,Go 会自动创建一个新的底层数组,并将原切片的内容复制到新数组中。

Go 复制代码
func main() {
	//创建一个长度为3的字符切片。
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	//追加元素
	s = append(s, "d", "e", "f")
	fmt.Println(s) // [a b c d e f]
}

复制切片:

使用copy 函数可以将一个切片的内容复制到另一个切片中。

Go 复制代码
func main() {

	//创建一个长度为3的字符切片。
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"

	//追加元素
	s = append(s, "d", "e", "f")
	fmt.Println(s) // [a b c d e f]

	//复制切片
	c := make([]string, len(s))
	copy(c, s)  //将s切片的内容复制到c切片中
	fmt.Println(c) // [a b c d e f]
}

切片的切片:

可以从一个切片中再创建一个子切片,子切片与原切片共享底层数组。

Go 复制代码
func main() {
	//创建一个长度为6的字符切片。
	s := make([]string, 6)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	s[3] = "d"
	s[4] = "e"
	s[5] = "f"
	//切片截取操作
	fmt.Println(s[2:5]) // [c d e]
	fmt.Println(s[:5])  // [a b c d e]
	fmt.Println(s[2:])  // [c d e f]
}

8. map

在 Go 语言中,map 是一种无序的键值对集合。它的主要作用是可以根据键(key )快速地查找对应的值(value )。map 的键是唯一的,在同一个**map**中不能有两个相同的键。

创建方式1:通过make函数

Go 复制代码
func main() {

	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m)           // map[one:1 two:2]
	fmt.Println(len(m))      // 2
	fmt.Println(m["one"])    // 1
	fmt.Println(m["unknow"]) // 0
}

创建方式1:使用字面量创建

Go 复制代码
	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3) // map[one:1 two:2] map[one:1 two:2]

删除键值对:

Go 复制代码
func main() {

	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	m["three"] = 3
	fmt.Println(m) // map[one:1 three:3 two:2]

	//删除键值对
	delete(m, "one")
	fmt.Println(m) // map[three:3 two:2]
}

**特别注意:**Go中的map是完全无序的,遍历的时候不会按字母顺序或插入顺序输出

9. range

在 Go 语言中,range 是一个用于遍历各种数据结构(如切片、数组、字符串、映射map)的关键字。它提供了一种简洁的方式来遍历这些数据结构中的元素。

Go 复制代码
func main() {

	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	m["three"] = 3
	
	//使用range遍历map
	for k, v := range m {
		fmt.Println(k, v)
	}
}

10. 函数

Go 复制代码
func add(a int, b int) int {
	return a + b
}
/*
func add(a, b int) int {
	return a + b
}
*/
func main() {
	res := add(1, 2)
	fmt.Println(res) // 3
}

实际的业务逻辑代码中,几乎所有的函数都返回多个值,第一个值是真正的返回结果,第二个值是错误信息。例如:

Go 复制代码
func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

该函数返回第一个值v,也就是真正的value,返回第二个值ok,也就是判断是否存在

11. 指针

相比于C/C++里的指针,golang中支持的操作非常有限,这里的主要用途就是对传入的参数进行修改,

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
}
  • add2 函数中,接受了一个整数参数 n ,并将其值加 2。但是由于 Go 语言是值传递,n += 2 只是修改了函数内部局部变量 n 的值,不会影响到函数外部调用处的变量。所以这个函数对传入的参数的修改不会影响到原始变量。所以在**main** 函数中调用**add2(n)** 后,**n**的值仍然是 5 。
  • add2ptr 函数中,接受一个整数指针参数 n ,通过解引用指针修改其所指向的值,将其值加 2。在**main** 函数中调用 add2ptr(&n) 后,由于是通过指针修改了原始变量的值,所以 n 的值变为 7。

在 Go 语言中,函数参数可以是值传递或指针传递。值传递会创建参数的副本,对副本的修改不会影响到原始值;而指针传递允许函数直接修改调用者的变量,因为它们共享相同的内存地址。

12. 结构体

在 Go 语言中,结构体 **(struct)**是一种用户自定义的数据类型,用于将多个不同类型的字段组合在一起,以表示一个复杂的数据结构。

Go 复制代码
	type student struct {
		name string
		age int
		gender string
	}

结构体实例化

  1. 基本实例化
Go 复制代码
func main() {
	type student struct {
		name   string
		age    int
		gender string
	}

	s := student{"小谷", 20, "男"} // {小谷 20 男}
	fmt.Println(s)
}
  1. 指定字段初始化
Go 复制代码
func main() {
	type student struct {
		name   string
		age    int
		gender string
	}
	
	s := student{name: "小谷", gender: "男"}
	fmt.Println(s) // {小谷 0 男}
}

没有被指定初始化的字段,会被自动初始化为其类型的零值。

  1. 使用new函数实例化 (有点Java的味道了)
Go 复制代码
package main

import "fmt"

func main() {
	type student struct {
		name   string
		age    int
		gender string
	}
	//使用new函数实例化
	s := new(student)
	s.name = "小谷"
	s.age = 20
	s.gender = "男"
	fmt.Println(s.name, s.age, s.gender) // 小谷 20 男
}
  • **new**函数用于创建一个指定类型的指针。对于结构体,它会返回一个指向结构体的指针。
  • 这里**new(student)** 创建了一个**student**结构体的指针,然后通过指针访问其字段并赋值。

结构体的比较

如果结构体的所有字段都是可比较的,那么这个结构体也是可比较的:

Go 复制代码
type Point struct {
	X int
	Y int
}

func main() {
	p1 := Point{1, 2}
	p2 := Point{1, 2}
	if p1 == p2 {
		fmt.Println("p1 and p2 are equal") // p1 and p2 are equal
	} else {
		fmt.Println("p1 and p2 are not equal")
	}
}

如果结构体包含不可比较的字段(如map、slice 等引用类型),那么这个结构体是不可比较的。如果尝试比较这样的结构体,会导致编译错误。

13. 结构体方法

Go 复制代码
package main

import "fmt"

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
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.resetPassword("2048")
	fmt.Println(a.checkPassword("2048")) // true
}

14. 错误处理

基础概念:

在 Go 语言中,错误处理是构建健壮程序的重要部分。Go 语言没有像其他一些语言那样的异常机制,而是使用返回值来表示错误。大部分可能出现错误的函数都会返回一个error 类型的值,这个**error**类型是一个接口,定义如下:

Go 复制代码
type error interface {
    Error() string
}

这意味着任何实现了**Error()** 方法并返回一个字符串的类型都满足**error** 接口**。当函数执行出现问题时,会返回一个非nilerror值来告知调用者错误信息。**

简单的错误处理示例:

Go 复制代码
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("nonexistent_file.txt")
    if err!= nil {
        fmt.Println("Error opening file:", err)
        return
    }
    // 正常处理文件
    defer file.Close()
}

这里尝试打开一个不存在的文件,os.Open 函数会返回一个非**nil** 的**err** 值(在文件操作中,os.Open 函数用于打开一个文件,它返回一个文件指针和一个**error值),通过检查err!= nil** 来判断是否出现错误。如果有错误,就打印错误信息并终止程序的执行;如果没有错误,就可以正常处理文件,并且使用**defer**关键字确保文件最终会被关闭。

案例二:

Go 复制代码
package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

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")
}

func main() {
	u, err := findUser([]user{{"wang", "2279"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) // wang

	if u, err := findUser([]user{{"wang", "2279"}}, "zhang"); err != nil {
		fmt.Println(err) // not found
		return
	} else {
		fmt.Println(u.name)
	}
}

15. 字符串操作

在标准库strings包中有许多字符串工具函数:

Go 复制代码
package main

import (
	"fmt"
	"strings"
)

func main() {
	a := "hello"
	//判断字符串中是否包含另一个字符串
	fmt.Println(strings.Contains(a, "ll")) // true
	//字符计数
	fmt.Println(strings.Count(a, "l")) // 2
	//判断开头
	fmt.Println(strings.HasPrefix(a, "he")) // true
	//判断结尾
	fmt.Println(strings.HasSuffix(a, "llo")) // true
	//查找某个字符串的位置
	fmt.Println(strings.Index(a, "ll")) // 2
	//连接多个字符串
	fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
	//重复多个字符串
	fmt.Println(strings.Repeat(a, 2)) // hellohello
	//字符替换 ,-1表示进行无限制的替换 即替换所有匹配的字符
	fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
	//分割
	fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
	//转小写
	fmt.Println(strings.ToLower(a)) // hello
	//转大写
	fmt.Println(strings.ToUpper(a)) // HELLO
	//获取字符串长度
	fmt.Println(len(a)) // 5
	//对于中文,一个中文会对应多个字符
	b := "你好"
	fmt.Println(len(b)) // 6
}

16. 字符串格式化

在go语言的 fmt 包中有许多字符串格式化的方法,常用的有:

fmt.Sprintf函数

在 Go 语言中,**fmt.Sprintf**是最常用的字符串格式化函数之一。例如,将一个整数和一个字符串格式化为一个新的字符串:

Go 复制代码
package main

import (
	"fmt"
)

func main() {
	age := 20
	name := "WenHui"
	result := fmt.Sprintf("My name is %s and I am %d years old.", name, age)
	fmt.Println(result) // My name is WenHui and I am 20 years old.
}

这里**%s** 是用于格式化字符串的占位符,它会被**name** 变量的值替换;%d 是用于格式化整数的占位符,会被**age** 变量的值替换。 (和C语言一样)

格式化占位符的类型:

  • 整数类型
    • %d:用于格式化十进制整数。例如,fmt.Sprintf("%d", 10)会返回"10"
    • %b:用于格式化二进制整数。如fmt.Sprintf("%b", 10)会返回"1010"
    • %o:用于格式化八进制整数,fmt.Sprintf("%o", 10)返回"12"
    • %x%X:用于格式化十六进制整数,%x返回小写字母表示的十六进制数,%X返回大写字母表示的十六进制数。例如,fmt.Sprintf("%x", 10)返回"a"fmt.Sprintf("%X", 10)返回"A"
  • 浮点数类型
    • %f:用于格式化十进制浮点数。例如,fmt.Sprintf("%f", 3.14)会返回"3.140000"。可以通过指定精度来控制小数点后的位数,如fmt.Sprintf("%.2f", 3.14)会返回"3.14"
  • 字符串类型
    • %s:用于格式化字符串,如前面例子所示。
    • %q:用于将字符串用双引号引起来并进行转义。例如,fmt.Sprintf("%q", "hello")返回""hello"",如果字符串中有特殊字符,会进行相应的转义。
  • 布尔类型
    • %t:用于格式化布尔值。例如,fmt.Sprintf("%t", true)返回"true"
  • 指针类型
    • %p:用于格式化指针的值,以十六进制形式输出。例如,对于一个指针变量pfmt.Sprintf("%p", p)会返回指针的十六进制表示。

在golang中还可以用%v来打印任意类型的变量

Go 复制代码
type point struct {
	x, y int
}

func main() {
	s := "hello"
	n := 123
	p := point{1, 2}
	fmt.Println(s, n) // hello 123
	fmt.Println(p)    // {1 2}

	fmt.Printf("s=%v\n", s)  // s=hello
	fmt.Printf("n=%v\n", n)  // n=123
	fmt.Printf("p=%v\n", p)  // p={1 2}
	fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
}

%v 只打印值

%+v 打印字段的名字和值 (对于结构体类型)

%#v 它比%+v更加详细,包含了结构体的包名(main)、结构体类型名(Point)以及字段名称和值

17. JSON处理

对于已有的结构体,保证其每个字段首字母大写,那么这个结构体就能用 json.Marshal 序列化,序列化后生成一个 buf数组(可以理解为一个字符串)

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

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)
	if err != nil {
		panic(err)
	}
	fmt.Println(buf)         // [123 34 78 97...]
	fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

	buf, err = json.MarshalIndent(a, "", "\t")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf))

	var b userInfo
	err = json.Unmarshal(buf, &b)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}

18. 时间处理

在 Go 语言中,可以使用**time.Now()** 函数来获取当前的时间。这个函数返回一个**time.Time**类型的值,它包含了年、月、日、时、分、秒、纳秒以及时区等信息。

时间戳的概念:时间戳是从 1970 年 1 月 1 日 UTC 到指定时间所经过的秒数。在 Go 语言中,可以使用**Unix()**方法来获取时间戳。

Go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	//获取当前时间
	now := time.Now()
	fmt.Println("当前时间:", now) // 2024-11-04 13:35:48.1742921 +0800 CST m=+0.000000001
	//时间格式化
	formattedTime := now.Format("2006-01-02 15:04:05")
	fmt.Println("格式化后的时间:", formattedTime) // 2024-11-04 13:35:48
	//获取时间戳
	timestamp := now.Unix() 
	fmt.Println("当前时间戳:", timestamp) // 1730698548
	//从时间戳转换为时间
	timeFromTimestamp := time.Unix(timestamp, 0)
	fmt.Println("从时间戳转换后的时间:", timeFromTimestamp) //2024-11-04 13:35:48 +0800 CST
	//时间计算
	newTime := now.Add(24 * time.Hour)
	fmt.Println("新的时间:",newTime) //2024-11-05 13:35:48.1742921 +0800 CST m=+86400.000000001
}

19. 数字解析

Go 复制代码
package main

import (
	"fmt"
	"strconv"
)

func main() {
	//将字符串转为整数
	n, _ := strconv.Atoi("521")
	fmt.Println(n) // 521

	//将字符串解析为浮点数(float64)
	f, _ := strconv.ParseFloat("3.14", 64)
	fmt.Println(f) // 3.14

	//将字符串 "1314" 按照十进制(基数为 10)解析为 int64 类型的整数
	n1, _ := strconv.ParseInt("1314", 10, 64)
	fmt.Println(n1) // 1314

	//整数转字符串
	str := strconv.Itoa(123)
	fmt.Println(str) //123
}

20. 进程信息

Go 复制代码
package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	// go run example/20-env/main.go a b c d
	fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
	fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
	fmt.Println(os.Setenv("AA", "BB"))

	buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf)) // 127.0.0.1       localhost
}


🌸🌸🌸 完结撒花🌸🌸🌸

博主WX:++g2279605572++ 欢迎大家与我交流!

相关推荐
莫名其妙小饼干2 分钟前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
四口鲸鱼爱吃盐4 分钟前
CVPR2024 | 通过集成渐近正态分布学习实现强可迁移对抗攻击
学习
十年一梦实验室11 分钟前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
isolusion14 分钟前
Springboot的创建方式
java·spring boot·后端
最爱番茄味20 分钟前
Python实例之函数基础打卡篇
开发语言·python
zjw_rp42 分钟前
Spring-AOP
java·后端·spring·spring-aop
Oneforlove_twoforjob1 小时前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
TodoCoder1 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
engchina1 小时前
如何在 Python 中忽略烦人的警告?
开发语言·人工智能·python
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎