改善java代码的建议

这篇文章是在对《编写高质量代码:改善Java程序的151个建议》这本书中提到的内容做笔记。

通用的方法和准则

三元操作符的类型务必唯一

<span style="font-size:18px;">public class Client {
	public static void main(String[] args) {
		int i = 80;
		String s = String.valueOf(i<100?90:100);
		String s1 = String.valueOf(i<100?90:100.0);
		System.out.println("两者是否相等:"+s.equals(s1));
	}
}</span>
可以看到输出结果是false,因为三元操作符的返回值必须唯一s的返回是int型,而s1是浮点型。

别人null值和空值影响变长方法

<span style="font-size:18px;">public class Client {
	public void methodA(String str,Integer... is){		
		System.out.println("Integer");
	}
	
	public void methodA(String str,String... strs){		
		System.out.println("String");
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.methodA("China", 0);
		client.methodA("China", "People");
//		client.methodA("China");
//		client.methodA("China",null);
	}
}</span>

//client.methodA("China")  由于变长方法重载,都满足只有一个String的参数,编译器没办法知道选在哪个函数,因此编译不通过。

//client.methodA("China",null)  null是没有类型的。编译器没办法知道选在哪个函数,因此编译不通过。

可以改用如下写法:明确告诉编译器strs类型,编译通过

public class Client {
	public void methodA(String str,Integer... is){		
		System.out.println("Integer");
	}
	
	public void methodA(String str,String... strs){		
		System.out.println("String");
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		Integer[] strs = null;
		client.methodA("China",strs);
	}
}

警惕自增的陷阱

public static void main(String[] args) {
		int count =0;
		for(int i=0;i<10;i++){
			count=count++;
		}
		System.out.println("count="+count);
	}

可以看到,count最终的输出值是0.

在jvm中的执行步骤如下:
1、jvm 把count值(0)拷贝到临时变量区。
2、count值加1,此count为1.
3、返回临时变量区的值,此时是0,值没有改变。
4、返回值赋值给count,count重新变为0。

大概类似于如下代码
public static int mockAdd(int count){
		//先保存初始值
		int temp =count;
		//做自增操作
		count = count+1;
		//返回原始值
		return temp;
	}

基本类型

用偶判断,不用奇判断

public static void main(String[] args) {
		//接收键盘输入参数
		Scanner input = new Scanner(System.in);
		System.out.print("请输入多个数字判断奇偶:");
		while(input.hasNextInt()){
			int i = input.nextInt();
			String str =i+ "->" + (i%2 ==1?"奇数":"偶数");
			System.out.println(str);
			
		}
	}

可以看到,当输入数字 -1 时,运行结果为偶数。模拟下java %符号的运行代码
<span style="font-size:18px;">//模拟取余计算,dividend被除数,divisor除数
	</span><span style="font-size:14px;">public static int remainder(int dividend,int divisor){
		return dividend - dividend / divisor * divisor;
	}</span>
可以看到java执行 %操作时原来是这么取值的。

因此修改这段代码只需改为 i%2==0?"偶数":"奇数"就可以。

边界、边界、边界

public class Client {
	//一个会员拥有产品的最大数量
	public final static int LIMIT = 2000;
	public static void main(String[] args) {
		//会员当前拥有产品数量
		int cur = 1000;
		Scanner input = new Scanner(System.in);
		System.out.print("请输入需要预定的数量:");
		while(input.hasNextInt()){
			int order = input.nextInt();
			//当前拥有的与准备订购的产品数量之
			if(order>0 && order+cur<=LIMIT){
				System.out.println("你已经成功预定的"+order+"个产品!");
			}else{
				System.out.println("超过限额,预订失败!");
			
			}
		}
	}
}
这段代码看似没有问题,但是当输入人员输入2147483647时,发现提示订购成功!没错,因为这个到达了int的边界, 2147483647 + 1000 = -xxxxxxxxxx当然小于2000了。!!所以边界一定要注意!!!

优先使用整形池

