Java封装字符串的类和异常

发布时间:2024年01月22日

1.API

1.1 API概述-帮助文档的使用

  • 什么是API

    API (Application Programming Interface) :应用程序编程接口

  • java中的API

    指的就是 JDK 中提供了各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们可以通过帮助文档来学习这些API如何使用。

如何使用API帮助文档 :

  • 打开帮助文档

  • 找到索引选项卡中的输入框

  • 在输入框中输入Random

  • 看类在哪个包下

  • 看类的描述

  • 看构造方法

  • 看成员方法

1.2 键盘录入字符串

java.util.Scanner类 :

nextXxx()方法:当扫描器扫描到空格,回车,tab时,会过滤掉它们并放入到扫描器的缓存中,如果下一次扫描器继续扫描时,就会将数据取出给下一个nextXxx方法。

通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串:

nextLine() : 只以 回车 作为结束输入的标记

next() : 结束标记: 以空格, 回车,tab键作为结束标记

next() 与 nextLine() 区别:

next():

  • 只有输入有效字符之后,next()方法才将其后面输入的空格键、Tab键或Enter键等视为分隔符或结束符。

  • 在输入有效字符之前遇到的空格键、Tab键或Enter键结束符,会自动去掉。

  • 一定要读取到有效字符,才会结束输入

  • next() 不能得到带有空格的字符串。

nextLine():

  • 1、只以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。

  • 2、可以获得空格。

代码实现 :

使用nextLine方法:

package com.wedu.api;

import java.util.Scanner;

public class Demo1Scanner {

    public static void main(String[] args) {
        // 1. 创建Scanner对象
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入:");
        // 2. 调用nextLine方法接收字符串
        // ctrl + alt + v : 快速生成方法的返回值
        String s = sc.nextLine();

        System.out.println(s);
    }
}

使用nextLine()的问题:nextInt和nextLine方法配合使用的时候,会导致nextLine方法没有键盘录入的机会:

package com.wedu.api;

import java.util.Scanner;

public class Demo2Scanner {
    /*
       
        建议: 今后键盘录入数据的时候, 如果是字符串和整数一起接受, 建议使用next方法接受字符串.
     */
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入整数:");
        int num = sc.nextInt(); // 10 + 回车换行
        System.out.println("请输入字符串:");
        String s = sc.nextLine();


        System.out.println(num);
        System.out.println(s);
    }
}

package demo;

import java.util.Scanner;

public class Demo2Scanner {
    /*
        

        建议: 今后键盘录入数据的时候, 如果是字符串和整数一起接受, 建议使用next方法接受字符串.
     */
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入整数:");
        int num = sc.nextInt(); // 10 + 回车换行
        System.out.println("请输入字符串:");
        String s = sc.next();


        System.out.println(num);
        System.out.println(s);
    }
}

2. String类

引言:java中定义了3个封装字符串的类,它们三个都位于java.lang包中,并提供了一系列操作字符串的方法。

  • String类定义的字符串对象一旦创建就不可修改,修改时会产生新的实例对象,而StringBuffer类和StringBuilder类的内容和长度都是可以修改的,不会产生新的对象;StringBuffer类是线程安全的,StringBuilder类是非线程安全的,但更高效

2.1 String概述

1. String 类在 java.lang 包下(使用在java.lang包下的类,不需要导包)

2. String类的对象一旦创建其内容就不可修改,修改会产生新的String实例对象;变的只是栈内存中引用对象的值

3. String 类代表字符串,Java 程序中,所有用双引号括起来的字符串(例如“abc”)都是String类的实例

2.2 String类的构造方法

使用String类的构造方法实例化String对象

常用的构造方法

使用String类的构造方法: ?

package com.wedu.string;

public class Demo2StringConstructor {


    public static void main(String[] args) {
        //使用String类的无参构造方法创建字符串对象
        String s1 = new String();
        System.out.println(s1);

        // public String(char[] chs) : 根据字符数组的内容,来创建字符串对象
        char[] chs = {'a','b','c'};
        String s2 = new String(chs);
        System.out.println(s2);

        // public String(String original) : 根据传入的字符串常量,来创建字符串对象
        String s3 = new String("123");
        System.out.println(s3);
         
    }
}

