代码部署git实践,shell教程,截图复制工具

git安装
git菜鸟教程

Linux 一般作为服务器使用,而服务器一般放在机房,你不可能在机房操作你的 Linux 服务器。
这时我们就需要远程登录到Linux服务器来管理维护系统。
多多剪贴板
screenpress

  1. 标识符的命名应该使用具有实际含义的英文单词作为标识符的名称。
     具体的标识符包括:包名、类名、方法名、属性名、方法参数、局部变量名等;
     要求使用具有实际含义的英文单词作为标识符的名称,不应该使用汉语拼音、数字序列等作为标识符的名称,如:Class Yonghu(用户)、int C_1001都不是符合规范的标识符。
     标识符应该尽量使用完整的英文单词的组合作为标识符的名称,当需要使用缩写时,只能使用计算机领域或业务领域内公认的缩写,如:url、html等就是符合规范的缩写;缩写的大小写要求同普通英文单词,具体视标识符的类型而定。

  2. 包的路径应该使用域名的反转,包名称应该全部使用小写字母。
     如:com.hisense.hitv就是一个符合规范的包路径。
     包的名称要全部使用小写字母,即使某个子包名由多个英文单词组成,如:“serviceloader”,也应该全部使用小写字母,这是Java约定俗成的规范。

  3. 类的名称通常使用名词,并且首字母大写,如果类名由多个英文单词组成时,每个英文单词的首字母也要大写。
    不符合规范的类名如:
    public class metadata
    public class Metadatafactory
    符合规范的如:
    public class Metadata
    public class MetadataFactory
    类名的首字母大写,类名由多个英文单词组成时,每个英文单词的首字母大写,这是Java约定俗成的规范。
    类的命名规范同样适用于接口和枚举。

  4. 常见的类和接口命名。
     抽象类一般以“Abstract”开始,如:AbstractSimpleQueryParams。
     异常类的类名的最后要加上“Exception”,如:ServiceActionException。
     当接口需要和类名称区分时,可增加前缀“I”,如:ICommonDao。
     当实现类需要和接口区分时,可增加后缀“Impl”,如:CasInfoDaoImpl。
     追加功能型的接口名要在名称后面加“able”后缀,如:Cloneable。
     单元测试类的类名一般以要测试类的名称+“Test”作为测试类名,如要编写类SampleClass的单元测试类,则该单元测试命名为:SampleClassTest。

  5. 方法名称的第一个单词通常使用动词,描述方法要进行的操作;方法名称首字母小写,如果方法名由多个英文单词组成时,第二个和以后的英文单词的首字母大写。
    不符合规范的方法名如:
    public void LoadMetadata
    public ClassMetadata getclassmetadatabyname
    符合规范的方法名如:
    public void loadMetadata
    public ClassMetadata getClassMetadataByName

方法名称首字母小写,如果方法名由多个英文单词组成时,第二个和以后的英文单词的首字母大写,这是Java约定俗成的规范。
6. 属性的get方法和set方法通常是在属性的名称前增加get或set前缀,并将属性的首字母转换成大写。
例如,当有属性:private int age;
符合规范的get方法和set方法为:
public int getAge();
public void setAge(int age);
在属性的名称前增加get或set前缀,并将属性的首字母转换成大写,这是JavaBean要求的规范。
7. 布尔类型的get方法,应使用能反映出属性状态的疑问词作为前缀。
符合规范的get方法示例如下:
public boolean isExisted(){}
public boolean canSpeak(){}
public boolean hasChild(){}
8. 用正确的反义词组命名具有相反动作的方法。
对于具有相反动作的方法,应该使用反义词的英文单词来命名方法。常见的反义词组如下:
get / set add / remove begin / end insert / delete
first / last next / previous open / close min / max
start / stop send / receive show / hide up / down
符合规范的示例如下:
public int getAge();
public void setAge(int age);
或 public void addUser(User user);
public void removeUser(User user);
9. 静态常量要使用static final作为修饰符,并且全部使用大写字母;常量由多个单词组成时,单词之间使用_隔开。
符合规范的示例如下:
public static final int SESSION_MAX_NUMBER = 20;
public static final String DEFAULT_CLASS_NAME = “BaseBean”;
常量全部使用大写字母,如常量由多个单词组成时,单词之间使用_隔开,这是Java约定俗成的规范。
常量的命名规范同样适用于枚举的枚举项。
10. 变量名首字母小写,如果变量名由多个英文单词组成时,第二个和以后的英文单词的首字母大写。
这里所说的变量包括:属性名、方法参数和局部变量。
符合规范的示例如下:
private int age;
public void setUserName(String userName);
int userId;
变量名首字母小写,如果变量名由多个英文单词组成时,第二个和以后的英文单词的首字母大写, 这是Java约定俗成的规范。
除了for循环的计数变量和catch中的异常变量外,禁止使用单个字符作为变量名;当多个循环嵌套时,推荐使用i, j, k 作为循环的计数变量。
 使用单个字符作为变量名时,无法表示出变量的意图,从而影响程序的可读性。好的变量命名能准确的表示出变量的意图,如:int index;
 在for循环中使用单个字符作为循环的计数变量,是多种开发语言约定俗成的规范。如:
for (int i = 0; i < 100; i++)
 当多个循环嵌套时,推荐使用i, j, k 作为循环的计数变量,这样从循环变量就能看出循环所在的嵌套层;
 在异常中的catch中可以使用e作为异常的变量名。
11. 方法参数或局部变量禁止与类的属性名称相同,除非是属性的set方法的参数。
在Java中允许方法参数和局部变量与类的属性名称同名,但调用类的属性时必须使用“this.属性名”的方式调用,这种情况导致了代码的可读性降低。
而在属性的set方法中使用和属性同名的方法参数,是Java约定俗成的规范。如有一个属性定义为:private int age,下面的set方法是符合规范的:
public void setAge (int age){
this.age = age;
}
12. 避免在编码中直接使用字符串或数字;正确的方式是定义常量,并在程序中使用常量的标识符。
String start = request.getParameter(“start”);
String limit = request.getParameter(“limit”);
上述代码是不符合规范的,在编码中直接使用字符串或数字,会导致使用硬编码的字符串或数字分布在程序的多个地方,使程序难以维护。
通过定义常量,并在程序中使用常量的标识符,能提高程序的可维护性。符合规范的示例如下:
private static final String BY_PAGE_START_NAME = “start”;
private static final String BY_PAGE_LIMIT_NAME = “limit”;
String start = request.getParameter(BY_PAGE_START_NAME);
String limit = request.getParameter(BY_PAGE_LIMIT_NAME);
13. 在定义数组时,应使用Type[] arrayName,而不是使用Type arrayName[]。
括号是数组类型的一部分,数组定义如:String[] args,而不是使用 String args[]的方式来定义。
14. 【推荐】在表示长整数常量的时候,用L来代替l。
Long id = 10l;
上述代码是不符合规范的,由于小写字符”l”在很多编辑器中和数字”1”很难区分,在定义长整数常量的时候,建议采用L来代替l,如:
Long id = 10L;
15. 【推荐】在import引入类时,应具体到要引入的类,不要使用号来引入整个包。
import java.util.
;
上述代码是不符合规范的,Java程序在执行时,JVM会根据import语句加载需要的类,当import语句引入了整个包时,JVM会加载整个包中的类,这样会给Java程序的执行带来性能上的损失。正确的方式是应该只引入需要的类。符合规范的示例如下:
import java.util.Date;
16. 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。
将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
推荐规范的示例如下:
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;
17. 【推荐】各层命名规范。

