Wind Mt


  • 首页

  • 标签

  • 归档

  • 搜索

Java 中 Map 按值排序

发表于 2016-02-10 | 更新于 2017-10-12 |

在 Java 中对一个 Map 按 Key 排序是很简单的一件事(TreeMap)。但是按 Value 排序的话,却略显麻烦。

在 Java 8 之前,对 Map 按 Value 排序一般有两种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 1. TreeMap
public class Testing {

public static void main(String[] args) {
HashMap<String, Integer> unsortMap = new HashMap<>();
ValueComparator comparator = new ValueComparator(unsortMap);
TreeMap<String, Integer> sortedMap = new TreeMap<>(comparator);

// put {A=9, B=2, C=7, D=1} into unsortMap

System.out.println("unsorted map: " + unsortMap);
sortedMap.putAll(unsortMap);
System.out.println("sorted map: " + sortedMap);
}
}

class ValueComparator implements Comparator<String> {

Map<String, Integer> base;

public ValueComparator(Map<String, Integer> base) {
this.base = base;
}

// this comparator imposes orderings that are inconsistent with equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return 1;
} else {
return -1;
} // returning 0 would merge keys
}
}
// output:
// unsorted map: {A=9, B=2, C=7, D=1}
// sorted map: {D=1, B=2, C=7, A=9}
阅读全文 »

ELK 部署笔记

发表于 2016-02-09 | 更新于 2017-07-19 |

ELK 是 Elasticsearch、Logstash、Kibana 三个开源软件的组合。在实时数据检索和分析场合,三者通常是配合共用,而且又都先后归于 Elastic.co 公司名下,故有此简称。

ELK 在最近两年迅速崛起,成为机器数据分析,或者说实时日志处理领域,开源界的第一选择。和传统的日志处理方案相比,ELK 具有如下几个优点:

  • 处理方式灵活。Elasticsearch 是实时全文索引,不需要像 Storm 那样预先编程才能使用;
  • 配置简易上手。Elasticsearch 全部采用 JSON 接口,Logstash 是 Ruby DSL 设计,都是目前业界最通用的配置语法设计;
  • 检索性能高效。虽然每次查询都是实时计算,但是优秀的设计和实现基本可以达到全天数据查询的秒级响应;
  • 集群线性扩展。不管是 Elasticsearch 集群还是 Logstash 集群都是可以线性扩展的;
  • 前端操作炫丽。Kibana 界面上,只需要点击鼠标,就可以完成搜索、聚合功能,生成炫丽的仪表板。
阅读全文 »

Java 中生成二维码(带 logo)

发表于 2016-02-06 | 更新于 2017-10-12 |

项目中涉及到生成二维码(像微信那种的),就用 ZXing 并混合 Java 的图形库搞了一个工具类。

奉上效果图一张
qrcode-1

废话不多说,直接上代码:

阅读全文 »

在 Java 项目中使用 ECharts

发表于 2016-02-04 | 更新于 2017-07-18 |

项目后台要加一个统计,产品只要求能看到数据就行了,但是做完后我发现只有文本不够直观,就想着用图表的形式的展现一下(顺便温习一下曾经用过的 JFreeChart XD)。JFreeChat 虽好,但也有些年头了,就去网上搜了搜有没有什么更好的图表生成的类库。

当在8 个华丽而实用的 Java 图表应用看到 ECharts 的时候,发现 JFreeChart 跟它比就不是一个级别的呀!至于开头说的温习嘛,就缓缓吧,先看一下 ECharts。

先看一下用 ECharts 做的柱状图:

echarts-bar

阅读全文 »

Let's Encrypt 试用手记

发表于 2016-02-03 | 更新于 2017-07-18 |

Let’s Encrypt 简介

Let’s Encrypt 是一个新的数字证书认证机构,它通过自动化的过程消除创建和安装证书的复杂性,为网站提供免费的 SSL/TLS 证书。Let’s Encrypt 项目获得了 Mozilla、思科、Akamai、IdenTrust 和 EFF 等组织的支持,由 Linux 基金会托管。它在9月中旬颁发了第一个证书,已经从12月3日起开放了 Beta 测试,它已经得了 IdenTrust 的交叉签名,这意味着其证书现在已经可以被所有主流的浏览器所信任。
你现在可以在此体验一下使用新的交叉签名的中级证书所签发证书的服务器。

