JavaSE
一、前言
1.1、解决大家的疑惑
Q:零基础可以学java吗?
R:必须的
Q:英语不好能学吗?
R:程序真的没有想象中的那么多的英语,而且天天都在用,满满就掌握了。
Q:理解慢能学好吗?
R:Of curse,编程需要一定的逻辑思维能力,但更多的是持续学习
Q:现在学java晚吗?
R:可以说,学好了Java,不愁没有用武之地,未来的开发之路会非常广
1.2、Java和Python的抉择
1.3、走进Java
-
到底什么是Java?
-
Java能干什么?
- 学习路线
-
如何更高效的学习Java
多写(代码),多写(笔记),多写(文章)
多练(交流),多练(思维),多练(技能)
多分享(知识),多提问(怎么了),多思考(为什么)
最重要(坚持)
每个牛逼人物背后都有段苦逼的岁月,只要像sb一样的坚持,终将牛逼!
二、预科
2.1、学习准备:博客
博客,英文名为Blog,它的正式名称为网络日记
为什么要写博客?
- 需要总结和先思考。有时候我们一直在赶路,却忘了放慢脚步
- 提升文笔组织能力
- 提升学习总结能力
- 提升逻辑思维能力
- 帮助他人,结交朋友
冰冻三尺非一日之寒,写博客也是,短期内可能看不到效果,但是长期坚持,对自己的提升很有帮助。
2.2、MarkDown
2.3、基本Dos命令
打开CMD的方式
1.开始+系统+命令提示符
2.Win键+R输入cmd打开控制台(推荐使用)
3.在任意的文件夹下面,按住shift键+鼠标右键点击,在此处打开命令行窗口
4.资源管理器的地址栏前面加上cmd路径
管理员方式运行: 选择以管理员方式运行
常用的Dos命令
#盘符切换
#查看当前目录下的所有文件 dir
#切换目录cd change directorycd ..
#清理屏幕cls(clear screen)
#退出终端exit
#查看电脑的ip ipconfig
#打开应用
calc
mspaint
notepad
#ping命令
ping www.baidu. com
#文件操作
md 目录名
rd 目录名
cd> 文件名
de1文件名
2.4、计算机语言发展史
第一代语言
机器语言
- 二进制
- 这种代码是直接输入给计算机使用的,不经过任何的转换
第二代语言
汇编语言
- 解决人类无法读懂机器语言的问题
- 指令代替二进制
目前应用:
- 逆向工程
- 机器人
- 病毒
第三代语言
摩尔定律
当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18个月翻两倍以上
高级语言
大体上分为:面向过程和面向对象两大类
c语言是典型的面向郭晨的语言。C++、Java是典型的面向对象的语言
三、Java入门
3.1、Java帝国的诞生
C & C++
1972年 C 诞生
- 贴近硬件,运行极快,效率极高
- 操作系统,编译器,数据库,网络系统等
- 指针和内存管理
1982年C++诞生
- 面向对象
- 兼容C
- 图形领域、游戏等
反抗
我们要建立一个新的语言:
- 语法有点像C
- 没有指针
- 没有内存管理
- 真正的可移植性,编写一次,到处运行
- 面向对象
- 类型安全
- 高质量的类库
- C++——
Java初生
-
1995年的网页简单而粗糙,缺乏互动性。
-
图形界面的程序(Applet)
-
Bill Gates说:这是迄今为止设计的最好的语言!
-
Java 2 标准版(J2SE):PC
-
Java 2 移动版(J2ME):手机
-
Java 2 企业版(J2EE):服务器
Java发展
他们基于Java发开了巨多的平台,系统,工具
-
构建工具:Ant,Maven,Jekins
-
应用服务器:Tomcat,Jetty,Jboss,Websphere,weblogic
-
Web开发:Struts,Spring,Hibernate,Mybatis
-
开发工具:Eclipse,Netbean,intellij idea,Jbuilder
-
…
-
2006:Hadoop(大数据领域)
-
2008:Android(手机端)
3.2、Java 特性和优势
- 简单性
- 面向对象
- 可移植性
- 高性能
- 分布式
- 动态性
- 多线程
- 安全性
- 健壮性
3.3、Java三大特性
Write Once、Run Anywhere
JavaSE:标准版(桌面程序,控制台开发…)
JavaME:嵌入式开发(手机,小家电…)
JavaEE:E企业级开发(web端,服务器开发…)
3.4、JDK、JRE、JVM
JDK:Java Development Kit
JRE:Java Runtime Environment
JVM:Java Virtual Machine
3.5、Java开发环境
JDK下载与安装
配置环境变量
JDK目录介绍
HelloWorld 及 简单语法规则
Notepad++安装和使用
3.6、HelloWorld详解
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello,Wrold!");
}
}
3.7、Java程序运行机制
-
编译型
-
解释型
程序运行机制
3.8、IDE
集成开发环境(IDE,Integrated Development Environment)
四、Java基础语法
4.1、注释
平时我们编写代码,在代码量表较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了。
注释并不会被执行,是给我们写代码的人看到的
书写注释是一个非常好的习惯
Java中的注释有三种:
- 单行注释
- 多行注释
- 文档注释
/***
* .::::.
* .::::::::.
* ::::::::::: FUCK YOU
* ..:::::::::::'
* '::::::::::::'
* .::::::::::
* '::::::::::::::..
* ..::::::::::::.
* ``::::::::::::::::
* ::::``:::::::::' .:::.
* ::::' ':::::' .::::::::.
* .::::' :::: .:::::::'::::.
* .:::' ::::: .:::::::::' ':::::.
* .::' :::::.:::::::::' ':::::.
* .::' ::::::::::::::' ``::::.
* ...::: ::::::::::::' ``::.
* ````':. ':::::::::' ::::..
* '.:::::' ':'````..
*/
JavaDoc
javadoc
命令是用来生成自己API文档的
参数信息:
- @author 作者名
- @version 版本号
- @since 指明需要最早使用的jdk版本
- @param 参数名
- @return 返回值情况
- @throws 异常抛出情况
/**
* @auther su
* @version 1.0
* @since 1.8
*/
public class Doc{
String name;
/**
* @auther su
* @param name
* @return
* @thorws Exception
*/
public String test(String name) throws Exception{
return name;
}
}
4.2、标识符
关键字
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
标识符注意点
-
所有的标识符都应该以字母(A - Z 或者 a - z),美元符($),或者下划线(_)开始
-
首字符之后可以是字母(A - Z 或者 a - z),美元符($),下划线(_)或者数字的任何字符组合
-
不能使用关键字作为变量名或者方法名
-
标识符是大小写敏感的
-
合法标识符举例:age、$salary、_value、__1_value
-
非法标识符举例:123abc、-salary、#abc
-
可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音,很low
-
public static void main(String[] args){ String 王者荣耀 = "王者荣耀"; System.out.println(王者荣耀); }
-
4.3、数据类型
内置数据类型
强类型语言
- 要求变量的使用要严格符合规范,所有变量都必须先定以后才能使用
Java的数据类型分为两大类
- 基本类型
- 整数类型:
- byte: 占1字节范围:-128—127
- short:占2字节范围:-32768—32767
- int: 占4个字节范围:-2147483648—2147483647
- long: 占8个字节范围:-9223372036854775808—9223372036854775807
- 浮点类型:
- float: 占4个字节
- double: 占8个字节
- 字符类型:
- char: 占2个字节
- boolean类型:占1个字节
- true
- false
- 整数类型:
- 引用类型
- 类
- 接口
- 数组
什么是字节
-
位(bit):是计算机 内部数据 存储的最小单位,11001100是一个八位二进制数
-
字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写 B 来变表示
- 1 B (byte,字节) = 8 bit(位)
-
字符:是指计算机中使用的字母、数字、字和符号
-
1 bit 表示 1 位
-
1 byte 表示一个字节 1B = 8 b
-
1024 B = 1 KB
-
1024 KB = 1 M
-
1024 M = 1 G
思考:电脑的32位和64位的区别是什么?
4.4、类型转换
由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换
低 ------------------------------------------------------高
byte,short,char -> int -> long -> float -> double
运算中,不同类型的数据先转换为同一类型,然后在进行计算
-
强制类型转换(高 —> 低)
-
自动类型转换(低 —> 高)
注意点:
- 不能对布尔值进行转换
- 不能把对象类型转换为不相干的类型
- 在把高容量转换到低容量的时候,强制转换
- 转换的时候可能存在内存溢出,或者精度问题!
4.5、变量、常量、作用域
什么是变量?
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
Java是一种强类型语言,每个变量都必须声明其类型
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域
type varName [= value] [{
,varName [ = value]}];
// 数据类型 变量名 = 值; 可以使用逗号隔开来声明多个同类型变量。
注意事项:
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句,因为每一个声明都必须以分好结束
变量作用域
- 类变量(静态变量):独立于方法之外的变量,用 static 修饰。
- 类变量也称为静态变量,在类中以 static 关键字声明,但必须在方法之外。
- 无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
- 静态变量除了被声明为常量外很少使用,静态变量是指声明为 public/private,final 和 static 类型的变量。静态变量初始化后不可改变。
- 静态变量储存在静态存储区。经常被声明为常量,很少单独使用 static 声明变量。
- 静态变量在第一次被访问时创建,在程序结束时销毁。
- 与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
- 默认值和实例变量相似。数值型变量默认值是 0,布尔型默认值是 false,引用类型默认值是 null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
- 静态变量可以通过:ClassName.VariableName的方式访问。
- 类变量被声明为 public static final 类型时,类变量名称一般建议使用大写字母。如果静态变量不是 public 和 final 类型,其命名方式与实例变量以及局部变量的命名方式一致。
- 实例变量:独立于方法之外的变量,不过没有 static 修饰。
- 实例变量声明在一个类中,但在方法、构造方法和语句块之外;
- 当一个对象被实例化之后,每个实例变量的值就跟着确定;
- 实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
- 实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
- 实例变量可以声明在使用前或者使用后;
- 访问修饰符可以修饰实例变量;
- 实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
- 实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
- 实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。
- 局部变量:类的方法中的变量。
- 局部变量声明在方法、构造方法或者语句块中;
- 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
- 访问修饰符不能用于局部变量;
- 局部变量只在声明它的方法、构造方法或者语句块中可见;
- 局部变量是在栈上分配的。
- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
public class Variable{
static int allClicks=0; // 类变量
String str="hello world"; // 实例变量
public void method(){
int i =0; // 局部变量
}
}
常量
常量(Constant):初始化(initialize)后不能再改变值!不会变动的值
所谓常量可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变
final 常量名 = 值;
final double PI = 3.14;
常量名一般使用大写字符
变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰原则:monthSalary
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则:Man,GoodMan
- 方法名:首字母小写和驼峰原则:run(),runRun()
4.6、运算符
Java语言支持如下运算符:
- 算术运算符:+,-,*,/,%,++,–,* =,/ =,
- 赋值运算符:=,+ =,- =,
- 关系运算符:>,<,>=,<=,==,!=,instanceof
- 逻辑运算符:&&,||,!
- 位运算符:&,|,^,~,<< ,>> ,>>>
- 条件运算符:? :
自增自减运算符:
public class Demo4{
public static void main(String[] args){
int a = 3;
int b = a++;
System.out.println(a); //4
int c = ++a;
System.out.println(a); //5
System.out.println(b); //3
System.out.println(C); //5
}
}
逻辑运算符:
public class Demo5{
public static void main(String[] args){
boolean a = true;
boolean b = false;
System.out.println("a && b:"+(a&&b)); //false
System.out.println("a || b:"+(a||b)); // true
System.out.println("!(a && b):"+!(a&&b)); // true
// 短路运算
int c = 5;
boolean d = (c<4)&&(c++<4);
System.out.println(d); // false
System.out.println(c); // 5
}
}
位运算:
/*
A = 0011 1100
B = 0000 1101
-------------------------------
A & B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~ B = 1111 0010
2*8 = 16 2*2*2*2
<<
>>
0000 0000 0
0000 0001 1
0000 0010 2
0000 0011 3
0000 0100 4
0000 1000 8
0001 0000 16
*/
System.out.println(2<<3); //16
4.7、包机制
为了更好第组织类,Java 提供了包机制,用于区别类名的命名空间。
包语句的语法格式为:
package pkg1[.pkg2[.pkg3...]];
一般利用公司域名倒置作为包名;
为了能够使用某一包的成员,我们需要在Java 程序中明确导入该包。使用 ” import “ 语句即可完成此功能。
import package[.pkg2[. pkg3...].(classname|*)];
五、流程控制
5.1、 用户交互Scanner
Scanner对象
java给我们提供了这样一个工具类,我们可以获取用户的输入。java.util.Scanner 是 Java 5 的新特征,我们可以通过 Scanner 类来获取用户的输入。
基本语法:
Scanner scan = new Scanner(System.in);
通过Scanner类的 next() 与 nextLine() 方法获取输入的字符串,在读取前我们一般需要使用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
public class Demo01{
public static void main(String[] args){
//创建一个扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner (System.in);
System.out.printle("使用next方式接收:");
//判断用户有没有输入字符串
if(scanner.hasNext()){
String str = scanner.next();
System.out.println("输入的内容为:" + str);
}
// 凡是属于IO流的类如果不关闭会一直占用资源,要养成好习惯用完就关闭
scanner.close();
}
}
next():
- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符之前遇到的空白,next()方法会自动将其去掉
- 只要输入有效字符后才将其后面输入的空白作为分隔符或者结束符
- next()不能得到带有空格的字符串
nextLine():
- 以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符
- 可以获得空白
public class Demo02{
public static void main(String[] args){
//创建一个扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner (System.in);
System.out.printle("使用next方式接收:");
//判断用户是否还要输入
if(scanner.hasNext()){
String str = scanner.next();
System.out.println("输入的内容为:" + str);
}
// 凡是属于IO流的类如果不关闭会一直占用资源,要养成好习惯用完就关闭
scanner.close();
}
}
public class Demo03{
//创建一个扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner (System.in);
System.out.printle("请输入数据:");
String str = scanner.nextLine();
System.out.println("输出的内容为:" + str);
scanner.close();
}
Scanner进阶使用
public class Demo04{
public static void main(String[] args){
//创建一个扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner (System.in);
int i = o;
float f= 0.0f;
System.out.println("请输入整数:");
if(scanner.hasNextInt()){
i = scanner.nextInt();
System.out.println("整数数据:" + i);
}else{
System.out.println("请输入的不是整数数据!");
}
if(scanner.hasNextFloat()){
f = scanner.nextFloat();
System.out.println("小数数据:" + f);
}else{
System.out.println("输出的不是小数数据!");
}
scanner.close();
}
}
public class Demo05{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
double sum = 0;
int m = 0;
//通过循环判断是否还有输入,并且里面对每一次进行求和和统计
while(scanner.hasNextDouble()){
double x = scanner.nextDouble();
m = m + 1;
sum = sum + x;
}
System.out.println(m + "个数的和为" + sum);
System.out.println(m + "个数的平均值是" + (sum / m));
scanner.close();
}
}
5.2、顺序结构
Java的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行。
顺序结构是最简单的算法结构。
语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个一次执行的处理步骤组成的,它是任何一个算法都离不开的一种基本算法结构。
5.3、选择结构
5.3.1、if 单选择结构
我们很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程在程序中用if语句来表示
语法
if(布尔表达式){
如果布尔表达式为true将执行的语句
}
demo
public static class main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("请输入内容:");
String s = scanner.nextLine();
if(s.equals("Hello")){
System.out.println(s);
}
System.out.pritln("End");
Scanner.close();
}
5.3.2、if 双选择结构
那现在有个需求,公司要收购一个软件,成功了,给人支付100万元,失败了,自己找人开发。这样的需求用一个if就搞不定了,我们需要有两个判断,需要一个双选择结构,所以就有了if-else结构。
语法
if(布尔表达式){
如果表示式的值为true
}else{
如果表示式的值为false
}
demo
public static class main(String[] args){
//考试分数大于60就是及格,小于60分就不及格
Scanner scan = new Scanner(System.in);
System.out.println("请输入成绩:");
String s = scanner.nextInt();
if(score>60){
System.out.println("及格");
}else{
System.out.println("不及格");
}
Scanner.close();
}
5.3.3、if 多选择结构
我们发现刚才的代码不符合实际情况,真实的情况还可能存在ABCD,存在区间多级判断。比如90-100就是A,80-90就是B…等等,在生活中我们很多时候的选择也不仅仅只有两个,所以我 们需要一个多选择结构来处理这类问题!
语法
if(布尔表达式1){
如果表示式1的值为true执行此处代码
}else if(布尔表达式2){
如果表示式2的值为true执行此处代码
}else if(布尔表达式3){
如果表示式3的值为true执行此处代码
}else{
如果以上表示式的值都不为true执行此处代码
}
5.3.4、嵌套的 if 结构
使用嵌套的i…else语句是合法的。也就是说你可以在另一个if或者else if语句中使用if或者else| if 语句。你可以像if 语句一样嵌套else if…else。
语法
if(布尔表达式1){
如果布尔表达式1的值为true执行代码
if(布尔表达式2){
如果布尔表达式2的值为true执行代码
}
}
5.3.5、switch 多选择结构
多选择结构还有一个实现方式就是switch case 语句
switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支
语法
switch(常量表达式){
case value :
// 语句
break;//可选
case value :
//语句
break;//可选
//你可以有任意数量的case语句
default : //可选
//语句
}
switch 语句中的变量类型可以是:
- byte、short、int或者char
- 从 Java 7开始,switch 支持字符串String类型了
- 同时case 标签必须为字符串常量或者字面量
5.4、循环结构
5.4.1、while 循环
语法
while(布尔表达式){
//循环内容
}
只要布尔表达式为true,循环就会一直执行下去。
我们大多数情况是会让循环停止下来的,我们需要一个让表达式失效的方式来结束循环。
少部分情况需要循环一直执行,比如服务器的请求响应监听等。
循环条件一直为true就会造成无限循环【死循环】,我们正常的业务编程中应该尽量避免死循环。会影响程序性能或者造成程序卡死奔溃!
思考:计算1+2+3+…+100=?
public static void main(String[] args){
int i = 0;
int sum = 0;
while(i<=100){
sum = sum + i;
i++;
}
System.out.println(sum);
}
5.4.2、do…while 循环
5.4.3、for循环
虽然所有循环结构都可以用while或者do…while表示,但Java提供了另一种语句for循环,使一些循环结构变得更加简单。
for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。
for循环执行的次数是在执行前就确定的。语法格式如下:
for(初始化; 布尔表达式; 更新){
//代码语句
}
练习1:计算0到100之间的奇数和偶数的和
练习2:用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个
练习3:打印九九乘法表
public static void main(String[] args){
int oddSum = 0;
int evenSum = 0;
for(int i=0; i<100; i++){
if(i%2 != 0){
oddSum += i;
}else{
evenSum += i;
}
}
System.out.println("奇数的和:" + oddSum);
System.out.println("偶数的和:" + evenSum);
}
public static void main(String[] args){
for(int i=0; i<= 1000; i++){
if(i%5 == 0){
System.out.print(i + " ");
}
if(i%{
583} == 0){
System.out.println();
}
}
}
public static void main(String[] args){
for(int j=1; j<=9; j++){
for(int i=1; i<=j; i++){
System.out.print(j+"*"+i+"="+(j*i)+"\t");
}
System.out.println();
}
}
5.4.4、增强for循环
Java5引入了一种主要用于数组或者集合的增强型for循环
Java 增强 for循环语法格式如下:
for(声明语句 : 表达式){
//代码块
}
声明语句:声明新的局部变量,该变量的类型必须和数组原色的类型匹配。其作用域限定在循环语句块,其值与此时数据元素的值相等
表达式:表达式是要访问的数组名,或者是返回值为数组的方法
5.4.5、break、continue
break在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。(break语句也在switch语句中使用)
continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
关于goto关键字
- goto关键字很早就在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用;Java没有goto。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子—带标签的break和continue。
- “标签”是指后面跟一个冒号的标识符,例如:
label:
- 对Java来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另-个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。
六、Java方法
6.1、何谓方法?
Java方法是语句的集合,他们在一起执行一个功能
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成一个功能,这样利于我们后期的扩展。
6.2、方法的定义和调用
定义
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:
方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
- 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法,定义了该方法的返回类型
- 返回值类型:方法可能会返回值。returnValueType 是方法返回值数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字 void。
- 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
- 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或者变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
- 形参:在方法被调用时用于接收外界输入的数据
- 实参:调用方法时实际传给方法的数据
- 方法体:方法体包含具体的代码块,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名){
方法体
return 返回值;
}
方法调用
调用方法:对象名.方法名(参数列表)
Java 支持两种调用方法的方式,根据方法是否返回值来选择
当方法返回一个值的时候,方法调用通常被当做一个值。例如:
int larger = max(30, 40);
如果方法返回值是void,方法调用一定是一条语句。
System.out.println("Hello, kuangshen");
6.3、方法的重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法的重载的规则:
- 方法名称必须相同
- 参数列表不同(个数不同、或类型不同、参数排列顺序不同等)
- 方法的放回类型可以相同也可以不相同
- 仅仅返回类型不同不足以成为方法的重载
实现理论:
- 方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错
6.4、命令行传参
public class CommandLine{
public static void main(String args[]){
for(int 1=0; i<args.length; i++){
System.out.println("args["+i+"]:" + args[i]);
}
}
}
通过小黑窗,找到文件位置,
java CommandLine xx x xxxx
6.5、可变参数
JDK1.5开始,Java支持传递同类型的可变参数给一个方法
在方法声明中,在指定参数类型后加一个省略号(…)
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public static void main(String[] args){
printMax(34, 3, 3, 2, 56.5);
printMax(new double[]{
1, 2, 3});
public static void printMax(double... numbers){
if(numbers.length == 0){
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for(int i=1; i<numbers.length; i++){
if(numbers[i]> result){
result = numbers[i];
}
}
System.out.println("The max value is " + result);
}
}
6.6、递归
递归就是:A方法调用A方法!就是自己调用自己
利用递归可以用简单的程序来解决一些复杂的问题。他通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包括两个部分:
- 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
- 递归体:什么时候需要调用自身方法。
public class Demo06{
public static void main(String[] args){
System.out.println(f(5));
}
public static int f(int n){
if(n==1){
retrun 1;
}else{
return n*f(n-1);
}
}
}
七、数组
7.1、数组的定义
数组是相同类型数据的有序集合
数组描述的是相同类型的若干数据,按照一定的先后次序排列组合而成
其中,每一个数据称为一个数组元素,每个数组元素可以通过一个小标来访问他们
7.2、数组的声明创建
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[] array; //首选方法
或
dataType array[]; //效果相同,但不是首选方法
Java语言使用new操作符来创建数组,语法如下:
dataType[] array = new dataType[arraySize];
数组的元素是通过索引访问的,数组索引从0开始
获取数组长度 : array.length
7.3、内存分析
Java内存分析
三种初始化
静态初始化
int[] a = {
1, 2, 3};
Man[] mans = {
new Man(1, 1), new Man(2, 2)};
动态初始化
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化
7.4、数组的四个基本特点
-
其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
-
其元素必须是相同类型,不允许出现混合类型。
-
数组中的元素可以是任何数据类型,包括基本类型和引用类型。
-
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象在堆中的,因此数组无论保存原始类型还是其他对象类型。
数组对象本身是在堆中的。
数组边界:
下标合法区间:[0, length-1],如果越界就会报错:
public static void main(String[] args){
int[] a = new int[2];
System.out.println(a[2]);
}
ArrayIndexOutOfBoundsException:数组下标越界异常
小结:
- 数组是相同数据类型(数据类型可以为任意类型)的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- 数组长度是确定的,不可变的。如果越界,则报:ArrayIndexOutOfBoundsException
7.5、数组的使用
public class ArrAyDemo03{
public static void main(String[] args){
int[] array = {
1, 2, 3, 4, 5};
for(int i=0; i<array.length; i++){
System.out.println(array[i]);
}
System.out.println("=============");
int sum = 0;
for(int i=0; i<array.length; i++){
sum += array[i];
}
System.out.println("sum=" + sum);
System.out.println("=============");
int max = array[0];
for(int i=1; i<array.length; i++){
if(array[i]>max){
max = array[i];
}
}
System.out.println(max);
}
}
7.6、多维数组
多维数组可以堪称四数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
二维数组
int a[][] = new int[2][5];
解析:以上二维数组a可以看成一个两行五列的数组。
public class ArrayDemo05{
pbulci static void main(String[] args){
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]);
}
}
}
}
7.7、Arrays 类
数组的工具类java.util.Arrays
由于数组对象本身并有没有什么方法可以供我们调用,但API 中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以使用类名进行调用,而“不用”使用对象来调用(注意:是“不用”而不是“不能”)
具有以下常用功能:
- 给数组赋值:通过fill方法
- 给数组排序:通过sort方法,按升序
- 比较数组:通过equals 方法比较数组或只能怪元素值是否相等
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找方操作
7.8、冒泡排序
冒泡排序无疑是最为出名的排序算法之一,总共有八大排序
冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人人尽 皆知
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2);
public class ArrayDemo07{
public static void main(String[] args){
int[] a = {
1,4,5,6,73,2,2,2,25,6,7};
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] array){
int temp = 0;
for(int i=0; i<array.length-1; i++){
for(int j=0; j<array.length-1-i; j++){
if(array[j+1]>array[j]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
return array;
}
}
7.9、稀疏数组
需求:编写五子棋游戏中,有存盘退出和续上盘的功能。
分析问题:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据
解决:稀疏数组
介绍
当一个数组中大部分元素为0,或者为同一值得数组时,可以使用稀疏数组来保存该属猪。
稀疏数组的处理方式是:
- 记录数组一共有几行激烈,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
如小图:左边是原始数组,右边是稀疏数组
public class Demo04 {
public static void main(String[] args) {
//1. 创建一个二维数组 11*11, 0:没有棋子, 1:黑棋 2: 白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
System.out.println("输出原始的数组");
for(int[] ints : array1) {
for(int anInt : ints) {
System.out.print(anInt + "\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);
//2. 创建一个稀疏数组的数组
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 < array2.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("================");
System.out.println("还原");
// 1. 读取稀疏数组
int[][] array3 = new int[array2[0][0]][array2[0][1]];
//2. 给其中的元素还原它的值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]] = array2[i][2];
}
System.out.println("输出还原的数组");
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
}
}
八、面向对象
8.1、什么是面向对象
8.1.1、面向过程 & 面向对象
面向过程思想:
- 步骤氢气简单,第一步做什么,第二部做什么。。。。
- 面向过程适合处理一些较为简单的问题
面向对象思想:
- 物理类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
对于描述复杂的事务,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到围观操作,仍然需要面向过程的思路去处理。
8.1.2、什么是面向对象
面向对象编程(Object-Oriented Programming,OOP)
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
三大特性:
- 封装
- 继承
- 多态
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
从代码运行角度考虑是现有类后又对象。类是对象的模板。
8.1.3、回顾方法及加深
方法的定义
- 修饰符
- 返回类型
- break 和 return 的区别
- 方法名
- 参数列表
- 异常抛出
方法的调用
- 静态方法
- 非静态方法
- 形参和实参
- 值传递和引用传递
- this 关键字
8.2、类与对象的创建
8.2.1、类与对象的关系
类是一种抽象的数据类型,它是对某一类事务整体描述/定义,但是并不能代表一个具体的事务。
- 动物、植物、手机、电脑…
- Person类,Pet类、Car类等,这些类都是用来描述/定义某一类具体的事务应该具备的特点和行为
对象是抽象概念的具体实例
- 张三就是人的一个具体事例,张三家里的旺财就是狗的一个具体事例。
- 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。
8.2.2、创建于初始化对象
使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有一下两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
public class Application{
public static void main(String[] args){
pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
}
class Pet{
public String name;
publci int age;
//无参构造
public void shout(){
System.out.println("叫了一声");
}
}
内存分析
小结
/**
1. 类与对象
类是一个模板:抽象,对象是一个具体的实例
2. 方法
定义、调用!
3. 对象的引用
引用类型: 除基本类型(8)
对象是通过引用来操作的: 栈 --> 堆
4. 属性:字段Field 成员变量
默认初始化:
数字:0 0.0
char: u0000
boolean: false
引用: null
修饰符 属性类型 属性名 = 属性值
5. 对象的创建和使用
- 必须使用new 关键字创建对象,构造器 person kuagnshen = new Person();
- 对象的属性 kuangshen.name
- 对象的方法 kuangshen.sleep()
6. 类:
静态的属性 属性
动态的行为 行为
*/
8.3、封装
该露的露,该藏的藏
- 我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问, 这称为信息隐藏。
记住这句话就够了:属性私有,get/set
/*
1. 提高程序的安全性,保护数据
2. 隐藏代码的实现细节
3. 统一接口
4. 增加系统可维护性
*/
public class Application{
public static void main(String[] args){
Student s1 = new Student();
s1.setName("秦疆");
System.out.println(s1.getName());
s1.setAge(-1);
System.out.println(s1.getAge());
}
}
class Student{
private String name;
private int id;
private int age;
public Student() {
}
public Student(String name, int id, int age) {
super();
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>120 || age<0) {
//不合法
this.age = 3;
}else {
this.age = age;
}
}
}
8.4、继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extends 的意思是“扩展”。子类是父类的扩展。
Java中类只有单继承,没有多继承!
-
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
-
继承关系的两个类,一个为子类,一个为父类。子类继承父类,使用关键字extends来表示
-
子类和父类之间,从意义上讲应该具有“ is a”的关系。
public class Application {
public static void main(String[] args) {
Student student = new Student();
}
}
// 父类
public class Person {
public Person() {
System.out.println("Person无参执行了");
}
//私有的东西无法被继承
protected String name = "kuangshen";
public void print() {
System.out.println("Person");
}
}
// 子类继承父类,就会拥有父类的全部方法
public class Student extends Person{
public Student() {
// 隐藏代码:调用了父类的无参构造
System.out.println("Student无参执行了");
}
private String name = "qingjiang";
public Student (String name) {
this.name = name;
}
public void test1() {
print();
this.print();
super.print();
}
public void print() {
System.out.println("Student");
}
}
super详解
super注意点:
1. super 调用父类的构造方法,必须在构造方法的第一个
2. super 必须只能出现在子类的方法或者构造方法中
3. super 和 this 不能同时调用构造方法
this:
代表的对象不同:
this :本身调用者这个对象
super :只能在继承条件下才可以使用
前提:
this : 没有继承也可以使用
super : 只能在继承条件下才可以使用
构造方法:
this() 本类的构造
super() 父类的构造
方法重写
public class Application {
public static void main(String[] args) {
// 方法的调用只和左边,定义的数据类型有关
A a = new A();
a.test();
// 父类的引用指向了子类
B b = new A();
b.test();
}
}
public class B {
public static void test() {
System.out.println("B=>test()");
}
}
//继承
public class A extends B{
public static void test() {
System.out.println("A=>test()");
}
}
// A=>test()
// B=>test()
去掉static
public class B {
public void test() {
System.out.println("B=>test()");
}
}
public class A extends B{
@Override
public void test() {
System.out.println("A=>test()");
}
}
// A=>test()
// A=>test()
静态方法和非静态的方法区别很大!
重写:需要有几成关系,子类重写父类的方法
- 方法名必须相同
- 参数类表必须相同
- 修饰符:可以范围可以扩大 public > protected > default > private
- 抛出的异常:范围可以被缩小,但不能扩大
重写,子类的方法和父类必须一致;方法体不同!
为什么需要重写:
- 父类的功能,子类不一定需要,或者不一定满足!
8.5、多态
动态编译
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
一个想的实际类型是确定的,但可以指向对象的引用的类型有很多
多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
public class Application {
public static void main(String[] args) {
// TODO Auto-generated method stub
//一个对象的实际类型是确定的
// 可以指向的引用类型就不确定了: 父类的引用指向子类
// student 能调用的方法都是自己的或者继承父类的!
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
// 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
((Student) s2).eat(); // 子类重写了父类的方法,执行子类的方法
s1.eat();
}
}
public class Person {
public void run() {
System.out.println("run");
}
}
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat() {
System.out.println("eat");
}
}
注意:
- 多态是方法的多态,属性没有多态性
- 父类和子类,有联系 类型转换异常!
- 存在条件: 继承关系,方法需要重写,父类引用指向子类对象
instanceof
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
public static void main(String[] args) {
Object object = new 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
System.out.println("==================================");
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); // 编译报错
System.out.println("==================================");
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(student instanceof String); // 编译报错
}
public class Person {
}
public class Student extends Person{
}
public class Teacher extends Person{
}
子类转换成父类,可能丢失自己本来的一些方法!
- 父类应用指向子类的对象
- 把子类转换成父类,向上转型
- 把父类转换成子类,向下转型:强制转换
- 方便方法的调用,减少重复的代码!
8.6、static 详解
public class Test {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Test() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Test test = new Test();
}
}
/*
静态代码块
匿名代码块
构造方法
*/
8.7、抽象类
abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
抽象类,不能使用new关键字来创建对象,他是用来让子类继承的。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
//抽象类
public abstract class Action{
//约束~有人帮我么实现
// abstract, 抽象方法,只有方法名字,没有方法的实现!
public abstract void doSomething();
/*
1. 不能new这个抽象类,只能靠子类去实现它: 约束
2. 抽象类中可以写普通的方法
3. 抽象方法必须在抽象类中
*/
}
// 抽象类的所有方法,继承了它的子类,都必须要实现它的方法~ 除非
public class A extedns Action{
@Override
public void doSomething(){
}
}
8.8、接口的定义和实现
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范! 自己无法写方法~专业的约束! 约束和实现分离: 面向接口编程~
接口及时规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如C++、java、C#等),就是因为设计模式锁研究的,实际上就是如何合理的去抽象。
声明类的关键字是class,声明接口的关键字是interface
// interface 定义的关键字,接口都需要有实现类
public interface UserService{
// 接口中的所有定义其实都是抽象的 public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
// 类 实现接口 implements
// 实现了接口的类,就需要重写接口中的方法~
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 timer() {
}
}
public interface TimeService{
void timer();
}
作用:
- 约束
- 定义一些方法,让不同的人实现~
- 方法都是 public abstract
- 常量都是 public static final
- 接口不能被实例化,接口中没有构造方法
- 可以实现多个 implements
- 必须要重写接口中的方法
8.8、内部类
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
public class Outer {
private int id=10;
public void out() {
System.out.println("这是外部类的方法");
}
//成员内部类
public class Inner{
public void in() {
System.out.println("这是内部类的方法");
}
// 多的外部类的私有属性
public void getID() {
System.out.println(id);
}
}
// 静态内部类
public static class Inner{
public void in() {
System.out.println("这是内部类的方法");
}
}
//局部内部类
public void method() {
class Inner{
public void in() {
}
}
}
}
//匿名内部类
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.in();
// 没有名字初始化类,不用讲实例保存到变量中~
new Apple().eat();
UserService useService = new UserService(){
@Override
public void hello(){
}
}
}
}
九、异常
9.1、什么是异常
实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了。等等。
软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理。而不至于程序崩溃。
异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。
异常发生在程序运行期间,它影响了正常的程序执行流程。
9.1.1、简单分类
要理解Java异常处理是如何工作的,需要掌握以下三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误(error):错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,他们在编译也检查不到的。
9.1.2、异常体系结构
Java把异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
在Java API 中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。
9.1.3、Error
Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;
还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
9.1.4、Exception
在Exception分支中有一个重要的子类RuntimeException(运行时异常)
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算术异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
9.2、异常处理机制
抛出异常
捕获异常
异常处理五个关键字:
- try、catch、finally、throw、throws
9.2.1、异常处理
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(a/b);
} catch (ArithmeticException e) {
System.out.println("程序出现异常,除数不能为0");
}finally {
System.out.println("finally");
}
}
}
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
// 假设要捕获多个异常: 从小到大!
try {
new Test().a();
} 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");
}
}
public void a() {
b();
}
public void b() {
a();
}
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
// 假设要捕获多个异常: 从小到大!
try {
new Test().a();
} 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");
}
}
public void a() {
b();
}
public void b() {
a();
}
}
public class Test {
public static void main(String[] args) {
new Test().test(1, 0);
}
public void test(int a, int b) {
if(b == 0) {
throw new ArithmeticException(); // 主动的抛出异常,一般在方法中使用
}
System.out.println(a/b);
}
public static void main(String[] args) {
try {
new Test().test(1, 0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
// 假设在方法中,处理不了这个异常,方法上抛出异常
public void test(int a, int b) throws Exception {
if(b == 0) {
throw new ArithmeticException(); // 主动的抛出异常,一般在方法中使用
}
System.out.println(a/b);
}
}
9.2.2、自定义异常
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需要继承Exception类即可。
在程序中使用自定义异常类,答题可分为以下几个步骤:
- 创建自定义异常类
- 在方法中通过throw 关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用 try-catch 语句捕获并处理;否则在方法的声明处通过 throws 关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获异常并处理异常。
// 自定义的异常类
public class MyException extends Exception{
private int detail;
public MyException(int a) {
this.detail = a;
}
//tostring : 异常的打印信息
@Override
public String toString() {
return "MyException [" + detail + "]";
}
}
public class Test {
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
System.out.println("MyException=>" + e);
}
}
public static void test(int a) throws MyException{
System.out.println("传递的参数为:" + a);
if (a>10) {
throw new MyException(a);
}
System.out.println("ok");
}
}
9.2.3、实际应用中的经验总结
- 处理运行时异常,采用逻辑去合理规避同时辅助 try-catch 处理
- 在多重catch块后面, 可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上 try-catch,处理潜在的异常
- 尽量去处理异常,切记只是简单地调用 printStackTrace() 去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加 finally 语句块去释放占用的资源
转载:https://blog.csdn.net/su2231595742/article/details/116295404