稿丨彭鏡陶
11月26日,蔣勁夫女友Julieta在社交網絡上傳遭受家暴的照片,聲稱和蔣勁夫住一起的日子很像在監獄里面。吃瓜群眾大吃一驚,要知道蔣勁夫去年家暴前女友中浦悠花并微博致歉的事情還歷歷在目,但當時的致歉微博已經刪除。第二天,蔣勁夫發律師函否認家暴,聲稱前女友誹謗。
再往前一天,美妝博主宇芽在微博上指控前男友沱沱的家暴行為,并聯合沱沱相同遭遇的前妻和學生拍攝了視頻,在視頻中,宇芽被沱沱強行拖出電梯,令人不寒而栗。而當天11月25日正是消除對婦女的暴力行為國際日,聯合國會發起一年一度的“消除性別暴力16日行動”,直到12月6日結束。
據統計世界上每三位婦女和女童,就有一位曾遭受過家庭暴力。我國在2016年就已經出臺了反家庭暴力法,但三年間遭受不同形式的家暴的女性數量并沒有太大的變化,始終穩定在20%左右。
擺脫家庭暴力,對女性來說到底有多難?
那些被家暴埋葬的人生
2005年,柴靜曾經做出一期意在反家暴的《女子監獄調查》,采訪了石家莊女子監區那些遭受嚴重家暴,最后忍無可忍,在反抗時殺死了丈夫的女性。小豆十五歲嫁給丈夫,他沒完沒了地虐待小豆,只要小豆“不聽話”,就把她吊起來用皮帶或者鞋底子抽。安華被打了二十年,一只眼睛被丈夫用酒瓶砸瞎了,只反抗過一次,也是最后一次,她的婆婆、孩子身上都留下了丈夫家暴的痕跡。在她殺夫入獄之前,全家人都生活在丈夫家暴的陰影之下。
面對無休無止的家庭暴力,她們嘗試過不再沉默,向親友尋求幫助;也試圖逃離不幸的婚姻,寄希望于離婚,甚至都已經走到了法院的門口。親友要么事不關己高高掛起,要么以和為貴勸她忍受,走到法院門口也被丈夫強行抓回。在絕望中掙扎的女性,走投無路地殺死了丈夫。但社會對她們沒有絲毫憐憫,即使擺脫了家暴,還要面臨著幾十年甚至無期的監獄生涯。
在記者手記中,柴靜提到,她看過一份報告提到,在各地監獄女性暴力重犯中,殺死丈夫的比例很高。數據顯示,女性殺人犯中已婚女性占百分之九十左右,被害人大多數是成年男性,主要原因是家庭暴力和婚外情糾紛。
《女子監獄調查》中,那些因被長期家暴而殺死丈夫的女人們
除了殺死丈夫,家暴之下的女性也會選擇自殺尋求解脫。中國是世界上唯一一個女性自殺率超過男性的國家,《人民日報》指出,我國每年有15.7萬婦女自殺,其中百分之六十是因為家庭暴力。
對于全球女性而言,家庭暴力都是環繞在生活里的夢魘。今年法國就發生了兩次抗議女性遭受家暴的游行:第一次是在7月6日,千余人聚集在巴黎街頭,截至當時今年已經有至少75名女性被伴侶殺害;第二次是在11月23日,消除對婦女的暴力行為國際日前夕,因伴侶家暴而死亡的女性已經增加了四十多名,約3.5萬人走上法國的城市街頭,人們拿著紫色的標語牌,有的寫著受害者的名字,有的寫著“別再謀殺了”的標語。
法國萬人游行抗議家暴
1999年,聯合國就已經確立了消除對婦女的暴力行為國際日。也就是說,至少二十年來一直都有人在不斷地為消除家庭暴力進行努力,家暴不是沒有得到重視。但令人難以置信的是,女性遭受家暴的幾率一直居高不下,每隔一段時間家暴案件就會上熱搜,成為全民焦點,那么到底為什么,家暴的陰影總是揮之不去?
下定決心擺脫受虐關系并非易事
宇芽在微博勇敢發聲后,有無數的質疑和苛責涌向她。宇芽提到,她是在遭受了第五次家暴后才決定分手。眾多網友表示:家暴只有1次和n次的區別,你為什么要忍受那么久?為什么不在第一次家暴發生后就離開?
雖然宇芽在微博發文說,后悔沒有第一時間報警,而是選擇沉默,但事實上,擺脫一段受虐關系,真正告別家暴你的對象,并沒有網友想象的那么簡單。根據美國反家暴聯盟(National Coalition Against Domestic Violence)的數據,85%的受虐者都無法徹底離開一段受虐關系。
對家暴幸存者的調查表示,受害者往往沒有意識到自己正在遭遇家暴。許多女性認為,家暴就是夫妻吵架罷了,忍受家暴是天經地義,“床頭吵架床尾和”,她們往往沒有把家暴真的放在心上。1993年,長期從事婦女兒童問題研究的孫曉梅教授去北京郊區調研,她的每個調查對象都表示遭遇過家暴,但她們認為,妻子被丈夫打一輩子是應該的。
“瘋狂英語”李陽對妻子Kim的家暴案曾經是震驚社會的大新聞。Kim遭遇家暴去報警的時候,有一位男性試圖阻攔她,告訴她“男人不可以打女人,但是老公可以打老婆”。
在聯合國毒品和犯罪問題辦公室(UNODC)發布的報告中,每10名被故意殺害的女性中,就有6名是被親密伴侶或家庭成員傷害的,2017年平均每天有137位女性被家庭成員殺害。正因為是在家庭中,女性受到人身傷害的可能性更大,也可能難處理,導致家反倒成了對女性而言最危險的地方。同樣,李陽從來沒因為家暴妻子而感到愧疚,反過來他對媒體說“爸爸媽媽打架不是正常的嗎”。Kim也是因為害怕女兒也會遭受同樣的命運,才站出來接受媒體的采訪。
李陽和前妻Kim
另一方面,施暴者會對家暴對象進行心理操縱,他們會把家暴的責任推到家暴對象身上去,認為家暴對象才是沖突的源頭。他們常常說,“都是因為你XXX,我才這樣,都是你的錯。”時間長了,施暴對象很容易相信一切都是因自己而起。
有的被家暴的女性能感受到施暴者對她的愛,并且她自己也很難從對對方的愛中抽身。無論是宇芽還是Julieta,都提到了施暴者對她們的控制欲,只要她們和男人一說話就吃醋,甚至和女人說話都會猜忌,這很容易使她們認為對方還是愛著自己的。
即使自我認識到了家暴,想要擺脫家暴也并非一件易事。《致命女人》中頻繁被丈夫家暴的瑪麗,只能用化妝來掩蓋傷痕,貝絲·安問她為什么不離開她的丈夫,她表示離開也沒有辦法自己一個人謀生。娜拉出走就能永遠不回來了嗎?大結局瑪麗的丈夫和貝絲·安的丈夫同歸于盡,可惜這只是女性開掛爽劇的美好幻想;在現實中,大多數被家暴的女性都深陷泥潭難以脫身。
《致命女人》中被家暴的Mary
一位曾經在婦聯工作的男社工表示,他曾經努力幫助一位被家暴的婦女,尋求一個保證她人身安全的辦法。她遭受了數十年的家暴,每次被打得頭破血流以后施暴者就逃之夭夭,即使婦聯介入他也不出面。但是,同事卻勸家暴對象原諒施暴者,讓施暴者做出不再家暴的承諾,這根本不可能根治家暴。婦聯的女同事們還秉承著“受害者有罪論”,聲稱一定是女性做錯了什么才會惹來丈夫的毒打。
在家暴面前,女性很難真正獲得援助,就算撥打110報案,警察一聽說二人是夫妻關系,本著“寧拆一座廟,不拆一樁婚”的原則,多半也只會當作家庭糾紛不予重視。2009年,北京女孩董珊珊被丈夫毆打致死,在此之前曾經八次報警,但最后都被警方以“不好管,現在還是夫妻”為借口拒絕。在上世紀六十到七十年代,美國警方也認為家暴這樣的家庭內部事務不宜多干涉。但是,在1984年,特雷西·瑟曼遭遇家暴多次報警,警方均以家庭事務為由置之不理,即使后來法院給特雷西頒布了保護令,警方也出于家庭事務不好干涉的原因,沒有真正對她丈夫采取行動。最后一次,她丈夫對她捅了十三刀,她對公權力提起訴訟,事情才出現了轉機。
與此同時,明尼蘇達州的明尼阿波利斯市警方發布了一個報告,稱他們選取了1981-1982一年內300多個家庭暴力案件,在隨后六個月中進行跟蹤記錄,警方一共采取了三種方式來處理家暴:送走施暴者八小時、調解糾紛、實施逮捕,最終發現實施逮捕的效果是最好的。這兩件事使美國加快了完善反家庭暴力法的腳步,直到2005年,已經有23個州和1個特區將強制逮捕施暴配偶寫入了法律。新西蘭也受此影響,將強制逮捕家庭暴力中的施暴對象寫入了法律。
在中國的《反家庭暴力法》中,人身安全保護令的實施有很大困難。早在2010年,江蘇省當地就出臺了人身保護令的規定,但截至2011年底,只發出了十多個人身安全保護令。大部分地區的人身安全保護令支持率只有不到20%,而經工作撤回的高達三分之一。
根據東方網反家暴法實施一周年的有關報道稱,法院積極開展法制教育和司法調解工作,一些施害人主動承諾不再實施家庭暴力,37位受害人自愿撤回申請。也就是說,法院寧愿花費精力去調解,也不愿去核發人身安全保護令。一方面是中國警察沒有強制逮捕施暴者的權力,另一方面是法官難以理解人身安全保護令的意義在于對有可能發生的家庭暴力防患于未然。
宇芽是著名美妝博主,有一定的社會地位,在社交媒體上有一定的號召力,懂得拍視頻取人證物證,所以她能夠借助輿論的力量維權。但是對于大多數遭受家暴的女性來說,家暴取證難,認證率低,家暴案的認定率僅有百分之十左右,且賠償數額也多在5萬元以下。
目前家庭暴力案件的證據,主要來自于醫療機構的記錄和證明,相較于物理虐待,精神虐待的取證難度更大。根據一項分析了2016年到2018年間400份涉及家庭暴力案件的研究發現,有半數案件都沒有提供相關證據,僅憑當事人自我陳述,家暴取證難可見一斑。就連宇芽自己都表示,后悔報警時間過晚,導致一些重要證據滅失。
擺脫家暴仍是難題,各國仍在不斷探索對策
各國也都對家暴加強重視,進行反家暴教育。11月22日,意大利一家醫院展出了遭受家暴女性的X光片,她想展示自己26年行醫歲月所見到的殘酷事實,把女性真實受到的傷害展現出來。羅馬前幾日也有數萬人游行抗議家暴,在抵達終點的時候,她們集體坐在地上為因家暴喪生的女性默哀。2018年,意大利共有119位女性死于家庭暴力,相當于每三天就有一名女性死于家暴。
意大利米蘭一家醫院展出的遭受家暴女性的X光片
今年9月3日,法國總統馬克龍視察法國反家暴專線的現場,旁聽了一位女性求助者的電話。一位57歲的婦女經常被她的老公家暴,她向憲兵隊求助,希望憲兵能夠陪她回家。但是,憲兵表示沒有法院的傳票,他不能這么做。馬克龍給接線員寫了很多建議,但沒有說服憲兵,最后只能讓受害者去求助一個家暴互助社團,接線員向總統表示他經常遇到這種事情。
法國已經是歐洲家暴率第二高的國家,截至11月23日,較之前一年法國因家暴死亡的女性已經增長了17位。法國的對策是數十條措施打擊家暴,其中有教師必須接受男女平等的培訓,成立針對有家暴行為的男性收容中心等,并提出給有家暴史的男子安裝電子腳鐐,只要超過安全距離地接近被保護的人,就會發出警告。同時,法國政府撥款3.6億歐元。但女權協會認為,政府采取措施太過遲緩,撥出的經費不足以推進反家暴事業。
在反家暴教育中,美國已經收到了一定的成效。美國政府讓家暴幸存者到監獄中與施暴男性進行交流,所謂“修復式正義”,讓男性直面自己家暴的歷史。參與過16周家暴介入服務的施暴者,在出獄后第一年施暴的幾率降低了80%,也有人出獄后成為了這個項目的推動者。Leigh Goodmark,一位律師兼教授表示,刑法并沒有降低家暴的概率,應該去思考家暴背后的社會問題,失業男性更容易家暴,創傷和暴力也與家暴高度相關。但是,對于“修復式正義”項目到底是否能代替監禁,還充滿著爭議。
美國家暴幸存者在監獄中和家暴者進行交流
盡管大多數國家都在為解決家暴問題思考對策,但也不乏截然相反的聲音。俄羅斯是世界上僅有的沒有出臺反家暴法案的國家,過去十年里下議院所有的反家暴法案提案都沒有通過。甚至在2017年2月,俄羅斯將家庭暴力認定為非刑事犯罪。
今年11月23日,近二百名東正教教徒,在莫斯科公園里聚集反對議員們正在籌備的“懲罰家庭暴力”的法案。他們表示,這項法案將違背俄羅斯家庭傳統價值觀。之前將家庭暴力認定為非刑事犯罪,已經很大程度地使受害者失去有力的保護。但,這掩蓋不了八成女殺人犯是因在家暴中進行自衛,每天俄羅斯有四十名婦女死于家庭暴力,何況還有百分之六十到七十的女性不會舉報家庭暴力。在此之前,俄羅斯女性波波娃和米楚辛納鼓勵女性化家暴妝并拍照發到社交媒體上,她們的發文已經獲得了超過40萬點贊,她們要求制定新的家暴法律,已經有超過58萬人簽署。
宇芽和Julieta的不再沉默,的確在輿論中為家庭暴力贏得了不少關注和思考。今年,中國人民大學法學院教授劉俊海建議,有關部門盡快建立家庭施暴者黑名單,納入征信,一次施暴,處處受限。但這實施起來可能有很大的難度,能意識到家暴的女性本就不多,家暴取證也困難,一旦實行這個黑名單制度,怎么樣才能不淪為雞肋還很難說。
但愿這次風波,能給反家暴行動,帶來更多的解題思路。
參考鏈接:
https://m.weibo.cn/5644317025/4442630170052219
https://mp.weixin.qq.com/s/9AJKlGdudWB90LwhJeeQ_w
https://mp.weixin.qq.com/s/OltcI5RIWiqCfiuNamBT7A
https://m.weibo.cn/2606218210/4382016629878356
https://kidsmatterinc.org/get-help/for-families/abuse-and-neglect/children-domestic-violence/?gclid=EAIaIQobChMI17H0z5eM5gIVR6WWCh3GPAdXEAAYBCAAEgIvIfD_BwE
http://www.oushinet.com/wap/europe/france/20190704/325186.html
http://www.oushinet.com/wap/europe/other/20190731/327222.html
http://www.oushinet.com/wap/europe/italy/20181126/306930.html
http://www.oushinet.com/wap/europe/other/20190726/326895.html
作者丨彭鏡陶
編輯丨徐悅東
校對丨薛京寧
者:CUGGZ
今天我們來看一個常見的概念 —— JSON,來看下它的概念、使用、技巧、相關工具!
JSON 全稱為 JavaScript Object Notation,是一種輕量級的數據交換格式。它是 JavaScript 中用于描述對象數據的語法的擴展。不過并不限于與 JavaScript 一起使用。它采用完全獨立于語言的文本格式,這些特性使 JSON 成為理想的數據交換格式。易于閱讀和編寫,同時也易于機器解析和生成。所有現代編程語言都支持這些數據結構,使 JSON 完全獨立于語言。
在 2000 年代初期,Douglas Crockford 創建了 JSON 以實現最小化、可移植和文本化。作為 JavaScript 的一個子集,JSON 與 Web 瀏覽器腳本語言大約在同一時間流行起來。到 2010 年代初,JSON 成為新公共 API 的流行選擇。
JSON 于 2013 年標準化為 ECMA-404,并于 2017 年發布為 RFC8259。RFC 和 ECMA 標準保持一致。JSON 的官方媒體類型是 application/json,JSON 文件名使用擴展名 .json。
JSON 源于對無狀態、實時的服務器到瀏覽器通信協議的需求,它旨在成為 XML 的輕量級替代方案,以允許在移動處理場景和Web 上輕松解析 JavaScript。
JSON 通常與 REST 服務相關聯,尤其是對于 Web 上的 API。盡管 API 的 REST 架構允許使用任何格式,但 JSON 提供了一種更靈活的消息格式,可以提高通信速度。在開發需要快速、緊湊和方便的數據序列化的 Web 或移動應用程序時,它非常有用。
JSON 的流行正是因為網站和移動應用程序需要更快捷、有效地將數據從一個系統傳輸到另一個系統。JSON 可以通過多種方式共享數據、存儲設置以及與系統交互。它的簡單性和靈活性使其適用于許多不同的情況。
JSON 最常見的用法是通過網絡連接交換序列化數據。JSON 的其他常見用途包括公共、前端或內部 API、NoSQL 數據庫、模式描述、配置文件、公共數據或數據導出。
JSON 的特點如下:
JSON 易于編寫和閱讀,并且易于在大多數語言使用的數據結構之間進行轉換。下面來看一下 JSON 的組成、JSON 支持的數據類型。
下面是一個最基本的 JSON 示例:
{"name": "zhangsan"}
在上面的示例中,key是 name,value 是 zhangsan。JSON 可以保存多個 key:value對:
{"name": "zhangsan", "age": 18, "city": "beijing"}
當然這只是一個簡單的例子,在實際應用中 JSON 可能會多層嵌套。對象和數組是可以保存其他值的值,因此 JSON 數據可能會發生無限嵌套。這允許 JSON 描述大多數數據類型。
下面是 JSON 數據類型的完整列表:
下面是一個包含這些數據類型的 JSON 對象示例:
{
"name": "zhangsan",
"age": 28,
"badperson":true,
"child": {
"name": "zhangxiaosan",
"age": 8
},
"job": ["React", "JavaScript"],
"wages": null,
}
下面來看看如何避免常見的 JSON 語法錯誤:
JSON 內置了兩種方法:
JSON.parse() 的語法如下:
JSON.parse(text, reviver)
const json='{"name": "zhangsan", "age": 18, "city": "beijing"}';
const myJSON=JSON.parse(json);
console.log(myJSON.name, myJSON.age); // zhangsan 18
我們可以啟用 JSON.parse 的第二個參數 reviver,一個轉換結果的函數,對象的每個成員都會調用此函數:
const json='{"name": "zhangsan", "age": 18, "city": "beijing"}';
const myJSON=JSON.parse(json, (key, value)=> {
if(typeof value==="number") {
return String(value).padStart(3, "0");
}
return value;
});
console.log(myJSON.name, myJSON.age); // zhangsan 018
JSON.stringify() 的語法如下:
JSON.stringify(value, replacer, space)
const json={"name": "zhangsan", "age": 18, "city": "beijing"};
const myJSON=JSON.stringify(json);
console.log(myJSON); // {"name":"zhangsan","age":18,"city":"beijing"}
那如果 JSON 無效怎么辦呢?比如缺少了逗號,引號等,上面的兩種方法都會拋出異常。建議在使用這兩個方法時使用try...catch來包裹,也可以將其封裝成一個函數。
let myJSON={}
const json='{"name": "zhangsan", "age": 18, "city": "beijing"}';
try {
myJSON=JSON.parse(json);
} catch (e){
console.error(e.message)
}
console.log(myJSON.name, myJSON.age); // zhangsan 18
如果 JSON 操作時出現問題,這樣就能確保應用程序不會因此中斷。
① 刪除鍵值對
可以使用 delete 運算符來刪除 JSON 中的鍵值對:
const json={"name": "zhangsan", "age": 18, "city": "beijing"};
delete json.city;
console.log(json); // {name: 'zhangsan', age: 18}
② 訪問數組項
可以使用方括號[]和索引從 JSON 中訪問數組項:
const json={
"name": "zhangsan",
"age": 18,
"job": ["React", "JavaScript"],
};
console.log(json.job[0]); // React
③ 遍歷數組項
可以使用for循環來遍歷 JSON 中的數組項:
const json={
"name": "zhangsan",
"age": 18,
"job": ["React", "JavaScript"],
};
for (item of json.job) {
console.log(item); // React JavaScript
}
下面來看看 JSON 有哪些實用技巧。
上面提到,可以使用JSON.stringify()來將 JSON 對象轉換為字符串。它支持第二、三個參數。我們可以借助第二三個參數來格式化 JSON 字符串。正常情況下,格式化后的字符串長這樣:
const json={"name": "zhangsan", "age": 18, "city": "beijing"};
const myJSON=JSON.stringify(json);
console.log(myJSON); // {"name":"zhangsan","age":18,"city":"beijing"}
添加第二三個參數:
const json={"name": "zhangsan", "age": 18, "city": "beijing"};
const myJSON=JSON.stringify(json, null, 2);
console.log(myJSON);
生成的字符串格式如下:
{
"name": "zhangsan",
"age": 18,
"city": "beijing"
}
這里的 2 其實就是為返回值文本在每個級別縮進 2 個空格。
如果縮進是一個字符串而不是空格,就可以傳入需要縮進的填充字符串:
const json={"name": "zhangsan", "age": 18, "city": "beijing"};
const myJSON=JSON.stringify(json, null, "--");
輸出結果如下:
{
--"name": "zhangsan",
--"age": 18,
--"city": "beijing"
}
我們知道JSON.stringify()支持第二個參數,用來處理 JSON 中的數據:
const user={
"name": "John",
"password": "12345",
"age": 30
};
console.log(JSON.stringify(user, (key, value)=> {
if (key==="password") {
return;
}
return value;
}));
// 輸出結果:{"name":"John","age":30}
可以將第二個參數抽離出一個函數:
function stripKeys(...keys) {
return (key, value)=> {
if (keys.includes(key)) {
return;
}
return value;
};
}
const user={
"name": "John",
"password": "12345",
"age": 30,
"gender": "male"
};
console.log(JSON.stringify(user, stripKeys('password', 'gender')))
// 輸出結果:{"name":"John","age":30}
當 JSON 中的內容很多時,想要查看指定字段是比較困難的。可以借助JSON.stringify()的第二個屬性來獲取指定值,只需傳入指定一個包含要查看的屬性 key 的數組即可:
const user={
"name": "John",
"password": "12345",
"age": 30
}
console.log(JSON.stringify(user, ['name', 'age']))
// 輸出結果:{"name":"John","age":30}
最后來推薦幾個好用的 JSON 查看器。
JSON Hero 是一個開源的、漂亮的 JSON 查看器,它提供了包含額外功能的干凈美觀的 UI,使閱讀和理解 JSON 文件變得容易。
Github:https://github.com/jsonhero-io/jsonhero-web
JSON Visio 是一個 JSON 數據的可視化工具,它可以無縫地在圖表上展示數據,而無需重組任何內容、直接粘貼或導入文件。
Github:https://github.com/AykutSarac/jsonvisio.com
JSON Viewer Pro 是一個Chrome擴展程序,主要用于可視化JSON文件。其核心功能包括:
輸入界面如下:
格式化之后:
[1] JSONLint: https://jsonlint.com/。
[2] JSONedit: https://mb21.github.io/JSONedit/。
[3] JSON API: https://jsonapi.org/。
[4] JSON Formatter: https://jsonformatter.org/。
[5] JSON Generator: https://extendsclass.com/json-generator.html。
來源: 前端充電寶
JDK8雖然出現很久了,但是我相信,很多公司依然在用JDK8,并沒有升級到JDK9+,包括阿里、美團等一線互聯網公司!但是可能我們還是有很多人并不太熟悉。本文主要就是介紹說明一些jdk8相關的內容:應該是JDK最成功的一個版本了。
主要會講解:
我們來看看阿里規范里面涉及到jdk8相關內容:
https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
主要有:
1:lambda表達式:一種新的語言特性,能夠把函數作為方法的參數或將代碼作為數據。lambda表達式使你在表示函數接口(具有單個方法的接口)的實例更加緊湊。
2:方法引用 是lambda表達式的一個簡化寫法,所引用的方法其實是lambda表達式的方法體實現,這樣使代碼更容易閱讀
3:默認方法:Java 8引入default method,或者叫virtual extension method,目的是為了讓接口可以事后添加新方法而無需強迫所有實現該接口的類都提供新方法的實現。也就是說它的主要使用場景可能會涉及代碼演進。
4:Stream 不是 集合元素,也不是數據結構,它相當于一個 高級版本的 Iterator,不可以重復遍歷里面的數據,像水一樣,流過了就一去不復返。它和普通的 Iterator 不同的是,它可以并行遍歷,普通的 Iterator 只能是串行,在一個線程中執行。操作包括:中間操作 和 最終操作(只能操作一遍) 串行流操作在一個線程中依次完成。并行流在多個線程中完成,主要利用了 JDK7 的 Fork/Join 框架來拆分任務和加速處理。相比串行流,并行流可以很大程度提高程序的效率
5:用Optional取代
6:新的日志和時間,可以使用Instant代替Date LocalDateTime代替Calendar DateTimeFormatter代替SimpleDateFormat
7:CompletableFuture:CompletableFuture提供了非常強大的Future的擴展功能,可以幫助我們簡化異步編程的復雜性,并且提供了函數式編程的能力,可以通過回調的方式處理計算結果,也提供了轉換和組合 CompletableFuture 的方法。
8:去除了永久代(PermGen) 被元空間(Metaspace)代替 配置:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m 代替 -XX:PermSize=10m -XX:MaxPermSize=10m
IDEA工具自動提示:
lambda語法結構 :
完整的Lambda表達式由三部分組成:參數列表、箭頭、聲明語句;
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM;}
絕大多數情況,編譯器都可以從上下文環境中推斷出lambda表達式的參數類型,所以參數可以省略:
(param1,param2, ..., paramN) -> { statment1; statment2; //............. return statmentM;}
當lambda表達式的參數個數只有一個,可以省略小括號:
param1 -> { statment1; statment2; //............. return statmentM;}
當lambda表達式只包含一條語句時,可以省略大括號、return和語句結尾的分號:
param1 -> statment
在那里以及如何使用Lambda????
你可以在函數式接口上面使用Lambda表達式。
備注:JDK定義了很多現在的函數接口,實際自己也可以定義接口去做為表達式的返回,只是大多數情況下JDK定義的直接拿來就可以用了。
Java SE 7中已經存在的函數式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.util.concurrent.Callable
java.io.FileFilter
java.beans.PropertyChangeListener
除此之外,Java SE 8中增加了一個新的包:java.util.function
,它里面包含了常用的函數式接口,例如:
Predicate
——接收T
對象并返回boolean
Consumer
——接收T
對象,不返回值Function
——接收T
對象,返回R
對象Supplier
——提供T
對象(例如工廠),不接收值隨便看幾個:
Java 8 引入了新的語言特性——默認方法(Default Methods)。
Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.
默認方法允許您添加新的功能到現有庫的接口中,并能確保與采用舊版本接口編寫的代碼的二進制兼容性。
默認方法是在接口中的方法簽名前加上了 default
關鍵字的實現方法。為什么要有默認方法
在 java 8 之前,接口與其實現類之間的 耦合度 太高了(tightly coupled),當需要為一個接口添加方法時,所有的實現類都必須隨之修改。默認方法解決了這個問題,它可以為接口添加新的方法,而不會破壞已有的接口的實現。這在 lambda 表達式作為 java 8 語言的重要特性而出現之際,為升級舊接口且保持向后兼容(backward compatibility)提供了途徑。
這個 forEach
方法是 jdk 1.8 新增的接口默認方法,正是因為有了默認方法的引入,才不會因為Iterable
接口中添加了forEach
方法就需要修改所有Iterable
接口的實現類。
如果一個Lambda表達式僅僅是調用方法的情況,那么就可以用方法引用來完成,這種情況下使用方法引用代碼更易讀。
方法引用語法:
目標引用放在分隔符::前,方法的名稱放在后面。
names2.forEach(System.out::println);//1
names2.forEach(s->System.out.println(s));//2
第二行代碼的lambda表達式僅僅就是調用方法,調用的System.out的println方法,所以可以用方法引用寫成System.out::println即可。
方法引用有很多種,它們的語法如下:
靜態方法引用:ClassName::methodName
實例上的實例方法引用:instanceReference::methodName
父類的實例方法引用:super::methodName
類型上的實例方法引用:ClassName::methodName
備注:String::toString 等價于lambda表達式 (s) -> s.toString
這里不太容易理解,實例方法要通過對象來調用,方法引用對應Lambda,Lambda的第一個參數會成為調用實例方法的對象。
構造方法引用:
Class::new
數組構造方法引用:
TypeName::new
個人理解:方法引用,說白了,用更好,不用也可以,如果可以盡量用!!!
Java 8 中的 Stream 是對集合(Collection)對象功能的增強,它專注于對集合對象進行各種非常便利、高效的聚合操作(aggregate operation),或者大批量數據操作 (bulk data operation)。Stream API 借助于同樣新出現的 Lambda 表達式,極大的提高編程效率和程序可讀性。同時它提供串行和并行兩種模式進行匯聚操作,并發模式能夠充分利用多核處理器的優勢,使用 fork/join 并行方式來拆分任務和加速處理過程。通常編寫并行代碼很難而且容易出錯, 但使用 Stream API 無需編寫一行多線程的代碼,就可以很方便地寫出高性能的并發程序。
Stream 不是集合元素,它不是數據結構并不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。
Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復返。
和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。
對stream的操作分為三類。
創建stream
中間操作(intermediate operations)【沒有終止操作是不會執行的】
終止操作(terminal operations):
中間操作會返回另一個流。可以用鏈式編程.的形式繼續調用。在沒有終止操作的時候,中間操作是不會執行的。
終止操作不會返回流了,而是返回結果(比如返回void-僅僅System.out輸出,比如返回總數 int,返回一個集合list等等)
例如:
3種方式創建流,普通流調用
通過Stream接口的靜態工廠方法
通過Arrays方法
通過Collection接口的默認方法
//通過Stream接口的靜態工廠方法
Stream stream=Stream.of("hello", "world", "hello world");
String strArray=new String{"hello", "world", "hello world"};
//通過Stream接口的靜態工廠方法
Stream stream1=Stream.of(strArray);
//通過Arrays方法
Stream stream2=Arrays.stream(strArray);
List<String> list=Arrays.asList(strArray);
//通過Collection接口的默認方法
Stream stream3=list.stream;
本質都是StreamSupport.stream。
通過Collection接口的默認方法獲取并行流。
或者通過stream流調用parallel
獲取并行流
只需要對并行流調用sequential
方法就可以把它變成順序流
塊,并用不同的線程分別處理每個數據塊的流。這樣一來,你就可以自動把給定操作的工作負荷分配給多核處理器的所有內核,讓它們都忙起來。
并行流用的線程是從哪兒來的?有多少個?怎么自定義這個過程呢?
并行流內部使用了默認的ForkJoinPool,它默認的線程數量就是你的處理器數量,這個值是由Runtime.getRuntime.availableProcessors得到的。但是你可以通過系統屬性 java.util.concurrent.ForkJoinPool.common. parallelism來改變線程池大小,如下所示:
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");
這是一個全局設置,因此它將影響代碼中所有的并行流。反過來說,目前還無法專門為某個
并行流指定這個值。一般而言,讓ForkJoinPool的大小等于處理器數量是個不錯的默認值,
除非你有很好的理由,否則我們強烈建議你不要修改它
測試并行流和順序流速度
//Sequential Sort, 采用順序流進行排序
@Test
public void sequentialSort{
long t0=System.nanoTime;
long count=values.stream.sorted.count;
System.err.println("count=" + count);
long t1=System.nanoTime;
long millis=TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));
//sequential sort took: 1932 ms
}
//parallel Sort, 采用并行流進行排序
@Test
public void parallelSort{
long t0=System.nanoTime;
long count=values.parallelStream.sorted.count;
System.err.println("count=" + count);
long t1=System.nanoTime;
long millis=TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms", millis));
//parallel sort took: 1373 ms 并行排序所花費的時間大約是順序排序的一半。
}
class Accumlator{
public long total=0;
public void add(long value) {
total +=value;
}
}
public class ParallelTest {
public static void main(String[] args) {
//錯誤使用并行流示例
System.out.println("SideEffect parallel sum done in :" + measureSumPerf(ParallelTest::sideEffectParallelSum, 1_000_000_0) + "mesecs");
System.out.println("=================");
//正確應該這樣的
System.out.println("SideEffect sum done in :" + measureSumPerf(ParallelTest::sideEffectSum, 1_000_000_0) + "mesecs");
}
//錯誤使用并行流
public static long sideEffectParallelSum(long n) {
Accumlator accumlator=new Accumlator;
LongStream.rangeClosed(1, n).parallel.forEach(accumlator::add);
return accumlator.total;
}
//正確使用流
public static long sideEffectSum(long n) {
Accumlator accumlator=new Accumlator;
LongStream.rangeClosed(1, n).forEach(accumlator::add);
return accumlator.total;
}
//定義測試函數
public static long measureSumPerf(Function<Long, Long> adder, long n) {
long fastest=Long.MAX_VALUE;
//迭代10次
for (int i=0; i < 2; i++) {
long start=System.nanoTime;
long sum=adder.apply(n);
long duration=(System.nanoTime-start)/1_000_000;
System.out.println("Result: " + sum);
//取最小值
if (duration < fastest) {
fastest=duration;
}
}
return fastest;
}
}
本質問題在于total +=value;它不是原子操作,并行調用的時候它會改變多個線程共享的對象的可變狀態,從而導致錯誤,在使用并行流需要避免這類問題發生!
思考:什么情況結果正常,但是并行流比順序流慢的情況呢???
并行流中更新共享變量,如果你加入了同步,很可能會發現線程競爭抵消了并行帶來的性能提升!
特別是limit和findFirst等依賴于元素順序的操作,它們在并行流上執行的代價非常大
對于較小的數據量,選擇并行流幾乎從來都不是一個好的決定。并行處理少數幾個元素的好處還抵不上并行化造成的額外開銷。
備注:sort或distinct等操作接受一個流,再生成一個流(中間操作),從流中排序和刪除重復項時都需要知道所有集合數據,如果集合數據很大可能會有問題(如果數據大,都放內存,內存不夠就會OOM了)。
使用并行流還是順序流都應該應該測試,以及壓測,如果在并行流正常的情況下,效率有提升就選擇并行流,如果順序流快就選擇順序流。
等待 Future 集合中的所有任務都完成。
通過編程方式完成一個 Future 任務的執行(即以手工設定異步操作結果的方式)。
新的CompletableFuture將使得這些成為可能。
CompletableFuture
提供了四個靜態方法用來創建CompletableFuture對象:
方法入參和返回值有所區別。
里面有非常多的方法,返回為CompletableFuture之后可以用鏈式編程.的形式繼續調用,最后調用一個不是返回CompletableFuture的介紹,和流式操作里面的中間操作-終止操作。
/**
* 可以使用Instant代替Date
* LocalDateTime代替Calendar
* DateTimeFormatter代替SimpleDateFormat
*/
public static void main(String args[]) {
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now=LocalDateTime.now;
System.out.println(now.format(formatter));
//10分鐘前
String d1=now.minusMinutes(10).format(formatter);
//10分鐘后
String d2=now.plusMinutes(10).format(formatter);
System.out.println(d1);
System.out.println(d2);
LocalDateTime t5=LocalDateTime.parse("2019-01-01 00:00:00", formatter);
System.out.println(t5.format(formatter));
}
去除了永久代(PermGen) 被元空間(Metaspace)代替 配置:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m 代替 -XX:PermSize=10m -XX:MaxPermSize=10m
Optional<String> optStr=Optional.empty;
上面的示例代碼調用empty方法創建了一個空的Optional
2、 創建對象:不允許為空Optional提供了方法of用于創建非空對象,該方法要求傳入的參數不能為空,否則拋PointException,示例如下:
Optional<String> optStr=Optional.of(str); // 當str為的時候,將拋出PointException
3、創建對象:允許為空如果不能確定傳入的參數是否存在值的可能性,則可以用Optional的ofable方法創建對象,如果入參為,則創建一個空對象。示例如下:
Optional<String> optStr=Optional.ofable(str); // 如果str是,則創建一個空對象
String str=;
len=Optional.ofable(str).map(String::length).orElse(0); //不會報PointerException
END
*請認真填寫需求信息,我們會在24小時內與您取得聯系。