Golang语言学习

Golang语言学习

花了两天时间看了下Golang语言,发现这语言很有趣,主要是一个土拨鼠形象的语言在goland中可以看到每一个文件都会有一个小图标,感觉还是挺可爱的,这门语言估计以后还是要拥抱了,CPP学了一段时间,从Java转过去着实是不简单,估计还是拥抱go了。

主要是通过菜鸟教程来学习的,主要学习了go的语言相关特性,go中保留了指针,可以通过指针操作对象,同时也增加了许多的新特性,在学习的过程中,首先是关于类型定义,一般的都是前面类型后面参数,但是在go中增加了更为人性化一面的变量定义,即通过var varName varType的形式定义类型。

同时在定义类型的时候我们可以使用a := 1这个可以自动识别a为一个整型变量。且其中的整型有四种有符号的整型,以及四种无符号的整型,在java中整型的话有short/int/long且大的可以囊括小的,但是在go中只要有类型出现变化就必须显示声明,否则不可成功。

还有就是在go中定义方法的话是使用func methodName(param1 paramType) returnResult Type{}这种形式来定义方法,即先声明名称,然后是参数,最后是返回值,然后再写具体的方法实现。在go中,有个make用来制作动态的切片,且make只能定义slice、map、chan三种,在go中所有的实现都是格局数组来的,就算是实例化什么的都是这样来的,保持了语言的原生特性,很多东西没有封装,需要自己在写的时候自行实现。

go中声明类的话是使用struct关键字来声明一个类,因为是结构化的语言,在go中是没有构造方法这一说的而且在struct中是默认实现了类似于java中的toString()方法。还有就是接口,功能和java interface差不多,都是方法抽象出来,等待实现自己的类进行实现,但是这里的实现需要使用new(subClass SubClass)的时候会触发方法实现的提示goland会提示

最后的话就是go的协程,协程的话是轻量级的线程,隶属于线程,需要线程作为载体支撑协程的运行。这里的话,协程一般用作对chan写入数据,但是注意,如果写入chan和读取chan是一个线程直接下去的话,会造成死锁,需要切换协程进行数据的写入和读取,且如果当前协程中的chan读取不到数据了,此时可以使用close(c)来关闭当前携程中的通道。

package main

import (
	"fmt"
	"time"
)

var (
	a int
	b bool
)
var c, d int = 1, 2

//var e, f = 3, "hello world"
var ptr *int

// var e, f := 123,"hello" :=这种形式只能在函数体中出现
func main() {
	//g, h := 100, "200"
	//fmt.Println(a, b, c, d, e, f, g, h)
	//fmt.Println(&a)
	 可以针对当前的指针类型的赋予其地址,*ptr则表示取出当前地址的值即*&d
	//ptr = &d
	//fmt.Println(*ptr)
	//fmt.Println(&*&d)
	//fmt.Println(add(c, d))
	//fmt.Println(getBol(1, 2))
	/* 定义局部变量 */
	//selectMethod()
	//loopMethod()
	//gotoMethod()
	//var a, b = 100, 200
	//fmt.Printf("值交换之前的值输出: %d %d\n", a, b)
	//swap(a, b)
	//fmt.Printf("值交换之后的值输出: %d %d\n", a, b)
	//fmt.Println("外部a的地址:", &a)
	//ptrSwap(&a, &b)
	//fmt.Printf("引用交换之后的值输出: %d %d\n", a, b)

	//var p *int
	//p = &a
	//fmt.Println(p) // 输出的就是a的地址,那为什么方法却接收的就是值
	//var d = 100
	//var c *int
	//c = &d
	//setValue(c)
	//fmt.Println(*c)
	//ptrMethod()
	//ptrPtrMethod()
	//structMethod()
	//sliceMethod()
	//rangeMethod()
	//mapMethod()
	//fmt.Println(recursion(15))
	//fmt.Println(getFibonacci(10))
	//typeTransMethod()
	//interfaceMethod()
	//errorMethod()
	//errorProcess()
	//goroutine()
	chanMethod()
	channelBuffer()
}

/*
通道方法
通道channel是用来传递数据的一个数据结构
通道可用于两个goroutine之间通过传递一个指定类型的值来同步运行和通讯。
操作符<-用于指定通道的方向,发送或接收,如果未指定方向,则为双向通道
注意:make只能用来初始化slice,map,channel三种类型
*/
func chanMethod() {
	s := []int{1, 2, 3, 4, 5, 6, 7, 8}
	//fmt.Println(s[0])
	//fmt.Println(len(s))

	c := make(chan int)
	go getSum(s[:len(s)/2], c)
	go getSum(s[len(s)/2:], c)
	x, y := <-c, <-c
	fmt.Println(x, y)

	openAndCloseChan()
}

