Treinta minutos para empezar con Go básico (Java Kid Edition)

Prefacio

Ir definición de idioma

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

Ámbito de aplicación

Este artículo es adecuado para principiantes que han aprendido otros lenguajes orientados a objetos (Java, PHP), pero no han aprendido el lenguaje Go. El artículo explica principalmente los cuatro aspectos de la sintaxis básica del lenguaje Go, la programación orientada a objetos, la concurrencia y los errores de la comparación de las funciones de Go y Java .

1. Gramática básica

La sintaxis básica del lenguaje Go es básicamente similar a la de los lenguajes de programación convencionales. La diferencia es la forma de declarar las variables. Sin embargo, estas estructuras de datos no son las mismas. En Java, todos se pueden usar por analogía. Usado en Go.

1.1 Variables, constantes, valores nulos y cero, métodos, paquetes, visibilidad, punteros

1.1.1 Declaración de variables

Hay dos formas en el idioma Go.

1. Utilice varla declaración de palabras clave. Cabe señalar que, a diferencia de la mayoría de los lenguajes fuertemente tipados, el tipo de variable declarada en el lenguaje Go se encuentra después del nombre de la variable. No es necesario poner un punto y coma al final de una declaración Go.

var num int

var result string = "this is result"

2. Utilice :=la asignación.

num := 3Equivalente avar num int = 3

El tipo de variable coincidirá según el valor del lado derecho. Por ejemplo, "3" coincidirá con int, "3.0" coincidirá con float64 y "resultado" coincidirá con cadena.

1.1.2 Declaración constante

Úselo constpara declarar una constante. Una constante no se puede cambiar después de declararla.

const laugh string = "go"

1.1.3 valor nulo y cero

Declare únicamente variables no asignadas, cuyo valor sea nulo. Similar a " nulo" en java .

A las declaraciones de variables sin valores iniciales explícitos se les asigna su valor cero .

El valor cero es:

  • El tipo numérico es 0,

  • El tipo booleano es false,

  • La cadena es ""(cadena vacía).

1.1.4 Métodos y paquetes

Definición de métodos en Go

Utilice la palabra clave func para definir un método, seguida del nombre del método, luego los parámetros y el valor de retorno (si lo hay, si no hay valor de retorno, no se escribirá).

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;
}

Múltiples valores de retorno

Una gran diferencia entre las funciones de Go y otros lenguajes de programación es que admiten múltiples valores de retorno, lo cual es muy útil cuando se manejan errores de programas. Por ejemplo, si la función anterior addsolo admite la suma de números enteros no negativos, se informará un error si se pasa un número negativo.

//返回值只定义了类型 没有定义返回参数
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)
}

Parámetros de longitud variable

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

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

Paquetes y visibilidad

En el lenguaje Go, ya sean variables, funciones o atributos de clase y métodos miembro, su visibilidad se basa en la dimensión del paquete, en lugar de como la programación tradicional, donde la visibilidad de los atributos de clase y los métodos miembro se encapsula en la clase a la que pertenecen private. modificar su visibilidad con estas palabras clave, protectedy .public

El lenguaje Go no proporciona estas palabras clave, ya sean variables, funciones o atributos y métodos miembros de clases personalizadas, su visibilidad se determina en función del caso de sus primeras letras, si el nombre de la variable, el nombre del atributo, el nombre de la función o están en mayúscula . la primera letra del nombre del método , puede acceder directamente a estas variables, propiedades, funciones y métodos fuera del paquete; de ​​lo contrario, solo se puede acceder a ellos dentro del paquete. Por lo tanto, la visibilidad de los atributos de clase del lenguaje Go y los métodos miembros está en el paquete. nivel, y no clase uno.

Si domainhay tres archivos .go en una carpeta denominada, entonces los tres archivos packagedeben ser domain. Entre ellos, el archivo donde se encuentra el método principal de entrada del programa y el paquete.main

//定义了此文件属于 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 Consejos

