Java SE(跟狂神老师学习的笔记)

JavaSE

(学习”狂神说Java“的笔记)


1-基础语法

1、注释、标识符、关键字

注释: 并不会被执行,而是对代码进行注解,方便了解该代码的作用。

// 单行注释

/*
	多行
	注释
*/

/**
*	JavaDos:文档注释
*/

// 有趣的代码注释:(原文链接:https://blog.csdn.net/ydk888888/article/details/81563608)
/***                                                                    
 *            .,,       .,:;;iiiiiiiii;;:,,.     .,,                   
 *          rGB##HS,.;iirrrrriiiiiiiiiirrrrri;,s&##MAS,                
 *         r5s;:r3AH5iiiii;;;;;;;;;;;;;;;;iiirXHGSsiih1,               
 *            .;i;;s91;;;;;;::::::::::::;;;;iS5;;;ii:                  
 *          :rsriii;;r::::::::::::::::::::::;;,;;iiirsi,               
 *       .,iri;;::::;;;;;;::,,,,,,,,,,,,,..,,;;;;;;;;iiri,,.           
 *    ,9BM&,            .,:;;:,,,,,,,,,,,hXA8:            ..,,,.       
 *   ,;&@@#r:;;;;;::::,,.   ,r,,,,,,,,,,iA@@@s,,:::;;;::,,.   .;.      
 *    :ih1iii;;;;;::::;;;;;;;:,,,,,,,,,,;i55r;;;;;;;;;iiirrrr,..       
 *   .ir;;iiiiiiiiii;;;;::::::,,,,,,,:::::,,:;;;iiiiiiiiiiiiri         
 *   iriiiiiiiiiiiiiiii;;;::::::::::::::::;;;iiiiiiiiiiiiiiiir;        
 *  ,riii;;;;;;;;;;;;;:::::::::::::::::::::::;;;;;;;;;;;;;;iiir.       
 *  iri;;;::::,,,,,,,,,,:::::::::::::::::::::::::,::,,::::;;iir:       
 * .rii;;::::,,,,,,,,,,,,:::::::::::::::::,,,,,,,,,,,,,::::;;iri       
 * ,rii;;;::,,,,,,,,,,,,,:::::::::::,:::::,,,,,,,,,,,,,:::;;;iir.      
 * ,rii;;i::,,,,,,,,,,,,,:::::::::::::::::,,,,,,,,,,,,,,::i;;iir.      
 * ,rii;;r::,,,,,,,,,,,,,:,:::::,:,:::::::,,,,,,,,,,,,,::;r;;iir.      
 * .rii;;rr,:,,,,,,,,,,,,,,:::::::::::::::,,,,,,,,,,,,,:,si;;iri       
 *  ;rii;:1i,,,,,,,,,,,,,,,,,,:::::::::,,,,,,,,,,,,,,,:,ss:;iir:       
 *  .rii;;;5r,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,sh:;;iri        
 *   ;rii;:;51,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.:hh:;;iir,        
 *    irii;::hSr,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,sSs:;;iir:         
 *     irii;;:iSSs:.,,,,,,,,,,,,,,,,,,,,,,,,,,,..:135;:;;iir:          
 *      ;rii;;:,r535r:...,,,,,,,,,,,,,,,,,,..,;sS35i,;;iirr:           
 *       :rrii;;:,;1S3Shs;:,............,:is533Ss:,;;;iiri,            
 *        .;rrii;;;:,;rhS393S55hh11hh5S3393Shr:,:;;;iirr:              
 *          .;rriii;;;::,:;is1h555555h1si;:,::;;;iirri:.               
 *            .:irrrii;;;;;:::,,,,,,,,:::;;;;iiirrr;,                  
 *               .:irrrriiiiii;;;;;;;;iiiiiirrrr;,.                    
 *                  .,:;iirrrrrrrrrrrrrrrrri;:.                        
 *                        ..,:::;;;;:::,,.                             
 */   

标识符: Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。

标识符注意点:

  • 所有的标识符都应该以字母(A-Z或a-z),美元符($)、或者下划线(_) 开头
  • 首字符之后可以是字母(A-Z或a-z),美元符($)、或者下划线(_)或数字的任何字符组合
  • 不能使用关键字作为变量名或方法名
  • 标识符是大小写敏感
  • 可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音。

关键字(如图):

在这里插入图片描述

2、数据类型

Java是强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用

// 数据类型 变量名 = 值
int a = 10;

Java的数据类型分为两大类:

  • 基本类型:整数类型、浮点类型、字符类型、Boolean类型
  • 引用类型:类、接口、数组(除了基本类型之外的所有数据类型)
// 八大基本数据类型
// 整数
int n1 = 1; // 最常用
byte n2 = 2;
short n3 = 3;
long n4 = 4L;
// 浮点数
float n5 = 1.1F;
double n6 = 3.14;
// 字符
char n8 = 'a';
// 布尔值
boolean flag = true;

3、类型转换

Java是强类型语言,部分运算的时候是需要用到类型转换。

低—————————————————>高

byte、short、char、int、long、float、double

运算中,不同类型的数据先转化成同一类型,然后进行运算。

  • 强制类型转换(高到低):(类型)变量名(注意避免内存溢出)
  • 自动类型转换(低到高)

注意点:

  1. 不能对布尔值进行转换
  2. 不能把对象转换为不相干的类型
  3. 在把高容量转换到低容量的时候,强制转换
  4. 转换的时候可能存在内存溢出,或者精度问题

4、变量、常量、作用域

变量:

  • 变量是什么:就是可以变化的量!
  • Java是一种强类型语言,每个变量都必须声明其类型
  • Java变量是程序中最基本的存储单元,其要素包括变量类型,变量名和作用域
int a = 1;
String name = "张三";
char x = 'x';
double d = 3.14;

常量: 不变化的量!可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变。

变量的作用域: 也就是变量的可用性范围。简单来说,一个变量的可用性范围,只存在于定义这个变量的大括号内。

类变量、实例变量、局部变量

public class Demo{
    
    // 常量 final
    final double PI = 3.14// 类变量 static
    static double salary = 2500;
    
    // 实例变量:从属于对象,如果不进行初始化,他会自动赋该类型的默认值
    String name; // 默认值null
    int age; // 默认值 0
    
