Java基础

基础

注释

  • 单行注释// (ctrl +/)

  • 多行注释(ctrl+shift+/)

    1
    /*文字*/

标识符

  • 字母(大小写),数字,下划线,美元符组成,但不能以数字开头
  • 大小写敏感

数据类型

基本数据类型,引用数据类型

  • 强类型语言:所有变量先定义后使用

  • 基本数据类型:

    数值类型:

    • 整数类型:byte(1个字节),short(2个字节),int(默认)(4个字节),long(long数字后要加L)(8个字节)
    • 浮点类型:float(float 数字后要加F),double(默认)
    • 字符类型:char(2个字节)

    boolean:

    ​ true false(1位)

  • 引用类型:

    • 接口
    • 数组

最好完全避免使用float类型的数字进行比较:float 离散,舍入误差

所有的字符本质上还是数字


1
2
3
4
5
6
7
8
String sa=new String("hello world");
String sb=new String("hello world");

System.out.println(sa==sb);//false

String sc="hello world";
String sd="hello world";
System.out.println(sc==sd);//true 对象 从内存分析

类型转换

  • 低—–>高

    byte,short,char,int,long,float,double

不能对布尔值进行转换

强制转换

  • 要避免内存溢出

自动转换

  • 低—>高

  • 注意计算时内存溢出

    1
    2
    3
    4
    5
    int money=1000000000;
    int yers=20;
    int total=money*years//结果为负数
    long total2=money*years//也为负,mony years 默认是int 计算之后还是int,在转换之前就出问题了
    long total3=money*((long)years)//先把一个数转换成long

    数据类型的范围

    • byte:-128-127
    • short:-32768~32767
    • int:-2147483648~2147483647
    • long:**-9223372036854775808~9223372036854775807**

变量

类变量(static),实例变量,局部变量

  • 实例变量:(在类里面 main方法外面)

    从属于对象,如果不初始化,会有默认值

    • 所有的数字默认值是0 或 0.0
    • 布尔值默认是:false
    • 除基本数据类型外(即引用类型):都是null
  • 类变量:**static** (当然也是在类里面main方法外面)

    从属于类,可以直接在main里面直接调用

  • 局部变量:(在main方法里面)

    必须声明变量类型和初始值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Dome01 {

//类变量 static 从属类
static double salary=2500;

//实例变量:从属于对象;(在类里面main方法外面)
String name;
int age;

//main 方法
public static void main(String[] args) {
//局部变量,在main里面:必须声明变量类型和初始化值
int i=10;
System.out.println(i);

//变量类型 变量名称
Dome01 dome01 = new Dome01();
System.out.println(dome01);
System.out.println(salary);
}



常量

final 常量名 =值

常量名一般使用大写字符

修饰符不存在先后顺序

命名规范

  • 所有变量,方法,类名:见名知意
  • 类成员变量:首字母小写和驼峰原则(monthSalary)即除第一个单词外后面的单词首字母都大写
  • 局部变量: 首字母小写+驼峰原则
  • 常量:大写字母和下划线MAX_VALUE
  • 类名:首字母大写+驼峰原则 Man GoodMan
  • 方法名:首字母小写+驼峰原则

运算符

  • long,int,short,byte 进行运算时,有long 的,直接变化为为long ,其余的都是**自动转化为int**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package operator;

public class Dome01 {
public static void main(String[] args) {
long a=1231231323123132L;
int b=123;
short c=10;
byte d=8;
System.out.println(a+b+c+d);//long
System.out.println(b+c+d);//int
System.out.println(c+d);//int

}
}
  • 关系运算符返回结果:true,false

    • instanceof 关系运算符
  • 自增减运算符

    1
    2
    3
    4
    5
    6
    7
    8
    int a=3;
    int b=a++;
    System.out.println(b);//b=3 a=4
    int c=++a;
    System.out.println(c);//c=5 a=5
    System.out.println(a);//a=5


  • 逻辑运算符:and or !

  • 位运算符:

    &:两个都是1才为1

    |:都是0才为0

    ^:对应位相同为0,不同为1

    << :*2

    >>:/2

  • 连接符+:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package operator;

    public class Dome03 {
    public static void main(String[] args) {
    int a=10;
    int b=20;
    System.out.println(a+b);//30
    System.out.println(""+a+b);//1020
    System.out.println(a+b+"");//30

    }
    }

  • 三元运算符

    x ? y : z


包机制

语法格式为:
package package1[.package2[.package3...]];

在正式的开发中,一般都采用公司域名倒置来作为包名

导入包,使用import语句将其导入,具体语法如下:
import package1[.package2[.package3...]].(ClassName|*);

javaDoc

将注释信息生成帮助文档

1
2
3
4
5
@auther 
@Version
@Since
@param
@throws

javaDoc用来生成自己的API文档

生成文档:cmd打开:javadoc -encoding UTF-8 -charset UTF-8 Doc.java UTF-8编码,避免出现非英语字符乱码

在代码文件对应的位置会多出许多文件,index.html打开就是生成的文档

理解Javadoc-CSDN博客

————

Scanner对象

  • next接受以空格作为结束标志
  • nextLine接受 回车作为结束标志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.scanner;
import java.util.Scanner;
public class Dome01 {
public static void main(String[] args) {

//创建扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方法接收:");
//判断用户有没有输入字符串
if (scanner.hasNext()){
//使用next方式接受
String str=scanner.next();
System.out.println("输出内容为:"+str);
}
//属于IO流类的不关闭会占用资源
scanner.close();

}
}


小练习

接受double类型的数字并计算总和及平均值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.scanner;

import java.util.Scanner;

public class Dome03 {

public static void main(String[] args) {
double sum=0;
int m=0;
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextDouble()) {
double a = scanner.nextDouble();
sum=sum+a;
m++;
}
System.out.println("sum为"+sum);
System.out.println("平均数为"+(sum/m));
scanner.close();
}
}