letsencrypt-helloworld

阅读全文 »

JSON库之性能比较:JSON.simple VS GSON VS Jackson VS JSONP

发表于 2016-02-03 | 更新于 2017-08-02 |

Java中哪个JSON库的解析速度是最快的?

JSON已经成为当前服务器与WEB应用之间数据传输的公认标准,不过正如许多我们所习以为常的事情一样,你会觉得这是理所当然的便不再深入思考了。我们很少会去想用到的这些JSON库到底有什么不同,但事实上它们的确是不太一样的。因此,我们运行了一个基准测试来对常用的几个JSON库进行了测试,看看在解析不同大小的文件时哪个库的速度是最快的。下面我会把结果分享给大家。

阅读全文 »

管理多个 Supervisor——集群管理

发表于 2016-02-02 | 更新于 2017-07-23 |

继前两篇文章(Supervisor 基础、监控并报警)后,用 Supervisor 管理单个机器上的进程没啥问题了。但是 A 机器上的 Supervisor 只能管理 A 机器上的进程,管理不了 B 上边的。在当今这个云的时代,大家都玩集群了,如果用其管理的话,需要打开 N 个网页去各各机器上的进程显然是不现实的(OP 们要哭晕在厕所了)。
其实之前仔细看配置文件的话,应该会想到一种解决的办法——通过 XML-RPC 接口。

nodervisor-dashboard

阅读全文 »

利用 Supervisor 的 Event & Listener 监控进程并报警

发表于 2016-02-02 | 更新于 2017-07-19 |

接触 Supervisor 还是学 Golang 的时候,用于把 Golang 应用的非守护进程转化为守护进程。不过这次到是要把 Supervisor 用到 Java 进程上,具体的操作和 Golang 应用的进程并无差别,毕竟 Supervisor 只要求被管理的进程是非守护进程即可。

阅读全文 »

Supervisor 基础

发表于 2016-02-02 | 更新于 2017-07-19 |

Supervisor

Supervisor 的官方标语是

A Process Control System

它是用 Python 实现的,不过它能管理任何非 Daemon 的进程,并会帮你把被管理的进程转成 Daemon 进程(只要你交给 Supervisor 的进程是非 daemon 的,无需在原程序中增加任何用于实现 daemon 的代码,就能实现 daemon)。

是不是感觉很方便?废话不多说,直接动手搞起来。

安装

安装 Supervisor 的方式多种多样,选择自己喜欢的方式吧

1
2
3
4
5
# easy_install supervisor        // yum install python-setuptools
# apt-cache show supervisor // ubuntu
# yum info supervisor // redhat centos
# pip install supervisor --pre // pip >= 1.4
# pip install supervisor // pip < 1.4

还可以直接去官网下载后,python setup.py install

阅读全文 »

rsync 命令详解

发表于 2016-02-01 | 更新于 2017-07-18 |

什么是Rsync

Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件。Rsync使用所谓的“Rsync算法”来使本地和远 程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。

Rsync本来是用于替代rcp的一个工具,目前由rsync.samba.org维护,所以rsync.conf文件的格式类似于Samba的 主配置文件。Rsync可以通过rsh或ssh使用,也能以daemon模式去运行,在以daemon方式运行时Rsync server会打开一个873端口,等待客户端去连接。连接时,Rsync server会检查口令是否相符,若通过口令查核,则可以开始进行文件传输。第一次连通完成时,会把整份文件传输一次,以后则就只需进行增量备份。

Rsync支持大多数的类Unix系统,无论是Linux、Solaris还是BSD上都经过了良好的测试。此外,它在windows平台下也有相应的版本,如cwRsync和Sync2NAS等工具。