    // main方法
    public static void main(String[] args){
        // 局部变量:必须声明和初始化值
        // 这个i只在main的大括号内有效
        int i = 10;

        if(1){
            //这个double只在if的大括号内有效
            double d = 3.14;
        }
    }
    
    // 其他方法
    public void add(){
        
    }
}

变量命名规范:

  • 所有变量、方法、类名:见名识意
  • 类成员变量:首字母小写和驼峰原则
  • 局部变量:首字母小写和驼峰原则
  • 常量:大写字母和下划线:MAX_VALUE
  • 类名:首字母大写和驼峰原则
  • 方法名:首字母小写和驼峰原则

注意事项:

  • 每个变量都有类型,类型可以是基本类型,也可以是引用类型
  • 变量名必须是合法的标识符
  • 变量声明是一条完整的语句,因此每一个声明都必须以分号结束

5、运算符

Java语言支持如下运算符: 优先级推荐看这里(https://blog.csdn.net/huangblog/article/details/8271791)

  • 算术运算符:+,-,*,/,%,++,–
  • 赋值运算符:=
  • 关系运算符:>,<,>=,<=,==,!=,instanceof
  • 逻辑运算符:&&,||,!
  • 位运算符(目前了解即可):&,|,^,~,>>,<<,>>>
  • 条件运算符:? :(没错就是这个问号+冒号)
  • 扩展赋值运算符:+=,-=,*=,/=
/* 位运算演示
	A = 0010 1110
	B = 1100 1100
----------------------
	A&B = 0000 1100
	A|B = 1110 1110
	A^B = 1110 0010(异或,相同为0,不同为1)
	~A = 1101 0001
	
	<< 左移 相当于*2(左移几位就*几个2)
	>> 右移 相当于/2
	举个栗子:2*16 => 2*2*2*2*2 => 2>>4 = 32
			32/8 => 32/2/2/2 => 32<<3 =4
*/

/* 三元运算符
	x ? y : z 
	如果x == true,则结果为y,否则为z。
	举个栗子:
		int a = 61;
		int b = 59;
		String type1 = a > 60 ? "及格" : "不及格"
		String type2 = b > 60 ? "及格" : "不及格"
*/

6、包机制、JavaDoc

包:

为了更好地组织类,Java提供了包机制,用于区别类名的命名空间。

一般利用公司域名倒置作为包名:例如:www.baidu.com 包名可为com.baidu.www

为了能够使用某一个包的成员,我们需要在Java程序中明确导入该包。使用"import"语句可完成此功能。(通常可自动导包)

JavaDoc:

javadoc命令是用来生成自己的API文档的。

具体内容推荐:https://blog.csdn.net/languan1639397/article/details/122560483


2-流程控制

1、用户交互Scanner

Java提供了一个让我们可以获取用户输入的工具类。java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入。

基本语法:

Scanner s = new Scanner(System.in);

通过Scanner类的next()和nextLine()方法获取输入的字符,在读取前我们一般需要使用hasNext()和hasNextLine()判断是否还要输入的数据。

// 创建一个扫描器,用户接收键盘数据
Scanner scanner = new Scanner(System.in);
System.out.print("请输入:");

// 判断用户有没有输入字符串,该方式读到空格就会停止
if (scanner.hasNext()) {
    // 使用next方式接收
    String str = scanner.next();
    System.out.println("输出的内容为:" + str);
}

// 凡是属于IO流的类,如果不关闭会一直占用资源,好的习惯是用完就关掉
scanner.close();

在这里插入图片描述

Scanner scanner = new Scanner(System.in);
System.out.print("请输入:");

if(scanner.hasNextLine()){
    String str = scanner.nextLine();
    System.out.println("输出的内容为:" + str);
}

scanner.close();

在这里插入图片描述

next():

  • 1.一定要读取到有效字符后才可以结束输入。
  • 2.对输入有效字符之前遇到的空白,next()方法会自动将其去掉。
  • 3.只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
  • 4.next()不能得到带有空格的字符串。

nextLine():

  • 1.以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。
  • 2.可以获得空白。

除此之外,Scanner还有很多方法,用于获取输入的不同类型的数据,如同:

在这里插入图片描述

2、顺序结构

Java的基本结构就是顺序结构,除非特别指明,否则就是按照顺序一句一句往下执行。顺序结构也是最简单的算法结构

语句于语句之间,框与框之间是按照从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的,它是任何一个算法都不开的一种基本算法结构

3、选择结构

if单选择结构

我们很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程在程序中用if语句来表示。

语法:

if(布尔表达式){
    // 如果布尔表达式为true,那么将执行大括号内的语句
}

if双选择结构

我们可能会需要判断一个条件是否能够满足,如果满足该条件,那么将会执行语句1,否则将会执行语句2。

语法:

if(布尔表达式){
    // 如果布尔表达式为true,那么将执行该大括号内的语句
} else {
    // 如果布尔表达式为false,那么将执行该大括号内的语句
}

举个栗子:如果小明数学考试考了100分,那么妈妈将会奖励小明一个他喜欢的玩具,否则就不奖励

import java.util.Scanner;

public class Demo1 {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入小明数学考了多少分:");

        int grade = scanner.nextInt();

        if (grade == 100) {
            System.out.println("妈妈奖励小明一个他喜欢的玩具");
        } else {
            System.out.println("妈妈不进行奖励");
        }

        scanner.close();
    }
}

if多选择结构

现实生活中存在多级判断的情况,这种时候就需要多选择结构来帮助我们完成,不多bb直接上语法。

语法:

if(布尔表达式1){
    // 如果布尔表达式1为true,那么将执行该大括号内的语句,否则往下判断表达式2
}else if(布尔表达式2) {
    // 如果布尔表达式2为true,那么将执行该大括号内的语句,否则继续往下判断
}else if(布尔表达式3) {
    // 如果布尔表达式3为true,那么将执行该大括号内的语句,否则继续往下判断
}else if(布尔表达式4) {
    // 如果布尔表达式4为true,那么将执行该大括号内的语句,否则继续往下判断
}else if(布尔表达式5) {
    // 如果布尔表达式5为true,那么将执行该大括号内的语句
}else {
    // 如果以上表达式判断都不为true,那么将执行该大括号内的语句
}

