Golang基础学习笔记

Go基础语法

声明变量

func main(){
    // 1.
    // 定义时直接赋值
    var i int = 10
    // 2.
    // 先定义后赋值
    // 注意,在go语言中,即使不赋值变量也是有默认值的
    var i int
    i = 10
    // 3.
    // 根据类型推导
    var i = 10
    // 4.
    // 使用:=替换var关键字
    // 这种使用方式,只能在初始定义变量时使用
    i := 10
    
}

定义全局变量

var n1 = 10

// 同时定义多个变量
var (
    n1 = 10
	n2 = 120
    )
func main(){
    
}

数据类型

  • 基本数据类型
    • 数值型
      • 整数类型
        • int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、byte
      • 浮点数
        • float32、float64
    • 字符型(没有专门的字符型,使用byte来保存,中文字符需要int来存)
    • 布尔型(bool)
    • 字符串(string)
  • 派生数据类型
    • 指针、数组、结构体、管道、函数、切片、接口、map

基本数据类型

整数类型

整数类型主要有:int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、byte

其中int是定义整数的默认类型

int8表示:8位二进制的整数,同理int16表示16位的二进制整数

uint8表示:8位无符号的二进制整数,同理uint16表示无符号16位二进制整数

浮点数

go语言中有两种浮点数类型:float32和float64

默认定义使用的是float64

package main
import "fmt"
// 演示golang中小数类型的使用
func main() {
   // go中默认定义的小数是float64
   var price1 = 3.90
   var price2 float32 = 2.0
   fmt.Printf(`%T`, price1)
   fmt.Printf(`%T`, price2)
}
字符类型

Golang中没有专门的字符类型,如果要存储单个字符,一般使用byte来保存

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。也就是说对于传统的字符串是由字符组成的,而Go的字符串不同,它是由字节组成的。

字符串

注意在Go中字符串属于基本数据类型,在go语言中string的默认值是空字符串

go中的字符串也是不可变的

go中字符串处理的函数都在strings包中

  • 遍历字符串

    var c4 = "测试狙击枪"
    for _, v := range c4 {
       fmt.Printf("%c", v)
    }
    

其他基本数据类型转字符串:

  • 使用fmt.Sprintf()进行格式化
  • 使用strconv.Formatxxx进行转换

字符串转基本数据类型

  • 使用strconv.Parsexxx进行转换

使用反引号定义字符串

在使用双引号定义字符串时,如果字符串中存在特殊字符如:""\等字符时,需要单独使用转义字符进行转义,十分麻烦

