Java IO操作

本文详细介绍了Java IO操作,包括文件操作类File、字节流(OutputStream和InputStream)、字符流(Writer和Reader)以及它们的区别。重点讨论了字节流和字符流在文件内容操作中的应用,如OutputStream的文件创建与追加,InputStream的文件读取,以及Writer和Reader在字符数据处理上的便利。此外,还提到了内存操作流、字符编码、打印流、System类的相关内容,以及JDK 1.5引入的Scanner类在输入数据方面的改进。

目录

1.1、文件操作类:File

1.2、字节流和字符流

1.2.1、字节输出流:OutputStream

1.2.2、字节输入流:InputStream

1.2.3、字符输出流:Writer

1.2.4、字符输入流:Reader

1.2.5、字节流和字符流的区别

1.3、内存操作流

1.4、字符编码

1.5、打印流

1.6、System类

1.7、缓冲区操作:BufferedReader

1.8、JDK 1.5的新支持:Scanner


Java SE的四大核心知识点:面向对象、类集框架、Java IO、JDBC。

Java IO的核心就一句话解释:如果抽象类或接口之中的抽象方法被子类所覆写了,那么实例化这个子类的时候,所调用的方法一定是被覆写过的方法

所有的IO操作都在java.io包之中进行定义,而且整个java.io包实际上就是五个类和一个接口:

· 五个类:File、InputStream、OutputStream、Reader、Wirter;

· 一个接口:Serializable。

1.1、文件操作类:File

                在整个java.io包之中,File类是唯一的一个与文件本身操作有关的类,所谓的文件本身指的是:文件的创建、删除、重命名、取得文件大小、修改日期。

                如果要想使用File类操作文件的话,那么肯定要通过构造方法实例化File类对象,而实例化File类对象的过程之中主要使用以下两种构造方法:

                · Java EE的开发之中:public File(String pathname)

                · Android开发之中:public File(File parent, String child)

范例:文件的基本操作,主要有两种功能:

                · 创建文件:public boolean createNewFile() throws IOException;

                · 删除文件:public boolean delete()

                · 判断路径是否存在:public boolean exists()

范例:使用File创建文件

路径分隔符 File.separator

import java.io.File;
import java.io.IOException;
public static void main(String[] args) throws IOException {
        File file = new File("U:" + File.separator + "Hello.txt"); // 文件的路径
        if (file.exists()) { // 文件存在
            file.delete(); // 删除文件
        } else { // 文件不存在
            file.createNewFile(); // 创建新文件
        }
    }
}

范例:使用File创建文件夹

import java.io.File;

public class FilleT3 {
    //使用Fille创建文件夹
    public static void main(String[] args) {
        File f = new File("U:"+File.separator+"www");
        f.mkdir();//创建文件
    }
}

范例:列出目录内容

import java.io.File;
public class FilleT4 {
    public static void main(String[] args) {
        File f = new File("U"+File.separator);
        String str[] = f.list();//列出给定目录的内容
        for (int i = 0;i< str.length;i++){
            System.out.println(str[i]);
        }
    }
}

1.2、字节流和字符流

        使用File类执行的所有操作都是针对于文件本身,但是却没有针对于文件的内容,而要进行文件内容操作就需要通过Java之中提供的两组类完成:

                · 字节操作流(是在JDK 1.0的时候定义的):OutputStream、InputStream;

                · 字符操作流(是在JDK 1.1的时候定义的):Writer、Reader。

        但是不管是字节流还是字符流的操作,本身都表示资源操作,而执行所有的资源操作都会按照如下的几个步骤进行,下面以文件操作为例(对文件进行读、写操作):

        · 如果要操作的是文件,那么首先要通过File类对象找到一个要操作的文件路径(路径有             可能存在,有可能不存在,如果不存在,则要创建路径);

        · 通过字节流或字符流的子类为字节流或字符流的对象实例化(向上转型);

         执行读 / 写操作;

              · 最后一定要关闭操作的资源(close()),不管日后如何操作,资源永远要关闭。

1.2.1、字节输出流:OutputStream

        java.io.OutputStream主要的功能是进行字节数据的输出的,而这个类的定义如下:

