16—JAVA(进阶)—文件操作

文件操作(IO)

  • 大多数的应用程序都需要与外部设备进行数据交换,最常见的外部设备包含磁盘和网络。 IO 就是指应用程序对这些设备的数据输入与输出
  • 在程序中,键盘被用作文件输入,显示器被用作文件输出。
  • JAVA 语言定义了许多类专门负责各种方式的输入输出,这些类都被放在 java.io 包中

01文件类(File)

1.1File类概述

  • File 类是 IO 包中唯一代表磁盘文件本身的对象,File 类定义了一些与平台无关的方法来操纵文件,通过调用 File 类提供的各种方法(具体可以查看JDK文档),能够完成创建、删除文件,重命名文件,判断文件的读写权限及是否存在,设置和查询文件的最近修改时间等操作。

File类的一个构造方法

  • public File(String pathname):通过给定路径名字符串转换为抽象路径名来创建一个新 File 实例。如果给定字符串是空字符串,那么结果是空抽象路径名。
  • pathName:路径名称

File类的常用方法

  • public boolean mkdir():创建指定的目录
  • public String getName():返回文件名称
  • public String getParent():返回父目录名
  • public boolean exists():判断文件是否存在

1.2File类的简单使用

File类的简单使用

import java.io.*;
public class Test {
    public static void main(String[] args)
    {
        //使用构造创建一个文件对象
        File f=new File("D:\\File\\Idea_File\\java_se\\02java_Object\\1.txt");
        //判断此文件对象是否存在
        if(f.exists())
            f.delete();
        else
            try {
            f.createNewFile();
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
        // getName()方法,取得文件名
        System.out.println("文件名: "+f.getName());
        // getPath()方法,取得文件路径
        System.out.println("文件路径: "+f.getPath());
        // getAbsolutePath()方法,得到绝对路径名
        System.out.println("绝对路径: "+f.getAbsolutePath());
        // getParent()方法,得到父文件夹名
        System.out.println("父文件夹名称: "+f.getParent());
        // exists(),判断文件是否存在
        System.out.println(f.exists()?"文件存在":"文件不存在");
        // canWrite(),判断文件是否可写
        System.out.println(f.canWrite()?"文件可写":"文件不可写");
        // canRead(),判断文件是否可读
        System.out.println(f.canRead()?"文件可读":"文件不可读");
       // isDirectory(),判断是否是目录
        System.out.println(f.isDirectory()?"是":"不是"+"目录");
        // isFile(),判断是否是文件
        System.out.println(f.isFile()?"是文件":"不是文件");
        // isAbsolute(),是否是绝对路径名称
        System.out.println(f.isAbsolute()?"是绝对路径":"不是绝对路径");
        // lastModified(),文件最后的修改时间
        System.out.println("文件最后修改时间: "+f.lastModified());
        // length(),文件的长度
        System.out.println("文件大小: "+f.length()+" Bytes");
    }
}
  • 输出结果
文件名: 1.txt
文件路径: D:\File\Idea_File\java_se\02java_Object\1.txt
绝对路径: D:\File\Idea_File\java_se\02java_Object\1.txt
父文件夹名称: D:\File\Idea_File\java_se\02java_Object
文件存在
文件可写
文件可读
不是目录
是文件
是绝对路径
文件最后修改时间: 1587954881797
文件大小: 0 Bytes
  • File类中还有很多方法具体可意查看JDK

02RandomAccessFile类

2.1RandomAccessFile类概述

  • RandomAccessFile 类可以说是 Java 语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法。

  • RandomAccessFile 类支持“随机访问”方式,可以跳转到文件的任意位置处读写数据。在要访问一个文件的时候,不想把文件从头读到尾,而是希望像访问一个数据库一样地访问一个文本文件,这时,使用 RandomAccessFile 类就是最佳选择。

  • RandomAccessFile 对象类有个位置指示器,指向当前读写处的位置,当读写 n 个字节后,文件指示器将指向这 n 个字节后的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。

  • RandomAccessFile 在数据等长记录格式文件的随机(相对顺序而言)读取时有很大的优势,但该类仅限于操作文件,不能访问其它的 IO 设备,如网络、内存映像等。

  • 有关 RandomAccessFile 类中的成员方法及使用说明请参阅 JDK 文档。

2.2RandomAccessFile类的简单使用

RandomAccessFile类的一个构造方法

