day5--java基础编程:异常,内部类

6 异常

6.1 异常概述

  • 出现背景
    在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。

  • 异常
    在Java语言中,将程序执行中发生的不正常情况称为"异常"。(开发过程中的语法错误和逻辑错误不是异常)

6.2 异常体系

在这里插入图片描述
说明

  • Throwable:在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)
    • Error:严重问题,不需要处理
    • Exception:称为异常类,它表示程序本身可以处理的问题
      • RuntimeException:在编译期是不检查的,开发中,通常就不进行显示的处理了。一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。

      • 非RuntimeException:编译期就必须处理的,否则程序不能通过编译,就更不能正常运行了

6.2.1 Error举例

package com.atguigu.java;
/*
 * Error:
 * Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。
 * 
 * 一般不编写针对性的代码进行处理。出现问题只能改代码。
 * 
 * 
 */
public class ErrorTest {
	public static void main(String[] args) {
		//1.栈溢出:java.lang.StackOverflowError
//		main(args);
		//2.堆溢出:java.lang.OutOfMemoryError 
		Integer[] arr = new Integer[1024*1024*1024];
		
	}
}

6.2.2 Exception举例

package com.atguigu.java1;

import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;

import org.junit.Test;

/*
 * 一、异常体系结构
 * 
 * java.lang.Throwable
 * 		|-----java.lang.Error:一般不编写针对性的代码进行处理。
 * 		|-----java.lang.Exception:可以进行异常的处理
 * 			|------编译时异常(checked)
 * 					|-----IOException
 * 						|-----FileNotFoundException
 * 					|-----ClassNotFoundException
 * 					|-----SQLException sql异常
 * 			|------运行时异常(unchecked,RuntimeException)
 * 					|-----NullPointerException
 * 					|-----ArrayIndexOutOfBoundsException
 * 					|-----ClassCastException
 * 					|-----NumberFormatException 数字格式异常
 * 					|-----InputMismatchException 输入不匹配异常
 * 					|-----ArithmeticException 算术异常
 * 
 * 
 * 
 * 面试题:常见的异常都有哪些?举例说明
 */
public class ExceptionTest {
	
	//******************以下是编译时异常***************************
	@Test
	public void test7(){
//		File file = new File("hello.txt");
//		FileInputStream fis = new FileInputStream(file);
//		
//		int data = fis.read();
//		while(data != -1){
//			System.out.print((char)data);
//			data = fis.read();
//		}
//		
//		fis.close();
		
	}
	
	//******************以下是运行时异常***************************
	//ArithmeticException
	@Test
	public void test6(){
		int a = 10;
		int b = 0;
		System.out.println(a / b);
	}
	
	//InputMismatchException
	@Test
	public void test5(){
		Scanner scanner = new Scanner(System.in);
		int score = scanner.nextInt();
		System.out.println(score);
		
		scanner.close();
	}
	
	//NumberFormatException
	@Test
	public void test4(){
		
		String str = "123";
		str = "abc";
		int num = Integer.parseInt(str);
		
		
		
	}
	
	//ClassCastException
	@Test
	public void test3(){
		Object obj = new Date();
		String str = (String)obj;
	}
	
	//IndexOutOfBoundsException
	@Test
	public void test2(){
		//ArrayIndexOutOfBoundsException
//		int[] arr = new int[10];
//		System.out.println(arr[10]);
		//StringIndexOutOfBoundsException
		String str = "abc";
		System.out.println(str.charAt(3));
	}
	
	//NullPointerException
	@Test
	public void test1(){
		
//		int[] arr = null;
//		System.out.println(arr[3]);
		
		String str = "abc";
		str = null;
		System.out.println(str.charAt(0));
		
	}
	
	
}

6.3 JVM的默认处理方案

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

  • 把异常的名称异常原因异常出现的位置等信息输出在了控制台(由下图可知)
  • 程序停止执行(由下图可知,结束并没有输出到控制台)

在这里插入图片描述

6.4 Throwable的成员方法

在这里插入图片描述
在这里插入图片描述

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

Java程序的执行分为编译时过程和运行时过程。有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等。

在这里插入图片描述

因此,根据异常可能出现的阶段,可以将异常分为:

  • 编译时期异常(即checked异常、受检异常):在代码编译阶段,编译器就能明确警示当前代码可能发生(不是一定发生)xx异常,并明确督促程序员提前编写处理它的代码。如果程序员没有编写对应的异常处理代码,则编译器就会直接判定编译失败,从而不能生成字节码文件。通常,这类异常的发生不是由程序员的代码引起的,或者不是靠加简单判断就可以避免的,例如:FileNotFoundException(文件找不到异常)。

    • 必须显示处理,否则程序就会发生错误,无法通过编译
    • 如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为运行时异常。
  • 运行时期异常(即runtime异常、unchecked异常、非受检异常):在代码编译阶段,编译器完全不做任何检查,无论该异常是否会发生,编译器都不给出任何提示。只有等代码运行起来并确实发生了xx异常,它才能被发现。通常,这类异常是由程序员的代码编写不当引起的,只要稍加判断,或者细心检查就可以避免。

    • 无需显示处理,也可以和编译时异常一样处理
    • 前面使用的异常都是RuntimeException类或是它的子类,这些类的异常的特点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过 ( 但运行时会发生异常使得程序运行终止 )。所以,对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。
    • java.lang.RuntimeException类及它的子类都是运行时异常。比如:ArrayIndexOutOfBoundsException数组下标越界异常,ClassCastException类型转换异常。

