欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

通過與Java功能上的對(duì)比來學(xué)習(xí)Go語言

 更新時(shí)間:2023年02月15日 08:18:39   作者:京東科技?韓國(guó)凱  
這篇文章主要介紹了通過與Java功能上的對(duì)比來學(xué)習(xí)Go語言的相關(guān)資料,需要的朋友可以參考下

前言

Go語言定義

??Go(又稱 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 開發(fā)的一種靜態(tài)、強(qiáng)類型、編譯型語言。Go 語言語法與 C 相近,但功能上有:內(nèi)存安全,GC,結(jié)構(gòu)形態(tài)及 CSP-style 并發(fā)計(jì)算??。

適用范圍

本篇文章適用于學(xué)習(xí)過其他面向?qū)ο笳Z言(Java、Php),但沒有學(xué)過Go語言的初學(xué)者。文章主要從Go與Java功能上的對(duì)比來闡述Go語言的基礎(chǔ)語法、面向?qū)ο缶幊獭⒉l(fā)與錯(cuò)誤四個(gè)方面。

一、基礎(chǔ)語法

Go語言的基礎(chǔ)語法與常規(guī)的編程語言基本類似,所不同的有聲明變量的方式,數(shù)組、切片、字典的概念及功能與Java不太相同,不過Java中這些數(shù)據(jù)結(jié)構(gòu)都可以通過類比功能的方式在Go中使用。

1.1 變量、常量、nil與零值、方法、包、可見性、指針

1.1.1 變量聲明

Go語言中有兩種方式

1.使用??var??關(guān)鍵字聲明,且需要注意的是,與大多數(shù)強(qiáng)類型語言不同,Go語言的聲明變量類型位于變量名稱的后面。Go語句結(jié)束不需要分號(hào)。?

?var num int??

??var result string = "this is result"??

2.使用??:=??賦值。

??num := 3?? 等同于 ??var num int = 3??

其中變量的類型會(huì)根據(jù)右側(cè)的值進(jìn)行匹配,例如"3"會(huì)匹配為int,"3.0"會(huì)匹配為float64,"result"會(huì)匹配為string。

1.1.2 常量聲明

使用??const??來聲明一個(gè)常量,一個(gè)常量在聲明后不可改變。?

const laugh string = "go"??

1.1.3 nil與零值

只聲明未賦值的變量,其值為nil。類似于java中的“null” 。

沒有明確初始值的變量聲明會(huì)被賦予它們的 零值。

零值是:

  • 數(shù)值類型為 ??0??,
  • 布爾類型為 ??false??,
  • 字符串為 ??""??(空字符串)。

1.1.4 方法、包

Go中方法的定義

使用func關(guān)鍵字來定義一個(gè)方法,后面跟方法名,然后是參數(shù),返回值(如果有的話,沒有返回值則不寫)。

??func MethodName(p1 Parm, p2 Parm) int{}??

//學(xué)習(xí)一個(gè)語言應(yīng)該從Hello World開始!
package main

import "fmt"

func main() {
fmt.Println("Hello World!")// Hello World!
fmt.Println(add(3, 5)) //8
var sum = add(3, 5)
}

func add(a int, b int) int{
return a+b;
}
多個(gè)返回值

Go 函數(shù)與其他編程語言一大不同之處在于支持多返回值,這在處理程序出錯(cuò)的時(shí)候非常有用。例如,如果上述 ??add?? 函數(shù)只支持非負(fù)整數(shù)相加,傳入負(fù)數(shù)則會(huì)報(bào)錯(cuò)。

//返回值只定義了類型 沒有定義返回參數(shù)
func add(a, b int) (int, error) {
if a < 0 || b < 0 {
err := errors.New("只支持非負(fù)整數(shù)相加")
return 0, err
}
a *= 2
b *= 3
return a + b, nil
}

//返回值還定義了參數(shù) 這樣可以直接return 并且定義的參數(shù)可以直接使用 return時(shí)只會(huì)返回這兩個(gè)參數(shù)
func add1(a, b int) (z int, err error) {
if a < 0 || b < 0 {
err := errors.New("只支持非負(fù)整數(shù)相加")
return //實(shí)際返回0 err 因?yàn)閦只定義沒有賦值 則nil值為0
}
a *= 2
b *= 3
z = a + b
return //返回 z err
}

func main() {
x, y := -1, 2
z, err := add(x, y)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}
變長(zhǎng)參數(shù)
func myfunc(numbers ...int) {
for _, number := range numbers {
fmt.Println(number)
}
}