字符串的特点

  • java程序中,所有的双引号字符串(例如:"123"),都是String类型的对象

  • 虽然String实例的值不可变,但是它们可以共享

注意:

  • 总结:java程序中,所有的字符串常量都是字符串对象,并且存储在方法区的字符串常量池中

  • 虽然String实例的值都不可变,但是它们可以共享

2.4 不同方式创建字符串对象的区别

  • 通过构造方法(new)创建字符串对象

    每次 通过new创建的字符串对象 时 ,都会在堆空间中为其分配一个新的内存空间,虽然内容相同,但是地址值不同

  • 字符串字面量直接赋值的方式创建字符串对象

    系统首先会检查该字符串是否存在于字符串常量池(方法区中):

    • 如果不存在,会在字符串常量池中创建一个新的字符串对象

    • 如果存在,不会创建新的String实例而是直接引用(String实例共享的原因)

2.5 字符串的比较

通过==运算符来比较:

  • 用于比较基本数据类型时:比较的是具体的值

  • 用于比较引用数据类型时:比较的是对象的地址值

通过String类的方法 :

  • public boolean equals(String s) :比较两个字符串的内容是否相同、区分大小写

  • equalsIgnoreCase(String s):比较两个字符串的内容是否相同,忽略大小写,

  • 注意

    • 1、equals()方法对于StringBuffer类,StringBuilder类不起作用,因为只有String类重写了Object类的equals方法,改为判断两个对象的内容

代码 :

package com.wedu.stringmethod;

public class Demo1Equals {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "ABC";
        String s3 = "abc";

        // equals : 比较字符串内容, 区分大小写
        System.out.println(s1.equals(s2));
        System.out.println(s1.equals(s3));

        // equalsIgnoreCase : 比较字符串内容, 忽略大小写
        System.out.println(s1.equalsIgnoreCase(s2));
    }
}

字符串中常见的面试题

①打印字符串对象名的时候,打印的是字符串的内容,而不是字符串对象的地址呢?

  • 打印字符串对象时,会自动调用对象的toString方法,toString方法会打印字符串的内容

  • 不论是哪种引用数据类型,直接打印对象名,都会自动调用toString方法.

    • 如果此类没有重写Object类中的toString方法,那么就调用Object类的toString方法,即打印出来的是地址(全类名+@+16进制的hashCode).

    • 如果此类重写了toString方法,那么就调用重写的toString方法.

②以下str1==str3为什么结果是false?

③为什么以下代码结果是true?

答:java中存在常量优化机制,在编译时就会将"a"+"b"+"c"拼接为"abc"

java中的常量优化机制:给一个变量赋值时,如果“=”号右边是常量的表达式没有一个变量,那么就会在编译阶段就计算该常量表达式的结果。

  • 编译时常量折叠(Compile-time Constant Folding):在编译阶段,编译器会对常量表达式进行求值,并将结果直接替换到代码中。 例如,对于表达式int result = 10 * 5;编译器会在编译阶段将其优化为int result = 50;,避免了在每次运行时都进行乘法运算。

  • 字符串常量拼接优化(String Concatenation Optimization):对于字符串常量的拼接操作,编译器会尽可能地在编译阶段进行优化,减少运行时的开销。例如,对于字符串常量的拼接操作String result = "Hello" + "World";,编译器会在编译阶段将其优化为String result = "HelloWorld";,避免了在运行时使用StringBuilder(或StringBuffer)类进行拼接操作。

  • 注:常量优化机制顾名思义,只能有常量参与表达式,不能有变量参与

2.6 用户登录案例

使用到的方法:字符串的内容比较,用equals() 方法实现

案例需求 :

已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示

实现步骤 :

  1. 已知用户名和密码,定义两个字符串表示即可

  2. 键盘录入要登录的用户名和密码,用 Scanner 实现

  3. 拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。

  4. 字符串的内容比较,用equals() 方法实现

  5. 用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循

