Java编程规范-性能编程

  • 格式:doc
  • 大小:270.54 KB
  • 文档页数:57

Java性能编程规范1 字符串操作规则1.1:需要同步的情况下,采用StringBuffer处理字符串拼装。

说明编程时,当字符串连接超过3次的情况下,请使用StringBuffer而不是“+”。

因为String 是不变(Immutable)对象类,使用“+”会创建新String临时对象,因此增加了创建与回收临时对象的开销。

StringBuffer对象是可变(mutable)对象类,不存在该问题。

在JDK1.5之后,增加了StringBuilder类,在单线程中使用时,StringBuilder的效率要高于StringBuffer。

详见规则1.2。

【示例】String str = strA + StrB + StrC; //会创建临时对象strA + StrB可修改为:StringBuffer strBuf = new StringBuffer(128);strbuf.append(strA).append(strB).append(strC);规则1.2:不需要同步的情况下,使用StringBuilder处理字符串拼装。

说明从JDK 5 开始,JDK为StringBuffer补充了一个单个线程使用的等价类StringBuilder。

StringBuilder与StringBuffer支持所有相同的操作,不同点在于StringBuilder的方法不是线程安全的,StringBuffer的方法是线程安全的,因此StringBuilder速度更快。

所以,当不存在线程安全问题时,应该优先使用StringBuilder。

【示例】String str = strA + StrB + StrC; //会创建临时对象strA + StrB当不需要考虑线程安全问题时,可修改为:StringBuilder strBuilder = new StringBuilder(128);strBuilder.append(strA).append(strB).append(strC);规则1.3:避免频繁或对大对象使用String类的replaceAll方法。

说明因为String是不变对象类,对象在执行replaceAll方法时,因为替换操作将创建大量新的临时对象,这些对象的创建和回收将消耗系统性能。

如果要进行这样的操作,考虑使用Pattern类和Matcher类。

【示例】Pattern p = pile("cat");Matcher m = p.matcher("one cat two cats in the yard");StringBuffer sb = new StringBuffer();while (m.find()){m.appendReplacement(sb, "dog");}m.appendTail(sb);System.out.println(sb.toString());规则1.4:避免大对象使用默认的toString方法转换为字符串。

说明【示例】规则1.5:不要通过new方式来创建常量字符串。

说明在java语言中,对于字符串常量,虚拟机会通过常量池机制确保其只有一个实例。

常量池中既包括了字符串常量,也包括关于类、方法、接口等中的常量。

当应用程序要创建一个字符串常量的实例时,虚拟机首先会在常量池中查找,看是否该字符串实例已经存在,如果存在则直接返回该字符串实例,否则新建一个实例返回。

我们说常量是可以在编译期就能被确定的,所以通过new方法创建的字符串不属于常量。

对于字符串常量,不要通过new方法来进行创建,因为这样可能会导致创建多个不必要的实例。

【示例】// 下面语句将返回”Hello! Welcome to NanJing.”的一个新实例对象String str = new String(”Hello! Welcome to NanJing.”);规则1.6:使用“+”拼接常量字符串。

说明因为使用“+”拼接常量字符串可以在编译期间完成,因此提高了运行时效率。

而使用Stringbuilder或StringBuffer进行常量字符串拼接,都是在运行时完成的。

因此相对前者,效率要低。

需要强调的是,这里是常量字符串拼接。

【示例】String str = “Hello! ” + “Welcom ” + “to ” + “NanJing.”;建议1.7:在使用StringBuffer进行字符串操作时,尽量设定初始容量大小。

说明StringBuffer的默认构造函数,其数据capacity初始值为16,在字符串操作过程中,如果字符串长度达到capacity并且需要继续增加时,系统将申请2倍于原capacity的数据空间作为新的数据空间,并将之前的数据拷贝到新的数据空间,原数据空间释放待回收。

为避免数据空间申请、数据拷贝,以及原数据空间的垃圾回收操作开销,在在使用StringBuffer 进行字符串操作时,尽量设定初始容量大小,使得容量大小足够容纳本身操作的数据。

也不要申请过大,以免影响其他操作对内存的使用。

规则1.8:避免通过String/CharSequence对象来构建StringBuffer对象。

说明使用String/CharSequence对象来构建StringBuffer对象时,其初始capacity值为String/CharSequence对象的长度+16,因此当进一步进行字符串拼装时,16个字符空间位置显得太小,因此将面临进一步的空间扩展问题(参见建议1.7),消耗性能。

建议先设置恰当的初始容量大小,然后将String/CharSequence对象添加进去。

【示例】String str = “Hello! Welcome to NanJing.”;StringBuffer strBuf = new StringBuffer(128);strBuf.append(str);strBuf.append(strA).append(strB).append(strC);规则1.9:如果不需要支持正则表达式,使用indexOf方法实现字符串查找;说明在java中,进行字符串查找匹配时一般有三种实现方式:第一种是调用String对象的indexOf(String str)方法;第二种是调用String对象的matches(String regex)方法;第三种是直接使用正则表达式工具类(包括Pattern类、Matcher类)来实现匹配。

这三种实现方式的各自特点如下:indexOf(String str)方法运行速度最快,效率最高,但不支持正则表达式。

matches(String regex)方法性能最差,但支持正则表达式,使用起来简单(该方法性能差的原因是每调用一次时,就重新对正则表达式编译了一次,新建了一个Pattern对象出来,而不是重复利用同一个Pattern对象)。

直接使用正则表达式工具类来实现匹配,可以支持正则表达式,在频繁操作下性能比matches(String regex)方法要好很多。

规则1.10:需要支持正则表达式时,如果频繁进行查找匹配,使用正则表达式工具类实现查找。

