异常简介

Java中的异常又称为例外,是一个程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类,这可以让程序具有记号的容错性且更加健壮

在Java中一个异常的产生,主要有如下三种原因:

1、Java内部错误发生异常,Java虚拟机产生的异常

2、编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等

3、通过throw语句手动生成的异常,一般用来告知该方法的调用者一些必要信息

我们把生成异常对象,并把它提交给运行系统的过程称为抛出(throw)异常。运行时系统在方法的调用堆栈中查找,直到找到能够处理该类型异常的对象,这一个过程为捕获(catch)异常。

异常分类

为了能够及时有效地处理程序中的运行错误,Java专门引入了异常类。在Java中所有异常类型都是内置类java.lang.Throwable类的子类,即Throwable位于异常类层次结构的顶层。Throwable类下有连个异常分支Exception和Error

Throwable类是所有异常和错误的超类,下面有Error和Exception两个子类分别表示错误和异常。其中异常类Exception又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)

import java.io.IOException;

public class Main
{
public static void test()
{
int m=Integer.parseInt("334sdfsd");
System.out.println("m:"+m);
}
public static void test2() throws IOException//throws IOException: 这表明方法可能会抛出IOException异常。
// 在Java中,如果一个方法可能会抛出检查异常(即必须在方法签名中声明的异常),那么必须使用throws关键字来声明这些异常。
// 在这个例子中,test2()方法声明可能会抛出IOException异常。

{
throw new IOException("IO出问题了");
}
public static void main(String[] args)
{

try
{
test();
test2();

}
// catch(NumberFormatException numberFormatException)
// {
// numberFormatException.printStackTrace();
// System.out.println(numberFormatException.getMessage());
// }
catch(Exception e)
{
e.printStackTrace();
System.out.println(e.getMessage());
}

}
}
//catch (NumberFormatException numberFormatException) { ... }: 如果在 try 块中出现了 NumberFormatException 异常(即字符串无法被解析为整数),则控制流会跳到 catch 块处理异常。
//numberFormatException.printStackTrace();: 这行代码打印异常的跟踪栈信息,显示异常发生的位置和调用序列。
//System.out.println(numberFormatException.getMessage());: 这行代码打印异常的消息。在这种情况下,异常消息通常是描述问题的文本,例如 "For input string: "334dsfs"",指出哪里出现了问题。

自定义异常

如果Java提供的内置异常不能满足程序设计的需求,这时我们可以自己设计Java类库或框架,其中包括异常类型。实现自定义异常类需要继承Exception类或其子类,如果自定义运行时异常类需继承RuntimeException类或其子类

语法形式:

<自定义异常名>

编码规范:

一般将自定义异常类的类名命名为XXXException,其中XXX用来代表该异常的作用

自定义异常类

自定义异常类一般包含两个二构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接受一个定制的异常消息,并将该消息传递给超类的构造方法

public class Main {

static int div(int a,int b) throws MyException
{

if(b==0)
{
throw new MyException("分母不能是0");
}
return a/b;
}
public static void main(String[] args)
{
try
{
System.out.println(div(8,1));
}
catch (MyException me)
{
System.out.println("出现异常,被catch捕捉到");
System.out.println(me.getMessage());

}
finally
{
System.out.println("进入finally块");
}
System.out.println("结尾return前");
return;
}
}

输入数据流与输出数据流

什么是I/O

Java中I/O操作主要是指使用Java进行输入,输出操作。java所有的I/O机制都是基于数据流进行输入输出,这些数据表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它数据的方法

数据流的基本概念

数据流:一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

输入流(Input Stream):

程序从输入流读取数据源。数据源包括外界(键盘、文件、网络、、),即是将数据源读入到程序的通信通道。

输出流:

程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络、、、)的通信通道

数据流分类:

1、字节流:数据流中最小的数据单元是字节

2、字符流:数据流中最小的数据单元是字符,Java中的字符是Unicode编码,一个字符占用两个字节

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
public static void main(String[] args)
{
// for(int i=0;i< args.length;i++) {
// //打印命令行参数
// System.out.println("参数" + i + ":" + args[i]);
// }
//标准io System.in System.out
// int userInput;
// System.out.println("请输入一个字符:");
// try{
// while((userInput=System.in.read())!='q')
// {
// System.out.println((char)userInput);
}
// }catch (IOException e)
// {
// System.out.println(e.toString());
// }
InputStreamReader ir = new InputStreamReader(System.in);
BufferedReader in = new BufferedReader(ir);
String s;
try
{
//获取用户输入的一行字符串
while(true)
{
s=in.readLine();
if(s.equals("exit"))
{
break;
}
System.out.println(s);
}

}catch (IOException e)
{
e.printStackTrace();
}

}
}