Para aquellos que han estudiado el lenguaje C, los punteros son bastante familiares. El puntero que entiendo es en realidad un valor de dirección hexadecimal real en la memoria. El valor de la variable de referencia se utiliza para recuperar el valor real correspondiente de la memoria.

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 Condiciones, bucles, ramas.

1.2.1 Condiciones

Básicamente lo mismo que si estuviera en lenguaje Java.

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

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 Sucursal

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 Matrices, cortes, diccionarios

1.3.1 Matriz

La función de matriz es similar al lenguaje Java, la longitud es inmutable y se pueden usar matrices multidimensionales, o se pueden almacenar u obtener valores a través de matrices [i].

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

Las matrices son relativamente simples de usar, pero hay un problema difícil de resolver: longitud fija .

Por ejemplo, cuando necesitamos una estructura de datos en el programa para almacenar todos los usuarios obtenidos, debido a que el número de usuarios cambia con el tiempo, pero la longitud de la matriz no se puede cambiar, la matriz no es adecuada para almacenar datos cuya longitud cambia. . Por lo tanto, los problemas anteriores se resuelven utilizando sectores en lenguaje Go.

1.3.2 Rebanar

Slicing es un concepto completamente nuevo en comparación con Java. En Java, para estructuras de almacenamiento de datos de longitud variable, puede utilizar la interfaz List para completar operaciones, como ArrayList y LinkList. Estas interfaces pueden agregar y obtener datos en cualquier momento y no hay límite de longitud. Sin embargo, no existe tal interfaz en Go. En cambio, el almacenamiento de datos de longitud variable se logra a través de segmentos .

La mayor diferencia entre sectores y matrices es que los sectores no necesitan declarar la longitud. Pero los sectores no están relacionados con las matrices. Una matriz puede considerarse como la matriz subyacente del sector, y el sector puede considerarse como una referencia a un fragmento continuo de la matriz. Se pueden crear sectores utilizando solo una parte de una matriz o la matriz completa, e incluso es posible crear un sector que sea más grande que la matriz subyacente:

longitud, capacidad

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

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

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

La longitud del segmento es funcionalmente análoga al tamaño () de List en Java, es decir, la longitud del segmento se percibe a través de len (slice), y len (slice) se puede buclear para controlar dinámicamente el contenido específico en el rebanada. La capacidad de cortes no se usa mucho en el desarrollo real, solo comprenda su concepto.

Crear rebanadas

//声明一个数组
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)

Operación dinámica de cortes.

//使用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:]

Escenarios comunes para rebanar

Simule el problema mencionado anteriormente utilizando una solución de corte

//声明切片
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 Diccionario

El diccionario, también conocido como "par clave-valor" o "valor clave", es una estructura de datos de uso común. Hay varias interfaces de mapas en Java, y las más utilizadas son HashMap, etc. En Go, los diccionarios se utilizan para almacenar pares clave-valor. Los diccionarios no están ordenados, por lo que el orden de los datos no está garantizado según el orden de adición.

Declaración e inicialización del diccionario.

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

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

Escenarios de uso del diccionario

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

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

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

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

2. Programación orientada a objetos

2.1 Clases en idioma Go

Como todos sabemos, en los lenguajes orientados a objetos, una clase debe tener tres estructuras: atributos, constructores y métodos miembros, y el lenguaje Go no es una excepción.

2.1.1 Declaración e inicialización de clase

No existe un concepto claro de clase en el lenguaje Go. Sólo structlas palabras clave pueden compararse funcionalmente con las "clases" en los lenguajes orientados a objetos. Por ejemplo, para definir una clase de estudiante, puedes hacer esto:

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 Métodos de miembros

La declaración del método de miembro en Go no es la misma que en otros idiomas. Tomando la clase Estudiante como ejemplo,

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

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

2.2 Interfaz

