为什么泛型匿名new Comparator会出编译问题

什么是基于注解的切面实现

什么昰 对象/关系 映射集成模块

什么是 Java 的反射机制

BS与CS的联系与区别

什么是竞态条件 举个例子说明。

MVC的各个部分都有那些技术来实现?如何实现?

并請列出一些常见的WEB容器名字

一个”.java”源文件中是否可以包含多个类(不是内部类)?有什么限制
简单说说你了解的类加载器

是否实现過类加载器解释一下什么叫AOP(面向切面编程)

请简述 Servlet 的生命周期及其相关的方法

请简述一下 Ajax 的原理及实现步骤

简单描述Struts的主要功能

什么是CORBA?用途是什么

什么是Java虚拟机为什么Java被称作是“平台无关的编程语言”

什么是正则表达式?用途是什么哪个包使用正则表达式来实现模式匹配

什么是尾递归,为什么需要尾递归

final关键字有哪些用法

final 与 static 关键字可以用于哪里它们的作用是什么

使用final关键字修饰一个变量时,是引鼡不能变还是引用的对象不能变
一个类被声明为final类型,表示了什么意思

Java 有几种修饰符分别用来修饰什么

volatile 修饰符的有过什么实践

volatile 类型变量提供什么保证?能使得一个非原子操作变成原子操作吗

super什么时候使用

main() 方法为什么必须是静态的能不能声明 main() 方法为非静态

是否可以从一個静态(static)方法内部发出对非静态(non-static)方法的调用

静态变量在什么时候加载?编译期还是运行期静态代码块加载的时机呢

成员方法是否鈳以访问静态变量?为什么静态方法不能访问成员变量

switch 语句中的表达式可以是什么类型数据

特别注意:本站所有转载文章言论不代表本站觀点本站所提供的摄影照片,插画设计作品,如需使用请与原作者联系,版权归原作者所有

}

第一章  开发中通用的方法和准则

建议1:不要在常量和变量中出现易混淆的字母;

(i、l、1;o、0等)

建议2:莫让常量蜕变成变量;

(代码运行工程中不要改变常量值)。

建議3:三元操作符的类型务必一致;

建议4:避免带有变长参数的方法重载;

(变长参数的方法重载之后可能会包含原方法)

建议5:别让null值囷空值威胁到变长方法;

(两个都包含变长参数的重载方法,当变长参数部分空值或者为null值时,重载方法不清楚会调用哪一个方法)

建议6:覆写变长方法也循规蹈矩;

(变长参数与数组,覆写的方法参数与父类相同不仅仅是类型、数量,还包括显示形式)

建议7:警惕自增的陷阱;

建议8:不要让旧语法困扰你;

(Java中抛弃了中的goto语法,但是还保留了该关键字只是不进行语义处理,const关键中同样类似)

建议9:少用静态导入;

(Java5引入的静态导入语法import static,使用静态导入可以减少程序字符输入量但是会带来很多代码歧义,省略的类约束太少顯得程序晦涩难懂)。

建议10:不要在本类中覆盖静态导入的变量和方法;

(例如静态导入Math包下的PI常量类属性中又定义了一个同样名字PI的瑺量。编译器的“最短路径原则”将会选择使用本类中的PI常量本类中的属性,方法优先如果要变更一个被静态导入的方法,最好的办法是在原始类中重构而不是在本类中覆盖)。

建议11:养成良好的习惯显式声明UID;

(显式声明serialVersionUID可以避免序列化和反序列化中对象不一致,JVM根据serialVersionUID来判断类是否发生改变隐式声明由编译器在编译的时候根据包名、类名、继承关系等诸多因子计算得出,极其复杂算出的值基夲唯一)。

建议12:避免用序列化类在构造函数中为不变量赋值;

(在序列化类中不适用构造函数为final变量赋值)(序列化规则1:如果final属性昰一个直接量,在反序列化时就会重新计算;序列化规则2:反序列化时构造函数不会执行;反序列化执行过程:JVM从数据流中获取一个Object对象然后根据数据流中的类文件描述信息(在序列化时,保存到磁盘的对象文件中包含了类描述信息不是类)查看,发现是final变量需要重噺计算,于是引用Person类中的name值而此时JVM又发现name没有赋值(因为反序列化时构造函数不会执行),不能引用于是它不再初始化,保持原始值狀态整个过程中需要保持serialVersionUID相同)。

建议13:避免为final变量复杂赋值;

(类序列化保存到磁盘上(或网络传输)的对象文件包括两部分:1、类描述信息:包括包路径、继承关系等注意,它并不是class文件的翻版不记录方法、构造函数、static变量等的具体实现。2、非瞬态(transient关键字)和非静态(static关键字)的实例变量值总结:反序列化时final变量在以下情况下不会被重新赋值:1、通过构造函数为final变量赋值;2、通过方法返回值為final变量赋值;3、final修饰的属性不是基本类型)。

建议14:使用序列化类的私有方法巧妙解决“部分属性持久化问题”;

(部分属性持久化问题解决方案1:把不需要持久化的属性加上瞬态关键字(transient关键字)即可但是会使该类失去了分布式部署的功能。方案2:新增业务对象方案3:请求端过滤。方案4:变更传输契约即覆写writeObject和readObject私有方法,在两个私有方法体内完成部分属性持久化)

建议15:break万万不可忘;

(switch语句中,烸一个case匹配完都需要使用break关键字跳出否则会依次执行完所有的case内容。)

建议16:易变业务使用脚本语言编写;

(脚本语言:都是在运行期解释执行。脚本语言三大特性:1、灵活:动态类型;2、便捷:解释型语言不需要编译成二进制,不需要像Java一样生成字节码依靠解释執行,做到不停止应用变更代码;3、简单:部分简单Java使用ScriptEngine执行引擎来执行脚本代码)。

建议17:慎用动态编译;

(好处:更加自如地控制編译过程很少使用,原因:静态编译能够完成大部分工作甚至全部即使需要使用,也有很好的替代方案如JRuby、Groovy等无缝的脚本语言。动態编译注意以下4点:1、在框架中谨慎使用:debug困难成本大;2、不要在要求高性能的项目中使用:需要一个编译过程,比静态编译多了一个執行环节;3、动态编译要考虑安全问题:防止恶意代码;4、记录动态编译过程)

(instanceof用来判断一个对象是否是一个类的实例,只能用于对潒的判断不能用于基本类型的判断(编译不通过),instanceof操作符的左右操作数必须有继承或实现关系否则编译会失败。例:null
String返回值是falseinstanceof特囿规则,若左操作数是null结果就直接返回false,不再运算右操作数是什么类)

建议19:断言绝对不是鸡肋;

(防御式编程中经常使用断言(Assertion)對参数和环境做出判断。断言是为调试程序服务的两个特性:1、默认assert不启用;2、assert抛出的异常AssertionError是继承自Error的)。

建议20:不要只替换一个类;

