前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java与Go的交流

java与Go的交流

作者头像
三产
发布2021-01-13 11:09:55
4940
发布2021-01-13 11:09:55
举报
  1. 语句结束没有分号
  2. import支持导入多个(中间没有逗号) 语法:import ("xx" "xx" "xx")
  3. 导出(类似于public等可被访问):首字母大写的变量及方法是被导出的
  4. 声明变量: 语法: 变量名:=值 var 变量名,变量名,...... 类型
  5. 函数: 函数可以没有参数或接受多个参数,函数可以返回任意数量的返回值;当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略; 函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样命名和使用。 如果命名了返回值参数,一个没有参数的 return 语句,会将当前的值作为返回值返回。 语法:func 方法名(参数列表)(返回值列表){ return } new 的用法不同: 语法:new(类等名称) 可以将函数赋予变量(与javascript相同) t:=func........ (`:=` 结构不能使用在函数外,函数外的每个语法块都必须以关键字开始。)
  6. Go 的基本类型有Basic types bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // uint8 的别名 rune // int32 的别名 // 代表一个Unicode码 float32 float64 complex64 complex128
  7. 常量的定义与变量类似,只不过使用 const 关键字。 常量可以是字符、字符串、布尔或数字类型的值。 语法:const Pi = 3.14 一个未指定类型的常量由上下文来决定其类型。
  8. 循环 Go 只有一种循环结构——`for` 循环。 基本的 for 循环除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 Java 中做的一样,而 `{ }` 是必须的。 基于此可以省略分号:java 的 while 在 Go 中叫做 `for` 如果省略了循环条件,循环就不会结束,因此可以用更简洁地形式表达死循环
  9. if 语句除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 Java 中的一样,而 `{ }` 是必须的。for 一样,`if` 语句可以在条件之前执行一个简单的语句。由这个语句定义的变量的作用域仅在 if 和else 范围之内 例子: package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else{ // v可使用 } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }
  10. 结构: type用于声明 struct为结构的关键字(与java类似) 例子: type Vertex struct { X int Y int } 结构体字段使用点号来访问。
  11. 指针: Go 有指针,但是没有指针运算。 结构体字段可以通过&结构体指针来访问。通过指针间接的访问是透明的。 例子: func main() { p := Vertex{1, 2} q := &p q.X = 1e9 fmt.Println(p) }
  12. 结构体文法: 结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。 使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。) 特殊的前缀 & 构造了指向结构体的指针。 例子: type Vertex struct { X, Y int } var ( p = Vertex{1, 2} // 类型为 Vertex q = &Vertex{1, 2} // 类型为 *Vertex r = Vertex{X: 1} // Y:0 被省略 s = Vertex{} // X:0 和 Y:0 ) func main() { fmt.Println(p, q, r, s) }
  13. slice: slice 指向一个数组,并且包含了长度信息。 例子: package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} //数组的声明方式 fmt.Println("p ==", p) for i := 0; i < len(p); i++ { fmt.Printf("p[%d] == %d\n", i, p[i]) //输出指定格式字符串 } } 对 slice 进行截取 slice 可以重新切片,创建一个新的 slice 值指向相同的数组。 表达式 s[lo:hi] 表示从 lohi-1 的 slice 元素,含两端。因此 s[lo:lo] 是空的,而 s[lo:lo+1] 有一个元素。 例子: package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) fmt.Println("p[1:4] ==", p[1:4]) // 省略下标代表从 0 开始 fmt.Println("p[:3] ==", p[:3]) // 省略上标代表到 len(s) 结束 fmt.Println("p[4:] ==", p[4:]) } slice 的零值是 `nil`。 一个 nil 的 slice 的长度和容量是 0。
  14. 迭代: for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。 可以将值赋值给 _ 来忽略序号和值。 如果只需要索引值,去掉“, value”的部分即可。 例子: package main import "fmt" var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } //只要索引 for i := range pow { fmt.Print(i) } }
  15. Map: map 映射键到值。 map 在使用之前必须用 make 而不是 new 来创建;值为 nil 的 map 是空的,并且不能赋值。 与java区别: 声明方式:var声明 m变量名 map[stringkey的类型]Vertexvalue的类型 =make生成对象的方法(map[string]Vertex); 例子: package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) } map 的文法跟结构体文法相似,不过必须有键名。 如果顶级的类型只有类型名的话,可以在文法的元素中省略键名?应该是vale的类名。 例子: package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": Vertex{ 40.68433, -74.39967, }, "Google": Vertex{ 37.42202, -122.08408, }, } func main() { fmt.Println(m) } 修改 map 在 map m 中插入或修改一个元素: m[key] = elem 获得元素: elem = m[key] 删除元素: delete(m, key) 通过双赋值检测某个键存在: elem, ok = m[key] 如果 keym 中,`ok` 为 true 。否则, ok 为 `false`,并且 elem 是 map 的元素类型的零值。 同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值
  16. switch: 你可能已经猜到 switch 可能的形式了。 switch 的条件从上到下的执行,当匹配成功的时候停止。 除非使用 fallthrough 语句作为结尾,否则 case 部分会自动终止。(加上fallthrough会执行之后的一个的内容) 例子: package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") //runtime.GOOS获取当前运行的系统 switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.", os) } } 没有条件的 switch 同 `switch true` 一样。 这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。
  17. 方法: Go 没有类。然而,仍然可以在结构体类型上定义方法。 方法接收者 出现在 func 关键字和方法名之间的参数中。 示例: package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} fmt.Println(v.Abs()) } 事实上,可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。 不能对来自其他包的类型或基础类型定义方法。 方法可以与命名类型或命名类型的指针关联。 刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在MyFloat 值类型上。 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。 示例: package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} v.Scale(5) fmt.Println(v, v.Abs()) } 尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。(这时是复制了 Vertex 的值,并不会改变原有的 Vertex 值 ) 当 vVertex 的时候 Scale 方法没有任何作用。`Scale` 修改 `v`。当 v 是一个值(非指针),方法看到的是 Vertex 的副本,并且无法修改原始值。 Abs 的工作方式是一样的。只不过,仅仅读取 `v`。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。
  18. 接口定义: 接口类型是由一组方法定义的集合。 接口类型的值可以存放实现这些方法的任何值。 示例: package main import ( "fmt" "math" ) type Abser interface { Abs() float64 } func main() { var a Abser f := MyFloat(-math.Sqrt2) //v := Vertex{3, 4} a = f // a MyFloat 实现了 Abser //a = &v // a *Vertex 实现了 Abser //a = v // a Vertex, 没有实现 Abser fmt.Println(a.Abs()) } type MyFloat float64 func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } ???隐式接口 类型通过实现那些方法来实现接口。 没有显式声明的必要。 隐式接口解藕了实现接口的包和定义接口的包:互不依赖。 因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。 包 io 定义了 Reader 和 `Writer`;其实不一定要这么做
  19. 错误: type error interface { Error() string } 当用 fmt 包的多种不同的打印函数输出一个 error 时,会自动的调用该方法。 示例: package main import ( "fmt" "time" ) type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf("at %v, %s", e.When, e.What) } func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } }
  20. Web 服务器: 包 http 通过任何实现了 http.Handler 的值来响应 HTTP 请求: package http type Handler interface { ServeHTTP(w ResponseWriter, r *Request) } 在这个例子中,类型 Hello 实现了 `http.Handler`。 访问 http://localhost:4000/ 会看到来自程序的问候。 示例: package main import ( "fmt" "net/http" ) type Hello struct{} func (h Hello) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello!") } func main() { var h Hello http.ListenAndServe("localhost:4000", h) }
  21. 处理文件: 1、图片(1) Package image 定义了 Image 接口: package image type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color } (参阅文档了解全部信息。) 同样,`color.Color` 和 color.Model 也是接口,但是通常因为直接使用预定义的实现 image.RGBAColorimage.RGBAColorModel 而被忽视了。 示例: package main import ( "fmt" "image" ) func main() { m := image.NewRGBA(image.Rect(0, 0, 100, 100)) fmt.Println(m.Bounds()) fmt.Println(m.At(0, 0).RGBA()) }
    1. 并发机制:

    线程 1、goroutine

    1. goroutine 是由 Go 运行时环境管理的轻量级线程。 go f(x, y, z) 开启一个新的 goroutine 执行 f(x, y, z) f , x , yz 是当前 goroutine 中定义的,但是在新的 goroutine 中运行 `f`。 goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。sync 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。(在接下来的内容中会涉及到。) package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") say("hello") }

    2、channel

    1. channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。 ch <- v // 将 v 送入 channel ch。 v := <-ch // 从 ch 接收,并且赋值给 v。 (“箭头”就是数据流的方向。) 和 map 与 slice 一样,channel 使用前必须创建: ch := make(chan int) 默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。 package main import "fmt" func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c <- sum // 将和送入 c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // 从 c 中获取 fmt.Println(x, y, x+y) } 3、缓冲 channel

    channel 可以是 _带缓冲的_。为 make 提供第二个参数作为缓冲长度来初始化一个缓冲 channel: ch := make(chan int, 100) 向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。 修改例子使得缓冲区被填满,然后看看会发生什么。 package main import "fmt" func main() { c := make(chan int, 2) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) }

  22. 控制线程范围 1、range 和 close 发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过 v, ok := <-ch 之后 ok 会被设置为 `false`。 循环 `for i := range c` 会不断从 channel 接收值,直到它被关闭。 注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 `range`。 package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } 2、select select 语句使得一个 goroutine 在多个通讯操作上等待。 select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。 package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) } 3、默认select 当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行。 为了非阻塞的发送或者接收,可使用 default 分支: select { case i := <-c: // 使用 i default: // 从 c 读取会阻塞 }

package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } } 使用并发 (1/2) 1、练习:Web 爬虫 在这个练习中,将会使用 Go 的并发特性来并行执行 web 爬虫。 修改 Crawl 函数来并行的抓取 URLs,并且保证不重复。 package main import ( "fmt" ) type Fetcher interface { // Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。 Fetch(url string) (body string, urls []string, err error) } // Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。 func Crawl(url string, depth int, fetcher Fetcher) { // TODO: 并行的抓取 URL。 // TODO: 不重复抓取页面。 // 下面并没有实现上面两种情况: if depth <= 0 { return } body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) for _, u := range urls { Crawl(u, depth-1, fetcher) } return } func main() { Crawl("http://golang.org/", 4, fetcher) } // fakeFetcher 是返回若干结果的 Fetcher。 type fakeFetcher map[string]*fakeResult type fakeResult struct { body string urls []string } func (f *fakeFetcher) Fetch(url string) (string, []string, error) { if res, ok := (*f)[url]; ok { return res.body, res.urls, nil } return "", nil, fmt.Errorf("not found: %s", url) } // fetcher 是填充后的 fakeFetcher。 var fetcher = &fakeFetcher{ "http://golang.org/": &fakeResult{ "The Go Programming Language", []string{ "http://golang.org/pkg/", "http://golang.org/cmd/", }, }, "http://golang.org/pkg/": &fakeResult{ "Packages", []string{ "http://golang.org/", "http://golang.org/cmd/", "http://golang.org/pkg/fmt/", "http://golang.org/pkg/os/", }, }, "http://golang.org/pkg/fmt/": &fakeResult{ "Package fmt", []string{ "http://golang.org/", "http://golang.org/pkg/", }, }, "http://golang.org/pkg/os/": &fakeResult{ "Package os", []string{ "http://golang.org/", "http://golang.org/pkg/", }, }, } 2、练习:等价二叉树 1. 实现 Walk 函数。 2. 测试 Walk 函数。 函数 tree.New(k) 构造了一个随机结构的二叉树,保存了值 `k`,`2k`,`3k`,...,`10k`。 创建一个新的 channel ch 并且对其进行步进: go Walk(tree.New(1), ch) 然后从 channel 中读取并且打印 10 个值。应当是值 1,2,3,...,10。 3.Walk 实现 Same 函数来检测是否 t1t2 存储了相同的值。 4. 测试 Same 函数。 `Same(tree.New(1), tree.New(1))` 应当返回 true,而 `Same(tree.New(1), tree.New(2))` 应当返回 false。 package main import "code.google.com/p/go-tour/tree" // Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。 func Walk(t *tree.Tree, ch chan int) // Same 检测树 t1 和 t2 是否含有相同的值。 func Same(t1, t2 *tree.Tree) bool func main() { }

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2014/03/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档