Golang basic learning notes

data type

1. Value type: variables store values directly. Memory is usually allocated in the stack. The stack will be released after the function is called

Basic data types: int series, float series, bool, string, array and struct

Int series (int(32 or 64), int8, int16, int32, int64, and uint(32 or 64), uint8, uint16, uint32, uint64)

Float series (flaot23, float64)

2. Reference type: variables directly store an address, and only the space corresponding to this address can really store data values. Memory is usually allocated on the heap. When no variable references this address, the data space corresponding to this address becomes garbage and is recycled by GC

Pointer, slice slice, map, pipe chan, interface, etc

Basic data type to string

package main
import (
	"fmt"
	"strconv"
)
//Basic data type to string
func main() {
	var num1 int = 99
	var num2 float64 = 23.345
	var b bool = true
	var myChar byte = 'm'
	var str string
	/*
		Mode 1: use the first mode FMT Sprintf method
		func Sprintf(format string, a ...interface{}) string
		Sprintf Generates a formatted string based on the format parameter and returns the string.
	*/
	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = fmt.Sprintf("%c", myChar)
	fmt.Printf("str type %T str=%q\n", str, str)
	/*
		Method 2: use the FormatXxx function in the strconv package
		func FormatBool(b bool) string
		func FormatInt(i int64, base int) string
		func FormatUint(i uint64, base int) string
		func FormatFloat(f float64, fmt byte, prec, bitSize int) string
		func Atoi(s string) (i int, err error)
		func Itoa(i int) string
	*/
	var num3 int = 99
	var num4 float64 = 23.345
	var b2 bool = true
	str = strconv.FormatInt(int64(num3), 10)
	fmt.Printf("str type %T str=%q\n", str, str)
	//Note: 'f' format 10: it means 10 decimal places are reserved. 64: it means that the decimal place is float64
	str = strconv.FormatFloat(num4, 'f', 10, 64)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = strconv.FormatBool(b2)
	fmt.Printf("str type %T str=%q\n", str, str)
    
	//The function Itoa in strconv converts int to string
	var num5 int = 456
	str = strconv.Itoa(num5)
	fmt.Printf("str type %T str=%q\n", str, str)

}

string to basic data type

package main
import (
	"fmt"
	"strconv"
)
/*
	Use the functions in strconv package to convert string to basic data type
	func ParseBool(str string) (value bool, err error)
	func ParseInt(s string, base int, bitSize int) (i int64, err error)
	func ParseUint(s string, base int, bitSize int) (n uint64, err error)
	func ParseFloat(s string, bitSize int) (f float64, err error)
*/
func main() {
	var str string = "true"
	var b bool
	//The ParseXxx function will return two values. We only want value bool, not err, so we use_ ignore
	b, _ = strconv.ParseBool(str)
	fmt.Printf("b type %T b=%v\n", b, b)

	var str2 string = "123456789"
	var n1 int64
	n1, _ = strconv.ParseInt(str2, 10, 64)
	fmt.Printf("n1 type %T n1=%v\n", n1, n1)

	var str3 string = "123.456"
	var n2 float64
	n2, _ = strconv.ParseFloat(str3, 64)
	fmt.Printf("n2 type %T n2=%v\n", n2, n2)

	//Note to the above: the returned is int64 float64, which can be converted to int32 float32
	//Note: when converting string to basic data type: ensure that string can be converted to corresponding basic data type

	var str4 string = "hello"
	var n3 int64
	n3, _ = strconv.ParseInt(str4, 10, 64)
	fmt.Printf("n3 type %T n3=%v\n", n3, n3) //No error will be reported, but it cannot be converted. The default value is n3=0
}

array

Definition of array:

var array name [array size] data type

How to initialize an array

var array [3]int = [3]int{1,2,3}
array := [4][2]int{{10,11},{20,21},{30,31},{40,41}}

var array = [3]{1,2,3}
array := [...]string{}

//Specify a specific value for a specific subscript
var array = [3]string{1:"tom",2:jack}
//... Instead of the length of the array, go language determines the length of the array according to the elements of the array during initialization
var array = [...]int{1,2,3}

Traversal of array

1,Conventional practice
var arr = [...]arr{1,2,3,4}
for i := 0;i < len(arr); i++ {
	fmt.Printf("%c \n",arr[i]) 
}
2,for-range Structure traversal
var arr = [...]int{1,2,3,4}
for _,v := range arr {
	fmt.Printf("%v \n",v) 
}

Precautions for using arrays:

  1. [10]int is not of the same type as [30]int
  2. After the array is created, the length is determined
  3. The address of the first element of the array is the first address of the array
  4. The address interval of each element of the array is determined according to the type of the array

2D array:

Two dimensional array traversal:

	var arr2 [2][3]int
	for i := 0; i < len(arr2); i++ {
		for j := 0; j < len(arr2[0]); j++ {
			arr2[i][j] = i * j
		}
	}

	for i, v := range arr2 {
		for j, value := range v {
			fmt.Printf("arr2[%v][%v]=%v\t", i, j, value)
		}
		fmt.Println()
	}

section

A slice is a reference to an array,

The length of slices can vary, so slices are a dynamic array

Definition of slice:

var slice name [] type

var intArr [5]int = [...]int{1,22,3,4,5}
slice := intArr[1:3]	//Slice slice refers to the intArr array with the initial subscript of 1 and the final subscript of 3 (but excluding 3)
fmt.Println("intArr",intArr)fmt.Println("intArr", intArr)
fmt.Println("slice The element is", slice)
fmt.Println("slice The number of elements is", len(slice))
fmt.Println("slice The capacity is", cap(slice))
fmt.Printf("%T", slice) //[]int

Output:
intArr [1 22 3 4 5]
slice The element is [22 3]
slice The number of elements is 2
slice The capacity of is 4

slice[0] = 100
fmt.Println("intArr", intArr)
Output:
intArr [1 100 3 4 5]

Understanding of slice memory:

  1. slice is indeed a reference type

  2. slice is actually a data structure (struct structure) from the bottom

    type slice struct{
    	ptr *[Array length]data type
    	len int		//length
    	cap int		//capacity
    }
    
  3. Slice refers to an array. If slice is modified, the referenced array will also be modified

Use of slices

  1. Define a slice, and then use the slice to reference an array that has been created, as shown above

  2. Create slices with make

    Basic syntax: var sliceName []type = make([]type,len,[cap])

    ​ //sliceName := make([]type,len,cap)

    Type is the data type, len size, and cap specifies the capacity of the slice. If cap is allocated, cap > = len

  3. Define a slice and directly specify a specific array. The principle is similar to the make method

    var slice []int = [...]int{1,2,3}

Precautions for slicing:

  1. If no value is assigned to each element of the slice, the default value is used
  2. The array corresponding to the slice created by make is maintained by the bottom layer of make and is invisible to the outside, that is, each element can only be accessed through slice
  3. The difference between method 1 and method 2 of creating slices: Method 1 refers to an array directly, which exists in advance and is visible to programmers. Method 2 is to create slices through make, and make will also create an array. The slices are maintained at the bottom, which is invisible to programmers

Traversal of slices

  1. Loop routine traversal
  2. For range structure traversal slice

Considerations and details of slicing

  1. During slice initialization, var slice = arr[startIndex:endIndex]

    Note: get the element with the index of endIndex from the ARR array with the index of startIndex (excluding arr[endIndex])

    var slice = arr[0:end] can be abbreviated as var slice = arr[:end]

    var slice = arr[start:len(arr)] can be abbreviated as var slice = arr[start:]

    var slice = arr[0:len(arr)] can be abbreviated as var slice = arr [:]

  2. After the slice is defined, it cannot be used because it is empty and needs to be referenced to an array or make a space for the slice

  3. Slicing can continue slicing

    slice2 := slice[1,2]

  4. Using the append built-in function, you can dynamically append slices

    func append(slice []Type, elems ...Type) []Type
     Built in function append Append the element to the end of the slice. If it has enough capacity, its target will be re sliced to accommodate new elements. Otherwise, a new basic array will be allocated. append Returns the updated slice, so the appended result must be stored.
    
var slice []int = [...]int{1,2,3}

slice = append(slice,400,500,600)
slice = append(slice,slice2)
  • The underlying principle of slice append operation is to expand the capacity of the array, which is invisible to the programmer
  1. Slice is a reference data type. When passing, it follows the reference passing mechanism

    func main() {
    	var arr [5]int = [5]int{11, 22, 33, 44, 55}
    	var slice = arr[:]
    	slice2 := slice
    	slice2[0] = 666
    	fmt.Println(arr)
    	fmt.Println(slice)
    	fmt.Println(slice2)
    }
    Output result:
    [666 22 33 44 55]
    [666 22 33 44 55]
    [666 22 33 44 55]
    

string and slice

  1. The bottom layer of string is a byte array, so string can also be sliced
  2. String cannot be changed, that is, the string cannot be modified by str[0] = 'z'
  3. If you need to modify the string, you can convert the string to [] byte or [] run - > Modify - > re convert to string

map

Map is a key value data structure, also known as a field or associative array. Similar to the collection of other programming languages, it is often used in programming. In go, map is implemented with hash table at the bottom

Mapping is a data structure used to store a series of unordered key value pairs. Mapping stores values based on keys. map can quickly retrieve data based on key. A key, like an index, points to the value associated with the key

Basic grammar

var map variable name map[keytype]valuetype

such as: var age map[string]int

Var users map [string] map [string] string -- > value is another map

  1. What type of key can it be?

In golang, the key of map can be bool, number, string, pointer, channel, or interface, structure and array containing the previous types

Usually, the key is int or string

Note: slice, map and function are not allowed because they cannot be judged by = =