在这里插入图片描述

6.6 异常处理概述

  • 为什么要进行异常处理
    因为java虚拟机的默认处理方案,会让程序在出现异常的地方直接结束掉。而在实际开发中我们程序某一个部分出现问题了,它不应该影响后续的执行,所以我们要自己处理异常。

  • 如果程序出现了问题,我们需要自己来处理,有两种方案:

    • try … catch …
    • throws
  • 异常处理抓抛模型

    • 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个异常对象,并将对象抛出。一旦抛出异常对象后,其后的代码就不会再执行。
      • 关于异常对象的产生
        ① 系统自动生成的异常对象
        ② 手动的生成一个异常对象,并抛出(throw)
    • 过程二:“抓”:可以理解为异常的处理方式:
      • try-catch-finally :真正的处理异常,后续代码会执行
      • throws:没有真正处理异常仅仅是抛给调用者,后续代码不会执行
  • 如果程序出现了异常没有处理:

    • 最终会跑到main方法中,main方法由jvm虚拟机管理,jvm虚拟机处理异常是杀死进程,结束掉程序。
  • 异常处理的意义:

    • 程序出现异常后仍能正确执行,不影响程序运行。

6.7 异常处理方式一:(try-catch-finally)

说明

  • try-catch-finally,又叫做捕获方式。

语法

//try只能而且出现一次  catch可以出现0到N次    finally出现0到1次  
try{
  	//可能出现异常的代码
  
  }catch(异常类型1 变量名1){
  		//处理异常的方式1
  }catch(异常类型2 变量名2){
  		//处理异常的方式2
  }catch(异常类型3 变量名3){
  		//处理异常的方式3
  }
 ....
  finally{
  		//一定会执行的代码
  }

执行流程:

  • 程序从try里面的代码开始执行
  • 出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统
  • 当Java运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理
  • 执行完毕之后,程序还可以继续往下执行

6.7.1 测试1:try-catch结构

package com.shuai;
/**
 * 1.finally是可选的。
 * 
 * 2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常就会生成一个对应类的异常对象,根据
 *   此对象的类型到catch中进行匹配。
 * 
 * 3.一旦try中的异常对象匹配到一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的
 *   try-catch结构(在没有写finally的情况),继续执行其后的代码。
 * 
 * 4.catch中的异常类型如果没有父子类关系,谁声明在上,,谁声明在下无所谓。
 *   catch中的异常类型如果有父子类关系,则要求子类在上父类在下,从小到大的排序,否则报错。
 * 
 * 5.常用的异常处理方式:1)String e.getMessage()  2)void e.printStackTrace() 这个比较常用
 * 
 * 6.在try结构中定义的变量,出了try结构以后就不能使用了。想要使用把变量声明在外面,赋值在结构里面。
 * 
 * 7.为什么不写成int num;而要多赋值个0,首先因为变量想要输出首先要声明好后并且有初始化的值,而局部变量
 *   没有默认值,一旦try中的程序报错,直接输出num,num没有值程序会报错,为了避免这种情况,加上0不会影响程
 *   序也不会因为异常情况导致num没有值而保错。
 * 
 *8.try-catch-finally也可以嵌套
 
 * 体会1:使用try-catch-finally结构处理编译异常时,使得编译时不会报错,但运行时有可能报错,相当于try-catch-finally
 *       将编译时可能出现的异常延迟到运行时出现。
 * 体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
 *       针对于编译时异常,我们说一定要考虑异常的处理。(如果与前端页面交互提示时,仍要处理。)
 */
public class Demo05 {
    public static void main(String[] args) {
        String str="123";
        str="abc";
        /*为什么不写成int num;而要多赋值个0,首先因为变量想要输出首先要声明好后并且有初始化的值,
        而局部变量没有默认值,一旦try中的程序报错,直接输出num,num没有值程序会报错,为了避免这种
        情况,加上0不会影响程序也不会因为异常情况导致num没有值而报错。*/

        int num = 0;
        try {
            num=Integer.parseInt(str);
            //int num=Integer.parseInt(str);//出现异常
            System.out.println("hell0-------01");//不会输出
        } catch (NullPointerException  e) { //习惯上异常名叫 e
            System.out.println("出现了空指针转换异常");
        }catch (NumberFormatException e) { //一个类型e只在一个catch中有效,所以不同的catch中名字可以相同。
            //System.out.println("出现了数值转换异常"); 一般不这样写,用异常类方法代替,如下:

            //String e.getMessage():此方法返回值是String类型,想要看就要输出它。因为有返回值就要定义变量进行接收,查看就要输出,这里简写一块了。
            // System.out.println(e.getMessage()); //打印异常信息

            //void e.printStackTrace():此方法返回值是void类型,不需要在进行输出就能查看。
            e.printStackTrace();  //打印异常详细信息


        }catch (Exception e){
            System.out.println("出现了异常");//不会输出
        }
        //System.out.println(num);报错不能调用,因为变量num在catch中定义的,作用域在catch中。
        System.out.println(num);
        System.out.println("hell0-------02");//会输出

    }

}

