admin 管理员组文章数量: 887021
(十二)Core Java IO流(Properties,序列化,管道流,字符编码)
目录 : 41 ). IO流(创建java文件列表)
42 ). IO流(Properties简述)
43 ). IO流(Properties存取)
44 ). IO流(Properties存取配置文件)
45 ). IO流(Properties练习)
46 ). IO流(PrintWriter)
47 ). IO流(合并流)
48 ).IO流(切割文件)
49 ).IO流(对象的序列化)
50 ). IO流(管道流)
51 ). IO流( RandomAccessFile)
52 ). IO流( 操作基本数据类型的流对象DataStream)
53 ). IO流(ByteArrayStream)
54 ). IO流(转换流的字符编码)
55 ).字符编码
56 ).字符编码-联通
57 ).练习
四十一 . IO流(创建java文件列表)
1 ) . 本文讲述 如何将 指定目录中的文件递归到集合,然后从集合通过字符流写入到指定目标地址文件
2 ) . Demo:
/* 本章讲述 : 需求 : 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中 也就是建立一个java文件列表清单 步骤: 1.对指定的目录进行递归 2.获取递归过程所有的java文件的路径 3.将这些路径存储到集合中 4,将集合中的数据写入到一个文件中 */ import java.io.*; import java.util.*; class javaFileList { public static void sop(Object obj) { System.out.println(obj); } //将指定目录下的文件放入指定集合中 public static void fileToList(File file,List<File> list) { //获取遍历文件对象的类 File[] file1 = file.listFiles(); //遍历文件对象 for(File fi : file1) { //判断是否是目录 if(fi.isDirectory()) fileToList(fi,list); //是目录继续递归遍历 else { if(fi.getName().endsWith(".java")); //是文件再判断是否是.java文件 list.add(fi); //是.java文件则添加入集合 } } } //用来将集合中的文件绝对路径放入指定地址文件的方法 public static void getFileList(List<File> list,String name) throws IOException { //因为文件内是字符串涉及数据因此用字符流 BufferedWriter bw= null; try { bw=new BufferedWriter(new FileWriter(name)); //将list集合中数据遍历出 for(File fi : list) { String path = fi.getAbsolutePath(); //获取文件绝对路径 bw.write(path); //写入目标地文件内 bw.newLine(); //换行 bw.flush(); //刷新 } } catch(IOException o) { throw o; } finally { try { if(bw!=null) bw.close(); } catch(IOException o) { throw o; } } } //主方法 public static void main(String args[]) throws IOException { //初始化文件对象的目标地 File fi1 = new File("S:\\develop\\JavaText"); //初始化一个存文件的集合 List<File> li =new ArrayList<File>(); //调用文件存储到集合的方法 fileToList(fi1,li); File fi2= new File("S:\\develop\\JavaText\\myJavaList.txt"); //指定一个目标存放地文件 getFileList(li,fi2.toString()); //将list集合与目标存放地一并传入 //打印集合大小 sop(li.size()); } }
小结 :
1. 数据存储在内存时临时存储,数据存储在硬盘式数据的持久化存储2. 数组有长度,集合有大小
四十 二. IO流(Properties简述)
1 ) . Properties
1.1 配置文件中的数据大多是以键值对的方式存在的,因此Properties是HashTable 的子类
1.2 特点 : Properties不仅可以操作键值对(设计Map体系),还可操作硬盘上(涉及IO流)的键值对
2 ) . 案例解说 : 当我们对一个软件的相关用户信息进行更改时,是在内存中更改的,而正常情况退出后,更改就失效了, 而如何让下次打开软件还是自己上次配置的,就用到了软件配置信息文件,我们通过更改配置信息文件中的数据而完成数据持久化,再启动软件之前先访问这个配置文件信息
/* 本章讲述 : Properties是hashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串 Properties是集合中和IO技术相结合的集合容器 该对象的特点是:可以用于键值对形式的配置文件 */2.1 通俗讲 : 想要当软件的配置每次打开都是上次配置的,那就需要将配置文件信息数据持久化,每次打开软件都会访问一次配置文件3 ). Demo:
小结 :
1. 当我们操作硬盘上的键值对文件时,便要想到Properties类
四十 三. . IO流(Properties存取)
1 ) . Properties 存 setProperty() 设置一对 : getProerty() 取一个 , stringPropertyNames() 取一堆
2 ) . Demo:
/* 本章讲述 : Properties类 : 是集合中与IO技术相结合的容器 setProperty(): 用来设置Properties类中的键值对 getProerty() : 用来通过键获取properties类中的值 stringPropertyNames() : 用来获取properties类中所有的键,返回值是set类型 */ import java.io.*; import java.util.*; class PropertiesDemo { public static void sop(Object obj) { System.out.println(obj); } public static void getAndSet() { Properties pro =new Properties(); //初始化资源集合类 pro.setProperty("summer","hot"); //设置资源键值对 pro.setProperty("winter","cold"); String value = pro.getProperty("summer"); //通过键获取值 //sop("sumer:"+value); //输出值 Set<String> keys = pro.stringPropertyNames(); //获取资源集合类中所有键 for(String key : keys) //迭代键 { sop(key+"::"+pro.getProperty(key)); //输出所有键值 } } //主方法 public static void main(String args[]) throws IOException { getAndSet(); } }
四十 四. IO流(Properties存取配置文件)
1 ) . Properties存取配置文件 : 用到了资源类中的 load () 加载 和 store()存储方法
2 ) . Demo:
/* 本章讲述 : 演示 :如何将流中的数据存储到集合中 案例 : 将info.txt文件中的键值对信息存储到集合中进行操作 步骤 : [1] 用一个流和info.txt文件关联 [2] 读取一行数据,将该行数据用"="进行切割 [3] 等号左边作为键,右边作为值,存入到Properties集合中即可 方法: properties类中的load() : 用来将字符/字节流指定文件中数据加载到资源类中 properties类中的store() : 用来将资源类中的数据存储到字符/字节流指定文件中 使用这两个方法都需要 初始化相关的流对象 */ import java.io.*; import java.util.*; class PropertiesDemo1 { public static void sop(Object obj) { System.out.println(obj); } //将磁盘文件信息读取到资源类调用已有工具方法实现 public static void readInfo1() throws IOException { BufferedReader br = new BufferedReader(new FileReader("info.txt")); //初始化一个字符流读对象与目标地址相关联 Properties pro = new Properties(); //初始化一个集合资源文件类用来存储信息 pro.load(br); //用来将目标地的文件信息加载进入 资源文件类 pro.setProperty("winter","100"); //设置更改资源类中的键值对 BufferedWriter bw =new BufferedWriter(new FileWriter("info.txt")); //初始化一个字符流写对象与目标地址相关联 pro.store(bw,"zhangxiaozong"); //将资源类中存储的信息写入到目标地址内,并且需要携带备注信息 sop(pro); } //将磁盘文件信息读取到资源类原理 public static void readInfo() throws IOException { BufferedReader br = new BufferedReader(new FileReader("info.txt")); //初始化一个字符流读对象与目标地址相关联 String line=null; Properties pro = new Properties(); //初始化一个集合资源文件类用来存储信息 while((line=br.readLine())!=null) //遍历目标地信息 { String[] str = line.split("="); //以==为标识符切割 pro.setProperty(str[0],str[1]); //切割后以键值对的方式放入资源文件类 } sop(pro); } //主方法 public static void main(String args[]) throws IOException { //readInfo(); readInfo1(); } }
小结 :
1. properties类操作数据的固定格式 : 键 = 值2. 我们平时的操作是在内存中操作,程序一旦关闭,及恢复默认,若需永久更改,则需更改本地配置文件
四十 五 . IO流(Properties练习)
1 ) . 练习 : 用来设置程序可免费执行的次数
2 ) . Demo:
/* 本章讲述 : 练习 : 用于记录应用程序运行次数,若使用次数已到规定次数,则给出注册提示 思考条件: [1] 次数自增 -->计数器 [2] 不随程序的结束而修改消失 -->存储硬盘配置文件 得出 : 用一个键值对的形式存储到硬盘上的配置文件,在程序开启时便加载即可 ,使用集合中的IO技术 : Map + IO = properties类 */ import java.io.*; import java.util.*; class PropertiesDemo2 { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws IOException { Properties prop = new Properties(); //初始化资源类用来加载和存储信息 File file =new File("S:\\develop\\JavaText\\info.properties"); //初始化文件类 用来指定地址 if(!file.exists()) //判断文件是否存在 file.createNewFile(); //不存在则创建 BufferedReader br =new BufferedReader(new FileReader(file)); //将文件放入字符流读的类中 prop.load(br); //将指定地址文件中的数据加载到资源类 int count =0; //定义计数器 String value = prop.getProperty("count"); //获取本地文件类中count的值 if(value!=null) //判断值是否为空,不为空时进入操作 { count = Integer.parseInt(value); //将字符串换算为数值 if(count>=5) { sop("您好,使用次数已到,请拿钱"); } } count++; //为空时自动++ prop.setProperty("count",count+""); //将 计数器的键值 放入 资源类 BufferedWriter bw =new BufferedWriter(new FileWriter(file)); //初始化文件字符流写的类 prop.store(bw,"xiaoqinagqiang"); //资源类中的数据写入到字符流写的类中的指定文件地址 } }
3 ) . 常见的两种配置文件 : .properties 与 .xml
3.1 区别 : .properties 讲究唯一性 ,最好存储 键值对都是唯一的 ;而 .xml中讲究多样性, 一个键可多个值 ,可操作关系复杂的情况
3.2 共同点 : .properties 与 .xml都是配置文件,而且 内格式都是 键值对
3.3 操作.properties的类是 properties , 操作 .xml的类是 document ,但是这个类不好操作,于是出现了 dom4j
3.4 dom4j 是 java的xml API,用来读写xml文件的
小结 :
1. 配置文件可以实现应用程序之间的共享2. 目前常见的两种配置文件: .properties 与 .xml
四十 六. IO流(PrintWriter)
1 ) . IO包中的其他类 :
1.1 打印流 : PrintWriter与PrintStream -->可以直接操作输入流和文件
1.2 序列流 : SequenceInputStream -->对多个流进行合并
1.3 操作对象 : ObjectInputStream与ObjectOutputStream --> 被操作的对象需要实现Serializable(标记接口)
1.4 练习 : 文件分割程序
2 ) . Demo :
/* 本章讲述 : 打印流 : 该流提供了打印方法,可将各种数据类型的数据都原样打印 分类: 1.字节打印流: PrintStream 构造函数可接收的参数类型 : 1.file对象,File 2.字符串路径 String 3.字节数据流 OutputStream 2. 字符打印流 : PrintWriter -->常用 构造函数可接收的参数类型 : 1.file对象,File 2.字符串路径 String 3.字节数据流 OutputStream 4.字符输出流 Writer 小结 : 字符打印流的厉害之处在于 他的构造函数 可传 文件,我们常用字符打印流操作 */ import java.io.*; import java.util.*; class PrintWriterDemo { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws IOException { //初始化一个字符流读对象,并把源头设为 键盘输出 BufferedReader br =new BufferedReader(new InputStreamReader(System.in)); //初始化一个输出写对象,并把目的设为控制台,并自动刷新 // PrintWriter pw =new PrintWriter(System.out,true); //这里加 true 的原因是 : println,printf或format 使用时会自动刷新 //初始化一个输出写对象,并把目的设为磁盘文件,并自动刷新 PrintWriter pw =new PrintWriter(new FileWriter("1.txt"),true); String str=null; while((str=br.readLine())!=null) { if(str.equals("over")) break; pw.println(str.toUpperCase()); //将输出的内容大写 } pw.close(); br.close(); } }
小结 :
1. PrintWriter 直接对文件进行操作更加便捷
四十 七. IO流(合并流)
1 ) . 应用场景 : 多个文件数据合并成一个文件时用到合并流
2 ) . Demo:
/* 本章讲述 : 合并流 : 该流提供了让多个流合并成一个流的方式,也就是让流串联起来 理解: [1] 序列 : 就是有序的排列,指串联,比如合并流就是序列流,也是序列的一种体现方式 ,序列通常应用于 音乐信息 [2] 枚举 : 让各个对象的元素得以连续存入,一次一个 SequenceInputStream : 合并流的方法 前提 : [1] 需先将流对象存入Vector集合 [2] 采用vector集合中方法elements 让其元素得以连续,返回值是 枚举Enumeration [3] 将元素放入序列流中进行排序合并为一流 */ import java.io.*; import java.util.*; class SequenceDemo { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws IOException { Vector<FileInputStream> v = new Vector<FileInputStream>(); //初始化一个用来装流的集合 //向集合内添加流对象 v.add(new FileInputStream("summer.txt")); //文件必须现实存在 v.add(new FileInputStream("winter.txt")); v.add(new FileInputStream("spring.txt")); //将集合内的元素存入一个枚举类型泛型是读流的类中 -->枚举是得到连续数据的手段 Enumeration<FileInputStream> en = v.elements(); SequenceInputStream sis =new SequenceInputStream(en); //初始化序列流对象,将刚才枚举对象中的流合并为一个流 FileOutputStream fos =new FileOutputStream("autumn.txt"); //初始化一个文件写出流 byte[] buf =new byte[1024]; //定义临时存储区 int len=0; while((len=sis.read(buf))!=-1) //读取序列流中数据 { fos.write(buf,0,len); //将序列流中数据读取到文件写出流中 } fos.close(); //关闭文件写出流 sis.close(); //关闭序列流 } }
小结 :
1. 多个源对应一个目标地时,采用合并流,合并流就是将流串联了起来,将最后一个流的返回值当做结束标识符
四十 八 . IO流(切割文件) --> 重要
1 ) . 本章讲述了 切割与合并 ,合并最重要的是用 好 Enumeration枚举对象
2 ) . Demo:
/* 本章讲述 : 切割流 : 该流提供了让一个流切割成多个流的方式 切割原理 : 就是 拿一个容器,容器满了再换个容器,也就是换了个文件,具体实现通过while 循环+ close 关流操作 完成 当我们做合并流时,最重要的是使用SequenceInputStream ,而它 只接受参数 Enumeration ,因此 需要放入集合,集合数据再放入Enumeration,然后Enumeration再放入SequenceInputStream 总结 : Java 获取Enumeration类型的集合方法 有两种: [1] 通过vector集合获得,详细见上一章,但不高效 [2] 通过List集合+iterator+匿名内部类的方法实现 */ import java.io.*; import java.util.*; class SplitDemo { public static void sop(Object obj) { System.out.println(obj); } //切割方法 public static void splitFile() throws IOException { FileInputStream fis =new FileInputStream("S:\\develop\\JavaText\\Beyond.mp3"); //初始化文件读取流并关联地址 FileOutputStream fos = null; //为文件写出流做外部引用 byte[] buf =new byte[1024*1024*3]; //定义一个数组容器,大小为3个MB int len=0; //用来临时存储数据 int count =0 ; //用来为文件夹计数 while((len=fis.read(buf))!=-1) //读取数据 { fos =new FileOutputStream("S:\\develop\\JavaText\\part\\Beyond"+(count++)+".part"); //初始化文件写出流并动态关联地址 fos.write(buf,0,len); //写入数据 fos.close(); //关流,意味着 文件写出流中只有3MB数据写入到了文件中,再次循环则会再次创建文件 } fis.close(); } //合并方法 public static void merge() throws IOException { ArrayList<FileInputStream> al =new ArrayList<FileInputStream>(); //初始化集合用来存文件读入流 for(int i=0;i<=3;i++) { al.add(new FileInputStream("S:\\develop\\JavaText\\part\\Beyond"+i+".part")); //将文件读入流对象动态加入ArrayList集合 } final Iterator<FileInputStream> it = al.iterator(); //获取迭代器 //这里是通过匿名内部类的方式实现其所有抽象方法来完成实例化,Enumeration 与 ArrayList 的关联是 依靠 迭代器 ,请看 点击下边 it Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() //创建枚举对象,将集合中元素存入,以便序列流使用 { public boolean hasMoreElements() //复写其测试此枚举是否包含更多元素的方法 { return it.hasNext(); } public FileInputStream nextElement() //复写其如果此枚举对象至少有一个要提供的元素,则返回此枚举的下一个元素的方法 { return it.next(); } }; //------------以上都是为了获取下边的这一句合并流的类 SequenceInputStream sis =new SequenceInputStream(en); //获取序列流用来多流合一 FileOutputStream fos =new FileOutputStream("S:\\develop\\JavaText\\part\\Beyond.mp3"); //初始化一个写出流并关联目标地址 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 main(String args[]) throws IOException { // splitFile(); //分割流方法 merge(); //合并流方法 } }
小结 :
1. 一个源对应是三个目标地是切割,三个源对应一个目标地是合并
四十 九 . IO流(对象的序列化)
1 ) . 序列化就是给对象起标识符存入介质的过程
2 ) . 案例解说 : 关于用户基本信息存储需要用到操作对象流的问题
2.1 初始化对象之后正常是存在内存中,程序结束之后调用GC进行垃圾回收,内存内数据消失 ;而 一些账户基本信息是不能消失的, 因此 要将其存入可持久化的介质(硬盘),这时 考虑到 IO流,但基本IO流是用来处理数据的,这时需处理对象, 需要用到 ObjectInputStream,ObjectOutputStrean 这是操作对象的流, 前提是 操作的对象需要被 序列化(实现serializable接口),也就是 为对象劳个章(起个UID,标识一下)
3 ) . Demo:
/* 本章讲述 : 操作对象的流对象 , 被操作的对象需要实现Serializable(标记接口) [1] ObjectInputStream : 用来读取对象 [2] ObjectOutputStream : 用来写出对象 [3]Serializable : 启用其序列化功能的接口,仅用来表示序列化对象 [4] transient : 该标识符标识到变量前,可让其变脸不参与序列化,但存在于堆内存中 [5] 一般存储序列化对象的文件名格式是 : 对象名.object 1.为何要序列化? 只有序列化后才可使用操作流对象,另外序列化后让对象存储时有一个uid去标识,这样在获取时方便 2.UUID是如何算出来的? 无论任何一个类中的属性参数其实都是具有标识的,UUID就是通过这些标识值算出来的,用来标识自己的类对象,因此唯一性非常之高 须知 : [1]静态是不能被序列化的,序列化的是堆内的数据,而静态是在静态区中的 [2] 用ObjectOutputStream写出,必须 用他ObjectInputStream才能读入,两者成对存在的 */ import java.io.*; import java.util.*; class ObjectInputStreamDemo { public static void sop(Object obj) { System.out.println(obj); } //读对象方法 public static void readObject() throws Exception { ObjectInputStream ois =new ObjectInputStream(new FileInputStream("obj.txt")); //初始化一个对象读入流,并关联字节读入流的目标地址 Person per =(Person) ois.readObject(); //读取目标地址中的对象与person关联 sop(per); //输出 } //写对象方法 public static void writeObje() throws IOException { ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("obj.txt")); //初始化一个对象写出流,并关联字节写出流的目标地址 oos.writeObject(new Person("summer",13)); //初始化一个对象,将对象写入到对象写出流中,写出流会写入到关联的字节写出流的目标地址内 oos.close(); //关闭对象写出流 } //主方法 public static void main(String args[]) throws Exception { // writeObje(); readObject() ; } } class Person implements Serializable //对象的可串行化-->指对象序列化 //默认的实现此接口 会自动为该类赋予一个UUID,没初始化一次便更新一次UUID { public static final long serialVersionUID=42L; //serialVersionUID : 这个名字是特定的,一旦写错则自定义UUID失败 private String name; transient int age; //意味着 该变量不被序列化 Person(String name,int age) { this.name=name; this.age=age; } public void setName(String name) { this.name=name; } public String getName() { return name; } public void setAge(int age) { this.age=age; } public int getAge() { return age; } }
小结 :
1. 对象的持久化就是将对象信息存储到可长期保存的介质上,也就是对象的序列化2. 我们可通过对象的系列化进行对象的重构,意味着将对象信息存储到硬盘,然后下次使用然后再读出来
3. 没有方法的接口通常称为标记接口 ,比如 Serializable 可串行化接口
4. 序列化的作用就是给类定义一个固定标识,就是为了使用操作对象流方便
五十. IO流(管道流)
1 ) . 简述 :
1.1 RandomAccessFile
[1] 随机访问文件,自身具备读写方法
[2] 通过skipBytes(int x),seek(int x)来达到随机访问
1.2 管道流 : PipedInputStream和PipedOutputStream
[1] 输入输出可以直接进行连接,通过结合线程使用
[2] 认识 : 以前的流需要输入流输出流两个流操作.并且两者没关系; 现在一个管道流就可搞定,内含输入输出
2 ) . Demo :
/* 本章讲述 : 操作线程的流是 PipedInputStream与PipedOutputStream ,这两者之间可以相连接组成管道流 //测试read与writer两个线程的运行先后顺序 ,下边有输出语句用来测试 线程运行的先后顺序 */ import java.io.*; import java.util.*; class RandomAccessFileDemo { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws Exception { PipedInputStream in =new PipedInputStream(); //实例化管道流的读入流对象 PipedOutputStream ou =new PipedOutputStream(); //实例化管道流的写入流对象 Read rd =new Read(in); //将读入流放入读方法 Writer wr =new Writer(ou); //将写入流放入写方法 in.connect(ou); //让其两个管道流相连接 new Thread(rd).start(); //启动读方法 new Thread(wr).start(); //启动写方法 } } //可写的线程对象 class Writer implements Runnable { private PipedOutputStream ou ; //管道流的可写流 ,在可写线程一实例化变出现 Writer(PipedOutputStream ou) { this.ou=ou; } public void run () //启动线程的方法 { try { System.out.println("开始写入数据......."); //测试read与writer两个线程的运行先后顺序 ou.write("piped a lai le".getBytes()); //往管道流的写流中写入数据 ou.close(); //关闭可写流 } catch(IOException e) { throw new RuntimeException("管道流读取失败"); //抛出异常 } } } //可读的线程对象 class Read implements Runnable { private PipedInputStream in ; //管道流的可读流 ,在可读线程一实例化变出现 Read(PipedInputStream in) { this.in=in; } public void run () //启动线程的方法 { try { byte[] buf= new byte[1024]; // 定义临时存储数据容器 System.out.println("读取前,目前没有数据产生"); //测试read与writer两个线程的运行先后顺序 int len =in.read(buf); //将读出的数据放入数组,并返回长度 System.out.println("已读到数据,堵塞结束"); //测试read与writer两个线程的运行先后顺序 String s =new String(buf,0,len); //将数据放入字符串 System.out.println(s); //打印字符串 in.close(); //关闭可读流 } catch(IOException e) { throw new RuntimeException("管道流读取失败"); //抛出异常 } } }
小结 :
1. 可以对接到一起的流对象就是管道流2. 集合当中涉及到IO流的是Properties , IO流当中涉及到多线程的就是PipedInputStream
五 十一. IO流( RandomAccessFile)
1 ) . RandomAccessFile : 随机文件的读取和写入 -->其中涉及到一个案例是 下载软件的原理
2 ) . Demo:
/* 本章讲述 : RandomAccessFile: 该类支持对随机文件的读取和写入 特点: [1] 该类不是IO体系中子类,是Object的子类; 但由于它具备读写功能,因此是IO包中成员 [2] 该类内部封装了一个数据,可通过指针对数据的元素进行操作 [3] 该类可通过getFilePointer获取指针位置,通过seek改变指针的位置 [4] 该类通过构造函数可看出,只能操作文件,而且操作文件时还得传入模式参数:例如: 只读r ,读写rw等 解析 : 他为何可完成读写? 因为内部封装了字节输入流和输出流 方法 : seek(); 调整指针指向的坐标 skipBytes(); 跳过指定字节数 write(); 向随机访问流对象内写数据 read(); 读取随机访问流对象的数据 小结 : [1] 若模式为只读r,则不会创建文件,会去读取一个已存在文件,若该文件不存在,则会出现异常 [2] 若模式为读写rw,操作的文件不存在时,会自动创建,若存在则不会覆盖 */ import java.io.*; import java.util.*; class RandomAccessFileDemo { public static void sop(Object obj) { System.out.println(obj); } public static void getRead()throws Exception { RandomAccessFile raf =new RandomAccessFile("ran.txt","r"); //初始化随机访问流对象 //该方法是调整指针,动态的获取文件中任何地方的数据,前提是文件中的数据有规律 // raf.seek(8*0); //这里存储的数据是 8 个字节的 ,若要让指针指到下一个坐标,即可获取下一个 8字节的数据 *2即可 //跳过指定字节数 raf.skipBytes(8); byte[] buf =new byte[4]; raf.read(buf); //可用来读取字节的方法 String name =new String(buf); int age = raf.readInt(); //专门用来读取int数据类型的方法 sop(name+"::"+age); raf.close(); } public static void getWrite()throws Exception { RandomAccessFile raf =new RandomAccessFile("ran.txt","rw"); raf.write("张三".getBytes()); //raf.write(97); //默认是char类型接收,即97占一个字节 raf.writeInt(97); //char类型向上提升为int,即占4个字节, 这样的好处是让字母与数字之间有了距离 raf.write("李四".getBytes()); raf.writeInt(99); raf.close(); } //主方法 public static void main(String args[]) throws Exception { // getWrite(); getRead(); } }
3 ) . 关于一个字符占几个字节 :
3.1 ASCII码表中 :
[1] 一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间
3.2 UTF-8中 :
[1] 一个英文字符等于一个字节,一个中文(含繁体)等于三个字节
3.3 Unicode编码中:
[1] 一个英文字符等于两个字节,一个中文 (含繁体) 等于两个字节
3.4 关于符号是 : 英文标点占一个字节,中文占两个字节
小结 :
1. 随机读写访问类可实现软件任务多线程的下载 ; 思想 : 将数据分为几段, 一个线程负责一段,即可完成同时下载 -->这就是下载软件的原理2. 若存储某事物时容量不确定时,尽量给最大容量,避免出现内存溢出 ; 例如 : 名字 , 中国人名字最长也就4个字, 当然还有 少数民族 有可能 6个字,那么给 16个字节即可
五 十二 . IO流( 操作基本数据类型的流对象DataStream)
1 ) . IO包中的其他类 :
1.1 DataInputStream与DataOutputStream -->操作基本数据类型
1.2 ByteArrayInputStream与ByteArrayOutputStream -->操作字节数组
1.3 CharArrayReader 与CharArrayWrite --> 操作字符数组
1.4 StringReader 与 StringWriter --> 操作字符串
2 ) . Demo:
/* 本章讲述 : DataInputStream与DataOutputStream : 可以用于操作基本数据类型的数据的流对象 须知 : DataOutputStream 写入的数据 必须 DataInputStream 读出,否则会出现乱码 */ import java.io.*; import java.util.*; class DataStreamDemo { public static void sop(Object obj) { System.out.println(obj); } //读入UTF-8修改版的编码格式的数据 public static void readUTF() throws IOException { DataInputStream dis =new DataInputStream(new FileInputStream("demoUTF.txt")); String summ= dis.readUTF(); sop("summ:"+summ); } //以UTF-8的修改版的编码格式写出 public static void writeUTF() throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream("demoUTF.txt")); dos.writeUTF("sumer"); } //写出数据类型相关的数据 public static void writeData() throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream("demo.txt"));//初始化数据类型输出流对象 dos.writeInt(99); dos.writeBoolean(true); dos.writeDouble(98.2342); dos.close(); } //读入数据类型相关的数据 public static void readData() throws IOException { DataInputStream dis =new DataInputStream(new FileInputStream("demo.txt")); //初始化数据类型输入流对象 int num= dis.readInt(); //写入int类型的数据 Boolean bol = dis.readBoolean(); Double dou =dis.readDouble(); sop(num+"::"+bol+"::"+dou); } //主方法 public static void main(String args[]) throws Exception { /* 演示读入写出数据类型相关数据的流 */ //writeData(); //readData(); /* 演示读入写出数据类型相关数据的流的其它字符写入流 */ // writeUTF(); readUTF(); } }
小结 :
1. UTF-8一个中文字3个字节,但UTF-8的修改版中是一个中文字4个字节2. 往后若只操作相关数据类型的流对象则用 Data...Stream
五 十三 . IO流(ByteArrayStream) --> 重点
1 ) . ByteArrayStream 该对象是数组与流对象的封装,使用方便
2 ) . Demo:
/* 本章讲述 : ByteArrayStream : 用于操作字节数组的流对象,底层封装的是 可变长度字节数组 ByteArrayInputStream :在构造的时候,需要接受数据源,而且数据源是一个字节数组 ByteArrayOutputStream : 在构造的时候,不用定义数据目的,因为该对象内部封装了可变长度的字节数据,因此这就是数据目击地 特点 : 该对象不涉及系统资源,因为都是在内存中操作数组,所以close也不用关闭 总结 : 流操作规律讲解 : 源设备 : 键盘 : System.in. 硬盘 FileStream 内存 ArrayStream 目的设备: 控制台: System.out 硬盘 FileStream 内存 ArrayStream */ import java.io.*; import java.util.*; class ByteArrayStreamDemo { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) { //数据源 ByteArrayInputStream bais =new ByteArrayInputStream("ASDFGH".getBytes()); //数据目地 ByteArrayOutputStream baos =new ByteArrayOutputStream(); int by=0; //临时存储变量 while((by=bais.read())!=-1) //读数据 { baos.write(by); //写数据 } sop(baos.size()); //获取数组大小 sop(baos.toString()); //以字符串的形式打印数组内容 //将数据写入磁盘文件 baos.writeTo(new FileOutputStream("rellay.txt")); } }
小结 :
1. 对数组的操作就是 设置和获取 ,同样对流的操作时 写 和读
五 十四. IO流(转换流的字符编码)
1 ) . 简述 : 字符编码
1.1 字符流的出现为了方便操作字符 ,更重要的是加入了编码转换
1.2 通过子类转换流来完成:
[1] InputStreamReader
[2] OutputStreamWriter
1.3 在两个对象进行构造的时候可以加入字符集
2 ) . 可以玩编码表的流 :
2.1 转换流:
[1] InputStreamReader
[2] OutputStreamWriter
2.2 打印流:
[1] printWriter
[2] printReader
3 ) . 编码表的由来 :
3.1 历程 : 开关开关的电信号-->1010的二进制 -->1010与不同国家的不同文字相对应而形成了一张表 --->这张表就是编码表
3.2 分类 :
[1] ASCII : 美国标准信息交换码 : 用一个字节的七位可以表示
[2] ISO8859-1: 拉丁码表.欧洲码表 : 用一个字节的8位表示
[3] GB2312 : 中国的中文编码表: 用两个字节的16位表示
[4] GBK : 中国的中文编码表升级融合了更多的中文文字符号
[5] Unicode : 国际标准码表,融合了多国文字 : 所有文字都是用两字节表示,java语言使用的就是unicode
[6] UTF-8 : 最多用三个字节来表示一个字符-->也就是 可用一个字节,也可用两个字节,最多用三个字节
4 ) . DEmo:
/* 本章讲述 : [1] OutputStreamWriter : 写出的字符字节转换流,其构造方法可指定编码 [2] InputStreamReader : 读入的字符字节转换流,其构造方法可指定编码 总结 : GBK编的码 用UTF-8去读则会出现 ?? 的乱码; UTF-8编的码用GBK去读则会出现类似繁文的文字 */ import java.io.*; import java.util.*; class EncodeStream { public static void sop(Object obj) { System.out.println(obj); } public static void writeText() throws IOException { OutputStreamWriter osw =new OutputStreamWriter(new FileOutputStream("demo.txt"),"UTF-8"); osw.write("你好"); osw.close(); } public static void readText() throws IOException { InputStreamReader isr =new InputStreamReader(new FileInputStream("demo.txt"),"GBK"); char[] buf =new char[1024]; int len =isr.read(buf); String str =new String(buf,0,len); sop(str); isr.close(); } //主方法 public static void main(String args[]) throws IOException { // writeText(); readText(); } }
小结 :
1. UTF-8编的码拿着GBK解码这时会出现繁体乱码; GBK编的码拿着UTF-8解码这时会出现??乱码
五 十五. 字符编码
1 ) . 字符编码就是字符串与字节数组来回转换的过程
2 ) . Demo:
/* 本章讲述 : 编码 : 字符串编程字节数组 解码 : 字节数组变成字符串 String--> byte[]; str.getBytes(charsetName); byte[] --> String : new String(byte[],cahrsetName); */ import java.io.*; import java.util.*; class EncodeDemo { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws IOException { String s = "你好"; byte[] b1 = s.getBytes("UTF-8"); // 编码 sop("编码输出:"+Arrays.toString(b1)); //输出编码内容 String str =new String(b1,"GBK"); //解码 sop("解码输出:"+str); ///解码失败,因为编码的码表与解码的码表不是一张表,一个UTF-8,一个GBK byte[] b2 = str.getBytes("GBK"); //反向编码 sop("反向编码输出:"+Arrays.toString(b2)); String str2 = new String(b2,"UTF-8"); //再次解码 sop("解码输出:"+str2); //解码成功,因为编码的码表与解码的码表是一张表,是UTF-8 //结论 : 拿什么编的码就得拿什么解码 ,若出现编码失败,则反向编码出原来内容即可,再次重新解码即可 } }
3 ) . 重点 :
3.1 当我们是get请求时 , 解决乱码的方式就是 再次 反向编码
小结 :
1. 所谓的数据编码及解码就是对数据的来回 转换2. Tomcat服务器的默认编码格式是ISO8859-1
3 .我们通常会用到的码表是 GBK ,ISO8859-1 ,UTF-8
4. 项目中要统一编码格式
五 十六 . 字符编码-联通
1 ) . 字符编码 : 明白UTF-8的编码怎么判断何时读一字节,何时读两字节,何时读三字节的, 当我们换算为 二进制时,是依靠二进制前的标识符判断的
2 ) . Demo:
/* 本章讲述 : Integer.toBinaryString() :用来将字符串换算为二进制 &255 : 用来取二进制的有效位,也就是后八位 小结 : 当字节前的标识符是111时,便一次读三个字节 当字节前的标识符是11时,便一次读两个字节 当字节前的标识符是1时,便一次读一个字节 */ import java.io.*; import java.util.*; class EncodeDemo1 { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws IOException { String str ="联通"; byte[] buf = str.getBytes("GBK"); for(byte b : buf) { sop(Integer.toBinaryString(b&255)); } } }
小结 :
1. 转码的过程 : 字节数据--><--编码表 -->生成 对应符号2. UTF-8的编码数据中有标识头信息,用来标识是读1个字节还是2个字节还是3个字节
[1] 当标识头是111时,便向下读两个字节+自身 =3字节
[2] 当标识头是11时,便向下读1个字节+自身 =2字节
[3] 当标识头是1时,便读自身即可也就是1字节
五 十七 . 练习
1 ) . 以下输入数据,处理数据(数据排序),存入磁盘的过程
2 ) . Demo:
/* 本章讲述 : 需求 : 有五个学生,每个学生有三门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩) 输入的格式: 如: 张三,30,40,60计算出总成绩 并把学生的信息和计算出的总分数高低顺序存放在磁盘文件的"stud.txt"中 实现步骤 : [1] 描述学生对象 [2] 定义一个可操作学生的工具类 思想: 1.通过获取键盘录入一行数据,并将该行的信息取出封装成对象 2.因为学生量大,因此需要存储集合,又因需总分排序,则需要TreeSet 3.通过IO流的方式将集合的信息写入到一个文件中 总结 : [1] HashSet底层是哈希表结构,依赖的是HashCode和Equals [2] TreeSet底层是二叉树结构,依赖的是CompareTo 用到的方法: [1] Comparable接口中的compare()方法 : 用来为指定数据进行排序 [2] equals()方法 : 默认排序 [3] TreeSet集合:存储可排序的数据 [4]BufferedReader : 用来加强读入IO流数据的临时容器 [5] InputStreamReader: 字符流与读字节流之间的转换器 [6]System.in : 用来在dos窗口输入数据的方法 [7]split : 字符串的数据切割 [8]BufferedWriter : 用来加强写出IO流数据的临时容器 [9]FileWriter: 用来写出数据的IO字符流 [10]compareto :比较器当中的比较方法 [11] Collections.reverseOrder() : 集合工具类当中让已排序的数据按逆序排列的方法 */ import java.io.*; import java.util.*; //该类当中默认使用equals , 优先使用compareTo class Student implements Comparable<Student> //可操作对象 { private String name; private int ma,cn,en; private int sum; Student(String name,int ma,int cn,int en) { this.name=name; this.ma=ma; this=cn; this.en=en; this.sum=ma+cn+en; } public int compareTo(Student s) //实现比较器 { int num =new Integer(this.sum)pareTo(new Integer(s.sum)); //比较总成绩 if(num==0) return this.namepareTo(s.name); return num; } public String getName() { return name; } public int getSum() { return sum; } public int hashCode() //这个方法在此过程中不具备意义,只是考虑若使用set存储该对象则需要实现此方法 { return name.hashCode()+sum*78; // } 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<Student> getStudents() throws IOException //该方法是下边方法的抽取版本,下边方法是此方法的完善版本 -->理解 : 一个 { return getStudents(null); } //将数据写入并放入集合的方法 public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException { BufferedReader bufr =new BufferedReader(new InputStreamReader(System.in)); //初始化一个缓存区写入流用来写入数据 String line= null; Set<Student> stus= new TreeSet<Student>(); //初始化一个临时存储集合 if(cmp==null) //判断是否传入比较器,若传入则初始化传入的TreeSet集合 stus= new TreeSet<Student>(); else stus= new TreeSet<Student>(cmp); while((line=bufr.readLine())!=null) //将数据放入集合的方法 { if("over".equals(line)) break; String[] info = line.split(","); //将数据切割 Student st =new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3])); stus.add(st); //切割后的数据放入集合 } bufr.close(); //关流 return stus; //返回集合中的数据 } //将集合中的数据通过IO流放入本地文件的方法 public static void write2File(Set<Student> stus) throws IOException { BufferedWriter bufw =new BufferedWriter(new FileWriter("stuinfo.txt")); for(Student stu:stus) { bufw.write(stu.toString()+"\t"); bufw.write(stu.getSum()+""); bufw.newLine(); bufw.flush(); } bufw.close(); } } class EncodeText { public static void sop(Object obj) { System.out.println(obj); } //主方法 public static void main(String args[]) throws IOException { //用来将下边集合中的数据的排序逆序的方法 Comparator<Student> cmp = Collections.reverseOrder(); //通过工具类调用输入流的方法用来输入数据,并将输入的数据放入集合 Set<Student> stus = StudentInfoTool.getStudents(cmp); //将数据通过流写出到本地磁盘文件的方法 StudentInfoTool.write2File(stus); } }
本文标签: (十二)Core Java IO流(Properties 序列化 管道流 字符编码)
版权声明:本文标题:(十二)Core Java IO流(Properties,序列化,管道流,字符编码) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1697821244h275439.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论