语法

  • 选择结构

    if

    switch:

    • 注意case穿透现象,写完每个case后都应该加上一个break

    • jdk7后switch也支持字符串比较即括号里的表达式也可以是字符串(字符串用双引号)

  • 循环结构

    while,do…while

    for:

    • (for (int i = 0; i < 100; i++) {}):快捷键:100.for

    • for(;;) {}死循环

      九九乘法表小练习

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      package com.zhang.struct;

      public class Dome02 {
      public static void main(String[] args) {
      int i=1;int j=1;
      for ( i = 1; i < 10; i++) {
      for( j=1;j<=i;j++){
      System.out.print(j+"*"+i+"="+(i*j)+ "\t");
      }
      System.out.println();
      }
      }
      }

    • 增强for循环:

      for(声明语句:表达式){

      }

      声明语句:声明新的局部变量,该变量的类型必须与数组元素的类型匹配

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

      之后数组重点使用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      package com.zhang.struct;

      public class Dome03 {
      public static void main(String[] args) {
      int[] numbers={10,20,30,40};//定义一个数组

      //遍历数组元素 增强for循环
      for (int x:numbers) {
      System.out.println(x);
      }
      System.out.println("=======");

      //等同于以下for循环
      for (int i=0;i<5;i++){
      System.out.println(numbers[i]);
      }

      }
      }


小练习打印三角形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.zhang.struct;

public class Dome05 {
public static void main(String[] args) {
for (int i=1;i<=5;i++){
//分割成三部分打印
for (int j=5;j>i;j--){
System.out.print("*");
}
for (int j=1;j<=i;j++){
System.out.print("~");
}
for (int j=1;j<i;j++){
System.out.print("^");
}
System.out.println();
}

}
/*结果,

****~
***~~^
**~~~^^
*~~~~^^^
~~~~~^^^^

将内层第一个for换成空格,第二三个for换成*,即可打印出三角形

*/

方法

类似c的函数

1
2
3
//eg:
System.out.prinntln()
//类 对象 方法

设计方法的原则:一个方法只完成一个功能,有利于后期去扩展

方法命名规则: 首字母小写+驼峰

方法的定义: 修饰符 返回类型 方法名(参数类型 参数名)

  • 修饰符:可选,告诉编译器如何调用该方法,定义了该方法的访问类型

方法的调用:对象名.方法名(实参列表)

方法的重载

在一个类中有相同的函数名,但形参不同的函数(调用时根据参数类型不同调用)

  • 重载规则:
  • 方法名称必须相同
  • 参数列表必须不同(个数或类型不同或参数排列顺序不同)
  • 方法返回类型可以相同也可以不同(重点在于参数那块不同)
  • 仅仅返回类型不同不足以成为方法重载

命令行传参

  • cmd窗口先编译成Dome01.class(命令:javac Dome01.java)

    执行编译文件时注意要加上包名,找对路径(在src目录下执行java xxx.xxx.xxx.Dome01 传参)

可变参数

本质是数组

  • 在方法声明中,在指定参数类型后加一个省略号…
  • 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数

递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//阶乘
package com.zhang.method;

public class Dome02 {
public static void main(String[] args) {
int b=4;
System.out.println(f(b));
}
public static int f(int a){
if (a==1){
return 1;
}
else{
return a*f(a-1);
}
}
}


数组

声明:

  • int[] array1; 首选方法
  • int array1[];

开辟空间:array1=new int [10] ;

—–>int[] array2=new int[10];

首先在栈里创建对象,new:在堆里开辟内存空间,在堆里赋值

初始化:

  • 静态初始化:创建以后不可更改

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

    静态初始化是在声明数组的同时为数组元素赋值。语法如下:

    1
    2
    3
    4
    5
    6
    // 语法
    type[] arrayName = {value1, value2, value3,...};

    // 示例
    int[] numbers = {1, 2, 3, 4, 5};
    String[] fruits = {"apple", "banana", "cherry"};

    在静态初始化中,数组的大小由花括号内元素的个数自动确定,不需要显式指定。

  • 动态初始化:(包含默认初始化)

    int[] b=new int [10];

    动态初始化是先声明数组,然后在后续代码中使用 new 关键字为数组分配内存空间并指定数组的大小

    初始化

    不同的数据类型在初始化是会有不同的默认值:

    • 数值类型:0

    • 布尔类型:false

    • 引用类型(即除基本类型外的):null

      数组默认初始化

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 数值类型数组
      int[] intArray = new int[3];
      // 数组元素默认值为 0,依次为:0, 0, 0

      // 布尔类型数组
      boolean[] booleanArray = new boolean[2];
      // 数组元素默认值为 false,依次为:false, false

      // 引用类型数组
      String[] stringArray = new String[4];
      // 数组元素默认值为 null,依次为:null, null, null, null

数组特点

