Golang基础概述

首先

此文章是基于在以下Udemy课程中学到的内容所创建的。
https://www.udemy.com/course/golang-webgosql/
如果您想要更详细地学习,建议您亲自参加该课程。
如果您已经参加过该课程,可以将其用于复习等用途。

将数据显示到控制台

使用标准包中的fmt。
Println函数在最后会换行,类似的函数还有Print和Printf。

package main

import "fmt"

func main() {
	fmt.Println("Hello World") // コンソールにHello Worldと表示される
}

Go的运行方式

去跑步

只需指定文件即可执行程序的简单方法。

go run ファイル名
go run main.go // 例

去构建

编译文件并创建它。然后可以执行所创建的文件。

go build -o コンパイル後のファイル名 goファイル名
go build -o main main.go // 例

变量

明确的定义

// 形式
var 変数名  = 初期値
// 例
var price int = 1000
var name string = "Golang”

可以省略初始值,这种情况下会使用类型的默认初始值。

var price int // int型は0
var name string // string型は空白

暗默的定義

由于可以从初始值进行类型推断,因此不需要进行以下类型指定。

// 形式
変数名 := 初期値
// 例
price := 1000
name := "Golang"

注意点是暗默定义只能在函数内部使用,在函数外部使用将会报错。

:price = 1000 // エラー

func main() {
 :name = "Golang" // OK
}

当需要同时定义多个变量时

var (
    price int = 1000
    name string = "Golang"
)
// or 
var price, name = 1000, "Golang"
// or
price, name := 1000, "Golang"

给变量赋值

var price int = 1000
price = 2000 // 代入
fmt.Println(price) // 2000

固定数量

与变量不同,常量无法重新赋值。

const pageCount = 3
const fileName = "filename" 

pageCount = 10 // エラー

常量和变量可以一起定义。

const (
    pageCount = 3
    fileName = "filename" 
)

不同类型的样式

整数类型、浮点数类型、逻辑值类型、字符串类型

// 整数型(int)
var i int = 1000

// 浮動小数点型(float)
// 64と32の違いは格納できる値の大きさ
var fl64 float64 = 3.14
var fl32 float32 = 3.14

// 論理値型(bool)
var t bool = true
var f bool = false

// 文字列型(string)
var s string = "Golang"

字节型

// byte型(uint8型)
var b = []byte{97, 98, 99}
fmt.Println(string(b)) // abc

以下链接中的文章对于byte类型很容易理解。
https://qiita.com/s_zaq/items/a6d1df0d1bf79f5bcc2a

数组型

能够存储多个值的数据类型

// 配列型の形式
[要素数]{, , , ...}

var arr1 = [3]int{1, 2, 3}
fmt.Println(arr1) // [1, 2, 3]
// 要素数を...と記載すると初期値の個数になる
var arr2 = [...]int{1, 2, 3} // arr2の型は[3]int

特征是不可变的元素数量,而不同的元素数量被视为不同的类型。

var arr1 = [3]int
var arr2 = [4]int
arri = arr2 // 要素数が異なり型が違うためエラー

切片类型

简而言之,这是一个可以改变数组元素数量的版本,类似于其他编程语言(如js和php)的数组类型。

// スライスの形式
[]{, , , ...} // 配列と違い要素数を指定しない

var s1 = []int{1, 2, 3}
s1 = append(s1, 4) // appendは要素を追加するメソッド
fmt.Println(s1) // [1, 2, 3, 4]

地图类型

// 形式
map[キーの型]値の型{"キー名": , ...}

var m = map[string]int{"apple": 100, "banana": 200}

// キーを指定して値を取得
m["apple"] // 100

界面类型

对于每一个值都有相应类型的类型

var x interface{} // {}までが型名
// どの型の値でも代入できる
x = 1
x = 3.14
x = "string"

需要注意的是无法进行计算等操作。

var x interface{} = 2
sum := x + 2 // エラー

类型转换

// float → int
var fl64 float64 = 3.14
int(fl64)

// int → float
var i int = 1
float64(i)

// string → int
var s string = "100"
i, _ = strconv.Atoi(s) // iの型はint

// int → string
var i int = 200
s := strconv.Itoa(i2) // sの型はstring

// string → byte[]
b :=[]byte("abc")
fmt.Println(b) // [97, 98, 99]

// byte[] → string
s := string([]byte{97, 98, 99})
fmt.Println(s) // "abc"

函数 shù)

// 形式
func 関数名(引数 , ...) 返り値の型 {
    // 処理
}

// 例
func Add(x int, y int) int {
	return x + y
}

func main() {
	i := Add(1, 2)
	fmt.Println(i) // 3
}

匿名函数

简单地说,我们可以在函数内部编写代码。
我们可以在函数参数中传递函数。

add := func (x int, y int) int {
    return x + y
}
add(1, 2) // 3

最后,您可以在括号内直接添加并执行未命名的函数。

func (x int, y int) int {
    return x + y
}(1, 2) // 3

for循环

用于执行重复处理的工具

// 形式
for 初期化; 条件; 繰り返し式 {
    // 処理
}
// 例
for i := 0; i < 10; i++ {
    fmt.Print(i) // 0123456789
}

在获取索引和值、键和值的方法中,可以使用range。

nums := []int{1, 2, 3}
for index, value := range nums {
    fmt.Printf("index: %d, value: %d\n", index, value)
    // 出力結果
    // index: 0, value: 1
    // index: 1, value: 2
    // index: 2, value: 3
}