After the map declaration, memory will not be allocated. make is required for initialization, and memory can be assigned and used only after allocation

  1. The type selection of valuetype is basically the same as that of key

    Usually: number (integer, floating-point number), string, map, struct

func main() {
	//Declaration and precautions of map
	var a map[string]string
	//Before using the map, you need to make and allocate memory space to the map
	a = make(map[string]string, 10)
	a["no1"] = "Liu Bei"
	a["no2"] = "Fei Zhang"
	a["no1"] = "Cao Cao"
	a["no3"] = "Guan Yu"

	fmt.Println(a)
}

Description of the above code:

  1. make a map before using it
  2. The key of map cannot be repeated. If it is repeated, the last key value shall prevail
  3. The value of a map can be the same
  4. The key value of map is out of order. The reason for the disorder is that the implementation of map uses hash table
  5. make built-in function tree
func make
func make(Type, size IntegerType) Type
 Built in function make Allocates and initializes an object of type slice, map, or channel. Its first argument is a type, not a value. make The return type of is the same as its parameter, not a pointer to it. The specific results depend on the specific type:

section: size Its length is specified. The capacity of the slice is equal to its length. Slicing supports the second integer argument, which can be used to specify different capacities;
     It must not be less than its length, so make([]int, 0, 10) A slice with a length of 0 and a capacity of 10 is allocated.
Mapping: the creation of the initial assignment depends on size,But the resulting mapping length is 0. size It can be omitted, in which case one will be assigned
     Small starting size.
Channel: the cache of the channel is initialized according to the specified cache capacity. if size If it is zero or omitted, the channel is uncached.

How to use map:

1: Declare first, then make, then assign

2: make the statement directly

3: Declare direct assignment

//The first method is to declare first, then make, and then assign values
	//Declaration and precautions of map
	var a map[string]string
	//Before using the map, you need to make and allocate memory space to the map
	a = make(map[string]string, 10)
	a["no1"] = "Liu Bei"
	a["no2"] = "Fei Zhang"
	a["no1"] = "Cao Cao"
	a["no3"] = "Guan Yu"
	fmt.Println(a)

	//The second way is to make the declaration directly and then assign the value
	cities := make(map[string]string)
	cities["no1"] = "Beijing"
	cities["no2"] = "Shanghai"
	cities["no3"] = "Tianjin"
	cities["no4"] = "Nanyang"
	fmt.Println(cities)

	//The third way: declare direct assignment
	heroes := map[string]string{
		"hero1": "Song Jiang",
		"hero2": "Li Kui",
	}
	heroes["hero3"] = "Lin Chong"
	fmt.Println(heroes)

example:

//Each student has 3 names and gender information
	stus := make(map[string]map[string]string)

	stus["stu01"] = make(map[string]string, 3)
	stus["stu01"]["name"] = "tom"
	stus["stu01"]["sex"] = "male"
	stus["stu01"]["address"] = "Zhengzhou, Henan"

	stus["stu02"] = make(map[string]string, 3)
	stus["stu02"]["name"] = "mary"
	stus["stu02"]["sex"] = "female"
	stus["stu02"]["address"] = "Pudong, Shanghai"

	fmt.Println(stus)

Common operations of map

map additions and updates:

  1. map ["key"] = value / / if the key does not exist, it is added. If the key exists, it is modified

map deletion:

  1. delete(map, "key"), delete is a built-in function. If a key exists, the key value will be deleted. If it does not exist, no operation will be performed, but no error will be reported
func delete(m map[Type]Type1, key Type)
Built in function delete Deletes the element from the map according to the specified key. if m by nil Or no such element, delete Do not operate.

Details:

If we want to delete all the keys in the map, there is no special method to delete them at one time. We can traverse the keys and delete them one by one, or map = make(...) and make a new one, so that the original one becomes garbage and is recycled by GC

stus = make(map[string]map[string]string)

map lookup

Note: if stu02 exists in the stus map, findRes will return true, otherwise false

	val, findRes := stus["stu02"]
	if findRes {
		fmt.Println("eureka, val=", val)
	} else {
		fmt.Println("No, stu02 this key")
	}
	//Yes, val= map[address: Pudong, Shanghai name:mary sex: female]

map traversal

map traversal uses for range

	cities := make(map[string]string)
	cities["no1"] = "Beijing"
	cities["no2"] = "Shanghai"
	cities["no3"] = "Tianjin"
	cities["no4"] = "Nanyang"
	fmt.Println(cities)
	
	for k,v := range cities {
		fmt.Printf("k=%v v=%v",k,v)
	}
	
	stus := make(map[string]map[string]string)

	stus["stu01"] = make(map[string]string, 3)
	stus["stu01"]["name"] = "tom"
	stus["stu01"]["sex"] = "male"
	stus["stu01"]["address"] = "Zhengzhou, Henan"

	stus["stu02"] = make(map[string]string, 3)
	stus["stu02"]["name"] = "mary"
	stus["stu02"]["sex"] = "female"
	stus["stu02"]["address"] = "Pudong, Shanghai"
	
	for k1,v1 := range stus{
		fmt.Println("k1=",k1)
		for k2,v2 := range v1{
			fmt.Printf("\tk2=%v v2=%v",k2,v2)
		}
		fmt.Println()
	}

map length

len(keyType)

func len(v Type) int
 Built in function len return v Depending on the type:

Array: v Number of elements in
 Array pointer:*v Number of elements in( v by nil Time panic)
Slice, map: v The number of elements in the; if v by nil,len(v)Is zero
 character string: v Number of bytes in
 Channel: the number of queued (unread) elements in the channel cache; if v by nil,len(v)Is zero

map slice

If the data type of slice is map, we call it slice of map. In this way, the number of maps can change dynamically

func main() {
	//Case: use a map to record monster's information name and age, and the number of monsters can increase dynamically
	var monsters []map[string]string
	monsters = make([]map[string]string, 2) //Prepare to put in two monsters
	if monsters[0] == nil {
		monsters[0] = make(map[string]string, 2)
		monsters[0]["name"] = "Ox demon king"
		monsters[0]["age"] = "100"
	}
	if monsters[1] == nil {
		monsters[1] = make(map[string]string, 2)
		monsters[1]["name"] = "Red boy"
		monsters[1]["age"] = "20"
	}
	//The following is wrong
	/*if monsters[2] == nil { //panic: runtime error: index out of range [2] with length 2
		monsters[2] = make(map[string]string, 2)
		monsters[2]["name"] = "Jade Rabbit“
		monsters[2]["age"] = "18"
	}*/

	//Here we need to use the append function to slice, which can dynamically add monster
	//1. First define a monster learning
	newMonster := map[string]string{
		"name": "Jade Hare",
		"age":  "18",
	}
	monsters = append(monsters, newMonster)
	
	fmt.Println(monsters)

}

map sorting

1. There is no special method in golang to sort map s

2. Maps in golang are unordered by default and are not stored in the order of addition

3. Sorting in golang is to sort the keys first, and then traverse the output according to the key value

func main() {
	map1 := make(map[int]int)
	map1[10] = 100
	map1[2] = 44
	map1[4] = 66
	map1[6] = 88
	map1[3] = 99
    
	//1. First put the key of the map into the slice
	var keys []int
	for k, _ := range map1 {
		keys = append(keys, k)
	}
	//2. Sort keys slices
	sort.Ints(keys)
	fmt.Println(keys)

    //3. Output map1 incrementally according to the key
	for _, k := range keys {
		fmt.Printf("map1[%v]=%v\n", k, map1[k])
	}
}

Usage details of map

  1. Map is a reference type. It follows the reference type passing mechanism. After a function receives a map, it will directly modify the original map

    func modify(map1 map[int]int) {
    	map1[10] = 666
    }
    func main() {
    	map1 := make(map[int]int)
    	map1[10] = 100
    	map1[2] = 44
    	map1[4] = 66
    	map1[6] = 88
    	map1[3] = 99
    
    	modify(map1)
    	fmt.Printn(mp1)
    }
    
  2. After the capacity of the map is reached, adding elements to the map will automatically expand the capacity without panic, that is, the map can dynamically increase key value pairs

  3. The value of map often uses struct type, which is more suitable for managing complex data (better than the previous value is a map). For example, value is the Student structure

    type Stu struct {
    	Name    string
    	Age     int
    	Address string
    }
    
    func main() {
    	students := make(map[string]Stu, 10)
    	stu1 := Stu{"tom", 20, "Pudong, Shanghai"}
    	stu2 := Stu{"marry", 18, "Beijing"}
    	students["no1"] = stu1
    	students["no2"] = stu2
    
    	fmt.Println(students)
    
    	//Traverse students
    	for k, v := range students {
    		fmt.Printf("The student's number is%v\n", k)
    		fmt.Printf("What is the student's name%v\n", v.Name)
    		fmt.Printf("What is the age of the student%v\n", v.Age)
    		fmt.Printf("What is the student's address%v\n", v.Address)
    		fmt.Println()
    	}
    }
    

identifier

Naming rules

  1. It consists of 26 English letters in case, 0-9_ form

  2. Number cannot start

  3. golang is strictly case sensitive

  4. The identifier cannot contain spaces

  5. Underline "" Itself is a special identifier in go, which is called null identifier. It can represent any other identifier, but its corresponding value will be ignored (for example, ignoring a return value). Therefore, it can only be used as a placeholder, not as an identifier alone (mixed with alphanumeric).

  6. System reserved keywords cannot be used as identifiers. (there are 25)

    breakdefaultfuncinterfaceselect
    casedefergomapstruct
    chanelsegotopackageswitch
    constfallthroughifrangetype
    continueforimportreturnvar

Predefined identifier

In addition to reserved keywords, GO also provides 36 predetermined identifiers, including basic data types and system embedded functions

