标识符:html
是用户或系统定义的有意义单词组合,或单词与数字组合(具体意义有定义者决定) python
标识符以字母下划线开头,大小写敏感,好比:boy, Boy, _boy, _(匿名变量,用来忽略结果)程序员
标识符命名规范:在习惯上,Go语言程序员推荐使用驼峰式命名,当名字有几个单词组成的时优先使用大小写分隔,而不是优先用下划线分隔。所以,在标准库有QuoteRuneToASCII和parseRequestLine这样的函数命名,可是通常不会用quote_rune_to_ASCII和parse_request_line这样的命名。而像ASCII和HTML这样的缩略词则避免使用大小写混合的写法,它们可能被称为htmlEscape、HTMLEscape或escapeHTML,但不会是escapeHtml。数据库
关键字:编程
是 Go 语言提供的有特殊含义的符号,也叫作“保留字”数组
系统保留关键字:安全
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthough | if | range | type |
continue | for | import | return | var |
常量数据结构
常量使用 const 修饰,表示是只读的,不能修改多线程
const 只能修饰 boolean,number(int相关,浮点数,complex)和 string 类型闭包
语法:const identifier [type] = value(type 可省略)
优雅写法:
const( name string = "skier" age int = 10 salary int = 5000 / 2 // gender boolean = getGender() // const不能从函数中获取 )
常量由于在编译期肯定,因此能够用于数组声明:
const size = 4 var arrayA [size]int
变量
声明一个变量:var identifier [type]
// 声明变量并赋值 var a int = 100 // 简写(自动推导类型) a := 100
优雅写法:
var ( name string = "johny" age int = 10 )
默认值:
做用域:从定义变量的代码行开始,一直到直接所属的大括号结束为止(全局变量除外)
在编程中,变量在其实现了变量的功能后,做用范围越小,所形成的问题可能性越小,每个变量表明一个状态,有状态的地方,状态就会被修改,函数的局部变量只会影响一个函数的执行,但全局变量可能会影响全部代码的执行状态,所以限制变量的做用范围对代码的稳定性有很大的帮助
数字类型:int8, int16, int32, int64, uint8, uint16, uint32, uint64
bool 类型:ture, false (bool 型没法参与数值运算,也没法与其余类型进行转换。)
浮点类型:float32, float64
字符类型:byte
字符串类型:字符串实现基于 UTF-8 编码
"" 双引号,定义单行字符串
`` 反引号,定义多行字符串(在这种方式下,反引号间换行将被做为字符串中的换行,可是全部的转义字符均无效,文本将会原样输出)
多行字符串通常用于内嵌源码和内嵌数据等
类型转换
格式:type(variable)
var a int = 8 var b int32 = int32(a)
浮点数转换成 int 类型,精度会丢失
var c float32 = math.Pi fmt.Println(int(c))
int32 转换成 int16,数据会截断
// 初始化一个32位整型值 var a int32 = 1047483647 // 输出变量的十六进制形式和十进制值 fmt.Printf("int32: 0x%x %d\n", a, a) // 将a变量数值转换为十六进制, 发生数值截断 b := int16(a) // 输出变量的十六进制形式和十进制值 fmt.Printf("int16: 0x%x %d\n", b, b) 结果是: int32: 0x3e6f54ff 1047483647 int16: 0x54ff 21759
字符串转义符
转移符 | 含 义 |
---|---|
\r | 回车符(返回行首) |
\n | 换行符(直接跳到下一行的同列位置) |
\t | 制表符 |
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
操做符
数字操做符:+, -, *, /, %
比较运算符:>, >=, <, <=, ==, !=
字符串操做
拼接:
var str1 string = "hello" var str2 string = "world" var str string = str1 + str2 // var str string = fmt.Sprintf("%s%s", str1, str2)
值类型
本质上是原始类型,变量直接储存值,内存一般在栈中分配,包括 int, float, bool, string 以及数组和 struct(结构体)
对值类型进行操做,通常都会返回一个新建立的值,因此把这些值传递给函数时,其实传递的是一个值的副本
func main() { name:="张三" fmt.Println(modify(name)) fmt.Println(name) } func modify(s string) string{ s=s+s return s } //Output 张三张三 张三
以上是一个操做字符串的例子,经过打印的结果,能够看到,原本 name 的值并无改变,也就是说,咱们传递的是一个副本,而且返回一个新建立的字符串
基本类型由于是值的拷贝,而且在对他进行操做的时候,生成的也是新建立的值,因此这些类型在多线程里是安全的,咱们不用担忧一个线程的修改影响了另一个线程的数据
引用类型
引用类型与值类型偏偏相反,它的修改能够影响到任何引用到它的变量;变量存储的是地址,这个地址存储最终的值,一般在堆内存上分配,经过 GC 回收,包括 指针,select,map,chan 等
引用类型之因此能够引用,是由于咱们建立引用类型的变量,实际上是一个标头值,标头值里包含一个指针,指向底层的数据结构,当咱们在函数中传递引用类型时,其实传递的是这个标头值的副本,它所指向的底层结构并无被复制传递,这也是引用类型传递高效的缘由。
if else 分支判断
if condition1 { block1 } else if condition2 { block2 } else { block3 }
switch case 语句
func main(){ var variabel string = "a" switch variabel { case "a", "b": fmt.Println(variabel) // fallthrough // 会执行下一个case的语句块 case "c": fmt.Println(variabel) default: fmt.Println("default output") } }
case 后边的值能够写多个,是 或 的关系
case 语句块末尾若是加上 fallthrough,会接着执行下一个 case 的语句块
for 循环
for i:=0; i<100 ; i++ { fmt.Println("hello, world~") }
死循环(for)
func main(){ for { fmt.Println("hello, world~") time.Sleep(time.Second) } }
加判断的 for 循环
func main(){ var i int for i<100 { fmt.Println(i) i += 1 } }
for range 语句
可使用 for range 遍历数组、切片、字符串、map 及通道(channel)。经过 for range 遍历的返回值有必定的规律:
遍历数组:
var arrayA = [3]string{"hammer", "soldier", "mum"} for index, value := range arrayA { fmt.Println(index, value) } 运行结果: 0 hammer 1 soldier 2 mum
用匿名标识符忽略 index
var arrayA = [3]string{"hammer", "soldier", "mum"} for _, value := range arrayA { fmt.Println(value) } 运行结果: hammer soldier mum
固然,for 循环中也可以支持:break, continue
goto 语句
能够经过标签进行代码间的无条件跳转,goto语句能够快速跳出循环 或 实现一样的逻辑 有必定的帮助:
快速跳出循环:
func main(){ for i:=0; i<=100; i++{ for j:=0; j<=100; j++{ if j == 10 { // 直接跳转到标签 goto breakHere } } } breakHere: fmt.Println("hello world~") } 运行结果: hello world~
使用 goto 集中处理错误:
err := firstCheckError() if err != nil { goto onExit } err = secondCheckError() if err != nil { goto onExit } fmt.Println("done") return onExit: fmt.Println(err) exitProcess()
Go 语言支持普通函数、匿名函数和闭包
普通函数声明:func 函数名(参数列表) (返回值列表) {函数体}
不支持重载,一个源文件内不能有两个相同名称的函数
函数是一等公民,也是一种类型,能够赋值给变量
函数的传参方式:
注意:不管是值传递仍是引用传递,传递给函数的都是变量的副本,不过,值传递是对值的拷贝,引用传递是地址的拷贝,通常来讲,地址拷贝更为高效,而值拷贝取决于拷贝对象的大小,对象越大,则性能越低
返回值命名:
返回值不须要定义,直接使用(命名的返回值变量的默认值为类型的默认值,即数值为 0,字符串为 "",布尔为 false、指针为 nil)
func calc(a int, b int) (c int) { c = a + b return c }
可变长参数:
可变参数变量是一个包含全部参数的切片
func calc(a int, b int, arg... int) { fmt.Println(arg[0]) }
defer 的用途:
关闭文件句柄
注意:不能将这一句代码放在第3行空行处,一旦文件打开错误,f将为空,在延迟语句触发时,将触发宕机错误
func read(){ file err := open(filename)
if err != nil{ return } defer file.Close() }
锁资源的释放
func lock(){ mc.Lock() defer mc.Unlock() }
数据库链接的释放
func connect(){ conn := openDatabase() defer conn.Close() }
调用函数
函数在定义后,能够经过调用的方式,让当前代码跳转到被调用的函数中进行执行。调用前的函数局部变量都会被保存起来不会丢失;被调用的函数结束后,恢复到被调用函数的下一行继续执行代码,以前的局部变量也能继续访问
一个函数在内部调用本身,就叫作递归,下面来举两个递归函数的Demo
递归求阶乘
func calc(n int) int { if n <= 1{ return 1 } return calc(n-1) * n } func main(){ result := calc(5) fmt.Println(result) } 运行结果: 120
斐波拉契数
func fab(n int) int { if n<=1{ return 1 } return fab(n-1) + fab(n-2) } func main(){ var n int = 6 var fabCount int for i:=0; i<=n; i++{ fabCount += fab(i) } fmt.Println(fabCount) } 运行结果: 33
匿名函数没有函数名,只有函数体,能够直接被当作一种类型赋值给函数类型的变量,匿名函数也每每以变量的方式被传递
匿名函数常常被用于实现回调函数、闭包
定义一个匿名函数:
func(str string){ fmt.Println("hello", str) }("world") 运行结果: hello world
也能够将匿名函数赋值给变量:
f := func(str string){ fmt.Println("hello", str) } f("world")
匿名函数当作参数:
func visit(sliceA []int, f func(int)){ for _, value := range sliceA { f(value) } } func main(){ var sliceA []int = []int{1,2,3,4,5} f := func(a int){ fmt.Print(a) } visit(sliceA, f) } 运行结果: 12345
使用匿名函数实现操做封装:
func main(){ var mapA map[string]func() mapA = map[string]func(){ "fire": func(){ fmt.Println("chicken fire") }, "run": func(){ fmt.Println("soldier run") }, "fly": func(){ fmt.Println("angel fly") }, } // fmt.Println(mapA) // 接收命令行参数,key,默认值,帮助 var skill *string = flag.String("skill", "", "skill type") flag.Parse() f, err := mapA[*skill] if err == true { f() } else { fmt.Println("skill not fount") } } 运行效果: $ go run main.go --skill=fly angel fly $ go run main.go --skill=run soldier run
闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,即便已经离开了自由变量的环境 也不会被释放或者删除,在闭包中能够继续使用这个自由变量(闭包(Closure)在某些编程语言中也被称为 Lambda 表达式)
简单的说:
闭包 = 函数 + 引用环境
同一个函数与不一样的引用环境组合,能够造成不一样的实例,如图:
实现一个简单的闭包:
func closureFunc(str string) func(){ wapper := func(){ fmt.Println("hello", str) } return wapper } func main(){ f := closureFunc("world~") // 调用闭包函数 f() } 运行结果: hello world~
累加器的实现(闭包的记忆效应)
func accumulate(num int) func() int { wapper := func() int { num += 1 return num } return wapper } func main(){ accumulator := accumulate(10) ret1 := accumulator() fmt.Println(ret1) ret2 := accumulator() fmt.Println(ret2) } 运行结果: 11 12
// 任何一个代码源文件隶属于一个包 package main //import 关键字,同一个包内的函数能够直接调用,不一样包中的函数经过 包名.函数名 的方式调用 import ( "fmt" "/go_dev/test" ) // 初始化函数 func init(){ fmt.Println("执行初始化操做") } // main 程序入口 func main(){ fmt.Println("hello, world~") }
init 函数
每一个源文件均可以包含一个 init 函数,会自动被编译器执行
包访问控制规则
程序执行顺序(栈) *
练习1:写一个函数,对于一个整数n,求出全部两两相加等于n的组合,好比 n=5
package main import ( "fmt" ) func calc(n int){ for i:=0; i<=n; i++ { num := n - i fmt.Printf("%d+%d=%d\n", i, num, n) } } func main(){ calc(5) } 结果: 0+5=5 1+4=5 2+3=5 3+2=5 4+1=5 5+0=5
练习2:一个程序包含 add 和 main 两个包,add 包中有两个变量 Name 和 age,在 main 中访问 Name 和 age 变量,打印输出
首先在 go_dev 下新建一个目录 example(go_dev 目录在环境变量 GOPATH 下 src 目录,go编译器根据系统路径会找到 go_dev 目录下的包)
add包下写 add.go
package add var Name string = "skier" var age int = 19
main包下写 main.go
package main import ( "go_dev/example/add" // a "go_dev/example/add" // 包别名的应用 "fmt" ) func main(){ fmt.Println(add.Name) fmt.Println(add.age) // 不能访问 }
1.判断101~200之间有多少个素数,并输出打印
package main import ( "fmt" ) func main(){ for i:=101; i<=200; i++{ var flag bool = true for j:=2; j<i; j++{ if (i % j == 0) { flag = false } } if (flag == true){ fmt.Println("素数i:", i) } } }
2.打印出101~999中全部的水仙花数,所谓水仙花数是指一个三位数,其各位数字的立方和等于该数自己,例如153是一个水仙花数,1*1*1 + 5*5*5 + 3*3*3 = 153
package main import ( "fmt" ) func narcissisticNum(num int) bool { var g int = num % 100 % 10 var s int = num % 100 / 10 var b int = num / 100 //fmt.Println(g, s, b) var cube int = g*g*g + s*s*s + b*b*b return num == cube } func main(){ for i:=100; i<=999; i++{ result := narcissisticNum(i) if result == true { fmt.Println("水仙花数是:", i) } } }
3.对于一个数n,求n的阶乘之和,即:1! + 2! + ... + n!
package main import ( "fmt" ) func summation(n int) int { var result int for i:=1; i<=n; i++ { var tmpe int = 1 for j:=1; j<=i; j++{ tmpe *= j } result += tmpe } return result } func main(){ var num int = 5 var result int = summation(num) fmt.Println("sum result:", result) }
4.在终端打印 9*9 乘法表
package main import( "fmt" ) func multiplication(){ for column:=1; column<=9; column++{ for row:=1; row<=column; row++{ fmt.Printf("%d*%d=%d\t", column, row, column*row) } fmt.Println() } } func main(){ multiplication() }
5.一个数若是刚好等于它的因子之和,这个数就称之为“完数”,例如:1+2+3=6,在终端输出1000之内的全部完数
package main import( "fmt" ) func showPerfect(n int){ for i:=1; i<n; i++{ var sum int for j:=1; j<i; j++{ if i % j == 0{ sum += j } } if sum == i{ fmt.Printf("完数:%d \n", i) } } } func main(){ const num int = 1000 showPerfect(num) }
6.输入一个字符串,判断其是否为“回文”,回文字符串是指从左到右读,与从右到左读是彻底相同的字符串
package main import ( "fmt" ) func isReverse(str string) bool { // 转为字符 char := []rune(str) var length int = len(char) for i:=0; i<=length / 2 - 1; i++{ if char[i] != char[length-1-i]{ return false } } return true } func main(){ var input string fmt.Scanf("%s", &input) result := isReverse(input) fmt.Println("result:", result) }
7.输出一行字符串,分别统计其中英文字母,空格,数字和其它符号的个数
package main import ( "fmt" "bufio" "os" ) func count(str string) (wordCount, spaceCount, numCount, otherCount int) { chars := []rune(str) for _, value := range chars{ switch { case value >= 'a' && value <= 'z': wordCount += 1 case value >= 'A' && value <= 'Z': wordCount += 1 case value == ' ': spaceCount += 1 case value >= '0' && value <= '9': numCount += 1 default: otherCount += 1 } } return } func main(){ reader := bufio.NewReader(os.Stdin) // 读取一行的内容 result, _, error := reader.ReadLine() if error == nil{ wordCount, spaceCount, numCount, otherCount := count(string(result)) fmt.Printf("wordCount:%d\nspaceCount:%d\nnumCount:%d\notherCount:%d\n", wordCount, spaceCount, numCount, otherCount) } }
8.计算两个大数相加的和,这两个大数会超过 int64 的表示范围