JavaSE总结

JavaSE目录

  • 初识Java
    • Java由来
    • main 方法介绍
    • Java程序的运行
  • 数据类型和变量
    • 数据类型
      • 基本数据类型
      • 引用数据类型
  • 运算符
    • 算术运算符
    • 关系运算符
    • 逻辑运算符
    • 移位运算
  • 逻辑控制
  • 方法
    • 方法的重载与重写
    • 关于递归
  • 数组
    • 二维数组
  • 类和对象
    • 成员变量
    • 成员方法
    • 对象
    • this 关键字
    • 构造方法
    • 封装
    • 代码块
    • 内部类
      • 非静态内部类
      • 静态内部类
  • 继承和多态
    • 继承
      • super 关键字
      • super 与 this 的区别
      • 代码块执行顺序
      • 访问修饰限定符
      • final 关键字
    • 多态
      • 向上转型
  • 抽象类和接口
    • 抽象类
      • 抽象类语法
      • 抽象类特性
      • 抽象类作用
    • 接口
      • 接口语法
      • 接口使用
      • 接口间的继承
      • 接口特性
    • 抽象类和接口的区别
  • String
    • 字符串构造的常用三种方法
    • String对象的比较
    • StringBuilder和StringBuffer
  • 异常
    • 异常的体系结构
    • 异常分类
    • 异常抛出
    • 异常的捕获
      • 异常声明throws
      • try-catch捕获并处理
      • finally
    • 自定义异常

初识Java

Java由来

Java 语言源于 1991 年 4 月,Sun 公司 James Gosling博士 领导的绿色计划(Green Project) 开始启动,此计划最初的目标是开发一种能够在各种消费性电子产品(如机顶盒、冰箱、收音机等)上运行的程序架构。这个就是Java的
前身:Oak (得名与Java创始人James Gosling办公室外的一棵橡树),但由于这些智能化家电的市场需求没有预期的高,Sun公司放弃了该项计划。随着1995年代互联网的发展,Sun公司看见Oak在互联网上应用的前景,于是改造了Oak,于1995年5月以Java的名称正式发布,并提出“Write once, Run anywhere"(一次写入,随处运行) 的口号。

JavaSE总结_第1张图片

main 方法介绍

示例:

public class HelloWorld{
	public static void main(String[] args){
		System.out.println("Hello world");
	}
}

Java程序的结构由如下三个部分组成:

  1. 源文件(扩展名为*.java):源文件带有类的定义。类用来表示程序的一个组件,小程序或许只会有一个类。类的内容必须包含在花括号里面。
  2. 类:类中带有一个或多个方法。方法必须在类的内部声明。
  3. 方法:在方法的花括号中编写方法应该执行的语句。

总结:类存在于源文件里面;方法存在于类中;语句存在于方法中。

注意:在一个源文件中只能有一个public修饰的类,而且源文件名字必须与public修饰的类名字相同。

Java程序的运行

先通过javac编译程序把源文件进行编译,编译后生成的.class文件是由字节
码组成的平台无关、面向JVM的文件。最后启动java虚拟机来运行.class文件,此时JVM会将字节码转换成平台能够理解的形式来运行。

我们可以用记事本或 idea(集成开发环境)来编写Java源程序,然后使用javac.exe编译器编译源程序,生成xxx.class的字节码文件,语法格式:
javac xxx.java ,最后用java运行字节码文件,语法格式:Java xxx

注意:在运行Java程序前,必须先安装好JDK(Java Development Kit即Java开发工具包),JDK里面就包含了javac和java工具,Java程序最终是在JVM(Java虚拟机)中运行的。

JDK、JRE、JVM之间的关系:

JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译
器javac与自带的调试工具Jconsole、jstack等。
JRE(Java Runtime Environment):Java运行时环境,包含了JVM,Java基础类库。是使用Java语言编写程
序运行的所需环境。
JVM:Java虚拟机,运行Java代码
JavaSE总结_第2张图片