代码实现 :

package com.wedu.test;

import java.util.Scanner;

public class Test1 {
    /*
        需求:已知用户名和密码,请用程序实现模拟用户登录。
              总共给三次机会,登录之后,给出相应的提示

        思路:
        1. 已知用户名和密码,定义两个字符串表示即可
        2. 键盘录入要登录的用户名和密码,用 Scanner 实现
        3. 拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。
            字符串的内容比较,用equals() 方法实现
        4. 用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环

     */
    public static void main(String[] args) {
        // 1. 已知用户名和密码,定义两个字符串表示即可
        String username = "admin";
        String password = "123456";
        // 2. 键盘录入要登录的用户名和密码,用 Scanner 实现
        Scanner sc = new Scanner(System.in);
        // 4. 用循环实现多次机会,这里的次数明确,采用for循环实现
        for(int i = 1; i <= 3; i++){
            System.out.println("请输入用户名:");
            String scUsername = sc.nextLine();
            System.out.println("请输入密码:");
            String scPassword = sc.nextLine();
            // 3. 拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。
            if(username.equals(scUsername) && password.equals(scPassword)){
                System.out.println("登录成功");
                break;
            }else{
                if(i == 3){
                    System.out.println("您的登录次数已达到今日上限, 请明天再来");
                }else{
                    System.out.println("登录失败,您还剩余" + (3-i) +"次机会");
                }

            }
        }

    }
}

2.7 遍历字符串案例

使用到的方法:

  • public char charAt(int index):返回指定索引处的字符,字符串的索引也是从0开始的

    • 字符串的索引从0开始,到字符串长度-1结束,没有c语言里面的'/0'为结束符

  • public int length():返回此字符串的长度

案例需求 :

键盘录入一个字符串,使用程序实现在控制台遍历该字符串的每个字符

实现步骤 :

  1. 键盘录入一个字符串,用 Scanner 实现

  2. 遍历字符串,首先要能够获取到字符串中的每一个字符, public char charAt(int index):返回指定索引处的char值,字符串的索引也是从0开始的

  3. 遍历字符串,其次要能够获取到字符串的长度, public int length():返回此字符串的长度

  4. 遍历打印

代码实现 :

package com.wedu.test;

import java.util.Scanner;

public class Test2 {
    /*
        需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串

        思路:
        1. 键盘录入一个字符串,用 Scanner 实现
        2. 遍历字符串,首先要能够获取到字符串中的每一个字符
            
        3. 遍历字符串,其次要能够获取到字符串的长度
            
        4. 遍历打印
9
     */
    public static void main(String[] args) {
        //  1. 键盘录入一个字符串,用 Scanner 实现
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入:");
        String s = sc.nextLine();
        // 2. 遍历字符串,获取字符串中的每一个字符
        for(int i = 0; i < s.length(); i++){
            // i : 字符串的每一个索引
            char c = s.charAt(i);
            System.out.println(c);
        }
    }
}

2.8 统计字符次数案例

使用到的方法:

  • public char[] toCharArray( ):将当前字符串拆分为字符数组并返回

案例需求 :

键盘录入一个字符串,统计该字符串中大写字母,小写字母,数字字符出现的次数(不考虑其他字符)

实现步骤 :

  1. 键盘录入一个字符串,用 Scanner 实现

  2. 将字符串拆分为字符数组

  3. 遍历字符数

代码实现 :

package string.demo;

import java.util.Scanner;

public class StringDemo3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入一个字符串:");
        String str = sc.nextLine();
        //定义三个变量,记录每一种类型出现的次数
        int upperCaseCount = 0;
        int lowerCaseCount = 0;
        int numberCount = 0;
        //将字符串拆分为字符数组
        char[] chars = str.toCharArray();
        //遍历字符数组,得到每一个字符
        for (int i = 0;i<chars.length;i++){
            char c = chars[i];
            if(c >='A' && c <= 'Z'){
                upperCaseCount++;
            } else if (c >= 'a' && c <= 'z') {
                lowerCaseCount++;
            }else if (c >= '0' && c <= '9'){
                numberCount++;
            }
        }
        System.out.println("大写字母出现的次数:" + upperCaseCount);
        System.out.println("小写字母出现的次数:" + lowerCaseCount);
        System.out.println("数字出现的次数:" + numberCount);

    }
}

