go基礎學習
壞境
安裝SDK
windows 安裝
可以直接下載安裝msi 不需要配置壞境變量默認配置好了
zip 里面包含源碼,但是需要配置壞境變量
liunx安裝SDK
wget https://dl.google.com/go/go1.20.2.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz
cd /usr/local
cd go # 發現里面右go目錄
## 可以看到結果
bin/go version #
配置 GOROOT 和 PATH壞境變量
GOROOT: export GOROOT=/usr/local/go
PATH : export PATH=$PATH:$GOROOT/bin:$GOBIN
vim /root/.profile 最后加入到 etc/.profile
將2個環境變量加入到最下面
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin:$GOBIN
刷新壞境變量
source /etc/profile
root source /root/.profile
第一次使用問題
go mod init 之類的錯誤 可以執行下命令
go env -w GO111MODULE=off
壞境變量配置
- GOROOT 指定sdk 路徑 新建系統壞境變量 GOROOT = go的安裝目錄C:\Program Files\Go
- Path 添加sdk bin目錄 修改PATH ;%GOROOT%/bin
- GOPATH 工作目錄
Path 環境變量添加只需要 引用 sdk的變量 %sdk變量%/bin
常量
簡單常量
const Pi=3.1415926
const abc="abc"
聲明賦值常量
const beef, two, sundsewew = "eat", 2, "veg"
const (monday,tuesday,wenday,t=1,2,3,4);
用作枚舉常量
const (
Unknown = 0
Female = 1
Male = 2
)
枚舉簡寫
iota用法
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //獨立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢復計數
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
iota,特殊常量,可以認為是一個可以被編譯器修改的常量。
iota 是0 ,后面都會依次,0,1,2,簡單來說每次遇見const iota 都會重置0
const (
a=iota
b
c
)
某個類型作為枚舉常量的類型
type Color int
const (
RED Color = iota // 0
ORANGE // 1
YELLOW // 2
GREEN // ..
BLUE
INDIGO
VIOLET // 6
)
變量
變量聲明了一般必須使用
小寫開頭的變量外部包是沒法訪問的(私有的),只有首字母大寫才可以訪問(公開的)
var a int
var b bool
var c string
var (a1 int,b1 bool,c1 string)
當變量聲明后就會有默認值 int 0,float 0.0 bool false string 為空字符串, 指針為 nil, 所有的內存在go中都需要經過初始化, 如果一個變量在函數體外定義,則它就是個全局變量,函數體內聲明局部變量 簡寫 a:=1 不支持函數外部定義
var goos string = runtime.GOOS
fmt.Printf("The operating system is: %s\n", goos)
// path 局部變量
path:= os.Getenv("PATH")
fmt.Printf("Path is %s\n", path)
// 局部變量和全局變量 名稱相同, 編譯器會采用就近原則
值類型
- 內存地址都是以16進制表示
- int、float、bool、string 都是值類型,使用這些類型的變量直接指向內存中的值,像數組復合類型也屬于值類型
- i=j 是將i的值拷貝了一分給了j
- 通過&i 可以獲取到內存地址,值類型變量的值都存在棧中
- 引用類型,變量存的是一個地址,
- 內存的地址被稱為指針,
- 指針指向的內存都是連續的,這也是計算效率最好的一種存儲形式。每個字節指向了下一個字節的地址
- 引用類型賦值,只是地址拷貝
字符串
- 不可變:一旦賦值就不能再修改了
- 表示方式
- 雙引號 :一般常規的字符串,特殊字符串需要轉移
- 反引號 : `` 直接原分不動的輸出
- 拼接方式:
- 直接用+ 拼接,或者+= 適用于短的字符串拼接
- 多行拼接 可以換行,換行后,需要將+放在上一行
解釋字符串
- \n:換行符
- \r:回車符
- \t:tab 鍵
- \u 或 \U:Unicode 字符
- \:反斜杠自身
非解釋字符串
字符串就是一串固定長度的字符連接起來的字符序列。Go 的字符串是由單個字節連接起來的。Go 語言的字符串的字節使用 UTF-8 編碼標識 Unicode 文本。
打印
通用
%v 值的默認格式表示
%+v 類似%v,但輸出結構體時會添加字段名
%#v 值的Go語法表示
%T 值的類型的Go語法表示
%% 百分號
整數
%b 表示為二進制
%c 該值對應的unicode碼值
%d 表示為十進制
%o 表示為八進制
%q 該值對應的單引號括起來的go語法字符字面值,必要時會采用安全的轉義表示
%x 表示為十六進制,使用a-f
%X 表示為十六進制,使用A-F
%U 表示為Unicode格式:U+1234,等價于"U+%04X"
布爾
%t 單詞true或false
浮點數與復數的兩個組分
%b 無小數部分、二進制指數的科學計數法,如-123456p-78;參見strconv.FormatFloat
%e 科學計數法,如-1234.456e+78
%E 科學計數法,如-1234.456E+78
%f 有小數部分但無指數部分,如123.456
%F 等價于%f
%g 根據實際情況采用%e或%f格式(以獲得更簡潔、準確的輸出)
%G 根據實際情況采用%E或%F格式(以獲得更簡潔、準確的輸出)
字符串和[]byte
%s 直接輸出字符串或者[]byte
%q 該值對應的雙引號括起來的go語法字符串字面值,必要時會采用安全的轉義表示
%x 每個字節用兩字符十六進制數表示(使用a-f)
%X 每個字節用兩字符十六進制數表示(使用A-F)
指針:
%p 表示為十六進制,并加上前導的0x
類型與運算
整數與浮點數
go沒有float 和doule 類型,只有 float32 和 float64
-
整數:
- int8(-128 -> 127)
- int16(-32768 -> 32767)
- int32(-2,147,483,648 -> 2,147,483,647)
- int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
-
無符號整型
- uint8(0 -> 255)
- uint16(0 -> 65,535)
- uint32(0 -> 4,294,967,295)
- uint64(0 -> 18,446,744,073,709,551,615)
-
浮點型(IEEE-754 標準):
- float32(+- 1e-45 -> +- 3.4 * 1e38)
- float64(+- 5 1e-324 -> 107 1e308)
整數是計算最快的一種類型
類型轉換
Sprint
package main
import "fmt"
func main() {
var num1 int =99
var num2 float64 =23.5566
var b bool =true
var str string
//Sprintf根據format參數生成格式化的字符串并返回該字符串。
str=fmt.Sprintf("%d",num1)
//%T 打印類型
fmt.Printf("類型 %T str=%v \n",str,str)
// %f 有小數部分但無指數部分,如123.456
str=fmt.Sprintf("%f",num2)
fmt.Printf("類型 %T str=%v \n",str,str)
//%t 單詞true或false
str=fmt.Sprintf("%t",b)
fmt.Printf("類型 %T str=%v \n",str,str)
}
strconv
package main
import "fmt"
import "strconv"
func main() {
var num1 int =99
var num2 float64 =23.5566
var b bool =true
var str string
str=strconv.FormatInt(int64(num1),10)
fmt.Printf("類型 %T str=%q \n",str,str)
// f 格式,10 保留位數,64 是float64
str=strconv.FormatFloat(num2,'f',10,64)
fmt.Printf("類型 %T str=%q \n",str,str)
str=strconv.FormatBool(b)
fmt.Printf("類型 %T str=%v \n",str,str)
}
Parse
他一般有 ParseBool ParseInt ParseFloat 等
返回2個值 接受方式用 n1,_=
指針
- 獲取變量地址用&
var i int=10 fmt.Println("i的地址是",&i) ``
package main
import "fmt"
// import "strconv"
func main() {
var i int=10
fmt.Println("i的地址是",&i)
//p是一個指針變量, 類型為*int p本身值是 &i
// p 存儲了一個地址 0xc00000e0a8 就是i的地址,這個地址指向了i的空間
var p *int =&i
fmt.Printf("p=%v\n",p)
fmt.Printf("p的地址=%v",&p)
// 通過p 取出 i的值 10
fmt.Printf("p指向的值是=%v \n",*p)
// 修改了i的值
*p=20
fmt.Printf("p指向的值是=%v \n",*p)
fmt.Println(i)
}
獲取用戶的輸入
fmt.Scanln()
package main
import "fmt"
// import "strconv"
func main() {
var name string
var age byte
var sal float32
fmt.Println("請輸入姓名")
fmt.Scanln(&name)
fmt.Println("請輸入年紀")
fmt.Scanln(&age);
fmt.Println("請輸入薪水")
fmt.Scanln(&sal);
fmt.Printf("姓名%v \n 年紀%v \n 薪水%v \n ",name,age,sal );
}
fmt.Scanf()
package main
import "fmt"
// import "strconv"
func main() {
var name string
var age byte
var sal float32
fmt.Println("請依次輸入,姓名,年紀 薪水,用空格隔開")
//接受數據的類型可以參考文檔
fmt.Scanf("%s %d %f",&name,&age,&sal)
fmt.Printf("姓名:%v 年齡:%v 薪資:%v ",name,age,sal)
}
運算
除法運算
package main
import "fmt"
func main() {
var n1 float32 =10/4
//這里 因為都是整數相除的,那么除后會去掉小數部分,保留整數部分,
fmt.Println(n1) // 結果是2
//如果要保留小數需要 10.0/4
var n2 float32 =10.0/4
fmt.Println(n2) // 結果是2
}
求模
公式 a%b =a-a/b*b
fmt.Println("10%3=",10%3," 10-10/3*3=",10-10/3*3) //1
fmt.Println("-10%3=",-10%3," -10-(-10)/3*3=",-10-(-10)/3*3) //-1
fmt.Println("10%-3=",10%-3," 10-10/-3*-3=",10-10/-3*-3) //1
流程控制
if else 語句
go 語言中,大括號,有時候是不能換行的,換行就會報錯
go 中的if 一般是不需要加括號的,除非是,if(a>0 && a<10) || a==90 其實這里的括號也不是給if的加的,是給條件加的
package main
import "fmt"
func main() {
var age int
fmt.Println("請輸入年齡")
fmt.Scanln(&age)
if age>18 && age<=60 { // 這個大括號不能換行的,換行就會報錯的
fmt.Println("成年人")
}else if age>60{
fmt.Println("老年人");
}else{
fmt.Println("未成年人");
}
}
swich
swich 語句后面是不需要加 break的
case :后面可是常量,也可以是個函數,也可以運算
package main
import "fmt"
func main() {
var key byte
fmt.Println("請輸入字符串a,b,c,d,e");
fmt.Scanf("%c",&key);
switch key {
case 'a':
fmt.Println("======>a");
case 'b':
fmt.Println("======>b");
case 'c':
fmt.Println("======>c");
case 'd':
fmt.Println("======>d");
case 'e':
fmt.Println("======>e");
case 'f','g','h':
fmt.Println("======>其他輸入");
default:
fmt.Println("輸入有誤");
}
}
package main
import "fmt"
func main() {
var age int =10
switch { //也可以 switch age:=10;{}
case age==10:
fmt.Println("10")
case age>10:
fmt.Println("大于10")
}
}
switch 穿透 fallthrought
package main
import "fmt"
func main() {
var age int =10
switch age {
case 10:
fmt.Println("10")
fallthrough //默認只能穿透一層
case 20:
fmt.Println("20")
fallthrough //默認只能穿透一層
case 30:
fmt.Println("30")
case 40:
fmt.Println("40")
}
}
循環
for
例子
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Println("您好!",i)
}
}
格式
循環初始,循環條件,循環迭代
順序
- 初始化i=0
- 條件 i<10
- 如果為真,執行fmt.Println
- 執行迭代,i++
- 反復執行,直到條件為假 退出
for 第二種寫法
和第一種寫法其實一樣
package main
import "fmt"
func main() {
i:=0
for i < 10 {
fmt.Println("您好!",i)
i++
}
}
for 第三種寫法
package main
import "fmt"
func main() {
// 配合break 用法
for {
fmt.Println("您好!",i)
}
}
字符串遍歷
func main() {
var str string="hello world!"
//str2=[]rune(str) // 將下面的str 改成str2 這個就是切片
for i := 0; i < len(str); i++ {
fmt.Printf("%c \n",str[i]);
}
}
如果 str字符串包含中文,就會出問題,亂碼,
傳統對字符串的便利是按照字字節來遍歷的,而一個漢字再utf8 中包含3個字節
解決的方法,需要將,str轉換成切片, 或者用 for-range
for-range
func main() {
var str string="hello world!"
for i := 0; i < len(str); i++ {
fmt.Printf("%c \n",str[i]);
}
str="abcd西安"
for index, val := range str {
fmt.Printf("index=%d, val=%c \n",index,val)
}
}
結果