数据类型和变量

数据类型

在Java中数据类型主要分为两类:基本数据类型和引用数据类型。

基本数据类型

基本数据类型有四类八种:

  1. 四类:整型、浮点型、字符型以及布尔型
  2. 八种:
    JavaSE总结_第3张图片
    它们对应的包装类分别是:

int 的包装类型为 Integer
long 的包装类型为 Long
short 的包装类型为 Short
byte 的包装类型为 Byte
double 的包装类型为 Double
float 的包装类型为 Float
char 的包装类型为 Character
boolean 的包装类型为 Boolean

总结:除了int 和 char 类型的包装类要特殊记忆外,其它六种基本数据类型所对应的包装类都是首字母大写即可。

引用数据类型

引用数据类型有:数组、类、接口、抽象类、自定义类型等

运算符

算术运算符

加减乘除模(+ - * / %)
都是二元运算符,使用时必须要有左右两个操作数
int / int 结果还是int类型,而且会向下取整
做除法和取模时,右操作数不能为0
% 不仅可以对整型取模,也可以对double类型取模,但是没有意义,一般都是对整型取模的
两侧操作数类型不一致时,向类型大的提升

增量运算符 += -= *= %=
该种类型运算符操作完成后,会将操纵的结果赋值给左操作数。
int a = 1; a += 2; // 相当于 a = a + 2
注意:只有变量才能使用该运算符,常量不能使用。

自增/自减运算符 ++ –
+是给变量的值+1,–是给变量的值-1。
注意:
如果单独使用,【前置++】和【后置++】没有任何区别
如果混合使用,【前置++】先+1,然后使用变量+1之后的值,【后置++】先使用变量原来的值,表达式
结束时给变量+1
只有变量才能使用自增/自减运算符,常量不能使用,因为常量不允许被修改

关系运算符

关系运算符主要有六个: == != < > <= >= ,其计算结果是 true 或者 false 。
注意:当需要多次判断时,不能连着写,比如:3 < a < 5,应该写成:3

逻辑运算符

逻辑运算符主要有三个: && || ! ,运算结果都是 boolean类型。比较简单简略带过。

移位运算

移位运算符有三个: << >> >>> ,都是二元运算符,且都是按照二进制比特位来运算的。

1、 左移 <<: 最左侧位不要了, 最右侧补 0.
注意:向左移位时,丢弃的是符号位,因此正数左移可能会编程负数
2、 右移 >>: 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)
3、 无符号右移 >>>: 最右侧位不要了, 最左侧补 0

逻辑控制

这里我们学习了if、if…else、switch语句,还有for、while、do…while循环等。
我们要注意:主要是判断条件,就一定是布尔表达式。
还要注意switch的参数的数据类型有哪些。

switch的括号内只能是以下类型的表达式:
基本类型:byte、char、short、int,注意不能是long类型
引用类型:String常量串、枚举类型

方法

java中把函数叫做方法,它是完成某个功能,模块化的组织代码。

方法的重载与重写

着重强调方法的重载、重写,以及这两者的区别。

重载特点:
方法名相同,参数列表不同(顺序、数据类型、个数),返回值不做要求。
注意:子类也可以重载父类的方法。

重写特点:
方法名相同,参数列表相同(顺序、数据类型、个数),返回值相同(除非可以构成父子类关系)。
注意:
private 修饰的方法不能重写
static 修饰的方法不能重写
被final修饰的方法不能重写
子类重写方法后,访问权限要大于等于父类的访问权限。

区别:
JavaSE总结_第4张图片

关于递归

一个方法在执行过程中调用自身, 就称为 “递归”。

递归的必要条件:

  1. 将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同
  2. 递归出口(设置条件,在合适的位置结束递归,否则会无限递归)

递归的程序的执行过程不太容易理解, 要想理解清楚递归, 必须先理解清楚 “方法的执行过程”, 尤其是 “方法执行结束之后, 回到调用位置继续往下执行”.