  • 数组是相同类型的有序集合,其中的元素可以是任何类型的,包括基本类型和引用类型
  • 数组也是对象,数组元素相当于对象的成员变量
  • 数组长度是不可变的

增强for循环:

适合打印输出,不适合操作元素

1
2
3
4
5
6
7
8
9
10
11
12
package com.zhang.array;

public class Dome01 {
public static void main(String[] args) {
int[] arrays={1,2,3,4,5};
for (int array : arrays) {
System.out.println(array);

}
}
}

数组作为方法的参数和返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.zhang.array;

public class Dome02 {
public static void main(String[] args) {
int[] arrays={1,2,3,4,5};

printArrays(arrays);
int[]res=reverseArrays(arrays);
printArrays(res);

}
//打印数组 数组为参数
public static void printArrays(int[] arrays) {
for(int i=0;i<arrays.length;i++){
System.out.println(arrays[i]);
}
}

//逆序数组 数组为返回值时,创建新数组作为返回数组
public static int[] reverseArrays(int[] arrays) {
//创建新数组作为结果
int[] result=new int[arrays.length];
for(int i=0,j=arrays.length-1;i<arrays.length;i++,j--){
result[j]=arrays[i];
}

return result;
}

}


多维数组

1
2
3
4
5
6
7
8
9
10
package com.zhang.array;

public class Dome03 {
public static void main(String[] args) {
int[][] array1={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};4
System.out.println(array1.length);//4 行的长度
System.out.println(array1[0].length);//3 列的长度
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.zhang.array;

public class Dome03 {
public static void main(String[] args) {
int[][] array1={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
//for循环输出二维数组
for (int i=0;i<array1.length;i++){
for(int j=0;j<array1[i].length;j++){
System.out.println(array1[i][j]);
}
}
}
}

1
2
3
for(int i=0;i<arrays.length;i++){
System.out.println(arrays[i]);
}

Arrays类

Arrays类位于 java.util 包中,主要包含了操作数组的各种方法。

eg:

1
2
3
4
5
6
7
8
9
10
11
import java.util.Arrays;

public class Dome01 {
public static void main(String[] args) {
int arr[] = {1,2,3,4,5,6};
//将数组转化为字符串,此时输出结果为字符串类型
String newArr = Arrays.toString(arr);
System.out.println(newArr);
}
}


冒泡排序

  • 外层循环:最后一次循环肯定就剩一个数了,所以循环次数为:个数-1次 for(i=0; i<arrar,length-1;i++)

  • 内层循环

    每次内层循环是从前(0或1)开始—->往后

    接下来比较前后两个数并交换,后面的数if小就是小的数一直往后,if大就是大的数一直往后,最终内循环一次结束后,就可以确定出最后面的是最大值或最小值,下一轮需要比较的数的个数就少一个,内循环几次,下一次比较就可以少比较几个 所以 for(j=0; j<array.length-1-i;j++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int[] arrays={12,34,23,44,38};

for (int i=0;i<arrays.length-1;i++){
boolean flag=false;
for(int j=0;j<arrays.length-1-i;j++){
if(arrays[j+1]>arrays[j]){
int temp=arrays[j+1];
arrays[j+1]=arrays[j];
arrays[j]=temp;
boolean flag=true;
}
}
}

for(int i=0;i<arrays.length;i++){
System.out.println(arrays[i]);
}

稀疏数组

记录有效值及其行列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.zhang.array;

public class Dome06 {
public static void main(String[] args) {
//创建一个新的目标数组
int[][] arrays1=new int[11][11];
arrays1[1][2]=1;
arrays1[2][3]=1;
for(int[] ants:arrays1){
for(int ents:ants){
System.out.print(ents+" ");
}
System.out.println();
}

//统计有效值,为新建稀疏数组的行数做准备
int sum=0;
for(int i=0;i<arrays1.length;i++){
for(int j=0;j<arrays1[i].length;j++){
if (arrays1[i][j]!=0){
sum++;
}
}
}
System.out.println(sum);



//创建稀疏数组及它的第0行(第0行是 原数组的行数 列数 有效值)
int[][] arrays2=new int[sum+1][3];
arrays2[0][0]=arrays1.length;
arrays2[0][1]=arrays1[0].length;
arrays2[0][2]=sum;


//将有效值放入稀疏数组
int m=0;
for(int i=0;i<arrays1.length;i++){
for(int j=0;j<arrays1[i].length;j++){
if(arrays1[i][j]!=0){
m++;
arrays2[m][0]=i;
arrays2[m][1]=j;
arrays2[m][2]=arrays1[i][j];
}
}
}




//打印稀疏数组
for (int[] ants: arrays2){
for(int ents:ants){
System.out.print(ents+" ");
}
System.out.println();

}

}
}

—————

面向对象

本质:
以类的方式组织代码,以对象的组织(封装)数据

三大特性:

封装,继承,多态

从代码的角度:先有类才有对象


回顾方法及加深

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.oop.dome01;

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

}
/*
* 修饰符 返回类型 方法名(...){
* //方法体
* return 返回值;
* }*/
public String sayhello(){
return "Hello World";
}
public int max(int a,int b){
return a>b? a:b;//三元运算符
}
}

return&& break:

return :标志方法的结束,返回一个结果或者空

break:跳出switch循环,结束循环

方法调用:

静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.oop.dome01;

public class Student {

//static 静态方法
public static void say(){
System.out.println("Hello World");

}
}


//**********在下面的程序可以直接在main中调用(类.方法)****************************


package com.oop.dome01;

public class Dome02 {
public static void main(String[] args) {
Student.say();
}
}

非静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.oop.dome01;

public class Student {

//非 静态方法
public void say(){
System.out.println("Hello World");

}
}

//****************非静态方法不可直接调用,实例化后再由对象调用(对象.方法)**********************


package com.oop.dome01;

public class Dome02 {
public static void main(String[] args) {
//非静态

//先实例化 new
Student student1=new Student();
//再调用
student1.say();

}
}

加static的方法与类一起加载,非静态的方法,实例化后才存在

在同一个类中,可以直接在main方法中调用其他静态方法,

调用其他非静态的方法:

1 .在main中先new 实例化,再调用

2 .或者 非静态方法改成静态方法(加static)

值传递&&引用传递:
值传递传递形参,另外开辟了一块内存

引用传递,将类实例化得到实例化对象,实例化对象传递时直接指向同一块内存传递


类与对象的创建

  • 一个程序应该只有一个main方法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.oop.dome02;

public class Student {

//属性: 字段
String name;
int age;

//方法
public void study(){ //this代表当前这个类,this.当前类的属性
System.out.println(this.name+"在学习");
}
}







package com.oop.dome02;

public class Application {
public static void main(String[] args) {
//类:抽象的,需要实例化
//类实例化后返回对象
Student xiaoming = new Student();
Student daming=new Student();

//给xiaoming的属性赋值
xiaoming.name = "小明";
xiaoming.age=20;

//调用方法
xiaoming.study();//小明在学习
daming.study();//null在学习

//打印属性
System.out.println(xiaoming.name);//小明
System.out.println(xiaoming.age);//20
System.out.println(daming.name);//null
System.out.println(daming.age);//0


}
}

类是对象的模板,对象是类的实例,就像以上Student类中属性name,age不能写死,它只是一个模板,对象就是它的实例,用对象来调用属性进行赋值,[创建对象时默认初始化属性(daming 的name=null age=0)]

所以回到了开始那句话:面向对象编程的本质是:以类的方式组织代码,以对象的组织(封装)数据


构造器详解

  • 类中的构造器也叫构造方法,是在创建对象时必须调用的,并且构造器具有以下两个特点:
    1 .必须和类的名称相同

    2 .必须没有返回类型,也不能写void

  • 一个类即使什么也没有写,它也有一个默认构造器

  • 显示定义构造器(它其实就是一个特殊的无返回值的方法):

    1
    2
    3
    4
    5
    6
    7
    8
    package com.oop.dome02;

    public class Person {
    public Person() {

    }
    }

    构造器的作用:

    1. 实例化初始值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      package com.oop.dome02;

      public class Person {
      String name;
      public Person() {
      this.name="lili";
      }
      }



      package com.oop.dome02;

      public class Application {
      public static void main(String[] args) {
      //new 实例化对象
      Person person = new Person();
      System.out.println(person.name);//lili


      }
      }

      所以实例化对象的属性:

      1. 可以通过在main中实例化对象后用对象.属性赋值(即使用默认无参构造器),
      2. 可以在类中通过 显示无参构造器(属性写死)
      3. 有参构造器初始化(属性传参 new时传参)

有参构造&&无参构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.oop.dome02;

public class Person {
String name;

//new 关键字实例化,本质是在调用构造器
public Person() {

}

//有参构造:一旦定义了有参构造,无参构造就必须显式定义
public Person(String name) {
this.name = name;
}
}


package com.oop.dome02;

public class Application {
public static void main(String[] args) {
//new 实例化对象
Person person1 = new Person("xiaoming");//不加参数调用的就是无参构造器,打印结果为null,加上参数调用有参构造器,打印机结果xiaoming
// Person person1 = new Person("xiaoming");=======Person person1=new person(); person1.name="小明";
System.out.println(person1.name);


}
}

  • new 实例化,本质在调用构造器,会自动根据参数判断,有参数的直接调用有参构造器 (方法重载)。构造器用来初始化值,在构造器中 this.属性 直接就可以赋值

  • 注意一旦定义了有参构造,并且也无参实例化了(无参创建对象),无参构造就必须显式定义,否则会报错

    默认类中是有无参构造的,写了有参构造,无参构造就没有了,就需要显式定义

    alt+insert 快捷键默认生成有参构造器

  • 定义不同有参构造器即方法重载,在类中创建不同的属性,不同属性作为构造器参数实现方法重载


创建对象内存分析

image-20250322185600248


小总结

  1. 类是一个模板(抽象的),对象是一个具体的实例

  2. 方法:定义,调用

  3. 对象是通过引用来操作的:栈—–>堆(地址)

  4. 属性:字段 Field 成员变量

    • 初始化 :数字0 0.0

      ​ char : u0000

​ boolean: false

​ 引用: null

​ 修饰符 属性类型 属性名=属性值

  1. 对象的创建和使用:
    • 必须使用new关键字创造对象 ,构造器
    • 对象的属性方法调用: 对象名.属性 对象.方法
  2. 类:静态的属性,动态的方法

封装

  • 程序追求 高内聚低耦合

    高内聚就是:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用

  • 属性私有 get/set (alt + insert 快捷键生成get/set方法)

  • 关键字private 就不能直接 s1.name 来操作 借助get方法返回name 调用的方法得到name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

package com.oop.dome03;

public class Student {
//属性私有 private 关键字 外部的就不能直接 对象,属性 来操作属性
private String name;
private int id;
private char sex;

//提供一些操作私有属性的方法
//提供一些public的get /set方法

//get 获得数据 就是借助方法获得
public String getName(){
return this.name;
}

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

}






package com.oop.dome02;

import com.oop.dome03.Student ;

public class Application {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.getName());//null
s1.setName("小明");//通过方法进行设置属性
System.out.println(s1.getName());//小明

}
}

思考:不借助set方法的话,有参构造器也可以直接初始化属性

所以又体现出来构造器就是特殊的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.oop.dome03;

public class Application {
public static void main(String[] args) {
Student s1 = new Student("xiaoming");
System.out.println(s1.getName());

}
}


package com.oop.dome03;

public class Student {
//属性私有 private 关键字 外部的就不能直接 对象,属性 来操作属性
private String name;
private int id;
private char sex;

//提供一些操作私有属性的方法
//提供一些public的get /set方法

//get 获得数据 就是借助方法获得
public String getName(){
return this.name;
}


//有参构造器初始化属性
public Student(String name) {
this.name = name;
}
}

使用属性的时候再传参即可,构造器在new的时候就会调用,在new的时候直接传参

封装的好处:

规避不合法的数据,在set方法中设置条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.oop.dome03;

public class Application {
public static void main(String[] args) {
Student s1 = new Student();

System.out.println(s1.getAge());
s1.setAge(999);//数据不合法
System.out.println(s1.getAge());//!!!!!!!这里在set方法中设置了合法条件,所以输出3


}
}





package com.oop.dome03;

public class Student {
//属性私有 private 关键字 外部的就不能直接 对象,属性 来操作属性
private String name;
private int id;
private char sex;
private int age;

//提供一些操作私有属性的方法
//提供一些public的get /set方法

//get 获得数据 就是借助方法获得
public int getAge(){
return this.age;
}

//set 给这个数据设置值
public void setAge(int age){
if(age>120||age<0){
this.age=3;
}else{
this.age = age;
}

}
}

好处:

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

继承

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

