黑马程序员,Java基础知识八:IO流

IO流

IO流用来处理设备之间的数据传输,Java对数据的操作都是通过流的方式,而操作流的对象都在IO包中。

流按操作数据分为两种:字符流和字节流,按流向分为输入流和输出流。

字节流的抽象基类:InputStream,OutputStream。

字符流的抽象基类:Reader,Writer。

注意:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如:InputStream的子类FileInputStream。Reader的子类FileReader。

字符流

创建文件,写入文件:

1,创建流对象,建立数据存放文件。

FileWriter fw = new FileWriter("Test.txt");

2,调用流对象的写入方法,将数据写入流

fw.write("text");

3,关闭流资源,并将流中的数据清空到文件中。

fw.close();

完整的代码示例:

FileWriter fw = null;
try{
     fw = new FileWriter("text.txt");
     fw.write("text");
}
catch(IOException e){
     System.out.println(e.toString());
}
finally{
      if(fw!=null)
      try{
          fw.close();
      }
      catch(IOException e){
          System.out.println(e.toString());
      }
}

读取文件

1,建立流对象,将已存在的一个文件加载进流。

FileReader fr = new FileReader("Test.txt")

2,创建一个临时存放数据的数组。

char[] ch = new char[1024];

3,调用流对象的读取方法将流中的数据读入到数组中。

fr.read(ch);

完整的代码示例:

import java.io.*;

class  FileReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个文件读取流对象,和指定名称的文件相关联。
		//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
		FileReader fr = new FileReader("demo.txt");

		//调用读取流对象的read方法。
		//read():一次读一个字符。而且会自动往下读。
		
		int ch = 0;

		while((ch=fr.read())!=-1)
		{
			System.out.println(
		}


		/*
		while(true)
		{
			int ch = fr.read();
			if(ch==-1)
				break;
			System.out.println("ch="+(char)ch);
		}
		*/



		fr.close();


	}
}

注意:

1,在定义文件路径时,可以用“/”或者“\\”。

2,在创建一个文件时,如果目录下有同名文件将被覆盖。

3,在读取文件时,必须保证该文件已经存在,否则出异常。

字符流缓冲区

缓冲区的出现提高了对数据的读写效率。对应的类有BufferWriter和BufferReader。缓冲区必须要结合流才可以使用,它在流的基础上对流的功能进行了增强。

字符流缓冲区提供了一次读一行的方法 readLine,方便于对文本数据的获取。

当返回null时,表示读到文件末尾。readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。

import java.io.*;

class  BufferedReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个读取流对象和文件相关联。
		FileReader fr = new FileReader("buf.txt");

		//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
		BufferedReader bufr = new BufferedReader(fr);
		

		String line = null;

		while((line=bufr.readLine())!=null)
		{
			System.out.print(line);
		}


		bufr.close();
	}

}

字节流

字节流的基本操作和字符流相同,但它不仅可以操作字符,还可以操作其他媒体文件。类似于字符流,它有InputStream和OutputStream

import java.io.*;
class  FileStream
{
	public static void main(String[] args) throws IOException
	{
		readFile_3();
	}

	public static void readFile_3()throws IOException
	{
		FileInputStream fis = new FileInputStream("fos.txt");
		
//		int num = fis.available();
		byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。

		fis.read(buf);

		System.out.println(new String(buf));

		fis.close();
	}


	public static void readFile_2()throws IOException
	{
		FileInputStream fis = new FileInputStream("fos.txt");

		byte[] buf = new byte[1024];
		int len = 0;
		while((len=fis.read(buf))!=-1)
		{
			System.out.println(new String(buf,0,len));
		}

		fis.close();
		
	}

	public static void readFile_1()throws IOException
	{
		FileInputStream fis = new FileInputStream("fos.txt");

		int ch = 0;

		while((ch=fis.read())!=-1)
		{
			System.out.println((char)ch);
		}

		fis.close();
	}

	public static void writeFile()throws IOException
	{
		FileOutputStream fos = new FileOutputStream("fos.txt");
		

		fos.write("abcde".getBytes());

		fos.close();

		
	}
}

字节流缓冲区

同字符流缓冲区,它也是提高了字节流的读写效率。

import java.io.*;

class MyBufferedInputStream
{
	private InputStream in;

	private byte[] buf = new byte[1024*4];
		
	private int pos = 0,count = 0;
	
	MyBufferedInputStream(InputStream in)
	{
		this.in = in;
	}

	//一次读一个字节,从缓冲区(字节数组)获取。
	public int myRead()throws IOException
	{
		//通过in对象读取硬盘上数据,并存储buf中。
		if(count==0)
		{
			count = in.read(buf);
			if(count<0)
				return -1;
			pos = 0;
			byte b = buf[pos];

			count--;
			pos++;
			return b&255;
		}
		else if(count>0)
		{
			byte b = buf[pos];

			count--;
			pos++;
			return b&0xff;
		}
		return -1;

	}
	public void myClose()throws IOException
	{
		in.close();
	}
}