6.7.2 测试2:finally使用场景分析

  • 因为异常会引发程序跳转,从而会导致有些语句执行不到。而程序中有一些特定的代码无论异常是否发生,都需要执行。例如,数据库连接、输入流输出流、Socket连接、Lock锁的关闭等,这样的代码通常就会放到finally块中。所以,我们通常将一定要被执行的代码声明在finally中。

    • 唯一的例外,使用 System.exit(0) 来终止当前正在运行的 Java 虚拟机。
  • 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。

  • finally语句和catch语句是可选的,但finally不能单独使用。

    try{
         
    }finally{
        
    } 
    
package com.atguigu.java1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.junit.Test;

/*
 * try-catch-finally中finally的使用:
 * 
 * 
 * 1.finally是可选的
 * 
 * 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有
 * return语句等情况。
 * 
 * 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的
 *   释放。此时的资源释放,就需要声明在finally中。
 * 
 * 
 * 
 */
public class FinallyTest {
	
	
	@Test
	public void test2(){
		FileInputStream fis = null;
		try {
			File file = new File("hello1.txt");//可能报FileNotFoundException (编译时异常)
			fis = new FileInputStream(file);
			
			int data = fis.read();//可能报IOException(编译时异常)
			while(data != -1){
				System.out.print((char)data);
				data = fis.read();//可能报IOException
			}
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
			//出现FileNotFoundException,文件创建不成功fis为null,
			//  之后走第一个catch处理异常,
			//  之后进入到finally当中,fis为null会出现空指针异常,加上if判断避免这一情况。
				if(fis != null)
					fis.close();//可能报IOException
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	@Test
	public void testMethod(){
		int num = method();
		System.out.println(num);
	}
	
	public int method(){
		
		try{
			int[] arr = new int[10];
			System.out.println(arr[10]);
			return 1;
		}catch(ArrayIndexOutOfBoundsException e){
			e.printStackTrace();
			return 2;
		}finally{
			System.out.println("我一定会被执行");
			return 3;
		}
		
		
	}
	
	@Test
	public void test1(){
		try{
			int a = 10;
			int b = 0;
			System.out.println(a / b);
			
		}catch(ArithmeticException e){
			e.printStackTrace();
			
//			int[] arr = new int[10];
//			System.out.println(arr[10]);
			
		}catch(Exception e){
			e.printStackTrace();
		}
//		System.out.println("我好帅啊!!!~~");
		
		finally{
			System.out.println("我好帅啊~~");
		}
		
	}
	
}

快捷键:选中报错包裹的代码—>Suround With—>Try/catch Block
在这里插入图片描述
注意:修改代码关闭流放在finally中。

在这里插入图片描述

6.8 异常处理方式二:(throws)

  • 如果在编写方法体的代码时,某句代码可能发生某个编译时异常,不处理编译不通过,但是在当前方法体中可能不适合处理无法给出合理的处理方式,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

在这里插入图片描述

  • 具体方式:在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
  • 执行流程
    • "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
    • 一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。
    • 异常代码后续的代码,就不再执行!

在这里插入图片描述

6.8.1 throws基本格式

声明异常格式:

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }	

在throws后面可以写多个异常类型,用逗号隔开。

  • 注意:这个格式是跟在方法的括号后面的

举例:

public void readFile(String file)  throws FileNotFoundException,IOException {
	...
	// 读文件的操作可能产生FileNotFoundException或IOException类型的异常
	FileInputStream fis = new FileInputStream(file);
    //...
}

6.8.2 测试throws关键字

package com.atguigu.java1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
 * 异常处理的方式二:throws + 异常类型
 *
 * 1. "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
 *     一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常
 *     类型时,就会被抛出。异常代码后续的代码,就不再执行!
 *
 * 2. 体会:try-catch-finally:真正的将异常给处理掉了。
 *        throws的方式只是将异常抛给了方法的调用者。  并没有真正将异常处理掉。
 *
 */
public class ExceptionTest2 {


    public static void main(String[] args){
        try{
            method2();

        }catch(IOException e){
            e.printStackTrace();
        }

//		method3();

    }


    public static void method3(){
        try {
            method2();
        } catch (IOException e) {
        //IOException 是FileNotFoundException异常的父类,这里处理方式又相同所以只需要写一个catch即可。
            e.printStackTrace();
        }
    }

