按位清零
看第二个操作数:
• 1: 清零
• 0: 保留
1 &^ 0 -> 1
1 &^ 1 -> 0
0 &^ 1 -> 0
0 &^ 0 -> 0
//类型别名的定义 = ,类型定义没有=
type MyInt2 = int
var i1 MyInt2 = 1
fmt.Printf("%T",i1)
0. 深入理解Go的slice及其扩容规则
ints := []int{1, 2} // 扩容前 oldCap = 2
fmt.Println(len(ints),cap(ints)) //2 2
ints = append(ints, 3,4,5,6,7) //
fmt.Println(len(ints),cap(ints)) //7 8
strs := []string{"a", "b"}
fmt.Println(len(strs), cap(strs)) //2 2
strs = append(strs, "c", "d", "e", "1", "2")
fmt.Println(len(strs), cap(strs)) //7 7
- 预估扩容后容量
- 匹配到合适的内存规格
所需内存 = 预估容量 * 元素类型大小
span等级 元素大小 span大小 对象个数
1 8 8192 1024
2 16 8192 512
3 32 8192 256
4 48 8192 170
5 64 8192 128
...
65 28672 57344 2
66 32768 32768 1
申请内存时,内存管理模块会帮我们选足够大且最接近的内存规格,所以上面3个int类型需要24个字节的内存,那么实际分配的是第3个span等级,32个字节大小的内存span。
32个字节的内存span 能存储 大小为8个字节的int类型 共四个。所以真实的扩容容量为 4 而不是预估容量3。
一、Go语言中的数据类型转换
在go语言中,不同类型的变量之间赋值需要显示转换。
语法:T t=T(e)
var i int=1
//将i转换为float类型
var j float32=float32(i)
基本数据类型转string
方法1:fmt.Sprintf(“%参数”,表达式)
func Sprintf(forat string,a …interface{}) string
Sprintf根据format参数生成格式化的字符串并返回该字符串
var num int =1
var str string="ok"
//int类型转string
str=fmt.Sprintf("%d",num)
fmt.Printf("n=%v,v=%v",num,str)
var e byte='a'
var str string=""
//byte类型转string
//%c 相应Unicode码点所表示的字符
str=fmt.Sprintf("%c",e)
fmt.Printf("n=%v,v=%v",e,str)
var b bool=true
var str string=""
//bool类型转string
//%t true 或 false。
//%q 双引号围绕的字符串,由Go语法安全地转义
str=fmt.Sprintf("%t",b)
fmt.Printf("n=%v,v=%q",b,str)
var f float32=20.39
var str string=""
//float类型转string
//%t 有小数而无指数
//%q 双引号围绕的字符串,由Go语法安全地转义
str=fmt.Sprintf("%f",f)
fmt.Printf("n=%v,v=%q",f,str)
### 方法2:使用strconv包的函数
var num int64=20
var str string=""
//int类型转string
//base后面跟进制
str=strconv.FormatInt(num,10)
fmt.Printf("n=%v,v=%q",num,str)
var num float64=20.55
var str string=""
//float类型转string
//'f'是格式 10表示小数保留十位 64表示这个小数是float64
str=strconv.FormatFloat(num,'f',10,64)
fmt.Printf("n=%v,v=%q",num,str)
var bl bool=true
var str string=""
//bool类型转string
str=strconv.FormatBool(bl)
fmt.Printf("n=%v,v=%q",bl,str)
string类型转基本数据类型
var num int=1
var str string=""
//string类型转int
str=strconv.Itoa(num)
fmt.Printf("n=%v,s=%q",num,str)
var bl bool
var str string="true"
//string类型转bool
//strconv.ParseBool会返回两个值
bl,_=strconv.ParseBool(str)
fmt.Printf("n=%v,s=%q",bl,str)
//string到int
int,err:=strconv.Atoi(string)
//string到int64
int64, err := strconv.ParseInt(string, 10, 64)
//int到string
string:=strconv.Itoa(int)
//int64到string
string:=strconv.FormatInt(int64,10)
//string到float32(float64)
float,err := strconv.ParseFloat(string,32/64)
//float到string
string := strconv.FormatFloat(float32, 'E', -1, 32)
string := strconv.FormatFloat(float64, 'E', -1, 64)
// 'b' (-ddddp±ddd,二进制指数)
// 'e' (-d.dddde±dd,十进制指数)
// 'E' (-d.ddddE±dd,十进制指数)
// 'f' (-ddd.dddd,没有指数)
// 'g' ('e':大指数,'f':其它情况)
// 'G' ('E':大指数,'f':其它情况)
二、go语言的排序、结构体排序
golang中sort包用法
sort包中实现了3种基本的排序算法:插入排序.快排和堆排序
1. 基本类型 int 、 float64 和 string 的排序
对于 int 、 float64 和 string 数组或是分片的排序, go 分别提供了 sort.Ints() 、 sort.Float64s() 和 sort.Strings() 函数, 默认都是从小到大排序
降序排序
sort.Sort(sort.Reverse(sort.IntSlice(intList)))
sort.Sort(sort.Reverse(sort.Float64Slice(float8List)))
sort.Sort(sort.Reverse(sort.StringSlice(stringList)))
args := "安倍12 Monday Tuesday Friday Sunday Wednesday Wednesday"
split := strings.Split(args, " ")
fmt.Println("前:",split)
//go 1.8 新增排序. 支持其它类型
sort.Slice(split, func(i, j int) bool { return len(split[i]) > len(split[j]) })
fmt.Println("后:",split)
//相同值保持原来顺序
sort.SliceStable(split, func(i, j int) bool { return len(split[i]) > len(split[j]) })
三、go 发送http请求
Go语言开发发送Get和Post请求
Go 语言中要请求网页时,使用net/http包实现。
一般情况下有以下几种方法可以请求网页:
Get, Head, Post, 和 PostForm 发起 HTTP (或 HTTPS) 请求:
1.普通的get请求
package main
import (
"io/ioutil"
"fmt"
"net/http"
)
func main() {
res,_ :=http.Get("https://www.baidu.com/")
defer res.Body.Close()
body,_ := ioutil.ReadAll(res.Body)
fmt.Print(body)
}
2.带参数的get请求(参数不放在url里)
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main(){
params := url.Values{}
Url, _:= url.Parse("https://www.baidu.com/")
params.Set("name","zhaofan")
params.Set("age","23")
//如果参数中有中文参数,这个方法会进行URLEncode
Url.RawQuery = params.Encode()
urlPath := Url.String()
fmt.Println(urlPath) //等同于https://www.xxx.com?age=23&name=zhaofan
resp,_ := http.Get(urlPath)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
3.get请求添加请求头
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
req,_ := http.NewRequest("GET","http://www.xxx.com",nil)
req.Header.Add("name","zhaofan")
req.Header.Add("age","3")
resp,_ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Printf(string(body))
}
4.post请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main() {
urlValues := url.Values{}
urlValues.Add("name","zhaofan")
urlValues.Add("age","22")
resp, _ := http.PostForm("http://www.xxx.com",urlValues)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
5.post请求的另一种方式`
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
func main() {
urlValues := url.Values{
"name":{"zhaofan"},
"age":{"23"},
}
reqBody:= urlValues.Encode()
resp, _ := http.Post("http://www.xxx.com/post", "text/html",strings.NewReader(reqBody))
defer resp.Body.Close()
body,_:= ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
6.post请求发送json数据
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
data := make(map[string]interface{})
data["name"] = "zhaofan"
data["age"] = "23"
bytesData, _ := json.Marshal(data)
req, _ := http.NewRequest("POST","http://www.xxx.com",bytes.NewReader(bytesData))
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
7.post请求发送json数据,不用client
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
data := make(map[string]interface{})
data["name"] = "zhaofan"
data["age"] = "23"
bytesData, _ := json.Marshal(data)
resp, _ := http.Post("http://www.xxx.com","application/json", bytes.NewReader(bytesData))
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
1. GET请求:
GET请求直接将参数拼接在URL里面,如同下面的示例:
func GetData() {
client := &http.Client{}
resp, err := client.Get("http://api.map.baidu.com/place/v2/suggestion?query=广州市天河区正佳广场®ion=广州&city_limit=true&output=json&ak=yX8nC9Qzpckek7lY9gGWmlD4TFcA2tzYx3")
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(body))
}
2. POST请求
Post相关的请求有三种,分别是:http.post、http.postForm、http.Do请求。
http.post请求:
func httpPost() {
resp, err := http.Post("http://www.01happy.com/demo/accept.php",
"application/x-www-form-urlencoded",
strings.NewReader("name=cjb"))
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
注意:请求里面的第二个参数必须带,否则会报错的。
http.postForm:
func PostData() {
//client := &http.Client{}
resp, err := http.PostForm("https://www.pgyer.com/apiv2/app/view", url.Values{"appKey": {"62c99290f0cb2c567cb153c1fba75d867e"},
"_api_key": {"584f29517115df2034348b0c06b3dc57"}, "buildKey": {"22d4944d06354c8dcfb16c4285d04e41"}})
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(body))
}
对于比较复杂的http请求,我们可以用到http.do的方式进行请求
func httpDo() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://www.01happy.com/demo/accept.php", strings.NewReader("name=cjb"))
if err != nil {
// handle error
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "name=anny")
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
四、Go语言之时间戳和时间格式如何相互转换?
package main
import(
"fmt"
"time"
)
func main() {
time.Now().Format("2006-01-02 15:04:05") //获取当前时间
datetime := "2019-03-11 21:07:00" //待转化为时间戳的字符串
//日期转化为时间戳
timeLayout := "2006-01-02 15:04:05" //转化所需模板
loc, _ := time.LoadLocation("Local") //获取时区
//调用转化方法,传入上面准备好的的三个参数
tmp, _ := time.ParseInLocation(timeLayout, datetime, loc)
timestamp := tmp.Unix() //转化为时间戳(秒级) 类型是int64
//timestamp = timestamp * 1000 //转化为毫秒级
log.Println(timestamp)
//时间戳转化为日期
datetime = time.Unix(timestamp, 0).Format(timeLayout)
fmt.Println(datetime)
}
五、for…range
在 range 迭代中,得到的值其实是元素的一份值拷贝,更新拷贝并不会更改原来的元素,即是拷贝的地址并不是原有元素的地址:
func main() {
data := []int{1, 2, 3}
for _, v := range data {
v *= 10 // data 中原有元素是不会被修改的
}
fmt.Println("data: ", data) // data: [1 2 3]
}
如果要修改原有元素的值,应该使用索引直接访问:
func main() {
data := []int{1, 2, 3}
for i, v := range data {
data[i] = v * 10
}
fmt.Println("data: ", data) // data: [10 20 30]
}
如果你的集合保存的是指向值的指针,需稍作修改。依旧需要使用索引访问元素,不过可以使用 range 出来的元素直接更新原有值:
func main() {
data := []*struct{ num int }{{1}, {2}, {3},}
for _, v := range data {
v.num *= 10 // 直接使用指针更新
}
fmt.Println(data[0], data[1], data[2]) // &{10} &{20} &{30}
}
for…range用于channel时,自动阻塞。无需再用写channel阻塞
六、defer
1.defer 函数的参数值
对 defer 延迟执行的函数,它的参数会在声明时候就会求出具体值,而不是在执行时才求值:
// 在 defer 函数中参数会提前求值
func main() {
var i = 1
defer fmt.Println("result: ", func() int { return i * 2 }()) //2
i++
}
七、go run/ go build
go build -gcflags -m main.go 或go run -gcflags -m main.go 能准确分析程序的变量分配位置
go run -race 启用数据竞争检测
go help build 查看go build 参数
八、死锁
主协程阻塞会导致死锁,子go程不会
九、四舍五入
func round(x float64){
return int(math.Floor(x + 0/5))
或
n10 := math.Pow10(2) // 保留2位小数。去掉末尾0
fmt.Println(math.Trunc((9.815+0.5/n10)*n10) / n10) //9.82
fmt.Println(math.Trunc((9.825+0.5/n10)*n10) / n10) //9.83
fmt.Println(math.Trunc((9.835+0.5/n10)*n10) / n10) //9.84
fmt.Println(math.Trunc((9.845+0.5/n10)*n10) / n10) //9.85
fmt.Println(math.Trunc((3.3+0.5/n10)*n10) / n10) //3.3
fmt.Println(math.Trunc((3.3000000000000003+0.5/n10)*n10) / n10) //3.3
或
//保留n位小数
func Trunc(str string, decimal int) string {
num, _ := strconv.ParseFloat(str, 10)
d := math.Pow10(decimal)
return strconv.FormatFloat(math.Trunc((num+0.5/d)*d)/d, 'f', -1, 64)
}
十、json解析
Go实战–golang中使用号称全世界最快的JSON解析器json-iterator(json-iterator/go)
Go语言解析Json(使用jsonparser)
解析对象
body:=[]byte()
str:=gjson.GetBytes(body, “content”).String()
str := jsoniter.Get(body, “content”).ToString() // json-iterator
getString, err:= jsonparser.GetString(body, “content”) //jsonparser
11. 切片
函数中切片追加(添加新元素), 不影响原切片
修改原切片,使用切片指针
slice:=[]int{1,2,3}
var pslice *[]int
pslice=&slice //切片的指针变量(二级指针变量)
(*pslice)[0]=10
fmt.Println(slice) //[10 2 3]
slice:=[]int{1,2,3}
trans(&slice)
fmt.Println(slice) //[1 2 3 10]
func trans(slice *[]int) {
//slice[0]=30
*slice=append(*slice,10)
}
12. 内存空间分布
栈区数据区内存空间由系统管理
new在堆区申请内存空间
var arr *[3]int= new([3]int)
arr[0]=10
(*arr)[0]=20
fmt.Println(*arr) //[20 0 0]
13.aop
Go语言本身不支持AOP(面向切面编程),但可以通过一些库和框架来实现该功能。常用的库包括go-aop和GoAop。GoAop比较新,但使用起来比较简单,支持常规拦截器和环绕拦截器等。
除了AOP外,还有其他一些解决方案可以实现基于切面的编程,例如使用“装饰器”模式,将函数或方法作为参数传递,并在某些前提下进行修改。建议根据实际需求来选择最适合的解决方案。
14.new和make
new 用于分配内存,返回对应类型的指针并分配好对应类型的空间;make 则是分配并初始化内置(切片、映射和通道)类型
new 分配返回的是指针,即类型 *Type。make 返回引用,即 Type;
new 分配的空间被清零,即new生成的变量的指针的值为对应类型的0值。make 分配空间后,会进行初始化;
c := new(chan int)
这种方式创建的是一个指向channel的指针,需要使用*操作符来解引用才能得到channel实例。通常不建议使用这种方式创建通道,因为它不能直接使用,需要进行额外的初始化操作才能使用,容易导致错误
正常使用
func main() {
ch := make(chan int) // 使用make()来初始化一个channel
go func() {
ch <- 10 // 将10写入channel中
}()
result := <- ch // 从channel中接收值并赋给result变量
fmt.Println(result)
}
使用new会有死锁问题
## new函数分配了一块内存空间,但没有初始化channel,所以在给channel发送和接收数据时会出现nil channel导致的死锁错误
func main() {
ch := new(chan int) // 初始化一个channel
go func() {
*ch <- 10 // 给channel发送一个值
}()
result := <- *ch // 从channel中接收一个值
fmt.Println(result)
}