20190608_淺談go&java差別(三)html
轉載請註明出處http://www.javashuo.com/article/p-rguxjsbk-hu.htmljava
java 提供了具備線程安全的類型以免線程問題,好比AtomicLong、AtomicArray、AtomicInteger等等,其中對於字符串類型則提供了
StringBuffer類型來操做字符串,若是多個線程操做同一個jdk的數據安全類型的須要手動添加synchronized或者Lock()來保證併發數據
的安全性git
public class AtomIntegerTest { private static final Logger LOG = LoggerFactory.getLogger(AtomIntegerTest.class); private static AtomicInteger atomicInt = new AtomicInteger(); /* private static AtomicLong atomicLong = new AtomicLong(); private static AtomicArray atomicArray = new AtomicArray(100); private static AtomicBoolean atomicBoolean = new AtomicBoolean(); */ @Test public void process01(){ IntStream.range(0,100).parallel().forEach(i->{ atomicInt.addAndGet(i); }); LOG.info("result : {}",atomicInt); } }
go語言則提供來chan關鍵字來輔助多協程通信,並且go相對於java來講,他的基本數據類型也具備數據安全特性,其解決的方式有點兒相似於
消息隊列的形式。web
func main() { c := make(chan int) go func() { for i := 0; i <= 100; i = i + 1 { c <- i } close(c) }() j := 0 // 這裏會阻塞 直到循環執行完成 for i := range c { j = j + i //fmt.Println(i) } fmt.Println("result : ", j) fmt.Println("Finished") }
其實這方面java與go是無法比較的,go偏向於過程,而java是強面向對象的,這裏僅僅闡述下各自對於數據的處理的結構差別
在java中能夠說一切皆爲對象,任什麼時候候須要調用對象裏面的函數必須new一個(也即建立一個),可是對於靜態的方法不須要new,可是靜態方法
必定是存在於對象之中的。java的數據對象定義是固定的,默認須要給參數加上getter和setter方法以作隱藏處理算法
public class PersonEntity { private String name; private int age; private boolean isAdult; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean isAdult() { return isAdult; } public void setAdult(boolean adult) { isAdult = adult; } }
func main() { p1 := PersonEntity{"Lina", 27, true} p2 := PersonEntity{name: "Steve", age: 15, isAdult: false} fmt.Println("p1 : ", p1) fmt.Println("p2 : ", p2) } type PersonEntity struct { name string age int8 isAdult bool }
在go中沒有異常拋出的概念,不過在大多數狀況下均將異常放入error中返回,手動判斷及處理異常;若是有顯性拋出並
處理的地方須要配合defer去處理,同時拋出的異常是在panic的參數中定義數據庫
func main() { defer func() { // 必需要先聲明defer,不然不能捕獲到panic異常 fmt.Println("process 03") if err := recover(); err != nil { fmt.Println(err) // 這裏的err其實就是panic傳入的內容,55 } fmt.Println("process 04") }() f() } func f() { fmt.Println("process 01") panic("error info") fmt.Println("process 02") }
java在可能出現異常的地方均會在方法上聲明拋出(但這並不表明未聲明的函數就必定不會拋出異常了),
這個時候須要在業務邏輯中選擇拋出或者抓取處理就職由用戶選擇了json
public class ThrowExceptionTest { private static final Logger LOG = LoggerFactory.getLogger(ThrowExceptionTest.class); @Test public void process01(){ String[] strArr = {"a","b"}; // 數組取值時越界可能會拋出異常 LOG.info("value : {}",strArr[3]); } @Test public void process02()/*throws UnsupportedEncodingException*/{ String str = "hello"; byte[] enCodeArr = {}; try { // getBytes 顯式拋出異常了,須要拋出或者抓取(try catch)處理 enCodeArr = Base64.getEncoder().encode(str.getBytes("utf-8")); }catch (UnsupportedEncodingException e){ LOG.error("異常 : ",e); } LOG.info("enCode result : {}",enCodeArr); } /* public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException(); return StringCoding.encode(charsetName, value, 0, value.length); } */ }
java([ ]、Array、Map)
java 的集合類型有三類:
數組
[] : 且稱它爲定長單值數組tomcat
Array :能夠理解是一個定長數組的管理器,它實現了不定長數組安全
根據不一樣的算法有ArrayList、Set、TreeSet 等等
Map : 是一個鍵值對的集合類型,它的值能夠是基本數據類型也能夠是自定義數據類型
它的實現也有不少 HashMap、TresMap、LinkedHashMap 等等
public class ArrayTest { private static final Logger LOG = LoggerFactory.getLogger(ArrayTest.class); @Test public void process01(){ // 這裏定義了長度爲4的定長數組,當取或放>4個值後會拋異常 String[] arrBase= new String[4]; arrBase[0] = "hello"; LOG.info("len {},{}",arrBase.length,arrBase[0]); // 這裏定義了一個默認長度爲4的不定長數組,固然是能夠放入>4個值的 List<Integer> lst = new ArrayList<Integer>(4){{ add(0); add(22); add(-1); }}; LOG.info("arr len {},{}",lst.size(),lst.toString()); } @Test public void process02(){ // 這裏定義了一個鍵值對集合 Map<String,Object> hashMap = new HashMap<String,Object>(2){{ put("a",1); put("b",2); }}; LOG.info("map len {},{}",hashMap.size(),hashMap.toString()); } }
go的集合有三種形式,其中數組與切片數組看似類似,其實對於內存分配有很大差別,通常實際使用後者,同時須要說明的是map也可以使用make關鍵字
作集合優化。
go 的集合類型有三類,目前均無多算法實現:
- 數組
- 切片數組(slice)
- 鍵值對集合(map)
func main() { // 這裏定義了一個不定長數組(這種描述可能不許確) var arr []int8 arr = append(arr, 100) arr = append(arr, -1) fmt.Println(arr) // 這裏使用slice 定義了一個長度爲3,容量爲3的數組 arr2 := make([]string, 3, 3) arr2 = append(arr2, "hello") arr2[2] = "youth" //arr2 = append(arr2, "youth") // arr2 = append(arr2, "good") // arr2 = append(arr2, "morning") fmt.Println(cap(arr2), len(arr2), arr2) }
java 有繼承extend和實現interface 之分,一個類只能單繼承或者多實現,但不論是被繼承仍是被實現,他們的類型仍是有差別的
(訪問類型也是有差別的)
public class ExtendIntfTest { } interface EntityA{ void doSth01(); // private doSth02(); } class EntityB{ public void doSth01(){ } } public abstract class EntityC { public void doSth01(){ // TODO } public void doSth02(){ // TODO } }
go更偏向於過程,只給出了組合做爲繼承的一種實現,並且是經過結構體嵌套實現的,不說了仍是看代碼吧:
package main import ( "fmt" ) type Base struct { } func (b *Base) ShowA() { fmt.Println("showA") } func (b *Base) ShowB() { fmt.Println("showB") } type Derived struct { Base } func (d *Derived) ShowB() { fmt.Println("Derived showB") } func main() { // 當 Derived 的結構體中包含Base時也就至關於繼承了Base 的 ShowA() 方法 d := Derived{} d.ShowA() d.ShowB() }
go的包引入與java比較類似,均是經過在java文件或者go文件首行定義包名稱以被引入,不過使用的細節上還有有丟丟 差別的,好比在go內若是有多個引入 則使用 import()來包含,同時還能夠對引入作忽略(不夠準確,與init相關)和別名處理 同時對於包(module模塊)的管理在go 1.11以前多用dep,而在go 1.11及以後則引入來go module,我的以爲有點兒像git,對於多個 工程的管理更加的方便了。 java中若是存在同包內多個子包引入則在包尾使用*,同一package內引入不用聲明引入,對於包的管理多用maven(以及gradle),但對於較老的 工程也有手動導入的方式。
go的打包只有官方標準的,每個安裝了go語言的機器都內置了go的一些列命令,包含 打包、構建、運行、測試、拉取依賴等等,不過 雖然方便但也有不足之處,好比`go run`命令沒有提供進程守護,須要第三方實現;再好比 `go package` 的包比較大不利於發佈, 通常使用upx命令縮減包的大小。 java的打包有官方和非官方兩種,官方只定義了jar包的打包的規範,對於工程管理卻沒有提供任何工具;而非官方的以maven爲主(還有gradle),不只僅 能夠管理依賴和工程結構等等~=,比官方好用多了~;而運行主要將打包後的文件扔進容器便可,同時容器提供了進程守護等功能,容器以tomcat、 jetty、webLogic、ws爲主,均爲非官方。