java基础:IO流---字节流

概述

计算机中,存储一切数据(文本、图片、视频等),都是以二进制的形式进行的,最终都是一个一个的字节,所以使用流,进行传输数据的时候,也是一个一个的字节进行的。

java.io.InputStream是所有字节输入流的抽象父类型,最核心的三个read方法

//每次读一个字节,返回值是本次读取的字节值
public abstract int read() throws IOException;

//每次读多个字节,并存放到指定的字节数组中,返回值是本次一共读取了多个字节(字节数)
public int read(byte b[]) throws IOException {    
	return read(b, 0, b.length);
}

//每次读多个字节,并存放到指定的字节数组中,返回值是本次一共读取了多个字节(字节数)
//同时可以指定从数组的什么位置开始存放,以及在数组中最多存放多个字节
public int read(byte b[], int off, int len) throws IOException {    
	if (b == null) {        
		throw new NullPointerException();   
	} else if (off < 0 || len < 0 || len > b.length - off) {        
		throw new IndexOutOfBoundsException();   
	} else if (len == 0) {        
		return 0;   
	}    
	
	int c = read();    
	if (c == -1) {
	    return -1;
	}    
	b[off] = (byte)c;    
	
	int i = 1;    
	try {        	
		for (; i < len ; i++) {            	
			c = read();            
		if (c == -1) {               
		 	break;          
		}            
		b[off + i] = (byte)c;       
		}   
	} catch (IOException ee) {
	   
	}    
	return i;
}  

java.io.OutputStream是所有字节输出流的抽象父类型,其中最核心的三个wirte方法

//写出去一个字节值
public abstract void write(int b) throws IOException;

//把一个自己数组中的值全部写出去:调用到的是重载的write方法
public void write(byte b[]) throws IOException {
    write(b, 0, b.length);
}

//写出字节数组,指定开始位置,以及写出的字节数量
public void write(byte b[], int off, int len) throws IOException {    
	if (b == null) {        	
		throw new NullPointerException();
	} else if ((off < 0) || (off > b.length) || (len < 0) ||               
				((off + len) > b.length) || ((off + len) < 0)) {        
				throw new IndexOutOfBoundsException();   
	} else if (len == 0) {        
		return;
    }    
    for (int i = 0 ; i < len ; i++) {        
    	write(b[off + i]);   
    }
}

字节输入流用来读取数据,字节输出流用来输出数据

使用流操作数据的基本步骤:

1、声明流
2、创建流
3、使用流
4、关闭流

字节流的使用

控制台输入输出

使用字节流从控制台读取数据,以及向控制台中写数据

用到了java.lang.System这个包,专门用于控制台输入输出的

public final static InputStream in = null;

public final static PrintStream out = null;

