java学习笔记

接口

1.定义

java单继承性原因:避免继承的多个类中有相同定义的方法名称导致二义性。
接口的存在是为了多实现,克服java单继承的缺点。而接口与类的区别如下:

  • 类中成员可分为public,private和proteced,而接口默认全为public
  • 类中的方法可以声明并定义,而接口的方法只能声明,注意,接口的静态方法也可以直接声明定义
  • 类中可以定义变量而接口只能定义常量

2.接口的声明与实现

1.接口的声明只需interface,示例如下:

1
2
3
4
5
interface  Printable {
final int MAX=100; //public final int
void add(); //public abstract void add
float sum(float x ,float y);//public abstract float..
}

2.实现接口用implements关键字,一个类支持实现多接口,不同的类也可以实现一个接口,示例如下:

class A implements interfaceA,interfaceB  //一个类多实现接口
class C implements interfaceA   //多个类实现同一个接口

3.接口变量做参数回调

1.首先声明接口,一个文件只能定义一个与文件名相同的接口:

2.类实现接口:
3.以接口对象作为参数

4.接口回调
形参为接口对象,实参为类实例,将类实例赋值给接口对象,通过回调实现函数的调用:

接口的默认方法

接口里可以声明默认方法,该方法可以在接口里直接实现,实现接口的类不需要对它进行重写:

1
2
3
4
5
6
public interface A{
void method();
default methodDefault(){
system.out.println("这是默认方法");
}
}

接口的私有方法

接口的私有方法

3.接口的一些注意事项

  • 接口中定义的静态方法不能通过实现接口的类的对象调用,而只能通过接口名直接调用

对象的一些注意事项

1.在创建一个对象,使用构造函数new一个对象时,先在堆区分配内存放置对象的成员变量,和成员方法的地址,在方法区放置方法的声明,当调用方法或者定义方法时,在栈区开辟内存存放。

2.当2个对象使用同一个方法时,不会相互干扰,因为使用了new创建新空间,但是当一个对象直接赋值给另一个对象,那么这2个对象共用一个内存空间,本质上是一样的。 3.java支持匿名对象,即可以直接使用new Object().method;直接调用对象的方法,但是匿名对象只能使用一次,使用完了就找不到匿名对象的地址了。所以匿名对象一般被用于作为方法的实参。

对象数组

对象数组,就是为对象类型的数组,数组元素存放的是对象的地址值,使用方法如下:

1
2
3
4
5
Person[] array = new Person[3];
array[0]=new Person("Tom"); //地址
array[1]=new Person("Jack"); //地址
array[2]=new Person("marial"); //地址
System.out.println(array[0].getName()); //将会打印Tom

ArrayList

ArrayList类似于动态数组,是java的一个封装类,可以实现长度的随意变化。

ArrayList的使用

1.声明ArrayList对象
首先要导入java.util.ArrayList的包

1
ArrayList <String> list = new ArrayList<>();  //尖括号内是list的数据类型,要求为泛型

2.ArrayList的增加和删除数据,查找数据
使用ArrayList类的add,remove和get方法可以实现数据的增删,查找

1
2
3
4
5
6
7
8
9
10
11
12
13
//增加数据
list.add("Tom");
System.out.println(list); //输出[Tom]
list.add("jack");
list.add("james");
System.out.println(list); //输出[Tom,jack,james]

//查找数据
String name = list.get(2); //索引从0开始,name为james

//删除数据
list.remove(2); //将会删除第三个元素,即删除james
System.out.println(list) //打印[Tom,jack]

3.获取ArrayList的长度
使用size()方法可以返回List的长度

1
int size = list.size(); //size为2,因为前面删除了一个元素

4.ArrayList的对象类型
ArrayList的泛型也支持自己写的类,假如我已经写好了Student类

