Golang基础测试

一、数字、字符串、常量

func main(){
	fmt.Println("----数字----")
	fmt.Println("不同数字类型不可算数运算,别名可以")
	// 定义类型别名 type New = Old
	// 定义新类型  type New Old

	// 默认的数字类型是int,int不是int32或int64的别名
	n := 123
	fmt.Printf("%T \n", n)

	//uint8(别名"byte",0-255), uint16, uint32, uint64
	var n1 uint8 = 11
	var n2 byte = 22
	// 别名相当于同一类型可以运算
	fmt.Printf("%T %T %T \n", n1, n2, n1+n2)

	//int8, int16, int32(别名"rune"), int(默认), int64
	var n3 rune = 11
	var n4 int32 = 22
	fmt.Printf("%T %T %T \n", n3, n4, n3+n4)

	//float32, float64(默认)
	var f = 3.14
	i := int(f) // 可以强制转换类型
	fmt.Printf("%T %T %d \n", f, i, i)

	// 关于溢出
	var x int8 = -128
	fmt.Println(x - 1)
	fmt.Println(x / -1)
	fmt.Println(x/-1 - 10)
	fmt.Println(x/-1 + 10)


	fmt.Println("----字符串----")
	str := "hallo "
	str += "world"//使用“+”拼接字符串
	//不能直接更改单个字符,拆成字节切片,修改后转回字符串
	s := []byte(str)
	fmt.Println(s)
	s[1] = 'e'
	str = string(s)
	fmt.Println(str)
	//字符串可以切割,字符串切片在底层上并不创建新的内存,是在原有的基础上偏移
	str = "I like the " + str[6:]
	fmt.Println(str)
	// 字符串长度,len()无法计算中文长度,可以利用utf8包,也可以使用[]rune
	str2 := "hello中国"
	fmt.Println(len(str2), len([]rune(str2)))


	fmt.Println("----常量----")
	//特殊常量iota,在 const关键字出现时将被重置为 0,const 中每新增一行常量声明将使 iota 计数一次
	const (
		A = iota
		B
		C string = "C"
		D
		E = iota
	)
	const F = iota
	fmt.Println(A, B, C, D, E, F)
}

二、指针

package main
import "fmt"
func main() {
   var a int = 4
   //一个指针变量通常缩写为ptr,空指针为nil
   var ptr *int
   fmt.Println(ptr == nil)
   ptr = &a
   fmt.Printf("ptr类型为%T\n", ptr)
   fmt.Println("ptr值为", ptr)
   fmt.Printf("*ptr值为%d\n", *ptr)
}

三、数组和切片

package main
import (
	"fmt"
)
func main() {
	fmt.Println("----数组----")
	//数组是具有相同唯一类型的一组已编号且长度固定的数据项序列
	arr1 := [3]byte{'a','b'}
	//也可以自行统计[...]byte{'a','b'},但此时固定长度为2,则不能进行下边赋值
	arr1[2] = 'c'
	//二维数组
	arr2 := [2][2]int{{1,2},{3}}
	arr2[1][1] = 4
	fmt.Println(arr1, arr2)


	fmt.Println("----切片----")
	//切片("动态数组",引用类型),与数组相比切片的长度是不固定的,可以追加元素
	//像一个结构体,包含了指针,长度,最大长度
	slice1 := []int{1,2}
	slice2 := append(slice1, 3)
	slice3 := slice2[1:]
	fmt.Println(slice1,slice2,slice3)
	fmt.Println(cap(slice1))
	//使用make形式,第二个参数代表初始化2个默认成员,第三个参数表示最大成员数(可省略)。
	slice4 := make([]int, 2, 3)//2个成员
	slice5 := append(slice4, 67373)//3个成员,地址和slice4相同
	slice6 := append(slice5, 67373)//超出,重新分配地址
	fmt.Println(len(slice4),cap(slice4),slice4,slice5)
	fmt.Printf("%p %p %p \n",slice4,slice5, slice6)
	//未指定长度的切片等于nil
	var slice7 []int
	fmt.Println(slice7 == nil)
}

切片cap容量增长原理:append函数执行时会判断切片容量是否能够存放新增元素,如果不能,则会重新申请存储空间,新存储空间将是原来的2倍或1.25倍

package main

import (
	"fmt"
)

func main() {
	var slice []int
	slice = append(slice, 1)
	fmt.Println(&slice[0], cap(slice))

	slice = append(slice, 2)
	fmt.Println(&slice[0], cap(slice))

	slice = append(slice, 3)
	fmt.Println(&slice[0], cap(slice))

	slice = append(slice, 4)
	fmt.Println(&slice[0], cap(slice))

	slice = append(slice, 5)
	fmt.Println(&slice[0], cap(slice))

	for i := 0; i < 256; i++ {
		slice = append(slice, i)
	}
	fmt.Println(&slice[0], cap(slice))

	for i := 0; i < 256; i++ {
		slice = append(slice, i)
	}
	fmt.Println(&slice[0], cap(slice))

	for i := 0; i < 256; i++ {
		slice = append(slice, i)
	}
	fmt.Println(&slice[0], cap(slice))

	for i := 0; i < 256; i++ {
		slice = append(slice, i)
	}
	fmt.Println(&slice[0], cap(slice))

}

四、Map集合

package main
import "fmt"
func main() {
	// 声明map时,是个空指针
	var m0 map[int]int
	// m0[0] = 0 不可以
	fmt.Println(m0, m0 == nil)
	
	//初始化, 正确姿势是使用make分配内存
	m1 := map[string]int{"a": 1}
	//添加键
	key := "b"
	m1[key] = 2
	//map为引用类型,m1和m2同一个地址
	m2 := m1
	m2["c"] = 3
	fmt.Println(m1)
	//遍历,注意是无序的!随机的!
	for key, val := range m1 {
		fmt.Printf("key=%s,val=%d\n", key, val)
	}
	//删除,不存在不会报错
	delete(m1, "c")
	fmt.Println(m2)
}

五、Struct结构体

package main
import "fmt"
type Human struct{
	Name string
	Age int
}
//组合,相当于类继承
type Student struct{
	Human
	Number int
}
func main(){
	me := Student{
		Human : Human{ Name: "abc"},
		Number: 67373,//换行结束时此处必须有逗号
	}
	//两种形式皆可获得,未赋值Age数字类型默认为0
	me.Age = 1
	me.Human.Age = 2
	fmt.Println(me)
	
	// 可以使用 new 去创建,但是返回的是指针类型
	you := new(Student)
	you.Number = 18 //指针类型不能.属性,这里做了语法糖,其实是(*you).Number 
	fmt.Printf("%T\n", you)

	//匿名结构体,临时使用
	his := struct{
		name string
		age uint8
	}{"his", 34} // 可以不指定key,值列表形式
	fmt.Println(his)

}

六、函数

package main
import "fmt"

//多个返回值
func fn1(c int) (d int,e int){
	d,e = c + 1, c + 2
	return 
}
func fn2(c int) (int, int){
	return c + 1, c + 2
}
//不定参数生成反转数组
func fn3(arg ...int) []int{
	length := len(arg)
	s := make([]int, length)
	for i := 0; i < length; i++ {
		s[i] = arg[length - i - 1]
	}
	return s
}

// 专有函数,也就是方法,作用于特定类型,需要有接收者
// 举例先声明一个结构体
type Me struct{
	name string
}
// 这里的变量名应使用结构体首字母小写,不要用this或者self
func (m *Me) say () {
	fmt.Println("我是" + m.name)
}

func main() {
	c := 1
	//多返回值
	d,e := fn1(c)
	f,g := fn2(c)
	fmt.Println(d, e, f, g)
	//不定参数
	s := fn3(1,2,3,4)
	fmt.Println(s)
	//say不能直接调用
	me := Me{"张三"}//new(Me)
	me.say()
}

七、接口

package main
import "fmt"
func main() {
	//空接口可以传任何类型
	type Element interface{}
	//一个空接口类型的切片的值可以是任意类型
	list := []Element{1,"Hello",'e'}
	fmt.Println(list)
	//断言类型
	val,ok := list[0].(int)
	fmt.Println(val,ok)
	
	//使用接口类型
	var phone Phone = new(Huawei)//这里必须取址,等同&Huawei{}
	phone.call()
}

//定义接口
type Phone interface {
	call()
}
//定义结构体
type Huawei struct {

}
//接口实现方法
func (huawei *Huawei) call() {
	fmt.Println("我用华为打电话")
}

八、并发goruntine

package main 
import (
	"fmt"
	"runtime"
)
func main() {
	fmt.Printf("CPU数量%d \n",runtime.NumCPU())  // 注意,这里返回的是逻辑核数,非物理核心数
	runtime.GOMAXPROCS(2)//设置可以并行计算的CPU核数,返回之前的值

	for i := 0; i < 10; i++ {
		//固定顺序
		fmt.Println(i)
		//非固定顺序,且不一定有机会输出,程序就结束了
		go func(i int){
			fmt.Printf("goruntine%d\n", i)
		}(i)
	}
	
}

使用 sync.WaitGroup保证goroutine都正常执行

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	fmt.Printf("CPU数量%d \n", runtime.NumCPU()) // 注意,这里返回的是逻辑核数,非物理核心数
	runtime.GOMAXPROCS(2)                      //设置可以并行计算的CPU核数,返回之前的值

	wg := sync.WaitGroup{}

	for i := 0; i < 10; i++ {
		wg.Add(1) // +1
		//固定顺序
		fmt.Println(i)
		//非固定顺序,且不一定有机会输出,程序就结束了
		go func(i int) {
			fmt.Printf("goruntine%d\n", i)
			wg.Done() // -1
		}(i)
	}

	wg.Wait() // 等待
}

九、chan通道

package main
import (
    "fmt"
	"time"
)
//通道可以声明接收或者发送类型
// type Sender chan<- int
// type Receiver <-chan int
func main() {
	//默认非缓存阻塞的,即第二个参数默认0.
	//设置缓存数量,可以直接写入,(再直接写入会报错),其后的可以通过goruntine写入
	c := make(chan int, 2)
	c <- 1
	c <- 2
	go func(c chan<- int) {
		for i := 3; i < 6; i++ {
			c <- i
			fmt.Println("发送",i)
		}
		close(c)
	}(c)
	//1,2可以直接读取,goruntine不一定这时候有没有发送,所以此时等待,接收到便读取,直到close
	for i := range c {
		fmt.Println("接收", i)
	}
	// 让main函数执行结束的时间延迟1秒,不然goroutine大概率没机会输出。
	// 以使上面两个代码块有机会被执行。这不是正确姿势,应该使用sync.WaitGroup包,或者通过非缓存阻塞的
	time.Sleep(time.Second)
}

非缓存阻塞的channel,select用于监听IO

package main

import (
	"fmt"
	"time"
)

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)

	ticker := time.NewTicker(time.Millisecond * 500)
	go func() {
		for t := range ticker.C {
			fmt.Println("Tick at", t)
		}
	}()
}

理解 select , 可以再尝试去掉 c = nil 查看一下输出。 (这个range空对象数组妙啊)

var o = fmt.Print

func main() {
    c := make(chan int, 1)
    for range [4]struct{}{} {
        select {
        default:
            o(1)
        case <-c:
            o(2)
            c = nil
        case c <- 1:
            o(3)
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值