文件数据流

流的原理:

输入流(input):将外部的文件通过流读取到内存中

输出流(output):将内存中的文件通过流输出到硬盘等存储设备中

分类:

按照操作单位分为:字节流,字符流

按照流向不同分为:输入流,输出流

按照角色不同分为:节点流,处理流

FileInputStream

1、FIleInputStream(File file):通过打开一个到实际文件的连接来创建一个FIleInputStream对象,该文件通过文件系统中的FIle对象file指定。

2、FileInputStream(String name):通过打开一个到实际文件的连接来创建一个FileInputStrem对象,该文件通过文件系统中的路径路径名name来指定

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
public static void main(String[] args)
{
//字节流操作文件
try
{
byte buff[]=new byte[1024];
int cnt=System.in.read(buff);
FileOutputStream fileOutputStream=new FileOutputStream("read.txt",true);
fileOutputStream.write(buff,0,cnt);
fileOutputStream.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}




FileInputStream fileInputStream = null;
try
{
fileInputStream = new FileInputStream("read.txt");
byte buffer[]=new byte[1024];
int readLen=-1;

while((readLen= fileInputStream.read(buffer,0,buffer.length))!=-1&& readLen>0)
{
byte tmp[] = new byte[readLen];
System.arraycopy(buffer,0,tmp,0,readLen);
System.out.println(new String(tmp ));
}

}catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}

FileReader

IO体系中的二FileReader:

FileReader是针对字符型文件(后缀.txt)的流,根据定义的不同也可以称其为字符流,结点流,输入流

FileReader的使用:

1、实例花一个FIle类用于操作的对象

2、实例化一个FIleReader类并且将file对象作为参数传入他的构造器中。

3、用FileReader的方法将数据读入

FileReader读入数据的方法是read()方法

->read()的空参构造器;返回读入的第一个字符并继续往下读,如果读到最后一个元素返回-1

->read(char cbuf[])一次读入cbuf中字符个元素的个数如果达到文件末尾返回-1

4、关闭流close();方法流不会自动关闭,必须手动关闭,不然会浪费资源

import com.sun.security.jgss.GSSUtil;

import java.io.*;

public class Main {
public static void main(String[] args)
{
//字节流操作文件
/*try
{
byte buff[]=new byte[1024];
int cnt=System.in.read(buff);
FileOutputStream fileOutputStream=new FileOutputStream("read.txt",true);
fileOutputStream.write(buff,0,cnt);
fileOutputStream.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}




FileInputStream fileInputStream = null;
try
{
fileInputStream = new FileInputStream("read.txt");
byte buffer[]=new byte[1024];
int readLen=-1;

while((readLen= fileInputStream.read(buffer,0,buffer.length))!=-1&& readLen>0)
{
byte tmp[] = new byte[readLen];
System.arraycopy(buffer,0,tmp,0,readLen);
System.out.println(new String(tmp ));
}

}catch (IOException ioe)
{
ioe.printStackTrace();
}*/
//字符文件操作
/*try
{
File file = new File("a.txt");
FileWriter fileWriter=new FileWriter(file);
fileWriter.write("这是文件内容");
fileWriter.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}
try
{
File file =new File("a.txt");
FileReader fileReader=new FileReader(file);
int readLen=-1;
int ct=-1;
while ((ct=fileReader.read())!=-1)
{
System.out.println((char) ct);
}
}catch (IOException ee)
{
ee.printStackTrace();
}*/
//2种转换
try {
FileInputStream input = new FileInputStream("read .txt");
InputStreamReader inputStream = new InputStreamReader(input);
FileOutputStream output=new FileOutputStream("b.txt");
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(output);
}catch (IOException ioe)
{
ioe.printStackTrace();
}

}
}

字节流–过滤流

缓冲区数据流:

缓冲区数据流有BufferedInputStream和BufferedOutputStream

网上错误说法:在关闭一个缓冲区输出流之前,要使用flush()方法,强制输出剩余数据,确保缓冲区里的所有数据全部写入输出流,错误说法。

数据数据流:

之前说的数据流中,处理的数据不是字节就是字节数组,但是有很多时候,不只是只有这两种数据,所以就要用专门的过滤数据流来处理,这里给出DataInputStream,DataOutputStream,他们允许对Java基本类型进行处理。

