|
![]() |
名片设计 CorelDRAW Illustrator AuotoCAD Painter 其他软件 Photoshop Fireworks Flash |
|
1. NIO 1.1. 说明:在新的I/O系统当中,我们将主要使用Channel和Buffer来描述我们底层的操作。 1.2. 模型: 1.3. 对Channel进行读写: /** * @author cenyongh@mails.gscas.ac.cn */ 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 cenyongh@mails.gscas.ac.cn */ 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 cenyongh@mails.gscas.ac.cn */ 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的值。 返回类别: 教程 上一教程: What is SOA?----------对SOA的思索,以及由此想到的人类社会的发展 下一教程: jad文件的错误代码,分享 您可以阅读与"J2SDK 1.4中的新功能类"相关的教程: · 一个用来访问http服务器的东西。功能类似于java.net中的那个。但要强。 · java新功能之一枚举 · Tomcat的Cluster功能复制session时会复制保存在其中的对象么? · Java中的类反射机制 · 正则表达式几种常用功能 |
![]() ![]() |
快精灵印艺坊 版权所有 |
首页![]() ![]() ![]() ![]() ![]() ![]() ![]() |