Java基础笔记

Java笔记

工作流程

image-20200927105038121

数据类型

  1. boolean 包含true或者false(默认值false)(注意:java中boolean的true和false与整数值1和0不等价,不可进行关系比较)
  2. 整数(默认值0)
    • byte 占一个字节,存储有符号整数-128~127,默认值0
    • short 两个字节,-32768~32767(-215 ~ 215 -1),默认值0
    • int 四个字节,约 -2x109 ~ 2x109 (-2^31 ~ 231 -1),默认值0
    • long 八个字节,约 -9x1018 ~ 9x1018 (-263 ~ 263 -1),默认值0L,整数表示在后面加L,如 0L
  3. 浮点型(不能表示很精确的数)
    • float 4字节,默认值0.0f。数量级1038 。数值表示时后面必须加f,如1.12f。
    • double 8字节,默认值0.0d。数量级10308 。数值表示时末尾d可省略
  4. char 字符型,占2字节,默认值'\u0000',用unicode编码表示,范围\u0000~\uffff。用单引号+字符/unicode码 表示,如 '我'、'\u4e00'
  5. 类成员变量有默认值,而函数成员变量是没有默认值的,需要初始化才可使用,否则报错。(所有变量定义时最好都初始化)

分支和循环语句(与C++基本相同)

  1. 分支语句
    • if-else语句
    • switch-case语句(注意:Java中支持case为字符串)
  2. 循环
    • while
    • do-while
    • for循环
    • for-each

函数定义

  1. 只能在类内定义
  2. 一般声明为public,方便外部访问
  3. 函数重载:函数名相同,函数参数表类型、参数数量不同
  4. main函数不是类的成员函数,而是程序入口,不能被其他函数调用(结构PSVM -- public static void main)

命名传统

  1. main函数所在的类名和文件名相同
  2. 类名每个单词首字母大写 → 大驼峰法
  3. 方法和变量名首字母小写,其余单词首字母大写 → 小驼峰法
  4. 常量 (public static final) 一般全字符大写,单词中间以下划线相连

面向对象

简介

  1. 对象 = 属性 + 方法, 对象规范(类) = 属性定义 + 方法定义
  2. 变量的发展:基本类型(一种变量,可继承)⇒ 结构体(多变量捆绑,可包含但无法控制粒度)⇒ 类(变量 + 方法,可继承非private成员)
  3. 由面向过程 OP ⇒ 面向对象OO:OP注重行为的执行,变量是被动地被使用;OO更加注重行为的主体和类的重用
  4. 基本类型赋值是拷贝赋值,对象赋值是reference赋值
  5. 类成员变量有默认值,而函数成员变量是没有默认值的,需要初始化才可使用,否则报错。(所有变量定义时最好都初始化)

构造函数

  1. 构造函数名和类名相同,且没有返回值
  2. 每个类必须有构造函数,如不定义,java自动生成一个无参构造函数;若定义构造函数,则java不会自动生成
  3. 每个变量都有生命周期,其生命周期包含在离它最近的{}内
  4. Java没有析构函数。这是因为Java提供内存自动回收机制,当变量退出其生命周期,JVM自动回收变量的内存。对象回收效率取决于JVM中的垃圾回收器GC(Garbage Collector)
  5. 每个子类的构造函数第一句话,都默认调用父类的无参构造函数super(),除非子类第一句话是super,且super语句必须放在第一条。不会连续出现两条super语句。

信息隐藏

  1. 信息隐藏原则:类的成员属性是private,类的方法是public,其他类无法直接访问private属性,需通过public方法访问
  2. get和set方法是public,称为getter和setter方法。成员属性只能通过get和set访问。可通过IDE自动生成

this指针

  1. 指代本类。 可访问类属性和方法,当不产生混淆时,可省略this(默认形参名称优先级高于成员变量)
  2. 可用于指代本类的构造函数

