【狂神说视频连接】
https://www.bilibili.com/video/BV12J41137hu?spm_id_from=333.999.0.0
Hello World
- 新建一个文件夹,存放代码
- 新建一个java文件
- 文件后缀名为.java
- Hello.java
- 【注意点】系统可能没有显示文件后缀名,需要我们手动打开
- 编写代码
public class Hello{
public static void main(String[] args){
System.out.print("Hello,World");
}
}
- 编译javac java文件,会生成一个class文件
- 运行class文件,java class文件
可能遇到的情况:
- 每个单词都要注意大小写正确,java对大小写敏感
- 尽量使用英文
- 文件名和类型必须保持一致,并且首字母大写
- 符号不要使用中文
第一章
注释
- 单行注释
- 多行注释
- 文本注释(JavaDoc)
public class Hello {
public static void main(String[] args) {
// 单行注释
// 输出一个Hello,World!
/*
我是多行注释
*/
/**
* 我是文档注释
*/
System.out.println("com.xie.base.Hello,World!");
}
}
标识符
-
关键字
-
Java所有的组成部分都需要名字,类名,变量名以及方法名都被称为标识符
标识符注意点
- 所有的标识符都应该以字母(A-Z或者a-z),美元符($),或者下划线(_)开始
- 首字符之后可以是字母(A-Z或者a-z),美元符($),或者下划线(_)或者数字的任意字符组合
- 不能使用关键字作为变量名或方法名
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value、 _1_value
- 非法标识符举例:123abc、-salary、#abc
- 可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音
数据类型
- 强类型语言
- 要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用
- 弱类型语言
- Java的数据类型分为两大类
- 基本类型(primitive type)
- 引用类型(reference type)
public class Demo2 {
public static void main(String[] args) {
// 八大基本数据类型
// 整数
int num1 = 10; // 最常用
byte num2 = 20;
short num3 = 30;
long num4 = 40L; // Long类型要在数字后面加个L
// 小数 浮点数
float num5 = 50.1F; // float类型要在数字后面加个F
double num6 = 3.1415926;
// 字符
char name = '国';
// 布尔值 是非
boolean flag = true;
}
}
拓展
public class Demo3 {
public static void main(String[] args) {
// 整数拓展:进制 二进制:0b 十进制 八进制:0 十六进制:0x
int i1 = 0b10; // 二进制:0b
int i2 = 10;
int i3 = 010; // 八进制:0
int i4 = 0x10; // 十六进制:0x
System.out.println(i1);
System.out.println(i2);
System.out.println(i3);
System.out.println(i4);
System.out.println("==============");
// 浮点数拓展
// 银行的业务一般不适用浮点数表述,有四舍五入等原因,而是使用BigDecimal:数学工具类
float f = 0.1f;
double d = 1.0/10;
System.out.println(f == d); // 理论显示应该是true,但是显示了false
float f1 = 2313132313f;
double d1 = f1 + 1 ;
System.out.println(f1 == d1); // 理论显示应该是false,应该是true
System.out.println("==============");
// 字符拓展 所有的字符本质还是数字 和编码有关 Unicode
char c1 = 'A';
char c2 = '中';
char c3 = '\u0061';
System.out.println(c1);
System.out.println((int)c1); // 强制类型转换
System.out.println(c2);
System.out.println((int)c2); // 强制类型转换
System.out.println(c3);
}
}
# 结果
2
10
8
16
==============
false
true
==============
A
65
中
20013
a
类型转换
-
由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换
-
低---------------------------------------------------------->高
byte,short,char -> int -> long -> float -> double (小数的优先级一定大于整数)
-
运算中,不同类型的数据先转换为同一类型,然后进行运算
-
强制类型转换
-
自动类型转换
public class Demo4 {
public static void main(String[] args) {
// 强制转换 强制转换:高->低 自动转换:低->高
int i = 128;
byte b = (byte)i; // 内存溢出
double b1 = i;
System.out.println(i);
System.out.println(b);
System.out.println(b1);
/*
注意点:
1. 不能对boolean类型转换
2. 不能把对象类型转换为不相干的类型
3. 在把高容量转换到低容量的时候,需要进行强制类型转换。低容量转高容量时自动转换
4. 转换的时候可能存在内存溢出,或者精度的问题
*/
System.out.println("===========================");
double d1 = 23.7;
float f1 = -45.87f;
System.out.println((int)d1);
System.out.println((int)f1);
System.out.println("===========================");
char c = 'a';
int d2 = c+1;
System.out.println(d2);
System.out.println((char)d2);
}
}
# 结果
128
-128
128.0
===========================
23
-45
===========================
98
b
注意细节
public class Demo5 {
public static void main(String[] args) {
// 操作比较大的数的时候,注意内存溢出问题
// jdk7新特性,数字之间可以用下划线分隔
int money = 10_0000_0000;
int years = 20;
int total1 = money*years; // 计算的时候内存溢出了
long total2 = money*years; // 在转换之前已经出问题了
long total3 = money*(long)years; // 正确的方式,先把一个转换为long
System.out.println(total1);
System.out.println(total2);
System.out.println(total3);
}
}
# 结果
-1474836480
-1474836480
20000000000
变量
- 变量是什么?就是可以变化的量
- Java是一种强类型语言,每个变量都必须声明其类型
- Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域
type varName [=value];
// 数据类型 变量名 = 值;
- 注意事项:
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束
- 变量作用域
- 类变量(静态变量)
- 实例变量
- 局部变量
public class Demo6 {
// 类变量 static 从属于类
static double salary = 2500;
// 实例变量:从属于对象,如果不进行初始化,数字类型的默认值为0 0.0
// 布尔类型的默认值为false
// 除了基本类型,其余类型的默认值都为null
String name;
int age;
public static void main(String[] args) {
// 局部变量:必须声明和初始化
int i = 10;
System.out.println(i);
System.out.println("===================");
Demo6 demo6 = new Demo6(); // 需要先new
System.out.println(demo6.age);
System.out.println(demo6.name);
System.out.println("===================");
System.out.println(salary); // 不需要new
}
}
# 结果
10
===================
0
null
===================
2500.0
变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰命名规则:monthSalary
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则:Man,GoodMan
- 方法名:首字母小写和驼峰原则:run(),runRun()
常量
- 常量(Constant):初始化(initialize)后不能再改变值!也就是不会变动的值。
- 所谓常量可以理解为一种特殊的变量
- final 类型 常量名 = 值;
- final double PI = 3.14;
- 常量名一般使用大写字符
public class Demo7 {
// 修饰符,没有先后顺序
static final double PI = 3.14;
// 以下写法也可
final static double PI1 = 3.14;
public static void main(String[] args) {
System.out.println(PI);
System.out.println(PI1);
}
}
# 结果
3.14
3.14
运算符
Java语言支持如下运算符:
- 算数运算符:+,-,*,/,%,++,–
- 赋值运算符:=
- 关系运算符:> , <、>=,<=, ==,!=,instanceof
- 逻辑运算符:&&,||,!
- 位运算符:&,|,^,~,>>,<<,>>>(了解)
- 条件运算符:? :
- 扩展赋值运算符:+=,-=,*=,/_=
public class Demo1 {
public static void main(String[] args) {
// 二元运算符
int a = 10;
int b = 20;
System.out.println(a+b);
System.out.println(a-b);
System.out.println(a*b);
System.out.println(a/(double)b);
}
}
# 结果
30
-10
200
0.5
public class Demo2 {
public static void main(String[] args) {
long a = 15874594213566L;
int b = 123;
short c = 10;
byte d = 8;
System.out.println(a+b+c+d); // 有long类型输出结果就为long类型
System.out.println(b+c+d); // int类型
System.out.println(c+d); // int类型
}
}
结果
15874594213707
141
18
public class Demo3 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 21;
System.out.println(a>b);
System.out.println(a<b);
System.out.println(a==b);
System.out.println(a!=b);
// 取余,模运算
System.out.println(c%a);
}
}
结果
false
true
false
true
1
public class Demo4 {
public static void main(String[] args) {
// ++ 自增 -- 自减 一元运算符
int a = 3;
System.out.println("a="+a);
int b = a++; // 先执行b=a,然后a自增
System.out.println("a="+a);
int c = ++a; // 先自增,然后执行c=a
System.out.println("a="+a);
System.out.println("b="+b);
System.out.println("c="+c);
// 扩展 幂运算 2^3=8 很多运算,我们会使用工具类来进行运算
double pow = Math.pow(2, 3);
System.out.println(pow);
}
}
结果
a=3
a=4
a=5
b=3
c=5
8.0
public class Demo5 {
public static void main(String[] args) {
// 逻辑运算
// 与(and) 或(or) 非(取反)
boolean a = true;
boolean b = false;
System.out.println(b&&a);
System.out.println(b||a);
System.out.println(!(b&&a));
System.out.println("==================");
// 短路运算
int c = 5;
boolean d = (c<4)&&(c++<4);
System.out.println(d);
System.out.println(c); // 因为判断c<4已经错了,所以没有执行c++,导致c依然等于5
boolean e = (c++>4)&&(c>4);
System.out.println(e);
System.out.println(c); // 这里执行了c++,c变为了6
}
}
结果
false
true
true
==================
false
5
true
6
public class Demo6 {
public static void main(String[] args) {
// 位运算
/*
A = 0011 1100
B = 0000 1101
------------------------
A&B = 0000 1100 //两个位都为1的时候才为1,其余都为0
A|B = 0011 1101 //两个位都为0的时候才为0,其余都为1
A^B = 0011 0001 // 两个位相同则为0,不相同则为1
~B = 1111 0010 // 和对应为完全相反
计算 2*8 = 16 => 2*2*2*2
<< 相当于 *2
<< 相当于 /2
0000 0000 0
0000 0001 1=2^0
0000 0010 2=2^1
0000 0100 4=2^2
0000 1000 8=2^3
0001 0000 16=2^4
*/
System.out.println(2<<3);
}
}
结果
16
public class Demo7 {
public static void main(String[] args) {
int a = 10;
int b = 20;
a+=b;
System.out.println(a);
System.out.println("=============");
// 字符串连接符
System.out.println(""+a+b);
System.out.println(a+b+"");
}
}
结果
30
=============
3020
50
public class Demo8 {
public static void main(String[] args) {
// x ? y : z
// 如果x==true,则结果为y,否则结果为z
int score1 = 50;
int score2 = 80;
String type1 = score1 < 60 ? "不及格" : "及格";
String type2 = score2 < 60 ? "不及格" : "及格";
System.out.println(type1);
System.out.println(type2);
}
}
结果
不及格
及格
包机制
-
为了更好地组织类,Java提供了包机制,用于区别类名的命名空间
-
包语句的语法格式为:
package pkg1[. pkg2[. pkg3]];
-
一般利用公司域名倒置作为包名
-
为了能够使用某一个包的成员,我们需要在ava程序中明确导入该包,使用“imoort”即可
import package1[. package2…].(classname|*);
JavaDoc
- javadoc命令生成自己API文档
- 参数信息
- @author 作者名
- @version 版本号
- @since 知名需要最早使用的jdk版本
- @param 参数名
- @return 返回值
- @throws 异常抛出
package operater;
/**
* @author xie_xuchun
* @create 2021-12-26 21:42
*/
public class Demo9 {
String name;
/**
* @author xie_xuchun
* @param name
* @return
* @throws Exception
*/
public String abc(String name) throws Exception{
return name;
}
}
第二章
Scanner对象
-
之前我们学的基本语法中并没有实现程序和人的交互,但是Java给我们提供了一个工具类,我们可以获取用户的输入。java.util.Scanner是Java5的新特性,我们可以通过Scanner类来获取用户的输入。
-
基本语法:
Scanner s = new Scanner(System.in);
-
通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前我们一般需要使用hasNext()与hasNextLine()判断是否还有输入的数据。
public class Demo1 {
public static void main(String[] args) {
Scanner scanner1 = new Scanner(System.in);
Scanner scanner2 = new Scanner(System.in);
System.out.println("使用next方式接收:");
if(scanner1.hasNext()){ // if判断可用可不用
String str = scanner1.next();
System.out.println("输出内容为:"+ str);
}
System.out.println("使用nextLine方式接收:");
if(scanner2.hasNextLine()){
String str1 = scanner2.nextLine();
System.out.println("输出内容为:"+ str1);
}
scanner1.close(); // 关于IO流的方法要关闭,不然会占用内存,注意在最后关闭
scanner2.close();
}
}
结果
使用next方式接收:
hello world
输出内容为:hello
使用nextLine方式接收:
hello world
输出内容为:hello world
public class Demo2 {
public static void main(String[] args) {
// 输入多个数字,计算和并求出平均值,不是数字的时候自动退出
Scanner scanner = new Scanner(System.in);
double sum = 0;
int m = 0;
System.out.println("请输入数据:");
while(scanner.hasNextDouble()){
double x = scanner.nextDouble();
m++ ;
sum = sum + x;
System.out.println("你输入了第"+m+"个数据,当前结果sum="+sum);
}
System.out.println(m+"个数的和为:"+sum);
System.out.println(m+"个数的平均值为:"+(sum/m));
scanner.close();
}
}
结果
请输入数据:
10
你输入了第1个数据,当前结果sum=10.0
20
你输入了第2个数据,当前结果sum=30.0
30
你输入了第3个数据,当前结果sum=60.0
dfgh
3个数的和为:60.0
3个数的平均值为:20.0
顺序结构
-
JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行。
-
顺序结构是最简单的算法结构
-
语句与语句之间,框与框之间是按从上到下的顺序执行的,它是由若干个依次执行的步骤组成的,它是任何一个算法都离不开的基本算法结构。
选择结构
- if单选择结构
- if双选择结构
- if多选择结构
- 嵌套的if结构
- switch多选择结构
if单选择结构
-
我们很多时候需要去判断一个东西是否可行,然后才去执行,这样一个过程中用if语句来表示
-
语法:
if(布尔表达式){
// 如果为true将执行的语句
}
public class Demo1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String str = scanner.nextLine();
if(str.equals("hello")){
System.out.println("你输入了:"+str);
}
System.out.println("End");
scanner.close();
}
}
结果
请输入:
hello
你输入了:hello
End
if双选择结构
-
语法
if(布尔表达式){
// 执行为true时的代码
}else{
// 执行为false时的代码
}
public class Demo2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入考试成绩:");
Double score = scanner.nextDouble();
if(score>=60){
System.out.println("考试及格!");
}else{
System.out.println("考试不及格!");
}
scanner.close();
}
}
结果
请输入考试成绩:
90
考试及格!
if多选择结构
-
语法:
if(布尔表达式1){
// 如果布尔表达式1成立时执行
}else if(布尔表达式2){
// 如果布尔表达式2成立时执行
}else if(布尔表达式3){
// 如果布尔表达式3成立时执行
}else{
// 以上布尔表达式都不成立时执行
}
public class Demo3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入考试成绩:");
Double score = scanner.nextDouble();
if(score==100){
System.out.println("满分!");
}else if(score >= 90 && score <100){
System.out.println("A级");
}else if(score >= 80 && score <90){
System.out.println("B级");
}else if(score >= 70 && score <80){
System.out.println("C级");
}else if(score >= 60 && score <70){
System.out.println("D级");
}else if(score >= 0 && score < 60){
System.out.println("不及格");
}else{
System.out.println("不合法");
}
scanner.close();
}
}
结果
请输入考试成绩:
85
B级
嵌套的if结构
-
语法
if(布尔表达式1){
// 当布尔表达式1成立时执行
if(布尔表达式2){
// 当布尔表达式2成立时执行
}
}
switch多选结构
-
多选结构还有一个实现方式就是switch case语句。
-
switch case语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支
-
语法
-
switch语句中的变量类型可以是:
- byte, short, int或者char
- 从java SE7开始,switch支持字符串String类型
- 同时case标签必须为字符串常量或者字面量
public class Demo4 {
public static void main(String[] args) {
String name = "江南";
switch (name){
case "哈哈":
System.out.println("哈哈");
break;
case "江南":
System.out.println("江南");
break;
default:
System.out.println("啥也不是");
break;
}
}
}
结果
江南
循环结构
- while循环
- do…while循环
- for循环
- 在java5中引入了主要用于数组的增强型for循环
while循环
-
while是最基本的循环,他的结构为:
while(布尔表达式){
// 循环结构
}
-
只要布尔表达式为true,循环就会一直执行下去
-
我们大多数情况是会让循环停止下来,我们需要一个让表达式失效的方式来结束循环
-
少部分情况需要循环一直执行,不如服务器的请求响应监听等
-
循环条件一直未true就会造成死循环,我们正常的业务编程应尽量避免死循环,会影响程序性能或者造成卡死奔溃
public class Demo5 {
public static void main(String[] args) {
int sum = 0;
int i = 0;
while(i<=100){
sum = sum + i;
i++;
}
System.out.println("0-100的和为:"+sum);
}
}
结果
0-100的和为:5050
do…while循环
-
对于while而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次
-
do…while循环和while循环相似,不同的是,do…while循环至少执行一次
do{
// 代码语句
}while(布尔表达式);
-
while和do…while的区别
- while先判断后执行,do…while是先执行后判断
- do…while总是保证循环体被至少执行一次。这是他们最主要的差别
public class Demo6 {
public static void main(String[] args) {
int i = 0;
while (i<0){
System.out.println(i);
}
System.out.println("===============");
do{
System.out.println(i);
}while(i<0);
}
}
结果
===============
0
for循环
-
for循环语句是支持迭代的一种通用结构,是最有效,最灵活的循环结构
-
for循环执行的次数是在执行前就确定的,语法格式如下
for(初始化:布尔表达式;更新){
// 代码结构
}
public class Demo7 {
public static void main(String[] args) {
// 计算0-100之间奇数和偶数的和
int oddSum = 0;
int evenSum = 0;
for (int i = 0; i <= 100; i++) {
if(i%2!=0){ // 取模不等于0位奇数
oddSum+=i;
}else{
evenSum+=i;
}
}
System.out.println("奇数的和:"+oddSum);
System.out.println("偶数的和:"+evenSum);
}
}
结果
奇数的和:2500
偶数的和:2550
public class Demo8 {
public static void main(String[] args) {
// 输出1-50之间能被5整除的数,每3个换一行
for (int i = 1; i <= 50; i++) {
if(i%5==0){
System.out.print(i+"\t");
}
if(i%(5*3)==0){
System.out.print("\n");
}
}
}
}
结果
5 10 15
20 25 30
35 40 45
50
public class Demo9 {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j+"*"+i+"="+i*j+"\t");
}
System.out.println();
}
}
}
结果
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
public class Demo10 {
public static void main(String[] args) {
int[] numbers = {10,20,30,40,50};
for (int i = 0; i < 5; i++) {
System.out.print(numbers[i]+"\t");
}
System.out.println();
System.out.println("================");
for (int x:numbers){
System.out.print(x+"\t");
}
}
}
结果
10 20 30 40 50
================
10 20 30 40 50
break && continue
- break在任何循环语句的主体部分,均可用break控制循环的流程,break用于强制退出循环,不执行循环中剩余的语句
- continue语句用于在循环语句中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定
练习
public class Demo11 {
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();
}
}
}
结果
*
***
*****
*******
*********
第三章
方法
何谓方法?
- Java方法是语句的集合,他们在一起执行一个功能
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
- 方法的设计原则:方法的本意是功能块,就是实现某个功能的语句块的集合,我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样有利于我们后期的扩展。
public class Demo1 {
public static void main(String[] args) {
int add = add(1, 2);
System.out.println(add);
}
public static int add(int a,int b){
return a+b;
}
}
结果
3
方法的定义
-
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:
-
方法包含一个方法头和一个方法体,下面是一个方法的所有部分:
-
修饰符:可选,告诉编译器如何调用该方法。定义该方法的访问类型
-
返回值类型:方法可能会返回值。returnValueType是方法返回值得数据类型。有些方法执行所需的操作,但没有返回值,关键字为void。
-
方法名:参数是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
- 形式参数:在方法被调用时用于接收外界输入的数据。
- 实参:调用方法时实际传递给方法的数据。
-
方法体:方法体包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 形式参数){ 方法体 返回值 }
-
-
方法调用
-
调用方法:对象名.方法名(实参列表)
-
Java支持两种调用方法的方式,根据方法是否返回值来选择
-
当方法返回一个值得时候,方法调用通常被当做一个值,例如:
- int large = max(30,40);
-
如果方法返回值是void,方法调用一定是一条语句。
- System.out.println(“Hello,World”);
-
Java是值传递
public class Demo2 {
public static void main(String[] args) {
int large = large(20, 20);
System.out.println(large);
}
public static int large(int num1,int num2){
int result = 0;
if(num1==num2){
return result;
}
if(num1 > num2){
result = num1;
}else{
result = num2;
}
return result;
}
}
结果
0
方法的重载
- 重载就是在一个类中,有相同的函数名称,但形参不同的函数。
- 方法重载的规则:简单来说就是同名不同参
- 方法名称必须相同
- 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为方法的重载。
- 实现理论
- 方法名称相同时,编译器会根据调用方法的参数个数,参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。
public class Demo2 {
public static void main(String[] args) {
double large = large(30.0, 20);
System.out.println(large);
}
public static int large(int num1,int num2){
int result = 0;
if(num1==num2){
return result;
}
if(num1 > num2){
result = num1;
}else{
result = num2;
}
return result;
}
public static double large(double num1,double num2){
double result = 0;
if(num1==num2){
return result;
}
if(num1 > num2){
result = num1;
}else{
result = num2;
}
return result;
}
}
结果
30.0
命令行传参
- 有时候希望运行一个程序的时候再传递给它参数,这就要靠命令行传参数给main()函数实现。
public class Demo3 {
public static void main(String[] args) {
for (int i = 0; i <args.length ; i++) {
System.out.println("args的第"+i+"个参数为:"+args[i]);
}
}
}
可变参数
- JDK1.5开始,Java支持传递同类型的可变参数给一个方法。
- 在方法声明中,在指定参数类型后加一个省略号(…)。
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public class Demo4 {
public static void main(String[] args) {
printMax(34,35,33,31.0,38);
printMax(new double[]{11,25.6,33.0,45});
}
public static void printMax(double... numbers){
if (numbers.length == 0){
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("数组中最大的数为:"+result);
}
}
结果
数组中最大的数为:38.0
数组中最大的数为:45.0
递归
- A方法调用B方法,我们很容易理解
- 递归就是:A方法调用A方法,就是自己调用自己
- 利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需的多次重复计算,大大减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
- 递归结构包括两个部分:
- 递归头:什么时候不调用自身方法,如果没有头,将陷入死循环
- 递归体:什么时候需要调用自身方法
public class Demo5 {
public static void main(String[] args) {
// 递归计算5!
int f = f(5);
System.out.println(f);
}
public static int f(int n){
if (n == 1){
return 1;
}
return n*f(n-1);
}
}
结果
120
数组
- 数组是相同类型数据的有序集合
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们
数组声明创建
-
首先必须声明数组变量,才能在程序中使用数组,下面是声明数组变量的语法:
-
dataType[] arrayRefvar; // 首选的方法
或
dataType arrayRefvar[]; // 效果相同,但不是首选方法
-
-
Java语言使用new操作符来创建数组,语法如下:
- dataType[] arrayRefvar = new dataType[arraySize];
-
数组的元素是通过索引访问的,数组索引从0开始
-
获取数组长度:arrays.length
public class Demo1 {
public static void main(String[] args) {
int[] nums = new int[10]; // 声明并创建数组
for (int i = 0; i < nums.length; i++) { // 给数组赋值
nums[i]=i+1;
System.out.print(nums[i]+"\t");
}
int sum = 0;
for (int i = 0; i < nums.length; i++) { // 数组元素求和
sum = sum + nums[i];
}
System.out.println();
System.out.println(sum);
}
}
结果
1 2 3 4 5 6 7 8 9 10
55
数组的四个基本特点
- 其长度是确定的,数组一旦被创建,它的大小就是不可以改变的
- 其元素必须是相同类型,不允许出现混合类型
- 数组中的元素可以是任意数据类型,包括基本类型和引用类型
- 数组变量属引用类型数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量
- 数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的
三种初始化
- 静态初始化
- 动态初始化
- 数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化
public class Demo2 {
public static void main(String[] args) {
// 静态初始化
int[] a = {1,2,3};
Man[] mans = {new Man(),new Man()};
System.out.println(a[1]);
// 动态初始化
int[] b = new int[5];
b[0] = 1;
b[1] = 2;
System.out.println(b[1]);
// 数组的默认初始化,默认值有0 0.0 null
String[] c = new String[8];
System.out.println(c[5]);
}
}
结果
2
2
null
数组边界
- 下标的合法区间:[0,length-1],如果越界就会报错
- ArrayIndexOutOfBoundsException : 数组下标越界异常
- 小结
- 数组是相同数据类型(数据类型可以为任意类型)的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- 数组长度是确定的,不可变的。如果越界会报错。
数组的使用
-
普通for循环
-
for-each循环
-
数组做方法入参
-
数组做返回值
public class Demo3 {
public static void main(String[] args) {
int[] nums = {-1,1,2,3,4,-5};
// 打印出数组的所有元素
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i]+"\t");
}
System.out.println();
System.out.println("===========================");
// 取出数组中的最大值
int max = nums[0];
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max){
max = nums[i];
}
}
System.out.println(max);
}
}
结果
-1 1 2 3 4 -5
===========================
4
public class Demo4 {
public static void main(String[] args) {
int[] nums = {-1,1,2,3,4,-5};
// 打印出数组的所有元素
for (int array: nums) {
System.out.print(array+"\t");
}
}
}
结果
-1 1 2 3 4 -5
public class Demo5 {
public static void main(String[] args) {
int[] nums = {-1,1,2,3,4,-5};
int[] reverse = reverse(nums);
show(reverse);
}
public static int[] reverse(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;
}
public static void show(int[] reverse){
for (int array: reverse) {
System.out.print(array+"\t");
}
}
}
结果
-5 4 3 2 1 -1
多维数组
- 多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一堆数组,其每一个元素都是一个一堆数组
- 二维数组
public class Demo6 {
public static void main(String[] args) {
int[][] array = {{1,2},{3,4},{5,6},{7,8}};
System.out.println(array.length);
System.out.println(array[3][1]);
System.out.println(array[1].length);
System.out.println("========================");
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j]+"\t");
}
}
}
}
结果
4
8
2
========================
1 2 3 4 5 6 7 8
Arrays类
- 数组的工具类java.util.Arrays
- 由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
- 查看JDK帮助文档
- Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象来调用(注意,是“不用”,而不是“不能”)
- 具有以下常用功能
- 给数组赋值:通过fill方法
- 对数组排序:通过通过sort方法,按升序
- 比较数组:通过equals方法比较数组中元素值是否相等
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作
public class Demo7 {
public static void main(String[] args) {
int[] a = {12,15,486,14,579,5416,265};
int[] b = {12,15,486,14,579,5416,265};
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println(Arrays.toString(a));
Arrays.fill(a,0,3,5);
System.out.println(Arrays.toString(a));
System.out.println(Arrays.equals(a,b));
System.out.println(Arrays.binarySearch(a,0,a.length,5416));
}
}
结果
[12, 15, 486, 14, 579, 5416, 265]
[12, 14, 15, 265, 486, 579, 5416]
[5, 5, 5, 265, 486, 579, 5416]
false
6
冒泡排序
- 冒泡排序无疑是最为出名的排序算法之一,总共有八大排序!
- 冒泡的代码环视相当简单的,两层循环,外层冒泡轮数,里层依次比较。
- 我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为0。
public class Demo8 {
public static void main(String[] args) {
double[] a = {1,58,45.6,32,15.5,54,89.0,32,54};
double[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
public static double[] sort(double[] arrays){
double temp = 0;
for (int i = 0; i < arrays.length-1; i++) {
boolean flag = false;
for (int j = 0; j < arrays.length-1; j++) {
if (arrays[j+1] < arrays[j]){
temp = arrays[j+1];
arrays[j+1] = arrays[j];
arrays[j] = temp;
flag = true;
}
}
if (flag == false){
break;
}
}
return arrays;
}
}
结果
[1.0, 15.5, 32.0, 32.0, 45.6, 54.0, 54.0, 58.0, 89.0]
第四章
面向对象 & 面向过程
- 面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么…
- 面向过程适合处理一些较为简单的问题
- 面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
- 对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
什么是面向对象
- 面向对象编程:Object-Oriented Programming,OOP
- 面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
- 抽象
- 三大特性:
- 封装
- 继承
- 多态
- 从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
- 从代码运行角度考虑是先有类后有对象。类是对象的模板
类与对象的关系
- 类是一种抽象的数据类型,它是对某一类事物整体的描述/定义,但是并不能代表某一个具体的事物。
- 动物,植物,手机,电脑…
- Person类,Pet类,Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
- 对象是抽象概念的具体实例
- 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例
- 能够体现出特点,展现出功能的是具体的实例,而不是有个抽象的概念
创建与初始化对象
- 使用new关键字创建对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象 进行初始化以及对类中构造器的调用。
- 类中的构造器称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
- 构造器必须要掌握
// 定义一个学生类
public class Student {
String name;
int age;
}
public class Man {
public static void main(String[] args) {
// 实例化学生类
Student student1 = new Student();
Student student2 = new Student();
student1.name="xiaohong";
student1.age = 10;
student2.name = "xiaoming";
student2.age = 10;
System.out.println(student1.name);
System.out.println(student1.age);
System.out.println(student2.name);
System.out.println(student2.age);
}
}
结果
xiaohong
10
xiaoming
10
结论:类是抽象的,对象是具体的。通过new关键字将抽象的类实例化为一个个的对象。
构造器
通过new关键字实例化对象实际上是调用抽象类中的构造器(构造器实际就是方法),分为有参构造和无参构造。在什么都不写的情况下,默认会有一个无参构造,当写了有参构造之后,如果想使用无参构造,无参构造必须显式写出。
public class Person {
String name;
int age;
// 写了有参构造,无参构造必须显式写出
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public Person(String name){
this.name = name;
}
}
public class Application {
public static void main(String[] args) {
// new的过程实际是调用类中的构造器
Person person = new Person("xiaoming",10);
System.out.println(person.name);
}
}
结果:
xiaoming
创建对象内存分析
public class Pet {
public String name;
public int age;
public void shout(String str){
System.out.println(str + "叫了一声...");
}
}
public class Animal {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout(dog.name);
Pet cat = new Pet();
cat.name = "兴旺";
cat.age = 2;
cat.shout(cat.name);
}
}
结果
旺财叫了一声...
兴旺叫了一声...
栈里面存放方法和对象的引用。堆中存放的是具体的实例对象,方法区也属于堆,在类加载的时候已经加载,所以静态方法可以直接调用。
对象小结
-
类与对象
- 类是一个模板:抽象。对象是一个具体的实例。
-
方法
- 定义、调用
-
对象的应用
- 引用类型,基本类型(八大类)
- 对象是通过引用来操作的:栈–>堆
-
属性
- 字段,成员变量
- 默认初始化:
- 数字:0 0.0
- char:u0000
- boolean:false
- 引用类型:ull
- 修饰符 属性类型 属性名 = 属性值
-
对象的创建和使用
- 必须使用new关键字来创建对象,构造器 Person xxc = new Person();
- 对象的属性:xxc.name;
- 对象的方法:xxc.sleep();
-
类
- 静态的属性 属性
- 动态的行为 方法
封装
- 该露的露,该藏的藏
- 我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
- 封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
- 记住这句就够了:属性私有,get/set
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 120 || age < 0){
this.age = 3;
}else{
this.age = age;
}
}
}
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("xxc");
s1.setAge(150);
System.out.println(s1.getName());
System.out.println(s1.getAge());
}
}
结果
xxc
3
注意:封装是属性的封装。
继承
- 继承的本质是对某一类的抽象,从而实现对现实世界更好的建模
- extends的意思是“扩展”。子类是父类的扩展。
- Java中只有单继承,没有多继承。一个儿子只能有一个爸爸。
- 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖,组合,聚合等。
- 继承关系的两个类:一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
- 子类和父类之间,从某种意义上讲应该具有“is a”的关系。
- object类
- super - this
- 方法重写:重点–>多态
public class Person {
private String name;
private int age;
public void sleep(int age , String name){
System.out.println(name+"今年"+age+"岁,现在睡着了...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// 子类继承父类,就会拥有父类的所有方法
public class Student extends Person {
}
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("小王");
s1.setAge(3);
String name = s1.getName();
int age = s1.getAge();
s1.sleep(age,name);
}
}
结果
小王今年3岁,现在睡着了...
super详解
public class Person {
protected String name = "xxc";
}
public class Student extends Person{
private String name = "jiangnan";
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("旺仔");
}
}
结果
旺仔
jiangnan
xxc
super注意点:
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
super vs this:
- 代表的对象不同
- this:本身调用者这个对象
- super:代表父类对象的引用
- 前提
- this:没有继承也可以使用
- super:只能在继承条件下使用
- 构造方法
- this(); 本来的构造
- super(); 父类的构造
重写
public class Person {
public void show(){
System.out.println("Person=>show");
}
}
public class Student extends Person{
public void show(){
System.out.println("Student=>show");
}
}
public class Application {
public static void main(String[] args) {
// 父类引用指向子类对象
Person student = new Student();
student.show();
}
}
结果
Student=>show
重写:
-
需要有继承关系,子类重写父类的方法
-
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小。public>protected>defaule>private
- 抛出的异常:范围,可以被缩小,但不能扩大。classNotFoundException<Exception
-
重写:子类和父类的方法必须一致,只是方法体不同
-
为什么要重写:
- 父类的功能子类不一定需要,或者不一定满足。
多态
-
即同一方法可以根据发送对象的不同而采取多种不同的行为方式
-
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
-
多态存在的条件
- 有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
-
注意:多态是方法的多态,和属性无关
-
instanceof
public class Person {
public void show(){
System.out.println("这里是Person");
}
}
public class Student extends Person{
public void show(){
System.out.println("这里是Student");
}
public void eat(){
System.out.println("Student==>eat");
}
}
public class Application {
public static void main(String[] args) {
Student student1 = new Student();
Person student2 = new Student();
student1.show(); // 显示Student本身的方法结果
student2.show(); // 显示重写后的方法结果
student1.eat(); // 显示Student本身的方法结果
((Student) student2).eat(); // 无法重写,只能先强制类型转换
}
}
结果
这里是Student
这里是Student
Student==>eat
Student==>eat
instanceof
public class Person {
public void show(){
System.out.println("这里是Person");
}
}
public class Student extends Person {
public void show(){
System.out.println("这里是Student");
}
public void eat(){
System.out.println("Student==>eat");
}
}
public class Teacher extends Person{
}
public class Application {
public static void main(String[] args) {
Student student1 = new Student();
System.out.println(student1 instanceof Object);
System.out.println(student1 instanceof Student);
//System.out.println(student1 instanceof Teacher); // 编译报错,因为Student1和Teacher没有关系
System.out.println("==========================");
Object student = new Student();
System.out.println(student instanceof Object);
System.out.println(student instanceof Teacher); // 编译不报错,因为Student和Teacher都继承与Object
System.out.println(student instanceof Student);
System.out.println("==========================");
Person student2 = new Student();
student2.show();
((Student) student2).eat(); // Person本身没有eat();方法,调用的话需要强制类型转换
}
}
结果
true
true
==========================
true
false
true
==========================
这里是Student
Student==>eat
结论:引用类型的强制类型转换和基本类型的相似。低转高自动转,高转低需要强制类型转换。
static
public class Student {
{ // 可以用这种方法赋初始值
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Student(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Student student1 = new Student();
System.out.println("===========================");
Student student2 = new Student();
}
}
结果
静态代码块
匿名代码块
构造方法
===========================
匿名代码块
构造方法
结论:可以发现静态代码块最先执行,而且只执行一次。其他的方法在每次实例化对象的时候都会执行。可以利用匿名代码块给对象赋一些初始值,因为匿名代码块在构造方法之前执行
抽象类
- abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
- 抽象类:不能使用new关键字来创建对象,它是用来让子类继承的
- 抽象方法:只有方法的声明,没有方法的实现,它是用来让子类实现的
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
public abstract class Action {
public abstract void run();
}
public class Action1 extends Action{
@Override
public void run() {
System.out.println("子类必须实现父类中的出现方法");
}
}
public class Application {
public static void main(String[] args) {
// Action action = new Action(); 无法使用new关键字
Action1 action1 = new Action1();
action1.run();
}
}
结果
子类必须实现父类中的出现方法
结论:父类为抽象类,那么子类必须实现父类中的抽象方法,除非子类也是抽象类。抽象类不能被new关键字进行实例化。
接口
- 普通类:只要具体实现
- 抽象类:具体实现和规范(抽象方法)都有
- 接口:只要规范。 约束和实现分离:面向接口编程
- 接口就是规范,定义的是一组规则。
- 接口的本质是契约,就像我们人间的法律一样,制定好之后就必须遵守
public interface UserService { // 只能定义方法,不能实现
void add();
void delete();
void update();
void query();
}
public interface TimerService {
void timer();
}
public class UserServiceImpl implements UserService,TimerService{ // 多继承
@Override
public void add() { // 实现具体的方法
System.out.println("我实现了add方法");
}
@Override
public void delete() {
System.out.println("我实现了delete方法");
}
@Override
public void update() {
System.out.println("我实现了update方法");
}
@Override
public void query() {
System.out.println("我实现了query方法");
}
@Override
public void timer() {
System.out.println("我实现了timer方法");
}
}
public class Application {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.add();
userService.delete();
userService.update();
userService.query();
userService.timer();
}
}
结果
我实现了add方法
我实现了delete方法
我实现了update方法
我实现了query方法
我实现了Timer方法
内部类
- 内部类就是在一个类的内部再定义一个类,比如,A类中定义了一个B类,那么B类相对A类来说就是内部类,而A类相对于B类就是外部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
// 成员内部类
public class Outer {
private int age = 10;
public void run(){
System.out.println("Outer=>run");
}
class Inner{
public void show(){
System.out.println("Inner=>run");
}
public void getAge(){
System.out.println("我是内部类,我可以操作外部类的属性和方法:"+age);
}
public void InnerRun(){
run();
}
}
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
outer.run();
Outer.Inner inner = outer.new Inner();
inner.InnerRun(); // 通过内部类获取外部类的方法
inner.show();
inner.getAge();
}
}
结果
Outer=>run
Outer=>run
Inner=>run
我是内部类,我可以操作外部类的属性和方法:10
静态内部类使用static修饰即可
// 局部内部类
public class Outer {
public void method(){
System.out.println("Outer=>method");
class Inner{
public void in(){
System.out.println("我是局部内部类的方法");
}
}
Inner inner = new Inner();
inner.in();
}
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
}
结果
Outer=>method
我是局部内部类的方法
结论:局部内部类只能在局部使用,不能再其他类中调用
// 匿名内部类
public class Outer {
public void show(){
System.out.println("我是外部类的方法");
}
}
class Inner{
public void eat(){
System.out.println("我是内部类的方法");
}
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer(); // 我被实例化为了具体的outer对象
outer.show(); // 通过我的实例化对象去调用了我的方法
new Inner().eat(); // 我没有被实例化对象,而是直接调用了方法
}
}
结果
我是外部类的方法
我是内部类的方法
public class OuterService {
public static void main(String[] args) {
new UserService(){
@Override
public void eat() {
System.out.println("我重写了eat方法");
}
}.eat();
}
}
interface UserService{
void eat();
}
结果
我重写了eat方法
第五章
异常体系结构
- Java把异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类
- 在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception
Error
- Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关
- Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,java虚拟机(JVM)一般会选择线程终止
- 还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError),链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且大多数是程序运行时不允许出现的状况。
Exception
- 在Exception分支中有一个重要的子类RuntimeException(运行时异常)
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算数异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
- 这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这些异常的发生
- Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常
异常处理机制
- 抛出异常
- 捕获异常
- 异常处理五个关键字
- try, catch, finally, throw, throws
public class Test {
public static void main(String[] args) {
try {
new Test().a();
}catch (Error e){
System.out.println("Error");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable e){
System.out.println("Throwable");
}finally {
System.out.println("finally");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
结果
Error
finally
结论:
- 五个关键字try和catch必须成对出现
- finally总会被执行
- 异常有大小顺序,最大的为Throwable,捕获时应当放到最后,不然会报异常
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 1;
try {
new Test().test(a,b);
System.out.println(new Test().shout(a,b));
} catch (Exception e) {
System.out.println("有异常,请处理");
}finally {
System.out.println("finally");
}
}
public void test(int a,int b) {
if (b == 0){
throw new ArithmeticException();
}
}
public int shout(int a,int b){
return a/b;
}
}
结果
1
finally
总结:throw在方法中手动抛出异常,throws在方法上主动抛出异常,一般在方法中没法处理异常的时候采用。
自定义异常
- 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自己定义异常。用户自定义异常类,只需要继承Exception类即可
- 在程序中使用自定义异常类,大体可以分为以下几个步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果咋当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理。否则在方法的声明出通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
- 在出现异常的方法的调用者中捕获并处理异常
实际应用中的经验总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切记只是简单地电泳printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
大多数是程序运行时不允许出现的状况。
Exception
- 在Exception分支中有一个重要的子类RuntimeException(运行时异常)
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算数异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
- 这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这些异常的发生
- Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常
异常处理机制
- 抛出异常
- 捕获异常
- 异常处理五个关键字
- try, catch, finally, throw, throws
public class Test {
public static void main(String[] args) {
try {
new Test().a();
}catch (Error e){
System.out.println("Error");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable e){
System.out.println("Throwable");
}finally {
System.out.println("finally");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
结果
Error
finally
结论:
- 五个关键字try和catch必须成对出现
- finally总会被执行
- 异常有大小顺序,最大的为Throwable,捕获时应当放到最后,不然会报异常
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 1;
try {
new Test().test(a,b);
System.out.println(new Test().shout(a,b));
} catch (Exception e) {
System.out.println("有异常,请处理");
}finally {
System.out.println("finally");
}
}
public void test(int a,int b) {
if (b == 0){
throw new ArithmeticException();
}
}
public int shout(int a,int b){
return a/b;
}
}
结果
1
finally
总结:throw在方法中手动抛出异常,throws在方法上主动抛出异常,一般在方法中没法处理异常的时候采用。
自定义异常
- 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自己定义异常。用户自定义异常类,只需要继承Exception类即可
- 在程序中使用自定义异常类,大体可以分为以下几个步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果咋当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理。否则在方法的声明出通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
- 在出现异常的方法的调用者中捕获并处理异常
实际应用中的经验总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切记只是简单地电泳printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加finally语句块去释放占用的资源