// map
var m = map[string]int{"apple": 100, "banana": 200}
for key, value := range m {
    fmt.Printf("key: %s, value: %d\n", key, value)
    // 出力結果
    // key: apple, value: 100
    // key: banana, value: 200
}

延期

通过在代码前面加上defer关键字,可以注册函数在结束时执行的操作。在下面的代码中,可以看到defer语句被首先编写,但实际上是在最后执行的。

func main() {
	defer fmt.Println("end")
	fmt.Println("start")
    
    // 出力結果
    // start
    // end
}

有一个使用案例是为了防止打开文件后忘记关闭。如果没有关闭的处理,会导致内存泄漏。

file, _ := os.Create("./test.txt")
// ファイルを開いあとに、defer文を使って閉じる処理をすぐに記述することで、閉じ忘れを防ぐ
defer file.Close() 

file.Write([]byte("Test\n"))

并行处理

使用Go语言中的go协程可以轻松编写异步处理。

由于以下情况下的sub方法内有无限循环,导致在调用sub方法后,Main循环无法执行。

func sub() {
	for {
		fmt.Println("Sub loop")
		time.Sleep(100 * time.Millisecond)
	}
}

func main() {
	sub()

	for {
		fmt.Println("Main loop")
		time.Sleep(200 * time.Millisecond)
	}
}

// 出力結果
// Sub loop
// Sub loop
// Sub loop
// ...

通过给`sub`方法的调用添加`go`,将`sub`循环转换为异步处理,可以同时运行两个循环。

func sub() {
	for {
		fmt.Println("Sub loop")
		time.Sleep(100 * time.Millisecond)
	}
}

func main() {
	go sub() <- ここにgoを追加するだけ

	for {
		fmt.Println("Main loop")
		time.Sleep(200 * time.Millisecond)
	}
}

// 出力結果
// Main loop
// Sub loop
// Sub loop
// Main loop
// ...

指针

在内存中指示变量的地址信息
通过这个可以将变量的值共享给其他变量
也就是所谓的传递引用状态。

i := 100
fmt.Println(i) // 100
// 値型のアドレス(&をつける)
fmt.Println(&i) // 0x140000a4008

// ポインタ型(型に*をつけて型指定する)
var p *int = &i // iのアドレスを代入
// ポインタ型のアドレス
fmt.Println(p) // 0x140000a4008
// ポインタ型の実体(*をつける)
fmt.Println(*p) // 100

// iのアドレスをpのポインタ型に代入したため、iとpは同じアドレスを参照している
// このため、iの値を変更するとpの値も変更される。逆も同様
i = 200
fmt.Println(*p) // pも200になる
*p = 300
fmt.Println(i) // iも300になる

通常情况下,函数的参数使用指针类型。
原因是为了可以在函数内部修改通过参数传递的值。

func Double(x int) {
	x = x * 2
}

func DoublePoint(x *int) {
	*x = *x * 2
}

func main() {
    var i int = 100
    // iと引数として渡されたi(関数内ではx)は別物のとして扱われる
    // このためiの値は変更されない
    Double(i)
    fmt.Println(i) // 100
    
    // ポインタ型を引数に渡すことで、値を共有するためiの値を変更することができる
    DoublePoint(&i)
    fmt.Println(i) // 200
}

结构体

在他的言语中,类似于“class”的概念。

// 構造体
type User struct {
	Name string
	Age	int
}

func main() {
    // 構造体の定義
	var user User = User{Name: "Taro", Age: 20}
	fmt.Println(user) // {Taro 20}
    
    // 書き換え
    user.Name = "Jiro"
	fmt.Println(user) // {Jiro 20}
}

struct 方法

他的言辭中類似於class方法的東西

type User struct {
	Name string
	Age	int
}

// 定義はfuncの後に以下のように(変数名 ストラクト名)を記載すること
func (u *User) UpdateName(name string) {
	u.Name = name
}

func main() {
	var user User = User{Name: "Taro", Age: 20}
	fmt.Println(user) // {Taro 20}
    
    // userから繋げて関数を実行できる
    // UpdateName関数内のuはこのuser変数の値になっている
	user.UpdateName("Saburo")
	fmt.Println(user) // {Saburo 20}
}

使用界面(interface)来通用化(different types)

接口的一般用法

由于下述的User和Admin具有不同的类型,通常情况下无法将它们合并为一起执行相同的操作。
然而,通过使用接口,可以实现以下的功能。

// interfaceの定義
// getName() stringを持つ型はHasName型として扱うことができる
type HasName interface {
	getName() string
}

type User struct {
	Name string
	Age  int
}

func (u *User) getName() string {
	return u.Name
}

type Admin struct {
	Name string
	Role int
}

func (u *Admin) getName() string {
	return u.Name
}

func main() {
    // UserとAdminは本来異なる型だが、両方ともgetName() stringメソッドを持っている
    // このためHasNameインターフェイスを用いることで同じ型として扱うことができる
    vs := []HasName{
        &User{Name: "Taro", Age: 20},
        &Admin{Name: "Hanako", Role: 1},
    }
    
    for _, v := range vs {
        fmt.Println(v.getName())
	}
}

从其他文件中使用变量、常量和方法。

通过将名称的首字母改为大写,可以从其他文件中使用。
反之,将名称的首字母改为小写,可以将其设为私有。

var PublicValue int // 他のファイルから使える
var privateValue int

func PublicMethod() { // 他のファイルから使える
}

func privateMethod() {
}

参考文献

 

广告
将在 10 秒后关闭
bannerAds