走进Go语言基础(1) | 青训营笔记

106 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

内容源于青训营课堂视频以及一些go文档和自己的经验、理解,若有错误欢迎及时指出

1简介

  1. 高性能、高并发
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收
package main

import(
	"net/http"
)

func main(){
	http.Handle("/", httpFileServer(http.Dir(".")))
	http.ListenAndServer(":8080", nil)
}

// 一段可以承载静态文件访问的支持高并发、高性能的服务端代码

2入门-基础语法

  • Hello World

    运行go run example

    编译go build example

2.1变量和常量

  • 变量

    • 强类型语言,每个变量都有自己的变量类型

    • 字符串是内置类型,可直接通过加号拼接,也能直接用等号比较两个字符串(和python一样)

    • 变量声明的两种方式

      • 通过var,一般会自动推导变量类型,也可以显式写出类型

        var a = "Ltd"
        var b, c int = 5, 2  // 可以多个赋值,语法糖
        var d float64  // 可以不赋值,只声明,默认空值
        
      • 通过:=,在函数内部声明并初始化,自动根据值推导类型

        a := "Ltd"
        
    • 特别注意:

      • 函数外的每个语句都必须以关键字开始(const, func, import, type, var)

        image-20230115105826242

      • :=不能在函数外使用,注意同一作用域下同名变量不能重复使用,会报错

      • _常用于占位,表示忽略该返回值

  • 常量

    • 和var声明一样,但常量在定义时必须赋值

      const (
      	i = 1
      	j = 2
      )
      
      // 省略值表示与上面的相同
      const (
          a = 7
          b      // 7
          c      // 7
      )
      
    • go语言中的常量没有确定的类型,会根据使用的上下文来自动确定类型

2.2流程控制

  • if else
    • 注意格式
      • 没有小括号
      • 注意大括号位置,换行
if a == b {
	fmt.Println("=")
}

特殊写法:在if表达式之前再加一个执行语句

// 这里getA()返回一个值赋值给a,再与b比较
if a := getA(); a == b {
	fmt.Println("=")
}

与正常的区别在于这个返回值的作用范围限制在了if-else中

image-20230115134838354

  • 循环

    只有for循环,在经典的C三段式for循环下进行了增强,每一段都可以省略,支持使用breakcontinue跳出和继续循环

    // 经典三段式
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
    
    // 省略初始语句或结束语句,但分号不能省略
    for ; i < 10; i++{
        fmt.Println(i)
    } 
    for i := 0 ;i < 10;{
        i++
        fmt.Println(i)
    }
    
    // 省略初始和结束语句,分号也省略,这里就像while循环了
    for i < 10{
        fmt.Println(i)
    } 
    
    // 全部省略,死循环
    for {
    }
    
  • switch

    • 与C或C++主要区别在于,go语言不需要在case中加break即可跳出,而C或C++会继续往下跑完所有case
    • 可以使用任意的变量类型,甚至可以取代任意的if-else语句,使得代码逻辑更加清晰
    • switch的一个分支可以有多个值,但只有一个default分支
    • fallthrough关键字可以执行满足条件case的下一个case(这个case跳过判断语句,直接执行),且可以叠加
func switch1() {
    a := 1
    switch a {
    case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	default:
		fmt.Println("other")
	}
}

func switch2() {
	s := 1
	switch {
	case s == 1:
		fmt.Println("case1")
		fallthrough
	case s == 2:
		fmt.Println("case2")
	case s == 3:
		fmt.Println("case3")
	default:
		fmt.Println("...")
	}
}

// 此时结果会是:
// case1
// case2

2.3数组

数组是一个具有编号且长度固定的元素序列,但在实际应用场景下更多的是用切片(slice)

  • 数组的长度必须是常量且不变,长度属于数组类型一部分,不同长度的int数组类型不同(不像python有list、set这种)
  • 数组越界是panic
  • 数组是值类型
  • 支持 “==”、“!=” 操作符
var array0 [3]int                 // 1.初始化为int类型的零值
var array1 = [3]int{1, 2, 3}      //   指定初始值
var array2 = [...]int{1, 2, 3, 4} // 2.让编译器根据值的个数自己推断,多维数组只有第一层可以这样用
array3 := [...]int{3: 2, 6: 5}    // 3.通过索引来初始化数组,长度则是最大索引,其余值为对应零值

fmt.Println(array0) //[0 0 0]

if fmt.Sprintf("%T", array1) != fmt.Sprintf("%T", array2) {
    fmt.Println("类型不同")
    fmt.Println("array1:", fmt.Sprintf("%T", array1))
    fmt.Println("array2:", fmt.Sprintf("%T", array2))
}
//类型不同
//array1: [3]int
//array2: [4]int

fmt.Println(array3) // [0,0,0,2,0,0,5]

注意声明的几种方式