slice := []int{1, 2, 3, 4, 5}
//使用...將slice打碎傳入
myfunc(slice...)
包與可見性

在 Go 語言中,無論是變量、函數(shù)還是類屬性和成員方法,它們的可見性都是以包為維度的,而不是類似傳統(tǒng)面向編程那樣,類屬性和成員方法的可見性封裝在所屬的類中,然后通過 ??private??、??protected?? 和 ??public?? 這些關(guān)鍵字來修飾其可見性。

Go 語言沒有提供這些關(guān)鍵字,不管是變量、函數(shù),還是自定義類的屬性和成員方法,它們的可見性都是根據(jù)其首字母的大小寫來決定的,如果變量名、屬性名、函數(shù)名或方法名首字母大寫,就可以在包外直接訪問這些變量、屬性、函數(shù)和方法,否則只能在包內(nèi)訪問,因此 Go 語言類屬性和成員方法的可見性都是包一級(jí)的,而不是類一級(jí)的。

假如說一個(gè)名為??domain??的文件夾下有3個(gè).go文件,則三個(gè)文件中的??package??都應(yīng)為??domain??,其中程序的入口main方法所在的文件,包為??main??

//定義了此文件屬于 main 包
package main

//通過import導(dǎo)入標(biāo)注庫(kù)中包
import "fmt"

func main() {
fmt.Println("Hello World!")// Hello World!
fmt.Println(add(3, 5)) //8
var sum = add(3, 5)
}

func add(a int, b int) int{
return a+b;
}

1.1.5 指針

對(duì)于學(xué)過C語言來說,指針還是比較熟悉的,我所理解的指針,其實(shí)就是一個(gè)在內(nèi)存中實(shí)際的16進(jìn)制的地址值,引用變量的值通過此地址去內(nèi)存中取出對(duì)應(yīng)的真實(shí)值。

func main() {
i := 0
//使用&來傳入地址
fmt.Println(&i) //0xc00000c054

var a, b int = 3 ,4
//傳入 0xc00000a089 0xc00000a090
fmt.Println(add(&a, &b))
}

//使用*來聲明一個(gè)指針類型的參數(shù)與使用指針
func add(a *int, b *int)int{
//接收到 0xc00000a089 0xc00000a090
//前往 0xc00000a089位置查找具體數(shù)據(jù) 并取賦給x
x := *a
//前往 0xc00000a090位置查找具體數(shù)據(jù) 并取賦給y
y := *b
return x+y
}

1.2 條件、循環(huán)、分支

1.2.1 條件

與Java語言的if基本相同

// if
if condition {
// do something
}

// if...else...
if condition {
// do something
} else {
// do something
}

// if...else if...else...
if condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}

1.2.2 循環(huán)

sum := 0

//普通for循環(huán)
for i := 1; i <= 100; i++ {
sum += i
}

//無限循環(huán)
for{
sum++
if sum = 100{
break;
}
}

//帶條件的循環(huán)
for res := sum+1; sum < 15{
sum++
res++
}

//使用kv循環(huán)一個(gè)map或一個(gè)數(shù)組 k為索引或鍵值 v為值 k、v不需要時(shí)可以用_帶替
for k, v := range a {
fmt.Println(k, v)
}

1.2.3 分支

score := 100
switch score {
case 90, 100:
fmt.Println("Grade: A")
case 80:
fmt.Println("Grade: B")
case 70:
fmt.Println("Grade: C")
case 65:
fmt.Println("Grade: D")
default:
fmt.Println("Grade: F")
}

1.3 數(shù)組、切片、字典

1.3.1 數(shù)組

數(shù)組功能與Java語言類似,都是長(zhǎng)度不可變,并且可以使用多維數(shù)組,也可以通過arrays[i]來存儲(chǔ)或獲取值。

//聲明
var nums [3]int
//聲明并初始化
var nums = [3]int{1,2,3} <==> nums:=[3]int{1,2,3}

//使用
for sum := 0, i := 0;i<10{
sum += nums[i]
i++
}
//修改值
num[0] = -1

數(shù)組使用較為簡(jiǎn)單,但是存在著難以解決的問題:長(zhǎng)度固定 。

例如當(dāng)我們?cè)诔绦蛑行枰粋€(gè)數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)獲取到的所有用戶,因?yàn)橛脩魯?shù)量是會(huì)隨著時(shí)間變化的,但是數(shù)組其長(zhǎng)度卻不可改變,所以數(shù)組并不適合存儲(chǔ)長(zhǎng)度會(huì)發(fā)生改變的數(shù)據(jù)。因此在Go語言中通過使用切片來解決以上問題。