append	bool	byte	cap	close	complex
complex64	complex128	uint16	copy	false	float32
float64	imag	int	int8	int16	uint32
int32	int64	iota	len	make	new
nil	panic	unit64	print	println	real
recover	string	true	uint	uint8	uintprt
hello //ok
hello12 //ok
1hello //error
h-b //error
x h //error
int //ok, grammar is ok, but don't use it like this
float32 //ok, grammar is ok, but don't use it like this
_ //error
Abc //ok

Identifier naming convention

  1. Package name: keep the package name and directory consistent. Try to use meaningful package names, which are short and meaningful. Don't conflict with the standard library, such as fmt. It is recommended to use lowercase letters for package names.

  2. Variable name, function name and constant name: hump method is adopted

    eg: var stuName string = "tom" form: xxxyyyzz

  3. If the variable name, function name and constant name are capitalized, they can be accessed by other packages; If the initial is lowercase, it can only be used in this package (Understanding: the initial uppercase is public and the initial lowercase is private; there are no public, private and other keywords in golang)

go operator priority

From low to high

classificationoperatorAssociativity
suffix() [] -> . ++ –Left to right
Monocular+ - ! ~ (type) * & sizeofRight to left
multiplication* / %Left to right
addition+ -Left to right
displacement<< >>Left to right
relationship< <= > >=Left to right
Equality (relation)== !=Left to right
Bitwise AND&Left to right
Bitwise XOR^Left to right
Bitwise OR|Left to right
Logical AND&&Left to right
Logical OR||Left to right
Assignment Operators = += -= *= /= %= >>= <<= &= ^= |=Right to left
commaLeft to right

go process control

Conditional statement

If judgment, if else judgment, else judgment

Initial sub statement: if statement can have a sub statement, which is used to initialize local variables. In addition, since the value of a is defined in the if code block, it cannot be called outside the code block.

func main(){
    if a := 10; a < 20 {
        fmt.Printf("a Less than 10")
    }else{
        fmt.Printf("a The value of is:%d\n",a)
    }
}

//Note that else cannot wrap lines
//Else in else if is not required

Select statement

  1. Switch statements (expression switch, type switch), which also have initialization statements, can only have one sentence after the switch keyword
  2. select statement, which is used to cooperate with the read-write operation of channels and the concurrent read-write operation of multiple channels

