序列流:
SequenceInputStream:对多个流进行合并
转换流:
InputStreamReader:将字符输入流转换成字节输入流
OutputStreamReader:将字符输出流转换成字节输出流
常见的编码表:
ASCII:美国标准信息交换码,用一个字节的七位表示
ISO8859-1:拉丁码表,欧洲码表,用一个字节的八位表示
GB2312:中文编码表,用两个字节表示
GBK:中文编码表升级,融合录入更多的中文字符,用两个字节表示,为避免和老美重复两字节的最高位都是1,即汉字都是用负数表示
Unicode:国际标准码,融合了多种文字,所有文字都用两个字节表示
UTF-8:用一个字节到三个字节表示。
注:Unicode能识别中文,UTF-8也能识别中文,但两种编码表示一个汉字所用的字节数不同
Unicode用两个字节,UTF-8用三个字节,故涉及到编码转换。
在流中涉及编码表的转换只有转换流
代码示例:
publicstatic void write() throws IOException{
OutputStreamWriterosw1 = new OutputStreamWriter(newFileOutputStream("gbk.txt"),"GBK");
osw1.write("你好");
osw1.close();
OutputStreamWriterosw2 = new OutputStreamWriter(newFileOutputStream("utf-8.txt"),"UTF-8");
osw2.write("你好");
osw2.close();
}
publicstatic void read() throws IOException{
InputStreamReaderisr = new InputStreamReader(newFileInputStream("gbk.txt"),"GBK");
byte[]buf = new byte[1024];
intlen = isr.read(buf);
sop(newString(buf,0,len));
}
编码解码
编码:字符串变成字节数组:String-->getBytes()-->byte[]()
解码:字节数组变成字符串:byte[]-->new String(byte[],0,len)-->String
管道流:
PipedInputStream
PipedOutputStream
操作基本数据类型的流对象:DateStream
操作字节数组流:
ByteArrayInputStream
ByteArrayOutputStream
随机访问文件:RandomAccess
自身具备读写方法(又可以读又可以写)
通过skipByte(intx)和seek(int x)来达到随机访问文件
该类不是IO体系子类,而是直接继承Object,但它是IO包中的成员,因为它具备读写方法
该类内部封装了数组,而且通过指针对数组的元素进行操作,可以通过getFilePoint获取指针位置,同时可以通过seek改变指针位置
该类完成读写的原理是内部封装了字节输入输出流
构造方法:
RandomAccessFile(Filefile, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(Stringname, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
通过该类的构造看出,该类只能操作文件,而且操作的文件只能有固定模式:
"r":只读."rw":读写"rws":"red":
方法摘要:
void write(byte[]b) 将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。
voidwrite(byte[] b, int off, int len) 将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。
voidwrite(int b) 向此文件写入指定的字节。
intread() 从此文件中读取一个数据字节。
intread(byte[] b) 将最多 b.length 个数据字节从此文件读入 byte 数组。
int read(byte[]b, int off, int len) 将最多 len 个数据字节从此文件读入 byte 数组。
StringreadLine() 从此文件读取文本的下一行。
longgetFilePointer() 返回此文件中的当前偏移量。
longlength() 返回此文件的长度。
voidseek(long pos) 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
Properties:是IO流合集合相结合的集合容器
(1)Properties是HashTable的子类,具备Map集合的特点,里面存储的是键值对
(2)Properties的特点是可以用于存储键值对形式的配置文件
(3)构造方法:
Properties()
创建一个无默认值的空属性列表。
Properties(Propertiesdefaults)
创建一个带有指定默认值的空属性列表。
(4)方法摘要:
ObjectsetProperty(String key, String value) 调用 Hashtable 的方法 put。
StringgetProperty(String key) 用指定的键在此属性列表中搜索属性。
voidload(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
voidload(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
voidlist(PrintStream out) 将属性列表输出到指定的输出流。
voidlist(PrintWriter out) 将属性列表输出到指定的输出流。
voidstore(OutputStream out, String comments) 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
voidstore(Writer writer, String comments) 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。
Set<String>stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键
(5)Properties代码示例:
public staticvoid show(){
Propertiesprop = new Properties();
prop.setProperty("张三","26");
prop.setProperty("赵六","30");
prop.setProperty("孙八","35");
sop(prop);
Stringvalue = prop.getProperty("张三");
Set<String>keys = prop.stringPropertyName();
for(Stringkey : values){
sop(key+":"+prop.getPropety(key));
}
}
(6)记录应用程序的使用次数,如果使用次数已到,则提示用户注册。
思路:
第一次使用时建立一个配置文件用于记录使用次数
每次使用都加载该配置文件,并先判断已使用次数
每次使用完使用次数加1,写入配置文件
public staticvoid main(String[] args) throws IOException{
Propertiesprop = new Properties();//定义Properties,用来和IO流结合
File file= new File("library\\time.ini");//配置文件
if(!file.exists())
file.createNewFile();//如果文件不存在则创建文件(用于第一次使用时创建文件)
FileInputStreamfis = new FileInputStream(file);//定义字节读取流,读取配置文件中记录的使用次数
prop.load(fis);//载入流,以获取文件中配置的键值对
int count= 0;//定义使用次数
StringcountValue = prop.getProperty("time");//通过键获取值
if(countValue!=null){//第一次时countValue为null
count= Integer.parseInt(countValue);//将字符串次数变成数字次数
if(count>3){
System.out.println("您使用次数已到,继续使用请注册!");
return;
}
}
count++;//如果使用次数未到则次数加1
prop.setProperty("time",count+"");//配置新的键值对
FileWriterfos = new FileWriter(file);
prop.store(fos,"这是应用程序使用次数的配置文件");//将新的键值对写入文件
fis.close();
fos.close();
}
操作对象的流:
ObjectOutputStream:将对象序列化
ObjectInputStream:对硬盘上存储的对象反序列化
被操作的对象需要实现Serializable (标记接口)
对象序列化:
对象实体化:找一个介质,能长期的存储对象。
对象的属性在Java程序中,都是存在于对内存中,随着对象的消失而消失,而ObjectOutputStream可以将对象实体化
Serializable接口没有一个方法,也就是说其是一个标记接口。比如盖章的猪肉才是安全的。
只有实现Serializable接口的子类才能被ObjectOutputStream系列化写入流,当某个类实现该接口后,会被Java自动分配UID号,以便编译器识别,区分不同对象。
用ObjectOutputStream系列化的对象存储到文件后,该文件是乱码,也就是不可读的的用ObjectInputStream读取该类对象的属性。
由于对象是有Java给对象分配相应的UID号,而UID号是根据对象的属性不同而分配的。当一个类对象被系列化到文件后,如果该类改动了对象的属性,比如将某个成员变量变成私有则该对象再用ObjectInputStream读取时会报异常,也就是说该系列化到文件的对象不能再被使用了
那么,要想继续使用属性被改动后的对象,我们可以自定义给对象分配UID号,让UID号不随对象的属性变化而变化。
自定义对象分配UID方法如下:
publicstatic final long serialVersion UID = 43L;
注意:
静态不能被系列化,因为静态成员变量实在内存的方法区,而ObjectOutputStream只能对对内存里面的数据进行系列化被transient修饰的非静态成员变量也不能被系列化被系列化的对象存储到文件中,该文件是不可读的,所以该文件的扩展名一般不写成.txt,通常后缀名写.object
LineNumberReader:跟踪行号的缓冲字符输入流
构造方法:
LineNumberReader(Readerin) 使用默认输入缓冲区的大小创建新的行编号 reader。
方法摘要:
intread() 读取单个字符。
intread(char[] cbuf, int off, int len) 将字符读入数组中的某一部分。
StringreadLine() 读取文本行。
longskip(long n) 跳过字符。
intgetLineNumber() 获得当前行号。
voidsetLineNumber(int lineNumber) 设置当前行号。
跟踪行号的缓冲字符输入流。默认情况下,行编号从 0 开始。
构造: LineNumberReader(Reader in) 使用默认输入缓冲区的大小创建新的行编号 reader。
方法: getLineNumber() 获得当前行号。
setLineNumber(intlineNumber) 设置当前行号。
class MyLineNumberReader{
<span style="white-space:pre"> </span>private Reader r;
<span style="white-space:pre"> </span>private intlineNumber;
<span style="white-space:pre"> </span>MyLineNumber(Readerr){----->super(r);
<span style="white-space:pre"> </span>this.r=r;
<span style="white-space:pre"> </span>}public StringmyReadLine() throws IOException{------>return super.myReadLine();
<span style="white-space:pre"> </span>lineNumber++;
<span style="white-space:pre"> </span>StringBuilder sb= new StringBuilder();
<span style="white-space:pre"> </span>int ch = 0;
<span style="white-space:pre"> </span>while((ch=r.read())!=-1){
<span style="white-space:pre"> </span>if(ch==’\r’)
<span style="white-space:pre"> </span>continue;
<span style="white-space:pre"> </span>if(ch==’\n’){
<span style="white-space:pre"> </span>return sb.toString();
<span style="white-space:pre"> </span>}else{
<span style="white-space:pre"> </span>sb.append((char)ch);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>if(sb.length()!=0){
<span style="white-space:pre"> </span>return sb.toString();}
<span style="white-space:pre"> </span>return null;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public voidgetLineNumber(){
<span style="white-space:pre"> </span>Return lineNumber;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public voidsetLineNumber(int lineNumber){
<span style="white-space:pre"> </span>this.lineNumber = llineNumber;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public voidmyClose(){---->删
<span style="white-space:pre"> </span>r.close();//关闭流
<span style="white-space:pre"> </span>}
}
练习:
//读取一个.java文件,并打印在控制台上。
//将C盘一个文本文件复制到D盘。
--->复制原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
--->步骤: 1、在D盘创建一个文件、用于存储C盘文件中的数据。
2、定义读取流和C盘文件关联。
3、通过不断的读写完成数据的存储。
4、关闭资源。
①
public staticvoid copy_1() throws IOException{
<span style="white-space:pre"> </span>//创建目的地
<span style="white-space:pre"> </span>FileWriter fw = new FileWriter(“Test.txt”);
<span style="white-space:pre"> </span>//与已有文件关联
<span style="white-space:pre"> </span>FileReader fr = new FileReader(“Test.java”);
<span style="white-space:pre"> </span>int ch = 0;
<span style="white-space:pre"> </span>while((ch=fr.read())!=-1){
<span style="white-space:pre"> </span>fw.write(ch);
<span style="white-space:pre"> </span>}fw.close();fr.close();
}
②
<span style="white-space:pre"> </span>public static void copy_2{
<span style="white-space:pre"> </span>FileWriter fw = null;
<span style="white-space:pre"> </span>new Filewriter;
<span style="white-space:pre"> </span>FileReader fr = null;
<span style="white-space:pre"> </span>char[] buf = new char[1024];int len = 0;
<span style="white-space:pre"> </span>while((len=fr.rad(buf))!=-1){
<span style="white-space:pre"> </span>fw.write(buf,0,len);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>catch(IOException e){
<span style="white-space:pre"> </span>throw new RuntimeException(“读写失败”);
<span style="white-space:pre"> </span>}finally{
<span style="white-space:pre"> </span>if(fr!=null)try{fr.close();}catch(IOExceptione){}
<span style="white-space:pre"> </span>if(fw!=null)try{fw.close();}catch(IOExceptione){}
<span style="white-space:pre"> </span>}
[下为图例:]
流操作的规律:
流操作的难点:流对象很多,不知道具体用哪个
规律:
(1)第一步:先明确源和目的
源:
文本:用Reader
字节:用InputStream
目的:
文本:用Writer
字节:用OutputStream
(2)第二步:明确是不是纯文本
是:用字符流;
不是:用字节流
(3)第三步:明确流体系后,通过设备来明确具体使用哪个流对象
源设备:
键盘:System.in
硬盘:文件流File
内存:数组流ArrayStream
目的设备:
键盘:System.out
硬盘:文件流File
内存:数组流ArrayStream
模拟readLine();代码:
public String myReadLine() throwsIOException{//谁调用谁处理
//定义一个临时容器,原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;
}
public voidmyClose() throws IOException{
r.close();
}[
File类的常用:
(1)文件名过滤:列出给定目录的所有.java文件
public void showFileName(File file){
String[] filenames = file.list(new FilenameFilter()//匿名内部类{
public boolean accept(File dir,String name)//复写唯一方法{
return name.endsWith(".java");//列出所有.java文件
}
});
}
(2)列出指定目录下的所有文件和文件夹(递归)
1:不带层次递归:
public static void showDir(File dir){
<span style="white-space:pre"> </span>File[] files = dir.listFile();
<span style="white-space:pre"> </span>for(int i = 0;i<files.length;i++){
<span style="white-space:pre"> </span>if(files[i].isDirectory&&!files[i].isHidden())
<span style="white-space:pre"> </span>showDir(files[i]);
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>sop(files[i]);
<span style="white-space:pre"> </span>}
}
2:带层次递归:
public static void showDir(File dir,int level){
<span style="white-space:pre"> </span>sop(getLevel(level)+C);//进来先打印层次和目录
<span style="white-space:pre"> </span>level++;
<span style="white-space:pre"> </span>File[] files = dir.listFile();
<span style="white-space:pre"> </span>for(int i = 0;i<files.length;i++){
<span style="white-space:pre"> </span>if(files[i].isDirectory&&!files[i].isHidden()){
<span style="white-space:pre"> </span>showDir(files[i]);
<span style="white-space:pre"> </span>{else{
<span style="white-space:pre"> </span>sop(getLevel(level)+files[i]);//是文件就打印层次和目录
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
}
public static String getLevel(int level){
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>StringBuilder sb = new StringBuilder();
<span style="white-space:pre"> </span>for(int i=0;i<level;i++){
<span style="white-space:pre"> </span>sb.inset(0."| ")
<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>return sb.toString();
}
(3)删除带内容的目录:
public static void removeDir(File dir){
File[] files = file.listFile();
for(int i = 0;i<files.length;i++){
if(files[i].isDirectory&&!files[i].isHidden())
removeDir(files[i]);//如果是文件夹则继续调用函数
else//如果是文件则删除。注意删除的时候打印删除的结果,防止误删或者重删的情况
sop(files[i].toString()+"::"+files[i].delete());
}
sop(dir+"::"+dir.delete());
}
(4)将制定目录下的java文件的绝对路径存储到文本文件中。
思路:
对指定目录进行递归
获取递归过程中所有java文件的路径
将这些路径存储到集合中
将集合中的数据写入文件中
//对指定目录进行递归并将所以Java文件存储到集合中
public static void getFileName(File file,ArrayList<File> arraylist){
<span style="white-space:pre"> </span>File[] files = file.listFiles();
<span style="white-space:pre"> </span>for (int i = 0; i < files.length; i++) {
<span style="white-space:pre"> </span>if(files[i].isDirectory()&&!files[i].isHidden()){
<span style="white-space:pre"> </span>getFileName(files[i],arraylist);
<span style="white-space:pre"> </span>}else{
<span style="white-space:pre"> </span>if(files[i].getName().endsWith(".java")){
<span style="white-space:pre"> </span>arraylist.add(files[i]);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
}
//将集合中所有数据存储到新文件中
public static void saveFileToNewDir(ArrayList<File> arraylist,File newDir){
BufferedWriter bufw = null;
try {
<span style="white-space:pre"> </span>bufw = new BufferedWriter(new FileWriter(newDir));
<span style="white-space:pre"> </span>for (File file : arraylist) {
<span style="white-space:pre"> </span>String fileAbsolutePath = file.getAbsolutePath();
<span style="white-space:pre"> </span>bufw.write(fileAbsolutePath);
<span style="white-space:pre"> </span>bufw.newLine();
<span style="white-space:pre"> </span>bufw.flush();
<span style="white-space:pre"> </span>}
} catch (Exception e) {
<span style="white-space:pre"> </span>System.out.println("文件写入失败");
}finally{
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>if(bufw!=null)
<span style="white-space:pre"> </span>bufw.close();
<span style="white-space:pre"> </span>} catch (Exception e2) {
<span style="white-space:pre"> </span>System.out.println("文件写入流关闭失败");
<span style="white-space:pre"> </span>}
}
}
Properties代码示例:
public static void show(){
<span style="white-space:pre"> </span>Properties prop = new Properties();
<span style="white-space:pre"> </span>prop.setProperty("张三","34");
<span style="white-space:pre"> </span>prop.setProperty("赵六","34");
<span style="white-space:pre"> </span>prop.setProperty("孙八","44");
<span style="white-space:pre"> </span>sop(prop);
<span style="white-space:pre"> </span>String value = prop.getProperty("张三");
<span style="white-space:pre"> </span>Set<String> keys = prop.stringPropertyName();
<span style="white-space:pre"> </span>for(String key : values){
<span style="white-space:pre"> </span>sop(key+":"+prop.getPropety(key));
<span style="white-space:pre"> </span>}
}
(7)记录应用程序的使用次数,如果使用次数已到,则提示用户注册。
思路:
第一次使用时建立一个配置文件用于记录使用次数
每次使用都加载该配置文件,并先判断已使用次数
每次使用完使用次数加1,写入配置文件
public static void main(String[] args) throws IOException{
Properties prop = new Properties();//定义Properties,用来和IO流结合
File file = new File("library\\time.ini");//配置文件
if(!file.exists())
file.createNewFile();//如果文件不存在则创建文件(用于第一次使用时创建文件)
FileInputStream fis = new FileInputStream(file);//定义字节读取流,读取配置文件中记录的使用次数
prop.load(fis);//载入流,以获取文件中配置的键值对
int count = 0;//定义使用次数
String countValue = prop.getProperty("time");//通过键获取值
if(countValue!=null){//第一次时countValue为null
count = Integer.parseInt(countValue);//将字符串次数变成数字次数
if(count>3){
System.out.println("您使用次数已到,继续使用请注册!");
return;
}
}
count++;//如果使用次数未到则次数加1
prop.setProperty("time", count+"");//配置新的键值对
FileWriter fos = new FileWriter(file);
prop.store(fos, "这是应用程序使用次数的配置文件");//将新的键值对写入文件
fis.close();
fos.close();
}