首页 > 技术文章 > 16.go语言基础学习(上)——2019年12月16日

oneapple 2019-12-16 16:55 原文

2019年12月13日10:35:20

1.介绍

2019年10月31日15:09:03

image-20191031150916257 image-20191031150951050 image-20191031151030511 image-20191031151138476 image-20191031151155689 image-20191031151211549 image-20191031152835829

2.基本语法

2.1 定义变量

2019年10月31日16:12:34

1.函数外必须使用var定义变量

image-20191031161310008
var a=1
b:=5

声明变量的一般形式是使用 var 关键字:

var name type

其中,var 是声明变量的关键字,name 是变量名,type 是变量的类型。

2.没有全局变量说法,只有包变量

3.集中定义变量

var (
	a int
	c string
	d bool
)

4.使用var关键字

var a,b,c bool

var s1,s2 string="hello","world"

5.使用:=定义变量

只能在函数体内使用

a,b,c:=1,2,"123"

6.基本类型

Go语言的基本类型有:
bool
string
int、int8、int16、int32、int64
uint、uint8、uint16、uint32、uint64、uintptr
byte // uint8 的别名
rune // int32 的别名 代表一个 Unicode 码
float32、float64
complex64、complex128

7.源代码:

package main

import "fmt"

var (
	a int
	c string
	d bool
)

func main() {
	varible()
	varibleType()
	varibleSimple()
	fmt.Println(d)

}

func varible(){
	var s int
	var t string
	fmt.Printf("%d %q\n",s,t)

}

func varibleSimple(){
	a,b,c :=1,2,"abc"
	b=5
	fmt.Println(a,b,c)
}

func varibleType(){

	var a,b,c=1,2,"abc"
	fmt.Println(a,b,c)

}

2.2 go内建变量类型

2019年10月31日16:26:53

1.复数类型

两种复数类型,分别是 complex64(32 位实数和虚数)类型和 complex128(64 位实数和虚数)类型。

内置的 complex 函数用于构建复数,内建的 real 和 imag 函数分别返回复数的实部和虚部:

var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y)                 // "(-5+10i)"
fmt.Println(real(x*y))           // "-5"
fmt.Println(imag(x*y))           // "10"

复数使用 re+imi 来表示,其中 re 代表实数部分,im 代表虚数部分,i 代表根号负 1。如果一个浮点数或一个十进制整数后面跟着一个 i,例如 3.141592i 或 2i,它将构成一个复数的虚部,而复数的实部是 0

fmt.Println(1i * 1i) // "(-1+0i)", i^2 = -1

2.强制类型转换

类型转换是强制的

规定是什么类型的参数一定要必须是

返回什么类型也得强转。

3.正弦

正弦函数由 math 包提供,函数入口为 math.Sin,正弦函数的参数为 float64,返回值也是 float64。在使用正弦函数时,根据实际精度可以进行转换。

2.3 常量与枚举

2019年10月31日18:08:39

1.常量

//常量
const s,t=1,2
const y int=5

2.枚举

iota 开始生成枚举值, 默认为0

iota 不仅可以生成每次增加 1 的枚举值。还可以利用 iota 来做一些强大的枚举常量值生成器

//枚举
func enums()  {
	const(
		cpp=iota
		java
		python
		golang
	)
	//b,kb,mb,gb,tb,pb
	
	const(
		b=1<<(10*iota)
		kb
		mb
		gb
		tb
		pb
	)

	fmt.Println(cpp,java,python,golang)
	fmt.Println(b,kb,mb,gb,tb,pb)
}

3.总结

  1. 变量类型写在变量名之后
  2. 编译器可推测变量类型
  3. 没有char,只有rune
  4. 原生支持复数类型

2.4 条件语句

2019年10月31日18:24:56

1.if的条件里不需要括号

func varible1() int {
	var a int=5
	if a>3 {
		return 100
	}else{
		return 122
	}
}

2.if的条件里可以赋值

	if contents,err:=ioutil.ReadFile(filename); err!=nil{
			fmt.Println(err)
		}else {
		fmt.Println("%s\n", contents)
	}

3.switch

