go之基本型別與函式使用
go之基本型別與函式使用
目錄一、go語言介紹
1 go語言介紹
Go 即Golang,是Google公司2009年11月正式對外公開的一門程式語言,特點:語法簡單,速度快。
Go是靜態(編譯型)強型別語言,是區別於解析型語言的弱型別語言(靜態:型別固定 強型別:不同型別不允許直接運算)。
python動態強型別語言。
編譯型語言:Java,c,c++,c#,go
解釋性語言:python,js,php...
編譯型語言涉及到跨平臺問題,因為它需要編譯成該平臺的可執行檔案。比如:java執行在jvm之上。go語言可以進行跨平臺編譯,即交叉編譯。比如,從windows上編譯成linux上的可執行檔案。
解釋性不涉及跨平臺問題,因為各個平臺有各個平臺的直譯器。
2 go語言特性
- 跨平臺的編譯型語言,交叉編譯
- 管道(channel),切片(slice),併發(routine)
- 有垃圾回收的機制
- 支援面向物件和麵向過程的程式設計模式(go的面向物件中沒有類的概念,python是完全的面向物件)
3 go的歷史發展
- 2009年11月7日 weekly.2009-11-06 —— 早期的版本
- 2015年8月19日 go1.5 —— 實現的架構變化,同時保留了和舊版本的相容性,本次更新中移除了”最後殘餘的C程式碼”。(從此以後,自舉,自己寫自己)
- 2018年8月24日 go 1.11 :增加了modules,包管理
- 2020 年 8 月 go 1.15
4 go語言的應用領域
Google、Facebook、騰訊、七牛雲
go語言書寫:docker、k8s、藍鯨...
應用領域:服務開發、併發、分散式、微服務等方向。
二、go語言開發環境
1 go環境配置
- IDE(Goland): 整合開發環境。
- 開發環境:sdk【一路下一步即可】
- go語言不需要手動新增到環境變數(python、java需要)
go version
: 列印go的版本資訊
注意事項:
-
goland建立專案:需要選擇go sdk的安裝路徑【預設被選中】。
-
指定gopath:程式碼必須放到該路徑下,否則無法執行。【預設會在環境變數中建立GOPATH,需要手動自定義進行修改】。同時,所有的go程式碼必須放在這個路徑下的src資料夾下,否則無法執行【E:\Go_project\src\專案名】。
-
路徑和檔案都不要出現中文。
2 go常用命令
go version // 檢視go的版本資訊
go env // 檢視go的環境變數
- GO111MODULE=on // 使用了go.mod模式
- GOPATH=E:\Go_project // 程式碼存放路徑
- GOROOT=D:\golang // go sdk安裝路徑
- ...
go build // 編譯。編譯型語言,需要先編譯再執行,編譯成可執行檔案
go run // 編譯並執行。兩步並作一步
go clean // 清除編譯後的可執行檔案
go fmt // 格式化程式碼【沒用】
go get // 下載並安裝包和依賴等同於pip install
...
三、第一個hello world
//單行註釋
/*多行註釋*/
編譯型語言:
- 編譯: go build .\test01.go
- 執行: .\test01.exe
- 編譯並執行: go run .\test01.go
package main // 宣告包名,包名是main
import "fmt" // 內建包
// 定義了一個main函式,go專案執行的入口【所以編譯型語言必須有一個入口】
func main() {
fmt.Println("hello world") // 等同於python中的print(python中的print是內建函式)
}
注意:每個go檔案都要有一個包名,代表其屬於某個包。
goland中檔案執行有file、package等幾種方式。【可以自己進行配置,一般一個專案中使用package即可,測試的時候使用file就行】
四 變數
1 變數定義
-
var 變數名 變數型別 = 變數值
var age int = 10
go中變數定義了就必須使用,如果不適用就報錯。
-
var 變數名 = 變數值
型別推導(可以不寫型別)var age = 10
package main import "fmt" // 內建包 func main() { var age int = 10 var name string = "YangYi" fmt.Printf("%T\n", age) // int (看型別,沒有python中的type方法,沒有自動換行,需要手動\n) fmt.Printf("%T\n", name) // string fmt.Print() // 列印不換行 fmt.Println() // 列印換行 fmt.Printf("%T\n", name) // 檢視型別 fmt.Printf("%p", &name) // 0xc0000881e0 }
go中變數型別一旦定義,後期不能改變。(編譯型語言通用,python中可以修改,python中存放的記憶體地址,所以可以改變型別)
-
變數名 := 變數值
簡略宣告(型別和var關鍵字都不寫)a := 10
a := 10 var a int = 100 var a = 99 // 重複定義,報錯(變數不可以重複定義,同時,變數必須先定義,後使用)
/* 只定義,不賦值 */ var a int // 可以(只能用這種方式) var a // 不可以(因為沒有型別,不知道a是什麼,因為型別需要在定義階段確定,且不能改)
/* 宣告多個變數 */
var width, height int = 100, 50 // 第一種
var width, height = 100, 50 // 第二種
width, height := 100, 50 // 第三種
var (
name = "YangYi" // 不賦型別
age int = 29 // 賦型別
height int // 宣告
)
/* 小坑 */
var a int =10
var b =99
b,a:=99,100 //這個不報錯,我們覺得是重複定義,冒號左側,只要有一個沒有定義過的變數,就可以
2 變數定義規範
2.1 變數命名規範
變數命令建議用駝峰(大小寫有特殊意義【私有和公有的意義】);
go檔案命名建議用 下劃線;(ps:python中都建議都使用下劃線;java中建議都是駝峰;js中變數名駝峰)
一個名字必須以一個字母(Unicode字母)或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線;
大寫字母和小寫字母是不同的:Name和name是兩個不同的變數;
關鍵字和保留字都不建議用作變數名;
2.2 go語言中的關鍵字
// Go語言中關鍵字有25個
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
// Go語言中保留字有37個,主要對應內建的常量、型別和函式
內建常量: true false iota nil
內建型別: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
內建函式: make len cap new append copy close delete
complex real imag
panic recover
五 資料型別
1 基礎資料型別
-
數字
// 有符號整形 int 在32位機器是int32,在64位機器是int64 int8 表示整數範圍是:8個位元位,8個bit是1byte ,負數和0, 2的7次方-1 的範圍 int16 2的15次方減一 int32 int64 /* 強型別舉例: var a int8 = 10 var b int16 = 20 fmt.Println(a + b) // 報錯【go是強型別,其中沒有java中的自動型別轉換一說】 fmt.Println(int16(a) + b) */ /* byte:是int8的別名 單引號包裹 rune:是int32的別名 單引號包裹 */ var a byte = 99 // int8 fmt.Println(a) var b byte = 'b' // 存放的是字元,其中也是數字而已 fmt.Println(b) // 98 var c rune = '你' // 可以列印,字元中其實是數字 fmt.Println(c) // 20320 var d byte = '你' fmt.Println(d) // 報錯,超出範圍 // 無符號整形 uint8 2的8次方減一 (定義一個人的年齡) uint16 uint32 uint64 // 浮點數(小數),表示小數點後長度多少位 float32 float64 // 複數 complex64 complex128 var a complex64 = 20 + 10i
-
字串
"雙引號包裹" `反引號包裹` // 和js一樣; python中是 """ """
-
布林型別
bool true 和 false var flag bool = false var flag = true
資料型別預設值:數字型別是0, 字串型別是空字串,布林型別是false。
java | go |
---|---|
byte | int8 |
short | int16 |
int | int32 |
long | int64 |
float | float32 |
double | float64 |
2 常量
go中常量定義使用const
關鍵字;java中的常量為final
關鍵字;python中是大寫
。
-
const 變數名 變數型別 = 變數值
const name string = "yangyi" // 常量可以定義之後不使用,不會報錯
-
const 變數名 = 變數值
const age = 90
-
其他定義方式
const name, age = "yangyi", 18 const ( name string = "yangyi" age = 18 ) // 應該不會使用吧 const ( s1 = iota s2 s3 = iota s4 = 99 s5 ) fmt.Println(s1) // 0 fmt.Println(s2) // 1 fmt.Println(s3) // 2 fmt.Println(s4) // 99 fmt.Println(s5) // 99 太奇怪 const ( s1 = iota s2 s3 = iota s4 = 99 s5 = iota ) fmt.Println(s1) // 0 fmt.Println(s2) // 1 fmt.Println(s3) // 2 fmt.Println(s4) // 99 fmt.Println(s5) // 4 太奇怪
六 函式
1 函式基礎
func 函式名(引數名 型別, 引數名 型別)(返回值型別, 返回值型別){
函式體內容
return 返回值1, 返回值2
}
函式使用示例
// 1 有引數無返回值
func add(a int, b int){
fmt.Println(a + b)
}
add(100, 200) // 呼叫函式
// 2 有引數無返回值,有多個相同型別的引數
func add(a, b int){
fmt.Println(a + b)
}
// 3 有引數無返回值,有多個不同型別的引數
func add(a, b int, msg,name string){
}
// 4 多個引數,一個返回值
func add(a, b int) int {
return a + b
}
// 5 多個引數,多個返回值【python和go支援多返回值】
func add (a, b int) (int, int){
return a + b, a * b
}
a, b := add(100, 200) // 多返回值就需要用多變數進行接受【和python一樣】
a, _ := add(3, 4) // python中的下劃線可以列印,但是go中就是空
// 6 命令返回值
func add(a, b int) (c int, d int){
c = a + b // 就是在函式體中已經聲明瞭c d的變數
d = a * b
return // 就會直接將c d返回出去,不需要寫 return c,d
}
2 函式高階
函式是一等公民,函式可以賦值給變數【python中也是如此】
在編譯型語言中,和解釋性語言不同,沒有先定義函式,後使用函式一說,編譯之後都能找到。
// 匿名函式(定義在函式內部,不能有名字): python中是lamdba
// 第一種
func (){
}() // 匿名函式直接呼叫
// 第二種
var a func() // 定義一個變數,型別為func()【哈哈】
a = func(){ // 此時a是一個函式
fmt.Println("我是匿名函式")
}
a() // 我是匿名函式
// 7 函式返回值為函式
func test() func(){
return func(){
fmt.Println("我是返回的函式...")
}
}
a := test()
fmt.Println(a) // 0x5451e0 【列印函式名,拿到的還是函式的記憶體地址】
// 8 返回值函式,返回的函式帶引數
// 返回值型別必須跟返回的函式型別匹配 即 func()和func(msg string)不是一個型別
func test() func(msg string){ // 返回值型別必須跟返回的函式型別匹配
return func(msg string){
fmt.Println("我是返回的函式...")
}
}
a := test()
a("hello") // 函式呼叫
// 9 函式返回值為函式,返回的函式帶引數,帶返回值
func test() func(a, b int) int {
return func(a, b int) int {
return a + b
}
}
a := test() // a的型別為 var a func(a, b int)int
fmt.Println(a(2, 3))
// 10 函式引數為函式型別,返回值為帶引數帶返回值的函式型別
func test(f func()) func(a, b int) (int, int){
f() // f函式執行
return func(a, b int) (int, int){
f() // f函式執行
return a + b, a * b
}
}
// 函式操作
a, b := test(
func(){
fmt.Println("我是函式引數...")
}
)(3, 4) // 函式呼叫
// 或者
f := func(){
fmt.Println("我是函式引數...")
}
f1 := test(f)
a, b := f1(3, 4)
fmt.Println(a, b)
3 閉包函式
①定義在函式內部; ②對外部作用域有引用;
閉包函式其實就是多了一種對函式傳參的方式。
func test(age int) func() {
// age := 18
a := func(){ // 定義在函式內部
fmt.Println(age) // 對外部作用域有引用
}
return a
}
a := test(19)
a() // 閉包函式的呼叫【並沒有傳參,實際上已經有引數傳遞進去】
// 一般來說,函式執行完畢,引數應該被釋放,但是由於閉包函式的存在,所以age一直沒有被釋放,有東西對其有引用。【引出記憶體逃逸: 正常應該被回收,其實沒有被回收】
// 裝飾器是閉包函式的典型應用(go語言中沒有裝飾器的語法糖,python中有語法糖)
4 type型別重新命名
type MyFunc func() // 型別重新命名
func test() MyFunc {
return func(){
}
}
type MyFunc func(a,b int)(int,string)
func test() MyFunc {
return func(a,b int)(int,string) {
fmt.Println("xxx")
return 10,"ok"
}
}
type Myint int // 這裡Myint和int已經不是銅鼓一個型別了
var a int = 10
var b Myint = 100
fmt.Println(a + b) // 報錯
fmt.Println(a + int(b)) // 強制型別轉換
5 變數的作用域範圍
同一個包下,函式名不能重名。比如:都在main包中
// 第一種方式
var a int //全域性變數,全域性有效,只要改了,就是改了
func main() {
fmt.Println(a) //0
a=19
fmt.Println(a) //19
test1() //99
fmt.Println(a) //99
}
func test1() {
a=99
fmt.Println(a) // 99
}
// 第二種方式【區域性 ——> 全域性 ——> 內建】
var a int //全域性變數,全域性有效,只要改了,就是改了
func main() {
var a int
fmt.Println(a) //0
a=19
fmt.Println(a) //19
test1() // 0
fmt.Println(a) //19
}
func test1() {
fmt.Println(a)
}