继承

  1. 子类/派生类(Child/Derived Class)、父类/基类/超类(Father/Base/Super Class)
  2. 子类继承(extends)父类(包括父类的父类,依次类推)所有的属性和方法(但不能直接访问父类的private成员)
  3. 当子类存在和父类同名的属性和方法时(称为重写/覆写/覆盖 override,非overload),子类的属性和方法优先级更高 → 优先调用子类属性/方法
  4. 每个子类的构造函数第一句话,都默认调用父类的无参构造函数super(),除非子类第一句话是super,且super语句必须放在第一条。不会连续出现两条super语句。
  5. Java只支持单根继承,不支持多继承(与C++继承最大的不同)
  6. Java所有的类都继承自java.lang.Object类,Object类里有toString、Equals、clone、hashCode、finalize、getClass等方法

抽象类

  1. 完整的类:所有的方法都被实现(存在 { 方法体 } )
  2. 当一个类的方法没有被全部实现时,该类需要被定义为抽象类,需要对类和未实现的方法添加abstract声明
  3. 子类继承抽象类时,必须实现抽象类的所有方法。否则子类也成为抽象类(需加abstract)
  4. 抽象类也是类,仍遵循单根继承
  5. 抽象类不能被实例化(被new出来)
  6. 抽象类组成:
    • abstract声明
    • (可选)成员变量,个数不限
    • (可选)具体方法,方法有实现,个数不限
    • (可选)抽象方法,加abstract,个数不限
public abstract class Shape{
    private int area;
    public abstract int calArea();
}

接口

  1. 所有的方法都没有实现的类称为接口,需要改成interface声明。接口不能被实例化(new出来)
  2. 接口不是类,或者说是一种特殊的类。子类可以同时实现(implements)多个接口。继承和实现可以同时。
  3. 继承(extends)必须写在实现(implements)之前
  4. 接口可以继承(extends)多个接口(父类逗号隔开),继承父类未实现的所有方法
  5. 类实现接口,就必须实现未实现的所有方法。否则,需声明为抽象类
  6. 接口内可以定义变量,但一般是常量
// Animal.java
public interface Animal {
	public void move();
	public void eat();
}

// LandAnimal.java
public abstract class LandAnimal implements Animal{
	public abstract void eat();
	public void move() {
		System.out.println("I can walk by feet.");
	}
}

// ClimbTree.java
public interface ClimbTree {
	public void climb();
}

// Rabbit.java
public class Rabbit extends LandAnimal implements ClimbTree{
	public void eat() {
		System.out.println("I can eat.");
	}
	public void climb() {
		System.out.println("I can climb tree.");
	}
	public static void main(String args[]) {
		/* ... */
	}
}

抽象类和接口的对比

抽象类 接口
abstract interface
可以有部分方法实现 方法均没有实现
子类只能继承一个抽象类 子类可以实现多个接口,接口可以继承多个接口
有构造函数 无构造函数
有main,能运行 无main
方法可为private和protected 方法均为public

转型、多态和契约设计

  1. 类转型:子类可以类型转换为父类(由大变小,向上转型。因为子类继承了父类所有成员),但父类不能转型为子类(由小变大,向下转型),除非父类对象本身就是子类转型得到
  2. 多态:子类转型为父类后,调用普通方法时,依旧是调用子类方法
  3. 多态的作用
    • 以统一的接口来操纵某一类不同对象的动态行为
    • 对象之间的解耦 👇
  4. 契约设计:类不会直接使用另一个类,而是采用接口的形式,外部可以“空投”这个接口下的任意子类对象(通过多态实现对象之间的解耦)
    • Java编程设计遵循契约设计
    • 契约:规范了对象应该包含的行为方法
    • 接口定义了方法名称、参数和返回值,规范了派生类的行为
    • 基于接口,利用转型和多态,不影响真正方法的调用,实现调用类和被调用类的解耦(decoupling)