数组

定义:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。
数组在java中是引用类型。

注意:数组可以作为参数和返回值:

操作数组的工具类:Arrays

二维数组

二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组。

数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };

例如:

    public static void main(String[] args) {
        int[][] arr = {
                {1, 2, 3, 4},
                {5, 6, 7, 8},
                {9, 10, 11, 12}  
        };    //二维数组的每一行,相当于一维数组的每个元素
        for(int row = 0; row < arr.length; row++) {
            for (int col = 0; col < arr[row].length; col++) {
                System.out.printf("%d\t", arr[row][col]);
            }
            System.out.println("");
        }
    }

输出:
1 2 3 4
5 6 7 8
9 10 11 12
二维数组就类似于一个特殊的一维数组,每个元素相当于一个一维数组,每个一维数组的大小可以不同。
JavaSE总结_第5张图片

类和对象

什么是类?
类就是描述某个对象的一些属性和行为。

成员变量

成员变量定义在方法的外部,类的内部。

普通成员变量:
属于对象的,对象的引用与点号的结合进行访问,所占用的内存是在对象中的。
静态成员变量:
加static关键字,表面当前成员变量是属于类的变量。类变量是存放在方法区的。

成员方法

普通成员方法:属于对象的,对象的引用与点号的结合进行访问。
静态成员方法:不依赖于对象,直接可以通过类名点出来。
注意:不能在静态方法中使用非静态成员变量。(因为非静态成员变量依赖于对象)

对象

通过描述,产生一个真正的实体对象,主要通过new关键字来实例化对象。
一个类可以实例化多个对象。

this 关键字

this是当前对象的引用,三种用法:
1.引用当前对象的变量:this.data
2.引用当前对象的方法:this.fun()
3.引用当前对象的构造方法:this()

构造方法

方法名与类名一样,没有返回值。

作用:实例化对象的时候调用,同时可以初始化我们的成员变量。

我们有两种初始化:
就地初始化:创建变量时赋值。
默认初始化:创建变量时不赋值。(变量是默认值,不同数据类型默认值是不同的)

当我们没有添加任何构造方法时,编译器会帮我们提供一个不带参数的构造方法,而当我们添加了构造方法时,编译器就不会再提供构造方法了。

注意:构造方法可以重载。

封装

含义:指将内部的实现细节进行了隐藏,不要让类外直接获取到我不想让它获取到的东西。从代码层面来讲,就是使用关键字private进行修饰。

我们可以使用get和set方法来对封装的数据进行访问。

代码块

