您的位置:首页 > 职场人生

黑马程序员——java编程那些事儿____IO流(二)

2013-03-18 08:59 211 查看
-------android培训java培训、期待与您交流!
----------

file类中涉及一个知识点递归,很重要

一、File类

1、File类概述

用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,

File对象可以作为参数传递给流的构造函数

2、File的类构造方法与方法摘要

构造方法:

File(String pathname)

通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

File(String parent, String child)

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

File(File parent, String child)

根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

方法摘要:

(1)创建:

boolean createNewFile()

当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。

boolean mkdir()

创建一级文件夹

boolean mkdirs()

创建多级文件夹

2(判断):

boolean canExecute()

测试应用程序是否可以执行此抽象路径名表示的文件。

boolean canRead()

测试应用程序是否可以读取此抽象路径名表示的文件。

boolean canWrite()

测试应用程序是否可以修改此抽象路径名表示的文件。

int compareTo(File pathname)

按字母顺序比较两个抽象路径名。

boolean isAbsolute()

测试此抽象路径名是否为绝对路径名。

boolean isDirectory()

测试此抽象路径名表示的文件是否是一个目录。

boolean isFile()

测试此抽象路径名表示的文件是否是一个标准文件。

boolean isHidden()

测试此抽象路径名指定的文件是否是一个隐藏文件。

boolean exists()

测试此抽象路径名表示的文件或目录是否存在。

(3)获取:

String getParent()

返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。

File getParentFile()

返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。

String getName()

返回由此抽象路径名表示的文件或目录的名称。

String getPath()

将此抽象路径名转换为一个路径名字符串。

String getAbsolutePath()

返回此抽象路径名的绝对路径名字符串。

File getAbsoluteFile()

返回此抽象路径名的绝对路径名形式。

(4)删除:

boolean delete()

删除此抽象路径名表示的文件或目录。

oid deleteOnExit()

在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。

import java.io.*;
class FileDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args) throws IOException
{
//consMethod();
//method_1();
//method_2();
//method_3();
//method_4();
method_5();
}

public static void method_5()
{
File f1 = new File("file.txt");
File f2 = new File("c:\\file-1.txt");
sop(f1.renameTo(f2));
}

public static void method_4()
{
File f = new File("file.txt");

sop(f.getPath());
sop(f.getAbsolutePath());
sop(f.getParent());
}

//判断是否是文件,是否是目录
public static void method_3()
{
File f = new File("file.txt");

//先得判断该文件对象封装的内容是否存在,通过exists判断
sop(f.isDirectory());//false
sop(f.isFile());//false

}

//判断
public static void method_2()
{
File f = new File("File.txt");

//sop(f.canExecute());//测试应用程序是否可以执行此抽象路径名表示的文件。
sop(f.exists());

sop(f.isAbsolute());//测试此抽象路径名是否为绝对路径名。

}

//创建和删除
public static void method_1()throws IOException
{
File f = new File("file.txt");
//f.deleteOnExit();//退出时删除
sop(f.createNewFile());
sop(f.delete());

//创建文件夹(创建目录)
File dir = new File("abc");
sop(dir.mkdir());//创建一级目录
//sop(dir.mkdirs());//创建多级目录

}

public static void consMethod()
{
//将a.txt封装成File对象,可以将已有和出现的文件或者文件夹封装成对象

File f1 = new File("a.txt");//f1:a.txt
//File f1 = new File("c:\\cac\\a.txt");//f1:c:\cac\a.txt

File f2 = new File("c:\\abc","a.txt");//f2:c:\abc\a.txt

File d = new File("c:\\abc");
File f3 = new File(d,"c.txt");//f3:c:\abc\c.txt

File f4 = new File("c:"+File.separator+"zzz"+File.separator+"d.txt");//f4:c:\zzz\d.txt

sop("f1:"+f1);
sop("f2:"+f2);
sop("f3:"+f3);
sop("f4:"+f4);

}
}


(5)获取全部:(非常重要!!!)

String[] list()

返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。

