黑马程序员--IO流02--字节流

本文详细介绍了Java中的字节流,包括字节流的概述、FileInputStream和FileOutputStream的使用,以及如何利用缓冲区复制图片和mp3文件。通过模拟BufferedInputStream,解释了字节码读取过程中的数据转换。最后,讨论了键盘录入数据及转换流在字符编码转换中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

               ------------android培训java培训、期待与您交流!--------------

大纲:

            一、字节流概述

            二、FileInputStream

            三、复制图片(字节输入输出流)

            四、复制mp3(通过字节流的缓冲区完成复制)

            五、模拟BufferedInputStream

            六、键盘录入

            七、转换流(属字符流)

            八、打印系统参数

一、字节流概述

    字节流的抽象基类:
    |---->InputStream:字节输入流
    |---->OutputStream:字节输出流

它的操作与字符流类似,可以参与字符流的定义、读取、写入、处理异常的格式,只不过

它可以对非字符的数据,比如图片、视频、音频文件(例如mp3)等进行操作,而字符流不能。

常用字节流:

FileInputStream
FileOutputStream

BufferedInputStream
BufferedOutputStream

二、FileInputStream
FileInputStream是InputStream的一个子类,用于读取诸如图像数据之类的原始字节流。

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[num];
		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();
	}
}


最常用最标准的是第二种方式,第三种方式效率太低,第一种方式虽然很方便,

但不适合大文件(如视频),这样内存会崩溃。

三、复制图片(字节输入输出流)

用FileInputStream和FileOutputStream流对象

思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。

import java.io.*;
class  CopyPic
{
	public static void main(String[] args) 
	{
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try
		{
			fos = new FileOutputStream("c:\\2.bmp");
			fis = new FileInputStream("c:\\1.bmp");

			byte[] buf = new byte[1024];
			int len = 0;
			while((len=fis.read(buf))!=-1)
			{
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("复制文件失败");
		}
		finally
		{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if(fos!=null)
					fos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入关闭失败");
			}
		}
	}
}

四、复制mp3(通过字节流的缓冲区完成复制)

用BufferedOutputStream和BufferedInputStream流对象,对象里面都有内置的数组做缓冲

import java.io.*;
class  CopyMp3
{
	public static void main(String[] args) throws IOException
	{
		long start = System.currentTimeMillis();
		copy();
		long end = System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");
	}

	//通过字节流的缓冲区完成复制。
	public static void copy()throws IOException
	{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
		
		int by = 0;
		while((by=bufis.read())!=-1)
		{
			bufos.write(by);
		}

		bufos.close();
		bufis.close();

	}
}

以上read方法虽然也是一个一个字节读取的,但是效率依然很高,因为read方法不从硬盘直接读取,

而是从缓冲流内部的字节数组中读取的。write方法也不是收到一个字节就输出到硬盘,而是先把它

存到内部数组中,等数组存满之后再输出到硬盘中。说到底read和write方法之间是通过数组交换

数据的,也就是数据在内存中进行交换。

五、模拟BufferedInputStream

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();
	}
}

1、myread返回值类型为什么是int而不是byte?

下面是一段字节码文件的二进制数据:

11111111-111111110000000000101001001010100101010010101001010

第一个字节就是11111111,十进制就是-1,所以如果不做任何转换的话myread

方法读到-1程序就会停止读取数据,数据还没有读完,程序就已经结束,读到的

数据就不再完整,所以需要转化。

2、为什么return b时要&255(0xff)?

因为针对字节是11111111,需要转化,先将byte提升为int,

即由11111111变为 11111111 11111111 11111111 11111111 

但int类型的还是-1,是-1的原因是因为在8个1前面补的是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。

这就需要&255(0xff),操作如下: 

   11111111 11111111 11111111 11111111                        
&00000000 00000000 00000000 11111111 
------------------------------------------------------------
   00000000 00000000 00000000 11111111 

得到的数据是255不再是-1

扩展一下:

一个数的负数,是取反之后再加1。

比如1的二进制是00000001

-1的二进制是:

0000-0001  --------〉 1     
1111-1110  --------〉 取反
0000-0001  --------〉 加1 
1111-1111  --------〉 -1二进制 


六、键盘录入

System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备:键盘。
需求:
通过键盘录入数据。
当录入一行数据后,就将该行数据进行打印。
如果录入的数据是over,那么停止录入。

import java.io.*;
class  ReadIn
{
	public static void main(String[] args) throws IOException
	{
		InputStream in = System.in;
		StringBuilder sb = new StringBuilder();

		while(true)
		{
			int ch = in.read();
			if(ch=='\r')
				continue;
			if(ch=='\n')
			{
				String s = sb.toString();
				if("over".equals(s))
					break;
				System.out.println(s.toUpperCase());
				sb.delete(0,sb.length());//清空数组
			}
			else
				sb.append((char)ch);

		}
	}
}

七、转换流(属字符流)

用InputStreamReader和OutputStreamReader

是字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。

键盘录入(缓冲方式)

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));
		
//		OutputStream out = System.out;
//		OutputStreamWriter osw = new OutputStreamWriter(out);
//		BufferedWriter bufw = new BufferedWriter(osw);
		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();

	}
}

通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。
也就是readLine方法。
readLine方法是字符流BufferedReader类中的方法。因此需要对InputStream类

进行装饰,但一个是字符流,一个是字节流,中间就需要一个转换流。


编码表

FileWriter是使用的默认编码表GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。而该转换流对象要接收一个字节输出流。

而且还可以操作的文件的字节输出流FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
还需要高效处理的话就要用缓冲流,BufferedWriter bufw = new BufferedWriter(osw);


八、打印系统参数

import java.util.*;
import java.io.*;
class  SystemInfo
{
	public static void main(String[] args) throws IOException
	{
		Properties prop = System.getProperties();

		//System.out.println(prop);
		prop.list(new PrintStream("sysinfo.txt"));
	}
}


               ------------android培训java培训、期待与您交流!--------------



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值