J2SDK 1.4中的新功能类

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的值。

时间: 2024-11-03 03:03:48

J2SDK 1.4中的新功能类的相关文章

如何充分利用Windows 8.1中的新功能

  距微软推出Windows 8.1公众预览版已差不多一个月时间,但这并不意味着Windows 8应用开发者们可以等到最终的RTM版发布的时候才开始应用的调节工作.今天,微软给Windows 8应用的制作者们带来了一些有用的提示,希望能帮助他们充分利用到Windows 8.1中的新功能. 微软在其Windows App Builder博客上发布了这篇技术性很强的文章,其中有提到:Windows 8.1去掉了"过程生命周期管理"(Process Lifetime Management)导

《Adobe Premiere Pro CS4经典教程》——1.2 Adobe Premiere Pro CS4中的新功能

1.2 Adobe Premiere Pro CS4中的新功能 虽然这不是Premiere Pro CS4项功能的完整列表,但会列出您在学习这个应用程序时所期望的一些功能改进.我们将在本书中使用很多这些功能. 1.2.1 特效 - 把特效应用到多个剪辑:选择序列中的多段剪辑,把一个或多个特效从Effects面板拖放到所选择的剪辑,这样可以提高编辑效率. - 使用多种预设特效:现在把一个或多个特效保存为预设,您可以把它应用到序列或项目面板内的一个或多个剪辑. - 删除所有特效:从一个或多个剪辑中快

《Adobe Premiere Pro CS5经典教程》——第1课 Adobe Premiere Pro CS5概述 1.1 Adobe Premiere Pro CS5中的新功能

第1课 Adobe Premiere Pro CS5概述 本课介绍的内容包括: dobe Premiere Pro CS5的新功能. Adobe Premiere Pro CS5内的非线性编辑. 标准的数字视频工作流. 将Adobe Creative Suite 3 Production Premium集成到工作流. Premiere Pro工作区介绍. 定制工作区. 本课大约需要40分钟. 在第一次编辑或者应用第一个切换之前,需要简要了解一下视频编辑.Adobe Premiere Pro对视频

使用.net framework中常用类在2.0版中的新功能

在上一篇<浏览.NET Framework 2.0 类型库中新增的常用功能>一文中我主要列了几个新增的常用主件,本文作为小结主要针对一些常用类的扩展来讲最近在使用C# 2.0 的时候发现的几个新特征,讲得不当之处请网友指正. 1.Exception异常基类在2.0下,Exception基类增加了Data属性,原型如下,public virtual IDictionary Data {get;}可见其实现了IDictionary接口,用来存储异常的自定义信息,由此想到在ExceptionMana

Java Math 类中的新功能,第 1 部分: 实数

有时候您会对一个类熟悉到忘记了它的存在.如果您能够写出 java.lang.Foo 的文档, 那么 Eclipse 将帮助您自动完成所需的函数,您无需阅读它的 Javadoc.例如,我使用 java.lang.Math(一个我自认为非常了解的类)时就是这样,但令我吃惊的是,我最近偶然 读到它的 Javadoc -- 这可能是我近五年来第一次读到,我发现这个类的大小 几乎翻了一倍,包含 20 种我从来没听说过的新方法.看来我要对它另眼相看了. Java 语言规范第 5 版向 java.lang.M

Spring Framework 4.2 中的新功能和增强功能

至今为止,Spring Framework 的最新版本为 4.2.1.RELEASE. 那么 Spring Framework 4.2 中的又有哪些新功能和增强功能呢? 核心容器改进 如 @bean 注释,就如同得到发现和处理 Java 8 默认方法一样,可以允许组合配置类与默认@bean 接口方法. 配置类现在可以声明 @import 作为常规组件类,允许引入的配置类和组件类进行混合. 配置类可以声明一个 @Order 值,用来得到相应的处理顺序(例如重写 bean 的名字),即使通过类路径扫

IBM Rational Rhapsody 8.0和Rhapsody Design Manager 4.0中的新功能

重要的新功能 IBM Rational Rhapsody 开发环境支持广泛的技术,可用于许多用途,例如: 需求分析 基于模型的系统工程 交易学习分析 嵌入式和实时软件开发 注重安全性的软件开发 基于模型的测试 AUTomotive Open System ARchitecture (AUTOSAR) 开发 捕获 DoDAF 或 MODAF 架构框架 Rational Rhapsody 8.0 和 Rational Rhapsody Design Manager 4.0 版本包含一些新功能和改进,

Delphi 2009中的新功能

JSON(JavaScript Object Notation) 类似与 XML; 虽始于 JavaScript, 但被多种语言支持, 它也成了 Delphi 2009 的新功能 之一. 假如有一段 XML: <id>2</id> <name>张三 </name> <age>99</age> 用 json 可以表示为: {"id":2, "name":"张三", "

2015WWDC:iOS9.0中Siri新功能初体验

iOS 9中Siri的新功能 在iOS 9中,Siri会有全新的UI.Siri技术背后有着每周处理10亿次请求的支持,是当今世界上最先进的只能语音引擎之一--仅有5%的词句错误率. Siri看穿你的应用 新一代的Siri可以"看到"你应用,你甚至能够设置"当我回家的时候提醒我做事"的提醒,以免到家之后忘记你的重要事项.   Siri日历的交通提醒功能 新一代Siri的日历功能可以根据实时交通情况进行提醒,非常类似安卓中Google Now的功能.   新一代Siri