String[] list(FilenameFilter filter)

返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。

File[] listFiles()

返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。

File[] listFiles(FileFilter filter)

返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。

****FilenameFilter接口只有一个方法:

boolean accept(File dir, String name)

测试指定文件是否应该包含在某一文件列表中。

****FileFilter接口只有一个方法:

boolean accept(File dir, String name)

测试指定文件是否应该包含在某一文件列表中。

import java.io.*;
class FileDemo2
{
public static void main(String[] args)
{
//listRootsDemo();
//listDemo();
//listDemo_2();
listDemo_3();

}

public static void listDemo_3()
{
//listFiles()   返回一个抽象路径名数组,获取该路劲下的所有目录

File dir = new File("c:\\");
File[] files = dir.listFiles();
for(File f : files)
{
System.out.println(f.getName()+"::"+f.length());
}

}

public static void listDemo_2()
{
//list(FilenameFilter filter)  返回一个字符串数组,这些字符串指定此抽象路径
//名表示的目录中满足指定过滤器的文件和目录。

File dir = new File("d:\\");

String[] arr = dir.list(new FilenameFilter()//匿名内部类
{
public boolean accept(File dir,String name)
{
return name.endsWith(".txt");
}
});
}

public static void listDemo()
{
//list()  返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的所有文件和目录。
//注意,调用list方法的file对象必须是封装了一个目录,而且该目录必须存在
File f = new File("c:\\");
String[] names = f.list();

for(String name : names)
{
System.out.println(name);
}
}

public static void listRootsDemo()
{
//listRoots()  列出可用的文件系统根。

File[] files = File.listRoots();

for(File f : files)
{
System.out.println(f);
}
}
}


3、递归

举例定义递归

列出目录下文件或者文件夹,包含子目录中的内容。也就是列出指定目录下的所有内容

因为目录中还有目录,只要使用同一个列出目录功能的函数listFiles()完成即可。在列出的过程中

如果还是目录的话,还可以再次调用本功能,也就是函数自身调用自身。

这种表现形式或者编程手法叫做递归

例如 事例二 带层次递归 FileDemo3.java

递归要注意

1.限定条件

2.要注意递归的次数,尽量避免内存溢出

举例说明

class DemoDG
{
public static void main(String[] args)
{
toBin(6);

//int n = getSum(100);
//System.out.println("n="+n);

}

public static int getSum(int n)
{
if(n==1)
return 1;
return n+getSum(n-1);
}

public static void toBin(int num)
{
if(num>0)
{
toBin(num/2);
System.out.println(num%2);
}
}
}


画图说明



4、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)
{
File[] files = dir.listFile();
for(int i = 0;i<files.length;i++)
{
if(files[i].isDirectory&&!files[i].isHidden())
showDir(files[i]);
else
System.out.println(files[i]);
}
}


**示例2:带层次递归:

import java.io.*;
class FileDemo3
{
public static void main(String[] args)
{
File dir = new File("d:\\myjava\\day19");
showDir(dir,0);
}
public static void showDir(File dir,int level)
{
sop(getLevel(level)+C);//进来先打印层次和目录
level++;
File[] files = dir.listFile();
for(int i = 0;i<files.length;i++)
{
if(files[i].isDirectory&&!files[i].isHidden())
showDir(files[i]);
else
System.out.println(getLevel(level)+files[i]);//是文件就打印层次和目录
}
}
public static String getLevel(int level)
{
sop("|--");
StringBuilder sb = new StringBuilder();
for(int i=0;i<level;i++)
{
sb.inset(0."|  ")
}
return sb.toString();
}
}


(3)需求:删除带内容的目录:

import java.io.*;
class RemoveDir
{
  public static void main(String[] args)
  {
  File dir = new File(“d:\\a.txt”);
  removeDir(dir);
  }
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//如果是文件则删除。注意删除的时候打印删除的结果,防止误删或者重删的情况
System.out.println(files[i].toString()+"::"+files[i].delete());
}
System.out.println(dir+"::"+dir.delete());
}
}