1
2
3
4
5
6
7
8
ArrayList <Student> list = new ArrayList<>();
Student one = new Student("stu1");
Student two = new Student("stu2");
list.add(one);
list.add(two);
//使用list当中的对象
Student s1 = list.get(0);
String name = s1.getName(); //name为stu1

ArrayList的注意事项

  • 声明ArrayList时尖括号内必须为泛型,泛型只能为引用类型,而不能是基本数据类型如int之类的,所以想使用基本数据类型,必须使用基本数据类型对应的包装类
  • 直接输出ArrayList的对象将会输出ArrayList的内容,并有着中括号[]括起来,如果ArrayList为空,会直接输出[]
  • list.add()方法有布尔返回值,添加成功返回true,失败返回false
  • list.remove()方法也有返回值,将会返回被删除的元素的值

字符串

字符串的3种创建方法

1
2
3
4
5
6
7
8
9
10
1.直接创建
String str1 = "Tom";
2.根据字符数组创建
char ch[]={'A','B','C'};
String str2 = new String(ch); //str2="ABC"
3.根据字节数组创建
byte by[] = {97,98,99};
String str3 = new String(by); //str3="abc"
4.不赋值将创建空字符串
String str4 = new String(); //str4为空字符串

字符串的一些方法

1.equals(String str)方法
使用equals方法将会比较字符串的内容,不会比较地址

1
2
3
4
5
6
String str1="hello";
char ch[]={'h','e','l','l','o'};
String str2=new String(ch);
str1.equals(str2); //将会返回true
str1.equals("hello"); //返回true
"hello".equals(str2); //返回true

2.equalsIgnoreCase(String str)方法
使用这个方法将会忽略字符串的大小写进行比较

1
2
3
4
5
String str1="Hello";
char ch[]={'h','e','l','l','o'};
String str2=new String(ch);
str1.equals(str2); //将会返回false
str1.equalsIgnoreCase(str2) //返回true

3.concat(String str)方法
使用concat方法可以拼接2个字符串,原字符串不变

1
2
3
String str1="hello";
String str2="world";
String str3=str1.concat(str2); //str3为helloworld

3.charAt(int index)方法
charAt方法可以找出字符串任何位置的字符,如果索引不合法将会出现异常

1
2
String str1="hello";
char ch = str1.charAt(1); //ch为e

4.indexOf(String str)方法
indexOf方法可以返回str在字符串中出现的第一个位置,没有将会返回-1

1
2
String str = "helloworld";
int index=str.indexOf("llo"); //index为2

5.subString(int index1,int index2)方法
subString方法可以获取原字符串从index1到index2-1的字串,如果只有一个参数,将默认index2为字符串的length

1
2
3
String str = "helloworld";
String str1=str.subString(5); //str1为world
String str2=str.subString(4,7); //str2为owo

6.toCharArray()方法
toCharArray方法可以将字符串变成字符数组

1
2
String str="hello";
char ch[]=str.toCharArray(); //ch[]={'h','e','l','l','o'}

7.replace(String str1,String str2)方法
replace方法可以将字符串里值为str1的字符串替换成str2