Precautions and details of switch use:

  1. case/switch is followed by an expression (that is, constant value, variable and a function with return value can all be used)
  2. The data type of the value of each expression after case must be consistent with the expression data type of switch
  3. case can be followed by multiple expressions, separated by commas
  4. If the expression after case is a constant value (literal), it must not be repeated
  5. There is no need to bring break after the case. After the program matches a case, it will execute the corresponding code block, and then exit the switch. If none of them match, it will execute default
  6. The default statement is not required. (even if they don't match)
  7. The switch can also be used without an expression, similar to the if else branch
  8. You can also directly declare / define a variable after switch, ending with a semicolon. Not recommended
  9. Switch penetration -fallthrough. If fallthrough is added after the case statement block, the next case will continue to be executed, which is also called switch penetration
var num int = 10
switch num {
    case 10:
    	fmt.Printf("ok1")
    	fallthrough		//By default, it can only penetrate one layer, and case 20 can be executed directly without judgment
    case 20:
    	fmt.Printf("ok2")
    case 30:
    	fmt.Printf("ok3")
    default:
    	fmt.Printf("No match found")
}
Output:
ok1
ok2
  1. Type Switch: the switch statement can also be used for Type Switch to determine the type of variable actually pointed to in an interface variable
package main
import "fmt"
func main(){
    grade := "E"
    marks := 90
    
    //1. Expression switch
    //Similar to writing in other languages
    //Generally, it is not written in this way. It is not easy to expand the selection statement. If the score is 100,91, D will also be returned
    switch marks {
        case 90 :
        	grade = "A"
        case 80 :
        	grade = "B"
        case 60,70 :
        	grade = "C"
        default:
        	grade = "D"
    }
    fmt.Printf("Your grades are:%s\n",grade)
    
    
    //Complete switch expression
    switch {		//switch is not followed by an expression
        case marks >= 90 :
        	grade = "A"
        case marks >= 80 :
        	grade = "B"
        case marks >= 70:
        	grade = "C"
        case marks >= 60:
        	grade = "D"
        default:
        	grade = "E"
    }    
    switch {		//Switch is not followed by an expression, and switch is not followed by default
        case grade == "A":
        	fmt.Printf("Excellent results!\n")
        case grade == "B":
        	fmt.Printf("Good performance!\n")
        case grade == "C",grade == "D":
        	fmt.Printf("make persistent efforts!\n")
        case grade == "A":
        	fmt.Printf("The result is unqualified!\n")
    }
    fmt.Printf("Your grade is%s\n",grade)    
}
package main
import "fmt"
var x interface{}//Empty interface
func main(){
    x = 1 //Modify the value of x to see the change of the returned result
    switch i := x.(type){	//The expression here has only one initialization sub statement
        case nil:
	        fmt.Printf("Here is nil,x The type of is%T",i)
        case int:
        	fmt.Printf("Here is int,x The type of is%T",i)
        case float64:
        	fmt.Printf("Here is float64,x The type of is%T",i)
        case bool:
        	fmt.Printf("Here is bool,x The type of is%T",i)
        case string:
        	fmt.Printf("Here is string,x The type of is%T",i)
        default:
	        fmt.Printf("unknown type")
    }
    

}

Circular statement

In go language, the keyword of circular statement is for, and there is no while keyword. Three sub statements after the for statement: initialization sub statement, condition sub statement and post sub statement. (the order cannot be reversed. The conditional sub statement is required. The conditional sub statement will return a bool value, true will execute the code block, and false will jump out of the loop.

- > range sub statement. Each for statement can use a special range sub statement, which is similar to the iterator. It can be used to query each element in the array or slice value, poll each character of the string, array, slice, and each key value pair in the dictionary value, and even read the element in a channel type value

for recycling notes and details:

  1. A loop condition is an expression that returns a Boolean value

  2. The second way to use the for loop

    for Cyclic judgment condition {
    	//Circular execution statement
    }
    //Write variable initialization and variable iteration to other locations
    
    eg: 
    j := 1
    for j <= 10{
    	fmt.Printf("Hello,",j)
    	j++
    }
    
  3. The third way to use the for loop

    for {
        //Circular execution statement
    }
    
    eg: 
    k := 1
    for {
        if k <= 10 {
            fmt.Println("ok",k)
        }else{
            break	//break is to jump out of the for loop
        }
        k++
    }
    
  4. Golang provides for range, which can easily traverse strings and arrays

    For the for range traversal mode, it is traversed in character mode. Therefore, if the string has Chinese, it is ok

    //String traversal mode 1: traditional mode
    var str string = "hello,world"
    for i := 0;i < len(str); i++ {
    	fmt.Printf("%c \n",str[i]) 
    }
    
    //String traversal mode 2: for - range
    str = "abc~ok"
    for index,val := range str{
    	fmt.Printf("index=%d,val=%c \n",index,val)
    }
    /*
    	//The above code is written in this way to understand the range clause, but it reduces the readability of the code and is not easy to manage the subsequent cyclic code blocks
    	for index,val := range "abc~ok"{
    		fmt.Printf("index=%d,val=%c \n",index,val)
    	}
    */
    for _,char := range str {	//Ignore the first value, ignore index
        fmt.Println(char)
    }
    for index := range str { //Ignore the second value
        fmt.Println(index)
    }
    for range str { //Ignore all return values and execute only the following code
        fmt.Println("Successful execution")    
    }
    

    Detailed discussion of two ways of traversing strings:

    If the string contains Chinese, the traditional way of traversing the string is error and garbled code will appear. The reason is that the traditional traversal of strings is based on bytes, while a Chinese character in utf-8 encoding corresponds to three bytes.

    How to solve:

    var str string = "hello,Beijing"
    str2 := []rune(str)	//Is to convert str to [] run
    for i := 0; i < len(str2); i++ {
    	fmt.Printf("%c ", str2[i])
    }
    
  5. range clause

    The left side of the range keyword represents a pair of index value pairs, which return different results according to different expressions

    The output of range return value is shown in the following table:

    The type returned by the expression on the rightFirst valueSecond value
    stringindexstr[index], return type is run
    array/sliceindexstr[index]
    mapkeym[key]
    channelelement
  6. Implementing while with for

    for {
    	if Cyclic conditional expression{
    		break
    	}
    	Cyclic operation
    	Cyclic variable iteration
    }
    
  7. do... while with for

    for	{
    	Cyclic operation
    	Cyclic variable iteration
    	if {
    		break
    	}
    }
    

Labels, jump statements

Tags can label for, switch, select and other process control code blocks. With tag identifiers, you can easily jump to a certain place to continue execution, which helps to improve programming efficiency. It is recommended to use capital letters and numbers for label names. Tags can mark any statement and are not limited to process control statements. Unused tags will cause errors

1. break statement

The break statement is used to terminate the execution of a statement block, interrupt the current for loop or jump out of the switch statement.

Precautions and details for use of break

  1. When break appears in a multi-layer nested statement block, you can indicate which layer of statement block to terminate through a label

    lable2:
    	for i := 0; i < 4; i++ {
    		//lable1:
    		for j := 0; j < 10; j++ {
    			if j == 2 {
    				//break will jump out of the nearest for loop by default
    				//break lable1 	// The effect is the same as not writing lable1
    				break lable2	//Execute to this point and exit the two-tier for loop directly
    			}
    			fmt.Println("j=", j)
    		}
    	}
    break The default will jump out of the nearest for loop
    break You can specify the label later and jump out of the corresponding field of the label for loop
    

2. continue statement

The continue statement ends this cycle and continues to execute the next cycle

When the continue statement appears in the body of a multi-layer nested loop statement, you can use the label to indicate which layer of loop to skip. This is the same as the previous break label

lable2:
	for i := 0; i < 4; i++ {
		//lable1:
		for j := 0; j < 10; j++ {
			if j == 2 {
				continue	//Output four times without j=2 each time,
			}
			fmt.Println("j=", j)
		}
	}

3. goto statement

goto statement can jump unconditionally to the labeled statement in the same function.

goto statements are usually used together with conditional statements to realize conditional transfer, jump out of the loop body, etc

goto is generally not advocated in go programming, so as not to cause confusion in the program flow and make it difficult to understand and debug the program

4,return

return when used in a method or function, it means to jump out of the method or function

  1. If return is in an ordinary function, it means that the function jumps out, that is, the code after return in the function will not be executed. It can also be understood as a termination function
  2. If return is in the main function, it means to terminate the main function, that is, to terminate the program

Deferred statement

defer is used to delay the calling of the specified function. defer can only appear inside the function.

Because of the delay characteristics of defer, it can be used to recover resources, clean up and close out. After using the defer statement, you don't have to worry about where the code is placed. Anyway, it is the last execution

In functions, programmers often need to create resources (such as database connection, file handle, lock, etc.). In order to release resources in time after the function is executed, the defer delay mechanism can be used

Notes and details of defer usage:

  1. The expression after defer must be a call to an external function
  2. Only when the defer statement is fully executed can the function in which the defer is located really end execution
  3. When there is a defer statement in the function, you need to wait until all defer statements are executed before executing the return statement
  4. When multiple defer statements appear inside a function, the last defer statement is executed first. (defer is actually a stack, which follows the last in, first out)
package main

import "fmt"

var i int = 0

func print(i int) {
	fmt.Printf("%d,", i)
}
func main() {
	//defer fmt.Printf("world")
	//fmt.Printf("hello") / / output: 5,5,5,5,5,
	for ; i < 5; i++ {
		defer print(i)
	}
}
//Output: 4,3,2,1,0,	
  1. When defer puts the statement on the stack, it will also copy the relevant values into the stack at the same time

    func sum (n1 int, n2 int) int {
        defer fmt.Println("ok1 n1=",n1)	//ok1 n1=10
        defer fmt.Println("ok2 n2=",n2)	//ok2 n2=20
        n1++ //n1=11
        n2++ //n2=21
        res := n1 + n2 //32
        fmt.Println("res=",res)
        return res
    }
    func main(){
        res := sum(10,20)
        fmt.Println("res=",res)
    }
    
    Output result:
    ok3 res= 32
    ok2 n2= 20
    ok1 n1= 10
    res= 32
    
  2. Because of the delay characteristics of defer, it can be used to recover resources, clean up and close out. After using the defer statement, you don't have to worry about where the code is placed. Anyway, it is the last execution

    • A common practice in golang programming is to add defer file after creating resources, such as opening files, obtaining links to databases, or locking resources Close() defer connect. Close()
    • After defer, you can continue to use to create resources
    • After the function is executed, the system will take out the statements from the defer stack in turn and close the resources

function

The set of instructions (statements) that complete a function is called a function

In go, functions are divided into user-defined functions and system functions

The introduction of functions into the program can reduce the redundancy of the code and facilitate the maintenance of the code

func Function name (formal parameter list) (return value list){
	Execute statement...
	return Return value list
}
1.Formal parameter list: represents the input of the function
2.Statement in function: it refers to the code block to realize a common function
3.A function can have a return value or no return value

Notes and instructions for using the function:

  1. Go language functions do not support nesting, overloading, and default parameters

  2. Function names can be called by other packages if they are capitalized, but not lowercase

  3. go language supports returning multiple values, which is not available in other languages

    func Function name (parameter list ) (Return value type list){
    	sentence..
    	return Return value list
    }
    1.If multiple values are returned and you want to ignore a value when accepting, use_Symbol placeholder ignored
    2.If there is only one return value, (return value list) can not be written ()
    
  4. The variables in the function are local and do not take effect outside the function

  5. The basic data type and array are passed by value by default, that is, the value is copied. Modification within the function will not affect the original value

  6. If you want the variables inside the function to modify the variables outside the function, you can pass in the address &, and operate the variables in the function in the form of pointers

  7. The go language does not support function overloading

  8. In go, function is also a data type, which can be assigned to a variable, which is a variable of function type, through which the function can be called

    func getSum(n1 int,n2 int) int {
    	return n1 + n2
    }
    func main(){
    	a := getSum
        //The type of a is func(int, int) int, and the type of getSum is func(int, int) int
    	fmt.Printf("a The type of is%T,getSum The type of is%T\n",a,getSum)
    	res := a(10,40) //Equivalent res: = getsum (10,40)
    	fmt.Println("res=",res)
    }
    
  9. Since the function is a data type, in go, the function can be used as a formal parameter and called

    func getSum(n1 int,n2 int) int {
    	return n1 + n2
    }
    func myFun(funvar func(int, int) int, num1 int, num2 int) int {
    	return funvar(num1, num2)
    }
    func main(){
    	res2 := myFun(getSum, 30, 30)
    	fmt.Println("res2=", res2)
    }
    
  10. To simplify data type definition, go supports custom data types (equivalent to an alias)

    • Basic syntax: type custom data type name data type
  11. Support naming function return value

    func getSumAndSub(n1 int, n2 int) (sum int, sub int){
    	sub = n1 - n2
    	sum = n1 + n2
        //You can no longer write the return value after return
    	return
    }
    func main(){
    	a1, b1 := getSumAndSub(3,4)
    	fmt.Printf("a1=%v,b1=%v",a1,b1)	//a1=7,b1=-1
    }
    
  12. Use_ Identifier, ignoring return value

  13. go supports variable parameters

    • Args is slice slice, and each value can be accessed through args[index]
    • If there are variable parameters in the formal parameter list of a function, the variable parameters need to be placed at the end of the formal parameter list
    //0 to more than one parameter is supported
    func sum(args... int) int{
        
    }
    //Support 1 to more parameters
    func sum (n1 int,args... int) int {
        
    }
    
    
    func sum(n1 int, args ...int) int {
    	sum := n1
    	for i := 0; i < len(args); i++ {
    		sum += args[i]
    	}
    	return sum
    }
    func main(){
        res4 := sum(10, 10, 10, 10, 10, 10)
    	fmt.Println(res4)
    }
    

Built in function

For convenience, the golang setter provides some functions that can be used directly, which are called built-in functions (built-in functions)

  1. len: used to find the length, such as string, array, slice, map and channel

  2. new: used to allocate memory. It is mainly used to allocate value types, such as int, float32, struct... Returns pointers

    func new(Type) *Type
     Built in function new Allocate memory. Its first argument is a type, not a value. Its return value is a pointer to the newly assigned zero value of the type
    
    
    num := new(int)
    fmt.Printf("num Type of%T,num Value of%v,num Address of%v,num The value pointed to is%v\n", num, num, &num,*num)
    
    //Type of num * int, value of num 0xc0000018030, address of num 0xc000006058, value pointed to by num is 0
    
  3. make: used to allocate memory. It is mainly used to allocate reference types, such as chan, map and slice

func make(Type, size IntegerType) Type
 Built in function make Allocates and initializes an object of type slice, map, or channel. Its first argument is a type, not a value. make The return type of is the same as its parameter, not a pointer to it. The specific results depend on the specific type:

section: size Its length is specified. The capacity of the slice is equal to its length. Slicing supports the second integer argument, which can be used to specify different capacities;
     It must not be less than its length, so make([]int, 0, 10) A slice with a length of 0 and a capacity of 10 is allocated. ( cap10 Yes (optional)
Mapping: the creation of the initial assignment depends on size,But the resulting mapping length is 0. size It can be omitted, in which case one will be assigned
     Small starting size.
Channel: the cache of the channel is initialized according to the specified cache capacity. if size If it is zero or omitted, the channel is uncached.

init function

Each source file can contain an init function, which will be called by the Go running framework before the main function is executed, that is, the init function will be called before the main function

  1. If a file contains global variables, init function and main function at the same time

    • The execution order is global variable definition - > init function - > main function
  2. If main Go introduce utils Go, both contain variable definitions and init functions. What is the order of execution?

    utils Global variable definition in -> utils in init function -> main Definition of global variables in -> main in init function -> main function
    
package main
import "fmt"

var age = test()
//To see that the global variable definition is initialized first, write a test function here
func test() int{
	fmt.Println("test()")
	return 66
}
//Init function, you can usually complete initialization in init function
func init(){
	fmt.Println("init()")
}
func main(){
	fmt.Println("main()")
}

results of enforcement
test()
init()
main()

Anonymous function

go supports anonymous functions. Anonymous functions are functions without names. If we want to call a function only once, we can consider using anonymous functions. Anonymous functions can also be called multiple times

  1. The anonymous function can be called directly when it is defined. In this way, the anonymous function can only be called once
func main(){
	res := func(n1 int, n2 int) int {
		return n1 + n2
	}(10,20)
}
  1. Assign the anonymous function to a variable (function variable), and then call the anonymous function through the variable
	a := func(n1 int, n2 int) int {
		return n1 + n2
	}
	res1 := a(10,20)
    res2 := a(20,30)
  1. If you assign an anonymous function to a global variable, the anonymous function becomes a global anonymous function, which can be effective in the program

closure

A closure is a whole (entity) composed of a function and its related reference environment. In essence, a closure returns a function

A closure inherits the scope of the function declaration. This state (variables within the scope) will be shared in the closure environment, so these variables can be operated in the closure until they are destroyed.

Closures are often used as wrapper functions. One or more parameters are predefined for wrapping. Another common application is to use closures to complete more concise error checking

package main

import "fmt"

//accumulator
func addUpper() func(int2 int) int {
	var n int = 10
	var str = "hello"
	return func(x int) int {
		n = n + x
		str += "a"
		fmt.Println("str=", str)
		return n
	}
}
func main() {
	f := addUpper()
	fmt.Println(f(1))
	fmt.Println(f(5))
	fmt.Println(f(10))
	fmt.Println(f(40))

}
Output result:
str= helloa
11
str= helloaa
16
str= helloaaa
26
str= helloaaaa
66

Description of the above functions
1.AddUpper()Is a function, and the data type returned is fun(int)int
2.Description of closure:
		var n int = 10
			return func(x int) int {
				n = n + x
				return n
			}
This is a closure. The returned function is an anonymous function, but the anonymous function is referenced outside the function n,So this anonymous function and n It forms a whole and constitutes a closure.
3.It can be understood that a closure is a class and a function is a method, n Is an attribute. A function and the variables it uses form a closure
4.Call us repeatedly AddUpper Function, n Initialize only once,
5.The key to find out the closure is to analyze the variables used by the returned function, because the function and the variables it references constitute the closure.

Practice on closures:

Write a makeSuffix function that can receive a file suffix (such as. jpg) and return a closure. Call closure to pass in a file name. If the file name has no specified suffix (such as. jpg), the file name will be returned jpg. If there is a suffix (such as. jpg), the original file name will be returned

Reference knowledge: func HasSuffix(s, suffix string) bool, which can judge whether a string has a specified suffix

func makeSuffix(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			//If name does not specify a suffix, it will be added; otherwise, the original name will be returned
			return name + suffix
		}
		return name
	}
}
func main() {
	f := makeSuffix(".jpg")
	fmt.Println("After document processing=", f("winter"))
	fmt.Println("After document processing=", f("summer.jpg"))
}
1.Returned anonymous functions and makeSuffix(suffix string)of suffix Variables are combined into a closure, and the returned function is referenced to suffix This variable

Function value transfer mechanism

Value passing and reference passing

In fact, whether it is value transfer or reference transfer, what is passed to the function is a copy of the variable. Value passing is a copy of the value, and reference passing is a copy of the address. Generally speaking, address copying is efficient because of the small amount of data

  1. Value types: basic data types: int series, float series, bool, string, array and struct
  2. Reference types: pointer, slice slice, map, pipe chan, interface, etc

Common system functions in string

Function call: package name Function name (parameter)

  1. Count the length of the string in bytes len(str). Built in function, no need to quote package

    func len(v Type) int
     Built in function len return v Depending on the type:
    
    Array: v Number of elements in
     Array pointer:*v Number of elements in( v by nil Time panic)
    Slice, map: v The number of elements in the; if v by nil,len(v)Is zero
     character string: v Number of bytes in	//ASCII characters occupy one byte and Chinese characters occupy three bytes
     Channel: the number of queued (unread) elements in the channel cache; if v by nil,len(v)Is zero
    
  2. String traversal, while dealing with Chinese problems R: = [] run (STR)

    Of course, the traversal of strings can use for range, which does not need to be sliced

    str := "hello Beijing"
    fmt.Println("str len=",len(str))	//11
    
    str2 := []rune(str)
    for i := 0; i <len(str2); i++ {
        fmt.Printf("character=%c ",str2[i])
    }
    
  3. String to integer: Atoi function in strconv package

    func Atoi(s string) (i int, err error)
    Atoi yes ParseInt(s, 10, 0)Short for.
    
    str := "hello"
    n,err := strconv.Atoi(str)
    if err != nil {
    	fmt.Println("Conversion error",err)
    }else {
    	fmt.Println("The result of the conversion is",n)
    }
    
  4. Integer to string: Itoa function in strconv

    func Itoa(i int) string
    Itoa yes FormatInt(i, 10) Short for.
    
  5. String to [] byte: VAR bytes = [] byte ("hello,go")

    var bytes = []byte("hello,go")
    fmt.Printf("bytes=%v\n",bytes)
    
    //Output: bytes = [104 101 108 111 44 103 111]
    
  6. [] byte to string: str = string([]byte{97,98,99})

    str = string([]byte{97,98,99})
    
  7. Conversion from decimal to 2,8,16 process: STR = strconv FormatInt(133,2)

    func FormatInt(i int64, base int) string
     return i of base Hexadecimal string representation. base It must be between 2 and 36, and lowercase letters will be used in the results'a'reach'z'Indicates a number greater than 10.
    
  8. Find whether the substring is in the specified string:

    func Contains(s, substr string) bool
     Judgment string s Whether to include substrings substr. 
    
  9. Count the number of specified substrings in a string

    func Count(s, sep string) int
     Return string s There are several non repetitive sep Substring.
    
  10. Case insensitive string comparison (= = case sensitive)

    func EqualFold(s, t string) bool
     Judge two utf-8 Encoding string unicode Whether the characters in upper case, lower case and title are the same.
    
  11. Returns the index value of the first occurrence of the string in the string. If not, - 1 is returned

    func Index(s, sep string) int
     Substring sep In string s The position that appears for the first time in. If it does not exist, it returns-1. 
    
  12. Returns the index of the last occurrence of the substring. If not, - 1 is returned

    func LastIndex(s, sep string) int
     Substring sep In string s The position of the last occurrence in the. If it does not exist, it will be returned-1. 
    
  13. Replace the specified substring with another substring: strings Replace ("go, go hello", "go", "go language", n) n can specify how many to replace. If n=-1, it means to replace all

    func Replace(s, old, new string, n int) string
     Return will s Middle front n No overlap old Substrings are replaced with new New string, if n<0 Will replace all old Substring.
    
  14. According to a specified character, a string is divided into a string array for segmentation identification: under the strings package

    func Split(s, sep string) []string
     Remove with s Appears in sep The method of segmentation will be segmented to the end, and the slices composed of all the generated fragments (each one) will be returned sep Will be cut once, even two sep Adjacent, two cuts will also be made). If sep Null character, Split Will s Cut into each unicode The code value is a string.
    
    strArr := strings.Split("hello,wrold,ok", ",")
    	for i := 0; i < len(strArr); i++ {
    		fmt.Printf("str[%v]=%v\n", i, strArr[i])
    	}
    	fmt.Printf("strArr=%v\n", strArr)
    
    The output is:
    str[0]=hello
    str[1]=wrold
    str[2]=ok
    strArr=[hello wrold ok]
    
  15. Converts the letters of a string to case

    func ToLower(s string) string
     Returns a copy that converts all letters to the corresponding lowercase version.
    
    func ToUpper(s string) string
     Returns a copy that converts all letters to the corresponding uppercase version.
    
  16. Remove the spaces on the left and right sides of the string

    func TrimSpace(s string) string
     Return will s All blanks at the front and rear ends( unicode.IsSpace Specifies the string to remove.
    
  17. Remove the characters specified on the left and right sides of the string

    func Trim(s string, cutset string) string
     Return will s All front and rear ends cutset Contained utf-8 A string whose code values are removed.
    
  18. Remove the specified character from the left of the string

    func TrimLeft(s string, cutset string) string
     Return will s Front end all cutset Contained utf-8 A string whose code values are removed.
    
  19. Remove the specified character from the right side of the string

    func TrimRight(s string, cutset string) string
     Return will s Back end all cutset Contained utf-8 A string whose code values are removed.
    
  20. Determines whether the string starts with the specified string

    func HasPrefix(s, prefix string) bool
     judge s Is there a prefix string prefix. 
    
  21. Determines whether the string ends with the specified string

    func HasSuffix(s, suffix string) bool
     judge s Is there a suffix string suffix. 
    

Time and date correlation function

Date related functions are often used in programming, such as counting the time spent on the execution of a piece of code

  1. Date and time related functions need to import the time package

  2. time.Time type, used to represent time

    now := time.Now()
    fmt.Printf("now=%v type=%T\n",now,now)
    //The output result is: now = 2022-04-02 21:20:23.5149432 + 0800 CST M = + 0.004880201 type = time Time
    
    fmt.Printf("year=%v\n", now.Year())
    fmt.Printf("month=%v\n", now.Month())
    fmt.Printf("month=%v\n", int(now.Month()))
    fmt.Printf("day=%v\n", now.Day())
    fmt.Printf("Time=%v\n", now.Hour())
    fmt.Printf("branch=%v\n", now.Minute())
    fmt.Printf("second=%v\n", now.Second())
    
    The output is:
    year=2022
     month=April
     month=4
     day=2
     Time=21
     branch=29
     second=11
    
  3. format date

    • Method 1: use Printf or Sprintf
    fmt.Printf("Current date %d-%d-%d %d:%d:%d\n",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second)
    
    dateStr := fmt.Sprintf("Current date %d-%d-%d %d:%d:%d\n",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second)
    fmt.Printf("dateStr=%v\n",dateStr)
    
    • Method 2: use time Format() method completed
    func (t Time) Format(layout string) string
    Format according to layout Return in the specified format t The formatted text representation of the point in time represented by. layout Defined reference time:
    
    Mon Jan 2 15:04:05 -0700 MST 2006
     The formatted string representation is used as an example of the desired output. The same formatting rules will be used to format the time.
    
    Predefined ANSIC,UnixDate,RFC3339 And other formats describe the standard or convenient representation of reference time. For more definitions and formats of reference time, see the of this package ANSIC And other layout constants.
    
    fmt.Printf(now.Format("2006-01-02 15:04:05"))
    fmt.Println()
    fmt.Printf(now.Format("2006-01-02"))
    fmt.Println()
    fmt.Printf(now.Format("15:04:05"))
    fmt.Println()
    
    Output:
    2022-04-03 11:12:52
    2022-04-03
    11:12:52
    
    explain;
    "2006/01/02 15:04:05" Each number of this string is fixed and must be written like this
    
  4. Constant of time

    In the program, it can be used to obtain the time in the specified time unit. For example, if you want to get 100 milliseconds: 100 * time Millisecond

    type Duration int64
    Duration Type represents the elapsed time between two time points, in nanoseconds. The longest period that can be expressed is about 290 years.
    
    const (
        Nanosecond  Duration = 1	//nanosecond
        Microsecond          = 1000 * Nanosecond //subtle
        Millisecond          = 1000 * Microsecond //millisecond
        Second               = 1000 * Millisecond //second
        Minute               = 60 * Second //minute
        Hour                 = 60 * Minute //hour
    )
    //The base number between milliseconds, subtleties and nanoseconds is 1000
    
  5. time.Sleep sleep

    //Output 1 number every 1 second
    i := 0
    for {
    	i++
    	fmt.Println(i)
    	time.Sleep(time.Second)
    	if i == 10 {
    		break
    	}
    }
    
  6. time Unix and Unix nano methods

    func (Time) Unix
    func (t Time) Unix() int64
    Unix take t Expressed as Unix Time, i.e. from time point January 1, 1970 UTC Time point t Elapsed time in seconds.
    
    func (Time) UnixNano
    func (t Time) UnixNano() int64
    UnixNano take t Expressed as Unix Time, i.e. from time point January 1, 1970 UTC It's time t Elapsed time in nanoseconds. If it's in nanoseconds unix Time out int64 The range that can be represented, and the result is undefined. Note that this means Time Zero value call UnixNano Method, the result is undefined.
    

Date and time function: Statistics code execution time

func test() {
	str := ""
	for i := 0; i < 100000; i++ {
		str += "hello" + strconv.Itoa(i)
	}
}
func main() {
	start := time.Now().Unix()
	test()
	end := time.Now().Unix()
	fmt.Printf("implement test()It takes time:%v\n", end-start)
}

Generate random number

1,func (*Rand) Intn

func (r *Rand) Intn(n int) int

Returns a pseudo-random int value with a value range of [0,n). If n < = 0, it will panic.

2,func Seed

func Seed(seed int64)

Use the given seed to initialize the default resource to a certain state; If seed is not called, the default resource behaves as if Seed(1) was called.

error handling

  1. By default, when a panic occurs, the program exits (crashes)
  2. If we want to catch and handle the error after the error occurs, so as to ensure that the program can continue to execute, and give the administrator a prompt (e-mail, SMS)

Basic description:

  1. Go pursues concise and elegant language, so go does not adopt try... catch... finally
  2. The processing methods referenced in go are defer, panic and recover
  3. The usage scenarios of these exceptions can be described as follows: a panic exception can be thrown in go, and then the exception can be caught in defer through recover, and then handled normally
func division() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println(err)
			//Error messages can be sent here
			fmt.Println("Send error message to administrator")
		}
	}()
	a := 10
	b := 0
	res := a / b
	fmt.Println("res=", res)
}
func main() {
	division()
	fmt.Printf("Here is main function")
}

