目录
一、字符流
1.1字符流概述
字符流是可以直接读写字符的IO流,
字符流读取字符,就是先读取到字节数据,然后转为字符,
如果要写出字符,需要把字符转为字节再写出。
1.2FileReader
FileReader是用于读取字符流的类,如果要读取原生的字节流,一般使用FileInputStream,
FileReader主要有以下几个构造方法,
FileReader(File file)//创建一个新的FileReader,给出File读取
FileReader(String fileName)//创建一个新的FileReader,给定要读取的文件的名称。
我们来看看具体如何使用,
import java.io.*;
public class IOTest{
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("data.txt");
int x;
while((x=fr.read())!=-1){
System.out.print((char)x);
}
fr.close();
}
}
1.3FileWriter
FileWriter类中的write()方法可以自动把字符转为字节写出,其主要构造函数如下,
FileWriter(File file)//给一个File对象构造一个FileWriter对象
FileWriter(File file, boolean append)//给一个File对象构造一个FileWriter对象,append为true时从文件末尾写入而不是文件开头
FileWriter(String fileName)//构造一个给定文件名的FileWriter对象
FileWriter(String fileName, boolean append)//构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据
我们看看具体如何使用该对象写入字符,
FileWriter fw=new FileWriter("data.txt",true);
fw.write("\r\nFileWriter中文写入测试");
fw.close();
1.4字符流的拷贝
字符流的拷贝也是一样,一个字符一个字符的读取到程序中,然后逐个写入,
FileReader fr=new FileReader("data.txt");
FileWriter fw=new FileWriter("dataCopy.txt");
int c;
while((c=fr.read())!=-1){
fw.write(c);
}
fr.close();
fw.close();
我们可以看到内容已经拷贝到目标文件中去了,
注意这里必须要关闭fw字符流对象,因为FileWriter类在写字符的时候,底层调用的是缓冲区写入,缓冲区大小为2k,
如果内容没有装满缓冲区,就不会刷新到文件中,所以我们要调用close方法将缓冲区刷新,将内容写入到目标的文件中。
1.5字符流使用情况
字符流可以拷贝文本文件,但是不推荐使用,
因为读取时会把字节转为字符,写出时还要把字符转回字节,直接使用字节流更方便快速,
当程序需要读取一段文本,或者需要写出一段文本的时候可以使用字符流,
读取的时候是按照字符的大小读取的,不会出现半个中文;写出的时候可以直接将字符串写出,不用转换为字节数组。
1.6字符流是否可以拷贝非纯文本文件
字符流不可以拷贝非纯文本文件,比如图片视频等文件,
在读的时候会将两个字节转换为字符,转换过程中可能找不到对应的字符,就会用?代替,
写出的时候会将字符转换成字节写出去,如果是?直接写出,那么文件就会乱码。
1.7带缓冲的字符流
同样的,在字符流中也有带缓冲的读写类,分别为BufferedReader和BufferedWriter,缓冲区大小都为16k,
BufferedReader的read()方法读取字符时会一次读取若干个字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率,
BufferedWriter的write()方法写出字符时会先写到缓冲区,缓冲区写满时才会写道文件,降低写文件的次数,提高效率。
1.7.1缓冲区字符流拷贝
我们来看看具体如何使用read()和write()方法进行拷贝,
import java.io.*;
public class IOTest{
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("data.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("dataCopy.txt"));
int c;
while((c=br.read())!=-1){
bw.write(c);
}
br.close();
bw.close();
}
}
1.7.2 readLine()和newLine()方法
BufferedReader的readLine()方法可以读取一行字符(不包括换行符号),
返回值为字符串,如果到达文件末尾则返回一个null,
BufferedWriter的newLine()方法可以输出一个跨平台的换行符号,
(在windows平台下换行符号为"\r\n",在linux平台下换行符号为"\n",在mac平台下换行符号为"\r")。
BufferedReader br=new BufferedReader(new FileReader("data.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("dataCopy.txt"));
String str;
while((str=br.readLine())!=null){
bw.write(str);//写入一行文本
bw.newLine();//换行
}
br.close();
bw.close();
1.8将文本反转
要求将一个文本文档中的文本反转,第一行和最后一行内容交换,第二行和倒数第二行内容交换,以此类推,
import java.io.*;
import java.util.ArrayList;
public class IOTest{
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("data.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("dataReverse.txt"));
ArrayList<String> list=new ArrayList<>();
String line;
while((line=br.readLine())!=null){
list.add(line);
}
for (int i = list.size()-1; i >=0 ; i--) {
bw.write(list.get(i));
bw.newLine();
}
br.close();
bw.close();
}
}
1.9LineNumberReader
LineNumberReader是BufferedReader的子类,具有相同的功能,
并且可以统计行号,默认情况下行号从0开始,
- getLineNumber()获取当前行号
- setLineNumber()设置当前行号
注意这里的setLineNumber()方法设置的行号并不会更改实际的行位置,更改的是getLineNumber()方法返回的值。
LineNumberReader lr=new LineNumberReader(new FileReader("data.txt"));
String line;
lr.setLineNumber(100);//设置初始行号为100
while((line=lr.readLine())!=null){
System.out.println(lr.getLineNumber()+":"+line);//输出行号和内容
}
二、装饰设计模式
装饰模式又名包装(Wrapper)模式,
装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,
public class WrapTest{
public static void main(String[] args) throws IOException {
ChineseDog cd=new ChineseDog(new Dog());
cd.ability();
}
}
interface Animal {
public void ability();
}
class Dog implements Animal{
@Override
public void ability() {
System.out.println("吃骨头");
}
}
class ChineseDog implements Animal{
private Dog d;
public ChineseDog(Dog d){
this.d=d;
}
@Override
public void ability() {
d.ability();
System.out.println("看门");
}
}
我们可以用继承实现这样的功能,但是继承关系耦合性太强了,父类修改了子类也会跟着改变,
而使用装饰模式就没有那么强的耦合性,两个类都是独立的,被装饰类的变化和装饰类的变化无关。
三、使用指定码表读写文件
FileReader使用默认码表读取文件,如果需要使用指定码表读取,则可以使用InputStreamReader(字节流,编码表),
FileWriter使用默认码表写出文件,如果需要使用指定码表写出,则可以使用OutputStreamReader(字节流,编码表)。
//读取uft-8编码格式的文本,将内容以gbk格式写入另一个文本中
InputStreamReader isr=new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
int c;
while((c=isr.read())!=-1){
osw.write(c);
}
isr.close();
osw.close();
四、试用版软件功能实现
当我们使用一款使用软件时,要求一共有10次试用的机会,每运行一次程序试用机会就会减少,
我们将使用机会次数存储到文件中,每次运行都会读取剩余试用机会,当使用完试用机会后,提示用户购买正版软件,
BufferedReader br=new BufferedReader(new FileReader("trialTimes.txt"));
String str=br.readLine();
int times=Integer.parseInt(str);
br.close();
if(times>0){
System.out.println("您的剩余使用次数为:"+ --times);
FileWriter fw=new FileWriter("trialTimes.txt");
fw.write(times+"");//以字符串的形式写出
fw.close();
}else{
System.out.println("试用次数已用完,请购买正版软件!");
}
五、递归
递归就是方法不断调用自己本身,很多递归都可以用for循环实现,
但是在有些不明确循环次数的时候,使用递归就非常简单了,我们举个例子来看看如何使用递归,
我们算一下10的阶乘,
public static void main(String[] args) throws IOException {
System.out.println(factorial(10));
}
public static int factorial(int num){
if(num ==1){
return 1;
}else{
return num * factorial(num-1);
}
}
使用递归有个很明显的缺点,就是使用递归时方法会不断压栈,并且不会释放内存空间,
如果调用的次数过多,很有可能会导致栈内存溢出。注意:构造方法中不能使用递归!否则就会一直在构造中死循环。