學習golang注意點:
- 導的包必須使用;或者使用
_
未使用的包,作用是調用該包下的初始化方法。- 局部變量聲明必須使用。
- go語言的包和java的相似,
包名.變量
訪問
1. 初識go語言
1.1 Hello World
package main
import "fmt"
func main() {
fmt.Println("hello world");
}
1.2 go 數據類型
布爾:
var a bool = true
var b bool = false
整型:
整型分為有符號和無符號的類型
8, 16 ,32 分別代表位數
int
: 根據系統決定是32位還64位
int8
: 1個字節,-128~127 相當於java中的short;
int16
: 2個字節,-215 ~ 215 -1
int32
: 4個字節 -231 ~231 - 1
int64
: 8個字節-263 ~263 - 1
uint : .....
無符號整形都是取值0~216 - 1
var a int = -3
var b uint = 3 //uint類型不可以為負數
浮點型:
var a float32 = 100.0
var b float64 = 100.00 //默認
字符類型
golang中的字符使用的是字節保存的,本質就是一個int32類型
var a byte = 'a'
var b byte = 'c'
fmt.Print(a, "===", b) //輸出的是該字節對應的字節碼: 97===99
fmt.Printf("%c === %c", a, b) // a === c
字符串型:
var str string = "hello world"
//多行字符串,不需要使用+來連接多行
var str2 string = `a
asda asdasdadsadasd `
復數類型:
complex64
是兩個float32組成 complex128
兩個float64組成
var a complex64 = 10 + 3i
var b complex128= 10 + 3i //默認
相關操作
var v= complex(2 , 3) //構造1個復數,
a := real(v) //返回復數實部 2
b := image(v) 返回復數虛部 3
rune類型:
// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
//int32的別名,幾乎在所有方面等同於int32
//它用來區分字符值和整數值
type rune = int32
1.3 變量常量
局部變量:
屬於函數或者方法;聲明之后必須使用
var a = 3;
var b int = 3;
c := 3
全局變量
b := 10
這種全局變量聲明是錯誤的
全局變量的概念:隸屬於包
,聲明之后可以不使用
var a int
var (
c int
d string
)
var e = 3
常量
局部
const a = 3
全局
const a int = 10
const b = 20
const (
d int = 10
e string = "ss"
f = 30
)
1.5 字符串相關操作
golang中string底層是通過byte數組實現的,byte使用utf-8編碼標識的Unicode文本,每個漢字占3個字節
- 求長度
func strDemo() {
fmt.Println(len("hello")) //5
fmt.Println(len("中")) //3
fmt.Println(len([]rune("中"))) //1, 正確獲取中文字符串長度
}
- 字符串遍歷
//對中文無法支持
func strEach() {
str := "hello world"
for i := 0; i < len(str); i ++ {
//fmt.Print(str[i] ,"\t") // 104 101 108 108 111 32 119 111 114 108 100
//講字節編碼轉為字符串輸出
fmt.Printf("%c\t", str[i]) //h e l l o w o r l d
}
}
func strEach() {
str := "hello world 中國"
for i, s := range str {
//0 h1 e2 l3 l4 o5 6 w7 o8 r9 l10 d11 12 中15 國
fmt.Print(i, "\t", string(s))
}
}
//這個可以正確的輸出索引
func strEachRune() {
str := "中國人民"
for i, s := range []rune(str) {
fmt.Println(i, string(s))
}
}
- 其他操作
str := "中國人民, hello world"
index := strings.Index(str, "國") //存在則 index > -1, 否則 == -1 此時index=-3
split := strings.Split(str, ",")
replace := strings.Replace(str, "o", "2", 1) //第三個參數標識替換幾個,小於0,則替換所有
result := strings.EqualFold("中國", "中國2") //不區分大小寫
fmt.Println("中國" == "中國H") //區分大小寫, 同strings.Compare()5
1.6 相互轉換
1.6.1 基本數據
golang基本數據數據之間的轉換可使用公:T(i)
進行相互轉換
//數據之間的相互轉換
func transfer() {
//
var i int = 10
var j float64 = 11.30
x := float64(i)
y := int(j)
fmt.Print(x, "\t", y) //10 11
}
1.6.2 string <=> 基本數據類型
- 基本類型=>string
fmt.Sprintf(format string, param interface{})
func stringTrans() {
var i int = 10
var flag bool = true
int_str := fmt.Sprintf("%d", i)
bool_str := fmt.Sprintf("%t", flag)
fmt.Println(int_str)
fmt.Println(bool_str)
}
strconv
func stringStrco() {
var i int = 10000
var flag bool = true
var price float64 = 130.32
formatInt := strconv.FormatInt(int64(i), 10) //等價: strconv.Itoa(i)
formatBool := strconv.FormatBool(flag)
formatFloat := strconv.FormatFloat(price, 'f', 10, 64)
fmt.Println(formatInt)
fmt.Println(formatBool)
fmt.Println(formatFloat)
}
- String=>基本數據類型
func strToBase() {
str_flag := "true"
str_age := "20"
str_price := "20.33"
flag, _ := strconv.ParseBool(str_flag)
age, _ := strconv.ParseInt(str_age, 10, 64)
age_int, _ := strconv.Atoi(str_age)
price, _ := strconv.ParseFloat(str_price, 64)
fmt.Println(flag)
fmt.Println(age)
fmt.Println(price)
fmt.Println(age_int)
}
1.6.3 字節數組和字符串
func byteAndStr() {
str := "hello world, 中國"
data := []byte(str)
s := string(data)
fmt.Println(s)
fmt.Println(data)
}
1.7 時間
- 獲取時間
func timeOperate() {
cur := time.Now()
curT := time.Now().Unix() //獲取時間戳
fmt.Println(cur) //2019-01-30 20:40:16.410689 +0800 CST m=+0.000353772
fmt.Println(curT) //1548852137
}
- 時間和字符串轉換
func formatTime() {
format := time.Now().Format("2006-01-02 15:04:05")
//時間戳轉時間
var timestamp int64 = 1548852137
unix := time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
//字符串轉時間
formatTimeStr := "2017-04-11 13:33:37"
strToTime, _ := time.Parse("2006-01-02 15:04:05", formatTimeStr)
fmt.Println(format) //2019-01-30 21:00:53
fmt.Println(unix)
fmt.Println(strToTime) //2017-04-11 13:33:37 +0000 UTC
}
1.8 復合數據類型
數組和切片(slice)之間的區別:
數組:聲明的時候必須指定長度
var arr [10]int
,值類型,但是在java里面數組是引用類型 Slice: 長度可變,不需要指定長度,引用類型
1. 數組
//數組的聲明
func createArray() {
var books [3]string
books[0] = "java"
books[1] = "python"
books[2] = "golang"
names := [3]string{"lisi", "zhansan", "hand"}
scores := [...]int{89, 59, 30, 100} //根據后面的內容決定長度
fmt.Println(books)
fmt.Println(names)
fmt.Println(scores)
}
//數組的相關操作
func operateArray() {
//數組長度
scores := [...]int{89, 59, 30, 100}
fmt.Println(len(scores))
//數組遍歷
for i := 0; i < len(scores); i++ {
fmt.Print(scores[i], "\t")
}
fmt.Println()
for index, value := range scores {
fmt.Print(index, "==", value, "\t")
}
}
2. slice
slice是一個比較復雜的數據結構,也就相當於Java里面集合的概念,是一個可變長的數據
//最簡單的一種聲明方式
func createSlice() {
var args []int
args = make([]int, 10)
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4
args[4] = 5
for index, value := range args {
fmt.Println(index, value)
}
}
通過數組定義一個切片
len
切片長度, 表示當前切片元素的個數
cap
切片容量,表示切片可以容納切片的個數,如果超出則報錯
func createSlice2() {
arrays := [...]int{1, 2, 3, 4, 5}
slice := arrays[1:4] //[2 3 4]
fmt.Println(len(slice)) //3
fmt.Println(cap(slice)) //4
slice[1] = 10
//這里可以解釋下圖
fmt.Println(arrays) //[1 2 10 4 5]
fmt.Println(slice) // [2 10 4]
}
這里容量為什么是4?,如圖
append©函數
當append超出原來容量的時候,會擴展原來的容量為原先的兩倍
//append
func appendFunc() {
slice := make([]int, 2, 4)
slice[0] = 1
slice[1] = 1
slice = append(slice, 2)
slice = append(slice, 3)
slice = append(slice, 4)
fmt.Println(len(slice)) //5
fmt.Println(cap(slice)) //8
}
//copy函數的用法
func copyFunc() {
slice := make([]int, 2, 4)
slice2 := make([]int, 2, 4)
slice[0] = 1
slice[1] = 1
copy(slice2, slice) //相當於 slice2 := slice[:]
fmt.Println(slice2) // [1 1]
}
3. map
map 數據結構和java的HashMap類似。
//創建一個map
func createMap() {
var product map[string]interface{} //聲明
product = make(map[string]interface{}) //初始化
product["id"] = 1
product["title"] = "口紅"
product["price"] = 199.33
fmt.Println(product)
}
//遍歷map
func mapForEach() {
var product map[string]interface{} //聲明
product = make(map[string]interface{}) //初始化
product["id"] = 1
product["title"] = "口紅"
product["price"] = 199.33
for key, value := range product {
fmt.Println(key, value)
}
}
1.9 golang面向對象
1. 結構體
go語言中的結構體和Java中的類很相似,包含屬性,方法等內容。首字母大寫對其他包可見,首字母小寫只是對本包可見。
指針和值類型: 指針類型的方法可以修改屬性的值,值類型的不可以修改,
package main
import "fmt"
func main() {
student := &Student{"zhansn", 24}
fmt.Println(student.GetName())//zhansn
student.SetName("lisi")
fmt.Println(student.GetName()) //lisi
}
//對屬性小寫可以封裝
type Student struct {
name string
age int
}
//定義結構體的方法GetName和SetName
func (this Student) GetName() string {
return this.name
}
//這里使用指針可以改變屬性的內容
func (this *Student) SetName(name string) {
this.name = name
}
2. 繼承
在go語言中結構體和結構體沒有繼承,而是通過組合的方式來獲取其他結構體的方法。此時的Student可以使用Person的所有屬性和方法,無論是否封裝。
package main
import "fmt"
func main() {
student := &Student{}
fmt.Println(student.GetName()) // ""
student.SetName("lisi")
fmt.Println(student.GetName()) //lisi
}
//對屬性小寫可以封裝
type Student struct {
Person
}
type Person struct {
name string
age int
}
//定義類型的方法
func (this Person) GetName() string {
return this.name
}
//這里使用指針可以改變屬性的內容
func (this *Person) SetName(name string) {
this.name = name
}
3. 接口
golang接口中沒有變量,只有方法。
對於java中的多態描述:重載和重寫兩種多態。但是在golang中無法對方法進行重載,因為golang是一門面向函數編程的語言。所以golang可以通過重寫來實現多態,而且是接口和子類之間的重寫。
package main
import "fmt"
func main() {
ben := &Ben{"benchi"}
ao := &Ao{"aodi"}
ToString(ben)
ToString(ao)
}
type Car interface {
GetName() string
SetName(name string)
}
func ToString(car Car) {
fmt.Println(car.GetName())
}
type Ben struct {
name string
}
func (ben Ben) GetName() string {
return ben.name
}
func (ben *Ben) SetName(name string) {
ben.name = name
}
type Ao struct {
name string
}
func (this Ao) GetName() string {
return this.name
}
func (this *Ao) SetName(name string) {
this.name = name
}
4. 類型斷言
golang 類型斷言和java中的instanceof
關鍵字相似,但是又比這個關鍵字高級,好用,實現方式val.(T)
func main() {
var x interface{}
x = 4
if y, ok := x.(int); ok {
fmt.Println(y)
}
}
斷言接口子類
package main
import "fmt"
func main() {
ben := Ben{"benchi"}
ao := Ao{"aodi"}
ToString(ben)
ToString(ao)
}
type Car interface {
GetName() string
}
func ToString(car Car) {
if ben, ok := car.(Ben); ok {
fmt.Println(ben.GetName())
} else if ao, ok := car.(Ao); ok {
fmt.Println(ao.GetName())
} else {
fmt.Println("other type")
}
}
type Ben struct {
name string
}
func (ben Ben) GetName() string {
return ben.name
}
type Ao struct {
name string
}
func (this Ao) GetName() string {
return this.name
}
如下代碼,有什么問題呢?此時我們使用指針類型是實現了接口notify
的方法,那么在SendNotify(u notify)
中我們必須使用子類的指針作為參數傳遞到該函數,如果我們使用值類型實現接口notify
的方法,例如func (this user) Notify()
這樣既可以使用指針也可以使用值傳遞參數。
package main
import "fmt"
func main() {
u := &user{"hello"} //此時必須傳遞指針參數
SendNotify(u)
}
type notify interface {
Notify()
}
type user struct {
name string
}
//指針實現接口
func (this *user) Notify() {
fmt.Println(this.name)
}
func SendNotify(u notify) {
u.Notify()
}
這是為什么呢?
對於一個方法method(param T)
可以接受值類型
和指針類型
的參數,method(param *T)
僅僅可以接受指針類
型的參數。
5. 閉包
java中有函數式編程,集合框架中有一個消費型函數forEach
,我們在golang中通過閉包實現該函數
package main
import "fmt"
func main() {
data := []int{1,2,3,4,5}
forEach(data, func(index int, value interface{}) {
fmt.Println(index, value)
})
}
func forEach(data []int, f func(int, interface{})) {
for index, value := range data {
f(index, value)
}
}
2. golang 雜項
2.0 defer
defer
會在函數或者方法結束前被調用,和Java中finally
相似
func main() {
/**
first
hello world
defer is called
*/
say()
}
func say() {
fmt.Println("first")
defer fmt.Println("defer is called")
fmt.Println("hello world")
}
//由於return了,所以return后面的語句不會被執行
func say2() {
fmt.Println("first")
return
defer fmt.Println("defer is called")
fmt.Println("hello world")
}
defer
使用場景:錯誤處理,關閉資源,釋放鎖,后續會見到這些使用操作
2.1 錯誤處理
這里訴說的錯誤處理和Java中的異常處理一樣,在java語言中錯誤處理一般都是try...catch…finally,而在golang語言中通過三個關鍵字對錯誤盡心處理:(defer recover) panic
- defer+recover來捕獲異常
func catchError() {
defer func() {
err := recover()
if err != nil {
fmt.Println("出現異常", err)
}
}()
a := 10
b := 0
x := a / b
fmt.Println(x)
}
- 自定義異常
func catchError() {
//在這里捕獲處理,如果不進行捕獲,則程序會崩潰
defer func() {
err := recover()
if err != nil {
fmt.Println("出現異常", err)
}
}()
err := selfError()
//向外拋出異常
panic(err)
}
func selfError() error {
return errors.New("自定義異常")
}
2.2 日志
package main
import "log"
func main() {
// info:2019/02/04 16:47:25 LoggerDemo.go:6: message
log.Println("message")
}
func init() {
log.SetPrefix("info:")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
func main() {
log.Println("message")
//Fatalln執行之后調用:os.Exit(1), 退出程序,后續程序不再執行
log.Fatalln("打印日志,程序退出")
fmt.Println("還會執行嗎")
}
定制日志記錄器
package logger
import (
"log"
"os"
"io"
)
var (
Debug *log.Logger //僅僅輸出到控制台
Info *log.Logger
Warning *log.Logger
Error *log.Logger
)
const (
logFlag = log.LstdFlags | log.Lshortfile
)
func init() {
file, error := os.OpenFile("info.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if error != nil {
panic(error)
}
Debug = log.New(os.Stdout, "debug:", logFlag)
Info = log.New(io.MultiWriter(file, os.Stdout), "info:", logFlag)
Warning = log.New(os.Stdout, "waring:", logFlag)
Error = log.New(io.MultiWriter(file, os.Stderr), "error:", logFlag)
}
測試
package main
import "logger"
func main() {
logger.Debug.Println("debug")
logger.Info.Println("create a info log")
logger.Error.Println("create a errr log")
}
2.3 IO流
package main
import (
"os"
"fmt"
"bufio"
"io"
"io/ioutil"
)
func openFile() {
file, err := os.Open("info.log")
if err != nil {
fmt.Println("文件錯誤", err)
}
defer file.Close()
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF { //表示讀取完畢
break
}
fmt.Print(str)
}
}
//讀取內容到內存中
func openFile2() {
data, err := ioutil.ReadFile("info.log")
if err != nil {
}
fmt.Print(string(data))
}
func writeFile() {
file, err := os.OpenFile("hello.txt", os.O_WRONLY| os.O_CREATE | os.O_APPEND, 0666)
if err != nil {
fmt.Println("創建文件錯誤")
return
}
defer file.Close()
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString("寫入數據:\n")
}
writer.Flush() //將緩沖區內容寫入到文件中
}
//判斷文件是否存在
func IsExist() {
_, e := os.Stat("info2.log")
if e != nil {
exist := os.IsNotExist(e)
fmt.Println(exist)
}
}
2.5 json
package main
import (
"encoding/json"
"fmt"
"time"
)
type Book struct {
Title string
Author string
Publish time.Time
}
//序列化map
func serializeMap() {
student := make(map[string]interface{})
student["name"] = "閏土"
student["age"] = 20
student["class"] = "大一"
bytes, err := json.Marshal(student)
if err != nil {
fmt.Println("序列化錯誤")
}
fmt.Println(string(bytes)) //{"age":20,"class":"大一","name":"閏土"}
}
//序列化結構體
func serializeStruct() {
book := Book{"青春","itcloud", time.Now()}
bytes, _ := json.Marshal(book)
fmt.Println(string(bytes)) // {"Title":"青春","Author":"itcloud","Publish":"2019-02-05T11:14:51.094709+08:00"}
}
func deserializeMap() {
var student map[string]interface{}
data := `{"age":20,"class":"大一","name":"閏土"}`
err := json.Unmarshal([]byte(data), &student)
if err != nil {}
fmt.Println(student)
}
func deserializeStruct() {
var book Book
bookStr := `{"Title":"青春","Author":"itcloud","Publish":"2019-02-05T11:14:51.094709+08:00"}`
json.Unmarshal([]byte(bookStr), &book)
fmt.Println(book)
}
2.6 網絡編程
1. TCP
客戶端
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
conn, _ := net.Dial("tcp", "127.0.0.1:8088")
reader := bufio.NewReader(os.Stdin)
line, _ := reader.ReadString('\n')
n, _ := conn.Write([]byte(line))
fmt.Println(n)
}
服務端
package main
import (
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", "127.0.0.1:8088")
if flag := Checkout(err, "監聽開啟錯誤"); !flag {
return
}
defer listener.Close()
for {
fmt.Println("等待客戶端建立連接...")
conn, err := listener.Accept()
if flag := Checkout(err, "打開連接失敗"); flag {
fmt.Printf("conn= %v, ip = %v\n", conn, conn.RemoteAddr().String())
}
go process(conn)
}
}
func process (conn net.Conn) {
defer conn.Close()
for {
buf := make([]byte, 1024)
readLen, err := conn.Read(buf)
if flag := Checkout(err, "讀取失敗"); !flag {
return
}
fmt.Println(string(buf[:readLen]))
}
}
func Checkout(err error, msg string) bool {
if err != nil {
fmt.Println(msg, err)
return false
}
return true
}
2. http
func main() {
http.HandleFunc("/echo", echo)
http.ListenAndServe(":8080", nil)
}
func echo(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.Write([]byte("get body error"))
return
}
strlen, err := w.Write(body)
if err != nil && strlen != len(body) {
w.Write([]byte("write a error"))
}
}
package main
import (
"net/http"
"time"
)
//自定義handler
func main() {
myHandler := &SelfHandle{format: time.RFC1123}
http.Handle("/time", myHandler)
http.ListenAndServe(":8080", nil)
}
type SelfHandle struct {
format string
}
func (h *SelfHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
forTime := time.Now().Format(h.format)
w.Write([]byte("time is " + forTime))
}
多路復用處理器
package main
import "net/http"
//多路復用處理器
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", hello)
mux.HandleFunc("/world", world)
server := &http.Server{Addr: ":8080", Handler: mux}
server.ListenAndServe()
}
func hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
}
func world(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("word"))
}