Rsync的基本特点如下:

  1. 可以镜像保存整个目录树和文件系统;
  2. 可以很容易做到保持原来文件的权限、时间、软硬链接等;
  3. 无须特殊权限即可安装;
  4. 优化的流程,文件传输效率高;
  5. 可以使用rsh、ssh等方式来传输文件,当然也可以通过直接的socket连接;
  6. 支持匿名传输。
阅读全文 »

Golang 学习笔记——变量的重声明问题

发表于 2016-01-14 | 更新于 2018-04-02 |

今天写 Golang 代码时遇到重复声明同名变量的问题,发现这个还是挺有意思的。

这里说的重复声明不是指这种

1
2
3
x := 1
x := 2
fmt.Println(x)

这样的话肯定是不行的,会报错

1
no new variables on left side of :=

而是指像下边这样的,这个是可以正常运行的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// code #1
x := 1
fmt.Println(x, &x)
x, y := 2, "???"
fmt.Println(x, &x, y)

// code #2
if x, y := 3, "!!!"; true {
fmt.Println(x, &x, y)
x := 4
fmt.Println(x, &x, y)
x, y := 5, "@@@"
fmt.Println(x, &x, y)
}
fmt.Println(x, &x, y)

阅读全文 »

Golang 学习笔记——交叉编译 & 部署

发表于 2016-01-12 | 更新于 2017-07-18 |

用 Golang 写了一个小功能,想部署到的服务器上。这里就简单记录一下过程吧。
我现在用的 Go 的版本是1.5.1,直接用安装包安装的,Mac OS X 平台。

交叉编译

Golang 的交叉编译还是蛮方便的。
首先用go env查看一下本机的 go 环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/yibo/golang/lib:/Users/yibo/golang/work"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT=""
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"

阅读全文 »

Docker 1.9 初体验

发表于 2016-01-10 | 更新于 2017-07-18 |

Docker 颠覆了容器技术,也将容器技术带到了新的高度。对于开发和运维人员(特别是运维),Docker 真是省了不少事儿。就我做开发的来说,比如前几天 mysql 5.7 GA 了,我想体验一下的其 Json 类型的支持,大可不必去下载安装包再在本机上安装(可能之前已经安装了较早版本的 mysql,还得考虑升级),用 docker 两个命令就可以愉快地玩耍了——这只是一个我个人使用中的小例子。Docker 对于部署、对于应用服务的横向扩展开说,都有很大的优势。

Docker 1.9(好像是从1.8的就开始了)没了 Boot2Docker,取而代之的是 Docker Machine。有一段时间没玩 Docker 了,更新个新版试试。不过也没感觉到什么区别,除了命令变了,能创建多个 VM,本质还是需要安装一个 VirtualBox 从而将 Docker 运行于 Linux 虚拟机内——毕竟 docker 还是得通过 linux 内核的 namespace 和 cgroups 机制实现资源隔离及限制。

本文系个人笔记,只做记录,不做详解。Docker 学习可以参考官方文档,或Docker 中文指南

阅读全文 »

Mac OS X 下安装 GDB

发表于 2016-01-07 | 更新于 2017-07-18 |

用 LiteIDE 可以方便调试 Go 程序,它是用的 GDB 调试的,如果没有安装 GDB 的话,运行“调试”就会提示:

1
2
11:21:45 GdbDebugger: /usr/local/bin/gdb was not found on system PATH (hint: is GDB installed?)
11:21:45 LiteDebug: Failed to start debugger

下边就说说怎么在 OS X 平台在安装 GDB。

Homebrew

用 Mac 的想必都知道这个了, 如果你还没有安装,去http://brew.sh/ 看看吧,或者直接用下边的命令安装

1
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

GDB

有了 brew 就方便了,直接执行下边的命令安装 gdb

1
$ brew install homebrew/dupes/gdb

如果上边的命令不行的试试下边的这个:

1
$ brew install https://raw.github.com/Homebrew/homebrew-dupes/master/gdb.rb

之后运行gdb --version看看是否安装成功

证书 & 授权

