前言
? ? ? ? 本章節(jié)開(kāi)始,通過(guò)不斷實(shí)踐和試錯(cuò)來(lái)對(duì)GO語(yǔ)言加快學(xué)習(xí)、加深了解;為了便于時(shí)間和試錯(cuò),我們先來(lái)學(xué)習(xí)如何在GO語(yǔ)言中編寫(xiě)測(cè)試程序:
1. 源碼文件以_test結(jié)尾:xxx_test.go
2. 測(cè)試方法名以Test開(kāi)頭:func TestXXX(t testing.T){ ... }
package try_test
import "testing"
func TestForTyrTest(t *testing.T) {
t.Log("Hi Go try test")
}
保存(ctrl+s)后,將自動(dòng)于控制臺(tái)(Output)輸出:
=== RUN TestForTyrTest
TestForTyrTest: try_test.go:6: Hi Go try test
--- PASS: TestForTyrTest (0.00s)
PASS
備注:IDE使用的Atom,需先安裝go-plus插件:
確認(rèn)安裝了go-plus插件
Atom->preferences->packages
搜索找到go-plus
在settings中,Test配置中選中 Run with verbose flag setting
如果在命令行里運(yùn)行測(cè)試文件,需要執(zhí)行命令,才能輸出 t.Log 里的文字:
go test -v xxx_test.go
變量
Go 語(yǔ)言變量名由字母、數(shù)字、下劃線組成,其中首個(gè)字符不能為數(shù)字。
變量聲明
對(duì)于變量聲明,典型的有兩種:
- 使用 var 關(guān)鍵字聲明:
var name = xxxxxx
其中,“xxxxxx”既可以是個(gè)數(shù)值,也可以是表達(dá)式執(zhí)行后的返回結(jié)果;可以給name賦值整形、浮點(diǎn)數(shù)及字符串等。
var name = "zhangsan"
var age = 28
- 使用短變量聲明:
name := "zhangsan"
age := 28
這兩種方式各有千秋,有著各自的特點(diǎn)和適用場(chǎng)景。前者可以被用在任何地方,而后者只能被用在函數(shù)或者其他更小的代碼塊中。
在這里,你可能會(huì)有個(gè)錯(cuò)覺(jué),認(rèn)為GO語(yǔ)言是弱類型語(yǔ)言;
其實(shí)不是的,變量的初始化時(shí)省略變量的類型而由系統(tǒng)自動(dòng)推斷,也就是Go 語(yǔ)言中的類型推斷功能;類型推斷是一種編程語(yǔ)言在編譯期自動(dòng)解釋表達(dá)式類型的能力。
Go 語(yǔ)言是靜態(tài)類型的,所以一旦在初始化變量時(shí)確定了它的類型,之后就不可能再改變。這就避免了在后面維護(hù)程序時(shí)的一些問(wèn)題。
Go 語(yǔ)言的類型推斷可以明顯提升程序的靈活性,使得代碼重構(gòu)變得更加容易,同時(shí)又不會(huì)給代碼的維護(hù)帶來(lái)額外負(fù)擔(dān)(實(shí)際上,它恰恰可以避免散彈式的代碼修改),更不會(huì)損失程序的運(yùn)行效率。
變量賦值
- 賦值時(shí)可以進(jìn)行自動(dòng)類型推斷
- 在一個(gè)賦值語(yǔ)句中可以對(duì)多個(gè)變量進(jìn)行同時(shí)賦值
a, b, c := 5, 7, "abc" //一個(gè)賦值語(yǔ)句中對(duì)多個(gè)變量進(jìn)行賦值
/*交換兩個(gè)變量的值*/
func TestExChange(t *testing.T){
/*
//常規(guī)寫(xiě)法:
a := 1
b := 2
tmp := a
a = b
b = tmp
t.Log(a,b)
*/
//改進(jìn)寫(xiě)法
a := 1
b := 2
a, b = b, a //一個(gè)賦值語(yǔ)句中對(duì)多個(gè)變量進(jìn)行賦值
t.Log(a,b)
}
常量
常量是一個(gè)簡(jiǎn)單值的標(biāo)識(shí)符,在程序運(yùn)行時(shí),不會(huì)被修改的量。
常量的聲明:
const c_name1, c_name2 = value1, value2
package try_test
import "testing"
/// 測(cè)試常量一般聲明
func TestNormalConstant(t *testing.T) {
//!普通常量定義
const LENGTH = 256
//!多重賦值
const i, b, str = 28, false, "string"
t.Log("LENGTH = ", LENGTH, ";i = ", i, ";b = ", b, ";str = ", str)
/*
=== RUN TestNormalConstant
TestNormalConstant: try_test.go:12: LENGTH = 256 ;i = 28 ;b = false ;str = string
--- PASS: TestNormalConstant (0.00s)
*/
}
///測(cè)試常量作為枚舉使用
func TestConstantUseOfEnum(t *testing.T) {
//!作為枚舉使用
const (
Jan = iota + 1
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
)
t.Log("Jan =", Jan, "; Feb =", Feb, "; Mar =", Mar, "...", "Dec = ", Dec)
/*
=== RUN TestConstantUseOfEnum
TestConstantUseOfEnum: try_test.go:33: Jan = 1 ; Feb = 2 ; Mar = 3 ... Dec = 12
--- PASS: TestConstantUseOfEnum (0.00s)
*/
}
///測(cè)試常量作為連續(xù)位使用
func TestConstUseOfBinary(t *testing.T) {
//!二進(jìn)制位表示可讀,可寫(xiě),可執(zhí)行
const (
Readable = 1 << iota
Writable
Excutable
)
t.Log("Readable = ", Readable, ";Writeable = ", Writable, ";Excutable = ", Excutable)
/*
=== RUN TestConstUseOfBinary
TestConstUseOfBinary: try_test.go:45: Readable = 1 ;Writeable = 2 ;Excutable = 4
--- PASS: TestConstUseOfBinary (0.00s)
*/
}
其中,iota,特殊常量,可以認(rèn)為是一個(gè)可以被編譯器修改的常量。
iota 在 const關(guān)鍵字出現(xiàn)時(shí)將被重置為 0(const 內(nèi)部的第一行之前),const 中每新增一行常量聲明將使 iota 計(jì)數(shù)一次(iota 可理解為 const 語(yǔ)句塊中的行索引)。
數(shù)據(jù)類型
- GO語(yǔ)言不支持隱式類型轉(zhuǎn)換;
- 別名和原有類型也不能進(jìn)行隱式類型轉(zhuǎn)換;
| 符號(hào) | 描述 |
|---|---|
| bool | 布爾值 |
| string | 字符串 |
| int int8 int16 int32 int64 | 有符號(hào)整形 |
| uint uint8 uint16 uint32 uint64 | 無(wú)符號(hào)整形 |
| uintptr | 無(wú)符號(hào)整型,用于存放一個(gè)指針 |
| byte | 類似 uint8 |
| rune | 類似 int32 ,represent a Unicode code point |
| float32 float64 | 32/64位浮點(diǎn)型數(shù) |
| complex64 complex128 | 32/64 位實(shí)數(shù)和虛數(shù) |
Go 語(yǔ)言支持指針,但不支持指針運(yùn)算
package type_test
import "testing"
/// 類型的預(yù)定義值(math的package中可找到這些值)
//! math.MaxInt64
//! math.MaxFloat64
//! math.MaxUint32
/// string 是值類型,其默認(rèn)的初始化值為空字符串,而不是nil
func TestPoint(t *testing.T) {
var arry = [5]int{1, 2, 3, 4, 5}
ptr := &arry[1]
t.Log(*ptr)
t.Logf("%T %T", arry[1], ptr)
/*
=== RUN TestPoint
TestPoint: type_test.go:14: 2
TestPoint: type_test.go:15: int *int
--- PASS: TestPoint (0.00s)
*/
//ptr += 1 //報(bào)錯(cuò):invalid operation: ptr += 1 (mismatched types *int and int)
}
運(yùn)算符
算術(shù)運(yùn)算符
| 運(yùn)算符 | 描述 | 實(shí)例 |
|---|---|---|
| + | 相加 | A + B輸出結(jié)果 30 |
| - | 相減 | A - B 輸出結(jié)果 -10 |
| * | 相乘 | A * B 輸出結(jié)果 200 |
| / | 相除 | B / A 輸出結(jié)果 2 |
| % | 求余 | B % A 輸出結(jié)果 0 |
| ++ | 自增 | A++ 輸出結(jié)果 11 |
| -- | 自減 | A-- 輸出結(jié)果 9 |
GO語(yǔ)言沒(méi)有前置的++,--
比較運(yùn)算符
| 運(yùn)算符 | 描述 | 實(shí)例 |
|---|---|---|
| == | 檢查兩個(gè)值是否相等,如果相等返回 True 否則返回 False。 | (A == B) 為 False |
| != | 檢查兩個(gè)值是否不相等,如果不相等返回 True 否則返回 False。 | (A != B) 為 True |
| > | 檢查左邊值是否大于右邊值,如果是返回 True 否則返回 False。 | (A > B) 為 False |
| < | 檢查左邊值是否小于右邊值,如果是返回 True 否則返回 False。 | (A < B) 為 True |
| >= | 檢查左邊值是否大于等于右邊值,如果是返回 True 否則返回 False。 | (A >= B) 為 False |
| <= | 檢查左邊值是否小于等于右邊值,如果是返回 True 否則返回 False。 | (A <= B) 為 True |
用==比較數(shù)組
在很多主流語(yǔ)言中,數(shù)組是個(gè)引用類型,不是值類型;所以在相比較的時(shí)候是比較數(shù)組的引用而不是里面的值;
這點(diǎn)在GO上是完全不同的:
- 相同維數(shù)且含有相同個(gè)數(shù)元素的數(shù)組才可以進(jìn)行比較
- 每個(gè)元素都相同才相等
邏輯運(yùn)算符
下表列出了所有Go語(yǔ)言的邏輯運(yùn)算符。假定 A 值為 True,B 值為 False
| 運(yùn)算符 | 描述 | 實(shí)例 |
|---|---|---|
| && | 邏輯 AND 運(yùn)算符。 如果兩邊的操作數(shù)都是 True,則條件 True,否則為 False。 | (A && B) 為 False |
| || | 邏輯 OR 運(yùn)算符。 如果兩邊的操作數(shù)有一個(gè) True,則條件 True,否則為 False。 | (A ||B) 為 True |
| ! | 邏輯 NOT 運(yùn)算符。 如果條件為 True,則邏輯 NOT 條件 False,否則為 True。 | !(A && B) 為 True |
位運(yùn)算符
&、|、^、<<、>>與主流編程語(yǔ)言沒(méi)有區(qū)別
&^:按位置零
1 &^ 0 --- 1
1 &^ 1 --- 0
0 &^ 1 --- 0
0 &^ 0 --- 0
如果右邊的操作數(shù)位上為1,不管左邊操作數(shù)為多少,結(jié)果都為0
如果右邊的操作數(shù)位上為0,那么其左邊操作數(shù)為什么,結(jié)果就是什么
const (
Readable = 1<<iota
Writable
Excutable
)
func TestBitClear(t *testing.T){
a:=7 ///0111
t.Log(a&Readable == Readable, a&Writable == Writable, a&Excutable == Excutable)
// ---> true, true, true
a = a&^Excutable //去掉可執(zhí)行權(quán)限
t.Log(a&Readable == Readable, a&Writable == Writable, a&Excutable == Excutable)
// ---> true, true, false
}
運(yùn)算符優(yōu)先級(jí)
有些運(yùn)算符擁有較高的優(yōu)先級(jí),二元運(yùn)算符的運(yùn)算方向均是從左至右。
下表列出了所有運(yùn)算符以及它們的優(yōu)先級(jí),由上至下代表優(yōu)先級(jí)由高到低:
| 優(yōu)先級(jí) | 運(yùn)算符 |
|---|---|
| 5 | * / % << >> & &^ |
| 4 | + - | ^ |
| 3 | == != < <= > >= |
| 2 | && |
| 1 | || |
條件和循環(huán)
循環(huán)
GO僅支持循環(huán)關(guān)鍵字for
for i:=0; i<5; i++ {
}
///代碼示例
package main
import "fmt"
/*while 條件循環(huán)*/
//while(n<5)
n := 5
for n<5 {
n++
fmt.Print(n)
}
/*while 無(wú)限循環(huán)*/
///while(true)
for true {
fmt.Printf("這是無(wú)限循環(huán)。\n");
}
條件語(yǔ)句
if 語(yǔ)句:
- condition表達(dá)式結(jié)果必須為布爾值
- 支持變量賦值
if var declaration; condition {
? ? ? ? //code to be executed if condition is true
}
package condition_test
import "testing"
func TestIfMultiSec(t *testing.T) {
if a:=1 == 1; a {
t.Log("1==1") /// 輸出:1==1
}
}
由于Go方法支持多返回值,因此特性為編程帶來(lái)了便捷;在后面的學(xué)習(xí)章節(jié)中在詳細(xì)介紹,這里先簡(jiǎn)單拋出個(gè)例子:
package condition_test
import "testing"
func TestIfMultiSec(t *testing.T) {
if ret, err:=function; err==nil {
......
}else{
......
}
}
switch語(yǔ)句:
- 條件表達(dá)式不限制為常量或者表達(dá)式
- 單個(gè)case中,可以出現(xiàn)多個(gè)結(jié)果選項(xiàng),使用逗號(hào)分隔
- 與C語(yǔ)言等規(guī)則相反,Go語(yǔ)言不需要用break來(lái)明確退出一個(gè)case
- 可以不設(shè)定switch之后的條件表達(dá)式,在這種情況下,整個(gè)switch結(jié)構(gòu)與多個(gè)if ... else ... 的邏輯作用相同
代碼示例1:
package condition_test
func TestSwitchMultiCase(t *testing.T) {
for i :=0; i<5; i++ {
switch i {
case 0,2:
t.Log("Event")
case 1,3:
t.Log("Odd")
default:
t.Log("it is not 0-3")
}
}
}
代碼示例2:
package condition_test
func TestSwitchMultiCase(t *testing.T) {
for i :=0; i<5; i++ {
switch {
case i%2==0:
t.Log("Event")
case i%2==1:
t.Log("Odd")
default:
t.Log("it is not 0-3")
}
}
}