鍍金池/ 教程/ GO/ 面向?qū)ο?/span>
面向?qū)ο?/span>
Go 語言簡(jiǎn)介
網(wǎng)頁下載
聊天室的開發(fā)
image 網(wǎng)站開發(fā)
基本語法
高級(jí)應(yīng)用

面向?qū)ο?/h1>

Go 的優(yōu)勢(shì)

  • 可以編譯成機(jī)器碼,不依賴其他庫
  • 靜態(tài)類型語言,有動(dòng)態(tài)語言的感覺。靜態(tài)語言就是可以在編譯的時(shí)候檢查出來隱藏的大多數(shù)問題,動(dòng)態(tài)語言的感覺-就是很多的包可以使用,寫起來效率很高
  • 語言層面支持并發(fā),這是 Go 語言的最大的特色,天生支持并發(fā)(天生的基因和后來的整容是有區(qū)別的)
  • 內(nèi)置 runtime,支持垃圾回收機(jī)制
  • 簡(jiǎn)單易學(xué),有C語言的基因,Go 語言有 25 個(gè)關(guān)鍵字,但是表達(dá)能力極強(qiáng)
  • 豐富的標(biāo)準(zhǔn)庫,內(nèi)置大量庫,特別是網(wǎng)絡(luò)庫非常強(qiáng)大
  • 內(nèi)置強(qiáng)大工具,使 review 變的簡(jiǎn)單,可以使代碼格式一模一樣
  • 跨平臺(tái)編譯,可以不依賴與系統(tǒng)信息
  • 內(nèi)置 C 支持

Go 適合

  • 服務(wù)器編程
  • 分布式系統(tǒng),數(shù)據(jù)庫代理器
  • 網(wǎng)絡(luò)編程
  • 云平臺(tái),國內(nèi)的七牛云存儲(chǔ)的關(guān)鍵服務(wù)就是使用Go語言開發(fā)的

Go 的缺點(diǎn)

  • Go的 import 包不支持版本,升級(jí)容易導(dǎo)致項(xiàng)目不可運(yùn)行,所以需要自己控制相應(yīng)的版本信息
  • Go的 gotoutine 一旦啟動(dòng),不同的 goroutine 之間切換不收程序的控制,runtime 調(diào)度的時(shí)候,需要嚴(yán)謹(jǐn)?shù)倪壿嫞?/li>
  • 不然 goroutine 休眠,過一段時(shí)間邏輯結(jié)束了,突然冒出來又執(zhí)行了,會(huì)導(dǎo)致邏輯出錯(cuò)等情況
  • GC 延遲有點(diǎn)大

Go 語言的面向?qū)ο筇匦?/h2>

Go 語言的大多數(shù)類型都是值語義,并且都可以包含對(duì)應(yīng)的操作方法。在需要的時(shí)候,你可以給內(nèi)置類型“增加”新方法。兒在實(shí)現(xiàn)某個(gè)具體的接口時(shí),無需從該接口繼承,只需要實(shí)現(xiàn)該接口要求的所有方法即可。任何類型都可以被Any 類型引用。Any 類型就是空接口,即interface{}。

給類型添加方法

type Integer int

func (a Integer) Less(b Integer) bool {
    return a < b
}

func main() {
    fmt.Println("Hello")
    var a, b Integer = 1,2
    fmt.Println(a,b)
    res := a.Less(b)
    fmt.Println(res)
}

上面給 Integer 添加了一個(gè) Less 方法第一個(gè) a 是給 a 添加方法,b 是參數(shù),bool 是函數(shù)返回值。

在 Go 語言中,面向?qū)ο蟮纳衩孛婕啿粡?fù)存在,不像 C++ 和 Java,方法都默認(rèn)有一個(gè)隱藏的 this 指針,Python 中方法中的 self 和 this 指針的作用是一樣的。Go 中方法施加的目標(biāo)(對(duì)象),顯示傳遞;不需要非得是指針,也不用非得叫this。只有在需要修改對(duì)象的時(shí)候才有必要使用指針。比如:

func (a *Integer) Add(b Integer) {
    *a += b
}

Go 語言和 C 語言是一樣的,都是基于值傳遞的。要想修改變量的值,只能傳遞指針。

值語義和引用語義

Go 語言大多數(shù)類型都基于值語義:基本類型+符合類型(數(shù)組、結(jié)構(gòu)體、指針)。和 C 語言不同的,數(shù)組的傳遞也是基于值語義的,而且很純粹。比如:

var arrayA [3]int = [3]int{1,2,3}
arrayB := arrayA
arrayB[1]++
fmt.Println(arrayA,arrayB)

會(huì)看到輸出,對(duì) B 中元素的操作,對(duì) A 并沒有任何影響。這表明在將 A 賦值給 B 的時(shí)候是數(shù)組內(nèi)容的完整復(fù)制,如果想要表達(dá)引用,需要使用指針: arrayB := &arrayA

數(shù)組切片、map、channel、接口看起來想引用類型

結(jié)構(gòu)體

Go 中的結(jié)構(gòu)體和其他語言的類有相同的地位,但是 Go 語言中沒有繼承在內(nèi)的大量面向?qū)ο蟮奶匦裕槐A袅私M合(composition)這個(gè)最基礎(chǔ)的特性。比如下面的例子對(duì) log 的使用

type Job struct {
    Command string
    *log.Logger
}

func NewJob(command string, logger *log.Logger) *Job {
    return &Job{command,logger}
}

func (job *Job)Start(){
    job.Println("Starting now....")
    job.Println("end")
}

func main() {
    mylog := log.New(os.Stdout, "",log.LstdFlags)
    j := NewJob("test",mylog)
    j.Start()
}

