Go语言有一些让人影响深刻的核心特性核心特性,比如:以消息传递模式的并发、独特的_符号、defer 、函数和方法、值传递等等,可以查看这篇文章《Go语言-让我印象深刻的13个特性》。首先要记住一些核心特性的用法。

创新互联建站长期为千余家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为南谯企业提供专业的网站设计制作、网站设计,南谯网站改版等技术服务。拥有十余年丰富建站经验和众多成功案例,为您定制开发。
Goroutine就是这种协程特性的实现。Goroutine 是通过通信来共享内存,而不是共享内存来通信。通过共享内存来控制并发,会使编程变得更复杂,容易引入更多的问题。
Goroutine是由Go的运行时调度和管理。Go程序会智能地将 Goroutine 中的任务合理地分配给每个CPU,它在语言层面已经内置了调度和上下文切换的机制,不需要程序员去操作各种方法实现调度。
在Go语言中,当需要让某个任务并发执行时,只需要把这个任务包装成一个函数,开启一个Goroutine去执行就可以了。如下,只需要在调用函数时,在前面加上go关键字。
func hello_go() {
  fmt.Println("hello go!!!")
}
func main() {
    go hello_go()
    fmt.Println("main done!!!")
    time.Sleep(time.Second)
}
在Go语言中接口interface是一种类型。Go语言的接口比较松散,只要是实现了接口定义的方法,就是实现了这个接口,无需使用implement等关键字去声明。
定义接口:
// 定义接口
type Sayer interface {
  say()
}
// 定义结构体
type dog struct {
}
type cat struct {
}
// 定义方法
func (d dog) say() {
  fmt.Println("狗叫")
}
func (c cat) say() {
  fmt.Println("猫叫")
}空接口可以存储任意类型:
// 比如定义一个map类型的对象
var obj = map[string]interface{}使用类型断言判断空接口中的值:
// x:表示类型为interface{}的变量
// T:表示断言x可能是的类型。
x.(T)
func main() {
  var x interface{}
  x = 123
  //v, ok := x.(int)
  v, ok := x.(string)
  if ok {
    fmt.Println(v)
  } else {
    fmt.Println("类型断言失败")
  }
}接口特性:
// 定义接口
type Sayer interface {
  say()
}
// 定义结构体
type dog struct {
}
type cat struct {
}
// 定义方法
func (d dog) say() {
  fmt.Println("狗叫")
}
func (c cat) say() {
  fmt.Println("猫叫")
}
func main(t *testing.T) {
  var x Sayer // 声明一个接口类型的变量
  c := cat{}  // 实例化cat
  d := dog{}  // 实例化dog
  x = c       // cat赋值给接口类型
  x.say()     // 打印:猫叫
  x = d       // dog赋值给接口类型
  x.say()     // 打印:狗叫
}
// 定义接口
type Sayer interface {
  say()
}
type Mover interface {
  move()
}
// 定义结构体
type dog struct {
}
// 定义方法
func (d dog) say() {
  fmt.Println("狗叫")
}
func (d dog) move() {
  fmt.Println("狗移动")
}
func main(t *testing.T) {
  var x Sayer
  var y Mover
  var d = dog{}
  x = d
  y = d
  x.say()
  y.move()
}
// 定义接口
type Mover interface {
  move()
}
type Sayer interface {
  say()
}
// 定义结构体
type dog struct {
}
// 定义方法
func (d *dog) say() {
  fmt.Println("狗叫")
}
func (d dog) move() {
  fmt.Println("狗移动")
}
func TestProgram(t *testing.T) {
  var x Sayer
  var y Mover
  //var d = dog{}
  var d = &dog{}
  x = d        // x不可以接收 dog类型,因为golang 不会 将值类型 转换 为指针类型
  y = d     // y可以接受  *dog类型,因为golang 会 将指针类型 转换 为值类型
  x.say()
  y.move()
}
_是特殊标识符,用来忽略结果。
buf := make([]byte, 1024)
f, _ := os.Open("/Users/***/Desktop/text.txt")
func main() {
  a := 10
  fmt.Printf("type of a: %T\n", a)
  b := &a // 取变量a的地址,将指针保存到b中
  fmt.Printf("type of b: %T\n", b)
  c := *b // 取出 指针b 所指向的值
  fmt.Printf("type of c: %T\n", c)
  fmt.Printf("value of c: %v\n", c)
}
关键字 defer 用于注册延迟调用。这些调用直到 return 前才被执。可以用来做资源清理,常用来关闭资源。defer 是先进后出。
func main() {
  arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  for _, v := range arr {
    defer fmt.Println("循环:", v)
  }
  fmt.Println("主流程跑完")
  time.Sleep(time.Second * 3)
  // 等待3秒后,执行defer,输出时先输出10,最后输出1,因为是先进后出
}
bool                                // 布尔
int, int8, int16, int32, int64      // 整数
uint, uint8, uint16, uint32, uint64 // 0 和正整数
float32, float64                    //浮点数
string                              // 字符串
complex64, complex128               // 数学里的复数
array     // 固定长度的数组
struct    // 结构体
string    // 字符串
slice     // 序列数组
map       // 映射
chan      // 管道
interface // 接口 或 任意类型
func      // 函数
append          // 追加元素到数组
copy            // 用于复制和连接slice,返回复制的数目
len             // 求长度,比如string、array、slice、map、channel
cap             // capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
delete          // 从map中删除key对应的value
panic           // 抛出异常(panic和recover:用来做错误处理)
recover         // 接受异常
make            // 分配内存,返回Type本身(只能应用于slice, map, channel)
new             // 分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
close           // 关闭channel
// 申明变量
var name string
// 申明常量
const pi = 3.1415
const e = 2.7182
// 或
const (
        pi = 3.1415
        e = 2.7182
    )