(4)需求:将制定目录下的java文件的绝对路径存储到文本文件中。

package blog.csdn.wz;

/*
练习
将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。
建立一个java文件列表文件。

思路:
1,对指定的目录进行递归。
2,获取递归过程所有的java文件的路径。
3,将这些路径存储到集合中。
4,将集合中的数据写入到一个文件中。

*/

import java.io.*;
import java.util.*;
class  Test11{

public static void main(String[] args) throws IOException{

//指定一个路径(前提是存在)
File dir = new File("d:\\java");
//定义一个list集合
List<File> list = new ArrayList<File>();
fileToList(dir,list);
System.out.println(list.size());

File file = new File(dir,"javalist.txt");
writeToFile(list,file.toString());

}
//将文件存到集合当中
public static void fileToList(File dir,List<File> list)
{
//调用file对象的listFiles()方法
File[] files = dir.listFiles();
//用高级for循环来获取该路径下的所有目录
for(File file : files)
{
//如果遍历到目录的话就继续递归
if(file.isDirectory()&&!file.isHidden())
fileToList(file,list);
else
{
//如果是.java文件的话就把文件对象添加到list集合中
if(file.getName().endsWith(".java"))
list.add(file);
}
}
}
//将存入集合中的文件写到javalist.txt文件中
public static void writeToFile(List<File> list,String javaListFile)throws IOException
{
//为了高效加入缓冲
BufferedWriter bufw =  null;
try
{
bufw = new BufferedWriter(new FileWriter(javaListFile));//关联一个文件javaListFile
//遍历列表
for(File f : list)
{
String path = f.getAbsolutePath();//获取每个文件的绝对路径
bufw.write(path);
bufw.newLine();
bufw.flush();
}

}
catch (IOException e)
{
throw e;
}
finally
{
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw e;
}
}
}
}


(5)需求:编写一个程序,将d:\java 目录下的所有.java 文件复制到d:\jad 目录下

package com.itheima;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
class  Test9{

public static void main(String[] args) throws Exception{

//指定一个路径d:\java(前提是存在这个目录)
File srcDir = new File("d:\\java");
//定义一个list集合
List<File> list = new ArrayList<File>();
fileToList(srcDir,list);
System.out.println(list.size());//验证一下执行了木有

}
public static void fileToList(File srcDir,List<File> list)throws Exception{

//调用file对象的listFiles()方法
File[] files = srcDir.listFiles();
//遍历该路径下的所有目录
for(File file : files){
//如果遍历到目录的话就继续递归
if(file.isDirectory()&&!file.isHidden())
fileToList(file,list);
else
{
//如果是.java文件的话就把文件对象添加到list集合中
if(file.getName().endsWith(".java"))
list.add(file);
}
}

//指定一个要存文件的路径
File destDir = new File("d:\\jad");
//如果该路径不存在需要创建
if(!destDir.exists())
destDir.mkdir();
//遍历集合中的路径
for(File file :list){
//定义一个读取流,来读取数据
FileInputStream fis = new FileInputStream(file);
//将得到的.java文件都改为.jad文件
String destFileName = file.getName().replaceAll(".java", ".jad");
//定义一个文件写入流,将读取到的文件写入到指定目录
FileOutputStream fos = new FileOutputStream(new File(destDir,destFileName));

int len = 0;
byte[] buf = new byte[1024];
//循环读写
while((len = fis.read(buf)) != -1){
fos.write(buf,0,len);
}
fis.close();//关闭读取流
fos.close();//关闭输出流
}
}

}


二、Properties类(map+IO)

(1)Properties是HashTable的子类,具备Map集合的特点,里面存储的是键值对

(2)Properties是IO流合集合相结合的集合容器

(3)Properties的特点是可以用于存储键值对形式的配置文件

(4)构造方法:

Properties()

创建一个无默认值的空属性列表。

Properties(Properties defaults)

创建一个带有指定默认值的空属性列表。

(5)方法摘要:

Object setProperty(String key, String value)

调用 Hashtable 的方法 put。

String getProperty(String key)

用指定的键在此属性列表中搜索属性。

void load(InputStream inStream)

从输入流中读取属性列表(键和元素对)。

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 表中的

属性列表(键和元素对)写入输出字符。

Set<String> stringPropertyNames()

返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中

未找到同名的键,则还包括默认属性列表中不同的键

import java.io.*;
import java.util.*;
class PropertiesDemo
{
public static void main(String[] args)throws IOException
{
//setAndGet();
//method_1();
loadDemo();

}

public static void loadDemo()throws IOException
{
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");

//将流中的数据加载进集合
prop.load(fis);

prop.setProperty("jlahf","5");//改变值
FileOutputStream fos = new FileOutputStream("info.txt");//将改过的值保存到文件中
prop.store(fos,"hahaha");//注释

//System.out.println(prop);
prop.list(System.out);// 将属性列表输出到指定的输出流。

fos.close();
fis.close();
}

//将流中的数据存储到集合中  将info.txt中的键值数据存到集合中进行操作
/*
1,用一个流和info.txt文件关联
2,读取一行数据,将该行数据用“=”进行分割
3,等号左边作为键,右边作为值,存入到Properties集合中即可
*/
public static void method_1()throws IOException
{
BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
String line = null;

Properties prop = new Properties();
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");
//System.out.println(arr[0]+"..."+arr[1]);
prop.setProperty(arr[0],arr[1]);
}

bufr.close();

System.out.println(prop);
}

//设置和获取
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

//获取全部
Set<String> names = prop.stringPropertyNames();
for(String s : names)
{
System.out.println(s+":"+prop.getProperty(s));
}

}
}


(6)Properties练习
package blog.csdn.wz;

/*

需求:用于记录应用程序运行次数,如果使用次数已到,给出注册提示

分析:

一般就是计数器,但是随着应用程序的启动运行到结束,计数器也会在内存中存在并且
运行自增,最后消失。下一次启动该程序,又重新开始从0计数。

所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式,
这样便于阅读数据,并操作数据。

键值对数据是map集合。数据是以文件形式存储使用io技术。
那么map+io就是Properties

配置文件可以实现应用程序数据的共享

思路:

第一次使用时建立一个配置文件用于记录使用次数
每次使用都加载该配置文件,并先判断已使用次数
每次使用完使用次数加1,写入配置文件

*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
class Test3
{

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>=5){
System.out.println("您使用次数已到,继续使用请注册!");
return;
}
}

count++;//如果使用次数未到则次数加1
prop.setProperty("time", count+"");//配置新的键值对

FileOutputStream fos = new FileOutputStream(file);

prop.store(fos, "这是应用程序使用次数的配置文件");//将新的键值对写入文件

fis.close();
fos.close();
}
}


三、IO中的其他流

(1)打印流:

**PrintWriter:字符打印流

****构造方法:

PrintWriter(String fileName)

创建具有指定文件名称且不带自动行刷新的新 PrintWriter。

PrintWriter(File file)

使用指定文件创建不具有自动行刷新的新 PrintWriter。

PrintWriter(Writer out)

创建不带自动行刷新的新 PrintWriter。

PrintWriter(Writer out, boolean autoFlush)

自动刷新

PrintWriter(OutputStream out)

根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。

PrintWriter(OutputStream out, boolean autoFlush)

自动刷新

****方法摘要:

PrintWriter append(char c)

将指定字符添加到此 writer。

void close()

关闭该流并释放与之关联的所有系统资源。

void flush()

刷新该流的缓冲。

void print(Object obj)

打印对象。

void print(String s)

打印字符串。

void println()

通过写入行分隔符字符串终止当前行。

**PrintStream:字节打印流

****构造方法:

PrintStream(String fileName)

创建具有指定文件名称且不带自动行刷新的新打印流。

PrintStream(File file)

创建具有指定文件且不带自动行刷新的新打印流。

PrintStream(OutputStream out)

创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush)

创建新的打印流。

****方法摘要:

PrintWriter append(char c)

将指定字符添加到此 writer。

void close()

关闭该流并释放与之关联的所有系统资源。

void flush()

刷新该流的缓冲。

void print(Object obj)

打印对象。

void print(String s)

打印字符串。

void println()

通过写入行分隔符字符串终止当前行。

/*
打印流
该流提供了打印方法,可以将各种数据类型的数据原样打印

字节打印流 PrintStream
构造函数可以接收的参数类型
1,file对象 File
2,字符串路径 String
3,字节输出流 OutputStream

字符打印流 PrintWriter
构造函数可以接收的参数类型
1,file对象 File
2,字符串路径 String
3,字节输出流 OutputStream
4,字符输出流 Writer
*/

import java.io.*;
class  PrintStreamDemo
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//PrintWriter out = new PrintWriter(System.out,true);//加个true自动刷新
PrintWriter out = new PrintWriter(new FileWriter("ppp.txt"),true);

String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase());
//out.flush();//有个true就不用这句了
}
out.close();
bufr.close();
}
}


(2)合并流 SequenceInputStream
/*
SequenceInputStream合并流

SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,
并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,
依次类推,直到到达包含的最后一个输入流的文件末尾为止。
*/
import java.io.*;
import java.util.*;
class SequenceDemo
{
public static void main(String[] args) throws IOException
{
Vector<FileInputStream> v = new Vector<FileInputStream>();
v.add(new FileInputStream("1.txt"));//将文件存入集合中
v.add(new FileInputStream("2.txt"));
v.add(new FileInputStream("3.txt"));

/*
通过记住参数来初始化新创建的 SequenceInputStream,
该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
将按顺序读取由该枚举生成的输入流,以提供从此 SequenceInputStream 读取的字节。
在用尽枚举中的每个输入流之后,将通过调用该流的 close 方法将其关闭。
*/
Enumeration<FileInputStream> en = v.elements();
SequenceInputStream sis = new SequenceInputStream(en);

FileOutputStream fos = new FileOutputStream("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();

}
}


练习:切割与合并文件
import java.io.*;
import java.util.*;
class SplitFile
{
public static void main(String[] args) throws IOException
{
//splitFile();
merge();
}

//合并文件
public static void merge() throws IOException
{
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<=748;x++)
{
al.add(new FileInputStream("c:\\splitfile\\"+x+".part"));
}

final Iterator<FileInputStream> it = al.iterator();//从内部类中访问本地变量it; 需要被声明为最终类型

Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
{
/*
Enumeration<E>类中的方法hasMoreElements()
boolean hasMoreElements()测试此枚举是否包含更多的元素
*/
public boolean hasMoreElements()
{
return it.hasNext();
}
/*
Enumeration<E>类中的方法nextElement()
E nextElement()如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
*/
public FileInputStream nextElement()
{
return it.next();
}
};
SequenceInputStream sis = new SequenceInputStream(en);//将几个流串联

FileOutputStream fos = new FileOutputStream("c:\\splitfile\\0.jpg");
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}

//切割文件
public static void splitFile()throws IOException
{
FileInputStream fis = new FileInputStream("c:\\1.jpg");

FileOutputStream fos = null;
byte[] buf = new byte[1024];
int len = 0;
int count = 1;
while((len=fis.read(buf))!=-1)
{
fos = new FileOutputStream("c:\\splitFile\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
}


(3)对象序列化

**ObjectOutputStream

**ObjectInputStream

成对使用

**对象实体化:找一个介质,能长期的存储对象。

**对象的属性在Java程序中,都是存在于对内存中,随着对象的消失而消失,而ObjectOutputStream可以将对象实体化

**Serializable接口没有一个方法,也就是说其是一个标记接口。比如盖章的猪肉才是安全的。

**只有实现Serializable接口的子类才能被ObjectOutputStream系列化写入流,当某个类实现该接口后,会被Java自动分配UID号,以便编译器识别,区分不同对象。

**用ObjectOutputStream系列化的对象存储到文件后,该文件是乱码,也就是不可读 的用ObjectInputStream读取该类对象的属性。

**由于对象是有Java给对象分配相应的UID号,而UID号是根据对象的属性不同而分配的。当一个类对象被系列化到文件后,如果该类改动了对象的属性,比如将某个成员变量变成私有, 则该对象再用ObjectInputStream读取时会报异常,也就是说该系列化到文件的对象不能再被使用了,
那么,要想继续使用属性被改动后的对象,我们可以自定义给对象分配UID号,让UID号不随对象的属性变化而变化。

自定义对象分配UID方法如下:

public static final long serialVersion UID = 43L;

**注意:

静态不能被系列化,因为静态成员变量实在内存的方法区,而ObjectOutputStream只能对对内存里面的数据进行系列化

被transient修饰的非静态成员变量也不能被系列化

被系列化的对象存储到文件中,该文件是不可读的,所以该文件的扩展名一般不写成.txt,通常后缀名写.object

import java.io.*;

class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
//writeObj();
readObj();
}
public static void readObj()throws Exception
{
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 oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));

oos.writeObject(new Person("lisi0",399,"kr"));

oos.close();
}
}
import java.io.*;
class Person implements Serializable
{

public static final long serialVersionUID = 42L;//当某个类实现该接口后,会被Java自动分配UID号,以便编译器识别,区分不同对象

private String name;
transient int age;
static String country = "cn";
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}