定義的 Job 結(jié)構(gòu)體包含一個(gè)匿名的 log.Logger 指針,在合適的賦值之后可以在 Job 類型的所有成員方法中可以很舒適的借用所有 log.Logger 提供的方法。類似于 Java 和 C++ 中繼承了 log.Logger

Go 語言中并沒有其他語言中的 public 等權(quán)限訪問控制的關(guān)鍵字。要使某個(gè)符號(hào)對(duì)其他包可見,需要將該符號(hào)定義為以大寫字母開頭,就想上面定義的 Job 結(jié)構(gòu)體首字母需要大寫。成員方法的訪問性遵循同樣的規(guī)則,如果上面的 Start 函數(shù)定義為 start,則該函數(shù)只能在該類型所在的包內(nèi)使用。

接口

Go 語言中的接口不需要像 C++ 和 Java 中的使用:或者 implements 關(guān)鍵字來實(shí)現(xiàn)接口,在 Go 語言中,一個(gè)類只需要實(shí)現(xiàn)了接口要求的所有函數(shù),那么這個(gè)類就實(shí)現(xiàn)了該接口。下面的例子:

//定義一個(gè)文件接口
type IFile interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
    Close() error
}

type IReader interface {
    Read(buf []byte) (n int, err error)
}

type IWriter interface {
    Write(buf []byte) (n int, err error)
}

type ICloser interface {
    Close() error
}

//定義文件類
type File struct {
    filename string
    fid int16
}
//定義文件的幾個(gè)方法
func (f *File)Read(buf []byte) (n int, err error){
    fmt.Println("File Opened")
    return 0,nil
}
func (f *File)Write(buf []byte) (n int,err error) {
    fmt.Println("File Writed")
    return 0,nil
}
func (f *File)Close() error {
    fmt.Println("File Closed")
    return nil
}

func main() {
    bytes := []byte{0,1,2,3,4,5,6,7,8,9}
    var file1 IFile = new(File)
    file1.Read(bytes)

    var file2 IReader = new(File)
    file2.Read(bytes)

    var file3 IWriter = new(File)
    file3.Write(bytes)

    var file4 ICloser = new(File)
    file4.Close()
}

File 類實(shí)現(xiàn)了上面的所有接口,所以可以直接進(jìn)行賦值。Go 語言的接口是非侵入式接口:

  • Go 語言標(biāo)準(zhǔn)庫不需要繪制類庫的繼承圖,在 Go 中,類的繼承樹并無意義,只有知道這個(gè)類實(shí)現(xiàn)了那些方法,每個(gè)方法是什么含義即可
  • 實(shí)現(xiàn)類的時(shí)候,只需要關(guān)系自己應(yīng)該提供那些方法,不用再糾結(jié)接口需要拆的多細(xì)才合理,按需定義
  • 不用為了實(shí)現(xiàn)一個(gè)即可而導(dǎo)入一個(gè)包,因?yàn)槎嘁靡粋€(gè)外部的包就意味著更多的耦合

接口還可以通過組合來實(shí)現(xiàn):

type IReaderWriter interface {
    IReader
    IWriter
}

和下面的實(shí)現(xiàn)是一樣的:

type IReaderWriter interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
}

Go 語言中的 Any 類型

Java 語言中所有的對(duì)象都繼承自 Object,而 Go 語言中的任何對(duì)象的實(shí)例都滿足空接口 interface{},所以 interface{}看起來就想指向任何對(duì)象的 Any 類型。

var v1 interface{} = 1
var v2 interface{} = "abc"
var v3 interface{} = &v2
var v4 interface{} = struct { x int }{1}

面向?qū)ο笕筇匦?/h2>

有過 C++ 語言學(xué)習(xí)經(jīng)歷的朋友都知道,面向?qū)ο笾饕巳齻€(gè)基本特征:封裝、繼承和多態(tài)。封裝,就是指運(yùn)行的數(shù)據(jù)和函數(shù)綁定在一起,C++ 中主要是通過 this 指針來完成的;繼承,就是指 class 之間可以相互繼承屬性和函數(shù);多態(tài),主要就是用統(tǒng)一的接口來處理通用的邏輯,每個(gè) class 只需要按照接口實(shí)現(xiàn)自己的回調(diào)函數(shù)就可以了。

作為集大成者的 Go 語言,自然不會(huì)在面向?qū)ο笊厦鏌o所作為。相比較 C++、java、C# 等面向?qū)ο笳Z言而言,它的面向?qū)ο蟾?jiǎn)單,也更容易理解。下面,我們不妨用三個(gè)簡(jiǎn)單的例子來說明一下 go 語言下的面向?qū)ο笫鞘裁礃拥摹?/p>

封裝特性

package main  

import "fmt"  

type data struct {  

    val int  
}  

func (p_data* data)set(num int) {  

    p_data.val = num  
}  

func (p_data* data)show() {  

    fmt.Println(p_data.val)  
}  

func main() {  

    p_data := &data{4}  
    p_data.set(5)  
    p_data.show()  

}  

繼承特性

package main  

import "fmt"  

type parent struct {  

    val int  
}  

type child struct {  

    parent  
    num int  
}  

func main() {  

    var c child  

    c = child{parent{1}, 2}  
    fmt.Println(c.num)  
    fmt.Println(c.val)  

}  

多態(tài)特性

package main  

import "fmt"  

type act interface {  

    write()  
}  

type xiaoming struct {  

}  

type xiaofang struct {  

}  

func (xm *xiaoming) write() {  

    fmt.Println("xiaoming write")  
}  

func (xf *xiaofang) write() {  

    fmt.Println("xiaofang write")  
}  

func main() {  

    var w act;  

    xm := xiaoming{}  
    xf := xiaofang{}  

    w = &xm  
    w.write()  

    w = &xf  
    w.write()  
}