Go函数--匿名函数与闭包

2023-11-13

0 匿名函数概念

Go语言提供两种函数:有名函数和匿名函数。所谓匿名函数就是没有函数名的函数。匿名函数没有函数名,只有函数体。它和有名函数的最大区别是:我们可以在函数内部定义匿名函数,形成类似嵌套的效果。

 匿名函数常用于实现回调函数、闭包等。

 1 匿名函数

 1.1 匿名函数的定义格式

func (参数列表) (返回值参数列表) {
    函数体
}

 <说明> 匿名函数除了没有函数名外,和普通函数完全相同。

1.2 匿名函数的特点

1、可以在定义匿名函数的同时直接调用执行。示例代码如下:

func main() {
    func(s string) {
        fmt.Println(s)
    }("hello, world")    //传入实参对匿名函数进行调用
}

 《代码说明》上面示例中,定义了匿名函数体后,直接传入实参调用匿名函数。

2、可以将匿名函数赋值给函数变量。示例代码如下:

func main(){
    add := func(x, y int) int {      //将匿名函数赋值给函数变量add
        return x + y
    }
    
    fmt.Printf("add type: %T\n", add)  //打印变量add的类型
    fmt.Printf("add(1, 2)= %d\n", add(1, 2))
}

运行结果:

add type: func(int, int) int
add(1, 2)= 3

 《代码说明》上面示例中,将匿名函数体赋值给一个函数变量add,然后通过这个函数变量来调用匿名函数。

<提示> 将匿名函数赋值给变量,与为普通函数提供函数名标识符有着根本的区别。当然,编译器会为匿名函数生成一个“随机”的符号名。

3、可以将匿名函数作为函数实参。其实这种代码设计方式就是将匿名函数作为回调函数来使用。示例代码如下:

//遍历切片的元素,通过给定函数访问切片元素
func visit(list []int, f func(int)) {
    for _, v := range list {
        f(v)
    }
}

func main() {
    //使用匿名函数打印切片内容
    visit([]int{1,2,3,4}, func(v int){
        fmt.Println(v)
    })
}

《代码说明》上面的代码中,使用匿名函数作为函数实参,传递给visit()函数的形参f,形参f是一个函数类型。在visit()函数中,就可以使用函数变量f 访问作为实参的匿名函数了。

<提示> 匿名函数作为回调函数来使用在Go语言的标准包中也是比较常见的。

4、匿名函数可以作为函数返回值使用。示例代码如下:

func test() func(int, int) int {
    return func(x, y int) int {
        return x + y
    }
}

func main() {
    add := test()
    
    fmt.Printf("add type: %T\n", add)       // add type: func(int, int) int
    fmt.Printf("add value: %v\n", add)      // add value: 0x49a800
    fmt.Printf("add(1,2)= %d\n", add(1,2))  // add(1,2)= 3
}

《代码说明》test()函数的返回值返回的是一个函数类型:func(int, int) int 的值。在test()函数体中,使用了匿名函数作为函数的返回值,然后在main()函数中,将test()函数的返回值赋值给一个函数变量add,它的类型和test()函数的返回值类型是一样的。从运行结果可以看出,test()函数返回的是匿名函数的入口地址,并赋值给函数变量add,然后通过这个函数变量add来调用匿名函数。

2 闭包(Closure) — 引用了外部变量的匿名函数

2.1 闭包的概念

闭包是引用了其外部作用域的变量的函数。这个函数在Go语言中一般是匿名函数。在《Go语言核心编程》一书中,是这样描述闭包概念的:闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿名函数中引用外部函数的局部变量或包全局变量构成。

简单的说就是:闭包 = 函数 + 引用环境。

  • 函数:一般都是匿名函数。
  • 引用环境:匿名函数引用的其外部作用域的变量,称为环境变量。

示例1:闭包的使用。

package main

import (
    "fmt"
)

func adder() func(int) int {
    var x int
    return func(y int) int {  //匿名函数引用了其外部作用域变量x,而x是该匿名函数外围函数adder()的局部变量
        x += y
        return x
    }
}

func main() {
    var f = adder()    //
    fmt.Println(f(10)) //x=0,y=10 输出:10
    fmt.Println(f(20)) //x=10,y=30  输出:30
    fmt.Println(f(30)) //x=30,y=30  输出:60

    f1 := adder()
    fmt.Println(f1(40)) //x=0,y=40   输出:40
    fmt.Println(f1(50)) //x=40,y=50  输出:90
}