    //method1抛出了2个异常,这里method2调用为什么只抛出一个异常呢??
    //如果2个异常是父子类关系,并且处理异常的方式相同,比如都是e.printStackTrace();,那么只需要抛出一个异常即可。
    public static void method2() throws IOException{
        method1();
    }


    public static void method1() throws FileNotFoundException,IOException{
        File file = new File("hello1.txt");
        FileInputStream fis = new FileInputStream(file);

        int data = fis.read();
        while(data != -1){
            System.out.print((char)data);
            data = fis.read();
        }

        fis.close();

        System.out.println("hahaha!");
    }


}

在这里插入图片描述
在这里插入图片描述

6.8.3 方法重写中throws的要求

方法重写时,对于方法签名是有严格要求的。复习:

1)方法名必须相同
(2)形参列表必须相同
(3)返回值类型
	- 基本数据类型和void:必须相同
	- 引用数据类型:<=4)权限修饰符:>=,而且要求父类被重写方法在子类中是可见的
(5)不能是staticfinal修饰的方法

此外,对于throws异常列表要求:

  • 如果父类被重写方法的方法签名后面没有 “throws 编译时异常类型”,那么重写方法时,方法签名后面也不能出现“throws 编译时异常类型”。
  • 如果父类被重写方法的方法签名后面有 “throws 编译时异常类型”,那么重写方法时,throws的编译时异常类型必须 <= 被重写方法throws的编译时异常类型,或者不throws编译时异常。
  • 方法重写,对于“throws 运行时异常类型”没有要求。
package com.atguigu03._throws;

import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * ClassName: OverrideTest
 * Description:
 *
 * @Author 尚硅谷-宋红康
 * @Create 9:34
 * @Version 1.0
 */
public class OverrideTest {
    public static void main(String[] args) {

        Father f = new Son();

        try{
            f.method1();
        }catch(IOException e){
            e.printStackTrace();
        }

        Number n = f.method4();



    }
}

class Father{

    public void method1() throws IOException {

    }

    public void method2(){

    }

    public void method3(){

    }

    public Number method4(){
        return null;
    }

}

class Son extends Father{
    /**
     * 规则1:子类方法抛出的异常要小于等于父类的。
     *   main方法中的多态创建对象,编译看左边调用的是父类方法的声明,
     *   所以他try-catch处理的是父类的异常,运行期实际上执行的是子类重写的方法体,
     *   此时子类抛出的异常大于所可以处理的异常(也就是说此时抛出的异常不在try-catch可以处理
     *   异常的范围内),那么try-catch处理异常就没有什么意义了。
     */
    @Override
    public void method1() throws FileNotFoundException {

    }

    /**
     * 规则2:父类的方法没有throws抛出异常,那么子类重写的方法也不能throws异常。(针对编译期异常)
     *   此时子类重写的方法有编译期异常,只能使用try-catch处理。
     */
//    @Override
//    public void method2() throws FileNotFoundException{
//
//    }

    /**
     * 规则3:父类的方法没有throws抛出异常,但是子类重写的方法仍然可以throws异常。(针对运行时异常)
     *   throws后面也可以写运行时异常类型,只是运行时异常类型,写或不写对于编译器和程序执行来说都没有任何区别。
     *   如果写了,唯一的区别就是调用者调用该方法后,使用try...catch结构时,IDEA可以获得更多的信息,需要添加哪种catch分支。
     *   总结:运行时异常在编写器不会报错,你抛不抛出都一个样。
     */
    @Override
    public void method3() throws RuntimeException{

    }

    /**
     * 同理:子类重写方法的返回值如果是引用类型,要小于等于父类的。
     *   Number n = f.method4();
     *   f.method4()  多态调用的是父类的方法返回Number类型,所以用Number来接收
     *   之后运行时输出的是子类重写后的方法,此时返回的类型只能是小于等于Number才可以接收,
     *   如果返回比Number类型还要大的比如Object类型,Number无法接收Object类型会报错。
     *
     * @return
     */
    @Override
    public Integer method4(){
        return null;
    }

}

6.9 两种异常处理方式的选择

前提:对于异常,使用相应的处理方式。此时的异常,主要指的是编译时异常。

  • 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。
  • 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。(针对编译器异常
  • 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。

6.10 手动抛出异常对象(throw)

  • 使用背景:在实际开发中,如果出现不满足具体场景的代码问题,我们就有必要手动抛出一个指定类型的异常对象。

  • 作用:用于在方法体内部抛出异常对象

  • 关于异常对象的产生

    • 系统自动生成的异常对象
    • 手动的生成一个异常对象,并抛出(throw)
  • 注意

    • 手动抛出异常如果是运行时异常对象可以不用处理,如果抛出的是编译期异常对象一定要处理异常。
    • throw语句会导致程序执行流程被改变,throw语句是明确抛出一个异常对象,因此它下面的代码将不会执行
    • 如果当前方法没有try…catch处理这个异常对象,throw语句就会代替return语句提前终止当前方法的执行,并返回一个异常对象给调用者。

6.10.1 没有手动抛出异常对象之前

在这里插入图片描述

package com.atguigu.java2;

public class StudentTest {
	
	public static void main(String[] args) {
		
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);//直接输出对象的引用实际上时输出.toString()方法。
		
	}
	
}