2.9 手机号屏蔽-字符串截取

需要用到的方法:

  • public String substring(int beginIndex, int endIndex):截取此字符串beginIndex到endIndex索引范围的字符串,并返回

案例需求 :

以字符串的形式从键盘接受一个手机号,将中间四位号码屏蔽 ? 最终效果为:1561234

实现步骤 :

  1. 键盘录入一个字符串,用 Scanner 实现

  2. 截取字符串前三位

  3. 截取字符串后四位

  4. 将截取后的两个字符串,中间加上进行拼接,输出结果

代码实现 :

package com.wedu.test;

import java.util.Scanner;

public class Test5 {
    /*
        需求:以字符串的形式从键盘接受一个手机号,将中间四位号码屏蔽
        最终效果为:156****1234

        思路:
        1. 键盘录入一个字符串,用 Scanner 实现
        2. 截取字符串前三位
        3. 截取字符串后四位
        4. 将截取后的两个字符串,中间加上****进行拼接,输出结果

     */
    public static void main(String[] args) {
        // 1. 键盘录入一个字符串,用 Scanner 实现
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入手机号:");
        String telString = sc.nextLine();
        // 2. 截取字符串前三位
        String start = telString.substring(0,3);
        // 3. 截取字符串后四位
        String end = telString.substring(7);
        // 4. 将截取后的两个字符串,中间加上****进行拼接,输出结果
        System.out.println(start + "****" + end);
    }
}

2.10 敏感词替换-字符串替换

案例需求 :

键盘录入一个 字符串,如果字符串中包含(TMD),则使用***替换

实现步骤 :

  1. 键盘录入一个字符串,用 Scanner 实现

  2. 替换敏感词 String replace(CharSequence target, CharSequence replacement) 将当前字符串中的target内容,用replacement替换,并返回新的字符串

  3. 输出结果

代码实现 :

package com.wedu.test;

import java.util.Scanner;

public class Test6 {
    public static void main(String[] args) {
        // 1. 从键盘录入一个字符串,用 Scanner 实现
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入:");
        String s = sc.nextLine();
        // 2. 替换敏感词
        String result = s.replace("TMD","***");
        // 3. 输出结果
        System.out.println(result);
    }
}

2.11 切割字符串

使用到的方法:

String[] split(String regex) :根据传入的字符串规则,将字符串切割成字符串数组,并将字符串数组返回

案例需求 :

以字符串的形式从键盘录入学生信息,例如:“张三 , 23”

从该字符串中切割出有效数据,封装为Student学生对象

实现步骤 :

  1. 编写Student类,用于封装数据

  2. 键盘录入一个字符串,用 Scanner 实现

  3. 根据逗号切割字符串,得到(张三)(23)

  4. 从得到的字符串数组中取出元素内容

  5. 通过Student类的有参构造方法封装为对象

  6. 调用对象getXxx方法,取出数据并打印。

代码实现 :

package pojo;

public class Student {
    private String name;
    private int age;

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
package string.demo;

import pojo.Student;

import java.util.Scanner;

public class StringDemo4 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入学生信息");
        String str = sc.nextLine();
        //用逗号分隔字符串
        String[] split = str.split(",");
        //从得到的字符串数组中取出元素内容,通过Student类的有参构造方法封装为对象
        String name = split[0];
        String ageStr = split[1];
        //将字符串转换为int类型
        int age = Integer.parseInt(ageStr);
        Student student = new Student(name, age);
        System.out.println(student);
    }
}

2.12 String方法小结

String类中的常用方法 :

public boolean equals(Object anObject) 比较两个字符串的内容,严格区分大小写

public boolean equalsIgnoreCase(String anotherString) 比较两个字符串的内容,忽略大小写

