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为主,均为非官方。