30 Minuten für den Einstieg in die Grundkenntnisse von Go (Java Kid Edition)

Vorwort

Gehen Sie zur Sprachdefinition

Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态、强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC,结构形态及 CSP-style 并发计算

Geltungsbereich

Dieser Artikel eignet sich für Anfänger, die andere objektorientierte Sprachen (Java, PHP) gelernt haben, aber noch nicht die Go-Sprache gelernt haben. Der Artikel erläutert hauptsächlich die vier Aspekte der grundlegenden Syntax der Go-Sprache, der objektorientierten Programmierung, der Parallelität und der Fehler aus dem Vergleich der Funktionen von Go und Java .

1. Grundlegende Grammatik

Die grundlegende Syntax der Go-Sprache ähnelt im Wesentlichen der der herkömmlichen Programmiersprachen. Die Konzepte und Funktionen von Arrays, Slices und Wörterbüchern sind jedoch nicht mit denen von Java identisch In Java können alle analog in Go verwendet werden.

1.1 Variablen, Konstanten, Null- und Nullwerte, Methoden, Pakete, Sichtbarkeit, Zeiger

1.1.1 Variablendeklaration

In der Go-Sprache gibt es zwei Möglichkeiten

1. Verwenden Sie vardie Schlüsselwortdeklaration. Beachten Sie, dass im Gegensatz zu den meisten stark typisierten Sprachen der deklarierte Variablentyp in der Go-Sprache nach dem Variablennamen steht. Am Ende einer Go-Anweisung ist kein Semikolon erforderlich.

var num int

var result string = "this is result"

2. :=Zuweisung verwenden.

num := 3Gleichwertigvar num int = 3

Der Typ der Variablen wird entsprechend dem Wert auf der rechten Seite abgeglichen. Beispielsweise entspricht „3“ dem Wert „int“, „3.0“ dem Wert „float64“ und „result“ dem Wert „string“.

1.1.2 Konstante Deklaration

Wird zum Deklarieren einer Konstante verwendet const. Eine Konstante kann nach der Deklaration nicht mehr geändert werden.

const laugh string = "go"

1.1.3 Null- und Nullwert

Deklarieren Sie nur nicht zugewiesene Variablen, deren Wert Null ist. Ähnlich wie „ null“ in Java .

Variablendeklarationen ohne explizite Anfangswerte erhalten ihren Nullwert .

Der Nullwert ist:

  • Der numerische Typ ist 0,

  • Der boolesche Typ ist false:

  • String ist ""(leerer String).

1.1.4 Methoden und Pakete

Definition von Methoden in Go

Verwenden Sie das Schlüsselwort func, um eine Methode zu definieren, gefolgt vom Methodennamen, dann den Parametern und dem Rückgabewert (falls vorhanden, wenn kein Rückgabewert vorhanden ist, wird dieser nicht geschrieben).

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

//学习一个语言应该从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;
}

Mehrere Rückgabewerte

Ein großer Unterschied zwischen Go-Funktionen und anderen Programmiersprachen besteht darin, dass sie mehrere Rückgabewerte unterstützen, was bei der Behandlung von Programmfehlern sehr nützlich ist. Wenn die obige Funktion beispielsweise addnur die Addition nicht negativer Ganzzahlen unterstützt, wird ein Fehler gemeldet, wenn eine negative Zahl übergeben wird.

//返回值只定义了类型 没有定义返回参数
func add(a, b int) (int, error) {
    if a < 0 || b < 0 {
        err := errors.New("只支持非负整数相加")
        return 0, err
    }
    a *= 2
    b *= 3
    return a + b, nil
}