说明正则表达式工具类Pattern在为其指定字符串正则表达式时必须首先被编译为此类的实例。

然后,得到的实例可用于创建的Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配,因此相对于String类的matches(String regex)方法每次调用均进行一次编译而言,Pattern类可以一次编译多次、多处使用,因此可以明显提升效率。

【示例】Pattern p = pile("a*b");Matcher m = p.matcher("aaaaab");boolean b = m.matches();规则1.11:对于简单的字符串分割,尽量使用自己定义方法或StringTokenizer。

说明在java中,JDK提供了两种方法可以实现字符串的分割:利用String对象提供的split(String regex)方法实现分割;利用StringTokenizer对象实现字符串分割;采用split(String regex)方法,由于使用了正则表达式,性能最差,然而功能性最强;采用StringTokenizer对象分割字符串,性能最强,功能也就相对较弱。

2 IO读写规则规则2.1:采用非阻塞I/O,避免使用大量通讯线程。

说明所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待状态, 直到有东西可读或者可写为止。

而对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待。

阻塞式IO每个连接必须要开一个线程来处理,并且没处理完线程不能退出。

由于每创建一个线程,就要为这个线程分配一定的内存空间,而且操作系统本身也对线程的总数有一定的限制,因此,如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。

非阻塞式IO,由于基于反应器模式,用于事件多路分离和分派的体系结构模式,所以可以利用线程池来处理。

事件来了就处理,处理完了就把线程归还,因此非阻塞IO可以减少通讯线程开销。

【示例】代码功能:client多线程请求server端,server接收client的名字,并返回Hello! +名字的字符格式给client服务端代码:public class HelloWorldServer{String sde;static int BLOCK = 1024;static String name = "";protected Selector selector;protected CharsetDecoder decoder;static CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();public HelloWorldServer(int port) throws IOException{selector = this.getSelector(port);Charset charset = Charset.forName("GB2312");decoder = charset.newDecoder();}// 获取Selectorprotected Selector getSelector(int port) throws IOException{ServerSocketChannel serverChannel = ServerSocketChannel.open();Selector selector = Selector.open();serverChannel.socket().bind(new InetSocketAddress(port));serverChannel.configureBlocking(false);serverChannel.register(selector, SelectionKey.OP_ACCEPT);return selector;}// 监听端口public void listen(){try{for (; ; ){selector.select();Iterator iter = selector.selectedKeys().iterator();while (iter.hasNext()){SelectionKey key = (SelectionKey) iter.next();iter.remove();try{process(key);}catch(Exception e){e.printStackTrace();key.channel().close();}}}}catch (IOException e){e.printStackTrace();}}// 处理事件protected void process(SelectionKey key) throws IOException{if (key.isAcceptable()) // 接收请求{ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel channel = server.accept();//设置非阻塞模式channel.configureBlocking(false);channel.register(selector, SelectionKey.OP_READ);}else if (key.isReadable()) // 读信息{ByteBuffer clientBuffer = ByteBuffer.allocate(BLOCK);SocketChannel channel = (SocketChannel) key.channel();int count = channel.read(clientBuffer);if (count > 0){clientBuffer.flip();CharBuffer charBuffer = decoder.decode(clientBuffer);name = charBuffer.toString();SelectionKey sKey = channel.register(selector, SelectionKey.OP_WRITE); sKey.attach(name);}else{channel.close();}clientBuffer.clear();}else if (key.isWritable()) // 写事件SocketChannel channel = (SocketChannel) key.channel();String name = (String) key.attachment();ByteBuffer block = encoder.encode(CharBuffer.wrap("Hello !" + name));channel.write(block);SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ);sKey.attach(name);}}public static void main(String[] args){int port = 8880;try{HelloWorldServer server = new HelloWorldServer(port);System.out.println("listening on " + port);server.listen();}catch (IOException e){e.printStackTrace();}}}客户端代码:public class HelloWorldClient{static int SIZE = 10;static InetSocketAddress ip = new InetSocketAddress("localhost", 8880);static CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();static class Message implements Runnable{protected String name;String msg = "";public Message(String index){ = index;public void run(){try{//打开Socket通道SocketChannel client = SocketChannel.open();//设置为非阻塞模式client.configureBlocking(false);//打开选择器Selector selector = Selector.open();//注册连接服务端socket动作client.register(selector, SelectionKey.OP_CONNECT);//连接client.connect(ip);//分配内存ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);int total = 0;for (; ; ){selector.select();Iterator iter = selector.selectedKeys().iterator();while (iter.hasNext()){SelectionKey key = (SelectionKey) iter.next();iter.remove();if (key.isConnectable()){SocketChannel channel = (SocketChannel) key.channel(); if (channel.isConnectionPending()){channel.finishConnect();}channel.write(encoder.encode(CharBuffer.wrap(name)));channel.register(selector, SelectionKey.OP_READ);}else if (key.isReadable()){SocketChannel channel = (SocketChannel) key.channel();int count = channel.read(buffer);if (count > 0){total += count;buffer.flip();while (buffer.remaining() > 0){byte b = buffer.get();msg += (char) b;}buffer.clear();System.out.println(msg);msg = "";}else{client.close();break;}channel.register(selector, SelectionKey.OP_WRITE);}else if (key.isWritable()){SocketChannel channel = (SocketChannel) key.channel();ByteBuffer block = encoder.encode(CharBuffer.wrap(name));channel.write(block);SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ);}}}}catch (IOException e){e.printStackTrace();}}}public static void main(String[] args) throws IOException{String names[] = new String[SIZE];for (int index = 0; index < SIZE; index++){names[index] = "jeff[" + index + "]";new Thread(new Message(names[index])).start();}}}规则2.2:进行IO读写操作时,使用缓冲机制。