整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

全球摆脱家暴的艰难尝试:当代女性摆脱家暴到底有多难?

全球摆脱家暴的艰难尝试:当代女性摆脱家暴到底有多难?

稿丨彭镜陶

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 概述

1. 概念

JSON 全称为 JavaScript Object Notation,是一种轻量级的数据交换格式。它是 JavaScript 中用于描述对象数据的语法的扩展。不过并不限于与 JavaScript 一起使用。它采用完全独立于语言的文本格式,这些特性使 JSON 成为理想的数据交换格式。易于阅读和编写,同时也易于机器解析和生成。所有现代编程语言都支持这些数据结构,使 JSON 完全独立于语言。

2、 历史

在 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 或移动应用程序时,它非常有用。

3. 特点

JSON 的流行正是因为网站和移动应用程序需要更快捷、有效地将数据从一个系统传输到另一个系统。JSON 可以通过多种方式共享数据、存储设置以及与系统交互。它的简单性和灵活性使其适用于许多不同的情况。

JSON 最常见的用法是通过网络连接交换序列化数据。JSON 的其他常见用途包括公共、前端或内部 API、NoSQL 数据库、模式描述、配置文件、公共数据或数据导出。

JSON 的特点如下:

  • 紧凑、高效的格式:JSON 语法提供了简单的数据解析和更快的实现。
  • 易于阅读:人类和计算机都可以快速解释语法且错误最少。
  • 广泛支持:大多数语言、操作系统和浏览器都可以使用开箱即用的 JSON,这允许使用 JSON 而不存在兼容性问题。
  • 自我描述:很容易区分数据类型,并且更容易解释数据,而无需提前知道会发生什么。
  • 格式灵活:JSON 支持多种数据类型,可以组合起来表达大多数数据的结构。

二、JSON 结构和语法

JSON 易于编写和阅读,并且易于在大多数语言使用的数据结构之间进行转换。下面来看一下 JSON 的组成、JSON 支持的数据类型。

1. 结构

下面是一个最基本的 JSON 示例:

{"name": "zhangsan"}

在上面的示例中,key是 name,value 是 zhangsan。JSON 可以保存多个 key:value对:

{"name": "zhangsan", "age": 18, "city": "beijing"}

当然这只是一个简单的例子,在实际应用中 JSON 可能会多层嵌套。对象和数组是可以保存其他值的值,因此 JSON 数据可能会发生无限嵌套。这允许 JSON 描述大多数数据类型。

下面是 JSON 数据类型的完整列表:

  • string:用引号括起来的文字。
  • number:正整数或负整数或浮点数。
  • object:用花括号括起来的键值对
  • array:一个或多个 JSON 对象的集合。
  • boolean:不带引号的 true 或 false 值。
  • null:表示键值对没有数据,表示为null,不带引号。

下面是一个包含这些数据类型的 JSON 对象示例:

{
  "name": "zhangsan",
  "age": 28,
  "badperson":true,
  "child": {
    "name": "zhangxiaosan",
    "age": 8
  },
  "job": ["React", "JavaScript"],
  "wages": null,
}

2. 语法

下面来看看如何避免常见的 JSON 语法错误:

  • 始终将键值对保存在双引号内,大多数 JSON 解析器使用双引号解析 JSON 对象。
  • 切勿在 key 中使用连字符。而是使用下划线 (_)、全部小写或驼峰式大小写。
  • 使用 JSON linter 来检查 JSON 是有效的,可以使用 JSONLint 等工具进行校验。

三、JSON 解析与序列化

JSON 内置了两种方法:

  • JSON.parse() :将数据转换为 JavaScript 对象。
  • JSON.stringify() :将 JavaScript 对象转换为字符串。

1、 JSON.parse()

JSON.parse() 的语法如下:

JSON.parse(text, reviver)
  • text: 必需, 一个有效的 JSON 字符串。
  • 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

2.、 JSON.stringify()

JSON.stringify() 的语法如下:

JSON.stringify(value, replacer, space)
  • value: 必需, 要转换的 JavaScript 值(通常为对象或数组)。
  • replacer: 可选。用于转换结果的函数或数组。如果 replacer 为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:""。如果 replacer 是一个数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。当 value 参数也为数组时,将忽略 replacer 数组。
  • space: 可选,文本添加缩进、空格和换行符,如果 space 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 space 大于 10,则文本缩进 10 个空格。space 也可以使用非数字,如:\t。
const json={"name": "zhangsan", "age": 18, "city": "beijing"};
const myJSON=JSON.stringify(json);
console.log(myJSON);  // {"name":"zhangsan","age":18,"city":"beijing"}

3、 异常处理

那如果 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 操作时出现问题,这样就能确保应用程序不会因此中断。

4、 其他操作

① 删除键值对

可以使用 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 有哪些实用技巧。

1、 格式化

上面提到,可以使用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"
}

2、隐藏属性

我们知道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}

3、 过滤结果

当 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 查看器。

1、 JSON Hero

