1.File:
a.) FilenameFilter:
class DirFilter implements FilenameFilter{
private Pattern pattern;
public DirFilter(String args) {
pattern = Pattern.compile(args);
}
@Override
public boolean accept(File file, String filename) {
return pattern.matcher(filename).matches();
}
}
File file = new File(".");
String[] list;
if(args.length==0){
list= file.list();
}else{
list = file.list(new DirFilter(args[0]));
}
Arrays.sort(list,String.CASE_INSENSITIVE_ORDER);
for(String item :list){
System.out.println(item);
}
总结:list()方法会回掉FileNameaFilter接口的accept()方法,你可以在list()方法中指定FileNameFilter参数,这样list()可以按照你想要的方式生成字符数组,仅包含你需要的文件名。
accept()方法可以接收一个文件和文件名,它是实现FileNnameFilter的最重要的方法,你可以指定你接受哪些文件。
Pattern.compile(String regex) regex是正则表达式,这个方法生成一个按照指定正则表达式编译的Pattern,通过这个matcher(String)生成一个Matcher,matches进行匹配,匹配到返回true。
2.输入和输出(字节流):
read()读单个字节或者字节数组
write()写单个字节或者字节数组
SequenceInputStream:流序列
将两个或者多个InputStreama对象转换成单一的InputStream;
FilterInputStream:抽象类,为其他InputStream提供有用功能。
InputStream中有:
ByteArrayInputStream
StringBufferInputStream(已弃用) ---将String转换成InputStream 接受String参数,底层实现用的事StringBuffer
FileInputStream
PipedInputStream
SequenceInputStream
FilterInputStream
OutputStream中有:
ByteArrayOutputStream
FileOutSputtream
PipedOutputStream
FilterOutputStream
修饰流:
FilterInputStream和FilterOutStream
它们为修饰器类提供了一个基类,“装饰器”类把属性或者有用的接口与输出流连接了起来。
核心的”I/O 加上所有的修饰器,才能得到我们想要的I/O对象”
a.)FilterInputStream:
DataInputStream(InputStream) 允许我们读取不同的基本数据类型和String对象,各种read()方法,如readByte()、readFloat()。-------从流中读取
BufferedInputStream(InputStream):内部改变了InputStream的行为方式,对数据进行缓冲。------改变流的行为方式
b.)FilterOutStream:
DataOutStream(向流中写入,负责数据的存储)、PrintStream(负责数据的显示)、BufferedOutStream
3.Reader和Writer(字符流):
Reader和Writer提供兼容Unicode与面向字符的I/O功能
Reader和Writer是为了在所有的io操作中支持Unicode,为了国际化
InputStreamReader把InputStream转换成Reader
OutputStreamWriter把OutputStream转换成Writer
一般我们在编写程序的时候,尽量使用Writer和Reader(几乎所有原始I/O流类都有相应的Writer和Reader),不得已的时候选用面向字节的类库(java.util.zip类库就是面向字节的而不是面向字符的)
FileReader(Writer)
StringReader(Writer)
CharArrayReader(Writer)
PipedArrayReader(Writer)
修饰FilterWriter:
BufferedWriter
PrintWriter
2和3中最特殊的是DataOutPutStream,如果想以“可传输的” 格式存储和检索数据,它依然是首先。
4.RandomAccessFile(只适用于文件,大多数功能由nio的存储映射文件代替):
RandomAccessFile用于大小已知的记录组成的文件,它是独立的类,和InputStream和Op继承结构无关。
它有文件指针pointer
getFilePointer()指针位置
seek()文件内移动新的位置
Length()判断文件大小
“r” “rw”--构造函数第二个参数,访问方式
5.avaliable():
用来检查还有多少个可供读取的字符。
DataInputStream d = new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\abc.txt")));
while(d.available() != 0){
System.out.print("还有"+d.available()+"个:"+(char)d.readByte());
}
6.DataOutPutStream DataIntPutStream:
writeUTF()和readUTF()可以使得String和其他数据类型相混合
String s = "D:\\abcd.txt";
File file = new File(s);//为文件指定编码方式
if(!file.exists()){
file.createNewFile();
}
DataOutputStream d = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(s)));
d.writeInt(1);
d.writeByte(2);
d.writeLong(68548964896578964l);
d.writeDouble(85468468d);
d.writeBoolean(false);
d.writeChars("结束");
d.flush();
d.close();
DataInputStream di = new DataInputStream(new BufferedInputStream(new FileInputStream(s)));
System.out.println(di.readInt());
System.out.println(di.readByte());
System.out.println(di);
System.out.println(di.readLong());
System.out.println(di.readChar());
1
2
java.io.DataInputStream@15db9742
68548964896578964
?
为了保证所有的读方法能正常工作,我们必须知道流中数据项所在的确切位置。所以他俩的时候,要么知道数据保存固定格式,要么有额外信息。
7.System.out和System.err被包装成了printStream对象
System.in是inputStream,读取它之前要进行包装。
PrintWriter可以接受一个OutputStream,而printStream是OutputStream,所以必要的时候可以把System.out转换成PrintWriter
8.标准I/O重定向:
System.setIn(InputStream)
System.setOut(PrintStream)
System.setErr(PrintStream)
重定向功能在特定情况下会很有用:
如果有了大量输出,并且输出滚动太快使得你无法阅读的时候,你重定向输出到一个文件中,这样方面阅读。
如果你要一段重复多次的命令行程序,你可以重定向输入到一个充满命令行的文件:
如下:我在abc里写了输入,我重定向输入abc,再把abc用作InputStream,把其中的内容输出并重定向到abcd文件中。
PrintStream console = System.out;
BufferedInputStream in = new BufferedInputStream(new FileInputStream("D:\\abc.txt"));
PrintStream out = new PrintStream(new FileOutputStream("D:\\abcd.txt"));
System.setIn(in);
System.setOut(out);
BufferedReader b = new BufferedReader(new InputStreamReader(System.in));
String s ;
while((s=b.readLine()) != null){
System.out.println(s);
}
b.close();
out.flush();
out.close();
in.close();
System.setOut(console);
9.进程控制:
Process p = new ProcessBuilder(xxxxxx).start();
10新I/O:
通道和缓冲期:
a.)通道:旧I/O类库中 有3个类被修改了,用以产生FileChannel,他们分别是FileInputStrea,FileOutPutStream,和RandomAccessFile。
注意: 这些都是字节流操作,Reader和Writer这种字符流的操作不能用于产生通道,但是Channel类提供了实用的方法,可以产生Writer和Reader。(你可以理解为Channel就是由一堆Byte构成的,所以它接受字节流的操作,不接受字符流的操作)。
b.)缓冲器:我们可以通过allocate(大小)来告知分配多少空间从而生成一个缓冲器对象,它可以读取和输出数据,但是不能读取和输出对象,字符串对象也不行。
FileChannel fc = new FileOutputStream(“data.txt”).getChannel();
fc.write(ByteBuffer.wrap("Some one".getBytes()));
fc.close();-----------通道用完要关起来
fc = new RandomAccessFile(“data.txt”, "rw").getChannel();
fc.position(fc.size());
fc.write(ByteBuffer.wrap(" Some two".getBytes()));
fc.close();
fc = new FileInputStream(“data.txt”).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(SIZE);
fc.read(buffer);
buffer.flip();
while(buffer.hasRemaining()){
System.out.print((char)buffer.get());
};
wrap()方法将已经存在的字节数组“包装到”ByteBuffer中。
allocate()方法分配ByteBuffer,nio的目标就是快速移动大量数据,因此ByteBuffer的大小就显得尤为重要。
为了达到更高速度的可能,也可以使用allocateDirect(),它会产生一种与操作系统有更高耦合性的”直接缓冲器”,但是这种分配的开支会更大。
缓冲区是特定基本类型元素的线性有限序列。除内容外,缓冲区的基本属性还包括容量(capacity)、限制(limit)和位置(position)还有标记(mark):
缓冲区的capacity是它所包含的元素的数量。缓冲区的capacity不能为负并且不能更改。
缓冲区的limit 是第一个不应该读取或写入的元素的索引。缓冲区的limit不能为负,并且不能大于其capacity。
缓冲区的position是下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其limit。对于每个非 boolean 基本类型,此类都有一个子类与之对应。
在使用缓冲区进行输入输出数据之前,必须确定缓冲区的position,limit都已经设置了正确的值。
如果现在想用这个缓冲区进行信道的写操作,由于write()方法将从position指示的位置开始读取数据,在limit指示的位置停止,因此在进行写操作前,先要将limit的值设为position的当前值,再将position的值设为0,这是为了得到正确的字节。这个操作可以通过这个flip()方法实现。
flip()使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为0,即上边的要求(红色字体表示)。
所以,上边的操作步骤为:buffer.flip();
out.write(buffer);
缓冲器的使用细节:
当你进行了Channel的read()操作之后,你的缓冲器里就有一定长度的数据了,你用flip()操作可以把你当前数据的position设置为缓冲器的limit,然后把posiotion变成0,方便数据的提取,然后可以进行write操作了。
当进行write()操作的时候,数据是写到Channel里面了,但是缓冲器ByteBuffer里面依然有数据,为了下次的read()操作,你应该clear()缓冲器里的数据,使其内部指针重新安排。
c.)我们可以使用transferTo()和transferFrom()将一个通道和另一个通道连接。
d.)转化数据:
缓冲器容纳的是普通的字节,为了把他们转换成字符,要么在输入他们的时候进行编码(为了输出时有意义),也可以buffer.asCharBuffer().put(“String”),输出的时候,buffer.asCharBuffer();即转换成charBuffer存入取出数据(这里有待理解)。
e.)获取基本类型:
可以利用asCharBuffer()、asIntBuffer()等获得该缓冲器上的视图,然后调用put()方法向曲终填入数据。有一个小小特例,使用ShortBuffer的put()方法时,需要进行数据的转换。 数据类型转换会截图或者改变结果。
f.)字节存放次序:
字节存放次序:1.big endian (高位优先) 将最重要的自己存放在地址最低的存储单元。
2.little endian(低位优先) 与之相反。
我们可以使用带有参数ByteOrder.BIG_ENDIAN或者ByteOrder.LITTLE_ENDIAN的order()方法来改变ByteBuffer的字节排序方式。
Buffered bb;
Bb.order(ByteOrder.BIG_ENDIAN)
对于由输出支持的缓冲期调用bb.array(),可以显示视图底层细节。
g.)我们不能把基本类型的缓冲器转换成ByteBuffer,但是我们可以经由视图缓冲器将基本类型数据移进移出ByteBuffer。
h.)关于缓冲器ByteBuffer的三个方法:flip(),clear(),remind()
一、flip():反转此缓冲区,将限制设置为当前位置,然后将位置设置为 0 !
之前的写操作会不断更新当前位置,当写操作完成之后,需调用此方法,将限制位置设置为当前位置,将当前位置设置为0,这样下一个读操作会从0开始,直到限制位置。
1. /**
2. * Flips this buffer. The limit is set to the current position and then
3. * the position is set to zero. If the mark is defined then it is
4. * discarded.
5. *
6. * <p> This method is often used in conjunction with the {@link
7. * java.nio.ByteBuffer#compact compact} method when transferring data from
8. * one place to another. </p>
9. *
10. * @return This buffer
11. */
12. public final Buffer flip() {
13. limit = position;
14. position = 0;
15. mark = -1;
16. return this;
17. }
/**
* Flips this buffer. The limit is set to the current position and then
* the position is set to zero. If the mark is defined then it is
* discarded.
*
* <p> This method is often used in conjunction with the {@link
* java.nio.ByteBuffer#compact compact} method when transferring data from
* one place to another. </p>
*
* @return This buffer
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
1. public byte get() {
2. return hb[ix(nextGetIndex())];
3. }
4.
5. final int nextGetIndex() { // package-private
6. if (position >= limit)
7. throw new BufferUnderflowException();
8. return position++;
9. }
public byte get() {
return hb[ix(nextGetIndex())];
}
final int nextGetIndex() { // package-private
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
二、rewind():与flip不同的是,不会修改限制位置。
比如初始化时:
1. ByteBuffer buffer=ByteBuffer.allocate(1024);
ByteBuffer buffer=ByteBuffer.allocate(1024);
那么做读操作的时候就会读到第(1024-1)个索引,而flip却不一定能读到(1024-1)个索引,这取决于他的写操作的数据长度。
1. /**
2. * Rewinds this buffer. The position is set to zero and the mark is
3. * discarded.
4. *
5. * <p> Invoke this method before a sequence of channel-write or <i>get</i>
6. * operations, assuming that the limit has already been set
7. * appropriately. For example:
8. *
9. * <blockquote><pre>
10. * out.write(buf); // Write remaining data
11. * buf.rewind(); // Rewind buffer
12. * buf.get(array); // Copy data into array</pre></blockquote>
13. *
14. * @return This buffer
15. */
16. public final Buffer rewind() {
17. position = 0;
18. mark = -1;
19. return this;
20. }
/**
* Rewinds this buffer. The position is set to zero and the mark is
* discarded.
*
* <p> Invoke this method before a sequence of channel-write or <i>get</i>
* operations, assuming that the limit has already been set
* appropriately. For example:
*
* <blockquote><pre>
* out.write(buf); // Write remaining data
* buf.rewind(); // Rewind buffer
* buf.get(array); // Copy data into array</pre></blockquote>
*
* @return This buffer
*/
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
三、clear():“清除”此缓冲区,将位置设置为 0,将限制设置为容量!此方法不能实际清除缓冲区中的数据,但从名称来看它似乎能够这样做,这样命名是因为它多数情况下确实是在清除数据时使用。因为调用该方法后,我们一般都会调用FileChannel.read(buff)或者buff.put()来把新的数据放到buff中,此时原来的内容就会被新的内容所覆盖!也不是全部覆盖,而是覆盖掉新数据所包含的字节数!所以看起来好象就是原来的内容被删除一样!
1. /**
2. * Clears this buffer. The position is set to zero, the limit is set to
3. * the capacity, and the mark is discarded.
4. *
5. * <p> Invoke this method before using a sequence of channel-read or
6. * <i>put</i> operations to fill this buffer. For example:
7. *
8. * <blockquote><pre>
9. * buf.clear(); // Prepare buffer for reading
10. * in.read(buf); // Read data</pre></blockquote>
11. *
12. * <p> This method does not actually erase the data in the buffer, but it
13. * is named as if it did because it will most often be used in situations
14. * in which that might as well be the case. </p>
15. *
16. * @return This buffer
17. */
18. public final Buffer clear() {
19. position = 0;
20. limit = capacity;
21. mark = -1;
22. return this;
23. }
/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*
* <p> Invoke this method before using a sequence of channel-read or
* <i>put</i> operations to fill this buffer. For example:
*
* <blockquote><pre>
* buf.clear(); // Prepare buffer for reading
* in.read(buf); // Read data</pre></blockquote>
*
* <p> This method does not actually erase the data in the buffer, but it
* is named as if it did because it will most often be used in situations
* in which that might as well be the case. </p>
*
* @return This buffer
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
四、ByteBuffer类中提供position(),remaining(),hasRemaining(),limit()等方法来验证以上三点。
五、上述代码中提到的:
1. and the mark is discarded.
and the mark is discarded.
i.)缓冲器细节:
mark标记 position位置 capacity容量 limit 限制
capacity(),
clear()-----这个方法并不是真正的删除数据,而是覆盖数据,覆盖的长度由新数据长度决定
filp()
limit()
limit(int) 设置limit值
mark()
position(int pos)设置position值
remaining()返回limit - position
hasRemaining()
j.)缓冲器妙用:
当调用put()和get()方法的时候,position指针就会改变,但是当你调用包含索引的get()和put()方法,索引的位置不会发生变化。
mark()方法用来设置mark()的值,reset()方法把potiostion的值设为mark的值。
个人理解:缓冲器是一个很好的数据承载结构,它有4个特性,mark标记 position位置 capacity容量 limit 限制,通过还有上述的改变postion的get()和put()方法以及不改变游标的get()和put()方法,有reset()和mark()方法等等,我们可以很方便的移动数据,我们可以改变缓冲器中数据的排列顺序(比如临近的char互换),因此,个人理解,缓冲期是一个很好的数据承载结构。
k.)内存映射文件:
获得Channel之后可以通过map()方法产生MappeByteBuffer(Mode,int from ,int to),生成MappedByteBuffer的时候必须指定映射文件的初始位置和映射区域的长度,这意味着我们可以映射某个大文件的较小的部分。映射文件速度很快。
l.)文件加锁:
无参形式的tryLock()和lock()可以获得整个文件的FileLock()。
tryLock()和lock()的区别:
tryLock()是非阻塞式的,它”设法”获取锁,但是如果不能获得,他将直接从方法调用返回。
lock()是阻塞式的,他要阻塞进程直至锁可以获得,或者lock()的线程中断,要么调用lock()的通道关闭。
也可以tryLock(long position ,long size ,boolean shared)或者
lock(long position,long size ,boolean shared)
加锁的区域由size-poition决定,第三个参数指定是否是共享锁。
无参数的加锁方法根据文件的尺寸的变化而变化,但是具有固定尺寸的锁不随文件的尺寸的变化而变化。文件变化的时候,无参锁会对整个文件进行加锁。
锁的类型:共享锁和独占锁的支持必须由底层的操作系统支持。如果操作系统不支持共享锁并为每一个请求加一个锁,那么会使用独占锁。
FileLock.isShared()可以查询锁的类型。
文件映射通常应用于极大的文件,我们可能需要对这种巨大的文件加锁,以便其他进程可以修改文件中违背加锁的部分。
我们不能得到缓冲器上的锁,只能获得通道上的锁。
11.压缩:
压缩类库是按字节而不是按字符方式处理的,所以它属于InputStreamhe 和OutputStream继承层次结构。但是有时候会和Reader和Writer混合使用。
a.)压缩类:
ChcekedInputStream---GetChecmSum()
ChcekedOutputStream
DeflaterOutputStream------压缩类的基类
ZipOutputStream
GZIPOutputStream
InflaterInputStream------解压类的基类
ZipInputStream
ZIPInputStream
压缩类构造器只接受 In/OutputStream
b.)方法:
BufferedReader b = new BufferedReader(new FileReader("d://abc.txt"));
File file = new File("d://data.zip");
ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(file));
BufferedOutputStream out = new BufferedOutputStream(zo);
zo.putNextEntry(new ZipEntry("d://fabc.txt"));
byte[] by = new byte[]{1,2,3,4,5,6,7,8,9,0};
out.write(by);
out.write(2000000000);
zo.putNextEntry(new ZipEntry("d://fabc2.txt"));
int c;
while((c = b.read())!= -1){
out.write(c);
}
out.flush();
zo.close();
b.close();
ZipInputStream zi = new ZipInputStream(new FileInputStream(file));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zi));
String s ;
ZipEntry getEntry;
while (( getEntry = zi.getNextEntry()) != null){
while((s = bufferedReader.readLine())!= null){
System.out.println(s);
}
}
bufferedReader.close();
zi.close();
ZipFile zipFile = new ZipFile("d://data.zip");
Enumeration e =zipFile.entries();
while(e.hasMoreElements()){
ZipEntry now = (ZipEntry) e.nextElement();
System.out.println("File"+now);
}
}
如上述程序,每要入一个压缩的文件,用putNextEntry(ZipEntry)。ZipEntry对象包含了一个功能很广泛的接口,允许你获取和设置Zip文件内该特定项上所有可得的数据。
ZipEntry只支持CRC(比Adler32稍慢一些,但是更准确)接口,不支持更快的Adler32。
读取文件的方式有两种:
1.)用ZipInputStream调用getNextEntry()返回下一个ZipEntry,然后对其进行处理。
2.)用ZipFile对象调用entries()来返回一个Enumeration(枚举),用e.hasMoreaElements()判断,用e.nextElement()得到ZipEntry。
Zip流中有一个令人困惑的方法setComment(),我们可以通过这个方法写注释,但是却没有任何方法恢复这个注释。似乎只能通过ZipEntry才能以逐条方式完全支持注释的获取。
12.对象序列化:
序列化机制可以自动弥补不同操作系统之间的不同。
a.)序列化机制的两个目的:
1.)远程方法调用(RMI),它使得存活于其他计算机上的对象使用起来就像是存活于本机上一样,当向远程对象发送消息的时候,需要通过对象序列化来传输参数和返回值。
2.)Java Beans
Class对象也可以被序列化。
b.)ObjectOutputStream(OutputStream) .writeObect(某对象)
对象序列化的优势:它能追踪对象内所包含的所有引用,并保存那些对象。接着又能对对象内包含的每个这样的引用进行追踪。简称”对象网”。
数据恢复的过程中,不调用任何构造器(包括默认构造器),整个对象都是从InputStream中恢复的。
c.)寻找类:将一个对象从它的序列化状态中恢复过来,需要Class对象,应该保证虚拟机可以获得相关的.class文件。
d.)序列化的控制:
当你对序列化有特殊需求的时候,比如某一部分不参与序列化,或者某子对象重新创建,而不是恢复,此时你需要对序列化的控制。
1.)Externalizable接口:
它有2个方法,writeExternal()和readExternal(),序列化和反序列化的时候自动调用。你必须在write里将对象的重要信息写入(wirteObject writeInt), 你还要在readExternal()里面恢复你想要的数据。
它与Serializable的不同:当恢复一个对象的时候,对于Serializable对象,对象完全以它存储的二进制位为基础来构造。而不调用构造器。
Externalizable接口要调用默认构造器来恢复对象。
2.)transient关键字:与Serializable配合,不序列化。
3.)writeObject(ObjectOutputStream stream)和readObject(ObjectIntputStream stream)方法:
在writeObject()里面可以调用stream.defaultWriteObject()来进行默认序列化
在readObject()里可以调用stream.defaultReadObject()来进行默认反序列化。
transient字段必须在程序中明确保存和恢复。
e.)流状态:
当我们把同一个对象放入同一个单一流的时候,无论你放入几个对象,读出的几个对象都是相同的。
当我们把对象放入2个不同的流中,对象就处于不同的状态了,我们可以理解为序列化是“流私有”的。
f.)static数据的序列化:
当你类实现Serializable的时候,如果你不对Static数据进行的特殊的序列化处理,那么static数据是不会序列化的。 假如我们想序列化static数据,必须自己动手去实现。
如下:
class Person implements Serializable{
private static int age;
public String name;
public Person(int age,String name){
this.age = age;
this.name = name;
}
@Override
public String toString() {
return age+name;
}
}
我在一个地方将我的类序列化:
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
Person p = new Person(18,"李雷");
Class c = p.getClass();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.txt"));
out.writeObject(c);
p.SerStaticState(out);
out.writeObject(p);
}
}
然后我在另一个地方反序列化:
public class A {
@SuppressWarnings("unused")
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.txt"));
@SuppressWarnings("rawtypes")
Class result = (Class)in.readObject();
Person p2 = (Person) in.readObject();
System.out.print(p2);
}
}
得到的结果是:0李雷
修改程序;
class Person implements Serializable{
private static int age;
public String name;
public Person(int age,String name){
this.age = age;
this.name = name;
}
@Override
public String toString() {
return age+name;
}
public static void SerStaticState(ObjectOutputStream os) throws IOException{
os.writeInt(age);
}
public static void DeSerStaticState(ObjectInputStream os) throws IOException{
age = os.readInt();
}
}
再次运行,得到:
0李雷
总结:所以当你要序列化static数据的时候,你应该对static进行单独的处理。