1.字节流与字符流
在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据时要使用输入流读取数据,当程序要将一些数据保存起来时,就要使用输出流。
JAVA IO包中,流的操作主要有字节流和字符流两大类,这两类都有输入和输出操作。字节流的顶级父类是InputStream,和OutputStream。字符流的顶级父类是Reader和Writer.这两个体系有个规律,字节流的子类都是以Stream结尾的,而字符流的子类是以Reader或者Writer结尾的。
1.1字节流的使用
一、字节输出流OutputStream
OutputStream是字节输出流的最大父类,如果想使用这个类必须要使用该类的子类。如果操作文件一般使用FileOutputStream类,OutputStream类中主要的操作方法有下面几种:
public void close() throws IOException
public void flush() throws IOException
public void write(byte[] b) throws IOException
public void write(byte[] b,int off,int len) throws IOException
public abstract void write(int b) throws IOException
此时使用FileOutputStream子类,构造方法如下:
public FileOutputStream(File file) throws FileNotFoundExcepton
public FileOutputStream(String path) throws FileNotFoundExcepton
操作时必须接受File类的实例,或指明输出的文件路径。
向文件中写入字符串示例代码:
public class Test {
public static void main(String[] args) throws IOException {
//第一部实例化一个File对象
File f = new File("d:/test.txt");
//通过子类实例化父类对象
OutputStream os = new FileOutputStream(f);
//进行写操作
String str = "hello world";
byte[] b = str.getBytes();
os.write(b);
//关闭资源
os.close();
}
}
运行结果会发现内容已经成功的写在了文件中。但是有个问题,多次运行之后会发现文件中始终只有一个hello world也就是说,写的操作每次都覆盖了之前的内容。
要想在文件末尾追加内容要用到下面的构造函数:
public FileOutputStream(String name, boolean append) throws FileNotFoundException
也就是说在实例化OutputStream时,改为
OutputStream os = new FileOutputStream(f, true);
这样就可以在文件末尾追加内容了。
二、字节输入流
当程序需要从文件中读取内容的时候就用到了输入流,与OutputStream一样,InputStream是个抽象类,必须依靠子类进行实例化,如果是读取文件,子类可以用FileInputStream, 常用方法如下:
public int available() throws IOException //可以取得输入文件的大小
public void close() throws IOException //关闭输入流
public abstract int read() throws IOException //读取内容,以数字的方式读取
public int read(byte[] b) throws IOException //将内容读到byte数组中,同时返回读入的个数
示例,从文件中读取内容:
public class Test {
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("d:/abc/abc.txt");
byte[] b = new byte[1024];
int len = is.read(b);
is.close();
System.out.println(new String(b,0,len));
}
}
程序运行结果为:
hello world
结合输入输出流复制mp3例子:
public static void copyMp3() throws IOException{
InputStream is = new FileInputStream("d:/1.mp3"); //定义输入流并绑定要拷贝的mp3
OutputStream os = new FileOutputStream("d:/2.mp3"); //定义输出流并制定拷贝到的路径和文件名称
byte[] b = new byte[1024]; //定义缓冲数组大小,一般是1024的整数倍
int len = 0;
while((len = is.read(b))!=-1){
os.write(b, 0, len); //将读取的数据直接写入到目标文件
}
is.close(); //关闭资源
os.close();
}
1.2字符流的使用
在java中一个字符等于两个字节,java提供了Reader和Writer两个专门操作字符流的类。需要明确的一点是,字符流只能操作纯文本文件。
如果想从一个文本文件读取内容可以用FileReader类,例如:
public static void read() throws IOException{
Reader reader = new FileReader("d:/test.txt");
char[] c = new char[1024];
int len = 0;
int count = 0;
while((len = reader.read())!=-1){
c[count] = (char) len;
count++;
}
System.out.println(new String(c));
reader.close();
}
但是这样读取效率比较慢,可以自己定义一个缓冲区,每次让读取出的数据存在缓冲区内,然后再从缓冲区内取数据,例:
public static void read() throws IOException{
Reader reader = new FileReader("d:/test.txt");
char[] b = new char[1024];
int len = reader.read(b);
System.out.println(new String(b,0,len));
reader.close();
}
向文件中写入数据可以使用FileWriter类:
public static void write() throws IOException{
Writer writer = new FileWriter("d:/test.txt");
String str = "java";
char[] c = str.toCharArray();
writer.write(c);
writer.close();
}
程序运行之后发现字符串成功的写入到文件中去了。
BufferedReader和BufferedWriter
BufferedReader类用于从缓冲区中读取数据,所有输入字节数据都将放在缓冲区中,此类还增加了readLine()方法,实现了一次读取一行紫字符的功能。例如:
public static void read() throws IOException{
Reader r = new FileReader("d:/test.txt");
BufferedReader br = new BufferedReader(r);
String str = null;
int count = 0;
while((str = br.readLine())!=null){
count++;
System.out.println("第"+count+"行内容是:"+str);
}r.close;br.close;
}
上面的例子就使用了BufferedReader类的readLine方法
输出内容为:
第1行内容是:abc
第2行内容是:def
第3行内容是:ghi
第4行内容是:xyz
第2行内容是:def
第3行内容是:ghi
第4行内容是:xyz
BufferedRead和BufferedWriter结合使用实现的文件复制功能:
public static void writeMethod2() throws IOException{
Reader r = new FileReader("d:/test.txt");
Writer w = new FileWriter("d:/test0.txt");
BufferedReader br = new BufferedReader(r);
BufferedWriter bw = new BufferedWriter(w);
String str = null;
int count = 0;
while((str = br.readLine())!=null){
count++;
bw.write(str);
bw.flush();
System.out.println("第"+count+"行内容是:"+str);
}
r.close();
w.close();
br.close();
bw.close();
}
BufferedRead和BufferedWriter由于使用了缓存,所以用它们读写文件效率非常高。
2.转换流
整个IO包分为字节流和字符流,但是除了这两个流之外还有一组字节流-字符流的转换类。
OutputStreamWriter:是Writer的子类,将输入的字节流变成字符流,即将一个字节流输出对象变成字符流输出对象。
InoutStreamReader:是Reader的子类,将输入的字节流变成字符流,即将一个字节流输入对象变成字符流输入对象。
例如,向一个文件写数据使用转换流的方式为:
public static void test() throws IOException{
OutputStream os = new FileOutputStream("d:/test.txt");
Writer w = new OutputStreamWriter(os);
String str = "hello world";
w.write(str);
os.close();
w.close();
}
实现了从字节流到字符流的转换。
从一个文件中读取信息使用转换流的方式为:
public static void test() throws IOException{
InputStream is = new FileInputStream("d:/test.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = null;
while((str = br.readLine())!=null){
System.out.println(str);
}
}
实现了从字节输入流到字符输入流的转换。
3.流的选择
当流的对象很多不知道该用哪个的时候,一般通过两个来明确:
1. 明确源和目的
源就是输入流:InputStream, Reader
目的是输出流:OutputStream, Writer
2.操作的数据是否为纯文本
2.操作的数据是否为纯文本
是:字符流
否:字节流
当体系明确后在明确要使用哪个具体的对象,通过设备区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
Reader体系中可以操作文件的对象是FileReader
Writer体系中可以操作文件的是FileWriter
如需提高效率,使用加入了缓冲区的BufferedReader
同理还有BufferedWriter
分析例子:将键盘录入信息输入一个文件
源:InputStream Reader
是纯文本:Reader
键盘录入对应的对象是System.in需要把字节流转换为字符流,所以要用到转换流:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
4.Properties文件
j
ava中的.properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是"键=值"的格式。
Properties类的重要方法
Properties 类存在于胞 Java.util 中,该类继承自 Hashtable
1. getProperty ( String key) , 用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到 key 所对应的 value。
2. load ( InputStream inStream) ,从输入流中读取属性列表(key和value)。通过对指定的文件进行装载来获取该文件中的所 有键 - 值对。以供 getProperty ( String key) 来搜索。
3. setProperty ( String key, String value) ,调用 Hashtable 的方法 put 。他通过调用基类的put方法来设置 键 - 值对。
4. store ( OutputStream out, String comments) , 以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键 - 值对写入到指定的文件中去。
Properties 类存在于胞 Java.util 中,该类继承自 Hashtable
1. getProperty ( String key) , 用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到 key 所对应的 value。
2. load ( InputStream inStream) ,从输入流中读取属性列表(key和value)。通过对指定的文件进行装载来获取该文件中的所 有键 - 值对。以供 getProperty ( String key) 来搜索。
3. setProperty ( String key, String value) ,调用 Hashtable 的方法 put 。他通过调用基类的put方法来设置 键 - 值对。
4. store ( OutputStream out, String comments) , 以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键 - 值对写入到指定的文件中去。
现在假设在src目录下有个名字为test.properties的文件,内容为:
key1=value1
key2=value2
读取配置文件的程序为:
public static void test() throws IOException{
Properties pro = new Properties();
InputStream is = new FileInputStream("src/test.properties");
pro.load(is);
System.out.println("key1对应的值为:"+pro.getProperty("key1"));
System.out.println("key2对应的值为:"+pro.getProperty("key2"));
is.close();
}
运行结果为:
key1对应的值为:value1
key2对应的值为:value2
key2对应的值为:value2
如果想改变其中某个key值所对应的属性可以这样:
public static void test() throws IOException{
Properties pro = new Properties();
InputStream is = new FileInputStream("src/test.properties");
pro.load(is);
pro.setProperty("key1", "hello world");
System.out.println("key1对应的值为:"+pro.getProperty("key1"));
System.out.println("key2对应的值为:"+pro.getProperty("key2"));
is.close();
}
这样程序运行结果为:
key1对应的值为:hello world
key2对应的值为:value2
key2对应的值为:value2
达到了改变某个key所对应的值。