1.3.2 切片

切片相比于Java來說是一種全新的概念。在Java中,對(duì)于不定長(zhǎng)的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu),可以使用List接口來完成操作,例如有ArrayList與LinkList,這些接口可以實(shí)現(xiàn)數(shù)據(jù)的隨時(shí)添加與獲取,并沒有對(duì)長(zhǎng)度進(jìn)行限制。但是在Go中不存在這樣的接口,而是通過切片(Slice)來完成不定長(zhǎng)的數(shù)據(jù)長(zhǎng)度存儲(chǔ)。

切片與數(shù)組最大的不同就是切片不用聲明長(zhǎng)度。但是切片與數(shù)組并非毫無關(guān)系,數(shù)組可以看作是切片的底層數(shù)組,而切片則可以看作是數(shù)組某個(gè)連續(xù)片段的引用。切片可以只使用數(shù)組的一部分元素或者整個(gè)數(shù)組來創(chuàng)建,甚至可以創(chuàng)建一個(gè)比所基于的數(shù)組還要大的切片:

長(zhǎng)度、容量

??切片的長(zhǎng)度就是它所包含的元素個(gè)數(shù)。??

??切片的容量是從它的第一個(gè)元素開始數(shù),到其底層數(shù)組元素末尾的個(gè)數(shù)。??

??切片 s 的長(zhǎng)度和容量可通過表達(dá)式 len(s) 和 cap(s) 來獲取。??

切片的長(zhǎng)度從功能上類比與Java中List的size(),即通過len(slice)來感知切片的長(zhǎng)度,即可對(duì)len(slice)進(jìn)行循環(huán),來動(dòng)態(tài)控制切片內(nèi)的具體內(nèi)容。切片的容量在實(shí)際開發(fā)中運(yùn)用不多,了解其概念即可。

創(chuàng)建切片
//聲明一個(gè)數(shù)組
var nums =[3]int{1, 2, 3}
//0.直接聲明
var slice =[]int{0, 1, 2}

//1.從數(shù)組中引用切片 其中a:b是指包括a但不包括b
var slice1 = nums[0:2] //{1,2}
//如果不寫的則默認(rèn)為0(左邊)或最大值(右邊)
var slice2 = slice1[:2] <==> var slice2 = slice1[0:] <==>var slice2 = slice1[:]

//2.使用make創(chuàng)建Slice 其中int為切片類型,4為其長(zhǎng)度,5為容量
slice3 := make([]int, 5)
slice4 := make([]int, 4, 5)
動(dòng)態(tài)操作切片
//使用append向切片中動(dòng)態(tài)的添加元素
func append(s []T, vs ...T) []T

slice5 := make([]int, 4, 5) //{0, 0, 0, 0}
slice5 = append(slice5, 1) //{0,0,0,0,1}

//刪除第一個(gè)0
sliece5 = slice5[1:]
切片的常用場(chǎng)景

模擬上述提到的問題使用切片解決方案

//聲明切片
var userIds = []int{}
//模擬獲取所有用戶ID
for i := 0; i< 100{
userIds = append(userIdS, i);
i++;
}
//對(duì)用戶信息進(jìn)行處理
for k,v := range userIds{
userIds[k] = v++
}

1.3.3 字典

字典也可稱為 ‘鍵值對(duì)’ 或 ‘key-value’,是一種常用的數(shù)據(jù)結(jié)構(gòu),Java中有各種Map接口,常用的有HashMap等。在Go中通過使用字典來實(shí)現(xiàn)鍵值對(duì)的存儲(chǔ),字典是無序的,所以不會(huì)根據(jù)添加順序來保證數(shù)據(jù)的順序。

字典的聲明與初始化
//string為鍵類型,int為值類型
maps := map[string]int{
"java" : 1,
"go" : 2,
"python" : 3,
}

//還可以通過make來創(chuàng)建字典 100為其初始容量 超出可擴(kuò)容
maps = make(map[string]int, 100)
字典的使用場(chǎng)景
//直接使用
fmt.Println(maps["java"]) //1

//賦值
maps["go"] = 4

//取值 同時(shí)判斷map中是否存在該鍵 ok為bool型
value, ok := maps["one"]
if ok { // 找到了
// 處理找到的value
}

