asp.net网站开发代码公司网站代码模板
asp.net网站开发代码,公司网站代码模板,安装完wordpress主题,百度推广自己怎么做简介#xff1a; Go语言出自Ken Thompson、Rob Pike和Robert Griesemer之手#xff0c;起源于2007年#xff0c;并在2009年正式对外发布。Go的主要目标是“兼具Python等动态语言的开发速度和C/C等编译型语言的性能与安全性”#xff0c;旨在不损失应用程序性能的情况下降低…简介 Go语言出自Ken Thompson、Rob Pike和Robert Griesemer之手起源于2007年并在2009年正式对外发布。Go的主要目标是“兼具Python等动态语言的开发速度和C/C等编译型语言的性能与安全性”旨在不损失应用程序性能的情况下降低代码的复杂性具有“部署简单、并发性好、语言设计良好、执行性能好”等优势。 作者 | 赋行 来源 | 阿里技术公众号
前言
曾经我是一名以Java语言为主的开发者做过JavaWeb相关的开发后来转Android还是离不开Java直到转去做大前端了其实也就是一直在用JS写业务。如今由于个人发展原因来到阿里云由于项目需要就撸起了Go语言多年编程经验告诉我语言只是工具罢了重要的还是其思想与逻辑所以只需学学语法就好了于是我便三天入门Go期间主要用Java和JS来类比语法变化之大差点让我从入门到放弃了其实还真不是学习语法就好了呢其中包含了很多Go的设计理念。正所谓好记性不如敲烂键盘学过的东西还是要沉淀沉淀也可以分享出来一起探讨更有助于成长于是我就简单记录了一下我的Go语言入门学习笔记。
一 简介
Go语言出自Ken Thompson、Rob Pike和Robert Griesemer之手起源于2007年并在2009年正式对外发布其实都是Google的设计Go语言的初衷都是为了满足Google的需求。Go的主要目标是“兼具Python等动态语言的开发速度和C/C等编译型语言的性能与安全性”旨在不损失应用程序性能的情况下降低代码的复杂性具有“部署简单、并发性好、语言设计良好、执行性能好”等优势。最主要还是为了并发而生并发是基于goroutine的goroutine类似于线程但并非线程可以将goroutine理解为一种虚拟线程。Go语言运行时会参与调度goroutine并将goroutine合理地分配到每个CPU中最大限度地使用CPU性能。
二 环境
我们玩Java的时候需要下载JDK类似于此用Go开发也需要下载Go里面提供各种develop-kit、library以及编译器。在官网下载mac版本pkg后直接安装最后用 go version 命令验证版本 然后就是设置这两个环境变量mac系统是在 .bash_profile 文件里面
export GOROOT/usr/local/go
export GOPATH$HOME/go
GOROOT表示的是Go语言编译、工具、标准库等的安装路径其实就相当于配置JAVA_HOME那样。GOPATH这个和Java有点不一样Java里并不需要设置这个变量这个表示Go的工作目录是全局的当执行Go命令的时候会依赖这个目录相当于一个全局的workspace。一般还会把$GOPATH/bin设置到PATH目录这样编译过的代码就可以直接执行了。
1 纯文本开发
编写代码可以保存在任意地方例如新建一个helloworld目录创建hello.go文件
package main
import fmt
func main() { fmt.Println(hello, world)
}
然后执行 go build hello.go 就可以编译出hello文件在./hello就可以执行了或者直接 go run hello.go 合二为一去执行。执行这个命令并不需要设置环境变量就可以了。看起来和c差不多但是和Java不一样运行的时候不需要虚拟机。早期的GO工程也是使用Makefile来编译后来有了强大的命令 go build、go run可以直接识别目录还是文件。 2 GoLand
自动import超爽的体验不用按command /了
运行项目需要设置build config和Android、Java的都差不多例如创建一个hello-goland项目 导入go module项目的时候需要勾选这项否则无法像maven/gradle那样sync下载依赖 3 VSCODE
直接搜索Go插件第一个最多安装量的就是了我还没用过所以不太清楚如何。
三 工程结构
在设置GOPATH环境变量的时候这个目录里面又分了三个子目录bin、pkg、src分别用于存放可执行文件、包文件和源码文件。当我们执行Go命令的时候如果我们指定的不是当前目录的文件或者绝对路径的目录的话就会去GOPATH目录的去找。这样在GOPATH目录创建了xxx的目录后就可以在任意地方执行 go build xx 命令来构建或者运行了。
pkg目录应该是在执行 go install 后生成的包文件包括.a这样的文件相当于一个归档。
├── bin
│ ├── air
│ ├── govendor
│ ├── swag
│ └── wire
├── pkg
│ ├── darwin_amd64
│ ├── mod
│ └── sumdb
└── src├── calc├── gin-blog├── github.com├── golang.org├── google.golang.org├── gopkg.in└── simplemath
这样对于我们具体项目来说并不好没有Workspace的概念来隔离每个项目了所以我觉得这个GOPATH目录放的应该是公用的项目例如开源依赖的。我们在开发过程中也会下载很多的依赖这些依赖都下载到这个目录和我们的项目文件混在一起了。
另外通过IDE可以设置project的GOPATH相当于在执行的时候给GOPATH增加了一个目录变量也就是说我们创建一个项目然后里面也有bin、src、pkg这三个目录和GOPATH一样的本质上IDE在运行的时候其实就是设置了一下GOPATH
GOPATH/Users/fuxing/develop/testgo/calc-outside:/Users/fuxing/develop/go #gosetupGo语言在寻找变量、函数、类属性及方法的时候会先查看GOPATH这个系统环境变量然后根据该变量配置的路径列表依次去对应路径下的src目录下根据包名查找对应的目录如果对应目录存在则再到该目录下查找对应的变量、函数、类属性和方法。
其实官方提供了Go Modules的方法更好解决。
1 Go Modules
从Go 1.11版本开始官方提供了Go Modules管理项目和依赖从1.13版本开始更是默认开启了对Go Modules的支持使用Go Modules的好处是显而易见的 —— 不需要再依赖GOPATH你可以在任何位置创建Go项目并且在国内可以通过 GOPROXY 配置镜像源加速依赖包的下载。也就是说创建一个项目就是一个mod基本上目前Go开源项目都是这样做的。其实就是类似于Maven和Gradle。
// 创建mod项目也是可以用IDE来new一个mod项目的
go mod init calc-mod// 一般开源在github上面的项目名字是这样的和maven、gradle不一样的是开发完成根本不需要发布到仓库只要提交代码后打tag就可以了
go mod init github.com/fuxing-repo/fuxing-module-name// 创建一个模块执行这个命令主要是多了一个go.mod文件里面就一行内容
module calc-mod// import以后执行下载依赖命令不需要编辑go.mod文件。依赖会下载到GOPATH/pkg/mod目录
go list
用GoLand来打开不同的项目显示依赖的外部库是不一样的如果是用GOPATH创建的项目需要用命令下载依赖包到GOPATH
go get -u github.com/fuxing-repo/fuxing-module-name四 语法
1 包Package 和 Import
Java里面的包名一般是很长的和文件夹名称对应作用就是命名空间引入的时候需要写长长的一串也可以用通配符 Go里面一般的包名是当前的文件夹名称同一个项目里面可以存在同样的包名如果同时都需要引用同样包名的时候就可以用alias区分类似于JS那样。一般import的是一个包不像Java那样import具体的类。同一个包内不同文件但是里面的东西是可以使用的不需要import。这有点类似于C的include吧。如果多行的话用括号换行包起来。 Go语言中无论是变量、函数还是类属性及方法它们的可见性都是与包相关联的而不是类似Java那样类属性和方法的可见性封装在对应的类中然后通过 private、protected 和 public 这些关键字来描述其可见性Go语言没有这些关键字和变量和函数一样对应Go语言的自定义类来说属性和方法的可见性根据其首字母大小写来决定如果属性名或方法名首字母大写则可以在其他包中直接访问这些属性和方法否则只能在包内访问所以Go语言中的可见性都是包一级的而不是类一级的。
在Java里面只有静态或者对象就可以使用点运算符而且是极其常用的操作而在Go里面还可以用一个包名来点这就是结合了import来使用可以点出一个函数调用也可以点出一个结构体一个接口。另外区别于C不管是指针地址还是对象引用都是用点运算符不需要考虑用点还是箭头了 入口的package必须是main否则可以编译成功但是跑不起来
Compiled binary cannot be executed.原因就是找不到入口函数跟C和Java一样吧也需要main函数。
2 变量
用 var 关键字修饰类似于JS有多个变量的时候用括号 () 包起来默认是有初始化值的和Java一样。如果初始化的时候就赋值了那可以不需要 var 来修饰和Java不同的是变量类型在变量后面而不是前面不过需要 : 符号。最大的变化就是类型在变量后面语句可以省略分号 ;
var v1 int 10 // 方式一常规的初始化操作
var v2 10 // 方式二此时变量类型会被编译器自动推导出来
v3 : 10 // 方式三可以省略 var编译器可以自动推导出v3的类型//java
private HashMapString, UGCUserDetail mBlockInfo;
多重赋值
i, j j, i
可以实现变量交换有点像JS的对象析构但是其实不一样。有了这个能力函数是可以返回多个值了
匿名变量
用 _ 来表示作用就是可以避免创建定义一些无意义的变量还有就是不会分配内存。 指针变量
和C语言一样的回想一下交换值的例子即可到底传值和传址作为参数的区别是啥。
Go语言之所以引入指针类型主要基于两点考虑一个是为程序员提供操作变量对应内存数据结构的能力另一个是为了提高程序的性能指针可以直接指向某个变量值的内存地址可以极大节省内存空间操作效率也更高这在系统编程、操作系统或者网络应用中是不容忽视的因素。
指针在Go语言中有两个使用场景类型指针和数组切片。
作为类型指针时允许对这个指针类型的数据进行修改指向其它内存地址传递数据时如果使用指针则无须拷贝数据从而节省内存空间此外和C语言中的指针不同Go语言中的类型指针不能进行偏移和运算因此更为安全。
变量类型
Go语言内置对以下这些基本数据类型的支持
布尔类型bool整型int8、byte、int16、int、uint、uintptr 等浮点类型float32、float64复数类型complex64、complex128字符串string字符类型rune本质上是uint32错误类型error
此外Go语言也支持以下这些复合类型
指针pointer数组array切片slice字典map通道chan结构体struct接口interface
还有const常量iota这个预定义常量用来定义枚举。可以被认为是一个可被编译器修改的常量在每一个const关键字出现时被重置为0然后在下一个const出现之前每出现一次iota其所代表的数字会自动增1。
const (Sunday iota Monday Tuesday Wednesday Thursday Friday Saturday numberOfDays
)
类型强转
v1 : 99.99
v2 : int(v1) // v2 99v1 : []byte{h, e, l, l, o}
v2 : string(v1) // v2 hello//字符相关的转化一般用strconv包
v1 : 100
v2, err : strconv.Atoi(v1) // 将字符串转化为整型v2 100v3 : 100
v4 : strconv.Itoa(v3) // 将整型转化为字符串, v4 100//结构体类型转换
//类型断言
//x.(T) 其实就是判断 T 是否实现了 x 接口如果实现了就把 x 接口类型具体化为 T 类型
claims, ok : tokenClaims.Claims.(*jwt.StandardClaims)
数组与切片
//定义数组
var a [8]byte // 长度为8的数组每个元素为一个字节
var b [3][3]int // 二维数组9宫格
var c [3][3][3]float64 // 三维数组立体的9宫格
var d [3]int{1, 2, 3} // 声明时初始化
var e new([3]string) // 通过 new 初始化
var f make([]string, 3) // 通过 make初始化//初始化
a : [5]int{1,2,3,4,5}
b : [...]int{1, 2, 3}//切片
b : []int{} //数组切片slice就是一个可变长数组
c : a[1:3] // 有点类似于subString或者js.slice
d : make([]int, 5) //make相当于new、alloc用来分配内存//数组的长度
length : len(a)//添加一个元素
b append(b, 4)
字典
其实就是Java里的map使用上语法有很多不同。
var testMap map[string]int
testMap map[string]int{one: 1,two: 2,three: 3,
}
//还可以这样初始化
var testMap make(map[string]int) //map[string]int{}
testMap[one] 1
testMap[two] 2
testMap[three] 3
make和new
// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, makes return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
// Slice: The size specifies the length. The capacity of the slice is
// equal to its length. A second integer argument may be provided to
// specify a different capacity; it must be no smaller than the
// length. For example, make([]int, 0, 10) allocates an underlying array
// of size 10 and returns a slice of length 0 and capacity 10 that is
// backed by this underlying array.
// Map: An empty map is allocated with enough space to hold the
// specified number of elements. The size may be omitted, in which case
// a small starting size is allocated.
// Channel: The channels buffer is initialized with the specified
// buffer capacity. If zero, or the size is omitted, the channel is
// unbuffered.
func make(t Type, size ...IntegerType) Type// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
区别就是返回值和参数不同一个是值一个是指针slice、chan、map只能用make本身就是指针。其他make、new都行。
神奇的nil
Java里面用null比较舒服直接就判空了除了在string类型的时候还要判断字符为 但是Go里面的string要判断为空就简单一点不能判断nil只能判断 。然而Go里面的nil却和null不一样其实是和JS里面 、 很像。
nil也是有类型的。
func Foo() error {var err *os.PathError nil// …return err //实际返回的是[nil, *os.PathError]//return nil //正确的方式是直接return nil 实际返回的是[nil, nil]
}func main() {err : Foo()fmt.Println(err) // nilfmt.Println(err nil) // falsefmt.Println(err (*os.PathError)(nil)) //true
}
根对象Object
在Java里面如果不用多态没有接口父类超类的话就用Object作为根对象在Go里面如果函数参数不知道用什么类型通常会用 interface{}这是个空接口表示任意类型因为不是弱类型语言没有any类型也不是强面向对象语言没有Object所以就有这个空接口的出现。 3 语句
比较大的一个特点就是能不用括号的地方都不用了。
控制流程
if语句的判断条件都没有了括号包起来还可以前置写变量初始化语句类似于for循环左花括号 { 必须与 if 或者 else 处于同一行。 switch语句变得更强大了有这些变化
switch关键字后面可以不跟变量这样case后面就必须跟条件表达式其实本质上就是美化了if-else-if。如果switch后面跟变量case也变得强大了可以出现多个结果选项通过逗号分隔。swtich后面还可以跟一个函数。不需要用break来明确退出一个case如果要穿透执行一层可以用 fallthrough 关键字。
score : 100
switch score {
case 90, 100:fmt.Println(Grade: A)
case 80:fmt.Println(Grade: B)
case 70:fmt.Println(Grade: C)
case 60:
case 65:fmt.Println(Grade: D)
default:fmt.Println(Grade: F)
}s : hello
switch {
case s hello:fmt.Println(hello)fallthrough
case s xxxx:fmt.Println(xxxx)
case s ! world:fmt.Println(world)
}
//outputhello xxxx
循环流程
去掉了 while、repeat 这些关键字了只保留了 for 这个关键字其实用起来差不多。break , continue 这些关键字还是有的。
//通用的用法
for i : 1; i 5; i {fmt.Println(i)
}//类似于while的用法
a : 1
for a 5 {fmt.Println(a)a
}//死循环
for {// do something
}
for ;; {// do something
}//类似java for-each的用法
listArray : [...]string{xiaobi, xiaoda, xiaoji}
for index, item : range listArray {fmt.Printf(hello, %d, %s\n, index, item)
}
//java
for (String item : someList) {System.out.println(item);
}
跳转流程
Go很神奇的保留了一直被放弃的goto语句记得是Basic、Pascal那些语言才会有不知道为啥。
i : 1
flag:
for i 10 {if i%2 1 {igoto flag}fmt.Println(i)i
}
defer流程有点像Java里面的finally保证了一定能执行我感觉底层也是goto的实现吧。在后面跟一个函数的调用就能实现将这个xxx函数的调用延迟到当前函数执行完后再执行。
这是压栈的变量快照实现。
func printName(name string) {fmt.Println(name)
}func main() {name : godefer printName(name) // output: goname pythondefer printName(name) // output: pythonname javaprintName(name) // output: java
}//output:
java
python
go//defer后于return执行
var name string go
func myfunc() string {defer func() {name python}()fmt.Printf(myfunc 函数里的name%s\n, name)return name
}
func main() {myname : myfunc()fmt.Printf(main 函数里的name: %s\n, name)fmt.Println(main 函数里的myname: , myname)
}//output:
myfunc 函数里的namego
main 函数里的name: python
main 函数里的myname: go
4 函数
关键字是 funcJava则完全没有 function 关键字而是用 public、void 等等这样的关键字JS也可以用箭头函数来去掉 function 关键字了。函数的花括号强制要求在首行的末尾。可以返回多个值返回值的类型定义在参数后面了而不是一开始定义函数就需要写上跟定义变量一样参数的类型定义也是一样在后面的如果相同则保留最右边的类型其他省略。可以显式声明了返回值就可以了必须每个返回值都显式就可以省略 return 变量。
//一个返回值
func GetEventHandleMsg(code int) string {msg, ok : EventHandleMsgMaps[code]if ok {return msg}return
}
//多个返回值
func GetEventHandleMsg(code int) (string, error) {msg, ok : EventHandleMsgMaps[code]if ok {return msg, nil}return , nil
}
//不显式return变量值
func GetEventHandleMsg(code int) (msg string, e error) {var ok boolmsg, ok EventHandleMsgMaps[code]if ok {//do somethingreturn}return
}
匿名函数和闭包
在Java里面的实现一般是内部类、匿名对象不能通过方法传递函数作为参数只能传一个对象实现接口。
Go则和JS一样方便可以传递函数定义匿名函数。
//传递匿名函数
func main() {i : 10add : func (a, b int) {fmt.Printf(Variable i from main func: %d\n, i)fmt.Printf(The sum of %d and %d is: %d\n, a, b, ab)}callback(1, add);
}
func callback(x int, f func(int, int)) {f(x, 2)
}//return 匿名函数
func main() {f : addfunc(1)fmt.Println(f(2))
}
func addfunc(a int) func(b int) int {return func(b int) int {return a b}
}
不定参数
和Java类似不同的是在调用是也需要用 ... 来标识。
//定义
func SkipHandler(c *gin.Context, skippers ...SkipperFunc) bool {for _, skipper : range skippers {if skipper(c) {return true}}return false
}//调用
middlewares.SkipHandler(c, skippers...)
五 面向对象
在C语言里面经常会有用到别名的用法可以用 type 类起一个别名很常用特别是在看源码的时候经常出现
type Integer int1 类
没有 class 的定义Go里面的类是用结构体来定义的。
type Student struct {id uintname stringmale boolscore float64
}//没有构造函数但是可以用函数来创建实例对象并且可以指定字段初始化类似于Java里面的静态工厂方法
func NewStudent(id uint, name string, male bool, score float64) *Student {return Student{id, name, male, score}
}func NewStudent2(id uint, name string, male bool, score float64) Student {return Student{id, name, male, score}
}
2 成员方法
定义类的成员函数方法比较隐式方向是反的不是声明这个类有哪些成员方法而是声明这个函数是属于哪个类的。声明语法就是在 func 关键字之后函数名之前注意不要把Java的返回值定义给混淆了
//这种声明方式和C一样的这个就是不是普通函数了而是成员函数。
//注意到的是两个方法一个声明的是地址一个声明的是结构体两个都能直接通过点操作。
func (s Student) GetName() string {return s.name
}
func (s *Student) SetName(name string) {s.name name
}//使用
func main() {//a是指针类型a : NewStudent(1, aa, false, 45)a.SetName(aaa)fmt.Printf(a name:%s\n, a.GetName())b : NewStudent2(2, bb, false, 55)b.SetName(bbb)fmt.Printf(b name:%s\n, b.GetName())
}//如果SetName方法和GetName方法归属于Student而不是*Student的话那么修改名字就会不成功
//本质上声明成员函数就是在非函数参数的地方来传递对象、指针、或者说是引用也就是变相传递this指针
//所以才会出现修改名字不成功的case
3 继承
没有 extend 关键字也就没有了继承只能通过组合的方式来实现。组合就解决了多继承问题而且多继承的顺序不同内存结构也不同。
type Animal struct {name string
}
func (a Animal) FavorFood() string {return FavorFood...
}
func (a Animal) Call() string {return Voice...
}
type Dog struct {Animal
}
func (d Dog) Call() string {return 汪汪汪
}//第二种方式在初始化就需要指定地址其他都没变化
type Dog2 struct { *Animal
}
func test() {d1 : Dog{}d1.name mydogd2 : Dog2{}d2.name mydog2//结构体是值类型如果传入值变量的话实际上传入的是结构体值的副本对内存耗费更大//所以传入指针性能更好a : Animal{ddog}d3 : Dog{a}d4 : Dog2{a}
}
这种语法并不是像Java里面的组合使用成员变量而是直接引用Animal并没有定义变量名称当然也是可以的不过没必要了然后就可以访问Animal中的所有属性和方法如果两个类不在同一个包中只能访问父类中首字母大写的公共属性和方法还可以实现方法重写。
4 接口
Java的接口是侵入式的指的是实现类必须明确声明自己实现了某个接口。带来的问题就是如果接口改了实现类都必须改所以以前总是会有一个抽象类在中间。
//定义接口
type Phone interface {call()
}
//实现接口
type IPhone struct {name string
}
func (phone IPhone) call() {fmt.Println(Iphone calling.)
}
Go的接口是非侵入式的因为类与接口的实现关系不是通过显式声明而是系统根据两者的方法集合进行判断。一个类必须实现接口所有的方法才算是实现了这个接口。接口之间的继承和类的继承一样通过组合实现多态的实现逻辑是一样的如果接口A的方法列表是接口B的方法列表的子集那么接口B可以赋值给接口A。
六 并发编程
目前并发编程方面还没学习多少就简单从网上摘了这一个经典的生产者消费者模型例子来初步感受一下后续深入学习过后再进行分享。 // 数据生产者
func producer(header string, channel chan- string) {// 无限循环, 不停地生产数据for {// 将随机数和字符串格式化为字符串发送给通道channel - fmt.Sprintf(%s: %v, header, rand.Int31())// 等待1秒time.Sleep(time.Second)}
}
// 数据消费者
func customer(channel -chan string) {// 不停地获取数据for {// 从通道中取出数据, 此处会阻塞直到信道中返回数据message : -channel// 打印数据fmt.Println(message)}
}
func main() {// 创建一个字符串类型的通道channel : make(chan string)// 创建producer()函数的并发goroutinego producer(cat, channel)go producer(dog, channel)// 数据消费函数customer(channel)
}//output:
dog: 1298498081
cat: 2019727887
cat: 1427131847
dog: 939984059
dog: 1474941318
cat: 911902081
cat: 140954425
dog: 336122540
七 总结
这只是一个简单入门其实Go还有很多很多东西我没有去涉及的例如context、try-catch、并发相关如锁等、Web开发相关的、数据库相关的。以此贴开始后续继续学习Go语言分享。
原文链接 本文为阿里云原创内容未经允许不得转载。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/86539.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!