代码规范 : 防御式编程

防御式编程

保护程序免遭非法输入数据的破坏

  • 垃圾进,什么都不出: 三种方法处理进来的垃圾情况

    • 检查所有来源于外部的数据的值,防止能攻击你系统的数据(sql注入命令,html注入,整数溢出,传给系统调用的数据)
  • 检查子程序所有输入参数的值
  • 决定如何处理错误的输入数据

断言

断言在程序运行错误时强制退出程序,有人认为开发阶段为了检测到所有bug,就应该将打上断言, 越能让你犯错,程序上线后的bug越少

错误处理技术

  • 返回中立值:初始化一个值,然后程序运行失败时也能有返回的值
  • 换用下一个正确的数据: 在处理数据流的时候,有时只需返回下一个正确的数据即可,为了处理完所有的数据
  • 返回与前次相同的数据
  • 把警告信息记录到日志文件中
  • 返回一个错误码
  • 调用错误处理子程序或对象,把错误处理集中在一个全局的错误处理的子程序或对象中
  • 当错误发生时,显示出错消息,把错误处理的开销减到最少
  • 用最妥当的方式在局部处理错误
  • 关闭程序:一检测错误就关闭程序
  • 健壮性与正确性:
    • 人身安全有关的软件,显示正确性而非健壮性,不返回结果,停止程序 胜于 返回结果
    • 消费类的软件 注重健壮性,返回一些结果比软件停止有用
  • 高层次设计多错误处理方式的影响

异常

  • 提供一种无法被忽略的错误通知机制,其他错误处理机制有可能会导致错误在不知不觉中向外扩散,而异常则消除了这种可能性
  • 只在真正例外的情况下抛出异常,异常应用来处理 那些不仅仅罕见,而且永远不该发生的情况
  • 不要用异常来推卸责任: 如果某种错误的情况可以在局部处理,就应该在局部处理
  • 避免在构造函数和析构函数中抛出异常,除非你在同一地方把它们捕获 :
  • 在恰当的抽象层次抛出异常: 否则会暴露内部的实现细节
  • 在异常消息中加入关于导入异常发生的全部信息:如果异常是因为一个数组下标错误而抛出的,就应该在异常消息中包含数组的上界,下界以及非法的下标值等信息.
  • 避免使用空的catch 语句: 在catch 里没有任何的处理,这就意味着,要么try的代码不对,无故抛出一个异常,要么是catch 里的代码不对,不能有效处理一个异常
  • 了解你的所用的函数库可能会抛出的异常
  • 考虑创建一个集中的异常报告机制
  • 包项目的异常的使用 标准化:为了保持对异常处理尽可能的便于管理
  • 考虑是否真的需要处理异常

隔离程序:使之包容由错误造成的损害
划分层次

  • UI和 外部文件 <==> 数据 <==> 内部类

  • 在 不同层之间 可以设置一个交换层,用来处理脏数据的保护膜

辅助调试的代码

  • 不要自动的把产品版的限制 强加的开发版之上, 就是意味着 ,可以在开发版加一些可能会轻微损耗资源的操作,来方便调试
  • 尽早引入辅助调试的代码
  • 采用进攻式编程 : 在开发阶段,显现出来所有 可能会出现,实际中不能出现的控制语句:例如,case if 中加入 默认的 语句 else default
    • 确保断言语句使程序终止运行,让问题引起的麻烦越大越好,这样修复的可能性大
    • 完全填充分配到所哟的内存,这样可以检测出内存分配的错误
    • 完全填充已分配到所有的文件或流,这样排查文件格式错误
    • 保证case 语句中有default ,if 后面有 else
      开发时惨痛的失败,会让你在发布产品时不会败得太惨
  • 计划移除调试辅助的代码: 编写自己的预处理器, 来处理(移除) //# BEGIN DEBUG 和 //# END DEBUG 之间的代码*

确定在产品代码中该保留多少的防御式代码

  • 保留那些检查重要错误的代码
  • 去除检查细微错误的代码
  • 去掉可以导致程序硬性崩溃的代码,比如断言
  • 保留让程序稳妥崩溃的代码
  • 为你的技术支持人员记录错误信息
  • 确认留在代码中错误消息是友好的

对防御式编程采取防御姿态

  • 过度的防御性会导致程序臃肿, 运行缓慢
  • 考虑什么地方需要进行防御,然后调整防御式编程的优先级别

对数据处理

  • 浮点数

    避免数量级相差巨大的数之间的加减运算
    避免等量判断: 应该用相减,得到的数字和允许出现溢出的范围内的小数进行比较.
    处理舍入误差的问题
    检查语言和函数库对特定数据类型的支持

  • 枚举类型
    枚举类型提高可靠性
    可将枚举类型作为布尔值的替换方案
    检查非法数值: if 语句 或者 case 语句中 使用枚举作为判断条件的值

  • 数组:
    确认所有数组小标没有超出数组边界
    考虑容器来取代数组,或者将数组作为顺序化接口来处理:当你习惯性的选用数组之前,考虑能否用其他可以顺序访问数据的容器类作为代替方案-例如:集合,栈,队列等
    检查数组的边界点
    如果数组是多维的,确认下标是否要使用更有意义的名字:使用 i 和 j 这样不清晰的下标,可能会带来误导
    地方下标串话

异常

  • 【强制】不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,
  • 【强制】异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低
  • 【强制】对大段代码进行 try-catch,这是不负责任的表现。 catch 时请分清稳定代码和非稳
    定代码,稳定代码指的是无论如何不会出错的代码。
  • 捕获所有的异常 :
  • 【强制】有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回
    滚事务
  • jdk 1.7 之前,finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
  • jdk 1.7 开始 ,使用 try-with-resources 方式。
  • 考虑到会出现 NPE(not point exception) ,所以使用集合时,如果是null 的, 请返回空的集合
  • 防止 NPE 出现的情况:

    1. ”string”.equals(null)
    2. 返回int 值时可能会有空的情况 public int f(){ return Integer 对象}; 如果为 null,自动解箱抛 NPE。
    3. 数据库的查询结果可能为 null。
    4. 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null
    5. 远程调用返回对象,一律要求进行 NPE 判断。
    6. 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
    7. 级联调用 obj.getA().getB().getC(); 一连串调用,易产生 NPE。
  • 错误码的设计:公司外的 http/api 开放接口必须使用“错误码”;

  • 用异常代替错误码,这条和上一条相反,这是为了方便测试
  • 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上抛
  • 避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。

其他

  • final 可提高程序响应效率,声明成 final 的情况:

    • 1) 不需要重新赋值的变量,包括类属性、局部变量。
    • 2) 对象参数前加 final,表示不允许修改引用的指向。
    • 3) 类方法确定不允许被重写。
  • Math.random()

    这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常) ,如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。

  • 获取当前毫秒数 System.currentTimeMillis();

  • 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。

  • 对于“明确停止使用的代码和配置”,如方法、变量、类、配置文件、动态配置属性等要坚决从程序中清理出去,避免造成过多垃圾。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值