/* Animal.java */
public interface Animal{
    public void eat();
    public void move();
}
/* Cat.java */
public class Cat implements Animal{
    public void eat(){
        System.out.println("Cat eat.")j;
    }
    public void move(){
        System.out.println("Cat move.");
    }
}
/* Dog.java */
public class Dog implements Animal{
    public void eat(){
        System.out.println("Dog eat.")j;
    }
    public void move(){
        System.out.println("Dog move.");
    }
}
/* AnimalTest.java */
public class AnimalTest{
    public static void main(String args[]){
        Animal[] A = new Animal[4];
        A[0] = new Cat();
        A[1] = new Dog();
        A[2] = new Cat();
        A[3] = new Dog();
        for(int i=0; i<A.length; ++i) {
            A[i].move();
        }
        haveLunch(new Cat());
        haveLunch(new Dog());
        haveLunch(new Animal{ // 匿名类
            public void eat(){
                System.out.println("Anonymous class eat.");
            }
            public void move(){
                System.out.println("Anonymous class move.");
            }
        })
    }
    public static void haveLunch(Aniaml a){
        a.eat();
    }
}
/* 输出:
Cat move.
Dog move.
Cat move.
Dog move.
Cat eat.
Dog eat.
Anonymous class eat.
*/

static和final

  1. static可修饰变量、方法、类、匿名代码块

    • 修饰变量:变量依赖于类存在,而不依赖于对象示例。不必new出来即可通过类名访问变量。类的所有对象实例共用一个静态变量(内存中只有一份拷贝)
    • 修饰方法:静态方法也可通过类名直接引用。静态方法中不能使用非静态成员(变量/方法),可以使用静态成员(变量/方法)。而非静态方法可以调用静态成员
    • 修饰类(内部类):用的较少
    • 修饰匿名方法块:
      • 直接由花括号{}包围的代码块成员称为匿名代码块
      • 调用顺序:static块 → 普通匿名代码块 → 构造函数
      • 静态static匿名代码块只在第一次加载类时调用,而普通匿名代码块在每次实例化时都会调用
      • 不推荐使用匿名代码块,可将代码块封装成如init函数等,方便管理和调用
  2. final可以修饰类、变量、方法

    • 修饰类:则该类不能被继承
    • 修饰方法:则该方法不能被子类重写override
    • 修饰变量:
      • 基本变量:变量的值不能被改变(即常量)
      • 对象类型:不能修改其指针(但能修改对象内部的值)
  3. 常量设计

    • Java中没有constant关键字
    • 常量不可被修改 → final,内存中只有一份 → static,方便被访问 → public(即public static final)
    • 常量 (public static final) 一般全字符大写,单词中间以连字符相连
    • 接口中的成员变量默认为常量(public static final可缺省)
  4. 常量池

    • Java为基本类型的包装类(不包括Float和Double)和String类型建立了常量池,相同的值只存储一份,以节省内存空间

    • 范围:

      • Boolean → true/false
      • Character → 0~127(\u0000 ~ \u007f)
      • Byte/Short/Integer/Long → -128 ~ 127
      • Float和Double:没有缓存(常量池)
      • Java常量字符串都建立常量池缓存机制
    • 包装类和字符串创建方式

      • 常量(字面量)赋值创建,放在栈内存(将被常量化)
      • new对象创建,放在堆内存(不会常量化)
      • 因此两种创建方式创建的对象地址不同
    • 包装类比较

      • 基本类型和包装类比较、包装类之间进行算术运算,将对包装类自动拆箱
      • 对象比较,比较地址
    • 字符串比较

      • 编译器会优化确定的字符串常量加法并使用常量池缓存。但对于包含new对象、变量之类的不确定字符串,则不优化

        String a = "abc";
        String b = "a" + "b" + "c";
        String c = "a" + new String("bc");
        // a == b ? true
        // a == c ? false
        
  5. 不可变对象(Immutable Object)

    • 典型不可变对象:八种基本类型的包装类、String、BigInteger、BigDecimal等
    • 创建方式:
      • Immutable对象不可改变,要改变,需clone/new一个对象进行修改
      • 所有属性都是private和final
      • 类是final或所有方法为final(避免继承过程中被修改)
      • 类中包含mutable对象,那么返回拷贝需要深度clone
    • 优点
      • 只读,线程安全
      • 并发读,提高性能
      • 可以重复使用
    • 缺点:制造垃圾,浪费空间
    • Java字符串
      • String类是典型的不可变对象,字符串内容比较用equals方法,是否指向同一对象用 ==
      • String类的加法效率低,可利用可变对象StringBuffer/StringBuilder类及其拥有的append方法运算
        • StringBuffer 同步,线程安全,修改快速
        • StringBuilder 不同步,线程不安全,修改极快
        • 修改速度:StringBuilder > StringBuffer > String
