JAVA-IO
早期IO都是对字节流的操作,但是为了方便操作文本后来出现了字符流。
ASCII-英文
GB1312-中文(不够)
GBK-中文(扩容)
Unicode(无论什么字符都用两位字节) -万国码
UTF-8(字节优化)-万国码
为了解决乱码问题,JAVA提供字符流,只需要设置码表即可正常查阅数据。
01 FileWriter
基本写入数据
/**
* FileWriter的父类是Writer
* FileWriter中没有方法,只有构造当需要写入操作时:调用父类的write();
* FileWriter:覆盖原文件,不管有没有都会自动创建
* 调用write()方法会把数据放入缓冲区
* 调用flush()方法会刷新缓冲区,吸入目的地
* 调用close()也会刷新缓冲区,但是关闭了流,继续写入会抛异常
*/
FileWriter fileWriter = new FileWriter("D:\\ok.txt");
fileWriter.write("嘿嘿");
fileWriter.flush();
在文件尾部追加
new FileWriter("path",true);//即可追加在文件末尾
02 FileReader
- int read();//读取的数据,没有时为-1
FileReader fileReader = new FileReader("D:\\ok.txt");
int data = 0;
while ((data=fileReader.read())!=-1){
System.out.println((char)data);
}
- int read(char [] c);//返回读取的个数,没有就是-1
FileReader fileReader = new FileReader("D:\\ok.txt");
char c[] = new char[4];
int len = 0;
while ((len=fileReader.read(c))!=-1){
System.out.println(len);
System.out.println(c);
}
//运行结果:
4
abcd
2
efcd
new String(char[],start,lenth);
FileReader fileReader = new FileReader("D:\\ok.txt");
char c[] = new char[4];
int len = 0;
while ((len=fileReader.read(c))!=-1){
System.out.println(len);
System.out.println(new String(c,0,len));
}
//运行结果:
4
abcd
2
ef
03 BufferedWriter
缓冲区的出现是为了提高效率
FileWriter fileWriter = new FileWriter("D:\\ok.txt");
//其内部其实就是为FileWriter对象进行了
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
for (int i = 0; i < 10; i++) {
bufferedWriter.write("abcdef"+i);
//不同的操作系统不同的换行符
bufferedWriter.newLine();
}
//其实就是对FileWriter进行关闭操作
bufferedWriter.close();
04 BufferedReader
FileReader fileReader = new FileReader("D:\\ok.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = "";
/**
*bufferedReader.readLine()
*只返回有效数据内容,并不返回回车等终止符
*其原理就在于:遇到\n就返回字符数组,遇到\r就continue,将字符装入数组
*/
while ((line = bufferedReader.readLine())!=null) {
System.out.println(line);
}
bufferedReader.close();//其实就是对fileReader进行关闭
05 LineNumberReader
这两个类都相当于是装饰类,和BufferedReader、BufferedWriter一样,只不过增加了设置行数和获取行数
06 LineNumberWriter
在FileReader的基础上,装饰扩展出读取一行数据的功能,并且可以设置初始行号和读取到的当前行号
- setLineNumber(int l);
- getLineNumber();
07 装饰设计模式
增强已有的对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类都属于一个体系
最经典的例子就是BufferedWriter与FileWriter的设计关系:
BufferedWriter其实是对FileWriter对象的功能增强,增加了使用数组做缓冲区,一次性写入的高效方法,其原理就是通过构造器传入FileWriter对象,在其功能的基础上进行功能扩展。
BufferedWriter的构造器只能传入Writer类型,这里之所以使用多态,是为了提高程序的可扩展性,否则如果每次针对一个类似于FileWriter的类型就要增加一种构造器,这样程序的可扩展性极差。
设计方式:
- 首先你需要使得各个功能类似的类继承同一个基类
- 那么,装饰类只需要把构造器对这个基类开放即可
装饰模式的优点:
- 比继承灵活,避免了继承体系的臃肿
- 降低类与类之间的关系
08 IO异常处理方式
如果有多个IO操作对象,则需要在finally中进行多次独立的if(obj!=null)的判断操作,这才是最标准的异常捕捉!
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter("D:\\ok.txt");
fileWriter.write("嘿嘿");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fileWriter!=null){
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
快速自定义抛出异常:
在catch块当中:throw new RuntimeException("自定义异常");
09 InputStream
假如有一个文件:
abcde
f
使用FileInputStream读取,获得available()时,其返回值是8!
因为在windows操作系统中,回车"\r\n"占据两个字节,到字节流后变成两个字节
10 OutputStream
FileInputStream fileInputStream = new FileInputStream("path");
FileOutputStream fileOutputStream = new FileOutputStream("path");
byte b[] = new byte[1024];
int len = 0;
while ((len = fileInputStream.read(b))!=-1){
fileOutputStream.write(b,0,len);
}
fileInputStream.close();
fileOutputStream.close();
11 BufferedInputStream
装饰FileInputStream的类,用法和字符流相似
12 键盘输入流
在java.lang包下面System类中in是一个被静态InputStream描述的字节读取流对象,out和err是PrintStream对象。
//读取一个字符
String str = ""+System.in.read();
System.out.println(str);
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
System.out.println(num);
13 转换流
- InputStreamReader:把输入字节流转换成字符流操作
- OutputStreamWriter:把输出字节流转换成字符流操作
//字节流输入
InputStream inputStream = System.in;
//输出的的时候操作字符流对象
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
//使用包装类,调用readLine();
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//读取一行数据,如果输入exit就退出
String line = "";
while (!(line = bufferedReader.readLine()).equals("exit")){
System.out.println(line);
}
//关闭
bufferedReader.close();
还有一个好玩的装饰类:
//字节流输入
InputStream inputStream = System.in;
//输出的的时候操作字符流对象
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
//使用包装类,调用readLine();
LineNumberReader lineNumberReader = new LineNumberReader(inputStreamReader);
//读取一行数据,如果输入exit就退出
String line = "";
while (!(line = lineNumberReader.readLine()).equals("exit")){
System.out.println(lineNumberReader.getLineNumber()+":"+line);
}
//关闭
lineNumberReader.close();
14 File
- boolean createNewFile();//存在文件返回false
- boolean delete();
- int compareTo(File f);//字母顺序比较名称
- boolean exists();
- boolean mkdir();//创建目录
- boolean isDirectory();//是否目录
- boolean isFile();//是否文件
- boolean isHidden();//是否隐藏
- String getPath();
- String getAbsolutePath();
- long length();
15 Properties
位于 java.util.Properties ,是Java 语言的配置文件所使用的类, Xxx.properties 为Java 语言常见的配置文件,如数据库的配置 jdbc.properties, 系统参数配置 system.properties。 这里,讲解一下Properties 类的具体使用。继承了Hashtable 类,以Map 的形式进行放置值, put(key,value) get(key),以key=value 的 键值对的形式进行存储值。 key值不能重复。
Properties properties=new Properties();
//用绝对路径
InputStream input=new BufferedInputStream(new FileInputStream("D:\\workspace\\JavaLearn\\src\\jdbc.properties"));
properties.load(new InputStreamReader(input,"utf-8"));
//多添加几个值。
properties.setProperty("name","两个蝴蝶飞");
properties.setProperty("sex","男");
OutputStream output=new FileOutputStream("D:\\jdbc.properties");
OutputStreamWriter out=new OutputStreamWriter(output,"utf-8");
properties.store(out,"填充数据");
16 对象序列化
对象持久化存储:找到可长久保存对象的介质,叫做持久化,想要一个对象被序列化,该类必须实现io下的接口:Serializable
标记接口:没有任何方法的接口,例如:Serializable,其原理就是为对象加个UID值,但是也可以自定义一个UID
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.txt"));
objectOutputStream.writeObject(new People("嘿嘿","男"));
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.txt"));
People people = (People) objectInputStream.readObject();
System.out.println(people.getName()+":"+people.getSex());
17 管道流
import java.io.*;
class MyReader implements Runnable {
public PipedInputStream pipedInputStream;
public MyReader(PipedInputStream input){
this.pipedInputStream = input;
}
@Override
public void run() {
try {
byte b[] = new byte[1024];
int len = pipedInputStream.read(b);
String str = new String(b,0,len);
System.out.println(str);
pipedInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyWriter implements Runnable{
private PipedOutputStream pipedOutputStream;
public MyWriter(PipedOutputStream out){
this.pipedOutputStream = out;
}
@Override
public void run() {
try {
Thread.sleep(6000);
pipedOutputStream.write("我爱你".getBytes());
pipedOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Out {
public static void main(String[] args) throws Exception {
PipedInputStream pipedInputStream = new PipedInputStream();
PipedOutputStream pipedOutputStream = new PipedOutputStream();
MyReader myReader = new MyReader(pipedInputStream);
MyWriter myWriter = new MyWriter(pipedOutputStream);
pipedInputStream.connect(pipedOutputStream);//管道流关联
new Thread(myReader).start();
new Thread(myWriter).start();
}
}
18 RandomAccessFile
RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。
- 可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。
- 允许自由定义文件记录指针(OutputStream、Writer等输出流不同),RandomAccessFile可以不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。
- 它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。
四种模式:
**"r" : ** 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw": 打开以便读取和写入。
"rws": 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
"rwd" : 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。
特殊方法:
long getFilePointer( );//返回文件记录指针的当前位置
void seek(long pos);//将文件指针定位到pos位置
利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。当RandomAccessFile向指定文件中插入内容时,将会覆盖掉原有内容。如果不想覆盖掉,则需要将原有内容先读取出来,然后先把插入内容插入后再把原有内容追加到插入内容后。
下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:
/**
* 测试利用多线程进行文件的写操作
*/
public class Test {
public static void main(String[] args) throws Exception {
// 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件
RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");
raf.setLength(1024*1024); // 预分配 1M 的文件空间
raf.close();
// 所要写入的文件内容
String s1 = "第一个字符串";
String s2 = "第二个字符串";
String s3 = "第三个字符串";
String s4 = "第四个字符串";
String s5 = "第五个字符串";
// 利用多线程同时写入一个文件
new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据
new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据
new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据
new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据
new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据
}
// 利用线程在文件的指定位置写入指定数据
static class FileWriteThread extends Thread{
private int skip;
private byte[] content;
public FileWriteThread(int skip,byte[] content){
this.skip = skip;
this.content = content;
}
public void run(){
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("D://abc.txt", "rw");
raf.seek(skip);
raf.write(content);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
raf.close();
} catch (Exception e) {
}
}
}
}
}
网友评论