有了 brew 后,安装 so easy。不过先别高兴的太早,在 LiteIDE 中随便找一个程序“调试”一下,会发现“控制台”中报错:

1
2
3
10000012^error,msg="Unable to find Mach task port for process-id 42864: (os/kern) failure (0x5).
(please check gdb is codesigned - see taskgated(8))"
(gdb)

这是由于 OS X 的安全机制阻止了我们的 GDB 对要调试的程序进行完全控制,所以我们要对 GDB 赋予合适的权限。

首先创建一个系统代码签名信任证书:

  1. 启动“钥匙串访问”应用(/Applications/Utilities/Keychain Access.app)
  2. 打开菜单:钥匙串访问 -> 证书助理 -> 创建证书
    输入证书名称,如:gdb-cert;
    选择身份类型:自签名根证书 (Identity Type to Self Signed Root)
    选择证书类型:代码签名 (Certificate Type to Code Signing)
    勾选:让我覆盖这些默认签名 (select the Let me override defaults)

  3. 一路next,直到选择存放证书地址,选择:系统。这样证书就创建好了

  4. 设置证书自定义信任
    右键刚才创建的 gdb-cert 证书,选择“显示简介” (Get Info)
    点击“信任”,会显示可以自定义的信任选项,“代码签名”选择“总是信任” (Code Signing to Always Trust)

其次,将证书授予gdb,执行命令

1
2
3
$ which go
/usr/local/go/bin/go
$ codesign -s gdb-cert /usr/local/bin/gdb

如果你创建的证书名不是gdb-cert的话注意自行替换

这样授权后,在 LiteIDE 中运行“调试”的话还是会提示错误please check gdb is codesigned,只要重启一下就好了。

之后我们便可以用 gdb 调试 go 了,但是每次使用的时候都会提示输入管理员密码。这是由于之前制作的证书是在系统下面的,所以每次执行 gdb 都会提示管理员密码。
解决方法也很简单,打开钥匙串访问,将“系统”下面的 gdb-cert 拷贝一份放到“登录”下面就行了。

参考:
解决Mac下GDB提示签名错误
Mac OS X10.9安装gdb

Golang 学习笔记(四)——复合类型、函数

发表于 2016-01-06 | 更新于 2017-07-18 |

Go语言中的复合类型包括:数组(array)、切片(slice)、哈希表(map)、结构体(struct)。
函数是 Go 语言里面的核心设计。
这里结合网上的一些资料和自己的学习理解,记录一下,加深理解。

说复合类型之前先说一下指针,这样复合类型里边的一些概念就好理解了。

指针

Go保留了指针,*T表示T对应的指针类型,如果包含包名, 则应该是*.T。
代表指针类型的符号*总是和类型放在一起,而不是紧挨着变量名。
同样支持指针的指针**T。

声明

1
var a, b *int

说明

操作符&取变量地址,用*透过指针变量间接访问目标对象
默认值是nil,没有NULL常量
不支持指针运算,直接’.’选择符操作指针目标对象成员
可以在unsafe.Pointer和任意类型指针间进行转换
可以将unsafe.Pointer转换为uintptr,然后变相做指针运算,uintptr可以转换为整数

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import "fmt"

type User struct {
Id int
Name string
}
func main(){
i := 100
var p *int = &i //取地址

println(*p) //取值

up := &User{1, "Jack"}
up.Id = 100 //直接取只针对想成员
fmt.Println(up)

u2 := *up //拷贝对象
u2.Name = "Tom"
fmt.Println(up, u2)
}

结果:

1
2
3
100
&{100 Jack}
&{100 Jack} {100 Tom}

Array

在 Go 语言中,数组是一个值类型(value type)。
所有的值类型变量在赋值和作为参数传递时都将产生一个复制动作。
如果作为函数的参数类型,则在函数调用时参数发生数据复制,在函数体中无法修改传入数组的内容。

声明 & 赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var VarName [n]type     // n>=0

e.g.
var a [5]int //[0 0 0 0 0]
var c [2][3]int //二维

var b int = [5]int{1,2,3,4,5} //声明并初始化