public int length() 返回此字符串的长度

public char charAt(int index) 返回索引处的字符(索引从0开始)

public char[] toCharArray() 将字符串拆分成字符数组后返回

public String[] split(String regex) 根据传入的字符串规则切割字符串,得到字符串数组并返回

public String substring(int beginIndex, int endIndex) 截取开始和结束索引范围的字符串(包含头,不包含尾),得到新的字符串返回

public String substring(int beginIndex) 从传入的索引处开始截取 到末尾,得到新的字符串

public String replace(CharSequence target, CharSequence replacement) :在当前字符串中,将旧字符序列,用新字符序列替换,并返回新的字符串

3 StringBuilder类

3.1 StringBuilder类概述

概述 : StringBuilder对象的内容是可修改的,并且修改不会产生新的对象,线程不安全,但是高效。也在java.lang包下

3.2 StringBuilder类和String类的区别

  • String类:内容是不可变的

  • StringBuilder类:内容是可变的

3.3 StringBuilder类的构造方法

常用的构造方法

方法名说明
public StringBuilder()创建一个空的StringBuilder对象,不含任何内容
public StringBuilder(String str)根据字符串的内容,创建一个可变字符串对象

示例代码

public class StringBuilderDemo01 {
    public static void main(String[] args) {
        //public StringBuilder():创建一个空白可变字符串对象,不含有任何内容
        StringBuilder sb = new StringBuilder();
        System.out.println("sb:" + sb);
        System.out.println("sb.length():" + sb.length());

        //public StringBuilder(String str):根据字符串的内容,来创建可变字符串对象
        StringBuilder sb2 = new StringBuilder("hello");
        System.out.println("sb2:" + sb2);
        System.out.println("sb2.length():" + sb2.length());
    }
}

3.4 StringBuilder常用的成员方法

  • 添加和反转方法

    方法名说明
    public StringBuilder append(任意类型)追加数据,并返回对象本身
    public StringBuilder reverse()将StringBuilder对象里的字符串反转,并返回本对象
  • 示例代码

public class StringBuilderDemo01 {
 ? ?public static void main(String[] args) {
 ? ? ? ?//创建对象
 ? ? ? ?StringBuilder sb = new StringBuilder();
?
 ? ? ? ?//链式调用
 ?      sb.append("hello").append("world").append("java").append(100);
 ? ? ? ?System.out.println("sb:" + sb);
?
 ? ? ? ?//public StringBuilder reverse():返回相反的字符序列
 ? ? ? ?sb.reverse();
 ? ? ? ?System.out.println("sb:" + sb);
 ?  }
}

3.5StringBuilder和String相互转换

都是使用StringBuilder类的方法

  • String转换为StringBuilder

public StringBuilder(String s)方法:通过StringBuilder类的构造方法可以把 String对象 转换为 StringBuilder

  • StringBuilder转换为String

    public String toString()方法:通过StringBuilder类的 toString() 可以把 此StringBuilder对象 转换为 String

  • 示例代码

package string.demo;
?
public class StringDemo4 {
 ? ?public static void main(String[] args) {
 ? ? ? ?//String转换为StringBuilder对象
 ? ? ? ?String str = "hello";
 ? ? ? ?StringBuilder sb = new StringBuilder(str);
 ? ? ? ?System.out.println(sb);
?
 ? ? ? ?sb.append(" world");
?
 ? ? ? ?//StringBuilder对象转换为String对象
 ? ? ? ?String result = sb.toString();
 ? ? ? ?System.out.println(result);
 ?  }
}

3.6 StringBuilder拼接字符串案例

案例需求 :

定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,

并在控制台输出结果。例如,数组为int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]

实现步骤 :

  1. 定义一个 int 类型的数组,用静态初始化完成数组元素的初始化

  2. 定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回。 返回值类型 String,参数列表 int[] arr

  3. 在方法中用 StringBuilder 按照要求进行拼接,并把结果转成 String 返回

  4. 调用方法,用一个变量接收结果

  5. 输出结果

代码实现 :

