【Go基础】2 - Go基本语句

一、Go语言的数据类型

在这里插入图片描述

数据类型完全不能隐式转换
如:
var a int32 = 1
var b int64 = 2
a = b

  • 指针类型
    • 不支持指针运算
    • string是值类型,其默认的初始化值为空字符串,不是nil
package main

import "testing"

func TestOne(t *testing.T)  {
   //指针
   a := 1
   aPtr := &a
   t.Log(a,aPtr)
   t.Logf("%T %T",a,aPtr)

   //指针不允许指针运算
   //aPtr += 1

   var s string //默认初始化是 “”,不是nil
   t.Log("*" + s + "*")
}

二、运算符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、循环和条件

1、循环

go只支持for

package class01

import "testing"

//for循环的讲解
/*
go语言中只支持for循环
*/

//如何编写一个有限循环
func TestOne5_1(t *testing.T) {
   n := 0
   for n < 5 {
      n++
      t.Log(n)
   }
}

//如何编写一个无限循环
func TestOne5_2(t *testing.T) {
   n := 0
   for {
      t.Log(n)
   }
   //如何break呢?
   //for {
   // t.Log(n)
   // if n == 10 {
   //    break
   // }
   // n++
   //}
}

2、if

在这里插入图片描述
在这里插入图片描述

package class01

import "testing"

//如何写一个条件
func TestOne6_1(t *testing.T) {
   n := 0
   if n == 1 {
      t.Log("n输入了1")
   } else if n == 0 {
      t.Log("n输入了0")
   } else {
      t.Log("n不知道输入了什么")
   }

   //与其他语言不同的地方是  条件必须是bool类型,不允许是整型或字符型,因为不存在隐式转换
   /*
      if 1 {
      }
   */
}
  • case后边默认加了有break,因此go里边不需要
  • case后边可以加多个case,命中的都可以进
package class01

import "testing"

func TestOne7_1(t *testing.T) {
   n := 0
   switch n {
   //第二种写法
   //switch n := 0 n {
   case 0, 1:
      t.Log("memexixi")
   case 2, 3, 4:
      t.Log("halouhalou")
   default:
      t.Log("*********")
   }
}

/*总结:
1、switch中可以后边跟一个赋值表达式,如函数等,再跟一个要判断的变量
2、默认每个case后边都有break,不需要再次添加
*/

四、数组和切片

package class01

import "testing"

//数组的声明
func TestOne8_1(t *testing.T) {
   var a [3]int
   a[0] = 1    //下标也是从0开始
   t.Log(a[0]) //打印刚才的赋值
   t.Log(a[1]) //查看没有赋值的部分,发现并不会报错,int类型 会给出默认值0

   n := 3
   //t.Log(a[3]) //直接写会报错
   t.Log(a[n]) //直接检查就会报错,不允许访问越界

   //那字符串类型会给出什么默认值呢?
   var b [3]string
   t.Log(b[0]) //给出了默认值""

   //数组的第二种声明方式
   c := [3]int{1, 2, 3}
   t.Log(c[0])

   //多维数组的声明方式
   //d := [2][2]int{1, 2, 3, 4} //不允许像c中这样
   d := [2][2]int{{1, 2}, {3, 4}}
   t.Log(d[0])

   //不定数组长度的声明方式
   e := [...]int{1, 2, 3, 4, 5}
   t.Log(e[3])
}

//数组的遍历
func TestOne8_2(t *testing.T) {
   arr := [...]int{1, 2, 3, 4, 5}

   //传统的遍历方式
   for i := 0; i < len(arr); i++ {
      //{ 强制代码格式,如果写到下一行会报错,expected '{', found newline
      t.Log(arr[i])
   }
}

func TestOne8_3(t *testing.T) {
   arr := [...]int{1, 2, 3, 4, 5}
   //range遍历
   for index, item := range arr {
      t.Logf("第%d个元素是%d", index, item) //格式化输入输出,跟c一样
   }

   //我们常常并不关注这个index的值,但是range又返回了两个,我们可以通过_进行接收
   for _, item1 := range arr {
      t.Log(item1)
   }
}

在这里插入图片描述
数组内部

  • 指针
  • 元素个数
  • 内部数组的容量
package class01

import "testing"

//数组的截取
func TestOne9_1(t *testing.T) {
   //总结就是前包含,后不包含
   a := [...]int{0, 1, 2, 3, 4, 5}
   t.Log(a[1:2])      //[1]
   t.Log(a[1:3])      //[1 2]
   t.Log(a[1:len(a)]) //[1 2 3 4 5]

   t.Log(a[1:]) //不写默认为 len(a)  [1 2 3 4 5]
   t.Log(a[:3]) //不写默认为0 [0 1 2]

   //不允许有-1,那种骚东西
   //t.Log(a[0:-1])

   /*
      切片总结:
      1、内部实现的本质是一个结构体,包含三个属性
         - 数组指针ptr
         - 数据个数len(int)
         - 内部数组的容量cap (int)
   */
}