ublic static void main(String[] args) {		
		Scanner input = new Scanner(System.in);
		while(input.hasNextInt()){
			int ii = input.nextInt();
			System.out.println("\n===="+ii+" 的相等判断======");
			//两个通过new产生的Integer对象
			Integer i =new Integer(ii);
			Integer j = new Integer(ii);
			System.out.println("new产生的对象:" + (i==j));
			
			//基本类型转为装箱类型后比较
			i=ii;
			j=ii;
			System.out.println("基本类型转换的对象:" + (i==j));
			
			//通过静态方法生成一个实例
			i=Integer.valueOf(ii);
			j = Integer.valueOf(ii);
			System.out.println("valueOf产生的对象:"  + (i==j));			
		}		
	}
当输入127时结果为:
====127 的相等判断======
new产生的对象:false
基本类型转换的对象:true
valueOf产生的对象:true

当输入128时结果为:
====128 的相等判断======
new产生的对象:false
基本类型转换的对象:false
valueOf产生的对象:false

127的包装类产生的对象是同一个,Integer.valueOf()产生的也是同一个对象,然后128就不同了,这是什么原因了?
看下Integer.valueOf()的代码发现问题:
public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
当数字小128&&大于-127时使用的是整数池中的对象,否则返回的是new的新对象。
private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low));
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }
因此在包装类进行比较时要使用equals()方法而不要使用==,如果需要创建包装对象时,使用valueOf()能够节约内存并提高性能。

类、对象和方法

使用构造代码块精炼程序

代码块分为4种:
1、普通代码块:在方法后面使用{}括起来的代码片段,不能单独执行,必须通过方法名调用执行。
2、静态代码块:在类中使用static修饰,并使用{}括起来的代码片段,用于静态变量的初始化或对象创建前的环境初始化。
3、同步代码块:使用  synchronized关键字修饰,并使用{}括起来的代码片段,他表示同一时间只能有一个线程进入到该方法块中,是一种多线程保护机制。
4、构造代码块:在类中没有任何的前缀或后缀,并使用{}括起来的代码片段。
这四种代码块是怎执行的?来看下下面的代码:
public class Client {
	{
		//构造代码块
		System.out.println("执行构造代码块");
	}
	
	public Client(){
		System.out.println("执行无参构造");
	}
	
	public Client(String _str){
		System.out.println("执行有参构造");
	}
}
当我们new一个Client对象时,发现,会先输出执行构造代码块。 由于构造代码块无法独立运行,实际编译器会把每个构造代码块插入到构造函数的最前端,相当于如下代码:
public class Client {
	
	public Client(){
		System.out.println("执行构造代码块");
		System.out.println("执行无参构造");
	}
	
	public Client(String _str){
		System.out.println("执行构造代码块");
		System.out.println("执行有参构造");
	}
}

构造代码块不是在构造函数之前执行,而是依托于构造函数执行,我们可以把构造代码块运用于如下场景中。
1、初始化实例变量(Instance Variable)
如果每个构造函数都要初始化变量,可以通过构造代码块实现。当然我们也可以通过定义一个方法,然后通过在构造函数中调用这个方法来实现,但是这样需要在每个构造函数中都添加方法调用。如果采用构造代码块,则不用定义和调用,会由编译器写入构造函数中,这才是解决此类问题的最佳方案。

2、初始化实例环境
一个对象必须在适当的场景下才能存在,如果没有适当的场景,则就需要在创建对象时创建此场景。例如J2EE中,要产生HTTP Request必须首先建立HTTP Session,在创建HTTP Request 时就可以通过构造代码块来检查HTTP Session是否存在,不存在就创建之。

是用匿名类的构造函数

看如下代码
public static void main(String[] args) {
		String[] strs = {"aaa","bbb","ccc"};		
		
		List l1 = new ArrayList();
		List l2 = new ArrayList(){};
		List l3 = new ArrayList(){{}};
		System.out.println(l1.getClass());
		System.out.println(l2.getClass());
		System.out.println(l3.getClass());
		System.out.println(l1.getClass() == l2.getClass());
		System.out.println(l2.getClass() == l3.getClass());
		System.out.println(l1.getClass() == l3.getClass());
	}
可以看到输出结果为:


这个是什么原因呢? 原来
l2代表的是一个匿名类的声明与赋值,他定义了一个继承于ArrayList的匿名类,只是没有任何的复写方法而已,其代码类似于:
public static void main(String[] args) {	
		//定义一个继承ArrayList的内部类
		class Sub extends ArrayList{			
		}
		//声明和赋值
		List l2 = new Sub();
	}
