10--File类&字节流&异常

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. 删除
  3. 判断
  4. 获取
  5. 改名
  6. 列举

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. 递归调用不能无限制的调用。递归中一定要有判断,能够保证递归最终可以停止调用。

递归的次数不能太多,太多也会死掉。

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、切割文件

将一个较大的文件切割成多个碎片文件。

切割的方式:

  1. 固定每个碎片的大小。
  2. 固定碎片文件的个数。
/*
 * 演示切割文件
 */
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);
	}
}

异常类的书写步骤:

  1. 让当前类继承Exception或RuntimeException
  2. 在类中提供构造函数(提供空参数构造函数,和可以接收一个字符串的构造函数)。

以后如果开发项目需要自定义异常:

         包名:公司域名.项目或模块名.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声明当前父类声明的这个异常的子类异常。

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QB哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值