public abstract class OutputStream

extends Object

implements Closeable, Flushable

        发现OutputStream类定义的时候实现了两个接口:Closeable、Flushable,那么这两个接口的定义如下:

CloseableJDK 1.5推出

FlushableJDK 1.5推出

public interface Closeable extends AutoCloseable {

public void close() throws IOException;

}

public interface Flushable {

public void flush() throws IOException;

}

提示:对于Closeable继承的AutoCloseable接口

        AutoCloseable是在JDK 1.7的时候又增加了一个新的接口,但是这个接口的定义和Closeable定义是完全一样的,我个人认为:有可能在一些其他的类上出现了自动的关闭功能,Closeable是手工关闭,AutoCloseable属于自动关闭。

        但是对于Closeable和Flushable这两个接口实话而言用户不需要关注,因为从最早的习惯对于flush()和close()两个方法都是直接在OutputStream类之中定义的,所以很少去关心这些父接口问题。

        对于OutputStream类而言发现其本身定义的是一个抽象类(abstract class),按照抽象类的使用原则来讲,需要定义抽象类的子类,而现在如果要执行的是文件操作,则可以使用FileOutputStream子类完成,如果按照面向对象的开发原则,子类要为抽象类进行对象的实例化,而后调用的方法以父类中定义的方法为主,而具体的实现找实例化这个父类的子类完成,也就是说在整个的操作之中,用户最关心的只有子类的构造方法:

        · 实例化FileOutputStream(新建数据):

        public FileOutputStream(File file) throws FileNotFoundException;

        · 实例化FileOutputStream(追加数据)

        :public FileOutputStream(File file, boolean append) throws FileNotFoundException

        当取得了OutputStream类的实例化对象之后,下面肯定要进行输出操作,在OutputStream类之中定义了三个方法:

        · 输出单个字节数据:public abstract void write(int b) throws IOException;

        · 输出一组字节数据:public void write(byte[] b) throws IOException;

        · 输出部分字节数据:public void write(byte[] b, int off, int len) throws IOException;

范例:使用OutputStream向文件之中输出数据,输出路径:u:\diyi\test.txt

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("u:" + File.separator + "diyi"
				+ File.separator + "test.txt"); // 第1步:定义文件路径
		if (!file.getParentFile().exists()) { // 父路径不存在
			file.getParentFile().mkdirs(); // 创建父路径
		}
		OutputStream output = new FileOutputStream(file); // 第2步:通过子类实例化父类
		String data = "Hello World .";// 要输出的数据
		output.write(data.getBytes()); // 第3步:输出数据,要将数据变为字节数组输出
		output.close(); // 第4步:关闭资源
	}
}

        在整个的文件输出过程之中可以发现,如果现在要输出的文件不存在,那么会出现自动创建文件的情况,并且如果重复执行以上的代码,会出现新的内容覆盖掉旧内容的操作,所以下面可以使用FileOutputStream类的另外一个构造方法进行数据的追加:

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 第1步:定义文件路径
		if (!file.getParentFile().exists()) { // 父路径不存在
			file.getParentFile().mkdirs(); // 创建父路径
		}
		OutputStream output = new FileOutputStream(file, true);// 第2步:通过子类实例化父类
		String data = "Hello World .\r\n";// 要输出的数据
		output.write(data.getBytes()); // 第3步:输出数据,要将数据变为字节数组输出
		output.close(); // 第4步:关闭资源
	}
}

在OutputStream类之中所有的数据都是以字节数据为主的。

1.2.2、字节输入流:InputStream

如果现在要从指定的数据源之中读取数据,使用InputStream,而这个类的定义如下:

public abstract class InputStream

extends Object

implements Closeable

在InputStream类之中,定义了三个读取数据的操作方法:

        · 读取单个字节:public abstract int read() throws IOException;

        |- 说明:每次执行read()方法都会读取一个数据源的指定数据,如果现在发现已经读取到了结尾返回-1;

· 读取多个字节:public int read(byte[] b) throws IOException;

|- 说明:如果现在要读取的数据小于byte的数据,这个时候read()方法的返回值int返回的是数据个数,如果现在开辟的字节数组小于读取的长度,如果数据已经读完了,则这个时候的int返回的是-1;