Las interfaces juegan un papel vital en el lenguaje Go. Si la rutina y el canal son las piedras angulares que respaldan el modelo de concurrencia del lenguaje Go, entonces las interfaces son la piedra angular de todo el sistema de tipos del lenguaje Go . La interfaz del idioma Go no es solo una interfaz. Exploremos las características de la interfaz del idioma Go paso a paso.

2.2.1 Implementación de interfaz intrusiva tradicional

De manera similar a la implementación de clases, el concepto de interfaz del lenguaje Go es completamente diferente de los conceptos de interfaz proporcionados en otros idiomas. Tomando Java y PHP como ejemplos, las interfaces existen principalmente como contratos entre diferentes clases, y la implementación del contrato es obligatoria. Esto se refleja en los detalles específicos: si una clase implementa una determinada interfaz, debe implementar la interfaz Todos los métodos. , esto se llama "cumplimiento del contrato":

// 声明一个'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;
    }
}

En este momento, si hay otra interfaz iTemplate2que declara un iTemplatemétodo de interfaz que es exactamente igual, incluso con el mismo nombre iTemplate, pero está ubicado en un espacio de nombres diferente, el compilador también pensará que la clase anterior Templatesolo implementa iTemplatey no implementa. iTemplate2La interfaz.

Esto se da por sentado en nuestro conocimiento anterior, ya sea que se trate de herencia entre clases o implementación entre clases e interfaces, en lenguajes de herencia única como Java y PHP, existe una relación jerárquica estricta. clase principal, y una clase solo puede implementar una interfaz específica. Si no se declara explícitamente que hereda de una clase principal o implementa una determinada interfaz, entonces la clase no tiene nada que ver con la clase principal o la relación de interfaz.

A este tipo de interfaz lo llamamos interfaz intrusiva . La llamada "intrusiva" significa que la clase de implementación debe declarar explícitamente que implementa una interfaz. Aunque este método de implementación es bastante claro y simple, todavía existen algunos problemas, especialmente al diseñar la biblioteca estándar, porque la biblioteca estándar debe involucrar el diseño de la interfaz. El demandante de la interfaz es la clase de implementación comercial, y solo la clase de implementación comercial puede hacerlo. Solo entonces sabemos qué métodos deben definirse. Antes de eso, la interfaz de la biblioteca estándar se ha diseñado. Si no hay una interfaz adecuada, podemos implementarla. diseñarlo nosotros mismos. El problema aquí es que la interfaz. El diseño y la implementación empresarial están separados. Los diseñadores de interfaces no siempre pueden predecir qué funciones implementará la parte empresarial, lo que resulta en una desconexión entre el diseño y la implementación.

El diseño excesivo de la interfaz hará que algunas clases de implementación de métodos declarados sean completamente innecesarias. Si el diseño es demasiado simple, no satisfará las necesidades del negocio. De hecho, esto es un problema y no tiene sentido discutirlo sin el usuario. Escenarios de uso Tome la interfaz SessionHandlerInterface como ejemplo. Los métodos de interfaz declarados por esta interfaz son los siguientes:

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
}

El administrador de sesión definido por el usuario necesita implementar esta interfaz, es decir, debe implementar todos los métodos declarados por esta interfaz. Sin embargo, cuando realmente se realiza el desarrollo empresarial, algunos métodos no necesitan implementarse, por ejemplo. Redis o Memcached como sesión En cuanto a las memorias, ellas mismas contienen un mecanismo de reciclaje de caducidad, por lo que gcno es necesario implementar el método en absoluto. Por ejemplo, closeel método no tiene sentido para la mayoría de los controladores.

Es precisamente debido a este diseño irrazonable que debes luchar con los dos problemas siguientes al escribir cada interfaz en la biblioteca de clases PHP (Java es similar):

  1. ¿Qué métodos de interfaz deben declararse para una interfaz?

  2. Si varias clases implementan el mismo método de interfaz, ¿cómo debería diseñarse la interfaz? Por ejemplo, el anterior SessionHandlerInterface, ¿es necesario dividirlo en varias interfaces más subdivididas para adaptarse a las necesidades de diferentes clases de implementación?