《代码说明》adder()函数返回的匿名函数中直接引用了上下文环境变量x,注意这个变量x不是在匿名函数中定义的,而是在adder()函数中定义的,它是adder()函数的局部变量。当adder()函数返回匿名函数,然后在main()函数中执行 f(10),它依然可以读取x的值,这种现象就称作闭包。

变量f 是一个函数变量并且它引用了其外部作用域中的变量x,此时 f 就是一个闭包。在闭包f 的生命周期内,被闭包引用的环境变量x也会一直存在,并且闭包拥有记忆效应,它能够保存上一次调用闭包f 时环境变量x被修改后的值。

此时,我们不禁要问:x不是adder()函数的局部变量吗,adder()函数都已经返回了,怎么它的局部变量x还能在main函数中被访问到呢?这个我们就需要知道闭包的底层实现原理是什么了,闭包的底层实现将会在下一篇博客中详细分析。简单描述就是:

(1)函数可以作为返回值。

(2)函数内部查找变量的顺序,先在自己内部找,找不到往外层找。这个外层变量就是闭包引用的环境变量。

示例2

package main

import (
    "fmt"
)

func test(x int) func() {
    fmt.Printf("test.x: &x=%p, x=%d\n", &x, x)
    return func(){
        fmt.Println("closure.x:", &x, x)
    }
}

func main(){
    f := test(0x100)
    //fmt.Printf("f type: %T, f value: %v\n", f, f)
    f()
}

运行结果:go run demo.go

test.x: &x=0xc000014088, x=256
closure.x: 0xc000014088 256

《结果分析》通过输出变量x的地址,我们注意到在闭包中直接引用了原环境变量x。我们使用go build命令来查看一下:

$ go build -gcflags '-m -l' demo.go
# command-line-arguments
./demo.go:7:11: moved to heap: x
./demo.go:8:15: ... argument does not escape
./demo.go:8:41: x escapes to heap
./demo.go:9:12: func literal escapes to heap
./demo.go:10:20: ... argument does not escape
./demo.go:10:21: "closure.x:" escapes to heap
./demo.go:10:35: x escapes to heap

可以看到,在编译期, 变量x被移动到堆内存中了,这就能解释为什么x的生命期在test()函数返回后还能继续存在了。事实上,编译器在编译的时候,如果检测到闭包,就会将闭包引用的外部变量移动到堆上。

 2.2 闭包的使用

如果函数返回的闭包引用了该函数的局部变量(函数参数或函数内部变量),使用闭包的注意事项:

1、多次调用闭包的外围函数,返回的多个闭包所引用的外部环境变量是多个副本,原因是每次调用函数都会为局部变量分配内存。

2、调用一个闭包函数多次,如果该闭包修改了其引用的外部环境变量,则每一次调用该闭包都会对该闭包产生影响,因为闭包函数共享其外部引用。

示例3

func fa(a int) func(int) int {
    return func(i int) int {
        fmt.Printf("&a=%p, a=%d\n", &a, a)
        a += i
        return a
    }
}

func main(){
    f := fa(1)  //f引用的外部的闭包环境包括本次函数调用的形参a的值1
    g := fa(1)  //g引用的外部的闭包环境包括本次函数调用的形参a的值1
	
    //此时f、g引用的闭包环境变量a的值并不是同一个,而是两次函数调用产生到副本
    
    fmt.Printf("f(1)=%d\n", f(1))
    //多次调用f引用的是同一个副本
    fmt.Printf("f(1)=%d\n", f(1))
    
    //g中的a的值仍然是1
    fmt.Printf("g(1)=%d\n", g(1))
    fmt.Printf("g(1)=%d\n", g(1))
}

运行结果:

&a=0xc000016090, a=1
f(1)=2
&a=0xc000016090, a=2
f(1)=3
&a=0xc000016098, a=1
g(1)=2
&a=0xc000016098, a=2
g(1)=3

《代码分析》从运行结果可以看到,f、g引用的闭包中的环境变量a是两个不同的副本,也就是说f、g引用的是两个不同的闭包。

3、如果一个函数调用返回的闭包引用的环境变量是全局变量,则每次调用都会影响全局变量。即使是多次调用外围函数返回的多个闭包引用的都是同一个环境变量。

示例4:修改示例3中的代码,将闭包中引用的局部变量改为全局变量。

var (
    a = 0
)

func fa() func(int) int {
    return func(i int) int {
        fmt.Printf("&a=%p, a=%d\n", &a, a)
        a += i
        return a
    }
}

