之劍java
2016.5.3 21:43:20python
像Java或者C#這樣強類型的準靜態語言在實現複雜的業務邏輯、開發大型商業系統、以及那些生命週期很長的應用中也有着很是強的優點git
下面咱們就來學習一下這些知識.程序員
有三個名詞容易混淆:github
Dynamic Programming Language (動態語言或動態編程語言)web
Dynamically Typed Language (動態類型語言)spring
Statically Typed Language (靜態類型語言)編程
先定義一下標準:json
是指須要進行變量/對象類型聲明的語言,通常狀況下須要編譯執行。例如C/C++/Java/C#api
是指不須要進行變量/對象類型聲明的語言,通常狀況下不須要編譯(但也有編譯型的)。例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等。
靜態類型語言的類型判斷是在運行前判斷(如編譯階段),好比C#、java就是靜態類型語言,靜態類型語言爲了達到多態會採起一些類型鑑別手段,如繼承、接口,而動態類型語言卻不須要,因此通常動態語言都會採用dynamic typing,常出現於腳本語言中.
不過,是否是動態類型語言與這門語言是否是類型安全的徹底不相干的,不要將它們聯繫在一塊兒!
沒有單元測試或者單元測試沒有達到語句覆蓋或者更強的弱條件組合覆蓋,從而致使某些非正常流程發生時,流經這些未被測試的語句致使語法錯誤而最終整個程序都掛掉.對於業務系統來講,這是很是嚴重的事情。
靜態類型語言的主要優勢在於其結構很是規範,便於調試,方便類型安全
如今有這樣一種趨勢,那就是合併動態類型與靜態類型在一種語言中,這樣能夠在必要的時候取長補短(下面在第4節中:在Scala語言的特點時介紹).
如今開發效率比之前高多了,主要緣由是由於開發語言和編譯器的進步,這個趨勢,只會繼續下去,不要抱着過去的教條不放,java也是在不斷改進的,加了reflection, 加了assert,加了泛型,下個版本,也要加腳本支持了。
其實靜態類型語言,除了性能方面的考量以外,最大的優點就是能夠提供靜態類型安全,編譯器能夠檢查你的每個函數調用是否是書寫了正確的名字,是否是提供了正確類型的參數。這樣一個系統,配合自定義類型的功能,可讓不少錯誤(比許多人想象的要多)在編譯時就能被發現和定位。
缺點是爲此須要寫更多的類型相關代碼,致使不便於閱讀、不清晰明瞭。
所謂的動態類型語言,意思就是類型的檢查是在運行時作的,好比以下代碼是否是合法的要到運行時才判斷(注意是運行時的類型判斷):
<img src="http://static.oschina.net/uploads/img/201412/23070943_OfhR.jpg">
def sum(a, b): return a + b
動態類型語言的優勢在於方便閱讀,不須要寫很是多的類型相關的代碼;動態語言表明着更快更簡單的技術大趨勢,所以它將必然成爲將來構建軟件和互聯網技術的主角。
動態語言足夠靈活,所以雖然它可以讓人更集中精力思考業務邏輯的實現,同時也向人工智能的方向走得更近一些,但所以它也更依賴於開發人員自己的技術功底,初學者、中級開發者,難以很好的利用它。 而靜態類型語言,與咱們計算機教學的基本科目(c/pascal/basic)延續性比較好,因此對於剛畢業的學生而言,更好接受和學習。
缺點天然就是不方便調試,命名不規範時會形成讀不懂,不利於理解等。
動態語言一般更方便開發較小的項目,由於能夠無需聲明類型而節省了不少麻煩。另一個答案是,動態類型解除了程序員的束縛,能夠最大的 發揮程序員的編程技能,能最有效的利用編程語言裏的各類特徵和模式。但這些能力都是一把雙刃劍,更多的依賴於程序員的我的才能,若是用很差,或用的過分, 都會產生負面的害處。
觀點一:靜態類型語言由於類型強制聲明,因此IDE能夠作到很好的代碼感知能力,由於有IDE的撐腰,因此開發大型系統,複雜系統比較有保障。
對於像Java來講,IDEA/Eclipse確實在代碼感知能力上面已經很是強了,這無疑可以增長對大型系統複雜系統的掌控能力。可是除了Java擁有這麼強的IDE武器以外,彷佛其餘語言歷來沒有這麼強的IDE。C#的Visual Studio在GUI開發方面和Wizard方面很強,可是代碼感知能力上和Eclipse差的不是一點半點。至於Visual C++根本就是一個編譯器而已,更不要說那麼多C/C++開發人員都是操起vi吭哧吭哧寫了幾十萬行代碼呢。特別是像Linux Kernel這種幾百萬行代碼,也就是用vi寫出來的阿,夠複雜,夠大型,夠長生命週期。
觀點二:靜態語言相對比較封閉的特色,使得第三方開發包對代碼的侵害性能夠降到很低。
也就是說靜態類型語言能夠保障package的命名空間分割,從而避免命名衝突,代碼的良好隔離性。可是這個觀點也缺少說服力。
靜態類型語言中C,VB都缺少良好的命名空間分割,容易產生衝突,可是並無影響他們作出來的系統就不夠大,不夠複雜。
而動態類型語言中Ruby/Python/Perl都有比較好的命名空間,特別是Python和Perl,例如CPAN上面的第三方庫成噸成噸的,也歷來沒有據說什麼衝突的問題。
誠然像PHP,JavaScript這樣缺少命名空間的動態語言很容易出現問題,可是這彷佛是由於他們缺少OO機制致使的,而不是由於他們動態類型致使的吧?
說到大型系統,複雜業務邏輯系統,Google公司不少東西都是用python開發的,這也證實了動態類型語言並不是不能作大型的複雜的系統。其實我我的認爲:
動態類型語言,特別是高級動態類型語言,反而可以讓人們不須要分心去考慮程序編程問題,而集中精力思考業務邏輯實現,即思考過程即實現過程,用DSL描述問題的過程就是編程的過程,這方面像Unix Shell,ruby,SQL,甚至PHP都是相應領域當之無愧的DSL語言。而顯然靜態類型語言基本都不知足這個要求。
那靜態類型語言的優點到底是什麼呢?我認爲就是執行效率很是高。因此但凡須要關注執行性能的地方就得用靜態類型語言。其餘方面彷佛沒有什麼特別的優點。
開發調試慢,總體解決方案複雜。這只是相對而言,對於熟練的開發這也許不是問題,做爲企業級解決方案而言,Java語言確實比較繁重,即便依賴了不少自動編譯、動態加載、打包並部署的持續集成工具,調試速度依然很慢,與如今的快節奏的開發要求有明顯矛盾. 不過, Java語言的生態不只僅是語言, 而是JVM. 因此java語言只是JVM生態的一個語言.後來者, 有Scala, Groovy, Fantom, Clojure, Ceylon, Kotlin 和Xtend–mostly等.
整體來講Java語言的編寫過程更傾向於過程式的開發,在上一層面上封裝了面向對象的特徵和行爲,語言的設計是上個世紀九十年代的風格,不是說語言自己很差是其抽象能力不夠,即便到了Java8也只是對Lambda表達式進行了支持,所以引入了Functional Interface也即只有一個方法的接口,和接口裏邊的帶具體實現的方法(爲了兼容之前的代碼不得不做出的讓步)。Java語言發展到如今其語言特性龐大,若是要徹底瞭解須要幾百頁的文檔,在其發展過程當中又只作加法沒又減法,語言慢慢風格混雜,變成了如今這種四不像的狀態,函數式的特性硬生生的嫁接在原來的面向對象特性之上。
一段Java 實體類代碼
package com.femon.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /** * @author 一劍 2015年12月23日 下午4:10:18 */ @Entity @Table(name = "cm_service") public class Service { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; private String host; private String requestUrl; private String responseBody; private String expect; private String method; private int paramsType;// 1 : 普通參數請求串query str // 2 : header參數 // 3: 表單方式提交參數 // 4: json post請求 private String paramsMap; private String platform;// H5 Web App private int todaySuccessTimes; private int todayFailTimes; /** * 0 失敗 1 正常 */ private int state; private int totalSuccessTimes; private int totalFailTimes; private Date gmtCreate; private Date gmtModify; /** * platform. * * @return the platform * @since JDK 1.7 */ public String getPlatform() { return platform; } /** * platform. * * @param platform * the platform to set * @since JDK 1.7 */ public void setPlatform(String platform) { this.platform = platform; } public String getParamsMap() { return paramsMap; } public void setParamsMap(String paramsMap) { this.paramsMap = paramsMap; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModify() { return gmtModify; } public void setGmtModify(Date gmtModify) { this.gmtModify = gmtModify; } public int getTodaySuccessTimes() { return todaySuccessTimes; } public void setTodaySuccessTimes(int todaySuccessTimes) { this.todaySuccessTimes = todaySuccessTimes; } public int getTodayFailTimes() { return todayFailTimes; } public void setTodayFailTimes(int todayFailTimes) { this.todayFailTimes = todayFailTimes; } public int getTotalSuccessTimes() { return totalSuccessTimes; } public void setTotalSuccessTimes(int totalSuccessTimes) { this.totalSuccessTimes = totalSuccessTimes; } public int getTotalFailTimes() { return totalFailTimes; } public void setTotalFailTimes(int totalFailTimes) { this.totalFailTimes = totalFailTimes; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getRequestUrl() { return requestUrl; } public void setRequestUrl(String requestUrl) { this.requestUrl = requestUrl; } public String getResponseBody() { return responseBody; } public void setResponseBody(String responseBody) { this.responseBody = responseBody; } public int getState() { return state; } public void setState(int state) { this.state = state; } public String getExpect() { return expect; } public void setExpect(String expect) { this.expect = expect; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public int getParamsType() { return paramsType; } public void setParamsType(int paramsType) { this.paramsType = paramsType; } /** * TODO * * @see java.lang.Object#toString() */ @Override public String toString() { return "Service [id=" + id + ", name=" + name + ", host=" + host + ", requestUrl=" + requestUrl + ", responseBody=" + responseBody + ", expect=" + expect + ", method=" + method + ", paramsType=" + paramsType + ", paramsMap=" + paramsMap + ", platform=" + platform + ", todaySuccessTimes=" + todaySuccessTimes + ", todayFailTimes=" + todayFailTimes + ", state=" + state + ", totalSuccessTimes=" + totalSuccessTimes + ", totalFailTimes=" + totalFailTimes + ", gmtCreate=" + gmtCreate + ", gmtModify=" + gmtModify + "]"; }}
而其實,咱們早就知道,雖然這些 getter,setter都是模板化的東西, 能夠自動生成的,可是這麼冗餘的代碼,看起來仍是不爽!那些寫Java代碼的牛逼程序員老鳥們裏面有一個叫Martin Odersky的, 就整了一個基於JVM的支持函數式又無縫融合OOP的程序設計語言Scala. 這樣的實體類代碼,在Scala中寫做以下:
package com.steda.entity import java.util.Date import javax.persistence.{Entity, GeneratedValue, GenerationType, Id} import scala.beans.BeanProperty @Entity class TedaCase { @Id @GeneratedValue(strategy = GenerationType.AUTO) @BeanProperty var id: Long = _ @BeanProperty var name: String = _ @BeanProperty var interfaceId: Long = _ @BeanProperty var paramJsonStr: String = _ @BeanProperty var expectOutput: String = _ @BeanProperty var actualOutput: String = _ @BeanProperty var dataSourceId: Long = _ @BeanProperty var clearSql: String = _ @BeanProperty var tddlApp: String = _ @BeanProperty var state: Integer = _ @BeanProperty var runTimes: Integer = _ @BeanProperty var owner: String = _ @BeanProperty var gmtCreate: Date = _ @BeanProperty var gmtModify: Date = _ }
咱們再看一個Controller層的寫做, Scala能夠與Java生態中優秀的框架無縫融合, 好比Spring,Junit. 尤爲當今發展勢頭正猛的SpringBoot, JPA等框架, 更是大大的提高了開發測試的生產力.
<pre>
package com.steda.controller
import java.util.Date
import com.steda.dao.{DataSourceDao, TedaCaseDao}
import com.steda.entity.TedaCase
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.ui.Model
import org.springframework.util.StringUtils
import org.springframework.web.bind.annotation._
import org.springframework.web.servlet.ModelAndView
@RestController
@RequestMapping(Array("/tedacase"))
class TedaCaseController @Autowired()(
private val tedaCaseDao: TedaCaseDao, private val dataSourceDao: DataSourceDao) {
@RequestMapping(Array("/newPage/{interfaceId}"))
def goNewPage(@PathVariable(value = "interfaceId") interfaceId: Long, model: Model): ModelAndView = {
model.addAttribute("dataSources", dataSourceDao.findAll()) model.addAttribute("interfaceId", interfaceId) new ModelAndView("/tedacase/new")
}
@RequestMapping(Array("/editPage/{id}"))
def goEditPage(model: Model, @PathVariable(value = "id") id: Long): ModelAndView = {
val tedacase = tedaCaseDao.findOne(id) model.addAttribute("tedacase", tedacase) model.addAttribute("dataSources", dataSourceDao.findAll()) new ModelAndView("/tedacase/edit")
}
@RequestMapping(Array("/detailPage/{id}"))
def goDetailPage(model: Model, @PathVariable(value = "id") id: Long): ModelAndView = {
val tedacase = tedaCaseDao.findOne(id) model.addAttribute("tedacase", tedacase) new ModelAndView("/tedacase/detail")
}
@RequestMapping(value = {
Array("", "/")
}, method = Array(RequestMethod.GET))
def list(model: Model, @RequestParam(value = "tedaCaseName", required = false) tedaCaseName: String): ModelAndView = {
var tedaCases: java.util.List[TedaCase] = new java.util.ArrayList[TedaCase] if (!StringUtils.isEmpty(tedaCaseName)) { tedaCases = tedaCaseDao.findByName(tedaCaseName) } else { tedaCases = tedaCaseDao.findAll() } model.addAttribute("tedaCases", tedaCases) model.addAttribute("tedaCaseName", tedaCaseName) new ModelAndView("/tedacase/list")
}
@RequestMapping(value = Array("/postnew"), method = Array(RequestMethod.POST))
@ResponseBody
def newOne(@RequestParam(value = "name") name: String, @RequestParam(value = "interfaceId") interfaceId: Long,@RequestParam(value = "paramJsonStr") paramJsonStr: String,@RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "owner") owner: String,@RequestParam(value = "clearSql") clearSql: String, @RequestParam(value = "dataSourceId") dataSourceId: Long, @RequestParam(value = "tddlApp") tddlApp: String) = {
val teda = new TedaCase() teda.clearSql = clearSql teda.dataSourceId = dataSourceId teda.interfaceId = interfaceId teda.tddlApp = tddlApp teda.expectOutput = expectOutput teda.owner = owner teda.paramJsonStr = paramJsonStr teda.name = name teda.state = -1 // -1 未執行 0 失敗 1 成功 teda.runTimes = 0 teda.gmtCreate = new Date() teda.gmtModify = new Date() tedaCaseDao.save(teda)
}
@RequestMapping(value = Array("/postedit"), method = Array(RequestMethod.POST))
@ResponseBody
def editOne(@RequestParam(value = "id") id: Long, @RequestParam(value = "name") name: String,@RequestParam(value = "paramJsonStr") paramJsonStr: String,@RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "owner") owner: String,@RequestParam(value = "clearSql") clearSql: String, @RequestParam(value = "dataSourceId") dataSourceId: Long, @RequestParam(value = "tddlApp") tddlApp: String) = {
val teda = tedaCaseDao.findOne(id) teda.clearSql = clearSql teda.dataSourceId = dataSourceId teda.tddlApp = tddlApp teda.expectOutput = expectOutput teda.owner = owner teda.paramJsonStr = paramJsonStr teda.name = name teda.gmtModify = new Date() tedaCaseDao.save(teda)
}
}
</pre>
Java語言號稱一次編譯,到處運行,就在於它基於一個須要首先先安裝到他所謂的到處的JDK,經過JVM解析編譯完成後的字節碼來運行,跟操做系統的接口也是在JVM託管的。這樣的好處是JVM能夠在實時運行的時候對字節碼進行進一步的優化,也就是大名鼎鼎的JIT,問題是全部的機器上都要安裝能夠兼容你的應用程序的JDK,同時JVM啓動消耗的資源很多,起碼數百M,且啓動速度緩慢,一樣的直接編譯成目標操做系統二進制可執行程序的服務,啓動起來消耗的資源小不少且速度快了不少。
在當前差別化的芯片結構中,像C、GO、RUST這種能直接運行於操做系統之上不基於某些龐大繁重的VM之上仍是頗有必要的,好比物聯網的控制芯片,一般內存也只有幾百K,適用性更強一些,並且如今LLVM架構的編譯器可以帶來性能的大幅優化,因此編譯依然是一個很好的選擇,除非JIT可以逆天的達到解釋執行的極限,所以假如咱們看到某些語言有Java語言的開發能力和內存安全特性,依然是能夠考慮的。
他雖然很老可是一直是做爲學院派函數式語言的表明,其純函數式的特性和簡潔漂亮的語法(糖)讓人看了很是舒服,在接觸了面向過程和麪向對象的開發後,若是要學習一種新的寫代碼的思路,面向函數式的語言是目前最好的選擇了,而Haskell有是函數式語言的先驅和集大成者,不少函數式語言的語法都是從Haskell借鑑來的。
做爲純函數式語言,Haskell將必然會產生Side-Effect的代碼好比IO操做放到了一塊兒,也即monad風格的部分,而其餘的函數能夠保證徹底的函數式特徵,對於一樣的輸入不管運行多少次結果都是同樣的,跟數學中函數的定義同樣嚴格,函數式是一種CPU友好的語言,在當前多核計算機發展情況下,函數式可讓程序很是安全的在多個核心上併發而不用擔憂大量的數據交互和side-effect, 從而在語言編譯過程當中可以針對併發進行大幅的優化。語言自己的不少寫法也跟數學中的定義很接近,好比定義一個集合
ghci> [x*2 | x <- [1..10]] [2,4,6,8,10,12,14,16,18,20]
看起來很像數學定義,語言可謂優雅漂亮,看着很舒服。做爲學院派語言,語言自身設計的要求不可謂不嚴格,完美的闡述了函數式是什麼意思,可是語言的複雜度較高,學習曲線很陡峭,很難保證團隊成員的接收程度,也很難招到相關的技術人才。從效率上來說,Haskell能夠優化的跟C語言的級別相似,但若是對某些特性不熟悉稍微改動一些就會形成性能的大幅降低,對新手不算友好。
同時在函數式不那麼擅長的領域Haskell的商業化程度很低,咱們不可能都用Haskell來寫一些語法解釋或者正則解析等,涉及IO的分佈式存儲和計算都相對很初級,尤爲是對於咱們比較感興趣的數據挖掘機器學習領域沒有成熟的解決方案,對於Web項目支持的尚可,有優秀的Yesod框架做爲表明。
總的來講,Haskell值的學習但不會在大型的生產環境中使用。
Scala語言的出現目的很明確,感受就是爲了替代Java而存在,在Java語言愈來愈力不從心的今天,可以有一門語言既繼承了它廣大的生態系統,又可以在表達能力和開發效率大大改進的狀況,能夠說是頗有但願的。
Scala從一開始就是一門設計良好的語言,幾乎完美的集合了函數式的特性和麪向對象的特性,雖然他的函數式不是純函數式。其面向對象的感受更像Ruby而不是Java,全部的東西都是對象,包括簡單類型例如Int,以及函數自己都是一種對象,這樣在這個層面實現了面向對象和函數式的統一。
Scala運行於JVM之上,可以無縫的使用全部的原來Java語言所開發的各類庫,語言上做爲Java的超集,遷移過來只會更強大而不會打折。
Java8的出現雖然增長了針對集合的stream api以及Lambda表達式這種函數式特性的支持,但只會讓人們以爲Java與Scala更像了,即便Java在之後的發展過程當中擁有了全部的Scala的能力.
打個比方一塊歪歪扭扭的通過各類後期焊接所建造起來的機器和一個一開始就有目的的設計出來的結構精密、風格統1、表達高效的機器比較,後者更像前者的重構,而前者雖然如日中天但已是暮年的四不像,不停的往身上增長各類各樣的功能.
也許Java9會有進步,但如今我看到Java8後反而更傾向於Scala。
Scala的元編程能力可讓他修改本身的語言定義,不僅是實現某些業務邏輯,這樣從符號層面上,scala能夠作到自洽,除了核心的一些規則,其餘的均可以被本身根據狀態調整所修改,這種能力能夠極大的擴展語言自身的能力,固然也帶來了一些負面效果,每學習一種新的包不僅是瞭解他的API,而是學習了一種新的語言,風格可能跟scala大不相同。
強有力的證實,大數據生態系統表明-Spark&Kafka,一個是分佈式計算一個是分佈式大規模數據吞吐,都證實了Scala的開發能力和效率。
Scala的問題其實也有跟Java相似的地方,首先這個語言雖然是從新設計的,可是使用起來複雜度依然很高,對於範型的繼承,+-等範型標註很差理解,
Go語言目前呈現了很火爆的趨勢,因爲其簡單,整個語言的specification也不過十幾頁,最多半天就可以徹底瞭解並上手寫一些小工具。GO語言最初是但願替代C和C++成爲新的系統語言,自帶GC垃圾回收,不過最終更多的是替代了python來開發一些服務或者工具,並無成爲系統級別的語言。 Go語言有不少的優勢,編譯速度快,有協程和Channel作併發支持和通訊,有不少官方的網絡協議的庫,很是適合於寫一些網絡服務,啓動一個http的接口服務只須要幾行代碼。目前github上也有大量的第三方項目使用go語言來開發應用或者擴展go的功能,在使用的時候直接import便可。Go的多返回機制也還不錯,節省了大量的無心義數據結構和不可讀的Map的使用,總的來講Go在其擅長的領域生產力很高,寫起來比較流暢,靜態類型也足夠的安全。目前Docker生態系統裏邊的各類工具都是Go來寫的。最新發布的1.5版本使得交叉編譯更加容易,靜態連接庫的方式使生成的可執行文件在相同CPU架構的操做系統都能運行,減小了額外查找依賴的問題,對咱們如今基本同構的Linux服務器而言,也打到了一次編譯到處運行的目的。同時Go語言在運行時消耗的資源也比Java要小,啓動速度更快,確實是輕量級服務的優選。