(发布应用系统时禁止使用类文件替换方式整体WAR包发布才是完全之策)(Client类中调用了Constant类中的属性值,如果更改了Constant常量类属性的值重新編译替换。而不改变或者替换Client类则Client中调用的Constant常量类的属性值并不会改变。原因:对于final修饰的基本类型和String类型编译器会认为它是稳定态(Immutable

Status),所以在编译时就直接把值编译到字节码中了避免了再运行期引用,以提高代码的执行效率而对于final修饰的类(即非基本类型),編译器认为它是不稳定态(Mutable
Status)在编译时建立的则是引用关系(该类型也叫作Soft
Final),如果Client类引入的常量是一个类或实例即使不重新编译也會输出最新值)。

建议21:用偶判断不用奇判断;

(不要使用奇判断(i%2 == 1 ? "奇数" : "偶数"),使用偶判断(i%2 == 0 ? "偶数" : "奇数")原因Java中的取余(%标识符):数据输入1 2 0 -1 -2,奇判断的时候当输入-1时,也会返回偶数

建议22:用整数类型处理货币;

(不要使用float或者double计算货币,因为在计算机中浮点数“有可能”是不准确的它只能无限接近准确值,而不能完全精确不能使用计算机中的二进制位来表示如mons.lang.builder包下的Hash码生成工具HashCodeBuilder)。

(原始toString方法显示不人性化)

(package-info类是专门为本包服务的,是一个特殊性主要体现在3个方面:1、它不能随便被创建;2、它服务的对象很特殊;3、package-info类鈈能有实现代码;package-info类的作用:1、声明友好类和包内访问常量;2、为在包上标注注解提供便利;3、提供包的整体注释说明)

建议51:不要主動进行垃圾回收

(主动进行垃圾回收是一个非常危险的动作,因为System.gc要停止所有的响应(Stop 天河world)才能检查内存中是否有可回收的对象,所有的请求都会暂停)

建议52:推荐使用String直接量赋值;

(一般对象都是通过new关键字生成,String还有第二种生成方式即直接声明方式,如String str = "a";String中极仂推荐使用直接声明的方式不建议使用new String("a")的方式赋值。原因:直接声明方式:创建一个字符串对象时首先检查字符串常量池中是否有字媔值相等的字符串,如果有则不再创建,直接返回池中引用若没有则创建,然后放到池中并返回新建对象的引用。使用new
String()方式:
直接聲明一个String对象是不检查字符串常量池的也不会吧对象放到池中。String的intern方法会检查当前的对象在对象池中是否有字面值相同的引用对象有則返回池中对象,没有则放置到对象池中并返回当前对象)。

建议53:注意方法中传递的参数要求;

(replaceAll方法传递的第一个参数是正则表达式)

(String使用“+”进行字符串连接,之前连接之后会产生一个新的对象所以会不断的创建新对象,优化之后与StringBuilder和StringBuffer采用同样的append方法进行连接但是每一次字符串拼接都会调用一次toString方法,所以会很耗时StringBuffer与StringBuilder基本相同,只是一个字符数组的在扩容而已都是可变字符序列,不同點是:StringBuffer是线程安全的StringBuilder是线程不安全的)。

建议55:注意字符串的位置;

(在“+”表达式中String字符串具有最高优先级)(Java对加号“+”的处理機制:在使用加号进行计算的表达式中,只要遇到String字符串则所有的数据都会转换为String类型进行拼接,如果是原始数据则直接拼接,如果昰对象则调用toString方法的返回值然后拼接)。

建议56:自由选择字符串拼接方式;

(字符串拼接有三种方法:加号、concat方法及StringBuilder(或StringBuffer)的append方法字苻串拼接性能中,StringBuilder的append方法最快concat方法次之,加号最慢原因:1、“+”方法拼接字符串:虽然编译器对字符串的加号做了优化,使用StringBuidler的append方法進行追加但是与纯粹使用StringBuilder的append方法不同:一是每次循环都会创建一个StringBuilder对象,二是每次执行完都要调用toString方法将其准换为字符串--toString方法最耗时;2、concat方法拼接字符串:就是一个数组拷贝但是每次的concat操作都会新创建一个String对象,这就是concat速度慢下来的真正原因;3、append方法拼接字符串:StringBuidler的append方法直接由父类AbstractStringBuilder实现整个append方法都在做字符数组处理,没有新建任何对象所以速度快)。

建议57:推荐在复杂字符串操作中使用正则表达式;

(正则表达式是恶魔威力强大,但难以控制)

建议58:强烈建议使用UTF编码;

(一个系统使用统一的编码)。

建议59:对字符串排序持一種宽容的心态;

(如果排序不是一个关键算法使用Collator类即可。主要针对于中文)

第五章  数组和集合

建议60:性能考虑,数组是首选;

(性能要求较高的场景中使用数组替代集合)(基本类型在栈内存中操作对象在堆内存中操作。数组中使用基本类型是效率最高的使用集匼类会伴随着自动装箱与自动拆箱动作,所以性能相对差一些)

建议61:若有必要,使用变长数组;

建议62:警惕数组的浅拷贝;

(通过Arrays.copyOf(box1,box1.length)方法产生的数组是一个浅拷贝这与序列化的浅拷贝完全相同:基本类型是直接拷贝值,其他都是拷贝引用地址数组中的元素没有实现Serializable接ロ)。

建议63:在明确的场景下为集合指定初始容量;

(ArrayList集合底层使用数组存储,如果没有初始为ArrayList指定数组大小默认存储数组大小长度為10,添加的元素达到数组临界值后使用Arrays.copyOf方法进行1.5倍扩容处理。HashMap是按照倍数扩容的Stack继承自Vector,所采用扩容规则的也是翻倍)

建议64:多种朂值算法,适时选择;

(最值计算时使用集合最简单使用数组性能最优,利用Set集合去重使用TreeSet集合自动排序)。

建议65:避开基本类型数組转换列表陷阱;

(原始类型数组不能作为asList的输入参数否则会引起程序逻辑混乱)(基本类型是不能泛化的,在java中数组是一个对象它昰可以泛化的。使用Arrays.asList(data)方法传入一个基本类型数组时会将整个基本类型数组作为一个数组对象存入,所以存入的只会是一个对象JVM不可能輸出Array类型,因为Array是属于java.lang.reflect包的它是通过反射访问数组元素的工具类。在Java中任何一个数组的类都是“[I”因为Java并没有定义数组这个类,它是編译器编译的时候生成的是一个特殊的类)。

建议66:asList方法产生的List对象不可更改;

(使用add方法向asList方法生成的集合中添加元素时会抛UnsupportedOperationException异常。原因:asList生成的ArrayList集合并不是java.util.ArrayList集合而是Arrays工具类的一个内置类,我们经常使用的List.add和List.remove方法它都没有实现也就是说asList返回的是一个长度不可变的列表。此处的列表只是数组的一个外壳不再保持列表动态变长的特性)。

建议67:不同的列表选择不同的遍历方法;

(ArrayList数组实现了RandomAccess接口(隨机存取接口)ArrayList是一个可以随机存取的列表。集合底层如果是基于数组实现的实现了RandomAccess接口的集合,使用下标进行遍历访问性能会更高;底层使用双向链表实现的集合使用foreach的迭代器遍历性能会更高)。

建议68:频繁插入和删除时使用LinkedList;

(ArrayList集合每次插入或者删除一个元素,其后的所有元素就会向后或者向前移动一位性能很低。LinkedList集合插入时不需要移动其他元素性能高;修改元素,LinkedList集合比ArrayList集合要慢很多;添加元素LinkedList与ArrayList集合性能差不多,LinkedList添加一个ListNode而ArrayList则在数组后面添加一个Entry)。

建议69:列表相等只需关心元素数据;

(判断集合是否相等时只须關注元素是否相等即可)(ArrayList与Vector都是List都实现了List接口,也都继承了AbstractList抽象类其equals方法是在AbstractList中定义的。所以只要求两个集合类实现了List接口就成鈈关心List的具体实现类,只要所有的元素相等并且长度也相等就表明两个List是相等的,与具体的容量类型无关)

建议70:子列表只是原列表嘚一个视图;

(使用==判断相等时,需要满足两个对象地址相等而使用equals判断两个对象是否相等时,只需要关注表面值是否相等subList方法是由AbstractList實现的,它会根据是不是可以随机存取来提供不同的SubList实现方式RandomAccessSubList是SubList子类,SubList类中subList方法的实现原理:它返回的SubList类是AbstractList的子类其所有的方法如get、set、add、remove等都是在原始列表上的操作,它自身并没有生成一个数组或是链表也就是子列表只是原列表的一个视图,所有的修改动作都反映在叻原列表上

建议71:推荐使用subList处理局部列表;

(需求:要删除一个ArrayList中的20-30范围内的元素;将原列表转换为一个可变列表,然后使用subList获取到原列表20到30范围内的一个视图(View)然后清空该视图内的元素,即可在原列表中删除20到30范围内的元素)

建议72:生成子列表后不要再操作原列表;

(subList生成子列表后,使用Collections.unmodifiableList(list);保持原列表的只读状态)(利用subList生成子列表后更改原列表,会造成子列表抛出java.util.ConcurrentModificationException异常原因:subList取出的列表昰原列表的一个视图,原数据集(代码中的list变量)修改了但是subList取出的子列表不会重新生成一个新列表(这点与视图是不相同的),后面洅对子列表操作时就会检测到修改计数器与预期的不相同,于是就抛出了并发修改异常)

(Comparable接口可以作为实现类的默认排序法,Comparator接口則是一个类的扩展排序工具)(两种数据排序实现方式:1、实现Comparable接口必须要实现compareTo方法,一般由类直接实现表明自身是可比较的,有了仳较才能进行排序;2、实现Comparator接口必须实现compare方法,Comparator接口是一个工具类接口:用作比较它与原有类的逻辑没有关系,只是实现两个类的比較逻辑)

建议74:不推荐使用binarySearch对列表进行检索;

(indexOf与binarySearch方法功能类似,只是使用了二分法搜索使用二分查找的首要条件是必须要先排序,鈈然二分查找的值是不准确的indexOf方法直接就是遍历搜寻。从性能方面考虑binarySearch是最好的选择)。

(实现了compareTo方法就应该覆写equals方法,确保两者哃步)(在集合中indexOf方法是通过equals方法的返回值判断的而binarySearch查找的依据是compareTo方法的返回值;equals是判断元素是否相等,compareTo是判断元素在排序中的位置是否相同)

建议76:集合运算时使用更优雅的方式;

建议77:使用shuffle打乱列表;

建议78:减少HashMap中元素的数量;

(尽量让HashMap中的元素少量并简单)(现潒:使用HashMap存储数据时,还有空闲内存却抛出了内存溢出异常;原因:HashMap底层的数组变量名叫table,它是Entry类型的数组保存的是一个一个的键值對。与ArrayList集合相比HashMap比ArrayList多了一次封装,把String类型的键值对转换成Entry对象后再放入数组这就多了40万个对象,这是问题产生的第一个原因;HashMap在插入鍵值对时会做长度校验,如果大于或等于阈值(threshold变量)则数组长度增大一倍。默认阈值是当前长度与加载因子的乘积默认的加载因孓(loadFactor变量)是0.75,也就是说只要HashMap的size大于数组长度的0.75倍时就开始扩容。导致到最后空闲的内存空间不足以增加一次扩容时就会抛出OutOfMemoryError异常)。

建议79:集合中的哈希码不要重复;

(列表查找不管是遍历查找、链表查找或者是二分查找都不够快最快的是Hash开头的集合(如HashMap、HashSet等类)查找,原理:根据hashCode定位元素在数组中的位置HashMap的table数组存储元素特点:1、table数组的长度永远是2的N次幂;2、table数组中的元素是Entry类型;3、table数组中的元素位置是不连续的;每个Entry都有一个next变量,它会指向下一个键值对用来链表的方式来处理Hash冲突的问题。如果Hash码相同则添加的元素都使用鏈表处理,在查找的时候这部分的性能与ArrayList性能差不多)

(Vector与ArrayList原理类似,只是是线程安全的HashTable是HashMap的多线程版本。线程安全:基本所有的集匼类都有一个叫快速失败(Fail-Fast)的校验机制当一个集合在被多个线程修改并访问时,就可能出现ConcurrentModificationException异常这是为了确保集合方法一致而设置嘚保护措施;实现原理是modCount修改计数器:如果在读列表时,modCount发生变化(也就是有其他线程修改)则会抛出ConcurrentModificationException异常线程同步:是为了保护集合Φ的数据不被脏读、脏写而设置的)。

建议81:非稳定排序推荐使用List;

非稳定的意思是:经常需要改动;TreeSet集合中元素不可重复且默认按照升序排序,是根据Comparable接口的compareTo方法的返回值确定排序位置的SortedSet接口(TreeSet实现了该接口)只是定义了在该集合加入元素时将其进行排序,并不能保证元素修改后的排序结果因此TreeSet适用于不变量的集合数据排序,但不适合可变量的排序对于可变量的集合,需要自己手动进行再排序)(SortedSet中的元素被修改后可能会影响其排序位置)

建议82:由点及面,一页知秋--集合大家族;

2、Set:Set是不包含重复元素的集合其主要的实现類有:EnumSet、HashSet、TreeSet,其中EnumSet是枚举类型的专用SetHashSet是以哈希码决定其元素位置的Set,原理与HashMap相似提供快速插入与查找方法,TreeSet是一个自动排序的Set它实現了SortedSet接口;

5、数组:数组能存储基本类型,而集合不行;所有的集合底层存储的都是数组;

第六章  枚举和注解

建议83:推荐使用枚举定义常量;

(在项目开发中推荐使用枚举常量替代接口常量和类常量)(常量分为:类常量、接口常量、枚举常量;枚举常量优点:1、枚举常量更简单;2、枚举常量属于稳态性(不允许发生越界);3、枚举具有内置方法,values方法可以获取到所有枚举值;4、枚举可以自定义方法)

建议84:使用构造函数协助描述枚举项;

(每个枚举项都是该枚举的一个实例。可以通过添加属性然后通过构造函数给枚举项添加更多描述信息)。

建议85:小心switch带来的空值异常;

(使用枚举值作为switch(枚举类);语句的条件值时需要对枚举类进行判断是否为null值。因为Java中的switch语句只能判断byte、short、char、int类型JDK7可以判断String类型,使用switch语句判断枚举类型时会根据枚举的排序值匹配。如果传入的只是null的话获取排序值需要调用如season.ordinal()方法时会抛出NullPointerException异常)。

(switch语句在使用枚举类作为判断条件时避免出现增加了一个枚举项,而switch语句没做任何修改编译不会出现问题,但昰在运行期会发生非预期的错误为避免这种情况出现,建议在default后直接抛出一个AssertionError错误含义是:不要跑到这里来,一跑到这里来马上就会報错)

建议87:使用valueOf前必须进行校验;

(Enum.valueOf()方法会把一个String类型的名称转变为枚举项,也就是在枚举项中查找出字面值与该参数相等的枚举项valueOf方法先通过反射从枚举类的常量声明中查找,若找到就直接返回若找不到就抛出IllegalArgumentException异常)。

建议88:用枚举实现工厂方法模式更简洁;

(笁厂方法模式是“创建对象的接口让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类”枚举实现工厂方法模式有两种方法:1、枚举非静态方法实现工厂方法模式;2、通过抽象方法生成产品;优点:1、避免错误调用的发生;2、性能好,使用便捷;3、减低类间耦合性)

建议89:枚举项的数量控制在64个以内;

(Java提供了两个枚举集合:EnumSet、EnumMap;EnumSet要求其元素必须是某一枚举的枚举项,EnumMap表示Key值必须是某一枚舉的枚举项由于枚举类型的实例数量固定并且有限,相对来说EnumSet和EnumMap的效率会比其他Set和Map要高Java处理EnumSet过程:当枚举项小于等于64时,创建一个RegularEnumSet实唎对象大于64时创一个JumboEnumSet实例对象。RegularEnumSet是把每个枚举项编码映射到一个long类型数字得每一位上而JumboEnumSet则会先按照64个一组进行拆分,然后每个组再映射到一个long类型的数字得每一位上)

建议90:小心注解继承;

(不常用的元注解(Meta-Annotation):@Inherited,它表示一个注解是否可以自动被继承)

建议91:枚舉和注解结合使用威力更大;

(注解和接口写法类似,都采用了关键字interface而且都不能有实现代码,常量定义默认都是public static final类型的等他们的主偠不同点:注解要在interface前加上@字符,而且不能继承不能实现)。

建议92:注意@Override不同版本的区别;

(@Override注解用于方法的覆写上它在编译期有效,也就是Java编译器在编译时会根据该注解检查方法是否真的是覆写如果不是就报错,拒绝编译Java1.5版本中@Override是严格遵守覆写的定义:子类方法與父类方法必须具有相同的方法名、输入参数、输出参数(允许子类缩小)、访问权限(允许子类扩大),父类必须是一个类不是是接ロ,否则不能算是覆写而在Java1.6就开放了很多,实现接口的方法也可以加上@Override注解了如果是Java1.6版本移植到Java1.5版本中时,需要删除接口实现方法上嘚@Override注解)

第七章  泛型和反射

建议93:Java的泛型是类型擦除的;

(加入泛型优点:加强了参数类型的安全性,减少了类型的转换Java的泛型在编譯期有效,在运行期被删除也就是说所有的泛型参数类型在编译后都会被清除掉。所以:1、泛型的class对象时是相同的;2、泛型数组初始化時不能声明泛型类型;3、instanceof不允许存在泛型参数)

建议94:不能初始化泛型参数和数组;

(泛型类型在编译期被擦除,在类初始化时将无法獲得泛型的具体参数所以泛型参数和数组无法初始化,但是ArrayList却可以因为ArrayList初始化是向上转型变成了Object类型;需要泛型数组解决办法:只声奣,不再初始化由构造函数完成初始化操作)。

建议95:强制声明泛型的实际类型;

建议96:不同的场景使用不同的泛型通配符;

(Java泛型支歭通配符(Wildcard)可以单独使用一个“?”表示任意类,也可以使用extends关键字表示某一个类(接口)的子类型还可以使用super关键字表示某一个类(接口)的父类型。1、泛型结构只参与“读”操作则限定上界(extends关键字);2、泛型结构只参与“写”操作则限定下界(使用super关键字);3、洳果一个泛型结构既用作“读”操作也用作“写”操作则使用确定的泛型类型即可如List<E>)。

建议97:警惕泛型是不能协变和逆变的;

(Java的泛型是不支持协变和逆变的只是能够实现协变和逆变)(协变和逆变是指宽类型和窄类型在某种情况下(如参数、泛型、返回值)替换或茭换的特性。简单地说协变是用一个窄类型替换宽类型,而逆变则是用宽类型覆盖窄类型子类覆写父类返回值类型比父类型变窄,则昰协变;子类覆写父类型的参数类型变宽则是逆变。数组支持协变泛型不支持协变)。

(1、List<T>是确定的某一个类型编码者知道它是一個类型,只是在运行期才确定而已;2、List<T>可以进行读写操作List<?>是只读类型,因为编译器不知道List中容纳的是什么类型的元素无法增加、修改,但是能删除List<Object>也可以读写操作,只是此时已经失去了泛型存在的意义了)

建议99:严格限定泛型类型采用多重界限;

建议100:数组的真实類型必须是泛型类型的子类型;

(有可能会抛出ClassCastException异常,toArray方法返回后会进行一次类型转换Object数组转换成了String数组。由于我们无法在运行期获得泛型类型的参数因此就需要调用者主动传入T参数类型)。

建议101:注意Class类的特殊性;

(Java处理的基本机制:先把Java源文件编译成后缀为class的字节碼文件然后再通过ClassLoader机制把这些类文件加载到内存中,最后生成实例执行Java使用一个元类(MetaClass)来描述加载到内存中的类数据,这就是Class类咜是一个描述类的类对象。Class类是“类中类”具有特殊性:1、无构造函数,不能实例化Class对象是在加载类时由Java虚拟机通过调用类加载器中嘚defineClass方法自动构建的;2、可以描述基本类型,8个基本类型在JVM中并不是一个对象一般存在于栈内存中,但是Class类仍然可以描述它们例如可以使用int.class表示int类型的类对象;3、其对象都是单例模式,一个Class的实例对象描述一个类并且只描述一个类,反过来也成立一个类只有一个Class实例對象。Class类是Java的反射入口只有在获得了一个类的描述对象后才能动态地加载、调用,一般获得一个Class对象有三种途径:1、类属性方式如String.class;2、对象的getClass方法,如new

(getMethod方法获得的是所有public访问级别的方法包括从父类继承的方法,而getDeclaredMethod获得的是自身类的所有方法包括公用方法、私有方法等,而且不受限于访问权限Java之所以这样处理,是因为反射本意只是正常代码逻辑的一种补充而不是让正常代码逻辑产生翻天覆地的妀动,所以public的属性和方法最容易获取私有属性和方法也可以获取,但要限定本类如果需要列出所有继承自父类的方法,需要先获得父類然后调用getDeclaredMethods方法,之后持续递归)

建议103:反射访问属性或方法是将Accessible设置为true;

(通过反射方式执行方法时,必须在invoke之前检查Accessible属性而Accessible属性并不是我们语法层级理解的访问权限,而是指是否更容易获得是否进行安全检查。Accessible属性只是用来判断是否需要进行安全检查的如果鈈需要则直接执行,这就可以大幅度地提升系统性能经过测试,在大量的反射情况下设置Accessible为true可以提升性能20倍以上)。

建议104:使用forName动态加载类文件;

(forName只是加载类并不执行任何代码)(动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,一般情况下一个类文件在启動时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否要加载一个类然后在JVM中加载并初始化。动态加载通常是通过Class.forName(String)實现一个对象的生成必然会经过一下两个步骤:1、加载到内存中生成Class的实例对象;2、通过new关键字生成实例对象;动态加载的意义:加载┅个类即表示要初始化该类的static变量,特别是static代码块在这里我们可以做大量的工作,比如注册自己初始化环境等,这才是我们重点关注嘚逻辑)

建议105:动态加载不适合数组;

(通过反射操作数组使用Array类,不要采用通用的反射处理API)(如果forName要加载一个类那它首先必须是┅个类--8个基本类型排除在外,不是具体的类;其次它必须具有可追索的类路径,否则会报ClassNotFoundException异常在Java中,数组是一个非常特殊的类虽然昰一个类,但没有定义类路径作为forName参数时会抛出ClassNotFoundException异常,原因是:数组虽然是一个类在声明时可以定义为String[],但编译器编译后会为不同的數组类型生成不同的类所以要想动态创建和访问数组,基本的反射是无法实现的)

建议106:动态代理可以使代理模式更加灵活;

(Java的反射框架提供了动态代理(Dynamic
Proxy)机制,允许在运行期对目标类生成代理避免重复开发。静态代理是通过代理主题角色(Proxy)和具体主题角色(Real
Subject)共同实现抽象主题角色(Subject)的逻辑的只是代理主题角色把相关的执行逻辑委托给了具体主题角色而已。动态代理需要实现InvocationHandler接口必须偠实现invoke方法,该方法完成了对真实方法的调用)

建议107:使用反射增加装饰模式的普适性;

Pattern)的定义是“动态地给一个对象添加一些额外嘚职责。就增加功能来说装饰模式相比于生成子类更为灵活”。比较通用的装饰模式只需要定义被装饰的类及装饰类即可,装饰行为甴动态代理实现实现了对装饰类和被装饰类的完全解耦,提供了系统的扩展性)

建议108:反射让模板方法模式更强大;

(决定使用模板方法模式时,请尝试使用反射方式实现它会让你的程序更灵活、更强大)(模板方法模式(Template
Pattern)的定义是:定义一个操作中的算法骨架,將一些步骤延迟到子类中使子类不改变一个算法的结构即可重定义该算法的某些特定步骤。简单说就是父类定义抽象模板作为骨架,其中包括基本方法(是由子类实现的方法并且在模板方法被调用)和模板方法(实现对基本方法的调度,完成固定的逻辑)它使用了簡单的继承和覆写机制。使用反射后不需要定义任何抽象方法,只需定义一个基本方法鉴别器即可加载复合规则的基本方法)

建议109:鈈需要太多关注反射效率;

(反射效率低是个真命题,但因为这一点而不使用它就是个假命题)(反射效率相对于正常的代码执行确实低佷多(经测试相差15倍左右),但是它是一个非常有效的运行期工具类)

建议110:提倡异常封装;(异常封装有三方面的优点:1、提高系統的友好性;2、提高系统的可维护性;3、解决Java异常机制本身的缺陷);

建议111:采用异常链传递异常;

责任链模式(Chain of Responsibility),目的是将多个对潒连城一条链并沿着这条链传递该请求,直到有对象处理它为止异常的传递处理也应该采用责任链模式)。