// 申明并且初始化
n := 10
数组的长度固定:
var arr1 = [5]int{1, 2, 3, 4, 5}
// 或
arr2 := [...]struct {
  name string
  age  int8
}{
  {"yangling", 1},
  {"baily", 2},
}
切片的长度不固定:
// 1.声明切片
var s1 []int
s2 := []int{}
var s3 = make([]int, 0)
// 向切片中添加元素
s1 = append(s1, 2, 3, 4)
// 从切片中按照索引获取切片
s1[low:high]
// 循环
for index, element := range s1 {
  fmt.Println("索引:", index, ",元素:", element)
}
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["李四"] = 100
userInfo := map[string]string{
  "username": "baily",
  "password": "111111",
}
// 如果key存在ok 为true,v为对应的值;
// 如果key不存在ok 为false,v为值类型的零值
v, ok := scoreMap["李四"]
if ok {
  fmt.Println(v)
} else {
  fmt.Println("查无此人")
}
// 循环
for k, v := range scoreMap {
  fmt.Println(k, v)
}
//将王五从map中删除
delete(scoreMap, "王五")
不同的使用方式,可能返回指针,也可能返回值。
// 定义结构体
type Student struct {
  name string
  age  int
}
func main() {
  // 使用结构体
  // 方式1,返回的是值
  var stu1 Student
  stu1.name = "baily"
  stu1.age = 1
  fmt.Println("baily1:", stu1)
  // 方式2,返回的是值
  var stu2 = Student{
    name: "baily",
    age:  1,
  }
  fmt.Println("baily2:", stu2)
  // 方式3,返回的是指针
  stu3 := &Student{
    name: "baily",
    age:  1,
  }
  fmt.Println("baily3指针:", stu3)
  fmt.Println("baily3值:", *stu3)
  // 方式4,返回的是指针
  var stu4 = new(Student)
  stu4.name = "baily"
  stu4.age = 1
  fmt.Println("baily4指针:", stu4)
  fmt.Println("baily4值:", *stu4)
}
流程控制包括:if、switch、for、range、select、goto、continue、break。主要记下select,其他的跟别的语言类似。主要用于等待资源、阻塞等待等等。
select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
func main() {
  var c1 = make(chan int)
  go func() {
    time.Sleep(time.Second * 10)
    c1 <- 1
  }()
  // 此处会一直等到10S到期,通道里有值才会继续往下走。
  // 如果增加了 time.After(time.Second * 3) ,则最多3秒则结束
  // 如果这2个case都不行,会走default,也可以不设置default
  select {
  case i, ok := <-c1:
    if ok {
      fmt.Println("取值", i)
    }
  case <-time.After(time.Second * 3):
    fmt.Println("request time out")
  default:
    fmt.Println("无数据")
  }
}
// 正常函数
func test(x int, y int, s string) (int, string) {
    n := x + y          
    return n, fmt.Sprintf(s, n)
}
// 匿名函数
func main() {
    getSqrt := func(a float64) float64 {
        return math.Sqrt(a)
    }
    fmt.Println(getSqrt(4))
}
在Go语言中,闭包是一种函数值,它引用了其函数体外部的变量。闭包允许函数访问并处理其外部范围内的变量,即使函数已经返回了,这些外部变量也会被保留在闭包内。
所以说,一个闭包由两部分组成:函数体 和 与其相关的引用外部变量的环境。
当一个函数被定义在另一个函数内部时,并且引用了外部函数的变量,就会创建一个闭包。这个闭包函数可以随时访问和修改外部函数中的变量,即使外部函数已经执行完毕。
func main() {
  // 外部函数定义并返回内部函数
  add := adder()
  
  // 通过闭包调用内部函数,increment是闭包函数
  fmt.Println(add(1)) // 输出:1
  fmt.Println(add(2)) // 输出:3
  fmt.Println(add(3)) // 输出:6
}
// 外部函数,返回一个闭包函数
func adder() func(int) int {
  sum := 0 // 外部函数中的变量
  // 闭包函数
  return func(x int) int {
    sum += x // 闭包函数使用了外部函数中的变量
    return sum
  }
}
type error interface { //只要实现了Error()函数,返回值为string的都实现了err接口
   Error()    string
}
使用 panic 抛出错误,然后在defer中通过recover捕获异常。
func main() {
    testPanic()
}
func testPanic() {
  defer func() {
    if err := recover(); err != nil {
      fmt.Println(err.(string))
    }
  }()
  panic("抛出异常")
}
// 隐式地返回2个值
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)
  }
}
可以使用匿名字段:
type Person struct {
  name string
  age  int
}
type Student struct {
  Person
  id   int
  addr string
}
func main() {
    s1 := Student{
      Person{"baily", 20},
      1,
      "南京市雨花台区南京南站",
    }
    fmt.Println(s1)
}如果对象内部嵌套的对象有同名字段的情况,只取对象自己的字段:
type Person struct {
  name string
  age  int
}
type Student struct {
  Person
  id   int
  addr string
  name string
}
func main() {
  var s Student
  s.name = "baily"
  s.Person.name = "baily-parent"
  fmt.Println(s) // 打印出 baily   
}
一个方法就是一个包含了接受者的函数,接受者可以是 类型或者结构体 的值或者指针。
type Test struct{}
// 多参数、多返回值
func (t Test) method1(x, y int) (z int, err error) {
  return
}
// 多参数、多返回值
func (t *Test) method2(x, y int) (z int, err error) {
  return
}
当方法作用于值接收者时,Go语言会在代码运行时将接收者的值复制一份。在值接收者的方法中可以获取接收者的成员值,但修改操作只是针对复制出来的副本,无法修改接收者本身。
而指针接受者,在修改成员时,会修改接受者本身。
// SetAge 设置p的年龄
// 使用指针接收者
func (p *Person) SetAge(newAge int) {
  p.age = newAge
}
// SetAge2 设置p的年龄
// 使用值接收者
func (p Person) SetAge2(newAge int) {
  p.age = newAge
}
func main() {
  p := new(Person)
  p.age = 11
  p.SetAge(22)   // 对象p的age会被改变
  fmt.Println(p.age)
  p.SetAge2(33)  // 对象p的age不会被改变
  fmt.Println(p.age)
}什么时候应该使用指针接受者?
TCP编程:
// 处理函数
func process(conn net.Conn) {
  defer conn.Close() // 关闭连接
  for {
    reader := bufio.NewReader(conn)
    var buf [128]byte
    n, err := reader.Read(buf[:]) // 读取数据
    if err != nil {
      fmt.Println("读取客户端数据失败:", err)
      break
    }
    recvStr := string(buf[:n])
    fmt.Println("收到client端发来的数据:", recvStr)
    conn.Write([]byte("回复客户端:" + recvStr)) // 发送数据
  }
}
func main() {
  listen, err := net.Listen("tcp", "127.0.0.1:9587")
  if err != nil {
    fmt.Println("启动监听异常:", err)
    return
  }
  for {
    conn, err := listen.Accept() // 建立连接
    if err != nil {
      fmt.Println("没有连接:", err)
      continue
    }
    go process(conn) // 启动一个goroutine处理连接
  }
}
var wg sync.WaitGroup
func hello_wg(i int) {
  defer wg.Done() // goroutine结束就登记-1
  fmt.Println("hello_wg!", i)
}
func main() {
  for i := 0; i < 10; i++ {
    wg.Add(1) // 启动一个goroutine就登记+1
    go hello_wg(i)
    time.Sleep(time.Second)
  }
  wg.Wait() // 等待所有登记的goroutine都结束
}
Go语言的并发模型是CSP(Communicating Sequential Processes),通过通信共享内存,而不是通过共享内存而实现通信。
func recv(c chan int) {
  ret := <-c
  fmt.Println("接收成功", ret)
}
func main() {
  c := make(chan int)
  go recv(c) // 启用goroutine从通道接收值
  c <- 10
  fmt.Println("发送成功")   
}
func main() {
  var c1 = make(chan int)
  go func() {
    time.Sleep(time.Second * 10)
    c1 <- 1
  }()
  // 此处会一直等到10S到期,通道里有值才会继续往下走。
  // 如果增加了 time.After(time.Second * 3) ,则最多3秒则结束
  // 如果这2个case都不行,会走default,也可以不设置default
  select {
  case i, ok := <-c1:
    if ok {
      fmt.Println("取值", i)
    }
  case <-time.After(time.Second * 3):
    fmt.Println("request time out")
  default:
    fmt.Println("无数据")
  }
}
多个go协程操作同一个资源时,会发生并发问题,需要加锁解决。有互斥锁,还有读写锁。
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)   
}
文件以_test.go结尾,方法以Test开头,方法入参t *testing.T。
func TestProgram(t *testing.T) {
  split := strings.Split("a,b,c", ",")
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("异常:", err)
    }
  }()
  findElement(split, "a")
}
// 查找元素
func findElement(split []string, target string) {
  flag := false
  for _, e := range split {
    if e == target {
      flag = true
      break
    }
  }
  if flag {
    fmt.Println("已经找到")
  } else {
    panic("没找到")
  }
}
                当前名称:Go语言的常用基础
                
                网站网址:http://www.csdahua.cn/qtweb/news22/151772.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网