当前位置: 代码迷 >> 综合 >> 输入输出
  详细解决方案

输入输出

热度:20   发布时间:2024-01-31 21:42:44.0

输入输出

一、Java IO

Java IO即Java 输入输出系统。流从概念上来说是一个连续的数据流。

按流向分为输入输出流,按维度分为字节流(8位字节)和字符流(16位字节),按角色分为节点流和处理流。

相对计算机程序而言,输入:读,输出:写。

节点流(低级流):向IO设备读写数据的流。

处理流(高级流、包装流):对已存在的流进行连接和封装后的流。是一种装饰器设计模式。只要流的构造器是流,则一定为处理流。关闭时只需关闭最上层的处理流,系统会自动关闭节点流。

例:

PrintStream ps=new PrintStream(new FileOutputStream(file));ps,println(str);

1、File操作

public class FileDemo {public static void main(String[] args) {//检查文件是否存在File file = new File( "d:/test.txt");boolean fileExists = file.exists();System. out.println( fileExists);//创建文件目录,若父目录不存在则返回falseFile file2 = new File( "d:/fatherDir/subDir");boolean dirCreated = file2.mkdir();System. out.println( dirCreated);//创建文件目录,若父目录不存则连同父目录一起创建File file3 = new File( "d:/fatherDir/subDir2");boolean dirCreated2 = file3.mkdirs();System. out.println( dirCreated2);File file4= new File( "d:/test.txt");//判断长度long length = file4.length();//重命名文件boolean isRenamed = file4.renameTo( new File("d:/test2.txt"));//删除文件boolean isDeleted = file4.delete();File file5= new File( "d:/fatherDir/subDir");//是否是目录boolean isDirectory = file5.isDirectory();//列出文件名String[] fileNames = file5.list();//列出目录File[] files = file4.listFiles();}

2、字节流

字节流对应的类是InputStreamOutputStream。最后应显式关闭IO资源或使用自动关闭资源的try语句

写文件

public static void writeByteToFile() throws IOException{//创建File实例File file= new File( "d:/test.txt");//要写的内容String hello= new String( "hello word!");byte[] byteArray= hello.getBytes();//因为是用字节流来写媒介,所以对应的是OutputStream //又因为媒介对象是文件,所以用到子类是FileOutputStreamOutputStream os= new FileOutputStream( file);os.write(byteArray);//显式关闭os.close();}

读文件

public static void readByteFromFile() throws IOException{//创建File实例File file= new File( "d:/test.txt");//要存储的数组byte[] byteArray= new byte[( int) file.length()];//用自动关闭资源的try语句try(//因为是用字节流来读媒介,所以对应的是InputStream//又因为媒介对象是文件,所以用到子类是FileInputStreamInputStream is= new FileInputStream( file)){while((int size= is.read( byteArray))>0){System. out.println( "大小:"+size +";内容:" +new String(byteArray));}}catch(IOException ex){ex.printStackTrace();}
}

3、字符流

字符流对应的类应该是ReaderWriter

写文件

public static void writeCharToFile() throws IOException{File file= new File( "d:/test.txt");String hello= new String( "hello word!");//因为是用字符流来读媒介,所以对应的是Writer,又因为媒介对象是文件,所以用到子类是FileWriterWriter os= new FileWriter( file);os.write( hello);os.close();}

读文件

public static void readCharFromFile() throws IOException{File file= new File( "d:/test.txt");//因为是用字符流来读媒介,所以对应的是Reader//又因为媒介对象是文件,所以用到子类是FileReaderReader reader= new FileReader( file);char [] byteArray= new char[( int) file.length()];int size= reader.read( byteArray);System. out.println( "大小:"+size +";内容:" +new String(byteArray));reader.close();}

4、转换流

可以将字节流转换成字符流。(没有相反的转换)

Reader reader= new InputStreamReader(is);

5、推回输入流

包括PushbackInputStream和PushbackReader。

可使用unread()方法将指定数组内容(字节/字符)推回到缓冲区中。

使用read方法读取的时候,先读取推回缓冲区内的内容,若容器没有满,则继续从推回输入流中读取。

PushbackReader pr=new PushbackReader(new FileReader(file),64);
pr.unread(array);

6、重定向标准输入/输出

System类里提供三个重定向标准输入/输出的方法:

  • static void setErr(PrintStream err):重定向“标准”错误输出流
  • static void setIn(InputStream in):重定向“标准”输入流
  • static void setOut(PrintStream out):重定向“标准”输出流

7、随机读取File文件

RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置。

局限:只可以读写文件。不可向文件指定位置插入内容。

读:

public static void randomAccessFileRead() throws IOException {// 创建一个RandomAccessFile对象RandomAccessFile file = new RandomAccessFile( "d:/test.txt", "rw");// 通过seek方法来移动读写位置的指针file.seek(10);// 获取当前指针long pointerBegin = file.getFilePointer();// 从当前指针开始读byte[] contents = new byte[1024];file.read( contents);long pointerEnd = file.getFilePointer();System. out.println( "pointerBegin:" + pointerBegin + "\n" + "pointerEnd:" + pointerEnd + "\n" + new String(contents));file.close();}

写:

public static void randomAccessFileWrite() throws IOException {// 创建一个RandomAccessFile对象RandomAccessFile file = new RandomAccessFile( "d:/test.txt", "rw");// 通过seek方法来移动读写位置的指针file.seek(10);// 获取当前指针long pointerBegin = file.getFilePointer();// 从当前指针位置开始写file.write( "HELLO WORD".getBytes());long pointerEnd = file.getFilePointer();System. out.println( "pointerBegin:" + pointerBegin + "\n" + "pointerEnd:" + pointerEnd + "\n" );file.close();}

8、缓冲流

见谷歌书签

二、序列化

1、序列化含义

序列化:将一个JAVA对象写入IO流中。

反序列化:将流转换为对象,必须提供class文件,否则会引发ClassNotFound异常。

要使类是可序列化的,必须实现Serializable或Externalizable接口。可序列化对象的父类要么有无参构造器,要么也是可序列化的。

Serializable接口:表明类可序列化,实现它无须实现任何方法。

2、对象引用的序列化

JAVA提供了序列化算法,使得多次输出同一对象时,只有第一次才会将对象转换成字节序列输出,其他输出一个序列化编号。

问题:多次输出同一对象时,发生在中间的改变不会影响输出内容。

3、Java9增加的过滤功能

利用ObjectInputStream的setObjectInputFilter()方法获取过滤器,

使用过滤器的checkInput()方法有三种返回值:

  • Status.REJECTED 反序列化停止
  • Status.ALLOWED 执行反序列化
  • Status.UNDECIDED 继续检查

4、版本

serialVersionUID:用于标识序列化版本,原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID一致,那么该对象才能被反序列化成功。

这个serialVersionUID的详细的工作机制是:在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一致则反序列化成功,否则就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且回报出错误。

最好自己定义这个类变量,保证反序列化的成功进行。

三、NIO

NIO即新的输入输出,在标准java代码中提供了高速的面向块的IO操作。

1、流与块的比较

NIO和IO最大的区别是数据打包和传输方式。IO是以流的方式处理数据,而NIO是以块的方式处理数据。

面向流的IO一次一个字节的处理数据,一个输入流产生一个字节,一个输出流就消费一个字节。为流式数据创建过滤器就变得非常容易,链接几个过滤器,以便对数据进行处理非常方便而简单,但是面向流的IO通常处理的很慢。

面向块的IO系统以块的形式处理数据。每一个操作都在一步中产生或消费一个数据块。按块要比按流快的多。

2、Buffer

Buffer是一个对象,它包含一些要写入或读出的数据。在NIO中,数据是放入buffer对象的,而在IO中,数据是直接写入或者读到Stream对象的。应用程序不能直接对 Channel 进行读写操作,而必须通过 Buffer 来进行,即 Channel 是通过 Buffer 来读写数据的。

在NIO中,所有的数据都是用Buffer处理的,它是NIO读写数据的中转池。Buffer实质上是一个数组,通常是一个字节数据,但也可以是其他类型的数组。但一个缓冲区不仅仅是一个数组,重要的是它提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

使用 Buffer 读写数据一般遵循以下四个步骤:

  1. 写入数据到 Buffer;
  2. 调用 flip() 方法;
  3. 从 Buffer 中读取数据;
  4. 调用 clear() 方法或者 compact() 方法。

flip() 方法:一旦要读取数据,需要通过 flip() 方法将 Buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 Buffer 的所有数据。

clear() 或 compact() 方法:一旦读完了所有的数据,就需要清空缓冲,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或 compact() 方法。clear() 方法会初始化整个缓冲区(没有删除内容)。compact() 方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

put()和get()方法:buffer放入或取出数据。

控制buffer状态的变量:

position:位置,跟踪已经写了多少数据或读了多少数据,它指向的是下一个字节来自哪个位置
limit:界限,代表还有多少数据可以取出或还有多少空间可以写入,它的值小于等于capacity。
capacity:容量,代表缓冲区的最大容量,一般新建缓冲区,limit的值和capacity的值相等。不可改变。

mark:可选标记。

3、Channel

Channel是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:

  1. Channel是双向的,既可以读又可以写,而流是单向的
  2. Channel可以进行异步的读写
  3. 程序不能直接访问其中的数据,对Channel的读写必须通过buffer对象
  4. 有映射数据到buffer的**map()**方法

在Java NIO中Channel主要有如下几种类型:

  • FileChannel:从文件读取数据的
  • DatagramChannel:读写UDP网络协议数据
  • SocketChannel:读写TCP网络协议数据
  • ServerSocketChannel:可以监听TCP连接

4、实例

public static void copyFileUseNIO(String src,String dst) throws IOException{//声明源文件和目标文件FileInputStream fi=new FileInputStream(new File(src));FileOutputStream fo=new FileOutputStream(new File(dst));//获得传输通道channelFileChannel inChannel=fi.getChannel();FileChannel outChannel=fo.getChannel();//获得容器bufferByteBuffer buffer=ByteBuffer.allocate(1024);//映射数据到buff,方法第一个参数代表映射模式//buffer=inChannel.map(FileChannel.MapMode.READ ONLY,0,length);while(true){//判断是否读完文件int eof =inChannel.read(buffer);//读完文件,此时 read() 方法会返回 -1 ,可以根据这个方法判断是否读完if(eof==-1){break;  }//重设一下buffer的position=0,limit=position,准备读缓冲区,写文件buffer.flip();//开始写文件outChannel.write(buffer);//重置buffer,重设position=0,limit=capacity,准备写缓冲区,读文件,缓冲区内容不变buffer.clear();}inChannel.close();outChannel.close();fi.close();fo.close();
}     

四、NIO.2

具体见《Java疯狂讲义》721页后……

  相关解决方案