A continuación, echemos un vistazo a cómo la interfaz del idioma Go evita estos problemas.

2.2.2 Implementación de la interfaz de idioma Go

En el lenguaje Go, la implementación de una interfaz por parte de una clase es lo mismo que la herencia de una subclase de una clase principal. No existe implementuna palabra clave como esta para declarar explícitamente qué interfaz implementa la clase. Una clase solo necesita implementar todas las. métodos requeridos por una interfaz, decimos que esta clase implementa la interfaz .

Por ejemplo, definimos una clase e Fileimplementamos Read()cuatro métodos: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

Supongamos que tenemos la siguiente interfaz (el lenguaje Go usa la palabra clave para interfacedeclarar la interfaz para mostrar la diferencia con el tipo de estructura, y las llaves contienen el conjunto de métodos que se implementará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 
}

Aunque Filela clase no implementa explícitamente estas interfaces, o ni siquiera conoce la existencia de estas interfaces, decimos que Filela clase implementa estas interfaces porque Filela clase implementa los métodos declarados por todas las interfaces anteriores. Cuando el conjunto de métodos miembro de una clase contiene todos los métodos declarados por una interfaz, en otras palabras, si el conjunto de métodos miembro de una interfaz es un subconjunto del conjunto de métodos miembro de una determinada clase, consideramos que la clase implementa la interfaz.

En comparación con Java y PHP, llamamos a esta interfaz del lenguaje Go una interfaz no intrusiva , porque la relación de implementación entre una clase y una interfaz no se declara explícitamente, sino que el sistema la juzga en función del conjunto de métodos de las dos. Esto tiene dos beneficios:

  • En primer lugar, la biblioteca estándar del lenguaje Go no necesita dibujar el diagrama de árbol de herencia/implementación de la biblioteca de clases. En el lenguaje Go, el árbol de herencia de la clase no tiene sentido. Solo necesita saber qué métodos implementa la clase. lo que hace cada método será suficiente.

  • En segundo lugar, al definir una interfaz, solo debe preocuparse por qué métodos debe proporcionar. Ya no necesita preocuparse por qué tan detallada debe ser la interfaz. No es necesario introducir el paquete donde se encuentra la interfaz. Implemente una interfaz. La interfaz la determina el usuario. Defina según demanda, no es necesario diseñar con anticipación y no es necesario considerar si otros módulos han definido interfaces similares antes.

De esta forma se evitan perfectamente los problemas de diseño de interfaces en la programación tradicional orientada a objetos.

3. Concurrencia y subprocesos múltiples

3.1 Gorrutina

Para cualquier lenguaje excelente, la capacidad de manejar el procesamiento concurrente es la clave para determinar sus ventajas. En el lenguaje Go, el procesamiento de concurrencia se implementa a través de Goroutine.

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

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

El lenguaje Go no tiene tantos bloqueos como Java para limitar el acceso simultáneo a los recursos y solo proporciona Mutex para operaciones de sincronización.

//给类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 Canal

La comunicación entre múltiples corrutinas se realiza a través del Canal, que puede compararse funcionalmente con la palabra clave volátil de Java.

ch := make(chan int)chDeclare un canal de tipo int y los datos int se pueden comunicar entre dos corrutinas .

La transmisión de datos se realiza a través del Canal.

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. Manejo de errores

error 4.1

El mecanismo de manejo de errores del lenguaje Go es muy simple y claro. No es necesario aprender conceptos, funciones y tipos complejos. El lenguaje Go define un patrón estándar para el manejo de errores, es decir, errorla definición de la interfaz es muy simple. :

type error interface { 
    Error() string 
}

Sólo Error()se declara un método, que devuelve un mensaje de error de tipo cadena. Para la mayoría de las funciones o métodos de clase, si desea devolver un error, básicamente puede definirlo como el siguiente patrón: devolver el tipo de error como segundo parámetro:

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

Luego, cuando llame a la función/método que devuelve información de error, simplemente escriba el código de procesamiento de acuerdo con la siguiente plantilla de "Declaración Wei Shu":