//返回值还定义了参数 这样可以直接return 并且定义的参数可以直接使用 return时只会返回这两个参数
func add1(a, b int) (z int, err error) {
    if a < 0 || b < 0 {
        err := errors.New("只支持非负整数相加")
        return   //实际返回0 err 因为z只定义没有赋值 则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)
}

Parameter mit variabler Länge

func myfunc(numbers ...int) {
    for _, number := range numbers {
        fmt.Println(number)
    }
}

slice := []int{1, 2, 3, 4, 5}
//使用...将slice打碎传入
myfunc(slice...)

Pakete und Sichtbarkeit

In der Go-Sprache basiert ihre Sichtbarkeit unabhängig davon, ob es sich um Variablen, Funktionen oder Klassenattribute und Mitgliedsmethoden handelt, auf der Paketdimension und nicht wie bei der herkömmlichen Programmierung, bei der die Sichtbarkeit von Klassenattributen und Mitgliedsmethoden in der Klasse gekapselt ist, zu der sie gehören Sie gehören dazu privateund ändern dann die Sichtbarkeit mit diesen Schlüsselwörtern protectedund .public

Die Go-Sprache stellt diese Schlüsselwörter nicht bereit, unabhängig davon, ob es sich um Variablen, Funktionen oder Attribute und Mitgliedsmethoden benutzerdefinierter Klassen handelt. Ihre Sichtbarkeit wird anhand der Groß- und Kleinschreibung ihrer Anfangsbuchstaben bestimmt Mit dem ersten Buchstaben des Methodennamens können Sie direkt auf diese Variablen, Eigenschaften, Funktionen und Methoden außerhalb des Pakets zugreifen, andernfalls ist der Zugriff auf sie nur innerhalb des Pakets möglich. Daher liegt die Sichtbarkeit von Go-Sprachklassenattributen und Mitgliedsmethoden im Paket Niveau und nicht Klasse eins.

Wenn domainsich in einem Ordner mit dem Namen drei .go-Dateien befinden, packagesollten diese drei Dateien sein domain. Darunter befinden sich die Datei, in der sich die Hauptmethode des Programms befindet, und das Paketmain

//定义了此文件属于 main 包
package main

//通过import导入标注库中包
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 Hinweise

Für diejenigen, die die C-Sprache studiert haben, ist der Zeiger, den ich verstehe, tatsächlich ein tatsächlicher hexadezimaler Adresswert im Speicher. Der Wert der Referenzvariablen wird verwendet, um den entsprechenden realen Wert abzurufen.

func main() {
    i := 0
    //使用&来传入地址
    fmt.Println(&i) //0xc00000c054
    
    var a, b int = 3 ,4
    //传入 0xc00000a089 0xc00000a090
    fmt.Println(add(&a, &b)) 
}

//使用*来声明一个指针类型的参数与使用指针
func add(a *int, b *int)int{
    //接收到 0xc00000a089 0xc00000a090
    //前往 0xc00000a089位置查找具体数据 并取赋给x
    x := *a
    //前往 0xc00000a090位置查找具体数据 并取赋给y
    y := *b
	return x+y
}

1.2 Bedingungen, Schleifen, Verzweigungen

1.2.1 Bedingungen

Im Grunde dasselbe wie in der Java-Sprache

// 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 Schleife

sum := 0 

//普通for循环
for i := 1; i <= 100; i++ { 
    sum += i 
}

//无限循环
for{
    sum++
    if sum = 100{
        break;
    }
}

//带条件的循环
for res := sum+1; sum < 15{
    sum++
    res++
}

//使用kv循环一个map或一个数组  k为索引或键值 v为值 k、v不需要时可以用_带替
for k, v := range a {
    fmt.Println(k, v)
}

1.2.3 Zweigstelle

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 Arrays, Slices, Wörterbücher

1.3.1 Array

Die Array-Funktion ähnelt der Java-Sprache, die Länge ist unveränderlich und es können mehrdimensionale Arrays verwendet werden, oder Werte können über Arrays [i] gespeichert oder abgerufen werden.

//声明
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

Arrays sind relativ einfach zu verwenden, aber es gibt ein Problem, das schwer zu lösen ist: feste Länge .

Wenn wir beispielsweise im Programm eine Datenstruktur benötigen, um alle erhaltenen Benutzer zu speichern, da sich die Anzahl der Benutzer mit der Zeit ändert, die Länge des Arrays jedoch nicht geändert werden kann, ist das Array nicht zum Speichern von Daten geeignet, deren Länge sich ändert . Daher werden die oben genannten Probleme durch die Verwendung von Slices in der Go-Sprache gelöst.

1.3.2 Schneiden

Slicing ist im Vergleich zu Java ein völlig neues Konzept. In Java können Sie für Datenspeicherstrukturen variabler Länge die List-Schnittstelle verwenden, um Vorgänge wie ArrayList und LinkList abzuschließen. Diese Schnittstellen können jederzeit Daten hinzufügen und abrufen, und es gibt keine Begrenzung der Länge. In Go gibt es jedoch keine solche Schnittstelle. Stattdessen erfolgt die Speicherung variabler Datenlänge über Slices .

Der größte Unterschied zwischen Slices und Arrays besteht darin, dass Slices die Länge nicht angeben müssen. Aber Slices sind nicht unabhängig von Arrays. Ein Array kann als das zugrunde liegende Array des Slice betrachtet werden, und das Slice kann als Referenz auf ein kontinuierliches Fragment des Arrays betrachtet werden. Slices können nur aus einem Teil eines Arrays oder aus dem gesamten Array erstellt werden, und es ist sogar möglich, ein Slice zu erstellen, das größer als das zugrunde liegende Array ist:

Länge, Kapazität

切片的长度就是它所包含的元素个数。

切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。

切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。

Die Länge des Slice ist funktional analog zur size() von List in Java, das heißt, die Länge des Slice wird durch len(slice) wahrgenommen, und len(slice) kann in einer Schleife ausgeführt werden, um den spezifischen Inhalt in der dynamisch zu steuern Scheibe. Die Kapazität von Slices wird in der tatsächlichen Entwicklung nicht viel genutzt. Verstehen Sie einfach das Konzept.

Erstellen Sie Scheiben

//声明一个数组
var nums =[3]int{1, 2, 3}
//0.直接声明
var slice =[]int{0, 1, 2}

//1.从数组中引用切片 其中a:b是指包括a但不包括b
var slice1 = nums[0:2] //{1,2}
//如果不写的则默认为0(左边)或最大值(右边)
var slice2 = slice1[:2] <==> var slice2 = slice1[0:] <==>var slice2 = slice1[:]

//2.使用make创建Slice 其中int为切片类型,4为其长度,5为容量
slice3 := make([]int, 5)
slice4 := make([]int, 4, 5)

Dynamischer Betrieb von Slices

//使用append向切片中动态的添加元素
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}

