http.HandleFunc("/", handler) // each request calls handler
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
func Exit(code int)
应用程序马上退出。
defer函数不会执行。
总结起来log.Fatal函数完成:
打印输出内容
退出应用程序
defer函数不会执行
Panic
函数立刻停止执行 (注意是函数本身,不是应用程序停止)
defer函数被执行
返回给调用者(caller)
调用者函数假装也收到了一个panic函数,从而
4.1 立即停止执行当前函数
4.2 它defer函数被执行
4.3 返回给它的调用者(caller)
...(递归重复上述步骤,直到最上层函数)
应用程序停止。
panic的行为
"sync"
var mu sync.Mutex
var count int
func handler(w http.ResponseWriter, r *http.Request) {
mu.Lock()
count++
mu.Unlock()
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
在这些代码的背后,服务器每一次接收请求处理时都会另起一个goroutine,这样服务器就可以同一时间处理多个请求。
switch coinflip() {
case "heads":
heads++
case "tails":
tails++
default:
fmt.Println("landed on edge!")
}
Go语言并不需要显式地在每一个case后写break,语言默认执行完case后的逻辑语句会自动退出。
如果你想要相邻的几个case都执行同一逻辑的话,需要自己显式地写上一个fallthrough语句来覆盖这种默认行为。不过fallthrough语句在一般的程序中很少用到。
Go语言里的switch还可以不带操作对象(译注:switch不带操作对象时默认用true值代替
func Signum(x int) int {
switch {
case x > 0:
return +1
default:
return 0
case x < 0:
return -1
}
}
像for和if控制语句一样,switch也可以紧跟一个简短的变量声明,一个自增表达式、赋值语句,或者一个函数调用
和其它语言中的break和continue一样,break会中断当前的循环,并开始执行循环之后的内容,而continue会跳过当前循环,并开始执行下一次循环。
这两个语句除了可以控制for循环,还可以用来控制switch和select语句
命名类型: 类型声明使得我们可以很方便地给一个特殊类型一个名字
type Point struct {
X, Y int
}
var p Point
指针是可见的内存地址,&操作符可以返回一个变量的内存地址,并且*操作符可以获取指针指向的变量内容
方法是和命名类型关联的一类函数。Go语言里比较特殊的是方法可以被关联到任意一种命名类型。
接口是一种抽象类型,这种类型可以让我们以同样的方式来处理不同的固有类型,不用关心它们的具体实现,而只需要关注它们提供的方法。
命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。
指针对应的数据类型是*int,指针被称之为“指向int类型的指针”。
对于聚合类型每个成员——比如结构体的每个字段、或者是数组的每个元素——也都是对应一个变量,因此可以被取地址。
任何类型的指针的零值都是nil
在Go语言中,返回函数中局部变量的地址也是安全的。
var n = flag.Bool("n", false, "omit trailing newline")
var sep = flag.String("s", " ", "separator")
flag.Parse()
对于非标志参数的普通命令行参数可以通过调用flag.Args()函数来访问,返回值对应对应一个字符串类型的slice。
如果在flag.Parse函数解析命令行参数时遇到错误,默认将打印相关的提示信息,然后调用os.Exit(2)终止程序。
达式new(T)将创建一个T类型的匿名变量,初始化为T类型的零值,然后返回变量地址,返回的指针类型为*T。
p := new(int) // p, *int 类型, 指向匿名的 int 变量
fmt.Println(*p) // "0"
*p = 2 // 设置 int 匿名变量的值为 2
fmt.Println(*p) // "2"
每次调用new函数都是返回一个新的变量的地址,
当然也可能有特殊情况:如果两个类型都是空的,也就是说类型的大小是0,例如struct{}和 [0]int, 有可能有相同的地址(依赖具体的语言实现)
由于new只是一个预定义的函数,它并不是一个关键字,因此我们可以将new名字重新定义为别的类型。例如下面的例子:
func delta(old, new int) int { return new - old }
由于new被定义为int类型的变量名,因此在delta函数内部是无法使用内置的new函数的。
img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
blackIndex)
函数的有右小括弧也可以另起一行缩进,同时为了防止编译器在行尾自动插入分号而导致的编译错误,可以在末尾的参数变量后面显式插入逗号
最后插入的逗号不会导致编译错误,这是Go编译器的一个特性
那么Go语言的自动垃圾收集器是如何知道一个变量是何时可以被回收的呢?这里我们可以避开完整的技术细节,基本的实现思路是,从每个包级的变量和每个当前运行函数的每一个局部变量开始,通过指针或引用的访问路径遍历,是否可以找到该变量。如果不存在这样的访问路径,那么说明该变量是不可达的,也就是说它是否存在并不会影响程序后续的计算结果。
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用var还是new声明变量的方式决定的。