n, err := Foo(0)

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

Muy simple y elegante.

4.2 aplazar

aplazar se utiliza para garantizar que después de ejecutar un método, las declaraciones en aplazar se ejecuten independientemente de si el resultado de la ejecución es exitoso o no. Similar al uso try...catch...finally en Java. Por ejemplo, en el procesamiento de archivos, la secuencia de archivos debe cerrarse independientemente de si el resultado es exitoso o no.

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 pánico

No hay muchas clases de excepción en el lenguaje Go. A diferencia de Java, existen tipos de error como Error y Exception. Por supuesto, no hay declaraciones try...catch.

El pánico significa que se produce un error durante la operación del programa. Si no se detecta el error, el sistema se bloqueará y se cerrará. Por ejemplo, un simple pánico: a := 1/0.

Causará pánico: número entero dividido por cero.

imagen.png

La primera línea indica la rutina con el problema, la segunda línea es el paquete y la función donde se encuentra el código del problema, la tercera línea es la ubicación específica del código del problema y la última línea es el estado de salida del programa. Esta información puede ayudarle a localizar rápidamente el problema y resolverlo.

4.4 recuperar

Cuando hay un error previsible y no desea que el programa falle y se cierre, puede utilizar la instrucción recovery() para capturar el pánico no controlado. recovery debe colocarse en la declaración aplazar, y la declaración debe estar al principio del método para evitar que el sistema salga de forma anormal si la declaración aplazar no se puede ejecutar.

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 函数")
}

imagen.png

Se puede ver que aunque ocurrirá una excepción, después de usar recovery() para capturarla, el sistema no fallará ni saldrá, sino que simplemente finalizará el método. La fmt.Printf("%d / %d = %d\n", i, j, k)declaración no se ejecutó porque se produjo una excepción cuando el código se ejecutó en el paso anterior, lo que provocó que el método finalizara antes de tiempo.
4 recuperar

Cuando hay un error previsible y no desea que el programa falle y se cierre, puede utilizar la instrucción recovery() para capturar el pánico no controlado. recovery debe colocarse en la declaración aplazar, y la declaración debe estar al principio del método para evitar que el sistema salga de forma anormal si la declaración aplazar no se puede ejecutar.

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 函数")
}

Se puede ver que aunque ocurrirá una excepción, después de usar recovery() para capturarla, el sistema no fallará ni saldrá, sino que simplemente finalizará el método. La fmt.Printf("%d / %d = %d\n", i, j, k)declaración no se ejecutó porque se produjo una excepción cuando el código se ejecutó en el paso anterior, lo que provocó que el método finalizara antes de tiempo.

5. Resumen

A través del estudio anterior, inicialmente puede comprender la sintaxis básica de go para su uso, pero este artículo por sí solo no es suficiente para aprender go. Por ejemplo, una de las mayores ventajas de Go, la "corrutina", no se detalla en detalle debido al propósito del artículo. Los estudiantes interesados ​​pueden continuar aprendiendo.

Decidí renunciar al código abierto Hongmeng Wang Chenglu, el padre del código abierto Hongmeng: El código abierto Hongmeng es el único evento de software industrial de innovación arquitectónica en el campo del software básico en China: se lanza OGG 1.0, Huawei contribuye con todo el código fuente. Google Reader es asesinado por la "montaña de mierda de códigos" Fedora Linux 40 se lanza oficialmente Ex desarrollador de Microsoft: el rendimiento de Windows 11 es "ridículamente malo" Ma Huateng y Zhou Hongyi se dan la mano para "eliminar rencores" Compañías de juegos reconocidas han emitido nuevas regulaciones : los regalos de boda de los empleados no deben exceder los 100.000 yuanes Ubuntu 24.04 LTS lanzado oficialmente Pinduoduo fue sentenciado por competencia desleal Compensación de 5 millones de yuanes
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/11054982
Recomendado
Clasificación