//删除第一个0
sliece5 = slice5[1:]

Häufige Szenarien für das Slicing

Simulieren Sie das oben genannte Problem mit der Slicing-Lösung

//声明切片
var userIds = []int{}
//模拟获取所有用户ID
for i := 0; i< 100{
    userIds = append(userIdS, i);
    i++;
}
//对用户信息进行处理
for k,v := range userIds{
    userIds[k] = v++
}

1.3.3 Wörterbuch

Wörterbuch, auch als „Schlüssel-Wert-Paar“ oder „Schlüsselwert“ bekannt, ist eine häufig verwendete Datenstruktur. In Java gibt es verschiedene Map-Schnittstellen, und die am häufigsten verwendeten sind HashMap usw. In Go werden Wörterbücher zum Speichern von Schlüssel-Wert-Paaren verwendet. Wörterbücher sind ungeordnet, daher ist die Reihenfolge der Daten nicht auf der Grundlage der Additionsreihenfolge garantiert.

Wörterbuchdeklaration und Initialisierung

//string为键类型,int为值类型
maps := map[string]int{
  "java" : 1,
  "go" : 2,
  "python" : 3,
}

//还可以通过make来创建字典 100为其初始容量 超出可扩容
maps = make(map[string]int, 100)

Szenarien für die Verwendung von Wörterbüchern

//直接使用
fmt.Println(maps["java"]) //1

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

//取值 同时判断map中是否存在该键 ok为bool型
value, ok := maps["one"] 
if ok { // 找到了
  // 处理找到的value 
}

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

2. Objektorientierte Programmierung

2.1 Kurse in der Go-Sprache

Wie wir alle wissen, sollte eine Klasse in objektorientierten Sprachen drei Strukturen haben: Attribute, Konstruktoren und Mitgliedsmethoden, und die Go-Sprache bildet da keine Ausnahme.

2.1.1 Klassendeklaration und Initialisierung

In der Go-Sprache gibt es kein klares Klassenkonzept. structIn objektorientierten Sprachen können nur Schlüsselwörter funktional mit „Klassen“ verglichen werden. Um beispielsweise eine Schülerklasse zu definieren, können Sie Folgendes tun:

type Student struct {
    id int
    name string
    male bool
    score float64
}//定义了一个学生类,属性有id name等,每个属性的类型都在其后面

//定义学生类的构造方法
func NewStudent(id uint, name string, male bool, score float64) *Student {
    return &Student{id, name, male, score}
}

//实例化一个类对象
student := NewStudent(1, "学院君", 100)
fmt.Println(student)

