目录

前言:

Go语言的设计初衷

Go语言很特别

Go语言的主要特征

GO概览

Go基础

应用程序入口

Go语言声明

下划线(很特殊)

变量声明

类型转化

类型的预定义值

指针类型

常量声明

数组

数组截取

用 == 比较数组

位运算符--&^ 按位置零

切片Slice

数组 vs. 切片

1 array

2 slice


前言:

Go语言的设计初衷

  • 针对其他语言的痛点进行设计
  • 并加入并发编程
  • 为大数据、微服务、并发而生的通用编程语言

Go语言很特别

  • 没有“对象”,没有继承多态、类等,没有泛型,没有try/catch
  • 有接口,函数式编程,CSP并发模型(goroutine+channel)
  • 自带gc
  • 静态编译,编译好后,扔服务器直接运行
  • 学习Go语言很简单,因为语法简单
  • 用好Go语言不容易,因为要调整三观

 

 

Go语言的主要特征

  • 1.自动立即回收
  • 2.更丰富的内置类型
  • 3.函数多返回值
  • 4.错误处理
  • 5.匿名函数和闭包
  • 6.类型和接口
  • 7.并发编程
  • 8.反射
  • 9.语言交互性

GO概览

  • 基本语法:变量,选择、循环,指针,数组,容器
  • 面向接口:接口体,duck typing的概念,组合的思想
  • 函数式编程:闭包的概念
  • 工程化:资源管理、错误管理、测试和文档、性能调优
  • 并发编程:理解调度器

Go基础

应用程序入口

应用程序入口

  • 1. 必须是 main 包: package mai
  • 2. 必须是 main 方法: func main()
  • 3. ⽂文件名不不⼀一定是 main.go

退出返回值---与其他主要编程语⾔言的差异

  • 1.Go 中 main 函数不不⽀支持任何返回值
  • 2. 通过 os.Exit 来返回状态

获取命令⾏行行参数---与其他主要编程语⾔言的差异

  • 1.main 函数不不⽀支持传⼊入参数

    func main(arg []string)

  • 2.在程序中直接通过 os.Args 获取命令⾏行行参数
     

Go语言声明

  • var(声明变量)
  • const(声明常量)
  • type(声明类型)
  • func(声明函数)

1)声明在函数内部,是函数的本地值,类似private
2)声明在函数外部,是对当前包可见(包内所有.go文件都可见)的全局值,类似protect
3)声明在函数外部且首字母大写是所有包可见的全局值类似public

下划线(很特殊)

“_”是特殊标识符,用来忽略结果。

  • 下划线在import中:用【import _ 包路径】只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数
  • 下划线在代码中:忽略这个变量,意思是那个位置本应赋给某个值,但是咱们不需要这个值。所以就把该值赋给下划线,意思是丢掉不要。

变量声明

  • Go语言中的变量需要声明后才能使用,同一作用域内(可以放在函数内,也可以放在包内)不支持重复声明。并且Go语言的变量声明后必须使用。
  • 使用var()集中定义常量
  • 在函数内部,可以使用更简略的 := 方式声明并初始化变量。:=不能使用在函数外
  • 没有char,只有rune: rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成
  • 原生支持复数:complex64  complex128 (实部、虚部)

类型转化

  • Go 语⾔言不允许隐式类型转换
  •  别名和原有类型也不能进行隐式类型转换

类型的预定义值

  • math.MaxInt64
  • math.MaxFloat64
  • math.MaxUint32

指针类型

  1. 不⽀持指针运算
  2. string 是值类型,其默认的初始化值为空字符串,而不是 nil

常量声明

  • 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值
  • Go语言的常量不用大写,在go中大小写是有区别的
  • iota(自增值)go语言的常量计数器,只能在常量的表达式中使用。

数组

Golang Array和以往认知的数组有很大不同。

1. 数组:是同一种数据类型的固定长度的序列。

2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。数量写在类型前

3. 长度是数组类型的一部分因此,var a[5] int和var a[10]int是不同的类型

4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1

for i := 0; i < len(a); i++ {
}
for index, v := range a {
}

5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
6. 调用func f(arr[10] int)会拷贝数组。数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
7.支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
8.指针数组 [n]*T,数组指针 *[n]T。

数组截取

a[开始索引(包含), 结束索引(不不包含)]

a := [...]int{1, 2, 3, 4, 5}
a[1:2] //2
a[1:3] //2,3
a[1:len(a)] //2,3,4,5
a[1:] //2,3,4,5
a[:3] //1,2,3

用 == 比较数组

  1. 相同维数且含有相同个数元素的数组才可以⽐比较
  2. 每个元素都相同的才相等

位运算符--&^ 按位置零

1 &^ 0 -- 1
1 &^ 1 -- 0
0 &^ 1 -- 0
0 &^ 0 -- 0

切片Slice

slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。
Slice本身没有数据,是对底层array的一个view

  • 1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
  • 2. 切片的长度可以改变,因此,切片是一个可变的数组
  • 3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。 
  • 4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
  • 5. 切片的定义:var 变量名 []类型,比如 var str []string  var arr []int。

  • 6. 如果 slice == nil,那么 len、cap 结果都等于 0

切⽚片共享存储结构

append超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满

package main

import (
	"fmt"
	"testing"
)

func TestEmptyInterfaceAssertion(t *testing.T) {
	// 10: 0 使用冒号初始化元素--表示第10个位置值为0
	data := [...]int{0, 1, 2, 3, 4, 10: 0}
	// 
	s := data[:2:3]
	fmt.Println(s)
	s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。

	fmt.Println(s, data)         // 重新分配底层数组,与原数组无关。
	fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。

}

输出结果:

[0 1]
[0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
0xc000086060 0xc000056060

append可能会导致cap不足,需要重新分配内存。此时需要修改slice指向的内存地址,len,cap。

但是由于append没法修改原slice变量的属性,这就出现了矛盾:需要修改原输入参数,但有不能产生副作用。


golang的解决方法是强制规定append的返回值必须赋值给slice。算是解决了忘记赋值的问题,但是太丑陋了。

给slice提供一个append方法不是更简单直接吗?

数组 vs. 切片

  • 容量是否可伸缩
  • 是否可以进⾏比较

1 array

  • 类型相同, 长度固定的数据集合. 长度无法修改
  • 类型为: [len]T (len不能省略)
  • 值是一段连续的内存区域和一些额外属性

属性示例:

  • beg_addr 起始地址. 赋值时固定
  • len 长度. 赋值时固定
  • cap 保存实际长度. 赋值时固定,始终和len相同

要点:

  • 编译时需要确定长度. 需要显式指定长度, 或者使用...由编译器自动推断
  • go中都是值传递(pass by value). 因此赋值会复制数组本身.
  • 引用需要借助pointer

2 slice

  • 可以看作是内存的视图,展示内存的一段区域
  • 类型为: []T
  • 值是一个指向内存的数据结构, 类似指向数组的指针

属性示例:

  • array 底层数组
  • len 长度. 赋值(包括reslice)时设置
  • cap 从起始地址开始,最多可以展示的内存长度. = 底层数组结束地址-起始地址

要点:

  • 由于传递的是指针, 函数内可以修改外部slice指向的底层数组.  
  • 多个slice可以指向同一个底层数组.
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