/*
 ? ?思路:
 ? ? ? ?1:定义一个 int 类型的数组,用静态初始化完成数组元素的初始化
 ? ? ? ?2:定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回。
 ? ? ? ? ?返回值类型 String,参数列表 int[] arr
 ? ? ? ?3:在方法中用 StringBuilder 按照要求进行拼接,并把结果转成 String 返回
 ? ? ? ?4:调用方法,用一个变量接收结果
 ? ? ? ?5:输出结果
 */
public class StringBuilderTest01 {
 ? ?public static void main(String[] args) {
 ? ? ? ?//定义一个 int 类型的数组,用静态初始化完成数组元素的初始化
 ? ? ? ?int[] arr = {1, 2, 3};
?
 ? ? ? ?//调用方法,用一个变量接收结果
 ? ? ? ?String s = arrayToString(arr);
?
 ? ? ? ?//输出结果
 ? ? ? ?System.out.println("s:" + s);
?
 ?  }
?
 ? ?//定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回
 ? ?/*
 ? ? ? ?两个明确:
 ? ? ? ? ? ?返回值类型:String
 ? ? ? ? ? ?参数:int[] arr
 ? ? */
 ? ?public static String arrayToString(int[] arr) {
 ? ? ? ?StringBuilder sb = new StringBuilder();
?
 ? ? ? ?sb.append("[");
?
 ? ? ? ?for(int i=0; i<arr.length; i++) {
 ? ? ? ? ? ?if(i == arr.length-1) {
 ? ? ? ? ? ? ? ?sb.append(arr[i]);
 ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ?sb.append(arr[i]).append(", ");
 ? ? ? ? ?  }
 ? ? ?  }
?
 ? ? ? ?sb.append("]");
?
 ? ? ? ?String s = sb.toString();
?
 ? ? ? ?return ?s;
 ?  }
}

4.异常

位于java.lang包下

4.1 异常

  • 异常的概述

    异常就是程序出现了不正常的情况

  • 异常的体系结构:java提供了大量异常类,它们都继承自java.lang.Throwable类。

    • Error是错误类,错误不是异常,通过代码无法处理。

    • Exception是异常类,它表示程序本身可以处理的错误

      • RuntimeException类及其子类:表示运行时异常

      • 除RuntimeException之外的所有异常类:表示编译时异常

4.2 编译时异常和运行时异常的区别

  • 编译时异常

    • 都是Exception类及其子类

    • 编译时必须显示处理,否则程序无法通过编译

  • 运行时异常

    • 都是RuntimeException类及其子类

    • 无需显示处理,也可以和编译时异常一样处理

  • 图示

?

异常处理的过程

当程序出现异常时:

  1. 会在异常的位置生成一个异常类的对象

  2. 看程序是否有处理异常的代码 (java中处理异常的两种方式throws try-catch)

    1. 如果有,按照处理异常的逻辑处理

    2. 如果没有,交给方法的调用者处理

  3. 方法调用会依次传给main方法,如果main方法依旧没有处理异常,就会给JVM来处理

4.3 JVM默认处理异常的方式

  • 如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理。

  • JVM默认处理异常的方式有如下两个步骤:

    • ①把异常的名称,错误原因及异常出现的位置等信息输出在控制台

    • ②让程序停止执行

4.4 查看异常信息

控制台在打印异常信息时,会打印异常类名,异常出现的原因,异常出现的位置

我们调bug时,可以根据提示,找到异常出现的位置,分析原因,修改异常代码

4.5 throws方式处理异常

  • 定义格式:在方法的括号后面写 throws 异常类型

  • 作用:使用throws关键字,声明方法可能会抛出(发生)的异常类型。

public void 方法() throws 异常类名 {
    
}
  • 示例代码
public class ExceptionDemo {
    public static void main(String[] args) throws ParseException{
        System.out.println("开始");
//        method();
          method2();

        System.out.println("结束");
    }

    //编译时异常
    public static void method2() throws ParseException {
        String s = "2048-08-09";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date d = sdf.parse(s);
        System.out.println(d);
    }