实例:
代码演示一:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Test01 {
	
	public static void main(String[] args) {
		//1、声明流
		InputStream in = null;
		OutputStream out = null;
		
		//2、创建流
		in = System.in;
		out = System.out;
		
		//3、使用流
		try {
			int data = -1;
			//每次读取一个字节
			data = in.read();
			out.write(data);
			
			//刷新缓冲区,强制所有数据写出去
			out.flush(); 
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			
			//使用流后要关闭流,避免浪费资源
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
}

这里一次读取一个字节,然后将字节输出,也可以读取一次读取多个字节,将本次读到了的字节输出

结果演示如下:
输入一个值:
在这里插入图片描述
输入一串值:
在这里插入图片描述

因为只读一个字节,意味着这串字母中只读了第一个字母,所以输出一个字母

补充

这个子类中重写的read方法,会让线程阻塞,等待用户在控制台中的输入,用户输入并按下回车,程序中的read方法就从阻塞状态恢复过来,从而读取到用户输入的内容。

代码演示二:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Test02 {
	public static void main(String[] args) {
		InputStream in = null;
		OutputStream out = null;
		
		in = System.in;
		out = System.out;
		
		int len = -1;
		
		//创建一个字节数组,大小为1024,用来存读到的一串字节
		byte[] buf = new byte[1024];
		
		try {
			len = in.read(buf); //每次读一串字节
			out.write(buf,0,len); //读几个字节,写几个字节
			out.flush(); 
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

这里读取一串字节,输出一串字节

在这里插入图片描述

代码演示三:

如果想让程序一直读取和写出,那么可以加入while循环

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Test02 {
	public static void main(String[] args) {
		InputStream in = null;
		OutputStream out = null;
		
		in = System.in;
		out = System.out;
		
		int len = -1;
		
		//创建一个字节数组,大小为1024,用来存读到的一串字节
		byte[] buf = new byte[1024];
		
		try {
			//这个是一次读取多个字节
//			while((len = in.read(buf)) != -1) {
//				out.write(buf,0,len);
//			}
			
			//这个是每次读取一个字节
			while((len = in.read()) != -1) {
				out.write(len);
			}
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

在这里插入图片描述

使用字节数组

简介:
使用字节流,从字节数组中读取数据,以及向字节数组中写数据。
其中
java.io.ByteArrayInputStream 负责从字节数组中读取数据j
ava.io.ByteArrayOutputStream 负责把数据写入到字节数组中

实例
代码演示一:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

public class Test03 {
	
	public static void main(String[] args) {
		//1、声明流
		InputStream in = null;
		OutputStream out = null;
		
		//2、创建流
		byte[] arr = "hello".getBytes(); //getByte()方法用来转换为字节
	
		in = new ByteArrayInputStream(arr); //将存储转换为了字节的hello的字节数组放到对象in中
		out = new ByteArrayOutputStream();
		
		//3、使用流
		int len = -1;
		byte[] buf = new byte[1024];
		
		try {
			//读取对象in中的内容,并存放到buf字节数组中,返回len,len表示读到的字节个数
			len = in.read(buf); 
			
			//len就是数组buf的长度,写出字节数组buf中内容,放到对象out中
			out.write(buf,0,len);
			out.flush();
			
			//ByteArrayOutputStream中的toByteArray方法,可以将写入到out对象中的数据返回
			byte[] toByteArray = ((ByteArrayOutputStream) out).toByteArray();
			//因为toByteArray()方法只在ByteArrayOutputStream中有,InputStream中没有,所以要进行强转
			
			System.out.println(Arrays.toString(toByteArray)); //将字符中各个字母对应的字节输出
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

在这里插入图片描述

管道字节流

使用字节流,可以从管道中读取数据,向管道中写数据。
java.io.PipedInputStream负责从管道中读取数据
java.io.PipedOutputStream负责将数据写入到管道中

一般可以在一个线程中,使用管道输出流,将数据写入到管道中,在另一个线程中,读取管道中的数据。

使用示意图
在这里插入图片描述

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class Test04 {
	
	public static void main(String[] args) {
		
		//声明流
		PipedInputStream in = null;
		PipedOutputStream out = null;
		
		//创建流
		in = new PipedInputStream();
		out = new PipedOutputStream();
		
		//使用流
		try {
			//管道对接
			in.connect(out);
			
			//创建线程对象
			Thread t1 = new WriteThread(out);
			Thread t2 = new ReadThread(in);
			
			//启动两个线程
			t1.start();
			t2.start();
			
			t1.join();
			t2.join();
			
			System.out.println("线程运行结束");
			
		} catch (IOException | InterruptedException e) {
			e.printStackTrace();
		}
	}
}

//创建线程1:将数据写到管道中
class WriteThread extends Thread{
	//定义一个OutputStream类型的变量
	private OutputStream out;
	
	//创建构造器
	public WriteThread(OutputStream out) {
		this.out = out;
	}

	@Override
	public void run() {
		
		byte[] arr = "hello world".getBytes();
		
			try {
				for(int i = 0; i<arr.length; i++) {
					out.write(arr[i]);
					out.flush();
					
					//让线程每输出一次就睡上1s
					Thread.sleep(1000);
				}
			} catch (IOException | InterruptedException e) {
				e.printStackTrace();
			} finally{
				if(out!= null) {
					try {
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
	}
	
	
}

//创建线程2:从管道中读取数据
class ReadThread extends Thread{
	//定义一个InputStream类型的变量
	private InputStream in;
	
	//创建构造器
	public ReadThread(InputStream in) {
		this.in = in;
	}

	@Override
	public void run() {
		int data = -1;
		
		try {
			//重复每次读取一个字节
			while((data = in.read()) != -1) {
				//输出读到的字节
//				System.out.print(data + " ");
				System.out.write(data);
				System.out.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

System.out.write(data);
System.out.print(data);
注意这两句代码的区别

对于System.out.write(data);
结果是一个字母一个字母一次输出的,中间间隔1s,输出结果如下:
在这里插入图片描述
对于System.out.print(data);
结果是一个一个的输出字母对应的字节,中间间隔1s,如下:
在这里插入图片描述

文件字节流

简介

java.io.File 类,是java中对文件和目录的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

File常用的构造器
public class File{        
//通过指定文件路径,创建File对象。    
public File(String pathname){...} 
  
//通过父路径名和子路径名,创建File实例。    
public File(String parent, String child){...}  
  
//通过父路径名和子路径名,创建File实例。        
public File(File parent, String child){...}    
}

parent对象表示了一个目录,而file对象表示了一个文件
如:

String parent = "D:/com";
String child = "Hello.java";
File file = new File(parent,child);

如:

String parentPath = "D:/com";
File parent = new File(parentPath);

String child = "Hello.java";
File file = new File(parent,child);

用file对象表示一个文件或目录

File既可以表示一个文件,也可以表示一个目录
Windows系统中的路径分隔符,使用/或者\都可以,但是\需要转义一下

如:

//String pathname = "D:\\com\\Hello.java";  
String pathname = "D:/com/Hello.java";  //D盘下面有目录com,目录下面有文件Hello.java
File file = new File(pathname);
常用方法

方法:
public String getAbsolutePath(),返回file的绝对路径
public String getPath(),返回创建file对象时传入的路径参数(有可能是相对路径)
public String getName(),返回file的名字
public long length(),file如果表示文件,则返回文件内容的长度(字节个数)


String pathname = "D:/com/Hello.java";
File file=newFile(pathname);
System.out.println(file.getAbsoluteFile());
System.out.println(file.getPath());
System.out.println(file.getName());
System.out.println(file.length())

结果如下
在这里插入图片描述
为甚么长度为0,因为文件中没有内容呀

程序中创建文件或目录使用相对路径,这个相对路径所相对的位置,是将来运行代码是执行java命令的路径,无论是在eclipse中还是手动编译都是这样的


方法:
public boolean exists(),判断此文件或目录是否真的存在
public boolean isDirectory() ,判断File表示的是否是一个目录
public boolean isFile(),判断file表示的是否是一个文件


例如:

String pathname = "hello.java";
File file = new File(pathname);
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());

在这里插入图片描述


方法:
public boolean createNewFile(),创建一个新文件
public boolean delete() ,删除文件或目录
public boolean mkdir() ,创建一个目录
public boolean mkdirs() ,创建多级目录


import java.io.File;
import java.io.IOException;

public class Test05 {
	public static void main(String[] args) {
		String pathname="src/a/b/c"; //在项目的src文件夹下面创建一个目录src/a/b/c
		File dir = new File(pathname);
		//当这个目录不存在,则创建,注意返回值是boolean类型的值
		if(!dir.exists()) {
			System.out.println(dir.getPath() + "目录是否创建成功" + dir.mkdirs());
		}
		
		
		File file = new File(dir,"hello.java");
		//当src/a/b/c/hello.java这个文件不存在,则创建,返回boolean类型的值
		if(!file.exists()) {
			try {
				System.out.println(file.getName() + "文件是否创建成功" + file.createNewFile());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

结果:
在这里插入图片描述


方法:
public String[] list(),返回目录中所有的子文件或子目录,返回值是String类型数组
public File[] listFiles(),返回目录中所有的子文件或子目录,返回值是File类型数组


例如:

public static void main(String[] args) {
		String pathname = "."; //.表示当前路径
		
		File dir = new File(pathname);
		
		String[] arr = dir.list();
		System.out.println(Arrays.toString(arr));
	}

结果:输出当前目录中的子文件或子目录
在这里插入图片描述

public static void main(String[] args) {
		String pathname = "."; //.表示当前路径
		
		File dir = new File(pathname);
		
		File[] file = dir.listFiles();
		for(int i = 0; i<file.length; i++)
		System.out.println(file[i]);
	}

在这里插入图片描述

使用字节流从文件中读取数据以及向文件中写数据

java.io.FileInputStream,负责从文件中读取数据
java.io.FileOutputStream,负责把数据写入到文件中

代码演示:
设有个文件a.txt,里面的内容为hello world,路径为src/Test/test1/files/a.txt,将a.txt文件的内容输出到另一个文件b.txt中。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Test06 {
	public static void main(String[] args) {
		InputStream in = null;
		OutputStream out = null;
		
		File file1 = new File("src/Test/test1/files/a.txt"); 
		File file2 = new File("src/Test/test1/files/b.txt"); //b.txt文件的路径(自己设置)
		
		try {
			in = new FileInputStream(file1); //将file1放到in对象中
			out = new FileOutputStream(file2); //将out对象中的数据输出到file2文件中
			
			int len = -1;
			byte[] buf = new byte[1024];
			while((len = in.read(buf))!=-1) {
				out.write(buf,0,len);
			}
			
			out.flush();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

在这里插入图片描述
文件b.txt中的内容也和a.txt一样
在这里插入图片描述

关于read每次读取的长度怎么算的问题

在这里插入图片描述

关于这行代码:while((len = read()) != -1),为什么每次都是不等于-1
答:查看源码可知,read方法返回-1时,表示读完了

文件输出流中还有以下构造器:

public FileOutputStream(String name, boolean append)

作用是用来追加文件内容的,当后面布尔类型的参数是true,则在指定文件后面追加内容

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值