爱心技术专栏专题

1.4中的新功能类

摘录:java基础 来源:java基础 加入时间:2006年08月19日
摘要:
1.4中的新功能类


1.        NIO











1.1.       说明:在新的I/O系统当中,我们将主要使用Channel和Buffer来描述我们底层的操作。


<…

转载:转载请保留本信息,本文来自
http://www.51dibs.com
/html/2006/article/info16/a_c736210a219425fc.htm

1.4中的新功能类

站点:爱心种子小博士 关键字:1.4中的新功能类

   
1.4中的新功能类

1.        NIO











1.1.       说明:在新的I/O系统当中,我们将主要使用Channel和Buffer来描述我们底层的操作。











1.2.       模型:



1.3.       对Channel进行读写:











/**











 * @author [email protected]











 */











public class CopyFile {











     public static void main(String[] args) throws Exception {











         String in = args[0];











         String out = args[1];











         FileInputStream fis = new FileInputStream(in);











         FileOutputStream fos = new FileOutputStream(out);











         FileChannel inc = fis.getChannel();











         FileChannel outc = fos.getChannel();











         ByteBuffer bb = ByteBuffer.allocate(1024);











         while (true) {











              int ret = inc.read(bb);











              if (ret == -1) {











                   break;











              }











              bb.flip();











              outc.write(bb);











              bb.clear();











         }











     }











}











注:我们并没有直接对Channel进行读写,而是通过Buffer来对Channel进行间接操作。这里有两个地方要注意,就是我们在拷贝的过程当中调用了flip()和clear()方法,这两个方法的作用,将在后面讲解。












 


 



 



1.4.       手工填充Buffer











/**











 * @author [email protected]











 */











public class WriteFile {











     public static void main(String[] args) throws Exception {











         String out = args[0];











         String in = args[0];











         FileInputStream fin = new FileInputStream(in);











         FileOutputStream fout = new FileOutputStream(out);











         FileChannel inc = fin.getChannel();











         FileChannel outc = fout.getChannel();











         ByteBuffer bb = ByteBuffer.allocate(256);











         for (int i = 0; i < 256; i++)











              bb.put((byte) i);











         bb.flip();











         outc.write(bb);












 


 



 



         bb.clear();











         inc.read(bb);











         bb.flip();











         for (int i = 0; i < bb.limit(); i++) {











              System.out.println(bb.get());











         }











     }











}











注:通过调用Buffer上的put()和get()方法,我们可以手工的往Buffer当中填充数据。












 


 



 



1.5.       Buffer的状态量。











Buffer主要使用三个状态量position,limit,capacity来标记底层的状态。其中capacity表征Buffer的最大容量,这个值在Buffer被分配时设定,一般不会随着操作改变。position表征Buffer的当前读写位置,不管是读操作还是写操作,都会导致position的增加。limit表征Buffer的最大可读写位置,limit总是小于或等于capacity。











1.5.1.      结构图:









































1.5.2.      flip()和clear()操作











flip(){











     limit = position;











     postion = 0;











}











clear(){











     limit = capacity;











     position = 0;











}











1.5.3.      例子:











/**











 * @author [email protected]











 */












 


 



 



public class CopyFile {












 


 



 



     public static void main(String[] args) throws Exception {











         String in = args[0];











         String out = args[1];











         FileInputStream fis = new FileInputStream(in);











         FileOutputStream fos = new FileOutputStream(out);











         FileChannel inc = fis.getChannel();











         FileChannel outc = fos.getChannel();











         ByteBuffer bb = ByteBuffer.allocate(1024);












 


 



 



         inc.read(bb);











         show(bb, "After read");











         bb.flip();











         show(bb, "After flip");











         outc.write(bb);











         show(bb, "After write");











         bb.clear();











         show(bb, "After clear");











     }











     public static void show(ByteBuffer bb, String msg) {











         System.out.println(msg + " p:" + bb.position() + " l:" + bb.limit()











                   + " c:" + bb.capacity());











     }











}











输出:   After read p:1024 l:1024 c:1024











After flip p:0 l:1024 c:1024











After write p:1024 l:1024 c:1024











After clear p:0 l:1024 c:1024











     注:在进行read()操作时,程序将尽量的填充从position到limit之间的空间。在进行write()操作时,











程序将读出从position到limit之间的空间。所以,在调用完read()操作以后,要进行其他操作以前,











我们必须要调用flip()操作,使得position的位置回指到开头;而当调用完write()操作以后,应调











用clear()操作,这一方面使得position回指到开头,同时使得limit到达Buffer最大的容量处。












 


 



 




 


 



 




 


 



 




 


 



 




 


 



 




 


 



 



1.6.       子Buffer