a := [3]int{1,2,3}
b := [10]int{1,2,3} //前三个元素,其他为0
c := [20]int{19:1} //第20个元素初始化为1,其他默认0
d := [...]int{4,5,6} //自动计算长度
e := [...]int{0:1, 1:2, 19:3} //自动推断


// 二维数组
doubleArray := [2][4]int{[4]int{1,2,3,4}, [4]int{5,6,7,8}}
easyArray := [2][4]int{{1,2,3,4}, {1,2,3,4}}
// 多维 [...][n] 前者可推断,但是后者必须显示赋值

数组的长度是该数组类型的一个内置常量

1
arrLength := len(arr)

注意,数组长度也是类型的一部分,因此不同长度数组为不同类型(内置常量)

即[3]int和[4]int是不同类型,并且数组不能改变长度

数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本(一次复制操作),而不是它的指针,如果要传入指针,使用slice

元素访问

1
2
3
4
5
6
7
for i:=0; i < len(array); i++ {
fmt.Println(i, array[i])
}

for i, v := range array {
fmt.Println(i, v)
}

可以用new创建数组, 返回一个指向数组的指针

1
p := new([10]int)

注意区分

1
2
3
4
5
6
7
// 指向数组的指针
a := [100]int{}
var p *[100]int = &a

// 指针数组
x, y = 1, 2
a := [...]*int{&x, &y}

Slice

切片(类似 Java 中的 ArrayList)就像一个指向数组的指针,但更复杂,实际上它拥有自己的数据结构,而不仅仅是指针(指向原生数组的指针 + 数组切片中元素个数 + 数组切片已分配的存储空间)。
切片是一个引用类型,总是指向一个底层array,声明可以向array一样,只是不需要长度。
slice就像一个结构体,包含三个元素:

  • 一个指针,指向数组中slice指定的开始位置
  • 长度,即slice的长度
  • 最大长度,也就是slice开始位置到数组的最后位置的长度

声明 & 赋值

通过 array 创建

1
2
3
4
5
6
7
var myArray [10]int = [10]int{1,2,3,4,5,6,7,8,9,10}
var mySlice []int = myArray[:5]

a := [5]int{1,2,3,4,5}
b := a[2:4]
b := a[:4]
b := a[2:]

从数组或已存在的 slice 再次声明

1
2
3
4
5
var ar [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}

var a, b []byte
a = ar[2:5]
b = ar[3:5]

直接创建

1
2
3
myslice1 := make([]int, 5)
myslice2 := make([]int, 5, 10) //初始个数5,预留10个元素的存储空间
myslice3 := []int{1,2,3,4,5}

元素访问

1
2
3
4
5
6
7
for i:=0; i<len(mySlice); i++ {
fmt.Println(i, mySlice[i])
}

for i, v := range mySlice {
fmt.Println(i, v)
}

其他操作

大小和容量

len获取slice的长度
cap获取slice的最大容量
容量需要注意一下,数组值的容量总是等于其长度。而切片值的容量则往往与其长度不同。请看下图。

动态增减元素

append向 slice 里面追加一个或者多个元素,然后返回一个和 slice 一样类型的 slice

1
2
3
//append
mySlice = append(mySlice, 1, 2, 3) //增加三个元素
mySlice = append(mySlice, mySlice2) //增加另一个

特别注意(这是一个大坑):
append会改变 slice 所引用的数组的内容,从而影响到引用统一数组的其他 slice,但当 slice 中没有剩余空间,此时动态分配新的数组空间返回的 slice 数组指针将指向这个空间,而原数组的内容将保持不变,其他引用此数组的 slice 不受影响

内容复制

可以用copy从源 slice 的 src 中复制到目标 dst,并且返回复制元素的个数。

1
2
3
4
5
6
7
copy(dst, source) //会按短的个数复制

slice1 := []int{1,2,3,4,5}
slice2 := []int{5,4,3}

copy(slice2, slice1) //复制slice1前三个 1 -> 2
copy(slice1, slice2) //复制slice2的前三个 2 -> 1

切片