举个栗子:考试等级划分

import java.util.Scanner;

public class Demo1 {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入得分:");

        int grade = scanner.nextInt();

        if (grade == 100) {
            System.out.println("A+");
        } else if(grade > 85){
            System.out.println("A");
        } else if(grade > 75){
            System.out.println("B");
        } else if(grade > 60){
            System.out.println("C");
        } else if(grade < 60  && grade >= 0) {
            System.out.println("D");
        } else{
            System.out.println("输入不合法");
        }

        scanner.close();
    }
}

嵌套的if结构

嵌套,也就是说你可以一个if或者else if语句中使用if或else if语句,看上去就很像套娃一样。

语法:

if(布尔表达式1){
    // 如果布尔表达式1为true,那么将执行该大括号内的语句
 	if(布尔表达式2){
        // 如果布尔表达式2为true,那么将执行该大括号内的语句
        if(布尔表达式3){
        	// 如果布尔表达式3为true,那么将执行该大括号内的语句
    	}
    }
}

switch多选择结构

switch case语句判断一个变量于一系列值是否相等,每个值称为一个分支。

switch语句中的变量类型可以是:byte、short、int、char、String(从Java SE 7开始支持),同时,case标签必须为字符串常量或字面量。

举个栗子:

import java.util.Scanner;

public class Demo1 {
	public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
		System.out.println("请输入月份:");
		int month = sc.nextInt();
        
		//利用之前学过的三元运算符,将月份分到各自的春夏秋冬
		int season = month == 12 ? 4:(m >= 9 ? 3:(m >= 6 ? 2:(m >= 3 ? 1:(m > 0 ? 4:0))));
        
		//根据分好的类,选择季节
		switch (season)
		{
			case 1:
				System.out.println("春季");
				break;
			case 2:
				System.out.println("夏季");
				break;
			case 3:
				System.out.println("秋季");
				break;
			case 4:
				System.out.println("冬季");
				break;
             default :
                System.out.println("输入不合法");
		}		
	}
}

4、循环结构

while循环

while是最基本的循环,语法格式如下:

while(布尔表达式){
    //循环的内容
}
  • 只要布尔表达式的结果为true,循环就会一直进行下去。
  • 我们大多数情况是会让循环在某种条件下停止下来,我没需要一个让表达式失效的方式来结束循环。
  • 少部分情况需要循环一直执行,比如服务器的请求响应监听等。
  • 循环条件一直为true就会造成无限循环(死循环),正常的业务编程中应该尽量避免死循环。因为死循环会影响程序性能甚至造成程序卡死崩溃!

举个栗子:

public class Demo1 {
    public static void main(String[] args) {
		// 输出1~100
        int i = 1;
        while(i <= 100){
            System.out.println(i);
            i++;
        }
    }
}

do…while循环

对于while语句来说,如果条件不满足,则不能执行循环体内的语句。但是有时候我没需要即使不满足条件,也至少执行一次。

这时我们就会用到do…while循环,因为它至少会执行一次。 语法格式如下:

do{
    // 循环内容
}while(布尔表达式);

while和do…while的区别就在于,while先进行判断,在执行循环;do…while则是先执行一次循环,再进行条件判断。

for循环(重点)

for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。

for循环执行的次数是再执行前就确定的。语法格式如下:

for(初始化; 布尔表达式; 更新){
    // 循环内容
}

for循环的执行步骤:

  1. 最先执行初始化的语句,可以声明一种类型,也可初始化一个或多个循环控制变量,也可以是空语句(本步骤只在开始的时候执行一次,每次循环不再执行)。
  2. 然后检测布尔表达式的值,如果为true则执行循环体的内容,如果为false则循环终止。
  3. 执行一次循环后,更新循环控制变量(迭代因子控制循环变量的增减)。
  4. 再次检测布尔表达式,重复上述步骤。

举个栗子:

public class Demo1 {
    public static void main(String[] args) {
		// 输出1+2+3+.....+100
        int sum = 0;
        for(int i = 1; i <= 100; i++){
            sum += i;
        }
        System.out.println(sum);
    }
}

增却for循环

这里先进行简单的了解即可。

增却for循环主要用于数组或集合的增强型for循环,语法格式如下:

for(声明语句 : 表达式){
    // 循环内容
}

声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

举个栗子:

public class Demo1 {
    public static void main(String[] args) {
		int[] num = {10, 20, 30, 40, 50};
        for(int i : num){
            System.out.println(i);
        }
        /* 输出结果为:
        10
        20
        30
        40
        50
        */
}

5、break&continue

break:在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出当前循环,不执行循环中剩余的语句。

continue:该语句用在循环语句体中,用于终止某次循环过程,即跳过本次循环体中尚未执行的部分,直接进入下一次循环的判定。


3-方法

1、何谓方法

Java方法是语句的集合,它们在一起执行一个功能。

  • 方法是解决一类问题的步骤的有序组合。
  • 方法包含于类或对象中。
  • 方法在程序中被创建,在其他地方被引用。

设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。设计方法的时候,最好保持方法的原子性,就是一个方法只完成一个功能,这样利于我们后期的扩展。

举个栗子:

public abstract class Demo1 {
    public static void main(String[] args) {
		// 调用方法 实际参数
        int sum = add(1, 2);
       
        System.out.println(sum);
    }
    
	// 加法求和方法        形式参数
    public static int add(int a, int b){
        return a+b;
    }	
}

2、方法的定义及调用

Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:

  • 方法包含一个方法头和一个方法体:
    • 修饰符:修饰符是可选的,告诉编译器如何调用该方法,定义了该方法的访问类型。
    • 返回值类型:方法可能会有返回值,需要填写对应的返回值类型,而有些方法可能不需要返回值(没有返回值),它们的返回值类型就是void。
    • 方法名:是方法的实际名称,方法名和参数列表共同构成方法签名。
    • 参数类型:当方法被调用时,传递值给参数,这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
      • 形参:在方法被调用时用于接受外界输入的数据。
      • 实参:调用方法时实际传给方法的数据。
    • 方法体:方法体包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值 //如果是void则不需要return。
}
  • 调用方法:对象名.方法名(实参列表)

  • Java支持两种调用方法的方式,根据方法是否返回值来下选择。

  • 当方法返回一个值的时候,方法调用通常被当做一个值:

    • int larger = max(30, 40);
      
  • 如果方法返回值是void,方法调用一定是一条语句,例如输出语句。