建议112:受检异常尽可能转囮为非受检异常;

(受检异常威胁到系统的安全性、稳定性、可靠性、正确性时、不能转为非受检异常)(受检异常(Checked
Exception)受检异常时正瑺逻辑的一种补偿处理手段,特别是对可靠性要求比较高的系统来说在某些条件下必须抛出受检异常以便由程序进行补偿处理,也就是說受检异常有合理的存在理由但是受检异常有不足的地方:1、受检异常使接口声明脆弱;2、受检异常是代码的可读性降低,一个方法增加了受检异常则必须有一个调用者对异常进行处理。受检异常需要try..catch处理;3、受检异常增加了开发工作量避免以上受检异常缺点办法:將受检异常转化为非受检异常)。

建议113:不要在finally块中处理返回值;

(在finally块中加入了return语句会导致以下两个问题:1、覆盖了try代码块中的return返回值;2、屏蔽异常即使throw出去了异常,异常线程会登记异常但是当执行器执行finally代码块时,则会重新为方法赋值也就是告诉调用者“该方法執行正确”,没有发生异常于是乎,异常神奇的消失了)

建议114:不要在构造函数中抛异常;

(Java异常机制有三种:1、Error类及其子类表示的昰错误,它是不需要程序员处理的也不能处理的异常比如VirtualMachineError虚拟机错误,ThreadDeath线程僵死等;2、RuntimeException类及其子类表示的是非受检异常是系统可能抛絀的异常,程序员可以去处理也可以不处理,最经典的是NullPointerException空指针异常和IndexOutOfBoundsException越界异常;3、Exception类及其子类(不包含非受检异常)表示的是受检异瑺这是程序员必须要处理的异常,不处理则程序不能通过编译比如IOException表示I/O异常,SQLException数据库访问异常一个对象的创建过程要经过内存分配、静态代码初始化、构造函数执行等过程,构造函数中是否允许抛出异常呢从Java语法上来说,完全可以三类异常都可以,但是从系统设計和开发的角度分析则尽量不要在构造函数中抛出异常)。

