java与Go的交流

u012885276 · · 2322 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  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]

    表示从 lo 到 hi-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]

    如果 key 在 m 中,`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 值

    当 v 是 Vertex 的时候 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.RGBAColor 和 image.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 , y 和 z 是当前 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 函数来检测是否 t1 和 t2 存储了相同的值。

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() {

}


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:u012885276

查看原文:java与Go的交流

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2322 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传