class Student{
	
	private int id;
	
	public void regist(int id)  {
		if(id > 0){
			this.id = id;
		}else{
			/*
			 * 即便你此时输入的是负数不合理,只能给你个提示输入的数据错误,但是这样写还是把结果输出了。
			 * 正常来讲:应该是输入的是负数不合理,程序直接报错不会再向下执行输出结果,所以这里一般是手动抛出异常对象,
			 *        一旦数据不合理直接报异常,程序停止运行。
			 */
			System.out.println("您输入的数据非法!");
			
		}
		
	}

	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
	
	
}

6.10.2 手动抛出异常对象之后

package com.atguigu.java2;

public class StudentTest {
	
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
//			e.printStackTrace();

         		
			/*throw new RuntimeException("您输入的数据非法!")里面的值是由RuntimeException的有参构造方法中super(xxx)调用--->
			 * 父类的Exception的有参构造方法中super(xxx)调用----> Throable中的有参构造方法,在Throable类中通过有参构造方法
			 * 赋值(this.detailMessage = message;) 给成员变量: private String detailMessage;为:您输入的数据非法!
			 * 即:值最终赋值给Throable类的成员变量detailMessage。
			 * 			 					 	
			 * void e.getMessage() 底层源码:
			 * public String getMessage() {
             *     return detailMessage;
             *  }
			 * 这个方法getMessage()的返回值为Throable类中的detailMessage成员变量,所以输出结果为:您输入的数据非法! */
			System.out.println(e.getMessage());
		}
	}
	
}


class Student{
	
	private int id;
	
	public void regist(int id) throws Exception {
		if(id > 0){
			this.id = id;
		}else{
//			System.out.println("您输入的数据非法!");
			//手动抛出异常对象
//			throw new RuntimeException("您输入的数据非法!");抛出运行时异常,调用方不用处理。
//			throw new Exception("您输入的数据非法!");Exception包含编译器异常,所以一旦throws抛出,在调用方一定要进行处理编译器异常。
			throw new MyException("不能输入负数");//抛出的是自定义异常
			//错误的 String不是异常类
//			throw new String("不能输入负数");
//return 0;如果方法有返回值且不影响结果,则可以用throw代替return,因为如果报异常也会结束方法运行。

		}
		
	}

	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
	
	
}

6.10.3 throw和throws的区别

在这里插入图片描述

6.11 自定义异常

使用背景:

  • 目的是系统提供的异常类型是有限的,如果程序产生的异常不在提供中,可以抛出自己定义的异常使得异常类型更加精确(一般和throw连用)
  • 一般这个自定义异常类,定义这个类时要做到见名知意

格式:
在这里插入图片描述
范例:
在这里插入图片描述

6.11.1 测试

和案例6.10.2连用。

package com.atguigu.java2;
/*
 * 如何自定义异常类?
 * 1.继承于现有的异常结构:RuntimeException 、Exception(包含运行和编译器异常,
 *    所以编译期就要进行处理异常)
 *   RuntimeException异常:只需要继承RuntimeException异常或者它的子类。
 *   Checked异常:只需要继承Exception异常或者Exception的子类,但需要除RuntimeException之外。
 * 2.提供全局常量:serialVersionUID
 * 3.提供重载的构造器,建议大家提供至少两个构造器,一个是无参构造,
 *   一个是(String message)构造器。
 * 
 */
public class MyException extends Exception{
	
	static final long serialVersionUID = -7034897193246939L;
	
	//无参构造方法
	public MyException(){
		
	}
	//有参构造方法
	public MyException(String msg){
	/*
		 * 调用父类的有参构造,因为上面的案例中处理异常使用的是e.getMessage()方法,
		 * 这个方法返回值为Throable的成员变量dtailMessage属性。而现在自定义异常对象
		 * 的通过有参构造方法创建对象并传参,这个自定义异常对象的参数不是自己定义的而是定义
		 * 在顶级父类Throable中的成员变量,所以想要赋值给父类Throable的属性需要在子类
		 * 的构造方法中通过super一步一步调用父类构造方法,最终在Throwable的构造方法中把
		 * 值赋值给成员变量dtailMessage。
		 */
		super(msg);
	}
}

7. 内部类(InnerClass)

8.1 概述

8.1.1 什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类(InnerClass),类B则称为外部类(OuterClass)

8.1.2 为什么要声明内部类呢

具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。

总的来说,遵循高内聚、低耦合的面向对象开发原则。

  • 内部类使用举例:
    • Thread类内部声明了State类,表示线程的生命周期
    • HashMap类中声明了Node类,表示封装的key和value

8.1.3 内部类的分类

根据内部类声明的位置(如同变量的分类),我们可以分为:

在这里插入图片描述

8.2 成员内部类

8.2.1 概述

如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。