默认开始位置0,ar[:n]等价于ar[0:n]
第二个序列默认是数组长度ar[n:]等价于ar[n:len(ar)]
从一个数组直接获取slice,可以是ar[:]
slice 是引用类型,所以当改变其中元素的时候,其他的所有引用都会改变。

1
2
aSlice = array[3:7]
bslice = aSlice[:3]

Map

Map 类型其实是哈希表的一个实现,类似 Java 中的 HashMap、Python 中字典的概念。
map是无序的,长度不固定,内置的len可以用于 map,可以方便的修改。

声明 & 赋值

初始化一个 Map

1
2
3
4
5
6
7
8
9
map[keyType]valueType

var m map[string] PersonInfo
m = make(map[string] personInfo[, 100])

var numbers map[string]int
or
numbers := make(map[string]int)
numbers["one"] = 1

元素访问

1
2
3
4
5
6
7
8
rating := map[string]float32 {"c":5, "Go":4.5}

csharpRating, ok := rating["C#"]
if ok {
fmt.Println("get the value")
} else{
fmt.Println("error")
}

基本操作

赋值

1
m["1234"] = PersonInfo{}

删除

1
delete(m, "1234")

struct

struct,一组字段的集合,类似其他语言的 class。
Go 放弃了大量包括继承在内的面向对象特性,只保留了组合(composition)这个最基础的特性。

声明及初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type person struct {
name string
age int
}
//初始化

func main() {
var P person

P.name = "tom"
P.age = 25
fmt.Println(P.name)

P1 := person{"Tom1", 25}
fmt.Println(P1.name)

P2 := person{age: 24, name: "Tom"}
fmt.Println(P2.name)
}

struct 的匿名字段(嵌入字段)

这是实现类似于继承的一种手段,但是其实不同于继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Human struct {
name string
age int
weight int
}

tyep Student struct {
Human //匿名字段,默认Student包含了Human的所有字段
speciality string
}

mark := Student(Human{"mark", 25, 120}, "Computer Science")

mark.name
mark.age

能够实现字段继承,当字段名重复的时候,优先取外层的,可以通过指定 struct 名还决定取哪个

1
2
mark.Human = Human{"a", 55, 220}
mark.Human.age -= 1

struct 不仅可以使用 struct 作为匿名字段,自定义类型、内置类型都可以作为匿名字段,而且可以在相应字段上做函数操作

method

1
2
3
4
5
6
7
8
9
type Rect struct {
x, y float64
width, height float64
}

//method
func (r ReciverType) funcName(params) (results) {

}

Reciver 默认以值传递,而非引用传递,还可以是指针。
指针作为 Receiver 会对实例对象的内容发生操作,而普通类型作为Receiv er仅仅是以副本作为操作对象,而不对原实例对象发生操作。

如果一个 method 的 receiver 是*T,调用时,可以传递一个T类型的实例变量V,而不必用&V去调用这个 method

1
2
3
4
5
6
7
func (r *Rect) Area() float64 {
return r.width * r.height
}

func (b *Box) SetColor(c Color) {
b.color = c
}

method 继承和重写

采用组合的方式实现继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Human struct {
name string
}

type Student struct {
Human
School string
}

func (h *Human) SayHi() {
fmt.Println(h.name)
}

//则Student和Employee的实例可以调用
func main() {
h := Human{name: "human"}
fmt.Print(h.name)
h.SayHi()

s := Student{Human{"student"}}
s.SayHi()

}

还可以进行方法重写

1
2
3
4
func (e *Student) SayHi() {
e.Human.SayHi()
fmt.Println(e.School)
}

make和new

make用于内建类型(map, slice, channel) 的内存分配。

new用于各种类型的内存分配。new本质上和其他语言中同名函数一样,new(T)分配了零值填充的T类型的内存空间,并返回其地址,即一个*T类型的值 即,返回一个指针,指向新分配的类型T的零值。

make(T, args),只能创建 slice、map、channel,并返回一个有初始值(非零值)的T类型,而不是*T。 本质来讲,导致这三个类型有所不同的原因是,指向数据结构的引用在使用前必须被初始化。