当在Buffer上面调用slice()操作时,将单独划出在[position,limit)之间的一段Buffer作为子Buffer,子Buffer与父Buffer使用相同的空间,但维护各自的状态量。












 


 



 



1.6.1.      结构图:











































































































1.6.2.      例子:











ByteBuffer original = ByteBuffer.allocate( 8 );











original.position( 2 );











original.limit( 6 );











ByteBuffer slice = original.slice();












 


 



 



1.7.       其他类型的Buffer











我们可以把最基本的ByteBuffer包装成其他的CharBuffer,FloatBuffer等。











1.7.1.      结构图:

































































1.7.2.      例子:











ByteBuffer buffer = ByteBuffer.allocate( size );











FloatBuffer floatBuffer = buffer.asFloatBuffer();












 


 



 



1.8.       在ByteBuffer上的多格式读取











在对ByteBuffer进行读取时,除了可以按照固定间隔的读取方式以外,我们也可以按照变长的方式读取。











1.8.1.      例子:











fch.read( bb );











bb.flip();











byte b0 = bb.get();











short s0 = bb.getShort();











byte b1 = bb.get();











float f0 = bb.getFloat();











1.9.       非阻塞I/O











在实现基于TCP/UDP的聊天服务器时,为了节省资源我们可以使用轮训技术。而为了让服务器不永远阻塞在accept()方法上,我们可以设置一个等待超时值。而通过使用Selector类,我们可以让以上方法更容易,更高效的得到实现。











public class ServerStub implements Runnable{











   private Selector selector = null;











   private int port = 0; 























   public void run() {











        started = true;











        try {











            selector = Selector.open();











            ServerSocketChannel schannel = ServerSocketChannel.open();











            schannel.configureBlocking(false);











            ServerSocket ssocket = schannel.socket();











            ssocket.bind(new InetSocketAddress("127.0.0.1", port));











            schannel.register(selector, SelectionKey.OP_ACCEPT);











            Set<SelectionKey> sks = null;











            int keys = 0;











            while (started) {











                 keys = selector.select();











                 if (keys > 0) {











                      sks = selector.selectedKeys();











                      Iterator<SelectionKey> it = sks.iterator();











                      while (it.hasNext()) {











                          SelectionKey key = it.next();











                          it.remove();











                          if (key.isReadable()) {











                               sender = (SocketChannel) key.channel();











                               String msg = receive(sender);                 











                          } else if (key.isAcceptable()) {











                               SocketChannel sc = schannel.accept();











                               sc.configureBlocking(false);











                               sc.register(selector, SelectionKey.OP_READ);











                          } else {











                               emit("Something Abnormal");











                          }











                      }











                 }











            }











        } catch (Exception e) {











            e.printStackTrace();











        }











   }











   …











}











注:Selector的使用,使得服务器能以一种事件响应的方式对客户端的连接进行监听。通过











SelectionKey提供的常量,管道可以注册他说感兴趣的事件,对于ServerSocketChannel他











只能注册OP_ACCEPT事件。当用户调用selector.select()方法时,线程将会被阻塞,直到某











些事件发生了。然后用户判断发生的事件类型并进行对应的操作。这里有几点需要注意,第一











是需要使用Selector的Channel需要设置为非阻塞模式(configureBlocking(false)),第二











是用户需要手工的把已处理的SelectionKey,从集合中移除。












 


 



 



public class ClientStub implements Runnable {











   private Selector selector = null;











   private SocketChannel channel = null;











   private boolean started = false;























   public void run() {











        started = true;











        try {











            selector = Selector.open();











            Selector sel = Selector.open();











            channel = SocketChannel.open();











            channel.configureBlocking(false);











            channel.register(selector, SelectionKey.OP_READ











                      | SelectionKey.OP_CONNECT);











            channel.connect(addr);











            Set<SelectionKey> sks = null;











            int keys = 0;











            while (started) {











                 keys = selector.select();











                 if (keys > 0) {











                      sks = selector.selectedKeys();











                      Iterator<SelectionKey> it = sks.iterator();











                      while (it.hasNext()) {











                          SelectionKey key = it.next();











                          it.remove();











                          if (key.isReadable()) {











                               String msg = receive(channel);                











                          } else if (key.isConnectable()) {











                               channel.finishConnect();











                               key.interestOps(SelectionKey.OP_READ);











                          } else {











                               emit("Something Abnormal");











                          }











                      }











                 }











            }











        } catch (Exception e) {











            e.printStackTrace();











        }











   }











   …











}











