欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javaIO包 博客分类: java基础 javaio输入输出流 

程序员文章站 2024-03-24 22:04:52
...
Java IO流:
流是一组有序的有头有尾的字节集合,是数据传输总称或抽象。
IO流是用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流
流按流分为:输入流(input),输出流(output)(相对于内存来说的)

根据处理的数据不同:分为:字节流和字符流。

**所有的数据都是以字节体现的,后期产生了字符流,因为字符数据涉及到了编码问题,所以在字符流对象中加入了编码机制。如果处理的数据都是字符数据,只是可以使用字符流对象来完成。

IO流常用基类:
  字节流的抽象基类:
  InputStream,OutputStrean
  
  字符流的抽象基类:
  Reader,Writer
  
  **由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
  如:InputStream的子类FileInputStream
  如:Reader的子类FileReader

IO程序的书写
1,导入IO包中的类
2,进行IO异常处理
3,在finally中对流进行关闭

FileWriter类:

FileWriter fw = new FileWriter(“Demo.txt”);
//FileWriter:用来操作文件的字符串输出流对象
/*
创建该对象做的事情:
1,在堆内存中产生了一个实体
2,调用了系统底层资源,其实是调用了windows的功能,在指定位置创建了一个文件,建立数据存储的目的地。用于存放即将写入的数据。
3,因为目的地可能因为路径错误,而导致失败,所以抛出了IOException,需要对其处理。

**如果创建的文件已经存在,那么会覆盖原文件。
*/

//既然有了流对象,指定具体数据,使用fw对象的write方法将数据写成,fw对象的write
//方法,将数据写入到了流中,其实就是内存中。
Fw.write(“abcdefg”);

//刷洗缓冲区,将流中的数据刷到目的地中。
Fw.flush();

Fw.write(“kkkkkk”);

Fw.flush();

Fw.write(“qqqqqq”);

/*
Close方法:
1,先刷新缓冲区的数据到目的地,其实就是调用了一次flush。
2,关闭了调用底层的资源。将资源释放。
*/
Fw.close();


Flush()和close()的不同:
Flush():只会刷新缓冲区,流依然存在,并可以继续使用
Close():也会刷新缓冲区,但是刷新后,立刻关闭流资源,流不可以在继续使用。


FileReader类:【


  /*
  创建一个读取流对象,并关联要读取的文件。
  将要读取的文件作为构造函数的参数传递,对象一初始化就必须有一个已经存在的数据。
  */
  FileReader fr = new FileReader(“Demo.txt”);
  
  /*
  //调用了读取流对象的read方法,一次读一个字符。
  Int ch = fr.read();
  */
  
  Int ch = 0;
  While((ch = fr.read())!=-1){//当为-1的时候就不读了
  System.out.println((char)ch);
  }
  
  System.out.println((char)ch);
  
  Fr.close();
  
  或者:(这种方法比较好,循环次数较少)
  
  FileReader fr = new FileReader(“Demo.txt”);
  
  Char[] buf = new char[1024];
  
  Int len = 0;
  While((len=fr.read(buf))!=-1){
  System.out.println(new String(buf,0,len));
  }




BufferedWriter类:(带缓冲技术的字符输出流)
  FileWriter fw = new FileWriter(“bufdemo.txt”);
  BufferedWriter bufw = new BufferedWriter(fw);

  Bufw.write(“haha”);
  Bufw.newLine();//依据不同的平台,进行换行的操作
  Bufw.write(“hehe”);
  bufw.newLine();
  Bufw.flush();//养车一个习惯,只要用到了缓冲区,就记着一定要刷新
  Bufw.close();不用关闭fw了,因为缓冲区是提高流的操作效率而存在,只是内部提供了 数组,对数据进行缓冲,最终的写入还是流完成的,所以关闭缓冲区,其实就是在关闭 流,也就是bufw.close其实内部调用的就是fw.close();


BufferedReader类:
  FileReader fr = new FileReader(“bufdemo.txt”);
  BufferedReader bufr = new BufferedReader(fr);
  String line = null;
  While((line=bufr.readLine())!=null){
  System.out.println(line);
  }
  Bufr.close();


