go基本语法

 


一、main函数与package声明

  1、main 函数概览

         

   main函数要点:无参数、无返回值;main 方法必须要在 main 包里面;`go run main.go` 就可以执行;如果文件不叫 `main.go`,则需要`go build` 之后再 `go run`。

  2、package 声明

    语法形式:package xxxx,package使用字母和下划线的组合,package名称可以和文件夹不同,但是同一个文件夹下的声明必须一致。

    引入包语法形式:import [alias] xxx,如果一个包引入了但是没有使用,会报错,如果需要匿名引入:前面多一个下划线。如下图所示,如果不使用fmt包,但是引入就会报错,如果是使用下划线匿名引用,则不会报错。这里可能会有疑问,不用为什么要引用,这是因为有的时候需要使用包的init方法,因此需要匿名引用。

                

二、string 和 基础类型

  1、String

    String 声明:双引号引起来,则内部双引号需要使用\转义,` 引号引起来,则内部`需要\转义

    String 长度:string 的长度很特殊:用 len(str)获取的是字节长度:和编码无关;字符数量:和编码有关,用编码库来计算。       

func main()  {
    println("hello world")
    println(len("你好")) // 输出6
    println(utf8.RuneCountInString("你好")) // 输出2
}

 

     String 包:string 的拼接直接使用 + 号就可以。注意的是,某些语言支持 string 和别的类型拼接,但是golang 不可以

     String的工具包是strings, 主要方法(你所需要的全部都可以找到):查找和替换、大小写转换、子字符串相关、相等 

  2、rune 类型

    rune,直观理解,就是字符,rune 不是 byte,其本质是 int32,一个 rune 四个字节。

    rune 在很多语言里面是没有的,与之对应的是,golang 没有 char 类型。rune 不是数字,也不是 char,也不是 byte,实际中不太常用

  3、bool, int, uint, float 家族

    bool: true, false

    int8, int16, int32, int64, int

    uint8, uint16, uint32, uint64, uint

    float32, float64

  4、byte 类型

    byte,字节,本质是 uint8,对应的操作包在 bytes 上

  总结:

    golang 的数字类型明确标注了长度、有无符号,golang 不会帮你做类型转换,类型不同无法通过编译。也因此,string 只能和string 拼接,golang 有一个很特殊的 rune 类型,接近一般语言的 char 或者 character 的概念,非面试情况下,可以理解为 “rune = 字符”。

    string 遇事不决找 strings 包。

三、变量声明 & 方法声明与调用

  1、变量声明

    var 声明变量:

      语法:var name type = value

      局部变量、包变量、块声明、驼峰命名、首字符是否大写控制了访问性,如下面的例子,如果是大写,在其他包也能访问,小写的话,不能在其他包访问,其子包也不能访问;在定义变量时,可以写变量类型,也可以不定义,golang 支持类型推断,但是如果其推断的和我们想定义的不一致,就必须明确,例如实例中的变量c

复制代码
func main()  {
    println("hello world")
    println(len("你好")) // 输出6
    println(utf8.RuneCountInString("你好")) // 输出2

    var a int = 16
    println(a)

    var b = 16
    println(b)

    var c uint = 16
    println(c)
}

var Global = "全局变量"

var local = "包变量"

var (
    First string = "abc"
    second int32 = 16
)
复制代码

     := 声明变量:

      只能用于局部变量,即方法内部。golang 使用类型推断来推断类型。数字会被理解为 int 或者 float64。(所以要其它类型的数字,就得用 var 来声明)     

    d := 17
    println(d)

 

     变量声明易错点:变量声明了没有使用、类型不匹配、同作用域下,变量只能声明一次,特别注意的是变量声明了没有使用的情况,也会编译不通过

     const 声明常量:首字符是否大写控制了访问性、驼峰命名、支持类型推断

const internal = "包内可访问"
const External = "包外可访问"

 

  2、方法声明

    方法声明包含四个部分:关键字 func、方法名字(首字母是否大写决定了作用域)、参数列表:[<name type>]、返回列表: [<name type>] ,如果是多返回值,可以直接使用return或者是return后带上具体返回项。   

复制代码
func main()  {
    println(Func01("zhangsan"))
    println(Func002)
    println(Func003("lisi", 20))
}

// 一个参数一个返回值
func Func01(name string) string {
    return "返回值" + name
}

func Func002() {
    println(" no param no return !")
}

func Func003(name string, age int) (returnName string, returnAge int)  {
    returnName = "param:" + name
    returnAge = age + 10
    return
    //return returnName, returnAge
}
复制代码

 

     具体怎么编码,怎么规范,看个人爱好和公司规范

  3、方法调用

     就正常调用,没什么可说的,但是可以使用 _ 忽略返回值

复制代码
    k := Func01("zhangsan")
    println(k)

    name1, age := Func003("wangyu", 30)
    println(name1)
    println(age)
    
    name, _ := Func003("lisi", 20)
    println(name)
复制代码

 

 

   4、方法声明与调用总结

    golang 支持多返回值,这是一个很大的不同点

    golang 方法的作用域和变量作用域一样,通过大小写控制

    golang 的返回值是可以有名字的,可以通过给予名字让调用方清楚知道你返回的是什么

四、最简单的Web服务器

  官方文档:

复制代码
package main

import (
    "fmt"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}
复制代码

  最简单的 web 服务器 —— 增加几个路由

复制代码
func main() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/home", home)
    http.HandleFunc("/user", user)
    http.HandleFunc("/order", order)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func home(w http.ResponseWriter, r *http.Request)  {
    fmt.Fprintf(w, "首页")
}