func getSum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // 将sum发送到c这个通道中去
}

/*
可以设置通道缓冲区
*/
func channelBuffer() {
	c := make(chan int, 100)
	c <- 1
	c <- 2
	fmt.Println(<-c)
	fmt.Println(<-c)
}

func openAndCloseChan() {
	c := make(chan int)
	go proc(c) // 协程的话如果需要再中途做某些的话,因为是共用线程中的资源了,此时一个协程不能既实现读又实现写的功能
	v, ok := <-c
	fmt.Println(ok, v)
}

func proc(c chan int) {
	c <- 1
}

/*
go中的goroutine机制
go关键字用于定义一个goroutine
同一个程序中所有的goroutine共享一个地址空间
goroutine就是一个协程,也被称作轻量级的线程
	Golang在runtime,系统调用等多方面对goroutine调度进行了封装和处理,
	即goroutine不完全是由用户控制,一定程度上由go运行时runtime管理
	当某个goroutine阻塞时,会让出cpu给其他goroutine
*/
func goroutine() {
	go say("hello") // 第一个goroutine
	say("world")    // 第二个goroutine
}

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func errorProcess() {
	fmt.Println("外层开始")
	defer func() {
		fmt.Println("外层准备recover")
		// recover类似于java中的finally
		// 即这里有异常打印异常,没有异常的话正常执行后续的一个元素打印即可
		if err := recover(); err != nil {
			fmt.Printf("%#v-%#v\n", "外层", err) // err已经在上一级的函数中捕获了,这里没有异常,只是例行先执行defer,然后执行后面的代码
		} else {
			fmt.Println("外层没做啥事")
		}
		fmt.Println("外层完成recover")
	}()
	fmt.Println("外层即将异常")
	f()
	fmt.Println("外层异常后")
	defer func() {
		fmt.Println("外层异常后defer")
	}()
}
func f() {
	fmt.Println("内层开始")
	defer func() {
		fmt.Println("内层recover前的defer")
	}()

	defer func() {
		fmt.Println("内层准备recover")
		if err := recover(); err != nil {
			fmt.Printf("%#v-%#v\n", "内层", err) // 这里err就是panic传入的内容
		}

		fmt.Println("内层完成recover")
	}()

	defer func() {
		fmt.Println("内层异常前recover后的defer")
	}()

	panic("异常信息")

	//defer func() {
	//	fmt.Println("内层异常后的defer")
	//}()
	//
	//fmt.Println("内层异常后语句") //recover捕获的一级或者完全不捕获这里开始下面代码不会再执行
}

/*
go的错误处理
*/
func errorMethod() {
	if result, errorMsg := Divide(100, 10); errorMsg == "" {
		fmt.Println("100/10=", result)
	}
	if _, errorMsg := Divide(100, 0); errorMsg != "" {
		fmt.Println("errorMsg is:", errorMsg)
	}
}

type DivideError struct {
	dividee int // 除数
	divider int // 被除数
}

func (de *DivideError) Error() string {
	strFormat := `
	Cannot proceed, the divider is zero.
	dividee: %d
	divider: 0
    `
	return fmt.Sprintf(strFormat, de.dividee)
}

func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
	if varDivider == 0 {
		dData := DivideError{
			dividee: varDividee,
			divider: varDivider,
		}
		errorMsg = dData.Error()
		return 0, errorMsg
	} else {
		return varDividee / varDivider, ""
	}
}

/**
golang的接口相关内容
golang的接口不像java,在java中接口与抽象类差不多,只不过是抽象出所有的公共方法
需要显式的去使用关键字implements来作为类实现接口的声明
在go中,只需要使用new关键字new相应的struct即可,会默认提示你去实现当前的接口方法
或者自己提前实现方法即可
*/
func interfaceMethod() {
	var school School

	school = new(NJUPT)
	fmt.Println(school.getSchoolName())
}

type NJUPT struct {
}

func (N NJUPT) getSchoolName() string {
	fmt.Println("my school is njupt")
	return "南京邮电大学"
}

func (N NJUPT) getStudentNum() int {
	fmt.Println("my student number is 100000")
	return 10000
}

type School interface {
	getSchoolName() string
	getStudentNum() int
}

/*
类型转换
注意:go不支持隐式类型转换,只支持显式类型转化
下面关于int64转成int32需要显式声明转类型
如果是java的话
int a = 32
long b = 64
b = a
此时b就等于32了是不需要强制转换的
*/
func typeTransMethod() {
	var sum int = 17
	var count int = 5
	//var mean float32 // 相当于java中的float,单精度的
	var mean float64 // 相当于java中的double,双精度的

	mean = float64(sum) / float64(count)
	fmt.Printf("mean的值为%v\n", mean)

	var a int64 = 100
	var b int32
	b = int32(a)
	fmt.Printf("b的值为:%v\n", b)
}

