内容学习于:edu.aliyun.com


1. 字符编码

  在计算机的世界里面只认0、1的数据,如果要想描述一些文字的编码就需要对这些二进制的数据进行组合,所以才有了现在可以看见的中文,但是在进行编码的时候如果要想正确显示出内容则一定需要有解码,所以编码和解码肯定要采用统一的标准,那么如果不统一的时候就会出现乱码。
  那么在实际的开发之中对于常用的编码有如下几种:

  •   GBK/GB2312: 国标编码,可以描述中文信息,其中GB2312只描述简体中文,而GBK包含有简体中文与繁体中文;
  •   ISO8859-1:国际通用编码,可以用其描述所有的字母信息,如果是像型文字则需要进行编码处理;
  •   UNICODE编码:采用十六进制的方式存储,可以描述所有的文字信息;
  •   UTF编码:象形文字部分使用十六进制编码,而普通的字母采用的是ISO8859-1编码,它的优势在于适合于快速的传输,节约带宽,也就成为了我们在开发之中首选的编码,主要使用“UTF-8”编码

  项目中出现的乱码问题就是编码和解码标准不统一,而最好的解决乱码的方式,所有的编码都使用UTF-8。

2. 内存操作流

  在之前使用的全部都是文件操作流,文件操作流的特点,程序利用InputStream 读取文件内容,而后程序利用OutputStream向文件输出内容,所有的操作都是以文件为终端的。
  如下图所示:

假设说现在需要实现I0操作,可是又不希望产生文件(临时文件)则就可以以内存为终端进行处理,这个时候的流程如下图所示:

  在Java里面提供有两类的内存操作流:

  •   字节内存操作流: ByteArrayOutputStream、 ByteArrayInputStream;
  •   字符内存操作流: CharArrayWriter、 CharArrayReader;

  继承结构如下图所示:

  下面以ByteArrayOutputStream和ByteArrayInputStream类为主进行内存的使用分析,首先来分析各自的构造方法:

  •   ByteArrayInputStream构造: public ByteArrayInputStream(byte[] buf);
  •   ByteArrayOutputStream构造: public ByteArrayOutputStream();

  在ByteArrayOutputStream类里面有一个重要的方法,这个方法可以获取全部保存在内存流中的数据信息,该方法为:

  •   获取数据: public byte[] toByteArray();
  •   使用字符串的形式来获取: public String toString();

实现大小写转换代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        String str = "www.mldn.cn";//小写字母
        InputStream input = new ByteArrayInputStream(str.getBytes());//将数据保存到内存中
        OutputStream output = new ByteArrayOutputStream();//读取内存数据
        int data = 0;
        while ((data = input.read())!=-1){//每次只读取一个字节
            output.write(Character.toUpperCase(data));//保存数据
        }
        System.out.println(output);
        output.close();
        input.close();
    }
}

结果:

WWW.MLDN.CN

  如果现在不希望只是以字符串的形式返回,因为可能存放的是其它二进制的数据,那么此时就可以利用ByteArrayOutputStream 子类的扩展功能获取全部字节数据。

自己处理字节数据代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        String str = "www.mldn.cn";//小写字母
        InputStream input = new ByteArrayInputStream(str.getBytes());//将数据保存到内存中
        // 必须使用子类调用扩展的方法
        ByteArrayOutputStream output = new ByteArrayOutputStream();//读取内存数据
        int data = 0;
        while ((data = input.read()) != -1) {//每次只读取一个字节
            output.write(Character.toUpperCase(data));//保存数据
        }
        byte[] result = output.toByteArray();//获取全部字节数据
        System.out.println(new String(result));//自己处理字节数据
        output.close();
        input.close();
    }
}

结果:

WWW.MLDN.CN

  在最初的时候可以利用ByteArrayOutputStream 实现大规模文本文件的读取。

3. 管道流

  主要功能如下图所示:
  对于管道流也是分为两类:

  •   字节管道流: PipedOutputStream、 PipedInputStream;
    •   连接处理: public void connect(PipedInputStream snk) throws IOException;
  • 字符管道流: PipedWriter、 PipedReader.
    •   连接处理: public void connect(PipedReader snk) throws IOException

  都是IO处理类的直接子类

3.实现管道流代码:

class SendThread implements Runnable {
    private PipedOutputStream output;//管道输出流

    public SendThread() {
        output = new PipedOutputStream();//实例化管道输出流
    }

