Go语言是二十一世纪的C语言,具有接近C/C++等编译语言的性能,又像脚本语言(PHP,Javascript,Python)一样容易上手学习,背靠Google,有强大的开发团队和活跃的社区。
Go语言的特性
- 语法简单
- 语言层面支持并发编程
- 跨平台编译
- 自带垃圾回收机制
Go语言语句不需要加分号
package main
import "fmt"
func main(){
s := "hello world"
fmt.Println(s)
}
复制代码
数据类型
Go是强类型语言,声明变量时,需要指明变量是什么类型
var i int = 10
b := 10//错误
func Test(){
s := 10
s := "test"//错误
}
复制代码
PHP是弱类型编程语言,声明变量时,可以不指定变量的类型,由语言自己推导,但在之后该变量仍然可以被赋于其他类型的值
$a = 10;
$a = "test";
复制代码
for、if和switch
for
语句、if
语句和switch
语句的条件体都不需要加括号()
for i := 0; i < 10; i++ {
}
i := 10
if i == 10 {
}
x := 10
switch x{
}
复制代码
switch
- PHP中的switch语句
$a = 10;
switch ($a) {
case 10:
echo "111";
break;
case 20:
echo "222";
break;
}
复制代码
Go的switch语句
func main() {
var level string = "B"
var score int = 90
switch score {
case 90: level = "A"
case 80: level = "B"
case 50,60,70 : level = "C"
default: level = "D"
}
switch {
case level == "A" :
fmt.Printf("优秀!\n" )
case level == "B", level == "C" :
fmt.Printf("良好\n" )
case level == "D" :
fmt.Printf("及格\n" )
case level == "F":
fmt.Printf("不及格\n" )
default:
fmt.Printf("差\n" );
}
fmt.Printf("你的成绩为: %s\n", level );
}
复制代码
- fallthrough关键字
package main
import "fmt"
func main() {
switch {
case false:
fmt.Println("1、case 条件语句为 false")
fallthrough
case true:
fmt.Println("2、case 条件语句为 true")
fallthrough
case false:
fmt.Println("3、case 条件语句为 false")
fallthrough
case true:
fmt.Println("4、case 条件语句为 true")
case false:
fmt.Println("5、case 条件语句为 false")
fallthrough
default:
fmt.Println("6、默认 case")
}
}
复制代码
循环
Go语言不像其他编程一样,支持while
或do...while
语句,也不像PHP一样有foreach
,Go的循环只有for
语句
- 普通循环
for i := 0; i < 10; i++ {
}
复制代码
- 遍历数组(array)、map或者切片(slice)
for k,v := range arr {
}
复制代码
变量可见性
在PHP中,如果不使用类,并没有什么变量可见性,声明的都是全局变量:
a.php
$a = 10;
复制代码
b.php
include_once("a.php");
echo $a;//输出10
复制代码
如果使用类,类的成员,由于public,protected,private 这三个来区分成员的可见性。
class A{
public $a;
private $b;
protected $c;
}
class B extends A{
}
复制代码
而Go语言以包来组织代码的,变量的可见性非常简单,以首字母是否大写包内可见和全部可见
a.go
package my
var Username string = "test"
var age int = 20
const Test = "test"
const t = 123
复制代码
b.go
package my
func GetUsername() string {
return Username
}
func GetAge()int{
return getAge()
}
func getAge()int{
return a
}
复制代码
main.go
package main
import "my"
import "fmt"
func main(){
fmt.Println(my.Username)
fmt.Println(my.GetUsername())
fmt.Println(my.GetAge())
fmt.Println(my.Test)
fmt.Println(my.age)//错误
fmt.Println(my.getAge())//错误
fmt.Println(my.t)//错误
}
复制代码
是否需要编译
Go静态编译型语言,PHP动态脚本语言
由于脚本语言需要由解释器解释执行,所以执行的性能比编译型语言差。
函数返回值
PHP语言与大多数据编程语言一样,函数或方法只支持一个返回值,而Go语言的函数直接多返回值。
func GetFile(path string)(file *os.File,err error){
f,err := os.Open(path)
return f,err
}
复制代码
空白标识符
由于声明变量后不使用会无法通过编译,因为当我们从某个函数接收多个值,但后面又不需要时,可以使用空白标志符。
func GetFile(path string) *os.File{
file,_ := os.Open(path)
return file
}
复制代码
字符双引号
Go语言的字符串类型不支持单引号,而PHP中,单引号或双引号都可以,如:
- PHP字符串
$a = '';
$b = ""
复制代码
- Go字符串
var s string = "" //必须使用双引号
复制代码
与Docker的结合
Go程序最终是打包为一个二进制文件,这个文件一般都很小,最多也就是几十M,而我们把可以直接把这个二制文件打包进Docker的镜像中。
但是PHP的项目运行离不开vendor这个存放所有第三方库的文件,这个文件可能很大,可能有几百M,所以使用docker打包PHP项目,镜像会变得非常大。
奇葩的日期格式化
- PHP时间与日期格式化
$t = time();
//Y:年份,m:月份,d:日期,H:时,i:分钟,s:秒
echo date("Y-m-d H:i:s",$t);
复制代码
- Go语言时间与日期格式化
//2006:年份,01:月份,02:日期,15/03:时,04:分钟,05:秒
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
fmt.Println(time.Now().Format("Jan"))//月份
fmt.Println(time.Now().Format("January"))//月份
fmt.Println(time.Now().Format("Monday"))//星期
fmt.Println(time.Now().Format("Mon"))//星期
复制代码
异常捕获机制
PHP像其他编程语言一样支持try...catch语句,用于捕获程序中的异常,如:
try{
主要逻辑代码
}catch(Exception $ex){
异常处理
}
复制代码
Go语言没有try...catch语句,Go语言的开发建议是程序员在开发的时候,返回error类型
func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("数据库连接错误")
}
//主要逻辑代码
}
复制代码
如果实在是一定要抛出异常,不让程序继续执行,可以使用panic()函数,然后通过recover捕捉。
func Parse(input string) (s *Syntax, err error) {
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("internal error: %v", p)
}
}()
// ...parser...
panic("报错啦")
}
复制代码
defer
Go语言的原创,用于函数执行完成后释放资源等后续操作
func main() {
f := GetFile("./1.txt")
defer f.Close()
f.Write([]byte("test"))
}
func GetFile(path string) *os.File {
file, _ := os.Open(path)
return file
}
复制代码
并发
Go从语言层面支持并发,使用go
关键字就可以简单直接创建一个goroutine,也就是协程,协程可以理解为一种轻量级的线程。
func main(){
go func(){
fmt.Println("hello world")
}()
}
复制代码
-
协程:大小开始只有2KB,可以动态调整,最大可以到达1GB。
-
线程:固定要2M。
因此Go语言在一台机器创建成千上万的协程,支持高并发。
协程通讯有两种方式,共享内存和channel
面向对象
Go语言不是传统的面向对象编程语言,不支持类或者对象,但可以通过结构体来模拟一个类
type Person struct {
name string //小写:不可导出
Age int //大写,可导出
}
复制代码
与类不同的是,结构体不支持继承,与继承与相比,更推荐Go开发者使用结构体组合
type Student struct{
p Person
number string
}
复制代码
func main() {
s := Student{
p: Person{
name: "test",
Age: 10,
},
number: "123333",
}
fmt.Println(s.number)
fmt.Println(s.p.name)
}
复制代码
匿名嵌入
type Student struct {
Person
number string
}
func main() {
s := Student{
Person: Person{
name: "test",
Age: 10,
},
number: "123333",
}
fmt.Println(s.number)
fmt.Println(s.name)
}
复制代码
结构体也可以像类一样定义自己的方法
func (s *Student) Say(){
fmt.Println("My name is:"+s.name)
}
func (s *Student) getName() string{
return s.name
}
复制代码
接口
Go与PHP一样也支持接口,但不同的是,在Go语言中,并不是需要显性实现某个接口.
- PHP实现某个接口
interface PosterInterface
{
public function getData();
}
class ActivityPosterData implements PosterInterface
{
public function getData(){
//实现接口
}
}
复制代码
- Go实现某个接口
type Reader interface{
Read(p []byte) (n int, err error)
}
type File struct{
}
func (f *File) Read(p []byte) (n int, err error){
return 10,nil
}
func main() {
var r Reader
r = &File{}
n, err := r.Read([]byte("test"))
if err != nil {
panic(err)
}
fmt.Println(n)//输出:10
}
复制代码
自定义类型
Go支持在已有的类型上定义新的类型
package main
import (
"fmt"
)
type cm int
func (c cm) Format() string {
return fmt.Sprintf("%d厘米", c)
}
func (c cm) Read(p []byte) (n int, err error){
return 20,nil
}
func main() {
var r Reader
var i cm = 10
fmt.Println(i.Format()) //输出:10厘米
r = i
m, err := r.Read([]byte("cm"))
if err != nil {
panic(err)
}
fmt.Println(m) //输出:20
}
复制代码
项目管理
在PHP中我们使用composer管理项目的依赖包,composer也是社区开发,并不是PHP官方提供的,而Go语言从诞生之初就提供了非常完善的项目管理工具。
工具链
$ go build . //编译
$ go run main.go //直接运行
$ go get github.com/spf13/viper //获取第三方包
$ go fmt //格式化
$ go test //单元测试或性能测试
$ go env //打印Go的环境变量
复制代码
Go Module
//初始化
go mod init jinhongtl/lottery
//整理模块
go mod tidy
//下载依赖包
go mod download
复制代码
风格
- 大括号问题
函数,方法,if,switch,for,在Go语言中任何使用大插号的地方,大括号都不能单独成一行
//正确
func a(){
}
//错误,无法通过编译
func b()
{
}
//正确
for i:= 0;i< 10;i++{
}
复制代码
- 命名问题
- 变量命名使用骆峰式命名
Username
username
复制代码
- 包命名建议使用小写字母,并且与目录同名
Web开发方面的区别
框架
PHP有大而全的框架,如Yii,Laravel,这些框架已经帮我们开发了很多直接使用的功能,比较日志打印,数据库ORM操作,队列,Redis读取等功能
在Go语言中,虽然Web开发方面也有类似的框架,但更常用的是根据自己的需求定制开发,比如,我只是简单开发一个Web应用,读取数据库,那么只需要引用相应的第三方库就可以了。
库名 | 作用 |
---|---|
github.com/spf13/viper | 配置读取 |
github.com/spf13/cobra | 命令行框架 |
github.com/spf13/pflag | 命令行参数 |
github.com/gin-gonic/gin | Web框架 |
github.com/go-playground/validator | 数据校验 |
gorm.io/gorm | 数据库ORM |
github.com/robfig/cron | 定时任务 |
github.com/sirupsen/logrus | 日志 |
go.uber.org/zap | 日志 |
github.com/gorilla/websocket | websocket |
github.com/go-redis/redis | Redis |
方式
Go语言只需要几句代码就可以直接创建一个Web服务器,接收HTTP请求,如:
package main
import "net/http"
func main() {
http.HandleFunc("/test", func(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("test"))
})
http.ListenAndServe(":8088", nil)
}
复制代码
而PHP如果不与nginx或apache配置,单独是无法提供HTTP服务的,PHP的工作方式如下所示:
推荐目录
如果我们要用Go开发自己的应用,推荐下面这个社区用得比较多的目录结构,在此结构的基础,我们可以引用自己需要的第三方库,来定制自己的项目。
├── README.md
├── api
├── assets
├── build
├── cmd
│ ├── apiserver
│ │ └── main.go
│ └── apiauth
│ └── main.go
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── init
├── internal
│ ├── guess
│ ├── lottery
│ └── pkg
├── pkg
├── scripts
├── test
├── third_party
├── tools
├── vendor
├── web
└── website
复制代码
小结
与PHP相比,Go语言开发的程序具有更高性能,可以支持大量的并发,Go语言的代码也有统一的风格,由于完善的工具链,更易进行项目管理,同时,Go语言社区有大量功能单一但很强大的第三方,使得我们可以根据自己的需要,按需使用第三方库组装自己的项目。
作者:程序员读书
链接:https://juejin.cn/post/7055217931344429093