/**
递归函数
实现阶乘
*/
func recursion(num int) int {
	if num == 1 {
		return 1
	}
	return num * recursion(num-1)
}

/**
这里需要注意如果使用数组定义的话,当前的数组长度是固定的,且返回的数组也必须是长度固定的
但是如果使用make来操作的话,此时就不需要使用固定长度的数组返回了
*/
func getFibonacci(num int) []int {
	//n := num + 1
	var dp = make([]int, num)
	dp[0] = 1
	dp[1] = 1
	if num <= 1 {
		return dp
	}
	for i := 2; i < num; i++ {
		dp[i] = dp[i-1] + dp[i-2]
	}
	return dp
}

/**
golang中的集合元素,定义Map操作
Map的整体操作
var map_variable map[key_data_type]value_data_type
*/
func mapMethod() {
	map1 := map[int]string{1: "caoduanxi", 2: "wangluyao"}
	for k, v := range map1 {
		fmt.Printf("%d -> %s\n", k, v)
	}
	// 定义
	var countryCapitalMap map[string]string
	// 初始化
	countryCapitalMap = make(map[string]string)

	countryCapitalMap["China"] = "北京"
	countryCapitalMap["France"] = "巴黎"
	countryCapitalMap["American"] = "纽约"

	fmt.Println(countryCapitalMap)

	// 查看元素在集合中是否存在
	capital, ok := countryCapitalMap["American"]
	fmt.Println(ok)
	if ok {
		fmt.Printf("American's captial: %s\n", capital)
	} else {
		fmt.Printf("Americal's captial is not exist!")
	}
	// 删除元素
	delete(countryCapitalMap, "American")
}

/*
golang数据范围方法
range是关键字
*/
func rangeMethod() {
	nums := []int{1, 2, 3, 4, 5}
	sum := 0
	// 这里传入的是index:nums[index],由于不需要使用该元素的符号,所以采用空白符_省略
	for _, num := range nums {
		sum += num
	}
	fmt.Printf("sum: %d\n", sum)
	for i, num := range nums {
		if num == 3 {
			fmt.Println("index:", i)
		}
	}

	mapKv := map[string]string{"a": "caoduanxi", "b": "wangluyao"}
	for k, v := range mapKv {
		fmt.Printf("%s -> %s\n", k, v)
	}
}

/**
切片方法
对数组的抽象,Go数组的长度不可变,在特定场景中不太适用,Go提供了一种灵活,功能强悍的内置类型切片,动态数组
这种可以实现数组的动态增加
*/
func sliceMethod() {
	// make([]int, len, cap)
	var numbers = make([]int, 3, 5)
	printSlice(numbers)
	// 这里相当于重新赋值了
	numbers = []int{1, 2, 3, 4, 5}
	printSlice(numbers)
	// 其实这里表示的是2->5之间的cap即从cap中可以看到当前的元素是否还可以存元素
	// len = 2 cap = 3 slice = [3 4] => 表示当前3 4还可以被拼接一个元素
	printSlice(numbers[2:4])
	printSlice(numbers[2:5])
	printSlice(numbers[1:5])
	numbers = append(numbers, 10, 11, 12)
	printSlice(numbers)
	numbers1 := make([]int, len(numbers), (cap(numbers))*2)
	// 用于参数的赋值
	copy(numbers1, numbers)
	printSlice(numbers1)

}
func printSlice(x []int) {
	fmt.Printf("len = %d cap = %d slice = %v\n", len(x), cap(x), x)
}

/**
测试结构体的方法
golang是结构化的语言
*/
type Student struct {
	name    string
	age     int
	address string
}

/**
结构体指针
*/
func structMethod() {
	var student Student

	student.name = "曹端喜"
	student.age = 24
	student.address = "广东省深圳市南山区"

	fmt.Println(student)
}

/**
指针的指针的方法
*/
func ptrPtrMethod() {
	var a int
	var ptr *int
	var pptr **int

	a = 1

	ptr = &a

	pptr = &ptr

	fmt.Printf("变量a的地址: %d\n", &a)
	fmt.Printf("变量a的值: %d\n", a)
	fmt.Printf("指向a的指针的地址: %d\n", &ptr)
	fmt.Printf("指向a的指针的值: %d\n", *ptr)
	fmt.Printf("指向a的指针的指针的地址: %d\n", &pptr)
	fmt.Printf("指向a的指针的指针的值: %d\n", **pptr)

}