  • public RandomAccessFile(File file, String mode) throws FileNotFoundException
  • 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定
  • mode 参数指定用以打开文件的访问模式
  • 例如:
new RandomAccessFile(f,"rw"); // 读写方式
new RandomAccessFile(f,"r"); // 只读方式

注意:

  • 当程序需要以读写的方式打开一个文件时,如果这个文件不存在,程序会自动创建此文件。

举个栗子

  • 下面是一个使用 RandomAccessFile 的例子,往文件中写入三名员工的信息,然后按照第二名员工,第一名员工,第三名员工的先后顺序读出。
  • 这里还需要设计一个类来封装员工信息。一个员工信息就是文件中的一条记录,而且必须保证每条记录在文件中的大小相同,也就是每个员工的姓名字段在文件中的长度是一样的,这样才能够准确定位每条记录在文件中的具体位置。
  • 假设 name 中有 8个字符,少于 8 个则补空格(这里用"\u0000"),多于 8 个则去掉后面多余的部分。由于年龄是整型数,不管这个数有多大,只要它不超过整型数的范围,在内存中都是占 4个字节大小,所以在代码中只需要对姓名进行限定就可以啦
import java.io.*;
public class Test {
    public static void main(String[] args) throws Exception {
        Employee e1=new Employee("AISMALL_01",18);
        Employee e2=new Employee("AISMALL_02",19);
        Employee e3=new Employee("AISMALL_03",20);

        //使用构造创建一个文件对象
        File f=new File("D:\\File\\Idea_File\\java_se\\02java_Object\\employee.txt");
        //创建一个RandomAccessFile对象,读写方式打开
        RandomAccessFile ra=new RandomAccessFile(f,"rw");
        ra.write(e1.name.getBytes());
        ra.writeInt(e1.age);
        ra.write(e2.name.getBytes());
        ra.writeInt(e2.age);
        ra.write(e3.name.getBytes());
        ra.writeInt(e3.age);
        ra.close();
        //创建一个RandomAccessFile对象,读方式打开
        RandomAccessFile ra1=new RandomAccessFile(f,"r");
        int len=8;
        ra1.skipBytes(12); // 跳过第一个员工的信息,其姓名 8 字节,年龄 4 字节
        System.out.println("第二个员工信息:");
        String str="";
        for(int i=0;i<len;i++)
            str=str+(char)ra1.readByte();
        System.out.println("name:"+str);
        System.out.println("age:"+ra1.readInt());
        System.out.println("第一个员工的信息:");
        ra1.seek(0); // 将文件指针移动到文件开始位置
        str="";
        for(int i=0;i<len;i++)
            str=str+(char)ra1.readByte();
        System.out.println("name:"+str);
        System.out.println("age:"+ra1.readInt());
        System.out.println("第三个员工的信息:");
        ra1.skipBytes(12); // 跳过第二个员工信息
        str="" ;
        for(int i=0;i<len;i++)
            str=str+(char)ra1.readByte();
        System.out.println("name:"+str.trim());
        System.out.println("age:"+ra1.readInt());
        ra1.close();
    }
}

//创建一个员工类用来封装员工的信息
class Employee{
    String name;
    int age;
    final static int lengh=8;
    Employee(String name,int age){
        //字符串长度固定为8位
        if(name.length()>lengh){
            name=name.substring(0,lengh);
        }else {
            //循环补空格,知道字符串长度等于8时跳出
            while(name.length()<lengh){
                name=name+"u\0000";
            }
        }
        this.name=name;
        this.age=age;
    }
}
  • 运行结果
第二个员工信息:
name:AISMALL_
age:19
第一个员工的信息:
name:AISMALL_
age:18
第三个员工的信息:
name:AISMALL_
age:20

03流类

3.1流类概述