复制的原理:
  对源文件进行读取。
  并将读取的数据存储到目的文件中。
  
  1,(不同缓冲区)
  FileReader fr = null;
  FileWriter fw = null;
  Try{
  Fr = new FileReader(“Demo.txt”);
  Fw = new FileWriter(“copyDemo.txt”);
  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{
  Try{
  If(fw!=null)
  Fw.close();
  }catch(IOException e){
  Throw new RuntimeException(“写入关闭失败”):
  }
  Try{
  If(fr!=null)
  Fr.close();
  }catch(IOException e){
  Throw new RuntimeException(“读取关闭失败”);
  }
  }
  
  2,(使用缓冲区)
  import java.io.*;
  
  class TestBuffer {
   public static void main(String[] args){
   FileReader fr = null;
   FileWriter fw = null;
   BufferedReader br = null;
   BufferedWriter bw = null;
   try
   {
   fr = new FileReader("E:\\ITcast\\day24\\copyText.java");
   fw = new FileWriter("E:\\ITcast\\day24\\Copy.txt");
   br = new BufferedReader(fr);
   bw = new BufferedWriter(fw);
   String str = null;
   while ((str=br.readLine())!=null)
   {
   bw.write(str);
   bw.newLine();
   bw.flush();
   }
   }
   catch (IOException e)
   {
   throw new RuntimeException("复制不成功");
   }finally{
   try
   {
   if(br!=null)
   br.close();
   }
   catch (IOException e)
   {
   throw new RuntimeException("关闭输入失败");
   }
  
   try
   {
   if(bw!=null)
   bw.close();
   }
   catch (IOException e)
   {
   throw new RuntimeException("关闭输出失败");
   }
   }
   }
  }




字符流的缓冲区,是为了提高效率而存在的。
BufferedWriter
  NewLine();
BufferedReader
  readLine();调用基础流对象的read方法,一次读一个字符,并吧该字符进行了临时存储。直到读到回车换行符为止,将存储的数据作为字符串返回。
缓冲区的出现提供了比以前流对象功能更前的函数。


异常处理方式:

Import java.io.*;
Class FileWriterDemo2{
  Public static void main(String[] args){
  FileWirter fw = null
  Try{
  Fw = new FileWriter(“d:\\Demo.txt”);
  Fw.write(“abcdef”);
  }catch(IOException e){
  System.out.println(e.toString());
  Throw new RuntimeException(“写入失败”);
  }finally{
  Try{
  If(fw != null)
  Fw.close();
  }catch(IOException e){
  System.out.println(e.toString());
  Throw new RuntimeException(“关闭失败”);
  }
  }
  }
}

这样写的好处,就是写入的时候发生了错误,finally中的代码也会继续执行,也就是说可以执行到close方法,将流关闭,将FileWriter写在try catch的外面,是为了让finally中的变量也try中的变量指向同一个流。


如何对已有的文件进行数据的续写?
使用FileWriter对象的另一个构造函数,带一个boolean类型的参数的构造函数。只要boolean类型值为true,就可以完成续写。
**如果写入回车换行符,在windows中需要\r\n来完成。

FileReader
FileWriter
BufferedWriter
BufferedReader
凡是直接操作数据的流对象,或者操作文件的流对象,都是最常用的流对象。
因为文件就是数据体现形式。
而操作流就需要提高效率,所以带缓冲的流对象,也是常用对象。

inputStream类:
  FileInputStream fis = new FileInputStream(“fos.txt”);
  Byte[] buf = new byte[1024];
  Int len = 0;
  While((len=fis.read(buf)) != -1){
  System.out.println(new String(buf,0,len));
  }
  Fis.close();


outputStream类:
  FileOutputStream fos = new FileOutputStream(“fos.txt”);
  Byte[] buf = “abcde”.getBytes();
  Fos.write(buf);//字节流的写入方法,直接将数据写到了目的地,因为对象中不存在缓冲区
  Fos.close();//关闭资源


复制图片:(复制的几种方式)
1,
  FileInputStream fis = new FileInputStream(“c:\\0.bmp”);
  FileOutputStream fos = new FileOutputStream(“c:\\1.bmp”);
  Byte[] byf = new byte[1024];
  Int len = 0;
  While((len=fis.read(buf))!=-1){
  Fos.write(buf,0,len);
  }
  Fos.close();
  Fis.close();

2,
  FileInputStream fis = new FileInputStream(“c:\\1.mp3”);
  BufferedInputStream bufis = new BufferedInputStream(fis);
  FileOutputStream fos = new FileOutputStream(“c:\\3.mp3”);
  BufferedOutputStream bufos = new BufferedOutputStream(fos);
  
  Int by = 0;
  While((by = byfis.read())!=-1){
  Byfos.write(by);
  }
  Bufos.close();
  Bufis.close();



不能通过字符流复制图片,因为读取图片数据时,字符流会查默认的编码表。如果在编码表中没有查到具体的数据,就会用其他数据替代。这时,元数据错乱,导致生成的图片无法观看。所占字节也不同。
**只要操作数据是文本,建议使用字符流,因为它可以进行指定编码的转换,除此之外,都用字节流。


键盘的录入:(将输入的字符转成大写,如果是over就结束)
  Int ch = 0;
  InputStream in = System.in;
  StringBuilder sb = new StringBuilder();
  While(true){
  Ch = in.read();
  If(ch == -1){ //通过控制台的ctrl+c可以强制结束录入,其实就是给流定义了结束标记
  System.out.println(“------”);
  Break;
  }
  If(ch == ‘\r’)
  Countinue;
  If(ch == ‘\n’){
  String s = sb.toString();
  If(s.equals(“over”))
  Break;
  System.out.println(s.toUpperCase());
  Sb.delete(0,sb.length()); //清空缓冲区
  }else
  Sb.append((char)ch);
  
  }
转换流:(字符流)
InputStreamReader字节通向字符流的桥梁
OutputStreamWriter字符通向字节的桥梁


InputStream in = System.in;//获取键盘录入读取流对象。//这个是字节流对象

//流转换,将字节流转成字符流
InputStreamReader isr = new InputStreamReader(in);

对字符流进行读取效率的提高,意思操作一行数据
BufferedReader bufr = new BufferedReader(isr);//这个是字符流对象

OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);