2.1.2 Mitgliedsmethoden

Die Deklaration der Mitgliedsmethode in Go ist nicht dieselbe wie in anderen Sprachen. Am Beispiel der Student-Klasse:

//在方法名前,添加对应的类,即可认为改方法为该类的成员方法。
func (s Student) GetName() string  {
    return s.name
}

//注意这里的Student是带了*的 这是因为在方法传值过程中 存在着值传递与引用传递 即指针的概念 当使用值传递时 编译器会为该参数创建一个副本传入 因此如果对副本进行修改其实是不生效的 因为在执行完此方法后该副本会被销毁 所以此处应该是用*Student 将要修改的对象指针传入 修改值才能起作用
func (s *Student) SetName(name string) {
    //这里其实是应该使用(*s).name = name,因为对于一个地址来说 其属性是没意义的 不过这样使用也是可以的 因为编译器会帮我们自动转换
    s.name = name
}

2.2 Schnittstelle

Schnittstellen spielen in der Go-Sprache eine wichtige Rolle. Wenn Goroutine und Kanal die Eckpfeiler sind, die das Parallelitätsmodell der Go-Sprache unterstützen, dann sind Schnittstellen die Eckpfeiler des gesamten Typsystems der Go-Sprache . Die Schnittstelle der Go-Sprache ist nicht nur eine Schnittstelle. Lassen Sie uns Schritt für Schritt die Schnittstellenfunktionen der Go-Sprache erkunden.

2.2.1 Traditionelle aufdringliche Schnittstellenimplementierung

Ähnlich wie bei der Implementierung von Klassen unterscheidet sich das Schnittstellenkonzept der Go-Sprache vollständig von den Schnittstellenkonzepten anderer Sprachen. Am Beispiel von Java und PHP existieren Schnittstellen hauptsächlich als Verträge zwischen verschiedenen Klassen, und die Implementierung des Vertrags ist obligatorisch. Dies spiegelt sich in den spezifischen Details wider: Wenn eine Klasse eine bestimmte Schnittstelle implementiert, muss sie alle deklarierten Methoden implementieren , das nennt man „Vertragserfüllung“:

// 声明一个'iTemplate'接口
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}


// 实现接口
// 下面的写法是正确的
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;
    }
}

Wenn es zu diesem Zeitpunkt eine andere Schnittstelle gibt , die eine Schnittstellenmethode iTemplate2deklariert , die genau mit dieser identisch ist, auch wenn sie denselben Namen hat, sich aber in einem anderen Namespace befindet, geht der Compiler auch davon aus, dass die obige Klasse nur implementiert und nicht implementiert wird die Schnittstelle.iTemplateiTemplateTemplateiTemplateiTemplate2

Dies ist nach unserem bisherigen Verständnis eine Selbstverständlichkeit. Unabhängig davon, ob es sich um die Vererbung zwischen Klassen oder die Implementierung zwischen Klassen und Schnittstellen handelt, gibt es in einzelnen Vererbungssprachen wie Java und PHP eine strikte hierarchische Beziehung. Eine Klasse kann nur direkt von einer erben übergeordnete Klasse, und eine Klasse kann nur eine bestimmte Schnittstelle implementieren, wenn sie nicht explizit als Erbschaft von einer übergeordneten Klasse oder als Implementierung einer bestimmten Schnittstelle deklariert ist.

Wir nennen diese Art von Schnittstelle eine aufdringliche Schnittstelle . Das sogenannte „aufdringlich“ bedeutet, dass die Implementierungsklasse explizit deklarieren muss, dass sie eine Schnittstelle implementiert. Obwohl diese Implementierungsmethode klar und einfach genug ist, gibt es immer noch einige Probleme, insbesondere beim Entwurf der Standardbibliothek, da die Standardbibliothek ein Schnittstellendesign beinhalten muss. Der Nachfrager der Schnittstelle ist die Geschäftsimplementierungsklasse, und nur die Geschäftsimplementierungsklasse kann dies tun Erst dann wissen wir, welche Methoden definiert werden müssen. Die Schnittstelle der Standardbibliothek kann entweder entsprechend der vereinbarten Schnittstelle entworfen werden Das Problem besteht darin, dass Schnittstellendesign und Geschäftsimplementierung nicht immer vorhersehbar sind, was zu einer Diskrepanz zwischen Design und Implementierung führt.