转换流

转换流是字符流和字节流之间的桥梁,它方便了字节流和字符流之间的操作。当字节流中的数据都是字符时,可以转成字符流进行操作更加高效。

import java.io.*;

class  TransStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		//获取键盘录入对象。
		//InputStream in = System.in;
		//将字节流对象转成字符流对象,使用转换流。InputStreamReader
		//InputStreamReader isr = new InputStreamReader(in);
		//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
		//BufferedReader bufr = new BufferedReader(isr);
		//键盘的最常见写法。
		BufferedReader bufr = 
				new BufferedReader(new InputStreamReader(System.in));

		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
		String line = null;
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}

		bufr.close();

	}
}

流操作的基本规律

最痛苦的就是流对象有很多,而我们不知道该用哪一个。

    通过两个明确来完成:

1、明确源和目的

源:输入流InputStream , Reader

目的:输出流OutputStream , Writer

2、操作的数据是否纯文本。

是:字符流。

否:字节流。

3.当体系明确后,再明确要使用哪个具体的对象。

通过设备来进行区分:

源设备:内存,硬盘,键盘。

目的设备:内存,硬盘,控制台。

这三个分别对应(ArrayStream,FileStream,System)

通常我们在设计到字符编码转换时,需要用转换流

另外转换流可以直接与流关联:键盘,控制台。

但不能直接与文件相关联,它与文件之间还需要加一个操作文件的流对象(FileInputStream,FileOutputStream或者FileReader,FileWriter)

class  TransStreamDemo2
{
	public static void main(String[] args) throws IOException
	{
		System.setIn(new FileInputStream("PersonDemo.java"));

		System.setOut(new PrintStream("zzz.txt"));

		//键盘的最常见写法。
		BufferedReader bufr = 
				new BufferedReader(new InputStreamReader(System.in));

		
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

		String line = null;

		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}

		bufr.close();

	}
}

标准输入输出流

在System类中有in和out流对象,他们代表了系统标准的输入和输出设备。默认输入设备是键盘,输出设备是显示器。

System.in的类型是InputStream, System.out的类型是PrintStream,它是OutputStream子类FileOutputStream的子类

键盘录入:

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

控制台输出:

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

流操作小结:

流是用来处理数据的,处理数据时,一定要明确数据源和数据目的地(数据汇)。

数据源可以是文件,也可以是键盘。

数据目的地可以是文件、显示器或者其他设备。

而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。

File类

用来将文件或者文件夹封装成对象。它方便对文件和文件夹的属性信息进行操作。

File对象也可以作为参数传递给流的构造函数。

File类常见方法:
1,创建。
    boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
    boolean mkdir():创建文件夹。
    boolean mkdirs():创建多级文件夹。
2,删除。
    boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel。
    void deleteOnExit();在程序退出时删除指定文件。
3,判断。
    boolean exists() :文件是否存在.
    isFile():
    isDirectory();
    isHidden();
    isAbsolute();
4,获取信息。
    getName():
    getPath():
    getParent():
    getAbsolutePath()
    long lastModified()
    long length()

class FileDemo 
{
	public static void main(String[] args) throws IOException
	{
		method_5();
	}

	public static void method_5()
	{
		File f1 = new File("c:\\Test.java");
		File f2 = new File("d:\\hahah.java");

		sop("rename:"+f2.renameTo(f1));

	}

	public static void method_4()
	{
		File f = new File("file.txt");

		sop("path:"+f.getPath());
		sop("abspath:"+f.getAbsolutePath());
		sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。
									//如果相对路径中有上一层目录那么该目录就是返回结果。



	}
	
	public static void method_3()throws IOException
	{
		File f = new File("d:\\java1223\\day20\\file2.txt");

		//f.createNewFile();

		//f.mkdir();


		//记住在判断文件对象是否是文件或者目的时,必须要先判断该文件对象封装的内容是否存在。
		//通过exists判断。
		sop("dir:"+f.isDirectory());
		sop("file:"+f.isFile());

		sop(f.isAbsolute());
	}


	public static void method_2()
	{
		File f = new File("file.txt");

		//sop("exists:"+f.exists());

		//sop("execute:"+f.canExecute());

		//创建文件夹
		File dir = new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");

		sop("mkdir:"+dir.mkdirs());
	}
	

	public static void method_1()throws IOException
	{
		File f = new File("file.txt");
//		sop("create:"+f.createNewFile());
		//sop("delete:"+f.delete());



		
	}