1
2
3
String str="hello";
String str1=str.replace("l","#"); //str1为he##o
String str2=str.replace("el",""ss"); //str2为hsslo

8.split(char ch)方法
split方法将会将字符串在ch处切割开成String数组,支持ch为空格

1
2
String str="aaa,bbb,ccc";
String s[]=str.split(","); //s[]={"aaa","bbb","ccc"}

字符串的注意事项

  • 字符串是常量
  • 字符串相当于char[]数组,但是在计算机底层是用byte[]数组实现的,因为字符使用ascii编码,在计算机底层翻译成数字储存在字节数组中
  • 当使用String直接赋值创建多个值相同的字符串时,实际上这几个字符串共用字符串常量池,即共用1个地址
  • new出来的String字符串不在常量池中,所以new出来的字符串即使只相同地址也不同
  • 使用==比较时,基本数据类型比较的是值,而其他的比较的是地址
  • 使用equals方法比较时,str.equals(str1)中,str不能为空字符串,否则会出现空指针异常
  • 字符串无论经过什么操作其都不会发生改变,给字符串赋其他值实际上是改变了字符串指向的地址
  • split方法中的参数是正则表达式参数,如要使用.作为分隔符,必须要写出//.才行,否则不会进行切割

关于static

  • static是修饰类静态成员的关键字,而静态成员是直接属于类的,即使用类名可以直接调用静态成员,同时,它属于类的成员,也可以使用对象进行调用

  • 静态成员可以不创建对象而直接通过类调用

  • 在类的内部,静态成员可以直接使用省略前面的类名或者对象名

  • 静态方法不可以调用非静态成员

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     public class Test {
    public static void main(String[] args) {
    StaticDemo sd = new StaticDemo();
    StaticDemo.staticMethod();
    System.out.println(StaticDemo.count);
    System.out.println(sd.count);
    method1();
    // method2(); //会报错
    System.out.println((staticNum));
    Test t=new Test();
    System.out.println((t.num));

    }

    static int staticNum=0;
    int num=1;
    public static void method1()
    {
    System.out.println("method1");
    }
    public void method2()
    {
    System.out.println("method2");
    }
    }

  • 静态方法的执行优先于非静态方法

  • 静态代码块用于对静态成员的一次性赋值,静态代码块只执行一次,无论创建多少个对象都只执行一次,用法为:

      static{
          //静态代码块
      }

Arrays类

Arrays类在java.util.Arrays包中,是对数组的辅助工具类
1.Arrays.toString()
toString方法可以将数组变成字符串形式

1
2
3
int[] arr1={1,2,3};
String StrArr1=Arrays.toString(arr1);
System.out.println(Array.toString(strArr1)); //输出[1,2,3]

2.Arrays.sort()
sort方法可以对数组进行升序排序,如果是数字则从小到大排列,如果是字母则按首字母字典序顺序排列

1
2
3
String[] strArr={"aaa","ccc","dd","bbb"};
Arrays.sort(strArr);
System.out.println(Arrays.toString(strArr)); //输出[aaa,bbb,ccc,dd]

3.toCharArray()
toCharArray方法可以将字符串转变为字符数组,用法为:Char []chStr=字符串名.toCharArray();

math类

math类在java.util.Math包中,提供了许多有数学运算相关的方法
1.abs()方法
使用Math.abs(double num)可以获得num的绝对值,但是num本身不变
2.ceil()方法
Math.ceil(double num)可以获取num的向上取整的浮点数,如num是3.1,则Math.ceil(3.1)为4.0
3.floor()方法
Math.floor(double num)方法为向下取整
4.round()方法
Math.round(double num)方法可以对num进行四舍五入取整

关于java的继承

  • 子类可以直接访问父类所有的public成员,

  • 当子类和父类有重名成员变量或方法时,通过对象调用该同名变量或方法,则先在该对象所在的类中寻找,若找不到则再向父类中寻找,这就是java的重写,可以通过在方法前加上@override检测是否合法重写

  • 当子类和父类有成员方法用到了同名成员变量时,则调用该方法时,优先在该方法的类中寻找变量,没有再向父类寻找

  • 关于类中的方法局部变量,成员变量和父类成员变量三者同名的调用:局部变量通过覆盖规则可以直接使用,本类成员变量则用this.变量名调用,父类变量用super.变量名调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //father类定义了num=10
    //son类如下:
    public class son
    {
    int num=20;
    void printNum()
    {
    int num=30;
    system.out.println(num); //30
    system.out.println(this.num); //20
    system.out.println(super.num); //10
    }
    }
  • 关于重写,只是方法的重写,而变量不能重写,

  • 必须要求方法名称,参数列表相同,并且子类的返回类型小于或者等于父类的返回值类型,比如:

    1
    2
    3
    4
    5
    6
    7
    //父类方法为public object method();
    //子类:
    public string method(); //符合重写

    //父类方法为public string method();
    //子类:
    public object method(); //不符合重写
  • 重写的目的:在应用中,主要是在产品的更新换代中,每一个子类代表一代新产品,而子类直接继承功能不变的函数,而对需要升级的函数进行重写覆盖

  • 对于构造方法:子类调用构造方法时必须调用父类的构造方法,且如果父类构造方法中有参数时,必须使用super关键字为父类构造方法传递实参

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //父类
    public class father {
    public father(){
    System.out.println("默认父类构造方法");
    }
    public father(int num){
    System.out.println("重载子类构造方法"+num);
    }

    }

    //子类
    public class son extends father {
    public son(){
    System.out.println("默认子类构造方法");
    }
    public son(int num){
    super(num);
    System.out.println("重载子类构造方法");
    }

    }

    //测试
    son son1=new son(); //输出:默认父类构造方法 默认子类构造方法
    son son2=new son(1); //输出:重载子类构造方法1 重载子类构造方法
  • 构造方法之间可以相互调用,通过this()即可,但是构造方法中不能同时存在super()和this()

  • 每次调用方法时,都是将方法区中的方法进栈

多态

  • 使用父类对象创建子类引用时,是将子类当成父类使用,如把猫当成动物看,所以调用方法时优先调用子类的,若子类没有则向上寻找父类的
  • 使用父类对象创建子类引用时,对象无法调用子类的变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //父类
    public class father {
    int num=10;
    public void showNum(){
    System.out.println(num);
    }

    }

    //子类
    public class son extends father {
    int num=20;
    int age=30;
    @Override
    public void showNum(){
    System.out.println(num);
    }
    }

    //测试
    public class Test {
    public static void main(String[] args) {
    father obj =new son();
    System.out.println(obj.num); //父类变量10
    // System.out.println(obj.age); //会报错
    obj.showNum(); //优先调用子类方法
    }
    }

final关键字

  • 使用final关键字修饰类时,该类不可以被继承,并且该类的成员不可以被重写覆盖,但是该类可以重写覆盖他的父类方法
  • final关键字修饰的方法不能被覆盖重写
  • abstract和final不能同时修饰一个方法
  • 用final关键字修饰的变量不可以被改变,但是只声明用final修饰的变量,依然可以后续为该变量赋值,但赋值后就不可更改了
  • 用final关键字修饰的对象其地址值不可改变,所以对该对象使用new方法重新分配内存空间会报错
  • 用final修饰的成员变量,要么直接赋值,要么在构造方法里赋值并且两者不能同时赋值,注意所有构造方法的重载形式中都要包含对final修饰的成员变量赋值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class son extends father {
    public final int age;
    public son(){
    age=10; //若是默认构造方法中没有对age赋值将会报错
    }
    public son(int age){
    this.age=age;
    }
    }

访问权限

对于不同权限修饰符,不同的类,不同的包中访问权限有所差异:

内部类

(一)类中的内部类
在java中,类中可以定义另一个类,称为内部类,关于内部类有2种使用方法
1.在外部类的方法中调用内部类的方法成员,然后在main函数中调用该外部类方法间接调用内部类方法
2.直接调用: 外部类名称.内部类名称 对象名 = new 外部类().new 内部类();该对象可以直接调用内部类方法
(二)局部内部类,即方法里的内部类
在方法里定义的内部类,要想使用它,只能在局部方法里声明对象并对局部内部类的方法进行调用

匿名内部类

如果接口的实现类只需要使用唯一的一次,那么这种情况下就可以省略该实现类的定义,而改用使用匿名内部类,其定义格式为:
接口名称 对象名 = new 接口名称(){
//覆盖重写抽象方法
};

  • 匿名内部类只能创建一个对象,因为它省略了【实现类/子类名称】,无法通过构造方法再次创建对象
  • 匿名对象只能调用一次,一个方法,因为他省略了【对象名称】,无法再次通过对象名调用方法

注意事项

  • 内部类可以使用外部类中的所有成员,包括private修饰的成员
  • 在内部类中和外部类中有同名变量时,在内部类中,使用this指的是内部类对象,要使用外部类的成员变量,则需要使用外部类名.this.变量名
  • 关于内部类和外部类的权限修饰问题
  • 在局部内部类中,要想访问局部变量,该变量必须是有效final的,有效指的是在局部方法中,该变量没有二次赋值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public outer{
    public void method(){
    int num=10; //等效final,因为没有改变
    int num1=20;
    num1=30; //发生了改变,不等效final
    class inner{
    System.out.println(num); //正确,输出10
    System.out.println(num1); //将会报错
    }
    }
    }
  • 匿名内部类的对象可以作为方法的参数,匿名内部类的匿名对象也可以直接作为方法的参数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 定义了接口为Skill
    // 定义了一个类Hero,Hero有一个方法为setSkill(Skill skill){}
    //使用匿名内部类的匿名对象
    Hero hero = new Hero();
    hero.setSkill(new Skill(){
    @override
    public void use(){
    System.out.println("use skill");
    }
    })

Object的一些方法

toString()方法

toString方法是Object类中的一个方法,返回值为字符串,而Object类是所有类的父类,所以toString方法可以供所有对象调用,一般来说,是在类中重写toString方法方便一些操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class test {
private String name = "tom";
private int age = 10;
private boolean married = false;

@Override
public String toString() {
return "test{" +
"name='" + name + '\'' +
", age=" + age +
", married=" + married +
'}';
}

public static void main(String[] args) {
test t =new test();
System.out.println(t);
//输出结果为:test{name:'tom',age:'10',married:'false'}
}
}
  • 一般来说,没有重写toString方法的类,其对象调用toString方法会返回对象的地址值,否则,该类中一定重写了toString方法
  • 只有对象能调用toString方法,而其他比如说int等基本类型不能
  • 字符串的toString为原字符串,ArrayList的toString为[…],它们都对toString进行了重写

equals()方法

equals方法也是Object类的一个方法,返回值为bool类型,基本格式为:
1.obj1.equals(obj2); //比较obj1和obj2的地址值
2.Object.equals(obj1,obj2); //另一种形式

  • 同样的,一般我们使用equals()都要对其进行重写,例如String就对equals方法进行了重写,所以String字符串使用equals方法比较的是字符串内容而不是地址值
  • null是不能调用方法的,所以使用null.equals(obj)方法将会出现空指针异常,所以我们对字符串使用equals方法时,最好使用第二种形式,防止字符串为空调用方法而抛出空指针异常

Date类

  • Date类位于java.util.Date包下,是与时间相关的一个工具类,Date的构造方法的常见重载形式有2种:
    1.无参数的构造方法
    无参数的Date构造方法将会获取当前的系统时间

    Date date = new Date(); //获取系统的当前时间

2.有参数的构造方法
有参数的Data构造方法中,其参数应为long类型的数,代表毫秒值,获取从初始时间后的多少毫秒之后的时间

Date date = new Date(long num);   //获取从1970年1月1日0分0秒之后的num毫秒的时间
  • getTime函数
    getTime()函数用于返回调用的Data对象距离初始时间1970年的时间,返回值是毫秒值
    1
    2
    Date date = new Date();
    long time = date.getTime(); //time=1605524769466

Date的格式化和格式化的日期转换

DateFormat类是一个抽象类,我们不能直接使用,而SimpleDateFormat类是DateFormat类的一个实现子类,我们可以直接使用来对日期进行格式化,simpleDateFormat类的构造方法使用了类似于正则表达式的模式匹配,用不同的模式来构造出不同的日期格式,具体如下图所示:

- format方法:对日期进行格式化
1
2
3
4
5
6
7
8
Date date = new Date();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
String text1 = sdf1.format(date);
String text2 = sdf2.format(date);
System.out.println(date); //Mon Nov 16 19:28:06 CST 2020
System.out.println(text1); //2020-11-16 19:28:06
System.out.println(text); //2020年11月16日 19时31分56秒
  • parse方法,还原格式化的日期
    parse方法将格式化的日期文本还原为日期形式,它声明了一个异常ParseException,当模式与日期文本不匹配时抛出异常,所以在使用时一定要声明异常或者使用try,catch自己解决异常
    1
    2
    3
    4
    5
    6
    7
    public static void parseDemo() throws ParseException{
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
    Date date = new Date(); //Mon Nov 16 19:47:37 CST 2020
    String text = sdf.format(date); //2020年11月16日 19时47分37秒
    Date date1 = sdf.parse(text);
    System.out.println(date1); //Mon Nov 16 19:47:37 CST 2020
    }

StringBuilder类

StringBuilder类是对字符串进行操作的一个工具类,使用StringBuilder类的好处是方便,节省空间:因为String类的底层是final修饰的字符数组,所以导致了字符串的不可改变,而StringBuilder类的底层是没有final修饰的字符数组,所以它可变。

- StringBuilder的构造方法 1.无参构造方法,将会创建一个空字符串 StringBuilder SB = new StringBuilder(); 2.有参构造方法,将会将StringBuilder对象的内容初始化为传递的参数 StringBuilder sb = new StringBuilder("abc"); - append()方法 append方法用于向StringBuilder对象的后面加上一个字符串,返回值为this,即调用该方法的对象:
1
2
StringBuilder sb = new StringBUilder();
String sb1 = sb.append("abc"); //此时sb=sb1="abc"
- delete(int start,int end)方法 delete方法用于删除StringBuilder对象的从start到end-1之间的内容,返回值也是this:
1
2
3
4
StringBuilder sb = new StringBuilder();
sb.apend("abc"); //sb="abc"
sb.delete(1,2);
System.out.println(sb); //sb="ac"
- reverse()方法 reverse方法可以将StringBuilder对象的内容反转
1
2
StringBuilder sb = new StringBuilder("abc");
sb.reverse(); //sb=cba
- toString()方法 toString方法可以将StringBuilder对象转换为String对象:
1
2
StringBuilder sb = new StringBuilder();
String str = sb.toString();

包装类

包装类就是把8种基本的数据类型封装到类中进行操作,下面以Interger为例说明:

装箱与拆箱

  • 装箱:装修就是把基本数据类型转换为包装类的对象
    Inter类就是对int数据类型的封装,其构造方法如下:

1.Interger in1 = new Interger(1); //参数为int类型
2.Interger in2 = new Interger(“1”); //参数为字符串类型,字符串类型的内容必须为int类型
在使用IDEA时用Interger的构造方法时,会发现Interger上会有一条横线,这代表这这个方法已经过时,不建议使用,所以相比于构造方法,还有2个静态方法生成Interger对象,效果等同于构造方法:
1.Interger in3 = Interger.valueOf(1);
2.Interger in4 = Interger.valueOf(“1”);

  • 拆箱:拆箱就是把包装类的对象转换为基本数据类型
    interger in1 = new Interger(1); //装箱
    int i = in1.intValue(); //拆箱

自动装箱与拆箱

自动装箱与拆箱就是自动将包装类对象和基本数据类型进行转换,Interger类的构造方法过时的原因就是jdk的新特性:自动装箱与拆箱

  • 自动装箱
    自动装箱就是省略了构造方法来将基本数据类型转为包装类对象,eg:
    Interger in = 1;
  • 自动拆箱
    自动拆箱就是将包装类对象转为基本数据类型,eg:
    Interger in = 1;
    in = in + 1;

字符串转变为其他基本数据类型

将其他类型转变为字符串有toString方法和valueOf方法,而将字符串转变为其他数据类型有parseInt,parseDouble等方法,使用方法为:

1
2
String str = "100";
int i = Interger.parseInt(str); //i=100

注意事项:

  • 转变为其他数据类型的字符串的内容必须和转变的基本数据类型相同
  • 使用parseInt只能由Interger调用,并且只能赋值给int变量,其他数据类型同理

异常

异常就是程序运行时发生的不正常情况,如打开一个不存在的文件,与错误不同(代码错误),异常一般分为两大类:

  • Error:由于java虚拟机的限制而产生的程序错误,一般无法解决。如:JVM系统内部错误、资源耗尽等严重情况。
  • Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理
    Error和Exception包含如下:
    ## 异常处理
  • Java采用抓抛模型catch-throw异常处理机制,将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁,易于维护。
  • 异常对象的生成两种方式

1.由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出
2.由开发人员手动创建:创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样

  • 捕获异常
    当一个方法抛出异常时,如果没有实现异常处理,会一直向上寻找异常解决方法,如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。
  • 选择处理异常
    如果这些异常是运行时异常,则方法的调用者有3种选择:
    (1)不理会这些声明,编译可以通过;
    (2)使用try…catch处理异常;
    (3)使用throws继续延迟处理
    如果是非运行时异常,方法调用者有两种选择:
    (1)使用try…catch处理异常;
    (2)使用throws继续延迟处理,让编译先通过。

try——catch捕获异常

  • try-catch语句
    在try语句块中,放置可能出现异常的代码。在catch语句块中是对异常对象进行处理的代码。当try中的保护代码块发生异常时,如果catch块中声明了该异常,异常将会被传递到catch块中。
  • 每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
  • 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数,但是参数不能是与已知异常无关的异常类,否则将不会执行
  • try-catch-finally语句
    try-catch-finally语句就是在try-catch语句的基础上,加上finally代码块,finally代码块是无论是否发生异常都会被执行

throw和throws

当程序没有使用try-catch语句捕获异常时,就需要用到throw和throws关键字

  • throws关键字
     throws关键字(必须在定义方法的时候使用),用于声明该方法可能会抛出的异常类型,一般用于不知道该怎么处理异常时,放任其不管,当有异常抛出时会中断该方法,而异常被抛到这个方法的调用者那里。
     使用throws关键字明确指出了该方法可能抛出的异常。throws后面如果声明多个异常类型,之间可用“,”分隔
  • throw关键字
    当创建一个异常对象之后,可以使用throw关键字抛出异常对象。throw必须在方法体中使用。
    手动抛出异常但是有时候有些错误在jvm看来不是错误,所以我们需要自己手动引发异常,这就是throw的作用,例如:
    1
    2
    3
    4
    5
    6
    7
    public void setAge(int age) throws Exception {
    if(age<0){
    Exception e = new ArithmeticException(“年龄不能为负”);
    throw e;
    }
    this.age = age;
    }
  • 注意重写方法不能抛出比被重写方法范围更大的异常类型:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class A {
    public void methodA() throws IOException {
    ……
    } }
    public class B1 extends A {
    public void methodA() throws FileNotFoundException {
    ……
    } }
    public class B2 extends A {
    public void methodA() throws Exception { //报错,抛出的异常类型比父类异常类型大
    ……
    } }

    人工抛出异常

    Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要人工创建并抛出。
  • 人工抛出异常,首先要生成异常类对象,然后通过throw语句实现抛出操作,注意,可抛出的异常必须是Throwable或其子类的实例。
  • 自定义异常类
    自定义异常类是包括人工抛出异常的自定义类:

1.一般地,用户自定义异常类都是RuntimeException的子类。
2.自定义异常类通常需要编写几个重载的构造器。
3.自定义的异常类对象通过throw抛出。
4.自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。
5.用户自己的异常类必须继承现有的异常类