Go 语言学习笔记
一、如何以及为什么要配置Go语言环境变量?
为了系统能在任何目录下都可以执行 go 指令,go 的指令在 go/bin 目录下。
命名 | 内容 |
---|---|
GOROOT | Go 的安装目录 |
Path | 指定 bin 目录,为了能找到 go 指令 |
GOPATH | 项目的工作路径. Java 没有这个 |
Mac 下配置环境变量方法
Vim .bash_profile 打开文件
i 进入编辑
esc 退出编辑
:wq 保存并退出
$PATH:user $ 代表引用 path 路径,: 代表追加。
go env 进入 go 环境配置
go env -w GOOS=“linux” 修改 go 环境配置
二、变量
- 无 char 类型,使用 byte 保存单个字符,byte 与 uint8 等价
- 字符串 string 划分到基本数据类型中
- int8 有符号整数,0正 1负。 全 0 出现 +0,-0,规定 -0 为-128
- 浮点数有可能精度损失,原因是尾数可能丢失
- 传统的字符串由字符组成,而 Go 的字符串由字节组成
- 字符串一旦赋值了,在 Go 中是不可改变的
- bool 占用 1 个字节,不可以用 0 或 非0 替代 false 和 true
- Go语言统一使用 utf-8 编码,英文字母占 1 个字节,汉字占 3 个字节
- byte类似uint8,rune类似int32,int与uint一样大小(取决于系统),uintptr无符号整型,存放指针
1.类型自动推倒
代码如下(示例):
a := 10 // a 为 int
b := 20.2 // b 为 float64
var c = 10 // 省略数据类型,数据类型自动推倒,c 为int
2.byte
var a byte = 'a'
fmt.Println(a) // 输出 97
fmt.Printf("%c",a) // 输出 a
var b = 'a' + 10 // b = 107
3.类型转换
数据类型转换不能自动转换
var a float32 = 10 // 错误
var b float32 = float32(10)
- 基本数据类型转 string
//建议使用第一种方式
var a int = 10
var b = fmt.Sprintf("%d", a). //%d 代表以十进制输出的字符串,%t bool 类型输出,以此类推
fmt.Printf("%T,%v", b, b) //%T 值的类型,%v 值的默认格式输出
//第二种方式
var a int = 99
var b = strconv.FormatInt(int64(a),10) //表示十进制
- string 转基本数据类型
var str = "10"
var a, _ = strconv.ParseInt(str, 10, 64) //十进制输出,int64 无溢出类型,_ 是将返回值忽略
fmt.Println(a)
4.指针
1.基本数据类型变量存的是值,也称为值类型,指针类型变量存的是值的地址。
2.值类型变量直接存储值,栈中分配。引用类型变量存储地址,在堆中分配,当没有变量引用这个地址时,会成为垃圾被回收。
var i int = 10
//p 是一个指针变量
//p 的类型是 *int
//p 本身的值是&i
//*p 是取出 p 所指向的值
var p *int = &i
三、循环控制语句
1.for 循环
//第一种写法
for i := 0; i < 10; i++ {
fmt.Println("学习 Go 语言")
}
//第二种写法
j := 5
for j < 10 { //只写循环条件
fmt.Println("学习 Go 语言!")
j++
}
//第三种写法,无限循环,通常配合 break 使用
for { //等价 for;;{}
if j < 15 {
fmt.Println("学习 Go 语言!!")
} else {
break
}
j++
}
//以字节为单位遍历字符串,一个汉字占三个字节所以会乱码
var str2 string = "hello go 北京"
for i := 0; i < len(str2); i++ {
fmt.Printf("%c\n", str2[i])
}
//以字符单位遍历字符串,含有汉字不会乱码
for index, val := range str2 {
fmt.Printf("%d %c\n", index, val)
}
四、函数
1.在import时,路径从$GOPATH/src下开始,所以按照规范在src文件夹下创建工程
2.在调用一个函数时,会给函数分配一个新的内存空间,编译器会将新的内存空间与其他栈空间区分,函数执行完毕后,程序会销毁这个函数对应的空间
3.基本数据类型和数组默认是值传递,即进行值拷贝。也可以传入变量的地址,类似于引用。
4.当执行 defer 时,暂时不执行,会将 defer 后面的语句包括相关的值会拷贝压入独立的 defer 栈,最后执行
type mySum func(int int) int //相当于一个别名
var Age int = 20 //ok
Name := "tom" //错误,var Name string Name = "tom" 赋值语句不能在函数体外
func main(){
a := getSum //函数也是一种数据类型,可赋值给变量
res := a(10, 30)
res2 := myFun(getSum, 20, 40) //函数作为形参
fmt.Printf("a 的类型%T,getSum的类型是%T\n", getSum, a) //类型:func(int int)int
fmt.Println("求和的结果是", res, res2)
//闭包
f := addUpper() //返回一个匿名函数,类型func (int) int,与函数外n构成闭包
fmt.Println(f(1)) //闭包类似于一个类,n 类似字段
fmt.Println(f(2)) //反复调用f,n 只初始化一次,关键是分析函数与哪些变量构成闭包
}
func getSum(a int, b int) int {
return a + b
}
//函数可以是一种数据类型,因此函数可以作为形参并调用
func myFun(funvar func(int, int) int, num1 int, num2 int) int {
return funvar(num1, num2)
}
//支持对函数返回值命名
func getSumAndSub(a int, b int) (sum int, sub int) { //自动创建好了返回值sum和sub
sum = a + b // := ----> =
sub = a - b
return
}
//定义匿名函数(没有函数名的函数)直接调用
res3 := func (a int,b int) int {
return a + b
}(10,20) //()代表调用
//闭包
func addUpper() func(int) int {
var n = 10
return func(i int) int {
n = n + i
return n
}
}
//init 函数通常完成初始化工作
//main.go、utils.go 都含有init函数和变量定义执行顺序是什么?
//main.go引用utils.go ,先执行utils.go中变量定义,init函数,再执行main.go中变量定义,init(),main()
1.递归
斐波那契数列1,1,2,3,5,8,13
求第 n 个整数的斐波那契数是多少
func fbn(n int) int {
if n == 1 || n == 2 {
return 1
} else {
return fbn(n-1) + fbn(n-2)
}
}
2.字符串函数应用
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
var str = "Hello"
var strLong = "hello,world,ok,go"
fmt.Println("字符串长度", len(str)) //按字节返回长度
n, _ := strconv.Atoi("123") //字符串转整数
str2 := strconv.Itoa(123) //整数转字符串
var bytes = []byte(str) //字符串转byte,比如写入文件
str3 := string([]byte{97, 98, 99}) //[]byte转string
b := strings.Contains(str, "o") //查找字串是否在指定字符串中
num := strings.Count(str, "l") //查找字符串中有几个指定的字串
b1 := strings.EqualFold(str, "HELLO") //区分字符串是否相等,不包含大小写
index := strings.Index(str, "l") //返回子串在字符串第一次出现的index
index1 := strings.LastIndex(str, "l") //返回字串在字符串最后一次出现的index
str4 := strings.Replace(str, "l", "o", -1) //指定字串替换另外一个字串,-1全部替换
str5 := strings.Split(strLong, ",") //按照指定字符对字符串进行分割,将会拆分成字符串数组
str6 := strings.ToLower(str) //所有字符串中的字母小写
str7 := strings.ToUpper(str) //所有字串串中的字母大写
str8 := strings.TrimSpace(" hello go ") //去掉字符串两边的空格
str9 := strings.Trim("! hello ! ", " !") //去掉字符串两边指定的字符
b2 := strings.HasPrefix("http:", "http") //判断字符串是否以指定字符开头
b3 := strings.HasSuffix("a.jpg", "jpg") //判断字符串是否以指定字符结尾
fmt.Println("n =", n)
fmt.Printf("%v\n", str2)
fmt.Printf("%v\n", bytes) //对应字符编码[104 101 108 108 111]
fmt.Printf("%v\n", str3) //输出abc
fmt.Printf("%v\n", b) //true
fmt.Printf("%v\n", num) //2
fmt.Printf("%v\n", b1) //true
fmt.Printf("%v\n", index) //从 0 开始算,输出2
fmt.Printf("%v\n", index1) //从 0 开始算,输出3
fmt.Printf("%v\n", str4) //heooo 全部替换 n==1 heolo,str本身不发生变化
fmt.Printf("%v\n", str5) //[hello world ok go]
fmt.Printf("%v\n", str6) //hello
fmt.Printf("%v\n", str7) //HELLO
fmt.Printf("%v\n", str8) //"hello go"
fmt.Printf("%v\n", str9) //"hello"
fmt.Printf("%v\n", b2) //true
fmt.Printf("%v\n", b3) //true
}
3.时间函数应用
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now() //获取当前时间
fmt.Printf("now=%v,type=%T\n", now, now) //type=time.Time(提供相关方法)
//获取当前时间年月日、时分秒
fmt.Printf("年=%v\n", now.Year())
fmt.Printf("月=%v\n", int(now.Month()))
fmt.Printf("日=%v\n", now.Day())
fmt.Printf("时=%v\n", now.Hour())
fmt.Printf("分=%v\n", now.Minute())
fmt.Printf("秒=%v\n", now.Second())
//格式化时间
fmt.Printf("%d-%d-%d %d:%d:%d\n", now.Year(), now.Month(), now.Day(),
now.Hour(), now.Minute(), now.Second())
str := fmt.Sprintf("%d-%d-%d %d:%d:%d\n", now.Year(), now.Month(), now.Day(),
now.Hour(), now.Minute(), now.Second())
fmt.Println(str) //2022-6-20 17:32:30
fmt.Printf(now.Format("2006-01-02 15:04:05")) //数字必须固定,格式可以改变
fmt.Println()
fmt.Printf(now.Format("01")) //数字必须固定,例要指定月份
fmt.Println()
//时间常量,go 语言内置时间常量应用
i := 0
for {
i++
//time.Sleep(time.Second) //时间常量1s
time.Sleep(time.Millisecond * 100) //100ms
fmt.Println(i)
if i == 5 {
break
}
}
//获取时间戳
fmt.Printf("%v\n", now.Unix()) //1970.7.1到现在的秒
fmt.Printf("%v\n", now.UnixNano()) //1970.7.1到现在的纳秒
}
4.内置函数
package main
import "fmt"
func main() {
num := 10
fmt.Printf("num的类型%T,num的地址%d,num的值%d\n", num, &num, num)
//new 用来分配内存,主要分配值类型,比如int,float32
num1 := new(int)
//num1的类型 *int 指针类型
//num1的地址 &num1 地址值
//num1的值 num1 地址值
//num1执指向的值 *num1 默认0
fmt.Printf("num1的类型%T,num1的地址%d,num1的值%d\n", num1, &num1, num1)
}
5.捕获并处理函数异常
package main
import (
"errors"
"fmt"
)
func main() {
test()
fmt.Println("继续执行")
test1()
fmt.Println("自定义错误后续")
}
func test() int {
//通过defer + recover来捕获和处理异常
defer func() {
error := recover() //recover()内置函数,捕获异常
if error != nil { //说明捕获到错误
fmt.Println("error", error)
}
}()
num := 10
num1 := 0
return num / num1
}
func test1() {
err := readConf("conf.ini")
if err != nil {
panic(err) //输出自定义错误,并终止程序
}
}
func readConf(name string) (err error) {
if name == "config.ini" {
//读取文件...
return nil
} else {
//返回一个自定义错误
return errors.New("读取文件错误...")
}
}
四、注意事项
1.变量名的首字母大写代表是公有的,首字母小写代表是私有的。
2.var a int = i++ //错误,i++ 必须单独使用
3.短路与和短路或
4.不支持三元运算符
5.如果内循环有break,它默认跳出最近的for循环,break 后面可以指定标签,跳出对应的for循环
6.在import时,路径从$GOPATH/src下开始,所以按照规范在src文件夹下创建工程
7.在调用一个函数时,会给函数分配一个新的内存空间,编译器会将新的内存空间与其他栈空间区分,函数执行完毕后,程序会销毁这个函数对应的空间