func eval(a,b int,op string) int{
	var result int
	switch op {
	case "+":
		result = a + b
	case "-":
		result = a - b
	default:
		panic("unsupported" + op)
	}
	return result
}

4.switch会自动break,除非使用fallthrough

5.switch后可以没有表达式

image-20191031190306641

6.源码

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {

	const filename="abc.txt"

	//contents,err:=ioutil.ReadFile(filename)
	//if err!=nil{
	//	fmt.Println(err)
	//}else {
	//	fmt.Println("%s\n",contents)
	//}
	//
	//fmt.Println(contents)
	if contents,err:=ioutil.ReadFile(filename); err!=nil{
			fmt.Println(err)
		}else {
		fmt.Println("%s\n", contents)
	}

	fmt.Println(eval(3,1,"+"))
	
}

func varible1() int {
	var a int=5
	if a>3 {
		return 100
	}else{
		return 122
	}
}

func eval(a,b int,op string) int{
	var result int
	switch op {
	case "+":
		result = a + b
	case "-":
		result = a - b
	default:
		panic("unsupported" + op)
	}
	return result
}

2.5 循环

2019年10月31日19:12:25

1.for的条件里不需要括号

//整数转成二进制
func main() {

	fmt.Println(
		convertToBin(5),
		convertToBin(15),
		)
	
}

//循环
func convertToBin(n int) string {
	result:=""
	for ;n>0;n/=2{
		lsb:=n%2
		result=strconv.Itoa(lsb)+result
	}
	return  result
}

2.没有while

3.源码

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {

	fmt.Println(
		convertToBin(5),
		convertToBin(15),
		)
	printFile("abc.txt")
}

//循环
func convertToBin(n int) string {
	result:=""
	for ;n>0;n/=2{
		lsb:=n%2
		result=strconv.Itoa(lsb)+result
	}
	return  result
}

//读取文本
func printFile(filename string){
	file,err:=os.Open(filename)
	if err!=nil{
		panic(err)
	}else{
		scanner:=bufio.NewScanner(file)
		for scanner.Scan(){
			fmt.Println(scanner.Text())
		}
	}
}

2019年10月31日19:28:28

2.6 函数

2019年10月31日19:28:41

1.函数可以返回多个值

func div(a,b int)(q,r int){
	return a/b,a%b
}

func main() {
	fmt.Println(123)
	div(13,4)

}

2.返回多个值可以起名字

3.没有默认参数,可选参数

4.介绍

函数的基本组成为:关键字 func、函数名、参数列表、返回值、函数体和返回语句,每一个程序都包含很多的函数,函数是基本的代码块。

因为Go语言是编译型语言,所以函数编写的顺序是无关紧要的,

鉴于可读性的需求,最好把 main() 函数写在文件的前面,其他函数按照一定逻辑顺序进行编写(例如函数被调用的顺序)。

2.7 指针

2019年10月31日20:19:48

1.指针不能运算

指针(pointer)在Go语言中可以被拆分为两个核心概念:

  • 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
  • 切片,由指向起始元素的原始指针、元素数量和容量组成。

切片比原始指针具备更强大的特性,而且更为安全。切片在发生越界时,运行时会报出宕机,并打出堆栈,而原始指针只会崩溃。

2.go语言只有值传递

image-20191031202634060 image-20191031202743997

3.

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用在变量名前面添加&

ptr := &v   // v 的类型为 T

其中 v 代表被取地址的变量,变量 v 的地址使用变量 ptr 进行接收,ptr 的类型为*T

称做 T 的指针类型,*代表指针。

3.数组切片容器

3.1 数组

2019年10月31日20:37:02

1.定义数组的集中方法

  var arr1 [5]int

	arr2:=[3]int{1,3,5}
	
	arr3:=[...]int{2,4,6,8,10}
	
	var grid [4][5]int

	fmt.Println(arr1,arr2,arr3)
	fmt.Println(grid)

image-20191031204120538

2.遍历数组 range

//传统方式
for i:=0;i< len(arr3);i++{
		fmt.Println(arr3[i])
	}

使用range

for i:=range arr3{
		fmt.Println(arr3[i])
	}

获得索引和值

	for i,v:=range arr3{
		fmt.Println(i,v)
	}
image-20191031204633138

