概述
计算机中,存储一切数据(文本、图片、视频等),都是以二进制的形式进行的,最终都是一个一个的字节,所以使用流,进行传输数据的时候,也是一个一个的字节进行的。
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,则在指定文件后面追加内容