Custom error

go language, you can also customize the error, using errors New and panic built-in functions

  1. errors.New("error description"), a value of error type will be returned, indicating an error
  2. The panic built-in function accepts a value of interface {} type as a parameter, accepts a variable of error type, outputs an error message, and exits the program
func readConf(name string) (err error) {
	if name == "config.ini" {
		//read
		return nil
	} else {
		//A custom error was returned
		return errors.New("Error reading file...")
	}
}
func test01() {
	err := readConf("confige.ini")
	if err != nil {
		//If there is an error reading the file, output the error and terminate the program
		panic(err)
	}
	fmt.Println("test01()Continue execution")
}

func main() {
	test01()
	fmt.Println("main()Function continues")
}


/*File error:
panic: Error reading file

goroutine 1 [running]:
main.test01()
        D:/MyCode/goWorkspace/gostudy/src/func4_2/errortest.go:36 +0x49
main.main()
        D:/MyCode/goWorkspace/gostudy/src/func4_2/errortest.go:44 +0x19
*/

package

A package is actually a collection of functions and data. Create different folders to store program files

In actual development, you need to call functions defined in other files in different files, such as: main Go, use utils Function in go file.

Go manages the file and project directory structure in the form of package. The go language does not allow importing packages without using them

Function of package:

  1. Identifiers that distinguish functions and variables with the same name
  2. When there are many program files, it can manage the project well
  3. Control the access scope of functions and variables, i.e. scope

