王朝网络
分享
 
 
 

J2SDK 1.4中的新功能类

王朝java/jsp·作者佚名  2006-01-31
宽屏版  字体: |||超大  

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("\\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的值。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有