3.range遍历时索引不想用到

	for _,v:=range arr3{
		fmt.Println(v)
	}

4.为什么用到range

image-20191031204944129

5.数组是值类型

6.[10]int 和[20]int是不同类型

7.调用func f(arr [10]int)会 拷贝 数组

8.go语言一般不直接使用数组

3.2 切片-slice

2019年10月31日20:55:34

image-20191031205824845

1.介绍

切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)

这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内。

切片一般用于快速地操作一块数据集合,如果将数据集合比作切糕的话,切片就是你要的“那一块”,切的过程包含从哪里开始(切片的起始位置)及切多大(切片的大小),容量可以理解为装切片的口袋大小,如下图所示。

img

2.切片是对数组的视图,同步更新。

//更新视图
func updateArr(arr []int){
	arr[0]=1000
}

func main() {
	arr3:=[...]int{2,4,6,7,8,5,4,33}
	s:=arr3[2:6]
	fmt.Println(s)
	fmt.Println("arr[:6]= ",arr3[:6])
	fmt.Println("arr[2:]= ",arr3[2:])
	fmt.Println("arr[:]= ",arr3[:])
	s1:=arr3[2:]
	s2:=arr3[:]
	updateArr(s1)
	fmt.Println("更新过的s1:=",s1)
	fmt.Println("更新过的arr:=",arr3)
	updateArr(s2)
	fmt.Println("更新过的s2:=",s2)
	fmt.Println("更新过的arr:=",arr3)

}

image-20191101120825792

3.slice本身没有数据,是对底层array的一个view

4.resilce,不断的切片

5.slice扩展

4.为什么用到range

image-20191101121901340 image-20191101121919104
	arr:=[...]int{1,2,3,4,5,6,7,8}
	fmt.Println("arr:=",arr)
	s1:=arr[2:6]
	s2:=s1[3:5]
	fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n",s1,len(s1),cap(s1))
	fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",s2,len(s2),cap(s2))

image-20191101122907451

6.slice实现

image-20191101122158601

7.slice可以向后扩展,不可以向前扩展

8.s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)

9.向slice添加元素

s4和s5不再是arr的视图了

	s3:= append(s2, 10)
	s4:=append(s3,11)
	s5:=append(s4,12)
	fmt.Println("s3,s4,s5",s3,s4,s5)
	fmt.Println("arr:=",arr)

image-20191101123729156

10.添加元素时超越cap,系统会重新分配更大的底层数组

11.由于值传递,必须接受append的返回值

s=append(s,val)

12.定义slice

var s []int

13.知道长度,初始化slice

func printSilce(arr []int){
	fmt.Printf("s=%v,len=%d,cap=%d\n",arr,len(arr),cap(arr))
}
func main() {
	s1:=[]int{2,4}
	printSilce(s1)

	s2:=make([]int,16)
	s3:=make([]int,16,32)
	
	printSilce(s2)
	printSilce(s3)

}

image-20191101125248148

14.copy slice

copy(s2,s1)

15.slice删除某个元素

	copy(s2,s1)

	printSilce(s2)

	s5:=append(s2[:3],s2[4:]...)
	printSilce(s5)

image-20191101125918617

16.删除头尾

	//删除头尾
	front:=s5[0]
	s5=s5[1:]

	tail:=s5[len(s5)-1]
	s5=s5[:len(s5)-1]

	fmt.Println(front,tail)

	printSilce(s5)

image-20191101130236425

2019年11月01日13:03:18

3.3 map

2019年11月01日13:03:26

1.介绍

Go语言中 map 是一种特殊的数据结构,一种元素对(pair)的无序集合,pair 对应一个 key(索引)和一个 value(值),所以这个结构也称为关联数组或字典,这是一种能够快速寻找值的理想结构,给定 key,就可以迅速找到对应的 value。

2.map建立方式

	m:=map[string] string{
		"name":"jixi",
		"age":"13",
	}

	m3:=make(map[string]int)

	fmt.Println(m,m3)

image-20191101130959805

3.遍历map

	for i,v :=range m{
		fmt.Println(i,v)
	}

4.取得对应key的value

	m2:=m["name"]
	fmt.Println(m2)