函数

函数是 Go 语言里面的核心设计,通过关键字func来声明

1
2
3
4
func funcName(input type1, input2 type2) (output1 type1, output2 type2) {
//logical code
return value1, value2
}

基本语法

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//一般函数
func func_name(a int) {
println(a)
}

//多参数,无返回值
func func_name(a, b int, c string) {
println(a, b, c)
}

//单个返回值
func func_name(a, b int) int { //同类型,可以省略 a, b int
return a + b
}

//多个返回值
func func_name(a, b int) (c int, err error) { //返回值还可以是 (int, error)
return a+b, nil
}

func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}

说明:

函数通过关键字func声明
可以有一个或多个参数,每个参数后面带有类型。通过”,“分隔函数可以返回多个值
返回值声明,可以只声明类型。
如果没有返回值,可以省略最后的返回信息;如果有返回值,必须在外层添加 return。

Go函数不支持嵌套(nested),重载(overload)和默认参数(default parameters)。
支持:

1. 无需声明原型
2. 不定长度变参
3. 多返回值
4. 命名返回值参数
5. 匿名函数
6. 闭包

注意:

  • 函数使用func开头,左大括号不能另起一行
  • 小写字母开头的函数指在本包内可见,大写字母开头的函数才能被其他包调用

多返回值

可以像 python 那样返回多个结果,只是非 tuple。对于不想要的返回值,可以扔垃圾桶_。

如果用命名返回参数,return 语句可以为空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

func change(a, b int) (x, y int) {
x = a + 100
y = b + 100

return //101, 102
//return x, y //同上
//return y, x //102, 101
}

func main(){
a := 1
b := 2
c, d := change(a, b)
println(c, d)

如果命名返回参数被代码块中的同名变量覆盖了,就必须使用显式 return 返回结果。
不需要强制命名返回值,但是命名后的返回值可以让代码更加清晰,可读性更强。

参数传递

传值与传指针

指针, Go保留指针,用.而非”->”操作指针目标对象成员。
操作符:
&取变量地址
*通过指针间接访问目标函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func add1(a int) int {
a = a + 1
return a
}

x := 3
x1 := add1(x)
x //3
x1 //4
传值,x1的值没有改变

func add2(a *int) int {
*a = *a + 1
return *a
}
x := 3
x1 := add2(&x)
x // 4
x1 // 4

传指针的好处:

  1. 传指针多个函数能操作同一个对象;
  2. 传指针比较轻量级(8byte),只是传内存地址,我饿们可以用指针来传递体积大的结构体。

Go语言中,string、slice、map 这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传指针
注意,若函数需要改变 slice 长度,仍需要取地址传指针

可变参数

变参本质上就是一个 slice,且必须是最后一个形参。
将 slice 传递给变参函数时,注意用…展开(类似 Java 中的可变参数),否则会被当做单个参数处理,和 python 类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

func sum(s string, args ...int) {
var x int
for _, n := range args {
x += n
}
println(s, x)
}
func main(){
sum("1+2+3=", 1, 2, 3)

x := []int{0,1,2,3,4}
sum("0+1+2+3=", x[:4]...)
}

...type类型只能作为函数的参数类型存在,并且是最后一个参数
本质上是一个数组切片,即[]type。

传入任意类型的不定参数:

1
2
func Printf(format string, args ...interface{}) {
}

匿名函数

1
2
3
f := func(x,y int) int {
return x + y
}

函数作为值、类型

在 Go 语言中,函数也是一种变量,可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的函数。

语法:

1
type typeName func (input1 inputType1, input2 inputType2 [, ....]) (result1 resultType1 [,....])

用法1 (这种用法,在写接口的时候非常有用)
e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type testInt func(int) bool //声明了一个函数类型

func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
}

func isOdd(integer int) bool {
if integer % 2 == 0 {
return false
}
return true
}

filter(a, isOdd)

用法2 可以定义函数类型,也可以将函数作为值进行传递(默认值nil)
e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

//定义函数类型callback
type callback func(s string)