import java.io.*;
public class Main {
public static void main(String[] args)
{
//缓冲区流对象
try
{
FileInputStream in=new FileInputStream("input.bin");
FileOutputStream out=new FileOutputStream("out.bin");
BufferedInputStream bufferedInputStream=new BufferedInputStream(in);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(out);
int data;
while((data= bufferedInputStream.read())!=-1)
{
bufferedOutputStream.write(data);
}
bufferedInputStream.close();
bufferedOutputStream.close();
in.close();
out.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}
//数据流对象
try
{

FileOutputStream out=new FileOutputStream("dataOut.bin");
DataOutputStream dataOutputStream=new DataOutputStream(out);

dataOutputStream.writeInt(123);
dataOutputStream.writeDouble(3.515);
dataOutputStream.writeBoolean(true);
dataOutputStream.close();
out.close();
FileInputStream in=new FileInputStream("dataOut.bin");
DataInputStream dataInputStream=new DataInputStream(in);
int num=dataInputStream.readInt();
double pi = dataInputStream.readDouble();
boolean flag=dataInputStream.readBoolean();
System.out.printf("%d,%f,%b",num,pi,flag);
dataInputStream.close();
in.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}

字节流–可持久化

可持久化就是对象通过描述自己状态的数值来记录自己的过程

当一个类实现Serializable接口时,表面该类加入了对象串行化协议

需要注意的是,要使一个java对象可序列化,必须实现Serializable接口。这个接口没有任何方法,只是一个标记接口,用于告诉java虚拟机这个类可以被序列化。

字节流–管道流、对象流

管道数据流:PipedOutputStream和PipedInputStream,管道的两端建立连接后就可以通信

对象流:ObjectOutputStream和ObjectInputStream,将一个对象示例写入文件

transient关键字用于标记一个变量不需要被序列化

import java.io.*;
import java.nio.charset.StandardCharsets;

class Person implements Serializable
{
private String name;
private int age;
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
public class Main {
public static void main(String[] args)
{
//缓冲区流对象
try
{
FileInputStream in=new FileInputStream("input.bin");
FileOutputStream out=new FileOutputStream("out.bin");
BufferedInputStream bufferedInputStream=new BufferedInputStream(in);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(out);
int data;
while((data= bufferedInputStream.read())!=-1)
{
bufferedOutputStream.write(data);
}
bufferedInputStream.close();
bufferedOutputStream.close();
in.close();
out.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}
//数据流对象
try
{

FileOutputStream out=new FileOutputStream("dataOut.bin");
DataOutputStream dataOutputStream=new DataOutputStream(out);

dataOutputStream.writeInt(123);
dataOutputStream.writeDouble(3.515);
dataOutputStream.writeBoolean(true);
dataOutputStream.close();
out.close();
FileInputStream in=new FileInputStream("dataOut.bin");
DataInputStream dataInputStream=new DataInputStream(in);
int num=dataInputStream.readInt();
double pi = dataInputStream.readDouble();
boolean flag=dataInputStream.readBoolean();
System.out.printf("%d,%f,%b",num,pi,flag);
dataInputStream.close();
in.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}
//序列化
Person person01 = new Person("张三",18);
try
{
FileOutputStream out=new FileOutputStream("objectOut.bin");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(out);
objectOutputStream.writeObject(person01);
objectOutputStream.close();
FileInputStream in = new FileInputStream("objectOut.bin");
ObjectInputStream objectInputStream = new ObjectInputStream(in);
try {
Person person02 = (Person) objectInputStream.readObject();
objectInputStream.close();
System.out.println("\r\n"+person02.getName()+person02.getAge()+","+person02.getAge());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
try {
objectInputStream.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}catch (IOException ioe)
{
ioe.printStackTrace();
}

}
}

管道

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;

public class Main
{
public static void main(String[] args) throws IOException {
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);

//线程1
Thread writeThread = new Thread(()->{
try {
pipedOutputStream.write("hello world".getBytes(StandardCharsets.UTF_8));
}catch (IOException ioe)
{
ioe.printStackTrace();
}
});
//线程2
Thread readThread = new Thread(()->{
try{
int data;
while((data=pipedInputStream.read())!=-1)
{
System.out.println((char)data);
}
}catch (IOException e)
{
e.printStackTrace();
}
});
writeThread.start();
readThread.start();
try {
writeThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
readThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}


}
}

字符流–缓冲区读者写者

缓冲区的出现,提高了对数据的读写效率,缓冲区要结合流才可以使用,缓冲区是在流的基础上对流的功能进行增强

import java.io.*;
import java.nio.charset.StandardCharsets;

public class Main
{
public static void main(String[] args) throws IOException {
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);

//线程1
Thread writeThread = new Thread(()->{
try {
pipedOutputStream.write("hello world".getBytes(StandardCharsets.UTF_8));
}catch (IOException ioe)
{
ioe.printStackTrace();
}
});
//线程2
Thread readThread = new Thread(()->{
try{
int data;
while((data=pipedInputStream.read())!=-1)
{
System.out.println((char)data);
}
}catch (IOException e)
{
e.printStackTrace();
}
});
writeThread.start();
readThread.start();
try {
writeThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
readThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//字符流 缓冲区
try
{
FileReader fileReader = new FileReader("test.iml");
BufferedReader reader = new BufferedReader(fileReader);
String line;
FileWriter fileWriter = new FileWriter("b.iml");
BufferedWriter writer = new BufferedWriter(fileWriter);
while((line=reader.readLine())!=null)
{
writer.write(line+"\r\n");
}
reader.close();
writer.close();
fileReader.close();
fileWriter.close();
}catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}

File类概述

java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关

File类中涉及到的关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成

想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录

绝对路径 vs 相对路径

1、绝对路径:是一个固定的路径,从盘符开始

2、相对路径:是相对于某个位置开始

main()方法中的相对路径是相对于当前工程

单元测试方法中的相对路径是相对于当前模块

路径分隔符:

1、路径中的每级目录之间用一个路径分隔符隔开

2、路径分隔符和系统有关:

windows和DOS系统默认使用”\“来表示

UNIX和URL使用“\”来表示

3、Java程序支持跨平台运行,因此路径分隔符要慎用。

为了解决这个隐患,File类提供了一个常量:

public static final String separator:根据操作系统,动态的提供分隔符。

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
public static void main(String[] args) throws IOException {
//File类的使用
File file = new File("src/a.txt");
System.out.println(file.getAbsoluteFile());
System.out.println(file.getParent());
System.out.println(file.getPath());
System.out.println(file.getCanonicalPath());
System.out.println(file.canWrite());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
System.out.println(file.getName());
System.out.println(file.length());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("最后修改时间"+simpleDateFormat.format(new Date(file.lastModified())));
System.out.println(File.separator);
}
}

随机访问文件

使用随机访问文件,我们可以从文件读取以及写入文件。使用文件输入和输出流读取和写入时顺序过程。

使用随机访问文件,可以在文件中的任何位置读取或写入。

RandomAccessFile类的一个对象可以进行随机文件访问。可以读/写字节和所有原始类型的值到一个文件

RandomAccessFile可以直接使用其readUTF()和writeUTF()方法处理字符串。

RandomAccessFile类在不在InputStream和OutputStream类的层次结构中。

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
public static void main(String[] args) throws IOException {
//File类的使用
// File file = new File("src/a.txt");
// System.out.println(file.getAbsoluteFile());
// System.out.println(file.getParent());
// System.out.println(file.getPath());
// System.out.println(file.getCanonicalPath());
// System.out.println(file.canWrite());
// System.out.println(file.isFile());
// System.out.println(file.isDirectory());
// System.out.println(file.getName());
// System.out.println(file.length());
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
// System.out.println("最后修改时间"+simpleDateFormat.format(new Date(file.lastModified())));
// System.out.println(File.separator);

//随机读写文件
String fileName = "randomAccessFile.txt";
File fo = new File(fileName);
if(!fo.exists())
{
RandomAccessFile randomAccessFile = new RandomAccessFile(fo.getPath(),"rw");
randomAccessFile.writeInt(1234);
randomAccessFile.writeDouble(5.67);
randomAccessFile.writeUTF("Hello World");
randomAccessFile.close();
}
RandomAccessFile randomAccessFile = new RandomAccessFile(fo.getPath(),"rw");
int intVal = randomAccessFile.readInt();
Double doubleVal = randomAccessFile.readDouble();
System.out.println("int:"+intVal+",double"+doubleVal);
long cur = randomAccessFile.getFilePointer();
randomAccessFile.seek(4);
randomAccessFile.writeDouble(8.99);
randomAccessFile.seek(cur);
randomAccessFile.close();

RandomAccessFile randomAccessFile2 = new RandomAccessFile(fo.getPath(),"rw");
int intVal2 = randomAccessFile2.readInt();
Double doubleVal2 = randomAccessFile2.readDouble();
System.out.println("int:"+intVal2+",double"+doubleVal2);
long cur2 = randomAccessFile.getFilePointer();
randomAccessFile2.seek(4);
randomAccessFile2.writeDouble(0.00);
randomAccessFile2.seek(cur);
randomAccessFile2.close();
}
}
//要注意文件是否已经存在!!!