5.删除元素

	fmt.Println("_____________删除元素")
	delete(m,"name")
	fmt.Println(m)

6.判断是否存在key

value,ok:=m[key]

7.除了slice,map,func的内建类型都可以作为key

4.面向对象

4.1 结构体和方法

2019年11月01日13:46:19

1.仅支持封装,不支持继承和多态。

2.go没有class,只有struct

3.不论地址还是结构本身,一律采用.来访问成员

type treeNode struct {
	value int
	left,right *treeNode
}

func main() {
	var root treeNode

	root=treeNode{value:3}
	root.left=&treeNode{value:10}
	root.right=&treeNode{5,nil,nil}
	root.right.right=new(treeNode)


	fmt.Println(root)
	
}

4.结构的创建

image-20191101140701689
//工厂函数
func createNode(value int) *treeNode{
	return &treeNode{value:value}
}

5.为结构定义方法

image-20191101142201332
//定义方法
func (node treeNode) print() {
	fmt.Print(node.value)
}

	root.print()

6.方法的不同

不带指针:就是拷贝一份数据,实际上不修改原来的值

带指针:会把地址传过去

//定义方法
func (node treeNode) print() {
	fmt.Println(node.value)
}

//带指针
func (node *treeNode) setValue(value int){
	node.value=value
}

func main() {
	var root treeNode

	root=treeNode{value:3}
	root.left=&treeNode{value:10}
	root.right=&treeNode{5,nil,nil}
	root.right.right=new(treeNode)


	//fmt.Println(root)
	root.print()
	root.setValue(200)
	root.print()
}

image-20191101142035852

7.源码

node.go

package tree

import "fmt"

type Node struct {
	Value       int
	Left, Right *Node
}

func (node Node) Print() {
	fmt.Print(node.Value, " ")
}

func (node *Node) SetValue(value int) {
	if node == nil {
		fmt.Println("Setting Value to nil " +
			"node. Ignored.")
		return
	}
	node.Value = value
}

func CreateNode(value int) *Node {
	return &Node{Value: value}
}

traversal.go

package tree

import "fmt"

func (node *Node) Traverse() {
	node.TraverseFunc(func(n *Node) {
		n.Print()
	})
	fmt.Println()
}

func (node *Node) TraverseFunc(f func(*Node)) {
	if node == nil {
		return
	}

	node.Left.TraverseFunc(f)
	f(node)
	node.Right.TraverseFunc(f)
}

func (node *Node) TraverseWithChannel() chan *Node {
	out := make(chan *Node)
	go func() {
		node.TraverseFunc(func(node *Node) {
			out <- node
		})
		close(out)
	}()
	return out
}

entry.go

package main

import (
	"fmt"

	"imooc.com/ccmouse/learngo/tree"
)

type myTreeNode struct {
	node *tree.Node
}

func (myNode *myTreeNode) postOrder() {
	if myNode == nil || myNode.node == nil {
		return
	}

	left := myTreeNode{myNode.node.Left}
	right := myTreeNode{myNode.node.Right}

	left.postOrder()
	right.postOrder()
	myNode.node.Print()
}

func main() {
	var root tree.Node

	root = tree.Node{Value: 3}
	root.Left = &tree.Node{}
	root.Right = &tree.Node{5, nil, nil}
	root.Right.Left = new(tree.Node)
	root.Left.Right = tree.CreateNode(2)
	root.Right.Left.SetValue(4)

	fmt.Print("In-order traversal: ")
	root.Traverse()

	fmt.Print("My own post-order traversal: ")
	myRoot := myTreeNode{&root}
	myRoot.postOrder()
	fmt.Println()

	nodeCount := 0
	root.TraverseFunc(func(node *tree.Node) {
		nodeCount++
	})
	fmt.Println("Node count:", nodeCount)

	c := root.TraverseWithChannel()
	maxNodeValue := 0
	for node := range c {
		if node.Value > maxNodeValue {
			maxNodeValue = node.Value
		}
	}
	fmt.Println("Max node value:", maxNodeValue)
}

4.2 包和封装

2019年11月01日14:33:25

封装

1.名字一般使用CamelCase

2.首字母大写: public

3.首字母小写:private

package tree

import "fmt"

type Node struct {
	Value       int
	Left, Right *Node
}

//工厂函数
func createNode(value int) *Node {
	return &Node{Value: value}
}

//定义方法
func (node Node) Print() {
	fmt.Println(node.Value)
}

//带指针
func (node *Node) SetValue(value int){
	node.Value =value
}

package main

import "awesomeProject/src/tree"

func main() {
	var root tree.Node

	root=tree.Node{Value: 3}
	root.Left=&tree.Node{Value: 10}
	root.Right=&tree.Node{5,nil,nil}
	root.Right.Right=new(tree.Node)


	//fmt.Println(root)
	root.Print()
	root.SetValue(200)
	root.Print()
}

4.2 包

4.每个目录一个包

5.main包包含可执行入口

6.为结构定义的方法必须放在同一个包内,但是可以是不同文件

7.封装的好处:

  • 隐藏实现细节;
  • 可以对数据进行验证,保证数据安全合理。

如何体现封装:

  • 对结构体中的属性进行封装;
  • 通过方法,包,实现封装。

封装的实现步骤:

  • 将结构体、字段的首字母小写;
  • 给结构体所在的包提供一个工厂模式的函数,首字母大写,类似一个构造函数;
  • 提供一个首字母大写的 Set 方法(类似其它语言的 public),用于对属性判断并赋值;
  • 提供一个首字母大写的 Get 方法(类似其它语言的 public),用于获取属性的值。

8.常用内置包

1) fmt

包 fmt 实现了格式化的 I/O 函数,这与 C 的 printf 和 scanf 类似。格式化短语派生于 C 。一些短语(%-序列)这样使用:

2) io

这个包提供了原始的 I/O 操作界面。它主要的任务是对 os 包这样的原始的 I/O 进行封装,增加一些其他相关,使其具有抽象功能用在公共的接口上。
这个包实现了缓冲的 I/O。它封装于 io.Reader 和 io.Writer 对象,创建了另一个对象(Reader 和 Writer)在提供缓冲的同时实现了一些文本 I/O 的功能。

4) sort

sort 包提供了对数组和用户定义集合的原始的排序功能。
strconv 包提供了将字符串转换成基本数据类型,或者从基本数据类型转换为字符串的功能。

6) os

os 包提供了与平台无关的操作系统功能接口。其设计是 Unix 形式的。
sync 包提供了基本的同步原语,例如互斥锁。

8) flag

flag 包实现了命令行解析。
encoding/json 包实现了编码与解码 RFC 4627 定义的 JSON 对象。

10) html/template

数据驱动的模板,用于生成文本输出,例如 HTML。
将模板关联到某个上进行解析。模板内容指向数据结构的元素(通常结构的字段或者 map 的键)控制解析并且决定某个值会被显示。模板扫描结构以便解析,而“游标” @ 决定了当前位置在结构中的值。

11) net/http

net/http 实现了 HTTP 请求、响应和 URL 的解析,并且提供了可扩展的 HTTP 服务和基本的 HTTP 客户端。
unsafe 包包含了 Go 程序中数据类型上所有不安全的操作。通常无须使用这个。

13) reflect

reflect 包实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类型 interface{} 的值,并且通过 Typeof 解析出其动态类型信息,通常会返回一个有接口类型 Type 的对象。
os/exec 包执行外部命令。

4.3 扩展已有类型

2019年11月01日14:49:06

1.如何扩充系统类型或者别人的类型

  • 定义别名
  • 使用组合

4.4 gopath

2019年11月01日15:12:38

1.

image-20191101151647737

2.查看gopath

apple$ pwd
/Users/apple/go/src/awesomeProject/src

 apple$ echo $GOPATH
/Users/apple/go/src/awesomeProject/src:/Users/apple/go/src/awesomeProject
:/Users/apple/go

 apple$ cat ~/.bash_profile
PATH=$PATH:/usr/local/mysql/bin
export GOPATH="/Users/apple/go"
export PATH=$PATH:$GOPATH/bin
export GOROOT=/usr/local/go

3.

image-20191101153930852 image-20191101155046820 image-20191101155109828

2019年12月16日16:51:53

END

推荐阅读