J2SDK 1.4中的新功能类

J2SDK 1.4中的新功能类

1.        NIO

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

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()操作时,程序将尽量的填充从positionlimit之间的空间。在进行write()操作时,

程序将读出从positionlimit之间的空间。所以,在调用完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包装成其他的CharBufferFloatBuffer等。

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()方法时,线程将会被阻塞,直到某

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

是需要使用SelectorChannel需要设置为非阻塞模式(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_READOP_CONNECT,而当客户端连接上服务器以后,他将会收到一个isConnectableSelectionKey。在这里我们需要先调用finishConnect()方法,然后由于我们不再需要监听连接事件,因此我们需要修改ChannelSelector上的监听事件类型,这需要调用interestOps()操作来完成,其中方法的参数就是我们所需要的新的事件类型,这一步骤非常重要。

 

 

 

1.10.    编码与解码

J2SDK 1.4提供了专门用于进行编/解码的类,CharsetEncoderCharstDecoder

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方法。而如果我们想对图片的读/写进行更多的控制的话,我们可以使用ImageReaderImageWriter,以及与图片读写相关的一系列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及其相关的HandlerFilterFormatter

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.       自定义HandlerFilterFormatter

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的实现:FileHandlerConsoleHandlerMemoryHandlerSocketHandlerStreamHandler。通过配置文件,我们可以设定其默认属性。而通过在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引入了对正则表达式的支持。这主要包括PatternMatcher类。

public class Test {

     public static void main(String[] args) {

         Pattern p = Pattern.compile("//S+//s");

         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

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

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

 

 

 

4.1.       捕获组(Capturing Group

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

public class Test {

     public static void main(String[] args) {

         Pattern p = Pattern.compile("//S+//s+(//S+)//s+//S+");

         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("//S+//s");

        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|//s)*)//)");

        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的值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值