3、方法的重载(重要)

重载就是在一个类中,有相同的函数名称,但是形参不同的函数。

方法的重载规则如下:

  • 方法名必须相同。
  • 参数列表必须不同(个数不同,类型不同,参数排列顺序不同,都可)。
  • 方法的返回类型可以相同,也可以不同。
  • 仅仅返回类型不同,不足以成为方法的重载。

举个栗子:

public abstract class Demo1 {
    public static void main(String[] args) {
        int sum1 = add(1, 2);
        int sum2 = add(1, 2, 3);
        double sum3 = add(1.1, 1.2);
        System.out.println(sum1);
        System.out.println(sum2);
        System.out.println(sum3);
    }
    
    public static int add(int a, int b){
        return a+b;
    }
    // 重载
    public static int add(int a, int b, int c){
        return a+b+c;
    }
    
    public static double add(double a, double b){
        return a+b;
    }
}

4、可变参数

JDK1.5开始,Java支持传递同类型的可变参数给一个方法。

在方法的声明中,在指定参数类型后面加一个省略号(…)。

一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通参必须在它之前声明。

举个栗子:

public abstract class Demo1 {
    public static void main(String[] args) {
        int sum = add();
        int sum1 = add(1, 2, 3);
        int sum2 = add(new int[]{1, 2, 3, 4, 5});
        System.out.println(sum);
        System.out.println(sum1);
        System.out.println(sum2);
    }
    
    public static int add(int...numbers){
        int sum = 0;
        if (numbers.length != 0) {
            for (int i = 0; i < numbers.length; i++){
                sum += numbers[i];
            }
            return sum;
        }else {
            System.out.println("没有传递参数");
            return 0;
        }
    }
}

5、递归

一个方法去调用另一个方法,这我们很容易理解,但是递归……

递归就是:一个方法调用这个方法本身,没戳!也就是自己调用自己!!!

利用递归可以用简单的程序来解决一些复杂的问题(想起来在大一萌新时期被斐波那契数列支配的恐惧)。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。

递归结构包括两部分:

  • 递归头:什么时候不调用自身方法。如果没有头,就会陷入死循环。
  • 递归体:什么时候需要调用自身方法。

举个栗子:(就举个斐波那契数列的吧)

public abstract class Demo1 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        System.out.println(fib(n));
        scanner.close();
    }
    
    public static int fib(int n){
        if (n == 1 || n == 2) {
            return 1;
        }else {
            return fib(n - 1) + fib(n - 2);
        }
    }
}

大佬的博客:https://blog.csdn.net/c_living/article/details/109479412


4-数组

1、数组概述

数组的定义:

  • 数组是相同类型数据的有序集合。
  • 数组描述的是相同类型的若干数据,按照一定的先后次序排列组合而成。
  • 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。

2、数组声明创建

首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:

dataType[] arrayRefVar; // 首选的方法
dataType arrayRefVar[]; // 效果相同,但不是首选的方法 

Java语言使用new操作符来创建数组,语法如下:

dataType[] arrayRefVar = new dataType[arraySize];

数组的元素是通过索引访问的,数组索引从0开始。

// 获取数组长度:
arrays.length
import java.util.Scanner;

public abstract class Demo1 {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        //1.声明一个数组
        int[] nums;  
        //2.这里面可以存放10个int类型的数字
        nums = new int[10]; 
        // 1,2可以合并
        // int[] nums = new int[10];
        
        // 3.赋值
        for (int i = 0; i < nums.length; i++) {
            nums[i] = scanner.nextInt();
        }

        // 4.遍历输出数组
        for (int i = 0; i < nums.length; i++) {
            System.out.println(nums[i]);
        }

        scanner.close();
    }
}

三种初始化:

  • 静态初始化:创建+复制
int[] a = {1,2,3};
  • 动态初始化:包含默认初始化
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
  • 数组的默认初始化
    • 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

数组的四个基本特点:

  • 长度是确定的,数组一旦被创建,它的大小就是不可改变的。
  • 其元素必须是相同类型,不允许出现混合类型。
  • 数组中的元素可以说任何数据类型,包括基本类型和引用类型。
  • 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论报错原始类型还是其他对象类型,数组对象本身是在堆中的。

数组边界:

  • 数组下标的合法区间:[0,length-1],如果越界就会报错。
  • ArrayIndexOutOfBoundsException:数组下标越界异常!

2.5、内存分析

Java内存:

  • 堆:
    • 存放new的对象和数组
    • 可以被所有的线程共享,不会存放别的对象引用
  • 栈:
    • 存放基本变量类型(会包含这个基本类型的具体数值)
    • 引用对象的变量(会存放这个引用在堆里面的具体地址)
  • 方法区:
    • 可以被所有的线程共享
    • 包含了所有的class和static变量

3、数组使用(重点)

  • For-Each循环
  • 数组作方法入参
  • 数组作返回值
public abstract class Demo1 {
    public static void main(String[] args) {

        int[] arrays = { 1, 2, 3, 4, 5 };

        // For-Each循环:没有下标
        for (int i : arrays) {
            System.out.print(i + " ");
        }
        System.out.println("===================");

        printArray(arrays);
        System.out.println("===================");
        
        int[] reverse = reverse(arrays);
        printArray(reverse);
    }

    // 打印数组元素(数组作方法入参)
    public static void printArray(int[] array) {
        for (int i : array) {
            System.out.print(i + " ");
        }
    }

    // 反转数组(数组作返回值)
    public static int[] reverse(int[] array) {
        int[] result = new int[array.length];

        for (int i = 0, j = result.length - 1; i < array.length; i++, j--) {
            result[j] = array[i];
        }
        return result;
    }
}

4、多维数组

多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。

二维数组:

int a[][] = new int[2][5];
// 可以看成是一个两行五列的数组:
/*
0 0 0 0 0
0 0 0 0 0
*/
package Java;

