异常、File类、I\O流
异常
异常就是Java程序在运行过程中出现的错误。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
从大体来分异常为两块:
1、error---错误 : 是指程序无法处理的错误,表示应用程序运行时出现的重大错误。例如jvm运行时出现的OutOfMemoryError以及Socket编程时出现的端口占用等程序无法处理的错误。
2、Exception --- 异常 :异常可分为运行时异常跟编译异常
1)运行时异常:即RuntimeException及其子类的异常。这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,但是程序员也可以根据需要进行捕获抛出。
2)编译异常:RuntimeException以外的异常。这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。
- 捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
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(异常类型2 异常的变量名2){
// 程序代码
}
- finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
注意下面事项:
1.catch 不能独立于 try 存在。
2.在 try/catch 后面添加 finally 块并非强制性要求的。
3.try 代码后不能既没 catch 块也没 finally 块。
4.try, catch, finally 块之间不能添加任何代码。
- throws/throw 关键字
如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
throws的方式处理异常:
定义功能方法时,需要把出现的问题暴露出来让调用者去处理。
那么就通过throws在方法上标识.
throw的方式处理异常:
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
throws和throw的区别:
1.throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
2.throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
public static void main(String[] args){
try {
str2int("a");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void str2int(String str) throws Exception { //这里将得到的异常向外抛出
try {
System.out.println(Integer.parseInt(str));
} catch(NumberFormatException e) {
//TODO 这里可以做一些处理,处理完成后将异常报出,让外层可以得到异常信息
throw new Exception("格式化异常");
}
}
/*
输出结果
java.lang.Exception: 格式化异常
at Exception.ExceptionDemo.str2int(ExceptionDemo.java:37)
at Exception.ExceptionDemo.main(ExceptionDemo.java:25)
*/
- 声明自定义异常
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws AgeOutOfBoundsException{
if (age <= 150 && age > 0){
this.age = age;
}else {
throw new AgeOutOfBoundsException("年龄非法");
}
}
}
public class ExceptionDemo {
public static void main(String[] args){
customExceptionDemo();
}
private static void customExceptionDemo(){
Person person = new Person();
try {
person.setAge(-1);
} catch (AgeOutOfBoundsException e) {
e.printStackTrace();
}
}
}
class AgeOutOfBoundsException extends Exception{
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
File类
构造方法
File(String pathname):根据一个路径得到File对象
File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象(更强大)创建功能
public boolean createNewFile(): 创建文件 如果存在这样的文件,就不创建了
public boolean mkdir(): 创建文件夹 如果存在这样的文件夹,就不创建了
public boolean mkdirs(): 创建文件夹,如果父文件夹不存在,会帮你创建出来重命名和删除功能
public boolean renameTo(File dest):把文件重命名为指定的文件路径
public boolean delete():删除文件或者文件夹
重命名注意事项:
1.如果路径名相同,就是改名。
2.如果路径名不同,就是改名并剪切。
删除注意事项:
Java中的删除不走回收站。
要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹.判断功能
public boolean isDirectory() 判断是否是目录
public boolean isFile() 判断是否是文件
public boolean exists() 判断是否存在
public boolean canRead() 判断是否可读
public boolean canWrite() 判断是否可写
public boolean isHidden() 判断是否隐藏获取功能
public String getAbsolutePath() 获取绝对路径
public String getPath() 获取路径(构造方法中的路径)
public String getName() 获取名称
public long length() 获取长度。字节数
public long lastModified() 获取最后一次的修改时间,毫秒值
public String[] list() 获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles() 获取指定目录下的所有文件或者文件夹的File数组文件名称过滤器
public String[] list(FilenameFilter filter)
public File[] listFiles(FileFilter filter)
I/O流
IO流用来处理设备之间的数据传输。
Java对数据的操作是通过流的方式。
流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。
IO流常用父类
字节流的抽象父类:
InputStream
OutputStream
字符流的抽象父类:
Reader
Writer
- FileInputStream
read()一次读入一个字节
FileInputStream fileInputStream = new FileInputStream("rr.txt");
int read;
// 每次读一个字节 -1表示结束
while ((read = fileInputStream.read()) != -1){
System.out.println(read);
}
fileInputStream.close(); // 关闭流释放资源
- FileOutputStream
write()一次写出一个字节
// 拷贝方式一 不推荐使用
// 如果t.txt文件不存在,就创建一个,否则,在将原文件清空
// FileOutputStream fileOutputStream = new FileOutputStream("/Users/zhangxiaohan/Desktop/t.txt");
// 如果t.txt文件不存在,就创建一个,否则,在原文件基础上增加内容
FileOutputStream fileOutputStream = new FileOutputStream("/Users/zhangxiaohan/Desktop/t.txt", true);
fileOutputStream.write(65);
fileOutputStream.write(66);
fileOutputStream.write(67);
fileOutputStream.close();
字节数组拷贝之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();
// 第三种拷贝
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, 0, len); //写出字节数组写出有效个字节个数
}
fis.close();
fos.close();
- BufferedInputStream和BufferOutputStream拷贝
BufferedInputStream:
BufferedInputStream内置了一个缓冲区(数组)
从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个,程序再次读取时, 就不用找文件了, 直接从缓冲区中获取,直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个。
BufferedOutputStream:
BufferedOutputStream也内置了一个缓冲区(数组)
程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中,直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
FileInputStream fis = new FileInputStream("致青春.mp3"); //创建文件输入流对象,关联致青春.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();
- flush和close方法的区别
flush()方法:
用来刷新缓冲区的,刷新后可以再次写出
需要实时写出的用flush刷新
close()方法:
用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出 。
字符流
字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
读取的时候是按照字符的大小读取的,不会出现半个中文
写出的时候可以直接将字符串写出,不用转换为字节数组
- FileReader
FileReader类的read()方法可以按照字符大小读取。
FileReader fr = new FileReader("aaa.txt");
int ch;
while((ch = fr.read()) != -1) { //将读到的字符赋值给ch
System.out.println((char)ch); //将读到的字符强转后打印
}
fr.close();
- FileWriter
FileWriter类的write()方法可以自动把字符转为字节写出.
FileWriter fw = new FileWriter("aaa.txt");
fw.write("aaa");
fw.close();
- 字符流的拷贝
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();
- 带缓冲的字符流
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();
- 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();
- LineNumberReader
LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
调用getLineNumber()方法可以获取当前行号
调用setLineNumber()方法可以设置当前行号
LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt"));
lnr.setLineNumber(100); //设置行号,行号从101开始,默认从0开始
while((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);//获取行号
}
lnr.close();
- 装饰设计模式
好处:耦合性不强,被装饰的类的变化与装饰类的变化无关。
interface Coder {
public void code();
}
class Student implements Coder {
@Override
public void code() {
System.out.println("javase");
System.out.println("javaweb");
}
}
class SuperStudent implements Coder {
private Student s; //获取到被包装的类的引用
public ItcastStudent(Student s) { //通过构造函数创建对象的时候,传入被包装的对象
this.s = s;
}
@Override
public void code() { //对其原有功能进行升级
s.code();
System.out.println("数据库");
System.out.println("ssh");
System.out.println(".....");
}
}
- 使用指定的码表读写字符
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();
递归
方法自己调用自己。
构造方法不能使用递归调用。
好处:不用知道循环次数。
弊端:不能调研次数过多,容易导致栈内存溢出。
private static int test(int num){
if (num > 1){
return num * test(num - 1) ;
}else {
return num;
}
}
- 序列流(了解)
序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.
SequenceInputStream(InputStream, InputStream)
FileInputStream fis1 = new FileInputStream("a.txt"); //创建输入流对象,关联a.txt
FileInputStream fis2 = new FileInputStream("b.txt"); //创建输入流对象,关联b.txt
SequenceInputStream sis = new SequenceInputStream(fis1, fis2); //将两个流整合成一个流
FileOutputStream fos = new FileOutputStream("c.txt"); //创建输出流对象,关联c.txt
int b;
while((b = sis.read()) != -1) { //用整合后的读
fos.write(b); //写到指定文件上
}
sis.close();
fos.close();
- 序列流整合多个 (了解)
SequenceInputStream(Enumeration)
FileInputStream fis1 = new FileInputStream("a.txt"); //创建输入流对象,关联a.txt
FileInputStream fis2 = new FileInputStream("b.txt"); //创建输入流对象,关联b.txt
FileInputStream fis3 = new FileInputStream("c.txt"); //创建输入流对象,关联c.txt
Vector v = new Vector<>(); //创建vector集合对象
v.add(fis1); //将流对象添加
v.add(fis2);
v.add(fis3);
Enumeration en = v.elements(); //获取枚举引用
SequenceInputStream sis = new SequenceInputStream(en); //传递给SequenceInputStream构造
FileOutputStream fos = new FileOutputStream("d.txt");
int b;
while((b = sis.read()) != -1) {
fos.write(b);
}
sis.close();
fos.close();
- 内存输出流
该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据。
创建对象: new ByteArrayOutputStream()
写出数据: write(int), write(byte[])
获取数据: toByteArray()
FileInputStream fis = new FileInputStream("a.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
while((b = fis.read()) != -1) {
baos.write(b);
}
//byte[] newArr = baos.toByteArray(); //将内存缓冲区中所有的字节存储在newArr中
//System.out.println(new String(newArr));
System.out.println(baos);
fis.close();
- 对象操作流ObjecOutputStream)(了解)
该流可以将一个对象写出, 或者读取一个对象到程序中. 也就是执行了序列化和反序列化的操作.
// Person
public class Person implements Serializable {
}
public class Demo3_ObjectOutputStream {
/**
* @param args
* @throws IOException
* 将对象写出,序列化
*/
public static void main(String[] args) throws IOException {
Person p1 = new Person("张三", 23);
Person p2 = new Person("李四", 24);
// FileOutputStream fos = new FileOutputStream("e.txt");
// fos.write(p1);
// FileWriter fw = new FileWriter("e.txt");
// fw.write(p1);
//无论是字节输出流,还是字符输出流都不能直接写出对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));//创建对象输出流
oos.writeObject(p1);
oos.writeObject(p2);
oos.close();
}
}
- 对象操作流ObjectInputStream)(了解)
public class Demo3_ObjectInputStream {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
* @throws FileNotFoundException
* 读取对象,反序列化
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();
System.out.println(p1);
System.out.println(p2);
ois.close();
}
}
- 对象操作流优化(了解)
// 将对象存储在集合中写出
Person p1 = new Person("张三", 23);
Person p2 = new Person("李四", 24);
Person p3 = new Person("马哥", 18);
Person p4 = new Person("辉哥", 20);
ArrayList list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f.txt"));
oos.writeObject(list); //写出集合对象
oos.close();
// 读取到的是一个集合对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f.txt"));
ArrayList list = (ArrayList)ois.readObject(); //泛型在运行期会被擦除,索引运行期相当于没有泛型
//想去掉黄色可以加注解 @SuppressWarnings("unchecked")
for (Person person : list) {
System.out.println(person);
}
ois.close();
- 打印流的概述和特点
该流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式。
System.out就是一个PrintStream, 其默认向控制台输出信息。
PrintStream ps = System.out;
ps.println(97); //其实底层用的是Integer.toString(x),将x转换为数字字符串打印
ps.println("xxx");
ps.println(new Person("张三", 23));
Person p = null;
ps.println(p); //如果是null,就返回null,如果不是null,就调用对象的toString()
- 标准输入输出流概述和输出语句
标准输入输出流:
System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据。
System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据。
修改标准输入输出流(了解):
修改输入流: System.setIn(InputStream)
修改输出流: System.setOut(PrintStream)
System.setIn(new FileInputStream("a.txt")); //修改标准输入流
System.setOut(new PrintStream("b.txt")); //修改标准输出流
InputStream in = System.in; //获取标准输入流
PrintStream ps = System.out; //获取标准输出流
int b;
while((b = in.read()) != -1) { //从a.txt上读取数据
ps.write(b); //将数据写到b.txt上
}
in.close();
ps.close();