· 读取指定多个字节:public int read(byte[] b, int off, int len) throws IOException。

        既然InputStream为抽象类,那么这个抽象类要使用就必须有子类,现在是通过文件读取内容,肯定使用FileInputStream子类进行操作,与OutputStream类的使用一样,对于FileInputStream也只关心构造方法:

· FileInputStream类构造方法:public FileInputStream(File file) throws FileNotFoundException;

范例:一次性全部读取数据进来

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 定义文件路径
		if (file.exists()) {	// 文件存在则可以读取
			InputStream input = new FileInputStream(file) ;
			byte data[] = new byte[1024]; // 假设要读的长度是1024
			int len = input.read(data) ;	// 读取数据,返回读取个数
			input.close() ;	// 关闭
			System.out.println("读取的数据是:【" + new String(data, 0, len) + "】");
		}
	}
}

单个字节读取

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 定义文件路径
		if (file.exists()) { // 文件存在则可以读取
			InputStream input = new FileInputStream(file);
			byte data[] = new byte[1024]; // 假设要读的长度是1024
			int foot = 0; // 操作data数组的脚标
			int temp = 0;
			do {
				temp = input.read(); // 读取了一个字节
				if (temp != -1) {
					data[foot++] = (byte) temp; // 保存读取进来的单个字节
				}
			} while (temp != -1);// 没有读取完,还有内容
			input.close(); // 关闭
			System.out.println("读取的数据是:【" + new String(data, 0, foot) + "】");
		}
	}
}

        很明显,对于不确定个数只知道结束条件的循环,只能采用while或者是do..while,而在讲解这两种循环的时候也强调过:几乎不会去使用do…while,主要使用while循环,那么以上的代码也可以修改为while循环。

范例:使用while循环修改之前的程序

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 定义文件路径
		if (file.exists()) { // 文件存在则可以读取
			InputStream input = new FileInputStream(file);
			byte data[] = new byte[1024]; // 假设要读的长度是1024
			int foot = 0; // 操作data数组的脚标
			int temp = 0;
			// 第一步:temp = input.read(),读取一个单个字节,并且将内容给temp变量
			// 第二步:temp != -1,将接收到的temp的数值判断是否为-1,如果为-1则表示退出循环,如果不是,则保存数据
			while ((temp = input.read()) != -1) {
				data[foot++] = (byte) temp; // 保存读取进来的单个字节
			}
			input.close(); // 关闭
			System.out.println("读取的数据是:【" + new String(data, 0, foot) + "】");
		}
	}
}

在日后的所有开发之中,都会使用以上的while循环方式进行数据的读取。

1.2.3、字符输出流:Writer

Writer类也是一个专门用于数据输出的操作类,这个类定义:

public abstract class Writer

extends Object

implements Appendable, Closeable, Flushable

在Wirter类之中定义的write()方法都是以字符数据为主,但是在这些方法之中,只关心一个:

· 输出一个字符串:public void write(String str) throws IOException。

在Wirter类之中比OutputStream类最为方便的一点就是其可以直接使用String型数据输出,并且不再需要将其变为字节数组了。而Writer类本身也是一个抽象类,那么如果要使用依然要依靠它的子类,尤其是现在操作的是文件,使用FileWriter子类。

范例:使用Wirter类进行内容的输出

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 定义文件路径
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();// 创建父目录
		}
		Writer out = new FileWriter(file);
		String data = "Hello World .";
		out.write(data) ;	// 直接输出字符串
		out.close() ;	// 关闭输出
	}
}

从输出来讲,Wirter类的输出要比OutputStream类更加的方便。

1.2.4、字符输入流:Reader

Reader是进行字符数据读取的操作类,其定义:

public abstract class Reader

extends Object

implements Readable, Closeable

        在Writer类之中存在了直接输出一个字符串数据的方法,可是在Reader类之中并没有定义这样的方法,只是定义了三个按照字符读取的方法,为什么会这样?

        因为在使用OutputStream输出数据的时候,其程序可以输出的大小一定是程序可以承受的数据大小,那么如果说使用InputStream读取的时候,可能被读取的数据非常的大,那么如果一次性全读进来了,就会出现问题,所以只能一个一个的进行读取。