l3分开来看也就会明白:
public static void main(String[] args) {	
		//定义一个继承ArrayList的内部类
		class Sub extends ArrayList{
			{
				//初始化块
			}
		}
		//声明和赋值
		List l3 = new Sub();
	}


字符串

正确使用String、StringBuffer、StringBuilder

CharSequence 有三个实现类与字符串有关,分别是String、StringBuffer、StringBuilder。
String类是不可改变的量,一旦创建完以后不能再修改,每次改变String对象值得时候,即使是使用String提供的方法,也是要么创建一个新的字符串对象,要么返回自己。例如String a = "abc"  SubString(1)返回"bc"是一个新对象,而subString(0)返回自己。

StringBuffer则是一个可变的字符序列,StringBuilder和StringBuffer基本相同,区别在于StringBuffer是线程安全的,而StringBuilder是线程不安全的,查看一下源码就可以知道StringBuffer的方法前都有synchronized关键字,因此StringBuilder的效率要比StringBuffer高的多。因此String、StringBuffer、StringBuilder的应用场景也会有一些不同:

1、使用String类的场景。
在字符串不常变化的场景中使用String类,例如常量的申声明、少量的变量运算等。

2、使用StringBuffer的场景。
在频繁进行字符串运算(如拼接、替换、删除等)并且运行在多线程环境中,则可以考虑StringBuffer,例如XML解析、HTTP参数解析和封装等。

3、使用StringBuilder的场景。
在频繁进行字符串的运算(如拼接、替换、删除等)并且运行在多线程环境中,则可以考虑StringBuilder,例如SQL语句的拼装、JSON封装等。

自由使用字符串拼接

字符串拼接方法共有三种 加号、contact和StringBuilder的append方法。

加号方法相当于如下代码
str = new StringBuilder(str).append("c").toString();
效率较低,因为不断创建StringBuilder对象,并且会执行toString方法返回,因此效率较低。

contact方法最后是return new String(buf, true),所以每次contact都会返回一个新的对象。

StringBuilder的append方法都是在做字符数组的处理,如加长和拷贝,因此append方法效率最高。

但是,“+”加号操作更加符合编程习惯,因此如果不是因为性能产生瓶颈的话,可以使用+来进行拼接。

数组和集合

不同列表选择不同的遍历方式

public static int average(List<Integer> list) {
		int sum = 0;		
		if (list instanceof RandomAccess) {
			//可以随机存取,则使用下标遍历
			for (int i = 0, size = list.size(); i < size; i++) {
				sum += list.get(i);
			}
		} else {
			//有序存取,使用foreach方式
			for (int i : list) {
				sum += i;
			}
		}
		// 除以人数,计算平均值
		return sum / list.size();
	}

由于ArrayList实现了RandomAccess的接口,因此用get(i)下标的方式访问速度将很快,如果使用迭代器将慢很多,而LinkedList 使用迭代器将提高速度。

优雅的使用对集合进行运算(并集、交集、差集)

并集:list1.addAll( list2 )
交集:list1.retainAll( list2 )
差集:  list1.removeAll( list2 )
无重复的并集 :
 //删除在1中出现的元素
list2.removeAll( list1 )
//把剩余的list2 添加到list1中
list1.addAll( list2 )

枚举和注解

推荐使用枚举定义常量


泛型和反射

Java的泛型是类型擦除的

Java的泛型是在编译时的,在编以后会擦除Java的泛型类型,相当于如下代码:
public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("abc");
		String str = list.get(0);
	}
	
	public void doSomething(){
		List list = new ArrayList();
		list.add("abc");
		String str = (String)list.get(0);
	}
理解了这个,就可以解释为什么如下代码的执行结果为true
public static void main(String[] args) {
		List<String> ls = new ArrayList<String>();
		List<Integer> li = new ArrayList<Integer>();
		System.out.println(li.getClass() == li.getClass());
	}

建议采用的顺序是List<T>、List<?>、List<Object>

