javaIO包

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

你可能感兴趣的:(java基础)