func main(){
    f := fa()   //f引用的外部的闭包环境包括全局变量a
    g := fa()   //g引用的外部的闭包环境包括全局变量a
    
    //此时f、g引用的闭包环境变量a的值是同一个
    
    fmt.Printf("f(1)=%d\n", f(1))
    fmt.Printf("f(1)=%d\n", f(1))
    fmt.Printf("g(1)=%d\n", g(1))
    fmt.Printf("g(1)=%d\n", g(1))
}

运行结果:

&a=0x5868e0, a=0
f(1)=1
&a=0x5868e0, a=1
f(1)=2
&a=0x5868e0, a=2
g(1)=3
&a=0x5868e0, a=3
g(1)=4

<提示> 使用闭包的目的就是为了减少全局变量的使用,所以闭包引用全局变量不是好的编程方式。

4、同一个函数返回的多个闭包共享该函数的局部变量。

示例5

func fa(base int) (func(int) int, func(int) int) {
    fmt.Printf("fa: &base=%p, base=%d\n", &base, base)
    
    add := func(i int) int {
        base += i            //匿名函数引用了fa的局部变量base
        fmt.Printf("add: &base=%p, base=%d\n", &base, base)
        return base
    }
    
    sub := func(i int) int {
        base -= i            //匿名函数引用了fa的局部变量base
        fmt.Printf("sub: &base=%p, base=%d\n", &base, base)
        return base
    }
    
    return add, sub
}

func main(){
    f,g := fa(0)  //f、g闭包引用的base是同一个,是fa函数调用传递的实参值
    
    s,k := fa(0)  //s、k闭包引用的base是同一个,是fa函数调用传递的实参值
    
    //f,g和s,k 这两组引用是不同的闭包变量,这是由于每次调用fa都要重新分配形参
    fmt.Printf("f(1)=%d, g(2)=%d\n", f(1), g(2))
    fmt.Printf("s(1)=%d, k(2)=%d\n", s(1), k(2))
}

运行结果:

fa: &base=0xc000016090, base=0
fa: &base=0xc000016098, base=0
add: &base=0xc000016090, base=1
sub: &base=0xc000016090, base=-1
f(1)=1, g(2)=-1
add: &base=0xc000016098, base=1
sub: &base=0xc000016098, base=-1
s(1)=1, k(2)=-1

《结果分析》fa()函数同时返回了两个闭包函数,这两个闭包函数共享了其引用环境变量base,这两个闭包其中任何一方对base的修改行为都会影响另一个闭包对base的取值,因此在并发环境下可能需要做同步处理。

##】面试题。

func calc(base int) (func(int) int, func(int) int) {
    add := func(i int) int {
        base += i
        return base
    }

    sub := func(i int) int {
        base -= i
        return base
    }
    return add, sub
}

func main() {
    f1, f2 := calc(10)
    fmt.Println(f1(1), f2(2)) //11 9
    fmt.Println(f1(3), f2(4)) //12 8
    fmt.Println(f1(5), f2(6)) //13 7
}

《代码分析》calc()函数返回了两个闭包f1、f2,这两个闭包都引用了calc()函数的局部变量base,那么此时闭包f1、f2共享引用环境变量base。

2.3 闭包的注意事项

示例6

func test() []func() {
    var s []func()

    for i := 0; i < 3; i++ {
        s = append(s, func() {  //将多个匿名函数添加到切片
            fmt.Println(&i, i)
        })
    }

    return s    //返回匿名函数切片
}
func main() {
    for _, f := range test() {  //执行所有匿名函数
        f()   
    }
}

运行结果:

0xc000016090 3
0xc000016090 3
0xc000016090 3

《代码分析》每次 append 操作仅仅是将匿名函数放入到列表中,但并未执行,并且引用的环境变量都是同一变量i,随着 i 的改变匿名函数中的 i 也在改变,所以当在main函数中执行这些函数时,他们读取的是环境变量 i 最后一次循环时的值=3。解决办法就是每次使用不同的环境变量,让各自闭包引用的环境变量各不相同。修改后的代码如下:

func test() []func() {
    var s []func()

    for i := 0; i < 3; i++ {
        x := i                  //x 每次循环都重新定义
        //fmt.Printf("&x=%p, x=%d\n", &x, x)
        s = append(s, func() {  //将多个匿名函数添加到切片
            fmt.Println(&x, x)
        })
    }

    return s    //返回匿名函数切片
}
func main() {
    for _, f := range test() {  //执行所有匿名函数
        f()   
    }
}

 运行结果:

0xc000016090 0
0xc000016098 1
0xc0000160a0 2

2.4 闭包的价值

闭包的最初目的是减少全局变量的使用,在函数调用过程中隐式地传递共享变量,有其有用的一面;但是这种隐秘的共享变量的方式带来的坏处是不够直接,不够清晰,除非是非常有价值的地方,一般不建议使用闭包。闭包让我们不用传递参数就可以读取或修改环境状态,当然这也需要付出额外的代价。对于性能要求较高的场合,须慎重使用。

##】对象和闭包的区别

对象是附有行为的数据,而闭包是附有数据的行为。类在定义时就已经显式地集中定义了行为,但是闭包中的数据没有显式地集中声明的地方,这种数据和行为耦合的模型不是一种推荐的编程模型,闭包仅仅是锦上添花的东西,不是不可缺少的。

2.4 后记

我将在下一篇博客中详细分析Go语言闭包的底层实现原理。

参考

《Go语言从入门到进阶实战(视频教学版)》

《Go语言核心编程》

《Go语言学习笔记》

《Go语言编程》

Go语言基础之函数

GO 匿名函数和闭包

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go函数--匿名函数与闭包 的相关文章

  • go struct{} 空结构体的特点和作用

    空结构体的特点和作用 参考代码 package main import fmt unsafe func main empStruct 空结构体的实例和作用 func empStruct 空结构体的特点 1 不占用内存 2 地址不变 var
  • 使用Docker registry镜像创建私有仓库

    2015 01 25 wcdj 摘要 安装Docker后 可以通过官方提供的registry镜像来简单搭建一套本地私有仓库环境 本文记录简单的搭建过程 1 使用registry启动私有仓库的容器 docker run d p 5000 50
  • Go开发命令行程序指南

    近期在Twitter上看到一个名为 Command Line Interface Guidelines 的站点 1 这个站点汇聚了帮助大家编写出更好命令行程序的哲学与指南 这份指南基于传统的Unix编程原则 2 又结合现代的情况进行了 与时
  • go字符串详解

    文章目录 摘要 1 byte和rune类型 2 字符串 string 3 练习 反转字符串 摘要 go字符串结构体包含 指向底层存储数组的指针 字符串长度 字符串按utf 8将字符编码成二进制数 然后存储在byte数组中 因为utf 8编码
  • Go语言入门【09】结构体

    结构体 相比于Java 在Go语言中没有类的概念 但是多了结构体 结构体与Java中的类很像 是表示一系列同一类型或不同类型的数据构成的数据集合 例如可以将学生抽象成一个结构体 每一个学生有以下属性 Name 姓名 Age 年龄 Gende
  • Go语言实现区块链与加密货币-Part3(交易优化,单机模拟多节点通信)

    交易 二 在这个系列文章的一开始 我们就提到了 区块链是一个分布式数据库 不过在之前的文章中 我们选择性地跳过了 分布式 这个部分 而是将注意力都放到了 数据库 部分 到目前为止 我们几乎已经实现了一个区块链数据库的所有元素 今天 我们将会
  • go语言基础-----03-----流程控制、函数、值传递、引用传递、defer函数

    1 流程控制 这里只讲 for range 语句 这个关键字 主要用于遍历 用来遍历数组 slice map chan 例如 package main import fmt func main str hello world 中国 for
  • 基于Go语言实现简易Web应用

    目录 前言 Go语言特点 写在使用Go语言实现Web应用前面 创建Web服务器 声明一个结构体操作 加入中间件的使用 使用静态文件服务器 最后 前言 在编程语言中 近几年问世的几个新语言都是非常不错的 比如Go Python Rust等等
  • 为什么最近听说 Go 岗位很少很难?

    大家好 我是煎鱼 其实这个话题已经躺在我的 TODO 里很久了 近来很多社区的小伙伴都私下来交流 也有在朋友圈看到朋友吐槽 Go 上海的大会没什么人 还不如 Rust 大会 比较尴尬 今天主要是看看为什么 Go 岗位看起来近来很难的样子 也
  • Go 程序编译过程(基于 Go1.21)

    版本说明 Go 1 21 官方文档 Go 语言官方文档详细阐述了 Go 语言编译器的具体执行过程 Go1 21 版本可以看这个 https github com golang go tree release branch go1 21 sr
  • 48.Go简要实现令牌桶限流与熔断器并集成到Gin框架中

    文章目录 一 简介 二 限流器与熔断器在微服务中的作用 1 限流器 对某个接口单位时间内的访问量做限制 2 熔断器 当服务连续报错 超过一定阈值时 打开熔断器使得服务不可用 三 具体实现 1 限流器实现逻辑 以令牌桶算法为例 2 限流器集成
  • 【go语言开发】Minio基本使用,包括环境搭建,接口封装和代码测试

    本文主要介绍go语言使用Minio对象存储 首先介绍搭建minio 创建bucket等 然后讲解封装minio客户端接口 包括但不限于 上传文件 下载 获取对象url 最后测试开发的接口 文章目录 前言 Minio docker安装mini
  • go-zero开发入门之网关往rpc服务传递数据2

    go zero 的网关服务实际是个 go zero 的 API 服务 也就是一个 http 服务 或者说 rest 服务 http 转 grpc 使用了开源的 grpcurl 库 当网关需要往 rpc 服务传递额外的数据 比如鉴权数据的时候
  • go-zero开发入门-API网关开发示例

    开发一个 API 网关 代理 https blog csdn net Aquester article details 134856271 中的 RPC 服务 网关完整源代码 file main go package main import
  • go-zero目录结构和说明

    code of conduct md 行为准则 CONTRIBUTING md 贡献指南 core 框架的核心组件 bloom 布隆过滤器 用于检测一个元素是否在一个集合中 breaker 熔断器 用于防止过多的请求导致系统崩溃 cmdli
  • go-zero开发入门-API网关鉴权开发示例

    本文是 go zero开发入门 API网关开发示例 一文的延伸 继续之前请先阅读此文 在项目根目录下创建子目录 middleware 在此目录下创建文件 auth go 内容如下 鉴权中间件 package middleware impor
  • go开发--操作mysql数据库

    在 Go 中访问 MySQL 数据库并进行读写操作通常需要使用第三方的 MySQL 驱动 Go 中常用的 MySQL 驱动有 github com go sql driver mysql 和 github com go xorm xorm
  • Golang拼接字符串性能对比

    g o l a n g golang g o l an g
  • 这套Go语言开发框架组合真的非常高效

    我尝试过很多框架 从Django Flask和Laravel到NextJS和SvelteKit 到目前为止 这是我唯一可以使用的不会让我感到疯狂或者放弃项目的堆栈 框架 我喜欢所有这些框架 但我只是不太适应它们的设计方式 实际上 我是一个弱
  • go cannot find package “github.com/gorilla/websocket“解读

    Go无法找到包 github com gorilla websocket 的解决方案 在Go开发过程中 我们经常会依赖第三方库来简化开发工作 而使用 go get 命令安装这些库时 有时候我们可能会遇到类似于以下错误的情况 plaintex