String line = null;
While((line = bufr.readLine())!=null){
  If(“over”.equals(line))
  Break;
  Bufw.write(line.toUpperCase());
  bufw.newLine();
  Bufw.flush();
}

Bufw.close();
Bufr.close();



**//一次读键盘一行
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)));
BufferedWriter bufw = new BufferedWriter(new OutStreamWriter(System.out)));

流的操作规律?
因为io包中的对象很多,最重要的是要知道完成数据处理时,要使用哪个对象最合适。
如何判断要使用那些对象?
  通过几个明确来判断对象的使用。
  1,明确数据源,和数据目的(数据汇)
  数据源:InputStream Reader
  数据目的:OutputStream Writer
  2,明确数据的内容是否是纯文本。
  数据源:是:Reader
  数据目的:是:Writer
  如果不是:就使用InputStream或者OutputStream
  
  如果数据不能明确,只有使用字节流。
  这样就可以将四个基类,进行确定,要使用哪个。
  3,明确具体设备。
  数据源:键盘(System.in),内存(数组),硬盘(File开头的流对象)。
  数据目的:控制台(System.out),内存(数组),硬盘(File开头的流对象)。
  4,明确是否需要提高效率?
  是:使用带Buffer对象
  

Writer
  |--OutputStreamWriter
  |--FileWriter
Reader
  |--InputStreamReader
  |--FileReader
转换流其实就是将字节流和编码表相结合,将字节流中的字节数据去查了具体的编码表。
所以转换流才可以获取一个中文字符。
那么转换流的子类用于操作文件的对象FileReader就直接使用父类的具体有转换功能的read方法,就可以一次读一个字符。

FileReader fr = new FileReader(“a.txt”);//该对象中已经内置了本机默认的字符编码表
  //对于简体中文版的机器默认编码表示gbk
//通过字节读取流读取a.txt中中文数据,按照gbk编码表的来读取中文字符。
InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),”gbk”);

InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),”gbk”);
FileReader fr = new FileReader(“a.txt”);
这两句代码的功能是一样的。

区别:
第一句可以指定编码表
第二句,固定本机默认编码表

如果操作中文数据,仅使用本机默认码表,那么第二句简单
如果操作中文数据,使用指定码表,必须使用第一句,而且要将指定码表作为字符串传递到构造函数中。

File类:
数据存放的形式最常见的就是文件。
文件的属性较多,如文件名称,路径,大小等属性。
为了方便与操作java就将其视为对象。
通过File类对其描述。
提供了多个属性和行为。便于对文件进行操作。

而流对象只能用于操作文件中的数据。
对于文件的属性,都是通过File对象来完成。

File类可以是文件,也可以是路径。
将制定文件封装成File对象。
File f = new File(“c:\\a.txt”);

File f1 = new File(“c:\\”,”a.txt”);

File dir = new File(“c:\\”);
File f2 = new File(dir,”a.txt”);