//刪除
delete(testMap, "four")

二、面向?qū)ο缶幊?/h2>

2.1 Go語言中的類

眾所周知,在面向?qū)ο蟮恼Z言中,一個(gè)類應(yīng)該具有屬性、構(gòu)造方法、成員方法三種結(jié)構(gòu),Go語言也不例外。

2.1.1 類的聲明與初始化

Go語言中并沒有明確的類的概念,只有??struct??關(guān)鍵字可以從功能上類比為 面向?qū)ο笳Z言中的“類” 。比如要定義一個(gè)學(xué)生類,可以這么做:

type Student struct {
id int
name string
male bool
score float64
}//定義了一個(gè)學(xué)生類,屬性有id name等,每個(gè)屬性的類型都在其后面

//定義學(xué)生類的構(gòu)造方法
func NewStudent(id uint, name string, male bool, score float64) *Student {
return &Student{id, name, male, score}
}

//實(shí)例化一個(gè)類對(duì)象
student := NewStudent(1, "學(xué)院君", 100)
fmt.Println(student)

2.1.2 成員方法

Go中的成員方法聲明與其他語言不大相同。以Student類為例,

//在方法名前,添加對(duì)應(yīng)的類,即可認(rèn)為改方法為該類的成員方法。
func (s Student) GetName() string {
return s.name
}

//注意這里的Student是帶了*的 這是因?yàn)樵诜椒▊髦颠^程中 存在著值傳遞與引用傳遞 即指針的概念 當(dāng)使用值傳遞時(shí) 編譯器會(huì)為該參數(shù)創(chuàng)建一個(gè)副本傳入 因此如果對(duì)副本進(jìn)行修改其實(shí)是不生效的 因?yàn)樵趫?zhí)行完此方法后該副本會(huì)被銷毀 所以此處應(yīng)該是用*Student 將要修改的對(duì)象指針傳入 修改值才能起作用
func (s *Student) SetName(name string) {
//這里其實(shí)是應(yīng)該使用(*s).name = name,因?yàn)閷?duì)于一個(gè)地址來說 其屬性是沒意義的 不過這樣使用也是可以的 因?yàn)榫幾g器會(huì)幫我們自動(dòng)轉(zhuǎn)換
s.name = name
}

2.2 接口

接口在 Go 語言中有著至關(guān)重要的地位,如果說 goroutine 和 channel 是支撐起 Go 語言并發(fā)模型的基石,那么接口就是 Go 語言整個(gè)類型系統(tǒng)的基石。Go 語言的接口不單單只是接口,下面就讓我們一步步來探索 Go 語言的接口特性。

2.2.1 傳統(tǒng)侵入式接口實(shí)現(xiàn)

和類的實(shí)現(xiàn)相似,Go 語言的接口和其他語言中提供的接口概念完全不同。以 Java、PHP 為例,接口主要作為不同類之間的契約(Contract)存在,對(duì)契約的實(shí)現(xiàn)是強(qiáng)制的,體現(xiàn)在具體的細(xì)節(jié)上就是如果一個(gè)類實(shí)現(xiàn)了某個(gè)接口,就必須實(shí)現(xiàn)該接口聲明的所有方法,這個(gè)叫「履行契約」:

// 聲明一個(gè)'iTemplate'接口
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}


// 實(shí)現(xiàn)接口
// 下面的寫法是正確的
class Template implements iTemplate
{
private $vars = array();

public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}

public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}

return $template;
}
}

這個(gè)時(shí)候,如果有另外有一個(gè)接口 ??iTemplate2?? 聲明了與 ??iTemplate?? 完全一樣的接口方法,甚至名字也叫 ??iTemplate??,只不過位于不同的命名空間下,編譯器也會(huì)認(rèn)為上面的類 ??Template?? 只實(shí)現(xiàn)了 ??iTemplate?? 而沒有實(shí)現(xiàn) ??iTemplate2?? 接口。

這在我們之前的認(rèn)知中是理所當(dāng)然的,無論是類與類之間的繼承,還是類與接口之間的實(shí)現(xiàn),在 Java、PHP 這種單繼承語言中,存在著嚴(yán)格的層級(jí)關(guān)系,一個(gè)類只能直接繼承自一個(gè)父類,一個(gè)類也只能實(shí)現(xiàn)指定的接口,如果沒有顯式聲明繼承自某個(gè)父類或者實(shí)現(xiàn)某個(gè)接口,那么這個(gè)類就與該父類或者該接口沒有任何關(guān)系。