Package structure:

Go language compiler has strict requirements for source directory. Each workspace must be composed of bin, pkg and src directories. The bin directory is mainly used to store executable files; pkg directory stores compiled library files, mainly * a) documents; src directory is mainly used to store the source files of go language.

Source file of package:

The header of the source file must be consistently declared with the statement of package < name >. go language package can be composed of multiple files, so the file name does not need to be consistent with the package name. It is recommended to use lowercase letters for package names, and try not to use reserved names (main, all, std). The executable file must contain package main and the entry function main.

go determines the access rights of an object (global variable, global constant, type, structure, field, function and method) through the case of the initial letter

Notes and details of the package

  1. When packaging a file, the package corresponds to a folder. The package name of the file is usually the same as the folder name where the file is located, usually in lowercase letters

  2. When a file needs to use other package functions or variables, the corresponding package needs to be introduced first

    • The package instruction is on the first line of the file, followed by the import instruction

    • When importing the package, the path starts from src of $GOPATH. Without src, the compiler will automatically import from src

    • In order to allow files in other packages to access functions in this package, the first letter of the function needs to be capitalized

    • When accessing other package functions and variables, the syntax is package name Function name

    • If the package name is long, Go supports aliasing the package. Note: after aliasing, the original package name cannot be used

    • You cannot have the same function name (or the same global variable) in the same package

      package main
      import (
      	"fmt"
      	util "go_code/chapter/fundemo/utils"
      )
      
    • If you want to compile into an executable file, you need to declare the package as main, that is, package main. This is a syntax specification. If you write a library, the package name can be customized

Sort and find

Sorting is divided into internal sorting and external sorting;

Eight internal sorting algorithms:

Insert sort: directly insert sort and insert sort

Swap sort: bubble sort, quick sort

Select sort: simple select sort and heap sort

Merge sort

Cardinality sort

Bubble sort:

func BubbleSort(arr *[5]int) {
	fmt.Println("Before sorting arr=", (*arr))
	temp := 0
	fmt.Printf("arr=%T,*arr=%T\n", arr, (*arr))
	for i := 0; i < len(arr)-1; i++ {
		for j := 0; j < len(*arr)-1-i; j++ {
			if (*arr)[j] > (*arr)[j+1] {
				temp = (*arr)[j]
				(*arr)[j] = (*arr)[j+1]
				(*arr)[j+1] = temp
			}
		}
	}
	fmt.Println("After sorting arr=", (*arr))
}

func main() {
	var arr = [5]int{5, 3, 87, 6, 66}
	BubbleSort(&arr)
	fmt.Println("After sorting arr=", arr)
}

Sequential search: traversal

Binary search: the sequence to be searched is required to be ordered

object-oriented

Golang also supports object-oriented programming (OOP), but different from traditional object-oriented programming, it is not a pure object-oriented language. Therefore, it is more accurate for us to say that golang supports object-oriented language.

Golang does not have classes. The structure of Go language has the same status as the classes of other languages. It can be understood that golang implements OOP features based on struct

Golang object-oriented programming is very concise, removing the inheritance, method overloading, constructor, destructor, hidden this pointer and so on of the traditional OOP language

Golang still has the characteristics of object-oriented inheritance, encapsulation and polymorphism, but the implementation method is different from other OOP languages. For example, golang does not have the extends keyword, and inheritance is realized through anonymous fields

Golang is very elegant object-oriented. OOP itself is a part of the language type system. It is associated through the interface. It has low coupling and is very flexible. Interface oriented programming is a very important feature in golang

structural morphology

Relationship between structure and structure variable (instance / object)

All cat features are extracted -- > cat structure {1. Field / attribute (Name, Age, Color) 2. Behavior (method)} -- > variable (instance), variable (instance)

  1. Extract the characteristics of a class of things to form a new data type, that is, a structure
  2. Through this structure, we can create multiple variables (instances / objects)
  3. Things can be cats, people, Fish, or tools

Case:

type Cat struct {
	Name  string
	Age   int
	Color string
	Hobby string
    Scores [3]int
}

func main() {
	//Use struct structure
	var cat1 Cat
	cat1.Name = "Xiaobai"
	cat1.Age = 3
	cat1.Color = "white"
	cat1.Hobby = "Eat fish"
	fmt.Println(cat1)
}

Differences and relations between structure and structure variables

It can be seen from the above example

  1. Structures are user-defined data types that represent a class of things
  2. Structural variables (instances) are concrete and practical, representing a specific variable
  3. Members of structure variables have default values
  4. Structure variables are value types

Declaration of structure:

type Structure name struct{
	field1 type
	field2 type
}

Fields / attributes:

  1. From the concept or name: structure field = attribute = field
  2. A field is an integral part of a structure. It is generally a basic data type, an array, or a reference type

Field / attribute considerations

  1. The syntax of field declaration is the same as that of variable
  2. The type of field can be basic type, array and reference type
  3. After creating a structure variable, if no value is assigned to the field, it corresponds to a zero value (default value),
    1. bool is false, the value is 0, and the string is' '
    2. The default value of array type is related to the type of element
    3. The zero values of pointer, slice and map are nil, that is, no space has been allocated
  4. The fields of different structure variables are independent and do not affect each other. The change of one structure variable field does not affect the other. The structure is a value type