    //运行时异常
    public static void method() throws ArrayIndexOutOfBoundsException {
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]);
    }
}
  • 注意事项

    • 编译时异常必须要进行异常处理,有两种处理方案:try...catch …或者 throws,如果采用 throws 这种方案,在方法上进行显示声明,将来谁调用这个方法谁处理

    • 运行时异常因为在运行时才会发生,所以在方法后面可以不写throws,运行时出现异常默认交给jvm处理

    • 定义方法时使用了throws关键字抛出异常,将来在调用该方法时必须用try...catch处理,否则就会编译错误。

4.6 throw手动抛出异常

以前出现的异常,都是虚拟机自动创建异常对象,然后抛给调用者。现在,我们可以通过throw关键字手动抛出一个异常实例

  • 格式

    throw new 异常类("告知信息");

  • 注意

    这个格式是在方法内的,手动抛出异常之后的代码不会执行

  • throws关键字和throw关键字的区别

    throwsthrow
    用在方法声明后面,跟的是异常类名用在方法体内,跟的是异常对象名
    表示声明异常,调用该方法有可能会出现这样的异常表示手动抛出异常对象,由方法体内的语句处理
  • 示例代码

public class ExceptionDemo8 {
    public static void main(String[] args) {
        //int [] arr = {1,2,3,4,5};
        int [] arr = null;
        printArr(arr);//就会 接收到一个异常.
                        //我们还需要自己处理一下异常.
    }

    private static void printArr(int[] arr) {
        if(arr == null){
            //调用者知道成功打印了吗?
            //System.out.println("参数不能为null");
            throw new NullPointerException(); //当参数为null的时候
                                            //手动创建了一个异常对象,抛给了调用者,产生了一个异常
        }else{
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }

}

4.7 try-catch方式处理异常

  • 定义格式

try {
	可能出现异常的代码;
} catch(异常类名 变量名) {   //小括号里面声明catch所能接收的异常类型:(Exception类及其子类 e)
	处理异常的代码;
}
  • 执行流程

    • 程序从 try 里面的代码开始执行

    • 出现异常时,会在异常的位置生成一个(对应的)异常类对象,并将这个异常对象传递给catch代码块,跳转到对应的 catch 中去处理异常

    • 执行完毕之后,程序还会继续执行try-catch后面的代码

  • 示例代码

public class ExceptionDemo01 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]);
            System.out.println("这里能够访问到吗");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
        }
    }
}
  • try-catch处理异常的注意事项

    1. 如果 try 中没有遇到问题,怎么执行?

      会把try中所有的代码全部执行完,但不会执行catch里面的代码

    2. 如果 try 中遇到了问题,那么 try 下面的代码还会执行吗?

      try中 异常语句 下面的代码就不会再执行了,直接跳转到对应的catch语句中。 当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码

    3. 如果出现的问题没有被捕获(也就是catch后声明的异常类型和try中发送的异常不匹配),那么程序如何运行?

      那么try...catch就相当于没有写. 默认交给虚拟机处理.

    4. 同时可能出现多个异常怎么处理?

      出现多个异常,那么就写多个catch就可以了. 注意点:如果多个异常之间存在继承关系.那么父类一定要写在下面,因为catch代码块是按顺序捕获的

4.8 Throwable成员方法

  • 常用方法

    方法名说明
    public String getMessage()返回此 throwable 的详细消息字符串
    public String toString()返回此可抛出的简短描述
    public void printStackTrace()把异常的错误信息输出在控制台
  • 示例代码

public class ExceptionDemo02 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]); //new ArrayIndexOutOfBoundsException();
            System.out.println("这里能够访问到吗");
        } catch (ArrayIndexOutOfBoundsException e) { //new ArrayIndexOutOfBoundsException();
//            e.printStackTrace();

            //public String getMessage():返回此 throwable 的详细消息字符串
//            System.out.println(e.getMessage());
            //Index 3 out of bounds for length 3

            //public String toString():返回此可抛出的简短描述
//            System.out.println(e.toString());
            //java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3

            //public void printStackTrace():把异常的错误信息输出在控制台
            e.printStackTrace();
//            java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
//            at com.wedu_02.ExceptionDemo02.method(ExceptionDemo02.java:18)
//            at com.wedu_02.ExceptionDemo02.main(ExceptionDemo02.java:11)

        }
    }
}