  • 关键字 extends 子类是父类的扩展

  • 继承是类与类之间的一种关系,类与类之间的关系还有依赖,组合,聚合等

  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。

  • 只要子类继承了父类,实例化子类对象,对象也可以用父类中的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//父类
package com.oop.dome04;

public class Person {
public int money=10_0000_0000;
public void say(){
System.out.println("说了一句话");

}}



//子类继承父类
package com.oop.dome04;

public class Student extends Person {
}



//实例化子类,子类对象调用父类的方法和属性
package com.oop;

import com.oop.dome04.Student;

public class Dome01 {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.money);

}
}


修饰符:public(要加public,才能用子类创建的对象调用父类的属性)protected,default,private(私有的 子类不能继承私有属性,要借助get,set)

  • 在java中所有的类都默认直接或间接继承object类,也可以显示定义继承object类

  • java中只有单继承(即一个儿子对应一个爸爸)

  • 犯的错误:

    在父类中定义方法无返回值,使用子类创建的对象调用该方法时,因为该方法无返回值,不能直接输出,可以直接调用就好


super详解

  • 子类的方法中访问父类的属性或方法 super.属性或方法

    回顾:this.属性或方法调用当前类中的属性或方法

    在方法中可以互相直接调用方法

以下代码实现了 通过子类方法执行类的其他方法,和子类方法中调用父类方法