普通代码块:定义在方法中的代码块。
构造代码块:定义在类中的代码块(加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
静态代码块:使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。

new对象时,静态代码块和非静态代码块与构造方法的执行顺序:先是静态的执行(只执行一次,下次就不执行了),然后是非静态的,最后是构造方法执行。

内部类

即在类里再定义一个类。

非静态内部类

public class OutClass {
    private int a;
    static int b;
    int c;
    
    public void methodA() {
        a = 10;
        System.out.println(a);
    }

    public static void methodB() {
        System.out.println(b);
    }

    // 实例内部类:未被static修饰
    class InnerClass {
        int c;

        public void methodInner() {
    // 在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员
            a = 100;
            b = 200;
            methodA();
            methodB();
    // 如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
            c = 300;
            System.out.println(c);
    // 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
            OutClass.this.c = 400;
            System.out.println(OutClass.this.c);
        }
    }

    public static void main(String[] args) {
    // 外部类:对象创建 以及 成员访问
        OutClass outClass = new OutClass();
        System.out.println(outClass.a);
        System.out.println(OutClass.b);
        System.out.println(outClass.c);
        outClass.methodA();
        outClass.methodB();
        System.out.println("=============实例内部类的访问=============");
    // 要访问实例内部类中成员,必须要创建实例内部类的对象
    // 而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类
    // 创建实例内部类对象
        OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
    // 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建实例内部类对象
        OutClass.InnerClass innerClass2 = outClass.new InnerClass();
        innerClass2.methodInner();
    }
}

注意:

  1. 外部类中的任何成员都可以在实例内部类方法中直接访问
  2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
  3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
  4. 实例内部类对象必须在先有外部类对象前提下才能创建
  5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
  6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。

静态内部类

被static修饰的内部成员类称为静态内部类。

public class OutClass {
    private int a;
    static int b;
    public void methodA(){
        a = 10;
        System.out.println(a);
    }
    public static void methodB(){
        System.out.println(b);
    } 
    // 静态内部类:被static修饰的成员内部类
    static class InnerClass{
        public void methodInner(){
        // 在内部类中只能访问外部类的静态成员
            // a = 100; // 编译失败,因为a不是静态变量
            b =200;
        // methodA(); // 编译失败,因为methodB()不是静态成员方法
            methodB();
        }
    }
    public static void main(String[] args) {
    // 静态内部类对象创建 & 成员访问
        OutClass.InnerClass innerClass = new OutClass.InnerClass();
        innerClass.methodInner();
    }
}

注意:

  1. 在静态内部类中只能访问外部类中的静态成员
  2. 创建静态内部类对象时,不需要先创建外部类对象

总结:如果设计的内部类依赖于外部类对象,那就设计成非静态的内部类,反之就设计成静态内部类。

还有一个匿名内部类,就是没有名字的内部类,多用于接口的实现。

继承和多态

继承

class A extends B {
}

为什么要继承呢?
继承是共性的抽取,从而达到对代码的复用。
子类继承父类的所有成员变量和方法(非静态的)。

super 关键字

该关键字主要作用:在子类方法中访问父类的成员。

super 的几种用法:
访问父类的成员变量:super.data
访问父类的方法:super.func()
调用父类的构造方法:super()

super 与 this 的区别

super和this都可以在成员方法中访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那它们之间有什么区别呢?

同:

  1. 都是Java中的关键字
  2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
  3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

异:

  1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
  2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
  3. 在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造 方法中出现
  4. 构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有

代码块执行顺序

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person:构造方法执行");
    }
    {
        System.out.println("Person:实例代码块执行");
    }
    static {
        System.out.println("Person:静态代码块执行");
    }
}
class Student extends Person{
    public Student(String name,int age) {
        super(name,age);
        System.out.println("Student:构造方法执行");
    }
    {
        System.out.println("Student:实例代码块执行");
    }
    static {
        System.out.println("Student:静态代码块执行");
    }
}
public class TestDemo4 {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 15);
        System.out.println("===========================");
        Student student2 = new Student("李四", 35);
    }
}

输出:JavaSE总结_第6张图片

1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

访问修饰限定符

JavaSE总结_第7张图片

final 关键字

final关键可以用来修饰变量、成员方法以及类。

注意三个点:

  1. 修饰变量或字段,表示常量(即不能修改)
  2. 修饰类:表示此类不能被继承
  3. 修饰方法:表示该方法不能被重写

多态

啥是多态?
就是一个引用,引用的对象不一样, 所表现的行为不一样, 这种思想被称为多态.

向上转型

class Animal {
}
class Cat extends Animal {
}

父类类型引用子类对象, 这便是向上转型.

	Animal animal = new Cat();  

缺点 : 只能通过父类引用 访问父类自己的成员.

什么时候发生向上转型呢?

第一种就是直接赋值了 :

	Animal animal = new Cat();  

还有就是方法的传参 , 就是将子类对象传入父类类型参数里 :

public static void fun(Animal animal) {   //这里传入Cat类型参数,就是向上转型了
}

再就是方法的返回值了 :

public static Animal fun(Animal animal) {
	return new Cat();
}

关于动态绑定 : 通过父类引用 , 调用子类重写了的父类的方法.

抽象类和接口

抽象类

抽象类语法

	// 抽象类:被abstract修饰的类