语法格式:

[修饰符] class 外部类{
    [其他修饰符] [static] class 内部类{
    }
}

成员内部类的使用特征,概括来讲有如下两种角色:

  • 成员内部类作为类的成员的角色
    • 和外部类不同,Inner class还可以声明为private或protected;
    • 可以调用外部类的结构。(注意:在静态内部类中不能使用外部类的非静态成员
    • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
  • 成员内部类作为类的角色
    • 可以在内部定义属性、方法、构造器等结构
    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以声明为abstract类 ,因此可以被其它的内部类继承
    • 可以声明为final的,表示不能被继承
    • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)

注意点

  1. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式

  2. 成员内部类可以直接使用外部类的所有成员,包括私有的数据

  3. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

8.2.2 创建成员内部类对象

  • 实例化静态内部类
外部类名.静态内部类名 变量 = 外部类名.静态内部类名();
变量.非静态方法();
  • 实例化非静态内部类
外部类名 变量1 = new 外部类();
外部类名.非静态内部类名 变量2 = 变量1.new 非静态内部类名();
变量2.非静态方法();

8.2.3 举例

package com.atguigu09.inner;

/**
 * ClassName: OuterClassTest
 * Description:
 *
 * @Author 尚硅谷-宋红康
 * @Create 10:31
 * @Version 1.0
 */
public class OuterClassTest {
    public static void main(String[] args) {

        //1. 创建Person的静态的成员内部类的实例
        Person.Dog dog = new Person.Dog();
        dog.eat();  //狗吃骨头
        //2. 创建Person的非静态的成员内部类的实例
//        Person.Bird bird = new Person.Bird(); //报错

        Person p1 = new Person();
        Person.Bird bird = p1.new Bird();//正确的
        bird.eat(); //鸟吃虫子
        bird.show("黄鹂");

        bird.show1();

    }

}
class Person{ //外部类
    String name = "Tom";
    int age = 1;
    //静态的成员内部类
    static class Dog{
        public void eat(){
            System.out.println("狗吃骨头");
        }
    }

    //非静态的成员内部类
    class Bird{
        String name = "啄木鸟";
        public void eat(){
            System.out.println("鸟吃虫子");
        }

        //3.内部类的方法中调用属性的情况:
        public void show(String name){ //内部类中的方法
            //成员内部类可以直接使用外部类的所有成员,包括私有的数据  (调用外部类中的成员变量)
            System.out.println("age = " + age);//age = 1    省略了Person.this
            //name相同时遵循就近原则   (调用内部类方法中的形参)
            System.out.println("name = " + name); //name = 黄鹂
            //通过this来区分成员和局部  (调用内部类的成员变量)
            System.out.println("name = " + this.name); //name = 啄木鸟
            //不是父子类关系不能用super (调用外部类的成员变量)
            System.out.println("name = " + Person.this.name);//name = Tom
        }

        //4.内部类的方法中调用方法的情况:
        public void show1(){ //内部类中的方法
            //在内部类的方法中调用内部类的另一个方法
            eat();   //鸟吃虫子
            this.eat();//this可省略   鸟吃虫子
            //在内部类的方法中调用外部类的方法
            Person.this.eat();  //人吃饭
        }
    }

    public void eat(){ //外部类中的方法
        System.out.println("人吃饭");
    }


    public void method(){//外部类中的方法(普通方法中定义的)

        //局部内部类
        class InnerClass1{

        }
    }

    public Person(){//外部类中的方法(构造方法中定义的)
        //局部内部类
        class InnerClass1{

        }
    }

    {
        //局部内部类 (外部类中的代码块中定义的)
        class InnerClass1{

        }
    }


}

8.3 局部内部类

8.3.1 非匿名局部内部类

语法格式:

[修饰符] class 外部类{
    [修饰符] 返回值类型  方法名(形参列表){
            [final/abstract] class 内部类{
    	}
    }    
}
  • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
    • 这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
  • 和成员内部类不同的是,它前面不能有权限修饰符等
  • 局部内部类如同局部变量一样,有作用域
  • 局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法

举例:

package com.atguigu09.inner;

/**
 * ClassName: OuterClassTest1
 * Description:
 *
 * @Author 尚硅谷-宋红康
 * @Create 10:55
 * @Version 1.0
 */
public class OuterClassTest1 {

    //说明:局部内部类的使用
    public void method1(){
        //局部内部类
        class A{
           //可以声明属性、方法等

        }

    }


    //开发中的场景:调用方法的时候返回一个实例,这个实例的类型是接口
    public Comparable getInstance(){

        //提供了实现了Comparable接口的类
        //方式1:提供了接口的实现类的对象
//        class MyComparable implements Comparable{

//            @Override
//            public int compareTo(Object o) {
//                return 0;
//            }
//        }
//
//        MyComparable m = new MyComparable();
//        return m;


        //方式2:提供了接口的实现类的匿名对象
//        class MyComparable implements Comparable{
//
//            @Override
//            public int compareTo(Object o) {
//                return 0;
//            }
//        }
//
//        return new MyComparable();

        //方式3:提供了接口的匿名实现类的对象
//        Comparable c = new Comparable(){
//            @Override
//            public int compareTo(Object o) {
//                return 0;
//            }
//        };
//        return c;

        //方式4:提供了接口的匿名实现类的匿名对象
        return new Comparable(){
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };

    }


}