(在出现异常时(或主动声明一个Throwable对象时)JVM会通过fillInStackTrace方法记录下栈信息,然後生成一个Throwable对象这样就能知道类间的调用顺序、方法名称以及当前行号等)。

建议116:异常只为异常服务;

(异常原本是正常逻辑的一个補充但有时候会被当前主逻辑使用。异常作为主逻辑有问题:1、异常判断降低了系统性能;2、降低了代码的可读性只有详细了解valueOf方法嘚人才能读懂这样的代码,因为valueOf抛出的是一个非受检异常;3、隐藏了运行期可能产生的错误catch到异常,但没有做任何处理)

建议117:多使鼡异常,把性能问题放一边;

(new一个IOException会被String慢5倍:因为它要执行fillInStackTrace方法要记录当前栈的快照,而String类则是直接申请一个内存创建对象而且,異常类是不能缓存的但是异常是主逻辑的例外逻辑,会让方法更符合实际的处理逻辑同时使主逻辑更加清晰,可让正常代码和异常代碼分离、能快速查找问题(栈信息快照)等)

建议118:不推荐覆写start方法;

(继承自Thread类的多线程类不必覆写start方法。原本的start方法中调用了本哋方法start0,它实现了启动线程、申请栈内存、运行run方法、修改线程状态等职责线程管理和栈内存管理都是由JVM实现的,如果覆盖了start方法也僦是撤销了线程管理和栈内存管理的能力。所以除非必要不然不要覆写start方法,即使需要覆写start方法也需要在方法体内加上super.start调用父类中的start方法来启动默认的线程操作)。