循環案例
打印金字塔
// 打印金字塔
// * 1-> 1個* i+(i-1) -> 2i-1 空格 2 3-1 空格數=n-層
// *** 2-> 3個* 2*2-1=3 1 3-2
// ***** 3-> 5個* 2*3-1=5 0 3-3
func main() {
var count int =30
for i := 1; i <= count; i++ {
// 打印空格
for k := 0; k < count-i; k++ {
fmt.Print(" ");
}
//打印星星
xx:=2*i-1
for j := 1; j <= xx; j++ {
//fmt.Print("*"); // 實心金字塔
if j==1 || j==xx { // 空心金字塔
fmt.Print("*"); //第一個和最后一個打印星星
}else{
if i==count{ // 如果是最后一行
fmt.Print("*");
}else{
fmt.Print(" ");
}
}
}
fmt.Println(); // 換行
}
}
九九乘法表
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf(" %d x %d = %d",j,i, i*j);
}
fmt.Println()
}
}
隨機數
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var count int =0
for {
//生成100次 退出
if count==100 {
break
}
//t:=time.Now().Unix() // 返回當前的時間戳
//fmt.Println(t)
rand.Seed(time.Now().UnixNano())// 納秒 以當前時間作為隨機種子 重復的幾率還是很大
n:=rand.Intn(100)+1 // 生產1到100 的隨機數
fmt.Println(n)
count++
}
}
break continue
和C#里面的差不多
break 直接跳出循環,結束了
continue 跳出當次循環,然后進行下一次,
goto
package main
import (
"fmt"
)
func main() {
var n int =4
fmt.Println("1")
fmt.Println("2")
if(n>3){
goto Lable
}
fmt.Println("3")
fmt.Println("4")
fmt.Println("5")
Lable:
fmt.Println("6")
fmt.Println("7")
}
return
結束方法 函數。
函數
實例
package main
import (
"fmt"
)
func main() {
result:=cal(2.3,2.7,'+')
fmt.Println("result=",result)
}
func cal(n1 float64,n2 float64,o byte ) float64{
switch o {
case '+':
return n1+n2
case '-':
return n1-n2
case '*':
return n1*n2
case '/':
return n1/n2
default:
fmt.Println("不支持的計算")
return -99999
}
}
返回多個參數的函數
package main
import (
"fmt"
)
func main() {
fmt.Println("返回多個參數的");
// res1,res2:=A(2,3)
// fmt.Println("sum=",res1)
// fmt.Println("ss=",res2)
_, res2:=A(2,3)
fmt.Println("ss=",res2)
}
func A(a int,b int)(int,int){
sum:=a+b
ss:=a*b
return sum ,ss
}
函數的引用傳值方式
上面的函數傳值 都是以值傳值的方式,通過拷貝的方式
package main
import (
"fmt"
)
func main() {
a:=4
A(&a) // 直接傳了a的地址
fmt.Println("main a=",a);
}
// a的指針
func A(a *int ) {
*a++ // 根據地址找到修改了
fmt.Println("A() a=",*a);
}
將函數當作參數傳遞
package main
import (
"fmt"
)
func main() {
s:=SS(sum,20,40)
fmt.Println("s=",s);
}
func SS(dd func(int,int)int, a int,b int) int{
return dd(a,b)
}
func sum(a int,b int ) int {
return a+b
}
自定義類型
我們可以給類型取別名
demo1
package main
import (
"fmt"
)
func main() {
type myInt int
var n1 myInt
var n2 int
// n1 和n2 是不同的類型
n1=100
//n2=n1 // 報錯的, 不同的類型,除非強制轉換
n2=int(n1)
fmt.Println("n1=",n1,"n2=",n2);
}
demo2
package main
import (
"fmt"
)
type my func(int,int)int
func main() {
c:=SS(sum,100,200)
fmt.Println(c);
}
func SS(dd my, a int,b int) int{
return dd(a,b)
}
func sum(a int,b int ) int {
return a+b
}
demo3
package main
import (
"fmt"
)
func main() {
s,ss:=count(2,3)
fmt.Println("sum=",s,"ss=",ss);
}
func count(a int,b int)(sum int,ss int){
sum=a+b
ss=a*b
return
}
可變參數
package main
import (
"fmt"
)
func main() {
n:=sum(3,4,10)
fmt.Println("sum=",n);
}
func sum(n1 int,args... int)int{
sum:=n1
for i := 0; i < len(args); i++ {
sum+=args[i]
}
return sum
}
init 函數
介紹
- 每個源文件中都可以包含一個init函數,該函數會在main之前執行,被go的框架調用
- 通??梢栽谒锩孀鰅初始化操作
注意
- 如果一個文件同時 包含全局變量,init 函數,main 函數,則執行流程是,全局變量->init->main
package main
import (
"fmt"
)
var a =test()
func main() {
fmt.Println("main")
}
func init (){
fmt.Println("init")
}
func test()int{
fmt.Println("test")
return 100
}
結果為
test
init
main
2. init 函數主要的作用,完成一些初始化工作
匿名函數
一般只使用一次的函數
package main
import (
"fmt"
)
func main() {
a:=func(n1 int,n2 int)int{
return n1+n2
}(10,20)
fmt.Println(a);
}
全局變量
package main
import (
"fmt"
)
var (
F1=func(n1 int ,n2 int) int{
return n1*n2
}
)
func main() {
a:=func(n1 int,n2 int)int{
return n1+n2
}(10,20)
fmt.Println(a);
fmt.Printf("全局匿名函數 %d",F1(3,4))
}
閉包
閉包就是一個函數和其他相關的引用環境組合的一個整體
package main
import (
"fmt"
)
func main() {
f:=AddUpper()
fmt.Println(f(1)) //1
fmt.Println(f(2)) //13
}
func AddUpper() func(int)int{
var n int =10
return func(x int)int{
n=n+x
return n
}
}
返回 11 13
上面的代碼拆分就是
package main
import (
"fmt"
)
func main() {
var n int=10;
f:=func(x int)int{ //f:=AddUpper()
n=n+x
return n
}
fmt.Println(f(1)) //11
fmt.Println(f(2)) //13
}
案例
根據傳入的文件名稱,如果沒后綴就加上,有就返回
package main
import (
"fmt"
"strings"
)
func main() {
f:=makeSuffix(".md")
fmt.Println("文件是",f("123"))
fmt.Println("文件是",f("123.md"))
}
func makeSuffix(suffix string )func (string) string{
//suffix 外部傳入的 和在這里生命一個差不多
return func(name string) string{
//if name 沒有后綴就加上,否則就直接返回
if !strings.HasSuffix(name,suffix) {
return name+suffix
}
return name
}
}
說白了,閉包就是可以保存上一次的引用
defer 函數
在函數中,我們經常需要創建資源 比如(數據庫連接,文件句柄,鎖等)為了在函數執行完畢后,能及時釋放資源,go的設計者 提出了defer (延時機制)
package main
import (
"fmt"
)
func main() {
res := sum(20,10)
fmt.Println("res=",res) //4 32
}
func sum(n1 int,n2 int)int{
// 當執行defer的時候,暫時不執行,會將defer后面的語句進行壓棧,當函數執行完畢后,在從derfer按先入后出的方式出棧執行
// 在defer 將語句放入到棧時,也會將相關的值拷貝同時進入棧,
// 資源釋放一般都會這弄
defer fmt.Println("ok1",n1) //3
defer fmt.Println("ok2",n2)//2
n1++ //21
n2++ //11
res:=n1+n2 // 32
fmt.Println("ok3 res=",res)// 1,32
return res
}
結果
ok3 res= 32
ok2 10
ok1 20
res= 32
函數遞歸
實例代碼
package main
import (
"fmt"
)
func main() {
//A(4) // 遞歸方式,
A4(4); // 模仿遞歸調用的流程
}
func A(a int ){
if(a>2){
a--
A(a)
}
fmt.Println("a=",a);
}
func A4(a int){
if(a>2){
a--
A3(a)
}
fmt.Println("a3=",a);
}
func A3(a int){
if(a>2){
a--
A2(a)
}
fmt.Println("a2=",a);
}
func A2(a int){
//a=2
if(a>2){
a--
A3(a)
}
fmt.Println("a1=",a);
}
遞歸調用其實表面就是自己調用自己,自己是沒法調用自己的,就像自己刪除自己沒法實現的。程序是這樣實現的。自己調用自己的時候,其實就是將自己拷貝了一份,然后再調用如代碼
- 先傳入4,條件滿足,-- 變成了3 又去調用自己(復制自己),此時等待3的結果
- 進入到3,條件滿足 -- 變成了2 又去調用自己(復制自己),此時等待2的結果
- 進入到2,條件不滿足 --直接打印 出2 返回給2等待地方
- 3接收到了2的結果后執行完if 又執行了打印,2
- 2接受3的結果 執行if 打印 3
- main 接受了4的結果
內置函數
-
len 用來求長度 stirng array slice map channel
-
new 用來分配內存,主要用來分配值類型,int float32 返回的指針
使用方式 n :=new(int) 值是0 -
make 用來分配內存,主要用來分配引用類型,chan map slice
時間
常用格式
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now() //獲取當前時間
fmt.Printf("時間:%v\n", now)
year := now.Year() //年
month := now.Month() //月
day := now.Day() //日
hour := now.Hour() //小時
minute := now.Minute() //分鐘
second := now.Second() //秒
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
timestamp1 := now.Unix() //時間戳
timestamp2 := now.UnixNano() //納秒時間戳
fmt.Printf("現在的時間戳:%v\n", timestamp1)
fmt.Printf("現在的納秒時間戳:%v\n", timestamp2)
timeObj := time.Unix(timestamp1, 0) //將時間戳轉為時間格式 2023-04-11 19:45:17 +0800 CST
fmt.Printf("時間戳轉換:%v\n", timeObj)
fmt.Printf("星期:%v\n", now.Weekday().String())
fmt.Printf("格式指定格式 %v \n",now.Format("2006-01-02 15:04:05"))
}
常用操作
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now() //獲取當前時間
later := now.Add(time.Hour) // 當前時間加1小時后的時間
fmt.Println("當前時間增加一小時",later.Format("2006-01-02 15:04:05"))
fmt.Println("時間差",later.Sub(now))
//Equal 函數會考慮時區的影響,因此不同時區標準的時間也可以正確比較,Equal 方法和用 t==u 不同,Equal 方法還會比較地點和時區信息。
fmt.Println("當前時間比較:",now.Equal(now))
fmt.Println("下一個小時時間比較:",now.Equal(later))
//判斷一個時間點是否在另一個時間點之后:
fmt.Println("當前時間大于某時間",now.After(now.Add(-time.Hour)))
//判斷一個時間點是否在另一個時間點之前:
fmt.Println("當前時間小于某時間",now.Before(now.Add(time.Hour)))
}
定時器
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.Tick(time.Second) //定義一個1秒間隔的定時器
for i := range ticker {
fmt.Println(i) //每秒都會執行的任務
}
}
轉換
package main
import (
"fmt"
"time"
)
func main() {
var layout string = "2006-01-02 15:04:05"
var timeStr string = "2023-04-11 15:22:12"
timeObj1, _ := time.Parse(layout, timeStr)
fmt.Println(timeObj1)
timeObj2, _ := time.ParseInLocation(layout, timeStr, time.Local)
fmt.Println(timeObj2)
}
包
go的每一個文件都是一個包,其實就和命名空間一樣
用import "包路徑"
如果提示找不到包 可以關閉mod go env -w GO111MODULE=off