Person 父类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.oop.dome04;

public class Person {
public String name="Person";
int age;
public void sayHello() {
System.out.println("Hello World");
}

public void print(){
System.out.println("Person");

}


}

Student 子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.oop.dome04;

public class Student extends Person {
private String name="小明";


public void print(){
System.out.println("Student");

}

public void text1(){
/*print();//Student
System.out.println(this.name);
System.out.println(super.name);*/

}
public void text2(){
print();//方法中调用方法
this.print();//调用这个类的print方法
super.print();//父类的print方法

}


}

main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.oop;
import com.oop.dome04.Person;
import com.oop.dome04.Student;


public class Dome01 {
public static void main(String[] args) {
Student s1 = new Student();
s1.text2();


}
}


this && super

1
2
3
4
5
6
7
public void text1(String name){
print();// 此类中的print方法 Student
System.out.println(this.name);// 就近原则 此类中的name
System.out.println(super.name);//父类中的属性name
System.out.println(name);//方法传参的name

}

有参构造&& 无参构造

以下部分代码 输出: Person无参构造

​ Student无参构造

new的时候就根据实际类型调用构造器,若是子类构造器,在子类构造器中有隐藏的调用父类构造器的代码 super()

  • Person s1 = new Person();
    //只调用Person 构造器
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39

    *x显示调用父类的构造器必须在子类的构造器第一行*

    ``` java
    public class Dome01 {
    public static void main(String[] args) {
    Student s1 = new Student(); //输出 Person无参构造 Student无参构造
    // s1.text2();


    }
    }

    //*******以下为父类**********

    package com.oop.dome04;

    public class Person {


    public Person() {
    System.out.println("Person无参构造");
    }
    }

    //*********以下为子类*************

    package com.oop.dome04;

    public class Student extends Person {


    public Student() {
    //在子类的构造方法中隐藏代码super() new的时候调用父类的无参构造
    // super();//调用父类的构造器必须在子类的构造器第一行
    System.out.println("Student无参构造");
    }
    }

  • 当子类定义一个有参构造后,无参构造就会消失,就不会默认new的时候调用父类的无参构造了——>只要定义了一个有参构造,就必须显示定义无参构造

小总结:

  1. super 注意点:

    • super 调用父类的构造方法,必须在构造方法的第一个
    • super必须只能出现在子类的方法或者构造方法中去调用父类的方法或属性
    • super() 和this() 不能同时调用构造方法,因为他们都要写在第一行
  2. super VS this:
    代表对象不同:this :代表调用者这个对象 super:代表父类对象的应用

    前提: this :没有继承也可以使用 super : 只能在继承条件下才能使用

    构造方法:this() 本类的构造 super() 父类的构造


方法重写

重写都是方法重写,与属性无关

方法重写只与非静态方法有关,与静态方法无关

定义的什么类(等号左边的)就调用的什么类的静态方法,回想之前学的方法的调用,静态方法可以直接用类调用(它随类一起加载)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Dome01 {
public static void main(String[] args) {

//方法的调用只和左边,定义的数据类型有关
A a = new A();
a.test();//A类中的方法 结果:A----test

//父类的引用指向了子类
B b = new A();
b.test();//B类中的方法 结果:B---->test
//!!!!它们调用的都是静态方法
}
}

//A类
public class A extends B {

public static void test(){
System.out.println("A--->test()");
}
}



//B类
public class B {

public static void test(){
System.out.println("B--->test()");
}
}

然而 非静态方法 与以上的静态方法不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Dome01 {
public static void main(String[] args) {
A a = new A();
a.test();//还是A类输出 A---->test
B b = new A();//子类重写了父类的方法 (非静态)
b.test();//!!!!这里就是调用的A了 输出A--->test


}
}




package com.oop.dome05;

import org.w3c.dom.ls.LSOutput;

public class A extends B {
@Override
public void test() {
System.out.println("A--->test()");
}

}






package com.oop.dome05;

public class B {

public void test(){
System.out.println("B--->test()");
}
}


理解:

静态方法属于类,非静态方法属于对象

静态时方法随类一起加载,new 的b 是B类(等号左边),因此执行B类方法

非静态时,子类重写了父类的方法,都执行子类方法

小总结

重写存在于子类父类,有了继承才能在子类中重写父类的方法

  1. 子类父类的方法名必须相同,(alt+insert快捷键重写方法自动给就可以实现)

  2. 参数列表也必须相同(不同那就是方法的重载啦,也不是,方法的重载是在一个类中的)

  3. 子类重写的方法的修饰符范围只能比父类的修饰符扩大,

    public>protected>default(默认什么都不写的)>private

  4. 抛出异常:范围,可以缩小,不能扩大

    ClassNotFoundException(小异常)—->Exception(大异常)

为什么需要重写:

  1. 父类的功能子类不需要,或者不一定满足!

    快捷键:alt+insert 选择重写方法


多态

1
2
3
//引用类型       实际类型
Person s1=new Student();
Student s2=new Student();
  • 一个对象的实际类型是确定的,但引用类型是不确定的

  • 方法重写后,父类的引用类型调用子类的方法(相比继承:子类继承父类的方法和属性),(多态讲的父类何时能调用子类的方法)

  • 当方法只有子类中有的时候,父类调用不了子类的,需要强制类型转换(继承时子类自然继承父类的)

  • 对象能执行哪些方法看左边引用类型:

    子类的引用类型能调用继承的父类的方法

    而父类的引用类型不能调用子类独有的(要强制类型转换),能调用子类重写后的

  • 注意事项:

    1. 多态时方法的重写,属性没有多态

    2. 创建对象时父类和子类有联系,要注意类型转换异常! ClassCastException!

    3. 多态存在条件:继承关系,方法重写,父类引用指向子类对象

      不能重写的方法

      • static 方法,不属于实例,属于类,

      • final 修饰的是常量

      • private 方法


instanceof

  • instanceof是Java中的一种关键字,用于判断一个对象是否属于某个类或其子类

  • 在Java中,instanceof关键字通过比较对象的类型类的类型来判断对象的实例关系。其底层实现原理是通过比较对象的类型和类的类型在内存中的地址。如果两者相同或者具有继承关系,则返回true;否则返回false。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.oop;
import com.oop.dome06.Person;

import com.oop.dome06.Student;
import com.oop.dome06.Teacher;


public class Dome01 {
public static void main(String[] args) {
//引用类型Student 实际类型Student
Student student = new Student();
System.out.println(student instanceof Student);//ture
System.out.println(student instanceof Object);//ture
System.out.println(student instanceof Person);//ture
//System.out.println(student instanceof Teacher);//编译报错
// System.out.println(student instanceof String); 编译报错


//引用类型Object 实际类型Student
Object object = new Student();
System.out.println(object instanceof Student);//ture
System.out.println(object instanceof Object);//ture
System.out.println(object instanceof Person);//ture
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);// false
System.out.println("=====================");



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


}
}

小总结:

  • X instanceof Y

    X:对象 Y:类或接口

    • *编译*:看对象左边的引用类型(如以下例子为:Object),比较引用类型和Y之间是否存在父子类关系,存在则成功编译。(以上面代码@@@标记处为编译失败的例子)

    • *执行*:看对象右边的实际类型(如以下例子为:Student),比较实际类型和Y之间是否存在父子类关系,存在就输出true 。(以上代码###标记处为输出false的例子)

      • eg:

        Object s1=new Student();==父类引用指向子类对象==

        System.out.println(s1 instanceof Student);

    • 还有一点注意:

      执行比较时:不是比较实际类型嘛,但***必须 实际类型 是Y的子类或就是Y类才输出ture,***实际类型如果是Y的父类则输出false

      (输出false的情况: 实际类型是Y的父类或他们两个不存在父子类关系)

      1
      2
      3
      4
      5
      6
      7
      //引用类型Object     实际类型Person
      Object object = new Person();
      System.out.println(object instanceof Student);//false Person是Student的父类输出为false
      System.out.println(object instanceof Object);//ture
      System.out.println(object instanceof Person);//ture
      System.out.println(object instanceof Teacher);//false 同理 Person 是Teacher的父类输出false
      System.out.println(object instanceof String);// false 这里就是不存在父子关系输出false

类型转换

  • 对象引用类型为父类,不能通过对象调用子类中独有的方法

    如果引用类型为父类,通过对象调用父类子类共有的方法(就是多态啦)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.oop;
import com.oop.dome06.Person;

import com.oop.dome06.Student;
import com.oop.dome06.Teacher;


public class Dome01 {
public static void main(String[] args) {
//类型之间的转换:父---->子
// 高<---转-----低
Person s = new Student();
//引用类型为父类的对象强制类型转换才能调用子类独有的方法
((Student)s).go();
//或
Student student=(Student)s;
student.go();

//自动低转高
//子类转父类可能丢失自己本来的一些方法(只存在于子类中的方法)
Person person=student;
//person.go(); 就无法调用


}
}

  1. 强制类型转换 存在条件:父类引用指向子类对象

    直接 父类引用指向父类对象 创建不能强制类型转换()

    会报ClassCastException的错误

  2. 父类转换为子类,向下转型,强制转换


static

  • 非静态方法必须new一个对象后才能用对象调用,

  • 静态方法跟类一起就加载出来了,可以直接 类.方法 调用

    在同一个类中也可以直接写方法调用,非静态方法中也是可以调用静态方法的

  • new的时候自动调用静态代码块(只执行一次),匿名代码块,构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.oop.dome07;

import com.sun.security.jgss.GSSUtil;

public class Person {
//2:赋初始值~
{
System.out.println("匿名代码块");
}
//1: 只执行一次~
static{
System.out.println("静态代码块");
}
//3:

public Person() {
System.out.println("构造方法");
}

public static void main(String[] args) {
Person p1 = new Person();
System.out.println("==========");
Person p2= new Person();
}
/*
* 输出:
* 静态代码块
匿名代码块
构造器
==========
匿名代码块
构造器*/


}

发现了一个比较好玩的:

new的时候调用顺序为静态代码块,匿名代码块,构造方法

调用构造方法的时候是根据对象的实际类型调用的!

  • new Student (),实际类型是子类就调用子类构造器并且先执行之前学习的隐藏代码super()即先调用父类的构造器,再调用子类的构造器
  • new Person(),实际类型是父类就直接调用父类构造器了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.oop.dome07;

import com.sun.security.jgss.GSSUtil;
import com.oop.dome07.Person;

public class Person {
//2:赋初始值~
{
System.out.println("匿名代码块");
}
//1: 只执行一次~
static{
System.out.println("静态代码块");
}
//3:

public Person() {
System.out.println("构造方法Person");
}

public static void main(String[] args) {
Person p1 = new Student();
System.out.println("==========");
Person p2= new Person();
}
/*静态代码块
匿名代码块
构造方法Person
构造方法:Student
==========
匿名代码块
构造方法Person*/


}

  • 遗留一个问题:静态代码,匿名代码写在了Person 类中

    new一个 引用属性,实际属性都是Student的对象 也会调用静态代码,匿名代码


静态导入包:

1
2
3
4
5
6
7
8
9
package com.oop.dome07;
//静态导入包
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
System.out.println(random());

}
}
  • final 修饰常量,如果类被final修饰是没有办法作为父类继承的

static 静态方法

父类是静态方法,子类是不能重写的


抽象类

  • 抽象类本质也是类,是由abstract声明的类 (abstract class) extends :单继承 (接口可以实现多继承)
  • 子类继承抽象类后,必须实现抽象方法,除非该子类也必须声明为抽象类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.oop.dome08;
//abstract 抽象类
public abstract class Action {
//abstract 抽象方法,只有方法的名字,在抽象类中没有方法体 方法的实现是在继承他的子类中重写实现
public abstract void doSonmething();
}








package com.oop.dome08;
//继承了抽象类的子类,都必须要实现(相当于重写) 抽象类 的方法(因为抽象类中没有方法体) 普通方法就不一定必须重写了
//除非子类也是抽象类,那就由子子类去实现了
public class A extends Action {
@Override
public void doSonmething() {

}
}

特点:

  1. 抽象类不能new,无法实例化,只能靠子类去实现它
  2. 抽象类里面也可以有普通方法,但一旦有了抽象方法就必须是在抽象类里面

接口

  • 普通类:只有具体实现方法

    抽象类:具体实现方法和定义(抽象方法)都有!

    接口:只有抽象方法的定义! 约束和实现分离

    接口的本质是契约,声明接口的关键词是:interface

  • 接口中定义的所有属性,只能是常量,默认修饰符public static final

    接口中定义的所有方法,默认什么都不写的修饰符public abstract

  • 没有构造方法

  • 实现类:一般以impl结尾 一个类实现一个接口通过 implements 关键字,并且一个实现类必须重写接口里面的所有方法

  • 实现类实现多个接口,要重写里面的所有抽象方法,如果接口中的方法重名,只需要在实现类中重写一次即可,重写的方法既代表对接口1中方法的重写,又代表对接口2中的方法的重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//**********接口*************
package com.oop.dome09;

public interface UserService {//接口都需要实现类
//接口中的所有方法都是默认抽象的 public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}



//**********实现方法**************

package com.oop.dome09;
//implements关键字 可以实现接口 extends 只能是单继承

//实现了接口的类,就需要重写接口的方法~
public class UserServiceImpl implements UserService {
@Override
public void add(String name) {

}

@Override
public void delete(String name) {

}

@Override
public void update(String name) {

}

@Override
public void query(String name) {

}
}

  • 接口里面定义的都是抽象方法,定义常量 静态常量方法

作用:

  1. 约束
  2. 在接口中定义一些方法(没有方法体),让不同的 实现类 实现,实现接口必须重写方法
  3. 接口不能实例化,没有构造方法。接口中没有方法实现(和抽象类一样),
  4. implements 可以实现多个接口

抽象类和接口的共同点

  1. 都是为了让方法抽象,再让子类去实现

  2. 都不能实例化本类对象,只能让子类实例化对象


接口与类之间的关系:

  • 类于类的关系:继承,只能是单继承,不能多继承,(单继承多继承主体是下一级 单继承 多继承)但可以是多层继承

  • 类与接口之间的关系:实现关系,实现类 来 实现接口

  • 接口与接口之间的关系:继承关系(也是extends关键字),可以单继承,*也可以多继承*

    若实现类实现的是最下面的子接口的话,需要重写这个体系中所有的抽象方法


N种内部类

  • java 一个类 中可以有多个class 但只能有一个public class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.oop.dome10;

import org.w3c.dom.ls.LSOutput;

public class Outer {
private int id=105;
public void out() {
System.out.println("这是外部类的方法");
}

private void outPrivate() {
System.out.println("这是外部类的私有方法");
}



public class Inner{

public void in() {
System.out.println("这是内部类的方法");
}

//内部类写方法访问外部类的私有属性
public void getId() {
System.out.println(id);
}
public void getOut() {
outPrivate();//调用外部方法
}

}
}




package com.oop;
import com.oop.dome10.Outer;

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

//实例化外部类
Outer outer = new Outer();
//通过外部类对象new内部类来实例化内部类~
Outer.Inner inner = outer.new Inner();
inner.in();

//内部类实例化对象调用内部类方法实现访问外部类的私有属性
inner.getId();//内部类获取外部类私有属性
inner.getOut();//内部类获取外部类私有方法

/*输出: 这是内部类的方法
105
这是外部类的私有方法*/

}
}


总结:

  • 实例化内部类对象时,先实例化一个外部的对象,再通过这个外部对象new内部对象

    • //实例化外部类
      Outer outer = new Outer();
      //通过外部类对象new内部类来实例化内部类~
      Outer.Inner inner = outer.new Inner();
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54

      - ***子类对象访问父类私有属性和方法,内部类对象访问外部类私有属性和方法***,通过在子类或内部类写类似get方法即可获取到

      ***

      **静态内部类**

      以上代码的内部类加上static 就是静态内部类了

      - 静态内部类可以直接访问外部类的静态成员,但不能访问外部类的实例成员

      - 静态内部类中 获取外部类的私有属性和方法的代码就会出错:因为先实例化的外部对象,static 静态内部类先加载出来,而外部类的实例成员还没有加载出来



      **匿名内部类:**

      ``` java
      package com.oop.dome10;

      public class Test {
      public static void main(String[] args) {
      /*Apple apple = new Apple();
      apple.eat();*/

      //没有名字初始化类,不用将实例保存单变量中~
      new Apple().eat();


      //new 接口实例化对象
      //下面这个类其实就是一个实现类,匿名内部类
      UserService userService = new UserService() {
      //不重写接口方法的话会报错
      @Override
      public void hello() {
      System.out.println("hello");
      }

      };//注意这里的分号

      }
      }
      //下面的在Test类外面写

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

      //接口
      interface UserService{
      void hello();
      }