我們把這種接口稱為侵入式接口,所謂「侵入式」指的是實(shí)現(xiàn)類必須明確聲明自己實(shí)現(xiàn)了某個(gè)接口。這種實(shí)現(xiàn)方式雖然足夠明確和簡(jiǎn)單明了,但也存在一些問題,尤其是在設(shè)計(jì)標(biāo)準(zhǔn)庫(kù)的時(shí)候,因?yàn)闃?biāo)準(zhǔn)庫(kù)必然涉及到接口設(shè)計(jì),接口的需求方是業(yè)務(wù)實(shí)現(xiàn)類,只有具體編寫業(yè)務(wù)實(shí)現(xiàn)類的時(shí)候才知道需要定義哪些方法,而在此之前,標(biāo)準(zhǔn)庫(kù)的接口就已經(jīng)設(shè)計(jì)好了,我們要么按照約定好的接口進(jìn)行實(shí)現(xiàn),如果沒有合適的接口需要自己去設(shè)計(jì),這里的問題就是接口的設(shè)計(jì)和業(yè)務(wù)的實(shí)現(xiàn)是分離的,接口的設(shè)計(jì)者并不能總是預(yù)判到業(yè)務(wù)方要實(shí)現(xiàn)哪些功能,這就造成了設(shè)計(jì)與實(shí)現(xiàn)的脫節(jié)。

接口的過分設(shè)計(jì)會(huì)導(dǎo)致某些聲明的方法實(shí)現(xiàn)類完全不需要,如果設(shè)計(jì)的太簡(jiǎn)單又會(huì)導(dǎo)致無法滿足業(yè)務(wù)的需求,這確實(shí)是一個(gè)問題,而且脫離了用戶使用場(chǎng)景討論這些并沒有意義,以 PHP 自帶的 ? ?SessionHandlerInterface?? 接口為例,該接口聲明的接口方法如下:

SessionHandlerInterface {
/* 方法 */
abstract public close ( void ) : bool
abstract public destroy ( string $session_id ) : bool
abstract public gc ( int $maxlifetime ) : int
abstract public open ( string $save_path , string $session_name ) : bool
abstract public read ( string $session_id ) : string
abstract public write ( string $session_id , string $session_data ) : bool
}

用戶自定義的 Session 管理器需要實(shí)現(xiàn)該接口,也就是要實(shí)現(xiàn)該接口聲明的所有方法,但是實(shí)際在做業(yè)務(wù)開發(fā)的時(shí)候,某些方法其實(shí)并不需要實(shí)現(xiàn),比如如果我們基于 Redis 或 Memcached 作為 Session 存儲(chǔ)器的話,它們自身就包含了過期回收機(jī)制,所以 ??gc?? 方法根本不需要實(shí)現(xiàn),又比如 ??close?? 方法對(duì)于大部分驅(qū)動(dòng)來說,也是沒有什么意義的。

正是因?yàn)檫@種不合理的設(shè)計(jì),所以在編寫 PHP 類庫(kù)中的每個(gè)接口時(shí)都需要糾結(jié)以下兩個(gè)問題(Java 也類似):

  • 一個(gè)接口需要聲明哪些接口方法?
  • 如果多個(gè)類實(shí)現(xiàn)了相同的接口方法,應(yīng)該如何設(shè)計(jì)接口?比如上面這個(gè) ??SessionHandlerInterface??,有沒有必要拆分成多個(gè)更細(xì)分的接口,以適應(yīng)不同實(shí)現(xiàn)類的需要?

接下我們來看看 Go 語言的接口是如何避免這些問題的。

2.2.2 Go 語言的接口實(shí)現(xiàn)

在 Go 語言中,類對(duì)接口的實(shí)現(xiàn)和子類對(duì)父類的繼承一樣,并沒有提供類似 ??implement?? 這種關(guān)鍵字顯式聲明該類實(shí)現(xiàn)了哪個(gè)接口,一個(gè)類只要實(shí)現(xiàn)了某個(gè)接口要求的所有方法,我們就說這個(gè)類實(shí)現(xiàn)了該接口。

例如,我們定義了一個(gè) ??File?? 類,并實(shí)現(xiàn)了 ??Read()??、??Write()??、??Seek()??、??Close()?? 四個(gè)方法:

type File struct {
// ...
}

func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Seek(off int64, whence int) (pos int64, err error)
func (f *File) Close() error

