一、 异常的概述和分类
异常就是Java程序在运行过程中出现的错误。
Throwable:异常的最顶层
Error:服务器宕机,数据库崩溃等
Exception: RuntimeException
1.JVM默认是如何处理异常的
main函数收到这个问题时,有两种处理方式:自己将该问题处理,然后继续运行;自己没有针对的处理方式,只有交给调用main的jvm来处理;
jvm有一个默认的异常处理机制,将该异常进行处理.并将该异常的名称,异常的信息。异常出现的位置打印在了控制台上,同时将程序停止运行
2.try...catch方式1
try…catch…finally
throws
3.try...catch方式2
try...catch的方式处理多个异常,即try后面有多个catch,每个catch不同的错误,一般用Exception就行。JDK7之后,用“|”就可以并列多个异常。
4.finally
finally用于释放资源,在IO流操作和数据库操作中会见到,被finally控制的语句体一定会执行,特殊情况:在执行到finally之前jvm退出了(比如System.exit(0)),如果catch里面有return语句, finally的代码还会执行,但是最好不要在里面写return语句,否则会覆盖掉其他的return。
5.final,finally和finalize
final可以修饰类,不能被继承,修饰方法,不能被重写,修饰变量,只能赋值一次。
finally是try语句中的一个语句体,不能单独使用,用来释放资源。
finalize是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
6.编译期异常和运行期异常的区别
Java中的异常被分为两大类:编译时异常和运行时异常。
所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常。编译时异常,程序可能不会出错,但是Java为了防止错误出现,在编译时候就会提示程序员必须要对这里进行处理。运行时异常,无需显示处理,也可以和编译时异常一样处理。
二、Throwable
getMessage()获取异常信息,返回字符串。
toString()获取异常类名和异常信息,返回字符串。
printStackTrace()获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
1.throws处理异常
定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在方法上标识。在方法中如果throw一个runtimeexception,不需要在方法上再throws。
2.throw和throws
throw在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。throws表示抛出异常,由该方法的调用者来处理,用在方法声明后面,跟的是异常类名,可以跟多个异常类名,用逗号隔开。
3.自定义异常概述和基本使用
继承自Exception
继承自RuntimeException
在实际用的时候调用此异常即可
class AgeOutOfBoundsException extends Exception {
public AgeOutOfBoundsException() {
super();
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
4.异常的注意事项
子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)。
如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常。
如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws。
5.练习
键盘录入一个int类型的整数,对其求二进制表现形式,如果录入的整数过大,给予提示,录入的整数过大请重新录入一个整数BigInteger,如果录入的是小数,给予提示,录入的是小数,请重新录入一个整数,如果录入的是其他字符,给予提示,录入的是非法字符,请重新录入一个整数。
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
while(true) {
String line = sc.nextLine(); //将键盘录入的结果存储在line中
try {
int num = Integer.parseInt(line); //将字符串转换为整数
System.out.println(Integer.toBinaryString(num));//将整数转换为二进制
break; //跳出循环
}catch(Exception e) {
try {
new BigInteger(line);
System.out.println("录入错误,您录入的是一个过大整数,请重新输入一个整数:");
}catch (Exception e2) { //alt + shif + z (try catch快捷键)
try {
new BigDecimal(line);
System.out.println("录入错误,您录入的是一个小数,请重新输入一个整数:");
} catch (Exception e1) {
System.out.println("录入错误,您录入的是非法字符,请重新输入一个整数:");
}
}
}
}
}
}
二、File
File更应该叫做一个路径,文件路径或者文件夹路径。
File(String pathname):根据一个路径得到File对象
File(String parent, String child):根据一个目录和一个子文件/目录得到File对象,就是两个路径的拼接。
File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象,就是把父级路径封装成一个file,可以调用里面的函数。
1.创建功能
public boolean createNewFile():创建文件 如果存在这样的文件,就不创建了
public boolean mkdir():创建文件夹 如果存在这样的文件夹,就不创建了
public boolean mkdirs():创建文件夹,如果父文件夹不存在,会帮你创建出来
如果你创建文件或者文件夹忘了写盘符路径,那么,默认在项目路径下。
2.重命名和删除功能
public boolean renameTo(File dest):把文件重命名为指定的文件路径,相当于剪切
public boolean delete():删除文件或者文件夹
3.File类的判断功能
public boolean isDirectory():判断是否是目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在
public boolean canRead():判断是否可读,windows系统默认都是可读的
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏
4.获取功能
public String getAbsolutePath():获取绝对路径
public String getPath():获取路径
public String getName():获取名称
public long length():获取长度。字节数
public long lastModified():获取最后一次的修改时间,毫秒值
public String[] list():获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组
5.输出指定目录下指定后缀的文件名
需求:判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
public static void main(String[] args) {
File dir = new File("f:\\");
String[] arr = dir.list(); //获取e盘下所有的文件或文件夹
for (String string : arr) {
if(string.endsWith(".jpg")) {
System.out.println(string);
}
}
或者
File[] subFiles = dir.listFiles(); //获取e盘下所有的文件或文件夹对象
for (File subFile : subFiles) {
if(subFile.isFile() && subFile.getName().endsWith(".jpg")) {
System.out.println(subFile);
}
}
6.文件名称过滤器
public String[] list(FilenameFilter filter):需要重写方法
public File[] listFiles(FileFilter filter):需要重写方法
File dir = new File("f:\\");
String[] arr = dir.list(new FilenameFilter() {
@Override
public boolean accept(File dir,String name) {
File file = new File(dir,name);
return file.isFile() && file.getName().endsWith(".jpg");
}
});
只会返回自定义的能被接受的文件
for (String string : arr) {
System.out.println(string);
}
三、IO流
IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的类都在IO包中,流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。
IO流常用父类
字节流的抽象父类:InputStream、OutputStream
字符流的抽象父类:Reader、Writer
1.FileInputStream
read()一次读取一个字节
FileInputStream fis = new FileInputStream("aaa.txt");
//创建一个文件输入流对象,并关联aaa.txt
int b; //定义变量,记录每次读到的字节
while((b = fis.read()) != -1) { //将每次读到的字节赋值给b并判断是否是-1
System.out.println(b); //打印每一个字节
}
fis.close(); //关闭流释放资源
这里也会将回车符号读到,文件中存的是字符,读进来变成int值,例如a为97。
2.read()方法返回值为什么是int
read()方法读取的是一个字节,为什么返回是int,而不是byte?
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111,那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了,这样可以保证整个数据读完,而结束标记的-1就是int类型。
3.FileOutputStream
write()一次写出一个字节
FileOutputStream fos = new FileOutputStream("bbb.txt"); //如没有bbb.txt,会创建出一个
//fos.write(97); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的一个byte,实际是对应a
fos.write(98);
fos.write(99);
fos.close();
FileOutputStream fos = new FileOutputStream("bbb.txt",true); true表示可以追加
4.IO流拷贝图片
FileInputStream读取
FileOutputStream写出
FileInputStream fis = new FileInputStream("QQ.mp3"); //创建输入流对象,关联QQ.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3");//创建输出流对象,关联copy.mp3
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close(); 弊端:效率太低
5.字节数组拷贝之available()方法
int read(byte[] b):一次读取一个字节数组,具体大小可定义
write(byte[] b):一次写出一个字节数组
available():获取读的文件所有的字节个数,弊端:有可能会内存溢出
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
byte[] arr = new byte[fis.available()]; //根据文件大小做一个字节数组
fis.read(arr); //将文件上的所有字节读取到数组中
fos.write(arr); //将数组中的所有字节一次写到了文件上
fis.close();
fos.close();
6.定义小数组拷贝
write(byte[] b)
write(byte[] b, int off, int len)写出有效的字节个数
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
int len;
byte[] arr = new byte[1024 * 8]; //自定义字节数组
while((len = fis.read(arr)) != -1) {
//fos.write(arr);
fos.write(arr, 0, len); //写出字节数组写出有效个字节个数 ,定义length长度是为了避免将数组后面已经写入的数据又写一遍
}
fis.close();
fos.close();
7.BufferedInputStream和BufferOutputStream拷贝
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流。
BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中读取一个字节时BufferedInputStream会一次性从文件中读取8192个,存在缓冲区中,返回给程序,程序再次读取时, 就不用找文件了,直接从缓冲区中获取,直到缓冲区中所有的都被使用过,才重新从文件中读取8192个。
BufferedOutputStream也内置了一个缓冲区(数组),程序向流中写出字节时,不会直接写到文件,先写到缓冲区中,直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
FileInputStream fis = new FileInputStream("致青春.mp3"); //创建文件输入流对象
BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对fis装饰
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
BufferedOutputStream bos = new BufferedOutputStream(fos); //创建缓冲区对fos装饰
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close(); //只关装饰后的对象即可
bos.close();
8.flush和close方法的区别
flush()方法用来刷新缓冲区的,刷新后可以再次写出。
close()方法用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出。
9.字节流读写中文
字节流在读中文的时候有可能会读到半个中文,造成乱码,可用字符流解决。
字节流写出中文的问题
字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组
写出回车换行write("\r\n".getBytes());因为没有字符串的方法
10.流的标准处理异常代码1.7版本
try(
FileInputStream fis = new FileInputStream("aaa.txt");
FileOutputStream fos = new FileOutputStream("bbb.txt");
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉。
11.图片加密
给图片加密
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"));
int b;
while((b = bis.read()) != -1) {
bos.write(b^123);
}
bis.close();
bos.close();
12.拷贝文件
在控制台录入文件的路径,将文件拷贝到当前项目下
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个文件路径");
String line = sc.nextLine(); //将键盘录入的文件路径存储在line中
File file = new File(line); //封装成File对象
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file.getName());
int len;
byte[] arr = new byte[8192]; //定义缓冲区
while((len = fis.read(arr)) != -1) {
fos.write(arr,0,len);
}
fis.close();
fos.close();
13.录入数据拷贝到文件
将键盘录入的数据拷贝到当前项目下的text.txt文件中,键盘录入数据当遇到quit时就退出
Scanner sc = new Scanner(System.in);
FileOutputStream fos = new FileOutputStream("text.txt");
System.out.println("请输入:");
while(true) {
String line = sc.nextLine();
if("quit".equals(line))
break;
fos.write(line.getBytes());
fos.write("\r\n".getBytes());
}
fos.close();
四、FileReader
字符流是可以直接读写字符的IO流,字符流读取字符,就要先读取到字节数据,然后转为字符。如果要写出字符,需要把字符转为字节再写出。
FileReader类的read()方法可以按照字符大小读取
FileReader fr = new FileReader("aaa.txt"); //创建输入流对象,关联aaa.txt
int ch;
while((ch = fr.read()) != -1) { //将读到的字符赋值给ch
System.out.println((char)ch); //将读到的字符强转后打印
}
fr.close();//关流
1.FileWriter
FileWriter类的write()方法可以自动把字符转为字节写出
2.字符流的拷贝
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
int ch;
while((ch = fr.read()) != -1) {
fw.write(ch);
}
fr.close();
fw.close();
3.什么情况下使用字符流
字符流也可以拷贝文本文件,但不推荐使用。因为读取时会把字节转为字符,写出时还要把字符转回字节。程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流,读取的时候是按照字符的大小读取的,不会出现半个中文,写出的时候可以直接将字符串写出,不用转换为字节数组。
不可以拷贝非纯文本的文件,因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去,如果是?,直接写出,这样写出之后的文件就乱了。
4.自定义字符数组的拷贝
FileReader fr = new FileReader("aaa.txt"); //创建字符输入流,关联aaa.txt
FileWriter fw = new FileWriter("bbb.txt"); //创建字符输出流,关联bbb.txt
int len;
char[] arr = new char[1024*8]; //创建字符数组
while((len = fr.read(arr)) != -1) { //将数据读到字符数组中
fw.write(arr, 0, len); //从字符数组将数据写到文件上
}
fr.close();
fw.close();
5.带缓冲的字符流
BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率。
BufferedWriter的write()方法写出字符时会先写到缓冲区,缓冲区写满时才会写到文件,降低写文件的次数,提高效率。
BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); //创建字符输入流对象,关联aaa.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt")); //创建字符输出流对象,关联bbb.txt
int ch;
while((ch = br.read()) != -1) { //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch
bw.write(ch); //write一次,是将数据装到字符数组,装满后再一起写出去
}
br.close();
bw.close();
6.readLine()和newLine()方法
BufferedReader的readLine()方法可以读取一行字符(不包含换行符号)
BufferedWriter的newLine()可以输出一个跨平台的换行符号"\r\n"
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));
String line;
while((line = br.readLine()) != null) {
bw.write(line);
//bw.write("\r\n"); //只支持windows系统
bw.newLine(); //跨平台的
}
br.close();
bw.close();
7.LineNumberReader
LineNumberReader是BufferedReader的子类, 具有相同的功能,并且可以统计行号
调用getLineNumber()方法可以获取当前行号
调用setLineNumber()方法可以设置当前行号
LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt"));
String line;
lnr.setLineNumber(100); //设置行号
while((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);//获取行号
}
lnr.close();
8.使用指定的码表读写字符
FileReader是使用默认码表读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流,编码表)
FileWriter是使用默认码表写出文件,如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)
BufferedReader br = //高效的用指定的编码表读
new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8"));
BufferedWriter bw = //高效的用指定的编码表写
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK"));
int ch;
while((ch = br.read()) != -1) {
bw.write(ch);
}
br.close();
bw.close();
9.递归读取文件名
public static void printJavaFile(File dir) {
//1,获取到该文件夹路径下的所有的文件和文件夹,存储在File数组中
File[] subFiles = dir.listFiles();
//2,遍历数组,对每一个文件或文件夹做判断
for (File subFile : subFiles) {
//3,如果是文件,并且后缀是.java的,就打印
if(subFile.isFile() && subFile.getName().endsWith(".java")) {
System.out.println(subFile);
//4,如果是文件夹,就递归调用
}else if (subFile.isDirectory()){
printJavaFile(subFile);
}
}
}