Ein übermäßiges Design der Schnittstelle führt dazu, dass einige deklarierte Methodenimplementierungsklassen völlig unnötig sind. Wenn das Design zu einfach ist, wird es den Anforderungen des Unternehmens nicht gerecht, und es ist sinnlos, diese ohne Benutzer zu diskutieren Anwendungsszenarien: Nehmen Sie als Beispiel die mit PHP gelieferte Schnittstelle SessionHandlerInterface. Die von dieser Schnittstelle deklarierten Schnittstellenmethoden lauten wie folgt:

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
}

Der benutzerdefinierte Sitzungsmanager muss diese Schnittstelle implementieren, das heißt, er muss alle von dieser Schnittstelle deklarierten Methoden implementieren. Bei der tatsächlichen Geschäftsentwicklung müssen jedoch einige Methoden nicht implementiert werden Redis oder Memcached als Sitzung Da Speicher selbst einen Ablaufrecyclingmechanismus enthalten, gcmuss die Methode beispielsweise closefür die meisten Treiber überhaupt nicht implementiert werden.

Gerade wegen dieses unvernünftigen Designs müssen Sie beim Schreiben jeder Schnittstelle in der PHP-Klassenbibliothek (Java ist ähnlich) mit den folgenden zwei Problemen kämpfen:

  1. Welche Schnittstellenmethoden müssen für eine Schnittstelle deklariert werden?

  2. Wenn mehrere Klassen dieselbe Schnittstellenmethode implementieren, wie sollte die Schnittstelle gestaltet sein? Ist es beispielsweise SessionHandlerInterfaceerforderlich, die obige Schnittstelle in mehrere, stärker unterteilte Schnittstellen aufzuteilen, um sie an die Anforderungen verschiedener Implementierungsklassen anzupassen?

Schauen wir uns als Nächstes an, wie die Go-Sprachschnittstelle diese Probleme vermeidet.

2.2.2 Implementierung der Go-Sprachschnittstelle

In der Go-Sprache ist die Implementierung einer Schnittstelle durch eine Klasse dasselbe wie die Vererbung einer Unterklasse von einer übergeordneten Klasse. Es gibt kein implementSchlüsselwort wie dieses, um explizit anzugeben, welche Schnittstelle die Klasse implementiert Methoden, die von einer Schnittstelle benötigt werden, sagen wir, dass diese Klasse die Schnittstelle implementiert .

Beispielsweise definieren wir eine Klasse und Fileimplementieren Read()vier Methoden:Write()Seek()Close()

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

Angenommen, wir haben die folgende Schnittstelle (die Go-Sprache verwendet das Schlüsselwort, um interfacedie Schnittstelle zu deklarieren, um den Unterschied zum Strukturtyp anzuzeigen, und die geschweiften Klammern enthalten den Satz der zu implementierenden Methoden):

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 
}

Obwohl Filedie Klasse diese Schnittstellen nicht explizit implementiert und nicht einmal von der Existenz dieser Schnittstellen weiß, sagen wir, dass Filedie Klasse diese Schnittstellen implementiert, weil Filedie Klasse die von allen oben genannten Schnittstellen deklarierten Methoden implementiert. Wenn der Mitgliedsmethodensatz einer Klasse alle von einer Schnittstelle deklarierten Methoden enthält, d. h. wenn der Methodensatz einer Schnittstelle eine Teilmenge des Mitgliedsmethodensatzes einer bestimmten Klasse ist, betrachten wir die Klasse als Implementierung der Schnittstelle.

Im Vergleich zu Java und PHP nennen wir diese Schnittstelle der Go-Sprache eine nicht aufdringliche Schnittstelle , da die Implementierungsbeziehung zwischen einer Klasse und einer Schnittstelle nicht explizit deklariert wird, sondern vom System anhand des Methodensatzes der beiden beurteilt wird. Dies hat zwei Vorteile:

  • Erstens muss die Standardbibliothek der Go-Sprache nicht das Vererbungs-/Implementierungsbaumdiagramm der Klassenbibliothek zeichnen. In der Go-Sprache ist der Vererbungsbaum der Klasse bedeutungslos. Sie müssen nur wissen, welche Methoden die Klasse implementiert was jede Methode bewirkt.

  • Zweitens müssen Sie sich beim Definieren einer Schnittstelle nur darum kümmern, welche Methoden Sie bereitstellen sollen. Sie müssen sich nicht mehr darum kümmern, wie detailliert die Schnittstelle sein muss. Es ist nicht erforderlich, das Paket einzuführen, in dem sich die Schnittstelle befindet Implementieren Sie eine Schnittstelle. Die Definition erfolgt bei Bedarf durch den Benutzer. Es ist nicht erforderlich, sie im Voraus zu entwerfen, und es ist nicht erforderlich, zu berücksichtigen, ob andere Module zuvor ähnliche Schnittstellen definiert haben.

