Skip to content

czgczg/golearn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

1.1 起源与发展

go是一种高性能的web开发分布式友好的语言 用更少的代码,更短的编译时间,创建运行更快的程序,享受更多的乐趣。起源于2007年,由google公司开发。

1.2 语言的主要特性

正如“21世纪的c语言”,go和C++和java、c#一样属于C系,设计者们汲取了其他编程预言的精粹部分融合到go语言中。
go语言完全开源,使用BSD授权许可,所以任何人都可以进行商业软件的开发而不需要支付费用。 尽管为了能够让目前主流的开发者们能够对 Go 语言中的类 C 语言的语法感到非常亲切而易于转型,但是它在极大程度上简化了这些语法,使得它们比 C/C++ 的语法更加简洁和干净。同时,Go 语言也拥有一些动态语言的特性,这使得使用 Python 和 Ruby 的开发者们在使用 Go 语言的时候感觉非常容易上手

1.3实战记录

先搭建环境 安装go,idea作为工具安装go language插件,然后新建工程,工程要自己建文件夹 pkg src bin,代码放src目录。注意点:要添加gopath,指定为创建项目的工程目录或者模块目录。

变量声明和变量类型

变量声明var关键字 :var identifier type go语言和其他编程语言不同,将变量类型放在变量后。因为C语言那样申明含糊不清 int* a,b;只有a是指针而b不是,go中 var a,b *int则将他们都申明为指针类型; go可以直接拿到变量指针地址 &a

举个🌰:

var a int   var b bool var str string

或者一起申明:(这种写法一般用于申明全局变量)

var (
      a int
      b bool
      str string
  ) 

当一个变量被申明后,系统自动赋予该值的零值,指针为nil,记住所有内存在go中都是经过初始化的.

变量的命名遵循驼峰命名,每个新单词的首字母大写

:=简写变量声明

count := 18

reflect包typeOf来判断变量所属的类型

reflect.TypeOf(count)

go的编译器可以根据变量的值自动推断其类型,有点像ruby和python这类动态语言,只不过他们是在运行时进行推断,Go则是在编译时就完成了推断。所以可以用如下方式来申明以及初始化变量:

var a=15
var b=false
var str="Go say Hello"

或者:

var (
	a=15
	b=false
	str="go say hello"
)

但是变量的自动推断类型并不是任何时候都是适用的,当你想给变量的类型并不是自动推断出的某中类型时,你需要显示指定变量类型,例如:

var n int64 =2

🔥 但是var a这种声明是错误的,因为编译器没有任何可以用于自动推断类型的依据。变量的类型也可以运行时实现推断,举个🌰:

var (
	HOME = os.Getenv("HOME")
	USER = os.Getenv("USER")
	GOROOT = os.Getenv("GOROOT")
)

这种写法主要是申明包级别的全局变量,当你在函数体内申明局部变量时,应使用剪短申明语法 := ,例如:

a := 1//要有空格

os包可以获取系统信息,类似java的System 利用Printf的格式化输出功能

指针内存:内存地址和os系统位数有关,64位的,8个字节表示一个字,用首字节的内存地址(十六进制表示)标志。所有int,float,bool,string这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中值。另外,像数组和结构这些复合类型都是值类型。 和c一样可以&来获取内存地址,值类型的变量的值存储在栈中。更复杂的数据通常会需要使用多个字长,这些数据一般使用引用类型保存。在go语言中,指针属于引用类型,其他的引用类型包括slices,maps,channel。被引用的变量会存储在堆中,便于垃圾回收。

简单语法糖: 变量可以在一行声明,(这也是将类型写在标识符后面重要原因)如:

var a,b,c int

多变量可以在同一行赋值,并行同时赋值 如:

a,b,c := 5,6,"abc"

如果想交换两个变量,则可以简单的是引用如:

a,b=b,a//省去了交换函数必要

行末尾不要强制增加分号,避免每次写完后还有跳到括号末尾加个分号,编码更加高效。q

init函数:变量除了可以在全局申明中初始化,也可以在init函数中初始化。这是一类非常特殊的函数,不能够被人调用,而是在每个包完成初始化后自动执行,并且执行优先级比main高。初始化是以单线程执行,并且安装包的依赖关系顺序执行。一个可能的用途就是在开始执行程序之前对数据进行校验或者修复,以保证程序的正确性。也经常被用在当一个程序开始之前调用后台执行的携程。

Go语言不允许不同类型之间的混合使用,到那时对于常量的类型限制很少,因此常量可以混合使用,变量之间不能混合用,举个例子:

package mainq

func main() {q
	var a intqq
	var b int32
	a = 15
	b = a + a	 // 编译错误q
	b = b + 5    // 因为 5 是常量,所以可以通过编译
}/README.md:2

字符串处理

strings包字符操作和strconv包实现类型转换

日期处理

time包 go的格式化处理有点不一样

format := now.Format("2006-01-02 15:04:05") //这些数字的含义可以在format.go文件中看到

指针

不像java和.net,go语言提供了用户控制数据结构的指针的能力;到那时你不能进行指针的运算。通过给予程序员基本内存布局,Go语言允许你控制指定集合的数据结构、分配的数量以及内存的访问方式,这些对于构建运行良好的系统是非常有必要的:指针对性能的影响是不言而喻的,而如果你要做的是系统编程或者是操作系统或者网络应用吗,指针更是不可或缺的部分。

不能得到const常量或者字面值常量的地址 举个🌰:

const i=5
fmt.Println(&10)
fmt.Println(&i)

指针类型用 *int 表示。如果我们想调用指针intP,我们可以这样申明:

var intP * int //最好有space 因为在复杂的表达式中*会被误认为是乘法表达式
intP=&i  //一个指针变量通常缩写ptr
* intP   //反指针  称为反引用

函数

函数是基本的代码块。go是编译型语言,函数编写的顺序无关紧要的;鉴于可读性的需求,最好把main()函数写在文件的前面,其他函数安装一定的逻辑顺序编写(例如函数被调用的顺序)。

go语言中有三种类型的函数:

  • 普通的带名字的函数
  • 匿名函数或者lambda函数
  • 方法

编写多个函数的主要目的是分解和重用,好的程序,非常注意dry原则(don't repeat yourself)。 return语句可以结束for循环或者一个协程。

❗ Go语言不支持函数重载,因为函数重载需要进行多余的类型匹配影响性能;没有重载意味着只有简单的函数调用。你只需要给不同的函数使用不同的名字。

函数在外部声明,只需要给出函数名和函数签名,不需要函数体;函数也可以以申明的方式被使用,作为一个函数类型。

函数是一等值,可以赋值给变量 add := binOp .

go语言中没有泛型的概念,不支持那种支持多种类型的函数。不过大部分情况下可以通过接口和反射实现。但是使用这些技术将会使代码更为复杂,性能更为低下哎,所以在非常注意性能的场合,最好是为每个类型单独创建一个函数,而且代码可读性更强。

😄 函数参数和返回值 :go语言可以返回一组返回值视为一大特性,可以为我们判断函数是否正常执行提供了方便。

//非命名返回值 就是函数申明的出参没有名字
func getX2AndX3(input int) (int, int) {
    return 2 * input, 3 * input
}

🔥 :入参在前 出参在后 都在函数名后面 这么做对于习惯了java来说有点反人类u emm..

defer和追踪: 关键字defer允许我们推迟到函数返回之前(或者任意位置执行return之后)一刻才执行某个语句或者函数,因为return可能表达式包含一些操作而不是单纯的返回某个值。类似java的finally语句块,一般用于释放资源。

举个🌰:(合理使用defer会使代码更加简洁)

//open a file
defer file.close()
//解锁一个加锁的资源
mu.lock()
defer mu.Unlock()
//打印最终报告
printHeader()
defer printFooter()
//关闭数据库连接
defer disconnectFromDB()

使用defer语句实现代码追踪:一个基础但十分实用的实现代码执行追踪的方案就是在进入和离开某个函数打印相关的消息,即可以提炼为下面两个函数:

func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }

内置函数

go语言拥有一些不需要进行导入就可以使用的内置函数,例如: len,cap,append,或必须用于系统级的操作,例如panic,他们需要直接获取编译器的支持。

将函数作为参数进行回调

闭包

我们不希望给函数起名字的时候,可以使用匿名函数;这样的函数当然不能独立存在,编译器会报错,但是可以赋值给变量,即保存函数的地址到变量中,fplus := func(x, y int) int { return x + y } 然后通过变量名直接对函数进行调用。

当然也可以直接对匿名函数进行调用,和ES6相同语法 func(x, y int) int { return x + y } (3, 4)

匿名函数同样被称为闭包(函数式语言的术语):它允许调用定义在其他环境的变量。闭包使得某个函数捕捉到一些外部状态,例如:函数创建是的状态。另一种表示方式为:一个闭包继承了函数所声明的作用域。这种状态(作用域内的变量)都被共享到闭包的环境中,因此这些变量可以在闭包中被操作,直到销毁,闭包通常被用作包装函数;

切片(Go 语言切片是对数组的抽象)

go语言数组长度不可变,在特定的场景就不太适用,所以go提供了切片的动态数组。切片slice是对数组的一个连续片段的引用(该数组我们称为相关数组,通常是匿名的),所以一个切片是一个引用类型(类似C++中的数组类型)。这个片段可以是整个数组,也可以是由起始和终止索引标志的一些项的子集。

切片初始化的格式: slice := make([]type ,start_length,capacity) 其中start_length作为切片初始化长度,capacity作为相关数组长度。

这么做好处是切片在达到容量上限可以扩容。改变切片长度的过程称为切片重组 reslicing slice1=slice[0:end] 其中end是新的末尾索引(即长度)

切片的复制和追加

map

map的创建用make 默认是无序的 不管按照key/value都不排序,要排序,需要将key拷贝的一个slice,再对slice排序,然后用切片的for-range方法打印所有的key和value

锁和sync包

在复杂的程序中,通过不同的线程执行不同应用来实现程序的并发。当不同线程同事修改

go协程

go被描述为一种适用于并发的语言。主要是因为go在两种强大的机制上提供了简单语法支持:go协程和通道 go协程类似一个线程,但是go协程是由go自己调度,而不是os,在协程中的代码可以和其他代码并发执行,go协程很容易创建且开销小,最终多个go协程讲会在同一个底层的系统线程上运行,一个go协程的开销和系统线程开销相比相对很低(一般几kb),而在现代的硬件上,有可能拥有成千上万的go协程。另外,这里还隐藏了映射和调度的复杂性。我们只需要申明这段代码需要并发执行,然后让go自己去处理。
😄 相当于java来说倒是有点好用 java还要自己去创建线程,去调度。

标准库

像fmt和os这样具有常用功能的内置包在Go语言中有150个以上,他们称为标准库,

Go web

通过go搭建一个web服务,一个简单应用的net/http包就可以处理。

About

Supervise myself to learn a new language

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published