8.3.2 匿名内部类

因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。

new 父类([实参列表]){
    重写方法...
}
new 父接口(){
    重写方法...
}

举例1:使用匿名内部类的对象直接调用方法:

interface A{
	void a();
}
public class Test{
    public static void main(String[] args){
    	new A(){
			@Override
			public void a() {
				System.out.println("aaaa");
			}
    	}.a();
    }
}

举例2:通过父类或父接口的变量多态引用匿名内部类的对象

interface A{
	void a();
}
public class Test{
    public static void main(String[] args){
    	A obj = new A(){
			@Override
			public void a() {
				System.out.println("aaaa");
			}
    	};
    	obj.a();
    }
}

举例3:匿名内部类的对象作为实参

interface A{
	void method();
}
public class Test{
    public static void test(A a){
    	a.method();
    }
    
    public static void main(String[] args){
    	test(new A(){

			@Override
			public void method() {
				System.out.println("aaaa");
			}
    	});
    }   
}

8.4 练习

练习:判断输出结果为何?

public class Test {
    public Test() {
        Inner s1 = new Inner();
        s1.a = 10;
        Inner s2 = new Inner();
        s2.a = 20;
        Test.Inner s3 = new Test.Inner();
        System.out.println(s3.a);
    }
    class Inner {
        public int a = 5;
    }
    public static void main(String[] args) {
        Test t = new Test();
        Inner r = t.new Inner();
        System.out.println(r.a);
    }
}

练习2:

编写一个匿名内部类,它继承Object,并在匿名内部类中,声明一个方法public void test()打印尚硅谷。

请编写代码调用这个方法。

package com.atguigu.test01;