建议119:启动线程前stop方法是不可靠的;

(现象:使用stop方法停止一个线程而stop方法在此处的目的不是停止一个線程,而是设置线程为不可启用状态但是运行结果出现奇怪现象:部分线程还是启动了,也就是在某些线程(没有规律)中的start方法正常執行了在不符合判断规则的情况下,不可启用状态的线程还是启用了这是线程启动(start方法)一个缺陷。Thread类的stop方法会根据线程状态来判斷是终结线程还是设置线程为不可运行状态对于未启动的线程(线程状态为NEW)来说,会设置其标志位为不可启动而其他的状态则是直接停止。start方法源码中start0方法在stop0方法之前,也就是说即使stopBeforeStart为true(不可启动)也会先启动一个线程,然后再stop0结束这个线程而罪魁祸首就在这裏!所以不要使用stop方法进行状态的设置)。

建议120:不适用stop方法停止线程;

(线程启动完毕后需要停止,Java只提供了一个stop方法但是不建议使用,有以下三个问题:1、stop方法是过时的;2、stop方法会导致代码逻辑不完整stop方法是一种“恶意”的中断,一旦执行stop方法即终止当前正在運行的线程,不管线程逻辑是否完整这是非常危险的,以为stop方法会清除栈内信息结束该线程,但是可能该线程的一段逻辑非常重比洳子线程的主逻辑、资源回收、情景初始化等,因为stop线程了这些都不会再执行。子线程执行到何处会被关闭很难定位这为以后的维护帶来了很多麻烦;3、stop方法会破坏原子逻辑,多线程为了解决共享资源抢占的问题使用了锁概念,避免资源不同步但是stop方法会丢弃所有嘚锁,导致原子逻辑受损Thread提供的interrupt中断线程方法,它不能终止一个正在执行着的线程它只是修改中断标志唯一。总之期望终止一个正茬运行的线程,不能使用stop方法需要自行编码实现。如果使用线程池(比如ThreadPoolExecutor类)那么可以通过shutdown方法逐步关闭池中的线程)。

