go-函数和错误处理
函数基本语法
func 函数名(形参列表)(返回值列表){
执行语句
return 返回值列表
}//返回值可以没有可以有多个可以有一个
包
引入
为了解决两个程序员取得函数名同名的情况
原理
本质就是创建不同的文件夹
概念
go的每一个文件都属于一个包,即go是以包来管理文件和项目目录结构的
作用
区分相同名字的函数,变量等标识符
程序文件很多时,很好的管理项目
控制变量,函数等访问范围,即作用域,大写公有,小写私有
说明
package:包名
import:包的路径
使用注意事项
给一个文件打包,该包对应一个文件夹,文件的包名通常和文件所在的文件夹名一致,一般为小写字母
要用包,先引包
import(
"包名"
”包名“
)
import “包名”
package指令在第一行,然后是import
在import包时,路径从$GOPATH的src下开始,不用带src,编译器会自己从src下引入
函数首字母大写才可以让其他包文件访问本包文件
访问:包名.函数名
如果包名较长,支持给包取别名,但是,取别名后原来的包名不能再用
在import里:别名 包名
同一包下,不能有相同函数名,否则报错
语法规范
编译生成一个可执行文件,需要将这个包名声明称main,即package main,但如果写的是一个库,包名可以自己定义
go build go_code/...../main
不需要带src编译器会自动带上
编译需编译main所在的文件夹
项目的目录结构最好按照规范来写
函数调用
说明
基本数据类型一般存放在栈区,编译器存在逃逸分析
引用类型存放在堆区,编译器存在逃逸分析
另外还有一个代码区
调用时分配新的空间,和其他栈区分开,每个空间独立不会混淆,函数执行完毕后销毁该空间
return语句
可接受多个,不想要可用占位符_忽略
若返回值有多个加(返回值类型列表),一个则不用_
res1, res2 := getsunAndsub(1,2)//该函数两个返回值
_, res2 := getsunAndsub(1,2)//占位符_忽略
递归
总结
执行函数,创建一个新的受保护的独立空间(新函数栈)
函数局部变量独立,不会相互影响
递归向退出递归条件的逼近,否则无限递归
函数执行完毕或遇到return就会返回,谁调用返回睡,自身销毁
小细节
数组是值传递,函数内修改,不会影响原来的值
希望函数可以改变函数外变量可以用&
函数内以指针操作
效果上看类似于引用
funct test(n1 *int){...}
test(&n1)
不同于c++的引用
int fun(int &a,int &b){...}
fun(a,b);
go中函数不支持重载,会报函数重复定义的错误
go中函数也是一种数据类型,可以赋值给一个变量,则该变量就是函数类型变量,可以通过改变量来对函数进行调用
func test(n int ,m,int){...}
a :=tset
res := a(10,20)//等价于res := test(10,20)
函数既然是一种数据类型,因此可以作为形参,并且调用
res2 :=myFun(getSun,n1,n2)
func myFun( funcvar fun(int, int) int, num1 int, num2 int) int{
return funcvar(num1,num2)
}
### go
支持自定义数据类型,可化简定义
type 自定数据类型名 数据类型
type myIny int
type myFuncType func(int, int )int
这是可用myFunType代替形参 func(int,int)int
支持函数返回值命名
func getSunAndSub( n1 int, n2 int) (sum int ,sub int){
sub = n1-n2
sum = n1+n2
return
}
_表示忽略
go支持可变参数
//支持0到多个参数
func sun(args...int) sun int{...}
//支持1到多个参数
func sum(n int, args...int) sun int{...}
//args是切片,通过args[index]访问到各个值
func sum(n1,n2 float)float{...}//n1 type=float
给函数类型的变量赋值函数:形参个数一样,返回值个数一样才算同种类型的即一个形参对应于一种函数类型
type myFuncType func(int, int )int
不能把func sum(n1,n2,n3 int)int{...}赋值给myFuncType
类型要匹配
init函数
介绍
每一个源文件都可以有一个init函数,会在main前执行,被go运行框架调用
通常在init中完成初始化工作
注意事项和细节
全局变量在init前执行
若import有utils.go则执行顺序
1.utils.go中的变量定义
2.utils.go中的init
3.main.go中的全局变量定义
4.main.go中的init
5.main.main函数
匿名函数(是一种类型)
没有名字,若只希望用一次,可以考虑使用匿名函数,当然也可调用多次
res := func( n1 int, n2 int) int {
return n1 +n2
}(10 ,20)
//这种方式为定义时调用,只能调用一次
res := func( n1 int, n2 int) int {
return n1 +n2
}
res2 := res(10 ,20)
//将函数付给一个变量,通过变量可多次调用匿名函数
全局匿名函数
将匿名函数付给一个全局变量,那吗可在程序中有效
var(
Fun1 = func (n1 int ,n2 int) int {
return n1*n2
}
)
闭包
就是一个函数和其相关的引用环境组合的一个整体
func AddUpper() func (int) int {
var n int = 10
return func (x int) int {
n = n+x
return n
}
}
//AddUpper是一个函数,返回类型为func(int) int
//n的值类似于一个静态变量
f := AddUpper()
fmt.println(f(1))//11
fmt.println(f(2))//13
上面匿名函数和函数外的形成了一个整体,构成闭包
函数和他引用到的变量构成了闭包
案例
判断文件后缀名是否为jpg,给文件加或不加
闭包完成
内函数引用到外函数的形参或定义的变量构成闭包
闭包可以保留上次引用的值,所以
优点:传入一次就可以反复使用
package main
import (
"fmt"
"strings"
)
func makeSuffix(suffix string) func (string) string {
return func (name string) string {
//如果 name 没有指定后缀,则加上,否则就返回原来的名字
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func makeSuffix2(suffix string, name string) string {
//如果 name 没有指定后缀,则加上,否则就返回原来的名字
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
func main(){
//测试makeSuffix 的使用
//返回一个闭包
f2 := makeSuffix(".jpg") //如果使用闭包完成,好处是只需要传入一次后缀。
fmt.Println("文件名处理后=", f2("winter")) // winter.jgp
fmt.Println("文件名处理后=", f2("bird.jpg")) // bird.jpg
fmt.Println("文件名处理后=", makeSuffix2("jpg", "winter")) // winter.jgp
fmt.Println("文件名处理后=", makeSuffix2("jpg", "bird.jpg")) // bird.jpg
}
defer
why
函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时释放资源,go的设计组提供了defer(延时机制)
执行到defer时将defer后面的语句压入到独立的栈中(defer栈)
当函数执行完毕后,再从defer栈中按先入后出的方式出栈
注意事项
defer入栈时,会将相关值进行拷贝一起放入栈,注意,此值不收其他表达式影响
其价值在于函数执行完毕后,及时释放函数创建的资源
创建资源
defer 释放资源,其他代码不用再担心在什么时候关闭资源
函数传参机制
值类型值拷贝,引用类型地址拷贝
一般来说地址拷贝效率高,因为数据小,而值拷贝,数据越大,效率越低
变量作用域
函数内部的作用域仅限于函数内
函数外部的定义变量叫作全局变量在整个包内有效,若首字母大写在整个程序有效
在一个代码块中定义,则只在代码块中有效
编译器会采用就近原则
name := "tom"//错误,因为等价于var Name string Name = "tom"
字符串常用的系统函数
统计字符串长度
len(str)
字符串遍历,可带中文
r :=[]rune(str)
字符串转整数
n,err := strconv.Atoi("12")
整数转字符串
str = strconv.Itoa(12345)
字符串转[]byte
var bytes = []byte("hello go")
[]byte转字符串
str = string([]byte{97,98,99})
10进制转2 8 16进制
str = strconv.FormatInt(132,2)
在母串中查子串
strings.Contains("seafood","food")
母串中统计子串
strings.Counts("seafood","food")
不区分大小写的比较
stringsEqualFold("abc",""ABC)
返回子串在字符串第一次出现的index若没有返回-1
strings.Index("NLT_abc","abc")
指定子串替换为另一个子串
strings.Replace("go go hello","go","go语言",n)//n可指定几个-1表示全部替换
按照特定的某个字符为标识符将一个字符串拆分成字符数组
strings.Split("hello,word,ok",",")
字符串大小写转换
strings.TOLower("Go")
strings.ToUpper("Go")
将字符串两边的空格去掉
strings.TrimSpace(" asassdf ")
将字符串两边指定字符去掉
strings.Trim("! hello!","!")//["!"],将“!”去掉
strings.TrimLeft()去左边
strings.TrimRight()去右边
判断是否已指定的字符串开头
strings.HasPrefix("ftp://192.168.10.1","ftp")
strings.HasSuffix()判断是否已字符串结束
时间和日期相关的函数
需先引入time包
time.Time获取时间
now := time.now()
now.year()
分秒以此类推,返回类型为字符串型可转换
格式化时间
法一:用Print或Sprintf
法二: 用time.Format()完成
注意格式
单位
Nanosecond纳秒
Microsecond微妙
Millisecond毫秒
在程序中获取指定单位的时间 100*time.Millisecond
结合sleep使用:time.Sleep(100*time.Millisecond)
time的UnixNano和Unix
时间戳
获取时间的单位为妙
单位为纳秒
Now.Unix() 从1970年1月1日到现在的时间差
可用来统计一个函数执行的时间。利用时间戳
len用来求长度
new用来分配内存,主要分配值类型,返回的是指针返回值是一个地址的数值,地址又是一个数值,都是系统分配的
make用来分配地址主要用于引用类型
错误处理
希望错误出现后可以捕获到错误并进行处理保证程序执行,还可以不货到后给管理员一个提示(邮件或短信),而不是崩溃,所以引出错误处理机制
go追求优雅,所以不支持try..catch...finally处理方式为defer,panic,recover
抛出一个panic错误然后在defer中通过recover捕获这个异常,然后正常处理
defer+recover处理
defer fucn() {
err := recover()
if err != nil {
fmt.Println("err=",err)
}
}
recover内置函数可以捕获到异常
错误处理好处
程序不会轻易挂掉,加入警戒代码,让代码更健壮
自定义错误
使用errows.New和panic内置函数
errors.New("错误说明"),会返回一个error类型的值,表示一个错误
panic内置函数接受一个interface{}类型的值(也就是任和值了),可以接受error类型变量,输出错误信息,并退出程序
package main
import (
"fmt"
_ "time"
"errors"
)
func test() {
//使用defer + recover 来捕获和处理异常
defer func() {
err := recover() // recover()内置函数,可以捕获到异常
if err != nil { // 说明捕获到错误
fmt.Println("err=", err)
//这里就可以将错误信息发送给管理员....
fmt.Println("发送邮件给admin@sohu.com~")
}
}()
num1 := 10
num2 := 0
res := num1 / num2
fmt.Println("res=", res)
}
//函数去读取以配置文件init.conf的信息
//如果文件名传入不正确,我们就返回一个自定义的错误
func readConf(name string) (err error) {
if name == "config.ini" {
//读取...
return nil
} else {
//返回一个自定义错误
return errors.New("读取文件错误..")
}
}
func test02() {
err := readConf("config2.ini")
if err != nil {
//如果读取文件发送错误,就输出这个错误,并终止程序
panic(err)
}
fmt.Println("test02()继续执行....")
}
func main() {
//测试
// test()
// for {
// fmt.Println("main()下面的代码...")
// time.Sleep(time.Second)
// }
//测试自定义错误的使用
test02()
fmt.Println("main()下面的代码...")
}
go-函数和错误处理的更多相关文章
- jquery ajax 总是还未等到success回调就刷掉了,就进入了onError函数的错误案例分析
jquery ajax 总是还未等到success回调就刷掉了,就进入了onError函数的错误案例分析: 同样的请求同时请求了2次,然后第二次的请求把第一次的给刷掉了! (比如:<div on ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 6.全局防护Bypass之一些函数的错误使用
0x01 背景 PHP程序员在开发过程中难免会使用一些字符替换函数(str_replace).反转义函数(stripslashes),但这些函数使用位置不当就会绕过全局的防护造成SQL注入漏洞. 0x ...
- Linux kernel ‘lbs_debugfs_write’函数数字错误漏洞
漏洞名称: Linux kernel ‘lbs_debugfs_write’函数数字错误漏洞 CNNVD编号: CNNVD-201311-421 发布时间: 2013-11-29 更新时间: 2013 ...
- Linux kernel get_prng_bytes函数数字错误漏洞
漏洞名称: Linux kernel get_prng_bytes函数数字错误漏洞 CNNVD编号: CNNVD-201310-142 发布时间: 2013-10-11 更新时间: 2013-10-1 ...
- 类模板语法知识体系梳理(包含大量常犯错误demo,尤其滥用友元函数的错误)
demo 1 #include <iostream> #include <cstdio> using namespace std; //template <typenam ...
- OPENSSL问题,使用fsockopen()函数提示错误
环境配置 系统环境 CentOS7.2WDCP v3.2.2 lanmp PHP 多版本 指定使用5.6 OpenSSL 1.0.2h 3 May 2016 php.ini相关设置allow_url ...
- linux下使用gcc/g++编译代码时gets函数有错误
今天在linux中使用个g++编译一个名为myfirst.cpp的代码的时候,出现如下错误 myfirst.cpp: In function ‘int main()’:myfirst.cpp:11:2 ...
- C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论
今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类.这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中.子类只需注册需要并发执行的入口函数即可在单独线程 ...
- C++ socket bind() 函数绑定错误
VS2015编译错误: errorCxxxx: 'initializing' : cannot convert from 'std::_Bind<false,void,SOCKET&,s ...
- sprintf()函数可能发生的错误
接收到如下数据: GET http://app.tdvpn.com/heartbeat?mac=898607B81017AT+CIPSTATUS? &status=/ HTTP/1.1 Hos ...
随机推荐
- Ubuntu安装Node和npm
本文简单介绍在Ubuntu上安装最新版本的node和npm. 本次试验环境是Ubuntu 18.10. 安装nodejs root@ubuntu:~# cat /etc/issue Ubuntu 18 ...
- C# 模拟Windows键盘事件
发送键盘消息 [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true) ...
- Java操作数据库——使用JDBC连接数据库
Java操作数据库——使用JDBC连接数据库 摘要:本文主要学习了如何使用JDBC连接数据库. 背景 数据持久化 数据持久化就是把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应 ...
- linux脚本判断当前的linux版本是6还是7
#!/bin/sh version="release 7." release=$(cat /etc/redhat-release) echo $release result=$(e ...
- vue-cli 引用elementUI打包后文件过大
解决方案:使用externals引用第三方资源,防止element资源被打包到自己项目中,(总共修改3个页面index.html.webpack.base.conf.js.main.js) 1.修改i ...
- 利用Azure虚拟机安装Dynamics 365 Customer Engagement之九:新建组织
我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...
- MySQL复习值代码知识点(1)
MySQL复习值代码知识点 一. 创建数据库 create database 数据库名: 二. 删除数据库 drop database 数据库名: 三. 选择相应的数据库 use 数据库名: 四. 创 ...
- 模块二之序列化模块以及collections模块
模块二之序列化模块以及collections模块 一.序列化模块 json模块 ''' 序列化:将python或其他语言的数据类型转换成字符串类型 json模块: 是一个序列化模块. json: 是一 ...
- python--django for 循环中,获取序号
功能需求:在前端页面中,for循环id会构不成连续的顺序号,所以要找到一种伪列的方式来根据数据量定义序号 因此就用到了在前端页面中的一个字段 forloop.counter,完美解决 1 <tb ...
- bay——Oracle RAC集群体系结构.docx
Oracle RAC集群体系结构 ————bayaim 2018年10月22日13:33 https://blog.51cto.com/ixdba/862207 一. Oracle集群体系结构 O ...