栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

go语言基本语法1

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

go语言基本语法1

文章目录
  • go
    • 1 Go基础
      • 1.1 基本概念
        • 1.1.2 值类型:
        • 1.1.3 引用类型:(指针类型)
        • 1.1.4 Go语言声明方式:
        • 1.1.5 强制类型转换:
        • 1.1.6 格式化输出
      • 1.2 命名规范
        • 1.2.1 包名、文件名
        • 1.2.2 变量名
        • 1.2.3 函数、方法名
        • 1.2.4 结构体、接口名
      • 1.3 数组 array
      • 1.4 切片 slice
      • 1.5 指针
      • 1.6 集合 map
      • 1.7 结构体 struct
        • 1.7.1 关键字type,实现自定义类的操作
        • 1.7.2 正常操作
        • 1.7.3 利用指针实现结构体
        • 1.7.4 匿名结构体
        • 1.7.5 实现构造函数
        • 1.7.6 方法——绑定
        • 1.7.7 嵌套匿名结构体——实现继承
    • 2 流程控制
      • 2.1 if
      • 2.2 switch
      • 2.3 select
      • 2.4 for——没有while
      • 2.5 range
    • 3 函数
      • 3.1 基本语法
      • 3.2 参数
      • 3.3 闭包
        • 3.3.1 函数变量
        • 3.3.2 闭包
      • 3.4 延迟调用
        • 3.4.1 defer用途:
        • 3.4.2 注意defer使用关闭资源
        • 3.4.3 defer和return的关系
      • 3.5 错误处理
    • 4 接口

go

goland快捷键

Go语言开发的开源项目,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。

Go类似于Java,是编译型语言,自带编译器

参考教程:

1 Go基础

新版本的go 都是基于 go module 创建项目

Go.mod是Golang1.11版本新引入的官方包管理工具用于解决之前没有地方记录依赖包具体版本的问题,方便依赖包的管理。Modules和传统的GOPATH不同,不需要包含例如src,bin这样的子目录,一个源代码目录甚至是空目录都可以作为Modules,只要其中包含有go.mod文件。

Go.mod其实就是一个Modules,关于Modules的官方定义为:

Modules是相关Go包的集合,是源代码交换和版本控制的单元。go命令直接支持使用Modules,包括记录和解析对其他模块的依赖性。Modules替换旧的基于GOPATH的方法,来指定使用哪些源文件。

1.1 基本概念

【import _ 包路径】只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数,下划线在代码意思是忽略这个变量

1.1.2 值类型:
    bool
    int(32 or 64), int8, int16, int32, int64
    uint(32 or 64), uint8(byte), uint16, uint32, uint64
    float32, float64
    string,字符串底层是一个byte数组,所以可以和[]byte类型相互转换。字符串是不能修改的
    complex64, complex128
    array    -- 固定长度的数组
1.1.3 引用类型:(指针类型)
    slice   -- 序列数组(最常用)
    map     -- 映射
    chan    -- 管道
1.1.4 Go语言声明方式:
    var(声明变量), const(声明常量), type(声明类型) ,func(声明函数)。
1.1.5 强制类型转换:
    T(表达式),T表示要转换的类型。表达式包括变量、复杂算子和函数返回值等.
1.1.6 格式化输出

1.2 命名规范 1.2.1 包名、文件名
  • 包名为全小写单词, 不使用复数,不使用下划线。尽可能简短。

    package domain
    package main
    
  • 文件名为全小写单词,使用 “_” 分词。

    approve_service.go
    
  • 测试文件必须以_test.go结尾。

1.2.2 变量名
  • 变量、类型、函数名、结构命名遵循驼峰法。大写字母开头=public,小写字母开头=private

    type UserController struct
    func isValidNumber(s string)
    

    局部变量、函数参数的命名全部采用 lowerCamelCase。我们尽量让局部变量和函数参数的命名意义明确。

    func Open(driverName, dataSourceName string) (*DB, error)
    
    • 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
    MAX_STOCK_COUNT
    
    • Golang中没有专门的枚举类型(enum),通常使用一组常量来表示,为了更好的区分不同的枚举类型值,使用完整的前缀加以区分:
       type PullRequestStatus int
       const  (
         PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
         PULL_REQUEST_STATUS_CHECKING
         PULL_REQUEST_STATUS_MERGEABLE
       )
    
  • 对于只在本文件中有效的顶级变量、常量,应该使用 “_” 前缀,避免在同一个包中的其他文件中意外使用错误的值。例如:

var (
  _defaultPort = 8080
  _defaultUser = "user"
)
  • 若变量、常量为 bool 类型,则名称应以 Has、Is、Can 或 Allow 开头:
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
  • 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀:
type PullRequestStatus int
const ( PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota PULL_REQUEST_STATUS_CHECKING PULL_REQUEST_STATUS_MERGEABLE
)
1.2.3 函数、方法名
  • 函数、方法(结构体或者接口下属的函数称为方法)命名规则: 动词 + 名词。
  • 若函数、方法为判断类型(返回值主要为 bool 类型),则名称应以 Has、Is、Can 或 Allow 等判断性动词开头:
func HasPrefix(name string, prefixes []string) bool { ... }
func IsEntry(name string, entries []string) bool { ... }
func CanManage(name string) bool { ... }
func AllowGitHook() bool { ... }
1.2.4 结构体、接口名
  • 结构体命名规则:名词或名词短语。
  • 接口命名规则:以 ”er” 作为后缀,例如:Reader、Writer。接口实现的方法则去掉 “er”,例如:Read、Write。
type Reader interface { Read(p []byte) (n int, err error)
}

// 多个函数接口
type WriteFlusher interface { Write([]byte) (int, error) Flush() error
}
1.3 数组 array
数组定义:var a [len]int,比如:var a [5]int
多维数组:var arr0 [5][3]int
   		var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}
   		
//数组遍历test
	var myString [5]int = [5]int{1, 2, 3, 4, 5}
	for i := 0; i < len(myString); i++ {
		fmt.Print(myString[i], ",")
	}
	fmt.Println()
	for index, value := range myString {
		fmt.Print(index, ":", value, ",")
	}
	fmt.Println()

	// 二维数组遍历
	var multiString [3][2]int = [...][2]int{{1, 2}, {3, 4}, {5, 6}}
	for k1, v1 := range multiString {
		for k2, v2 := range v1 {
			fmt.Printf("(%d,%d)=%d,", k1, k2, v2)
		}
		fmt.Println()
	}
1.4 切片 slice

切片跟数组的区别在于 Type 前的“ [] ”中是否有数字,为空,则代表切片,否则则代表数组。因为切片是长度可变的

var slice []type = make([]type, len, cap)
var slice []type = []type{1,2,3,4}
// 切片test
var slice = []int{1, 2, 3, 4}
var slice1 = make([]int, 4)
fmt.Printf("%v", slice) // [1 2 3 4]
fmt.Printf("%v", slice1)// [0 0 0 0]

原理:

假想每次传参都用数组,每次数组都要被复制一遍。消耗掉大量的内存。——函数传参用数组的指针。

函数传参问题:原数组的指针指向更改了,那么函数里面的指针指向都会跟着更改

切片优势:切片的指针和原来数组的指针是不同的

1.5 指针

指针地址、指针类型、指针取值

&arrayA //& 得到数组的内存地址,取地址
*[]int  //* 获取内存地址对应值,取值

new函数得到的是一个类型的指针
a := new(int)//a := *int
make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建

//用指针修改值
var num = 3
var ptr = &num
*ptr = 6
fmt.Println(num) // 6
1.6 集合 map

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

//maptest: map[keyType]valueType
var myMap map[string]int = make(map[string]int, 3)
myMap1 := make(map[string]int, 3)

myMap["liming"] = 12
myMap1["zhangming"] = 18
fmt.Println(myMap["liming"])
val, ok := myMap1["zhangming"]//判断集合是否包含某key
fmt.Println(val, ok)//18 true

//遍历map
for k, v := range myMap {
	fmt.Print(k, "_", v)
}
1.7 结构体 struct 1.7.1 关键字type,实现自定义类的操作
// typetest
type MyInt int    //自定义一种新类型叫MyInt
type NewInt = int //给int起一个小名叫NewInt
type person struct {
    name string
    age  int
    like map[string]string
    have []int
}
1.7.2 正常操作
var myPerson person
myPerson.name = "Lily"
myPerson.age = 20
myPerson.have = []int{1, 2, 3, 4, 5}
myPerson.like = map[string]string{
    "run":     "yes",
    "swim":    "yes",
    "bicycle": "yes",
}
fmt.Printf("%v", myPerson) // {Lily 20 map[bicycle:yes run:yes swim:yes] [1 2 3 4 5]}
1.7.3 利用指针实现结构体
//利用指针进行新建
var p = new(person) //new是新建了一个指针
p.name = "lucy"
fmt.Printf("%v", *p)//{lucy 0 map[] []}