List<T>、List<?>、List<Object>这三者都可以容纳所有的对象,但使用的顺序应该首选List<T>、次之List<?>,最后List<Object>原因如下:

(1)List<T>是确定的某一个类型

List<T>表示的是list列表中的所有元素都是T类型,具体类型在运行时决定;List<?>表示是任意类型,与List<T>类似,而List<Object>则表示集合中所有元素都为Object类型,因为Object是所有类型的父类,所以List<Object>也可以容纳所有的类类型,从这一字面意义上分析,List<T>更符合习惯:编码者知道他是某一类型,只是在运行时期才确定而已。

(2)List<T>可以进行读写操作

List<T>可以进行add,remove等操作,因为他的类型是固定的T类型,在编码期不需要进行任何的转型操作。

List<?>是只读类型的,不能进行增加、修改的操作,因为编译器不知道List<?>中容纳的是什么类型的元素,也就无法类型是否安全了,而List<?>读出的全部都是Object类型,需要主动转型,所以它经常用于泛型方法的返回值。需要注意List<?>虽然无法添加修改,但是可以做删除操作。比如remove,clear,因为删除动作与泛型类型无关。

List<Object>也可以进行读写操作,但是他执行写入操作时需要向上转型(Up Cast),在读取数据后需要向下转型(Down Cast),而此时已经失去了泛型的意义了。

父类引用指向子类对象,而子类引用不能指向父类对象。

异常

提倡异常封装

