所谓IO就是输出输出(input/output)。一般的理解都是相对于计算机而言的输入输出。
比如:
输出设备:显示器,耳机,音响,打印机.....
输入设备:键盘,鼠标,麦克风......
上面的所有的输入输出都是相对于计算机而言的。
我们这里说的输入输出是相对于内存而言的
-
数据读取到内存就是输入。
-
数据从内存写出就是输出。
输入输出的原理:
输出的数据流称之为输出流,输入的数据流称之为输入流。
面试题:java的流是如何分类的?
按照方向分为:输入流和输出流。
按照处理方式:分为字节流和字符流。
File类
在java.io包下有一个File。 这个类的所有对象都表示一个文件。(一个File类的对象就是一个文件或者文件夹的抽象表示)
内部的常量
-
static String pathSeparator 与系统相关的路径分隔符字符,为方便起见,表示为字符串。
-
static char pathSeparatorChar 与系统相关的路径分隔符。
-
static String separator 与系统相关的默认名称 - 分隔符字符,以方便的方式表示为字符串。
-
static char separatorChar 与系统相关的默认名称分隔符。
前面两个常量是路径相关的分隔符。后面两个是名称分隔符。
以前在linux中分隔符是 "/" 在windows中是 "\"
构造方法
-
File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例。
-
File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
-
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。
-
File(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。
相对路径和绝对路径。
所谓相对路径,就是相对于当前的文件而言,目标的位置。
所谓绝对路径,就是从盘符开始的路径。
一般建议使用相对路径。
public class HelloFile{
public static void main(String [] args){
// 我们这里先使用绝对路径
File file = new File("E:\\202201\\第二阶段\\01.java高级\\0317\\temp\\hello.txt");
// 当然我们也可以使用相对路径表示
File file1 = new File("s.txt");
file1.createNewFile();
// 使用父子文件创建(父路径字符串,子路径字符串)
File file2 = new File("E:\\202201\\第二阶段\\01.java高级\\0317\\temp","hello.txt");
File file3_1 = new File("E:\\202201\\第二阶段\\01.java高级\\0317\\temp");
File file3 = new File(file3_1,"hello.txt");
}
}
File类的一些API:
获取信息的---
-
exists() 测试此抽象路径名表示的文件或目录是否存在。
-
getName() 返回由此抽象路径名表示的文件或目录的名称。
-
getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
-
getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。
-
getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。
-
isDirectory() 测试此抽象路径名表示的文件是否为目录。
-
isFile() 测试此抽象路径名表示的文件是否为普通文件。
-
lastModified() 返回此抽象路径名表示的文件上次修改的时间。
-
length() 返回由此抽象路径名表示的文件的长度。
-
list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。
-
listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。
案例:
public class FileTest1 {
public static void main(String[] args) {
// 文件
String path = "E:\\202201\\第二阶段\\01.java高级\\0317\\temp\\hello.txt";
File file = new File(path);
// 文件夹
String path1 = "E:\\202201\\作业\\20220315";
File file1 = new File(path1);
System.out.println("------------------");
System.out.println("文件名:"+file.getName());
System.out.println("文件夹名:"+ file1.getName());
System.out.println("文件的绝对路径"+file.getAbsolutePath());
System.out.println("文件夹的绝对路径:"+file1.getAbsolutePath());
System.out.println("文件是否存在:"+file.exists());
System.out.println("文件夹是否存在:"+file1.exists());
System.out.println("文件的父文件夹路径:"+file.getParent());
System.out.println("文件是否是文件:"+file.isFile());
System.out.println("文件是否是文件夹:"+file.isDirectory());
System.out.println("文件的最后修改时间:"+new Date(file.lastModified()));
System.out.println("文件夹的最后修改时间:"+new Date(file1.lastModified()));
System.out.println("文件长度:"+file.length()+" 字节");
System.out.println("-- 文件夹的length是无效 --");
System.out.println("文件夹长度:"+file1.length()+" 字节");
// list是获取当前文件夹下的所有文件和文件夹的名字的字符串数组
String[] names = file1.list();
System.out.println(Arrays.toString(names));
// listFile获取File类型的数组
File[] files = file1.listFiles();
for (File f: files){
System.out.println(f.getAbsolutePath()+":"+f.length());
}
}
}
我们查看文件的大小,从现在开始都是看字节:
1B byte = 8bit
1K = 1024B
1M = 1024K
1G = 1024M
1T = 1024G
1P = 1024T
创建删除判断的一些API----
-
canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件。
-
canRead() 测试应用程序是否可以读取由此抽象路径名表示的文件。
-
canWrite() 测试应用程序是否可以修改由此抽象路径名表示的文件。
-
delete() 删除由此抽象路径名表示的文件或目录。
-
boolean mkdir() 创建由此抽象路径名命名的目录。
-
boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
-
createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
异常问题:
案例:
public class FileTest2 {
public static void main(String[] args) throws IOException {
File file = new File("file-list");
// 创建这个文件夹
// 文件不存在或者文件不是文件夹
if(!file.exists() || !file.isDirectory()){
// 创建它
file.mkdir();
}
// 创建一组文件夹
File file1 = new File("E:\\202201\\第二阶段\\01.java高级\\0317\\temp\\stu\\info\\names");
if(!file1.exists() || !file1.isDirectory()){
// 创建它 前往不要忘记s
boolean mkdirs = file1.mkdirs();
System.out.println(mkdirs);
}
// 创建文件
File file2 = new File("file-list\\names.txt");
if(!file2.exists() || !file2.isFile()){
file2.createNewFile();// 这里会有异常,直接抛出
}
// 删除文件
File file3 = new File("s.txt");
boolean delete = file3.delete();
System.out.println(delete);
}
}
字节输入流InputStream
所有的字节输入流的老祖宗就是 java.io.InputStream。
字节流的特点:是以字节的方式处理数据
这个类是一个抽象类,其目的就是为了让其他的类实现。所以我们都是使用这个类的子类。
浏览API:
-
int available() 返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 (这个方法对于网络流是无效的)
-
void close() 关闭此输入流并释放与流相关联的任何系统资源。
-
void mark(int readlimit) 标记此输入流中的当前位置。
-
boolean markSupported() 测试这个输入流是否支持 mark和 reset方法。
-
abstract int read() 从输入流读取数据的下一个字节。
-
int read(byte[] b) 从输入流读取一些字节数,并将它们存储到缓冲区 b 。
-
int read(byte[] b, int off, int len) 从输入流读取最多 len字节的数据到一个字节数组。
-
void reset() 将此流重新定位到上次在此输入流上调用 mark方法时的位置。
-
long skip(long n) 跳过并丢弃来自此输入流的 n字节数据。
FileInputStream--InputStream的实现类
构造方法:
-
FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
-
FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
API:就是参考父类的API。
使用:按照单个的字节读取数据。
public class FileInputStreamTest {
public static void main(String[] args) {
// 通过文件名构建一个字节输入流
InputStream in = null;
try {
// 创建输入流
in = new FileInputStream("names.txt");
int len = in.available();// 可读去字节的长度
System.out.println("数据长度:"+len);
// 准备一个len长度的字节数组
byte [] buff = new byte[len];
int index = 0;
// 准备读取数据
// 读取一个字节的数据
//int read = in.read();
// 循环读取
int data = -1;
while((data = in.read())!=-1){
//System.out.println(data);
// 将数据放入字节数组
buff[index++] = (byte)data;
// index++;
};
// 字节数组中的数据
System.out.println(Arrays.toString(buff));
// 根据字节数组创建一个字符串
String str = new String(buff);
System.out.println(str);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
输入流还提供了一些API可以按组读取数据
按组读取数据的API-1:
public class FileInputStreamTest1 { public static void main(String[] args) { // 通过文件名构建一个字节输入流 InputStream in = null; try { // 根据文件创建输入流 File file = new File("names.txt"); in = new FileInputStream(file); // 按组读取 // int read(byte[] b)读取最多b.length字节的数据到字节数组 返回值实际读取的长度。如果到达文件末尾返回-1 // 准备一个字节数组 byte [] buff = new byte[500]; // 读取数据到缓冲数组 int len = in.read(buff); System.out.println("实际读取的长度是:"+len); String str = new String(buff);// 根据整个数组创建字符串 System.out.println(str); System.out.println("--------------"); // 根据实际读取的字节长度创建字符串 //(字节数组,偏移量,使用长度) String info = new String(buff,0,len); System.out.println(info); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(in!=null){ in.close(); } }catch (IOException ex){ ex.printStackTrace(); } } } }
按组读取数据API-2:
public class FileInputStreamTest2 {
public static void main(String[] args) {
// 通过文件名构建一个字节输入流
InputStream in = null;
try {
// 根据文件创建输入流
File file = new File("names.txt");
in = new FileInputStream(file);
// 按组读取
// read(byte[] b, int off, int len) 读取数据,从还数组的off位置开始放数据,放len长度
// 准备一个字节数组
byte [] buff = new byte[500];
// 读取数据
int len = in.read(buff, 100, 355);
// 创建字符串
String str = new String(buff,100,len);
System.out.println(str);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
实际应用中读取文件:
上面的按组读取的例子中,都是一次性将所有的数据读取到内存中。
如果一个文件有一个T,我们肯定无法一次性读取到内存。
解决方案:按组,循环读取,重复使用缓冲数组。
看程序:
public class FileInputStreamTest3 {
public static void main(String[] args) {
// 通过文件名构建一个字节输入流
InputStream in = null;
try {
// 根据文件创建输入流
File file = new File("data.txt");
in = new FileInputStream(file);
// 准备一个缓冲数组
byte [] buff = new byte[1024];
int len = -1;
long total = 0;
// 将每次读取到数组中的实际的长度赋值给len,如果len是-1表示已经到达文件末尾,结束读取
while((len = in.read(buff))!=-1){
total += len;
// 输出缓冲数组中的数据
String str = new String(buff,0,len);
System.out.print(str);
}
System.out.println("\n读取的数据的总长度是:"+total);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字节输出流OuputStream
OuputStream是所有的字节输出流的老祖宗。
API:
-
void close() 关闭此输出流并释放与此流相关联的任何系统资源。
-
void flush() 刷新此输出流并强制任何缓冲的输出字节被写出。
-
void write(byte[] b) 将 b.length字节从指定的字节数组写入此输出流。
-
void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
-
abstract void write(int b) 将指定的字节写入此输出流。
FileOutputStream--OutputStream的实现类
-
FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
-
FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
-
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。
-
FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
api就是父类中的方法。
构造方法案例:
public class FileOutputStreamTest1 { public static void main(String[] args) { // 申明字节输出流 OutputStream out = null; try{ // 创建流 // 根据文件创建输出流 File file = new File("st.txt"); // 从文件开头开始写数据。 覆盖了原有的数据 out = new FileOutputStream(file); // 根据文件创建输出流 // append: // true 从文件的末尾开始写,续在文件原有数据的后面。 // false 从文件的开头开始写,覆盖原有数据 out = new FileOutputStream(file,true); // 根据文件名创建输出流 // 从文件开头开始写数据。 覆盖了原有的数据 out = new FileOutputStream("hell.txt"); // append: // true 从文件的末尾开始写,续在文件原有数据的后面。 // false 从文件的开头开始写,覆盖原有数据 out = new FileOutputStream("hell.txt",true); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null){ out.close(); } }catch (IOException ex){ ex.printStackTrace(); } } } }
tips:如果我们创建的输出流指向的文件不存在,当我们写出数据的时候会自动创建这个文件。
三个写数据的API:
输出一个字节:
public class FileOutputStreamTest1 { public static void main(String[] args) { // 申明字节输出流 OutputStream out = null; try{ //-------------------------------------------------- // 创建流 out = new FileOutputStream("hell.txt",true);// *** // 准备一个字符串 String str = "吃鸡全靠苟"; // 将字符串转换为字节数组 byte[] bytes = str.getBytes();//??? // 循环的将字节数组的内容写入文件 for(byte b : bytes) { out.write(b);// **** } //------------------------------------------------- }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null){ out.close(); } }catch (IOException ex){ ex.printStackTrace(); } } } }
写出一个字节数组:
public class FileOutputStreamTest2 { public static void main(String[] args) { // 申明字节输出流 OutputStream out = null; try{ //------------------------ // 创建流 // 根据文件创建输出流 File file = new File("st1.txt"); // 从文件开头开始写数据。 覆盖了原有的数据 out = new FileOutputStream(file); // 准备一个字符串 String str = "吃鸡全靠苟"; // 将字符串转换为字节数组 byte[] bytes = str.getBytes(); // 直接写出一个字节数组 out.write(bytes); //------------------ }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null){ out.close(); } }catch (IOException ex){ ex.printStackTrace(); } } } }
写出字节数组的一部分:
public class FileOutputStreamTest3 {
public static void main(String[] args) {
// 申明字节输出流
OutputStream out = null;
try{
// 创建流
// 根据文件创建输出流
File file = new File("st2.txt");
// 从文件开头开始写数据。 覆盖了原有的数据
out = new FileOutputStream(file);
// 准备一个字符串
String str = "吃鸡全靠苟";
// 将字符串转换为字节数组
byte[] bytes = str.getBytes();
// 直接写出一个字节数组的一部分
// (字节数组,偏移量,长度) 从字节数组的偏移量位置开始写数据,写长度这么常
out.write(bytes,0,6);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
一个需求:
从控制台输入一个商品的信息,将商品信息写入商品文件。文件中一行存储一个商品信息。
/**
* 从控制台输入一个商品的信息,将商品信息写入商品文件。文件中一行存储一个商品信息。
*/
public class Ex1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
OutputStream out = null;
try{
// 创建输出流
out = new FileOutputStream("products.txt",true);
// 提示输出商品的内容
String op = "";
do{
System.out.println("请输入商品编号:");
String itemId = sc.next();
System.out.println("请输入商品名称:");
String itemName = sc.next();
System.out.println("请输入商品价格:");
String itemPrice = sc.next();
// 用户输入的数据要写入文件
String info = itemId+"&"+itemName+"&"+itemPrice+"\n";
// 将字符串写入文件
out.write(info.getBytes());
System.out.println("退出请输入(exit):");
op = sc.next();
}while(!op.equalsIgnoreCase("exit"));
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
再来一个需求:
将上一个需求写文件的商品,读取出来,使用[9527,尤克里里,1525] 形式输出到控制台。
/**
* 将上一个需求写文件的商品,读取出来,使用[9527,尤克里里,1525] 形式输出到控制台。
*/
public class Ex2 {
public static void main(String[] args) {
// 读取所有的数据,再进行分割处理
InputStream in = null;
try{
// 创建输入流
in = new FileInputStream("products.txt");
// 循环的读取所有的内容放在一个StringBuilde中
StringBuilder sb = new StringBuilder();
// 准备读取
int len = -1;// 每次读取的实际长度
byte [] buff = new byte[1024]; // 缓冲数组
while((len = in.read(buff))!=-1){
// 将数据转换为字符串
String str = new String(buff,0,len);
// 将字符串键入StringBuilder
sb.append(str);
}
System.out.println(sb.toString());
// 完整的商品信息字符串
String ps = sb.toString();
String[] rows = ps.split("\n");// 使用换行符拆分
for (String row : rows){
// 9530&路基亚打电脑&150
String[] attrs = row.split("&");
String info = "["+attrs[0]+","+attrs[1]+","+attrs[2]+"]";
System.out.println(info);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
文件拷贝:
文件拷贝流程:
我们来拷贝一个压缩文件:
public class CopyFileTest {
public static void main(String[] args) {
InputStream in = null;
OutputStream out = null;
try {
// -------------------------------------------
// 创建输入流
in = new FileInputStream("java-SE.rar");
// 创建输出流
out = new FileOutputStream("java-SE-V1.rar");
// 使用字节流读取数据
int len = -1;
byte [] buff = new byte[1024];
while((len = in.read(buff))!=-1){
// 写出数据
out.write(buff,0,len);
}
// ---------------------------------------------
}catch (IOException e){
e.printStackTrace();;
}finally {
try{
if(in != null){
in.close();
}
if(out != null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字符流,就是按照字符处理文件的。
字节流,按照字节处理文件。可以处理所有的文件。一般使用中,字节流偏多。
文本文件:纯文本文件。不保存任何的格式,文件头之类的信息。
字符流往往是用来处理纯文本文件或者网络上的文本信息。
字符输入流Reader
Reader本身是一个抽象类
API:
-
abstract void close() 关闭流并释放与之相关联的任何系统资源。
-
void mark(int readAheadLimit) 标记流中的当前位置。
-
boolean markSupported() 告诉这个流是否支持mark()操作。
-
int read() 读一个字符
-
int read(char[] cbuf) 将字符读入数组。
-
abstract int read(char[] cbuf, int off, int len) 将字符读入数组的一部分。
-
int read(CharBuffer target) 尝试将字符读入指定的字符缓冲区。
-
boolean ready() 告诉这个流是否准备好被读取。
-
void reset() 重置流。
-
long skip(long n) 跳过字符
FileReader --Reader类的子类
也是我们常用的字符输入流. (FileInputStream)
构造方法
-
FileReader(File file) 创建一个新的 FileReader ,给出 File读取。
-
FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
API:所有的API都是来自于父类。
使用:
public class FileReaderTest1 {
public static void main(String[] args) {
FileReader reader = null;
try{
// 创建reader
reader = new FileReader("names.txt");
// 读取一个字符(这里返回的是整形)
int read = reader.read();
// 将整形强制转换为字符型
char ch = (char)read;
System.out.println(ch);
// 读取一组字符
// 准备一个字符缓冲区
char [] buff = new char[20];
reader.read(buff);
System.out.println(Arrays.toString(buff));
// 读取一部分字符,放在一个字符数组的某一部分
int len = reader.read(buff,3,10);
System.out.println(Arrays.toString(buff));
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(reader!=null)
reader.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字符输出流Writer
API:
-
Writer append(char c) 将指定的字符附加到此作者。
-
Writer append(CharSequence csq) 将指定的字符序列附加到此作者。
-
Writer append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。
-
abstract void close() 关闭流,先刷新。
-
abstract void flush() 刷新流。
-
void write(char[] cbuf) 写入一个字符数组。
-
abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
-
void write(int c) 写一个字符
-
void write(String str) 写一个字符串
-
void write(String str, int off, int len) 写一个字符串的一部分。
FileWriter--在字节流中对应:FileOutputStream
构造方法:
-
FileWriter(File file) 给一个File对象构造一个FileWriter对象。
-
FileWriter(File file, boolean append) 给一个File对象构造一个FileWriter对象。
-
FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。
-
FileWriter(String fileName, boolean append) 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
没有自己的特殊API。全部是父类的API。
案例:
public class FileWriterTest1 {
public static void main(String[] args) {
FileWriter writer = null;
try{
// 创建writer (当写入数据的时候,会自动创建这个文件)
writer = new FileWriter("rzs.txt",true);
// 可以写数据了
writer.write("木叶的旗木五五开老师!!!\n");
// 也可以写一个字符
writer.write(96);
writer.write(97);
// 可以写一个字符数组
char [] chs = {'宇','智','波','五','五','开','\n'};
writer.write(chs);
// 写字符数组的一部分
writer.write(chs,3,4);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(writer!=null)
writer.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
下载一个HTML文件
package com.qidian.spider;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class DownloadHtml {
private static String path = "http://search.dangdang.com/?key=java&act=input";
public static void main(String[] args) {
// 申明
InputStream in = null;
OutputStream out = null;
try {
// 连接网络-------------
URL url = new URL(path);
HttpURLConnection urlConnection =
(HttpURLConnection) url.openConnection();
urlConnection.connect();
// 得到指向这个网页的输入流
in = urlConnection.getInputStream();
out = new FileOutputStream("hehe.html");
// 拷贝的过程
int len = -1;
byte [] buff = new byte[1024];
while((len = in.read(buff))!=-1){
out.write(buff,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null)
in.close();
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字符流的文件拷贝
package com.qidian.demo1;
import java.io.*;
public class CopyFileTest {
public static void main(String[] args) {
Reader reader = null;
Writer writer = null;
try {
reader = new FileReader("hehe.html");
writer = new FileWriter("haha.html");
int len = -1;
char [] buff = new char[256];
while((len = reader.read(buff))!=-1){
writer.write(buff,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(reader!=null)
reader.close();
if(writer!=null)
writer.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
缓冲字符输入流:BufferedReader
构造方法:
-
BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
-
BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。
在缓冲流中用到了“装饰设计模式”。 使用BufferedReader包裹一个Reader,实现缓冲。
其中大部分API和Reader没啥区别。有一个特殊的API:
-
String readLine() 读一行文字。
案例:
public class BufferedReaderTest {
public static void main(String[] args) {
BufferedReader bufReader = null;
Reader reader = null;// 这个reader对象就是让BufferedReader包裹的对象
try{
// 先创建reader
reader = new FileReader("names.txt");
// 创建缓冲输入流 (任何需要父类型的地方,都可以使用子类型代替)
bufReader = new BufferedReader(reader);
// 按行读取(读取不到数据的时候,返回null)
// String s = bufReader.readLine();
// System.out.println(s);
String line = null;
while((line = bufReader.readLine())!=null){
System.out.println(line);
};
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(bufReader!=null)
bufReader.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
缓冲字符输出流--BufferedWriter
构造方法:
-
BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
-
BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
创建BufferedWriter的时候需要包裹一个Writer对象
特殊的API:
-
void newLine() 写一行行分隔符。
缓冲输出流要注意的问题:
当调用了缓冲输出流的write方法之后,数据可能只是写在缓冲区,并没有实际写到输出流。理论上当关闭输出流的时候,所有缓冲区的数据会刷出。 一般建议我们手动调用 flush方法刷出缓冲区数据。
案例:
public class BufferedWriterTest {
public static void main(String[] args) {
BufferedWriter bufWriter = null;
Writer writer = null;
try{
// 创建writer
writer = new FileWriter("rzs.txt");
// 创建BufferedWriter
bufWriter = new BufferedWriter(writer);
// 写数据
// 循环写
for(int i = 0;i < 100;i++){
// 缓冲流在调用了write方法之后,不一定把数据写出去了。
// 数据可能仅仅在缓冲区。
bufWriter.write("[adminName:'鸣人"+i+"',adminPass:'123456']");
// 写入一个换行符
bufWriter.newLine();
}
// 刷出缓冲区的数据
//bufWriter.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
// 缓冲流在关闭的时候,理论上会将缓冲区所有的数据全部写出去
try{
if(bufWriter!=null)
bufWriter.close();
if(writer!=null)
writer.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
缓冲字节输入流--BufferedInputStream
构造方法
-
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
-
BufferedInputStream(InputStream in, int size) 创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。
其他的API,不需要说明,和FileOutputStream用法完全一致。只不过提高了底层的效率。
缓冲字节输出流:BufferedOutputStream
构造方法
-
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
-
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
API........你就把它当FileOutputStream使用就行了。
我们使用 缓冲(高效)字节流完成一个文件拷贝的过程。
案例:
public class CopyFileTest {
public static void main(String[] args) {
InputStream in = null;
OutputStream out = null;
BufferedInputStream bufIn = null;
BufferedOutputStream bufOut = null;
try{
// 创建4个流。
// 输入流
in = new FileInputStream("java-SE.rar");
bufIn = new BufferedInputStream(in);
// 输出流
out = new FileOutputStream("java-SE-V1.rar");
bufOut = new BufferedOutputStream(out);
// 拷贝过程。。。。。。
int len = -1;
byte [] buff = new byte[1024];
while((len = bufIn.read(buff))!=-1){
bufOut.write(buff,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
// 输入流先关闭里面的流,在关闭外面的流
if(in!=null)
in.close();
if(bufIn!=null)
bufIn.close();
// 输出流先关闭外面的流,再关闭里面的流
if(bufOut!=null)
bufOut.close();
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
转换流目的:将字节流转换为字符流。 能不能将字符流转换为字节流?NO
转换输入流:InputStreamReader
构造方法:
-
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。
-
InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。
-
InputStreamReader(InputStream in, CharsetDecoder dec) 创建一个使用给定字符集解码器的InputStreamReader。
-
InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。
API和Reader一致。
转换输出流:OutputStreamWriter
-
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
-
OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter。
-
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建一个使用给定字符集编码器的OutputStreamWriter。
-
OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。
API和writer完全一致。
案例:转换输入流。
public class InputStreamReaderTest {
public static void main(String[] args) {
InputStream in = null;
InputStreamReader reader = null;
BufferedReader bufReader = null;
try{
// 创建这些流
// 创建字节流
in = new FileInputStream("names.txt");
// 将字节流转换字符流
reader = new InputStreamReader(in);
// 将字符流包裹为缓冲字符流
bufReader = new BufferedReader(reader);
// 循环读取数据
String line = null;
while((line = bufReader.readLine())!=null){
System.out.println(line);
};
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(bufReader!=null)
bufReader.close();
}catch (IOException ex){ex.printStackTrace();}
}
}
}
字节输出流转换为字符输出流:
案例:
public class OutputStreamWriterTest { public static void main(String[] args) { OutputStream out = null; OutputStreamWriter writer = null; try { // 创建字节流 out = new FileOutputStream("hehe.txt"); // 将字节流包裹成字符流 writer = new OutputStreamWriter(out); // 写字符串 writer.write("一个字符串\n"); writer.write("下午两点上课\n"); writer.write("上完可,做好防护,去做核算"); writer.flush(); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null){ out.close(); } }catch (IOException ex){ ex.printStackTrace(); } } } }
对象流
序列化
为了方便数据的存储或者传输,将数据或者对象转换为可存储或者传输的形式的过程称之为序列化。相反就是反序列化。
例子:在线购物,卖家给你打包的过程就是序列化。你拆包裹的过程就是反序列化。
项目中的:
将一个对象转换为json输出到客户端。将对象转换为json就是序列化。
将JSON转换对象的过程就是反序列化。
对象流就是专门用来对对象进行序列化和反序列化。
对象输出流:ObjectOutputStream(写出)
专门用来序列化对象的。(卖家打包)
构造方法:
-
ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
对象输出流内部其实是包裹了一个字节输出流。
对象输出流的作用:
API:
-
void close() 关闭流。
-
void write(byte[] buf) 写入一个字节数组。
-
void write(byte[] buf, int off, int len) 写入一个子字节数组。
-
void write(int val) 写一个字节。
-
void writeBoolean(boolean val) 写一个布尔值。
-
void writeByte(int val) 写入一个8位字节。
-
void writeBytes(String str) 写一个字符串作为字节序列。
-
void writeChar(int val) 写一个16位的字符。
-
void writeChars(String str) 写一个字符串作为一系列的字符。
-
void writeDouble(double val) 写一个64位的双倍。
-
void writeFloat(float val) 写一个32位浮点数。
-
void writeInt(int val) 写一个32位int。
-
void writeLong(long val) 写一个64位长
-
void writeShort(int val) 写一个16位短。
-
void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
对象输出流是写一个对象出去的操作。
java中都有哪些对象?
java中的对象真的很多,但是所有的对象统称为Object。其实对象流特意准备了几个方法,写这些对象。
-
writeXxxx(Xxx xxx) 这里的Xxxx表示任何一个基本类型。
-
writeObject(Object obj) 其余的类型全部使用Object。
案例1: 序列化基本类型
public class ObjectWriteTest1 { public static void main(String[] args) { OutputStream out = null; ObjectOutputStream objOut = null; try{ // 创建字节输出流 out = new FileOutputStream("ints.txt"); // 创建对象流 objOut = new ObjectOutputStream(out); // 写出一个整形 objOut.writeInt(12); objOut.writeDouble(12.5); // 刷出数据 objOut.flush(); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null) out.close(); }catch (IOException ex){ ex.printStackTrace();; } } } }
案例2: 序列化自定义类型
准备一个实体类:
public class User { private int id; private String name; }
测试:
出现异常。
这个异常产生的原因是因为ObjectOutputStream写的对象必须实现接口java.io.Serializable。否则无法序列化。 这个接口本身是没有任何的方法和成员的,它就是用来标记一个类的对象是否可以被序列化。
public interface Serializable { }
我们让我们的User类实现这个接口:
完整的测试代码:
public class ObjectWriteTest2 { public static void main(String[] args) { OutputStream out = null; ObjectOutputStream objOut = null; try{ // 创建字节输出流 out = new FileOutputStream("u.txt"); // 创建对象流 objOut = new ObjectOutputStream(out); // 创建一个User User u = new User(1,"就乌尔奇"); // 写出对象u objOut.writeObject(u); // 刷出数据 objOut.flush(); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null) out.close(); }catch (IOException ex){ ex.printStackTrace();; } } } }
对象输入流:ObjectInputStream(读取)
专门用来反序列化对象的。(买家拆包裹)
构造方法:
-
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。
ObjectInputStream会包裹一个InputStream
其他的API:
-
int read() 读取一个字节的数据。
-
int read(byte[] buf, int off, int len) 读入一个字节数组。
-
boolean readBoolean() 读取布尔值。
-
byte readByte() 读取一个8位字节。
-
char readChar() 读一个16位字符。
-
double readDouble() 读64位双倍。
-
float readFloat() 读32位浮点数。
-
int readInt() 读取一个32位int。
-
long readLong() 读64位长。
-
short readShort() 读取16位短。
-
Object readObject() 从ObjectInputStream读取一个对象。
有个小问题:无法判定是否到达了文件的末尾。
当使用ObjectInputStream读取数据的时候,如果出现了EOFException。说明到达了文件的末尾。
案例1: 读取上一小节写入文件的int和double。
public class ObjectInputTest1 { public static void main(String[] args) { InputStream in = null; ObjectInputStream objIn = null; try{ // 创建字节流 in = new FileInputStream("ints.txt"); // 创建对象流 objIn = new ObjectInputStream(in); // 反序列化数据 int i = objIn.readInt(); System.out.println(i); double d = objIn.readDouble(); System.out.println(d); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(objIn!=null) objIn.close(); }catch (IOException ex){ ex.printStackTrace(); } } } }
案例2:读取写入的自定义对象
public class ObjectInputTest2 { public static void main(String[] args) { InputStream in = null; ObjectInputStream objIn = null; try{ // 创建字节流 in = new FileInputStream("u.txt"); // 创建对象流 objIn = new ObjectInputStream(in); // 反序列化数据 Object o = objIn.readObject(); // 类型转换 User u = (User) o; System.out.println(u); }catch (Exception e){ e.printStackTrace(); }finally { try{ if(objIn!=null) objIn.close(); }catch (IOException ex){ ex.printStackTrace();; } } } }
一组对象的读写
情况1: 循环读写
循环的写入数据
public class ObjectWriteTest3 { public static void main(String[] args) { OutputStream out = null; ObjectOutputStream objOut = null; try{ // 创建字节输出流 out = new FileOutputStream("us.txt"); // 创建对象流 objOut = new ObjectOutputStream(out); // 创建一组User List<User> us = new ArrayList<>(); for (int i = 0;i< 100; i++){ us.add(new User(i+1,"鸣人"+i)); } // 循环的写出对象 for (User u : us) { objOut.writeObject(u); } // 刷出数据 objOut.flush(); }catch (IOException e){ e.printStackTrace(); }finally { try{ if(out!=null) out.close(); }catch (IOException ex){ ex.printStackTrace();; } } } }
循环的读数据
public class ObjectInputTest3 { public static void main(String[] args) { InputStream in = null; ObjectInputStream objIn = null; try{ // 创建字节流 in = new FileInputStream("us.txt"); // 创建对象流 objIn = new ObjectInputStream(in); // 反序列化数据 // 循环读取数据 // 知道循环次数的处理 // for (int i = 0;i < 100;i++){ // Object o = objIn.readObject(); // User u = (User) o; // System.out.println(u); // } // 不知道循环次数 while(true){ // 当到达文件的末尾的时候,会抛出异常EOFException Object o = objIn.readObject(); User u = (User) o; System.out.println(u); } }catch(EOFException e){ System.out.println("读取完成"); } catch (Exception e){ e.printStackTrace(); }finally { try{ if(objIn!=null) objIn.close(); }catch (IOException ex){ ex.printStackTrace();; } } } }
本文来自博客园,作者:{潇潇消消气},转载请注明原文链接:{https://www.cnblogs.com/xiaoxiaodeboke/}