JAVA优化编程读书笔记
一、基本的代码规范 我就不记了
二、JVM内存管理
垃圾回收GC:
垃圾:对象在JVM运行空间中无法通过根集合到达时。根集合是由类中的静态引用域和本地引用域组成的。
堆、栈、代码区、数据段
新对象区域与老对象区域。
JVM中对象的生命周期
Creation---Using---Invisible---Unreachable---Collected---Finalized---Free
创建对象时:
1. 避免在循环体中创建对象
2. 及时使对象符合垃圾回收准则,引用置空
3. 不要采用过深的继承层次
4. 访问本地变量优于访问类中的变量
5. 不要对一个对象多次初始化,尽量在使用时再初始化
Strong---Soft---Weak---Phantom Reference
在出了作用范围后,及时把对象的引用置为null。
最后一个阶段:
1. GC发现对象不可达
2. Finialize已经被执行
3. 对象空间被重用
Finalize
可以单独写一个destroy,在finalize中调用,并且在子类中重写这两个方法以实现递归释放
数组创建
可以用软引用来引用数组,提醒JVM及时回收
静态变量
1、 变量包含的对象体积大
2、 变量所包含对象生命周期较长
3、 对象数据稳定
4、 该类的对象实例共享该变量
对象重用与GC
连接池要注意:
及时清除不用的对象,注意内存限制
瞬间值
在RMI时对于不需要传输的数据加上transient修饰,防止被序列化。
不要提前创建对象
JVM内存参数调优
可以更改堆栈的大小,类文件的大小
三、表达式、语句与保留字
简单,单一意图。
== 比 equals更有效,在比较对象的引用是否相等时可采用==
字符串intern驻留后更方便的使用==来进行比较。
常用保留字:
Static:
在非静态方法中不能引用静态变量
Final:
防止方法被子类覆盖。
内联函数:
把代码插入到调用者代码处的函数,宏是由预处理器对宏进行替代,内联函数是由编译器控制的,在需要用到时像宏一样展开,取消了参数压栈,减小了调用开销,但是代码不能太多。
Final的方法的代码展开后插入到调用者代码处,提高效率。
Synchronized: 只能被一个线程访问。
Instanceof: null不是任何对象的引用。
For:循环中使用int类型作为步进比较好,short/byte会被隐式转化为int。
1. 尽量使用system.arraycopy来拷贝数组。
2. 尽量避免在循环中调用方法。
3. 避免在循环中存取数组元素,最好在循环体内使用临时变量,在循环体外更改数组的值
4. 尽量采用0作为循环的终结条件,即i—
5. 避免在最终条件的比较时采用方法返回值来判断,可以单独写一个Boolean再比较
6. 在循环体外使用try/catch
7. 把最长的循环放在内层,最短的放在外层,减少循环切换
8. 循环体内有逻辑判断时,尽量放在循环外
四、JAVA核心类与性能优化
1. 散列表类: Vector、HashTable线程安全的,ArrayList、HashMap非线程安全的,但是效率更高。尽量使用ArrayList,可以通过 List list = Collections.synchronizedList(newArrayList());使其线程安全。
2. 在已知容量时,ensureCapacity()更好
3. Array与Linked选择根据需要来。
4. 字符串String:尽量使用StringBuffer和append
5. 尽量避免在循环中调用string.length(),因为调用方法是需要代价的,与array.length不同,这个只是一个属性,只会初始化一次
6. toCharArray代替charAt()去访问特定下标的元素,因为索引更加高效
7. 字符串转换为数字不如直接把数字转换为数字高效。
8. 最好使用Buffered来缓冲输入输出流,自定义的缓冲区最好是512的倍数。
9. 通过压缩流来减少网络上传输负载
10. 通过非阻塞流NIO
11. 最好使用操作符如+等来格式化数据,生成一致格式的字符串
12. File长度的length()方法需要OS的系统调用来完成,尽量避免重复使用。
五、JNI
六、类与接口
1、 不要再构造器中初始化其他类。
2、 不用再构造器中初始化静态变量,只需要初始化一次即可
3、 不能再构造器中创建自身实例,会造成死循环
4、 类名一般用public修饰,除非是内部类或者不想被包以外的访问
5、 内部类只能被主类以外的其他内部类继承,但是也尽量避免使用
6、 分清楚何时该使用继承或者组合
7、 接口与抽象类:
相同:1. 接口和抽象类均不能被实例化
2. 接口和抽象类中都可以有属性
不同:1. 接口的属性是public static final,方法是public abstract的
2. 接口的方法不能有方法体,抽象类可以,甚至可以有非抽象方法
3. 接口可以多重继承,抽象类单重
4. 接口不能作为主应用程序独立运行,抽象类在特定的声明下可以,如static方法在main中被调用
public abstract class GCTeat{
private static void show()
{
System.out.println("hello, i am abstract class");
}
public static void main(String[] args)
{
show();
}
}
在接口中尽量少定义常量,避免名称空间混乱;抽象类的效率更高一点,两个一般都采用工厂方法模式实现。
8、 内部类:可以访问主类所有成员,但是主类不能直接访问内部类,需要创建实例来引用
内部类中不能声明static成员。常见用法:在类中创建一个线程类,调用主类的返回周期较长的方法。
import java.lang.*;
public class InnerClass
{
private boolean flag = true;
public InnerClass()
{
new Thread(new innerThread()).start();
}
private void mainMethod()
{
while(flag)
{
System.out.println("hello!");
//forbidden: System.out.println(index);
try
{
Thread.sleep(3000);
}
catch(Exception e)
{
}
}
}
public class innerThread implements java.lang.Runnable{
//forbidden: private static int index = 1;
public int index = 1;
public void run()
{
mainMethod();
}
}
public static void main(String [] args)
{
InnerClass ic = new InnerClass();
}
}