//如何声明一个切片呢?
func TestOne9_2(t *testing.T) {
   var sl []int //这样不声明长度,就是一个切片,它就是  可变长
   t.Log(len(sl), cap(sl))

   sl = append(sl, 1)
   t.Log(len(sl), cap(sl))

   sl = append(sl, 2, 3)
   t.Log(len(sl), cap(sl))

   s2 := []int{1, 2, 3, 4}
   t.Log(len(s2), cap(s2))

   s3 := make([]int, 3, 5)
   t.Log(len(s3), cap(s3))
   t.Log(s3[0], s3[1], s3[2])
   //t.Log(s3[0], s3[1], s3[2], s3[3], s3[4]) //最后2个报错,告诉我们下标越界
   //因此,cap表示的是整个容量,len表示当前可用的范围
}

//测试切片的增长
func TestOne9_3(t *testing.T) {
   s := []int{}
   for i := 0; i < 10; i++ {
      s = append(s, i)
      t.Log(len(s), cap(s))
   }
   //Java的vector相似,容量不够的时候自动拓展,并且是x2增长
}

//切片共享内存的理解
func TestOne9_4(t *testing.T) {
   arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   slice1 := arr[:5]
   slice2 := arr[3:]
   t.Log(slice1)
   t.Log(slice2)

   //如果我修改了原始数组arr的arr[3],两个切片的内容也会跟着修改,本质上还是需要了解切片的底层实现
   arr[3] = 11
   t.Log(slice1)
   t.Log(slice2)
}

五、Map的声明、元素访问和遍历

package class01

import (
   "testing"
)

//map的声明
func TestOne10_1(t *testing.T) {
   var myMap1 map[string]string //只是声明没有开辟空间
   if myMap1 == nil {
      t.Log("myMap1 是一个空map")
   }

   myMap1 = make(map[string]string, 10)
   myMap1["one"] = "java"
   myMap1["one1"] = "java1"
   myMap1["one2"] = "java2"
   myMap1["one3"] = "java3"
   myMap1["one4"] = "java4"
   t.Log(myMap1)
   t.Log(myMap1["one3"])

   //第二种方式 使用make函数进行声明
   myMap2 := make(map[int]string)
   myMap2[1] = "java"
   myMap2[2] = "java2"
   myMap2[3] = "java3"
   myMap2[4] = "java4"
   t.Log(myMap2)
   t.Log(len(myMap2))

   //第三种方式
   myMap3 := map[string]string{
      "one":   "php",
      "two":   "java",
      "three": "python",
   }
   t.Log(myMap3)
   t.Logf("myMap3的长度是%d", len(myMap3))

   //问:为什么map不需要初始化len?只需要cap?
}

//map常用操作
func TestOne10_2(t *testing.T) {
   var myMap1 map[int]int
   var myMap2 map[string]string

   t.Log(myMap1[0])    //0
   t.Log(myMap2["no"]) //""

   //我们发现go会给我们一个map的默认值,如果不存在这个key的话,那如果我真的存了一个0或""呢?如何判断?
   //其实go语言中的map取值,m[0]是返回了两个变量,第一个是value,第二个是bool,存在为true,不存在是false
   if v, exist := myMap1[0]; exist { //如果存在就打印这个值
      t.Log(v)
   } else {
      t.Log("这个key不存在")
   }

   //myMap1[2] = 0
   //t.Log(myMap1[2]) //会报错,因为myMap1只是声明,没有开辟控件

   myMap1 = make(map[int]int)
   myMap1[2] = 0
   t.Log(myMap1[2])

   delete(myMap1, 2) //删除操作
}

//map的遍历
func TestOne10_3(t *testing.T) {
   myMap3 := map[string]string{
      "one":   "go",
      "two":   "java",
      "three": "python",
   }

   for k, v := range myMap3 {
      t.Logf("key是%s,value是%s", k, v)
   }
}
package main

import "fmt"

func printMap(cityMap map[string]string)  {
   //map传递就是  本质也是值传递,原因是结构被复制,结构内部的指针没有变化,看起来像是引用传递
   for s, s2 := range cityMap {
      fmt.Println("key = ",s)
      fmt.Println("value = ",s2)
   }
}

func main() {
   cityMap := make(map[string]string)

   //添加
   cityMap["china"] = "beijing"
   cityMap["amaircan"] = "niuyue"
   cityMap["japan"] = "tokyo"

   //遍历
   printMap(cityMap)
   
   //当map中获取一个值不存在的时候,会返回默认值0
   
   
   //删除
   delete(cityMap,"china")

   //修改
   cityMap["amaircan"] = "henan"
   fmt.Println("----------")

   //遍历
   for key, value := range cityMap {
      fmt.Println("key = ",key)
      fmt.Println("value = ",value)
   }
}

六、在Go中实现Set集合

Go中没有Set,但是可以map[type] bool
就是用键当做存储单元,内部的值当做是否有效的标记

