用于处理设备上数据。
流:可以理解数据的流动,就是一个数据流。IO流最终要以对象来体现,对象都存在IO包中。
流也进行分类:
①:输入流(读)和输出流(写)。
②:因为处理的数据不同,分为字节流和字符流。
字节流抽象基类:
InputSteam,OutputStream
字符流抽象基类:
Reader,Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。
字节流: 处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
1.那么为什么要有字符流呢?
因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是 字符流 。只要操作字符数据,优先考虑使用字符流体系。
2.如何判断是输入流,还是输出流?
以内存为参照,如果数据向内存流动,则是输入流,反之则为输出流.
二.字符流
字符流的抽象基类:
|---->Reader:字符输入流
|---->Writer:字符输出流
FileWriter
FileWriter是Writer的一个子类,用来写入字符文件的便捷类
构造方法:
|--->FileWriter(File file)
根据给定的 File 对象构造一个 FileWriter 对象。
|--->FileWriter(File file, boolean append)
根据给定的 File 对象构造一个 FileWriter 对象。
|--->FileWriter(FileDescriptor fd)
构造与某个文件描述符相关联的 FileWriter 对象。
|--->FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
|--->FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
可以实现文件的追加写入,第二个参数表示是否追加写入,默认是false,就会重头开始写入,如果是true,就会追加写入
常用方法:
|--->void write(String str):写入字符串。
|--->abstract void flush():刷新该流的缓冲
|--->abstract void close():关闭此流,但要先刷新它。
创建文件步骤:
1.创建流对象,建立数据存放文件,指定目的地
FileWriter fw = new FileWriter(“Test.txt”);
(注:该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖)
2.调用流对象的写入方法,将数据写入流
fw.write(“text”);
3.刷新流对象中的缓冲中的数据,将数据刷到目的地中。
//fw.flush();
4.关闭流资源,并将流中的数据清空到文件中。
fw.close();
5.close()和flush()的区别:
flush():将缓冲区的数据刷到目的地中后,流可以使用。
close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。
public class FileWriterDemo{
public static void main(String[] args){
FileWriter fw = null;
try{
fw = new FileWriter("Test.txt");
/* 1:创建了fw对象
* 2:底层调用了操作系统的资源创建了一个Test.txt文件
* 3:把fw对象指向Test.txt文件
*/
fw.write("text");//调用对象的写方法
fw.flush();刷新缓冲区
}
catch (IOException e){ //写入异常
System.out.println(e.toString());
}
finally{
if(fw!=null){
try{
fw.close();//释放资源,关闭之前,会自动去调用flush方法。
}
catch (IOException e){ //捕获关闭流异常
throw new RuntimeException("关闭流失败");
}
}
}
}
}
在文本后面追加操作示例:
public class FileWriterDemo2 {
public static void main(String[] args) throws IOException{
FileWriter fw = null;
try{
fw = new FileWriter("fw2.txt",true);
for (int x = 0; x < 4; x++){
fw.write(x + "hello\r\n");// 调用writer方法
}
}
catch (IOException e){
throw new RuntimeException("写入失败");
}
finally { // 释放资源
if(fw!=null){
try{
fw.close();
}
catch (IOException e2) {
throw new RuntimeException("关闭流失败");
}
}
}
}
}
FileReader
FileReader是Reader的一个子类,用来读取字符文件的便捷类
构造方法:
|--->FileReader(File file)
在给定从中读取数据的 File 的情况下创建一个新 FileReader。
|--->FileReader(FileDescriptor fd)
在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。
|--->FileReader(String fileName)
在给定从中读取数据的文件名的情况下创建一个新 FileReader。
两种读取方法:
|--->int read():读取单个字符。
|--->int read(char[] cbuf):将字符读入数组。
两种读取方法示例:
public class FileReaderDemo {
public static void main(String[] args)throws IOException {
//第一种方式read();
FileReader fr = new FileReader("D:\\Demo\\sdfs.txt");
//创建一个文件读取流对象,和指定名称的文件相关联。
//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
int ch = 0;
while((ch = fr.read())!=-1){
System.out.print((char)ch);
}
fr.close();
System.out.println("\n-------------");
//第二种方式read(char[]);
FileReader fr2 = new FileReader("D:\\Demo\\sdfs.txt");
int i = 0;
char[] ch2 = new char[1024];
while((i = fr2.read(ch2))!=-1){
System.out.print(new String(ch2,0,i));
}
fr2.close();
}
}
注意:
1.定义文件路径时,可以用“/”或者“\\”。
2.在创建一个文件时,如果目录下有同名文 件将被覆盖。
3.在读取文件时,必须保证该文件已存在, 否则出异常。
三.字符流缓冲区
缓冲区的出现提高了对数据的读写效率。要结合流才可以使用,在流的基础上对流的功能进行了增强。
对应类:
BufferedWriter (extends Reader)
BufferedReader (extends Writer)
这两个类同样具有内部缓存机制,并可以以行为单位进行输入/输出.
BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。该缓冲区中提供了一个跨平台的换行符:newLine();
本类方法(全部方法返回类型void):
|--->close():关闭此流,但要先刷新它。
|--->flush():刷新该流的缓冲。
|--->newLine():写入一个行分隔符。 可跨平台使用
|--->write(char[] cbuf, int off, int len):写入字符数组的某一部分。
|--->write(int c):写入单个字符。
|--->write(String s, int off, int len):写入字符串的某一部分。
public class BufferedWriterDemo{
public static void main(String[] args) throws IOException{
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("D:\\Demo\\sdfs.txt");
//为了提高字符写入流效率。加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<5; x++){
bufw.write("第"+x+"次写入");
bufw.newLine();
bufw.flush();//只要用到缓冲区,就要记得刷新。
}
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
特别注意:用到缓冲区,就要记得刷新。
BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
该缓冲区提供了一个一次读一行的方法 readLine(),方便于对文本数据的获取。当返回null时,表示读到文件末尾。(注:readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。)
常用方法:
|--->void close():关闭该流并释放与之关联的所有资源。
|--->int read():读取单个字符。
|--->int read(char[] cbuf, int off, int len):将字符读入数组的某一部分。
|--->String readLine():读取一个文本行。
class BufferedReaderDemo{
public static void main(String[] args) throws IOException{
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("D:\\Demo\\sdfs.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;//定义一个用作临时存储数据的变量
while((line=bufr.readLine())!=null)//返回值不为null就一直循环
{
System.out.print(line);
}
bufr.close();
}
}
自定义BufferedReader
自定义缓冲区,模拟BufferedReader
class MyBufferedReader extends Reader{
private Reader r;
MyBufferedReader(Reader r){
this.r = r;
}
//可以一次读一行数据的方法。
public String myReadLine()throws IOException{
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0){
return sb.toString();
}
return null;
}
/*
覆盖Reader类中的抽象方法。
*/
public int read(char[] cbuf, int off, int len) throws IOException{
return r.read(cbuf,off,len) ;
}
public void close()throws IOException{
r.close();
}
public void myClose()throws IOException{
r.close();
}
}
class MyBufferedReaderDemo{
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line=myBuf.myReadLine())!=null){
System.out.println(line);
}
myBuf.myClose();
}
}
LineNumberReader类
java.lang.Object
----java.io.Reader
----java.io.BufferedReader
-----java.io.LineNumberReader
LineNumberReader也是一个包装类,它在BufferedReader的基础上增加了一个记录行号的功能,而记录行号是在readLine方法中操作的,所以它继承了BufferedReader并复写了readLine方法,同时定义了方法setLineNumber(int) 和getLineNumber(),它们可分别用于设置和获取当前行号。
用法示例:
public class LineNumberReaderDemo{
public static void main(String[] args)throws IOException{
FileReader fr = new FileReader("D:\\123.txt");//建立读取流对象
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;//定义用作临时存储的字符串变量
lnr.setLineNumber(100);//设置起始行号,设置为100,从101开始
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);//连同行号一起打印
}
//打印结果为前面带着行号的数据
lnr.close();//关键资源
}
}
一.字节流
字节流的抽象基类:
|---->InputStream:字节输入流
|---->OutputStream:字节输出流
它的操作与字符流类似,可以参与字符流的定义、读取、写入、处理异常的格式,只不过是处理的数据不同,因为对于非字符的数据,比如图片、视频、音频文件(例如mp3)等,这些文件只能用字节流对之进行操作。
FileInputStream
FileInputStream是InputStream的一个子类,用于读取诸如图像数据之类的原始字节流
构造方法:
|--->FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
|--->FileInputStream(FileDescriptor fdObj)
通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。
|--->FileInputStream(String name)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
常用方法:
|--->int available()返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
|--->void close()关闭此文件输入流并释放与此流有关的所有系统资源。
|--->int read()从此输入流中读取一个数据字节。
|--->int read(byte[] b)从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
|--->int read(byte[] b, int off, int len)从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
字节读取流代码示例:
public class InputStreamDemo {
public static void main(String[] args) {
read1();
}
//第一种读取方式:按字节来读取
public static void read1() {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\\Demo\\demo.java");//新建字节读取的对象,明确源文件
int x = 0;
while((x = fis.read())!=-1) {
System.out.print((char)x);
}
}catch (IOException e) {
e.printStackTrace();
}
finally { //执行关闭资源的操作
if(fis!=null) {
try{
fis.close();
}catch(IOException e2){
e2.printStackTrace();
}
}
}
}
//第二种读取方式:按字节数组读取
public static void read2() {
FileInputStream fis = null;
try {
fis = new FileInputStream("D:\\Demo\\demo.java");//新建字节读取的对象,明确源文件
int len = 0;
byte[] buff = new byte[1024];//定义一个字节数组,用于存储字节
while((len=fis.read(buff))!=-1) { //每次将读取到的字节存储进buff数组
System.out.println(new String(buff,0,len));//将字节数组转换成字符串输出
}
} catch (IOException e) {
e.printStackTrace();
}
finally { //执行关闭资源的操作
if(fis!=null) {
try {
fis.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
}
}
注意available()这个方法的用法,取文件字节数,然后定义一个刚好大小的字节数组。代码如下:
fis =new FileInputStream(file);
byte [] ch =newbyte[fis.available()];
fis.read(ch);
System.out.println(new String(ch));
但是如果文件过大,会造成内存溢出。
FileOutputStream
FileOutputStream是OutputStream的一个子类,用于写入诸如图像数据之类的原始字节的流。
构造方法:
|--->FileOutputStream(File file)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
|--->FileOutputStream(File file,boolean append)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
|--->FileOutputStream(FileDescriptor fdObj)
创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。
|--->FileOutputStream(String name)
创建一个向具有指定名称的文件中写入数据的输出文件流。
|--->FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。
常用方法:
|--->close():关闭此文件输出流并释放与此流有关的所有系统资源。
|--->write(byte[] b):将 b.length 个字节从指定byte数组写入此文件输出流中。
|--->write(byte[] b, int off, int len):将指定byte数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
|--->write(int b):将指定字节写入此文件输出流。
字节写入流代码示例:
public class OutputStreamDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("D:\\Demo\\fos.txt");
//定义一个字符串,因为字节流只能以字节或字节数组的形式读取
String str = "努力努力";
byte [] by =str.getBytes();//转成字节数组形式
fos.write(by);//不用刷新
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
if(fos!=null) {
try {
fos.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
二.字节流缓冲区
字节流缓冲区同样是提高了字节流的读写效率。
对应类:
BufferedOutputStream (extends OutputStream)
BufferedInputStream (extends InputStream)
利用缓冲区的技术,拷贝Mp3文件
public class copyMp3 {
public static void main(String[] args) {
BufferedInputStream buis=null;
BufferedOutputStream buos=null;
try {
buis=new BufferedInputStream(new FileInputStream("D:\\play.mp3"));
buos=new BufferedOutputStream(new FileOutputStream("D:\\play_copy2.mp3"));
//第一种复制方式
int num=0;
while((num=buis.read())!=-1) {
buos.write(num);
}
//第二种复制方式
byte[] buff = new byte[1024];
int len = 0;
while((len=buis.read(buff))!=-1) {
buos.write(buff,0,len);
}
}
catch(IOException e) {
throw new RuntimeException("复制失败");
}
finally {
if(buis!=null) {
try {
buis.close();
}catch (Exception e2) {
throw new RuntimeException("关闭读取流失败");
}
}
if(buos!=null) {
try {
buos.close();
} catch (Exception e2) {
throw new RuntimeException("关闭写入流失败");
}
}
}
}
}
三.转换流
转换流包括:
InputStreamReader:字节流通向字符流的桥梁
OutputStreamWriter:字符流通向字节流的桥梁
转换流的由来:是字节流和字符流的桥梁;方便了字节流和字符流之间的操作
转换流的应用:字节流中的数据都是字符时,转成字符流操作更高效
InputStreamReader
是字节流通向字符流的桥梁。
每次调用 InputStreamReader 中的一个 read()方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。
构造方法:
InputStreamReader(InputStream in)
InputStreamReader(InputStream in, String charsetName)
示例:BufferedReader in = new BufferedReader(new InputStreamReader(System.in));(必须记住)
OutputStreamWriter
是字符流通向字节流的桥梁,将要写入流中的字符编码成字节。
每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。
构造方法:
OutputStreamWriter(OutputStream out)
OutputStreamWriter(OutputStream out, String charsetName)
示例:
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
System.in 默认设备为键盘
System.setIn(InputStream in)\\ 重新分配“标准”输入流。
System.out 默认设备为控制台
System. setOut(PrintStream out) \\重新分配“标准”输出流。
四.标准输入与输出
System类中的字段:in,out,它们各代表了系统标准的输入和输出设备,默认输入设备是键盘,输出设备是显示器。
System.in的类型是InputStream.
System.out的类型是PrintStream
java.lang.Object
|--java.io.OutputStream
|--java.io.FilterOutputStream
|---java.io.PrintStream
示例:
例:获取键盘录入数据,然后将数据流向显示器,那么显示器就是目的地。
通过System类的setIn,setOut方法对默认设备进行改变:
System.setIn(new FileInputStream(“1.txt”));//将源改成文件1.txt。
System.setOut(new FileOutputStream(“2.txt”));//将目的改成文件2.txt
因为是字节流处理的是文本数据,可以转换成字符流,操作更方便。
BfferedReader bufr =new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw =new BufferedWriter(new OutputStreamWriter(System.out));
五 File类
File类是文件和目录路径名的抽象表示形式。 是io包中唯一代表磁盘文件本身的对象,其定义了一些与平台无关的方法来操作文件,可以通过调用File类中的方法,实现创建.删除.重命名文件等操作,File类是对象主要用来获取未文件本身的一些信息,如文件所在的目录、文件的长度、文件的读写权限等。File对象可以作为参数传递给流的构造函数。
构造方法:
|--->File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
|--->File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
|--->File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
|--->File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。
常见方法:
1.创建
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
注:File类创建文件的方法createNewFile()与输出流的方法不同点:
File类通过createNewFile方法来创建文件的话,如果文件存在则不创建
输出流是对象一建立就创建文件,如果文件存在就会覆盖原文件。
2.删除
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel。
void deleteOnExit():在程序退出时删除指定文件。
3.判断
boolean exists():文件或目录是否存在。
boolean isFile():是否是一个标准文件。
boolean isDirectory():是否是一个目录。
boolean isHidden():是否是一个隐藏文件。
boolean isAbsolute():是否为绝对路径名。
boolean canRead()是否可读
boolean canWrite()是否可写
4.获取
String getName():返回由此抽象路径名表示的文件或目录的名称。
String getPath():获取相对路径
String getParent():获取父级目录,如果此路径名没有指定父目录,则返回 null。
String getAbsolutePath():获取绝对路径
long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
long length():返回由此抽象路径名表示的文件的长度.
static File[] listRoots():列出系统的可用的根
String[] list():获取指定目录下的所有文件和文件夹的名称数组
File[] listFiles():获取指定目录下的所有文件和文件夹的File数组
5.修改
boolean renameTo(File dest):重新命名此抽象路径名表示的文件。
class FileDemo {
public static void main(String[] args) throws IOException {
method_5();
}
//renameTo()方法,相当于剪切
public static void method_5() {
File f1 = new File("c:\\Test.java");
File f2 = new File("d:\\hahah.java");
sop("rename:"+f2.renameTo(f1)); //false
}
//获取各种路径
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("c:\\abc\\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);//打印结果c:\\abc\\a.txt
sop("f2:"+f2);//打印结果c:\\abc\\b.txt
sop("f3:"+f3);//打印结果c:\\abc\\c.txt
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);
}
}
六、Properties类
Properties是hashtable的子类,也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。
是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件,那么在加载数据时,需要数据有固定格式:键=值。
构造方法:
|--->Properties():创建一个无默认值的空属性列表。
|--->Properties(Properties defaults):创建一个带有指定默认值的空属性列表。
常用方法:
|--->Object setProperty(String key,String value)
调用Hashtable的put方法,设置键值对
|--->String getProperty(String key)
用指定的键在此属性列表中搜索属性
|--->Set stringPropertyNames
获取集合中所有的键
|--->void load(InputStream in)
从输入流中读取属性列表(键和元素对)。
|--->void load(Reader reader)
按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
|--->void list(PrintStream out)
将属性列表输出到指定的输出流。
|--->void list(PrintWriter out)
将属性列表输出到指定的输出流。
|--->void Store(OutputStream out,String comments)
以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
|--->void store(Writer writer, String comments)
以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。
class PropertiesDemo {
public static void main(String[] args) throws IOException {
loadDemo();
}
public static void loadDemo()throws IOException{
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
//将流中的数据加载进集合。
prop.load(fis);
prop.setProperty("wangwu","39");//并没有写入到硬盘中,只写入到流中
FileOutputStream fos = new FileOutputStream("info.txt");
prop.store(fos,"haha");//将流中的数据写入到硬盘中
prop.list(System.out);//列出目录
fos.close();
fis.close();
}
//设置和获取元素。
public static void setAndGet() {
Properties prop = new Properties();
prop.setProperty("zhangsan","30");//设置元素
prop.setProperty("lisi","39");
String value = prop.getProperty("lisi");
System.out.println(value);//打印出39
prop.setProperty("lisi",89+"");
Set names = prop.stringPropertyNames();
for(String s : names){
System.out.println(s+":"+prop.getProperty(s));
}
}
}
七.打印流
可以直接操作输入流和文件,该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流:PrintStream
构造函数可以接收的参数类型:
1.file对象:File
2.字符串路径:String
3.字节输出流:OutputStream
字符打印流:PrintWriter
构造函数可以接收的参数类型:
1.file对象:File
2.字符串路径:String
3.字节输出流:OutputStream
4.字符输出流:Writer
注:与其他输出流不同,PrintStream 永远不会抛出IOException,而且打印流可以根据指定编码转成字符!
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);//ture代表自动刷新
String line = null;
while((line=bufr.readLine())!=null) {
if("over".equals(line))
break;
out.println(line.toUpperCase());//转换成大写
//out.flush();//刷新
}
out.close();
bufr.close();
}
}
八..序列流
SequenceInputStream是能对多个流进行合并成一个读取流,它在构造时需要传入Enumeration,而这个只用Vector中有,所以这个多个读取流要加入Vector集合中。
注意:它只是对读取流进行合并。所以此对象没有对应的OutputStream
使用步骤:
1.创建Vector
2.将要合并的InputStream加入Vector
3.通过Vector获取Enumeration
4.创建SequenceInputStream对象,将Enumeration作为参数传入。
class SequenceDemo {
public static void main(String[] args) throws IOException {
//创建Vector集合
Vector v = new Vector();
//将要合并的InputStream加入Vector
v.add(new FileInputStream("c:\\1.txt"));
v.add(new FileInputStream("c:\\2.txt"));
v.add(new FileInputStream("c:\\3.txt"));
//通过Vector获取Enumeration
Enumeration en = v.elements();
创建SequenceInputStream对象
SequenceInputStream sis = new SequenceInputStream(en);
//定义输出流
FileOutputStream fos = new FileOutputStream("c:\\4.txt");
byte[] buf = new byte[1024];
int len =0;
while((len=sis.read(buf))!=-1) {
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
一.对象序列化
数据可以封装成对象,对象运行时是在堆内存中的,如果对象的数据需要存储在硬盘上,那么就要用到对象的序列化流。对象序列化(也叫对象的可串行性)其实就是对象持久化,把内存中的对象,变成硬盘上的文件内容。
IO中供对象序列化的流对象为ObjectInputStream和ObjectOutputStream。
注意:
1.用ObjectOutputStream写入的的文件,只能用ObjectInputStream来重构读取。
2.被序列化的对象必须实现Serializable接口。
3.对象的静态成员和被transient关键字修饰的成员不能被序列化。(当对象在堆内存的私有对象不希望被序列化时,可以使用transient关键字标识)。
ObjectInputStream
对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
构造方法:
|--->protected ObjectInputStream()
为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。
|--->ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream。
特有方法:
|--->Object readObject() :从 ObjectInputStream 读取对象。
ObjectOutputStream
将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
构造方法:
|--->protected ObjectOutputStream()
为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
|--->ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。
特有方法:
|--->void writeObject(Object obj):将指定的对象写入 ObjectOutputStream。
Serializable接口
在对对象进行序列化时,必须实行Serializable接口,否则使用ObjectOutputStream写入时,会出现NotSerializableException异常。
Serializable接口并没必须要实现的方法,类定义时仅标示一下实现即可。实现Serializable的类,都有serialVersionUID,如果你没有在类中显式定义一个serialVersionUID,那么编译器会根据该类中的成员生成一个具有唯一性的serialVersionUID。
显式定义serialVersionUID的好处:
1.如果你在对类对象进行了序列化之后,又修改了这个类,那么再次读取修改前序列化的对象时,编译器可以识别;
2.如果没有显式定义,你修改后的类经过编译器编译后会生成一个新的serialVersionUID,这个serialVersionUID跟修改前类的serialVersionUID不同,当你再次读取时,编译器会报出InvalidClassException异常。所以,如果类对象需要序列化,建议显式定义serialVersionUID。
class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
//writeObj();
readObj();
}
public static void readObj()throws Exception {
//通过ObjectInputStream读取序列化后的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
public static void writeObj()throws IOException {
//通过ObjectOutputStream将对象序列化
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",23,"kr"));//country为静态,不能序列化。
oos.close();
}
}
class Person implements Serializable//实现Serializable接口 {
public static final long serialVersionUID = 12L;//显式定义serialVersionUID
private String name;
private int age;
//age如果不想序列化,可以在前边加 transient 关键字,保证其值在堆内存中存在而不在文本文件中存在。
static String county = "cn";
public Person(String aName,int aAge,String cCounty){
this.name = aName;
this.age = aAge;
this.county = cCounty;
}
public String getInfo(){
return this.name + this.age + county;
}
}
二.管道流
管道流的主要作用是可以进行两个线程之间的通信,按作用分可以 分为管道输出流和管道输入流,按对象分可分为字节管道流和字符管道流。它是IO技术和多线程技术的结合。
管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。
通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
使用步骤:
1.分别定义写入和读取的Runnable接口子类,把相应的管道流作为构造参数传入给定义的私有管道流成员。
2.将配对的管道流通过connect()方法连接起来。
3.启动线程。
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);//运用读取的connect方法与写入流对接
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();//启动读取流线程
new Thread(w).start();//启动写入流线程
}
}
三.RandomAccessFile类
该类不是算是IO体系中子类,而是直接继承自Object,但是它是IO包中成员。因为它具备读和写功能。其实完成读写的原理就是内部封装了字节输入流和输出流。
内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。(此类可以跟多线程下载联系起来)
通过构造函数可以看出,该类只能操作文件。
构造函数:
|--->RandomAccessFile(File file, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
|--->RandomAccessFile(String name, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
mode 参数值及其含意:
|--->"r" :以只读方式打开。调用结果对象的任何 write 方法或者如果该文件不存在将导致抛出 IOException。
|--->"rw" :打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。 如果文件存在不会覆盖。
|--->"rws" :打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
|--->"rwd" :打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
常见方法:
|--->void writeInt(int v):按四个字节将 int 写入该文件,先写高字节。
|--->voidseek(long pos):设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
|--->int skipBytes(int n):尝试跳过输入的 n 个字节以丢弃跳过的字节。
class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
//writeFile_2();
//readFile();
}
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.write(97)只读取最低8位。
raf.writeInt(97);//按四个字节将 int 写入该文件,先写高字节
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}
四.操作基本数据类型
DataInputStream与DataOutputStream,可以用于操作基本数据类型的数据的流对象。两个类中的方法都很简单,基本结构为readXXXX()和writeXXXX()其中XXXX代表基本数据类型或者String。
DataOutputStream
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
构造方法:
|--->DataOutputStream(OutputStream out)
创建一个新的数据输出流,将数据写入指定基础输出流。
常用方法:
|--->void writeUTF(String str)
以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。(用此方法写的要用对应的方法读取)
DataInputstream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
对于多线程访问不一定是安全的。线程安全是可选的,它由此类方法的使用者负责。
构造方法:
|--->DataInputStream(InputStream in)
使用指定的底层 InputStream 创建一个 DataInputStream。
常用方法:
|--->final String readUTF():从包含的输入流中读取此操作需要的字节。
注:如果用此方法去读取非对应输出流写入的字节的话,也就是读取正常的UTF-8编码,会抛出: EOFException - 如果此输入流在读取所有字节之前到达末尾。
class DataStreamDemo {
public static void main(String[] args) throws IOException {
//writeData();
//readData();
//writeUTFDemo();
readUTFDemo();
}
public static void readUTFDemo()throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();//通过对应方法读取
System.out.println(s);
dis.close();
}
public static void writeUTFDemo()throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");
dos.close();
}
public static void readData()throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
public static void writeData()throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
ObjectOutputStream oos = null;
oos.writeObject(new O());
}
}
五.操作字节数组
ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
注意:
1.因为这两个流对象都操作的是数组,并没有使用系统资源,所以,不用进行close关闭,即使你关闭了,它的其他方
法还可以使用,而不会抛出IOException。
2.使用这对对象操作时,它的源和目的都是内存。
用途:这两个对象是在用流的思想来操作数组,当我们需要把一个文件中的数据加入内存中的数组时,就可以考虑用这个两个对象。此外,它还有writeTo(OutputStream os)可以把ByteArrayOutputStream对象内部定义的缓冲区内容,一次性写入os中。操作字符数组、字符串的流对象类型与之相似,可以参与它们的使用方法。
class ByteArrayStream {
public static void main(String[] args) {
//数据源。
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1) {
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
bos.writeTo(new FileOutputStream("a.txt"));
}
}
六.字符编码
字符流的出现是为了方便操作字符数据,其方法操作的原因是因为内部加入了编码表。Java中能够实现字节根据指定编码表转成字符的,有四个类:InputStreamReader和OutputStreamWriter,PrintStream和PrintWriter。它们都能够加构造时,指定编码表;但后两个是打印流,只能用于打印,使用有局限,所以相对而言,还是前两个转换流使用多一些。
编码表的由来:
计算机只能识别二进制数据,早期是电信号。为了应用计算机方便,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并将文字与二进制数字一一对应,形成了一张表,这个表就是编码表。
常见的编码表
地域码表:
1.ASCII:美国码表,息交换码,用一个字节的7位表示。
2.ISO8859-1:欧洲码表,拉丁码表,用一个字节的8位表示,最高位1
3.GB2312:中国中文编码表,它用两个字节表示,为兼容ASCII,它的两个字节的高位都是1,也即是两个负数;但与ISO8859-1冲突。大概有六七千个字。
4.GBK:中国的中文编码表的升级版,扩容到2万多字。
通用码表:
1.Unicode:国际标准码,融合多种语言文字。所有的文字都用两个字节表示,Java默认使用的就是Unicode。
2.UTF-8:UnicodeTransform Format -8。Unicode码把用一个字节能装下的文字,也用两个字节表示,有些浪费空间,对之进行优化的结果就是UTF-8。UTF-8编码表,一个文字最少用1个字节表示,最多用3个字节表示,并且每个字节开始都有标识头,所以很容易于其他编码表区分出来。
当一写入文件采用的编码与读取文件采用的编码不相同时,可能会出现乱码,因为每个编码在各自的编码表中对应的编码值是不同的,所以在读取与写入的时候就要指定好使用的是何种编码,而转换流就可以指定编码表,他的应用可以分为两种:
1.可以将字符以指定的编码格式存储。
2.可以对文本数据以指定的编码格式来解读。
指定编码表的动作由构造函数完成:
1.InputStreamReader(InputStream in,String charsetName)
创建使用指定字符集的InputStreamReader。
2.OutputStreamWriter(OutputStream out,String charsetName)
创建使用指定字符集的OutputStreamWriter。
class EncodeStream {
public static void main(String[] args) throws IOException {
//writeText();
readText();
}
public static void readText()throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"UTF-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
public static void writeText()throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
}
编码:字符串变成字节数组,String--->byte[],使用str.getBytes(charsetName);
解码:字节数组变成字符串,byte[]--->String,使用new String(byte[] b, charsetName);
class EncodeDemo {
public static void main(String[] args)throws Exception {
String s = "哈哈";
byte[] b1 = s.getBytes("GBK");
System.out.println(Arrays.toString(b1));
String s1 = new String(b1,"iso8859-1");
System.out.println("s1="+s1);
//对s1进行iso8859-1编码。
byte[] b2 = s1.getBytes("iso8859-1");
System.out.println(Arrays.toString(b2));
String s2 = new String(b2,"gbk");
System.out.println("s2="+s2);
}
}
IO流总结
使用IO思考步骤:
1:搞清楚数据源和目的地都可以用哪些对象操作
数据源:InputStream
Reader
目的地:OutputStream
Writer
2:分清楚数据源和目的地是什么类型的文件?
数据源:Reader:文本文件
InputStream:媒体文件
目的地:Writer:文本文件
OutputStream:媒体文件
3:搞清楚数据源和目的地的设备
数据源:
文件 FileReader
键盘录入 InputStream is = System.in;
使用转换流InputStreamReader isr = new InputStreamReader(System.in);
目的地:
文件 FileWriter
控制台输出 OutputStream os = System.out;
使用转换流OutputStreamWriter osw = new OutputStreamWriter(os);
4:是否要求高效
是:使用Buffered流对象
否:不使用Buffered流对象
源与目的地操作规律:
1.文本文件--文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
2.文本文件--控制台输出
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
3.键盘录入--文本文件
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
4.键盘录入--控制台输出
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
练习
1每一行前面加上行号和冒号
public class Exercise1 {
public static void main(String[] args) throws IOException {
List list = new ArrayList(); // 定义List, 用来存储行数据
// 定义LineNumberReader, 用来读取行数据
LineNumberReader lnr = new LineNumberReader(new FileReader("day19-笔记.txt"));
String line;
while ((line = lnr.readLine()) != null) // 定义循环读取数据, 加上行号和冒号, 存入List
list.add(lnr.getLineNumber() + ": " + line);
lnr.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("day19-笔记.txt")); // 定义BufferedWriter
for (String s : list) { // 迭代List, 将存储的行写出
bw.write(s);
bw.newLine();
}
bw.close();
}
}
2:所有行反转(第一行换到最后一行, 第二行换到倒数第二行)
public class Exercise2 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("day19-笔记.txt"));
// 定义BufferedReader, 用来读取行数据
List list = new ArrayList(); // 定义List, 用来存储行数据
String line;
while ((line = br.readLine()) != null) // 定义循环, 读取数据到List中
list.add(line);
br.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("day19-笔记.txt")); // 定义BufferedWriter
for (int i = list.size() - 1; i >= 0; i--) { // 倒着遍历List, 将数据写出
bw.write(list.get(i));
bw.newLine();
}
bw.close();
}
}
3:所有字符按照码表值排序, 存入另一个文件中
public class Exercise3 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("day19-笔记.txt")); // 定义BufferedReader
TreeSet ts = new TreeSet(new CharComparator()); // 定义TreeSet
int ch; // 读取文件数据, 存入TreeSet排序
while ((ch = br.read()) != -1) {
if (ch == '\r' || ch == '\n' || ch == ' ' || ch == '\t')
continue;
ts.add((char)ch);
}
br.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("sort.txt")); // 定义BufferedWriter
for (char c : ts) // 迭代TreeSet, 将数据写出到另一个文件
bw.write(c);
bw.close();
}
}
class CharComparator implements Comparator {
public int compare(Character o1, Character o2) {
return o1 - o2 != 0 ? o1 - o2 : 1;
}
}
4:编写一个程序, 该程序只能运行10次, 每次运行时提示剩余次数, 10次之后提示已到期
public class Exercise4 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("times.txt")); // 定义输入流, 指向times.txt
int times = Integer.parseInt(br.readLine()); // 读取一行数据, 转为int值
br.close(); // 关闭输入流
if (times > 0) { // 如果次数大于0
System.out.println("欢迎使用Xxx软件, 剩余使用次数: " + times); // 打印次数
FileWriter fw = new FileWriter("times.txt"); // 定义输出流, 指向times.txt
fw.write(--times + ""); // 将次数减1, 写回文件中. 注意: 将次数写回文件的时候要转为String
fw.close(); // 关闭输出流
} else { // 如果次数不大于0
System.out.println("软件已到期"); // 提示已到期
}
}
}
5:从键盘接收3个学生的考试成绩, 对其按照总分排序, 输出到屏幕.
考试成绩输入格式:
张三,80,85,80
李四,70,70,80
王五,90,90,90
屏幕输出格式:
王五,90,90,90,270
张三,80,85,80,245
李四,70,70,80,220
public class Exercise5 {
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("请输入学生成绩:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 定义BufferedReader
TreeSet set = new TreeSet(); // 为了要排序, 所以创建TreeSet集合
for (;;) {
String line = br.readLine();
if ("quit".equals(line)) {
br = new BufferedReader(new FileReader("stu.txt"));
continue;
} else if (line == null) {
br.close();
break;
}
String[] arr = line.split(",");
// 读取数据, 按逗号分割
Student stu = new Student(arr[0],
Integer.parseInt(arr[1]),
Integer.parseInt(arr[2]),
Integer.parseInt(arr[3]));
set.add(stu); // 每次将读取到的数据封装成一个对象, 装到集合中排序
}
PrintStream ps = new PrintStream(new FileOutputStream("stu.txt"));
System.setOut(ps);
for (Student stu : set)
System.out.println(stu); // 迭代TreeSet集合, 打印结果
ps.close();
}
}
6:从键盘输入接收一个文件夹路径, 将该文件夹下的所有.java文件的文件名写入到java.txt文件中
public class Exercise1 {
public static void main(String[] args) throws IOException {
System.out.println("请输入一个文件夹路径:");
File dir = Util.getDir();
String[] arr = dir.list(); // 获取子文件名
BufferedWriter bw = new BufferedWriter(new FileWriter("java.txt"));
for (String name : arr)
if (name.endsWith(".java")) { // 判断如果是.java结尾就写出
bw.write(name);
bw.newLine();
}
bw.close();
}
}
//这里定义了Util类,以后的代码为了简单可以省略该类,是在控制台中输入对应的路径或者文件。
public class Util {
public static File getDir() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
File file = new File(br.readLine()); // 从键盘读取一个路径, 封装成File对象
if (!file.exists()) // 如果不存在, 提示, 重输
System.out.println("您输入的路径不存在, 请重新输入:");
else if (!file.isDirectory()) // 如果不是文件夹, 提示, 重输
System.out.println("您输入的不是文件夹路径, 请重新输入:");
else
return file;
}
}
public static File getFile() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
File file = new File(br.readLine());
if (!file.exists())
System.out.println("您输入的路径不存在, 请重新输入:");
else if (!file.isFile())
System.out.println("您输入的不是文件路径, 请重新输入:");
else
return file;
}
}
}
7:从键盘输入接收一个文件夹路径, 将该文件夹下的所有子文件名写入到name.txt中, 包括子文件夹下的子文件
public class Exercise2 {
public static void main(String[] args) throws IOException {
System.out.println("请输入一个文件夹路径: ");
File dir = Util.getDir(); // 调用工具类中的方法, 从键盘接收一个文件路径封装成File对象
System.setOut(new PrintStream("name.txt")); // 改变默认输出流, 指向文件
Exercise2.writeSubfileNames(dir, 0); // 将dir文件夹下所有子文件名(包括子文件夹的子文件)写出
}
private static void writeSubfileNames(File dir) {
File[] subFiles = dir.listFiles(); // 获取当前文件夹下所有的子文件组成的File[]
if (subFiles != null) // 有些系统文件夹不允许获取子文件, 会返回null, 为了避免空指针异常所以判断
for (File subFile : subFiles) { // 循环遍历所有子文件
System.out.println(subFile.getName()); // 打印每一个子文件的名字
if (subFile.isDirectory()) // 如果是一个文件夹
writeSubfileNames(subFile); // 再打印出这个子文件夹下所有子文件的名字
}
}
private static void writeSubfileNames(File dir, int lv) { // lv记住递归的次数
File[] subFiles = dir.listFiles();
if (subFiles != null)
for (File subFile : subFiles) {
for (int i = 0; i < lv; i++)
System.out.print("\t");
System.out.println(subFile.getName());
if (subFile.isDirectory())
writeSubfileNames(subFile, lv + 1); // 每递归一次加1
}
}
}
8:从键盘输入接收一个文件夹路径, 统计该文件夹大小.
public class Exercise3 {
public static void main(String[] args) throws IOException {
System.out.println("请输入一个文件夹路径: ");
File dir = Util.getDir();
System.out.println(getLength(dir));
}
public static long getLength(File dir) {
long len = 0; // 定义变量用来记住大小
File[] subFiles = dir.listFiles(); // 找到所有子文件
if (subFiles != null) // 为了避免系统文件不允许访问
for (File subFile : subFiles) // 遍历每一个子文件
if (subFile.isFile())
len += subFile.length();
else
len += getLength(subFile);
// len += subFile.isFile() ? subFile.length() : getLength(subFile); // 如果是文件则加上文件大小, 是文件夹则递归计算文件夹大小
return len;
}
}
9:从键盘输入接收一个文件夹路径, 删除该文件夹.
public class Exercise4 {
public static void main(String[] args) throws IOException {
System.out.println("请输入一个文件夹路径: ");
File dir = Util.getDir();
System.out.println("确定要删除\"" + dir.getAbsolutePath() + "\"吗? (Y/N)");
if ("Y".equalsIgnoreCase(new BufferedReader(new InputStreamReader(System.in)).readLine()))
deleteDir(dir);
System.out.println(dir.exists() ? "出错了" : "删除成功!");
}
public static void deleteDir(File dir){
File[] subFiles = dir.listFiles(); // 获取所有子文件
if (subFiles != null) // 避免NullPointerException
for (File subFile : subFiles) // 遍历
if (subFile.isFile()) // 如果是文件就删除
subFile.delete();
else // 如果是文件夹就递归, 删除文件夹
deleteDir(subFile);
dir.delete(); // 删除所有子文件之后, 删除当前文件夹
}
}
10:从键盘输入接收两个文件夹路径, 将一个文件夹拷贝到另一个文件夹中.
public class Exercise5 {
public static void main(String[] args) throws IOException {
System.out.println("请输入源文件夹路径:");
File src = Util.getDir();
System.out.println("请输入目标文件夹路径:");
File dest = Util.getDir();
copyDir(src, dest);
}
public static void copyDir(File src, File dest) throws IOException {
File newDir = new File(dest, src.getName()); // 在dest文件夹下创建和src同名的文件夹
newDir.mkdir();
File[] subFiles = src.listFiles(); // 获取src下所有子文件
if (subFiles != null) {
for (File subFile : subFiles) { // 循环遍历
if (subFile.isFile()) { // 如果是文件直接考到新建的文件夹下
FileInputStream fis = new FileInputStream(subFile);
FileOutputStream fos = new FileOutputStream(new File(newDir, subFile.getName()));
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1)
fos.write(buffer, 0, len);
fis.close();
fos.close();
} else { // 如果是文件夹, 递归拷贝子文件夹
copyDir(subFile, newDir);
}
}
}
}
}
11:分割文件
public class Exercise6 {
public static void main(String[] args) throws IOException {
System.out.println("请输入要分割的文件路径:");
File file = Util.getFile(); // 获取文件路径
File tempDir = new File(file.getParent(), ".temp"); // 创建文件夹
tempDir.mkdir();
long partLen = file.length() / 3 + 1; // 每一段的大小
FileInputStream fis = new FileInputStream(file); // 定义流从文件中读数据
for (int i = 0; i < 3; i++) { // 循环3次
FileOutputStream fos = new FileOutputStream(new File(tempDir, i + "")); // 每次定义一个输出流
byte[] buffer = new byte[(int) partLen]; // 定义字节数组, 长度为一份大小
int len = fis.read(buffer); // 将数据读入数组
fos.write(buffer, 0, len); // 写出到小文件中
fos.close(); // 关闭输出流
}
fis.close();
file.delete(); // 删除原文件
tempDir.renameTo(file); // 改文件夹名字
}
}