异常

Exception

  • 异常指程序运行中出现的不期而至的各种状况:文件找不到,网络连接失败,非法参数,异常发生在程序运行期间,它影响了程序正常执行流程

    • int a=10
      int b=0;
      int res=a/b;//1.异常是程序当执行到a/b 时,因为 b=0,程序就会出现(抛出)异常,ArithmeticException(算术异常)
      //2.当抛出异常后,程序就退出,崩溃了
      //3.这样一个不算致命的错误导致程序崩溃  不好
      //4.所以需要异常处理机制来解决这个问题
      System.out.println("程序继续运行");
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32

      - 异常分为两大类:

      Error:(错误) JVM无法解决的严重错误 eg: 栈溢出 Error 是严重错误,程序会崩溃

      Excpetion: 分为两大类: 运行时异常(程序运行时异常)和编译时异常(编程时编译器就检查出异常)

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

      ***



      #### **异常处理机制**

      - 进行了异常处理,即使出现了异常,程序仍可继续执行



      *try-catch-finally*

      - 将一段可能有问题的代码选中 ctrl+alt+t 使用try-catch-finally 捕获

      ```java
      try{
      //可能异常的代码
      }catch(Exception e){
      //当捕获到异常时,系统将异常封装成Exception 对象e 传递给catch
      }finally{
      //不管try代码块是否有异常,最终都要执行finally
      //所以一般将关闭资源放在finally中
      }

throws(投掷)

  • try-catch-finally 和throws 二选一,程序员如果没有显示处理异常,默认throws

  • 将发生的异常抛出,交给调用者(方法来处理),最顶级的处理者就是JVM

    throw 后面的异常类型可以是方法中产生的异常类型,也可以是它的父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

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

}
public void f2() throws FileNotFoundException,NullPointerException ,ArithmeticException {
//创建了一个文件流对象
//1.这里异常是一个编译异常 FileNotFoundException
//2.可以使用try-catch-finally
//3. 使用throws ,抛出异常,放调用f2的调用者(方法)处理
//4.throws 后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
//5. throws 后面的关键字也可以是抛出多个异常

FileInputStream fis = new FileInputStream("d://aa.txt");
}
}

image-20250326094727521

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.exception;

public class Test {
public static void main(String[] args) {
int a=1;
int b=0;

//快捷键 ctrl +alt+ t
try {//try 监控区
System.out.println(a/b);
}catch (ArithmeticException e) {//catch 捕获异常
System.out.println("程序异常,变量b不能为0");
}finally {//处理善后工作 !!!!没有捕获到错误也是执行的
System.out.println("finally");
}

//finally 可以不要 后续学习的 IO流 资源需要关闭!
}
}

捕获多个异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.exception;

public class Test {
public static void main(String[] args) {
int a=1;
int b=0;



try {//try 监控区
if (b==0){
throw new ArithmeticException();//主动的抛出异常
}

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("Exception");
}
finally {
System.out.println("finally");
}


}
}

Java异常详解(全文干货) - 个人文章 - SegmentFault 思否

自定义异常

当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息

步骤:

  1. 定义类:自定义异常类名,继承Exception 或RuntimeException
  2. 如果继承Exception ,属于编译异常
  3. 如果继承RuntimeException ,属于运行异常(一般都是继承RuntimeExceptipn)

即我们把自定义异常作为运行时异常(好处可以使用默认的处理机制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.exception;

public class CustomExcepption {
public static void main(String[] args) { /*throws AgeException*/
int age=818;
if (!(age>=18 && age<=120)){
throw new SelfException("年龄需要在18~120之间");//new 自定义异常类,才能调用构造方法
}
System.out.println("你的年龄范围正确");
}
}
//自定义类名 继承 运行异常 就变成 自定义异常 一般情况下自定义异常是继承编译异常 (好处:我们可以使用默认的处理机制)
class SelfException extends RuntimeException {
public SelfException(String message) {
super(message);
}
}

image-20250326192411669


throw vs throws:

  • throws :代表异常处理方式 位于方法声明中 后面跟异常类型

    1
    throws AgeException
  • throw: 是手动生创建异常对象的关键字 位于方法体中 后面跟异常对象

    1
    throw new SelfException("年龄需要在18~120之间");


Java基础
https://bxhhf.github.io/2025/03/20/java-基础/
作者
bxhhf
发布于
2025年3月20日
许可协议