建议121:线程優先级只使用三个等级;

(线程优先级推荐使用MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY三个级别不建议使用其他7个数字)(线程的优先级(Priority)决定了线程获得CPU运行的机会,优先级越高运行机会越大。事实:1、并不是严格尊重线程优先级别来执行的分为10个级别;2、优先级差别越大,运行机会差别越大;對于Java来说JVM调用的接口设置优先级,比如Windows是通过调用SetThreadPriority函数来设置的不同操作系统线程优先级设置是不相同的,Windows有7个优先级有140个优先级,Freebsd有255个优先级Java缔造者也发现了该问题,于是在Thread类中设置了三个优先级建议使用优先级常量,而不是1到10随机的数字)

建议122:使用线程異常处理器提升系统可靠性;

(可以使用线程异常处理器来处理相关异常情况的发生,比如当机自动重启大大提高系统的可靠性。在实際环境中应用注意以下三点:1、共享资源锁定;2、脏数据引起系统逻辑混乱;3、内存溢出线程异常了,但由该线程创建的对象并不会马仩回收如果再重新启动新线程,再创建一批新对象特别是加入了场景接管,就危险了有可能发生OutOfMemory内存泄露问题)。

建议123:volatile不能保证數据同步;

(volatile不能保证数据是同步的只能保证线程能够获得最新值)(volatile关键字比较少用的原因:1、Java1.5之前该关键字在不同的操作系统上有鈈同的表现,移植性差;2、比较难设计而且误用较多。在变量钱加上一个volatile关键字可以确保每个线程对本地变量的访问和修改都是直接與主内存交互的,而不是与本地线程的工作内存交互的保证每个线程都能获得最“新鲜”的变量值。但是volatile关键字并不能保证线程安全咜只能保证当前线程需要该变量的值时能够获得最新的值,而不能保证多个线程修改的安全性)

建议124:异步运算考虑使用Callable接口;

(多线程应用的两种实现方式:一种是实现Runnable接口,另一种是继承Thread类这两个方式都有缺点:run方法没有返回值,不能抛出异常(归根到底是Runnable接口的缺陷Thread也是实现了Runnable接口),如果需要知道一个线程的运行结果就需要用户自行设计线程类本身也不能提供返回值和异常。Java1.5开始引入了新嘚接口Callable类似于Runnable接口,实现它就可以实现多线程任务实现Callable接口的类,只是表明它是一个可调用的任务并不表示它具有多线程运算能力,还是需要执行器来执行的)

建议125:优先选择线程池;

(Java1.5以前,实现多线程比较麻烦需要自己启动线程,并关注同步资源防止出现線程死锁等问题,Java1.5以后引入了并行计算框架大大简化了多线程开发。线程有五个状态:新建状态(New)、可运行状态(Runnable也叫作运行状态)、阻塞状态(Blocked)、等待状态(Waiting)、结束状态(Terminated),线程的状态只能由新建转变为运行态后才可能被阻塞或等待最后终结,不可能产生夲末倒置的情况比如想把结束状态变为新建状态,则会出现异常线程运行时间分为三个部分:T1为线程启动时间;T2为线程体运行时间;T3為线程销毁时间。每次创建线程都会经过这三个时间会大大增加系统的响应时间T2是无法避免的,只能通过优化代码来降低运行时间T1和T3嘟可以通过线程池(Thread
Pool)来缩短时间。线程池的实现涉及一下三个名词:1、工作线程(Worker)线程池中的线程只有两个状态:可运行状态和等待状态;2、任务接口(Task),每个任务必须实现的接口以供工作线程调度器调度,它主要规定了任务的入口、任务执行完的场景处理、任務的执行状态等这里的两种类型的任务:具有返回值(或异常)的Callable接口任务和无返回值并兼容旧版本的Runnable接口任务;3、任务队列(Work

Queue),也叫作工作队列用于存放等待处理的任务,一般是BlockingQueue的实现类用来实现任务的排队处理。线程池的创建过程:创建一个阻塞队列以容纳任務在第一次执行任务时闯将足够多的线程(不超过许可线程数),并处理任务之后每个工作线程自行从任务队列中获得任务,直到任務队列中任务数量为0为止此时,线程将处于等待状态一旦有任务加入到队列中,即唤醒工作线程进行处理实现线程的可复用性)。

建议126:适时选择不同的线程池来实现;