Auf diese Weise werden die Schnittstellendesignprobleme der herkömmlichen objektorientierten Programmierung perfekt vermieden.

3. Parallelität und Multithreading

3.1 Goroutine

Für jede hervorragende Sprache ist die Fähigkeit zur gleichzeitigen Verarbeitung der Schlüssel zur Bestimmung ihrer Vorzüge. In der Go-Sprache wird die Parallelitätsverarbeitung durch Goroutine implementiert.

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

func main() {
    //通过 go 关键字新开一个协程
	go say("world")
	say("hello")
}

Die Go-Sprache verfügt nicht über so viele Sperren wie Java, um den gleichzeitigen Zugriff auf Ressourcen einzuschränken, und stellt Mutex nur für Synchronisationsvorgänge bereit.

//给类SafeCounter添加锁
type SafeCounter struct {
	v   map[string]int
	mux sync.Mutex
}

// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
    //给该对象上锁
	c.mux.Lock()
	// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
	c.v[key]++
    //解锁
	c.mux.Unlock()
}

3.2 Kanal

Die Kommunikation zwischen mehreren Coroutinen erfolgt über Channel, der funktional mit dem volatilen Schlüsselwort von Java vergleichbar ist.

ch := make(chan int)chDeklarieren Sie einen Kanal vom Typ int, und int-Daten können zwischen zwei Coroutinen kommuniziert werden.

Die Datenübertragung erfolgt über den Kanal.

ch <- v    // 将 v 发送至信道 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
}

//对于main方法来说 相当于就是开启了一个协程
func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
    //通过go关键字开启两个协程 将chaneel当做参数传入
	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)
}

4. Fehlerbehandlung

4.1 Fehler

Der Fehlerbehandlungsmechanismus der Go-Sprache ist sehr einfach und klar. Es ist nicht erforderlich, komplexe Konzepte, Funktionen und Typen zu lernen. Die Go-Sprache definiert ein Standardmuster für die Fehlerbehandlung, dh errordie Definition der Schnittstelle :

type error interface { 
    Error() string 
}

Es wird nur eine Error()Methode deklariert, die eine Fehlermeldung vom Typ String zurückgibt. Wenn Sie bei den meisten Funktionen oder Klassenmethoden einen Fehler zurückgeben möchten, können Sie ihn grundsätzlich wie folgt definieren: Geben Sie den Fehlertyp als zweiten Parameter zurück:

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

Wenn Sie dann die Funktion/Methode aufrufen, die Fehlerinformationen zurückgibt, schreiben Sie einfach den Verarbeitungscode gemäß der folgenden Vorlage „Wei Shu Statement“:

n, err := Foo(0)

if err != nil { 
    // 错误处理 
} else{
    // 使用返回值 n 
}

Sehr schlicht und elegant.

4.2 aufschieben

Mit defer wird sichergestellt, dass nach der Ausführung einer Methode die Anweisungen in defer ausgeführt werden, unabhängig davon, ob das Ausführungsergebnis erfolgreich ist oder nicht. Ähnlich der Verwendung von try..catch..finally in Java. Beispielsweise muss bei der Dateiverarbeitung der Dateistream geschlossen werden, unabhängig davon, ob das Ergebnis erfolgreich ist oder nicht.

