java基础 (四)

来源:互联网 发布:中等职业教育网络课程 编辑:程序博客网 时间:2024/04/29 17:20
  • 数组声明的时候使用 int[] index,而不要使用 int index[] 说明:看起来更符合人的阅读习惯。
  • 将局部变量的作用域最小化:变量的声明应该尽可能靠近变量第一次使用的位置
  • 给if、for、do、while、switch等语句的执行体加大括号{} (即使只有一条语句)
  • 避免随意进行类型强制转换,应改善设计,或在转换前用instanceof进行判断(object instanceof class)
  • 基本类型优于包装类型,注意合理使用包装类型(for循环用的是基本类型,定义lang,则不停转换)
  • 方法设计应遵循单一抽象层次原则(SLAP)
    说明:SLAP原则,是指让一个方法中所有的操作处于相同的抽象层。否则跳跃的代码的抽象层次破坏了代码的流畅性
  • 当父类和子类有同名静态方法时,声明父类变量引用子类实例,调用该静态方法时调用的是父类的静态方法,而非子类的静态方法

类和接口的设计应遵循面向对象SOLID设计原则

  • 单一职责原则(Single Responsibility Principle)
  • 开放封闭原则(Open Closed Principle)
  • 里氏替换原则(Liskov Substitution Principle)
    说明:里氏替换原则是指,子类的实例应该能够替换任何其超类的实例。违反此原则的经典例子是,从长方形中派生一个正方形。因为,正方形要求长宽一起变化,而长方形无此限制,则在长方形出现的地方,无法用正方形来替换
  • 接口分离原则(Interface Segregation Principle)
  • 依赖倒置原则(Dependency Inversion Principle)
    说明:面向过程的开发中,高层直接调用底层,造成高层依赖于底层的具体实现。依赖倒置原则认为,高层应该调用抽象的接口,底层则应实现这个接口,两者都依赖于接口。这样解除了高层和底层的耦合。
  • 说明:迪米特法则(Law of Demeter)又叫最少知识原则(Least Knowledge Principle),初衷在于降低类之间的耦合。
  • 覆写(override)——子类与父类间一个实例方法可以覆写(override)在其超类中可访问到(非private)的具有相同签名的实例方法(非static)
  • 在接口定义中,属性已缺省具有public static final修饰词,方法已缺省具有public abstract修饰词。
  • 日志工具对象logger应声明为private static final
  • 使用System.arraycopy()进行数组复制
  • 在Java的IO操作中,尽量使用带缓冲的实现 .对于性能要求更高的实现,可以使用Java NIO
  • 字符串大小写转换时,应加上Locale.US, (例如:testString.toUpperCase(Local.US));

安全

信任边界:位于信任边界之内的所有组件都是被系统本身直接控制的。所有来自不受控的外部系统的连接与数据,包括客户端与第三方系统,都应该被认为是不可信的,要先在边界处对其校验,才能允许它们进一步与本系统交互。

  • 接受已知好的数据
    这种策略被称为“白名单”或者“正向”校验。这种策略检查数据是否属于一个严格约束的、已知的、可接受的合法数据集合。例如:用正则表达式控制的形式就属于此类。
  • 拒绝已知坏的数据
    这种策略被称为“黑名单”或者“负向”校验,相对于正向校验,这是一种较弱的校验方式。这种策略比较危险,因为潜在的不合法数据可能是一个不受约束的无限集合。采取这种策略意味着你必须一直维护一个已知不合法字符或者模式的列表。除非对不合法的正则表达式进行日常更新以及定期研究新的攻击方式,否则程序中的校验就会很快过时。
  • “白名单”方式净化
    对数据中任何不属于某个已验证的、合法字符列表的字符进行删除、编码、或者替换,然后后再使用这些净化后的数据。以下给出了一些列子:如果你期望接收一个电话号码,那么你可以剔除掉输入中所有的非数字字符,因此,“(555)123-1234”,“555.123.1234”,与“555\”;DROP TABLE USER;–123.1234”全部会被转换成“5551231234”。注意,这里你还需要继续对转换的结果进行校验。正如你所看到的,这样做不仅有利于安全,同时它允许你接收和使用一个更宽泛的有效用户输入。但是,对于一个用户评论栏的文本输入,确定一个合法的数据集合是非常困难的,因为几乎所有的字符都可以被用到。一种解决方案是:将所有非字母数字替换成其编码后的版本,那么“I like your web page!”被净化后将输出为“I+like+your+web+page%21”,这里使用了URL编码。
  • “黑名单”方式净化
    为了确保输入数据是“安全”的,可以剔除或者转换某些字符(例如,删除引号、转换成HTML实体)。跟“黑名单”校验类似,这种策略需要对不合法字符进行日常维护,并且不合法字符的范围很难保证是完整的。实际上大部分输入字段都有其特定的语法格式,相对于为了应对当前和未来所有攻击方式而引入一个复杂、迟钝的净化程序,执行一个单纯针对正确输入的正向校验更加简单,高效,与安全。
储存过程:CREATE PROCEDURE sp_queryItem    @userName varchar(50),     @itemName varchar(50) AS BEGIN     SELECT * FROM t_item      WHERE userName = @userName    AND itemName = @itemName; END GO
  • 非信任代码:非产品包中的代码,如通过网络下载到本地虚拟机中加载并执行的代码。
  • POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
  • 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。
  • 不允许任何魔法值(即未经定义的常量)直接出现在代码中。
  • Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
    正例:”test”.equals(object);
    反例:object.equals(“test”); 说
  • 关于基本数据类型与包装数据类型的使用标准如下:
    1) 所有的 POJO 类属性必须使用包装数据类型。
    2) RPC 方法的返回值和参数必须使用包装数据类型。
    3)所有的局部变量使用基本数据类型。

集合处理

  • 关于 hashCode 和 equals 的处理,遵循如下规则:
    1) 只要重写 equals,就必须重写 hashCode。
    2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的 对象必须重写这两个方法。
    3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。 说明:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象 作为 key 来使用。
  • 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全 一样的数组,大小就是 list.size()。
  • 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。 说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。
   HashMap hm=new HashMap();        hm.put("name", "zhangsan");        hm.put("age", "18");       Set< Map.Entry<String,String>> entrySet = hm.entrySet();       Iterator< Map.Entry<String,String>> iter=entrySet.iterator();       while(iter.hasNext())       {      Map.Entry<String,String> entry=iter.next();           System.out.println(entry.getKey()+"\t"+entry.getValue());       }        

并发处理

  • 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    • FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    • CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
  • SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类。
    • 正例:注意线程安全,使用 DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){         @Override    protected DateFormat initialValue()     {    return new SimpleDateFormat("yyyy-MM-dd");       }   };   

说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

  • 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
  • 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造 成死锁。
  • 使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行 至 await 方法,直到超时才返回结果。 说明:注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。
  • ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用 static 修饰。这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享 此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只 要是这个线程内定义的)都可以操控这个变量。

单元测试

  • 单元测试应该是全自动执行的,并且非交互式的。测试框架通常是定期执行的,执行 过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测 试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
  • 对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的, 或者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据
  • 和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者 对单元测试产生的数据有明确的前后缀标识。

mysql数据库

  • 表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint ( 1 表示是,0 表示否)
  • 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库 名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。
  • 主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
    说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
  • 小数类型为 decimal,禁止使用 float 和 double。
    说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
  • varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长 度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
  • 表必备三字段:id, gmt_create, gmt_modified。
    说明:其中 id 必为主键,类型为 unsigned bigint、单表时自增、步长为 1。gmt_create, gmt_modified 的类型均为 date_time 类型,前者现在时表示主动创建,后者过去分词表示被 动更新
  • 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
    说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必 然有脏数据产生
  • :count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
  • count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。