JSON Hero 是一个开源的、漂亮的 JSON 查看器,它提供了包含额外功能的干净美观的 UI,使阅读和理解 JSON 文件变得容易。

  • 以任何方式查看 JSON:列视图、树视图、编辑器视图等。
  • 自动推断字符串的内容并提供有用的预览。
  • 创建可用于验证 JSON 的推断 JSON 模式。
  • 快速扫描相关值以检查边缘情况。
  • 搜索您的 JSON 文件(键和值)。
  • 可使用键盘。
  • 具有路径支持的可轻松共享的 URL。

Github:https://github.com/jsonhero-io/jsonhero-web

2、 JSON Visio

JSON Visio 是一个 JSON 数据的可视化工具,它可以无缝地在图表上展示数据,而无需重组任何内容、直接粘贴或导入文件。

Github:https://github.com/AykutSarac/jsonvisio.com

3、 JSON Viewer Pro

JSON Viewer Pro 是一个Chrome扩展程序,主要用于可视化JSON文件。其核心功能包括:

  • 支持将JSON数据进行格式化,并使用属性或者图表进行展示。
  • 使用面包屑深入遍历 JSON 属性。
  • 在输入区写入自定义 JSON。
  • 导入本地 JSON 文件。
  • 使用上下文菜单下载 JSON 文件。
  • 网址过滤器。
  • 改变主题。
  • 自定义 CSS。
  • 复制属性和值。

输入界面如下:

格式化之后:

4、 其他工具

  • JSONLint[1]:JSON 数据的验证器。
  • JSONedit[2]:一个可视化 JSON 构建器,可以轻松构建具有不同数据类型的复杂 JSON 结构。
  • JSON API[3]:用于在 JSON 中构建 API 的规范。
  • JSON Formatter[4]:用于验证、美化、缩小和转换 JSON 数据的在线工具。
  • JSON Generator[5]:生成随机 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虽然出现很久了,但是我相信,很多公司依然在用JDK8,并没有升级到JDK9+,包括阿里、美团等一线互联网公司!但是可能我们还是有很多人并不太熟悉。本文主要就是介绍说明一些jdk8相关的内容:应该是JDK最成功的一个版本了。

主要会讲解:

  • lambda表达式
  • 方法引用
  • 默认方法
  • Stream
  • 用Optional取代
  • 新的日志和时间
  • CompletableFuture
  • 去除了永久代(PermGen) 被元空间(Metaspace)代替

我们来看看阿里规范里面涉及到jdk8相关内容:

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

lambda

JDK8最大的特性应该非lambda莫属!

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接口的实现类。

方法引用(Method references)

如果一个Lambda表达式仅仅是调用方法的情况,那么就可以用方法引用来完成,这种情况下使用方法引用代码更易读。

方法引用语法:

目标引用放在分隔符::前,方法的名称放在后面。

names2.forEach(System.out::println);//1
names2.forEach(s->System.out.println(s));//2

第二行代码的lambda表达式仅仅就是调用方法,调用的System.out的println方法,所以可以用方法引用写成System.out::println即可。

方法引用的种类(Kinds of method references)

方法引用有很多种,它们的语法如下:

  • 静态方法引用:ClassName::methodName

  • 实例上的实例方法引用:instanceReference::methodName

  • 父类的实例方法引用:super::methodName

  • 类型上的实例方法引用:ClassName::methodName

备注:String::toString 等价于lambda表达式 (s) -> s.toString

这里不太容易理解,实例方法要通过对象来调用,方法引用对应Lambda,Lambda的第一个参数会成为调用实例方法的对象。

  • 构造方法引用:Class::new

  • 数组构造方法引用:TypeName::new

个人理解:方法引用,说白了,用更好,不用也可以,如果可以尽量用!!!

Stream

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。

  • Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。

  • Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

  • 和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。

对stream的操作分为三类。

  1. 创建stream

  2. 中间操作(intermediate operations)【没有终止操作是不会执行的】

  3. 终止操作(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方法就可以把它变成顺序流

中间操作

终止操作

并行流

可以通过对收集源调用parallelStream方法来把集合转换为并行流。并行流就是一个把内容分成多个数据

块,并用不同的线程分别处理每个数据块的流。这样一来,你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核,让它们都忙起来。

并行流用的线程是从哪儿来的?有多少个?怎么自定义这个过程呢?

并行流内部使用了默认的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了)。

使用并行流还是顺序流都应该应该测试,以及压测,如果在并行流正常的情况下,效率有提升就选择并行流,如果顺序流快就选择顺序流。

`CompletableFuture`异步函数式编程

引入CompletableFuture原因

Future模式的缺点

  • Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成
  • 要么使用阻塞,在future.get的地方等待future返回的结果,这时又变成同步操作。要么使用isDone轮询地判断Future是否完成,这样会耗费CPU的资源。

Future 接口的局限性

future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的:
  • 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第
    一个的结果。
  • 等待 Future 集合中的所有任务都完成。

  • 仅等待 Future 集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同
    一个值),并返回它的结果。
  • 通过编程方式完成一个 Future 任务的执行(即以手工设定异步操作结果的方式)。

  • 应对 Future 的完成事件(即当 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));


}

JVM方面改变

去除了永久代(PermGen) 被元空间(Metaspace)代替 配置:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m 代替 -XX:PermSize=10m -XX:MaxPermSize=10m

用Optional取代

Optional对象创建

1、创建空对象
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