为什么java不要在java8 foreach循环环里进行元素的remove/add操作

问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
选自《阿里巴巴JAVA开发手册》
图1代码执行情况是:解释删除1这个元素不会报错,但是删除2这个元素报错了,这个情况如何解释?
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
从报的错误中可以知道错误的来源 checkForComodification() ,如果要避免错误需要保持 modCount != expectedModCount 为
false 。list.remove(Object)会去调用fastRemove(int)方法,这个时候必然会去修改 modCount ,这个时候就会出现错误。Iterator&String& iterator = list.iterator() ;这个方法的实现就是返回一个内部类 Itr,(迭代的过程都是使用的这个类),但是为什么这个 iterator.remove() 不会出现错误了,原因在与这个方法的实现是在进行实际的 ArrayList.this.remove 之前进行的 checkForComodfication 检查,remove 之后又使 expectedModCount = modCount,所以不会出现错误。
Itr.remove 的实现
public void remove() {
if (lastRet & 0)
throw new IllegalStateException();
checkForComodification();
ArrayList.this.remove(lastRet);
cursor = lastR
lastRet = -1;
expectedModCount = modC
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
如果有不对的地方请指出
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
单线程的情况下,在遍历List时删除元素,必须要用Iterator的remove方法而不能使用List的remove方法,否则会ConcurrentModificationException。试想如果一个老师正在点整个班级所有学生的人数,而学生如果不遵守纪律一会出去一会进来,老师肯定点不下去。
多线程的情况下,参考我的一篇博客:
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
首先,这涉及多线程操作,Iterator是不支持多线程操作的,List类会在内部维护一个modCount的变量,用来记录修改次数举例:ArrayList源码
protected transient int modCount = 0;
每生成一个Iterator,Iterator就会记录该modCount,每次调用next()方法就会将该记录与外部类List的modCount进行对比,发现不相等就会抛出多线程编辑异常。
为什么这么做呢?我的理解是你创建了一个迭代器,该迭代器和要遍历的集合的内容是紧耦合的,意思就是这个迭代器对应的集合内容就是当前的内容,我肯定不会希望在我冒泡排序的时候,还有线程在向我的集合里插入数据对吧?所以Java用了这种简单的处理机制来禁止遍历时修改集合。
至于为什么删除“1”就可以呢,原因在于foreach和迭代器的hasNext()方法,foreach这个语法糖,实际上就是
while(itr.hasNext()){
itr.next()
所以每次循环都会先执行hasNext(),那么看看ArrayList的hasNext()是怎么写的:
public boolean hasNext() {
return cursor !=
cursor是用于标记迭代器位置的变量,该变量由0开始,每次调用next执行+1操作,于是:你的代码在执行删除“1”后,size=1,cursor=1,此时hasNext()返回false,结束循环,因此你的迭代器并没有调用next查找第二个元素,也就无从检测modCount了,因此也不会出现多线程修改异常但当你删除“2”时,迭代器调用了两次next,此时size=1,cursor=2,hasNext()返回true,于是迭代器傻乎乎的就又去调用了一次next(),因此也引发了modCount不相等,抛出多线程修改的异常。
当你的集合有三个元素的时候,你就会神奇的发现,删除“1”是会抛出异常的,但删除“2”就没有问题了,究其原因,和上面的程序执行顺序是一致的。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
倒序删除就可以了,反正list尽量不要remove。可以加delete标记
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
文档中那个黄色的说明很有意思。
这个例子的执行结果会出乎大家的意料,那么试下把“1”换成“2”,会是同样的结果吗?
这个还是要看ArrayList的源码,一看便知。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
倒序删除就可以了
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
ArrayList不是线程安全的,这样相当于你在遍历的时候修改了List。ArrayList在这种情况下是会抛出并发修改异常的。
同步到新浪微博
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:Java程序员们最常犯的10个错误 - ImportNew
1.将数组转化为列表
将数组转化为一个列表时,程序员们经常这样做:
List&String& list = Arrays.asList(arr);
Arrays.asList()会返回一个ArrayList对象,ArrayList类是Arrays的一个私有静态类,而不是java.util.ArrayList类,java.util.Arrays.ArrayList类有set()、get()、contains()方法,但是没有增加元素的方法,所以它的大小是固定的,想要创建一个真正的ArrayList类,你应该这样做:
ArrayList&String& arrayList = new ArrayList&String&(Arrays.asList(arr));
ArrayList的构造方法可以接受一个集合类型,刚好它也是java.util.Arrays.ArrayList的超类。
2.判断一个数组是否包含一个值
程序员们经常这样做:
Set&String& set = new HashSet&String&(Arrays.asList(arr));
return set.contains(targetValue);
这段代码起作用,但是没有必要把一个数组转化成列表,转化为列表需要额外的时间。它可以像下面那样简单:
Arrays.asList(arr).contains(targetValue);
for(String s:arr){
if(s.equals(targetValue)){
第一种方法比第二种更容易读
3.在一个循环中删除一个列表中的元素
思考下面这一段在循环中删除多个元素的的代码
ArrayList&String& list = new ArrayList&String&(Arrays.asList(&a&,&b&,&c&,&d&));
for(int i=0;i&list.size();i++){
list.remove(i);
System.out.println(list);
输出结果是:
在这个方法中有一个严重的错误。当一个元素被删除时,列表的大小缩小并且下标变化,所以当你想要在一个循环中用下标删除多个元素的时候,它并不会正常的生效。
你也许知道在循环中正确的删除多个元素的方法是使用迭代,并且你知道java中的foreach循环看起来像一个迭代器,但实际上并不是。考虑一下下面的代码:
ArrayList&String& list = new ArrayList&String&(Arrays.asList(&a&,&b&,&c&,&d&));
for(String s:list){
if(s.equals(&a&)){
list.remove(s);
它会抛出一个ConcurrentModificationException异常。
相反下面的显示正常:
ArrayList&String& list = new ArrayList&String&(Arrays.asList(&a&,&b&,&c&,&d&));
Iterator&String& iter = list.iterator();
while(iter.hasNext()){
String s = iter.next();
if(s.equals(&a&)){
iter.remove();
.next()必须在.remove()之前调用。在一个foreach循环中,编译器会使.next()在删除元素之后被调用,因此就会抛出ConcurrentModificationException异常,你也许希望看一下ArrayList.iterator()的源代码。
4.Hashtable与HashMap的对比
就算法而言,哈希表是数据结构的一个名字。但是在java中,这个数据结构的名字是HashMap。Hashtable与HashMap的一个重要不同点是Hashtable是同步的。所以你经常不需要Hashtable,相反HashMap经常会用到。
5.在集合中使用原始类型
在Java中原始类型与无界通配符类型很容易混合在一起,拿Set来说,Set是一个原始类型,而Set&?&是无界的通配符类型。
考虑下面使用原始类型List作为参数的代码:
public static void add(List list,Object o){
list.add(o);
pulbic static void main(String[] args){
List&String& list = new ArrayList&String&();
add(list,10);
String s = list.get(0);
这段代码会抛出一个异常:
Exception in thread &main& java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
使用原生类型集合是危险的,这是因为原生类型集合跳过了泛型类型检查,并且不是安全的,在Set、Set&?&和Set&Object&中有很大的不同,具体请看和。
6.访问级别
程序员们经常使用public作为类字段的修饰符,可以很简单的通过引用得到值,但这是一个坏的设计,按照经验,分配给成员变量的访问级别应该尽可能的低。
7.ArrayList与LinkedList的对比
当程序员们不知道ArrayList与LinkedList的区别时,他们经常使用ArrayList,因为它看起来比较熟悉。然而,它们之前有巨大的性能差别。简而言之,如果有大量的增加删除操作并且没有很多的随机访问元素的操作,应该首先LinkedList。如果你刚接触它们,请查看 来获得更多关于它们性能的信息。
8.可变与不可变
不可变对象有许多的优点,比如简单,安全等等。但是对于每一个不同的值都需要一个独立的对象,太多的对象可能会造成大量的垃圾回收。当选择可变与不可变时应该有一个平衡。
一般的,可变对象用来避免产生大量的中间对象。一个典型的例子是连接大量的字符串。如果你用一个不可变的字符串,你会产生很多需要进行垃圾回收的对象。这很浪费CPU的时间,使用可变对象是正确的解决方案(比如StringBuilder)。
String result=&&;
for(String s: arr){
result = result +
有时在某些情况下也是需要可变对象的,比如将可变对象作为参数传入方法,你不用使用很多语句便可以得到多个结果。另外一个例子是排序和过滤:当然,你可以写一个方法来接收原始的集合,并且返回一个排好序的集合,但是那样对于大的集合就太浪费了。(来自StackOverFlow的)
9.父类与子类的构造函数
这个编译期错误的出现是父类默认的构造方法未定义,在java中,如果一个类没有定义构造方法,编译器会默认的为这个类添加一个无参的构造方法。如果在父类中定义了构造方法,在这个例子中是Super(String s),编译器就不会添加默认的无参构造方法,这就是上面这个父类的情形。
子类的构造器,不管是无参还有有参,都会调用父类的无参构造器。因为编译器试图在子类的两个构造方法中添加super()方法。但是父类默认的构造方法未定义,编译器就会报出这个错误信息。
想要修复这个问题,可以简单的通过1)在父类中添加一个Super()构造方法,像下面这样:
public Super(){
System.out.println(&Super&);
或者2)移除父类自定义的构造方法,或者3)在子类的构造方法中调用父类的super(value)方法。
10.”"还是构造器
有两种方式可以创建字符串
//1.使用字符串
String x = &abc&;
//2.使用构造器
String y = new String(&abc&);
有什么区别?
下面的例子会给出一个快速的答案:
String a = &abc&;
String b = &abc&;
System.out.println(a==b);//true
System.out.println(a.equals(b));//true
String c = new String(&abc&);
String d = new String(&abc&);
System.out.println(c==d);//false
System.out.println(c.equals(d));//true
关于它们内存分配的更多信息,请参考
将来的工作
这个列表是我基于大量的github上的开源项目,Stack overflow上的问题,还有一些流行的google搜索的分析。没有明显示的评估证明它们是前10,但它们绝对是很常见的。如果您不同意任一部分,请留下您的评论。如果您能提出其它一些常见的错误,我将会非常感激。
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(新浪微博:)
Hi,请到伯乐在线的小组发帖提问,支持微信登录。链接是: http://group.jobbole....
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2017 ImportNew博客分类:
引用
foreach中循环次数操作 11:42jstl 的foreach语句中有可以获得循环次数的变量,以前不清楚,这次要使用,在网上查了一下,记下来,方便以后学习。
&c:forEach&标签的语法定义如下所示。
&c:forEach var="name" items="expression" varStatus="name"
begin="expression" end="expression" step="expression"&
body content
&/c:forEach&
&c:forEach&标签具有以下一些属性:
l var:迭代参数的名称。在迭代体中可以使用的变量的名称,用来表示每一个迭代变量。类型为String。
l items:要进行迭代的集合。对于它所支持的类型将在下面进行讲解。
l varStatus:迭代变量的名称,用来表示迭代的状态,可以访问到迭代自身的信息。
l begin:如果指定了items,那么迭代就从items[begin]开始进行迭代;如果没有指定items,那么就从begin开始迭代。它的类型为整数。
l end:如果指定了items,那么就在items[end]结束迭代;如果没有指定items,那么就在end结束迭代。它的类型也为整数。
l step:迭代的步长。
&c:forEach&标签的items属性支持Java平台所提供的所有标准集合类型。此外,您可以使用该操作来迭代数组(包括基本类型数组)中的元素。它所支持的集合类型以及迭代的元素如下所示:
l java.util.Collection:调用iterator()来获得的元素。
l java.util.Map:通过java.util.Map.Entry所获得的实例。
l java.util.Iterator:迭代器元素。
l java.util.Enumeration:枚举元素。
l Object实例数组:数组元素。
l 基本类型值数组:经过包装的数组元素。
l 用逗号定界的String:分割后的子字符串。
l javax.servlet.jsp.jstl.sql.Result:SQL查询所获得的行。
不论是对整数还是对集合进行迭代, &c:forEach&的varStatus属性所起的作用相同。和var属性一样,varStatus用于创建限定了作用域的变量(改变量只在当前标签体内起作用)。不过,由varStatus属性命名的变量并不存储当前索引值或当前元素,而是赋予javax.servlet.jsp.jstl.core.LoopTagStatus类的实例。该类包含了一系列的特性,它们描述了迭代的当前状态,如下这些属性的含义如下所示:
l current:当前这次迭代的(集合中的)项。
l index:当前这次迭代从0开始的迭代索引。
l count:当前这次迭代从1开始的迭代计数。
l first:用来表明当前这轮迭代是否为第一次迭代,该属性为boolean类型。
l last:用来表明当前这轮迭代是否为最后一次迭代,该属性为boolean类型。
l begin:begin属性的值。
l end:end属性的值
l step:step属性的值
下面就来看一个个基本的例子,表格隔行背景色变化
&c:forEach var="item" items="${contents}" varStatus="status"&
&tr &c:if test="${status.count%2==0}"&bgcolor="#CCCCFE" &/c:if& align="left"&
&/c:forEach&
为了明确理解index和count的区别,我做了一个测试:
&c:forEach items="${allList}" var="al" varStatus="vs"&
&&&&& ${vs.index }&& ${vs.count }
&&& &br/&
&& &/c:forEach&
输出结果为:
浏览 15724
浏览: 40560 次
来自: 北京
楼主你好!我运行你的例子时出现个问题使我难以继续!请指教 Un ...
文章不错的
解释的很详细。
用的是Netbeans
你用什么开发工具写的?代码看起来好乱啊!【强制】不要在 foreach 循环里进行元素的 remove/add 操作。
remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
反例: List a = new ArrayList();
&a.add(&1&);&
&a.add(&2&);
&for (String temp : a) { if(&2&.equals(temp)){ a.remove(temp); } }&
说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“2”换成“1”,会是同样的 结果吗?&
&Iterator it = a.iterator(); while(it.hasNext()){ String temp = it.next(); if(删除元素的条件){ it.remove(); } }
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:6299次
排名:千里之外
原创:22篇
(4)(1)(3)(5)(2)(3)(3)(3)(7)(1)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'阿里巴巴Java开发手册快速学习
善医者“未有形而除之”,提高工程健壮性最好的方式是在代码出现问题之前就排除掉,不给Bug出现的机会。一份好的开发规范就可以起到这样的作用,大大减少产品上线后的问题。《阿里巴巴Java开发手册》是阿里巴巴的内部编码规范,阿里官方的Java代码规范标准,这份开发手册不仅规范了一些开发细节,也提出了很多工程开发的哲学,值得好好阅读。
Java作为一门名副其实的工业级语言,语法友好,学习简单,大规模的应用给代码质量的管控带来了困难,特别是团队开发中,开发过程中的规范会直接影响最终项目的稳定性。
善医者“未有形而除之”,提高工程健壮性最好的方式是在代码出现问题之前就排除掉,不给Bug出现的机会。一份好的开发规范就可以起到这样的作用,大大减少产品上线后的问题。
《阿里巴巴Java开发手册》是阿里巴巴的内部编码规范,阿里官方的Java代码规范标准, 手册以Java应用开发为维度,分为编程规约、异常日志规约、MYSQL规约、工程规约、安全规约五个章节,给出了强制、推荐、参考三个级别,每条规范都有推荐的约束力度,从命名到项目拆分,不仅规范了一些开发细节,也提出了很多工程开发的哲学,值得好好阅读。
下面记录一些比较有启发的条款,提纲挈领,快速学习。
一、编程规约
1.如果使用到了设计模式,建议在类名中体现出具体模式
将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
2.相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object
可变参数必须放置在参数列表的最后,尽量不用可变参数编程。
3.对外暴露的接口签名,原则上不允许修改方法签名,避免对接口调用方产生影响
接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
4.关于基本数据类型与包装数据类型的使用标准如下
1) 所有的POJO类属性必须使用包装数据类型
2) RPC方法的返回值和参数必须使用包装数据类型
3) 所有的局部变量【推荐】使用基本数据类型
POJO 类属性没有初值是醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。数据库的查询结果可能是null,因为自动拆箱,用基本数据类型接收有NPE风险。
5.注意 serialVersionUID 不一致会抛出序列化运行时异常
序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。
6.POJO 类必须写 toString 方法
使用 IDE 的中工具:source& generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。 在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。
7.final 可提高程序响应效率,声明成 final 的情况:
1) 不需要重新赋值的变量,包括类属性、局部变量
2) 对象参数前加final,表示不允许修改引用的指向
3) 类方法确定不允许被重写
8.慎用 Object 的 clone 方法来拷贝对象
对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象 的拷贝。
9.类成员与方法访问控制从严
1) 如果不允许外部直接通过new来创建对象,那么构造方法必须是private
2) 工具类不允许有public或default构造方法
3) 类非static成员变量并且与子类共享,必须是protected 4) 类非static成员变量并且仅在本类使用,必须是private
5) 类static成员变量如果仅在本类使用,必须是private
6) 若是static成员变量,必须考虑是否为final
7) 类成员方法只供类内部调用,必须是private
8) 类成员方法只对继承类公开,那么限制为protected
任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。思考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 Service 方法,或者一个 public 的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,如果无限制的到处跑,那么你会担心的。
10.ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常
subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。
11.使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法
使用add/remove/clear 方法会抛出 UnsupportedOperationException 异常。asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
12.不要在 foreach 循环里进行元素的 remove/add 操作
remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
13.获取单例对象需要保证线程安全,其中的方法也要保证线程安全
资源驱动类、工具类、单例工厂类都需要注意。
14.线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
15.SimpleDateFormat 是线程不安全的类,一般不要定义为static变量
如果定义为static,必须加锁,或者使用 DateUtils 工具类。 注意线程安全,使用 DateUtils。亦推荐如下处理:
private static final ThreadLocal&DateFormat& df = new ThreadLocal&DateFormat&() { @Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
16.高并发时,同步调用应该去考量锁的性能损耗
能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
17.并发修改同一记录时,避免更新丢失
要么在应用层加锁,要么在缓存加锁,要么在 数据库层使用乐观锁,使用 version 作为更新依据。 如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。
18.对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁
19.使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用countDown
方法,线程执行代码注意 catch 异常,确保 countDown 方法可以执行,避免主线程无法执行 至 await 方法,直到超时才返回结果。注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。
20.避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。
Random 实例包括 java.util.Random 的实例或者 Math.random()实例。
21.volatile 解决多线程内存不可见问题
对于一写多读,是可以解决变量同步问题, 但是如果多写,同样无法解决线程安全问题。如果是 count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。
22.ThreadLocal 无法解决共享对象的更新问题,建议使用 static 修饰
这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享 此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。
二、异常日志
1.对大段代码进行 try-catch,这是不负责任的表现
catch 时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分 异常类型,再做对应的异常处理。
2.捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之
如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
3.在代码中使用“抛异常”还是“返回错误码”
对于公司外的 http/api 开放接口必须 使用“错误码”;而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封 装 isSuccess、“错误码”、“错误简短信息”。
4.避免出现重复的代码(Don’t Repeat Yourself),即DRY原则
随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。
5.对trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方
6.异常信息应该包括两类信息:案发现场信息和异常堆栈信息
如果不处理,那么往上抛。
三、MySQL 规约
1.表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint ( 1表示是,0表示否),此规则同样适用于odps建表。 任何字段如果为非负数,必须是unsigned。
2.小数类型为 decimal,禁止使用 float 和 double
float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
3.表必备三字段:id, gmtcreate, gmtmodified
其中id必为主键,类型为unsigned bigint、单表时自增、步长为1。gmtcreate, gmtmodified 的类型均为 date_time 类型。
4.单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。避免过度设计。
5.业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引
6.在 varchar 字段上建立索引时,必须指定索引长度
没必要对全字段建立索引,根据实际文本区分度决定索引长度。 说索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分 度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度 来确定。
7.利用覆盖索引来进行查询操作,来避免回表操作
能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种 效果,用explain的结果,extra列会出现:using index。如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作
8.利用延迟关联或者子查询优化超多分页场景
MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
9.SQL 性能优化的目标
至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 2)ref 指的是使用普通的索引(normal index)。 3)range 对索引进行范围检索。
10.不要使用 count(列名)或 count(常量)来替代 count(*)
count()就是 SQL92 定义 的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 count()会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
11.使用 ISNULL()来判断是否为 NULL 值
注意,NULL与任何值的直接比较都为 NULL
12.不得使用外键与级联,一切外键概念必须在应用层解决
外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
13.iBATIS 自带的 queryForList(String statementName,int start,int size)不推荐使用
其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList 取 start,size 的子集合,线上因为这个原因曾经出现过 OOM。
14.不要写一个大而全的数据更新接口
传入为 POJO 类,不管是不是自己的目标更新字段, 都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。
执行 SQL 时,尽量不要更新无改动的字段,一是易出错;二是效率低;三是 binlog 增加存储。
四、工程规约
1.高并发服务器建议调小 TCP 协议的 time_wait 超时时间
操作系统默认 240 秒后,才会关闭处于 timewait 状态的连接,在高并发访问下,服 务器端会因为处于 timewait 的连接数太多,可能无法建立新的连接,所以需要在服务器上 调小此等待值。 正例:在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值(秒): net.ipv4.tcpfintimeout = 30
2.调大服务器所支持的最大文件句柄数(File Descriptor,简写为fd)
主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对应于一个 fd。主流的 linux 服务器默认所支持最大 fd 数量为 1024,当并发连接数很大时很 容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 建议将 linux 服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
五、安全规约
1. 隶属于用户个人的页面或者功能必须进行权限控制校验
防止没有做水平权限校验就可随意访问、操作别人的数据,比如查看、修改别人的订单。
2. 用户敏感数据禁止直接展示,必须对展示数据脱敏
查看个人手机号码会显示成:158****9119,隐藏中间 4 位,防止隐私泄露。
3. 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库
4. 用户请求传入的任何参数必须做有效性验证
忽略参数校验可能导致: page size过大导致内存溢出 恶意order by导致数据库慢查询 任意重定向 SQL注入 反序列化注入 正则输入源串拒绝服务ReDoS——Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题, 但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的效果。
5. 禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据
6. 表单、AJAX 交必须执行 CSRF 安全过滤
CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在 CSRF 漏洞的应用/网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户 不知情情况下对数据库中用户参数进行相应修改。
7. 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制, 如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损
如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其 它用户,并造成短信平台资源浪费。
8. 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略
本文为云栖社区原创内容,未经允许不得转载,如需转载请发送邮件至yqeditor@list.;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@ 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】红轴机械键盘、无线鼠标等753个大奖,先到先得,云栖社区首届博主招募大赛9月21日-11月20日限时开启,为你再添一个高端技术交流场所&&
存储过程不适合MySQL,但是,对于mssql 和oracle是非常不错的。 至于说不方便调试等问题那要看对于存储过程的熟悉程度而看
阿里云机器学习是基于阿里云分布式计算引擎的一款机器学习算法平台。用户通过拖拉拽的方式可视化的操作组件来进行试验,...
凝聚阿里巴巴多年来在无线业务安全防御的成功经验和技术成果,并面向开发者和企业提供安全扫描、应用加固、安全组件、数...
大数据开发套件(Data IDE),提供可视化开发界面、离线任务调度运维、快速数据集成、多人协同工作等功能,为您...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
MaxCompute75折抢购
Loading...

我要回帖

更多关于 java结束foreach循环 的文章

 

随机推荐