package class01

import "testing"

func TestOne11_1(t *testing.T) {
   //go语言中没有原生支持set,是通过map的变形操作制作 使用map[type] bool
   set1 := map[string]bool{}

   //新增的方式
   set1["qsr"] = true
   set1["wxy"] = true

   //删除的方式
   set1["qsr"] = false

   //本质上就是通过map的key唯一来进行实现的
}

//顺带说一下,Map与工厂模式的实现
func TestOne11_2(t *testing.T) {
   m := map[int]func(op int) int{} //定义一个map,key是int,value是一个函数func,这个函数的参数是op int类型,返回值是int
   m[1] = func(op int) int { return op }
   m[2] = func(op int) int { return op * op }
   m[3] = func(op int) int { return op * op * op }
   t.Log(m[1](2), m[2](2), m[3](2))
}

七、字符串

在这里插入图片描述

package class01

import "testing"

func TestOne12_1(t *testing.T) {
   var s string
   t.Log(s)

   s = "hello"
   t.Log(len(s))

   //s[1] = '3' //go中,string是不可变的,原因是string类型的实现方式本质上是byte类型的slice

   s = "\xE4\xB8\xA5" //可以存储任何二进制数据
   t.Log(s)
   t.Log(len(s)) //输出的是byte数,并非是简单的中文*2的长度
}

func TestOne12_2(t *testing.T) {
   //对比Unicode和UTF8的区别
   s1 := "中"
   t.Log(len(s1))

   c := []rune(s1)
   t.Log(len(c))

   t.Logf("中 的Unicode %x", c[0])
   t.Logf("中 的UTF8 %x", s1)

   /*
      Unicode是一种编码规则
      UTF8是存储规则
   */
}

//字符串的遍历
func TestOne12_3(t *testing.T) {
   s := "中华人民共和国"
   for _, c := range s {
      t.Log(c)
      t.Logf("%[1]c %[1]x %[1]d", c) //  %[1] 表示都跟第一个参数对应
   }
}
  • 常用的字符串库
    在这里插入图片描述
package class01

import (
   "strconv"
   "strings"
   "testing"
)

//字符串分割  和  合并
func TestOne13_1(t *testing.T) {
   s := "a:b:c"
   parts := strings.Split(s, ":")
   for _, ch := range parts {
      t.Log(ch)
   }
   t.Log(strings.Join(parts, "-"))
}

//字符串类型转换
func TestOne13_2(t *testing.T) {

   //将数字强制转换为string类型
   s := strconv.Itoa(10)
   t.Log("str:" + s) //字符串之间可以直接+

   //将string类型强制转换为int
   if n, err := strconv.Atoi("10"); err == nil {
      t.Log(n)
   }
}

八、函数

在这里插入图片描述

1、函数式编程

package class01

import (
   "fmt"
   "math/rand"
   "testing"
   "time"
)

//多返回值的函数
func returnMuliValues() (int, int) {
   return rand.Intn(10), rand.Intn(20)
}

func TestOne14_1(t *testing.T) {
   a, b := returnMuliValues()
   t.Log(a, b)
   a, _ = returnMuliValues()
   t.Log(a)
}

//计算方法时长,装饰器的使用
func timeSpent(inner func(op int) int) func(op int) int { //声明一个入参是函数,返回值也是一个函数的方法
   return func(n int) int {
      start := time.Now()
      ret := inner(n) //相当于将入参中的函数放在中间执行
      fmt.Println("函数执行总时间花费", time.Since(start).Seconds())
      return ret
   }
}

func slowFun(op int) int {
   time.Sleep(time.Second * 1)
   return op * 3
}

func TestOne14_2(t *testing.T) {
   res := timeSpent(slowFun) //res函数,就是装饰之后的函数,返回值还是保留了原来的值,当然也可以进行改动
   t.Log(res(3))
}

2、可变参数 和 defer的用法

在这里插入图片描述
都会转化为数组,最终通过数组遍历来实现

  • defer:无论如何都会最后执行一下的
    可以用作安全的释放资源,释放锁等
package class01

import (
   "fmt"
   "testing"
)

//可变参数的函数
func Sum(ops ...int) int {
   resu := 0
   for _, v := range ops {
      resu += v
   }
   return resu
}

func TestOne15_1(t *testing.T) {
   t.Log(Sum(1, 2, 3))
   t.Log(Sum(1, 2, 3, 4, 5))
}

//defer的用法
func DeferDemo() {
   fmt.Println("执行了defer")
}

func TestOne15_2(t *testing.T) {
   defer DeferDemo()
   fmt.Println("run now")
   panic("e")
}

/*
defer总结
1、一定会执行
2、一定是终止前执行
3、即使是panic崩溃也不会影响,类似于try catch中的finally,一定会执行

用处:安全的释放资源,释放锁等
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李易安QSR

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

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

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

打赏作者

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

抵扣说明:

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

余额充值