    @Override
    public void run() {
        for (int x = 0; x < 10; x++) {
            try {//利用管道实现数据的发送
                this.output.write(("第" + (x + 1) + "次返送--"
                        + Thread.currentThread().getName() + "www.mldn.cn\n").getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        try {
            this.output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public PipedOutputStream getOutput() {
        return this.output;
    }
}


class ReciveThread implements Runnable {
    private PipedInputStream input;

    public ReciveThread() {
        input = new PipedInputStream();
    }


    @Override
    public void run() {
        byte data[] = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//所有的数据保存在内存输出流
        try {
            while ((len = this.input.read(data)) != -1) {
                bos.write(data, 0, len);//数据保存在内存流中
            }
            System.out.println("{" + Thread.currentThread().getName() + "}"
                    + "接受消息\n" + new String(bos.toByteArray()));
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            this.input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public PipedInputStream getInput() {
        return this.input;
    }
}


public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        SendThread sendThread = new SendThread();
        ReciveThread reciveThread = new ReciveThread();
        sendThread.getOutput().connect(reciveThread.getInput());//进行管道连接
        new Thread(sendThread, "消息发送线程").start();
        new Thread(reciveThread, "消息接收线程").start();
    }
}

结果:

{消息接收线程}接受消息
第1次返送–消息发送线程www.mldn.cn
第2次返送–消息发送线程www.mldn.cn
第3次返送–消息发送线程www.mldn.cn
第4次返送–消息发送线程www.mldn.cn
第5次返送–消息发送线程www.mldn.cn
第6次返送–消息发送线程www.mldn.cn
第7次返送–消息发送线程www.mldn.cn
第8次返送–消息发送线程www.mldn.cn
第9次返送–消息发送线程www.mldn.cn
第10次返送–消息发送线程www.mldn.cn

  管道就类似于医院打点滴效果,一个只是负责发送,一个负责接收,中间靠一个管道连接。

4. 随机读取类

  对于文件内容的处理操作主要是通过InputStream (Reader)、 OutputStream (Writer) 来实现,但是利用这些类实现的内容读取只能够将数据部分部分读取进来,如果说现在有这样一种要求。
现在给了你一个非常庞大的文件,这个文件的大小有20G,如果此时按照传统的IO操作进行读取和分析根本就不可能完成,所以这种情况下在java.io包里面就有一个RandomAccessFile类,这个类可以实现文件的跳跃式的读取,可以只读取中间的部分内容(前提,需要有一个完善的保存形式),数据的保存位数要都确定好。

  RandomAccessFile类里面定义有如下的操作方法:

  •   构造方法: public RandomAccessFile(File file, String mode) throws FileNotFoundException;
  •   文件处理模式: r、rw;

文件保存代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        File file = new File("d:" + File.separator + "mldn.txt");
        RandomAccessFile raf = new RandomAccessFile(file, "rw");//读写模式
        String str[] = new String[]{"zhangsan", "wangwu ", "lisi "};
        int age[] = new int[]{30, 20, 16};
        for (int i = 0; i < str.length; i++) {
            raf.write(str[i].getBytes());//
            raf.writeInt(age[i]);
        }
        raf.close();
    }
}

  RandomAccessFile最大的特点是在于数据的读取处理上,因为所有的数据是按照固定的长度进行的保存,所以读取的时候就可以进行跳字节读取:

  •   向下跳: public int skipBytes(int n) throws IOException;
  •   向回跳: public void seek(long pos) throws IOException。

跳字节读取代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        File file = new File("d:" + File.separator + "mldn.txt");
        RandomAccessFile raf = new RandomAccessFile(file, "rw");//读写模式
        {
            raf.skipBytes(24);//向下跳24个字节读取
            byte[] data = new byte[8];
            System.out.println("姓名:" + new String(data, 0, raf.read(data)).trim()
                    + "、年龄:" + raf.readInt());
        }
        {
            raf.seek(12);//回跳12字节
            byte[] data = new byte[8];
            System.out.println("姓名:" + new String(data, 0, raf.read(data)).trim()
                    + "、年龄:" + raf.readInt());
        }
        {
            raf.seek(0);//回跳到顶点
            byte[] data = new byte[8];
            System.out.println("姓名:" + new String(data, 0, raf.read(data)).trim()
                    + "、年龄:" + raf.readInt());
        }
    }
}

结果:

姓名:lisi、年龄:16
姓名:wangwu、年龄:20
姓名:zhangsan、年龄:30