注:客户端程序的实现与服务器端的基本相似。唯一的一点区别就是,客户端一开始注册了两个事件类型OP_READ和OP_CONNECT,而当客户端连接上服务器以后,他将会收到一个isConnectable的SelectionKey。在这里我们需要先调用finishConnect()方法,然后由于我们不再需要监听连接事件,因此我们需要修改Channel在Selector上的监听事件类型,这需要调用interestOps()操作来完成,其中方法的参数就是我们所需要的新的事件类型,这一步骤非常重要。












 


 



 



1.10.    编码与解码











J2SDK 1.4提供了专门用于进行编/解码的类,CharsetEncoder和CharstDecoder。











public CharBuffer decode(ByteBuffer bb){











Charset c = Charset.forName("gb2312");











CharsetDecoder cd = c.newDecoder();











CharBuffer cb = cd.decode(bb);











return cb;











}











注:编码(CharsetEncoder)的方法与解码的类似。












 


 



 



2.        Image I/O











J2SDK 1.4提供了专门用于图片读写的类。ImageIO。如果我们只是想简单的读取或输出图片的话,那么我们可以直接使用ImageIO提供的static方法。而如果我们想对图片的读/写进行更多的控制的话,我们可以使用ImageReader和ImageWriter,以及与图片读写相关的一系列Listener。











public Image readImage(String filename){











     BufferedImage bi = ImageIO.read(new File(filename));











     return bi;











}












 


 



 



public void writeImage(){











     BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);











     Graphics2D g2 = bi.createGraphics();











     //   绘图操作











     ImageIO.write(bi, "jpeg",new File("pic.jpg"));











}












 


 



 




 


 



 




 


 



 




 


 



 




 


 



 




 


 



 



3.        Log











J2SDK 1.4提供了专门用于书写日志的类,Logger及其相关的Handler,Filter和Formatter。











3.1.       结构图:



























































































































在Logger系统当中,我们需要先获取一个Logger实例,然后通过调用Logger上的日志方法,我们将产生一个LogRecord实例,该实例将会被传送到在Logger上注册的所有Handler内,然后Handler使用他内部的Filter对象,以判断是否要处理该LogRecord记录,如果要处理的话,则把LogRecord传递给Formatter,让他对输出格式进行格式化。











public class Test{











     public static void main(String[] args){











         Logger log = Logger.getLogger("Test");











         StreamHandler sh = new StreamHandler(System.out, new SimpleFormatter());











         log.addHandler(sh);











         log.info("Hello World");











     }











}











输出:   2005-3-12 1:06:25 nick.log.Test main











信息: Hello World











2005-3-12 1:06:25 nick.log.Test main











信息: Hello World











          说明:由于Logger机制会递归的调用父类的Logger,因此,这里输出了两份日志记录。












 


 



 



3.2.       自定义Handler,Filter,Formatter











public class TestFormatter extends Formatter {











     public String format(LogRecord record) {











         return "INFO MESSAGE:" + record.getMessage();











     }











}












 


 



 



public class TestFilter implements Filter {











     public boolean isLoggable(LogRecord record) {











         if (record.getLevel() == Level.INFO)











              return true;











         else











              return false;











     }











}












 


 



 



public class TestHandler extends Handler {











     public void publish(LogRecord r) {











         if (!isLoggable(r))











              return;











         System.out.println(getFormatter().format(r));











     }











     public void close() throws SecurityException {}











     public void flush() {}











}












 


 



 



public class Test {











     public static void main(String[] args) {











         Logger log = Logger.getLogger("Test");











         log.setLevel(Level.ALL);        











         log.setUseParentHandlers(false);











         TestHandler th = new TestHandler();











         th.setFilter(new TestFilter());











         th.setFormatter(new TestFormatter());











         log.addHandler(th);











         log.info("info");











         log.fine("fine");











     }











}












 


 



 



输出:INFO MESSAGE:info











说明:在主程序里面,我们调用了setUseParentHandlers(false)方法,这样做是为了禁止当前











的Logger调用其父类Logger,默认情况下该值为true。












 


 



 



3.3.       默认Handler及其配置











Log系统提供了五个默认Handler的实现:FileHandler,ConsoleHandler,MemoryHandler,SocketHandler,StreamHandler。通过配置文件,我们可以设定其默认属性。而通过在System.setProperty()方法里面设定“java.util.loggin.config.file”的值,可以指定配置文件的位置,默认情况下系统使用/jre/lib/logging.properties作为配置文件。












 


 



 



FileHandler











ConsoleHandler











MemoryHandler











SocketHandler











StreamHandler











level











y











y











y











Y











Y











filter











y











y











y











Y











Y











formatter











y











y











y











Y












 


 



 



encoding











y











y











y











Y












 


 



 



limit











y












 


 



 




 


 



 




 


 



 




 


 



 



count











y












 


 



 




 


 



 




 


 



 




 


 



 