(4)管道流:

涉及多线程,内含缓冲流

PipedInputStream

PipedOutputStream

import java.io.*;
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void run()//覆盖父类的run方法只能try
{
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("管道流 来了".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);

Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}


(5)随机访问文件:RandomAccess(重要!!!)

**自身具备读写方法(很牛逼!又可以读又可以写)

**通过skipByte(int x)和seek(int x)来达到随机访问文件

**该类不是IO体系子类,而是直接继承Object,但它是IO包中的成员,因为它具备读写方法

**该类内部封装了数组,而且通过指针对数组的元素进行操作,可以通过getFilePoint获取指针位置,同时可以通过seek改变指针位置

**该类完成读写的原理是内部封装了字节输入输出流

**通过该类的构造看出,该类只能操作文件,而且操作的文件只能有固定模式:

"r":只读

"rw":读写

"rws":

"red":

如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。

如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。

**构造方法:

该对象的构造函数要操作的文件不存在会自动创建,如果存在则不会覆盖

RandomAccessFile(File file, String mode)

创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。

RandomAccessFile(String name, String mode)

创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。

**方法摘要:

void write(byte[] b)

将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。

void write(byte[] b, int off, int len)

将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。

void write(int b)

向此文件写入指定的字节。

int read()

从此文件中读取一个数据字节。

int read(byte[] b)

将最多 b.length 个数据字节从此文件读入 byte 数组。

int read(byte[] b, int off, int len)

将最多 len 个数据字节从此文件读入 byte 数组。

String readLine()

从此文件读取文本的下一行。

long getFilePointer()

返回此文件中的当前偏移量。

long length()

返回此文件的长度。

void seek(long pos)

设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。

import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
writeFile_2();
//writeFile();
//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();//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.writeInt(90);//writeInt是为了不丢失字节。Write读取的是int的最低八位

raf.write("王五".getBytes());
raf.writeInt(97);//writeInt是为了不丢失字节。Write读取的是int的最低八位

raf.close();
}
}


(6)操作基本数据类型的流对象:DateStream

DataInputStream

DataOutputStream

可以用于操作基本数据类型的数据的流对象。成对使用

import java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
readUTFDemo();

/*
//指定UTF-8或GBK编码表  可以直接读出来
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
osw.write("你好");
osw.close();
*/

}

public static void readUTFDemo() throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));

String s = dis.readUTF();

System.out.println(s);

dis.close();
}

public static void writeUTFDemo()throws IOException//想要读出来必须有对应的读取方法
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.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();
}
}


(7)操作字节数组流

ByteArrayInputStream 在构造的时候,需要接收数据源。而且数据源是一个字节数组。