  • Java 的流式输入/输出建立在四个抽象类的基础上:
    InputStream
    OutputStream
    Reader
    Writer
  • 它们用来创建具体流式子类。尽管程序通过具体子类执行输入/输出
    操作,但顶层的类定义了所有流类的基本通用功能。
  • InputStream 和 OutputStream 设计成字节流类
  • Reader 和 Writer 为字符流设计。
  • 字节流类和字符流类形成分离的层次结构。一般说来,处理字符或字符串时应使用字符流类,处理字节或二进制对象时应用字节流类。
  • 一般在操作文件流时,不管是字节流还是字符流都可以按照以下的方式进行:
    1、 使用 File 类找到一个文件
    2、 通过 File 类的对象去实例化字节流或字符流的子类
    3、 进行字节(字符)的读、写操作
    4、 关闭文件流

3.2IO包中的继承关系

3.2.1字节输入流(InputStream)

InputStream
FileInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream
ByteArrayInputStream
FilterInputStream
DataInputStream
PushbackInputStream
BufferedInputStream
LineNumberInputStream

3.2.2字节输出流(InputStream)

OuputStream
FileOutputStream
ObjectOutputStream
PipedOutputStream
ByteArrayOutputStream
FilterOuputStream
DataOutputStream
PrintStream
BufferedOuputStream

3.2.3字符输入流(Reader)

Reader
BufferedReader
LineNumberReader
CharArrayReader
StringReader
InputStreamReader
FileReader
PipedReader
FilterReader
PushbackReader

3.2.3字符输出流(Writer)

Writer
BufferedWriter
CharArrayWriter
StringWriter
OuputStreamWriter
FileWriter
PipedWriter
FilterWriter

3.3字节流

3.3.1InputStream(输入字节流)

  • InputStream 是一个定义了 Java 流式字节输入模式的抽象类。该类的所有方法在出错条件下都会引发一个 IOException 异常
  • 类中的方法:
方法 描述
int available( ) 返回当前可读的输入字节数
void close( ) 关闭输入流。关闭之后若再读取则会产生IOException异常
void mark(int numBytes) 在输入流的当前点放置一个标记。该流在读取N个Bytes字节前都保持有效
boolean markSupported( ) 如果调用的流支持mark( )/reset( )就返回true
int read( ) 如果下一个字节可读则返回一个整型,遇到文件尾时返回-1
int read(byte buffer[ ]) 试图读取buffer.length个字节到buffer中,并返回实际成功读取的字节数。遇到文件尾时返回-1
int read(byte buffer[ ], int offset,int numBytes) 试图读取buffer中从buffer[offset]开始的numBytes个字节,返回实际读取的字节数。遇到文件结尾时返回-1
void reset( ) 重新设置输入指针到先前设置的标志处
long skip(long numBytes) 忽略numBytes个输入字节,返回实际忽略的字节数

3.3.2OutputStream(输出字节流)

  • OutputStream是定义了流式字节输出模式的抽象类。该类的所有方法返回一个void值并且在出错情况下引发一个IOException异常
  • 类中的方法:
方法 描述
void close( ) 关闭输出流。关闭后的写操作会产生IOException异常
void flush( ) 定制输出状态以使每个缓冲器都被清除,也就是刷新输出缓冲区
void write(int b) 向输出流写入单个字节。注意参数是一个整型数,它允许设计者不必把参数转换成字节型就可以调用write()方法
void write(byte buffer[ ]) 向一个输出流写一个完整的字节数组
void write(byte buffer[ ], int offset,int numBytes) 写数组buffer以buffer[offset]为起点的numBytes个字节区域内的内容
  • 注意:
    上两个表中的多数方法由 InputStream 和 OutputStream 的子类来实现,但mark( )和 reset( )方法除外。
  • 在后面介绍几个InputStream和OutputStream常用的子类对象

3.3.3InputStream和OutputStream常用的子类对象

3.3.3.1FileInputStream(文件输入流)
  • FileInputStream 类创建一个能从文件读取字节的InputStream 类

它的两个常用的构造方法如下:

  • FileInputStream(String filepath)
  • FileInputStream(File fileObj)
    这两个构造方法都能引发FileNotFoundException异常。这里, filepath 是文件的绝对路径, fileObj是描述该文件的File对象。
  • 举个栗子
    FileInputStreams类构造方法的使用:
//传入文件的绝对路径
InputStream f0 = new FileInputStream("c:\\test.txt") ;
File f = new File("c:\\test.txt");
//传入File对象
InputStream f1 = new FileInputStream(f);
3.3.3.2FileOutputStream(文件输出流)
  • FileOutputStream 创建了一个可以向文件写入字节的OutputStream类

它常用的构造方法如下:

  • FileOutputStream(String filePath)
  • FileOutputStream(File fileObj)
  • FileOutputStream(String filePath, boolean append)
    它们可以引发 IOException 或 SecurityException 异常。
    filePath 是文件的绝对路径,
    fileObj 是描述该文件的 File 对象
    如果 append 为 true,文件则以设置搜索路径模 式 打 开 。
  • FileOutputStream 的 创 建 不 依 赖 于 文 件 是 否 存 在 。 在 创 建 对 象 时 ,FileOutputStream 会在打开输出文件之前就创建它。这种情况下如果试图打开一个只读文件,会引发一个 IOException 异常
  • 举个栗子:
    用 FileOutputStream 类 向 文 件 中 写 入 一 字 符 串 , 并 用FileInputStream 读出写入的内容
public class Test {
    public static void main(String[] args) throws Exception {
        //创建一个从读取键盘数据的流
        System.out.println("请输入一个字符串");
        Scanner sc=new Scanner(System.in);
        String str=sc.next();

        /*向文件中写数据*/
        //创建一个File对象
        File f=new File("D:\\File\\Idea_File\\java_se\\02java_Object\\1.txt");
        //创建一个输出流往文件中写数据
        FileOutputStream fo=new FileOutputStream(f);
        //由于此流只能向文件中写入字节,所以我们要把创建的数据转换为字节数组
        byte[] b=str.getBytes();
        fo.write(b);
        fo.close();

        /*从文件中读数据*/
        FileInputStream fi=new FileInputStream(f);
        // 开辟一个空间用于接收文件读进来的数据
        byte[] b1=new byte[1024];
        int i=0;
        // 将 b1 的引用传递到 read()方法之中,同时此方法返回读入数据的个数
        i=fi.read(b1);
        fi.close() ;
        //将 byte 数组转换为字符串输出
        System.out.println(new String(b1,0,i)) ;
    }
}

3.4字符流

  • 尽管字节流提供了处理任何类型输入/输出操作的足够的功能,但它们不能直接操作 Unicode 字符。

3.4.1Reader

  • Reader是定义Java的流式字符输入模式的抽象类。该类的所有方法在出错情况下都将引发IOException 异常
  • 常用方法介绍
方法 描述
abstract void close( ) 关闭输入源。进一步的读取将会产生IOException异常
void mark(int numChars) 在输入流的当前位置设立一个标志。该输入流在numChars个字符被读取之前有效
boolean markSupported( ) 该流支持mark( )/reset( )则返回true
int read( ) 如果调用的输入流的下一个字符可读则返回一个整型。遇到文件尾时返回-1
int read(char buffer[ ]) 试图读取buffer中的buffer.length个字符,返回实际成功读取的字符数。遇到文件尾返回-1
abstract int read(char buffer[ ],int offset,int numChars) 试图读取buffer中从buffer[offset]开始的numChars个字符,返回实际成功读取的字符数。遇到文件尾返回-1
boolean ready( ) 如果下一个输入请求不等待则返回true,否则返回false
long skip(long numChars) 跳过numChars个输入字符,返回跳过的字符设置输入指针到先前设立的标志处数

3.4.2Writer

  • Writer 是定义流式字符输出抽象类。所有该类的方法都返回一个void 值并在出错条件下引发IOException 异常
  • 常用方法介绍
方法 描述
abstract void close( ) 关闭输出流。关闭后的写操作会产生IOException异常
abstract void flush( ) 定制输出状态以使每个缓冲器都被清除。也就是刷新输出缓冲
void write(int ch) 向输出流写入单个字符。注意参数是一个整型,它允许设计者不必把参数转换成字符型就可以调用write()方法
void write(char buffer[ ]) 向一个输出流写一个完整的字符数组
abstract void write(char buffer[ ],int offset,int numChars) 向调用的输出流写入数组buffer 以buffer[offset] 为 起点的N个Chars区域内的内容
void write(String str) 向调用的输出流写str
void write(String str, int offset,int numChars) 写数组str中以制定的offset为起点的长度为numChars个字符区域内的内容

3.4.3Reader和Writer的子类对象

3.4.3.1FileReader
  • FileReader 类创建了一个可以读取文件内容的 Reader 类。

它的常用的构造方法如下:

  • FileReader(String filePath)
  • FileReader(File fileObj)
    每一个都能引发一个 FileNotFoundException 异常。
    filePath 是一个文件的完整路径
    fileObj 是描述该文件的 File 对象
3.4.3.2FileWriter
  • FileWriter 创建一个可以写文件的 Writer 类。

它的常用的构造方法如下:

  • FileWriter(String filePath)
  • FileWriter(String filePath, boolean append)
  • FileWriter(File fileObj)
    它们可以引发 IOException 或 SecurityException 异常
    filePath 是文件的绝对路径,
    fileObj 是描述该文件的 File 对象。如果 append 为 true,输出是附加到文件尾的。
    FileWriter 类的创建不依赖于文件存在与否。在创建文件之前,FileWriter 将在创建对象时打开它来作为输出。如果试图打开一个只读文件,将引发一个 IOException异常。
  • 举个栗子
public class Test {
    public static void main(String[] args) throws Exception {
        //创建一个从读取键盘数据的流
        System.out.println("请输入一个字符串");
        Scanner sc=new Scanner(System.in);
        String str=sc.next();

        /*向文件中写数据*/
        //创建一个File对象
        File f=new File("D:\\File\\Idea_File\\java_se\\02java_Object\\1.txt");
        //创建一个输出流往文件中写数据
       FileWriter fw=new FileWriter(f);
       fw.write(str);
       fw.close();

        /*从文件中读数据*/
      FileReader fr=new FileReader(f);
        // 开辟一个空间用于接收文件读进来的数据
        char c1[] =new char[1024];
        int i=0;
        // 将 c1 的引用传递到 read()方法之中,同时此方法返回读入数据的个数
        i=fr.read(c1);
        fr.close() ;
        //将 byte 数组转换为字符串输出
        System.out.println(new String(c1,0,i)) ;
    }
}
  • 注意:
    在向文件写入内容之后如果不关闭文件,这时再打开文件,可以发现文件中没有任何内容,因为FileWriter 类并不是直接继承自 Writer 类,而是继承了Writer 的子类(OutputStreamWriter)此类为字节流和字符流的转换类,也就说真正从文件中读取进来的数据还是字节,只是在内存中将字节转换成了字符。
    所以得出一个结论,字符流用到了缓冲区,而字节流没有用到缓冲区。
    另外也可以用 Writer 类中的 flush()方法,强制清空缓冲区。

3.5字节流和字符流的其他子类介绍(了解)

3.5.1 管道流

  • 管 道 流 主 要 作 用 是 可 以 连 接 两 个 线 程 间 的 通 信 。 管 道 流 也 分 为 字 节 流( PipedInputStream、 PipedOutputStream)与字符流( PipedReader、 PipedWriter)两种类型,
  • 我们主要说一下 PipedInputStream 与 PipedOutputStream。
  • 一个 PipedInputStream 对象必须和一个 PipedOutputStream 对象进行连接而产生一个通信管道,
  • PipedOutputStream 可以向管道中写入数据
  • PipedInputStream 可以从管道中读取数据
  • 这两个类主要用来完成线程 之 间 的 通 信 , 一 个 线 程 的 PipedInputStream 对 象 能 够 从 另 外 一 个 线 程 的PipedOutputStream 对象中读取数据
    在这里插入图片描述
  • 我们可以把管道比作硬盘,向管道中写数据,类似向硬盘中写数据(OutputStream),从管道中读数据,类似从硬盘中读数据(InputStream)
  • 举个栗子
public class Test {
    public static void main(String[] args) throws Exception {
        //创建线程对象
       Sender sender=new Sender();
       Receiver receiver=new Receiver();
       //创建管道流对象
       PipedInputStream pis=receiver.getPipedInputStream();
       PipedOutputStream pos=sender.getPipedOutputStream();
       //将管道对接
       pos.connect(pis);
       sender.start();
       receiver.start();
    }
}
//定义一个发送管道(向管道中写数据,类似向硬盘中写数据)线程
class Sender extends Thread{
    //类做成员变量,创建输入管道
    private PipedOutputStream pos=new PipedOutputStream();
    //定义获得成员变量的方法
    public PipedOutputStream getPipedOutputStream(){
        return pos;
    }
    //重写run方法