Create structure variables and access structure fields

  1. Method 1: direct declaration

    var person Person

  2. Mode 2: {}

​ var person Person {}

  1. Mode 3:&

    var person *Person = new(Person)

  2. Mode 4: {}

    var person *Person = &Person{}

func main() {
	//Method 1: direct declaration
	var p1 Person
	p1.Name = "xioaming"
	p1.Age = 17

	//Mode 2: {}
	p2 := Person{"marry", 20}
	fmt.Println(p2)

	//Mode 3:&
	var p3 *Person = new(Person)
	//Since p3 is a pointer, the standard assignment method is
	(*p3).Name = "smith"
	(*p3).Age = 18
	fmt.Println(*p3)
	//(*p3).Name = "smith" can also be written P3 Name = "smith"
	//Reason: for the convenience of programmers, go designers will use p3 Name = "Smith" and add value operation (* p3) to p3 Name = "smith"
	var p4 *Person = new(Person)
	p4.Name = "john"
	p4.Age = 20
	fmt.Println(*p4)

	//Mode 4: {}
	//The following methods can also be used for direct assignment
	var p5 *Person = &Person{}
	//p5 is a pointer, the same as above
	p5.Name = "tom"
	p5.Age = 16

}

Memory allocation mechanism of struct type

  1. All fields of the structure are continuous in memory

    type Point struct {
    	x int
    	y int
    }
    type Rect struct {
    	leftUp, rightDown Point
    }
    type Rect1 struct {
    	leftUp, rightDown *Point
    }
    
    func main() {
    	//r1 has four int s that are contiguous in memory
    	r1 := Rect{Point{1, 2}, Point{3, 4}}
    	fmt.Printf("r1.leftUp.x address=%p r1.leftUp.y address=%p r1.rightDown.x address=%p r1.rightDown.y address=%p\n",
    		&r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)
    	//1.leftUp.x address = 0xc000000e1c0 R1 leftUp. Y address = 0xc000000e1c8 R1 rightDown. X address = 0xc000000e1d0 R1 rightDown. Y address = 0xc000000e1d8
    
    	//r2 has two * Point types. The addresses of these two * Point types are continuous, but the addresses they Point to are not necessarily continuous
    	r2 := Rect1{&Point{10, 20}, &Point{30, 40}}
    	fmt.Printf("r2.leftUp Self address=%p r2.rightDown Self address=%p\n",
    		&r2.leftUp, &r2.rightDown)
    
    	fmt.Printf("r2.leftUp Point to address=%p r2.rightDown Point to address=%p\n",
    		r2.leftUp, r2.rightDown)
    }
    
  2. Structure is a user-defined type. It needs exactly the same fields (name, number, type) when converting with other types

    type A struct {
    	Num int
    }
    type B struct {
    	Num int
    }
    
    func main() {
    	var a A
    	var b B
    	a = A(b)
    	fmt.Println(a, b)
    }
    
  3. The structure is redefined by type (equivalent to taking an alias). Golang believes that it is a new data type. The structure after type cannot be directly assigned to the original data type, but it can be forcibly transferred to each other

    1. A tag can be written on each field of struct, which can be obtained through reflection mechanism. The common use scenario is serialization and deserialization

      Monster serialization [json] – > the string is sent to the client for processing

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Skill string `json:"skill"`
}

func main() {
	monster := Monster{"Ox demon king", 500, "Banana fan"}

	//Serialize the monster variable into a json format string
	//json. Reflection in Marshal function
	jsonStr, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("json Processing error", err)
	}
	fmt.Println("jsonStr", string(jsonStr))
}

method

In Golang, methods work on specified data types (i.e., bind to data types). Therefore, custom types can have methods, not just struct s.

Go method is a function that acts on the receiver. The receiver is a certain type of variable, so in go language, method is a special type of function

Declaration of method:

func (recv receiver_type) methodName(parameter_list)(return_value_list){

......

}

func (data type bound by variable name) method name (formal parameter list) (return value list){

Operation

}

type A struct{
	Num int
}
func (a A) test(){
	fmt.Println(a.Num)
}

//explain
1.func (a A) test() {} express A The structure has a method called test
2.(a A) reflect test The method is and A Type bound

Case:

type Person struct {
	Name string
}

func (p Person) test() {
	fmt.Println(p.Name)
}

func main() {
	var p Person
	p.Name = "tom"
	p.test()
}
  1. The test() method is bound to the Person type
  2. rest() can only be called through Person type variables, and cannot be called directly or with variables of other types
  3. P in func(p Person) test() {} indicates which Person variable is called. This p is its copy, which is very similar to the function parameter passing. Changing the structure variable member in the method has no effect on the structure variable in the main function, except for the transmission address
  4. The name p is specified by the programmer and is not fixed. For example, it can be modified to person. Try to write something meaningful

How to get started:

  1. Add the speak method to the Person structure, and the output xxx is a good Person

    func (p Person) speak() {
    	fmt.Println(p.Name, "He is a good man")
    }
    
  2. Add the jisuan method to the Person structure and calculate the result from 1 + 1000. It shows that the method and function are the same

    func (p Person) jisuan() {
    	res := 0
    	for i := 1; i <= 1000; i++ {
    		res += i
    	}
    	fmt.Println(p.Name, "The calculation result is:", res)
    }
    
  3. Add jisuan2 to the Person structure. This method can accept a number N and calculate from 1 to n

    func (p Person) jisuan2(n int) {
    	res := 0
    	for i := 1; i <= n; i++ {
    		res += i
    	}
    	fmt.Println(p.Name, "The result of calculation 2 is:", res)
    }
    
  4. Add getSum to the Person structure. This method can calculate the sum of two numbers and return the result

    func (p Person) getSum(n1 int, n2 int) int {
    	return n1 + n2
    }
    
  5. Method call

    var p Person
    	p.Name = "tom"
    	p.test()
    	p.speak()
    	p.jisuan()
    	p.jisuan2(100)
    	fmt.Println("sum=", p.getSum(3, 3))
    

Method invocation and parameter passing mechanism

The mechanism of method calling and parameter passing is basically the same as that of a function. The difference is that when a method is called, the variable calling the method will also be passed to the method as an argument (if the variable is a value type, the value will be copied. If the variable is a reference type, the address will be copied)

Case:

type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	return 3.14 * c.radius * c.radius
}
func main() {
	var circle Circle
	circle.radius = 1.0
	fmt.Println("Ciecle area =", circle.area())
}
//Note: radius in return 3.14 * c.radius * c.radius is copied
func (c *Circle) area() float64 {
	return 3.14 * c.radius * c.radius
}
What's in this radius Is in the main function radius

Precautions and details of the method

  1. The structure type is a value type. In the method call, it follows the value transfer mechanism and is the value copy transfer mode

  2. If the programmer wants to modify the value of structure variable in the method, it can be handled by structure pointer

    type Circle struct {
    	radius float64
    }
    
    func (c *Circle) area() float64 {
    	fmt.Printf("c yes *Circle Address to=%p\n", c)
    	c.radius = 2
    	//c.radius is equivalent to (* c) radius
    	return 3.14 * c.radius * c.radius
    }
    func main() {
    	var circle Circle
    	circle.radius = 1.0
    	fmt.Printf("main circle Structure variable address =%p\n", &circle)
    	fmt.Println("Ciecle area =", circle.area())
    }
    
  3. In Golang, methods work on specified data types (i.e., bind to data types). Therefore, custom types can have methods, not just struct s.

    type integer int
    
    func (i integer) print() {
    	fmt.Println("i=", i)
    }
    func main() {
    	var i integer
    	i.print()
    }
    
  4. The access scope control of method is the same as that of function. The method name is lowercase and can only be accessed in this package. The method name is capitalized and can be accessed in this package and other packages

  5. If a method implements the String() method, FMT Println will call String() of this variable by default for output

    type Student struct {
    	Name string
    	Age  int
    }
    
    //Implement String() for students
    func (stu *Student) String() string {
    	str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
    	return str
    }
    func main() {
    	stu := Student{"msk", 20}
    	fmt.Println(&stu)
    }
    

Difference between method and function

  1. The calling method is different

    Functions: function name (argument list)

    Method: variable Method name (argument list)

  2. For ordinary functions, when the receiver is of value type, the data of pointer type cannot be passed directly, and vice versa

  3. For methods (such as struct methods), when the receiver is a value type, you can directly call the method with a pointer type variable, and vice versa. Tip: see whether the value type or pointer type is bound in the method

    • Regardless of the call form, the real decision is whether to copy the value or address, depending on which type the method is bound to

encapsulation

Encapsulation is to encapsulate the abstract fields and field operations. The data is protected internally. Other packages of the program can only operate the fields through authorized operations (Methods)

benefit

  1. Hide implementation details
  2. Verify the data to ensure safety and rationality

Packaging steps:

  1. Lowercase the first letter of structure and field

  2. Provide a factory mode function for the package where the structure is located, with the initial capital. Similar to a constructor

  3. Provide an initial capitalization Set method to judge and assign values to attributes

    func (var Structure type name) SetXxx(parameter list)(Return value list){
    	//Add data validation logic
    	var.field = parameter
    }
    
  4. Provide an uppercase Get method to Get the value of the property

    func (var Structure type name) GetXxx(){
    	return var.age
    }
    

Case:

package model

import "fmt"

type account struct {
	accountNo string
	pwd       string
	balance   float64
}

func NewAccount(accountNo string, pwd string, balance float64) *account {
	if len(accountNo) < 6 || len(accountNo) > 10 {
		fmt.Println("Wrong number of account numbers")
		return nil
	}
	if len(pwd) != 6 {
		fmt.Println("Wrong number of password digits")
		return nil
	}
	if balance < 20 {
		fmt.Println("The initial balance is less than 20!")
		return nil
	}

	return &account{
		accountNo: accountNo,
		pwd:       pwd,
		balance:   balance,
	}
}

