java IO流
字节流和字符流
InputStream(字节流)和Reader(字符流)是所有输入流的基类,本身不能创建实例来实现,但是是所有输入流的模板它们提供的方法所有的流都可以使用
OutPutStream(字节流) 与write (字符流)分别是按字节,与字符操作流中的数据。
java IO流执行输出时,不要忘记关闭关闭,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将文件缓冲区中的数据flush到物理结点(因为在执行clos ()后, 自动输出流的flush ()方法)。
如果希直接输出字符串的内容,则使用Write会有更好的效果
public class PrintStreamTest {
public static void main(String[] args) {
try {
FileOutputStream ps = new FileOutputStream("b.txt");
PrintWriter pw = new PrintWriter(ps);// 用printWrite对文件输出流进行包装
pw.print("hello b.txt");
pw.close();
} catch (FileNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
处理流的使用:
使用处理流的思路:使用处理流包装节点流,程序通过处理流来执行输入输出功能,让结点流与底层I/O设备文件交互。
只要流的构造器参数不是一个物理节点,而是已经存在的流,那么这种流就是一定是处理流,而所有节点流都是直接以物理IO节点作为构造参数的。
一个节点流作为构造器的参数即可,这样创建的处理流就是包装该结点的处理流。
在使用处理流包装底层节点流之后,关闭输入/ 输出流资源时,只要关闭最上层的处理流即可,关闭最上层的处理时,系统会自己关闭该处理流包装了该节点流的处理流。
通常使用中如果要处理的内容是文本内容,则使用字符流,如果进行输入输出的内容是二进制则应使用字节流。
转换流:
转换流用于字符与字节之间的转换,其中InputStreamReadr (将字节流转换为字符流),OutputStreamWrite (将字符流转换为字节流)。
BufferReader :BufferedReader 流具有缓冲功能,其可以一次读取一行文本(ReadLine ()方法),以换行符为标志,如果没有读到换行符,程序阻塞,直到读到换行符位置。
RandomAcessFile :
RandomAcessFile :是java输入输出流体系中功能最丰富的文件内容访问类,它既可以读取文件内容,也可以向文件输出数据,与普通的输入输出流不同的是其支持随机访问的方式,程序可以直接跳转到文件任意地方读写文件。
setFilePointer ()返回文件记录指针当前的位置
seek () 将文件记录指针定位到pos位置。
public class RandomAceesFileTest {
public static void main(String[] args) {
try {
System.out.println(System.getProperties());
RandomAccessFile raf = new RandomAccessFile("G:\\a.txt", "r");
System.out.println("文件指针的初始位置"+raf.getFilePointer());
raf.seek(3000); // 将文件指针定位到指定位置(从300字节出开始读数据)
byte [] buffer =new byte [1024];
int hasRead = 0 ;
while ((hasRead=raf.read(buffer))!=-1) { // read返回读取字节的个数
System.out.println(new String (buffer, 0 , hasRead));
System.out.println("已读取字节数"+hasRead);
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
当向文件中追加数据时,首先应将记录指针移动到文件最后。
RandomAcessFile依然不能向文件
NIO:前面介绍的输入流输出流都是都是阻塞式的输入输出,不仅如此传统的输入输出都是通过字节的移动来处理的(即使不直接处理字节但底层的实现还是还是依赖于字节的处理)因此一次只能处理一个字节,因此效率不高。
Jdk1.4后增加了提供了一系列改进的输入输出处理的新功能,被称为nio, 这些类都放在java.nio包以及子包下。
java新IO的概述:
新IO使用了不同的方式来处理输入输出,新IO采用内存映射文件的方式来处理输入输出, 新IO将文件或文件的一端区域映射到内存中,这样就可以向访问内存一样访问文件了,这比传统的处理速度快的多。
Channel和Buffer 是新IO的核心概念,Channle (通道)在新IO中所有的数据都需要通过通道传输,Channel提供了一个map()方法,通过该方法可以可以直接将“一块数据”映射到内存中,传统的输入输出系统是面向流的,则新IO则是面向块的。
Buffer可以被理解为容器,其本质是一个数组,发送到Channel中的所有对象必须首先放到Buffer中,而从Channel中读取的数据也必须放在Bufffer中。
使用Buffer
Buffer就像一个数组,可以保存多个类型相同的数据,可以保存多个类型相同的数据,Buffer是一个抽象类,最常用的是ByteBuffer,可以在底层字节数组上进行get/set除了ByteBuffer
之外对应的还有其类CharBuffer,ShortBuffer , INtBuffer ,LongBuffer , 。。。
Buffer中有三个参数:
容量:表示Buffer的最大容量
界限:第一个不应该被读出或写入的缓冲区位置索引。位于limt后的数据即不可写也不可被读入
位置:用于指明下一个可以被读出或者写入位置的索引。
Buffer的主要作用是装入数据,然后输出数据,当Buffer装入数据后,调用flip()方法该方法将limit设置为position位置,并将position位置设置为0,这就使得Buffer读写指针又移到了开始的位置,即Buffer调用了filp()方法后为输出数据做好了准备 当Bufffer调用clear()方法后,不是清空buffer的数据,仅是将position位置置为0,将limit置为capacity
这样就为再次向Buffer中输入数据做好了准备。
public class BufferTest {
public static void main(String[] args) {
CharBuffer buffer = CharBuffer.allocate(8);//创建一个容量为8字节的charBuffer。
System.out.println("capacity:" + buffer.capacity());
System.out.println("limit:"+buffer.limit());
System.out.println("posution:"+buffer.position());
buffer.put('a');
buffer.put('b');
buffer.put('c');
System.out.println("插入元素后position="+buffer.position());
buffer.flip();//将position置为0,并将limit置于position位置处。
System.out.println("调用flip后limit=:"+buffer.limit());
System.out.println("调用flip后position=:"+buffer.position());
System.out.println("第一个元素(position=0)"+buffer.get());
System.out.println("输出一个元素后position: "+buffer.position());
buffer.clear(); //将position置为0 ,将limit置为capacity(该方法并不会将缓冲区置为空)
System.out.println("调用clear后position:"+buffer.position());
System.out.println("调用clear后limit="+buffer.limit());
System.out.println("第三个元素为:"+buffer.get(2));// 使用绝对方式来访问Buffer里的数据时不会影响position的值
System.out.println("执行绝对读取后position为:"+buffer.position());
}
}
Channel的使用:
1:Channel 可以直接将指定的文件的部分或全部直接映射成Buffer。
2:程序不能直接访问Channel中的数据,包括读取与写入,Channel只能与Buffer进行交互。如果从Channel中取得数据,必须先用Buffer从Channel中取出一些数据,让程序从Buffer中取出这些数据,写入也一样,java为Channel接口提供DatagramChannel(基于UDP通信的Channel),FileChannel,SelectableChannel,(ServerSocketChannel,SocketChannel(用于Tcp通信的Channel))。
所有的Channel都不是通过构造方法来获取的,而是通过传统的节点流(InputStream,OutPutStream,)的getChannel获取的,不同类型的节点流获得的Channel不一样。
public class FileChannelTest {
public static void main(String[] args) {
File file = new File("G:\\a.txt");
try {
FileChannel inchannel = new FileInputStream(file).getChannel();
FileChannel outchannel = new FileOutputStream("G:\\c.txt").getChannel();
//将inchannel里的全部数据映射成ByteBuffer
MappedByteBuffer buffer = inchannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
//使用GBK的字符集来创建解码器
Charset charset = Charset.forName("GBK");
//直接将Buffer里的数据全部输出
outchannel.write(buffer);
// 将position置为0,将limit置为capacity,为再次读入做准备。
buffer.clear();
// 创建解码器对象
CharsetDecoder decoder = charset.newDecoder();
//使用解码器将ByteBuffer转化为charBufffer
CharBuffer charbuffer = decoder.decode(buffer);
System.out.println(charbuffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符集合CharSet
在计算机中所有的文件在底层都是二进制文件,全不都是字节码,对于文本文件可以看到哟个个字符,这是因为系统将底层的二进制序列转换为字符的缘故,这个过程涉及编码(Encode)解码(Decoder),通常而言把明文的字符转换为计算机理解的二进制序列,称为编码,把二进制序列转换为人能够看懂的称为解码。
一旦知道字符集的别名之后,程序就可以调用CharSet的forName()方法来创建对应的CharSet对象,forName()方法的参数就是对应的字符集的别名。
获得CharSet对象之后就可以通过该对象的newDecoder()或newEncoder()分别返回CharSetDecode,和CharSetEncoder代表该charSet的解码编码器,调用CharSetDecoder的decode方法将ByteBuffer(字节序列)转换为CharBuffer(字符序列)调用charSetEncoder的encoder()方法可将CharBuffer或Sring装换为ByteBuffer(字节序列)。
在String类里提供了一个getBytes (String charSets)该方法返回byte[];将指定的字符集转换成字节序列。
public class CharSetTransform {
public static void main(String[] args) throws CharacterCodingException {
Charset cn = Charset.forName("GBK");// 创建中文简体对应的CharSet对象
// 获取cn对象的编码器解码器
CharsetEncoder charsetEncoder = cn.newEncoder();
CharsetDecoder charsetDecoder = cn.newDecoder();
//创建一个CharBuffer
CharBuffer buffer = CharBuffer.allocate(8);
buffer.put('王');
buffer.put ('星');
buffer.put('宇');
buffer.flip();
ByteBuffer byteBuffer = charsetEncoder.encode(buffer);//将CharBuffer中的字符序列编码为字节序列。
System.out.println(byteBuffer);
System.out.println(charsetDecoder.decode(byteBuffer));//将字节序列解码为字符序列
}
}
文件锁:
如果多个程序需要并发修改同一个文件时,程序之间需要某种机制进行通信,使用文件锁可以有效地阻止一个多个进程并发的修改同一个文件