	//创建File对象
	public static void consMethod()
	{
		//将a.txt封装成file对象。可以将已有的和为出现的文件或者文件夹封装成对象。
		File f1 = new File("a.txt");

		//
		File f2 = new File("c:\\abc","b.txt");


		File d = new File("c:\\abc");
		File f3 = new File(d,"c.txt");

		sop("f1:"+f1);
		sop("f2:"+f2);
		sop("f3:"+f3);

		File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");


	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

IO包中的其他类


RandomAccessFile

RandomAccessFile可以随机的访问文件,自身具备读写的方法。该类不能算是IO体系中的子类,因为它直接继承自Object,但它也是IO包中的成员,因为它具备读和写的功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。通过skipBytes(int x)和seek(int x)来达到随机访问的效果。其实完成读写的原理就是内部封装了字节输入流和输出流。

该类只能操作文件,而且操作文件有几种模式可选:只读r,读写rw等。如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。

class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeFile_2();
		//readFile();

		//System.out.println(Integer.toBinaryString(258));

	}

	public static void readFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
		
		//调整对象中指针。
		//raf.seek(8*1);

		//跳过指定的字节数
		raf.skipBytes(8);

		byte[] buf = new byte[4];

		raf.read(buf);

		String name = new String(buf);

		int age = raf.readInt();


		System.out.println("name="+name);
		System.out.println("age="+age);

		raf.close();


	}

	public static void writeFile_2()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
		raf.seek(8*0);
		raf.write("周期".getBytes());
		raf.writeInt(103);

		raf.close();
	}

	public static void writeFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

		raf.write("李四".getBytes());
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.writeInt(99);

		raf.close();
	}
}

管道流

pipedInputStream和pipedOutputStream,他们就像形同了管道,让输入输出连接起来,可以结合线程来使用。

import java.io.*;

class Read implements Runnable
{
	private PipedInputStream in;
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];

			System.out.println("读取前。。没有数据阻塞");
			int len = in.read(buf);
			System.out.println("读到数据。。阻塞结束");



			String s= new String(buf,0,len);

			System.out.println(s);

			in.close();

		}
		catch (IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}

class Write implements Runnable
{
	private PipedOutputStream out;
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	public void run()
	{
		try
		{
			System.out.println("开始写入数据,等待6秒后。");
			Thread.sleep(6000);
			out.write("piped lai la".getBytes());
			out.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}

class  PipedStreamDemo
{
	public static void main(String[] args) throws IOException
	{

		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();


	}
}

打印流

该流提供了打印方法,可以将各种数据类型的数据都原样打印。

字节打印流:PrintStream,构造函数可以接收的参数类型:1,file对象。File,2,字符串路径。String,3,字节输出流。OutputStream
字符打印流:PrintWriter,构造函数可以接收的参数类型:1,file对象。File,2,字符串路径。String,3,字节输出流。OutputStream,4,字符输出流,Writer。

import java.io.*;

class  PrintStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));

		PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);

		String line = null;

		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			out.println(line.toUpperCase());
			//out.flush();
		}

		out.close();
		bufr.close();

	}	
}

序列流

SequenceInputstream,和SequenceOutputStream使用序列流可以将多个流合并。

import java.io.*;

class Read implements Runnable
{
	private PipedInputStream in;
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];

			System.out.println("读取前。。没有数据阻塞");
			int len = in.read(buf);
			System.out.println("读到数据。。阻塞结束");



			String s= new String(buf,0,len);

			System.out.println(s);

			in.close();

		}
		catch (IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}

class Write implements Runnable
{
	private PipedOutputStream out;
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	public void run()
	{
		try
		{
			System.out.println("开始写入数据,等待6秒后。");
			Thread.sleep(6000);
			out.write("piped lai la".getBytes());
			out.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}

class  PipedStreamDemo
{
	public static void main(String[] args) throws IOException
	{

		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();


	}
}

操作对象

ObjectInputStream和ObjectOutputStream,被操作的对象需要实现Serializable(标记接口)。

操作基本数据类型

DataInputStream和DataOutputStream可以用于操作基本数据类型的数据的流对象。

操作字节数组

用于操作字节数组的流对象。

ByteArrayInputStream :  在构造的时候,需要接收数据源,而且数据源是一个字节数组。

ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。也就是数据的目的地。

注意:因为这两个流对象都操作的数组,并没有使用系统资源。所以不用进行close关闭。

操作字符数组

CharArrayReader与CharArrayWrite

操作字符串

StringReader与StringWriter

字符编码

字符流的出现是为了方便操作字符,而且更重要的是它加入了编码转换。

通过子类转换流来完成:inputStreamReader和OutputStreamWriter。

在两个对象进行构造的时候可以加入字符集。

它可以让字符以指定编码格式进行存储。

可以让文本数据以指定编码格式来解读。































  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值