public abstract class Demo1 {
    public static void main(String[] args) {

        // 4行2列
        int[][] array = {{1,2},{2,3},{3,4},{4,5}};

        // 打印输出
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++){
                System.out.println(array[i][j]);
            }
        }
    }
}

5、Arrays类

数组的工具类java.util.Arrays

由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本操作。

Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象来调用(注意是“不用”,而不是“不能”)。

具有以下常用功能:

  • 给数组复制:通过fill方法。
  • 对数组排序:通过sort方法,按升序。
  • 比较数组:通过equals方法比较数组中元素值是否相等。
  • 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作。
package Java;

import java.util.Arrays;

public abstract class Demo1 {
    public static void main(String[] args) {

        int[] a = {12, 135, 75, 354, -5, 431, 312, 0, 6745, 321};

        System.out.println(a);

        // 打印数组元素
        System.out.println(Arrays.toString(a));
        // 数组排序
        Arrays.sort(a);
        System.out.println(Arrays.toString(a));
         // 数组填充
        Arrays.fill(a, 8);
        System.out.println(Arrays.toString(a));
        // 指定范围填充(3~5)
        Arrays.fill(a, 3, 5, 6);
        System.out.println(Arrays.toString(a));
    }
}

6、冒泡排序

总共有八大排序,冒泡排序是较为出名的排序算法之一。

冒泡排序的步骤:

  1. 从一端开始,比较数组中两个相邻的元素。
  2. 根据排序要求(升序还是降序)来重新排列这两个数字,例如升序时,就将两个数中较大的放在后面,较小的放在前面(通过交换它们的位置来实现)
  3. 每一次比较都会产生一个最大或者最小的数,如此则下一轮就会减少一次排序。
  4. 依次循环,直到结束。
package Java;

public abstract class Demo1 {
    public static void main(String[] args) {

        int[] a = {12, 135, 75, 354, -5, 431, 312, 0, 6745, 321};
        int t = 0; 

        // 冒泡排序
        // 外层循环,判断要比较多少轮
        for (int i = 0; i < a.length - 1; i++){
            // 内层循环,判断相邻两个数的大小
            for (int j = 0; j < a.length - 1 - i; j++){
                // 判断是否需要交换两个数的位置
                if (a[j + 1] > a[j]) {
                    t = a[j + 1];
                    a[j + 1] = a[j];
                    a[j] = t;
                }
            }
        }
    }
}

冒泡的代码,两层循环,外层冒泡轮数,内层依次比较。时间复杂度为O(n2)

超详细的冒泡排序:https://blog.csdn.net/hcz666/article/details/117810787

7、稀疏数组

在这里插入图片描述

在这里插入图片描述

package Java;

public abstract class Demo1 {
    public static void main(String[] args) {
        // 原始数组
        int[][] array1 = new int[11][11];
        array1[2][4] = 1;
        array1[4][2] = 21;
        array1[6][5] = 34;
        array1[8][10] = 54;
        array1[10][3] = 112;

        System.out.println("原始数组打印:");
        for (int[] is : array1) {
            for (int is2 : is) {
                System.out.print(is2 + "\t");
            }
            System.out.println();
        }

        // 转换为稀疏数组
        // 计算有效值个数
        int sum = 0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if (array1[i][j] != 0) {
                    sum++;
                }
            }
        }
        System.out.println("有效值的个数:" + sum);

        // 创建稀疏数组
        int[][] array2 = new int[sum + 1][3];
        array2[0][0] = 11;
        array2[0][1] = 11;
        array2[0][2] = sum;

        // 给稀疏数组赋值
        int count = 0;
        for (int i = 0; i < array1.length; i++) {
            for (int j = 0; j < array1[i].length; j++) {
                if (array1[i][j] != 0) {
                    count++;
                    array2[count][0] = i;
                    array2[count][1] = j;
                    array2[count][2] = array1[i][j];
                }
            }
        }
        // 打印稀疏数组
        System.out.println("稀疏数组打印:");
        for (int i = 0; i < array2.length; i++) {
            System.out.println(array2[i][0] + "\t" + array2[i][1] + "\t" + array2[i][2] + "\t");
        }

        // 还原原始数组
        System.out.println("还原");
        // 读取稀疏数组
        int[][] array3 = new int[array2[0][0]][array2[0][1]];
        // 给其中的元素还原它的值
        for (int i = 1; i < array2.length; i++) {
            array3[array2[i][0]][array2[i][1]] = array2[i][2];
        }

        System.out.println("还原数组打印:");
        for (int[] is : array3) {
            for (int is2 : is) {
                System.out.print(is2 + "\t");
            }
            System.out.println();
        }

    }
}

5-面向对象

1、初识面向对象

面向过程的思想:

  • 步骤清晰简单,线性思维,如:第一步做什么,第二步做什么……
  • 面对过程适合处理一些较为简单的问题

面向对象的思想:

  • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
  • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,据跳到围观操作,仍然需要面向对象过程的思路去处理。

什么是面向对象?

  • 面向对象编程(Object-Oriented Programming,OOP)
  • 面向对象编程的本质是:以类的方式组织代码,以对象的方式组织(封装)数据。
  • 抽象
  • 三大特性:封装、继承、多态
  • 从认识论角度考虑是现有对象后有类。对象,是具体的事物,类是抽象的,是对对象的抽象。
  • 从代码运行角度考虑是现有类然后有对象,类是对象的模板。

2、方法回顾和加深

  • 修饰符
  • 返回值类型
  • 方法名
  • 参数列表
  • 异常抛出(下期介绍)

方法的调用:

  • 静态方法:有static修饰
    • 又叫类方法,属于类,不属于对象,在实例化对象前可以使用类名.方法名的方式调用静态方法。
    • 在静态方法中,可以调用静态方法,不能调用非静态方法,可以引用类变量,不能引用成员变量,不能使用super和this关键字。
  • 非静态方法:没有static修饰
    • 又叫成员方法,属于对象,不属于类,必须实例化对象后,通过对象来调用
    • 在非静态方法中,可以调用静态方法,也可以调用非静态方法,可以引用类变量,也可以引用成员变量,可以使用super和this关键字。
  • 形参与实参
  • 值传递和引用传递
    • 值传递:在调用函数时,将实际的参数复制一份传递到函数中,在函数中的对复制的这一份参数进行修改,不会影响到原来的实际参数。
    • 引用传递:在调用函数时,将实际参数的地址传递到函数中,在函数中对参数进行修改,将会影响到实际参数。
  • this关键字
    • 当成员变量和局部变量重名时,可以用this进行区分,this.成员变量
    • 在构造函数中调用其他构造函数时,可以用this(); 来进行调用。

