GO 语言快速学习 | PHP转GO笔记
web框架
流行度排行
https://github.com/speedwheel/awesome-go-web-frameworks/blob/master/README.md#popularity
beego
https://beego.me/products
echo
https://echo.labstack.com/
gin
go社区文档
https://learnku.com/docs/gin-gonic/2019
看云gin文档
https://www.kancloud.cn/shuangdeyu/gin_book/949418
gin快速使用
https://www.jianshu.com/p/98965b3ff638
gin-admin-vue
https://www.gin-vue-admin.com/docs/gorm
官网
https://golang.org/dl/
中文官网
https://go-zh.org/pkg/
go常用库
https://github.com/jobbole/awesome-go-cn
查看版本号
go version
查看env
go env
拉不下来包
export GOPATH=/usr/local/go/bin
export GOPROXY=https://goproxy.io
export PATH=$PATH:$GOPATH
export GO111MODULE=on
开启module
go env -w GO111MODULE=on
设置代理
go env -w GOPROXY=https://goproxy.io,direct
微服务框架
go-kit
go micro
go-zero
常用网址
go基础知识
go基础知识v1
go基础知识v2—micro
菜鸟GO
文档和技术论坛
gomod 详细使用
go官网库
go官方pkg中文
github对pgk的使用例子
go mod使用
基本数据类型
字符串操作
本地gorm struct生成
https://github.com/xxjwxc/gormt.git
本地struct生成
https://github.com/hantmac/fuckdb.git
1 使用docker-composer方式启动
2 遇到docker容器不能链接本地mysql的时候
在本地mysql
GRANT ALL PRIVILEGES ON *.* TO "root"@"%" IDENTIFIED BY "root" WITH GRANT OPTION;
FLUSH PRIVILEGES
struct 生成
go get -u github.com/gohouse/converter
err := converter.NewTable2Struct().
SavePath("./model.go").
Dsn("root:root@tcp(127.0.0.1:3306)/foroo_beta_shopify?charset=utf8").
TagKey("db").
EnableJsonTag(true).
Table("fr_store").
Run()
fmt.Println(err)
return
基础
变量或者方法使用小写相当于protect 首字母大写 相当于public
使用+来拼接字符串
变量声明 var age int;
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
可以将 var f string = “Runoob” 简写为 f := “Runoob”:
函数外的每个语句都必须以关键字开始(var、const、func等)
:=不能使用在函数外。
_多用于占位,表示忽略值。import _ “./hello” 用_占位只会执行导入包内的init,其他方法都不能调用
iota是go语言的常量计数器,只能在常量的表达式中使用
strconv.Itoa(97) 将一个数字转成字符串类型 string(97)go自带的会把数字转换成对应的ascii码
const (
n1 = iota //0
n2 //1
n3 //2
n4 //3
)
map、slice、chan、指针、interface默认以引用的方式传递。
互斥锁 var lock sync.Mutex
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
for i := 0; i < 5000; i++ {
lock.Lock() // 加锁
x = x + 1
lock.Unlock() // 解锁
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
读写互斥锁 需要注意的是读写锁非常适合读多写少的场景,如果读和写的操作差别不大,读写锁的优势就发挥不出来。
var (
x int64
wg sync.WaitGroup
lock sync.Mutex
rwlock sync.RWMutex
)
func write() {
// lock.Lock() // 加互斥锁
rwlock.Lock() // 加写锁
x = x + 1
time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
rwlock.Unlock() // 解写锁
// lock.Unlock() // 解互斥锁
wg.Done()
}
func read() {
// lock.Lock() // 加互斥锁
rwlock.RLock() // 加读锁
time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
rwlock.RUnlock() // 解读锁
// lock.Unlock() // 解互斥锁
wg.Done()
}
func main() {
start := time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start))
}
sync.Once其实内部包含一个互斥锁和一个布尔值,互斥锁保证布尔值和数据的安全,而布尔值用来记录初始化是否完成。这样设计就能保证初始化操作的时候是并发安全的并且初始化操作也不会被执行多次。
var icons map[string]image.Image
var loadIconsOnce sync.Once
func loadIcons() {
icons = map[string]image.Image{
"left": loadIcon("left.png"),
"up": loadIcon("up.png"),
"right": loadIcon("right.png"),
"down": loadIcon("down.png"),
}
}
// Icon 是并发安全的
func Icon(name string) image.Image {
loadIconsOnce.Do(loadIcons)
return icons[name]
}
goroutine高并发下 操作map 要用 sync.Map 来操作
//sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法
var m = sync.Map{}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
m.Store(key, n)
value, _ := m.Load(key)
fmt.Printf("k=:%v,v:=%v
", key, value)
wg.Done()
}(i)
}
wg.Wait()
}
代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好。Go语言中原子操作由内置的标准库sync/atomic提供。
// 原子操作版加函数
func atomicAdd() {
atomic.AddInt64(&x, 1)
wg.Done()
}
make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身
变量之间使用引用地址直接可以改变
package main
func swap(a, b *int) {
var temp int
temp = *a
*a = *b
*b = temp
}
func main() {
var a, b = 1, 2
swap(&a, &b)
fmt.Println(a)
fmt.Println(b)
}
//指针小案例
var a int
fmt.Println(&a)
var p *int
p = &a
*p = 20
fmt.Println(a)
在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可
package main
import (
"fmt"
)
func test(s string, n ...int) string {
var x int
for _, i := range n {
x += i
}
return fmt.Sprintf(s, x)
}
func main() {
s := []int{1, 2, 3}
res := test("sum: %d", s...) // slice... 展开slice
println(res)
}
“_”标识符,用来忽略函数的某个返回值
没有参数的 return 语句返回各个返回变量的当前值。这种用法被称作“裸”返回。
func add(a, b int) (c int) {
c = a + b
return
}
func calc(a, b int) (sum int, avg int) {
sum = a + b
avg = (a + b) / 2
return
}
func main() {
var a, b int = 1, 2
c := add(a, b)
sum, avg := calc(a, b)
fmt.Println(a, b, c, sum, avg)
}
命名返回参数允许 defer 延迟调用通过闭包读取和修改。
defer 是先进后出
package main
func add(x, y int) (z int) {
defer func() {
z += 100
}()
z = x + y
return
}
func main() {
println(add(1, 2))
}
定义一维数组
var a [5]int = [5]int{1,2,5,6,7}
var a = [...]int{1,2,3,5,6}
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // 可省略元素类型。
{"user2", 20}, // 别忘了最后一行的逗号。
}
定义二维数组 第 2 纬度不能用 “…”
var b [3][2]int = [3][2]int{{1,2},{3,4},{5,6}}
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
定义slice
全局:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最后一个元素
局部:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
//通过make来创建切片
var slice []type = make([]type, len)
slice := make([]type, len)
slice := make([]type, len, cap)
定义map
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
v, ok := scoreMap["张三"]
if ok {
fmt.Println(v)
} else {
fmt.Println("查无此人")
}
//使用delete()函数删除键值对
delete(scoreMap, "小明")//将小明:100从map中删除
数组按照下标排序输出
tetcc := make(map[int]string,5)
tetcc[1] = "smallsha"
tetcc[5] = "smallsha1"
tetcc[3] = "smallsha2"
sli := []int{}
for i := range tetcc {
sli = append(sli,i)
}
sort.Ints(sli)
for i := 0; i < len(tetcc); i++ {
fmt.Println("内容",tetcc[sli[i]])
}
fmt.Printf("%#v
", sli)
return
if判断
if test2 :=19; test2> 20 {
fmt.Println("test11212121212")
}else if test2 < 20 {
fmt.Println("test1111")
}
switch使用
test3 := 80
switch test3 {
case 80:
fmt.Println("1")
fallthrough //可以使用fallthrough强制执行后面的case代码。
case 90:
fmt.Println("2")
case 100:
fmt.Println("3")
default:
fmt.Println("default")
}
多个参数调用
func test(s string, n ...int) string {
var x int
for _, i := range n {
x += i
}
return fmt.Sprintf(s, x)
}
s := []int{1, 2, 3}
res := test("sum: %d", s...) // slice... 展开slice
println(res)
闭包使用
func a() func() int {
i := 0
b := func() int{
i++
fmt.Println(i)
return i
}
return b
}
ccccc := a()
ccccc()
ccccc()
return
关键字 defer 用于注册延迟调用。
这些调用直到 return 前才被执。因此,可以用来做资源清理。
多个defer语句,按先进后出的方式执行
惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
func main() {
test()
}
func test() {
defer func() {
if err := recover(); err != nil {
println(err.(string)) // 将 interface{} 转型为具体类型。
}
}()
panic("panic error!")
}
表达式
package main
import "fmt"
type User struct {
id int
name string
}
func (self *User) Test() {
fmt.Printf("%p, %v
", self, self)
}
func main() {
u := User{1, "Tom"}
u.Test()
mValue := u.Test
mValue() // 隐式传递 receiver
mExpression := (*User).Test
mExpression(&u) // 显式传递 receiver
}
定义error
http://www.topgoer.com/方法/自定义error.html
//返回异常
package main
import (
"errors"
"fmt"
)
func getCircleArea(radius float32) (area float32, err error) {
if radius < 0 {
// 构建个异常对象
err = errors.New("半径不能为负")
return
}
area = 3.14 * radius * radius
return
}
func main() {
area, err := getCircleArea(-5)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(area)
}
}
//系统抛异常
package main
import "fmt"
func test01() {
a := [5]int{0, 1, 2, 3, 4}
a[1] = 123
fmt.Println(a)
//a[10] = 11
index := 10
a[index] = 10
fmt.Println(a)
}
func getCircleArea(radius float32) (area float32) {
if radius < 0 {
// 自己抛
panic("半径不能为负")
}
return 3.14 * radius * radius
}
func test02() {
getCircleArea(-5)
}
//
func test03() {
// 延时执行匿名函数
// 延时到何时?(1)程序正常结束 (2)发生异常时
defer func() {
// recover() 复活 恢复
// 会返回程序为什么挂了
if err := recover(); err != nil {
fmt.Println(err)
}
}()
getCircleArea(-5)
fmt.Println("这里有没有执行")
}
func test04() {
test03()
fmt.Println("test04")
}
func main() {
test04()
}
//自定义error
package main
import (
"fmt"
"os"
"time"
)
type PathError struct {
path string
op string
createTime string
message string
}
func (p *PathError) Error() string {
return fmt.Sprintf("path=%s
op=%s
createTime=%s
message=%s", p.path,
p.op, p.createTime, p.message)
}
func Open(filename string) error {
file, err := os.Open(filename)
if err != nil {
return &PathError{
path: filename,
op: "read",
message: err.Error(),
createTime: fmt.Sprintf("%v", time.Now()),
}
}
defer file.Close()
return nil
}
func main() {
err := Open("/Users/5lmh/Desktop/go/src/test.txt")
switch v := err.(type) {
case *PathError:
fmt.Println("get path error,", v)
default:
}
}
interface 应用
// 空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v
", a, a)
}
// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "李白"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)
go调度 sync.WaitGroup
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done() // goroutine结束就登记-1
fmt.Println("Hello Goroutine!", i)
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) // 启动一个goroutine就登记+1
go hello(i)
}
wg.Wait() // 等待所有登记的goroutine都结束
}
go runtime.Gosched()
//让出CPU时间片,重新等待安排任务(大概意思就是本来计划的好好的周末出去烧烤,但是你妈让你去相亲,两种情况第一就是你相亲速度非常快,见面就黄不耽误你继续烧烤,第二种情况就是你相亲速度特别慢,见面就是你侬我侬的,耽误了烧烤,但是还馋就是耽误了烧烤你还得去烧烤)
package main
import (
"fmt"
"runtime"
)
func main() {
go func(s string) {
for i := 0; i < 2; i++ {
fmt.Println(s)
}
}("world")
// 主协程
for i := 0; i < 2; i++ {
// 切一下,再次分配任务
runtime.Gosched()
fmt.Println("hello")
}
}
runtime.Goexit()
//退出当前协程(一边烧烤一边相亲,突然发现相亲对象太丑影响烧烤,果断让她滚蛋,然后也就没有然后了)
package main
import (
"fmt"
"runtime"
)
func main() {
go func() {
defer fmt.Println("A.defer")
func() {
defer fmt.Println("B.defer")
// 结束协程
runtime.Goexit()
defer fmt.Println("C.defer")
fmt.Println("B")
}()
fmt.Println("A")
}()
for {
}
}
runtime.GOMAXPROCS
//Go1.5版本之后,默认使用全部的CPU逻辑核心数
///两个任务只有一个逻辑核心,此时是做完一个任务再做另一个任务。 将逻辑核心数设为2,此时两个任务并行执行,代码如下。
func a() {
for i := 1; i < 10; i++ {
fmt.Println("A:", i)
}
}
func b() {
for i := 1; i < 10; i++ {
fmt.Println("B:", i)
}
}
func main() {
runtime.GOMAXPROCS(2)
go a()
go b()
time.Sleep(time.Second)
}
通道
1.对一个关闭的通道再发送值就会导致panic。
2.对一个关闭的通道进行接收会一直获取值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。
5.chan<- int是一个只能发送的通道,可以发送但是不能接收;
6 <-chan int是一个只能接收的通道,可以接收但是不能发送。
func counter(out chan<- int) {
for i := 0; i < 100; i++ {
out <- i
}
close(out)
}
func squarer(out chan<- int, in <-chan int) {
for i := range in {
out <- i * i
}
close(out)
}
func printer(in <-chan int) {
for i := range in {
fmt.Println(i)
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go counter(ch1)
go squarer(ch2, ch1)
printer(ch2)
}
通道使用案例
type Job struct {
Id int
RandNum int
}
type Result struct {
job *Job
sum int
}
func createPool(num int, jobChan chan *Job,resultChan chan *Result) {
for i := 0; i < num; i++ {
go func(jobChan chan *Job,resultChan chan *Result) {
for i2 := range jobChan {
r_num := i2.RandNum
var sum int
for r_num !=0 {
tmp := r_num % 10
sum += tmp
r_num /= 10
}
r := &Result{
job: i2,
sum: sum,
}
resultChan <- r
}
}(jobChan,resultChan)
}
}
func main() {
flag.Parse()
jobChan := make(chan *Job, 128)
resultChan := make(chan *Result, 128)
createPool(64, jobChan, resultChan)
// 4.开个打印的协程
go func(resultChan chan *Result) {
// 遍历结果管道打印
for result := range resultChan {
fmt.Printf("job id:%v randnum:%v result:%d
", result.job.Id,
result.job.RandNum, result.sum)
}
}(resultChan)
var id int
// 循环创建job,输入到管道
for {
id++
// 生成随机数
r_num := rand.Int()
job := &Job{
Id: id,
RandNum: r_num,
}
jobChan <- job
}
return
}
select 监听通道
select {
case <-chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}
flag使用 和 os.args
var name string
var age int
var married bool
var delay time.Duration
flag.StringVar(&name, "name", "张三", "姓名")
flag.IntVar(&age, "age", 18, "年龄")
flag.BoolVar(&married, "married", false, "婚否")
flag.DurationVar(&delay, "d", 0, "延迟的时间间隔")
或者使用
name := flag.String("name", "张三", "姓名")
age := flag.Int("age", 18, "年龄")
married := flag.Bool("married", false, "婚否")
delay := flag.Duration("d", 0, "时间间隔")
//解析命令行参数
flag.Parse()
fmt.Println(name, age, married, delay)
//返回命令行参数后的其他参数
fmt.Println(flag.Args())
//返回命令行参数后的其他参数个数
fmt.Println(flag.NArg())
//返回使用的命令行参数个数
fmt.Println(flag.NFlag())
//os.args
if len(os.Args) > 0 {
for index, arg := range os.Args {
fmt.Printf("args[%d]=%v
", index, arg)
}
}
Log文件
logFile, err := os.OpenFile("./smallsha.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("open log file failed, err:", err)
return
}
log.SetOutput(logFile) //设置日志文件写入
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
log.SetPrefix("[smallsha]")
log.Printf("%s","smallsha")
log.Println("test")
//log.Panic("test1")
//log.Fatalln("test2")
logger := log.New(os.Stdout, "<New>", log.Lshortfile|log.Ldate|log.Ltime)
logger.Println("这是自定义的logger记录的日志。")
循环操作
arr := [5]int{1,2,4,5,6,8}
for i,num :=range arr {
fmt.Println(i,num)
}
//while for 循环配合通道
var (
ch11 = make(chan int)
ch12 = make(chan int)
)
go func() {
for i := 0; i < 10; i++ {
ch11 <- i
}
close(ch11)
}()
go func() {
for {
i, ok := <-ch11
if !ok {
break
}
ch12 <- i * i
}
close(ch12)
}()
for i := range ch12 {
fmt.Println(i)
}
指针
const MAX int =3
func main(){
a :=[]int{1,3,5}
var ptr [MAX]*int;
for i = 0; i < MAX; i++ {
ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
}
for i = 0; i < MAX; i++ {
fmt.Printf("a[%d] = %d
", i,*ptr[i] )
}
}
strconv 使用
s3, _ := strconv.ParseBool("1")
fmt.Printf("%# v
", pretty.Formatter(s3))
return
//int 转 string
s2 := 100
i2 := strconv.Itoa(s2)
fmt.Printf("%# v
", pretty.Formatter(i2))
return
//string 转 int
s1 := "100"
i1, err := strconv.Atoi(s1)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%# v
", pretty.Formatter(i1))
return
template使用可以注册http
package logic
import (
"fmt"
"html/template"
"net/http"
)
func init() {
http.HandleFunc("/",sayHello)
err := http.ListenAndServe(":9090", nil)
if err != nil {
fmt.Println(err)
return
}
}
type UserInfo struct {
Name string
Gender string
Age int
}
func sayHello(w http.ResponseWriter,r *http.Request) {
// 解析指定文件生成模板对象
tmpl, err := template.ParseFiles("./hello.html")
if err != nil {
fmt.Println("create template failed, err:", err)
return
}
// 利用给定数据渲染模板,并将结果写入w
user := UserInfo{
Name: "枯藤",
Gender: "男",
Age: 18,
}
// 利用给定数据渲染模板,并将结果写入w
tmpl.Execute(w, user)
}
定义结构体
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
printBook(&Book1)
/* 打印 Book2 信息 */
printBook(&Book2)
}
func printBook( book *Books ) {
fmt.Printf( "Book title : %s
", book.title)
fmt.Printf( "Book author : %s
", book.author)
fmt.Printf( "Book subject : %s
", book.subject)
fmt.Printf( "Book book_id : %d
", book.book_id)
}
//结构体new 和赋值
type Person struct {
Username string `json:"username"`
Password string `json:"password"`
}
//声明构造方法
func newPerson(username string, password string) *Person {
return &Person{
Username: username,
Password: password,
}
}
//定义修改结构体的方法
func (p *Person) setUsername(username string) {
p.Username = username
}
map操作
package main
import "fmt"
func main() {
var countryCapitalMap map[string]string /*创建集合 */
countryCapitalMap = make(map[string]string)
/* map插入key - value对,各个国家对应的首都 */
countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "罗马"
countryCapitalMap [ "Japan" ] = "东京"
countryCapitalMap [ "India " ] = "新德里"
/*使用键输出地图值 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [country])
}
/*查看元素在集合中是否存在 */
capital, ok := countryCapitalMap [ "American" ]
/*删除元素*/ delete(countryCapitalMap, "France")
/*如果确定是真实的,则存在,否则不存在 */
/*fmt.Println(capital) */
/*fmt.Println(ok) */
if (ok) {
fmt.Println("American 的首都是", capital)
} else {
fmt.Println("American 的首都不存在")
}
}
声明变量
package main
import "fmt"
func main() {
var a string = "Runoob"
fmt.Println(a)
var b, c int = 1, 2
fmt.Println(b, c)
}
声明方法
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Google", "Runoob")
fmt.Println(a, b)
}
定义数组
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d
", j, n[j] )
}
}
Go 语言结构体
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
fmt.Printf( "Book 1 title : %s
", Book1.title)
fmt.Printf( "Book 1 author : %s
", Book1.author)
fmt.Printf( "Book 1 subject : %s
", Book1.subject)
fmt.Printf( "Book 1 book_id : %d
", Book1.book_id)
/* 打印 Book2 信息 */
fmt.Printf( "Book 2 title : %s
", Book2.title)
fmt.Printf( "Book 2 author : %s
", Book2.author)
fmt.Printf( "Book 2 subject : %s
", Book2.subject)
fmt.Printf( "Book 2 book_id : %d
", Book2.book_id)
}
结构体作为函数参数
func printBook( book Books ) {
fmt.Printf( "Book title : %s
", book.title)
fmt.Printf( "Book author : %s
", book.author)
fmt.Printf( "Book subject : %s
", book.subject)
fmt.Printf( "Book book_id : %d
", book.book_id)
}
声明函数和方法区别
#函数
func main() {
sum := add(1, 2)
fmt.Println(sum)
}
func add(a, b int) int {
return a + b
}
#方法
type person struct {
name string
}
func (p person) String() string{
return "the person name is "+p.name
}
func main() {
p:=person{name:"张三"}
fmt.Println(p.String())
}
#可变参数
func main() {
print("1","2","3")
}
func print (a ...interface{}){
for _,v:=range a{
fmt.Print(v)
}
fmt.Println()
}
修改字符串 需要先转换成 byte 或者 rune
func changeString() {
s1 := "hello"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = "H"
fmt.Println(string(byteS1))
s2 := "博客"
runeS2 := []rune(s2)
runeS2[0] = "狗"
fmt.Println(string(runeS2))
}