A) Service/DAO 层方法命名规约
1)获取单个对象的方法用 get 做前缀。
2)获取多个对象的方法用 list 做前缀。
3)获取统计值的方法用 count 做前缀。
4)插入的方法用 save/insert 做前缀。
5)删除的方法用 remove/delete 做前缀。
6)修改的方法用 update 做前缀。
B) 领域模型命名规约
1)数据对象:xxxDO,xxx 即为数据表名。
2)数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3)展示对象:xxxVO,xxx 一般为网页名称。
4)POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
源代码格式

  1. 在设置源代码的编码方式时,请选择“UTF-8”作为源代码的编码格式。
  2. 【推荐】Java源文件总体的格式,推荐采取下述的顺序:
     类注释,通常描述类的版权、分发信息等;
     包定义;package + 包路径;
     引入的包列表:import + 引入的包;
     类的Javadoc注释;
     public类定义;
     定义类的serialVersionUID值(如果存在);
     类常量,按可见性的从高到低定义,如:public->protected->默认->private;
     类属性,按可见性的从高到低定义;
     类的构造方法,按参数的由少到多排序;
     类的其他方法,按可见性的从高到低定义;
     类属性的get和set方法;
     非匿名的内部类(如果存在)。
     一个接口中方法一般应保持在10个以下,当接口的方法超过15个时,请考虑接口是否过于臃肿,是否需要将接口划分为更小的接口;
     一个类的public方法一般应保持在15个以下(get方法和set方法除外),当类的public方法超过30个时,请考虑类的功能是否过多,是否需要将类划分为更小的类;
     一个方法的代码行数应保持在40行以下(一般以一屏为宜),当一个方法的代码行数超过100行时,请考虑是否需要将其中的代码提炼成子方法;
     一个方法的参数一般以4个以下为宜,如果方法的参数超过了6个,请考虑是否需要将方法的参数进行封装。
  3. 有意义的注释应该描述代码本身不能说明的内容。
    好的命名、代码结构是自解释的,注释力求精简准确、表达到位,有意义的注释应该是对代码的补充说明,而不是代码的翻译。
    避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
    示例:
    Double discountPrice = price * discount;
    不符合规范的注释://折扣单价等于单价乘以折扣率;
    符合规范的注释://当用户使用会员卡时可以享有一定的折扣,具体的折扣率视会员卡的级别而定。
  4. 只在需要的情况下增加注释,注释应该简洁、准确,防止注释二义性。
    对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。 下述几种情况必须增加注释:
  1. 代码实现的算法或实现的逻辑比较复杂时,要求增加注释进行说明;
  2. 在需要描述代码所实现的业务逻辑时,特别对于不符合常规的或比较特殊的业务逻辑,要求增加注释进行说明;
  1. 对于源文件中的类、protected和public的类属性、protected和public的类方法注释必须使用Javadoc规范。
    使用/*内容/格式,不得使用//xxx方式。
    说明:在IDE编辑窗口中,Javadoc方式会提示相关注释,生成Javadoc可以正确输出相应注释;在IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
  2. 规范使用单行注释和多行注释。
    方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
  3. 代码修改的同时,注释也要进行相应的修改。
    尤其是参数、返回值、异常、核心逻辑等的修改。
    代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义

面向对象编程

  1. 一个类应该只做一件事。
    当我们设计或创建一个类时,应该明确的知道这个类的职责,这个类的所有代码应该都和这个职责相关,不要仅仅把类当成实现功能的容器,而把多个不相关的功能放到一个类中,这样做可以提高类的内聚性,也符合“单一职责”的设计原则。

  2. 当一个属性或方法不能确定它的可见性时,优先采用较低的可见性。
    Java提供了public、protected、默认的和private四种由高到低的可见性,当我们不能确定一个属性和方法应该使用什么可见性时,应该优先选择较低的可见性。如:一个方法,我们希望这个方法能在子类中也能被调用,但不能确定是使用public还是protected,这时应该优先选择protected。
    在面向对象的系统中,一个低可见性的方法修改为高可见性时,不会产生任何的问题,如:一个private的方法修改为protected时不会有任何的问题;但相反则不行。

  3. 一个public方法应该只提供一个完整的、唯一的功能实现。
     一个public的方法应该提供一个完整的功能实现,如:一个类有两个属性,分别为性别标识和性别名称,假设当性别标识为“1”,性别名称为 “男”,当性别标识为“2”,性别名称为“女”,反之也存在如此的逻辑。当一个public的方法为设置性别时,应该同步设置性别标识和性别名称;假设一个public方法只能设置性别标识或性别名称,则是一个不完整的功能实现,不完整的public方法会将这种本来应该属于类内部的逻辑扩散到了类外部,从而影响到了类的内聚性。另外,不完整的public方法也可能导致类内部数据的不一致,如出现性别标识为“1”,但性别名称为 “女”的情况。
     一个public的方法应该提供一个唯一的功能实现,即一个方法只做一件事,如上述的例子,如果在设置性别的方法内同时也设置了年龄,这样就会违反“单一职责”的设计原则,影响方法的可扩展性和可重用性。

  4. 一个方法的参数之间不应该存在逻辑关系。
    引用上述的例子,假设有一个方法需要根据性别查询用户,方法的伪码如下:
    List<用户> getUserList(int 性别标识, String 性别名称);
    上述示例是不符合规范的,由于性别标识和性别名称之间存在一定的逻辑关系,如果同一个方法的参数之间使用存在逻辑关系,会使这种逻辑关系扩散,从而影响类的内聚性;同样,也可能会出现参数不一致的情况。

  5. 接口的方法应尽量保持稳定,不要因为细节的变化而频繁改变接口方法。
    如接口方法:
    public List getUserList(int userId, String userName,int sexFlag);
    该接口方法主要用来根据查询条件查询符合条件的用户列表,但由于查询条件的变化,可能导致接口方法的参数频繁变化,导致接口方法不稳定。
    正确的方法应该是将查询条件进行封装,将查询条件的变化隔离到查询条件类中,从而保证接口方法的稳定。如接口方法:
    public List getUserList(UserQueryParams queryParams);
    可定义查询条件类UserQueryParams,如下:
    public class UserQueryParams {
    private int userId;
    private String userName;
    private int sexFlag;
    //get和set方法略
    }

  6. 谨慎的运用final来限制继承和重载。
     当final关键字应用到类时,可以限制不能从该类继承;当final关键字应用到方法,可以限制该方法不会在子类中被覆盖。在正常的情况下,Java中的类和方法应该是能够被继承和被覆盖的,只有在明确希望类不能被继承和方法不能被覆盖时才使用final关键字。
     不能因为性能的问题而将方法设置为final,Java中的方法在默认情况下都是动态绑定的,也就是说具体执行的方法需要在运行期才能决定,这种动态绑定机制提供了Java语言的灵活性。当一个方法被设置为final时,该方法也就失去这种灵活性,虽然方法被设置为final后会带来些许的性能提升,但这种置换往往是得不偿失的。因此,只有在明确希望方法不能被覆盖时才将方法设置为final。

  7. 在进行方法的重载时,重载方法的参数之间不应该存在继承关系。
    public void draw(Object object)
    public void draw(String string)
    上述示例是不符合规范的,当重载方法的参数之间存在继承关系时,仅通过阅读代码很难确定在运行期执行的方法,导致代码的可读性降低。符合规范的示例如下:
    public void draw(int number)
    public void draw(String string)

  8. 【推荐】尽量采取“面向抽象编程”的思路。
    在类的继承体系中,上层的接口和类相对于下层的接口和类来说是抽象的,“面向抽象编程”就是说要在开发时尽量使用上层的接口和类。
    在Java中可以使用父类或接口声明变量,使用子类实例化;而父类能适用的场合子类都能适用,因此可采用不同的子类来实例化声明的父类或接口,而无需修改任何其他的代码。如:
    List list = new ArrayList();
    采用List接口定义变量,具体应用时只调用List接口的方法,这样如果将ArrayList更换为LinkedList时,无需修改其他任何的代码,这样做也是符合“依赖倒转原则”的。
    另外,在我们使用的Spring中,可将要实例化的类通过配置注入,这样进一步提高了“面向抽象编程”的应用场合。

  9. 子类的属性名称不要和父类的属性名称重名。
    子类的属性名称和父类的属性名称重名时,容易照成混乱,导致代码的可读性降低,当具体需要访问子类或父类的属性时往往需要强制转型,破坏了“面向抽象编程”的原则。
    禁止子类的属性名称和父类的属性名称重名,可以避免上述的问题。

  10. 【推荐】优先使用组合或聚合,而不是继承。
    继承是一种“白盒”复用,继承的子类和父类之间是一种强耦合的关系,当父类的细节发生变化时,子类相应的也会发生变化。而组合或聚合是一种“黑盒”复用,组合或聚合的类之间是一种弱耦合的关系,被引用的类的细节对于引用者来说是不可见,被引用的类细节的变化也不会影响到引用者,而且被引用的类可以依赖于抽象而进一步提升扩展性和重用性。
    因此,在能使用组合或聚合时,应优先使用组合或聚合。

  11. 【推荐】对于公司外的 http / api 开放接口必须使用“错误码” ; 而应用内部推荐异常抛出 ;跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法 、“错误码”、“错误简短信息”。

  12. 【推荐】对于浮点型变量,当对两个变量进行是否相等判断时应避免使用”==”,防止因为精度问题引起执行结果的错误。
    6.5 集合处理

  13. hashCode和equals的重写需要遵循规则。
    遵循如下规则:
    1)只要重写equals,就必须重写hashCode。
    说明:由于equals()方法的结果,取决于hashCode()方法的返回值,因此实现了类的equals()方法时,应同时实现hashCode ()方法。
    2)因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
    3)如果自定义对象做为Map的键,那么必须重写hashCode和equals。
    说明:String重写了hashCode和equals方法,所以我们可以非常愉快地使用String对象作为key来使用。

  14. ArrayList的subList结果不可强转成ArrayList类型。
    如果强转,会抛出ClassCastException异常,即:
    java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。
    说明:subList 返回的是 ArrayList 的内部类 SubList,并不是ArrayList ,而是 ArrayList 的一个视图,对SubList子列表的所有操作最终会反映到原列表上。

  15. 在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生ConcurrentModificationException 异常。
    不符合规范的示例如下:
    List listNum = new ArrayList();
    listNum.add(1);
    listNum.add(2);

    // 通过subList生成一个与listNum一样的列表 listSubNum
    List<Integer> listSubNum = listNum.subList(0, listNum.size());
    // 修改listNum
    listNum.add(3);
    
    // 正常
    System.out.println("listNum'size:" + listNum.size());
    // 出现ConcurrentModificationException异常
    System.out.println("listSubNum'size:" + listSubNum.size());
    
  16. 使用集合转数组的方法,必须使用集合的toArray(T[] array)。
    toArray传入的是类型完全一样的数组,大小就是list.size()。
    使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。
    符合规范的示例如下:
    List list = new ArrayList(2);
    list.add(“guan”);
    list.add(“bao”);
    String[] array = new String[list.size()];
    array = list.toArray(array);
    说明:直接使用toArray无参方法存在问题,此方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。

  17. 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。
    asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
    String[] str = new String[] { “you”, “wu” };
    List list = Arrays.asList(str);
    第一种情况:list.add(“yangguanbao”); 运行时异常。
    第二种情况:str[0] = “gujin”; 那么list.get(0)也会随之修改。

  18. 泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用add方法,而<? super T>不能使用get方法,做为接口调用赋值时易出错。
    扩展到PECS(Producer Extends Consumer Super)原则:
    1)频繁往外读取内容的,适合用<? extends T>。
    2)经常往里插入的,适合用<? super T>。

  19. 不要在foreach循环里进行元素的remove/add操作。
    remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
    符合规范的示例如下:
    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
    String item = iterator.next();
    if (删除元素的条件) {
    iterator.remove();
    }
    }

不符合规范的示例如下:
List list = new ArrayList();
list.add(“1”);
list.add(“2”);
for (String item : list) {
if (“1”.equals(item)) {
list.remove(item);
}
}
8. 【推荐】使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。
keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。
如果是JDK8,使用Map.foreach方法。 正例:values()返回的是V值集合,是一个list集合对象;keySet()返回的是K值集合,是一个Set集合对象;entrySet()返回的是K-V值组合集合。
9. 【推荐】高度注意Map类集合K/V能不能存储null值的情况。
Map类集合K/V的存储状况如下表:
集合类 Key Value Super 说明
Hashtable 不允许为null 不允许为null Dictionary 线程安全
ConcurrentHashMap 不允许为null 不允许为null AbstractMap 锁分段技术(JDK8:CAS)
TreeMap 不允许为null 允许为null AbstractMap 线程不安全
HashMap 允许为null 允许为null AbstractMap 线程不安全

注意: 由于HashMap的干扰,很多人认为ConcurrentHashMap是可以置入null值,而事实上,存储null值时会抛出NPE异常。
10. 【推荐】合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。
有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次序是一定的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort。
11. 【推荐】利用Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重操作。
6.6 并发处理

  1. 获取单例对象需要保证线程安全。
    单例的目的是为了保证运行时只有唯一的一个实例,多个线程同时获取该实例,容易出现多例的状况。获取单例时的工具类、单例工厂类、资源驱动类(如:获取DB数据源类)都应注意保证线程安全。
    不符合规范的示例如下:
    public class MyFactory {
    private static MyFactory instance = null;
    public static MyFactory getInstance(){
    if(instance == null){
       instance = new MyFactory();
    }
    return instance;
    }
    }
    符合规范的示例如下:
    public class MyFactory {
    private static class instanceHolder {
    public static MyFactory instance = new MyFactory();
    }

    public static MyFactory getInstance() { 
        return MyFactory.instanceHolder.instance; 
    }
    

}
2. 【推荐】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
符合规范的示例如下:
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName(“TimerTaskThread”);

}
}
3. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
4. 【推荐】线程池不使用Executors去创建,而是通过ThreadPoolExecutor的方式。
这样的处理方式让编写的同学更加明确线程池的运行规则,规避资源耗尽的风险。Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
5. SimpleDateFormat 是线程不安全的类,一般不要定义为static变量。
如果确实需要定义为static,必须加锁,或者使用其它线程安全的类库。
符合规范的做法如下:
注意线程安全,使用DateUtils工具类或joda-time库。
或者:
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat(“yyyy-MM-dd”);
}
};
说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

  1. 高并发时,同步调用要考量锁的性能损耗。
    锁会导致并发性能受到损耗,所以:能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。 尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法。
  2. 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,避免死锁。
    符合规范的做法如下:
    “线程1”需要对表A、B、C依次全部加锁后才可以进行更新操作,那么,“线程2”的加锁顺序也必须是A、B、C,否则可能出现死锁。
  3. 并发修改同一业务数据时,如要避免更新丢失,需要加锁。
    要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。
    说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。
  4. 用CountDownLatch进行异步转同步,每个线程退出前必须调用countDown方法。
    线程执行代码注意catch异常,确保countDown方法被执行到,避免主线程无法执行至await方法,直到超时才返回结果。
    注意:子线程抛出异常堆栈,不能在主线程try-catch到。
  5. 避免Random实例被多线程使用。
    虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
    Random实例包括java.util.Random 的实例或者 Math.random()的方式。
    在JDK7之后,可以直接使用API ThreadLocalRandom,而在 JDK7之前,需要编码保证每个线程持有一个实例。
    6.7 控制语句
  6. switch块的编码规范。
    在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
  7. if/else/for/while/do的编码规范。
    在if/else/for/while/do语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:
    符合规范的示例如下:
    if (condition) {
    statements;
    }
    不符合规范的示例如下:
    if (condition) statements;
  8. 【推荐】表达异常的分支时,少用if-else方式。
    这种方式可以改写成:
    if (condition) {

    return obj;
    }
    // 接着写else的业务逻辑代码;
    如果非得使用if()…else if()…else…方式表达逻辑,避免后续代码维护困难,请勿超过3层。
    超过3层的 if-else 的逻辑判断代码可以使用卫语句来实现,示例如下:
    public void today() {
    if (isBusy()) {
    System.out.println(“change time.”);
    return;
    }

if (isFree()) {
System.out.println(“go to travel.”);
return;
}

System.out.println(“stay at home to learn Java.”);
return;
}
4. 【推荐】除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
符合规范的示例如下:
final boolean existed = (file.open(fileName, “w”) != null)
&& (…) || (…);
if (existed) {

}
不符合规范的示例如下:
if ((file.open(fileName, “w”) != null) && (…) || (…)) {

}
5. 【推荐】应尽量保证循环内的工作最小化;除非循环的规模能得到控制,循环内禁止调用如数据库操作、文件读写、网络访问等高负荷的操作。
1)当某些代码既可以放在循环内,又可以放在循环外时,应尽量保证循环内的工作最小化,这样做可以提高程序的性能。
2)数据库操作、文件读写、网络访问,实现这些功能的代码在执行时都需要付出高昂的代价,在循环的规模不可控的情况下,在循环中执行这些代码,可能导致软件的性能达不到要求或出现执行超时的情况。
6. 【推荐】除非必须,不要手工改变for循环计数器的值。
改变for循环中计数器的值,会让循环变得不易控制,让程序难以理解而容易产生错误。
7. 【推荐】进行参数校验的场景
1) 调用频次低的方法。
2) 执行时间开销很大的方法。
3) 需要极高稳定性和可用性的方法。
4) 对外提供的开放接口,不管是RPC/API/HTTP接口。
5) 敏感权限入口。
8. 【推荐】不需要进行参数校验的场景
1) 极有可能被循环调用的方法。但在方法说明里注明外部参数检查要求。
2) 底层调用频度比较高的方法。如:DAO层与Service层都在同一个应用中,部署在同一台服务器中,所以DAO的参数校验,可以省略。
3) 被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题,可以不校验参数。
6.8 异常处理

  1. 不能只捕获异常,而不处理异常。
    捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
    不符合规范的示例如下:
    try {
    Class<?> clazz = Class.forName(className);
    } catch (ClassNotFoundException e) {
    }
    说明:在进行异常处理时,应该进行类似:转换错误信息、恢复可能因异常导致的问题、记录异常日志等等的处理操作。
  2. Java 类库中定义的运行时异常RuntimeException可以通过预先检查进行规避,而不应该通过catch 来处理,比如:IndexOutOfBoundsException,NullPointerException等等。
    从逻辑的角度来说,checked exceptions和runtime exception是有不同的使用目的的。checked exception用来指示一种调用方能够直接处理的异常情况。而runtime exception则用来指示一种调用方本身无法处理或恢复的程序错误,如果要捕获它的话,就会冒这样一个风险:程序代码的错误(bug)被掩盖在运行当中无法被察觉。无法通过预检查的异常除外,如在解析一个外部传来的字符串形式数字时,通过catch NumberFormatException来实现。
    符合规范的示例如下:
    if (obj != null) {

    }
    不符合规范的示例如下:
    try {
    obj.method()
    } catch (NullPointerException e) {

    }
  3. 【推荐】避免使用Exception类替代具体的异常类。
    示例1:
    public static final Object createObject(String className) throws Exception;
    示例2:
    try {
    Class<?> clazz = Class.forName(className); } catch (Exception e) { e.printStackTrace(); } 上述两个示例是不推荐的,Exception类是所有异常类的父类,当方法抛出Exception异常或捕获Exception异常时就无法区分具体发生了什么异常,也就无法根据具体的异常类做相应的异常处理。同样,使用RuntimeException类来替代所有的运行期异常也是不符合规范的。(在架构级的实现中可能因为异常的不确定而使用了RuntimeException类,这种情况不在此列。) 正确的方式是抛出具体的异常类或捕获具体的异常类。如: 示例1: public static final Object createObject(String className) throws ClassNotFoundException, SecurityException; 示例2: try { Class<?> clazz = Class.forName(className);
    } catch (ClassNotFoundException e) {
    //do Something
    e.printStackTrace();
    } catch (SecurityException e) {
    //do Something
    e.printStackTrace();
    }
  4. finally块必须对资源对象、流对象进行关闭,如:数据库连接、IO输入输出流等。有异常也要做try-catch。
    符合规范的示例如下:
    PrintWriter out = null;
    try {
    out = response.getWriter();
    //do Something
    } catch (DataAccessException e) {
    out.write(e.getMessage());
    } finally {
    if (out != null){
    out.flush();
    }
    }
    不符合规范的示例如下:
    PrintWriter out = null;
    try {
    out = response.getWriter();
    //do Something
    out.flush();
    } catch (DataAccessException e) {
    out.write(e.getMessage());
    }
    说明:当异常发生时,代码执行的顺序会从try块中发生异常的地方跳转到相应的catch块,try块中异常下面的代码将不会被执行,这样会导致占用的资源不能被释放。而finally块中的代码,不管是否发生异常,都肯定会被执行到,因此释放资源的代码应该放到finally块中。
  5. 不能在finally块中使用return。
    finally块中的return返回后方法结束执行,不会再执行try块中的return语句。
  6. 异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
    不符合规范的示例如下:
    String numberStr = “123”;
    try {
    Long.valueOf(numberStr);
    //数字处理流程
    } catch (NumberFormatException e) {
    //非数字处理流程
    }
    说明:异常处理机制是一种运行期的错误处理机制,不应该用来控制软件执行的流程;另外,在异常发生时,JVM会创建异常类,并创建异常类的堆栈信息,这些操作将消耗大量的系统资源。
  7. 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
    如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。
  8. 自定义异常的类名应以Exception结尾,表示该类是一个异常类。
    符合规范的示例如下:
    public class MetadataException extends Exception
    自定义的异常应该从Exception类及其子类继承,而不是Error类或Throwable类继承。Error类用于表示应用程序不应该试图捕获的严重问题,一般反映JVM内部的错误;而Throwable类是Exception类和Error类的父类。
  9. 避免在循环中捕获和处理异常。
    符合规范的示例如下:
    try {
    for (int i = 0; i < size; i++) {
    someMethod(num);
    }
    } catch (NumberFormatException e) {
    e.printStackTrace();
    }
    不符合规范的示例如下:
    for (int i = 0; i < size; i++) {
    try {
    someMethod(num);
    } catch (NumberFormatException e) {
    e.printStackTrace();
    }
    }
    说明:当代码进入try块时,JVM会创建一个监听器来监听try块内的代码,如果在循环内频繁的进出try块时,会导致JVM频繁的创建和释放监听器,从而导致性能上的损失。因此除非必要,不要在循环内捕获和处理异常。
  10. 针对非稳定代码进行异常处理,不能随意大段代码try-catch。
    对大段代码进行try-catch,这是不负责任的表现。catch时要分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。
  11. 应该在实际的处理模块中捕获并处理异常,不要将异常抛给容器处理。
    不符合规范的示例如下:
    public ActionForward getAccountitemTypes(
    ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws IOException {
    PrintWriter out = response.getWriter();
    out.write(agencyAccountitemTypeService.getAccountitemTypes(request));
    out.flush();
    return null;
    }
    说明:Action中的方法由容器调用,不应该将异常抛出给容器处理。该IOException异常由response.getWriter()方法抛出,方法getAccountitemTypes应该是异常的实际处理类,应该在方法getAccountitemTypes中捕获并处理异常。
  12. 【推荐】防止NPE,是程序员的基本修养,注意NPE产生的场景:
    1) 返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
    不符合规范的示例如下:
    public int f() {
    return Integer对象 //如果为null,自动解箱抛NPE。
    }
    2) 数据库的查询结果可能为null。
    3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
    4) 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
    5) 对于Session中获取的数据,建议NPE检查,避免空指针。
    6) 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
    注:可使用JDK8的Optional类来防止NPE问题。
    6.9 常见的Java编程建议
  13. 【推荐】不要忽略任何警告。
    一个优秀的开发人员不应该让自己的代码中有任何的编译期的警告,这些警告可能来自于引入了一个没有使用的包、定义了一个没有使用的局部变量或私用方法、使用了没有应用类型的泛型、实现了Serializable接口但没有定义serialVersionUID的值、或者进行了一个不确定的类型转换,这些警告可能让你的代码有些许的性能损失,或者可能会发生潜在的错误,而消除这些警告往往只是举手之劳,我们为什么不提交一份没有任何警告的代码呢?
  14. 【推荐】不要使用不推荐的类或方法,当一个类或方法被声明成deprecated后,不要再使用它。
    当一个类或方法被声明成deprecated后,表示该类或方法是不推荐使用的,而且在后续的版本升级中不能保证deprecated的类或方法能提供向下的兼容性。在通常的情况下,一个负责的开发者在将一个类和方法声明为deprecated时,往往会说明替代的类或方法,查一查资料,使用替代的类或方法,将保证我们的代码不会因为第三方组件的升级而产生兼容性的问题。
  15. 【推荐】要谨慎的判断类是否需要实现Serializable接口,当一个类实现了Serializable接口时请为这个类分配一个‘serialVersionUID’值。
    当一个类实现了Serializable接口时,表示这个类可以被持久化到磁盘,或者可以在网络上传输,这样可能带来额外的安全问题,所以要谨慎的判断类是否需要实现Serializable接口。当一个类实现了Serializable接口后,在序列化和反序列化的过程中,JVM会根据serialVersionUID来判断类的序列化版本,当serialVersionUID的值没有被定义时,JVM会随机的产生一个,这样可能会导致序列化的失败。在设置serialVersionUID值时,可以是你自己指定的,也可以是由开发工具创建的。另外,如果你修改了序列化类的结构后,最好重新分配一个serialVersionUID值。
  16. 【推荐】不要使用new关键字来创建String对象。
    String str = new String(“abc”);
    上述代码是不符合规范的,在Java中,String对象是一种特殊的Java对象,在JVM中维护了一个共享的字符串对象池,当使用new关键字来创建String对象时,JVM首先会判断字符串对象池中是否存在”abc”的对象,如存在,则返回该对象;如不存在,则创建一个值为”abc”的字符串对象,并增加到对象池中。在使用new关键字时,JVM还会在堆内存中创建一个String对象,然后将字符串对象池中的”abc”赋值给新创建的String对象。因此,不管”abc”对象在共享字符串对象池中是否存在,使用new关键字来创建String对象都会产生额外的对象。
    正确的方式是直接使用赋值语句创建String对象,如:
    String str = “abc”;
  17. 【推荐】在频繁使用字符串连接的场合,使用StringBuffer类替代String类。
    String outStr = null;
    for (VOPropertyMetadata property : clazz.getMetadatas()){
    if (outStr != null) {
    outStr +=“,”;
    }
    outStr += “'" + property.getFieldName() + “'”;
    }
    上述代码是不符合规范的,由于String对象是个不可变的对象,如:一个String对象的值为”abc”,当该对象和另一个String对象采用”+”号进行连接时,会在共享的字符串对象池中创建连接前后的String对象,当频繁使用字符串连接时,会在共享的字符串对象池中创建大量的String对象,从而导致软件性能上的损失。
    在频繁使用字符串连接的场合,应该使用StringBuffer类,StringBuffer对象的值是可变的,进行字符串连接时不会产生新的对象,如下的例子:
    StringBuffer outStr = new StringBuffer();
    for (VOPropertyMetadata property : clazz.getMetadatas()){
    if (outStr.length() != 0) {
    outStr.append(“,”);
    }
    outStr.append("'
    ” + property.getFieldName() + “'”);
    }
  18. 【推荐】谨慎使用synchronized关键字,并保证synchronized涉及的范围最小化。
    使用synchronized关键字控制的代码块一般称为同步代码块,当线程执行到同步代码块时会增加同步锁,代码没有执行完毕,同步锁就不会释放;当其他线程试图执行该同步代码块时,需要等待同步锁的释放。在给代码块增加synchronized关键字时,需要谨慎的判断该代码块是否会运行在多线程环境中,是否需要同步机制,同步机制会对性能带来很大影响,并且可能造成死锁。
    在确定需要synchronized关键字时,应保证synchronized涉及的范围最小化,例如:如果只需要将方法中的一部分代码设置为synchronized,就能解决问题,那么就没有必要将整个方法设置为synchronized。
    不能从synchronized块中调用含有synchronized块的方法,这样导致死锁的可能性将大增。
  19. 【推荐】定义局部变量时,应保证局部变量的作用域最小;并且不要重复使用局部变量。
    反例:
    int i;
    for (i = 0; i < 100; i++){
    //do Something
    }
    上述代码是不符合规范的,局部变量i仅在循环内部使用,但对局部变量i的定义却在循环外,这样实际局部变量i的作用域超出了循环;在Java中,局部变量的生存周期由其作用域决定,当局部变量的作用域扩大时,其对系统资源的占用时间也相继延长,这样会给软件带来性能上的损失。
    正确的方式是在局部变量作用的最小作用域内定义局部变量。
    每一个局部变量应该只是一次性使用,重复使用同一个局部变量容易导致理解上的偏差。
  20. 【推荐】调用类的静态方法和静态属性时应直接使用类名进行调用。
    采用static修饰符的静态方法和静态属性应直接使用类名进行调用,这样做可以提高代码通用性和可读性。如定义如下:
    public class ReflectUtils {
    public static final Object createObjectByDefaultConstructor(String className){}
    }
    访问时应该直接使用类名进行访问:
    ReflectUtils. createObjectByDefaultConstructor(className);
    采用static修饰符的静态方法和静态属性是在类加载的时候被初始化的,无需实例化就可访问。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值