假設(shè)我們有如下接口(Go 語言通過關(guān)鍵字 ??interface?? 來聲明接口,以示和結(jié)構(gòu)體類型的區(qū)別,花括號(hào)內(nèi)包含的是待實(shí)現(xiàn)的方法集合):

type IFile interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Seek(off int64, whence int) (pos int64, 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
}

盡管 ??File?? 類并沒有顯式實(shí)現(xiàn)這些接口,甚至根本不知道這些接口的存在,但是我們說 ??File?? 類實(shí)現(xiàn)了這些接口,因?yàn)?nbsp;??File?? 類實(shí)現(xiàn)了上述所有接口聲明的方法。當(dāng)一個(gè)類的成員方法集合包含了某個(gè)接口聲明的所有方法,換句話說,如果一個(gè)接口的方法集合是某個(gè)類成員方法集合的子集,我們就認(rèn)為該類實(shí)現(xiàn)了這個(gè)接口。

與 Java、PHP 相對(duì),我們把 Go 語言的這種接口稱作非侵入式接口,因?yàn)轭惻c接口的實(shí)現(xiàn)關(guān)系不是通過顯式聲明,而是系統(tǒng)根據(jù)兩者的方法集合進(jìn)行判斷。這樣做有兩個(gè)好處:

  • 其一,Go 語言的標(biāo)準(zhǔn)庫(kù)不需要繪制類庫(kù)的繼承/實(shí)現(xiàn)樹圖,在 Go 語言中,類的繼承樹并無意義,你只需要知道這個(gè)類實(shí)現(xiàn)了哪些方法,每個(gè)方法是干什么的就足夠了。
  • 其二,定義接口的時(shí)候,只需要關(guān)心自己應(yīng)該提供哪些方法即可,不用再糾結(jié)接口需要拆得多細(xì)才合理,也不需要為了實(shí)現(xiàn)某個(gè)接口而引入接口所在的包,接口由使用方按需定義,不用事先設(shè)計(jì),也不用考慮之前是否有其他模塊定義過類似接口。

這樣一來,就完美地避免了傳統(tǒng)面向?qū)ο缶幊讨械慕涌谠O(shè)計(jì)問題。

三、并發(fā)與多線程

3.1 Goroutine

對(duì)于任何一個(gè)優(yōu)秀的語言來說,并發(fā)處理的能力都是決定其優(yōu)劣的關(guān)鍵。在Go語言中,通過Goroutine來實(shí)現(xiàn)并發(fā)的處理。

func say(s string) {
fmt.Println(s)
}

func main() {
//通過 go 關(guān)鍵字新開一個(gè)協(xié)程
go say("world")
say("hello")
}

Go語言中沒有像Java那么多的鎖來限制資源同時(shí)訪問,只提供了Mutex來進(jìn)行同步操作。

//給類SafeCounter添加鎖
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}

// Inc 增加給定 key 的計(jì)數(shù)器的值。
func (c *SafeCounter) Inc(key string) {
//給該對(duì)象上鎖
c.mux.Lock()
// Lock 之后同一時(shí)刻只有一個(gè) goroutine 能訪問 c.v
c.v[key]++
//解鎖
c.mux.Unlock()
}

3.2 Channel

多協(xié)程之間通過Channel進(jìn)行通信,從功能上可以類比為Java的volatile關(guān)鍵字。

??ch := make(chan int)?? 聲明一個(gè)int型的Channel,兩個(gè)協(xié)程之間可以通過??ch??進(jìn)行int數(shù)據(jù)通信。

通過Channel進(jìn)行數(shù)據(jù)傳輸。

ch <- v // 將 v 發(fā)送至信道 ch。
v := <-ch // 從 ch 接收值并賦予 v。
package main

import "fmt"

func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 將和送入 c
}

//對(duì)于main方法來說 相當(dāng)于就是開啟了一個(gè)協(xié)程
func main() {
s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int)
//通過go關(guān)鍵字開啟兩個(gè)協(xié)程 將chaneel當(dāng)做參數(shù)傳入
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
//通過箭頭方向獲取或傳入信息
x, y := <-c, <-c // 從 c 中接收

fmt.Println(x, y, x+y)
}

四、錯(cuò)誤處理

4.1 error

Go 語言錯(cuò)誤處理機(jī)制非常簡(jiǎn)單明了,不需要學(xué)習(xí)了解復(fù)雜的概念、函數(shù)和類型,Go 語言為錯(cuò)誤處理定義了一個(gè)標(biāo)準(zhǔn)模式,即 ??error?? 接口,該接口的定義非常簡(jiǎn)單:

type error interface {
Error() string
}

其中只聲明了一個(gè) ??Error()?? 方法,用于返回字符串類型的錯(cuò)誤消息。對(duì)于大多數(shù)函數(shù)或類方法,如果要返回錯(cuò)誤,基本都可以定義成如下模式 —— 將錯(cuò)誤類型作為第二個(gè)參數(shù)返回:

func Foo(param int) (n int, err error) {
// ...
}

然后在調(diào)用返回錯(cuò)誤信息的函數(shù)/方法時(shí),按照如下「衛(wèi)述語句」模板編寫處理代碼即可:

n, err := Foo(0)

if err != nil {
// 錯(cuò)誤處理
} else{
// 使用返回值 n
}

非常簡(jiǎn)潔優(yōu)雅。

4.2 defer

defer用于確保一個(gè)方法執(zhí)行完成之后,無論執(zhí)行結(jié)果是否成功,都要執(zhí)行defer中的語句。類似于Java中的try..catch..finally用法。例如在文件處理中,無論結(jié)果是否成功,都要關(guān)閉文件流。

func ReadFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
//無論結(jié)果如何 都要關(guān)閉文件流
defer f.Close()

var n int64 = bytes.MinRead

if fi, err := f.Stat(); err == nil {
if size := fi.Size() + bytes.MinRead; size > n {
n = size
}
}
return readAll(f, n)
}

4.3 panic

Go語言中沒有太多的異常類,不像Java一樣有Error、Exception等錯(cuò)誤類型,當(dāng)然也沒有try..catch語句。

Panic(恐慌),意味在程序運(yùn)行中出現(xiàn)了錯(cuò)誤,如果該錯(cuò)誤未被捕獲的話,就會(huì)造成系統(tǒng)崩潰退出。例如一個(gè)簡(jiǎn)單的panic:??a := 1/0??。

就會(huì)引發(fā)panic: integer divide by zero。

其中第一行表示出問題的協(xié)程,第二行是問題代碼所在的包和函數(shù),第三行是問題代碼的具體位置,最后一行則是程序的退出狀態(tài),通過這些信息,可以幫助你快速定位問題并予以解決。

4.4 recover

當(dāng)有可以預(yù)見的錯(cuò)誤時(shí),又不希望程序崩潰退出,可以使用recover()語句來捕獲未處理的panic。recover應(yīng)當(dāng)放在defer語句中,且該語句應(yīng)該在方法中前部,避免未能執(zhí)行到defer語句時(shí)就引發(fā)了系統(tǒng)異常退出。

package main

import (
"fmt"
)

func divide() {
//通過defer,確保該方法只要執(zhí)行完畢都要執(zhí)行該匿名方法
defer func() {
//進(jìn)行異常捕獲
if err := recover(); err != nil {
fmt.Printf("Runtime panic caught: %v\n", err)
}
}()

var i = 1
var j = 0
k := i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}

func main() {
divide()
fmt.Println("divide 方法調(diào)用完畢,回到 main 函數(shù)")
}

可以看到,雖然會(huì)出現(xiàn)異常,但我們使用recover()捕獲之后,就不會(huì)出現(xiàn)系統(tǒng)崩潰退出的情形,而只是將該方法結(jié)束。其中??fmt.Printf("%d / %d = %d\n", i, j, k)??語句并沒有執(zhí)行到,因?yàn)榇a執(zhí)行到他的上一步已經(jīng)出現(xiàn)異常導(dǎo)致該方法提前結(jié)束。
4 recover

當(dāng)有可以預(yù)見的錯(cuò)誤時(shí),又不希望程序崩潰退出,可以使用recover()語句來捕獲未處理的panic。recover應(yīng)當(dāng)放在defer語句中,且該語句應(yīng)該在方法中前部,避免未能執(zhí)行到defer語句時(shí)就引發(fā)了系統(tǒng)異常退出。

package main

import (
"fmt"
)

func divide() {
//通過defer,確保該方法只要執(zhí)行完畢都要執(zhí)行該匿名方法
defer func() {
//進(jìn)行異常捕獲
if err := recover(); err != nil {
fmt.Printf("Runtime panic caught: %v\n", err)
}
}()

var i = 1
var j = 0
k := i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}

func main() {
divide()
fmt.Println("divide 方法調(diào)用完畢,回到 main 函數(shù)")
}