//&
var person1 = &person{
	name: "Make",
}
fmt.Printf("%v", person1)//&{Make 0 map[] []}
1.7.4 匿名结构体
var car struct {
	Name  string
	Speed int
}
car.Speed = 120
car.Name = "BMW"
fmt.Printf("%v", car)
1.7.5 实现构造函数
type Student struct {
	name    string
	age     int
	subject string
}

func newStudent(name string, age int, subject string) *Student {
	return &Student{
		name:    name,
		age:     age,
		subject: subject,
	}
}

func main(){
    student := newStudent("sommer", 17, "math")
	fmt.Printf("%#vn", student)//&main.Student{name:"sommer", age:17, subject:"math"}
}
1.7.6 方法——绑定
 func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
        函数体
    }

func (student Student) read(book string) {
	fmt.Println(student.name, "读", book) 
}

指针方法原值,拷贝方法不改变原值

注意:

当传递的是值拷贝类型时,不能传递指针*person和值&person

当传递的是指针类的时候,指针类型和值类型的变量均可相互调用

func main(){ 
	student.changSubject("history")
    fmt.Printf("%#vn", student)//&main.Student{name:"sommer", age:17, subject:"history"}
    student.changSubject2("history")
    fmt.Printf("%#vn", student)//&main.Student{name:"sommer", age:17, subject:"math"}
}  

func (student *Student) changSubject(newSubject string) {
	student.subject = newSubject
}
func (student Student) changSubject2(newSubject string) {
	student.subject = newSubject
}
1.7.7 嵌套匿名结构体——实现继承

所有的内置类型和自定义类型都是可以作为匿名字段去使用

func main(){
    myStudent := Student{
		name:    "Jack",
		age:     18,
		subject: "English",
		Person: Person{//匿名结构体直接嵌套,如下如果是*Person,那么这里应该是&Person
			hight: 180,
		},
	}
	fmt.Printf("%#vn", myStudent)//main.Student{name:"Jack", age:18, subject:"English", Person:main.Person{hight:180}}
}

type Student struct {
	name    string
	age     int
	subject string
	Person// 对应的如果这里是*Person
}

type Person struct {
	hight int
}
2 流程控制 2.1 if
 if a < 20 {
       fmt.Printf("a 小于 20n" )
   } else {
       fmt.Printf("a 不小于 20n" )
   }
2.2 switch
//Go里面switch默认相当于每个case最后带有break,匹配成功自动跳出整个switch,但是可以使用fallthrough强制执行后面的case代码
//如果switch没有表达式,它会匹配true
switch i := x.(type) { // 带初始化语句
    case nil:
        fmt.Printf(" x 的类型 :%Trn", i)
    case int:
        fmt.Printf("x 是 int 型")
    case float64:
        fmt.Printf("x 是 float64 型")
    case func(int) float64:
        fmt.Printf("x 是 func(int) 型")
    case bool, string:
        fmt.Printf("x 是 bool 或 string 型")
    default:
        fmt.Printf("未知型")
    }
2.3 select 2.4 for——没有while

基本三种语法

s := "abc"

for i, n := 0, len(s); i < n; i++ { // 常见的 for 循环,支持初始化语句。
    println(s[i])
}

n := len(s)
for n > 0 {                // 替代 while (n > 0) {}
    println(s[n])        // 替代 for (; n > 0;) {}
    n-- 
}

for {                    // 替代 while (true) {}
    println(s)            // 替代 for (;;) {}
}
2.5 range

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。

for key, value := range oldMap {
    newMap[key] = value
}
1st value2nd value
stringindexs[index]unicode, rune
array/sliceindexs[index]
mapkeym[key]
channelelement
3 函数 3.1 基本语法
func test(x, y int, s string) (int, string) {
    // 类型相同的相邻参数,参数类型可合并。 多返回值必须用括号。
    n := x + y          
    return n, fmt.Sprintf(s, n)
}

// 利用别名操作
// 定义函数类型。
type FormatFunc func(s string, x, y int) string 

func format(fn FormatFunc, s string, x, y int) string {
    return fn(s, x, y)
}
3.2 参数

在默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。map、slice、chan、指针、interface默认以引用的方式传递。

可变参数在参数后加上“…”即可,