//定义一个函数,可以接收另一个函数作为参数
// sum为参数名称, func(int, int) int为参数类型
func test(a, b int, sum func(int, int) int) {
println( sum(a,b) )
}

func main(){
//演示1
var cb callback
cb = func(s string) {
println(s)
}
cb("hello world")

//演示2
test(1, 2, func(a, b int) int {return a + b})
}

结果:

1
2
hello world
3

###main函数和init函数

Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数。

Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。

程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。

参考资料:
谢大的《Go Web 编程》
wklken 的 Golang 笔记

Golang 学习笔记(三)——变量常量 基础类型

发表于 2016-01-05 | 更新于 2017-07-18 |

目录:

  • 类型列表
  • 标识符 & 关键字
  • 变量
  • 常量
  • 布尔类型 bool
  • 数值类型
  • 字符串类型
  • 错误类型型
  • 一些技巧(分组声明、枚举、运算符)
阅读全文 »

Golang 学习笔记(二)——命令基础

发表于 2016-01-04 | 更新于 2017-07-18 |

go build

go build命令用于编译我们指定的源码文件或代码包以及它们的依赖包。如果我们在执行go build命令时不后跟任何代码包,那么命令将试图编译当前目录所对应的代码包。

阅读全文 »

Golang 学习笔记(一)——安装、环境

发表于 2016-01-03 | 更新于 2017-07-18 |

为什么学习 Go?

我目前的主要开发语言是 Java。其实在 Java7 以后,使用 Java 开始起来已经是比较顺手的,特别是现在我使用的 Java8 中引入的 Lambda 表达式,更是让其开发效率有了明显的提升。

我也了解过 Python 一段时间,不过主要用它就是做 spider 或为了替代一部分 shell 脚本。用 Python 的时候深深的被它的效率所吸引,并曾准备了解一下 Flask 或 Django 以做 Web 开发。不过后来因为别的原因还是放弃了(并行、性能、语法糖等等)。

那为什么学习 Go 呢?
首先 Go 的背景很“硬”——一帮子大牛设计者、Google 出品。其设计理念很明确,就是将动态类型语言的编程容易度和静态类型语言的安全效率结合起来。
其次已经有很多公司和项目开始 Go 进行开发了(国外如 Google、AWS、CloudFlare、CoreOS、Docker 等,国内如七牛、阿里等),跟着世界级巨人的脚步应该不至于走错方向。

在这几天简单的学习了一下 Golang 后,我对其有以下几点浅薄理解:

  • 关键词少(25个)、语法简单,有其他类 C 语言基础的,入门很快
  • 语言层面支持并发
  • 性能强劲的同时,开发效率又不差
  • 代码简洁,风格统一(再也不用为代码风格而争论了)
  • 部署简单
  • 多返回值
  • 非侵入式的 interface 设计
  • OO
阅读全文 »

在 CentOS 7 上安装、配置 Kerberos

发表于 2016-01-02 | 更新于 2017-10-12 |

公司有个新项目要部署在新的服务器上,几台服务器之间要用 Kerberos 来做身份验证,并由我来安装和配置。期间翻阅了不少网上的文章,也遇到了几个坑,故总结了一下其安装配置的过程,以作笔记。

Kerberos 简介:它是一个身份验证协议,提供一个在客户端跟服务器端之间或者服务器与服务器之间的身份验证机制 (并且是相互的身份验证机制)。

环境配置:

OS 版本: CentOS 7
Kerberos 版本: krb5-1.12.2
Server:grape.jie.hn
Client:peach.jie.hn

阅读全文 »

Hexo 的一些额外配置

发表于 2016-01-01 | 更新于 2017-07-19 |

和 Hexo 相关的一些小配置、小功能就都放这里了。
目录:

  • 域名 & DNS
  • README.md
  • 404 页面
  • 评论系统
  • 图床
  • 百度统计
  • apple-touch-icon
阅读全文 »
1234
Yibo

Yibo

61 日志
66 标签
RSS
© 2015 — 2018 Yibo
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Muse v6.2.0