Reader依然是抽象类,那么如果从文件读取,依然使用FileReader类。

import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 定义文件路径
		if (file.exists()) {
			Reader in = new FileReader(file); // 字符输入流
			char data[] = new char[1024]; // 开辟数组
			int len = in.read(data); // 读取数据
			System.out.println("读取数据内容:【" + new String(data, 0, len) + "】");
			in.close() ;
		}
	}}

        字符比字节的好处就是在于字符串数据的支持上,而这个好处还只是在Writer类中体现,所以与字节流相比,字符流的操作并不是对等的关系。

1.2.5、字节流和字符流的区别

两者的区别:

        字节流在进行IO操作的时候,直接针对的是操作的数据终端(例如:文件),而字符流操作的时候不是直接针对于终端,而是针对于缓存区(理解为内存)操作,而后由缓存取操作终端(例如:文件),属于间接操作,按照这样的方式,如果说在使用字节流的时候不关闭最后的输出流操作,也可以将所有的内容进行输出,而字符输出流的时候如果不关闭,则意味着缓冲区之中的内容不会被输出,当然,这个时候可以由用户自己去调用flush()方法进行强制性的手工清空:

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:" + File.separator + "hellodemo"
				+ File.separator + "test.txt"); // 定义文件路径
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();// 创建父目录
		}
		Writer out = new FileWriter(file);
		String data = "Hello World .";
		out.write(data) ;	// 直接输出字符串
		out.flush() ;	// 清空缓冲区
	}
}

主要的区别:

        · 字节流没有使用到缓冲区,而字符流使用了;

        · 处理各种数据都可以通过字节流完成,而在处理中文的时候使用字符流会更好。

1.3、内存操作流

· 字节内存操作流:内存输入流(ByteArrayInputStream)、内存输出流(ByteArrayOutputStream);

· 字符内存操作流:内存输入流(CharArrayReader)、内存输出流(CharArrayWriter)。

两个类的继承结构以及构造方法的定义:

ByteArrayInputStream

ByteArrayOutputStream

java.lang.Object

|- java.io.InputStream

|- java.io.ByteArrayInputStream

java.lang.Object

|- java.io.OutputStream

|- java.io.ByteArrayOutputStream

public ByteArrayInputStream(byte[] buf)

public ByteArrayOutputStream()

范例:使用内存操作流完成一个字符串大小写字母的转换操作

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		String str = "hello world ."; // 有非字母组成
		InputStream input = new ByteArrayInputStream(str.getBytes()); // 将数据输出到内存之中
		OutputStream output = new ByteArrayOutputStream(); // 准备从内存之中读取数据
		int temp = 0;
		while ((temp = input.read()) != -1) {
			output.write((char) Character.toUpperCase(temp)); // 所有内容都在字节输出流中
		}
		String newStr = output.toString(); // 取数据
		output.close();
		input.close();
		System.out.println(newStr);
	}
}

本程序之中发生了IO操作,但是并没有临时文件的产生,一切的操作都是以内存为主。

通过本程序也应该可以发现一个很熟悉的身影,观察一下继承结构。

同样都是输出(write())或者是输入(read())方法,但是根据实例化父类的子类对象不同,输入或输出的位置也不同,此处就很好的体现了面向对象的多态性。

1.4、字符编码

常见的编码

        · GBK / GBK2312:表示国标中文编码,其中GBK是包含简体中文和繁体中文,而GB2312只有简体;

        · ISO 8859-1:是一种国际通用编码,可以表示任何文字,但是对于窗格文字需要进行一些转码;

        · UNICODE:使用了十六进制完成的编码,可以准确的表示出任何的语言文字;

        · UTF-8编码:部分编码使用UNICODE,而一些编码继续使用像ISO 8859-1类型的编码,适合于网络传输,在以后的所有的项目开发之中,都必须采用此编码。可是考虑到日后学习的方便,几乎都会使用命令行进行操作,所以命令行只支持GBK编码,UTF不支持,一旦程序设置了UTF编码,那么肯定通过命令行查看的就是乱码。