File f3 = new File(“c:”+File.separator+”a.txt”);
File.separator是跨平台性的目录分隔符

File常见的方法:
1,创建文件
  Boolean createNewFile();//如果f这个文件不存在,该方法会对其创建
  //如果f已经存在,该方法不会创建
  //而输出流创建文件,如果该文件存在,会覆盖,但是,如果输出流
  //的构造函数传入参数为true,不覆盖文件,可以完成续写。
  创建文件夹
  Boolean mkdir();//创建单级目录,如果已经存在不创建
  Boolean mkdirs();//创建多级目录(就是目录下还有子目录),可以在已存在的目录下继 //续创建
2,删除
  Boolean delete();//java中的删除不走回收站
  //删除目录时,如果目录中有内容,应该先将内部内容删除,再删该目录
  //windows的删除就是从里往外删
  Void deleteOnExit();//程序退出时,把该文件删除。
3,判断(想要判断file对象封装的是文件还是目录,必须要确定内容是存在的)
  Boolean isFile()//是不是文件
  Boolean isDirectory()//是不是目录
  Boolean isAbsolute()//是不是据对路径
  Boolean exists()//判断封装的对象是否存在
  Boolean canExecute()//判断文件是否可以执行
  Boolean isHidden()//判断文件是否是隐藏文件
4,获取
  String getAbsolutePath();//获取绝对路径(完整路径)
  String getPath();//获取相对路径(file内封装的路径内容)
  String getName();//获取文件名
  String getParent();//获取父目录
  Long length();//获取文件的长度(文件的字节数,该方法只针对于与文件而言)
  Long lastModified();//文件最后一次修改的时间
5,重命名
  Boolean renameTo(file);//将文件名修改,可以进行文件的移动(剪切+重命名)
6,文件列表
  Static File[] listRoots();//列出系统的根
  String[] list();//获取当前目录下的文件夹和和文件的名称,如果file中封装的是一个文件,那么返回数组为null,所以最好做一个安全的判断
  File[] listFiles();//获取的是当前目录下文件或者文件夹对应的对系那个。
  
  (如果仅获取文件名称,就用list方法。如果还用获取文件的其他信息,最好用listFiles,因为它可以获取到文件对象,这样就可以通过文件对象方法,获取其他内容)



IO包中的其他类:
PrintWriter与PrintStream
可直接操作输入流和文件。

PrintStream:字节流中的打印流,可以直接操作设备的流对象。
  构造函数的参数特点:
  1,字符串路径。
  2,File对象。
  3,字节输出流。
  
PrintWriter:字符流中的打印流。
  构造函数的参数特点:
  1,字符串路径
  2,File对象
  3,字节输出流
  4,字符输出流
  
打印流可以直接操作文件,算是较为常用的流对象。
可以打印任意数据类型。


管道流:
  PipedInputStream和PipedOutputStream
  输入输出可以直接进行连接,通过线程使用。
  读取流和写入流可以进行连接。
  但是需要被多线程操作。
  因为read方法是阻塞式方法。容易引发死锁。



RandomAccessFile:
随机访问文件,自身具备读写的方法。
通过skipBytes(int x),seek(int x)来达到随机访问。
特点:
  1,既可以读又可以写。
  2,内部封装了一个大的byte类型的数组,这就说明该对象操作的数据是字节数据,说明其中封装了字节的读取流和写入流。而且可以使用内部的指针对这个数组进行数据的操作
  3,提供了getFilePointer方法获取指针的位置,还提供了seek方法设置指针的位置。
  4,通过该对象的构造函数可以得知,该对象只能操作文件。
  也就是说源和目的都是一个文件。
  并通过构造函数的另一个参数来确定,访问方式。该变量只能接收四个值。
  R:只读,rw:读写。Rws,rwd
  5,该对象中的方法可以操作基本数据类型。
  6,注意被操作的文件数据,希望有规律。这样可以通过数据的整数倍来控制指针的偏移。
   对数据进行操作,达到,随机访问的效果。

  可以应用于多线程对大数据的写入。同时写入,只要给每一个线程分配
  起始索引位,就可以完成多线程随机写入。
  提高了写入效率。
  
  
  使用的前提:
  1,必须是文件。
  2,数据有规律。比如等长数据。

*/

class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
writeDemo3();
// readDemo2();
}


//对已有数据进行修改。
public static void writeDemo3()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("info.txt","rw");

raf.seek(8*3);//从指针索引位8开始进行写入。
raf.write("赵六".getBytes());
raf.writeInt(72);