    @Override
    public void run() {
        System.out.println("请输入一个字符串:");
        //创建一个用来接收键盘录入信息的对象
        Scanner sc=new Scanner(System.in);
        String str=sc.nextLine();
        try {
            pos.write(str.getBytes());
            pos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//定义一个接收管道(从管道中读取数据,类似从硬盘中读取数据)线程
class Receiver extends Thread{
    private PipedInputStream pis=new PipedInputStream();
    public PipedInputStream getPipedInputStream(){
        return pis;
    }

    @Override
    public void run() {
        String str=null;
        byte buf[]=new byte[1024];
        try {
            int lengh=pis.read(buf);
            str=new String(buf,0,lengh);
            System.out.println("接收到的信息为:"+str);
            pis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.5.2ByteArrayInputStream和ByteArrayOutPutStream

  • ByteArrayInputStream 是输入流的一种实现,它有两个构造函数,每个构造函数都需要一个字节数组来作为其数据源
  • ByteArrayInputStream的构造函数:
    ByteArrayInputStream(byte[] buf)
    ByteArrayInputStream(byte[] buf,int offse , int length)
  • ByteArrayOutputStream的构造函数:
    ByteArrayOutputStream()
    BuyteArrayoutputStream(int)
  • 如果程序在运行过程中要产生一些临时文件,可以采用虚拟文件方式实现, JDK中提供了 ByteArrayInputStream 和 ByteArrayOutputStream 两个类可实现类似于内存虚拟文件的功能
public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println("请输入一个字符串:");
        Scanner sc=new Scanner(System.in);
        String temp=sc.nextLine();

        //把接收到的字符串转化为字节数组
        byte[] src=temp.getBytes();
        //创建流对象
        ByteArrayInputStream in=new ByteArrayInputStream(src);
        ByteArrayOutputStream out=new ByteArrayOutputStream();
        //调用本类中的方法,进行转换
        new Test().transform(in,out);
        //将输出的数据装换为字节数组
        byte[] result=out.toByteArray();
        //遍历这个字节数组
        for (byte b:result) {
            System.out.println(b);
        }
        //通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String
        System.out.println(new String(result));

    }
    //使用父类接收子类对象(多态),函数功能将字符串转换为大写形式
    public void transform(InputStream in,OutputStream out) throws IOException {
        int c=0;
        //将缓存中的数据读入(逐字节读入)
        while((c=in.read())!=-1){
            //将字符串装换为大写形式,并强转成整形(参考)
            int C=(int) Character.toUpperCase((char)c);
            //将读入的数据读出
            out.write(C);
        }
    }
}

3.5.3System.in和System.out

  • 为了支持标准输入输出设备, Java 定义了两个特殊的流对象: System.in 和System.out。
  • System.in 对应键盘,是 InputStream 类型的,程序使用 System.in 可以读取从键盘上输入的数据
  • System.out 对应显示器,是 PrintStream 类型的, PrintStream类是 OutputStream 的一个子类,程序使用 System.out 可以将数据输出到显示器上。
  • 键盘可以被当作一个特殊的输入流,显示器可以被当作一个特殊的输出流

3.5.4打印流

  • PrintStream 类提供了一系列的 print 和 println 方法,可以实现将基本数据类型的格式转化成字符串输出。在前面的程序中大量用到“ System.out.println”语句中的System.out 就是 PrintStream 类的一个实例对象。

PrintStream 有下面几个构造方法:

  • PrintStream(OutputStream out)

  • PrintStream(OutputStream out,boolean auotflush)
    PrintStream(OutputStream out,boolean auotflush, String encoding)
    autoflush 控制在 Java 中遇到换行符(\n)时是否自动清空缓冲区
    encoding 是指定编码方式

  • println 方法与 print 方法的区别是:前者会在打印完的内容后再多打印一个换行符(\n),所以 println()等于 print("\n")。

  • Java 的 PrintStream 对象具有多个重载的 print 和 println 方法,它们可输出各种类型(包括 Object)的数据。

  • 对于基本数据类型的数据, print 和 println 方法会先将它们转换成字符串的形式然后再输出,而不是输出原始的字节内容,

  • 如:整数 221 的打印结果是字符‘ 2’、‘ 2’、‘ 1’所组合成的一个字符串,而不是整数 221 在内存中的原始字节数据。

  • 对于一个非基本数据类型的对象, print 和 println 方法会先调用对象的toString 方法,然后再输出 toString 方法所返回的字符串。

  • IO 包中提供了一个与 PrintStream 对应的 PrintWriter 类,

PrintWriter 类的有下列几个构造方法:

  • PrintWriter(OutputStream)
  • PrintWriter(OutputStream, boolean)
  • PrintWriter(Writer)
  • PrintWriter(Writer, boolean)
  • PrintWriter 即使遇到换行符(\n)也不会自动清空缓冲区,只在设置了 autoflush 模式下使用了 println 方法后才自动清空缓冲区。
  • PrintWriter 相对 PrintStream 最有利的一个地方就是 println 方法的行为,在 Windows 的文本换行是"\r\n",而 Linux 下的文本换是"\n",如果希望程序能够生成平台相关的文本换行,而不是在各种平台下都用"\n"作为文本换行,那么就应该使用 PrintWriter 的 println 方法时, PrintWriter 的 println方法能根据不同的操作系统而生成相应的换行符。
public class Test {
    public static void main(String[] args) throws Exception {
        //PrintStresm的构造要传进来一个流对象
        //System.out是封装好的流对象
       PrintStream ps=new PrintStream(System.out);
       ps.println("AISMALL");
       ps.close();
    }
}

3.5.5DataInputStream和DataOutputStream

  • DataInputStream 与 DataOutputStream 提供了与平台无关的数据操作,通常会先通过 DataOutputStream 按照一定的格式输出,再通过 DataInputStream 按照一定格式读入。
  • 由于可以得到 java 的各种基本类型甚至字符串,这样对得到的数据便可以方便地进行处理,这在通过协议传输的信息的网络上是非常适用的
  • DataOutputStream的构造:
    public DataOutputStream(OutputStream out)
  • DataInputStream的构造:
    public DataInputStream(InputStream in)

举个栗子

  • 将一个用户的订单信息存储到一个txt文件中,然后读取出来
  • 用户的定单用如下格式储存为 order.txt 文件:
价格 数量 描述
18.99 18.99 T 恤杉
9.22 10 杯子
  • 代码实现:
public class Test {
    public static void main(String[] args) throws Exception {
        //创建一个文件输出流对象
       FileOutputStream fos=new FileOutputStream("D:\\File\\Idea_File\\java_se\\02java_Object\\order.txt");
       //将数据写入某一种载体
        DataOutputStream dos=new DataOutputStream(fos);
        // 价格
        double[] prices = { 18.99, 9.22, 14.22, 5.22, 4.21 };
        // 数目
        int[] units = { 10, 10, 20, 39, 40 };
        // 产品名称
        String[] descs = { "T 恤杉", "杯子", "洋娃娃", "大头针", "钥匙链" };
        // 向数据过滤流写入主要类型
        for (int i = 0; i < prices.length; i++)
        {
            // 写入价格,使用 tab 隔开数据
            dos.writeDouble(prices[i]);
            dos.writeChar('\t');
            // 写入数目
            dos.writeInt(units[i]);
            dos.writeChar('\t');
            // 写入产品名称,行尾加入换行符
            dos.writeChars(descs[i]);
            dos.writeChar('\n');
        }
        dos.close();
        fos.close();

        //计价程序读入并在标准输出中输出:
        // 将数据读出
        FileInputStream fis=new FileInputStream("D:\\File\\Idea_File\\java_se\\02java_Object\\order.txt");
        DataInputStream dis = new DataInputStream(fis);
        double price;
        int unit;
        StringBuffer desc;
        double total = 0.0;
        try
        {
            // 当文本被全部读出以后会抛出 EOFException 例外,中断循环
            while (true)
            {
                // 读出价格
                price = dis.readDouble();
                // 跳过 tab
                dis.readChar();
                // 读出数目
                unit = dis.readInt();
                // 跳过 tab
                dis.readChar();
                char chr;
                // 读出产品名称
                desc = new StringBuffer();
                while ((chr = dis.readChar()) != '\n') {
                    desc.append(chr);
                }
                System.out.println("定单信息: " + "产品名称: "+desc+",\t " + "数量: "+unit+", \t 价格: "+price);
                total = total + unit * price;
            }
        }
        catch (EOFException e)
        {
            System.out.println("\n 总共需要: " + total+"元");
        }
        dis.close();
        fis.close();
    }
}
  • 运行结果:
定单信息: 产品名称: T 恤杉,	 数量: 10, 	 价格: 18.99
定单信息: 产品名称: 杯子,	 数量: 10, 	 价格: 9.22
定单信息: 产品名称: 洋娃娃,	 数量: 20, 	 价格: 14.22
定单信息: 产品名称: 大头针,	 数量: 39, 	 价格: 5.22
定单信息: 产品名称: 钥匙链,	 数量: 40, 	 价格: 4.21
总共需要: 938.4799999999999

3.5.6 合并流

  • 采用 SequenceInputStream 类,可以实现两个文件的合并操作

SequenceInputStream的构造函数

  • public SequenceInputStream(InputStream s1, InputStream s2)
    按顺序读取这两个参数,先读取 s1,然后读取 s2
    s1 - 要读取的第一个输入流。
    s2 - 要读取的第二个输入流。

在这里插入图片描述

  • 分析
    我们需要创建三个文件对象,两个用在构造输入流,一个用来构造输出流,然后还需要创建一个序列流,用来存储合并的内容
  • 代码演示:
public class Test {
    public static void main(String[] args) throws Exception {
       //创建三个文件对象,两个用于输入流,一个用于输出流
        File inputFile1=new File("H:\\1.txt");
        File inputFile2=new File("H:\\2.txt");
        File outputFile=new File("H:\\3.txt");

        //声明两个输入流
        FileInputStream fis1=null,fis2=null;
        //声明一个输出流
        FileOutputStream fos=null;
        //声明一个序列流
        SequenceInputStream sis=null;

        //创建输入流
       fis1= new FileInputStream(inputFile1);
       fis2=new FileInputStream(inputFile2);
       //创建序列流
       sis=new SequenceInputStream(fis1,fis2);
       //创建输出流,将两个输入流合并
        fos=new FileOutputStream(outputFile);

        //下面开始将序列流中的数据,读到输出流
        int c;
        while((c=sis.read())!=-1){
            fos.write(c);
        }
        //关流
        fis1.close();
        fis2.close();
        fos.close();
        sis.close();
        System.out.println("运行结束。。。。");
    }
}
  • 运行结果
    在这里插入图片描述

04字符编码(了解)

  • 计算机里只有数字,在计算机软件里的一切都是用数字来表示,屏幕上显示的一个个字符也不例外。
  • 最初的计算机的使用是在美国,当时所用到的字符也就是现在键盘上的一些符号和少数几个特殊的符号,每一个字符都用一个数字来表示,一个字节所能表示的数字范围内足以容纳所有的这些字符,实际上表示这些字符的数字的字节最高位( bit)都为 0(即127个编码就够表示全部的字符),也就是说这些数字都在 0 到 127 之间,如字符 a 对应数字 97,字符 b 对应数字 98 等,这种字符与数字对应的编码固定下来后,这套编码规则被称为ASCII 码(美国标准信息交换码)。
  • 随着计算机在其它国家的逐渐应用和普及,许多国家都把本地的字符集引入了计算机,大大扩展了计算机中字符的范围。比如一个字节所能表示的数字范围是不能容纳所有的中文汉字的。中国大陆将每一个中文字符都用两个字节来表示,原有的ASCII 码字符的编码保持不变,仍用一个字节表示,为了将一个中文字符与两个 ASCII码字符相区别,中文字符的每个字节的最高位( bit)都为 1,中国大陆为每一个中文字符都指定了一个对应的数字,并作为标准的编码固定了下来,这套编码规则称为GBK(国标码),后来又在 GBK 的基础上对更多的中文字符(包括繁体)进行了编码,新的编码系统就是 GB2312,而 GBK 则是 GB2312 的子集。使用中文的国家和地区很多,同样的一个字符,如“中国”的“中”字,在中国大陆的编码是十六进制的 D6D0,而在中国台湾的编码是十六进制的 A4A4,台湾地区对中文字符集的编码规则称为BIG5(大五码)。
  • 可见,各个地区都使用各自不同的本地化字符编码,很不方便,严重制约了计算机使用和技术方面的交流。
  • 为了解决字符编码带来的不便,人们将全世界所有的符号进行了统一编码,称之为 Unicode 编码。所有字符不再区分国家和地区。
  • Unicode 编码的字符都占用两个字节的大小,也就是说全世界所有的字符个数不会超过 2 的 16 次方( 65536)
  • 目前人们看到的大多都是本地化字符编码与 Unicode 编码共存的景象。既然本地化字符编码与 Unicode 编码共存,那就少不了涉及两者之间的转化问题,而在Java 中的字符使用的都是 Unicode 编码, Java 技术在通过 Unicode 保证跨平台特性的前提下也支持了全扩展的本地平台字符集,而显示输出和键盘输入都是采用的本地编码。

拓展语句

  • 输出全部环境变量语句
    System.getProperties().list(System.out);
  • 修改JDK编码语句
    System.getProperties().put("file.encoding","GB2312");

猜你喜欢

转载自blog.csdn.net/weixin_45583303/article/details/105784588