范例:查看当前环境下的所有属性

public class TestDemo {
	public static void main(String[] args) throws Exception {
		System.getProperties().list(System.out);
	}
}

        这个时候显示出来的信息是很多的,这里面有专门的编码选项“file.encoding=GBK”也就是说如果没有任何的意外,所有的文字编码都是GBK。

1.5打印流

        如果说现在要想输出数据,肯定使用OutputStream或者是Writer,那么请问,这两个操作类在执行输出的时候你认为它好用吗?

        如果现在要想输出字符串,使用Writer可以直接输出,而使用OutputStream还需要将字符串变为字节数组,那么如果现在要想输出数字(int型或double型),还需要将这些数据先变为字符串,之后再变为字节数组输出,所以,如果用户直接调用OutputStream或Writer输出的时候本身并不方便,所以在这个时候可以想办法将OutputStream或Wirter变得加强一些,定义一个专门的工具类:PrintUtil.java。

范例:编写一个输出功能类

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
class PrintUtil {
	private OutputStream output = null ;
	public PrintUtil(OutputStream output) {	// 通过构造方法设置输出的位置
		this.output = output ;
	}
	public void print(String str) {
		try {
			this.output.write(str.getBytes()) ;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void println(String str) {
		this.print(str.concat("\r\n")) ;
	}
	public void print(int num){
		this.print(String.valueOf(num)) ;
	}
	public void println(int num) {
		this.println(String.valueOf(num)) ;
	}
	public void print(double num){
		this.print(String.valueOf(num)) ;
	}
	public void println(double num) {
		this.println(String.valueOf(num)) ;
	}
	public void close() {
		try {
			this.output.close() ;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
public class TestDemo {
	public static void main(String[] args) throws Exception {
		PrintUtil tools = new PrintUtil(new FileOutputStream(new File("D:"
				+ File.separator + "test.txt")));
		tools.print("姓名:");
		tools.println("张三。");
		tools.print(1);
		tools.print(" + ");
		tools.print(1);
		tools.print(" = ");
		tools.println(1 + 1);
		tools.close();
	}
}

        如果此时不看PrintUtil类,可以清楚的发现,客户端的输出操作要比直接使用OutputStream输出更加简单,那么既然我们都可以想到的问题,Java也早就想到了,为此它专门提供了两个类:字节打印流类(PrintStream)、字符打印流类(PrintWriter),现在还是以字节打印流为主,而字符打印流主要是可以方便的输出中文。

范例:直接使用打印流

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		PrintStream tools = new PrintStream(new FileOutputStream(new File("D:"
				+ File.separator + "test.txt")));
		tools.print("姓名:");
		tools.println("张三。");
		tools.print(1);
		tools.print(" + ");
		tools.print(1);
		tools.print(" = ");
		tools.println(1 + 1);
		tools.close();
	}
}

范例:在内存流上使用打印流

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		String str = "hello world ."; // 有非字母组成
		InputStream input = new ByteArrayInputStream(str.getBytes()); // 将数据输出到内存之中
		OutputStream output = new ByteArrayOutputStream(); // 准备从内存之中读取数据
		PrintStream out = new PrintStream(output) ;	// 使用打印流间接调用了内存输出流
		int temp = 0;
		while ((temp = input.read()) != -1) {
			out.print((char) Character.toUpperCase(temp)); // 所有内容都在字节输出流中
		} 
		String newStr = output.toString(); // 取数据
		output.close();
		input.close();
		System.out.println(newStr);
	}
}

        而通过本程序也可以发现,如果要想决定输出的位置完全由子类决定,打印流照单全收。

【了解】但是在JDK 1.5之后,打印流也进行了更新,增加了一新的方法,格式化输出:

· 格式化输出:public PrintStream printf(String format, Object... args)