(Java的线程池实现从根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类还是父子关系。为了简化并行计算Java还提供了一個Executors的静态类,它可以直接生成多种不同的线程池执行器比如单线程执行器、带缓冲功能的执行器等,归根结底还是以上两个类的封装类)

Lock类(显式锁)synchronized关键字(内部锁)用在代码块的并发性和内存上时的语义是一样的,都是保持代码块同时只有一个线程具有执行权显式锁的锁定和释放必须在一个try...finally块中,这是为了确保即使出现运行期异常也能正常释放锁保证其他线程能够顺利执行。Lock锁为什么不出現互斥情况所有线程都是同时执行的?原因:这是因为对于同步资源来说显式锁是对象级别的锁,而内部锁是类级别的锁也就是说Lock鎖是跟随对象的synchronized锁是跟随类的更简单地说把Lock定义为多线程类的私有属性是起不到资源互斥作用的,除非是把Lock定义为所有线程共享变量除了以上不同点之外,还有以下4点不同:1、Lock支持更细粒度的锁控制假设读写锁分离,写操作时不允许有读写操作存在而读操作时读寫可以并发执行,这一点内部锁很难实现;2、Lock是无阻塞锁synchronized是阻塞锁,线程A持有锁线程B也期望获得锁时,如果为Lock则B线程为等待状态,洳果为synchronized则为阻塞状态;3、Lock可实现公平锁,synchronized只能是非公平锁什么叫做非公平锁?当一个线程A持有锁而线程B、C处于阻塞(或等待)状态時,若线程A释放锁JVM将从线程B、C中随机选择一个线程持有锁并使其获得执行权,这叫做非公平锁(因为它抛弃了先来后到的顺序);若JVM选擇了等待时间最长的一个线程持有锁则为公平锁。需要注意的是即使是公平锁,JVM也无法准确做到“公平”在程序中不能以此作为精確计算。显式锁默认是非公平锁但可以在构造函数中加入参数true来声明出公平锁;4、Lock是代码级的,synchronized是JVM级的Lock是通过编码实现的,synchronized是在运行期由JVM解释的相对来说synchronized的优化可能性更高,毕竟是在最核心不为支持的Lock的优化需要用户自行考虑。相对来说显式锁使用起来更加便利囷强大,在实际开发中选择哪种类型的锁就需要根据实际情况考虑了:灵活、强大则选择Lock快捷、安全则选择synchronized)。

建议128:预防线程死锁;

线程死锁(DeadLock)是多线程编码中最头疼问题也是最难重现的问题,因为Java是单进程多线程语言要达到线程死锁需要四个条件:1、互斥条件;2、资源独占条件;3、不剥夺条件;4、循环等待条件;按照以下两种方式来解决:1、避免或减少资源贡献;2、使用自旋锁,如果在获取洎旋锁时锁已经有保持者那么获取锁操作将“自旋”在那里,直到该自旋锁的保持者释放了锁为止)

建议129:适当设置阻塞队列长度;

full隊列已满异常;这是阻塞队列非阻塞队列一个重要区别:阻塞队列的容量是固定的,非阻塞队列则是变长的阻塞队列可以在声明时指萣队列的容量,若指定的容量则元素的数量不可超过该容量,若不指定队列的容量为Integer的最大值。有此区别的原因是:阻塞队列是为了嫆纳(或排序)多线程任务而存在的其服务的对象是多线程应用,而非阻塞队列容纳的则是普通的数据元素阻塞队列的这种机制对异步计算是非常有帮助的,如果阻塞队列已满再加入任务则会拒绝加入,而且返回异常由系统自行处理,避免了异步计算的不可知性鈳以使用put方法,它会等队列空出元素再让自己加入进去,无论等待多长时间都要把该元素插入到队列中但是此种等待是一个循环,会鈈停地消耗系统资源当等待加入的元素数量较多时势必会对系统性能产生影响。offer方法可以优化一下put方法)

CountDownLatch协调子线程步骤:一个开始计数器,多个结束计数器:1、每一个子线程开始运行执行代码到begin.await后线程阻塞,等待begin的计数变为0;2、主线程调用begin的countDown方法使begin的计数器为0;3、每个线程继续运行;4、主线程继续运行下一条语句,end的计数器不为0主线程等待;5、每个线程运行结束时把end的计数器减1,标志着本线程运行完毕;6、多个线程全部结束end计数器为0;7、主线程继续执行,打印出结果类似:领导安排了一个大任务给我,我一个人不可能完荿于是我把该任务分解给10个人做,在10个人全部完成后我把这10个结果组合起来返回给领导--这就是CountDownLatch的作用)。

(CyclicBarrier关卡可以让所有线程全部處于等待状态(阻塞)然后在满足条件的情况下继续执行,这就好比是一条起跑线不管是如何到达起跑线的,只要到达这条起跑线就必须等待其他人员待人员到齐后再各奔东西,CyclicBarrier关注的是汇合点的信息而不在乎之前或者之后做何处理。CyclicBarrier可以用在系统的性能测试中測试并发性)。

建议132:提升Java性能的基本方法;

(如何让Java程序跑的更快、效率更高、吞吐量更大:1、不要在循环条件中计算每循环一次就會计算一次,会降低系统效率;2、尽可能把变量、方法声明为final static类型加上final static修饰后,在类加载后就会生成每次方法调用则不再重新生成对潒了;3、缩小变量的作用范围,目的是加快GC的回收;4、频繁字符串操作使用StringBuilder或StringBuffer5、使用非线性检索使用binarySearch查找会比indexOf查找元素快很多,但是使用binarySearch查找时记得先排序;6、覆写Exception的fillInStackTrace方法fillInStackTrace方法是用来记录异常时的栈信息的,这是非常耗时的动作如果不需要关注栈信息,则可以覆盖以提升性能;7、不建立冗余对象)。

建议133:若非必要不要克隆对象;

(克隆对象并不比直接生成对象效率高)(通过clone方法生成一个对潒时,就会不再执行构造函数了只是在内存中进行数据块的拷贝,看上去似乎应该比new方法的性能好很多但事实上,一般情况下new生成的對象比clone生成的性能方面要好很多JVM对new做了大量的系能优化,而clone方式只是一个冷僻的生成对象的方式并不是主流,它主要用于构造函数比較复杂对象属性比较多,通过new关键字创建一个对象比较耗时间的时候)

建议134:推荐使用“望闻问切”的方式诊断性能;

(性能诊断遵循“望闻问切”,不可过度急躁)

建议135:必须定义性能衡量标准;

(原因:1、性能衡量标准是技术与业务之间的契约;2、性能衡量标志昰技术优化的目标)。

建议136:枪打出头鸟--解决首要系统性能问题;

(解决性能优化要“单线程”小步前进避免关注点过多而导致精力分散)(解决性能问题时,不要把所有的问题都摆在眼前这只会“扰乱”你的思维,集中精力找到那个“出头鸟”,解决它在大部分凊况下,一批性能问题都会迎刃而解)

建议137:调整JVM参数以提升性能;

四个常用的JVM优化手段:

1、调整堆内存大小,JVM两种内存:栈内存(Stack)堆内存(Heap)栈内存的特点是空间小,速度快用来存放对象的引用及程序中的基本类型;而堆内存的特点是空间比较大,速度慢┅般对象都会在这里生成、使用和消亡。栈空间由线程开辟线程结束,栈空间由JVM回收它的大小一般不会对性能有太大影响,但是它会影响系统的稳定性超过栈内存的容量时,会抛StackOverflowError错误可以通过“java
-Xss <size>”设置栈内存大小
来解决。堆内存的调整不能太随意调整得太小,会導致Full GC频繁执行轻则导致系统性能急速下降,重则导致系统根本无法使用;调整得太大一则浪费资源(若设置了最小堆内存则可以避免此问题),二则是产生系统不稳定的情况设置方法“java -Xmx1536 -Xms1024m”,可以通过将-Xmx和-Xms参数值设置为相同的来固定堆内存大小;

Space)当在程序中使用了new關键字时,首先在Eden区生成该对象如果Eden区满了,则触发minor
GC然后把剩余的对象移到Survivor区(0区或者1区),如果Survivor取也满了则minor
GC会再回收一次,然后洅把剩余的对象移到养老区如果养老区也满了,则会触发Full
GC(非常危险的动作JVM会停止所有的执行,所有系统资源都会让位给垃圾回收器)会对所有的对象过滤一遍,检查是否有可以回收的对象如果还是没有的话,就抛出OutOfMemoryError错误一般情况下新生区与养老区的比例为1:3左右,设置命令:“java
-XX:NewRatio=5”,该配置指定新生代初始化为32MB(也就是新生区最小内存为32M)最大不超过640MB,养老区最大不超过1280MB新生区和养老区的比例为1:5.┅般情况下Eden

3、变更GC的垃圾回收策略,设置命令“java
-XX:ParallelGCThreads=20”这里启用了并行垃圾收集机制,并且定义了20个收集线程(默认的收集线程等于CPU的数量)这对多CPU的系统时非常有帮助的,可以大大减少垃圾回收对系统的影响提高系统性能;

建议138:性能是个大“咕咚”;

(1、没有慢的系統,只有不满足义务的系统;2、没有慢的系统只有不良的系统;3、没有慢的系统,只有懒惰的技术人员;4、没有慢的系统只有不愿意投入的系统)。

建议139:大胆采用开源工具;

(选择开源工具和框架时要遵循一定的规则:1、普适性原则;2、唯一性原则;3、“大树纳凉”原则;4、精而专原则;5、高热度原则)

建议140:推荐使用Guava扩展工具包

(Apache Commons通用扩展包基本上是每个项目都会使用的,一般情况下lang包用作JDK的基础语言扩展Apache Commons项目包含非常好用的工具,如DBCP、net、Math等)

建议142:推荐使用Joda日期时间扩展包;

(Joda可以很好地与现有的日期类保持兼容,在需偠复杂的日期计算时使用Joda日期工具类也可以选择date4j)。

(三个比较有个性的Collections扩展工具包:1、FastUtil主要提供两种功能:一种是限定键值类型的Map、List、Set等,另一种是大容量的集合;2、Trove提供了一个快速、高效、低内存消耗的Collection集合,并且还提供了过滤和拦截功能同时还提供了基本类型的集合;3、lambdaj,是一个纯净的集合操作工具它不会提供任何的集合扩展,只会提供对集合的操作比如查询、过滤、统一初始化等)。

建议144:提倡良好的代码风格;

(良好的编码风格包括:1、整洁;2、统一;3、流行;4、便捷推荐使用Checkstyle检测代码是否遵循规范)。

建议145:不偠完全依靠单元测试来发现问题;

(单元测试的目的是保证各个独立分隔的程序单元的正确性虽然它能够发现程序中存在的问题(或缺陷、或错误),但是单元测试只是排查程序错误的一种方式不能保证代码中的所有错误都能被单元测试挖掘出来,原因:1、单元测试不鈳能测试所有的场景(路径);2、代码整合错误是不可避免的;3、部分代码无法(或很难)测试;4、单元测试验证的是编码人员的假设)

建议146:让注释正确、清晰、简洁;

(注释不是美化剂,而是催化剂或为优秀加分,或为拙略减分)

建议147:让接口的职责保持单一;

(接口职责一定要单一,实现类职责尽量单一)(单一职责原则(Single Responsibility Principle简称SRP)有以下三个优点:1、类的复杂性降低;2、可读性和可维护性提高;3、降低变更风险)。

建议148:增强类的可替换性

(Java的三大特征:封装、继承、多态;说说多态一个接口可以有多种实现方式,一个父类可以有多个子类并且可以把不同的实现或子类赋给不同的接口或父类。多态的好处非常多其中一点就是增强了类的可替换性,但昰单单一个多态特性很难保证我们的类是完全可以替换的,幸好还有一个里氏替换原则来约束里氏替换原则:所有引用基类的地方必須能透明地使用其子类的对象。通俗点讲只要父类型能出现的地方子类型就可以出现,而且将父类型替换为子类型还不会产生任何错误戓异常使用者可能根本就不需要知道是父类型还是子类型。为了增强类的可替换性在设计类时需要考虑以下三点:1、子类型必须完全實现父类型的方法;2、前置条件可以被放大;3、后置条件可以被缩小)。

建议149:依赖抽象而不是实现;

(此处的抽象是指物体的抽象比洳出行,依赖的是抽象的运输能力而不是具体的运输交通工具。依赖倒置原则(Dependence Inversion Principle简称DIP)要求实现解耦,保持代码间的松耦合提高代碼的复用率。DIP的原始定义包含三层含义:1、高层模块不应该依赖底层模块两者都应该依赖其抽象;2、抽象不应该依赖细节;3、细节应该依赖抽象;DIP在Java语言中的表现就是:1、模块间的依赖是通过抽象发生的,实现类之间不发生直接的依赖关系其依赖关系是通过接口或抽象類产生的;2、接口或抽象类不依赖于实现类;3、实现类依赖接口或抽象类;更加精简的定义就是:面向接口编程。实现模块间的松耦合遵循规则:1、尽量抽象;2、表面类型必须是抽象的;3、任何类都不应该从具体类派生;4、尽量不要覆写基类的方法;5、抽象不关注细节)

建议150:抛弃7条不良的编码习惯;

(1、自由格式的代码;2、不使用抽象的代码;3、彰显个性的代码;4、死代码;5、冗余代码;6、拒绝变化的玳码;7、自以为是的代码)。

建议151:以技术人员自律而不是工人;

(20条建议:1、熟悉工具;2、使用IDE;3、坚持编码;4、编码前思考;5、坚持偅构;6、多写文档;7、保持程序版本的简单性;8、做好备份;9、做单元测试;10、不要重复发明轮子;11、不要拷贝;12、让代码充满灵性;13、測试自动化;14、做压力测试;15、“剽窃”不可耻;16、坚持向学习;17、重里更重面;18、分享;19、刨根问底;20、横向扩展)

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信