随机推荐

  • (代码练习)1~100中出现数字9的个数---C语言

    出现9的数字有哪些 出现整十之前的数字就是含有9的数字 所以方法一 打印整十前面的数 运行结果 这种方法显得不够专业 而且漏掉了90及其以后十位为9的数 下面介绍方法二 思路 给一个循环从1遍历到100 拿到每个数据后进行一下操作 a 通过
  • MySQL-分库分表详解(三)

    作者 小刘在C站 个人主页 小刘主页 努力不一定有回报 但一定会有收获加油 一起努力 共赴美好人生 学习两年总结出的运维经验 以及思科模拟器全套网络实验教程 专栏 云计算技术 小刘私信可以随便问 只要会绝不吝啬 感谢CSDN让你我相遇 前言
  • 手动加载logback配置文件

    前言 自己写了个压测的main方法 这里主要介绍如果在main方法中输出logback日志 正文 import java io File import java io IOException import org slf4j LoggerF
  • 网站报错 :SLQSTATE[HY000]:General error:145 Table ‘./**@002******@/002ecn/ey_config‘ is mar

    后台使用过程中 因为某个操作而出现报错 SQLSTATE HY000 General error 145 Table 002 002ecn ey config is marked as crashed and should be repai
  • 《Linux运维实战:Centos7.6一键离线部署docker19.03.9》

    文章目录 一 部署背景 三 部署工具 三 部署演示 总结 整理不易 如果对你有帮助 可否点赞关注一下 一 部署背景 由于业务系统的特殊性 我们需要面向不通的客户安装我们的业务系统 而作为基础组件中的docker针对不同的客户环境需要多次部署
  • 通过docker搭建skywalking

    简述 这里引用skywalking官方网站上简介 分布式系统的应用程序性能监视工具 专为微服务 云原生架构和基于容器 Docker K8s Mesos 架构而设计 SkyWalking 是观察性分析平台和应用性能管理系统 提供分布式追踪 服
  • IPO 后,北森不断超越自身

    北森锐意变革的思路值得很多行业借鉴 数科星球原创 作者丨苑晶 编辑丨大兔 在所有 HR SaaS 软件中 北森较为独特 这种独特不光体现在其切入赛道的一体化产品 也体现在它所走过的发展路径 2023 年年中 当人口红利消逝之际 人们对人力资
  • 面试题_Java源文件中可以有多个类,为什么只能有一个public类?

    Java中源文件中可以有多个类 最多只能有一个public类 如果源文件中有多个类 那么只能有一个类是public类 如果有一个类是public类 那么源文件的名字必须和这个类的名字完全相同 扩展名为 java java程序的入口是main
  • 计算机网络——数据链路层の选择题整理

    数据链路层的功能 1 数据链路层协议的功能不包括 A 定义数据格式 B 提供结点之间的可靠传输 C 控制对物理传输介质的访问 D 为终端结点隐蔽物理传输的细节 解析 选D 对于A 定义数据格式 即采用帧作为传输单元 对于B 提供结点之间的可
  • 安卓开发之前端

    安卓开发之前端界面的搭建 前段时间因为网络编程这门课要做一个课程project 题目是本地服务器的开发 需要开发一个安卓客户端 于是就把去年小学期学的那点安卓知识捡起来了 心累 想到去年小学期学的那点安卓真的不算什么 就是一些很简单的东西
  • windgb调试

    reference http hi baidu com lewutian blog item 191047882b9c399fa5c27261 html 调试前的必备工作 在开始调试前首先要做的工作是设置好符号 Symbols 路径 没有符
  • C++constexpr函数(常量表达式函数)

    1 语法 在定义函数时用constexpr关键字作为前缀修饰函数 如 constexpr int fun return 20 注意 定义了一个constexpr函数后 函数声明也要加上constexpr关键字 constexpr函数只能有一
  • 如何安装svelte_在Svelte JS中使用环境变量

    如何安装svelte Process is not defined the compiler just slapped you in the face again You are tempted to hardcode your envir
  • 使用 PyTorch C++ 前端

    PyTorch C 前端是 PyTorch 机器学习框架的纯 C 接口 虽然 PyTorch 的主要接口是 Python 但 Python API 位于大量 C 代码库之上 提供基础数据结构和功能 例如张量和自动微分 C 前端公开了一个纯
  • linux查看系统中文件的内存占用以及具体某个文件夹下内存占用

    1 linux查看系统文件内存占用 df hl 2 Linux查看某个具体文件夹下文件的内存占用 du sh
  • SpringCloud——网关Gateway

    文章目录 六 统一网关 Gateway 6 1 网关介绍 6 2 快速搭建网关 6 3 断言工厂 6 4 过滤器工厂 6 5 全局过滤器 GlobalFIlter 6 6 过滤器的执行顺序 6 7 跨域问题 六 统一网关 Gateway 6
  • C编写的程序可能产生的主要缺陷

    摘自 软件静态分析工具评析 王 凯 孔祥营 空指针引用 悬空指针 资源泄露 函数返回值 使用未初始化变量 无限循环 死亡代码 缓冲区溢出 野指针等 1 空指针引用 空指针引用会导致程序崩溃 空指针引用的情况包括 忘记对指针为NULL 的情况
  • java入门四:数组

    1 数组概述 数组是最简单的数据结构 是相同类型数据的有序集合 数组描述的是相同类型的若干个数据 按照一定的先后次序排列组合而成的 数组中 每一个数据称作一个数组元素 每个数组元素可以通过一个下标来访问他们 2 数组的声明创建 首先必须声明
  • 分享解决jar包冲突问题的方法:(看了这个你就能解决所有包冲突问题!)

    1 问题描述 maven eclipse环境 1 1 昨晚发布这个新功能 接入notify消息中间件 预发失败 报 nested exception is java lang NoSuchMethodError org springfram
  • Go函数--匿名函数与闭包

    0 匿名函数概念 Go语言提供两种函数 有名函数和匿名函数 所谓匿名函数就是没有函数名的函数 匿名函数没有函数名 只有函数体 它和有名函数的最大区别是 我们可以在函数内部定义匿名函数 形成类似嵌套的效果 匿名函数常用于实现回调函数 闭包等