        当看到此方法名称的时候应该首先想到的是C语言中的输出,而现在Java也具备了同样的功能,而输出的时候可以使用一些标记来表示要输出的内容,例如:字符串(%s)、整数(%d)、小数(%m.nf)、字符(%c)等,之所以有这样的输出,其实目的还是在于抢夺C语言的开发人员市场。

范例:格式化字符串

public class TestDemo {
	public static void main(String[] args) throws Exception {
		String name = "张三";
		int age = 20;
		double score = 89.9876321; // 成绩,小数点太多
		String str = String.format("姓名:%s,年龄:%d,成绩:% 5.2f ", name, age, score);
		System.out.println(str);
	}
}

以后只要是程序输出数据的操作,都使用PrintStream类。

1.6System

· 错误输出:public static final PrintStream err;

· 系统输出:public static final PrintStream out;

· 系统输入:public static final InputStream in;

范例:

1、观察错误输出:(几乎没人使用)

public class TestDemo {
	public static void main(String[] args) throws Exception {
		try {
			Integer.parseInt("abc");
		} catch (Exception e) {
			System.err.println(e);
			System.out.println(e);
		}
	}
}

        从Java本身的规定是这样解释的:System.err输出的是不希望用户看见的错误,而System.out输出的是希望用户看见的错误,两者没什么区别,都用不上,知道就行了。

2 系统输出

        系统输出是将所有信息输出到指定的输出设备上 —— 显示器。而System.out本身是属于PrintStream对象,而PrintStream是OutputStream子类,所以现在实际上可以利用System.out为OutputStream类执行实例化操作。

import java.io.OutputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		OutputStream output = System.out ;	// 具备系统输出
		output.write("Hello World .".getBytes()) ;
	}
}

        本程序没有任何的意义,而讲解主要目的就希望可以理解:OutputStream会根据实例化它的子类或对象不同,输出的位置也不同。

3 系统输入

        系统输入针对于标准的输入设备 —— 键盘,也就是俗称的键盘输入数据,但是System.in返回的是InputStream型的数据,所以下面编写一个操作由键盘输入数据。

import java.io.InputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		InputStream input = System.in;
		byte data[] = new byte[1024];
		System.out.print("请输入数据:");
		int len = input.read(data);  等待用户输入,程序进入到阻塞状态
		System.out.println("输入的内容是:" + new String(data, 0, len));
	}
}

        除了实例化InputStream类的对象不同之外,其他的地方和之前文件输入数据没有任何的区别,但是这个程序本身有问题,已经开辟的空间大小是1024,如果输入的数据超过1024呢?发现只会接收满足于指定长度的数据,程序有bug,那么最好的解决方法是不设置长度,输入一个读取一个,一直到用户不输入为止。

import java.io.InputStream;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		InputStream input = System.in;
		StringBuffer buf = new StringBuffer();
		System.out.print("请输入数据:");
		int temp = 0;
		while ((temp = input.read()) != -1) { // 用户可以一直输入下去
			if (temp == '\n') { // 输入结束
				break; // 退出循环
			}
			buf.append((char) temp);
		}
		System.out.println("输入的内容是:" + buf);
	}
}

        这个时候看似长度问题解决了,并且可以由用户随意输入任意长度的数据,但是新的问题又来了,此时的程序是按照一个一个字节的方式取的内容,所以如果输入的是中文,那么一个中文被读了两次,所以一定会出现乱码。

1.7、缓冲区操作:BufferedReader

        如果说现在把所有的输入数据都放在一起了,一次性读取出来了,那么这个时候肯定就能够避免中文问题了,而这一操作就必须依靠缓冲区操作流完成,对于缓冲区的读取在IO包中定义了两种类:BufferedInputStream、BufferedReader,但是考虑到本次操作有中文的问题,肯定使用BufferedReader类完成操作,下面就需要来观察一下BufferedReader类的继承结构、构造方法、主要操作方法。

继承结构:

java.lang.Object

|- java.io.Reader

|- java.io.BufferedReader

构造方法:

public BufferedReader(Reader in)

读取操作:

public String readLine() throws IOException

        实际上之所以不使用BufferedInputStream而使用BufferedReader类的另外一个原因在于BufferedReader类有一个方法可以读取一行数据,而这个方法BufferedInputStream类没有。

        在BufferedReader类之中的readLine()方法返回的是String数据,而一旦返回的数据类型是String就非常方便:

        · String类之中有大量的方法供用户操作;

        · String类可以直接使用正则验证其格式;

        · String类可以向基本数据类型或日期型进行转型操作。