func user(w http.ResponseWriter, r *http.Request)  {
    fmt.Fprintf(w, "用户")
}

func order(w http.ResponseWriter, r *http.Request)  {
    fmt.Fprintf(w, "订单")
}
复制代码

 

   fmt 格式化输出,由于golang不允许不同类型的变量拼劲,因此需要使用 fmt 的格式化 拼接

    name := "zhangsan"
    age := 20
    // 格式化输出
    str := fmt.Sprintf("hello world %s hello world %d hahaha\n", name, age)
    println(str)

   fmt 包有完整的说明:

    掌握常用的:%s, %d, %v, %+v, %#v,不仅仅是 `fmt`的调用,所有格式化字符串的 API 都可以用;因为golang字符串拼接只能在 string 之间,所以这个包非常常用

      例如 %s 是字符串占位符,%d 是数字占位符,%v 是返回体占位符等,一般相关的描述都在goSDK对应包中的doc.go文档中

五、数组与切片

  1、数组,语法是:[cap]type

    数组需要指定初始化长度(或者叫做容量),在定义时可以直接设置初始值,如果不设置,初始值为默认值,这里有个坑需要注意一下,虽然没有初始化内容,但是默认已经被初始化,长度和容量都是定义的长度。  

    数组支持使用arr[i]下标的形式访问元素,len 和 cap 操作用于获取数组长度(数组的len 和 cap 结果是一样的,就是数组的长度)        

复制代码
func main() {
    a1 := [3]int{1,2,3}
    print(a1)

    var a2 [3]int
    print(a2)
}

func print(a1 [3]int) {
    fmt.Printf("a1: %v,len: %d, cap: %d ", a1, len(a1), cap(a1))
}
复制代码

 

  2、切片,语法:[]type

    切片和数组的区别就是,数组需要直接指定长度,切片不需要指定。

    可以直接初始化,也可以使用make初始化:make([]type, length, capacity);可以使用arr[i] 下标的形式访问元素;使用append 追加元素、len 获取元素数量、cap 获取切片容容量;

    推荐写法:s1 := make([]type, 0, capacity) ,否则的话,使用append会直接在已有的数据后面添加。        

复制代码
func main() {
    a3 := []int{1,2,3,4}
    print2(a3)

    a4 := make([]int, 3, 4)
    print2(a4)

    a5 := make([]int, 4)
    print2(a5)

    a4 = append(a4, 5)
    print2(a4)

    a4 = append(a4, 6)
    print2(a4)

    fmt.Printf("%d", a4[4])
}

func print2(a1 []int)  {
    fmt.Printf("a1: %v,len: %d, cap: %d \n", a1, len(a1), cap(a1))
}
复制代码

 

   3、数组与切片对比

        

    通过上面对比可以看到,切片比数组功能更强大,因此遇事不决用切片,基本不会出错

   4、子切片:

    数组和切片都可以通过[start:end] 的形式来获取子切片,遵循 左闭右开原则:1. arr[start:end],获得[start, end)之间的元素;2. arr[:end],获得[0, end) 之间的元素;3. arr[start:],获得[start, len(arr))之间的元素   

    s1 := a4[1:3]
    print3(s1)
    s2 := a4[1:]
    print3(s2)
    s3 := a4[:2]
    print3(s3)

 

    对于使用子切片有个需要注意的地方,在没有扩容时,切片和自切片是共享底层的,但是如果切片或者自切片发生了扩容,就不再是共享底层了,因此使用子切片一般用于只读场景,不要用于写场景

五、控制语句

  1、for

    for 和别的语言差不多,有三种形式:1. for {} ,类似 while 的无限循环;2. fori,一般的按照下标循环;3. for range 最为特殊的 range 遍历;

复制代码
func main() {
    arr := []int {1,2,3,4,5}
    forLoop(arr)
    forI(arr)
    forRange(arr)
}

func forLoop(arr []int)  {
    index := 0
    for {
        if index == 3 {
            break
        }
        fmt.Printf(" index = %d,number = %d \n", index, arr[index])
        index++
    }
}

func forI(arr []int)  {
    for i := 0; i < len(arr); i++ {
        fmt.Printf(" index = %d,number = %d \n", i, arr[i])
    }
}

func forRange(arr []int){
    for index, value := range arr{
        fmt.Printf(" index = %d,number = %d \n", index, value)
    }
}
复制代码

 

     break 和 continue 和别的语言一样

  2、if else

    if-else 和别的语言也差不多。这里有个比较特殊的用法,带局部变量声明的 if- else,例如下面例子的第二个if中的sub变量,只能在 if 块,或者后边所有的 else 块里面使用,脱离了 if - else 块,则不能再使用

复制代码
func ifelse(age int)  {
    if age<18{
        fmt.Printf("age < 18")
    }else {
        fmt.Printf("age >= 18")
    }

    if sub := age -10; sub > 10 {
        fmt.Printf("age-10=%d", sub)
    }
}
复制代码

 

   3、switch

    switch 和别的语言差不多,switch 后面可以是基础类型和字符串,或者满足特定条件的结构体最大的差别:终于不用加 break 了! 

    大多数时候,switch 后面只会用基础类型或者字符串       

复制代码
func chooiceFood(food string)  {
    switch food {
    case "apple":
        fmt.Printf("apple")
    case "banana":
        fmt.Printf("banana")
    default:
        fmt.Printf("other")
    }
}
复制代码

 

 

posted @   李聪龙  阅读(859)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示