其中args是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数.

  func myfunc(args ...int) {    //0个或多个参数
  }

  func add(a int, args…int) int {    //1个或多个参数
  }

  func add(a int, b int, args…int) int {    //2个或多个参数
  }

用interface{}传递任意类型数据是Go语言的惯例用法,而且interface{}是类型安全的。

func myfunc(args ...interface{}) {
  }

使用 slice 对象做变参时,必须展开。(slice...)

package main

import (
    "fmt"
)

func test(s string, n ...int) string {
    var x int
    for _, i := range n {
        x += i
    }

    return fmt.Sprintf(s, x)
}

func main() {
    s := []int{1, 2, 3}
    res := test("sum: %d", s...)    // slice... 展开slice
    println(res)
}
3.3 闭包 3.3.1 函数变量
func square(x int) {
    println(x * x)
}
  1. 直接调用:square(1)
  2. 把函数当成变量一样赋值:s := square;接着可以调用这个函数变量:s(1)。s就是这个函数变量
    注意:这里 square 后面没有圆括号,调用才有。
3.3.2 闭包

闭包 = 函数 + 相关引用环境,组合而成的实体,闭包对它作用域上部的变量可以进行修改,修改引用的变量会对变量进行实际修改

str := "hello world"

foo := func() {
	str = "hello dude" // 匿名函数中并没有定义 str,str 的定义在匿名函数之前,str 就被引用到了匿名函数中形成了闭包
}

foo()//执行闭包,此时 str 发生修改,变为 hello dude。

把整个匿名函数当做函数变量,用另一个函数进行整合,就形成了常见的模式

// 1
func() int {//无输入参数,返回int
    var x int
    func() {
        x++
    }
    return x
}
// 2  整合  
func() int {
    var x int
    return func() int {
        x++
        return x
    }
}
// 3 作为另一个函数的返回值
func incr() func() int {
    var x int
    return func() int {
        x++
        return x
    }
}

i := incr() //通过把这个函数变量赋值给 i,i 就成为了一个闭包。i 保存着对 x 的引用
println(i()) // 1
println(i()) // 2
println(i()) // 3
3.4 延迟调用 3.4.1 defer用途:
    1. 关闭文件句柄
    2. 锁资源释放,defer 是先进后出
    3. 数据库连接释放
3.4.2 注意defer使用关闭资源
type Member struct {
   name string
}

func (member *Member) Close() {
   fmt.Println(member.name, "closed")
}

func main(){
   arr := []Member{{"a"}, {"b"}, {"c"}}
   for _, m := range arr {
      myMember := m		//这里需要多一步赋值,不然无法正常关闭资源
      defer myMember.Close()
   }
}

// c closed
// b closed
// a closed
3.4.3 defer和return的关系

A. 无名返回值的情况

package main

import (
	"fmt"
)

func main() {
	fmt.Println("return:", a()) // 打印结果为 return: 0
}

func a() int {
	var i int
	defer func() {
		i++
		fmt.Println("defer2:", i) // 打印结果为 defer: 2
	}()
	defer func() {
		i++
		fmt.Println("defer1:", i) // 打印结果为 defer: 1
	}()
	return i
}

B. 有名返回值的情况

package main

import (
	"fmt"
)

func main() {
	fmt.Println("return:", b()) // 打印结果为 return: 2
}

func b() (i int) {
	defer func() {
		i++
		fmt.Println("defer2:", i) // 打印结果为 defer: 2
	}()
	defer func() {
		i++
		fmt.Println("defer1:", i) // 打印结果为 defer: 1
	}()
	return i // 或者直接 return 效果相同
}

原因

return会将返回值先保存起来,对于无名返回值来说, 保存在一个临时对象中,defer是看不到这个临时对象的;

而对于有名返回值来说,就保存在已命名的变量中。整个过程中这个变量的内存地址并没有变化,所以此时defer会修改实际返回的结果

3.5 错误处理

defer、panic 和 recover的配合

  • panic 出错调用 panic 后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方的 defer;
  • recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用;
func main(){
	testError()
}

func getAeril(radius float32) (area float32) {
   if radius < 0 {
      panic("注意!半径不能为负数")
   } else {
      return radius * radius * 3.14
   }
}

func testError() {
   defer func() {
      if err := recover(); err != nil {
         fmt.Println(err)
      }
   }()
    getAeril(-3)//出错调用pinic,得到err,跳转到defer执行,运行到recover()继续程序的执行
   fmt.Println("错误之后的内容执行") //没有执行
}
4 接口
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1032688.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号