3、对象的创建分析

1.类与对象的关系:

类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。例如Person类、Pet类、Student类等等,这些都是用来描述/定义某一项具体书屋应该具备的特点和行为。

对象是抽象概念的具体实例,例如,张三、李四就都是人(Person类)的一个具体实例。能够体现出特点,展示出功能的具体的实例,而不是抽象的概念。

2.创建和初始化对象:

使用new关键字创建对象,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中的构造器的调用。

类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。

构造器的特点:

  1. 必须和类的名字相同
  2. 必须没有返回类型,也不能些void

构造器的作用

  1. new本质在调用构造方法
  2. 初始化对象的值

注意点:

  • 如果自己不定义构造方法,默认会有一个无参构造。
  • 如果自己定义了有参构造之后,想使用无参构造时,则必须要自己定义一个无参构造。

举个栗子:

package Java;

// 学生类
public class Student {
    // 属性
    private String name;
    private int age;

    // 无参构造
    public Student(){}

    // 有参构造
    public Student(String name, int age){
        this.name = name;
        this.age = age;
    }

    // Getter/Setter方法
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

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

    // 方法
    public void who(){
        System.out.println("我叫" + this.name + ",今年" + this.age + "岁了。");
    }

    public void study(){
        System.out.println(this.name + "爱学习");
    }
}

package Java;

public class Demo1 {

    public static void main(String[] args) {

        // 实例化对象
        // 无参构造
        Student s1 = new Student();

        s1.setName("张三");
        s1.setAge(18);
        
        // 有参构造
        Student s2 = new Student("李四", 20);

        // 调用方法
        s1.who();
        s2.study();
    }
}

4、面向对象三大特征(重点)

1、封装

我们程序设计要追求 “高内聚,低耦合”。高内聚就是类的内部数据操作细节自己来完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。

封装(数据的隐藏):通常应该禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这成为信息隐藏。封装的特点就是属性私有(private),get/set

这里需要先介绍以下四个关键字的作用域:

作用域当前类同一package下(同包)子、孙类其他package(其他包)
public
protected×
default(默认)××
private×××

也就是说,被private修饰的属性只能被当前类所使用,其他任何地方都不可以使用。

封装的意义:

  1. 提高程序的安全性,保护数据
  2. 因此代码的实现细节
  3. 统一接口
  4. 系统可维护性增加

举个栗子:

package Java;

// 学生类
public class Student {
    // 属性私有
    private String name;
    private int age;
    private char sex;

    // 无参构造
    public Student() {
    }

    // 有参构造
    public Student(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    // Getter/Setter方法
    // get 获得这个数据
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public char getSex() {
        return sex;
    }

    // set 给这个数据设置值
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        } else {
            System.out.println("年龄输入不合法");
        }
    }

    public void setSex(char sex) {
        if (sex == '男' || sex == '女') {
            this.sex = sex;
        } else {
            System.out.println("性别输入不合法");
        }
    }

    // 方法
    public void who() {
        System.out.println("我叫" + this.name + ",今年" + this.age + "岁了,我是一名" + this.sex + "孩子。");
    }

    public void study() {
        System.out.println(this.name + "爱学习!");
    }
}

package Java;

public class Demo1 {

    public static void main(String[] args) {

        // 实例化对象
        // 无参构造
        Student s1 = new Student();

        s1.setName("林琳");
        s1.setAge(18);
        s1.setSex('女');

        // 有参构造
        Student s2 = new Student("李四", 20, '男');
        
        // 调用方法
        s1.who();
        s2.study();
    }
}

2、继承

继承的本质是对某一批类的抽象。

Java中,类只有单继承,没有多继承!

继承是类和类之间的一种关系。除此之外,类和类直接的关系还有依赖、组合、聚合等。

继承关系的两个类,一个为子类(派生),一个为父类(基类)。子类继承父类,使用关键字extends来表示。extends的意思是“扩展”,子类是父类的扩展。

修饰符 class 子类 extends 父类 {
    ...
}

子类继承父类的成员变量和成员方法,继承父类后,需要添加自己独有的成员,以此来体现出与父类的不同。

注意点(就近原则):

  • 如果访问的成员变量,子类中有,则优先访问子类的成员变量。
  • 如果访问的成员变量,子类中没有而父类中有,则访问继承下来的父类的变量。
  • 如果子类与父类有同名的成员变量,则优先访问子类的,可以理解为子类将父类的同名变量给覆盖了。
  • 通过子类访问成员方法时,先看子类有没有,如果有就访问子类,如果没有则访问父类。
  • 通过子类访问与父类同名方法时,如果子类和父类方法的参数列表不同,则构成方法的重写(覆盖),根据调用方法传递的参数选择合适的方法。
  • 如果子类与父类同名方法的原型一致,那么就只能访问到子类的。

在Java中,所有的类都默认直接或间接继承Object。

super关键字:

如果子类中存在与父类同名的成员变量或成员方法,则通过super关键字在子类方法中访问父类方法成员

super注意点:

  1. super调用父类的构造方法,必须在构造方法的第一个。
  2. super必须只能出现在子类的成员方法或构造方法中。
  3. super和this不能同时调用构造方法。

举个栗子:

package Java;
public class Person {
	String name = "李一";
    public Person(){
        System.out.println("Preson无参构造");
    }
    public void print(){
         System.out.println("Preson");
    }
}
package Java;
public class Student extends Person{
    private String name = "李二";
    
     public Student(){
         // 隐藏了super();
         // 调用父类的无参构造器,必须在子类构造器的第一行
        System.out.println("Student无参构造");
    }
    
    public void print(){
         System.out.println("Student");
    }
    
    public void test1(){
        print(); // 子类的 Student
        this.print(); // 子类的 Student
        super.print(); // 父类的 Preson
    }
    
    public void test2(String name){
        System.out.println(name); // 形参 李三
        System.out.println(this.name); // 子类 李二
        System.out.println(super.name); // 父类 李一
    }
}
package Java;
 public static void main(String[] args) {
    Student s1 = new Student();
    s1.test2("李三");
 }

方法重写:

重写:需要有继承关系,子类重写父类的方法

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符:修饰符范围可以扩大但不能缩小public>protected>default>private
  4. 抛出异常:范围可以缩小但不能扩大。

前面学过一个方法的重载,这里是方法的重写,也叫方法的覆盖。二者的区别如下:

  1. 定义:重载是定义相同的方法名,参数不同;重写是子类重写父类的方法。
  2. 范围:重载是在一个类中;重写是子类与父类之间的。
  3. 多态:重载是编译时的多态性;重写是运行时的多态性。
  4. 返回:重载对返回类型没有要求;重写要求返回类型,有兼容的返回类型。
  5. 参数:重载的参数个数、参数类型、参数顺序可以不同;重写父子方法参数必须相同。

不能重写的方法:

  1. static 属于类,不属于实例
  2. final 常量
  3. private 私有
package Java;
public class A {
	public static void test1(){
         System.out.println("A");
    }
    
    public void test2(){
         System.out.println("A");
    }
}
package Java;
public class B extends A{
    
	public static void test1(){
         System.out.println("B");
    }
    
    // @Override 重写
    @Override// 注解:有功能的注解
    public void test2(){
         System.out.println("B");
    }
}
package Java;
 public static void main(String[] args) {
    // 静态方法:方法的调用只和左边定义的数据类型有关
    B b = new B();
    b.test1(); // B
    // 父类的引用指向了子类
    A a = new B();
    a.test1(); // A 
     
    // 非静态:重写 
    B b = new B();
    b.test2(); // B
    A a = new B();
    a.test2(); // B 
 }

大佬关于继承的文章:https://blog.csdn.net/qq_58710208/article/details/120410867

3、多态

即同一方法可以根据发送对象的不同而采用多种不同的行为方式。

一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多。

多态存在的条件:

  • 有继承关系
  • 子类重写父类方法
  • 父类引用指向子类对象

注意:多态是方法的多态,属性没有多态。

栗子:

package Java;
public class Person {

    public void run(){
        System.out.println("person");
    }
}
package Java;

public class Student extends Person{
    
    @Override
    public void run(){
        System.out.println("student");
    }

    public void eat(){
        System.out.println("eat");
    }
}
package Java;

public class Demo1 {
    public static void main(String[] args) {

        // 一个对象的实际类型是确定的: new Student();   new Person();

        // 可以指向的引用类型就不确定了:父类的引用指向子类
        // Student 能调用的方法都是自己的或者继承父类的
        Student s1 = new Student();
        // Person 父类可以指向子类,但是不能调用子类独有的方法
        Person s2 = new Student();

        // 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大!
        s1.run();
        s2.run(); // 子类重写了父类的方法,执行子类的方法
        s1.eat();
        // s2.eat(); 编译报错
    }
}

instanceof 类型转换----引用类型

package Java;
public class Person {

    public void run(){
        System.out.println("person");
    }
}
package Java;
public class Student extends Person {
}
package Java;
public class Teacher extends Person {
}
package Java;

public class Demo1 {
    public static void main(String[] args) {

        // Object > Person > Student 
        // Object > Person > Teacher
        Object object = new Student();
				  //判断:object   是否为   Student 类型
        System.out.println(object instanceof Student); // true
        System.out.println(object instanceof Person); // true
        System.out.println(object instanceof Object); // true
        System.out.println(object instanceof Teacher); // false
        System.out.println(object instanceof String); // false

        Person person = new Student();
        System.out.println(person instanceof Student); // true
        System.out.println(person instanceof Person); // true
        System.out.println(person instanceof Object); // true
        System.out.println(person instanceof Teacher); // false
        // System.out.println(person instanceof String); 编译报错

        Student student = new Student();
        System.out.println(student instanceof Student); // true
        System.out.println(student instanceof Person); // true
        System.out.println(student instanceof Object); // true
        // System.out.println(student instanceof Teacher); 编译报错
        // System.out.println(person instanceof String); 编译报错
        
        // 类型之间的转换: 父->子 向下转型,强制转换
        // 高             低
        Person obj = new Student();
        // 将obj转换为Student类型,就可以使用Student类型的方法了,否则不能用(父类引用不能调用子类方法)
        ((Student)obj).eat();
        
        // 类型之间的转换: 子->父 向上转型,可能丢失自己的本来的一些方法
        Student student = new Student();
        student.eat();
        Person person = student;
        
    }
}

5、抽象类

abstract修饰符可以用来修饰方法,也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。

抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。

抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。

抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。

子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

package Java;
// abstract 抽象类
public abstract class Action {

    // 约束 有人帮我们实现
    // abstract,抽象方法,只有方法名字,没有方法的实现
    public abstract void doSometing();
    
    // 普通方法
    public void go(){
        ...
    }
}

public class A extends Action{
    
   @Override
   public void doSometing(){
       ...
   }
}

大佬关于abstract的介绍:https://blog.csdn.net/weixin_40096176/article/details/79094991

6、接口

声明类的关键字是class,声明接口的关键字是 interface

接口不能被实例化,接口中没有构造方法。

  • 普通类:只有具体实现
  • 抽象类:具体实现和规范(抽象方法)都有
  • 接口:只有规范,也就是只有抽象方法!自己无法写方法,约束和实现的分离。

接口体现了现实世界中的“如果你是…则必须能…”的思想。栗子:如果你是人,则必须能吃!接口的本质是契约,就像法律一样,制定好之后大家都要遵守。

package Java;

// 抽象的思维
// interface 定义接口的关键字
public interface UserService{

    // 接口中所有定义的属性都是常量,默认都是pubic static final
    int AGE = 18 ;

    // 接口中的所有定义的方法都是抽象的,默认都是 public abstract,可以省略不写
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}
package Java;
// abstract 抽象类
public interface TimeService {
    void time();
}
package Java;

// 实现接口的类,就必须要重写接口中的所有方法
// 接口可以实现多继承
public class UserServiceImpl implements UserService, TimeService{
   @Override
   public void add(String name) {}

   @Override
   public void delete(String name) {}

   @Override
   public void update(String name) {}

   @Override
   public void query(String name) {   }

   @Override
   public void time() {}
}

7、内部类

内部类就是在一个类的内部再定义一个类,栗子:A类中定义一个B类,那么B类相对于A类来说就称为内部类,而A类相对于B类来说就是外部类了。

  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类
package Java;

public class Outer {
    private int id = 10;
    public void out(){
        System.out.println("这是外部类的方法");
    }
    // 成员内部类
    class Inner{
        public void in() {
            System.out.println("这是内部类的方法");
        }
        // 获得外部类的私有属性
        public void getID(){
            System.out.println("外部类的私有属性id:" + id);
        }
    }
    
    // 静态内部类
    public static class Inner2{
    }
    
    // 方法中的内部类:局部内部类
    public void method(){
        class Inner3{
        }
    }
}
package Java;

public class Demo1 {

    public static void main(String[] args) {
         // 没有名字初始化类,不用将实例保存到变量中:匿名内部类
		new Apple().eat();
    }
}

class Apple{
    public void eat(){
        System.out.println("eat apple");
    }
}

大佬的详解:https://blog.csdn.net/weixin_46972127/article/details/123609366


6-异常

1、什么是异常

实际工作中,遇到的情况不可能是非常完美的。栗子:你写的某个模块,用户输入不一定符合你的要求;你的程序要打开某个文件,这个文件可能不存在或者文件格式不对;你要读取数据库的数据,数据可能是空的;我们的程序跑着跑着,内存或者硬盘就满了。

软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们称之为异常,英文是:Exception,意识是例外。这些异常,怎么让我慢慢写的程序做出合理的处理,而不至于程序崩溃。

异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。

异常发送在程序运行期间,它影响了正常的程序执行流程。

异常主要分为以下三种类型:

  1. 检查性异常:最具代表性的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单的忽略。
  2. 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常想法,运行时异常可以在编译时被忽略。
  3. 错误ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译页检查不到的。

2、异常体系结构

Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。

在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception

在这里插入图片描述

图中只列举了一部分。

1、Error

Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。

Java虚拟机(JVM)运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,即将出现OutOfMemoryError。这些异常发送时,JVM一般会选择线程终止。

还有发送在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,是因为它们在应用程序的控制和处理能力之外,而且大多数是程序运行时不允许出现的状况。

2、Exception

上图所列举的异常中,RuntimeException是Exception分支中重要的子类。

这些异常一般是由程序逻辑错误引起的。程序应该从逻辑角度尽可能避免这类异常的发送。

3、Error和Exception的区别:

Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,JVM一般会选择线程终止。Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

3、异常处理机制

抛出异常

捕获异常

异常处理的五个关键字:try、catch、finally、throw、throws

栗子:

package Java;
public class Demo1 {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;
        System.out.println(a / b);
/*	运行报错:除数不能为0
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at Java.Demo1.main(Demo1.java:10)
*/
    }
}
package Java;
public class Demo1 {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;
        
        try { // 监控区域
            System.out.println(a / b);
        } catch (ArithmeticException e) {// 如果捕获到了异常,就会执行catch的内容:catch(想要捕获的异常类型)
            System.out.println("程序出现异常,除数不能为0");
        } finally {// 处理善后工作,最终会被执行的语句,可以不需要
            System.out.println("finally");
        }
        /*  运行不报错,而是输出catch中的语句以及最后finally的语句
        	程序出现异常,除数不能为0
        	finally
        */
    }
}
package Java;
public class Demo1 {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;
        try { // 监控区域
            System.out.println(a / b);
        } catch (Error e) {// catch也可以层层递进,范围必须从小到大
            System.out.println("Error");
        } catch (Exception e) {
            System.out.println("Exception");
        } catch (Throwable e) {
            System.out.println("最大的捕获类型:Throwable");
        } finally {
            System.out.println("finally");
        }
        /*运行结果为:Exception*/
    }
}
package Java;
public class Demo1 {
    public static void main(String[] args) {
        test(1, 0);
    }

    public static void test(int a, int b) {
        if (b == 0) { // 在进行除法运算前
            throw new ArithmeticException(); // 主动抛出异常,一般在方法中使用
        }
    }
}
package Java;
public class Demo1 {
    public static void main(String[] args) {

        try {
            test(1, 0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }
    // 假设方法中处理不了异常,则可以在方法上抛出异常:throws
    public static void test(int a, int b) throws ArithmeticException {
        System.out.println(a / b);
    }
}

4、自定义异常

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需要继承Exception类即可。

在程序中使用自定义异常类,大体可分为以下几个步骤:

  1. 创建自定义异常类。
  2. 在方法中通过throw关键字抛出异常对象。
  3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
  4. 在出现异常方法的调用者中捕获并处理异常。

栗子:

package Java;
// 自定义异常类
public class MyException extends Exception{
    // 传递数字:假设数字大于10,就抛出异常
    private int detail;
    public MyException(int a){
        this.detail = a;
    }

    // 异常的打印信息
    @Override
    public String toString() {
        return "MyException {" + "detail = " + detail + "}";
    }
}
package Java;

public class test {
    // 可能会存在异常的方法
    static void test(int a) throws MyException {
        System.out.println("传递的参数为:" + a);
        if (a > 10) {
            throw new MyException(a); // 抛出
        }
        System.out.println("不大于10");
    }

    public static void main(String[] args) {
        try {
            test(1);
            test(11);
        } catch (MyException e) {
            /*此处还可以增加一些处理异常的代码*/
            System.out.println(e); // e => 调用toString方法(异常的打印信息)
        }
    }
}
/* 输出结果:
传递的参数为:1
不大于10
传递的参数为:11
MyException {detail = 11}
*/

5、总结

  • 处理运行时异常的时候,采逻辑去合理规避同时辅助try-catch处理。
  • 在多重catch块后面,尽可能加一个范围较大的异常处理,来处理可能会被遗漏的异常,例如:catch(Exception)、catch (Throwable)。
  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常。
  • 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出。
  • 具体如何处理异常,要根据不同的业务需求和异常类型去决定。
  • 尽量添加finally语句块去释放占用的资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值