1、IO技术
1.1、介绍IO
IO:I input、O output。IO:输入和输出。
Java中的IO技术:主要是操作文件和文件夹中的数据的。
学习IO:必须分清楚I和O的方向。然后确定到底使用I还是O。
在Java中IO:
Input:输入、读取操作。
Output:输出、写操作。
不同的操作,在JDK中提供不能的类。根据操作的数据的不同,又将这些类分成字节和字符。
在Java中的IO流分成:
字节流:主要以字节的方式读写数据的。
字节输入流:
字节输出流:
字符流:主要以字符的方式读写数据的。
字符输入流:
字符输出流:
1.2、File类
1.2.1、File类介绍
任何数据在存储设备(硬盘、光盘、U盘等等)都是以二进制存储。但是体现出来的文件或文件夹。所以在Java中提供File类,它是专门操作文件或文件夹的。
文件:是最终存放数据的地方。
文件夹:存放文件或文件夹的地方。
File类,主要是对文件和文件夹进行:
- 创建
- 删除
- 判断
- 获取
- 改名
- 列举
1.2.2、File类的构造函数
File类没有对外提供空参数的构造函数。因为主要存在一个File类,就说明当前File肯定指定的是一个文件或文件夹。
/*
* 演示File类的构造函数
*/
public class FileDemo {
public static void main(String[] args) {
/*
* File(String pathname)
* 参数:它是将需要封装的文件夹或文件作为参数
*/
File file = new File("D:/abc");
System.out.println(file);
/*
* File(String parent, String child)
* String parent : 当前需要封装的文件或文件夹的父目录
* String child : 子目录(文件夹)或文件
*/
File file2 = new File("d:/","abc");
System.out.println(file2);
/*
* File(File parent, String child)
*/
File parent = new File("d:/");
File file3 = new File(parent ,"abc/bbb");
System.out.println(file3);
}
}
注意:使用File类的构造函数封装某个文件或文件夹,这时得到的File对象,代表的文件或文件夹到底会不会存在,创建File对象的时候是不会去检测的。
1.2.3、获取方法
/*
* File中的获取方法
*/
public class FileDemo2 {
public static void main(String[] args) throws IOException {
File file = new File("d:/abc/bbb/../ccc/dd");
// getAbsolutePath 获取绝对路径
System.out.println("getAbsolutePath:"+file.getAbsolutePath());
System.out.println("getAbsoluteFile:"+file.getAbsoluteFile());
// getCanonicalPath 获取绝对真实路径
System.out.println("getCanonicalPath:"+file.getCanonicalPath());
System.out.println("getCanonicalFile:"+file.getCanonicalFile());
// getParentFile 获取到的当前封装的文件或文件夹的父目录 得到的父目录的File对象
System.out.println("getParentFile:"+file.getParentFile());
// getPath 获取到的是File对象中封装的路径
System.out.println("getPath:"+file.getPath());
// getParent 获取到的当前封装的文件或文件夹的父目录 得到的是父目录的字符串表示形式
System.out.println("getParent:"+file.getParent());
// getName 获取到的是File对象中封装的多级目录中最后一级内容
System.out.println("getName:"+file.getName());
// getTotalSpace 某个盘符的最大容量
System.out.println("getTotalSpace:"+file.getTotalSpace());
// 可用空间
System.out.println("getFreeSpace:"+file.getFreeSpace());
System.out.println("getUsableSpace:"+file.getUsableSpace());
}
}
1.2.4、绝对路径和相对路径
绝对路径:以根目录(盘符)开始的路径。或者以”/”开始的也称为绝对路径。
相对路径:不为”/”或盘符开始的路径。
1.2.5、创建方法
File file = new File("d:/abc");
/*
* createNewFile 创建文件,返回true,创建成功,返回false创建失败
*/
boolean b = file.createNewFile();
System.out.println(b);
/*
* 演示File类中的创建方法
*/
public class FileDemo3 {
public static void main(String[] args) throws IOException {
File file = new File("d:/abc");
/*
* createNewFile 创建文件,返回true,创建成功,返回false创建失败
*/
boolean b = file.createNewFile();
System.out.println(b);
/*
* 创建目录mkdir 创建单级目录
*/
File file2 = new File("d:/abcd/aaa/bb");
boolean b2 = file2.mkdir();
System.out.println(b2);
/*
* mkdirs 创建多级目录
*/
boolean c = file2.mkdirs();
System.out.println(c);
}
}
1.2.6、删除方法
// 创建File对象
File file = new File("d:/abc");
boolean b = file.delete();
System.out.println(b);
delete方法:删除文件或文件夹,不走回收站。慎用。
如果是多级目录,只能删除最后一级目录。
1.2.7、判断方法
/*
* 判断方法
*/
public class FileDemo5 {
public static void main(String[] args) {
File file = new File("d:/abc.txt");
// exists 判断File表示的文件或文件夹是否存在
System.out.println(file.exists());
// isDirectory 判断File表示的是否是文件夹
System.out.println(file.isDirectory());
// isFile 判断File表示的是否是文件
System.out.println(file.isFile());
// isHidden 判断File表示的文件或文件夹是否是隐藏的
System.out.println(file.isHidden());
}
}
1.2.8、列举方法
/*
* 演示ListRoots方法
*/
public class FileDemo6 {
public static void main(String[] args) {
// 获取系统的所有根目录
File[] roots = File.listRoots();
for (File f : roots) {
System.out.println(f);
}
}
}
list方法和listFiles方法:
共同点:都可以获取指定目录(文件夹)下面的所有文件和文件夹。
不同点:list方法是将获取到的文件和文件夹的名称作为字符串的方式存储在String数组中。
listFiles方法它是将获取到的每个文件和文件夹再次分别封装成不同的File对象,将每个File对象最后存储在File数组中。
/*
* 演示list方法
*/
public class FileDemo7 {
public static void main(String[] args) {
// 获取指定目录下的文件或文件夹
File file = new File("d:/");
String[] files = file.list();
for (String s : files) {
System.out.println(s);
}
}
}
/*
* 演示listFiles方法
*/
public class FileDemo8 {
public static void main(String[] args) {
// 获取指定目录下的文件或文件夹
File file = new File("d:/");
// 获取到指定目录下的文件和文件夹
File[] files = file.listFiles();
// 遍历数组
for (File f : files) {
// 从数组中得到了每个File对象
if( f.isDirectory() ){
System.out.println(f.getName() +" 是文件夹");
}else{
System.out.println( f.getName() +" 是文件");
}
}
}
}
1.2.9、遍历多级目录
/*
* 遍历多级目录
*/
public class FileDemo9 {
public static void main(String[] args) {
File dir = new File("d:/");
getFiles(dir);
//File dir2 = new File("d:/System Volume Information");
//System.out.println(dir2.exists());
//File[] files = dir2.listFiles();
//System.out.println(files);
}
public static void getFiles( File dir ){
/*
* 获取指定目录下的文件和文件夹
* list 和 listFiles可以获取指定目录下的文件和文件夹,但是如果指定的目录权限较高,java根本无法获取其下的文件和文件夹
* ,那么list和listFiles方法将返回的null。这时如果继续操作就会发生NullPointerException
*/
File[] files = dir.listFiles();
if( files != null ){
// 遍历 for( int i = 0 ; i < files.length ; i++ )
for( File file : files ){
// 遍历取出的是文件还是文件夹
if( file.isDirectory() ){
/*
* 判断成立,说明当前取出的一定是文件夹(目录),
* 是目录就需要继续调用listFiles方法,获取当前这个目录下的所有文件和文件夹
*/
getFiles(file);
}else{
System.out.println(file);
}
}
}
}
}
1.2.10、递归
递归:主要指的是方法的相互调用。
直接递归:方法自己调用自己。
间接递归:A方法调用B方法,B方法调用........ 最后会调用A方法。
练习递归:计算1~5之间的和值。
/*
* 递归演示
*/
public class DiGuiDemo {
public static void main(String[] args) {
int sum = getSum(10);
System.out.println(sum);
}
// 采用递归的方法计算1~i的和值
public static int getSum(int i) {
if( i > 1 ){
return i + getSum( i - 1 );
}
return 1;
}
}
递归细节问题:
- 递归调用不能无限制的调用。递归中一定要有判断,能够保证递归最终可以停止调用。
递归的次数不能太多,太多也会死掉。
1.3、过滤器
1.3.1、过滤器介绍
过滤器:它的主要作用将符合条件的内容留下,将不符合的剔除掉。
在IO中的过滤器,主要是将我们需要的文件或文件夹留下,将不符合条件的文件或文件夹过滤掉。
FileFilter:文件或文件夹对象过滤器。
FilenameFilter:文件名或文件夹的名称过滤器。
1.3.2、FileFilter过滤器
定义一个类,实现FileFilter接口,在实现类中书写对应的过滤器条件(accept方法)。
/*
* 文件或文件夹对象过滤器FileFilter
*/
class MyFileFilter implements FileFilter{
@Override
public boolean accept(File pathname) {
//在accept方法中书写具体的过滤的条件
return pathname.isFile();
}
}
public class FileFilterDemo {
public static void main(String[] args) {
// 创建File对象
File dir = new File("d:/123");
/*
* 给listFiles传递过滤器,在过滤器中书写具体的过滤条件
* listFiles的运行原理:
* 当通过一个File对象封装的目录去调用listFiles方法的时候,其实在listFiles方法的底层
* 会根据当前是否传递的过滤器,如果没有传递,就直接将当前目录下的所有文件和文件夹全部封装成File对象
* 最终保存在File数组中。
*
* 如果listFiles方法接收到了过滤器对象,那么底层就会在获取每个文件或文件夹的时候,都会将这个文件或文件夹作为对象
* 传递给过滤器中的accept方法,如果accept方法返回的true,就listFiles方法就会将当前这个File对象留下,如果accept方法
* 返回的false,就listFiles方法就会将当前这个File对象过滤掉。
*
*/
File[] files = dir.listFiles( new MyFileFilter() );
for (File file : files) {
System.out.println(file);
}
}
}
1.3.3、FilenameFilter过滤器
/*
* 自定义的文件名过滤器
*/
class MyFilenameFilter implements FilenameFilter{
@Override
public boolean accept(File dir, String name) {
/*
* File dir 表示的父目录,就是需要被过滤的文件或文件夹所在的目录
* String name 表示的当前指定的目录下的文件或文件夹名称
*/
return name.endsWith(".txt");
}
}
public class FilenameFilterDemo {
public static void main(String[] args) {
// 创建File对象
File dir = new File("d:/123");
String[] files = dir.list( new MyFilenameFilter() );
for (String f : files) {
System.out.println(f);
}
}
}
2、IO流
2.1、IO流的分类
在Java中的IO流分成:
字节流:主要以字节的方式读写数据的。
字节输入流:
字节输出流:
字符流:主要以字符的方式读写数据的。
字符输入流:
字符输出流:
任何数据在存储设备上肯定都是字节方式(二进制、1010101001010010)。但是不同的二进制合并在一起,可以表示字符内容。
字节数据:
特定的二进制数据合并在一起,它们表示的含义并不是某个写字符内容。这些二进制就称为字节数据。
常见的字节数据文件:
word、图片、音频、视频、压缩文件等。
字符数据:
特定的二进制数据合并在一起,它们可以表示一定的字符数据。这些二进制就可以理解为字符数据。
常见的字符数据文件:记事本。
2.2、字节流
2.2.1、介绍字节流
字节输入流:
InputStream:它是字节输入流的顶层父类。
它里面定义了如何读取字节数据的方法 read
字节输出流:
OutputStream:它是字节输出流的顶层父类。
定义了如何写字节数据的方法 write
原则性问题:只要是使用Java操作其他非Java程序本身的数据,在操作完成之后,都需要让java程序和外围的这些存储数据的设备之间断开连接。
2.2.2、字节输出流
/*
* 字节输出流
*/
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
/*
* 创建字节输出流对象
* 只要是是输出流,如果关联的文件不存在,它就会创建这个文件。
* 如果文件存在,就会将原来文件中的内容覆盖掉。
*/
FileOutputStream fos = new FileOutputStream("d:/xyz.txt" /*, true*/);
/*
* 写数据write( int b) 它其实在写出数据的时候,仅仅只能将int中的最低的8个二进制数位写出去。
fos.write(97);
fos.write(353);
fos.write(354);
fos.write(355);
*/
byte[] buf = {65,66,97,98,103};
/*
* write( byte[] buf ) :它是将整个字节数组中的数据写出。
*/
//fos.write(buf);
/*
* write( byte[] buf , int off , int len )
* 将buf字节数组中的数据从off位置开始共计写出len个
*/
fos.write(buf, 3 , 1 );
// 关闭流
fos.close();
}
}
2.2.3、字节输出流练习
/*
* 需求:将键盘录入的数据,写到文件中。
*/
public class FileOutputStreamTest {
public static void main(String[] args) throws IOException {
demo2();
}
// 循环录入,写到文件中 , 假设录入exit 认为停止录入
public static void demo2() throws FileNotFoundException, IOException {
// 键盘录入
Scanner sc = new Scanner( System.in );
System.out.println("请输入数据:");
String data = sc.nextLine();
// 创建输出流
FileOutputStream fos = new FileOutputStream("d:/data.txt");
while( ! data.equalsIgnoreCase("exit") ){
fos.write( data.getBytes() );
System.out.println("请输入数据:");
data = sc.nextLine();
}
fos.close();
}
// 键盘录入,写到文件中
public static void demo() throws FileNotFoundException, IOException {
// 键盘录入
Scanner sc = new Scanner( System.in );
System.out.println("请输入数据:");
String data = sc.nextLine();
// 创建输出流
FileOutputStream fos = new FileOutputStream("d:/data.txt");
// 写数据
fos.write( data.getBytes() );
// 关流
fos.close();
}
}
2.2.4、字节输入流
InputStream:它是字节输入流的超类。它其中定义了如何读取字节的方法。
read() 调用一次,从文件中读取一个字节。 相反操作 write( int b)
read( byte[] b ) 调用一次,就从底层读取很多个字节,直接将这个数组填满为止。 相反操作 write( byte[] b)
read( byte[] b , int off , int len) 调用一次,从底层读取若干个字节,存放在字节数组中,从off位置开始存储,共计能够存储len个。相反操作 write( byte[] b , int off , int len)
当调用read方法读取到文件末尾的时候,统一返回-1。
2.2.5、演示读取操作
// 演示最基本的读取操作
public static void demo() throws IOException {
// 创建流对象,和被读取的文件关联上
FileInputStream fis = new FileInputStream("d:/xyz.txt");
System.out.println( fis.read() ); // 每次读取一个字节
System.out.println( fis.read() );
System.out.println( fis.read() );
System.out.println( fis.read() ); // 读取到文件末尾返回-1
// 关流
fis.close();
}
2.2.6、演示一次读取一个字节 ★★★★★
// 演示输入流 一次读取一个字节的模版代码
public static void demo2() throws IOException {
// 创建流对象,和被读取的文件关联上
FileInputStream fis = new FileInputStream("d:/xyz.txt");
// 使用这个int类型的变量,保存每次读取到的那个字节数据
int ch = 0;
// 使用用循环重复读取字节数据,只要不是-1,就认为没有读取到文件末尾
while( ( ch = fis.read() ) != -1 ){
// 在循环中处理读取到的数据
System.out.println(ch);
}
// 关流
fis.close();
}
2.2.7、演示一次读取多个字节 ★★★★★
// 演示输入流一次读取多个字节数据 read(byte[] b)
public static void demo3() throws IOException {
FileInputStream fis = new FileInputStream("d:/xyz.txt");
// 定义变量,记录本次读取的字节个数
int len = 0;
/*
* 定义字节数据,用于存储每次读取到的若干个字节数据
* 如果定义的数组,作为临时的存储读取数据的空间,一般情况下
* 将这个数组定义成1024的整数倍
*/
byte[] buf = new byte[1024];
/*
* 使用循环读取
* len = fis.read( buf )
* fis.read( buf ) 先从文件中读取若干个字节数据。最多只能读取到buf.length个字节
* 如果文件中的字节不够buf.length个字节,这时肯定一次就将文件中的数据读取完了。
* 如果超过buf.length个字节,循环就会多次去执行读取操作
*
* len = fis.read( buf ) 最终将每次读取到的字节个数赋值给len变量。 如果读取到了文件末尾
* 这时就相当于根本就没有读取到任何的数据,这时就不给buf中存放任何的字节数据,同时会将-1保存到len中
*/
while( ( len = fis.read( buf ) ) != -1 ){
/*
* 处理读取到的字节数据 , 数据在buf数组中保存着,到底buf中有多个少有效字节数,len中记录
* 我们每次只要从数组的0位置开始处理到len个字节数据即可。不需要将整个数组全部处理。
*/
String s = new String( buf , 0 , len );
System.out.println(s);
}
// 关流
fis.close();
}
2.2.8、字节流练习(复制单个文件)
/*
* 演示 复制文件
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
copy2();
}
// 一次读写多个字节数据
public static void copy2() throws FileNotFoundException, IOException{
// 创建输入流对象,读取字节数据
FileInputStream fis = new FileInputStream("d:/1.avi");
// 创建输出流对象,用于将读取到的字节数据写到指定的文件中
FileOutputStream fos = new FileOutputStream("e:/1.avi");
// 获取系统时间
long start = System.currentTimeMillis();
int len = 0;
byte[] buf = new byte[8192];
while( ( len = fis.read( buf ) ) != -1 ){
// 写的时候一定要读取几个,写几个
fos.write(buf, 0, len);
}
// 获取复制完文件之后的系统时间
long end = System.currentTimeMillis();
System.out.println( "复制文件所需的事件:"+ (Math.abs( start - end )) );
// 关流
fis.close();
fos.close();
}
//一次复制一个字节,效率太低
public static void copy() throws FileNotFoundException, IOException {
// 创建输入流对象,读取字节数据
FileInputStream fis = new FileInputStream("d:/1.avi");
// 创建输出流对象,用于将读取到的字节数据写到指定的文件中
FileOutputStream fos = new FileOutputStream("e:/1.avi");
// 获取系统时间
long start = System.currentTimeMillis();
// 一次读取一个字节,同时写一个字节
int ch = 0;
while( ( ch = fis.read() ) != -1 ){
// 写字节数据
fos.write(ch);
}
// 获取复制完文件之后的系统时间
long end = System.currentTimeMillis();
System.out.println( "复制文件所需的事件:"+ (Math.abs( start - end )) );
// 关流
fis.close();
fos.close();
}
}
2.3、切割合并文件
2.3.1、切割文件
将一个较大的文件切割成多个碎片文件。
切割的方式:
- 固定每个碎片的大小。
- 固定碎片文件的个数。
/*
* 演示切割文件
*/
public class CutFile {
public static void main(String[] args) throws IOException {
// 定义输入流,用于读取被切割的文件
FileInputStream fis = new FileInputStream("d:/1.jpg");
// 定义变量,充当碎片文件名
int name = 1;
// 定义数组,充当缓冲区。目的一次可以读取多个字节数据
byte[] buf = new byte[10240];
int len = 0;
while( ( len = fis.read( buf ) ) != -1 ){
// 在循环中创建输出流对象。每次循环都会读取10KB的字节,需要单独一个输出流写到当前某个碎片文件中
FileOutputStream fos = new FileOutputStream("d:/123/"+name+".jpg");
name++;
// 写每次读取到的字节数据
fos.write(buf, 0, len);
// 将输出流关闭
fos.close();
}
// 最后关闭输入流
fis.close();
}
}
2.3.2、合并文件
/*
* 演示合并文件
*/
public class MergeFile {
public static void main(String[] args) throws IOException {
//创建输出流,用于将不同的输入流读取的字节写到同一个文件中
FileOutputStream fos = new FileOutputStream("d:/123/merge.jpg");
for( int i = 1 ; i < 16 ; i++ ){
// 在循环中创建输入流,每个输入流都可以和一个文件进行关联
FileInputStream fis = new FileInputStream("d:/123/"+i+".jpg");
int len = 0;
byte[] buf = new byte[1024];
while( ( len = fis.read(buf) ) != -1 ){
// 写数据
fos.write(buf, 0, len);
}
// 内层循环结束,就表示某个碎片文件读取结束
fis.close();
}
// 外面循环结束,就说明所有碎片全部读写完成
fos.close();
}
}
2.4、第三方jar包
2.4.1、介绍第三方jar包
在我们程序中可以引入使用不是JDK(Oracle)提供的一些java程序(jar包)。这些程序都称为第三方jar包。
常用第三方:
Apache:http://www.apache.org/ http://commons.apache.org/
Spring: spring.io
IBM:
google:
github:
alibaba:
2.4.2、项目中引入第三方jar包
将当前lib下的所有jar包添加到classpath中
3、异常
3.1、异常介绍
异常:程序运行过程中发生的一些问题。Exception。
在Java中将很多常见的异常(问题)进行总结和抽象的描述。在JDK中,Java封装好了很多很多常见的一些异常类。我们在开发中程序会发生各种各样的异常(问题),需要根据这些类名已经相关的报错信息进行分析。
3.2、异常的体系结构
在进行问题过程中,形成了异常的体系结构:
Throwable:它是异常的顶层父类(它的父类是Object)。
Error:它是错误的问题顶层父类。
Exception:它是一些常见的异常问题的顶层父类。
我们讲解的异常主要针对的Exception这类问题。而针对Error问题,这时就必须对程序的架构、设计、代码进行重新的调整。
3.3、异常的应用
3.3.1、通过异常显示告诉使用者问题
/**
* 演示异常的简单应用
*/
// 定义圆
class Circle{
// 半径
private double radius;
// 圆周率
private static double PI = Math.PI;
// 定义构造函数
public Circle( double radius ){
// 健壮性判断。函数接收数据,在应用之前,需要判断,防止非法数据
if( radius <= 0 ){
/*
* 这里需要使用Java中的异常手动的告诉对方,传递的数据有问题,并且程序不再运行
* 需要使用Java中已经存在的某个异常类对象,通过这个对象将问题抛给当前的调用者
* 如果在程序中需要将异常问题抛给使用者,必须使用throw关键字
*
* 抛出异常的格式:
* throw new 异常类("异常的信息");
*/
throw new IllegalArgumentException( "圆的半径不能小于零!!!" );
}
this.radius = radius;
}
// 计算面积
public double getArea(){
return PI * radius * radius ;
}
}
public class ExceptionDemo {
public static void main(String[] args) {
Circle c = new Circle( -2 );
double area = c.getArea();
System.out.println(area);
}
}
3.3.2、异常的抛出和捕获
两个角色:
1、函数的定义者:
如果是自己在定义函数(功能)时,如果发现程序中有问题,并且这个问题不是自己代码造成的,而是由于调用者数据等其他原因造成。这时我们就可以在自己的函数中,将这个问题抛给调用这个函数的使用者。
2、函数的调用者:
如果我们调用别人的函数,被调用的函数中有异常问题抛出来,我们可以在调用语句的地方去截获这个问题,然后将这个问题在自己调用的过程中进行处理掉。
抛出throw关键字:在程序中遇到问题,可以找到问题对应的异常类,然后使用throw 将问题对象抛出给调用者。
捕获:如果调用别人的程序,遇到异常,这时我们尽可能将异常捕获掉(有时有些问题我们必须将其给其他使用者继续暴漏出去)。
抛异常的格式:
throw new 异常类名( ”异常的信息” );
3.3.3、捕获异常
捕获异常的格式:
try{
可能有异常的代码(调用语句);
}catch( 异常类名 变量名 ){
处理异常的代码;
}
public class ExceptionDemo {
public static void main(String[] args) {
try{
// 书写可能有异常的代码
Circle c = new Circle( -2 );
double area = c.getArea();
System.out.println(area);
}catch( IllegalArgumentException e ){
/*
* 捕获到异常之后,应该如何处理的代码书写位置
* 经常在catch中,使用e.printStackTrace() 打印异常信息
* 同时可能会将异常进行其他的转换。
*/
System.out.println("异常啦!!!!");
e.printStackTrace();
//System.err.println("111111111111111");
}
System.out.println(".......");
}
}
3.4、异常的分类
关于Java中的异常分类:
Exception:它是异常的顶层父类。如果在程序中直接看到Exception表明当前的异常属于编译时期会被检测的异常(编译时期异常)。
RuntimeException:它属于运行时期异常(编译的时候不会被检测)。大部分情况下,我们都会使用运行时期异常。
如果我们在程序中,使用throw关键字,抛出了编译时期会被检测的异常,这时必须有严格解决方案,否则程序无法编译通过。
但是如果我们使用了RuntimeException异常,编译的时候,编译器不检测,因此不用考虑编译的问题,但是运行的过程中我们使用throw关键字将其抛出,这些异常也会抛给调用者。
/*
* 演示 Exception和RuntimeException两个异常
*/
class Demo{
// 只算正数的和值
public int getSum( int a , int b ){
if( a <= 0 || b <= 0 ){
// 告诉使用者,数据有问题 , 抛出的运行的异常
throw new RuntimeException("传递的数据只能正数!!!");
}
return a + b;
}
/*
* 计算商
*
* 在定义函数的过程中,函数中有编译时期的异常,
* 那么我们就必须在自己的函数中对异常做处理,不处理自己的函数无法编译通过。
*
* 如果自己的函数中的异常,必须让调用者知道,这时可以在函数上使用throws 关键字,对异常进行声明
*
* 如果这个异常不需要对方知道,可以将异常在自己的函数中捕获。
*/
public int div( int a , int b) throws Exception {
// 保证除数不能为零
if( b == 0 ){
throw new Exception("除数不能为零!!!");
}
return a / b;
}
}
public class ExceptionDemo2 {
public static void main(String[] args) throws Exception {
Demo d = new Demo();
int sum = d.getSum(1, 4);
System.out.println(sum);
int div = d.div(12, 0);
System.out.println(div);
}
}
在程序中遇到不管是什么异常:
处理方案都是两种:
1、在函数上使用throws关键字,将函数中的问题直接声明出去。
2、在函数中使用try-catch代码将问题进行捕获。
注意:只要是代码中出现编译时期的异常,不管是在定义函数时函数中的编译异常,还是作为调用者,被调用的函数上有编译异常。都需要给出对应的处理方案。
大部分情况下:
定义函数时,如果函数中有编译异常, 我们都会使用throws关键字将异常显示声明在函数上,目的是让调用者知道。
如果是调用函数,那么肯定在编写调用代码的时候,就会知道函数上有问题,这时作为调用者,一般都会使用try-catch处理这个异常。当然也可以在函数上将异常继续声明给其他的调用者。
3.5、自定义异常
本身在JDK中,已经存在很多的类来表示不同的异常问题。
ArrayIndexOutOfBoundsException:数组角标越界异常
NullPoniterException:空指针异常
ClassCastException:类型转换异常
......
后期开发中,难免还会遇到一些异常问题,但是这些问题在JDK中是没有对应的异常类对其进行的描述。
这时就需要我们开发者自己认为的根据异常定义相关的异常类。然后在程序中应用这些自己定义的异常类。
自定义异常,就是在定义一个类。只不过这个类不是普通的(一般的)类,要想一个类能够表示某中异常,这时定义好的这个类,必须去继承JDK中异常存在的某个异常类(Exception、RuntimeException)。
/*
* 自定义异常
*/
// 1、定义类,继承Exception或RuntimeException
public class RadiusException extends Exception{
// 2、在类中提供构造函数
public RadiusException(){}
public RadiusException(String message){
super(message);
}
}
异常类的书写步骤:
- 让当前类继承Exception或RuntimeException
- 在类中提供构造函数(提供空参数构造函数,和可以接收一个字符串的构造函数)。
以后如果开发项目需要自定义异常:
包名:公司域名.项目或模块名.exception
例如: com.xuexi.exception
3.6、异常的细节
3.6.1、捕获的其他组合
常用:
try{
可能会有异常的代码
}catch( 异常类名 变量名 ){
处理捕获的异常
}
其他的组合方式:
try{
可能会有异常的代码
}catch( 异常类名 变量名 ){
处理捕获的异常
}finally{
永远都会被执行的代码
}
/*
* 演示try-catch-finally
*
*/
class Demo4{
public int show( int x ) {
try{
if( x % 2 == 0 ){
throw new RuntimeException("x % 2 == 0 ");
}
if( x % 3 == 0 ){
throw new Exception(" x % 3 == 0 ");
}
return 1;
}catch( Exception e){
/*
* 上面的代码发生了异常,被catch捕获到了,
* 程序开始执行catch中的代码,本身应该是将catch中的return 2
* 执行完成,将数字2作为返回值。但是由于程序有finally代码块
* 而finally代码块是永远都需要被执行的代码。所以本身应该执行return 2 的时候
* JVM去执行的finally代码块,但是在finally中有个return 3 ,导致将finally中的
* 3 作为返回值返回了。
*/
System.out.println(".....");
return 2;
}finally{
return 3;
}
//return return 4;
}
}
public class ExceptionDemo4 {
public static void main(String[] args) {
Demo4 d = new Demo4();
int x = d.show(5);
System.out.println("over..... x = " + x);
}
}
try-finally组合:这个组合,其实并没有真正的去捕获异常。因此这种组合不能解决编译时期的异常问题。这种组合的出现仅仅是为了保证程序中的某些代码不管有没有异常,都一定会被执行的。
try-catch-catch-catch...... {} finally{}一个try对应多个catch代码块
当我们的try中的可能发生异常的代码中存在很多个异常的时候,需要根据每个异常给出不同的处理方案,这时就需要书写多个catch,将不同的异常分别捕获到,在catch中书写具体的每个异常的处理办法。
3.6.2、方法复写中的异常问题
如果子类复写父类的方法:
1、如果父类的方法上没有使用throws声明异常,子类复写完的方法上也不能使用throws关键字声明异常。
2、如果父类上使用throws声明了异常,这时子类复写完之后可以不声明异常。
3、如果父类上使用throws声明了多个异常,子类可以使用throws声明多个异常中的某几个。
4、如果父类上使用throws声明了异常,子类复写的方法上可以使用throws声明当前父类声明的这个异常的子类异常。