public class Test01 {
	public static void main(String[] args) {
		new Object(){
			public void test(){
				System.out.println("尚硅谷");
			}
		}.test();
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/286210.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

磁盘管理与文件系统

步骤&#xff1a; 1.建立分区&#xff08;必须分区&#xff09; 在文件中的格式开头为b &#xff0c;块设备 2.文件系统 因公安是个硬件设备&#xff0c;是一类软件的总称&#xff0c;管理文件的功能&#xff0c;下载文件占硬盘的空间 3.挂载 将硬盘与系统内的文件夹做关…

图论及其应用(匈牙利算法)---期末胡乱复习版

目录 题目知识点解题步骤小结题目 T1:从下图中给定的 M = {x1y4,x2y2,x3y1,x4y5},用 Hungariam算法【匈牙利算法】 求出图中的完美匹配,并写出步骤。 知识点 关于匈牙利算法: 需要注意的是,匈牙利算法仅适用于二分图,并且能够找到完美匹配。什么是交替路?从一个未匹…

Linux/Unix/国产化操作系统常用命令(二)

目录 后CentOS时代国产化操作系统国产化操作系统有哪些常用Linux命令关于Linux的LOGO 后CentOS时代 在CentOS 8发布后&#xff0c;就有了一些变化和趋势&#xff0c;可以说是进入了"后CentOS时代"。这个时代主要表现在以下几个方面&#xff1a; CentOS Stream的引入…

刚来实习就跑路,可行么?

最近 编程导航 的一位鱼友问了个让我血压升高的问题&#xff1a; 鱼友提问 鱼皮你好&#xff0c;我投了两周简历&#xff0c;然后昨天面了一个小厂的远程实习并且拿到了 offer&#xff0c;我要不要试试呢&#xff1f; 我在顾虑比如我如果在远程实习期间找到一个中厂或者大厂…

vite项目中动态引入src失败的问题解决:require is not defined

问题复现 静态引入路径(无问题) <el-menu-item v-for"(item,index) in menuList" :index"item.name" :key"index"><img class"menuItemImg" src"../svg/router/homePage.svg" alt"">{{ item.meta.c…

浙大链协2023年终总结

2 0 2 4 元旦 快乐 龙腾虎跃 01 引言 俗话说&#xff1a;"币圈一天&#xff0c;人间十年"&#xff0c;数字货币一天的涨跌可能抵上其他资产价格一年的波动幅度。而经历过漫长的熊市后&#xff0c;铭文的火爆十分生动地表述了这一口号...... 2023年&#xff0c;浙大链…

odoo17后台启动过程3——三种server

文件位置&#xff1a;odoo\service\server.py 1、三种server&#xff1a; 1.1、Threaded 这是Odoo默认的选项&#xff0c;线程模式&#xff0c;我们知道python的多线程并不是真正的多线程&#xff0c;所以&#xff0c;这种模式下&#xff0c;并发性能较低&#xff0c;也无法利…

使用拉普拉斯算子的图像锐化的python代码实现——数字图像处理

原理 拉普拉斯算子是一个二阶导数算子&#xff0c;用于图像处理中的边缘检测。它通过计算图像亮度的二阶空间导数来工作&#xff0c;能够突出显示图像中的快速变化区域&#xff0c;如边缘。 图像锐化的原理&#xff1a; 图像锐化是指增强图像中的边缘和细节&#xff0c;使图像…

Python基础知识:整理1 使用函数实现银行ATM操作

定义一个全局变量&#xff1a; money, 用来记录银行卡余额&#xff08;默认为5000000&#xff09; 定义一个全局变量&#xff1a; name, 用来记录客户姓名&#xff08;启动程序时输入&#xff09; 定义如下函数&#xff1a; 查询余额的函数&#xff1b; 存款函数&#xff1b; 取…

blender mix节点和它的混合模式

Mix 节点是一种用于混合两个颜色或者两个图像的节点&#xff0c;它有以下几个输入和输出&#xff1a; Color1&#xff1a;用于接收第一个颜色或者图像&#xff0c;也就是基色。Color2&#xff1a;用于接收第二个颜色或者图像&#xff0c;也就是混合色。Fac&#xff1a;用于控制…

第六节、项目支付功能实战-保证金支付、支付回调

摘要 上一节中,我们申请了商户的证书、APIv3密钥,以及编写了微信平台证书的下载的相关代码,并以微信平台证书下载和微信下单接口为例分析了sdkapi的使用、sdk是如何封装加签和验签的流程的。这一节我们将结合实际保证金支付业务来实现整个支付的功能。 功能实现 1、实现小…

RDS快速入门

目录 实例创建 设置白名单 RDS&#xff08;Relational Database Service&#xff09;是一种托管式的关系型数据库服务&#xff0c;它为用户提供了一种简单、可靠、安全的方式来部署、操作和扩展数据库。具有安全可靠、解决运维烦恼、有效降低成本和自研增加等四大特性&#x…

chromium通信系统-ipcz系统(九)-ipcz系统代码实现-跨Node通信-代理和代理消除

chromium通信系统-ipcz系统(六)-ipcz系统代码实现-跨Node通信-基础通信 一文我们分析了跨Node的基础通信过程。 a进程和b进程通信的过程。 但在程序中a进程将自己打开的一对portal中的一个portal传递给了b进程。由于篇幅问题这个过程我们并没有分析&#xff0c;这篇文章我们就来…

学习MySQL(5.7)第二战:四大引擎、账号管理以及建库(干货满满)

目录 前言&#xff1a; 一.数据库存储引擎 1.存储引擎简介 存储引擎查看 support字段说明 2.四大引擎详细介绍 InnoDB MylSAM MEMORY Archive 二.数据库管理 1.元数据库简介 2.元数据库分类 infomation_schema mysql performance_schema ​编辑3.数据库…

协议基础笔记

Android串口使用方法_android-serialport的使用-CSDN博客 安卓与串口通信-基础篇_安卓串口通信-CSDN博客 串口简介 串口通信是Android智能硬件开发所必须具备的能力&#xff0c;市面上类型众多的外设基本都是通过串口进行数据传输的&#xff0c;所以说不会串口通信根本就做不…

亚马逊鲲鹏系统给我带来的真实体验感

我想分享一下我对亚马逊鲲鹏系统的使用体验。我一直在寻找一个方便且高效的方法来批量注册亚马逊买家账号&#xff0c;而亚马逊鲲鹏系统给了我一个理想的解决方案。 使用亚马逊鲲鹏系统进行批量注册是非常简便的。系统内置了全自动化的操作功能&#xff0c;只要提前准备好所需的…

华为交换机入门(六):VLAN的配置

VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是将一个物理的LAN在逻辑上划分成多个广播域的通信技术。VLAN内的主机间可以直接通信&#xff0c;而VLAN间不能直接互通&#xff0c;从而将广播报文限制在一个VLAN内。 VLAN 主要用来解决如何…

Java-执行系统命令进程-实践篇

1 需求 2 接口 3 示例 import java.io.IOException;/*** 现象&#xff1a;1输出后马上输出2* 原因&#xff1a;子进程如果没有调用waitFor()&#xff0c;不会阻塞主进程*/ public class Test {public static void main(String[] args) {System.out.println(1);try {Runtime.ge…

全局异常和自定义异常处理

全局异常GlobalException.java&#xff0c;basePackages&#xff1a;controller层所在的包全路径 import com.guet.score_management_system.common.domian.AjaxResult; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bi…

CMake是什么?为什么学习CMake

文章目录 1.CMake简介2.为什么要学习CMake3.什么样的项目需要用CMake3.1大型或复杂项目3.2跨平台项目3.3需要高度定制化构建的项目3.4 研究和开源项目3.5小型或简单项目 4.CMake的作用5.支持的编译器5.1Windows平台5.2Unix/Linux平台5.3macOS平台5.4其他编译器5.5支持的平台 CM…
最新文章