func (account *account) Depsodite(money float64, pwd string) {
	if pwd != account.pwd {
		fmt.Println("Password input error!")
		return
	}

	if money <= 0 {
		fmt.Println("Incorrect amount entered!")
		return
	}

	account.balance += money
	fmt.Println("Deposit successful")
}

func (account *account) WithDraw(money float64, pwd string) {
	if pwd != account.pwd {
		fmt.Println("Password input error!")
		return
	}
	if money > account.balance || money < 0 {
		fmt.Println("Insufficient account balance!")
	} else {
		account.balance -= money
		fmt.Println("Withdrawal succeeded")
	}

}
func (account *account) Query(pwd string) {
	if pwd != account.pwd {
		fmt.Println("Password input error!")
	}
	fmt.Println(account.accountNo, "Your account balance is:", account.balance)
}

package main

import (
	"fmt"
	"gostudy/src/oop4_15/exer/factory/model"
)

func main() {
	account := model.NewAccount("123456", "234564", 20)
	account.WithDraw(20, "234564")
	account.Query("234564")
	account.Depsodite(1000, "234564")
	account.Query("234564")
}

inherit

Inheritance can improve code reusability and make our programming closer to human thinking. Solve code redundancy, facilitate function expansion and code maintenance

When the same attributes (fields) and methods exist in multiple structures, structures can be abstracted from these structures. (teenagers, teenagers, adults and the elderly all inherit Person). Therefore, after the structure Perosn is abstracted, the same attributes in other structures do not need to be defined repeatedly, and only one Person anonymous structure needs to be nested

Details:

  1. Structures can use all fields and methods in nested anonymous structures, that is, fields and methods with uppercase or lowercase letters can be used

    type A struct {
    	Name string
    	Age  int
    	sal  float64
    }
    
    func (a *A) sayOK() {
    	fmt.Println("Hello", a.Name)
    }
    func (a *A) saySal() {
    	fmt.Println(a.Name, "The salary is:", a.sal)
    }
    
    type B struct {
    	A
    }
    
    func (b *B) sayOk(){
    	fmt.Println("Hello",b.Name)
    }
    
    func main() {
    	var b B
    	b.A.Name = "Tom"
    	b.A.Age = 21
    	b.A.sal = 33333
    	b.A.saySal()
    }
    
  2. Anonymous structure field access can be simplified

func main() {
	//var b B
	//b.A.Name = "Tom"
	//b.A.Age = 21
	//b.A.sal = 33333
	//b.A.saySal()
	var b B
	b.Name = "Tom"
	b.Age = 21
	b.sal = 33333
	b.saySal()
}

Summary of the above codes
 When we go straight through b When accessing fields or methods, such as b.Name,Its execution process: the compiler will look at it first b Is there a corresponding type Name,If so, call directly B Type Name Field. If not, go see it B Embedded anonymous structure A Is there a statement in the Name Field, call if there is one, continue to search if there is none, and report an error if there is none
  1. When the structure and anonymous structure have the same fields or methods, the compiler adopts the principle of nearest access. If you want to access the fields and methods of anonymous structure, you can distinguish them by the name of anonymous structure

    func main() {
    	var b B
    	b.Name = "Tom"    //Proximity principle
    	b.A.Name = "jack" //The Name field in A anonymous structure is explicitly accessed
    	b.Age = 18
    	b.sayOK() //Proximity principle
    	b.A.sayOK() //The explicit access is to the sayOk() method in the A anonymous structure
    }
    
  2. Two (or more) anonymous structures are embedded in the structure. If two anonymous structures have the same fields and methods (and the structure itself does not have the same fields and methods), the name of the anonymous structure must be clearly specified during access, otherwise the compilation will report an error

  3. If a struct is nested with a well-known structure, this pattern is a combination. If it is a combination relationship, the name of the structure must be brought when accessing the fields or methods of the combined structure

    type D struct {
        a A //Famous structure
    }
    
    func main(){
        //For example, if there is a famous structure in D, the name of the famous structure must be brought when accessing the fields of the famous structure
        var d D
        d.a.Name = "jack"
    }
    
  4. After nesting anonymous structures, you can also directly specify the values of each anonymous structure field when creating a structure variable (instance).

    type TV1 struct {
    	Goods
    	Brand
    }
    
    type TV2 struct {
    	*Goods
    	*Brand
    }
    
    func main() {
    	tv1 := TV1{Goods{"TV 01", 1200}, Brand{"Haier", "Shandong"}}
    	tv2 := TV1{
    		Goods{
    			"TV 02",
    			1100,
    		},
    		Brand{
    			"SHARP",
    			"Beijing",
    		},
    	}
    	fmt.Println(tv1)
    	fmt.Println(tv2)
    
    	tv3 := TV2{&Goods{"TV 03", 1000}, &Brand{"SKYWORTH", "Shanghai"}}
    	tv4 := TV2{
    		&Goods{
    			"TV 04",
    			999,
    		},
    		&Brand{
    			"millet",
    			"Shanghai",
    		},
    	}
    	fmt.Println(*tv3.Goods, *tv3.Brand)
    	fmt.Println(*tv4.Goods, *tv4.Brand)
    
    }
    
  5. If there is an anonymous field of type int in a structure, there cannot be a second one. If you need more than one int field, you must specify a name for the int field

multiple inheritance

If a struct is nested with multiple anonymous structures, the structure can directly access the fields and methods of the nested anonymous structures, so as to realize multiple inheritance

type Goods struct {
	Name  string
	Price float64
}
type Brand struct {
	Name    string
	address string
}

type TV1 struct {
	Goods
	Brand
}

Details of multiple inheritance:

  1. If the embedded anonymous structure has the same field name or method name, it needs to be distinguished by the anonymous structure type during access
  2. In order to ensure the simplicity of the code, it is recommended not to use multiple inheritance as much as possible

Interface

The interface type can define a set of methods, but these do not need to be implemented. And the interface cannot contain any variables. When a user-defined type (such as structure Phone) is to be used, these methods will be written out (implemented) according to the specific situation

Basic syntax:

type Interface name interface{
	method1(parameter list) Return value list
	method2(parameter list) Return value list
}

func (t Custom type) method1(parameter list) Return value list{
	//Method implementation
}
func (t Custom type) method2(parameter list) Return value list{
	//Method implementation
}

Description:

  1. All methods in the interface have no method body, that is, the methods of the interface have no implementation. The interface embodies the polymorphism, high cohesion and low coupling of programming

  2. The interface in Golang does not need to be displayed. As long as a variable contains all the methods in the interface type, the variable implements the interface. Therefore, there is no keyword such as implement in Golang

  3. The interface itself cannot create an instance, but it can point to a variable (instance) of a custom type that implements the interface

    type AInterface interface {
    	Say()
    }
    type Stu struct {
    	Name string
    }
    
    func (stu Stu) Say() {
    	fmt.Println("stu Say()")
    }
    func main() {
    	var stu Stu
    	var a AInterface = stu
    	a.Say()
    }
    
  4. In Golang, a user-defined type needs to implement all methods of an interface. We say that this user-defined type implements the interface

  5. Only when a user-defined type implements an interface can an instance (variable) of the user-defined type be assigned to the interface type

  6. As long as it is a user-defined data type, you can implement an interface, not just a structure type

    type AInterface interface {
    	Say()
    }
    type Integer int
    
    func (i Integer) Say() {
    	fmt.Println("Integer Say i =", i)
    }
    func main() {
    	var i Integer = 10
    	var b AInterface = i
    	b.Say()
    }
    
  7. A custom type can implement multiple interfaces

  8. Cannot have variables in Golang interface

  9. For example, interface A and interface B can also implement one or more interfaces (for example, if interface A and interface b) respectively

  10. The interface type is a pointer (reference type) by default. If it is used without interface initialization, nil will be output

  11. The empty interface {} has no method. All types implement the empty interface, that is, we can assign any variable to the empty interface

    func main() {
    	var i Integer = 10
    	var b AInterface = i
    	b.Say()
    
    	var t I = i
    	var t2 interface{} = i
    	var num1 float64 = 8.9
    	t2 = num1
    	t = num1
    	fmt.Println(t, t2)
    }
    

Factory mode

There is no constructor (constructor) in the structure of Golang, which can usually be solved by using the factory pattern

package model
type Student struct{
	Name string
	......
}
When Student Capitalize if you want to create in another package Student Examples, introduction model Just a bag, but when student If the initial is lowercase, it won't work --> Factory mode solution

Case:

When the Student structure is capitalized, there is no problem

package model

type Student struct {
   Name  string
   Score float64
}
package main

import (
	"fmt"
	"gostudy/src/oop4_15/exer/factory/model"
)

func main() {
	var stu = model.Student{"msk", 99}
	fmt.Println(stu)
}

When student initial is lowercase, factory mode:

package model

type student struct {
	Name  string
	Score float64
}

//Because the initial letter of student structure is lowercase, it can only be used in model
//We solve it through factory mode
func NewStudnet(n string, s float64) *student {
	return &student{
		Name:  n,
		Score: s,
	}
}
//If the field is lowercase, it cannot be accessed directly in other packages Write a return method, call stu. middle note in other packages GetScore()
func (s *student) GetScore() float64{
    return s.score
}
package main

import (
	"fmt"
	"gostudy/src/oop4_15/exer/factory/model"
)

func main() {
	var stu = model.NewStudnet("tom", 88)
	fmt.Println(stu)
	fmt.Println("name=", stu.Name, "score=", stu.Score)
}

Tags: Go

Posted by lotrfan on Sat, 16 Apr 2022 19:01:53 +0930