范例:使用BufferedReader进行数据读取

import java.io.BufferedReader;
import java.io.InputStreamReader;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		BufferedReader buf = new BufferedReader(
				new InputStreamReader(System.in));
		System.out.print("请输入数据:");
		String str = buf.readLine() ;
		System.out.println("输入的内容是:" + str);
	}
}

此时输入的数据不再存在长度的限制了,并且可以方便的进行中文数据的输入。

范例:对输入的数据进行验证,现在要求一个用户由键盘输入一个数字,而后进行数字的乘法错误,如果用户输入的不是数字,则需要提醒用户重新输入

import java.io.BufferedReader;
import java.io.InputStreamReader;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		BufferedReader buf = new BufferedReader(
				new InputStreamReader(System.in));
		boolean flag = true;
		int num = 0;
		while (flag) { // 处于循环操作
			System.out.print("请输入数据:");
			String str = buf.readLine();
			if (str.matches("\\d+")) { // 是数字
				num = Integer.parseInt(str);
				flag = false; // 退出循环
			} else {
				System.out.print("您输入的不是数字!");
			}
		}
		System.out.println("输入的内容是:" + num * num);
	}
}

如果要想使用BufferedReader多次输入数据的话,重复调用readLine()方法即可。

1.8JDK 1.5的新支持:Scanner

        在JDK 1.5之后为了方便用户进行输入数据的操作,专门提供了一个java.util.Scanner类,这个类是作为了一个工具类出现的,在Scanner类之中定义了如下的一些主要操作方法:

        · 构造方法:public Scanner(InputStream source)

        · 判断是否有数据:public boolean hasNextXxx()

        · 取得数据:public 数据类型 nextXxx()

        · 修改分隔符:public Scanner useDelimiter(String pattern)

        以后调用的时候在执行nextXxx()之前一定要首先使用hasNextXxx()判断是否有指定格式的数据出现。

范例:通过Scanner进行数据的输入

import java.util.Scanner;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		Scanner scan = new Scanner(System.in);
		scan.useDelimiter("\n"); // 只将换行作为分隔符
		System.out.print("请输入数据:");
		if (scan.hasNext()) { // 有内容
			String str = scan.next();
			System.out.println("输入数据:" + str);
		}
	}
}

范例:使用Scanner判断输入数据是否是int

import java.util.Scanner;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		Scanner scan = new Scanner(System.in);
		System.out.print("请输入数据:");
		if (scan.hasNextDouble()) { // 有内容
			double data = scan.nextDouble() ;
			System.out.println("输入数据:" + data);
		} else {
			System.out.println("输入的数据不是数字!");
		}
	}
}

在Scanner类之中,useDelimiter()方法的输入针对于字符串,但是其他的数据类型并不方便使用。

范例:利用正则验证

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		Scanner scan = new Scanner(System.in);
		System.out.print("请输入您的生日:");
		if (scan.hasNext("\\d{4}-\\d{2}-\\d{2}")) { // 有内容
			String str = scan.next("\\d{4}-\\d{2}-\\d{2}") ;
			Date date = new SimpleDateFormat("yyyy-MM-dd").parse(str) ;
			System.out.println(date);
		} else {
			System.out.println("输入的数据不是数字!");
		}
	}
}

        如果说现在由程序向文件输出内容使用PrintStream可以方便完成,但是反过来,如果说现在在程序之中读取文件内容,如果使用InputStream类读取并不方便,而使用Scanner就可以方便完成了。

范例:使用Scanner读取文件

import java.io.File;
import java.io.FileInputStream;
import java.util.Scanner;
public class TestDemo {
	public static void main(String[] args) throws Exception {
		Scanner scan = new Scanner(new FileInputStream(new File("D:"
				+ File.separator + "test.txt")));
		scan.useDelimiter("\n") ;
		while (scan.hasNext()) {
			System.out.println(scan.next());
		}
		scan.close() ;
	}
}

        这个时候的操作发现明显很简单,所以这样就得出一个结论:以后程序输出数据使用打印流,程序输入数据使用Scanner

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值