18.1 File类
DirList
class DirFilter implements FilenameFilter {
private Pattern pattern;
public DirFilter(String regex) {
pattern = Pattern.compile(regex);
}
@Override
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
}
class DirList {
public static void chapter18_1() {
File path = new File("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh");
System.out.println("path:" + path.toString());
String[] list = path.list(new DirFilter(".*\\.java"));
System.out.println("list.length:" + list.length);
Arrays.sort(list);
for(String s:list) {
System.out.println("s:" + s);
}
}
}
目录列表器
".*\\.java"
.*: .表示任意字符,*表示0个或多个
\\.:转义字符.
18.6 I/O流的典型使用方式
18.6.1 缓冲输入文件
BufferedInputFile
class BufferedInputFile {
static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while((s=in.readLine())!=null) {
sb.append(s + "\n");
}
return sb.toString();
}
public static void chapter18_6() {
try {
System.out.println(read("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\Main.java"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
18.6.2 从内存输入
MemoryInput
class MemoryInput {
public static void chapter18_6() throws IOException {
StringReader in = new StringReader("xxxyyyzzz");
int c;
while((c=in.read())!=-1) {
System.out.print((char)c);
}
System.out.println();
}
}
read()是以int形式返回下一字节,因此必须类型转换为char才能正确打印。
18.6.4 基本的文件输出
BasicFileOutput
class BasicFileOutput {
public static void chapter18_6() throws IOException {
BufferedReader in = new BufferedReader(new StringReader("xxx xxx yyy yyy zzz zzz"));
String file = "D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BasicFile";
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
int line = 1;
String s;
while( (s=in.readLine())!=null ) {
out.println( (line++) + ":" + s );
}
out.close();
System.out.println(BufferedInputFile.read(file));
}
}
18.6.5 存储和恢复数据
StoringAndRecoveringData
class StoringAndRecoveringData {
public static void chapter18_6() throws IOException {
String file = "D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\Data.txt";
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
out.writeDouble(3.14);
out.writeUTF("this is pi");
out.writeDouble(1.414);
out.writeUTF("square root of 2");
out.close();
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
System.out.println(in.readDouble());
System.out.println(in.readUTF());
System.out.println(in.readDouble());
System.out.println(in.readUTF());
}
}
18.7 文件读写的实用工具
TextFile
class TextFile {
// Read a file as a single string
static String read(String filename) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new FileReader(new File(filename).getAbsoluteFile()));
// try...finally
try {
String s;
while( (s=in.readLine())!=null ) {
sb.append(s);
sb.append("\n");
}
}finally {
in.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
// Write a single file
static void write(String filename, String text) {
try {
PrintWriter out = new PrintWriter(new File(filename).getAbsoluteFile());
try {
out.print(text);
}finally {
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void chapter18_7() {
String str = read("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BasicFile");
System.out.println("TextFile:" + str);
write("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\TextFile", str);
}
}
Static functions for reading and writing text files as a single string
18.8 标准I/O
System.out:PrintStream对象
System.err:PrintStream对象
System.in:InputStream对象
setIn(InputStream)
setOut(PrintStream)
setErr(PrintStream)
Redirecting
class Redirecting {
public static void chapter18_8() throws IOException {
// default
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s;
while( (s=br.readLine())!=null ) {
System.out.println(s);
if(s.equals("quit")) {
// "quit" to quit
break;
}
}
// redirect
System.out.println("redirect:");
PrintStream console = System.out;
BufferedInputStream in = new BufferedInputStream(new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\TextFile"));
PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\Redirecting")));
System.setIn(in);
System.setOut(out);
BufferedReader br1 = new BufferedReader(new InputStreamReader(System.in));
String s1;
while ( (s1=br1.readLine())!=null ) {
// redirect to out file
System.out.println(s1);
}
out.close();
System.setOut(console);
}
}
此例子将标准输入附接到文件上,并将标准输出和标准错误重定向到另一个文件。
I/O重定向操作的是字节流,不是字符流。所以使用的是InputStream和OutputStream,而不是Reader和Writer。
18.10 新I/O nio
提高速度,更接近于操作系统的I/O方式:通道和缓冲器。唯一直接与通道交互的缓冲器是ByteBuffer。
用以产生FileChannel:三个类FileInputStream FileOutputStream和用于既读又写的RandomAccessFile。
GetChannel
class GetChannel {
public static void chapter18_10() throws IOException {
FileChannel fc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel").getChannel();
fc.write(ByteBuffer.wrap("Some text".getBytes(StandardCharsets.UTF_8)));
fc.close();
fc = new RandomAccessFile("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel","rw").getChannel();
// Move to the end
fc.position(fc.size());
fc.write(ByteBuffer.wrap("\nSome more".getBytes(StandardCharsets.UTF_8)));
fc.close();
fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel").getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
fc.read(buf);
// read(buf)后需要调用buf.flip,让buf做好让别人读取字节的准备
buf.flip();
while(buf.hasRemaining()) {
System.out.print((char)buf.get());
}
System.out.println();
}
}
FileChannnel通道,可以向它传送用于读写的ByteBuffer。可以使用wrap()方法将已存在的字节数组包装到ByteBuffer。
一旦调用read()来告知FileChannel向ByteBuffer存储字节,就必须调用缓冲器上的flip(),让它做好让别人读取字节的准备。
ChannelCopy
class ChannelCopy {
public static void chapter18_10() throws IOException {
FileChannel in = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel").getChannel();
FileChannel out = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\ChannelCopy").getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
while(in.read(buf) != -1) {
buf.flip();
out.write(buf);
buf.clear();
}
}
}
read(buffer)之后需要flip(),让它做好让别人读取字节的准备;read(buffer)之前,需要clear()来为read()做好准备。
18.10.1 转换数据
ByteBuffer转换为字符串
BufferToText
class BufferToText {
public static void chapter18_10() throws IOException {
//(1)write[getBytes()] and read
FileChannel fc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();
fc.write(ByteBuffer.wrap("Some text".getBytes()));
fc.close();
fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
fc.read(buf);
buf.flip();
//(i)
System.out.println(buf.asCharBuffer());
//(ii)
String encoding = System.getProperty("file.encoding");
System.out.println("Decoded using " + encoding + ":" + Charset.forName(encoding).decode(buf));
//(2)write[getBytes("UTF-16BE")] and read
fc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();
fc.write(ByteBuffer.wrap("Some text2".getBytes("UTF-16BE")));
fc.close();
fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();
buf.clear();
fc.read(buf);
buf.flip();
System.out.println(buf.asCharBuffer());
//(3)write[buf.asCharBuffer().put()] and read
fc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();
buf.asCharBuffer().put("Some text3");
fc.write(buf);
fc.close();
fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();
buf.clear();
fc.read(buf);
buf.flip();
System.out.println(buf.asCharBuffer());
}
}
(1)write[getBytes()],read[asCharBuffer()]不起作用,read[decode]起作用
(2)write[getBytes("UTF-16BE")]
(3)write[buf.asCharBuffer().put()]
18.10.2 获取基本类型
尽管ByteBuffer只能保存字节类型的数据,但是它具有可以从其所容纳的字节中产生出各种不同基本类型值的方法。
GetData
class GetData {
public static void chapter18_10() {
// allocate default value 0
ByteBuffer bb = ByteBuffer.allocate(1024);
//(1)char array
bb.rewind(); //bytebuffer回到0位置
bb.asCharBuffer().put("xxxxxxx");
System.out.println(bb.asCharBuffer());
char c;
while((c = bb.getChar()) != 0) {
System.out.print(c);
}
System.out.println();
//(2)short
bb.rewind();
bb.asShortBuffer().put((short) 2423);
System.out.println(bb.getShort());
//(3)int
bb.rewind();
bb.asIntBuffer().put(12314);
System.out.println(bb.getInt());
//(4)long
bb.rewind();
bb.asLongBuffer().put(123123139);
System.out.println(bb.getLong());
//(5)double
bb.rewind();
bb.asDoubleBuffer().put(77.777);
System.out.println(bb.getDouble());
}
}
只有short需要进行类型转换
clear,flip,rewind区别:
18.10.3 视图缓冲器
可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。
IntBufferDemo
class IntBufferDemo {
public static void chapter18_10() {
ByteBuffer bb = ByteBuffer.allocate(1024);
IntBuffer ib = bb.asIntBuffer();
ib.put(new int[]{11,42,47,99,143,811});
System.out.println(ib.get(3));
ib.put(3,777);
ib.flip();
while(ib.hasRemaining()) {
int i = ib.get();
System.out.println(i);
}
}
}
在channel_read和put后调用flip,是为了channel_write和get。
通过在同一个ByteBuffer上建立不同的视图缓冲器,将同一字节序列翻译成了short,int,float,long和double类型的数据。
ViewBuffers
class ViewBuffers {
public static void chapter18_10() {
ByteBuffer bb = ByteBuffer.wrap(new byte[]{0,0,0,0,0,0,0,97});
bb.rewind();
System.out.println("ByteBuffer:");
while(bb.hasRemaining()) {
System.out.print(bb.position() + "->" + bb.get() + " ");
}
System.out.println();
bb.rewind();
CharBuffer cb = bb.asCharBuffer();
System.out.println("CharBuffer:");
while(cb.hasRemaining()) {
System.out.print(cb.position() + "->" + cb.get() + " ");
}
System.out.println();
bb.rewind();
ShortBuffer sb = bb.asShortBuffer();
System.out.println("ShortBuffer:");
while(sb.hasRemaining()) {
System.out.print(sb.position() + "->" + sb.get() + " ");
}
System.out.println();
bb.rewind();
IntBuffer ib = bb.asIntBuffer();
System.out.println("IntBuffer:");
while(ib.hasRemaining()) {
System.out.print(ib.position() + "->" + ib.get() + " ");
}
System.out.println();
bb.rewind();
FloatBuffer fb = bb.asFloatBuffer();
System.out.println("FloatBuffer:");
while(fb.hasRemaining()) {
System.out.print(fb.position() + "->" + fb.get() + " ");
}
System.out.println();
bb.rewind();
LongBuffer lb = bb.asLongBuffer();
System.out.println("LongBuffer:");
while(lb.hasRemaining()) {
System.out.print(lb.position() + "->" + lb.get() + " ");
}
System.out.println();
bb.rewind();
DoubleBuffer db = bb.asDoubleBuffer();
System.out.println("DoubleBuffer:");
while(db.hasRemaining()) {
System.out.print(db.position() + "->" + db.get() + " ");
}
System.out.println();
}
}
ByteBuffer默认大端字节序
new byte[]{0, 97}
高字节存储在内存低地址:大端字节序
0x0002(高地址) 0x0001(低地址)
byte[0](低字节) byte[1](高字节)
00000000 01100001
==>97
低字节存储在内存低地址:小端字节序
0x0002(高地址) 0x0001(低地址)
byte[1](高字节) byte[0](低字节)
01100001 00000000
==>24832
18.10.5 缓冲器的细节
mark标记,position位置,limit界限,capacity容量
交换相邻字符以对Charbuffer中的字符进行编码和译码
UsingBuffers
class UsingBuffers {
static void symmetricSramble(CharBuffer buf) {
while(buf.hasRemaining()) {
buf.mark();
char c1 = buf.get();
char c2 = buf.get();
buf.reset();
buf.put(c2).put(c1);
}
}
public static void chapter18_10() {
char[] data = "UsingBuffers".toCharArray();
// char = 2 * byte
ByteBuffer bb = ByteBuffer.allocate(data.length*2);
CharBuffer cb = bb.asCharBuffer();
cb.put(data);
cb.rewind();
System.out.println(cb);
// encode
symmetricSramble(cb);
cb.rewind();
System.out.println(cb);
// decode
symmetricSramble(cb);
cb.rewind();
System.out.println(cb);
}
}
尽管可以对char数组调用wrap()方法生成一个CharBuffer,但本例是分配一个底层的ByteBuffer,产生的CharBuffer只是ByteBuffer的一个视图。
mark()将mark设置为position
get()将postion位置+1
put()将postion位置+1
reset()将position的值设为mark的值
rewind()将position设置为缓冲器的开始位置
18.10.7 文件加锁
JDK1.4引入了文件加锁机制,允许我们同步访问某个作为共享资源的文件。
FileLocking
class FileLocking {
public static void chapter18_10() throws IOException, InterruptedException {
FileOutputStream fos = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\FileLocking.txt");
FileLock fl = fos.getChannel().tryLock();
if(fl != null) {
System.out.println("Lock file!");
TimeUnit.MILLISECONDS.sleep(2000);
// false:独占锁
System.out.println("fl.isShared():" + fl.isShared());
fl.release();
System.out.println("Lock release!");
}
fos.close();
}
}
tryLock非阻塞,lock阻塞
fl.isShared():java并发包提供的加锁模式分为独占锁和共享锁,独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问共享资源,如:ReadWriteLock。很显然,独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。