raf.close();
}

/*
既然能写,那么读也应该没有问题。
通过指针的操作,完成读取的随机效果。
*/
public static void readDemo2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("info.txt","r");

raf.seek(8*1);


byte[] buf = new byte[4];

int len = raf.read(buf);
String s= new String(buf,0,len);

System.out.println("name="+s);
int age = raf.readInt();//一次读四个字节并转成int数值。
System.out.println("age="+age);
raf.close();
}



//通过seek方法指定指针的位置,进行数据写入。
/*
发现RandomAccessFile操作的文件如果已经存在,不会再次创建,直接操作已有文件。。

发现通过seek的指针定位,就可以完成数据的随机写入。

它可以完成已有数据的修改。
*/
public static void writeDemo2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("info.txt","rw");

raf.seek(8*2);//从指针索引位8开始进行写入。
// raf.write("李四".getBytes());
// raf.writeInt(67);
raf.write("王武".getBytes());
raf.writeInt(68);

raf.close();

}
/*
通过该对象写点数据。
数据: 人员信息: 姓名,年龄
*/
public static void writeDemo()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("info.txt","rw");

raf.write("张三".getBytes());
//raf.writeBytes("张三");//解析出了问题。
//raf.writeChars("张三");//解析出了问题。
//raf.write(65);//write:只将一个int整数的最低字节写出。


raf.writeInt(65);

raf.close();

}

public static void readDemo()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("info.txt","r");

byte[] buf = new byte[4];

int len = raf.read(buf);
String s= new String(buf,0,len);

System.out.println("name="+s);
int age = raf.readInt();//一次读四个字节并转成int数值。
System.out.println("age="+age);
raf.close();
}

}




序列流:
SequenceInputStream
对多个流进行合并。

操作对象:
ObjectInputSteam与ObjectOutputStream
被操作的对象需要实现


ProPerties类:
Map
  |--Hashtable
  |--Properties
Properties:该集合中存储的键和值都是字符串类型的数据,通常同于配置文件的定义。
Properties类表示了一个持久的属性集,Properties可以保存在流中或从流中加载,属性列表中每个键及其对应都是一个字符串。
1,添加元素:
  Properties prop = new Properties();
prop.setProperty("xiaowang","12");
prop.setProperty("xiaozhang","13");
prop.setProperty("xiaoli","14");
  System.out.println(prop);

2,列表方法:
  Properties prop = new Properties();
prop.setProperty("xiaowang","12");
prop.setProperty("xiaozhang","13");
prop.setProperty("xiaoli","14");
  prop.list(System.out);//打印到控制台,只要让参数是System.out
  ------------------------------------------------------------------
Properties prop = System.getProperties();
  Prop.list(new PrintStream(“sys.txt”));//将系统信息写入到sys.txt文件中去

3,
Properties prop = new Properties();
System.out.println(prop);
FileReader fr = new FileReader("prop.txt");
prop.load(fr);
//使用集合的特有方法load
System.out.println(prop);
4,
//将硬盘中的键值对加载到Properties集合中。
Properties prop = new Properties();
System.out.println(prop);
FileInputStream fis = new FileInputStream("info.txt");
//使用集合的特有方法load,将流的特定规则信息存储到集合中,注意:流中的信息必须有规则是键值对。用=分隔
prop.load(fis);
System.out.println(prop);
fis.close();
5,
//模拟一个load方法。
/*
其实load方法很简单,就是通过流对象,读取文本中一行数据 。
在将该行数据通过=进行切割。左边作为键,右边作为值。
存入到Properties集合中。
*/
Properties prop = new Properties();
BufferedReader bufr =
new BufferedReader(new FileReader("info.txt"));
String line = null;
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");
prop.setProperty(arr[0],arr[1]);
}
System.out.println(prop);
bufr.close();
6,
/*
将硬盘上的数据进行集合的存储,希望在运算后,将改变后的结果,重新存回配置文件中。
*/
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
prop.load(fis);
System.out.println(prop);
prop.setProperty("wangwu","29");//把集合修改后的数据重新存回配置文件。
//需要写入流。需要一个存储方法。store(outputStream.string);
FileOutputStream fos = new FileOutputStream("info.txt");
prop.store(fos,"hah");
System.out.println(prop);
fos.close();
fis.close();


SequenceInputStreamm类:
可以将多个读取流变成一个读取流。