public abstract class A {
	// 抽象方法:被abstract修饰的方法,没有方法体
	abstract public void fun();
	abstract void func();
	// 抽象类也是类,也可以增加普通方法和属性
	public double funcc(){
		return m;
	}
	protected double f;  
}

抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

抽象类特性

  1. 抽象类不能直接实例化对象
  2. 抽象方法不能是 private 的
  3. 抽象方法不能被final和static修饰,因为抽象方法要被子类重写
  4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰
  5. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
  6. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

抽象类作用

抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类, 然后让子类重写抽象类中的抽象方法,使用抽象类相当于多了一重编译器的校验。

接口

接口语法

public interface 接口名称,接口名称...{  //可以实现多个接口
	// 抽象方法
	public abstract void method1(); // public abstract 是固定搭配,可以不写
	public void method2();
	abstract void method3();
	void method4();
	// 注意:在接口中上述写法都是抽象方法,少写固定搭配让代码更简洁
}

接口使用

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。

接口间的继承

类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字。
接口间的继承相当于把多个接口合并在一起,不需要去实现接口中的方法。

接口特性

  1. 接口类型是一种引用类型,但是不能直接new接口的对象
  2. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
  3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
  4. 重写接口中方法时,不能使用默认的访问权限 (因为接口中的方法默认为 public abstract)
  5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
  6. 接口中不能有静态代码块和构造方法
  7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
  8. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
  9. jdk8中:接口中还可以包含default方法

注意 :
一个类可以实现多个接口 (继承是只能继承一个)
一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。( IDEA 中使用 ctrl + i 快速实现接口)

抽象类和接口的区别

核心区别:
抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.

JavaSE总结_第8张图片

String

字符串构造的常用三种方法

public static void main(String[] args) {
	// 使用常量串构造
	String s1 = "hello world";
	System.out.println(s1);
	// 直接newString对象
	String s2 = new String("hello world");
	System.out.println(s1);
	// 使用字符数组进行构造
	char[] array = {'h','e','l','l','o','w','o','r','l','d'};
	String s3 = new String(array);
	System.out.println(s1);
}

String对象的比较

1.比较是否引用同一个对象

对于内置类型,== 比较的是变量中的值;对于引用类型,比较的是引用中的地址。

  1. boolean equals(Object anObject) 方法:按照字典序比较(字符大小的顺序)

String类重写了父类Object中equals方法,Object中equals默认按照==比较,String重写equals方法后,按照如下规则进行比较,比如: s1.equals(s2)

public boolean equals(Object anObject) {
// 1. 先检测this 和 anObject 是否为同一个对象比较,如果是返回true
	if (this == anObject) {
		return true;
	} 
// 2. 检测anObject是否为String类型的对象,如果是继续比较,否则返回false
	if (anObject instanceof String) {
// 将anObject向下转型为String类型对象
		String anotherString = (String)anObject;
		int n = value.length;
// 3. this和anObject两个字符串的长度是否相同,是继续比较,否则返回false
		if (n == anotherString.value.length) {
			char v1[] = value;
			char v2[] = anotherString.value;
			int i = 0;
// 4. 按照字典序,从前往后逐个字符进行比较
			while (n-- != 0) {
				if (v1[i] != v2[i])
				return false;
				i++;
			} 
			return true;
		}
	}
	return false;
}
  1. int compareTo(String s) 方法: 按照字典序进行比较

与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较方式:

  1. 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值
  2. 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值
public static void main(String[] args) {
	String s1 = new String("abc");
	String s2 = new String("ac");
	String s3 = new String("abc");
	String s4 = new String("abcdef");
	System.out.println(s1.compareTo(s2)); // 不同输出字符差值-1
	System.out.println(s1.compareTo(s3)); // 相同输出 0
	System.out.println(s1.compareTo(s4)); // 前k个字符完全相同,输出长度差值 -3
}
  1. int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较