const MAX int = 10

func ptrMethod() {
	var ptr *int
	fmt.Printf("当前ptr指针的值: %d\n", ptr)
	// %t表示true or false => bool
	fmt.Printf("当前ptr是否为空指针: %t\n", ptr == nil)

	var arr [10]int
	for i := 0; i < 10; i++ {
		arr[i] = i + 10
	}
	for j := 0; j < 10; j++ {
		fmt.Printf("Element[%d] = %d\n", j, arr[j])
	}
	// 定义数组指针类型
	var ptrArr [MAX]*int
	for i := 0; i < MAX; i++ {
		ptrArr[i] = &arr[i]
	}
	for i := 0; i < MAX; i++ {
		fmt.Printf("ptrArr[%d] = %d\n", i, *ptrArr[i])
	}

}

/**
数组方法:
主要是针对一维数组和多维数组来实现的
*/
func arrMethod() {
	var arr [10]int
	for i := 0; i < 10; i++ {
		arr[i] = i + 10
	}
	for j := 0; j < 10; j++ {
		fmt.Printf("Element[%d] = %d\n", j, arr[j])
	}
	// 这种属于可以自动识别当前的数组的元素长度
	var arr2 = [...]int{1, 2, 3, 454365, 75, 676}
	var len2 = len(arr2)
	for k := 0; k < len2; k++ {
		fmt.Printf("arr2[%d] = %d\n", k, arr2[k])
	}
	// 多维数组
	var arr3 = [2][3]int{{1, 2, 3}, {4, 5, 6}}
	var len31 = len(arr3)
	var len32 = len(arr3[0])
	for k := 0; k < len31; k++ {
		for l := 0; l < len32; l++ {
			fmt.Printf("arr3[%d][%d] = %d\n", k, l, arr3[k][l])
		}
	}
}
func setValue(c *int) {
	*c = 200
}

/**
引用传递
*/
func ptrSwap(a *int, b *int) {
	fmt.Println(a)
	var temp = *a
	*a = *b
	*b = temp
}

func swap(a int, b int) {
	fmt.Println("值交换的a地址: ", &a)
	var temp = a
	a = b
	b = temp
}
func gotoMethod() {
	var a = 10
LABEL:
	for a < 20 {
		if a == 15 {
			a += 1
			goto LABEL
		}
		fmt.Printf("a's value is: %d\n", a)
		a++
	}
}

/**
循环方法
*/
func loopMethod() {
	var i int
	// 第一种形式:经典的遍历形式
	//for i = 1; i < 10; i++ {
	//	fmt.Println(i)
	//}
	// 类似于while形式的循环
	for i < 10 {
		fmt.Println(i)
		i++
	}
}

func selectMethod() {
	var c1, c2, c3 chan int
	var i1, i2 int
	select {
	case i1 = <-c1:
		fmt.Printf("received", i1, " from c1\n")
	case c2 <- i2:
		fmt.Printf("sent", i2, " to c2\n")
	case i3, ok := <-c3:
		if ok {
			fmt.Printf("received", i3, " from c3\n")
		} else {
			fmt.Printf(" c3 is closed\n")
		}
	default:
		fmt.Printf("no communication\n")

	}
}
func switchMethod() {
	var grade string = "B"
	var marks int
	// 给当前的地址注入一个值
	fmt.Scanln(&marks)

	switch marks {
	case 90:
		grade = "A"
	case 80:
		grade = "B"
	case 50, 60, 70:
		grade = "C"
	default:
		grade = "D"
	}

	switch {
	case grade == "A":
		fmt.Printf("优秀!\n")
	case grade == "B", grade == "C":
		fmt.Printf("良好\n")
	case grade == "D":
		fmt.Printf("及格\n")
	case grade == "F":
		fmt.Printf("不及格\n")
	default:
		fmt.Printf("差\n")
	}
	fmt.Printf("你的等级是 %s\n", grade)
}

/**
采用的是方法第一位与第二位有差距
*/
func getBol(a int, b int) bool {
	if a < b {
		return true
	} else {
		return false
	}
}

/**
注意在go语言中,也使用了&符号和*符号
这些符号的作用就是&符号是获取当前的变量的地址
而*符号则表明当前变量是一个指针
指针变量保存的是一个地址值,会分配独立的内存在存储一个整型数字
*/

/**
声明方法
*/
func add(int, int) int {
	x, y := 1, 2
	return x + y
}

Keep thinking! Keep coding!

加油,前进的路从来不是一帆风顺的,遇到坎坷便是成功的冲锋号!加油! 2020-8-27 16:02:15 写于深圳

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值