可以看到,雖然會(huì)出現(xiàn)異常,但我們使用recover()捕獲之后,就不會(huì)出現(xiàn)系統(tǒng)崩潰退出的情形,而只是將該方法結(jié)束。其中??fmt.Printf("%d / %d = %d\n", i, j, k)??語句并沒有執(zhí)行到,因?yàn)榇a執(zhí)行到他的上一步已經(jīng)出現(xiàn)異常導(dǎo)致該方法提前結(jié)束。

五、總結(jié)

通過以上的學(xué)習(xí),大家可以以使用為目的的初步了解到go的基礎(chǔ)語法,但是僅憑本文想要學(xué)明白go是完全不夠的。例如go的最大優(yōu)勢(shì)之一“協(xié)程”,由于文章目的就沒有特別詳細(xì)展開,有興趣的同學(xué)可以繼續(xù)學(xué)習(xí)。

到此這篇關(guān)于通過與Java功能上的對(duì)比來學(xué)習(xí)Go語言的文章就介紹到這了,更多相關(guān)對(duì)比Java來學(xué)習(xí)Go語言內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go項(xiàng)目實(shí)現(xiàn)優(yōu)雅關(guān)機(jī)與平滑重啟功能

    Go項(xiàng)目實(shí)現(xiàn)優(yōu)雅關(guān)機(jī)與平滑重啟功能

    無論是優(yōu)雅關(guān)機(jī)還是優(yōu)雅重啟歸根結(jié)底都是通過監(jiān)聽特定系統(tǒng)信號(hào),然后執(zhí)行一定的邏輯處理保障當(dāng)前系統(tǒng)正在處理的請(qǐng)求被正常處理后再關(guān)閉當(dāng)前進(jìn)程,這篇文章主要介紹了Go實(shí)現(xiàn)優(yōu)雅關(guān)機(jī)與平滑重啟 ,需要的朋友可以參考下
    2022-10-10
  • go語言讀取csv文件并輸出的方法

    go語言讀取csv文件并輸出的方法

    這篇文章主要介紹了go語言讀取csv文件并輸出的方法,實(shí)例分析了go語言操作csv文件的技巧,需要的朋友可以參考下
    2015-03-03
  • Go標(biāo)準(zhǔn)庫(kù)之Requests的介紹與基本使用

    Go標(biāo)準(zhǔn)庫(kù)之Requests的介紹與基本使用

    Python中的Requests庫(kù)非常強(qiáng)大,所以Go開發(fā)者模仿Python的Requests庫(kù),由此誕生了Grequests庫(kù),本文主要介紹了Requests的基本使用,有需要的可以參考下
    2024-04-04
  • 一文帶你輕松學(xué)會(huì)Go語言動(dòng)態(tài)調(diào)用函數(shù)

    一文帶你輕松學(xué)會(huì)Go語言動(dòng)態(tài)調(diào)用函數(shù)

    這篇文章主要是帶大家學(xué)習(xí)一下Go語言是如何動(dòng)態(tài)調(diào)用函數(shù)的,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的可以參考下
    2022-11-11
  • Golang的md5 hash計(jì)算操作

    Golang的md5 hash計(jì)算操作

    這篇文章主要介紹了Golang的md5 hash計(jì)算操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Gin框架自帶參數(shù)校驗(yàn)的使用詳解

    Gin框架自帶參數(shù)校驗(yàn)的使用詳解

    這篇文章主要為大家詳細(xì)介紹了如何使用Gin框架自帶的參數(shù)校驗(yàn),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下
    2023-09-09
  • Go語言為什么很少使用數(shù)組原理解析

    Go語言為什么很少使用數(shù)組原理解析

    這篇文章主要為大家介紹了Go語言為什么很少使用數(shù)組原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語言基礎(chǔ)數(shù)組用法及示例詳解

    Go語言基礎(chǔ)數(shù)組用法及示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)Go語言數(shù)組的用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • golang rate令牌桶源碼分析實(shí)現(xiàn)方式

    golang rate令牌桶源碼分析實(shí)現(xiàn)方式

    這篇文章主要介紹了golang rate令牌桶源碼分析實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Go-RESTful實(shí)現(xiàn)下載功能思路詳解

    Go-RESTful實(shí)現(xiàn)下載功能思路詳解

    這篇文章主要介紹了Go-RESTful實(shí)現(xiàn)下載功能,文件下載包括文件系統(tǒng)IO和網(wǎng)絡(luò)IO,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10

最新評(píng)論