func ReadFile(filename string) ([]byte, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    //无论结果如何 都要关闭文件流
    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 Panik

Im Gegensatz zu Java gibt es nicht viele Ausnahmeklassen wie Error und Exception. Natürlich gibt es keine try..catch-Anweisungen.

Panik bedeutet, dass während des Programmbetriebs ein Fehler auftritt. Wenn der Fehler nicht erkannt wird, führt dies zum Absturz und Beenden des Systems. Zum Beispiel eine einfache Panik: a := 1/0.

Es wird Panik auslösen: Ganzzahl durch Null dividieren.

Bild.png

Die erste Zeile gibt die Coroutine mit dem Problem an, die zweite Zeile ist das Paket und die Funktion, in der sich der Problemcode befindet, die dritte Zeile ist der spezifische Speicherort des Problemcodes und die letzte Zeile ist der Exit-Status des Programms Informationen können Ihnen dabei helfen, das Problem schnell zu lokalisieren und zu beheben.

4.4 erholen

Wenn ein vorhersehbarer Fehler auftritt und Sie nicht möchten, dass das Programm abstürzt und beendet wird, können Sie die unbehandelte Panik mit der Anweisung „recover()“ erfassen. Recovery sollte in der Defer-Anweisung platziert werden und die Anweisung sollte am Anfang der Methode stehen, um zu verhindern, dass das System abnormal beendet wird, wenn die Defer-Anweisung nicht ausgeführt werden kann.

package main

import (
    "fmt"
)

func divide() {
    //通过defer,确保该方法只要执行完毕都要执行该匿名方法
    defer func() {
        //进行异常捕获
        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 方法调用完毕,回到 main 函数")
}

Bild.png

Es ist ersichtlich, dass zwar eine Ausnahme auftritt, das System jedoch nicht abstürzt und beendet wird, nachdem wir sie mit „recover()“ erfasst haben, sondern lediglich die Methode beendet. Die fmt.Printf("%d / %d = %d\n", i, j, k)Anweisung wurde nicht ausgeführt, da bei der Ausführung des Codes bis zum vorherigen Schritt eine Ausnahme aufgetreten ist, wodurch die Methode vorzeitig beendet wurde.
4 erholen

Wenn ein vorhersehbarer Fehler auftritt und Sie nicht möchten, dass das Programm abstürzt und beendet wird, können Sie die unbehandelte Panik mit der Anweisung „recover()“ erfassen. Recovery sollte in der Defer-Anweisung platziert werden und die Anweisung sollte am Anfang der Methode stehen, um zu verhindern, dass das System abnormal beendet wird, wenn die Defer-Anweisung nicht ausgeführt werden kann.

package main

import (
    "fmt"
)

func divide() {
    //通过defer,确保该方法只要执行完毕都要执行该匿名方法
    defer func() {
        //进行异常捕获
        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 方法调用完毕,回到 main 函数")
}

Es ist ersichtlich, dass zwar eine Ausnahme auftritt, das System jedoch nicht abstürzt und beendet wird, nachdem wir sie mit „recover()“ erfasst haben, sondern lediglich die Methode beendet. Die fmt.Printf("%d / %d = %d\n", i, j, k)Anweisung wurde nicht ausgeführt, da bei der Ausführung des Codes bis zum vorherigen Schritt eine Ausnahme aufgetreten ist, wodurch die Methode vorzeitig beendet wurde.

5. Zusammenfassung

Durch die obige Studie können Sie zunächst die grundlegende Syntax von go für den Verwendungszweck verstehen, aber dieser Artikel allein reicht nicht aus, um go zu lernen. Einer der größten Vorteile von Go, „Coroutine“, wird beispielsweise aufgrund des Zwecks des Artikels nicht näher erläutert. Interessierte Schüler können weiter lernen.

Ich habe beschlossen, Open-Source-Hongmeng aufzugeben . Wang Chenglu, der Vater von Open-Source-Hongmeng: Open-Source-Hongmeng ist die einzige Architekturinnovations- Industriesoftwareveranstaltung im Bereich Basissoftware in China – OGG 1.0 wird veröffentlicht, Huawei steuert den gesamten Quellcode bei Google Reader wird vom „Code-Scheißberg“ getötet Fedora Linux 40 wird offiziell veröffentlicht Ehemaliger Microsoft-Entwickler: Windows 11-Leistung ist „lächerlich schlecht“ Ma Huateng und Zhou Hongyi geben sich die Hand, um „Groll zu beseitigen“ Namhafte Spielefirmen haben neue Vorschriften erlassen : Hochzeitsgeschenke für Mitarbeiter dürfen 100.000 Yuan nicht überschreiten Ubuntu 24.04 LTS offiziell veröffentlicht Pinduoduo wurde wegen unlauteren Wettbewerbs zu einer Entschädigung von 5 Millionen Yuan verurteilt
{{o.name}}
{{m.name}}

Ich denke du magst

Origin my.oschina.net/u/4090830/blog/11054982
Empfohlen
Rangfolge