4.9 异常的练习?

  • 需求

    键盘录入学生的姓名和年龄,其中年龄为18 - 25岁,超出这个范围是异常数据不能赋值.需要重新录入,一直录到正确为止

  • 实现步骤

    1. 创建学生对象

    2. 键盘录入姓名和年龄,并赋值给学生对象

    3. 如果是非法数据就再次录入

  • 代码实现

    学生类

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age >= 18 && age <= 25){
            this.age = age;
        }else{
            //当年龄不合法时,产生一个异常
            throw new RuntimeException("年龄超出了范围");
        }
    }

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

测试类

package pojo;

import java.util.Scanner;

public class ExceptionDemo12 {
    public static void main(String[] args) {
        // 键盘录入学生的姓名和年龄,其中年龄为 18 - 25岁,
        // 超出这个范围是异常数据不能赋值.需要重新录入,一直录到正确为止。

        Student s = new Student();
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入姓名");
        String name = sc.nextLine();
        s.setName(name);

       while(true){
           System.out.println("请输入年龄");
           String ageStr = sc.nextLine();
           try {
               int age = Integer.parseInt(ageStr);  //不能解析,匹配NumberFormatException异常
               s.setAge(age);    //超出范围,匹配RuntimeException异常
               break;    //年龄正常后,跳出循环
           } catch (NumberFormatException e) {
               System.out.println("请输入一个整数");
           }catch (RuntimeException e){
               System.out.println(e.getMessage());
           }
       }

        System.out.println(s);
    }
}

4.10 自定义异常

  • 自定义异常概述

    • Java中定义的异常不能满足我们的需求时,我们可以自定义异常

    • 自定义异常类必须继承Exception类或其子类

  • 实现步骤

    1. 定义异常类,继承Exception或其子类

    2. 提供空参构造

    3. 提供带参构造

  • 代码实现

    自定义异常类型:代表学生年龄超出范围的异常

public class AgeOutOfBoundsException extends RuntimeException {
    public AgeOutOfBoundsException() {
    }

    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}

学生类

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age >= 18 && age <= 25){
            this.age = age;
        }else{
            //如果Java中提供的异常不能满足我们的需求,我们可以使用自定义的异常
            throw new AgeOutOfBoundsException("年龄超出了范围");
        }
    }

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

测试类

package pojo;

import java.util.Scanner;

public class ExceptionDemo12 {
    public static void main(String[] args) {
        // 键盘录入学生的姓名和年龄,其中年龄为 18 - 25岁,
        // 超出这个范围是异常数据不能赋值.需要重新录入,一直录到正确为止。

        Student s = new Student();
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入姓名");
        String name = sc.nextLine();
        s.setName(name);

       while(true){
           System.out.println("请输入年龄");
           String ageStr = sc.nextLine();
           try {
               int age = Integer.parseInt(ageStr);  //不能解析,匹配NumberFormatException异常
               s.setAge(age);    //超出范围,匹配RuntimeException异常
               break;    //年龄正常后,跳出循环
           } catch (NumberFormatException e) {
               System.out.println("请输入一个整数");
           }catch (AgeOutOfBoundsException e){
               System.out.println(e.getMessage());
           }
       }

        System.out.println(s);
    }
}

4.11 finally代码块

概述

  • :无论程序是否发生异常,还是使用return语句返回,finally代码块都会执行。

作用:

  • finally语句块一般用来编写 无论是否发生异常都必须执行的代码

如果在try...catch中执行了System.exit(0)语句,finally代码块才不会执行

  • System.exit(0)表示退出当前java虚拟机,java虚拟机停止了,任何代码就都不能执行了。

文章来源:https://blog.csdn.net/m0_61925586/article/details/135753133
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。