package main
import (
"fmt"
"goCode/utils" //這里直接是文件夾
//u "goCode/utils" //u 是取的別名
)
func main() {
fmt.Println("333")
result:=utils.Cal(2.3,2.7,'+') // 這里是 包名.函數
fmt.Println("result=",result)
}
package utils
import (
"fmt"
)
//首寫比較是大寫 才能被外部使用
func Cal(n1 float64,n2 float64,o byte ) float64{
switch o {
case '+':
return n1+n2
case '-':
return n1-n2
case '*':
return n1*n2
case '/':
return n1/n2
default:
fmt.Println("不支持的計算")
return -99999
}
}
編譯項目
- 編譯只需要要編譯main所在目錄
- go build -o bin\ gocode.....\main 執行后會生成 exe文件 會放到bin里面
- pkg 庫文件
異常處理
默認情況下,程序報錯后會直接退出,
我們希望程序報錯后,可以捕獲異常,并進行處理,保證程序可以正常執行
go語言處理方式有 defer panic recover
defer 在panic之前執行
panic
這玩意執行了,是不能恢復的。直接終止了
defer+recover處理
defer 是函數執行完后執行的, 如果有多個,他會從后往前執行。也就是會先執行最后一個
package main
import (
"fmt"
)
func test(){
defer func(){
err:=recover() // reconver 系統內置函數,可以捕獲異常
if err!=nil{
// 拋出異常
fmt.Println("err=",err)
}
}()
n1:=10
n2:=0
n:=n1/n2 // 這里會報錯
fmt.Println("n=",n)
}
func main() {
test()
fmt.Println("執行完畢!")
}
自定義錯誤
package main
import (
"fmt"
"errors"
)
func test(n int)(err error){
if n==1{
return nil
}else{
//返回自定義錯誤
return errors.New("發生了錯誤")
}
}
func test1(){
err:=test(2)
if err!=nil{
//如果發生錯誤,就輸出這個錯誤,并且終止程序
panic(err)
}
fmt.Println("發生錯誤后這里是不執行的")
}
func main() {
test1()
fmt.Println("執行完畢!")
}
命令行參數
Variables
- os.Agrs提供了簡單的命令行參數,以命令行參數個數作為標識,參數列表是一個切片,索引0代表程序本身,1代表第一個參數,以此類推,沒有更細粒度的參數區分,使用起來簡單
- flag提供了更為科學的命令行參數處理辦法,提供更細粒度的和更全的參數解析,推薦使用
os.Agrs
os.Args 提供原始命令行參數訪問功能。注意,切片中的第一個參數是該程序的路徑,并且 os.Args[1:]保存所有程序的的參數。
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("命令行參數",len(os.Args))
for i,v:=range os.Args{
fmt.Printf("args[%v]=%v\n",i,v)
}
}
先執行go build main.go 生成exe程序
cmd 進入 main.exe 參數1 參數2 慘數3
flag包
Args 的方式 傳參數 必須遵守 順序,解決這問題可以用flag 包 來解析命令行 參數可以隨意
package main
import (
"fmt"
"flag"
)
func main() {
var user string
var pwd string
var host string
var port int
// &user 就是接用戶命令行輸入的-u
// u 就是-u指定參數
// "" 默認為空
// 說明
flag.StringVar(&user,"u","","用戶名,默認為空")
flag.StringVar(&pwd,"p","","密碼,默認為空")
flag.StringVar(&host,"h","","host,默認為空")
flag.IntVar(&port,"t",3306,"端口,默認為空") // intvar
// 轉換
flag.Parse()
//main.exe -u root -p 123456 -h 192.168.2.3
fmt.Printf("user=%v,pwd=%v,host=%v,port=%v",user,pwd,host,port)
}
浙公網安備 33010602011771號