(1)提高系统的友好性
public class Client {
	public static void main(String[] args) {
		try {
			doStuff();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void doStuff() throws Exception {
		InputStream is = new FileInputStream("无效文件.txt");
		/*文件操作*/
	}
	
	public static void doStuff2() throws MyBussinessException{
		try {
			InputStream is = new FileInputStream("无效文件.txt");
		} catch (FileNotFoundException e) {
			//为方便开发和维护人员而设置的异常信息
			e.printStackTrace();
			//抛出业务异常
			throw new MyBussinessException(e);
		}
		/*文件操作*/
	}
}

class MyBussinessException extends Exception{
	public MyBussinessException(Throwable t){
		super(t);
	}
}


(2)提高系统的可维护性

(3)解决java异常机制自身的缺陷
java中的异常只能抛出一个,但是可以使用封装来解决
public class Client {
	public static void main(String[] args) {
		try {
			doStuff();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void doStuff() throws MyException {
		List<Throwable> list = new ArrayList<Throwable>();
		// 第一个逻辑片段
		try {
			// Do Something
		} catch (Exception e) {
			
			list.add(e);
		}
		// 第二个逻辑片段
		try {
			// Do Something
		} catch (Exception e) {
			list.add(e);
		}

		if (list.size() > 0) {
			throw new MyException(list);
		}

	}
}

class MyException extends Exception {
	// 容纳所有的异常
	private List<Throwable> causes = new ArrayList<Throwable>();

	// 构造函数,传递一个异常列表
	public MyException(List<? extends Throwable> _causes) {
		causes.addAll(_causes);
	}

	// 读取所有的异常
	public List<Throwable> getExceptions() {
		return causes;
	}
}

不要再finally代码块中处理返回值

public class Client {
	public static void main(String[] args) {
		try {
			doStuff(-1);
			doStuff(100);
		} catch (Exception e) {
			System.out.println("这里是永远都不会到达的");
		}
	}

	public static int doStuff(int _p) throws Exception {
		try {
			if (_p < 0) {
				throw new DataFormatException("数据格式错误");
			} else {
				return _p;
			}
		} catch (Exception e) {
			//异常处理
			throw e;
		} finally {
			return -1;
		}
	}
}

上面这段代码的doStuff(-1) 和doStuff(100)的返回值都是-1,导致这个问题的原因有两个:
(1)覆盖了try代码块中的return 返回值。
public class Client {
	
	public static void main(String[] args) {
		System.out.println(doStuff());
		System.out.println(doStuff2().getName());
	}
	
	public static int doStuff() {
		int a = 1;
		try {
			return a;
		} catch (Exception e) {

		} finally {
			//重新修改一下返回值
			a = -1;
		}
	
		return 0;
	}
	
	
	public static Person doStuff2() {
		Person person = new Person();
		person.setName("张三");
		try {
			return person;
		} catch (Exception e) {
		} finally {
			//重新修改一下返回值
			person.setName("李四");
		}
		person.setName("王五");
		return person;
	}
}

class Person{
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
doStuff()永远都返回1,因为返回的是int类型,java对于int型返回的是值。
而doStuff2()返回的都是"李四",因为try中返回了person的引用,但是在finally中修改了引用中的值,所以返回的是李四。
(2)屏蔽异常
	public static void doSomething() {
		try {
			//正常抛出异常
			throw new RuntimeException();
		} finally {
			return;
		}
	}
	
	public static void main(String[] args) {
		try {
			doSomething();
		} catch (RuntimeException e) {
			System.out.println("这里永远都不会到达!");
		}
	}

上面的finally代码块中的return已经告诉jvm,doSomething已经正常执行结束了,也没有任何异常,所以main中永远都不会捕获到异常。

多线程、并发

volatile不能保证数据同步



volatile变量能够直接写入到主内存中,保证读取的数据是最新的,但依然存在问题,看如下代码
public static void main(String[] args) throws Exception {
		// 理想值,并做为最大循环次数
		int value = 1000;
		// 循环次数,防止出现无限循环造成死机情况
		int loops = 0;
		//主线程组,用于估计活动线程数
		ThreadGroup tg = Thread.currentThread().getThreadGroup();
		while (loops++ < value) {
			// 共享资源清零
			UnsafeThread ut = new UnsafeThread();
			for (int i = 0; i < value; i++) {
				new Thread(ut).start();
			}
			// 先等15毫秒,等待活动线程数量成为1
			do {
				Thread.sleep(15);
			} while (tg.activeCount() != 1);
			// 检查实际值与理论值是否一致
			if (ut.getCount() != value) {			
				// 出现线程不安全的情况
				System.out.println("循环到第 " + loops + " 遍,出现线程不安全情况");
				System.out.println("此时,count=" + ut.getCount());
				System.exit(0);
			}
		}
		
		System.out.println("循环结束");
	}
}

class UnsafeThread implements Runnable {
	// 共享资源
	private volatile int count = 0;

	@Override
   public void run() {
		// 为了增加CPU的繁忙程度
		for (int i = 0; i < 1000; i++) {
			Math.hypot(Math.pow(92456789, i), Math.cos(i));
		}
		// 自增运算
		count++;
	}

	public int getCount() {
		return count;
	}
}
最终的输出结果

第7遍出现了线程不安全,当然这个数量是随机的。

异步运算考虑使用Callable接口

	public static void main(String[] args) throws Exception {
		//生成一个单线程的异步执行器
		ExecutorService es = Executors.newSingleThreadExecutor();
		//线程执行后的期望值
		Future<Integer> future = es.submit(new TaxCalculator(100));
		while(!future.isDone()){
			//还没有运算完成,等待10毫秒
			TimeUnit.MILLISECONDS.sleep(200);
			//输出进度符号
			System.out.print("#");
		}
		System.out.println("\n计算完成,税金是:"+ future.get() + " 元");	
		//关闭异步执行器
		es.shutdown();
	}
}

//税款计算器
class TaxCalculator implements Callable<Integer> {
	//本金
	private int seedMoney;
	//接收主线程提供的参数
	public TaxCalculator(int _seedMoney) {
		seedMoney = _seedMoney;
	}
	@Override
	public Integer call() throws Exception {
		//复杂计算,运行一次需要10秒
		TimeUnit.MILLISECONDS.sleep(10000);
		return seedMoney /10;
	}
}

此类异步计算的好处是:
(1)尽可能多的占用系统资源,提供快速运算
(2)可以监控线程执行的情况,比如是否执行完毕、是否有返回值,是否有异常
(3)可以为用户提供更友好的支持,比如例子中的运算进度

使用CountDownLatch协调子线程

思考这样一个案例:百米赛跑,多个参加赛跑的人员在听到发令枪响后,开始跑步,到达终点后结束计时,然后统计平均成绩。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Client {
	static class Runner implements Callable<Integer> {
		//开始信号
		private CountDownLatch begin;
		//结束信号
		private CountDownLatch end;

		public Runner(CountDownLatch _begin, CountDownLatch _end) {
			begin = _begin;
			end = _end;
		}

		@Override
		public Integer call() throws Exception {
			// 跑步的成绩
			int score = new Random().nextInt(25);
			// 等待发令枪响起
			begin.await();
			// 跑步中……
			TimeUnit.MILLISECONDS.sleep(score);
			// 跑步者已经跑完全程
			end.countDown();
			return score;
		}
	}

	public static void main(String[] args) throws Exception {
		//参加赛跑人数
		int num = 10;
		// 发令枪只响一次
		CountDownLatch begin = new CountDownLatch(1);
		// 参与跑步有多个
		CountDownLatch end = new CountDownLatch(num);
		// 每个跑步者一个跑道
		ExecutorService es = Executors.newFixedThreadPool(num);
		// 记录比赛成绩
		List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
		// 跑步者就位,所有线程处于等待状态
		for (int i = 0; i < num; i++) {
			futures.add(es.submit(new Runner(begin, end)));
		}
		// 发令枪响,跑步者开始跑步
		begin.countDown();
		// 等待所有跑步者跑完全程
		end.await();
		int count = 0;
		// 统计总分
		for (Future<Integer> f : futures) {
			count += f.get();
		}
		System.out.println("平均分数为:" + count / num);
	}
}
CountDownLatch是一个倒数的同步计数器,每个线程在执行完毕后执行countDown,表示自己运行结束,这对于多个子任务的计算特别有效。

CyclicBarrier让多线程齐步走

思考这样一个案例:两个工人从两段挖据隧道,各自独立奋战,中间不沟通,如果两人在汇合点处碰头了,则表明隧道已经挖通。这描绘的是在多线程编程中,两个线程独立运行,在没有线程间通信的情况下,如何解决两个线程汇合在同一点的问题。
public class Client {
	static class Worker implements Runnable {
		// 关卡
		private CyclicBarrier cb;

		public Worker(CyclicBarrier _cb) {
			cb = _cb;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(new Random().nextInt(1000));
				System.out.println(Thread.currentThread().getName() + "-到达汇合点");
				// 到达汇合点
				cb.await();
			} catch (Exception e) {
				// 异常处理
			}

		}
	}

	public static void main(String[] args) throws Exception {
		// 设置汇集数量,以及汇集完成后的任务
		CyclicBarrier cb = new CyclicBarrier(2, new Runnable() {
			public void run() {
				System.out.println("隧道已经打通!");
			}
		});
		// 工人1挖隧道
		new Thread(new Worker(cb), "工人1").start();
		// 工人2挖隧道
		new Thread(new Worker(cb), "工人2").start();
	}
}

 CyclicBarrier可以让全部线程处于等待状态(阻塞),然后在满足条件的情况下全部执行,这就好比是一条起跑线,不管是如何达到起跑线的,只要到达这条起跑线就必须等待其他人员,在人员到齐后再各奔东西,CyclicBarrier关注的是汇合点的信息,而不在乎之前或之后如何处理。

性能和效率

提升java新能的基本方法

(1)尽可能将变量、方法生命为final static类型
加入要将阿拉伯数字转换为中文数字
public static String toChineseNum(int num){
		//中文数字
		String[] cns = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
		return cns[num];
	}
每次调用该方法时都会重新生成一个cns数组,注意该数组不会改变,属于不变数组,在这种情况下把它声明为类变量,并且加上final static修饰会更合适,在类加载后就生成了该数组,每次方法调用则不再重新生成数组对象了,这有助于提高系统新能,代码如下:
	//中文数字
	final static String[] cns = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
	
	public String toChineseNum(int num){
		return cns[num];
	}

 (2)缩小变量的作用范围
变量能在方法内定义就定义在方法内,内在循环内定义就在循环内,能在try...catch中就在代码块中,目的是加快GC的回收。
(3)频繁的字符串操作使用StringBuffer和StringBuilder
 (4)使用非线性检索
如果在ArrayList中存储了大量数据,使用indexOf查找会比java.utils.Collections.binarySearch的效率低很多,但是需要注意的是,binarySearch搜索的数据必须是经过排序,否则准确性不可靠。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值