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