public static void main(String[] args) {
	String s1 = new String("abc");
	String s2 = new String("ac");
	String s3 = new String("ABc");
	String s4 = new String("abcdef");
	System.out.println(s1.compareToIgnoreCase(s2)); // 不同输出字符差值-1
	System.out.println(s1.compareToIgnoreCase(s3)); // 相同输出 0
	System.out.println(s1.compareToIgnoreCase(s4)); // 前k个字符完全相同,输出长度差值 -3
}

StringBuilder和StringBuffer

由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的.

String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder.

注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:
String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
StringBuilder变为String: 调用toString()方法

String、StringBuffer、StringBuilder的区别:

String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
StringBuffer与StringBuilder大部分功能是相似的
StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

异常

异常的体系结构

异常种类繁多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构:
JavaSE总结_第9张图片

  1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
  2. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:
    StackOverflowError和OutOfMemoryError,一旦发生回力乏术。
  3. Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception。

异常分类

  1. 编译时异常
    在程序编译期间发生的异常,称为编译时异常,也称为受查异常
  2. 运行时异常
    在程序执行期间发生的异常,称为运行时异常,也称为非受查异常
    RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。

异常抛出

借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。

	throw new XXXException("异常产生的原因");

注意:

  1. throw必须写在方法体内部
  2. 抛出的对象必须是Exception 或者 Exception 的子类对象
  3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
  4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
  5. 异常一旦抛出,其后的代码就不会执行

异常的捕获

主要有两种:异常声明throws 以及 try-catch捕获处理。

异常声明throws

处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常。

修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}    //可以声明多个异常

注意:

  1. throws必须跟在方法的参数列表之后
  2. 声明的异常必须是 Exception 或者 Exception 的子类
  3. 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
  4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出

try-catch捕获并处理

throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch.

try{
// 将可能出现异常的代码放在这里
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类时,就会被捕获到
// 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码
}[catch(异常类型 e){
// 对异常进行处理
}finally{
// 此处代码一定会被执行到
}]
// 后序代码
// 当异常被捕获到时,异常就被处理了,这里的后序代码一定会执行
// 如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行

注:

  1. [ ]中表示可选项,可以添加,也可以不用添加
  2. try中的代码可能会抛出异常,也可能不会

注意:

  1. try块内抛出异常位置之后的代码将不会被执行
  2. 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后 中断程序----异常是按照类型来捕获的
  3. try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获
  4. 可以通过一个catch捕获所有的异常,即多个异常,一次捕获(用它们的父类异常捕获,如果这样做我们就不知道发生了什么异常,因此不推荐)

在catch 语句里我们有三种处理异常方法:
只打印异常信息:System.out.println(e.getMessage());
打印异常类型,异常信息:System.out.println(e);
打印信息最全面:e.printStackTrace();

finally

try{
// 可能会发生异常的代码
}catch(异常类型 e){
// 对捕获到的异常进行处理
}finally{
// 此处的语句无论是否发生异常,都会被执行到
} 
// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行

finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。

自定义异常

Java 中虽然已经内置了丰富的异常类, 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构。

实现方式:

  1. 自定义异常类,然后继承自Exception 或者 RunTimeException
  2. 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因

注意:
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常

举个例子,实现一个用户登录功能:

public class Test {
    public static void fun(String name2, String code2) throws PasswordException, UsernameException {
        String name = "张三";
        String code = "666666";
        if(!name2.equals(name)) { //如果输入的姓名或密码错误,则抛出异常
            throw new PasswordException("用户名错误!");
        }
        if(!code2.equals(code)) {
            throw new UsernameException("密码错误!");
        }
        System.out.println("输入成功!!!");
    }

    public static void main(String[] args) throws PasswordException, UsernameException {
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        String code = scanner.nextLine();
        fun(name,code);
    }
}
public class PasswordException extends Exception{
    public PasswordException(String message) {
        super(message);
    }
}
public class UsernameException extends Exception{
    public UsernameException(String message) {
        super(message);
    }
}

你可能感兴趣的:(Java学习,java,开发语言)