操作对象流:
  ObjectInputStream和ObjectOutputStream
  对象的持久化存储。
  也就是说将对象中封装的数据保存到持久化的设备上,比如硬盘。
  那么其他应用程序都需要建立该对象,直接读取设备上的对象即可。
  
  ObjectInputStream ObjectOutputtStream
  专门用于操作对象的流,封装了直接操作对象的方法。

写入对象:
  使用输出流中的writerObject();方法,里面传递一个对象。
  如果一个对象要被写入,必须具备序列化功能,也就是说必须要实现Serializable接口


读取对象:
  使用输入流中的readObject();方法,要注意的是需要强制转换,
  Person p = (Person)ois.readObject();


**对象中的静态数据是不会被持久化的。
只要将不需要被持久化的非静态数据进行transient关键字修饰即可。

Sirializable序列化接口:
  用于给类文件加一个UID,就是一个序列号。
  该序列通过类中的成员的数字签名完成运算得来的。
  
  
  当类中的成员发生大的改动时,类会重新编译,生成带有新的UID的序列号。这样就 可以和曾经存储的原来的类生成的对象的序列号不匹配。
  这样就可以让使用者必须重新对新类产生的对象进行存储。避免新类接收老对象出现安 全隐患,这就是序列号的功能所在。
  
  如果类中没有成员大的改动,只是有个别的一些修改,没有对对象产生打的影响,这时 候,不必去重新对类进行存储,这时可以在定义类时指定序列化,而不让jvm自动算 该序列化。
  
  Public static final long serialVersionUID = 42L;//给person指定一个固定的序列化。这样就 可以保证该类即使被改动,序列化也不会变





操作基本数据类型
  DataInputStream与DataOutputStream
  DataInputStream和DataOutputStream:
  writeUTF()方法一般流对里面的数据读取不出来,因为它用的是utf的修改表。只能用 DataInputStream中的readUTF()方法进行读取。
  
  
操作字节数组
  ByteArrayInputStream与ByteArrayOutputStream
  操作数组流对象,它对应的设备就是内存。
  ByteArrayOutputStream:内部封装了一个可变长度的字节数组
  关闭它是无效,因为该对系那个根本就没有调用过底层资源
  可以通过toByteArray()或者toString()获取数组中的数据
  
  ByteArrayInputStream负责数据源,在初始化的时候,必须要有一个数据源内容。
  因为操作的是数组,所以源就是一个字节数组。
  该对象中不会有异常发生,因为没有调用过底层资源。
  
  为什么不直接操作byte[],还要用到流?
  因为数组的操作,无非就是数组中的元素进行设置和获取,这个操作正好符合了读和写 的操作。
  使用流的操作思想在操作数组
  
操作字符数组
  CharArrayReader与CharArrayWrite
操作字符串
  StringReader与StringWriter


字符编码:
  字符流的出现为了方便操作字符
  更重要的是加入了编码转换。
  通过子类转换流来完成
  InputStreamReader OutputStreamWriter
  在两个对象进行构造的时候可以加入字符集。


常见的编码表:
  ASCII:美国标准信息交换码
  用一个字节的7位可以表示
  ISO8859-1:拉丁码表,欧洲码表
  用一个字节的8位表示
  GB2312:中国的中文编码表
  GBK:中国的中文编码表升级,融合了更多的中文文字符号。
  Unicode:国际标准码,融合了多种文字。
  所有文字都用两个字节来表示,java语言使用的就是unicode
  UTF-8:最多用三个字节表示一个字符

字符流 = 字节流+编码表,能指定编码表的转换流
内部默认了编码表的是转换流的子类FileReader,FileWriter,默认的是本机码表

OutputStreamWriter osw = new OutputStreamWrtier(new FileOutputStream(“gbk.txt”));
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream(“gbk,txt”),”GBK”);
FileWriter fw = new FileWriter(“gbk.txt”);
这三句是一样的,都是在使用默认的GBK表,在操作gbk.txt


编码解码问题:
编码:字符串——》字节数组。
  使用的是String类中的getBytes(charset);
  Byte[] buf = “你好”.getBytes(“utf-8”);
  
解码:字节数组——》字符串。
  使用String类中的构造函数。
  String s = new String(buf,”utf-8”);

在gb2312的码表中,中文是两个字节,都是负数,也就是每一个字节的最位都是1
在gbk的码表中,中文还是两个字节,第一个字节最高位是1,第二个字节最高位可能是0



上一篇: Android 蓝牙4.0 BLE问题总结

下一篇: