1,字符编码
在Java程序的开发中最常见的是ISO8859-1,GBK/GBK2312,unicode,UTF编码.
ISO8859-1:属于单字节编码,最多只能表示0-255的字符范围,主要在英文上应用.
GBK/GB2312:中文的国际编码,专门用来表示汉字,是双字节编码,如果在此编码中出现中文,则使用ISO8859-1编码,GBK可以表示简体中文和繁体中文,而GB2312只能表示简体中文,GBK兼容GB2312
unicode:Java中使用此编码方式,是最标准的一种编码,使用十六进制进行编码,但此编码不兼容ISO8859-1编码
UTF:它兼容ISO8859-1编码,同时也可以用来表示所有的语言字符,不过UTF编码是不定长编码,每一个字符的长度为1-6个字节不等,一般在中文网页中使用此编码,可以节约空间
在IO体系中,使用到转化流的时候,我们可以对输入或者输出的内容指定其特定的编码表
2,内存操作流
在IO中,是使用ArrayByteInputStream和ArrayByteOutputStream来完成内存的输入和输出,由于数据是在内存中进行操作的,不涉及到具体的文件,所以在输出的时候,
ArrayByteOutputStream这个类只需要构造无参的构造方法.
内存中数据的存在方式都是字节,所以在ArrayByteInputStream中的构造函数传入的是byte类型的数组.
package cn.bytearray; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class ByteArrayDemo { public static void main(String[] args) throws IOException { String str = "abcsw"; ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); System.out.println("available:"+bis.available()); int len = 0; byte[] buf = new byte[1024]; while((len=bis.read(buf))!=-1){ String s = new String(buf,0,len); System.out.println(s); } bis.close(); bos.close(); } }
关于内存操作流的使用
内存操作流一般在生成临时信息的时候才会使用到,如果将这些临时信息保存在文件中,当代码执行完毕的时候,在将储存临时信息的文件删除,这样操作起来会比较麻烦,这个时候使用内存操作流是最合适的.
3,数据操作流
4,跟踪行号的缓冲字符输入流(LineNumberReader)
该类在对读到的字符输入流的时候,可以为该文本文件上加上行号,它里面也有读取文本行的方法,还有一个可以获取当前行号的方法
package cn.linenumberread; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; public class LineNumberReadDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("各种需求.txt"); LineNumberReader lnr = new LineNumberReader(fr); String lin = null; while((lin=lnr.readLine())!=null){ System.out.println(lnr.getLineNumber()+":"+lin); } fr.close(); lnr.close(); } }
输出结果的实例,该输出结果的左侧就具备了行号
5,关于window中的‘\r‘和‘\n‘的知识点
在Java中\r的阿斯科码值是13,\n的阿斯科码值是10.在window中的换行是有两个阿斯科码值13和10组成的,其实在window中的enter键,这个键按一下,它是实现了2个动作的,
\r和\n,所以在window中的换行是/r/n.
package cn.io; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class ReadKeyBoard { public static void main(String[] args) throws IOException { //ReadKey_1(); ReadKey_2(); } public static void ReadKey_2() throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String temp = null; while((temp=bufr.readLine())!=null){ if("over".equals(temp)) break; System.out.println(temp.toUpperCase()); bufw.newLine(); } bufr.close(); bufw.close(); } public static void ReadKey_1() throws IOException { StringBuilder sb = new StringBuilder(); InputStream in = System.in; int len = 0; while((len=in.read())!=-1){ if(len==‘\r‘) continue; if(len==‘\n‘){ String str = sb.toString(); if("over".equals(str)) break; System.out.println(str.toUpperCase()); sb.delete(0, sb.length()); } else sb.append((char)len); } } }
在以上代码中,ReadKey_1这个方法是原始的在控制台上打印出内容,在window中,打印的时候,当一句话打完,就回按一下enter键,这个时候对于程序来说,会产生两个char,\r和\n,这个时候就必须对程序进行判断,当是/r的时候,我们不记录字符,当/n的时候,表示一句话完成了,就得记录打印的数据,然后再把它显示在控制台上去
那么每次记录的字符该如何处理,这个时候就得想到容器的存在,而由于容器一定义就会一直存在在程序中,就是每次打印结果都是把上次记录的数据也打印出来,这个时候,没记录一次数据,打印数据之后,就得删除上次打印的内容,集合清空一般使用clear方法,StringBuffer和StringBuilder用delete方法
6,对象序列化(ObjectOutputStream和ObjectInputStream)
对象序列化就是将一个对象变成二进制的数据流的一种方法,,如果一个类需要被序列化,那么对象所在的类必须实现java.io.Serializable接口,该接口中没有定义任何的方法,它只是一个标示接口,标示这一个类具有被序列化的能力.
使用对象输出流输出序列化对象的步骤叫做序列化,反之叫做反序列化
对象序列化和对象反序列化操作时的版本兼容的问题
在对象序列化和反序列化的时候,JDK版本的不统一的话就有可能会造成异常,所以在序列化操作的时候引入一个serialVersionUID的常量,例如
private static final long serialVersionUID = 1L;这里的具体内容可以我们自己决定
一个对象被序列化的内容,只有对象的属性被序列化了
既然Serializable接口中没有任何方法的话,是不是所有的类都可以实现该接口?
不可以,这样在以后的JDK版本的升级中,会出现问题,以后升级的时候,有可能会在该接口中增加方法的.
transient关键字
如果一个对象中的某个属性不希望被序列化,则可以使用该关键字进行声明.
package cn.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import cn.bean.Person; public class ObjectOutputStreamDemo { public static void main(String[] args) throws IOException, IOException { File f = new File("person.txt"); ObjectOutputStream ops = new ObjectOutputStream(new FileOutputStream(f)); ops.writeObject(new Person("wjd",20)); ops.close(); } } package cn.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream; import cn.bean.Person; public class ObjectInputStreamDemo { public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { File f = new File("person.txt"); ObjectInputStream ops = new ObjectInputStream(new FileInputStream(f)); Person p = (Person)ops.readObject(); System.out.println(p.getName()+".."+p.getAge()); } } package cn.bean; import java.io.Serializable; public class Person implements Serializable { private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
7,打印流(PrintStream和PrintWriter)
PrintStream:字节打印流,提供的打印方法可以对多种的数据类型的值进行打印,并保持数据的表示形式,不会抛出IO异常,
构造函数传三种类型的数据,字符串路径,File对象,字节输出流(outputstream)
PrintWriter:字符打印流,构造函数传入的参数有,字节输出流,字符输出流(writer),File对象,字符串路径
TIPS:当在上面两个类中传入的是字符或者是字节输出流的时候,都可以在构造函数中加入一个自动刷新的功能true.
package cn.io; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; public class PrintWriterDemo { public static void main(String[] args) throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(System.out,true); String str = null; while((str=bufr.readLine())!=null){ if("over".equals(str)) break; pw.println(str); //pw.flush();这里一定要刷新下,才能在控制台上打印出内容来 } bufr.close(); pw.close(); } } package cn.io; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; public class PrintStreamDemo { public static void main(String[] args) throws IOException { //FileReader f = new FileReader("w.txt"); PrintStream ps = new PrintStream("w.txt"); ps.println("奥巴马"); ps.println(11); } }
8,将文件切割和合并的2个需求
需求切割一首MP3歌曲,并要求有切割后的配置信息文件,记录歌曲的名字和切割后的切割文件的个数
1,创建一个fileinputstream对象,将源和需要切割的文件相互关联起来
2,创建一个fileoutputstream对象,由于被切割的文件的个数的不确定性,目的的初始值为null
3,定义变量计数器count用来记录切割后文件名的前缀,变量len,用于进行读操作,记录读到数据的长度
4,定义配置文件信息的对象,properties,创建一个File对象的目录文件夹mik,用来保存切割好的文件和配置文件
5,进行标准的读写操作,在while循环中,确定切割文件的数量,利用计数器count来命名切割文件的文件名
6,设置配置信息的键值对于关系,比如原始文件+原始文件名,part+切割后的数量
7,fos对象中,设定需要放配置文件的位置和配置文件的名称
8,将配置文件的信息放进去,store
9,关闭流操作
package cn.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; public class SplitMP3 { public static void main(String[] args) throws IOException { File f = new File("d:"+File.separator+"pushu.mp3"); SplitMP3(f); } public static void SplitMP3(File f) throws IOException { //创建源,将需要切割的文件与源进行关联 FileInputStream fis = new FileInputStream(f); //创建目的,由于切割文件的数量不确定性,初始值为null FileOutputStream fos = null; //创建一个1m的缓冲区 byte[] buf = new byte[1048576]; //创建两个变量,计数器count和记录读到数据长度的len int count = 1; int len = 0; //创建配置信息对象,并创建相应的放切割文件和配置信息文件的File对象 Properties pro = new Properties(); File file = new File("d:"+File.separator+"parts"); if(!file.exists()) file.mkdirs(); //开始读写操作,进行文件的切割并存储到对于的文件夹中 while((len=fis.read(buf))!=-1){ fos = new FileOutputStream(new File(file,(count++)+".part")); fos.write(buf, 0, len); } //将关键的信息加入到配置文件中 pro.setProperty("filename", f.getName()); pro.setProperty("part", count+""); fos = new FileOutputStream(new File(file,count+".info")); pro.store(fos, "splitMp3 info"); //各种关闭流 fis.close(); fos.close(); } }
需求 将上面分割的文件,在合并成一首MP3歌曲,必须要读取配置文件中的信息来完成
1,对方法中传入的File对象的目录参数进行listFiles方法,返回一个File数组,在listFiles的方法中传入一个文件过滤器,作用是在目录中去寻找后缀名是.info的配置文件.由于配置文件肯定只有一个存在,所以取出File[]数组中的第一个元素,这个就是配置文件的File对象
2,创建配置文件properties对象,和fileinputstream对象,将配置文件的File对象穿入fileinputstream对象的构造函数,然后在加载配置文件
Load,通过配置文件对象的方法,去得到mp3的文件名,被分割的文件的个数
3,再次使用文件过滤器,找到目录中后缀名是.part的文件(被分割的文件),
这样就得到一个File[]的数组
4,创建arraylist集合对象,使用for循环,在File[]通过fileinputstream,加入到arraylist集合中,再使用collections这个集合工具类,来得到该集合的枚举对象
5,创建源sequenceinputstream类,将枚举对象给它,再创建目的Fileoutputstream,传入file对象,这个使用到了上面在配置文件中得到的MP3文件名的那个字符串变量.
6,进行标准的读写操作
7,关闭各个流
在上述2个分割和合并的需求中,我们在字节输入和输出流,传入File对象的时候,一定要留意到File中有个构造方法可以传两个对象的,一个是爹,一个是儿子.
package cn.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.Properties; public class MergeMP3 { public static void main(String[] args) throws IOException { File dir = new File("d:"+File.separator+"parts"); //MergeMP3(dir); MergeMP3_2(dir); } public static void MergeMP3_2(File dir) throws IOException { File[] fileInfo = dir.listFiles(new FindNameFilter(".info")); File info = fileInfo[0]; FileInputStream fis = new FileInputStream(info); Properties pro = new Properties(); pro.load(fis); int part = Integer.parseInt(pro.getProperty("part")); String filename = pro.getProperty("filename"); File[] filemp3 = dir.listFiles(new FindNameFilter(".part")); ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for(int i=0;i<filemp3.length;i++){ al.add(new FileInputStream(filemp3[i])); } Enumeration<FileInputStream> en = Collections.enumeration(al); SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream(new File(dir,filename)); byte[] buf = new byte[10]; int len = 0; while((len=sis.read(buf))!=-1){ fos.write(buf, 0, len); } fis.close(); fos.close(); sis.close(); } public static void MergeMP3(File dir) throws IOException { ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for(int i=1;i<8;i++){ al.add(new FileInputStream(new File(dir,i+".part"))); } Enumeration<FileInputStream> en = Collections.enumeration(al); SequenceInputStream sis = new SequenceInputStream(en); int len=0; FileOutputStream fis = new FileOutputStream(new File(dir,"3.mp3")); while((len=sis.read())!=-1){ fis.write(len); } sis.close(); fis.close(); } }