go中允许使用==`==来定义字符串,这种定义方式不会对其中的特殊字符进行处理

func main() {
   var s = `sad\t\b\\asda`
   // 这里不会对字符串进行任何转义,直接输出所有内容
   println(s)
}
布尔类型

布尔类型在go中只占一个字节

数据类型转换

go语言中只有显示类型转换

T(v)

T:需要转换的类型

派生数据类型

指针

go语言中的指针不像C语言的指针那样复杂,只有对指针的基本赋值、访问等操作

  • &a:取变量a的指针
  • *a:表示对指针a所指向的内存空间进行赋值
package main

func main() {
   a := 10
   test(&a)
   println(a)
}
func test(a *int) {
   *a = 20
}

在go中每个go文件都必须有归属的包。

在包中使用其他包的内容需要通过import引入

包名命名

尽量保持package的名字和目录名字相同,不要和标准库冲突

变量名、函数名、常量名

采用驼峰命名

注意如果变量、函数、常量首字母大写就可以被其他包访问

导包

在.go文件中需要导入其他包时,导包路径是从$GOPATH/src/后开始计算的,使用/分割

go项目中import其他包时实际上是在引入路径下的目录(并不是按包名进行导入的)

例如对于目录:~/gocode/project/test下存在一个utils.go文件

utils.go文件中定义的包名并不是test,而是test2

此时在其他文件引入该文件时需要使用 gocode/project/test 而不是 gocode/project/test2

但是在程序代码层面,导入后调用对应方法时需要使用test2来进行调用

go命令

编译运行

go有两种运行方式:编译运行、直接运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDmobkkp-1651498935545)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220420162304842.png)]

编译运行

  • 在cmd中可以直接通过go build xx.go将go文件编译为可执行文件,例如在Windows系统下执行此命令可以直接编译成.exe文件来执行

    编译时编译器会将程序运行依赖的库文件包含在可执行文件中

直接运行

  • go run xx.go

文件格式化

go语言提倡开发者编码统一规范化,

gofmt -w xxx.go

如果不加-w,不会写入源文件中

变量

声明变量

声明变量的方式主要有四种

func main() {

   // 第一种:var 变量名 变量类型 = 变量值
   var num1 int = 1
   println(num1)

   // 第二种:var 变量名 变量类型
   // 这种定义方式变量的值为该类型的默认值
   var num2 int
   println(num2)

   // 第三种:var 变量名 = 变量值
   // go编译程序会自动根据变量值对变量进行类型推导
   var num3 = 3
   println(num3)

   // 第四种:变量名 := 变量值
   // 编译程序会自动根据变量值赋值并且省略掉 var关键字
   sex := "男"
   println(sex)
}

go中还可以同时定义多个变量

func main() {
	// 声明多个变量
	var n1,n2,n3 int
	println(n1)
	println(n2)
	println(n3)
	var n4,n5,n6 = "a",'a',10
	println(n4)
	println(n5)
	println(n6)
}

变量作用域

变量分为:全局变量局部变量

全局变量定义在方法外部,局部变量定义在方法内

全局变量如果使用首字母大写进行命名,则其他包可以引用,如果首字母小写,则只能在本包中使用

变量、函数、常量同理

自定义变量类型

package main
// 定义新的变量类型,myAge
// 可以看作是对原有类型int的别名
// 但是go在编译时会认为myAge和int类型的变量是两种不同类型,所以不能直接转换
// 必要时需要用显示转换实现
type myAge int
func main() {
   var age myAge = 10
   println(age)
   // 这里age2和age类型不同所以直接赋值会报错
   var age2 int = age
}

拓展知识

随机数

使用rand时需要设置一个seed值,如果seed值不变的话,每次运行得到的随即值都相同

package main

import (
   "fmt"
   "math/rand"
   "time"
)
func main() {
   rand.Seed(time.Now().Unix())
   //seed 的参数不变的话 -> 此程序的运行环境是固定的,因此 rand.Intn 总是会返回相同的数字   ->所以要讲seed的参数一直改变(获取时间)
   fmt.Printf("随机产生的数字是 : %d \n", rand.Intn(100))
}

转义字符

转移字符解释
\n换行
\b退格
\r光标回到开始,后续输入替换前面的输入
\t制表符
\c格式化输出(例如将int值按编码进行输出)

判断-循环

IF判断

func main() {
   var score float64
   if fmt.Scanln(&score); score > 100 || score < 0 {
      println("错误成绩")
   } else if score >= 60 {
      println("及格")
   }
}

SWITCH判断

go中的switch不需要单独加break,自动case结束后出判断。如果需要让case继续向下穿透需要加关键字fallthrough

    // 写法一
	switch score {
    case 100 :
       println("")
    case 10:
       println("")
    default:
       println()
    }
	// 写法二
	switch {
	case score<0 || score>100:
		println("非法数值")
	case score>60:
		println("及格")
	
	}

For循环

// for循环
for i := 0; i < 5; i++ {
   println(i)
}
// 使用for循环实现while循环
var i = 0
for i < 10 {
   println(i)
   i++
}

For Range循环

func main() {
   for key, val := range "sdaas" {
      fmt.Printf("%v:%c\n", key, val)
   }
}

函数

  • go中的函数都是值传递
  • 支持多个返回值
  • 不支持函数重载,即任意函数不能存在多个同名函数(以包为单位,即使函数在两个文件中,只要包名相同则不能同名)
  • 支持可变数量的参数
// 函数一:直接调用的函数
func test(nums []int) {
   for i, _ := range nums {
      nums[i] *= 2
   }
   for i := range nums {
      println(nums[i])
   }
   //println(nums)
}
// 函数二:只能由具体的结构体调用
func (user User) getName() string {
   return user.name
}

函数做参数

Go语言中的函数可以作为变量进行赋值

func main() {
   var a func(int) = test1
   test2(10, 20, a)
}
func test1(num int) {
   println(num)
}
func test2(num1 int, num2 int, fun func(int)) {
   fun(num1 + num2)
}

Init函数

每个.go 文件都可以设置一个init()函数,所有定义的init函数都会在main函数启动前执行

执行顺序

package main

import (
   "go_code/project01/learn-01/init/db"
)

var num int = test()

func test() int {
   println("main.test")
   return 10
}

func init() {
   println("main.init")
}
func main() {
   db.Test()
}

输出:

db.test
db.init
main.test
main.init
db.Test

从上面可以看到输出顺序为:

  • main包中引入的其他包先进行初始化
  • 引入包的变量进行初始化
  • 引入包的init函数的执行
  • main包下的变量初始化
  • main包下的init函数执行
  • main函数执行

匿名函数

func main() {
   // 匿名函数
   ret := func(num1 int, num2 int) int {
      return num1 + num2
   }(10, 20)
   println(ret)
}

结构体

和类的概念相似,但是结构体中不能对方法进行定义,只能在外部对

func (user User) getName() string {
   return user.name
}

type User struct {
   name string
   age  int
}

闭包

应用场景:闭包可以保留上次引用的某个值,我们传入一次就可以反复使用

package main

func getSum() func(num int) int {
   var sum = 0
   return func(num int) int {
      sum += num
      return sum
   }
}

func main() {
   var fun = getSum()
   println(fun(1))
   println(fun(2))
   println(fun(3))
}
/**
输出
1
3
6
 */

从上面可以看出,sum变量被传入了函数fun中,所以后续对函数进行操作时sum的值进行了累计,此时sum值是属于函数对象fun的

进阶闭包

package main

func getSum() func(num int) int {
   var sum = 0
   return func(num int) int {
      sum += num
      return sum
   }
}

func main() {
   var fun = getSum()
   println(fun(1))
   println(fun(2))
   println(fun(3))
   var fun2 = getSum()
   println(fun2(1))
   println(fun2(10))
}
package main

func getSum() func(num int) int {
	var sum = 0
	return func(num int) int {
		sum += num
		return sum
	}
}

func main() {
	var fun = getSum()
	println(fun(1))
	println(fun(2))
	println(fun(3))
	var fun2 = getSum()
	println(fun2(1))
	println(fun2(10))
}

/**
输出
1
3
6
1
11

 */

系统函数

字符串函数

所有内置函数都存放在 builtin包下

  • 统计字符串的长度按字节进行统计

    len("沈洋")
    // 输出值为6
    
  • 字符串遍历

     // 方法一
     r := []rune("芜湖起飞")
     for _, v := range r {
         fmt.Printf("%c", v)
     }
     
     // 方法二
     for _, v := range "芜湖起飞" {
         fmt.Printf("%c", v)
     }
    
  • 字符串转整数

    n, err := strconv.Atoi("1撒旦撒2")
    if err != nil {
       println("数值错误")
       return
    }
    println(n)
    
  • 整数转字符串

    str := strconv.Itoa(10)
    println(str)
    
  • 查找子串是否在指定的字符串中

    var flag bool = strings.Contains("javaandgolang", "go")
    println(flag)
    
  • 统计一个字符串有几个指定的字串

    var count = strings.Count("javaandgolang","a")
    println(count)
    
  • 不区分大小写的字符串进行比较

    var same = strings.EqualFold("java", "Java")
    println(same)
    
  • 返回字串在字符串第一次出现的索引值,如果没有返回-1

    var index = strings.Index("javaandgolang", "l")
    println(index)
    
  • 字符串替换

    var replaceStr = strings.Replace("goandjava", "go", "golang", -1)
    println(replaceStr)
    
  • 按照指定的某个字符进行分割,返回字符串数组

    str = "hell nice to meet you"
    var strArray = strings.Split(str, " ")
    for i := range strArray {
       print(strArray[i])
       if i != len(strArray)-1 {
          print("_")
       }
    }
    
  • 将字符串进行大小写替换

    str = strings.ToUpper(str)
    str = strings.ToLower(str)
    
  • 去掉字符串左右部分

    // 取出字符串左右空格
    strings.TrimSpace(str)
    // 去除字符串左右的~
    strings.Trim("str","~")
    // 去除字符串左边的~
    strings.TrimLeft(str,"~")
    // 去除字符串右边的~
    strings.TrimRight(str,"~")
    
  • 判断字符串是否以指定字符串开头

    strings.HasPrefix("http://www.baidu.com","http")
    
  • 判断字符串是否以指定字符串结尾

    strings.HasSuffix()s("http://www.baidu.com", "com")
    

日期函数

  • 获取当前时间

    var now time.Time = time.Now()
    fmt.Println(now)
    
  • 获取当前年、月、日、时、分、秒等

    var now = time.Now()
    fmt.Println(now.Year())
    fmt.Println(now.Month())
    fmt.Println(now.Day())
    fmt.Println(now.Hour())
    fmt.Println(now.Minute())
    fmt.Println(now.Second())
    
  • 格式化日期

    注意格式化中每个数字是固定的,不能随便修改

    var now time.Time = time.Now()
    fmt.Println(now.Format("2006-01-02 Jan"))
    

内置函数

内置函数是指go中提供的不需要导入包的函数,内置函数是builtin包下的函数

  • len函数

    获取数组、切片、字符串(注意如果带中文需要先转成rune数组,因为其底层是按字节数来统计的)等变量的长度

    var arr [10]int
    fmt.Println(len(arr))
    
  • new函数

    分配内存,用于分配值类型(int系列、float系列、bool、string、数组、结构体)

    返回值为指针

    package main
    
    func main() {
       // new函数返回的是指针
       var sy *Person = new(Person)
       println(sy.age)
       println(sy.name)
    }
    
    type Person struct {
       name string
       age  int
    }
    
  • make函数

    分配内存,用来分配引用类型(指针、slice、map、管道、interface等)

错误处理

go中使用defer+recover实现

defer

defer定义的语句不会立即执行,程序会将defer语句压入一个栈中,等函数结束后依次执行栈中的语句

在一个函数中连续定义的两个defer语句会先执行后面的语句

recover

内置函数,允许程序管理Panic过程中的Go程。在defer的函数中,执行recover调用会取回传至panic调用的错误值,恢复正常执行,停止恐慌过程。若recover在defer函数之外被调用,它将不会停止Panic。

处理异常的方式主要有两种:①在方法中处理②抛出异常到方法外部

在当前函数中处理异常

package main

import "fmt"

func main() {
   ret := test(10, 1)
   fmt.Println(ret)
}
func test(num1 int, num2 int) int {
   defer func() {
      err := recover()
      if err != nil {
         fmt.Printf("捕获到异常:%v\n", err)
         return
      }
      println("没有发生异常")
   }()
   sum := num1 / num2
   return sum
}

抛出异常

package main

import (
   "errors"
   "fmt"
)

func main() {

   ret, err := test2(10, 0)
   if err != nil {
      println("err:" + err.Error())
   }
}

func test2(num1 int, num2 int) (int, error) {
   if num2 == 0 {
      return 0, errors.New("除数不能为0")
   }
   sum := num1 / num2
   return sum, nil
}

数组

在go语言中,数组的类型取决于:基本元素类型+数组长度

与其他语言不同在go中[3]int[4]int不是同一种数据类型并且

[]int是切片类型,不是数组

数组的定义方式:

// 方法一:直接指定数组长度并静态初始化值
var arr = [3]int{3, 6, 9}
// 方法二:...表示由编译器自动根据后面{}中元素的个数进行推断
// 注意这里不能使用 var arr2 = []int{10,2,3,4}
// 这种方式也能进行定义,但定义出来的arr2不是数组是切片数据类型
var arr2 = [...]int{10, 2, 3, 4}
// 方法三:直接指定大小并且采用默认值
var arr3 [100]int

与其他语言不同的是在go中如果按如下定义方法

这里的arr其实并不是一个数组,也就是说如果你按上面定义数组的方式进行定义并尝试传入test,将会报错

func test(arr []int) {
   for i := range arr {
      println(arr[i])
      arr[i] += 1
   }
}

正确的方式是

func test(arr [3]int){
   for i := range arr {
      println(arr[i])
      arr[i] += 1
   }
}

go中所有数组类型都需要指定大小,否者为切片类型

注意

并且在go中数组传递的是数组元素的拷贝,即方法中对数组进行修改后,不会影响外部

而切片传递的是引用,方法内的修改可以影响到外部

切片

切片是go中特有的数据类型,类似于Java中的List

  • 切片就是一个结构体,它有三个重要参数:引用数组地址、长度、容量

    长度和容量,分别可以使用函数len()cap()获取到

    • 长度表示当前切片有元素的个数(遍历和访问都不能超过len的长度)

    • 容量表示切片底层使用的数组实际的大小,当长度等于容量时,再继续添加元素时则底层会自动对引用的数组进行扩容和ArrayList相似。注意每次扩容后底层引用的数组和原来的数组不同

  • 切片会自动扩容

func main() {
   // 定义数组
   var intarr [6]int = [6]int{1, 2, 3, 4, 5, 6}
   // 切片是构建在数组之上的
   // 定义一个切片名字为slice,[]动态变化的数组长度不写
   // [1:3]切片 - 表示从数组下标1开始 切到下标3之前(不包括3)
   slice := intarr[1:3]
   // 输出数组
   fmt.Println("intarr:", intarr)
   // 输出切片
   fmt.Println("slice:", slice)
   // 切片元素个数 - 2
   fmt.Println(len(slice))
   // 切片容量 - 5
   // slice是从intarr中切出来的,所以此时slice底层使用的数组其实就是intarr
   // slice初始是切的是[1:3]所以底层引用时其数组的容量为intarr.len-1
   // 如果后续向slice中添加元素超过cap-5之后,切片会进行扩容,扩容后cap增加并且
   // 扩容后底层使用的数组将不再是intarr
   fmt.Println(cap(slice))
}

注意事项

  • 切片的默认值是nil,切片定义完后不能直接使用,因为本身是一个空的,需要引用其他数组或者make一个空间供切片来使用

  • cap是内置函数,用于统计切片的容量,即最大可以存放多少各元素(可以扩容)

  • 切片可以继续切片

  • 切片不能越界访问(0~len(slice)-1)

创建

方式一

// 定义数组
var intarr [6]int = [6]int{1, 2, 3, 4, 5, 6}
// 切片是构建在数组之上的
// 定义一个切片名字为slice,[]动态变化的数组长度不写
// [1:3]切片 - 表示从数组下标1开始 切到下标3之前(不包括3)
slice := intarr[3:]

方式二

// make(切片类型,长度、初始容量)
var slice []int = make([]int,5,10)

方式三

var slice []int=[]int{1,2,3} 

String和Slice关系

  1. string底层是一个byte数组,因此string也可以进行切片处理
  2. string和

判断变量地址

package main

import (
   "fmt"
)
func main() {
   slice1 := []int{10, 20, 30}
   slice2 := slice1
   i1 := fmt.Sprintf("%p", slice1)
   i2 := fmt.Sprintf("%p", slice2)
   if i1 == i2 {
      println("地址相等地址为:", i1)
   } else {
      println("地址不同,slice1地址为:", i1, ",slice2地址为:", i2)
      println()
   }
   slice2[2] = 100
   slice2 = append(slice1, 10)
   slice2[0] = 1000
   slice2 = slice2[0:3]
   slice2[0] = 10
   i1 = fmt.Sprintf("%p", slice1)
   i2 = fmt.Sprintf("%p", slice2)
   if i1 == i2 {
      println("地址相等地址为:", i1)
   } else {
      println("地址不同,slice1地址为:", i1, ",slice2地址为:", i2)
      println()
   }
}

Map

基本语法

var map 变量名 map[keytype]valuetype

注意声明是不会分配内存的,初始化需要make,分配内存才能赋值和使用

func main() {
	
    // 方法一
	var reMap1 map[int]string = make(map[int]string, 10)
	reMap1[10] = "沈洋"
	println(reMap1[10])
    
	// 方法二
	var reMap2 map[int]string
	reMap2 = make(map[int]string, 10)
	println(reMap2)

	// 方法三
	var reMap3 map[int]string = map[int]string{1: "12312"}
	println(reMap3)
}

增删改查

go中的map直接赋值如果键值对不存在则新增

var reMap1 map[int]string = make(map[int]string, 10)
reMap1[10] = "沈洋"

go中的map直接赋值如果键值对存在则修改

var reMap1 map[int]string = make(map[int]string, 10)
reMap1[10] = "沈洋"

reMap[10]会返回两个值,valueokok是bool类型的值

ok为``true`时表示值存在,否者值不存在,使用时注意先判断是否存在再拿值,否者map会针对不存在的key返回默认值

reMap3 = make(map[int]string, 10)
reMap3[10] = "沈洋"
var name, ok = reMap3[10]
if ok {
    println(name)
}

delete(map,“key”),delete是一个内置函数,如果key存在,删除该key-value。如果key不存在,不操作并且不会报错

var reMap3 map[int]string = map[int]string{1: "12312"}
println(len(reMap3))
delete(reMap3, 1)
println(len(reMap3))

删除全部map,map中没有clear方法来直接删除所有key-value

  • 可以遍历map来依次删除所有key-value
  • 直接将map = make(map[string]int,10),来将原来的map成为垃圾,gc回收
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenyang1026

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值