ByteArrayOutputStream 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。

因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭。

在流操作规律讲解时:

源设备,

键盘 System.in,硬盘 FileStream,内存 ArrayStream。

目的设备:

控制台 System.out,硬盘FileStream,内存 ArrayStream。

用流的读写思想来操作数据。

import java.io.*;
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"));

}
}


四 IO流转换流的字符编码

(1)字符流的出现为了方便操作字符,更重要的是加入了编码转换

(2)通过子类转换流来完成

InputStreamReander

OutputStreamWriter

(3)在两个子类对象进行构造的时候可以加入编码表

(4)编码表:

将各个国家的文字用二进制数字表示并一一对应,形成一张表,这就是编码表

(5)常见的编码表:

**ASCII:美国标准信息交换码,用一个字节的七位表示

**ISO8859-1:拉丁码表,欧洲码表,用一个字节的八位表示

**GB2312:中文编码表,用两个字节表示

**GBK:中文编码表升级,融合录入更多的中文字符,用两个字节表示,为避免和老美重复,两字节的最高位都是1,即汉字都是用负数表示

**Unicode:国际标准码,融合了多种文字,所有文字都用两个字节表示

**UTF-8:用一个字节到三个字节表示。

注:Unicode能识别中文,UTF-8也能识别中文,但两种编码表示一个汉字所用的字节数不同,Unicode用两个字节,UTF-8用三个字节,故涉及到编码转换。

(6)在流中涉及编码表的转换只有转换流:

InputStreamReander

OutputStreamWriter

import java.io.*;

class EncodeStream
{
public static void main(String[] args) throws IOException
{
//write();
read();
}

public static void write() throws IOException
{
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
osw1.write("你好");
osw1.close();

OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"UTF-8");
osw2.write("你好");
osw2.close();
}
public static void read() throws IOException
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
byte[] buf = new byte[1024];
int len = isr.read(buf);
sop(new String(buf,0,len));
}
}


(7)编码解码

编码:字符串变成字节数组:String-->getBytes()-->byte[]()

解码:字节数组变成字符串:byte[]-->new String(byte[],0,len)-->String

import java.util.*;
class  EncodeDemo
{
public static void main(String[] args)
{
//编码解码1:默认编码
String str1 = "你好";
byte[] buf1 = str1.getBytes();//默认解码:Unicode,四个字节

//编码解码2:指定编码
String str2 = "你好";
byte[] buf2 = str2.getBytes("UTF-8");//指定解码:UTF-8,六个字节

//编码解码3:编码正确解码错误
String str3 = "你好";
byte[] buf3 = str3.getBytes("GBK");//指定编码:GBK,四个字节
String str3 = new String(buf3,"ISO8859-1");//错误解码

//编码解码4:错误编码正确解码
String str4 = "你好";
byte[] buf4 = str4.getBytes("ISO8859-1");//错误编码
String str4 = new String(buf4,"GBK");//正确解码,读不出来

//编码解码5:编码对了,但是解码错误了,怎么办呢?
//此时可以将错误的解码再错编回去,再用正确编码解码
String str5 = "你好";
byte[] buf5 = str5.getBytes("GBK");//正确编码
String str6 = new String(buf5,"ISO8859-1");//用ISO8859-1错误解码,读不出来
byte[] buf6 = str6.getBytes("ISO8859-1");//再用ISO88595-1错误编码
String str7 = new String(buf6,"GBK");//再正确解码,这样就可以读出来了
}
}


图解编码解码



图解IOS8859-1解码出错,再用其编码



(8)字符编码——联通
“联通”二进制编码形式正好符合UTF-8的编码形式,而默认的GBK编码,再解码的时候按照UTF—8解得就会出错

class EncodeDemo2
{
public static void main(String[] args) throws Exception
{
String s = "联通";

byte[] by = s.getBytes("gbk");

for(byte b : by)
{
System.out.println(Integer.toBinaryString(b&255));//&&255是为了获取最低8位
}
}
}


-------android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: