精华内容
下载资源
问答
  • 三大特性 封装:把数据包装起来,装到盒子里 继承 多态 先有对象后有,对象是具体事务。 创建对象: 使用new关键字创建对象。 构造器 快捷键:alt+insert public +类名{} 一个什么都不写也会给一个,自动...

    面向过程

    线性思维

    第一步做什么,第二步做什么

    属性加方法变成一个类

    面向对象

    分类的思维模式,需要先分类

    类是一个模板,对象是一个具体的实例

    适合处理发杂的问题

    本质:以类方式组织代码,以对象的形式封装数据

    三大特性

    • 封装:把数据包装起来,装到盒子里

    • 继承

    • 多态

    先有对象后有类,对象是具体事务。

    创建对象:

    使用new关键字创建对象。

    构造器

    快捷键:alt+insert

    public +类名{}

    一个类什么都不写也会给一个,自动加一个构造器

    作用:使用new关键字必须要有构造器

    用来初始化值:有参构造器()

    注意点:

    定义有参构造后,再使用无参构造,必须明确的定义一个无参构造

    封装

    追求:高内聚,低偶和

    即private的使用

    属性私有,get/set

    作用:

    • 提高安全性

    • 隐藏代码细节,设置年龄先支

    • 统一的接口

    继承

    类的抽象,字类时父类的拓展

    子类可以继承父类的方法(注意权限修饰符)

    类只有单继承,一个儿子只能有一个爸爸,一个爸爸可以有多个儿子

    语法:extands:拓展

    快捷键:ctrl+h //显示出继承关系

    在JAVA中所有的类都是Object类

    image-20210131180004669

    Super

    先调用父类的无参构造,若自己构造父类的构造器,必须要把super放在第一行

    注意点:

    • 调用父类的构造方法
    • 必须只能出现中子类的方法或者构造方法中
    • super跟this不能同时调用构造方法
    • this本身调用的对象,代表父类对象的引用

    方法的重写跟多态

    并不是重载。

    非静态的方法是重写

    重写的关键词也只能是public

    方法的名字之类的全都是一样的。

    都写都是方法的重写,跟属性无关。

    方法的调用只跟左边有关,跟右边定义的类型无关

    image-20210131182543954

    这是两个静态方法的重写

    image-20210131182654356

    这是两个普通方法的重写,子类重写了父类的方法

    多态

    有一个比较需要注意的东西

    Student student = new Student();
    Person person = new Student();
    Object object=new Student();
    

    对象能执行哪些方法主要看左边。

    instanceof和类型转换

    判断是不是那个类型

    A instanceof B:A是对象 B是类型,继承关系也行,A必须是父类对象。

    类型转换:父类代表高的,子类代表低的。

    Person stdent=new Student();
    // 此时不能使用Student中的方法
    

    如果要使用student中的方法,要进行强制转换

    例子:

    (子类)父类.方法

    (Student)Person.show();

    注意:

    • 父类引用指向子类的对象
    • 子类转换为父类为向上转型
    • 父类转换为子类,向下转型

    Static

    静态的变量,一般在多线程中使用

    静态方法不能去调用非静态方法,因为在加载类方法的时候,非静态方法还没有被调用

    静态代码块比构造方法的加载时间还要早一些

    静态代码块,只执行一次

    静态导入包:用于导入包是直接导入方法,以方便直接使用该方法

    抽象类

    使用abstract来修饰类名

    接口可以多继承

    抽象方法:只有方法的约束,只有方法名字,不需要写方法体,在别人继承的时候必须实现。

    正常类继承抽象类以后必须实现他的抽象方法

    特点:

    • 不能new,也就是不能实现,只能用子类进行

    接口

    面向接口编程,接口的本质是锲约

    接口的实现语法:interface 不用class

    子类的定义通过:implements

    接口里边的所有定义都是public,而且是抽象的

    接口里边定义的是静态的常量

    接口的一些特点:

    • 约束
    • 定义一些方法,让不同的人实现
    • 不能被实例化
    • 可以实现多个
    • 都是public

    内部类

    成员内部类

    通过外部类实例化内部类

    Outer.Inner =outer.new.Inner()

    内部类可以访问内部类的私有方法

    局部内we部类

    在方法中行成内部类

    匿名内部类

    不定义内部前边的名字 只用new

    new A();

    异常

    Exception

    提出异常处理框架

    程序运行中,出现了不期而至的各种情况

    1. 检查性异常
    2. 运行时异常
    3. 错误ERROR

    image-20210131203957146

    运行时异常

    Error跟Exception

    Error JVM直接给关闭了

    异常的抛出跟抓取

    try 尝试着去处理,监控区域,必须得要

    catch抓取,可以捕获多个异常

    finally走不走都要,处理关闭工作

    throw:主动抛出异常,一般在方法里边用

    throws:throws是方法可能抛出异常的声明,有多个

    假设要捕获多个异常要从小到大的去捕获

    自动生成捕获代码块快捷建 :ctrl+alt+t //选择代码块以后

    image-20210131205936896


    image-20210131204917337

    image-20210131204942642

    捕获异常的实例

    image-20210131205333435

    自定义异常

    1. 自定义异常类
    2. 在方法中用throws

    这是第一步:自定义异常类

    image-20210131212322400

    展开全文
  • 记录一个咸鱼学生个月的奋进生活008复习Java(异常处理)异常分类常见的异常及其含义异常处理方法(try、catch、throw、throws、finally)异常对象的常用方法学习Java面试题(List和Set的区别,Array和ArrayList...

    复习Java(异常处理)

    不寻常的事就是异常,并不只是错误,异常处理是Java语言健壮性的体现。

    异常分类

    这时候就可以去我之前的帖子:复习Java封装继承&Java异常抛出面试题&生活【记录一个咸鱼大学生三个月的奋进生活】004 回顾一下Error和Exception的区别

    接下来我从生活中再举个例子对应一下Java中的异常处理,更方便大家理解

    生活中异常可以分为:灾难(不能处理的) 能处理的 异常。
    这就对应了Java的Throwable类 中的: Error(不能解决的系统错误) Exception(能解决的)异常。

    其中 能解决的异常 又可以分为:错误(必须要处理的) 小毛病(可处理可不处理的)
    这又对应了 Exception 中的:checked(必须要处理的) unchecked(依据需求选择处理的)

    常见的异常及其含义

    RuntimeException —— java.lang包中多数异常的基类

    ArithmeticException —— 算术错误,如除以 0

    IllegalArgumentException —— 方法收到非法参数

    ArrayIndexOutOfBoundsException —— 数组下标出界

    NullPointerException —— 试图访问 null 对象引用

    SecurityException —— 试图违反安全性

    ClassNotFoundException —— 不能加载请求的类

    AWTException —— AWT中的异常

    IOException —— I/O异常的根类

    FileNotFoundException —— 不能找到文件

    EOFException —— 文件结束

    IllegalAccessException —— 对类的访问被拒绝

    NoSuchMethodException —— 请求的方法不存在

    InterruptedException —— 线程中断

    异常处理方法(try、catch、throw、throws、finally)

    处理方法:

    1、要么在相关的方法中提供所需代码来直接处理抛出的异常。(catch)
    2、要么根本不去理会它,在抛出异常代码的方法把这个异常向上抛出给该方法的调用代码。(throw)

    try、catch的用法:

    	try {
    		可能发生异常的语句;
    	} catch (异常原因 e){
    		发生异常之后的处理代码;
    	}
    
    	// 例子:
    	try {
    		System.out.println(57 / 0);
    	} catch (Exception e){
    		System.out.println("除数为0 错了!");
    	}
    

    throw、throws的用法:

    即使你在一个catch中处理了异常,但是在许多情况下,调用的程序需要知道这种异常发生的情况。

    如果你需要把自己捕获的异常传递给调用的程序时可以使用throw语句在catch块中把它再次抛出,此时该方法的声明部分添加throws和异常类型。

    	// 例子:
    	public void test() throws Exception{
    		try {
    			System.out.println(57 / 0);
    		} catch (Exception e){
    			throw e;
    		}
    	}
    

    finally的用法:

    无论是否出现异常,finally块都将运行,确保了在出现异常时所有清除工作都将得到处理,与 try 块一起使用。

    异常对象的常用方法

    用于打印异常信息的方法:

    toString() —— 返回异常的信息

    getMessage() —— 返回消息的内容,说明当前的异常

    printStackTrace() —— 此方法把消息和栈跟踪记录输出到标准输出流,对于控制台程序,这个输出流就是屏幕

    学习Java面试题(List和Set的区别,Array和ArrayList的区别)

    还是指路→Java经典面试题 陈哈哈大佬的原帖地址

    List和Set的区别

    Array和ArrayList的区别

    健身

    今天看着卡路里消耗不咋样但是汗如雨下,舒服了舒服了

    照片分享

    摄于广州,当时找角度找了很久






    2021.06.08  by wyh

    展开全文
  • 【十二】Java异常

    2020-03-07 14:53:28
    思维导图参考:【十二】Java异常思维导图 一、异常的概念 概念: 程序在运行过程中出现的特殊情况 异常处理的必要性: 任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序...

    所有知识体系文章,GitHub已收录,欢迎Star!再次感谢,愿你早日进入大厂!

    GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual

    搜索关注微信公众号“码出Offer”,送你学习福利资源!


    目录


    一、异常的概念
    二、异常的分类
    三、异常的产生
    四、异常的传递
    五、异常的处理
    六、自定义异常(方法覆盖详解)
    七、自定义异常实例(详解)


    思维导图参考:【十二】Java异常思维导图


    一、异常的概念

    • 概念: 程序在运行过程中出现的特殊情况
    • 异常处理的必要性: 任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失

    二、异常的分类

    • Throwable: 可抛出的,一切错误或异常的父类,位于java.lang包下
    • Error: JVM、硬件、执行逻辑错误,不能手动处理
    • Exception: 程序在运行和配置中产生的问题,可处理
      • RuntimeException: 运行时异常,可处理,可不处理
      • CheckedException: 受查异常,必须处理

    三、异常的产生

    • 自动抛出异常: 当程序在运行时遇到不符合规范的代码或结果时,会产生异常
    • 手动抛出异常: 语法:throw new 异常类型(“实际参数”);
    • 产生异常结果: 相当于遇到return语句,导致程序因异常而终止

    四、异常的传递

    • 异常的传递: 按照方法的调用链反向传递,始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)
    • 受查异常: throws 声明异常,修饰在方法参数列表的后端
    • 运行时异常: 因可处理可不处理,无需声明异常

    五、异常的处理

    	try {
    		//可能出现异常的代码
    	} catch (Exception e) {
    		//异常处理的相关代码,如:getMessage()、printStackTrace()
    	} finally {
    		//无论是否异常,都需执行的代码结构,常用于释放资源
    	}
    	
    
    • printStackTrace()
      • 打印堆栈跟踪信息
    • getMessage()
      • 返回错误详细信息(没有跟踪信息只有错误原因)

    常见异常结果

    	//处理异常方式支持以下几种写法
    	
    	try {} catch {}
    
    	try {} catch {} catch {}
    
    	try {} catch {} finally {}
    
    	try {} catch {} catch {} finally {}
    	
    	try {} finally {}
    

    注意:多重catch,遵循从子(小)到父(大)的顺序,父类异常在最后。否则会出现编译错误


    六、自定义异常

    • 需继承自Exception(受查异常)或Exception的子类,常用RuntimeException(运行时异常)
    • 必要提供的构造方法:
      • 无参数构造方法
      • String message参数的构造方法
    • System.err.println(); (了解即可)
      • System.out指的是标准输出
      • System.err则指的是错误输出,如果你用LOG4J记录日志的话,且设定错误等级的话,System.err的输出是将记录到日志中
      • 使用err打印出的字符串,在eclipse的console会显示成红色的(以示警告异常结果),如下打印结果图
      • 这个错误输出,在自定义异常小实例里会用到
        在这里插入图片描述

    方法覆盖

    • 带有异常声明的方法覆盖:
      • 方法名、参数列表、返回值类型必须和父类相同
      • 子类的访问修饰符符合父类向相同或是比父类更宽
      • 子类中的方法,不能抛出比父类更宽泛的异常

    方法覆盖的小例子

    import java.io.IOException;
    import java.sql.SQLException;
    /** 
    * @author Ziph
    * @date 2020年3月10日
    * @Email mylifes1110@163.com
    */
    public class TestOverrideExceptionMethod {
    	public static void main(String[] args) {
    		Super sup = new Sub();// 父类引用指向子类对象 多态
    		try {
    			sup.method();// 在编译期间,调用的父类中声明的方法是有异常的,需要处理
    		} catch (Exception e) {
    			e.printStackTrace();// 处理方案之1:打印堆栈跟踪信息
    		}
    
    	}
    }
    
    // 带有异常的方法覆盖
    // 1.父类中方法声明了异常。子类重写后可声明也可不声明
    // 2.父类中方法没有声明异常,则子类也不可以声明异常。
    // 3.父类中方法声明了异常,子类可以声明的异常与其相等或是其子类
    // 4.子类可以声明比父类更多的异常。必须小于其父类声明的异常(子类)
    class Super {
    	public void method() throws Exception {
    		System.out.println("method in  Super");
    	}
    }
    
    class Sub extends Super {
    	public void method() throws ClassNotFoundException, RuntimeException, IOException, SQLException {
    		System.out.println("method in  Sub");
    	}
    }
    
    // 带有异常的接口方法也是与父类方法覆盖的原理相同,这里我就不再一一重复了
    interface Printable {
    	public void print() throws Exception;
    }
    
    class MyClass implements Printable {
    	public void print() throws ClassNotFoundException, RuntimeException {
    
    	}
    }
    

    七、自定义异常实例

    import java.util.InputMismatchException;
    /** 
    * @author Ziph
    * @date 2020年3月10日
    * @Email mylifes1110@163.com
    */
    public class TestDefinedException {
    	public static void main(String[] args) {
    		Student stu = new Student();
    		try {
    			stu.setAge(250);//是可能出现异常的代码
    		}catch(Exception e) {
    			System.err.println(e.getMessage());//只获得报错的原因即可
    		} 
    		
    		try {
    			stu.setSex("未知");//受查异常,编译期间就报错,需要处理
    		}catch(SexMismatchException se) {//根据方法声明的异常,捕获相应的类型
    			System.err.println(se.getMessage());
    		}catch(Exception e ) {
    			e.printStackTrace();
    		}
    		
    		
    //		Class.forName("xxx.xxx");//参数(包名.类名)   可能写错!
    	}
    }
    
    //受查异常(是在编译期间,就必须处理的异常! 需要声明出去)
    class SexMismatchException extends Exception{
    	public SexMismatchException() {}
    	public SexMismatchException(String message) {
    		super(message);
    	}
    }
    
    //运行时异常
    class AgeInputException extends RuntimeException{
    	
    	public AgeInputException() {}//支持创建无异常原因信息的异常对象
    	public AgeInputException(String message) {//提供有参构造方法,支持编写异常原因信息
    		super(message);//调用父类的有参构造方法,为message属性赋值。
    	}
    }
    //在应用场景下,可以根据自身的需要,自定义异常。
    class Student{
    	private int age;//年龄
    	private String sex;//性别  男  女
    	
    	public void setSex(String sex) throws SexMismatchException {
    	//告知调用者,使用该方法,会存在异常。必须处理.声明的异常类型最好与抛出的异常类型一致
    		if(sex.equals("男") || sex.equals("女")) {
    			this.sex = sex;
    		}else {
    			//在用户输入一个性别后!就做好提醒!性别的输入可能不准确!   受查异常。
    			throw new SexMismatchException("性别输入的值为:“男”或者“女”");
    		}
    	}
    	public String getSex() {
    		return this.sex;
    	}
    	
    	
    	public int getAge() {
    		return this.age;
    	}
    	public void setAge(int age){
    		if(age > 0 && age < 123) {
    			this.age = age;
    		}else {
    			throw new AgeInputException("年龄的赋值应该在0岁到123岁之间");
    			//抛运行时异常的父类。 不合理。现存的定义好的异常,没有符合现在程序的场景
    		}
    	}
    }
    

    在这里插入图片描述

    展开全文
  • 异常分类 死缠烂打的finally! 自定义异常类 IO流 IO流的亲戚——File 常用方法 IO流基本概念 基本分类 IO家族 文件访问流 FileWriter 常用方法 FileReader 常用方法 FileOutputStream、FileInputStream 文件...
    版本 说明 发布日期
    1.0 发布文章第一版 2020-12-04
    1.1 【死缠烂打的finally!】小节中新增一个更复杂的例子 2021-02-03
    新增小节【关于read的返回值为什么是int】
    【文件访问流】的常用方法中新增以File为参数的构造方法

    前言

    • 这篇文章是我个人的学习笔记,可能无法做到面面俱到,也可能会有各种纰漏。如果任何疑惑的地方,欢迎一起讨论~
    • 如果想完整阅读这个系列的文章,欢迎关注我的专栏《Java基础系列文章》~
    • 哦对了!请不要吝啬->点赞、关注、收藏~

    异常机制

    基本概念

    • 异常机制可以说是大家日常接触最多的概念之一了,所以就只说重点了。
    • 所有异常类的超类时Exception,位于java.lang.Exception。
    • 异常类Exception继承自java.lang.Throwable,同样继承Throwable的还有Error。
      • Error通常描述JVM无法解决的严重错误,无法通过编码解决。例如JVM崩溃了。
      • Exception可以通过编码解决。
    • 异常的三种处理方式:
      • 通过条件语句,避免异常的发生;
      • try/catch/finally语句处理发生的异常;
      • throw抛出异常。

    异常的分类

    • java.lang.Exception的子类:
      • RuntimeException:运行时异常,也叫非检测异常。编译阶段无法检测出来是否会发生此类异常。主要子类:
        • ArithmeticException:算术异常
        • ArrayIndexOutOfBoundsException:数组下标越界异常
        • NullPointerException:空指针异常
        • ClassCastException:类型转换异常
        • NumberFormatException:数字格式异常
      • IOException和其他异常:检测性异常,编译阶段能够被编译器(或者说现在通常都能被IDE检测)检测出来的异常。

    死缠烂打的finally!

    先来个简单的

    • 大家都知道finally是无论有没有异常都会执行的,但是你真的知道这家伙有多么死缠烂打么?比如下面这个例子,小伙伴们觉得结果是什么?
    public class ExceptionTest {
        public static void main(String[] args) {
            System.out.println(finallyTest());
        }
    
        private static String finallyTest(){
            String a = "aAa";
            try{
                System.out.println(1);
                int b = 1/0;
                System.out.println(2);
            }catch (ArithmeticException e){
                System.out.println(3);
                return a = a.toUpperCase();
            }finally {
                System.out.println(4);
                return a = a.substring(1);
            }
        }
    }
    
    • 结果如下。怎么样,有没有感受到finally的死缠烂打?
      • catch中return准备返回了,甚至a = a.toUpperCase()都执行完了,然后finally突然吼了一嗓子:桥豆麻袋!
      • 然后finally里面正常执行,甚至还return了。
      • 只留下catch里面的return,在原地傻了~
    1
    3
    4
    AA
    

    再来个小学二年级难度的

    • 下面这个执行结果是多少?
    public class ExceptionTest {
        public static void main(String[] args) {
            System.out.println(finallyTest());
        }
    
        private static int finallyTest(){
            int x = 1;
            try {
                return ++x;
            } catch (Exception e) {
            } finally {
                ++x;
            }
            return x;
        }
    }
    
    • 答案是2。我知道大多数人都答错了哈哈哈。下面来解释一下。
      1. 执行return ++x;,++x已经执行完了此时x为2,但是发现有finally,于是跑去执行finally;
      2. 执行finally,x变为3。因为finally并没有返回,所以执行完之后,又跑回去执行return ++x;
      3. 注意了,return返回的是表达式的运算结果,而不是x的值。而return ++x;的结果在步骤1已经算出来了,是2!所以返回的结果是2,而不是3。
      4. 可能还是有小伙伴十脸懵逼。我觉得可以如下理解。也就是说,return的表达式的值算出来了,固定了,不会再改变了。
      public class ExceptionTest {
          public static void main(String[] args) {
              System.out.println(finallyTest());
          }
      
          private static int finallyTest(){
              int x = 1, y;
              try {
                  return y = ++x;
              } catch (Exception e) {
              } finally {
                  ++x;
              }
              return x;
          }
      }
      

    自定义异常类

    • 对于Java官方没有提供的异常类,但业务场景需要对此类异常进行区分,就需要小伙伴们自己编写。
    • 自定义异常很简单,只需要以下步骤:
      • 继承Exception类或其子类;
      • 编写一个无参构造和带有String的有参构造。其中String是用来记录错误描述的。
      • 两个构造方法体中只需要调用父类的对应构造方法即可。
      • 定义序列化版本号serialVersionUID。至于这玩意儿是什么东西,等讲IO的时候再说~
    • 举例如下:
    public class CustomException extends Exception {
        static final long serialVersionUID = -3387516993124229948L;
    
        public CustomException(){
            super();
        }
    
        public CustomException(String message){
            super(message);
        }
    
        public static void main(String[] args) {
            try{
                throw new CustomException("抛出去啦~");
            } catch (CustomException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 运行结果如下。
    com.UsefulNativeClass.Exception.CustomException: 抛出去啦~
    	at com.UsefulNativeClass.Exception.CustomException.main(CustomException.java:16)
    

    IO流

    IO流的亲戚——File类

    • 位于java.io.File。主要用于描述文件或目录路径的特征信息, 如:大小、文件名等。
    • 流相关的类其实在实例化的时候都是用到了File类的。

    常用方法

    方法声明 功能
    File(String pathname) 根据指定的路径构造文件或者目录对象
    File(String parent, String child) 根据指定的父路径和子路径构造文件或者目录对象
    File(File parent, String child) 根据指定的父文件路径和子路径构造文件或者目录对象
    boolean exists() 判断此路径表示的文件或目录是否存在
    String getName() 获取文件或者目录的名称
    long length() 返回文件的长度,单位是byte
    long lastModified() 获取文件的最后一次修改时间。是long类型的时间戳
    String getAbsolutePath() 获取绝对路径
    boolean delete() 删除文件或目录,当删除目录时要求是空目录。删除成功则返回true。
    boolean createNewFile() 创建新的空文件。创建成功则返回true。
    boolean mkdir() 用于创建单级目录。当创建d:/A/B时,如果A不存在,则创建失败。
    boolean mkdirs() 用于创建多级目录当创建d:/A/B时,如果A不存在,则A和B一起创建。
    File[] listFiles() 获取该目录下的所有文件和目录。如果调用对象是文件,则会返回null。
    boolean isFile() 判断是否为文件。是文件则返回true。
    boolean isDirectory() 判断是否为目录。是目录则返回true。
    File[] listFiles(FileFilter filter) 获取目录下满足筛选器的所有内容。FileFilter是一个接口类,定义了抽象方法accept,该方法要求想要的文件返回true,想被过滤的文件返回false。
    • 这些方法的使用还是说得比较清楚的,下面就实现一个遍历某目录下所有目录和java文件的例子吧:
    public class FileTest {
        //实现过滤器,只寻找目录和.java文件
        private final FileFilter fileFilter = pathname -> {
            if(pathname.isFile()){
                return pathname.getName().endsWith(".java");
            }
            return true;
        };
    
        public static void main(String[] args) {
            FileTest fileTest = new FileTest();
            fileTest.listAllFile(new File("C:\\Users\\米\\Desktop\\模块四 Java核心类库(下)\\01任务一 异常机制和File类"));
        }
    
        private void listAllFile(File file){
            if(file.isFile()){
                return;
            }
            
            File[] listFile = file.listFiles(fileFilter);
            for (File f:listFile){
                if(f.isFile()){
                    System.out.println(f.getName());
                }else if(f.isDirectory()){
                    System.out.println("[" + f.getName() + "]");
                    listAllFile(f);
                }
            }
        }
    }
    
    • 运行结果如下。当然每个人电脑里面的文件不一样,所以执行结果也没啥参考价值。
    [01_课件]
    [02_图片]
    [04_代码]
    AgeException.java
    ExceptionCatchTest.java
    ExceptionFinallyTest.java
    ExceptionMethod.java
    ExceptionPreventTest.java
    ExceptionTest.java
    ExceptionThrowsTest.java
    FileTest.java
    Person.java
    PersonTest.java
    SubExceptionMethod.java
    

    IO流基本概念

    • 上面以迅雷不及掩耳盗铃之势讲完了异常机制,然后顺道还擦边讲了个File。接下来才是这篇文章的主角——IO stream。
    • IO相关类都位于java.io包。
    • 为什么叫IO流呢?首先IO是in和out的意思,这个大家应该都知道,至于stream,可能java的coder们觉得数据能有几多愁,恰似一江春水向东流吧~
    • 不开玩笑了,其实流实例化的过程,就像是给水桶中插了根水管;对流进行读入、写出的过程,就像是给水桶抽水、加水的过程;刷新流,就像是把水管中残留的水分给清干净;关闭流,就像是把水管从水桶中抽出来。

    基本分类

    • 按照读写数据的基本单位不同,分为字节流和字符流。
      • 字节流:主要指以字节为单位进行数据读写的流,可以读写任意类型的文件。
      • 字符流:主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本(字符)文件。
    • 按照读写数据的方向不同,分为输入流和输出流。
      • 输入流:主要指从文件中读取数据内容输入到程序中,也就是读文件。
      • 输出流:主要指将程序中的数据内容输出到文件中,也就是写文件。
    • 按照流的角色不同分为节点流和处理流。
      • 节点流:主要指和输入输出源直接对接的流。
      • 处理流:主要指建立在节点流的基础之上的流。处理流与文件是间接关联的。

    IO大家族

    分类 字节输入流 字节输岀流 字符输入流 字符输岀流
    抽象基类 InputStream OutputStream Reader Writer
    访问文件 FileInputStream FileOutputStream FileReader FileWriter
    访问数组 ByteArrayInputStream ByteArrayOutpuStream CharArrayReader CharArrayWriter
    访问管道 PipedlnputStream PipedOutputStream PipedReader PipedWriter
    访问字符串 StringReader StringWriter
    缓冲流 BufferedlnputStream BufferedOutputStream BufferedReader BufferedWriter
    转换流 InputStreamReader OutputStreamWriter
    对象流 ObjectInputStream ObjectOutputStream
    过滤流 FilterInputStream FiIterOutputStream FilterReader FilterWriter
    打印流 PrintStream PrintWriter
    推回输入流 PushbackinputStream PushbackReader
    特殊流 DatalnputStream DataOutputStream

    文件访问流

    FileWriter

    • 主要用于对文件进行数据写入。

    常用方法

    • 如果构造流对象的时候,文件不存在,则会自动创建对应文件。
    方法声明 功能
    FileWriter(String fileName) 根据参数指定的路径构造对象。实例化之后,会清空文件原有数据。
    FileWriter(File file) 根据参数指定的文件构造对象。实例化之后,会清空文件原有数据。
    FileWriter(String fileName, boolean append) 根据参数指定的路径来构造对象。传入true时,表示不清空原有数据,写出数据追加在末尾。
    void write(int c) 写入单个字符。如果传入的是整数,则代表的是Unicode值。
    void write(char[] cbuf, int off, int len) 将指定字符数组,从下标off开始的,len个字符写入文件。
    void write(char[] cbuf) 将指定字符数组写入文件。
    void flush() 刷新流。因为各种机制的原因,write操作并不一定会立即写入数据。而刷新的作用就是将目前打算写入,但还未写入的数据,全部立即写入。
    void close() 关闭流对象并释放有关的资源。关闭流自带刷新流的功能。
    • 老规矩,栗子来了:
    public class FileWriterTest {
        public static void main(String[] args) {
            FileWriterTest test = new FileWriterTest();
            test.baseUse();
        }
    
        private void baseUse(){
            FileWriter fileWriter = null;
            try {
                fileWriter = new FileWriter("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\1.txt");
                fileWriter.write('a');
                String str = "啊哦一";
                fileWriter.write(str, 1, 2);
                System.out.println("执行成功");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fileWriter != null){
                    try {
                        fileWriter.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            try {
                fileWriter = new FileWriter("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\1.txt", true);
                fileWriter.write('a');
                String str = "啊哦一";
                fileWriter.write(str, 1, 2);
                System.out.println("执行成功");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fileWriter != null){
                    try {
                        fileWriter.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    • 文件内容如下。为了方便演示,创建了两次流操作。可以看到,不管代码执行多少次,文件里面的内容始终都是一样的。因为第一次的流操作,会清空文件数据。
    a哦一a哦一
    

    FileReader

    • 主要用于对文件内容进行读操作。

    常用方法

    方法声明 功能
    FileReader(String fileName) 根据参数指定路径构造对象。
    FileReader(File file) 根据参数指定文件构造对象。
    int read() 读取单个字符的数据并返回,返回-1表示一个字符都没读到。
    int read(char[] cbuf, int offset, int length) 将最多length个字符的数据读入一个字符数组中,从下标位置offset开始放(注意不要下标越界)。返回读取到的字符个数,返回-1表示一个字符都没读到。
    int read(char[] cbuf) 将最多cbuf.length个字符的数据读入字符数组中。返回读取到的字符个数,返回-1表示一个字符都没读到。
    void close() 关闭流对象并释放有关的资源
    • 栗子来啦:
    public class FileReaderTest {
        public static void main(String[] args) {
            FileReaderTest test = new FileReaderTest();
            test.baseUse();
        }
    
        private void baseUse(){
            FileReader fr = null;
            try {
                fr = new FileReader("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\1.txt");
                int res;
                while((res = fr.read()) != -1){
                    System.out.println("读取的内容是:" + (char)res);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(null != fr){
                    try {
                        fr.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            try {
                fr = new FileReader("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\1.txt");
                int res;
                char[] chars = new char[3];
                res = fr.read(chars, 1, 2);
                System.out.println("读取到了:"+ res + "个字符,读取结果是:" + Arrays.toString(chars));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(null != fr){
                    try {
                        fr.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    • 结果如下。如果第二段栗子中,读取的个数改为3,则会有下标越界异常。
    读取的内容是:a
    读取的内容是:哦
    读取的内容是:一
    读取的内容是:a
    读取的内容是:哦
    读取的内容是:一
    读取到了:2个字符,读取结果是:[ , a, 哦]
    

    关于read的返回值为什么是int

    • 我们知道,Java中一个char固定占2个字节,因为其用Unicode来编码的。但为什么返回值要用int呢?int可是得用4个字节呢。这就涉及到一个设计思想的问题了。
      • 假设我们只用char来作为返回值(2个字节),那么我们怎么表示没有读到任何数据呢?没办法表示吧?
      • 所以Java就用int来作为返回值,用-1表示没有读取到任何值。而正常读取到的字符对应的二进制,只会用到低16位,并不会和-1(1111 1111 1111 1111)冲突。
      • 下面要讲的字节流,也是同样的道理~是不是觉得Java的设计师们很聪明呢?

    FileOutputStream、FileInputStream

    • 上面介绍的是字符流,操作文本(字符)文件妥妥的。但如果操作的不是文本文件,那么用字符流就会导致文件不正常或者损坏。这个时候就需要我们的字节流上场了。
    • 常用方法和字符流几乎一样,只是之前的char[]变成了byte[]。
    • FileInputStream多了一个比较常用的方法:int available()。用于获取文件的大小(单位为字节)。

    文件拷贝案例

    • 既然都说了字节流和字符流的使用基本一样,那我就不费口舌来介绍怎么使用字节流了。我们直接来玩一个有意思的东西——文件拷贝。
    第一种方式
    public class FileCopy {
        public static void main(String[] args) {
            FileCopy test = new FileCopy();
            test.fileCopyFirst("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background.jpg",
                    "D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background-back.jpg");
        }
    
        private void fileCopyFirst(String srcPath, String destPath) {
            OutputStream out = null;
            InputStream in = null;
            try {
                out = new FileOutputStream(destPath);
                in = new FileInputStream(srcPath);
    
                System.out.println("文件大小:" + in.available());
    
                //记个时
                long beginTime = System.currentTimeMillis();
                System.out.println("开始copy");
    
                int result;
                while (-1 != (result = in.read())) {
                    out.write(result);
                }
    
                long endTime = System.currentTimeMillis();
                System.out.println("结束copy");
                System.out.println("耗时:" + (endTime - beginTime));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != out) {
                        out.close();
                    }
                    if (null != in) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 这种方式运行结果如下。大家可以感受到一个问题。对于一个2MB的图片,拷贝过程需要将近10秒,这是一件非常夸张的事情。
    文件大小:2153023
    开始copy
    结束copy
    耗时:16107
    
    • 为什么会这样呢?因为每一次执行read或者write的时候,其实JVM是与计算机底层硬件做了很多交互的,这个过程开支比较大。如果一个字节就进行一次read和write,总共需要多少次?amazing!
    • 这个感觉就像什么呢?老妈让你去买50个鸡蛋,然后你去菜市场,每次就买1个鸡蛋回家。然后来回跑了50次!amazing!
    第二种方式
    • 你吸取教训了,这次打算一次性把所有需要的鸡蛋给买回去,于是代码变成了这样:
    public class FileCopy {
        public static void main(String[] args) {
            FileCopy test = new FileCopy();
            test.fileCopyFirst("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background.jpg",
                    "D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background-back.jpg");
        }
    
        private void fileCopySecond(String srcPath, String destPath) {
            OutputStream out = null;
            InputStream in = null;
            try {
                out = new FileOutputStream(destPath);
                in = new FileInputStream(srcPath);
    
                //记个时
                long beginTime = System.currentTimeMillis();
    
                //读取数据至缓存
                System.out.println("开始读数据");
                byte[] buffer = new byte[in.available()];
                int result = in.read(buffer);
                System.out.println("实际读取的字节数:" + result);
    
                //从缓存写出数据
                System.out.println("开始写数据");
                out.write(buffer);
    
                long endTime = System.currentTimeMillis();
                System.out.println("耗时:" + (endTime - beginTime));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != out) {
                        out.close();
                    }
                    if (null != in) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 运行结果如下。我去,这也太快了。然后你又觉得你自己行了,于是决定:以后不管多少个鸡蛋,我就一次性捞回去完事儿~
    开始读数据
    实际读取的字节数:2153023
    开始写数据
    耗时:30
    
    • 结果小区里面的大爷大妈都觉得你行了,于是都找你帮忙买鸡蛋。于是,你一天需要买一万个鸡蛋。然后你试着一次性买回去一万个鸡蛋…你又发现你不行了。
    第三种方式
    • 你痛并思痛,想到为什么我不能每次就买50个呢?虽然需要多跑几次,但总比被直接压死强是吧!于是代码变成了这样:
    public class FileCopy {
        public static void main(String[] args) {
            FileCopy test = new FileCopy();
            test.fileCopyThird("D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background.jpg",
                    "D:\\WorkSpace\\Work\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background-back.jpg");
        }
    
        private void fileCopyThird(String srcPath, String destPath) {
            OutputStream out = null;
            InputStream in = null;
            try {
                out = new FileOutputStream(destPath);
                in = new FileInputStream(srcPath);
    
                //记个时
                long beginTime = System.currentTimeMillis();
    
                //读取数据至缓存
                System.out.println("开始拷贝数据");
                byte[] buffer = new byte[102400];
                int result;
                //只要还没有读到文件末尾,就一直循环
                while((result = in.read(buffer)) != -1){
                    out.write(buffer, 0, result);//注意,因为最后一次写入,可能并没有放满buffer,所以,每一次放的时候,应该是buffer有多少,就放多少
                    System.out.println("实际读取的字节数:" + result);
                }
    
                long endTime = System.currentTimeMillis();
                System.out.println("耗时:" + (endTime - beginTime));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != out) {
                        out.close();
                    }
                    if (null != in) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 代码执行结果如下。你觉得你双行了。好吧,这次确实行了。但是机智的java官方早已看穿一切,于是提供了缓冲流。缓冲流的原理基本就是如此。
    开始拷贝数据
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:2623
    耗时:37
    

    缓冲流

    • 缓冲流提供了一个缓冲区,用于对流数据进行缓冲。就像上面文件拷贝的例子三那样。
    • 缓冲流的使用还是大同小异,但是需要注意一点:缓冲流是处理流,所以其构造方法的入参是InputStream和OutputStream。
    • 关闭缓冲流的时候,与其关联的流也会自动关闭。所以每次操作完成后,close缓冲流即可。

    BufferedOutputStream

    • 常用构造方法
    方法声明 功能
    BufferedOutputStream(OutputStream out) 根据参数指定的输出流来构造对象,默认缓冲大小为8192字节。
    BufferedOutputStream(OutputStream out, int size) 根据参数指定的输出流来构造对象,并手动指定缓冲区大小

    BufferedInputStream

    • 常用构造方法
    方法声明 功能
    BufferedInputStream(InputStream in) 根据参数指定的输入流构造对象,默认缓冲大小为8192字节。
    BufferedInputStream(InputStream in, int size) 根据参数指定的输入流构造对象,并手动指定缓冲区大小

    用缓冲流拷贝文件

    • 还是上面那个例子的文件,我们再用缓冲流来试一试:
    public class BufferTest {
        public static void main(String[] args) {
            BufferTest test = new BufferTest();
            test.fileCopyThird("E:\\Gitee repository\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background.jpg",
                    "E:\\Gitee repository\\java-practice\\JavaPractice\\src\\com\\UsefulNativeClass\\IO\\FileIO\\background-back.jpg");
        }
    
        private void fileCopyThird(String srcPath, String destPath) {
            BufferedOutputStream out = null;
            BufferedInputStream in = null;
            try {
                out = new BufferedOutputStream(new FileOutputStream(destPath), 102400);
                in = new BufferedInputStream(new FileInputStream(srcPath), 102400);
    
                System.out.println("文件大小:" + in.available());
    
                //记个时
                long beginTime = System.currentTimeMillis();
    
                //读取数据至缓存
                System.out.println("开始拷贝数据");
                byte[] buffer = new byte[102400];
                int result;
                //只要还没有读到文件末尾,就一直循环
                while ((result = in.read(buffer)) != -1) {
                    out.write(buffer, 0, result);//注意,因为最后一次写入,可能并没有放满buffer,所以,每一次放的时候,应该是buffer有多少,就放多少
                    System.out.println("实际读取的字节数:" + result);
                }
                System.out.println("实际读取的字节数:" + result);
    
                long endTime = System.currentTimeMillis();
                System.out.println("耗时:" + (endTime - beginTime));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != out) {
                        out.close();
                    }
                    if (null != in) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 运行结果如下。你大爷还是你大爷,果然牛逼是吧~注意啦,可以看到虽然我用了缓冲流,但我还是使用的第三种方式拷贝,即自己手动写了个缓冲。实践证明这样的效率是最高的。如果我一次性只读一个字节,即使使用缓冲流,耗时大概会在90左右。
    文件大小:2153023
    开始拷贝数据
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:102400
    实际读取的字节数:2623
    实际读取的字节数:-1
    耗时:9
    

    BufferedWriter、BufferedReader

    • 字符流依然有缓冲流,同样也是效率很高的一种流。
    • BufferedWriter常用方法:
    方法声明 功能
    BufferedWriter(Writer out) 根据参数指定的输出流来构造对象。默认缓冲大小为8192。
    BufferedWriter(Writer out, int sz) 根据参数指定的输出流来构造对象。并指定缓冲大小。
    void write(int c) 写出单个字符
    void write(char[] cbuf, int off, int len) 将字符数组cbuf中,从下标off开始的,len个字符写出
    void write(char[] cbuf) 将字符数组cbuf的所有内容写出
    void write(String s, int off, int len) 将字符串s中,下标从off开始的,len个字符写出
    void write(String str) 将字符串str的所有内容写出
    void newLine() 写出行分隔符。对于WINDOWS而言,即写出"\r\n"
    void flush() 刷新流
    void close() 关闭流对象并释放有关的资源
    • BufferedReader常用方法:
    方法声明 功能
    BufferedReader(Reader in) 根据参数指定的输入流来构造对象。默认缓冲大小为8192.
    BufferedReader(Reader in, int sz) 根据参数指定的输入流来构造对象。并指定缓冲大小。
    int read() 从输入流读取单个字符并返回。如果读取到末尾则返回-1。
    int read(char[] cbuf, int off, int len) 从输入流中读取len个字符,放入数组cbuf中,放入的位置从下标off开始。返回实际读取到的字符个数。如果读到末尾,则返回-1
    int read(char[] cbuf) 从输入流中读满整个数组cbuf。返回实际读取到的字符个数。如果读到末尾,则返回-1
    String readLine() 读取一行字符串并返回,返回null表示读取到末尾
    void close() 关闭流对象并释放有关的资源

    打印流——PrintStream、PrintWriter

    • 打印流也是一个处理流。
    • 这个流其实小伙伴们都非常非常非常非常非常非常熟悉,为什么呢?因为我们天天在用的System.out的这个out,其实就是一个PrintStream。哈哈哈!意不意外?
    • 只是说System的这个out呢,都是向控制台写出数据。但实际上,PrintStream是一个处理流。所以可以关联OutputStream,从而向其他文件写数据。
    • PrintStream常用方法如下:
    方法声明 功能
    PrintStream(OutputStream out) 根据参数指定的输出流来构造对象。
    void print(String s) 输出字符串内容。
    void println(String x) 输出字符串内容,并追加换行符。
    void flush() 刷新流
    void close() 用于关闭输出流并释放有关的资源
    • PrintWriter除了构造方法入参是字符输出流,其他的方法基本一致。

    转换流——OutputStreanWriter、InputStreamReader

    • 用于将字节流转换为字符流。是一种处理流。
    • OutputStreamWriter的主要方法如下:
    方法声明 功能
    OutputStreamWriter(OutputStream out) 根据参数指定的字节流来构造对象
    OutputStreamWriter(OutputStream out, String charsetName) 根据参数指定的字节流和编码构造对象
    void write(String str) 将参数指定的字符串写入
    void flush() 刷新流
    void close() 用于关闭输出流并释放有关的资源
    • 可以看到,依然大同小异,只是构造方法的参数是字节流。输出流也是同理,就不列举啦~

    举栗子:模拟聊天日志功能

    • 我们可以通过缓冲字节流、打印流、转换流,来实现一个聊天日志的功能。而聊天的内容通过控制台输入。输入bye表示聊天结束,结束程序。
    • 代码如下:
    public class ChatRoom {
        private void chatLog() {
            BufferedReader reader = null;
            PrintWriter writer = null;
            try {
                //首先,System.in是一个获取键盘输入的标准方法,但是这个in呢,它是一个字节输入流。
                //然后呢,我们要获取的是字符,所以理所应当用缓冲字符输入流。
                //所以,我们需要在二者之间加一个隔壁老王——转换流
                reader = new BufferedReader(new InputStreamReader(System.in));
                //因为聊天一般都是一个换行符作为一段话,所以正好可以用打印流
                writer = new PrintWriter(new FileWriter("D:\\logs.txt", true));
    
                //用于判断该谁说话勒
                boolean flag = false;
    
                String content;
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyy-MM-dd hh:mm:ss");
                while (true) {
                    System.out.println(flag ? "张三说:" : "李四说:");
                    content = reader.readLine();
                    if ("bye".equals(content)) {
                        writer.println("聊天结束");
                        System.out.println("聊天结束");
                        break;
                    } else {
                        writer.println(formatter.format(LocalDateTime.now()) + (flag ? " 张三说:" : " 李四说:") + content);
                        flag = !flag;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != reader) {
                        reader.close();
                    }
                    if (null != writer) {
                        writer.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            ChatRoom chatRoom = new ChatRoom();
            chatRoom.chatLog();
        }
    }
    
    • 控制台内容如下:
    李四说:
    hello, little jade
    张三说:
    who?
    李四说:
    sorry
    张三说:
    bye
    聊天结束
    
    • 文件中内容如下:
    2020-12-02 10:19:24 李四说:hello, little jade
    2020-12-02 10:19:28 张三说:who?
    2020-12-02 10:19:33 李四说:sorry
    聊天结束
    
    • 这个栗子中,因为始终是对聊天内容的处理,所以最外层都是选择字符流进行操作。而控制台输入是一个字节流,因此用到了转换流。
    • 通常情况下,不论是输出还是输入,都应当使用一个缓冲流来提高效率。
    • 而这个栗子中,为什么输出还套了个打印流呢?因为println这个方法真香,哈哈哈~

    数据流——DataOutputStream、DataInputStream

    • 用于对基本数据类型进行读写。是一个处理流。
    • DataOutputStream常用方法:
    方法声明 功能
    DataOutputStream(OutputStream out) 根据参数指定的输出流构造对象。
    void writeInt(int v) 将一个整数一次性写出。同理,所有基本类型都有一个对应的write方法
    void close() 用于关闭文件输出流并释放有关的资源。
    • DataInputStream
    方法声明 功能
    DataInputStream(InputStream in) 根据参数输入流来构造对象。
    int readInt() 一次性读取一个整数数据。同理,别的类型也有
    void close() 用于关闭文件输出流并释放有关的资源
    • 数据流虽然看起来很简单,但是有一个坑,第一次接触的小伙伴那是前仆后继地往里跳。下面来填一下:
    public class DataIOTest {
        public static void main(String[] args) throws IOException {
            try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/data.txt"));
                 DataInputStream dis = new DataInputStream(new FileInputStream("d:/data.txt"))) {
    
                int num = 69;//0000 0000 0000 0000 0000 0000 0100 0011
                dos.writeInt(num);
                System.out.println("数据写入成功");
    
                System.out.println(dis.readInt());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 运行结果如下。风平浪静,波澜不惊。但是再去看一下txt文件,小伙伴们可能会有点懵
    数据写入成功
    69
    
    • data.txt内容如下。这是什么意思呀?其实道理很简单:
      • 我们知道int是4个字节,所以69对应的二进制编码如上面代码里面的注释所示。
      • 但是我们把文件的类型设置为了txt,也就是说当成文本文件来看了。
      • 当我们打开文件的时候,将二进制编码以默认编码UTF-8进行文本解析。
      • 解析结果就是:空字符(0000 0000)、空字符(0000 0000)、空字符(0000 0000)、E(0100 0011)。
      • 注意空字符和空格不是一个意思哈,虽然看起来是一样的。
       E
    
    • 数据流还有一个小坑:
      • 如果文件里面就只有1个字节的内容。
      • 此时我们readInt(),那么就会发生EOFException(文件末尾异常)。
      • 这个异常有点数组下标越界异常内味儿,但还是不一样的哈。

    对象流——ObjectOutputStream、ObjectInputStream

    • 将对象作为一个整体,进行读写操作。是一个处理流。
    • ObjectOutputStream常用方法:
    方法声明 功能
    ObjectOutputStream(OutputStream out) 根据参数指定的输出流来构造对象
    void writeObject(Object obj) 将一个对象整体写出
    void close() 用于关闭输出流并释放有关的资源
    • ObjectInputStream常用方法:
    方法声明 功能
    ObjectInputStream(InputStream in) 根据参数指定的输入流来构造对象。
    Object readObject() 读取一个对象。无法通过返回值来判断是否读取到文件的末尾。
    void close() 用于关闭输入流并释放有关的资源。
    • 可以看到,对象流的写出和读入方法,都是对对象进行操作的。并且,操作的对象必须要启用序列化,否则会抛出NotSerializableException(不可序列化异常)。
    • 因为readObject()无法判断是否读取到文件末尾,所以我个人建议一个序列化文件中就存放一个对象。
      • 那如果有一个文件中存放多个对象的需求怎么办呢?很简单啦,可以采用集合存储,然后序列化集合对象喽~

    说到对象流,就得说一说序列化

    what is 序列化?

    • 所谓序列化,是指将一个对象需要存储的相关信息通过一定规则,有效地组织成字节序列的过程。
    • 而反序列化,是指将有效组织的字节序列,按照一定规则,恢复成对象的过程。
    • 而实现序列化的方式很简单,只需要实现Serializable接口即可,不需要重写任何方法。因为其实所有类已经是可以序列化的,但默认情况都未启用。只有当实现了接口,才会告诉JVM:这个类启用序列化。

    序列化版本号

    • 为了在一定程度保证安全性,序列化机制通过serialVersionUID来验证版本一致性的。
    • 在进行反序列化时,JVM会把传来的流中的serialVersionUID与本地相应类的serialVersionUID进行比较。
      • 如果相同就认为是一致的,可以进行反序列化。
      • 如果不一致,就会抛出InvalidCastException(序列化版本不一致)异常。

    transient

    • 是Java语言的关键字。当给属性加上该关键字后,该属性将不会参与序列化。

    啃一块栗子

    public class Person implements Serializable {
        private static final long serialVersionUID = 13412341324L;
        private String name;
        private transient String gender;
    
        public Person(String name, String gender) {
            this.name = name;
            this.gender = gender;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", gender='" + gender + '\'' +
                    '}';
        }
    
        public static void main(String[] args) {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/se.txt"));
                 ObjectInputStream ois = new ObjectInputStream((new FileInputStream("d:/se.txt")))) {
                Person person = new Person("angel", "糙汉子");
                oos.writeObject(person);
                System.out.println("写出成功");
    
                Object newPer = ois.readObject();
                System.out.println(newPer);
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 运行结果如下。这个栗子有几点说一下:
      • 文件内容就是一堆乱码,因为不是按照字符写的文件嘛。
      • 可以看到,序列化主要是用于将类中的属性写入文件。所以序列化主要适用于Bean。
      • 序列化版本号其实不写也不会报错。但是上面说了,出于安全性考虑,建议添加序列化版本号。
      • readObject的返回值其实用到了多态。虽然返回值是Object,但是实例依然是对应的类的实例。
      • 这个栗子中,我把gender用了transient关键字修饰。可以发现gender没有序列化,所以是String的默认值null。
    写出成功
    Person{name='angel', gender='null'}
    

    RandomAccessFile

    基本概念

    • 这个类支持对文件的随机读写。
      • 这里所谓的随机,是指在文件中读写的位置是任意的。而不是像通常的流,固定从开头往后读。

    常用方法

    方法声明 功能
    RandomAccessFile(String name, String mode) 根据参数指定的路径和模式构造对象。如果文件不存在则报错。
    RandomAccessFile(File file, String mode) 根据参数指定的File对象和模式构造对象。
    int read() 读取单个字节的数据
    void seek(long pos) 将读写位置设置成距文件开头pos个字节的位置
    void write(int b) 写出单个字节的数据,写出的数据覆盖当前位置的数据。
    void close() 用于关闭流并释放有关的资源
    • 构造方法中的这个模式啊,简单来说有几种:
      • r:以只读方式打开;
      • rw:以读写方式打开;
      • rwd:以读写方式打开,且同步文件内容的更新;
      • rws:以读写方式打开,且同步文件内容和元数据的更新。
    • 什么叫同步呢?意思是每进行一次写操作,就会将内容及时写入磁盘。
      • 这样做的好处是当系统发生崩溃时,已经调用write()方法的内容不会丢失。
      • 坏处也很明显,就是降低了写出的速度。
      • 而rwd和rws有什么区别呢?目前我只知道rws需要同步的东西更多,速度更慢。具体什么是元数据,我还没有研究。
    • 这个类还有很多东西比较复杂,但是平时工作中基本不会用到,所以就不展开讲了。

    栗子↓

    public class RandomAccessTest {
        public static void main(String[] args) {
            //这个类不会自动创建文件,记得先用别的方式创建一个
            try (RandomAccessFile file = new RandomAccessFile("d:/ra.txt", "rw")) {
                //为了测试方便,先向文件中写一点内容。
                //建议不要写中文,这个类的写方法对非ASCII码的字符没有太好的支持
                file.writeBytes("what are you doing?");
    
                //然后我们来读“doing”这个单词。
                //注意,seek偏移的是字节量。而一个ASCII码字符是1个字节。
                //所以可以算出,“doing”在文件中,需要偏移13个字节
                file.seek(13);
                System.out.println(file.readLine());
    
                //然后再来测试一下把you改成she。
                //不要忘了这个类的写方法是覆盖的。
                file.seek(9);
                file.writeBytes("she");
    
                //展示一下结果
                file.seek(0);
                System.out.println(file.readLine());
    
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    • 执行结果如下。重要的信息我都写在代码注释里面了,小伙伴们可以自行欣赏~~~~
    doing?
    what are she doing?
    

    彩蛋

    • 刚刚发文章的时候,看到CSDN系统给我发了个段子哈哈哈。分享一下:
    • 论Java变量赋值的鄙视链
      论Java变量赋值的鄙视链
    展开全文
  • Java异常的捕获及处理

    多人点赞 2021-04-26 11:14:43
    Java异常的捕获及处理一、Java异常简介二、处理异常捕获异常异常处理流程抛出异常种类型的异常Java异常分类四、自定义异常 一、Java异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序...
  • 第七章 异 常 本模块讲述建立在 Java 编程语言中的错误处理装置 第一...catch 和 finally 语句 - 描述异常分类 - 开发程序来处理自己的异常 第节 异 常 异 常 异常定义程序所遇到的轻微错误 发生下列情况时会出现异
  • PAGE PAGE 97 第七章 异 常 本模块讲述建立在Java编程语言中的错误...catch和finally语句 描述异常分类 开发程序来处理自己的异常 第节 异 常 异 常 异常定义程序所遇到的轻微错误 发生下列情况时会出现异常 想打
  • Java中的异常

    2021-04-30 17:18:56
    1.什么是异常     写模块,用户输入不一定符合要求,程序要求开某个文件,这个文件不存在,或格式不对,程序运行时内存满了     异常的英文是Exception     异常是指程序运行中出现...种类型异常 ...
  • Java中的异常处理

    2020-04-25 22:51:13
    异常发生的原因有很多,通常包含以下几大类: 用户输入了非法数据。 要打开的文件不存在。 网络通信时连接中断,或者JVM内存溢出。 这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为...
  • 者设计理念不一样,体现了java设计者对不同异常情况的分类 error表示发生了意料之外的错误,部分情况下,会使得程序处于不可恢复的状态,不需要也不应该被捕获,比如OutOfMemoryError StackOverFlowError ...
  • 抽象和接口 抽象的规则及应用 接口的规则及应用 默认方法、静态方法、函数式接口、Lambda表达式 异常 异常的定义 异常的处理:抓(try-catch-finally)、抛(throws) 异常分类、自定义异常的应用、throw关键字 ...
  • 面向对象三大特性 抽象和接口 内部及OOP实战 初始面向对象 面向过程 & 面向对象 面向过程思想 步骤清晰简单,第一步做什么,第二步做什么… 面对过程适合处理一些较为简单的问题 面向对象思想 ...
  • 异常分类: (1)运行时异常(非受检性异常),可处理,可不处理:   编译器不要求我们处理的异常,只有在运行的时候才会发生的异常,主要原因是编写程序的时候不够严谨,是需要避免的异常。 (2)检查时...
  • 异常

    2021-05-08 13:56:29
    Java API中定义了许多异常类,异常类分为两大类,错误error,异常Exception Java异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类 检查性异常 运行时异常 错误(error)一般和...
  • java面试题集锦()

    千次阅读 2016-08-01 22:55:14
    (35):Java中的异常分类  Java中的异常分为三大类:Error/Runtime Exception(运行时异常)/普通异常  这三类异常的类继承结构是  java.lang.Throwable  java.lang.Error  java.lang.Exception
  • 1.面向对象三大特征 封装 继承 多态 2.的定义 [修饰符列表] class 类名 { // 体 = 属性 + 方法 } 3.变量的分类 方法体当中声明的变量:局部变量 方法体外声明的变量:成员变量 成员变量又被称为实例...
  • 面向对象三大特性 抽象和接口 内部及OOP实战 初始面向对象 面向过程 & 面向对象 面向过程思想 步骤清晰简单,第一步做什么,第二步做什么… 面对过程适合处理一些较为简单的问题 面向对象思想 ...
  • 开心一笑【今天压力特别,一直感觉有一种无形...解决问题以下来自《Effective Java》这本书的笔记:在这里复习下异常分类java中异常分为两:checked exception(检查异常)和unchecked exception(未检查异常),对于未
  • JavaSE_第十章:异常

    2021-04-05 15:00:52
    第八章:三大特性 第九章:三个修饰符 第十章:接口 第十一章:常用 第十二章:集合 第十三章:异常 1 异常 1.1 异常 概念:程序在运行过程中出现的特殊情况. 1.2 异常的必要性 任何程序都可能存在大量的未知问题、...
  • java 入门、简介

    2019-06-26 16:08:47
    文章目录Java 和 C/C++ 的区别Java 程序的分类Java 程序结构命名规范三种注释风格一些 java 语法字符串类java.util.Scanner 的使用:数组for 循环的使用向量面向对象三大特性和它的属性、方法异常处理错误异常输入...
  • 面向对象三大特性5.抽象与接口6.内部7.异常机制 面向对象编程 1.何谓面向对象 面向过程编程(线下思维) 清楚知道每一步做什么,但仅适合处理较为简单的问题 面向对象编程(分类思维) 采用分类的思维模式,先...
  • 零基础Java入门课程

    2020-12-06 18:54:17
    抽象和接口 抽象的规则及应用 接口的规则及应用 默认方法、静态方法、函数式接口、Lambda表达式 异常 异常的定义 异常的处理:抓(try-catch-finally)、抛(throws) 异常分类、自定义异常的应用、throw关键字 ...
  • Java基础(部分)

    2021-05-03 22:21:44
    文章目录Java基础面向对象和面向过程的区别Java语言的特点Java和C++对比面向对象三大特性构造器关键字String包装Object方法接口和抽象类Java异常机制反射泛型枚举深浅拷贝IO流分类 面向对象和面向过程的区别 ...
  • 疯狂JAVA讲义

    2014-10-17 13:35:01
    10.2.2 异常类的继承体系 360 10.2.3 访问异常信息 363 10.2.4 使用finally回收资源 364 10.2.5 异常处理的嵌套 367 10.3 Checked异常和Runtime异常体系 367 10.3.1 使用throws声明抛出异常 367 10.4 使用...
  • JAVA基础课程讲义

    2017-08-30 23:39:14
    面向对象编程的语言的三大特征简介 56 对象和的概念 56 和对象初步 57 测试的定义方式 57 简单的学生编写示例 58 内存分析 59 属性(field,或者叫成员变量) 59 引用类型 60 的方法 60 对象的创建和使用 ...
  • 第四章:异常处理

    2020-11-14 21:14:57
    章:集合03 一:什么是异常,异常的作用是什么?? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期...二:java异常分类 异常的根接口Throwable,其下有2个子接口,Error和Exception。 Error:指
  • java复习-7

    2021-01-24 16:14:22
    三大特性:封装,继承,多态 和对象的创建 的定义: class 名称{ 属性名称; 返回值类型 方法名称(){} } 使用new关键字创建对象 new 关键字表示创建一个对象 new 关键字表示实例化对象 new 关键字表示申请...
  • 抽象和接口 抽象的规则及应用 接口的规则及应用 默认方法、静态方法、函数式接口、Lambda表达式 异常 异常的定义 异常的处理:抓(try-catch-finally)、抛(throws) 异常分类、自定义异常的应用、throw关键字 ...
  • java面试题

    2018-01-01 15:35:15
    Java 软件工程师面试资料整合 1 Java 面霸 1 1. int 和 Integer 有什么区别? 8 2. String 和StringBuffer的区别 8 3. 运行时异常与一般异常有何异同? 8 4. 说出ArrayList,Vector,LinkedList的存储性能和特性 8 5...
  • Java虚拟机规范

    2012-11-13 11:21:20
    它是一份保证各个公司的Java虚拟机实现具备统一外部接口的契约文档,书中的概念和细节描述曾经与Sun的早期虚拟机的实现高度吻合,但是随着技术的发展,高性能虚拟机真正的细节实现方式已经渐渐与虚拟机规范所描述的...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 141
精华内容 56
关键字:

java异常分类三大类

java 订阅