Java IO
- Java.io包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
- Java.io包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
- 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
一、读取控制台输入
Java的控制台输入由System.in完成。为了获得一个绑定到控制台的字符流,可以把System.in包装在一个BufferedReader对象中来创建一个字符流。创建BufferedReader的基本语法:
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
BufferedReader对象创建后,便可以使用read()方法从控制台读取一个字符,或者用readLine()方法读取一个字符串。
二、从控制台读取多字符输入
1、从BufferedReader对象读取一个字符要使用read()方法,它的语法如下:
int read( ) throws IOException
每次调用read()方法,它从输入流读取一个字符并把该字符作为整数值返回。当流结束的时候返回-1。该方法抛出IOException。
2、实例:用read()方法从控制台不断读取字符直到用户输入q
//使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRread {
public static void main(String[] args) throws IOException {
char c;
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符, 按下 'q' 键退出。");
// 读取字符
do {
c = (char) br.read();
System.out.println(c);
} while (c != 'q');
}
}
输入字符, 按下 'q' 键退出。
Say the name!SEVENTEEN!
S
a
y
t
h
e
n
a
m
e
!
S
E
V
E
N
T
E
E
N
!
qq
q
三、从控制台读取字符串
从标准输入读取一个字符串需要使用BufferedReader的 readLine()方法。它的一般格式是:
String readLine( ) throws IOException
实例:读取和显示字符行直到输入了单词"end"
//使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRReadLines {
public static void main(String[] args) throws IOException {
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter lines of text.");
System.out.println("Enter 'end' to quit.");
do {
str = br.readLine();
System.out.println(str);
} while (!str.equals("end"));
}
}
Enter lines of text.
Enter 'end' to quit.
This is line one
This is line one
This is line two
This is line two
end
end
四、控制台输出
控制台的输出由print( )和println()完成。这些方法都由类 PrintStream定义,System.out是该类对象的一个引用。PrintStream继承了OutputStream类,并且实现了方法 write()。这样,write()也可以用来往控制台写操作。
void write(int byteval)
将byteval低八位字节写到流中
- 实例:用write()把字符"A"和紧跟着的换行符输出到屏幕
import java.io.*;
//演示 System.out.write().
public class WriteDemo {
public static void main(String[] args) {
int b;
b = 'A';
System.out.write(b);
System.out.write('\n');
}
}
A
五、读写文件
1、FileInputStream
用于从文件读取数据,它的对象可以用关键字new来创建。
- 可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream("C:/java/hello");
- 也可以使用一个文件对象来创建一个输入流对象来读取文件,首先得使用File()方法来创建一个文件对象:
File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);
创建了InputStream对象,就可以使用下面的方法来读取流或者进行其他的流操作。
序号 | 方法 | 描述 |
---|---|---|
1 | public void close() throws IOException{} | 关闭此文件输入流并释放与此流有关的所有系统资源,抛出IOException异常 |
2 | protected void finalize()throws IOException {} | 清除与该文件的连接,确保在不再引用文件输入流时调用其 close方法。抛出IOException异常 |
3 | public int read(int r)throws IOException{} | 从InputStream对象读取指定字节的数据,返回为整数值,返回下一字节数据,如果已经到结尾则返回-1 |
4 | public int read(byte[] r) throws IOException{} | 从输入流读取r.length长度的字节,返回读取的字节数,如果是文件结尾则返回-1 |
5 | public int available() throws IOException{} | 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数,返回一个整数值 |
2、FileOutputStream
该类用来创建一个文件并向文件中写数据。如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
- 使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream("C:/java/hello")
- 可以使用一个文件对象来创建一个输出流来写文件:
File f = new File("C:/java/hello");
OutputStream fOut = new FileOutputStream(f);
3、综合实例
import java.io.*;
public class fileStreamTest {
public static void main(String[] args) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上面的程序首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。以上代码由于是二进制写入,可能存在乱码,可以使用以下代码实例来解决乱码问题:
//文件名 :fileStreamTest2.java
import java.io.*;
public class fileStreamTest2 {
public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f);
// 构建FileOutputStream对象,文件不存在会自动新建
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
// 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
writer.append("中文输入");
// 写入到缓冲区
writer.append("\r\n");
// 换行
writer.append("English");
// 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
writer.close();
// 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
fop.close();
// 关闭输出流,释放系统资源
FileInputStream fip = new FileInputStream(f);
// 构建FileInputStream对象
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
// 构建InputStreamReader对象,编码与写入相同
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
// 转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close();
// 关闭读取流
fip.close();
// 关闭输入流,释放系统资源
}
}
六、Java中的目录
1、创建目录:File类中有两个方法可以用来创建文件夹
- mkdir()方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
- mkdirs()方法创建一个文件夹和它的所有父文件夹。
- 创建 "/tmp/user/java/bin"文件夹
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 现在创建目录
d.mkdirs();
}
}
2、读取目录
- 一个目录其实就是一个File对象,它包含其他文件和文件夹
- 如果创建一个File对象并且它是一个目录,那么调用 isDirectory()方法会返回true
- 可以通过调用该对象上的list()方法,来提取它包含的文件和文件夹的列表
- 使用list()方法来检查一个文件夹中包含的内容:
import java.io.File;
public class DirList {
public static void main(String args[]) {
String dirname = "/tmp";
File f1 = new File(dirname);
if (f1.isDirectory()) {
System.out.println("目录 " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " 是一个目录");
} else {
System.out.println(s[i] + " 是一个文件");
}
}
} else {
System.out.println(dirname + " 不是一个目录");
}
}
}
目录 /tmp
bin 是一个目录
lib 是一个目录
demo 是一个目录
test.txt 是一个文件
README 是一个文件
index.html 是一个文件
include 是一个目录
3、删除目录或文件
- 删除文件可以使用java.io.File.delete()方法
- 当删除某一目录时,必须保证该目录下没有其他文件才能正确删除,否则将删除失败。
异常处理
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据
- 要打开的文件不存在
- 网络通信时连接中断,或者JVM内存溢出
三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这些异常在编译时强制要求程序员处理。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。这类异常通常使用try-catch块来捕获并处理异常,或者在方法声明中使用throws子句声明方法可能抛出的异常。
try {
// 可能会抛出异常的代码
} catch (IOException e) {
// 处理异常的代码
}
或者:
public void readFile() throws IOException {
// 可能会抛出IOException的代码
}
- 运行时异常:这些异常在编译时不强制要求处理,通常是由程序中的错误引起的,例如NullPointerException、ArrayIndexOutOfBoundsException等,这类异常可以选择处理,但并非强制要求
try {
// 可能会抛出异常的代码
} catch (NullPointerException e) {
// 处理异常的代码
}
- 错误:错误不是异常,而是脱离程序员控制的问题,错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的
Java 提供了以下关键字和类来支持异常处理:
- try:用于包裹可能会抛出异常的代码块。
- catch:用于捕获异常并处理异常的代码块。
- finally:用于包含无论是否发生异常都需要执行的代码块。
- throw:用于手动抛出异常。
- throws:用于在方法声明中指定方法可能抛出的异常。
- Exception类:是所有异常类的父类,它提供了一些方法来获取异常信息,如getMessage()、printStackTrace()等。
一、Java内置异常类
1、Java.lang包中的部分非检查性异常
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程 |
2、Java.lang包中的部分检查性异常
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常 |
CloneNotSupportedException | 当调用Object类中的clone方法克隆对象,但该对象的类无法实现Cloneable接口时,抛出该异常 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常 |
InstantiationException | 当试图使用Class类中的 newInstance方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常 |
NoSuchFieldException | 请求的变量不存在 |
二、Throwable类的主要方法
方法 | 说明 |
---|---|
public String getMessage() | 返回关于发生的异常的详细信息,这个消息在Throwable类的构造函数中初始化了 |
public Throwable getCause() | 返回一个Throwable对象代表异常原因 |
public String toString() | 返回此Throwable的简短描述 |
public void printStackTrace() | 将此Throwable及其回溯打印到标准错误流 |
public StackTraceElement [] getStackTrace() | 返回一个包含堆栈层次的数组,下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底 |
public Throwable fillInStackTrace() | 用当前的调用栈层次填充Throwable对象栈层次,添加到栈层次任何先前信息中 |
三、捕获异常
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
try/catch代码块中的代码称为保护代码,Catch语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try后面的 catch块就会被检查。如果发生的异常包含在catch块中,异常会被传递到该catch块,这和传递一个参数的方法是一样。
// 文件名 : ExcepTest.java
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
四、多重捕获块
一个try代码块后面跟随多个catch代码块的情况就叫多重捕获
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}
可以在try语句后面添加任意数量的catch块。如果保护代码中发生异常,异常被抛给第一个catch块。如果抛出异常的数据类型与 ExceptionType1匹配,它在这里就会被捕获。如果不匹配,它会被传递给第二个catch块。如此,直到异常被捕获或者通过所有的 catch块。
五、throws/throw关键字
throw关键字用于在代码中抛出异常,而throws关键字用于在方法声明中指定可能会抛出的异常类型。
1、throw关键字
用于在当前方法中抛出一个异常。当代码执行到某个条件下无法继续正常执行时,可以使用throw关键字抛出异常,以告知调用者当前代码的执行状态。
- 在方法中判断num是否小于0,如果是,则抛出一个 IllegalArgumentException异常
public class CheckNum {
public static void checkNumber(int num) {
if (num < 0) {
throw new IllegalArgumentException("Number must be positive");
}
}
public static void main(String args[]){
CheckNum.checkNumber(-2);
}
}
Exception in thread "main" java.lang.IllegalArgumentException: Number must be positive
at com.example.helloworld.CheckNum.checkNumber(CheckNum.java:6)
at com.example.helloworld.CheckNum.main(CheckNum.java:10)
2、throws关键字
用于在方法声明中指定该方法可能抛出的异常。当方法内部抛出指定类型的异常时,该异常会被传递给调用该方法的代码,并在该代码中处理异常。
- 下面的代码中,当readFile方法内部发生IOException异常时,会将该异常传递给调用该方法的代码。在调用该方法的代码中,必须捕获或声明处理IOException异常。
public void readFile(String filePath) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String line = reader.readLine();
while (line != null) {
System.out.println(line);
line = reader.readLine();
}
reader.close();
}
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
六、finally关键字
无论是否发生异常,finally代码块中的代码总会被执行。在finally代码块中,可以运行清理类型等收尾善后性质的语句。finally代码块出现在catch代码块最后,语法如下:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
- catch不能独立于try存在
- 在try/catch后面添加finally块并非强制性要求的
- try代码后不能既没catch块也没finally块
- try, catch, finally块之间不能添加任何代码
七、try-with-resources
try-with-resources是一种异常处理机制,它能够自动关闭在 try块中声明的资源,无需显式地在finally块中关闭。在try-with-resources语句中,只需要在try关键字后面声明资源,然后跟随一个代码块。无论代码块中的操作是否成功,资源都会在try 代码块执行完毕后自动关闭。
try (resource declaration) {
// 使用的资源
} catch (ExceptionType e1) {
// 异常块
}
- 对比try-with-resources和finally来关闭资源
import java.io.*;
public class RunoobTest {
public static void main(String[] args) {
String line;
try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
while ((line = br.readLine()) != null) {
System.out.println("Line =>"+line);
}
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
}
}
}
IOException in try block =>test.txt (No such file or directory)
import java.io.*;
class RunoobTest {
public static void main(String[] args) {
BufferedReader br = null;
String line;
try {
System.out.println("Entering try block");
br = new BufferedReader(new FileReader("test.txt"));
while ((line = br.readLine()) != null) {
System.out.println("Line =>"+line);
}
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
} finally {
System.out.println("Entering finally block");
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
System.out.println("IOException in finally block =>"+e.getMessage());
}
}
}
}
Entering try block
IOException in try block =>test.txt (No such file or directory)
Entering finally block
- try-with-resources语句中可以声明多个资源,方法是使用分号";"分隔各个资源
八、声明自定义异常
- 所有异常都必须是Throwable的子类
- 如果希望写一个检查性异常类,则需要继承Exception类
- 如果想写一个运行时异常类,那么需要继承RuntimeException类
class MyException extends Exception{}
- 实例:银行账户模拟,通过银行卡的号码完成识别,进行存钱和取钱的操作
// 文件名InsufficientFundsException.java
import java.io.*;
//自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception
{
//此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}
// 文件名称 CheckingAccount.java
import java.io.*;
//此类模拟银行账户
public class CheckingAccount
{
//balance为余额,number为卡号
private double balance;
private int number;
public CheckingAccount(int number)
{
this.number = number;
}
//方法:存钱
public void deposit(double amount)
{
balance += amount;
}
//方法:取钱
public void withdraw(double amount) throws
InsufficientFundsException
{
if(amount <= balance)
{
balance -= amount;
}
else
{
double needs = amount - balance;
throw new InsufficientFundsException(needs);
}
}
//方法:返回余额
public double getBalance()
{
return balance;
}
//方法:返回卡号
public int getNumber()
{
return number;
}
}
//文件名称 BankDemo.java
public class BankDemo
{
public static void main(String [] args)
{
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try
{
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
}catch(InsufficientFundsException e)
{
System.out.println("Sorry, but you are short $"
+ e.getAmount());
e.printStackTrace();
}
}
}
Depositing $500...
Withdrawing $100...
Withdrawing $600...
Sorry, but you are short $200.0
com.example.helloworld.InsufficientFundsException
at com.example.helloworld.CheckingAccount.withdraw(CheckingAccount.java:32)
at com.example.helloworld.BankDemo.main(BankDemo.java:16)
九、通用异常
1、JVM(Java虚拟机)异常:由JVM抛出的异常或错误。例如NullPointerException类,ArrayIndexOutOfBoundsException类,ClassCastException类
2、程序级异常:由程序或者API程序抛出的异常。例如 IllegalArgumentException类,IllegalStateException类