概述
按流向分为输入流和输出流
按操作数据分为字节流和字符流
字符流,基于字节流,但是字符流对象融合了编码表
文字用字符流,图片用字节流
IO体系的四个基类InputStream OutputStream
Reader Writer
由这四个类派生出来的子类名称都是以其父类名作为后缀,比如FileInputStream,FileReader
前缀名是流对象的功能
//创建一个FileWriter对象,该对象一被创建就必须明确被操作的文件,而且该文件会被创建到指定的目录下,如果该目录下有同名文件,会被覆盖:
FileWriter fw = new FileWriter("demo.txt"); //注意会抛出异常:比如文件路径不存在
//调用write方法,将字符串写入到流中
fw.write("abcde");
//刷新流对象中的缓冲,将其中的数据刷到目的地中
fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。因为Java会调用系统中的内容来完成数据的写入,完成之后必须将资源释放,所以close
//将数据刷到目的地中。
//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
FileWriter fw = null;//在外面建立引用,在try里初始化,如果都写在try里,关闭流对象会发生编译错误,因为fw引用作用不到try以外
try
{
fw = new FileWriter("demo.txt");
fw.write("abcdefg");
}
catch (IOException e)
{
System.out.println("catch:"+e.toString());
}
finally //流关闭,必须执行
{
try//close也会发生异常,需要单独用try处理
{
if(fw!=null)//不判断会发生空指针异常
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
/*
演示对已有文件的数据续写。
*/
import java.io.*;
class FileWriterDemo3
{
public static void main(String[] args) throws IOException
{
//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
FileWriter fw = new FileWriter("demo.txt",true);
fw.write("nihao\r\nxiexie");//为了在windows记事本下显示换行,用到了\r\n,linux系统为\n
fw.close();
}
}
//创建一个文件读取流对象,和指定名称的文件相关联。
//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr = new FileReader("demo.txt");
//调用读取流对象的read方法。
//read():一次读一个字符。而且会自动往下读。返回的是作为整数读取的字符
int ch = 0;
while((ch=fr.read())!=-1) //返回-1表示文件已经读到末尾
{
System.out.println("ch="+(char)ch);
}
fr.close();
FileReader fr = new FileReader("demo.txt");
//定义一个字符数组。用于存储读到字符。
//该read(char[])返回的是读取的字符数。
char[] buf = new char[1024]; //通常定义1024的整数倍,这里定义了一个2k大小的字符数组,因为一个字符是两个字节
int len = 0;
while((len=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));//读到多少就打出多少,因为最后一次循环可能不会读满整个数组,如果只打整个数组就会出现多余的字符
}
fr.close();
总结:方式二好,读一串存一下,再全部打印,效率高
//将C盘一个文本文件复制到D盘。
/*
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
*/
import java.io.*;
class CopyText {
public static void main(String[] args) throws IOException {
copy_2();
}
//方式一:从C盘读一个字符,就往D盘写一个字符。(未加入IO异常处理,所以需要抛出异常对象)
public static void copy_1()throws IOException {
//创建目的地。
FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
//与已有文件关联。
FileReader fr = new FileReader("RuntimeDemo.java");
int ch = 0;
while((ch=fr.read())!=-1) {
fw.write(ch);
}
fw.close();
fr.close();
}
//方式二:利用缓存数组读写(加入了IO异常处理)
public static void copy_2() {
FileWriter fw = null;
FileReader fr = null;
try{
fw = new FileWriter("SystemDemo_copy.txt");
fr = new FileReader("SystemDemo.java");
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){ //使用数组进行读取
fw.write(buf,0,len);//使用数组进行写入
}
}
catch (IOException e){
throw new RuntimeException("读写失败");
}
finally{
if(fr!=null)
try{
fr.close();
}catch (IOException e){
throw new RuntimeException("读取流关闭失败");
}
if(fw!=null)
try{
fw.close();
}catch (IOException e){
throw new RuntimeException("写入流关闭失败");
}
}
}
}
要点:read(buf)使用数组进行读取,write(buf,0,len)使用数组进行写入
该缓冲区对象提供了一个跨平台的换行符 newLine()方法
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入流效率。加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush(); //防止因为断电导致数据丢失,所以刷新一句,写入一句
}
//记住,只要用到缓冲区,就要记得刷新。
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象,不用再关闭流对象了
bufw.close();
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);//注意这里的打印加了换行符,因为readLine不返回回车符
}
bufr.close();
BufferedReader bufr = null;
BufferedWriter bufw = null;
bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt"));
/*
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
*/
import java.io.*;
class MyBufferedReader extends Reader //需要继承Reader,因为装饰类和被装饰类属于同一体系
{
private Reader r;//技巧:定义一个 是的在整个类中都有效
MyBufferedReader(Reader r)
{
this.r = r;
}
//可以一次读一行数据的方法。
public String myReadLine()throws IOException //这里不用try和catch异常,而只将异常抛出,因为这里定义的是供调用者使用的功能,应该让调用者去处理异常
{
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1)
{
if(ch=='\r')//这里不需要强转
continue; //读到该字符,不存入,继续while的下一循环读取下一个字符
if(ch=='\n')
return sb.toString();//读到该字符返回数组容器中的字符串形式
else
sb.append((char)ch);
}
if(sb.length()!=0) //缓冲区里面只要有数据,就把剩下的数据返回来,避免了因为最后一行末尾没有换行符而不能将最后一行输出的问题
return sb.toString();
return null;
}
/*
覆盖Reader类中的抽象方法,因为继承了该类
*/
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len) ; //自己覆写该类麻烦,可以用传进来的子类来实现
}
public void close()throws IOException//需要覆写抽象的close方法,因为每个流对象关闭实现不同,直接关闭资源的和调用流对象close方法的两种
{
r.close();
}
public void myClose()throws IOException //关闭资源
{
r.close();
}
}
import java.io.*;
class MyLineNumberReader extends MyBufferedReader//使用装饰设计模式,继承父类的功能,利用关键字super引用父类对象
{
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r);//父类中已经进行了传递,直接调用父类的功能,因为当父类中没有空参数的构造函数时,子类中必须手动通过super语句来显示指定要访问的父类中的构造函数
}
public String myReadLine()throws IOException
{
lineNumber++;//每读取一行,就调用一次myReadLine方法,行号就自增一次。
return super.myReadLine();//父类中已经有了实现,直接调用
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
}
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
import java.io.*;
class FileStream
{
public static void main(String[] args) throws IOException
{
readFile_3();
}
public static void readFile_3()throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
// int num = fis.available();//available方法返回文件中字节数
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。该方法慎用,当文件数据过大时,会造成内存溢出!!
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
public static void readFile_2()throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024]; //为readFile_1和readFile_3之间的折中方式
int len = 0;
while((len=fis.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
fis.close();
}
public static void readFile_1()throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1)//read方法返回的是int型,如果是byte型,可能读到8个1时就结束循环
{
System.out.println((char)ch);
}
fis.close();
}
public static void writeFile()throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("abcde".getBytes()); //getBytes方法将字符串转换为字节数组
fos.close();
}
}
/*
演示mp3的复制。通过缓冲区。
BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
long start = System.currentTimeMillis();
copy_2();
long end = System.currentTimeMillis();
System.out.println((end-start)+"毫秒");//打印复制时间
}
//通过自定义的字节流缓冲区(见下)完成复制
public static void copy_2()throws IOException
{
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\9.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\3.mp3"));
int by = 0;
//System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.myClose();
}
//通过字节流的缓冲区完成复制。
public static void copy_1()throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
int by = 0;
while((by=bufis.read())!=-1)//read方法返回的是int型,如果是byte型,可能读到8个1时就结束循环
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
}
import java.io.*;
class MyBufferedInputStream
{
private InputStream in; //注意这里传的不是FileInputStream
private byte[] buf = new byte[1024*4];
private int pos = 0,count = 0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区(字节数组)获取。
public int myRead()throws IOException
{
//通过in对象读取硬盘上数据,并存储buf中。
if(count==0) //count等于0表示buf中无数据,需要重新从硬盘读取数据
{
count = in.read(buf);
if(count<0)//表示硬盘数据读取完毕
return -1;
pos = 0;//将指针归零,返回数组第一个元素
byte b = buf[pos];
count--;
pos++;
return b&255;//与上255,在原字节数据的前面补上了24个0,将读取的byte转为int
}
else if(count>0)
{
byte b = buf[pos];
count--;
pos++;
return b&0xff;//0xff是255的16进制表示
}
return -1;//这个return语句为了保证有返回值,无其他意义
}
public void myClose()throws IOException
{
in.close();
}
}
结论:
/*
需求:
通过键盘录入数据。
当录入一行数据后,就将该行数据进行打印。
如果录入的数据是over,那么停止录入。
*/
import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true)
{
int ch = in.read();//调用read方法一次就会将键盘录入的字符读取一次!
if(ch=='\r')
continue;
if(ch=='\n')
{
String s = sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());//StringBuilder的清空
}
else
sb.append((char)ch);
}
}
}
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args) throws IOException
{
//获取键盘录入对象。
//InputStream in = System.in;
//将字节流对象转成字符流对象,使用转换流。InputStreamReader
//InputStreamReader isr = new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
//BufferedReader bufr = new BufferedReader(isr);
//读取键盘录入的最常见写法。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
// OutputStream out = System.out;
// OutputStreamWriter osw = new OutputStreamWriter(out);
// BufferedWriter bufw = new BufferedWriter(osw);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();//字符输出流用到了缓冲区,需要刷新
}
bufr.close();
bufw.close();
}
}
转换流可以指定编码表
import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo
{
public static void main(String[] args)throws IOException
{
try
{
int[] arr = new int[2];
System.out.println(arr[3]);
}
catch (Exception e)
{
try
{
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
PrintStream ps = new PrintStream("exeception.log");
ps.println(s);
System.setOut(ps);
}
catch (IOException ex)
{
throw new RuntimeException("日志文件创建失败");
}
e.printStackTrace(System.out);
}
}
}
//log4j 网上提供的专门针对建立异常信息的java包
import java.util.*;
import java.io.*;
class SystemInfo
{
public static void main(String[] args) throws IOException
{
Properties prop = System.getProperties();
//System.out.println(prop);
prop.list(new PrintStream("sysinfo.txt"));
}
}
//将a.txt封装成file对象。可以将已有的和未出现的文件或者文件夹封装成对象。
File f1 = new File("a.txt"); //此例传入相对路径
//将父目录和文件名分别传入
File f2 = new File("c:\\abc","b.txt");
//将父目录封装成对象传入
File d = new File("c:\\abc");
File f3 = new File(d,"c.txt");
sop("f1:"+f1);//打印出相对路径
sop("f2:"+f2);//打印出绝对路径
sop("f3:"+f3);
//通过File类的seperator字段获取系统的目录分隔符,实现跨平台操作
File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");
//创建单个文件夹
File dir = new File("abc\\kkk\\a");//目录abc\\kkk已经存在
sop("mkdir:"+dir.mkdir());
boolean mkdirs():创建多级文件夹。在File对象封装时进行设定:new File("abc\\kk\\dd");
//创建多级文件夹
File dir = new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");
sop("mkdir:"+dir.mkdirs());
File dir = new File("d:\\java1223\\day18");
String[] arr = dir.list(new FilenameFilter()
{
public boolean accept(File dir,String name)
{
return name.endsWith(".bmp");
}
});
System.out.println("len:"+arr.length);
for(String name : arr)
{
System.out.println(name);
}
//该方法返回该级文件或文件夹前面所插入的字符串
public static String getLevel(int level){
StringBuilder sb = new StringBuilder();
sb.append("|--");
for(int i=0;i
public static void removeDir(File dir)
{
File[] files = dir.listFiles();
for(int x=0; x
import java.io.*;
import java.util.*;
class JavaFileList
{
public static void main(String[] args) throws IOException
{
File dir = new File("d:\\java1223");
List list = new ArrayList();
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 list)
{
File[] files = dir.listFiles();
for(File file : files)
{
if(file.isDirectory())
fileToList(file,list);
else
{
if(file.getName().endsWith(".java"))
list.add(file);
}
}
}
public static void writeToFile(List list,String javaListFile)throws IOException
{
BufferedWriter bufw = null;
try
{
bufw = new BufferedWriter(new FileWriter(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;
}
}
}
}
Properties prop = new Properties();//创建对象
prop.setProperty("zhangsan","30");
String value = prop.getProperty("lisi");//根据键名获取值
prop.setProperty("zhangsan",89+"");//修改
Set names = prop.stringPropertyNames();//返回简明的set集合,该方法1.6才有效
for(String s : names)
{
System.out.println(s+":"+prop.getProperty(s));
}
//想要将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; //用line记录读取到的一行字符串数据
Properties prop = new Properties();
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");//用等号分割,返回一个字符串数组
prop.setProperty(arr[0],arr[1]);
}
bufr.close();//注意关闭流
System.out.println(prop);
}
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
//将流中的数据加载进集合。
prop.load(fis);
prop.setProperty("wangwu","39");
FileOutputStream fos = new FileOutputStream("info.txt");
//将集合中的数据写入输出流
prop.store(fos,"haha");
prop.list(System.out);
fos.close();
fis.close();
练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。
/*
用于记录应用程序运行次数。
如果使用次数已到,那么给出注册提示。
很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了。
下一次在启动该程序,又重新开始从0计数。
这样不是我们想要的。
程序即使结束,该计数器的值也存在。
下次程序启动会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数。
该配置文件使用键值对的形式。
这样便于阅读数据,并操作数据。
键值对数据是map集合。
数据是以文件形式存储,使用io技术。
那么map+io -->properties.
配置文件可以实现应用程序数据的共享。
*/
import java.io.*;
import java.util.*;
class RunCount
{
public static void main(String[] args) throws IOException
{
Properties prop = new Properties();
//用File来对象化文件,那么文件不存在则创建
File file = new File("count.ini"); //此处采用相对路径,因为绝对路径会因为盘符不存在而出现IO异常
if(!file.exists())
file.createNewFile();
FileInputStream fis = new FileInputStream(file);
prop.load(fis);
int count = 0;
String value = prop.getProperty("time");
if(value!=null)
{
count = Integer.parseInt(value);
if(count>=5)
{
System.out.println("您好,使用次数已到,拿钱!");
return ; //用该语句直接终止程序的运行
}
}
count++;//当value不为空但又小于5时,外层if代码块执行完之后就会执行此语句保证count的续增
prop.setProperty("time",count+"");
FileOutputStream fos = new FileOutputStream(file);//输出流对象不能在前面创建,否则会出现次数无法递增的问题!
prop.store(fos,"");
fos.close();
fis.close();
}
}
zhagnsan
30
bj
4,字符输出流,Writer。可带自动刷新
//读取键盘录入的标准语句
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//PrintWriter out = new PrintWriter(System.out,true);//打印到控制台
//PrintWriter out = new PrintWriter("a.txt");//直接存入文件,后面不能写刷新,因为刷新只能针对流而言
PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);//在文件里面自动刷新,就将文件封装进流中
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line.toUpperCase());
}
//out.flush();
}
out.close();
bufr.close();
将多个读取流合并成一个流
SequenceInputStream(Enumeration extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数(可通过Vector获得)。
Vector v = new Vector();
v.add(new FileInputStream("c:\\1.txt"));
v.add(new FileInputStream("c:\\2.txt"));
v.add(new FileInputStream("c:\\3.txt"));
Enumeration en = v.elements();//vector的elements方法返回此向量的组件的枚举
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("c:\\4.txt");
byte[] buf = new byte[1024];
int len =0;
while((len=sis.read(buf))!=-1) //read方法返回读取的字符的长度
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
public static void merge()throws IOException
{
//Vector执行效率低,此处用ArrayList
ArrayList al = new ArrayList();
for(int x=1; x<=3; x++)
{
al.add(new FileInputStream("c:\\splitfiles\\"+x+".part"));
}
//因为是匿名内部类,所以要对访问的局部变量进行final修饰,规定的
final Iterator it = al.iterator();
//使用ArrayList的迭代器覆盖Enumneration匿名内部类的两个方法创建一个Enumneration对象
Enumeration en = new Enumeration()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("c:\\splitfiles\\0.bmp");
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.bmp");
FileOutputStream fos = null;
byte[] buf = new byte[1024*1024];//当操作较大数据时,为了防止内存溢出,可以用每一个流对象操作100M数据
int len = 0;
int count = 1;
while((len=fis.read(buf))!=-1)
{
fos = new FileOutputStream("c:\\splitfiles\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
public static void writeObj()throws IOException
{
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",399,"kr"));
oos.close();
}
public static void readObj()throws Exception//这里会抛出ClassNotFoundException和类没有序列化异常,所以抛出Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
import java.io.*;
class Person implements Serializable
{
public static final long serialVersionUID = 42L; //不让java自动生成uid,通过自定义uid
private String name;
transient int age; //对非静态的成员也不使其序列化,则加上transient关键字,即使它在堆内存中
static String country = "cn"; //静态成员是不能被序列化的,因为静态成员在方法区里面,所以不论构造Person时传入何值,读取的时候都是cn
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
import java.io.*;
//读取线程
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void run()
{
try//此处不能抛异常,必须处理异常,因为覆盖了run方法
{
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("piped lai la".getBytes());//write方法写入的一定是字节型数据
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);//可以使用connect方法来连接两个流对象,也可以使用构造函数传递参数连接两个流对象
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
随机访问文件,自身具备读写的方法,结尾处没有父类名
通过skipBytes(int x),seek(int x)来达到随机访问,前提是保证数据的存储是有规律的
该类不算是IO体系中子类。
而是直接继承自Object。
但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组(以文件的数据的一个字节为单位),而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。不能操作键盘录入输出等其他形式
而且操作文件还有模式:只读r,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存在则不会覆盖。
public static void readFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中指针。至第8个字节。数组的前后都能指定
//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方法一次读4个字节
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");//rw代表“读写”模式
/*raf.write("2".getBytes());
raf.write(258);//此时会取258的最低8位写出
raf.writeInt(258);//此时会取258的4个字节写出 */
raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
可以实现读取,写入,修改操作
该类可以实现多线程的下载文件。每个线程负责文件的一段数据的下载,保证下载后数据的完整性
为了保证数据的规律性,可以进行定义,比如,姓名16个字节,年龄4个字节,空的字节会拿空补位
DataInputStream和DataOutputStream
可以用于操作基本数据类型的数据的流对象
import java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk"); //使用转换流指定编码表gbk
//
// osw.write("你好");
// osw.close();
// readUTFDemo();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();//读取时应使用相应的方法,否则读取错误
System.out.println(s);
dis.close();
}
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");//该方法使用修改过的UTF表写入数据
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();
ObjectOutputStream oos = null;
oos.writeObject(new O());
}
用于操作字节数组的流对象。ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。因为这两个流对象都操作的数组,并没有使用系统资源。不用抛IO异常所以,不用进行close关闭。
在流操作规律讲解时:
源设备:键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:控制台 System.out,硬盘FileStream,内存 ArrayStream。
其实就是用流的读写思想来操作数组。
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异常或者捕获该异常
}
类似字节数组
StringReader和StringWriter
类似字节数组
private static void readText() throws IOException {
InputStreamReader isr =
new InputStreamReader(new FileInputStream("utf.txt"),"GBK");//字节流到字符流
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
private static void writeText() throws IOException {
/*OutputStreamWriter osw =
new OutputStreamWriter(new FileOutputStream("gbk.txt"));//因为系统默认的是GBK码表,这里将字符流转换为字节 流,所以此句等价于FileWriter
*/
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");//按照UTF-8码表写出数据
osw.write("你好");//osw是字符流对象,所以不用将数据转换为字节数组
osw.close();
}
查错表的情况分析:
String str = "你好";
// byte[] arr = str.getBytes();//结果:[-60, -29, -70, -61]
// byte[] arr = str.getBytes("GBK");//结果:[-60, -29, -70, -61]会抛出异常,因为可能传入的值不识别
byte[] arr = str.getBytes("UTF-8");//结果:[-28, -67, -96, -27, -91, -67]
System.out.println(Arrays.toString(arr));//将字节数组直接转换成字符串形式
常用的码表就两张:GBK和UTF-8,他们都识别中文
String s = "你好";
byte[] b = s.getBytes("GBK");
System.out.println(Arrays.toString(b));
String s1 = new String(b,"UTF-8");
System.out.println(s1);
//对s1进行再次编码
byte[] b1 = s1.getBytes("UTF-8");
System.out.println(Arrays.toString(b1));
String s2 = new String(b1,"GBK");
System.out.println(s2);
运行结果:
/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
1,描述学生对象。
2,定义一个可操作学生对象的工具类。
思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。
所以可以使用TreeSet。
3,将集合的信息写入到一个文件中。
*/
import java.io.*;
import java.util.*;
//定义Student类实现Comparable接口并指定泛型,这样在实现compareTo方法时就只能接收该泛型了
class Student implements Comparable{
private String name;
private int ma,cn,en,sum;
Student(String name, int ma, int cn, int en){
this.name = name;
this.ma = ma;
this.cn = cn;
this.en = en;
sum = ma+cn+en;
}
public String getName(){
return this.name;
}
public int getSum(){
return this.sum;
}
public int compareTo(Student s){
int num = new Integer(this.sum).compareTo(new Integer(s.sum));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
//一般自定义对象都要覆盖Object类的hashCode和equals方法,还有toString方法
public int hashCode(){
return name.hashCode()+sum*35;
}
public boolean equals(Object obj){
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name)&&this.sum==s.sum;
}
public String toString(){
return "Student:"+name+"["+ma+","+cn+","+en+"]";
}
}
class StudentInfoTool{
//按照默认排序方式,总成绩从小到大
public static Set getStudents() throws IOException{
return getStudents(null);//不需要比较器,传入null。
}
//按照指定比较器排序方式,总成绩从大到小
public static Set getStudents(Comparator cmp) throws IOException{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
Set stus = null;
if(cmp==null)
stus = new TreeSet();//如果没有传入比较器,那么就创建一个不带比较器的集合
else
stus = new TreeSet(cmp);//如果传入了比较器,就创建一个带比较器的集合
while(true){
line = bufr.readLine();
if("over".equals(line))
break;
String[] arr = line.split(",");
Student s = new Student(arr[0],Integer.parseInt(arr[1]),
Integer.parseInt(arr[2]),Integer.parseInt(arr[3]));
stus.add(s);
}
bufr.close();
return stus;
}
//将存有学生信息的集合写入文件
public static void write2File(Set stus) throws IOException{
BufferedWriter bufw = new BufferedWriter(new FileWriter("stuInfo.txt"));
for(Student s: stus){
bufw.write(s.toString()+'\t');
bufw.write(s.getSum()+"");//如果单独输出整数只输出最后一个八位,会出现乱码,所以要转为字符串
bufw.newLine();
bufw.flush();//写入文件需要刷新,因为用到了缓存
}
bufw.close();
}
}
public class StudentInfoDemo {
public static void main(String[] args) throws IOException {
Comparator cmp = Collections.reverseOrder();//返回一个逆序自身比较性的比较器
Set stus = StudentInfoTool.getStudents(cmp);
StudentInfoTool.write2File(stus);
}
}