文章目录
Hello,GO!
package main // 定义包名
func main() {
print("Hello,Go")
}
main函数要点
- ++无参数、无返回值++
- main方法必须要在main包里面
go run main.go
就可以执行- 如果文件不叫main.go,则需要
go build
之后再go run
package声明-包声明
- 语法形式:package xxx
- 字母和下划线的组合
- ++可以和文件夹不同名字++
- 同一个文件夹下的声明一致
- 引入包语法形式:import [alias] xxx
- 如果包引入了但是没有使用,会报错
- 匿名引入:前面多一个下划线
基本类型
String
- 双引号括起来String,"转义
- 反引号括起来大段文字(需要换行的),里面双引号不用转义了
goland会帮忙转义好 - len()计算字节长度
len("你好")//=6
计算长度:utf8.RuneCountInString("你好")//为2
- strings主要方法:查找和替换;大小写转换;子字符串相关;相等
rune类型
rune类型直观理解就是:字符
rune不是byte
rune本质是int32,一个rune四个字节
go当中没有char只有rune
rune不是数字,也不是char,也不是 byte !
(但实际中不太常用)
bool,int,unit.float
- bool: true,false
- int8,int16,int32,int54,++int++
- unit8,unit16,unit32,unit64,++unit++(无符号数)
- float32,float64
byte类型
++byte,字节,本质上是unit8++
对应的操作包在bytes上
类型总结
- golang的数字类型明确标注了长度、有无符号数
- golang不会帮你做类型转换。类型不同无法通过编译。也因此,++string只能和string拼接在一起++
- golang有一个很特殊的rune类型,接近一般语言的char或者character的概念,++非面试情况下,可以理解为“rune=字符”++
- string遇事不决找strings包
变量声明
变量声明var
- var,语法:var name type=value
- 全局变量还是包变量是由首字母大小写决定的
如:
var Global="全局变量" //Global首字母大写,全局可以访问
var local="包变量" //首字母小写,只能在这个包里面使用,子包也不能用
var ( //块声明
First string ="abc"
second int32 = 16
)
golang是强类型语言,类型不一致==判定不等
变量声明 :=
- 只能用于局部变量,即方法内部
- golang使用类型推断来推断类型。数字会被理解为int或者float64。(所以其他类型的数字,就得用var来声明
func main(){
a:=13
println(a)
b:="你好"
println(b)
}
变量声明易错点
- 变量声明了没有使用:与import一样,没有使用的话编译不会通过
- 类型不匹配
- 同作用域下,变量只能声明一次
常量声明const
- 首字母是否大写控制了访问性:大写包
- 驼峰命名
- 支持类型推断
- 无法修改值
const internal = "包内可访问"
const External = "包外可访问"
方法声明
四个部分:
- 关键字func
- 方法名:首字母是否大写决定了作用域
- 参数列表[]
- 返回列表[]
Go支持多返回值
多返回值,参数有名字,但返回值没有:
func Fun1(a string, b int) (int, string){
return 0,"你好"
}
返回值具有名字,可以在内部直接复制,然后返回,也可以忽略age,name直接返回别的:
func Fun2(a string, b int) (age int, name string){
age=19
name="Tom" <
return
}
单返回值
func Fun0(a string) string{
return "Hello, "+name
}
不定参数-切片(后面讲)
_,d :=Fun2(a:"a",b:"b")
下划线_为匿名
- golang支持多返回值,这是一个很大的不同点
- golang方法的作用域和变量作用域一样,通过大小写控制
- golang的返回值是可以有名字的,可以通过给予名字让调用方清楚知道你返回的是什么
最简单的web服务器
package main // 定义包名
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, Ilove %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8088", nil))
}
增加几个路由
func home(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w,"这是主页")
}
func user(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w,"这是用户")
}
func main(){
http.HandleFunc("/",home)
http.HandleFunc("/user",user)
log.Fatal(http.ListenAndServe(":8088", nil))
}
fmt格式化输出
返回字符串的API
str:=fmt.Sprintf("hello,I am %s",name)
直接输出
fmt.Printf("hello,i am %s",name)
占位符:
- %s字符串 %d整型
- %v 值的默认格式。当打印结构体时,“加号”标记(%+v)会添加字段名
- %#v 相应值的Go语法表示
- %T 相应值的类型的Go语法表示
- %% 字母上的百分号,并非值的占位符
package main
import (
"fmt"
)
type Sample struct {
a int
str string
}
func main() {
s := Sample{a: 1, str: "hello"}
fmt.Printf("%v\n", s) //{1, hello}
fmt.Printf("%+v\n", s) //{a:1, str:hello}
fmt.Printf("%#v\n", s) //main.Sample{a:1, str:"hello"}
fmt.Printf("%T\n", s) // main.Sample
fmt.Printf("%%\n", s.a) // % %!(EXTRA int=1)
}
数组和切片
数组和别的语言的数组差不多,语法是:[cap]type
- 初始化要指定长度(容量)
- 直接初始化
- arr[i]的形式访问元素
- len和cap操作用于获取数组长度
package main // 定义包名
import "fmt"
func main() {
//直接初始化一个三个元素的数组。大括号里面多一个或者少一个都编译不通过
a1 := [3]int{9, 8, 7}
fmt.Printf("a1: %v, len: %d, cap: %d\n", a1, len(a1), cap(a1))
//初始化一个三个元素的数组,所有元素都是0
var a2 [3]int
fmt.Printf("a2: %v, len: %d,cap: %d\n", a2, len(a2), cap(a2))
//a1=append(a1,a2) 数组不支持append操作
//按下标索引
fmt.Printf("a1[1]:%d", a1[1])
//超出下标范围,直接崩溃,编译不通过
//fmt.Printf("a1[99]: %d",a1[99])
}
结果如下:
a1: [9 8 7], len: 3, cap: 3
a2: [0 0 0], len: 3,cap: 3
a1[1]:8
切片,语法:[]type
- 直接初始化
- make初始化:make([]type, length, capacity)
- arr[i]的形式访问元素
- append追加元素
- len获取元素数量
- cap获取切片容量
- 推荐写法:s1 :=make([]type,0,capacity)
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4} //直接初始化了4个元素的切片
fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1))
s2 := make([]int, 3, 4) //创建了一个包含三个元素,容量为4的切片
fmt.Printf("s2: %v, len: %d, cap: %d\n", s2, len(s2), cap(s2))
s2 = append(s2, 7) //后边添加一个元素,没有超出容量限制,不会发生扩容
fmt.Printf("s2: %v, len: %d, cap: %d\n", s2, len(s2), cap(s2))
s2 = append(s2, 8) //后边添加一个元素,触发扩容
fmt.Printf("s2: %v, len: %d, cap: %d\n", s2, len(s2), cap(s2))
s3 := make([]int, 4) //只传入一个参数,表示创建一个含有四个元素,容量也为四个元素的
fmt.Printf("s3: %v, len: %d, cap: %d\n", s3, len(s3), cap(s3))
//按下标索引
fmt.Printf("s3[2]: %d", s3[2])
//超出下标范围,直接崩溃
//runtime error: index out of range[99] with length 4
//fmt.Printf("s2[99]:%d",s3[99])
}
结果如下:
s1: [1 2 3 4], len: 4, cap: 4
s2: [0 0 0], len: 3, cap: 4
s2: [0 0 0 7], len: 4, cap: 4
s2: [0 0 0 7 8], len: 5, cap: 8
s3: [0 0 0 0], len: 4, cap: 4
s3[2]: 0
数组 | 切片 | |
---|---|---|
直接初始化 | 支持 | 支持 |
make | 不支持 | 支持 |
访问元素 | arr[i] | arr[i] |
len | 长度 | 已有元素个数 |
cap | 长度 | 容量 |
append | 不支持 | 支持 |
是否可以扩容 | 不可以 | 可以 |
用切片基本不会出错
子切片
数组和切片都可以通过[start:end]的形式来获取子切片:
- arr[start:end],获得[start,end)之间的元素
- arr[:end],获得[0,end)之间的元素
- arr[start:],获得[start,len(arr)]之间的元素
左闭右开原则
如何理解切片
最直观的对比:ArrayList,即基于数组的List的实现,切片的底层也是数组
跟ArrayList的区别:
- 切片操作是有限的,不支持随机增删(即没有add,delete方法,需要自己写代码)
- 只有append操作
- 切片支持子切片操作,和原本切片是共享底层数组
共享底层(optional)
核心:共享数组
子切片和切片究竟会不会互相影响,就抓住一点:它们是不是还共享数组?
就是如果它们结构没有变化,那肯定是共享的;但是结构变化了,就可能不是共享了
package main
import "fmt"
func ShareSlice() {
s1 := []int{1, 2, 3, 4}
s2 := s1[2:]
fmt.Printf("s1: %v,len: %d,cap: %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2: %v,len: %d,cap: %d\n", s2, len(s2), cap(s2))
s2[0] = 99
fmt.Printf("s1: %v,len: %d,cap: %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2: %v,len: %d,cap: %d\n", s2, len(s2), cap(s2))
s2 = append(s2, 199)
fmt.Printf("s1: %v,len: %d,cap: %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2: %v,len: %d,cap: %d\n", s2, len(s2), cap(s2))
s2[1] = 1999
fmt.Printf("s1: %v,len: %d,cap: %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2: %v,len: %d,cap: %d\n", s2, len(s2), cap(s2))
}
func main() {
ShareSlice()
}
结果如下:
s1: [1 2 3 4],len: 4,cap: 4
s2: [3 4],len: 2,cap: 2
s1: [1 2 99 4],len: 4,cap: 4
s2: [99 4],len: 2,cap: 2
s1: [1 2 99 4],len: 4,cap: 4
s2: [99 4 199],len: 3,cap: 4
s1: [1 2 99 4],len: 4,cap: 4
s2: [99 1999 199],len: 3,cap: 4
for
for和别的语法差不多,有三种形式:
- for{},类似while的无限循环
func ForLoop() {
arr := []int{9, 8, 7, 6}
index := 0
for {
if index == 3 {
break
}
fmt.Printf("%d=>%d,", index, arr[index])
index++
}
fmt.Printf("for loop end\n")
}
- fori,一般的按照下标循环
func ForI() {
arr := []int{9, 8, 7, 6}
for i := 0; i < len(arr); i++ {
fmt.Printf("%d=>%d,", i, arr[i])
}
fmt.Printf("for i loop end\n")
}
- for range最为特殊的range遍历
func ForR() {
arr := []int{9, 8, 7, 6}
//如果只是需要value,可以用_代替index
//如果只需要index也可以去掉写成for index:=range arr
for index, value := range arr {
fmt.Printf("%d=>%d,", index, value)
}
fmt.Printf("for r loop end")
}
- break和continue和别的语言一样
if-else
if-else和别的语言差不多
if condition{…}
else if condition{…}
else{…}
带局部变量声明的if-else:
func IfUsingNewVariable(start int, end int) {
if distance := end - start; distance > 100 {
fmt.Printf("距离太远:%d\n", distance)
} else {
// else分支也可以没有
fmt.Printf("距离不远:%d\n",distance)
}
//这里不能访问distance
}
- distance只能在if块,或者后面所有的else块里面使用
- 脱离了if-else块,则不能再使用
switch
switch和别的语言差不多
switch后面可以是基础类型的字符串,或者满足特定条件的结构体
最大差别:不用加break了!
func ChooseFruit(fruit string) {
switch fruit {
case "苹果":
fmt.Println("这是一个苹果")
case "草莓", "蓝莓":
fmt.Println("这是莓果")
default:
fmt.Printf("新水果:%s\n", fruit)
}
}