GO?还在GO!(GO语言入门) | 豆包MarsCode AI 刷题

64 阅读10分钟

GO?还在GO!

做为一个双非大三JAVA方向的学生,很荣幸能够参与豆包的后端青训营,那接下来让我们学习GO语言吧,let's go!!!!!(内容只涉及我主观觉得的重要语法)

一.常量与变量

go语言中声明变量有两种方式

通过var来声明变量

var 变量名 变量类型var 变量名

前者是自定变量类型,而后者则是自动推导变量的类型

通过:=来声明变量

变量名 := 表达式

通过短变量声明,快速声明变量,此方法也是自动推导变量的类型

深入浅出理解两种方式的异同

相同点

  • 都是声明变量的手段:二者的主要目的都是在 Go 程序中创建变量,以便存储和操作数据。

不同点

  • 语法形式
    • var:语法格式较为多样。可以只声明变量不初始化,如var num int;也可以在声明时初始化,如var str string = "hello",还可以使用类型推断进行初始化,如var anotherStr = "world"
    • :=:语法格式为变量名 := 表达式,它将变量声明和初始化合并为一步,并且根据表达式的值推断变量类型,例如num := 10
  • 使用范围
    • var:可以用于全局变量声明和函数内局部变量声明。例如:
var globalVar int // 全局变量声明
func main() {
    var localVar int // 局部变量声明
}
  • :=:只能用于函数内部声明局部变量,不能用于声明全局变量。例如:

  • 变量声明的灵活性和限制

    • var:当声明多个变量时,可以为每个变量指定不同的类型(如果需要)。例如:var (a int; b string)。在声明变量时,如果没有初始化,变量会被赋予零值(如int类型为0string类型为""等)。
    • :=:使用:=声明变量时,至少有一个变量必须是新声明的。例如,x := 10; y := 20是合法的,但x := 10; x := 20是不合法的,因为第二次x不是新声明的变量。而且它主要用于简洁地初始化新变量,通常在变量的初始值已知且不需要显示指定类型的场景下使用。

二. 循环与判断

循环

  1. for 循环

    • 在 Go 语言中,for循环是最常用的循环结构,它有多种形式。

    • 基础形式for 初始语句; 条件表达式; 后置语句 { 循环体 }。例如:

for i := 0; i < 10; i++ {
    println(i)
}

这里初始语句i := 0初始化了循环变量i,条件表达式i < 10决定了循环是否继续执行,后置语句i++在每次循环结束后执行。这种形式的for循环与 C、Java 等语言中的for循环类似,但要注意for循环中的三个部分都可以省略。

省略初始语句和后置语句:当省略初始语句和后置语句时,for循环类似于while循环。例如:

i := 0
for i < 10 {
    println(i)
    i++
}

判断

  1. if 语句

if语句用于条件判断。基本语法为if 条件表达式 { 执行语句 }。例如:

num := 10
if num > 5 {
    println("num 大于 5")
}

if语句还可以和elseelse if配合使用,实现多分支判断。例如:

num := 7
if num > 10 {
    println("num 大于 10")
} else if num > 5 {
    println("num 大于 5 且小于等于 10")
} else {
    println("num 小于等于 5")
}
  1. switch 语句

switch语句用于多分支选择。基本形式如下:

switch 表达式 {
case1:
    执行语句 1
case2:
    执行语句 2
...
default:
    执行默认语句
}

Go 语言的switch语句还有一种特殊形式,当省略switch后面的表达式时,switch语句会将true作为表达式,每个case语句都相当于一个if条件判断。例如:

num := 7
switch {
case num > 10:
    println("num 大于 10")
case num > 5:
    println("num 大于 5 且小于等于 10")
default:
    println("num 小于等于 5")
}

这种形式在多个条件判断且逻辑较为复杂时非常有用,可以提高代码的可读性。同时,switch语句中的case语句默认情况下是自动break的,即执行完一个case分支后就会跳出switch,如果需要继续执行下一个case分支,可以使用fallthrough关键字。例如:

num := 2
switch num {
case 1:
    println("num 是 1")
case 2:
    println("num 是 2")
    fallthrough
case 3:
    println("num 是 3 或者 2(因为 fallthrough)")
default:
    println("num 不是 1、2、3")
}

深入浅出理解判断与循环

1.循环中的标签与控制流

在 Go 语言的循环中,可以使用标签来控制多层嵌套循环的跳转。标签是一个紧跟着冒号的标识符,放在循环语句的前面。例如:

outer:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if i == 1 && j == 1 {
            continue outer
        }
        println(i, j)
    }
}

在这个例子中,当i == 1j == 1时,continue语句会跳过外层循环的本次迭代,直接进入下一次外层循环。

2. 判断语句中的类型断言与类型转换

  • if语句和switch语句中,可以结合类型断言和类型转换来处理接口类型的值。例如,假设有一个接口类型的值,可能是int或者string,可以在if语句中使用类型断言来判断并处理:
var value interface{} = 10
if v, ok := value.(int); ok {
    println("值是整数:", v)
} else if s, ok := value.(string); ok {
    println("值是字符串:", s)
}

这里value.(int)是类型断言表达式,如果value的值是int类型,那么v就会被赋值为value的值,oktrue。这种方式可以安全地处理接口类型可能存储的不同类型的值。同时,在switch语句中也可以使用类似的方式进行类型判断:

var value interface{} = "hello"
switch v := value.(type) {
case int:
    println("是整数:", v)
case string:
    println("是字符串:", v)
default:
    println("其他类型")
}

这种switch语句的类型断言形式可以更清晰地处理接口类型的多种可能类型,提高代码的可读性和可维护性。

4. 判断语句中的短路求值
if语句的条件表达式中,Go 语言采用短路求值的方式。例如,对于&&(逻辑与)和||(逻辑或)操作符,当计算a && b时,如果afalse,则不会再计算b,因为无论b的值是什么,整个表达式都为false;同理,当计算a || b时,如果atrue,则不会再计算b。这种短路求值特性可以在编写条件表达式时巧妙利用,例如:

func DoSomething() bool {
    println("执行了 DoSomething")
    return true
}
num := 5
if num > 10 && DoSomething() {
    // 这里 DoSomething 不会被执行,因为 num > 10 为 false
}
if num < 10 || DoSomething() {
    // 这里 DoSomething 会被执行,因为 num < 10 为 true
}

理解短路求值可以帮助我们编写更高效、更简洁的条件判断逻辑,避免不必要的函数调用或复杂计算。

三. 结构体

1.结构体的定义
结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。在Go语言中,使用typestruct关键字来定义结构体。例如:

type Person struct {
    Name string
    Age  int
}

这里定义了一个名为Person的结构体,它包含了Name(字符串类型)和Age(整数类型)两个字段。

2.结构体的实例化

方法一:按字段顺序初始化
可以使用以下方式实例化结构体:

p1 := Person{"Alice", 25}

这里按照结构体中字段定义的顺序为NameAge赋值。
方法二:指定字段初始化
也可以指定字段来初始化结构体:

p2 := Person{Age: 30, Name: "Bob"}

这种方式在初始化结构体时更加清晰,尤其是当结构体有很多字段或者某些字段有默认值时。

3.结构体的嵌套
结构体可以嵌套其他结构体,实现更复杂的数据结构。例如:

type Address struct {
    City    string
    Country string
}

type Employee struct {
    Person
    Address
    Salary float64
}

这里Employee结构体嵌套了Person结构体和Address结构体,以及一个Salary字段。可以这样实例化和访问:

e := Employee{
    Person: Person{
        Name: "Charlie",
        Age:  35,
    },
    Address: Address{
        City:    "New York",
        Country: "USA",
    },
    Salary: 5000.0,
}
println(e.Name)        // 访问嵌套的Person结构体中的Name字段
println(e.Address.City) // 访问嵌套的Address结构体中的City字段

4.结构体方法
可以为结构体定义方法,方法类似于函数,但有一个接收者,接收者表示这个方法是属于哪个结构体类型的。例如:

func (p Person) Greet() {
    println("Hello, my name is", p.Name)
}

这里Greet方法属于Person结构体,可以这样调用:

p := Person{"David", 28}
p.Greet()

如果方法需要修改结构体的值,可以使用指针接收。例如:

func (p *Person) SetAge(newAge int) {
    p.Age = newAge
}

深入浅出理解结构体与面向对象编程概念及异同

在传统的面向对象编程(Object - Oriented Programming,OOP)中,有几个核心概念:封装继承多态

相同点

数据和行为的组合:结构体可以将相关的数据(字段)和操作这些数据的方法组合在一起,这与面向对象编程中类的概念相似。它们都提供了一种将数据和逻辑封装在一个单元中的方式,使得代码更加模块化和易于理解。

代码复用:通过结构体嵌套或者传统面向对象语言中的继承,都可以实现代码的复用。在Go语言中,结构体嵌套允许在新的结构体中包含已有结构体的字段和方法,减少了重复代码的编写。

抽象层次:结构体和面向对象编程都有助于提高代码的抽象层次。可以将复杂的系统分解为多个结构体或类,每个结构体或类负责处理特定的功能,隐藏了内部实现细节,使代码的整体结构更加清晰。

不同点

继承方式:传统面向对象语言通常有明确的类继承语法,例如Object extentds Yumazi 。而Go语言通过结构体嵌套来模拟继承,这种方式相对更加灵活,但也与传统继承有一些区别。例如,在Go语言中没有像其他语言那样的方法重写(override)的语法,需要通过接口等其他机制来实现类似的功能。

类型系统:Go语言是一种静态类型语言,结构体类型在编译时就确定了。而一些动态类型的面向对象语言(如Python)在运行时对象的类型可以更加灵活地改变。此外,Go语言的接口实现是隐式的,只要结构体实现了接口定义的所有方法,就被认为实现了该接口,而不需要像某些语言那样显式地声明实现关系。

多态实现机制:虽然Go语言可以通过接口实现多态,但与传统面向对象语言中的多态实现方式有所不同。在Go语言中,接口类型的变量可以存储实现了该接口的任何结构体类型的值,调用接口方法时根据实际存储的值来确定具体的实现。而在一些传统面向对象语言中,多态可能更多地依赖于类继承和虚函数表等机制。

访问控制:Go语言通过大小写来控制结构体字段和方法的访问权限(大写开头表示在包外可访问,小写开头表示仅在包内可访问),这种访问控制方式相对简单直接。而其他面向对象语言可能有更复杂的访问修饰符(如publicprivateprotected等)来控制类成员的访问。