javaer to go之基礎
1、開始
我是一個javaer,最近空閒時間在學習golang。
度娘後,安裝好Go環境和LiteIDE後,一開始我也沒從基礎開始看,而是想把現有的java專案改成是golang版本的。
原專案內容:
- socket模組接收下位機的資料
- 對協議資料進行解析
- 把協議資料解析後存進資料庫
- web子專案
golang相比java,有很多很方便的特性。特別是併發與網路方面更是golang的賣點。所以我就直接找了個socket的例子開始模擬著實現專案的socket模組
2、第一個程式
server.go :
package socket
import (
"fmt"
"net"
"strings"
)
func StartServer() {
service := ":3338"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
fmt.Println("新連線:", conn.RemoteAddr().String())
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
for {
buffer := make([]byte, 1024)
length, err := conn.Read(buffer)
if err != nil {
conn.Close()
}
if length > 12 {
data := buffer[:length]
switch data[11] & 0xff {
case 0x80:
//桌子
fmt.Println("桌子")
case 0x90:
//椅子
case 0xA0:
//檯燈
default:
//其它
}
//寫資料
// conn.Write(data)
}
}
}
func isProtocol(data []byte) bool {
if (data[0]&0xff) == 0xC0 && (data[len(data)-1]&0xff) == 0xC1 {
return true
}
return false
}
func checkError(err error) {
if err != nil {
fmt.Println(err.Error())
}
}
因為是第一篇筆記,也簡單地說一下golang的基礎語法。
對於一個javaer來說,或者一個有計算機語言基礎的朋友來說,golang的語法看起來不會太困難。
3、包路徑
像java一樣,一開始我們為程式宣告一個包路徑
package socket
和java不一樣的是,golang的包不是層疊式的。所以為了方便識別我們也可以為我們所有的專案放到一個父包下。
類似與java,我這裡使用了一個letus.xyz的命名作為父包(資料夾)。這樣其它的程式就能比較方便地找到server.go程式來呼叫。
如果想要構建一個程式,則包和包內的檔案都必須以正確的順序進行編譯。包的依賴關係決定了其構建順序。
屬於同一個包的原始檔必須全部被一起編譯,一個包即是編譯時的一個單元,因此根據慣例,每個目錄都只包含一個包。
如果對一個包進行更改或重新編譯,所有引用了這個包的客戶端程式都必須全部重新編譯。
4、包與庫的匯入
import (
"fmt"
"net"
"strings"
)
和java相比,golang使用的這種方式進行導包和庫看起來優雅多了。
當然,你也可以像java一樣,一個一個地import
import "fmt"
import "net"
import "strings"
注意事項:
如果你匯入了一個包卻沒有使用它,則會在構建程式時引發錯誤,如 imported and not used: os,這正是遵循了 Go 的格言:“沒有不必要的程式碼!“。
當你匯入多個包時,匯入的順序會按照字母排序。
如果包名不是以 . 或 / 開頭,如 “fmt” 或者 “container/list”,則 Go 會在全域性檔案進行查詢;如果包名以 ./ 開頭,則 Go 會在相對目錄中查詢;如果包名以 / 開頭(在 Windows 下也可以這樣使用),則會在系統的絕對路徑中查詢。
匯入包即等同於包含了這個包的所有的程式碼物件。
除了符號 _,包中所有程式碼物件的識別符號必須是唯一的,以避免名稱衝突。但是相同的識別符號可以在不同的包中使用,因為可以使用包名來區分它們。
5、 函式
匯入包和庫之後,就是我們的程式主體了。當然,我們寫程式的時候肯定是package之後就直接寫程式主體,而包與庫是到用到這個包內容的時候再導。
golang和c一樣,是面向過程的函數語言程式設計,而不是java那樣的面向物件。
func StartServer() {
}
func isProtocol(data []byte) bool {
return false
}
func checkError(err error) {
}
可見性規則:
當識別符號(包括常量、變數、型別、函式名、結構欄位等等)以一個大寫字母開頭,如:Group1,那麼使用這種形式的識別符號的物件就可以被外部包的程式碼所使用(客戶端程式需要先匯入這個包),這被稱為匯出(像面嚮物件語言中的 public);識別符號如果以小寫字母開頭,則對包外是不可見的,但是他們在整個包的內部是可見並且可用的(像面嚮物件語言中的 private )。
函式的基本結構:
func functionName(parameter_list) (return_value_list) {
…
}
其中:
- 引數:parameter_list 的形式為 (param1 type1, param2 type2, …)
- 返回型別return_value_list 的形式為 (ret1 type1, ret2 type2, …)
golang的方法比java有意思的是它允許返回多個值。而它的引數表示形式與java也不一樣,名字是放在型別的前面。
6、變數
service := ":3338"
:=是簡短宣告語法 ,表示宣告並賦值。
或者你也可以用var來宣告:
var service = ":3338"
或
var service string
service = ":3338"
簡短宣告法看起來更加優雅些。
7、常量
const (
Unknown = 0
Female = 1
Male = 2
)
我們第一個程式沒用到常量。常量是通過const來定義的。
常量的定義格式:
const identifier [type] = value
8、基本資料型別
- int,Runes(注:Rune 是int 的別名)
- int8 ,int16 ,int32 ,int64
- byte ,uint8 ,uint16 ,uint32 ,uint64 (注:byte是uint8 的別名)
- float32 ,float64 (沒有float 型別)
- bool
- string
- complex128,complex64
9、main函式
package main
import (
"letus.xyz/socket"
)
func main() {
socket.StartServer()
}
這裡我們通過絕對路徑letus.xyz主包找到socket包來呼叫程式。當然我們也可以相對目錄匯入。很明顯,相對路徑的方式不利於包的複用。
所以個人建議使用絕對路徑來導包,畢竟思想上和java相似。
import (
"../socket"
)
10、其它
golang的運算子與控制結構語句的使用基本和java的一樣,但值得一提的是,golang的switch語法支援字串的匹配。這是作為一個javaer經常想java也提供的一個特性。
switch field.Type().String() {
case "time.Time":
v, _ := time.Parse("2006-01-02 15:04:05", s)
field.Set(reflect.ValueOf(v))
}