import java.util.Calendar;

public class Test {

	public static void main(String[] args) {
		final int n = 50000;
		
		Calendar t1 = Calendar.getInstance();
		String a = "";
		for(int i=0;i<n;++i) {
			a = a + i + ",";
		}
		System.out.println(Calendar.getInstance().getTimeInMillis()-t1.getTimeInMillis());
		
		Calendar t2 = Calendar.getInstance();
		StringBuffer b = new StringBuffer();
		for(int i=0;i<n;++i) {
			b.append(i);
			b.append(",");
		}
		System.out.println(Calendar.getInstance().getTimeInMillis()-t2.getTimeInMillis());
		
		Calendar t3 = Calendar.getInstance();
		StringBuilder c = new StringBuilder();
		for(int i=0;i<n;++i) {
			c.append(i);
			c.append(",");
		}
		System.out.println(Calendar.getInstance().getTimeInMillis()-t3.getTimeInMillis());
	}
}
//2712
//6
//3

单例模式

  1. 限定某一个类在整个程序运行中只能有一个实例对象在内存空间中(即一个类有且只有一个对象)
    • 采用static共享对象实例
    • 采用private构造函数,防止外界new操作
public class Singleton {

	private static Singleton obj = new Singleton();
	private String content;
	
	private Singleton() {
		content = "abc";
	}
	
	public String getContent() {
		return content;
	}


	public void setContent(String content) {
		this.content = content;
	}
	
	public static Singleton getInstance() {
		return obj;
	}
	
	public static void main(String[] args) {
		Singleton obj1 = Singleton.getInstance();
		Singleton obj2 = Singleton.getInstance();
		System.out.println(obj1.getContent() + " " + obj2.getContent());
        //abc abc
        
		obj1.setContent("def");
		System.out.println(obj1.getContent() + " " + obj2.getContent()); //def def
		System.out.println(obj1 == obj2); // true
	}
}

java访问权限

访问权限 同一个类 同一个包 不同包的子类 不同包的非子类
private
(default)
protected
public

建议:成员属性都用private,成员方法都用public

package、import、jar和classpath

package管理

  1. Java支持多目录放置java文件,通过package/import/jar包/classpath等实现跨目录放置和调用java
  2. package类似于C++ 中的namespace,需在文件第一句话给出
  3. java文件要严格放置在声明的package目录下
  4. 包名尽量唯一。常用域名逆序作为包名,如package cn.edu.zju;

import

  1. 若所有java文件都放在同一目录下,则可以不进行显示调用声明
  2. import时,必须采用全称引用(包名+类名,无后缀),程序正文可以用短名称
  3. import必须放在package之后,类定义之前。多个import的顺序无关
  4. 可用*来import目录下的所有类,如import A.B.* (不建议,容易导致同名程序混乱),但是不包括下面的子目录文件(即不能递归包含)

jar

  1. 本质:一组class文件的压缩包(其实存放java文件也是可以的)
  2. 优点:
    • 只有一个文件,便于网络传输
    • 只包含class文件,有利于保护版权
    • 便于版本控制
  3. 可利用Eclipse等IDE工具或jar.exe 打包和导入jar文件

classpath(命令行)

  1. 编译格式:javac -classpath ".;<路径1>;<路径n>" A/B/C.java → 文件全路径
  2. 运行格式:java -classpath ".;<路径1>;<路径n>" A.B.C → 类的全称
  3. 编译时,用javac,classpath包含这个类依赖的类所在路径(以及依赖的类再次依赖的其他类)
  4. 运行时,用java,classpath除了包含编译时的classpath外,还要包括本类所在路径
  5. windows中路径用分号 ; 分隔,linux中用冒号 : 分隔;若路径中不含空格,classpath两侧双引号可省略

Java常用类

  1. Java中的类已达几千个,所以记住每个类是不现实的,只需记住几个常用的类。遇到其他的类可查询Java的API文档
  2. java -- Java核心包, javax -- Java扩展包

数字相关类

  1. BigInteger → 存储任意精度的整数
    • 不能直接用算术符号运算,相应的,用add(n) subtract(n) multiply(n) divide(n) 运算, divideAndRemainder(n)返回数值,包含商和余数
    • max(n) min(n)返回最大值和最小值
    • equals(n)比较数值大小,compareTo(n) 返回比较结果±1或0
  2. BigDecimal → 存储任意精度的浮点数,最好用字符串构造(用浮点数构造存在误差)
    • 不能直接用算术符号运算,相应的,用add(n) subtract(n) multiply(n) divide(n, 保留位数) 运算, divideAndRemainder(n)返回数值,包含商和余数
    • max(n) min(n)返回最大值和最小值
    • equals(n)比较数值大小,compareTo(n) 返回比较结果±1或0
  3. Random类
    • nextInt() 返回随机int, nextInt(n) 返回[0, n) 之间的随机int, nextDouble() 返回[0, 1]之间的浮点数,nextLong等依此类推
    • ints(n, left, right).toArray() 返回包含给定范围[left, right) 范围内(若省略则为int范围内)的整数流,加上toArray()转换为数组
    • Math类中的Math.random()返回[0.0, 1.0] 之间的浮点数
  4. java.lang.Math类
    • abs、sin、cos、pow、round、floor、ceil、PI

字符串类

  1. String类(牢记常用方法) -- 不可变类,调用方法不影响对象本身
String a = "123;456;789;123 ";
System.out.println(a.charAt(0)); // 返回第0个元素
System.out.println(a.indexOf(";")); // 返回第一个;的位置
System.out.println(a.concat(";000")); // 连接一个新字符串并返回,a不变
System.out.println(a.contains("000")); // 判断a是否包含000
System.out.println(a.endsWith("000")); // 判断a是否以000结尾,startsWith类推
System.out.println(a.matches("[a-z]*")); //判断是否符合给定正则表达式
System.out.println(a.equals("000")); // 判断是否等于000
System.out.println(a.equalsIgnoreCase("000")); // 判断在忽略大小写情况下是否等于000
System.out.println(a.compareTo("abc"); //比较字符串大小,返回0或正负值。而compareToIgnoreCase忽略大小写
System.out.println(a.hashCode()); // 返回字符串hash值
System.out.println(a.length()); // 返回a长度
System.out.println(a.trim()); // 返回a去除前后空格后的字符串,a不变
String[] b = a.split(";"); // 将a字符串按照;分割成数组
for (int i = 0; i < b.length; i++) {
    System.out.println(b[i]);
}

System.out.println("===================");

System.out.println(a.substring(2, 5)); // 截取a的第2个到第5个字符(不包括第5), a不变
System.out.println(a.replace("1", "a")); // replace和replaceAll均替换所有符合的字符串
System.out.println(a.replaceAll("1", "a")); // replaceAll第一个参数是正则表达式

System.out.println("===================");

String s1 = "12345?6789";
String s2 = s1.replace("?", "a");
String s3 = s1.replaceAll("[?]", "a");
// 这里的[?] 才表示字符问号,这样才能正常替换。不然在正则中会有特殊的意义就会报异常
System.out.println(s1.replaceAll("[\\d]", "a")); //将s1内所有数字替换为a并输出,s1的值未改变。
  1. StringBuffer/StringBuilder类,可变对象,方法与String基本相同,区别在同步
    • 额外的方法:append/delete/insert/replace/substring
    • length() 字符串实际大小,capacity() 字符串占用空间大小(capacity >= length)
    • trimToSize() 取出空隙,将字符串压缩到实际大小(使capacity等于length)
    • 若有大量append,预估大小,再调用相应构造函数,可以提升性能

时间类

  1. java.util.Date类

    • 基本废弃,主要使用getTime() 获取自1970-01-01年开始的毫秒数
    • 可直接System.out.println(d) 输出
  2. Calendar类 -- 目前主要使用的类(线程不安全)

    • Calendar为抽象类,不能直接new。需通过Calendar.getInstance()获取类(实际上创建的是其子类GregorianCalendar类)
    • get(field) 获取指定时间域,field包括Calendar类内的静态常量,通过Calendar.name调用。常用的有YEAR MONTH(0~11), DAY_OF_MONTH(可用DATE代替), HOUR(12小时制) , HOUR_OF_DAY(24小时制), MINUTE, SECOND, DAY_OF_WEEK(星期,1~7,从星期日开始) 注意月份和星期的特殊性
    • set(field, value) 设置某个时间域的值。set(year, month, date)设置年月日
    • add(field, value) 遵照日期规则增减某个时间域的值
    • roll(field, value) 不遵照日期规则,仅增减某个时间域的值(在该时间域范围内循环,如10月1日中日期减一天变成10月31日)
    • getTime() 返回相应的Date对象
    • getTimeInMillis() 返回1970-01-01以来的毫秒数
  3. java.time包(java 8 新类,线程安全,遵循设计模式)

    • LocalDate类

      • now() 返回当前时间的LocalDate对象, now(ZoneId.of("Asia/Shanghai")) 可加时区
      • of(year, month, date) 返回指定时间的LocalDate对象(month建议用Month类的枚举成员替代,如Month.January)
      • ofEpochDay(day) 返回自纪元日1970-1-1开始后的day天的日期
      • ofYearDay(day) 返回给定年份第day天的日期
    • LocalTime类

      • now() 用法同LocalDate,可加时区
      • of(hour, minute, second, nanoSecond) 创建指定时间
      • ofSecondOfDay(n) 返回一天中的第n秒的时间
    • LocalDateTime类

      • now() 用法同LocalDate,可加时区

      • of(year, month, day, hour, minute, second) 创建指定时间

        of(LocalDate.now(), LocalTime()) 通过LocalDate和LocalTime创建

      • ofEpochSecond(second, nanoSecond, ZoneOffset) 创建自纪元日开始的第几秒的时间

格式化相关类

  1. java.text包

    • NumberFormat 数字格式化,抽象类,通过getInstance()实例化出子类DecimalFormat。具体格式查java API文档

      DecimalFormat df1 = new DecimalFormat("0.##"); // 0代表不可省略,#代表可省就省
      System.out.println(df1.format(0.80)); // output: 0.8, 若输入0.006则输出为0.01
      df1 = new DecimalFormat("#,##0.00"); //每3为用逗号,分隔
      
    • MessageFormat 字符串类格式化

      • 支持模板(参数)和文本(值)对位输出
      • 支持变量自定义格式,如数字、日期等
      String message = "{0}{1}{2}{3}";
      Object[] array = new Object[]{"A","B","C","D"};  
      String value = MessageFormat.format(message, array);  
      System.out.println(value);  
      message = "oh, {0,number,#.##} is a good number";  
      array = new Object[]{13.1415};  
      
    • DateFormat 日期格式化,抽象类,利用getInstance()示例化出子类SimpleDateFormat

      • parse方法:将字符串格式化为日期Date对象
      • format方法:将日期格式化为字符串
      String strDate = "2008-10-19 10:11:30.345" ;  
      // 准备第一个模板,从字符串中提取出日期数字  
      String pat1 = "yyyy-MM-dd HH:mm:ss.SSS";  
      // 准备第二个模板,将提取后的日期数字变为指定的格式  
      String pat2 = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒" ;  
      SimpleDateFormat sdf1 = new SimpleDateFormat(pat1); // 实例化模板对象 
      SimpleDateFormat sdf2 = new SimpleDateFormat(pat2); // 实例化模板对象  
      Date d = null;
      try{
          d = sdf1.parse(strDate) ; // 将给定的字符串中的日期提取出来  
      }catch(Exception e){  // 如果提供的字符串格式有错误,则进行异常处理 
          e.printStackTrace() ;   // 打印异常信息  
      }
      System.out.println(sdf2.format(d));// 将日期变为新的格式  
      
  2. java.time.format.DateFormatter 时间格式化(java8发布,线程安全。SimpleDateFormat线程不安全)

    • ofPattern 设定时间格式
    • parse 将字符串格式化为时间对象
    • format 将时间对象格式化为字符串
    //将字符串转化为时间
    String dateStr= "2016年10月25日";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    LocalDate date= LocalDate.parse(dateStr, formatter);
    System.out.println(date.getYear() + "-" + date.getMonthValue() + "-" + date.getDayOfMonth());
    
    System.out.println("==========================");
    
    //将日期转换为字符串输出
    LocalDateTime now = LocalDateTime.now();
    DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss");
    String nowStr = now.format(format);
    System.out.println(nowStr);
    

Java笔记2

异常

分类

  1. 分类1:

    • Throwable 所有错误的祖先
    • Error系统内部错误或资源耗尽,不管
    • Exception:程序有关异常,所有异常的父类
      • RuntimeException:程序自身错误(如除0, 空指针,数组越界)
      • 非RuntimeException: 外界相关错误(打开一个不存在的文件、加载不存在的类)

    image-20201011082349790

  2. Unchecked Exception:包括Error和RuntimeException的子类(编译器不会辅助检查,需要程序员自己管的)。Exception中不是RuntimeException的即为Unchecked Exception

    Checked Exception:非RutimeException(编译器辅助检查,如果没有处理会报错)

异常处理

  1. try-catch-finally(书写顺序不可变)
    • try中包含可能出现错误的代码
    • catch可以有0到多个,从上到下依次匹配,所以小异常要写在前面,大异常写在后面
    • 当有一个catch匹配时,进入相应catch代码块,且仅会进入一个catch代码块
    • 发生异常后,代码块后续代码不再执行,catch处理异常后也不再返回抛出异常的地方
    • finally可以有0到1个。无论是否有异常,异常是否被捕捉,finally都会执行
    • catch和finally至少要有一个
    • 代码块中能够再包含try-catch-finally结构
  2. 抛出异常
    • 方法存在可能发生异常的语句,但不处理,可以添加throws声明异常
    • 调用带有checked Exception的方法,要么处理这些异常,要么再次向外throws异常,直到main函数为止
    • 如果一个方法被覆盖,则覆盖它的方法也需要抛出相同的异常或异常的子类(不能超过父类抛出的异常的范围,即如果父类抛出n个异常,子类重写的方法可以不抛出、抛出1~n个异常或对应异常的子类)
  3. 自定义异常
    • 继承自Exception则变成unchecked Exception,继承自RuntimeException则变成Checked Exception
    • 自定义重点在构造函数
      • 调用父类Exception的message构造函数
      • 可以自定义自己的成员变量
    • 在程序中采用throw主动抛出异常
posted @ 2022-02-16 14:26  DreamEagle  阅读(9)  评论(0编辑  收藏  举报