pattern











y












 


 



 




 


 



 




 


 



 




 


 



 



append











y












 


 



 




 


 



 




 


 



 




 


 



 



size












 


 



 




 


 



 



y












 


 



 




 


 



 



push












 


 



 




 


 



 



y












 


 



 




 


 



 



target












 


 



 




 


 



 



y












 


 



 




 


 



 



host












 


 



 




 


 



 




 


 



 



Y












 


 



 



port












 


 



 




 


 



 




 


 



 



Y












 


 



 



     logging.properties的内容:











nick.log.level = WARNING











    











public class Test {











     public static void main(String[] args) {











         System.setProperty("java.util.logging.config.file",











                   "./logging.properties");











         Logger log = Logger.getLogger("nick.log");











         System.out.println(log.getLevel());      











         log.setUseParentHandlers(false);











         StreamHandler sh = new StreamHandler(System.out, new SimpleFormatter());











         log.addHandler(sh);











         log.warning("warning");











         log.info("info");











         log.fine("fine");











     }











}











输出:   WARNING











2005-3-12 1:05:44 nick.log.Test main











警告: warning












 


 



 



4.        正则表达式











J2SDK 1.4引入了对正则表达式的支持。这主要包括Pattern和Matcher类。











public class Test {











     public static void main(String[] args) {











         Pattern p = Pattern.compile("\ +\ ");











         String inputString = "well, hey there feller";











         Matcher matcher = p.matcher(inputString);











         while (matcher.find()) {











              int start = matcher.start();











              int end = matcher.end();











              String matched = inputString.substring(start, end);











              System.out.println(matched);











         }











         System.out.println("===== Using Group: =====");











         matcher.reset();











         while (matcher.find()) {











              String matched = matcher.group();











              System.out.println(matched);











         }











     }











}











输出:   well,











hey











there











===== Using Group: =====











well,











hey











there











         说明:Pattern对需要进行识别的模式进行编译,这可以提高之后的识别速度。在使用Pattern











时有一点要特别注意,就是正则表达式单中,大量的使用以“\”开头的符号,所以为了在Pattern











中表示“ ”我们需要写成“\ ”。而当中的加号并不是表示连接,而是表示“1此或多次”











上述程序演示了如何使用一个模式去识别一个字符串,并提取每一个匹配的串。












 


 



 



4.1.       捕获组(Capturing Group)











在Pattern当中的正则表达当中,通过使用括号,我们可以在原来的表达式当中定义子表达式,或者称为Capturing Group。通过Matcher,我们还可以直接提取某一个Capturing Group的内容。











public class Test {











     public static void main(String[] args) {











         Pattern p = Pattern.compile("\ +\ +(\ +)\ +\ +");











         String inputString = "hey there feller";











         Matcher matcher = p.matcher(inputString);











         while (matcher.find()) {











              int start = matcher.start(1);











              int end = matcher.end(1);











              String matched = inputString.substring(start, end);











              System.out.println(matched);











         }











         System.out.println("===== Using Group: =====");











         matcher.reset();











         while (matcher.find()) {











              String matched = matcher.group(1);











              System.out.println(matched);











         }











     }











}











输出:   there











===== Using Group: =====











there











说明:Capturing Group的编号是从1开始的。组号为0的组表示整个串。












 


 



 



4.2.       替换











Matcher提供用于替换的方法。一种是简单进行查找替换,使用replaceAll()方法。第二种更加灵活的方式,在使用的时候可以结合Capturing Group。











public class Test {











   public static void main(String[] args) {











        Pattern p = Pattern.compile("\ +\ ");











        String inputString = "hey there feller";











        Matcher matcher = p.matcher(inputString);











        String ns = matcher.replaceAll("Hello ");











        System.out.println(ns);











   }











}











输出:Hello Hello feller












 


 



 



public class Test {











   public static void main(String[] args) {











        Pattern pattern = Pattern.compile("\\(((\\w|\ )*)\\)");











        String inputString = "These should be (square brackets).(hello)";











        StringBuffer sb = new StringBuffer();











        Matcher matcher = pattern.matcher(inputString);











        while (matcher.find()) {











            matcher.appendReplacement(sb, "[$1]");











        }











        matcher.appendTail(sb);











        String newString = sb.toString();











        System.out.println(newString);











   }











}











输出:These should be [square brackets].[hello]











说明:这种方式的替换,由于加入了Capturing Group。所以比之前的方法更加灵活。在appendReplacement()方法中,我们使用第二个参数的内容,替换匹配的部分。而$X则是用于引用对应的Capturing Group的值。











客户服务中心信箱:[email protected] [email protected] 网站地图

声明

合作伙伴: