【JAVA基础】初学者指南--两万字知识点总结--零基础,超详细 。

Java基础知识

JAVA入门

JAVA三大版本

  • JavaSE(Java Standard Edition):标准版,在个人计算机上使用。

​ JavaSE是Java平台的核心,它提供了非常丰富的API来开发一般个人计算机上的应用程序。

  • JavaEE(Java Enterprise Edition):企业版,定位在服务器端的应用。

​ JavaEE是JavaSE的扩展,增加了用于服务器开发的类库。

  • JavaME(Java Micro Edition):微型版,定位在消费性电子产品应用上。

​ JavaME是JavaSE的内申,包含J2SE的一部分核心类,也有自己的扩展类,增加了适合微小装置的类库。该版本针对资源有限的电子消费产品的需求精简核心库,并提供了模块化的架构让不同产品随时能增加支持的能力。

安卓开发属于JavaME,但不要认为JavaME就是安卓开发,安卓开发只是JavaME其中的一部分。

JAVA的特性和优势

  • 跨平台/可移植性

    这是Java的核心优势。比如Java的int类型永远是32位,不像C/C++可能是16或32位,根据编译器厂商规定来变化。这样Java程序的移植就会很方便。

  • 安全性

    Java适合网络/分布式环境,为了达到这个目标,在安全性方面投入了很大的精力,使Java可以很容易构建防病毒,防篡改的系统。

  • 面向对象

    与C++相比,Java是完全面向对象的语言。

  • 简单性

    Java就是C++的简化版,也可以把Java称之为“C+±”,指的就是将C++的一些内容去掉,比如头文件、指针、结构、联合、操作符重载、虚基类等等。同时,Java的语法是基于C语言的,学习成本会很低。

  • 高性能

    Java在最初发展阶段性能较低,这在高级语言中往往是难以避免的,Java语言通过虚拟机的优化可以提升几十倍的运行效率。这样Java程序的执行效率就会大大提高。

  • 分布式

    Java是为Internet的分布式环境设计的,因为它能够处理TCP/IP协议。

  • 多线程

    多线程的使用可以带来更好的交互响应和实时行为。

  • 健壮性

    Java是一种健壮性的语言,它吸收了C/C++的优点,但去掉了影响程序健壮性的部分(如:指针,内存的申请和释放等)。Java程序不会造成计算机崩溃。及时Java程序出错时会把异常抛出,在通过异常处理机制加以处理。

JAVA运行机制

在这里插入图片描述

  1. Java首先用文本编辑器编写Java源程序,源文件的后缀名为.java;
  2. 再利用编译器(javac)将源程序编译成字节码文件,字节码文件的后缀名为.class;
  3. 最后利用虚拟机(解释器,java)解释执行。如上图所示。

​ 计算机高级语言主要类型有编译型和解释型两种,而Java是两种类型的结合。

JVM、JRE、JDK

在这里插入图片描述

  • JVM(Java Virtual Machine):Java虚拟机,用于执行字节码的“虚拟计算机”。

    不同操作系统上有不同版本的JVM,屏蔽了底层运行平台的差异,是实现跨平台的核心。

  • JRE(Java Runtime Environment):包含JVM,库函数等。

  • JDK(Java Development kit):包含JRE,编译器和调试器等。

JAVA开发环境搭建

  • 下载JDK

    https://www.oracle.com/java/technologies/downloads/

  • 安装JDK

    选择JDk安装目录,采用默认即可。(如果自定义目录,注意路径中不能包含中文)

变量、数据类型和运算符

变量(variable)

变量的本质

  1. 变量是一个可操作的内存空间,空间位置是确定的,但里面当放的值不确定。
  2. 可通过变量名来访问对应的存储空间,从而操作这个空间存储的值。
  3. Java是一种强类型语言,每个变量都必须声明其数据类型。变量的数据类型决定了变量所占存储空间的大小。

变量的声明

double salary;
int age;
long earth_moon_distance;

声明变量的初始化,这一点和C语言相同

int age = 18;
double e = 2.717271828;
int i,j;	//同时定义两个变量,都是int类型

变量的分类和作用域

变量有三种类型:局部变量、成员变量(实例变量)和静态变量。

局部变量

方法或语句块内部定义的变量。生命周期从声明位置开始到方法或语句块执行完毕为止。局部变量在使用前必须先声明和初始化后再使用。

public void test(){
    int i;
    int j = i+1;//编译会出错,变量i未初始化
}
public void test(){
	int i = 1;
	int j = i+1;//编译正确
}
成员变量(也叫实例变量)

方法外部、类内部定义的变量。从属于对象,生命周期伴随对象始终。可以不自行初始化,系统会自动初始化为对应类型的默认值。

public class Test{
    int age;		//int类型,默认初始化为0
    double score;	//double类型:默认初始化为0.0
    char ch;		//char类型:默认初始化为:'\u0000'(前缀u是指Unicode编码表示)
    boolean flag;	//boolean类型:默认初始化为false
}
静态变量(又叫类变量)

使用static定义。从属于类,生命周期伴随类始终,从类加载到卸载。如果不自行初始化,与成员变量一样会自动初始化为对应类型的默认值。

常量(Constant)

在Java语言中,用关键字final来定义一个常量。

声明格式:

final 类型 变量名 = 初始化值;
public class TestConstant{
    public static void main(String[ ] args){
        final double PI = 3.14;
        PI = 3.15;		//编译错误!final修饰不能再被赋值
    }
    /*
    我们把像PI这样的常量成为符号常量,因为定义好PI常量后,就可以把它当做一个符号来使用。事实上,这里的PI是我们自定义的一个圆周率π,完全可以用PI替换π来使用。因此,在它使用过程中是一定不能被修改的。
    */
}

基本数据类型

整型

byte

1字节 表述范围:-2(7) ~ 2(7)-1 (-128~127)

short

2字节 表述范围:2(15) ~ 2(15) -1 (-32768~32767)

int

4字节 表述范围:-2(31) ~ 2(31)-1 (-2147483648~2147483647) 约21 亿

long

8字节 表述范围:-2(63) ~ 2(63)-1

Java中整型默认为int型,声明long型常量可以在后面加 ’ l ’ 或 ’ L ’ 。

long a = 10000000;		//编译通过,因为在int表示范围内(21亿)。
long b = 10000000000;	//编译错误(报错:The literal 10000000000 of type int is out of range),因为会把右边的数默认为int型,但超出int表示范围。必须在后面加上L
long c = 10000000000l	//编译通过。
整型的不同进制表示方式
  • 十进制整数,如:99,-100,0
  • 八进制整数,以0开头,如:015
  • 十六进制整数,以0x开头,如:0x15
  • 二进制数,以0b开头,如:0b01100101

浮点型

float

4字节 表述范围:-3.403E38~3.403E38

double

8字节 表述范围:-1.798E308~1.798E308

浮点型常量默认是double,要改成float可以在后面加f或F。

通常不要比较两个浮点数的值,因为浮点数不够精确。

【示例 1】浮点型数据的比较一

float f = 0.1f;
double d = 1.0/10;
System.out.println(f==d);	//结果为false

【示例 2】浮点型数据的比较二

float d1 = 123456789f;
float d2 = d1+1;
if(d1==d2)
    System.out.println("d1=d2");	
else
    System.out.println("d1!=d2");
//输出结果为d1=d2,说明浮点数的精度是有限的,即使d1加1也被忽略掉了。

所以,不要使用浮点数进行比较!需要比较可以使用BigDecimal类

java.math包下面有两个类:BigInteger和BigDecimal,这两个类可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点数运算。

字符型

char

2字节 在Java中使用单引号来表示字符常量。例如:‘A’,它与**“A”**是不同的。

char类型用来表示Unicode编码表中的字符。Unicode编码被设计用来处理各种语言的文字,它占2个字节,可允许有65536个字符。所以也可以用中文表示一个char类型字符。

char c1 = 'a';
char c2 = '中';	//汉字“中”和“a”的地位相同。
常用转义字符
char c1 = '\n';		//换行
char c2 = '\b';		//退格
char c3 = '\r';		//回车
char c4 = '\t';    	//制表符
char c5 = '\“';		//双引号
char c6 = '\\';		//反斜杠

布尔型(boolean)

  1. boolean类型有两个常量值,true和false。
  2. 占1个或4个字节,不能使用0或非0的整数代替true和false,这点和C语言不同。

​ JVM把单独一个boolean当做int来处理,所以占4字节,把boolean数组当byte处理,占1字节。所以,boolean类型单独使用时占4字节,在数组中时占1字节。

运算符

算术运算符
  1. +,-,*,/,%属于二元运算符。%是取模运算符,就是我们常说的求余数操作。
  2. 算术运算符中++(自增),–(自减)属于一元运算符。

二元运算符的运算规则:

整数运算:

  • 如果两个操作数有一个为 long, 则结果也为 long。

  • 没有 long 时,结果为 int。即使操作数全为 short,byte,结果也是int。

    浮点运算:

  • 如果两个操作数有一个为 double,则结果为 double。

  • 只有两个操作数都是 float,则结果才为 float。

取模运算:

  • 其操作数可以为浮点数,一般使用整数,结果是“余数”,“余数”符号和左边操作数相同,如:7%3=1,-7%3=-1,7%-3=1。
赋值及其扩展赋值运算符
+=a+=ba = a+b
-=a-=ba = a-b
*=a*=ba = a*b
/=a/=ba = a/b
%=a%=ba = a%b
关系运算符

关系运算符用来进行比较运算。关系运算的结果是布尔值:true/false;

  • =是赋值运算符,而真正的判断两个操作数是否相等的运算符是==。
  • ==、!= 是所有(基本和引用)数据类型都可以使用。
  • > 、>=、 <、 <= 仅针对数值类型
逻辑运算符

逻辑运算的操作数和运算结果都是 boolean 值。

&只要有一个为 false,则 false
短路与&&只要有一个为 false,则 false
|只要有一个为 true, 则 true
短路或||只要有一个为 true, 则 true
!取反
异或^相同为 false,不同为 true
位运算符

位运算指的是进行二进制位的运算。

~取反
&按位与
|按位或
^按位异或
<<左移运算符,左移 1 位相当于乘 2
>>右移运算符,右移 1 位相当于除 2 取商

&和|既是逻辑运算符,也是位运算符。如果两侧操作数都是 boolean 类型,就作为逻辑运算符。如果两侧的操作数是整数类型,就是位运算符。

字符串连接符

“+”运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。

int a = 10;
System.out.println("a="+12);	//输出结果为:a=10
条件运算符
x?y:z
运算符优先级的问题

运算符的优先级

优先级运算符
1()括号运算符
2!、+(正号)、-(负号)一元运算符
2~位逻辑运算符
2++、–递增与递减运算
3*、/、%算术运算符
4+、-算术运算符
5<<、>>位左移、右移运算符
6>、>=、<、<=关系运算符
7==、!=关系运算符
8&位运算符、逻辑运算符
9^位运算符、逻辑运算符
10|位运算符、逻辑运算符
11&&逻辑运算符
12||逻辑运算符
13? :条件运算符
14=、+=、-=、*=、/=、%=赋值运算符、扩展运算符

数据类型的转换

自动类型转换

容量小的数据类型可以自动转换为容量大的数据类型

比如:int转为long、short转为int、char转为int等,所有基本数据类型都可以转为double,但转换时可能会有精度的损失。

强制类型转换

double a = 3.91;
int x = (int)a;		//将a强转为int型,但损失了小数后的数据

注意:

强转时如果超出范围,就会被截断成一个错误的值!

int x = 300;
byte b = (byte)x;	//byte的范围是-128~127,x的值超过强转类型的范围
					//输出结果 b = 44,是一个错误的值。

IDEA的使用

下载和安装IDEA

下载地址: https://www.jetbrains.com/idea/download/#section=windows

IDEA的配置和使用

在IDEA中创建Java项目

点击“Create New Project”,创建新的项目。

  1. 选择 JD
  2. 根据项目模板创建项目
  3. 填写项目名称和包名
  4. 开始编写代码

IDEA快捷键大全

Ctrl

Ctrl + F 在当前文件进行文本查找 (必备)
Ctrl + R 在当前文件进行文本替换 (必备)
Ctrl + Z 撤销 (必备)
Ctrl + Y 删除光标所在行 或 删除选中的行 (必备)
Ctrl + X 剪切光标所在行 或 剪切选择内容
Ctrl + C 复制光标所在行 或 复制选择内容
Ctrl + D 复制光标所在行 或 复制选择内容,并把复制内容插入光标位置下面 (必备)
Ctrl + W 递进式选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展选中范围 (必备)
Ctrl + E 显示最近打开的文件记录列表
Ctrl + N 根据输入的 类名 查找类文件
Ctrl + G 在当前文件跳转到指定行处
Ctrl + J 插入自定义动态代码模板
Ctrl + P 方法参数提示显示
Ctrl + Q 光标所在的变量 / 类名 / 方法名等上面(也可以在提示补充的时候按),显示文档内容
Ctrl + U 前往当前光标所在的方法的父类的方法 / 接口定义
Ctrl + B 进入光标所在的方法/变量的接口或是定义出,等效于 Ctrl + 左键单击
Ctrl + K 版本控制提交项目,需要此项目有加入到版本控制才可用
Ctrl + T 版本控制更新项目,需要此项目有加入到版本控制才可用
Ctrl + H 显示当前类的层次结构
Ctrl + O 选择可重写的方法
Ctrl + I 选择可继承的方法
Ctrl + + 展开代码
Ctrl + - 折叠代码
Ctrl + / 注释光标所在行代码,会根据当前不同文件类型使用不同的注释符号 (必备)
Ctrl + [ 移动光标到当前所在代码的花括号开始位置
Ctrl + ] 移动光标到当前所在代码的花括号结束位置
Ctrl + F1 在光标所在的错误代码出显示错误信息
Ctrl + F3 调转到所选中的词的下一个引用位置
Ctrl + F4 关闭当前编辑文件
Ctrl + F8 在 Debug 模式下,设置光标当前行为断点,如果当前已经是断点则去掉断点
Ctrl + F9 执行 Make Project 操作
Ctrl + F11 选中文件 / 文件夹,使用助记符设定 / 取消书签
Ctrl + F12 弹出当前文件结构层,可以在弹出的层上直接输入,进行筛选
Ctrl + Tab 编辑窗口切换,如果在切换的过程又加按上delete,则是关闭对应选中的窗口
Ctrl + Enter 智能分隔行
Ctrl + End 跳到文件尾
Ctrl + Home 跳到文件头
Ctrl + Space 基础代码补全,默认在 Windows 系统上被输入法占用,需要进行修改,建议修改为 Ctrl + 逗号 (必备)
Ctrl + Delete 删除光标后面的单词或是中文句
Ctrl + BackSpace 删除光标前面的单词或是中文句
Ctrl + 1,2,3…9 定位到对应数值的书签位置
Ctrl + 左键单击 在打开的文件标题上,弹出该文件路径
Ctrl + 光标定位 按 Ctrl 不要松开,会显示光标所在的类信息摘要
Ctrl + 左方向键 光标跳转到当前单词 / 中文句的左侧开头位置
Ctrl + 右方向键 光标跳转到当前单词 / 中文句的右侧开头位置
Ctrl + 前方向键 等效于鼠标滚轮向前效果
Ctrl + 后方向键 等效于鼠标滚轮向后效果

————————————————

Alt
Alt + ` 显示版本控制常用操作菜单弹出层
Alt + Q 弹出一个提示,显示当前类的声明 / 上下文信息
Alt + F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择
Alt + F2 对于前面页面,显示各类浏览器打开目标选择弹出层
Alt + F3 选中文本,逐个往下查找相同文本,并高亮显示
Alt + F7 查找光标所在的方法 / 变量 / 类被调用的地方
Alt + F8 在 Debug 的状态下,选中对象,弹出可输入计算表达式调试框,查看该输入内容的调试结果
Alt + Home 定位 / 显示到当前文件的 Navigation Bar
Alt + Enter IntelliJ IDEA 根据光标所在问题,提供快速修复选择,光标放在的位置不同提示的结果也不同 (必备)
Alt + Insert 代码自动生成,如生成对象的 set / get 方法,构造函数,toString() 等
Alt + 左方向键 按左方向切换当前已打开的文件视图
Alt + 右方向键 按右方向切换当前已打开的文件视图
Alt + 前方向键 当前光标跳转到当前文件的前一个方法名位置
Alt + 后方向键 当前光标跳转到当前文件的后一个方法名位置
Alt + 1,2,3…9 显示对应数值的选项卡,其中 1 是 Project 用得最多

————————————————

Shift
Shift + F1 如果有外部文档可以连接外部文档
Shift + F2 跳转到上一个高亮错误 或 警告位置
Shift + F3 在查找模式下,查找匹配上一个
Shift + F4 对当前打开的文件,使用新Windows窗口打开,旧窗口保留
Shift + F6 对文件 / 文件夹 重命名
Shift + F7 在 Debug 模式下,智能步入。断点所在行上有多个方法调用,会弹出进入哪个方法
Shift + F8 在 Debug 模式下,跳出,表现出来的效果跟 F9 一样
Shift + F9 等效于点击工具栏的 Debug 按钮
Shift + F10 等效于点击工具栏的 Run 按钮
Shift + F11 弹出书签显示层
Shift + Tab 取消缩进
Shift + ESC 隐藏当前 或 最后一个激活的工具窗口
Shift + End 选中光标到当前行尾位置
Shift + Home 选中光标到当前行头位置
Shift + Enter 开始新一行。光标所在行下空出一行,光标定位到新行位置
Shift + 左键单击 在打开的文件名上按此快捷键,可以关闭当前打开文件
Shift + 滚轮前后滚动 当前文件的横向滚动轴滚动

————————————————

Ctrl + Alt
Ctrl + Alt + L 格式化代码,可以对当前文件和整个包目录使用 (必备)
Ctrl + Alt + O 优化导入的类,可以对当前文件和整个包目录使用 (必备)
Ctrl + Alt + I 光标所在行 或 选中部分进行自动代码缩进,有点类似格式化
Ctrl + Alt + T 对选中的代码弹出环绕选项弹出层
Ctrl + Alt + J 弹出模板选择窗口,讲选定的代码加入动态模板中
Ctrl + Alt + H 调用层次
Ctrl + Alt + B 在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口
Ctrl + Alt + V 快速引进变量
Ctrl + Alt + Y 同步、刷新
Ctrl + Alt + S 打开 IntelliJ IDEA 系统设置
Ctrl + Alt + F7 显示使用的地方。寻找被该类或是变量被调用的地方,用弹出框的方式找出来
Ctrl + Alt + F11 切换全屏模式
Ctrl + Alt + Enter 光标所在行上空出一行,光标定位到新行
Ctrl + Alt + Home 弹出跟当前文件有关联的文件弹出层
Ctrl + Alt + Space 类名自动完成
Ctrl + Alt + 左方向键 退回到上一个操作的地方 (必备)(注意与其他软件快捷键冲突)
Ctrl + Alt + 右方向键 前进到上一个操作的地方 (必备)(注意与其他软件快捷键冲突)
Ctrl + Alt + 前方向键 在查找模式下,跳到上个查找的文件
Ctrl + Alt + 后方向键 在查找模式下,跳到下个查找的文件

————————————————

Ctrl + Shift
Ctrl + Shift + F 根据输入内容查找整个项目 或 指定目录内文件 (必备)
Ctrl + Shift + R 根据输入内容替换对应内容,范围为整个项目 或 指定目录内文件 (必备)
Ctrl + Shift + J 自动将下一行合并到当前行末尾 (必备)
Ctrl + Shift + Z 取消撤销 (必备)
Ctrl + Shift + W 递进式取消选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展取消选中范围 (必备)
Ctrl + Shift + N 通过文件名定位 / 打开文件 / 目录,打开目录需要在输入的内容后面多加一个正斜杠 (必备)
Ctrl + Shift + U 对选中的代码进行大 / 小写轮流转换 (必备)
Ctrl + Shift + T 对当前类生成单元测试类,如果已经存在的单元测试类则可以进行选择
Ctrl + Shift + C 复制当前文件磁盘路径到剪贴板
Ctrl + Shift + V 弹出缓存的最近拷贝的内容管理器弹出层
Ctrl + Shift + E 显示最近修改的文件列表的弹出层
Ctrl + Shift + H 显示方法层次结构
Ctrl + Shift + B 跳转到类型声明处
Ctrl + Shift + I 快速查看光标所在的方法 或 类的定义
Ctrl + Shift + A 查找动作 / 设置
Ctrl + Shift + / 代码块注释 (必备)
Ctrl + Shift + [ 选中从光标所在位置到它的顶部中括号位置
Ctrl + Shift + ] 选中从光标所在位置到它的底部中括号位置
Ctrl + Shift + + 展开所有代码
Ctrl + Shift + - 折叠所有代码
Ctrl + Shift + F7 高亮显示所有该选中文本,按Esc高亮消失
Ctrl + Shift + F8 在 Debug 模式下,指定断点进入条件
Ctrl + Shift + F9 编译选中的文件 / 包 / Module
Ctrl + Shift + F12 编辑器最大化
Ctrl + Shift + Space 智能代码提示
Ctrl + Shift + Enter 自动结束代码,行末自动添加分号 (必备)
Ctrl + Shift + Backspace 退回到上次修改的地方
Ctrl + Shift + 1,2,3…9 快速添加指定数值的书签
Ctrl + Shift + 左方向键 在代码文件上,光标跳转到当前单词 / 中文句的左侧开头位置,同时选中该单词 / 中文句
Ctrl + Shift + 右方向键 在代码文件上,光标跳转到当前单词 / 中文句的右侧开头位置,同时选中该单词 / 中文句
Ctrl + Shift + 左方向键 在光标焦点是在工具选项卡上,缩小选项卡区域
Ctrl + Shift + 右方向键 在光标焦点是在工具选项卡上,扩大选项卡区域
Ctrl + Shift + 前方向键 光标放在方法名上,将方法移动到上一个方法前面,调整方法排序
Ctrl + Shift + 后方向键 光标放在方法名上,将方法移动到下一个方法前面,调整方法排序

————————————————

Alt + Shift
Alt + Shift + N 选择 / 添加 task
Alt + Shift + F 显示添加到收藏夹弹出层
Alt + Shift + C 查看最近操作项目的变化情况列表
Alt + Shift + F 添加到收藏夹
Alt + Shift + I 查看项目当前文件
Alt + Shift + F7 在 Debug 模式下,下一步,进入当前方法体内,如果方法体还有方法,则会进入该内嵌的方法中,依此循环进入
Alt + Shift + F9 弹出 Debug 的可选择菜单
Alt + Shift + F10 弹出 Run 的可选择菜单
Alt + Shift + 左键双击 选择被双击的单词 / 中文句,按住不放,可以同时选择其他单词 / 中文句
Alt + Shift + 前方向键 移动光标所在行向上移动
Alt + Shift + 后方向键 移动光标所在行向下移动

————————————————

Ctrl + Shift + Alt
Ctrl + Shift + Alt + V 无格式黏贴
Ctrl + Shift + Alt + N 前往指定的变量 / 方法
Ctrl + Shift + Alt + S 打开当前项目设置
Ctrl + Shift + Alt + C 复制参考信息

————————————————

其他
F2 跳转到下一个高亮错误 或 警告位置 (必备)
F3 在查找模式下,定位到下一个匹配处
F4 编辑源
F7 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中
F8 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则不进入当前方法体内
F9 在 Debug 模式下,恢复程序运行,但是如果该断点下面代码还有断点则停在下一个断点上
F11 添加书签
F12 回到前一个工具窗口
Tab 缩进
ESC 从工具窗口进入代码文件窗口
连按两次Shift 弹出 Search Everywhere 弹出层

控制语句

IF

if单分支结构

语法结构:

if(布尔表达式){

语句块

}

if-else双分支结构

语法结构:

if(布尔表达式)

{ 语句块 1 }

else

{ 语句块 2 }

if-else if-else 多分支结构

语法结构:

if(布尔表达式 1)

{ 语句块 1; }

else if(布尔表达式 2)

{ 语句块 2; }……

else if(布尔表达式 n)

{ 语句块 n; }

else

{ 语句块 n+1; }

SWITCH结构

switch多分支结构(多值情况)

语法结构:

switch(表达式){

case 值1:

语句块1;

break;

case 值2:

语句块2;

break;

... ... ...

default:

默认语句块;

}

  1. switch会根据表达式的值从相匹配的case处开始执行,一直执行到break处或者是switch的末尾。如果表达式的值没有与任何一个case值匹配,则执行default语句。
  2. switch中表达式的值,可以int(byte、short、char)、枚举、字符串(JDK7 新特性)。
public class TestSwitch{
    public static void main(String[] args){
        int month = 0;
       /* 
       if(month==1||month==2||month==3)
            System.out.println("春季");
        else if(month==4||month==5||month==6)
            System.out.println("夏季");
        else if(month==7||month==8||month==9)
            System.out.println("秋季");
        else if(month==10||month==11||month==12)
            System.out.println("冬季");
        */ 
        
        //用switch实现上述if多分支语句
        switch(month){
            case 1:
            case 2:
            case 3:
                System.out.println("春季");
                break;
            case 4:
            case 5:
            case 6:
                System.out.println("夏季");
                break;
            case 7:
            case 8:
            case 9:
                System.out.println("秋季");
                break;
            case 10:
            case 11:
            case 12:
                System.out.println("冬季");
                break;
            default:
                System.out.println("月份信息错误!");
        }
    }
}

break和continue

  1. break用于强制退出整个循环
  2. continue用于结束本次循环,继续执行下一次循环。
/*薪水计算器,输入月薪和每年的薪资月数,
接下来按任意键可计算出年薪,并打印年薪的值;
若输入exit可以直接退出程序,输入continue可重新加载程序。*/
while(true){
    System.out.println("请输入您的月薪:");
    int month_salary = scanner.nextInt();		//从键盘上读取月薪
    System.out.println("请输入每年的薪资月数:");
    int months = scanner.nextInt();				//从键盘读取月份数
    System.out.println("按任意键获得年薪结果--------");
    System.out.println("输入exit可退出程序---------");
    System.out.println("输入next可以重新输入--------");//打印提示信息
    scanner.nextLine();
    String command = scanner.nextLine();		//获取用户下一步的操作指令
    switch(command){//用switch实现多分支语句
        case "exit":
            break;
        case "next":
            continue;
        default:
            System.out.println("您的年薪是:"+month_salary*months);
    }
}

嵌套循环

/*使用嵌套循环打印九九乘法表*/
public class Test{
    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+"="j*i+"\t");
            }
            System.out.println();
        }
    }
}

递归结构

递归的思想:自己调用自己。

递归结构包括两部分:

  • 定义递归头 递归头就是递归的结束条件。没有递归头递归将会陷入死循环。
  • 递归体 调用自身的方法
//利用递归求阶乘
static long factorial(int n){
    if(n==1){//递归头
        return 1;
    }
    else{//递归体
        return n*factorial(n-1);
    }
}

递归的缺陷:

算法简单是递归的优点。但是递归会调用大量的系统堆栈,导致内存消耗多,在递归层次较多时速度明显比循环慢,所以使用递归需要慎重。

方法

介绍方法前,先讲语句块。语句块就是复合语句,一个语句块需要用**{}**包起来。

语句块内定义的变量是局部变量,只能在块内使用,不能在块外使用。当然,在语句块内也可以使用语句块外的全局变量。

方法:

Java中的方法相当于C语言的函数

  1. 方法用于定义该类或该类的实例的行为特征和功能实现。
  2. 面向过程中,函数是最基本的单位,整个程序由一个个的函数调用组成。
  3. 面向对象中,是最基本的单位,方法是从属于类和对象的。

方法的声明和定义也和C语言相同,方法也可以重载,可以参照C语言的规则。

面向对象概念

初识面向对象

引入表格和类的比较

想必我们对表格再熟悉不过了,实际上,**“表格思维”**就是一种典型的面向对象思维。

在本质上,互联网上的数据都是用“表格”实现的,下面以公司雇员为例,下面介绍一下怎么由表格到类的。

ID姓名岗位基本工资绩效工资入职日期
1001张三程序员1500060009/10
1002李四销售1200080008/21
1003王五财物12000011/20
1004赵六经理20000100003/19

上面这个雇员表,可以把公司的员工信息**“结构化”,“标准化”**,让管理者可以更方便的进行管理。

我们把表中的叫做 “字段” ,英文叫 “filed” 。显然,一个表格的结构是有多个field构成的。

在面向对象中,类与表格的结构很相似,我们定义一个 “雇员类” ,类中的属性就是 “字段” ,一个类的实例叫 “对象” ,对应表格中的一行信息。

类的方法和表格中的动作

在现实中,公司中的每个雇员都会集体参加一些动作,这些动作有:

  1. 开会
  2. 午休
  3. 提交工作日志

这些动作对应在类中就是方法

对象对应表格中的行数据
  1. 表结构对应:类结构
  2. 一行数据对应:一个对象
  3. 表中所有行数据对应:这个类的所有对象

面向过程和面向对象

面向过程和面向对象的区别

面向过程重点关注如何执行,面向过程时,首先思考 “ 怎么按步骤实现?” ,并将步骤对应成方法,按步骤,一步步完成这些方法。

面向对象(Oriented-Object)的思维更契合人的思维模式。首先思考 “ 怎么设计这个事物?” ,因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到 实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。

面向对象和面向过程思想的总结

  • 都是解决问题的思维方式,都是代码组织的方式。
  • 面向过程是一种“执行者思维”,解决简单问题可以使用面向过程。
  • 面向对象是一种“设计者思维”,解决复杂、需要协作的问题可以使用面向对象。
  • 面向对象离不开面向过程:
    • 宏观上:通过面向对象进行整体设计
    • 微观上:执行和处理数据,仍然是面向过程。

对象和类的详解

类的定义

//每个源文件必须有且只有一个public class,并且类名和文件名保持一致!
public class Car{
    ...
}
//一个java文件可以同时定义多个class,但只能有一个public class
class Tyre{
    ...
}
class Engine{
    ...
}
class Seat{
    ...
}

对一个类来说,有三种成员:

  • 属性field
  • 方法method
  • 构造器constructor

属性(field)

属性用于定义该类的对象包含哪些数据或者静态特征。定义成员变量时可以不必初始化,Java将会用默认值对其初始化。

属性定义格式:

【修饰符】 类型 属性名 【= 初始值】;

方法(method)

方法用于定义该类的行为特行和功能实现。

方法定义格式:

【修饰符】 方法返回值类型 方法名(形参列表){方法体}

构造方法(构造器constructor)

构造器用于对象的初始化,而不是创建一个对象!

声明格式:

【修饰符】 类名(【形参列表】){…}

构造器的四个要点:

  • 构造器通过new关键字调用
  • 构造器不能定义返回值类型,不能在构造器中使用return语句
  • 如果没有手动定义构造器,系统会默认调用一个无参构造器。
  • 构造器的方法名就是类名

面向对象的内存分析

Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area。
在这里插入图片描述

栈(stack)

虚拟机栈(简称“栈”)的特点如下:

  1. 栈描述的是方法执行的内存模型。每个方法被调用后都会在栈中创建一个栈帧(存储局部变量、操作数、方法出口等)。
  2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)。
  3. 栈属于线程私有,不能实现线程间共享
  4. 栈的存储特性是 “先进后出,后进先出” 。
  5. 栈是由系统自动分配的,速度快,是一段连续的内存空间。

堆(heap)

堆的特点如下:

  1. 堆用于存储创建好的对象或数组。(数组也是对象)
  2. JVM只有一个堆,被所有线程共享。
  3. 堆是不连续的内存空间,分配灵活,速度慢
  4. 堆被所有线程共享,在堆上的区域,被划分为新生代、老年代,以方便内存回收。

在这里插入图片描述

方法区(method area)

方法区(也是堆)特点如下:

  1. 方法区根据JAVA虚拟机的规范,有不同的实现。

    ​ JDK7以前是“永久代“

    ​ JDK7部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中

    ​ JDK8是“元数据空间”和堆结合起来

  2. JVM只有一个方法区,被所有线程共享。

  3. 方法区实际也是堆,用于存储类、常量相关的信息。

  4. 存放程序中一些永远不变的信息或唯一的内容。

  5. 常量池主要存放常量:如文本字符串、final常量值。

【示例 】编写 Person 类并分析内存

public class Person{
    String name;
    int age;
    public void show(){
        System.out.println(name);
    }
    public static void main(String[] args){
        //创建p1对象
        Person p1 = new Person();
        p1.age = 23;
        p1.name = "张三";
        p1.show();
        //创建p2对象
        Person p2 = new Person();
        p2.age = 25;
        p2.name = "李四";
        p2.show();
        
        Person p3 = p1;
        Person p4 = p2;
        p4.age = 80;
        System.out.println(p1.age);
    }
}

运行时的内存分配图:
在这里插入图片描述

垃圾回收机制(Garbage Collection)

垃圾回收原理

  • 内存管理

    Java的内存管理很大程度上就是:堆中对象的管理,包括对象空间的分配和释放。

    • 对象空间的分配:使用new关键字创建对象后,会在堆中分配一块内存空间。

    • 对象空间的释放:将对象赋值null即可。

  • 垃圾回收过程

    1. 发现无用对象
    2. 回收内存

    当一个对象没有被任何变量引用时,该对象就成了无用对象。Java通过垃圾回收算法来发现并回收无用对象。

垃圾回收算法

引用计数法

堆中的每个对象都对应一个引用计数器,当有引用指向这个对象时,引用计数器的值加1,而当指向该对象的引用失效时,引用计数器的值减1。当引用计数器减为0时,Java的垃圾回收器就认为该对象是无用对象,对其进行回收。

优点是算法简单,缺点是当出现两个对象互相引用时,即出现“循环引用的无用对象”,该算法就无法识别其为无用对象。

引用可达法(根搜索算法)

把所有的引用关系看做一张图,从一个节点 GC ROOT 开始,寻找对应的引用 节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找 完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

分代回收垃圾机制

分代垃圾回收机制,基于这样一个事实:不同对象的生命周期不一样。因此,不同生命周期的对象可以采用不同的回收算法。将对象分为三种状态:年轻代、年老代、永久代。同时,处于不同状态的对象放到堆中的不同的区域。

包机制(package和import)

package

包(package)相当于文件夹对文件的作用,用于管理类,解决类重名的问题。

package的使用

  • 通常是类的第一句
  • 包的命名规则:域名倒着写。

导入类import

如果要使用其他包的类,就要导入这个包。在本类中可以通过类名直接调用。

注意

  • Java会默认导入java.lang包下的所有类,因此这些类我们可以直接使用。
  • 如果导入后出现两个同名的类,则只能用包名加类名来调用。
静态导入

静态导入(static import):其作用是用于导入指定类的静态属性和静态方法,这样我们就可以直接使用这些静态属性和静态方法。

package com.myb;
import static java.lang.Math.*;
import static java.lang.Math.PI;

public class Tset{
    public static void main(String[] args){
        System.out.println(PI);		//直接调用静态属性
        System.out.print(random());	//直接调用静态方法
    }
}

继承

继承是面向对象的三大特征之一。

继承主要有两个作用:

  1. 代码复用,更加容易实现类的扩展。
  2. 方便建模

继承的实现

关键字extends,字面上的意思是 “扩展” 。所以和C语言不同,Java的继承只能继承一个 “父类” 。

instanceof运算符

instanceof运算符是二元运算符,左边是对象,右边是类。当左边的对象是右边的类或子类所创建的对象时,返回true,否则返回false。

继承使用要点

  1. Java只有单继承
  2. Java的类不能多继承,但接口可以
  3. 子类继承父类,可以得到父类所有的属性和方法,但不见得可以直接访问。
  4. 如果定义一个类时,没有调用extends,则他的父类的:java.lang.Object

父类和子类的相互转换

在Java中我们可以将子类的引用赋值给父类的对象,这个过程称为 ”向上转型“ ,那么这时子类中那些不是从父类继承过来的成员将不再可见,我们可以再通过强制类型转换将这个父类再转换成子类的类型,这个过程称为 ”向下转型“,那些成员又变得可见了!由此可见,将子类引用赋值给父类对象时,Java虚拟机并没有将那些非继承成员丢弃,例如:

Bus bus = new Bus();
Car car = bus;
System.out.println(car.p);

此时编译将产生错误,在car中p是不可见的。

下面将car强转成Bus类,子类的非继承域又可见了。

Bus bus2 = (Bus)car;
System.out.println(bus2.p);

此时编译不会产生错误,可见子类的非继承域并没有被抛弃。

方法重写override

子类继承父类的方法,可以用自身行为替换父类的行为。重写是实现多态的必要条件。

方法重写需要符合以下三个要点:

  1. “全等”:方法名和形参列表相同
  2. ”小于等于“:返回值类型和声明异常类型,子类小于等于父类
  3. “大于等于”:访问权限,子类大于父类
public class Person{
    int age;
    String name;
    ...
    public Person getFriend(){
        return new Person();
    }
}

public Student extends Person{
    int id;
    double scoure;
    ...
    @Override
    public Student getFriend(){
        return new Student();
    }//重写方法,返回值从Person变为Student,合法
    
    @Override
    public Object getFriend(){
        return new Object();
    }//重写不合法,返回值变成了Object,范围变大
}

final关键字

final关键字的作用:

  • 修饰变量:被final修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
final int MAX_SPEED = 120;
MAX_SPEED = 180;//任何试图改变final值都会报错
  • 修饰方法:被final修饰的方法不能被子类重写。但可以被重载!
  • 修饰类:被final修饰的类不能被继承。比如Math、String类。

组合

除了继承,组合也能实现代码复用。组合的核心是 “将父类的对象作为子类的属性”。

public class Test{
	public void main(String[] args){
       	Student stu = new Student("张三",180,"计算机");
        stu.person.rest();
        stu.study();
    }
    
    
    
    class Person{
        String name;
        int height;
        public void rest(){
            System.out.println("休息!");
        }
    }
    
    class Stuent{
        Person person = new Person();//在Student类中定义了一个Person类对象,通过这个对象可以间接拥有它的属性和方法。
        String major;
        
        public Student(String name,int height,String major){
            
            this.person.name = name;
            this.person.height = height;
            this.major = majior;
            
         	this.person.rest();   
        }
        
        public void study(){
            System.out.orintln("学习");
        }
    }
}

继承只能有一个父类,而组合比较灵活,可以定义多个外类的属性,这样相当于继承了多个父类。

对于“is-a"关系,建议用继承,”has-a"关系,建议用组合。

封装

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

封装的实现——使用访问控制符

Java是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。

Java中4中访问控制符分别为:private、default、protected、public。

  • public修饰的类可以被任何类访问
  • protected修饰的类可以被同一个包内的类和子类访问
  • 如果没有访问控制符修饰,默认为default修饰,此时的类只能被所在包的类访问
  • private修饰的类只能在类内访问。

注意:关于protected的两个细节

  1. 若父类和子类在一个包内,则子类可以访问父类的protected成员,也可以访问父类对象的protected成员。
  2. 若父类和子类不在一个包中,则子类只能访问父类的protected成员,不能访问父类对象的protected成员。
    在这里插入图片描述

封装的一般规则:

  • 属性一般使用private访问权限
    • 属性私有后,可以提供相应的get/set方法来访问和修改相关的私有属性,这些方法通常是public修饰的,以提供对属性的读写操作。(boolean属性的get方法命名要以is开头)
  • 方法:一些只用于本类的方法可以有private修饰,希望被其他类调用的方法可以用public修饰。

多态

多态是指方法的多态,不是属性的多态。

多态的实现:继承,方法重写,父类引用指向子类的对象。

父类引用子类的对象时,该父类引用会调用子类重写的方法,这样多态就实现了。

package com.myb.test;

abstract class Animal{
    abstract String shout();
}

class Dog extends Animal{
    @Override
    String shout() {
        return "汪汪汪";
    }
}

class Cat extends Animal{
    @Override
    String shout() {
        return "喵喵喵";
    }
}

class Sheep extends Animal{
    @Override
    String shout() {
        return "咩咩咩";
    }
}

public class Test2 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println(dog.shout());
        Cat cat = new Cat();
        System.out.println(cat.shout());
        Sheep sheep = new Sheep();
        System.out.println(sheep.shout());
    }
}

Object类详解

Object类基本特性

Object类是所以类的父类,所有Java对象都拥有Object类的属性和方法。

toString()方法

Object类中定义有public String tiString()方法,其返回值是String类型。其源码为:

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

根据源码得知,默认返回的是“类名+@+16进制的hashcode”。在打印输出对象时或用字符串连接对象时,会自动调用该对象的toString()方法。

在类中重写toString()方法

class Person{
    int age;
    String name;

    public String toString(){
        return name+"年龄: "+age;
    }
}

“==”和equals比较

“==” 代表比较双方是否相同。如果是基本数据类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。

euqals()用于比较两个对象,默认比较的是两个对象的hashcode。但可以根据自己的要求重写equals方法。

继承数追溯

super关键字

  1. 调用父类中的某一个构造函数,引用格式如下:

    super(实参);
    
  2. 通过super来访问父类中被子类覆盖的方法或属性。

    super.方法名/属性名
    

在子类构造函数中,如果我们不显式地调用super()完成父类的构造,系统会自动调用父类的构造函数,但有时会存在一些问题。

如果我们自定义了父类的构造函数,那么系统提供我们了不带参数的默认构造函数将会被收回,这时在子类定义构造函数时,我们必须显式指定super(),否则系统自动调用的super()会去找那个已经被回收的构造函数,编译器就会报错!

class Person{
    int age;
    String name;
    
    Person(int age,String name){
        this.age = age;
        this.name = name;
    }
    /**系统默认构造函数,当用户自定义构造函数后就会被系统收回。
    Person(){
        
    }
    */
}

class Student extends Person{
    int id;
    
    Student(int id){
        super(int age,String name)//如果不显式调用super(),编译器会报错!
            this.id = id;
    }
    
    Student(int age,String name,int id){
        super(age,name);//如果不显式调用super(),编译器会报错!
        this.id = id;
    }
    
    Student(int age,String name,int id){
        this.age = age;
        this.name = name;
        this.id = id;
    }//想让编译器不报错可以在父类添加默认构造函数Person(){}
}

抽象类

抽象方法

使用abstract修饰的方法,没有方法体,只有声明。

这种定义方法就是一种 “规范”,告诉子类必须要给抽象方法提供具体的实现。

抽象类

包含抽象方法的类就是抽象类

通过抽象类,我们就可以严格限制子类的设计,使子类之间更加通用。

abstract class Animal{
    abstract public void shout();
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
}
class Cat extends Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
}

注意:

  1. 有抽象方法的类必须定义为抽象类
  2. 抽象类不能实例化,不能用new来创建对象
  3. 抽象类可以包含属性、方法和构造方法,可以被子类调用。
  4. 抽象类只能被继承
  5. 抽象方法必须被子类实现

接口interface

为什么需要接口?接口和抽象类的区别?

接口就是比 ”抽象类“ 还抽象的 ”抽象类“ ,可以更加规范的对子类进行约束。全面实现了:规范和具体实现的分离

把一个接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。

一个接口和实现的类不是父子关系,而是实现规则的关系,一个类如果声明一个接口,那么这个类必须严格按照该接口的规则来实现。

声明格式:

[访问修饰符] interface 接口名 [extends 父接口1,父接口2...]{

常量定义;

方法定义;

}

定义接口的说明:

  • 访问修饰符:只能是public或默认。
  • 接口名:和类名采用相同的命名机制。
  • extends:接口可以多继承。
  • 常量:接口中的属性只能是常量,总是public static final修饰,可以不写。
  • 方法:接口中的方法只能是public abstract,也可以不写。
  • 子类:子类通过implements来实现接口中的规范

要点:

  • 接口不能创建实例,这点和抽象类相同
  • 一个类实现了接口,就必须实现接口中所有的方法,并且这些方法只能是public的。
  • 接口中可以包含普通的静态方法、默认方法。(JDK1.8后)

接口的多继承

接口支持多继承,一个接口可以继承多个父接口。

interface A{
    void testa();
}

interface B{
    void testb();
}

interface C extends A,B{
    void testc();
}

public class Test3 {
    public void testc(){

    }
    public void testb(){

    }
    public void testa(){

    }
}

字符串String类

  • String类又称不可变字符序列

  • String位于java.lang包中,Java程序默认导入java.lang包下的所有类

  • Java字符串就是Unicode字符序列,例如“Java”就是4个Unicode字符 ‘J’、’a‘、’v’、‘a’组成的

  • Java没有内置字符串类型,而是在标准Java库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例

String e = "";	//空字符串
String greeting = "hello!";
//e和greeting都是String类的两个实例对像

“+” 连接符

用于连接两个String类型的对象或者一个String类型和其他类型的对象或常量。

String类和常量池

在这里插入图片描述

每各class都有一个运行时的常量池

String g1 = "123";
String g2 = "123";
String g3 = new String("123");
//比较g1、g2、g3是否相等
System.out.println(g1==g2);//true
System.out.println(g1==g3);//false
System.out.println(g1.equals(g3));//true

上述示例说明字符串“123”首次被创建后会存到常量池中,第二次可以直接引用。

String类常用方法:

  • char charAt(int index);返回指定索引处的char值
  • int compareTo(Object o);把一个字符串和另一个字符串比较
  • String concat(String str);将指定字符串连接到此字符串的结尾
  • boolean endsWith(String suffix);测试此字符串是否以指定字符后缀结束
  • boolean equalsIgnoreCase(String str);将此字符串与另一字符串比较,不考虑大小写
  • byte[] getBytes();使用平台默认字符集将此字符串编码成byte,存储到一个新的byte数组中
  • void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);将此字符串的部分字符复制到指定数组中,从指定数组的desBegin索引处开始存储。
  • int indexOf(int ch);返回此字符在字符串中第一次出现的位置
  • int indexOf(String str);返回此子字符串在字符串中第一次出现的位置
  • int length();返回此字符串的长度
  • String replace(char oldChar,char newChar);返回一个新的字符串,

内部类

把一个类放在另一个类的内部定义,成为内部类。

  • 内部类只能让外部类访问,不允许同一个包中的其他类访问。
  • 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员 。但外部类不能访问内部类的内部属性。

非静态内部类

内部类的访问:

  1. 外部类中定义内部类:new Inner()
  2. 外部类以外的地方使用非静态类内部类:Outer.Inner varname = new Outer().new Inner()

静态内部类

定义方式:

static class Inner{

``

}

  • 静态内部类只能访问外部类的静态成员。
  • 静态内部类看做外部类的一个静态成员。

匿名内部类

适合那种只需要使用一次的类。比如:键盘监听操作等等。在安卓开发、awt、swing开发中最常见。

构造方法:

new 父类构造器(实参列表)/实现接口(){

​ ``

}

  • 匿名内部类没有访问修饰符
  • 匿名内部类没有构造方法。它连名字都没有何谈构造方法。

数组

数组的概念

数组的定义

数组是相同类型数据的有序集合。

数组的四个特点:长度确定、类型相同、任意类型、引用类型

数组的声明方式

类型[] arr_name;

类型 arr_name[];

声明的时候没有实例化对象,声明数组的时候并没有数组真正被创建。

构造一个数组必须指定其长度。

数组的初始化

静态初始化

除了用new关键字来产生数组外,还可以直接在定义数组的时候就为数组元素分配空间并赋值

int[] a = {1,2,3};
Man[] man = {new Man(1,1),new Man(2,2)};
动态初始化

数组定义与为数组分配空间并赋值分开进行

int[] a1 = new int[2];
a1[0] = 1;
a1[1] = 2;
数组的默认初始化

数组是对象,它的元素相当于对象的属性;每个元素也可以按照对象属性的初始化来进行默认初始化。

int[] a2 = new int[2];//默认值为:0,0
Boolean[] b = new Boolean[2];//默认值为:false,false
String[] s = new String[2];//默认值为:null,null 

数组常见操作

数组的遍历

  • 通过数组元素下标来遍历数组中的元素

  • f-each循环(增强for循环)

    for-each专门用于读取数组或容器中所有的元素

    定义格式:

    for(类型 实例对象 :数组对象){}

String[] ss = {"aa","bb","cc"};
for(String s:ss){
    ...
}

缺点:只能遍历不能修改元素的值。

数组的拷贝

System.arraycopy(arr1,x1,arr2,x2,length);

x1:源数组开始拷贝的位置 x2:目的数组开始存储的位置

java.util.Arrays类

Array类包含了:排序、查找、填充、打印等常见数组操作。

  • 用**toString()**方法打印数组元素的值,该方法在Arrays类里是静态方法。
import java.util.Arrays;
public class Test{
    public static void main(Stringp[] args){
        int a[] = {1,2};
        System.out.println(a);//打印数组引用的值
        System.out.println(Arrays.toString(a));//打印数组元素的值
    }
}

执行结果为:

[I@15db9742]

[1,2]

  • 用**Arrays.sort()**对数组元素进行排序

  • 用**Arrays.binarySearch()**对有序数组进行二分查找

  • 用**Arrays.fill()**对数组进行填充

多维数组

多维数组可以看出元素是数组的数组。

二维数组的声明:

int[][] a = new int[x][];

其中用new申请数组空间时,需要指明二维数组的行数,但不必指明二维数组的列数!

二维数组的静态初始化:

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

二维数组的动态初始化:

int[][] a = new int[3][];
a[0] = {1,2,3};//错误!没有声明类型
a[0] = new int[]{1,2};
a[1] = new int[]{2,3};
a[2] = new int[]{1,4,5,6}

用二维数组存储表格数据

在这里插入图片描述

观察表格,每行可以用一个一维数组存储,然后把每个一维数组当做对象存储到另一个数组中,构成二维数组。

Object[ ] a1 = {1001,"高淇",18,"讲师","2-14"};
Object[ ] a2 = {1002,"高小七",19,"助教","10-10"};
Object[ ] a3 = {1003,"高小琴",20,"班主任","5-5"};
Object[ ][ ] emps = new Object[3][ ];
emps[0] = a1;
emps[1] = a2;
emps[2] = a3;

用javabean和一维数组存储表格数据

利用java封装特性实现表格类,把表格类的对象存到一维数组中。

public static void main(String[] args) {
        Emp[] emps = new Emp[3];
        emps[0] = new Emp(1001,"高淇",21,"讲师","2-14");
        emps[1] = new Emp(1002,"高小七",19,"助教","10-10");
        emps[2] = new Emp(1003,"高小八",17,"班主任","5-5");
}

class Emp{
    private int id;
    private String name;
    private int age;
    private String job;
    private String hireDate;

    public Emp(int id, String name, int age, String job, String hireDate) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.job = job;
        this.hireDate = hireDate;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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 String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public String getHireDate() {
        return hireDate;
    }
    public void setHireDate(String hireDate) {
        this.hireDate = hireDate;
    }
}

Comparable接口

Comparable接口只有一个方法:

public int compareTo(Object obj)

obj是要比较的对象

Comparable接口就是用来定义排序规则的。

当对象与obj这个对象比较,如果大于返回1,小于返回-1,等于返回0(此处的1可以是正整数,-1可以是负整数)。

compareTo方法的代码比较固定:

public int compareTo(Object o){
    Emp e = (Emp)o;
    if(this.age<o.age){
        return -1;
    }
    if(this.age>o.age){
        return 1;
    }
    return 0;
}//此方法实现了两个表格类对象比较大小的规则,自定义的比较规则是按照年龄的大小。

常用类

基本数据类型的包装类

为了将基本数据类型和对象之间实现相互转化,Java为每个基本数据提供了相应的包装类。

Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这八个基本数据类型对应的类统称为“包装类”。

包装类

包装类位于java.lang包中,八种包装类和基本数据类型的对应关系:

基本数据类型包装类
byteByte
booleanBoolean
shortShort
charCharacter
intInteger
longLong
floatFloat
doubleDouble

包装类创建对象的方式跟其他类一样

Integer num1 = new Integer(0);//手动创建对象,现实情况不需要
Integer num2 = Integer.valueOf(1);//手动装箱,现实情况不需要

包装类的转换机制:

Integer num1 = new Integer(1);//基本数字类型向包装类转换
int num2 = num1.intValue();//包装类向基本数据类型转换

数字型包装类继承了Number类,Number是抽象类,它的抽象方法所有包装类都提供了实现。Number类中的抽象方法有:intValue()、longValue()、floatValue()、doubleValue(),意味着数字型包装类都可以互相转型。

自动装箱、自动拆箱机制

Java为了方便我们使用,以及出于其他目的如性能调优,给我们提供了自动装箱、拆箱机制。这种机制简化了基本类型和包装类型的转换。

Integer num1 = 1;//自动装箱
int num2 = num1;//自动拆箱

可见,java编译器帮我们完成了转换操作。

另外,使用new关键字和valueOf()方法创建对象是有区别的。

new关键字和valueOf()

Integer num3 = 10;
Integer num4 = 10;
Integer num5 = new Integer(20);
Integer num6 = new Integer(20);
Integer num7 = 128;
Integer num8 = 128;
System.out.println(num3==num4)+" "+num3.equals(num4);
System.out.println(num5==num6)+" "+num5.equals(num6);
System.out.println(num7==num8)+" "+num7.equals(num8);

运行结果为:

true true

false true

false true

我们看下它的反编译代码:

Integer integer = Integer.valueOf(10);
Integer integer1 = Integer.valueOf(10);
Integer integer2 = new Integer(20);
Integer integer3 = new Integer(20);
Integer integer4 = Integer.valueOf(128);
Integer integer5 = Integer.valueOf(128);
System.out.println((new StringBuilder()).append(integer == integer1).append("\t").append(integer.equals(integer1)).toString());
System.out.println((new StringBuilder()).append(integer2 == integer3).append("\t").append(integer2.equals(integer3)).toString());
System.out.println((new StringBuilder()).append(integer4 == integer5).append("\t").append(integer4.equals(integer5)).toString());

首先,我们查看Integer的valueOf()方法的源码

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

再查看Integer的内部类IntegerCache的cache数组成员:low、high成员

        static final int low = -128;
        static final int high;
        static final Integer cache[];
 
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
 
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
 
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

可以发现,只要Integer类第一次被使用到,Integer的静态内部类就会被加载,加载的时候会创建-128到127的Integer对象,同时创建一个cache数组来缓存这些对象。当使用valueOf()方法创建对象时,就直接返回已缓存的对象,也就是说不会再创建新的对象(前提是创建的值合法);当使用new关键字创建对象或者用valueOf()方法创建小于-127大于127的对象时,就会创建一个新的对象。

这时候就能理解只有num3==num4的值是true,其他两个的值都是false

接着,我们再看看源码中Integer类的equals()方法的实现

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

可见,equals()方法比较的是Integer对象的值,并不关心是否属于同一对象。

所以上述三次调用equals()返回值都是true

在8种包装类中,有缓存区的有Character、Byte、Short、Integer、Long,都是-128到127的缓存范围。

为什么需要包装类?

为什么需要包装类?有了包装类为什么还要保留基本数据类型?

首先,java语言是一个面向对象的语言,但java中的基本数据类型不是面向对象的,将每个基本数据类型设计一个对应的类进行代表,这种方式增强了java面向对象的性质。

其次,如果仅仅有基本数据类型,那么在使用的时候存在很多不便,很多地方需要使用对象而不是基本数据类型。比如,在集合类中我们需要的元素类Object类型,无法将int、double等类型放进去。而集合类的存在使得向集合中传入数值成为可能。包装类弥补了基本数据类型的不足。

此外,包装类还为基本数据类添加了属性和方法,丰富了基本数据类型的操作。如当我们想知道int的取值范围的时候,我们需要进行运算,但是有了包装类,我们可以直接使用Integer.MAX_VALUE即可。

//求int的最大值
int max = 0;
int flag = 1;
for (int i=0; i<31; i++) {
	max += flag;
	flag = flag << 1;
}
System.out.println(max +"	"+ Integer.MAX_VALUE); //2147483647      2147483647

为什么要保留基本数据类型?

我们知道在Java语言中,用new关键字创建的对象是存储在堆区中,我们通过栈中的引用来使用这些对象,所以对象本身是很消耗资源的。如果我们常用的基本数据类型也封装成对象,在大量使用的时候很容易浪费大量资源,这就是java保留基本数据类型的原因。

字符串相关类

StringBuffer和StringBuilder

StringBuffer和StringBuilder都是可变字符序列。

  • StringBuffer 线程安全,做线程同步检查,效率较低。
  • StringBuilder 线程不安全,不做线程同步检查,因此效率高,建议采用该类。

常用方法列表:

  • 重载的public StringBuilder append(…)方法

    为该字符串添加字符序列,仍然返回自身对象。

  • public StringBuilder delete(int start,int end)

    删除从start开始到end-1结束的字符序列,仍然返回自身对象。

  • public StringBuilder deleteCharAt(int index)

    移除此序列指定位置的字符,仍然返回自身对象。

  • 重载的public StringBuilder insert()

    在指定位置插入指定字符(串)序列,仍然返回自身对象。

  • public String toString()

    返回此序列中数据的字符串表示形式

  • 和String类相似的方法

    public int indexOf(String str)

    public int indexOf(String str,int fromIndex)

    public String substring(int start)

    public String substring(int start,int end)

    public int length()

    char charAt(int index)

不可变和可变字符序列使用陷阱

String的使用陷阱

String已经初始化后,就不会再改变其内容了。对String字符串的操作实际上是对其副本的操作,原来的字符串没有改变。比如:

String s = “a”;

s = s + “b” ;

此时会产生一个新的s,实际上就是"a+b",原来的 s = “a” 被忽略但仍然留存在内存中。如果多次执行这些改变字符串的操作,会导致大量副本字符串对象被留存在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能。

相反,StringBuffer和StringBuilder类是对原字符串本身操作,不会产生副本,因此可以提高效率。

时间相关类

时间是一维的,我们可以用一把刻度尺来表示和度量时间,在计算机中,我们把1970年1月1日00:00:00定为基准时间,每个度量单位是毫秒。

我们用long类型的变量来表示时间,long类型能表示的时间范围是从基准时间前后几亿年,所以完全足够用。

Date时间类(java.util.Date)

在标准java类库中包含了一个Date类。它的对象表示一个特定的瞬间,精确到毫秒。

  • Date()分配一个Date对象,并初始化此对象为当前的日期和时间,可以精确到毫秒。

  • Date(long date)分配Date对象并初始化,表示从基准时间到当前时间以来的毫秒数。

  • boolean equals(Object o)比较两个日期的想等性。

  • long getTime()返回当前时间的毫秒数

  • String tostring()把此Date对象转换为以下形式的String:

    dow mon dd hh:mm:ss zzz yyyy 其中:dow 是一周中的某一天。

Date类的使用:

Date d = new Date();
System.out.println(d.getTime());
Date d2 = new Date(1000L*3600*24*365*100);//距离1970年100年的时间
System.out.println(d2);

DateFormat类和SimpleDateFormat类

DateFormat类:把时间对象转化为指定格式的字符串。反之,把指定格式的字符串转化为时间对象。

DateFormat是抽象类,一般使用它的子类SimpleDateFormat类来实现。

//new出SimpleDateFormat对象
SimpleDateFormat s1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss") ;
SimpleDateFormat s2 = new SimpleDateFormat("yyyy-MM-dd") ;
//将时间对象转换为字符串格式的时间
String daytime = s1.format(d);
System.out.println(daytime);
System.out.println(s2.format(new Date()));
//将指定格式的字符串转化为时间对象,字符串格式需要和指定格式一致
String time = "2049-10-1";
Date date = s2.parse(time);
System.out.println("date1: "+date);
time = "2049-10-1 20:15:30";
date = s1.parse(time);
System.out.println("date2: "+date);

格式化字符的具体含义:

字母日期或时间元素表示示例
GEra标志符TextAD
yYear1996,96
M月份MonthJuly;Jul;07
w年中的周数Number27
W月中的周数Number2
D年中的天数Number189
d月中的天数Number10
F月份中的星期Number2
E星期中的天数TextTuesday;Tue
aAm/pmTextPM
H一天中的小时数(0-23)Number0
k一天中的小时数(1-24)Number24
Kam/pm 中的小时数(0-11)Number0
ham/pm 中的小时数(1-12)Number12
m小时中的分钟数Number30
s分钟中的秒数Number55
S毫秒数Number978
z时区General time zonePST; GMT-08:00
Z时区RFC 822 time zone0800

时间格式化类为我们提供了很方便的获得不同时间的方法。比如:获取今天是今年的第几天:

SimpleDateFormat s1 = new SimpleDateFormat("D");
String daytime = s1.format(new Date());
System.out.println(daytime);

Calendar日历类

Calendar类是一个抽象类,为我们提供了关于日期计算的功能,比如:年、月、日、时、分、秒的展示和计算。

GregorianCalendar是Calendar的子类,表示公历。

注意月份的表示,一月是 0,二月是 1,以此类推,12 月是 11。

// 得到相关日期元素
GregorianCalendar calendar = new GregorianCalendar(2049, 9, 1, 22, 10, 50);
int year = calendar.get(Calendar.YEAR); // 打印:2049
int month = calendar.get(Calendar.MONTH); // 打印:9
int day = calendar.get(Calendar.DAY_OF_MONTH); // 打印:1
int day2 = calendar.get(Calendar.DATE); // 打印:1
// 日:Calendar.DATE 和 Calendar.DAY_OF_MONTH 同义
int date = calendar.get(Calendar.DAY_OF_WEEK); // 打印:1
// 星期几 这里是:1-7.周日是 1,周一是 2,。。。周六是 7
System.out.println(year);
System.out.println(month);
System.out.println(day);
System.out.println(day2);
System.out.println(date);
// 设置日期
GregorianCalendar calendar2 = new GregorianCalendar();
calendar2.set(Calendar.YEAR, 2049);
calendar2.set(Calendar.MONTH, Calendar.OCTOBER); // 月份数:0-11
calendar2.set(Calendar.DATE, 1);
calendar2.set(Calendar.HOUR_OF_DAY, 10);
calendar2.set(Calendar.MINUTE, 20);
calendar2.set(Calendar.SECOND, 23);
printCalendar(calendar2);
// 日期计算
GregorianCalendar calendar3 = new GregorianCalendar(2049, 9, 1, 22, 10, 50);
calendar3.add(Calendar.MONTH, -7); // 月份减 7
calendar3.add(Calendar.DATE, 7); // 增加 7 天
printCalendar(calendar3);
// 日历对象和时间对象转化
Date d = calendar3.getTime();
GregorianCalendar calendar4 = new GregorianCalendar();
calendar4.setTime(new Date());
}
static void printCalendar(Calendar calendar) {
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int date = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期几
String week = "" + ((date == 0) ? "日" : date);
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
System.out.printf("%d 年%d 月%d 日,星期%s %d:%d:%d\n", year, month, day, week, hour, minute, second);

其他常用类

异常机制

软件程序在运行过程中,可能会遇到一些异常,例如打开文件是文件不存在或者格式不对、程序运行时内存满了等等,我们把这些异常成为:exception,所以又叫例外。

异常机制是为了让程序遇到异常时做出合理的处理,并安全的退出,而不至于程序崩溃。

试想,如果没有异常机制,我们设计程序时必须事先考虑到所有可能出现的意外情况,这时候会用到大量判断语句,造成代码冗长,可读性差。

异常(Exception)的概念

异常是指程序运行过程中出现的非正常现象,例如除数为0、需要处理的文件不存在、数组下标越界等。

在Java中的异常处理机制中,引进了很多用来描述和处理异常的类,成为异常类。异常类中定义了异常信息和对异常的处理方法。

Java采用面向对象的方式来处理异常。处理过程:

  • **抛出异常:**在执行一个方法时,如果发生了异常,则这个方法生成代表该异常的一个对象,停止当前执行路经,并把异常提交给JRE。
  • **捕获异常:**JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。

异常分类

Java中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能满足需要,就需要自己创建自定义异常类来解决实际问题。

java对异常类进行了分类,不同类型的异常分别用不同的java类表示,所有异常的根类为:java.lang.Throwable,Throwable下面有派生了两个子类:Error和Exception。Java异常类的层次结构如图所示:

在这里插入图片描述

Error

Error是程序无法处理的错误,表示运行应用程序出现较严重的问题。大多数错误与代码编 写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源 时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机一般会选择线程终止。

Error声明系统JVM已经处于不可恢复的崩溃状态中。

Error的直接子类:

在这里插入图片描述

Error与Exception的区别

  • 车子在路上开着,前面堵车了,需要停车。这叫一个异常。
  • 车子在路上开着,突然抛锚了,车子被迫停下了。这叫错误。

Exception

Exception是程序本身能够处理的异常。

Exception类是所有异常类的父类。通常的Java异常可分为:

  1. RuntimeException 运行时异常
  2. CheckedException 已检查异常
RuntimeException

派生于RuntimeException的异常,如被0除、数组下标越界、空指针异常等。为避免这些异常,通常需要增加逻辑处理来避免这些异常。

【示例】NullPointerException 异常

public class Test4 {
	public static void main(String[ ] args) {
		String str=null;
		System.out.println(str.charAt(0));
	}
}

执行结果如图所示:

在这里插入图片描述

解决空指针异常,通常是增加非空判断:

public class Test4 {
	public static void main(String[ ] args) {
		String str=null;
		if(str!=null){
			System.out.println(str.charAt(0));
		}
	}
}

【示例】ClassCastException 异常

Animal a = new Dog();
Cat c = (Cat)a;

执行结果如图所示:
在这里插入图片描述

错误原因:不能将狗类强转成猫类。

解决ClassCastException的典型方式:

Animal a = new Dog();
if(a instanceof Cat){
    Cat c = (Cat)a;
}

【示例】NumberFormatException 异常

数字格式化异常的解决,可以引入正则表达式判断是否为数字:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test7 {
	public static void main(String[ ] args) {
		String str = "1234abcf";
		Pattern p = Pattern.compile("^\\d+$");
		Matcher m = p.matcher(str);
		if (m.matches()) { // 如果 str 匹配代表数字的正则表达式,才会转换
			System.out.println(Integer.parseInt(str));
		}
	}
}
CheckedException

已检查异常是指在编译时就必须处理,否则无法通过编译。

CheckedException的处理方式:

  1. 使用 “try/catch” 捕获异常
  2. 使用 “throws” 声明异常
捕获异常

try/catch语句用于处理异常,这些异常通常是程序员造成的编码错误或错别字,也可能是语言中缺少的功能,以及一些语法错误。

如果没有try/catch的话,就有可能出现程序崩溃,而try/catch则可以保证程序的正常运行。

例如:当除数出现0时,编译器不会报错,如果没有try/catch的话,程序直接崩溃。用try/catch可以跳过该异常,让程序继续执行下去,并且输出异常信息。

try语句包括的代码是异常捕获并处理的范围,在执行过程中,只要try语句中的代码出现异常,就会跳过后面的代码。异常代码会产生并抛出一种或几种类型的异常对象,它后面的catch语句会对这些异常做出相应的处理。

一个try语句后面必须带有至少一个catch语句或一个finally语句块。

finally语句:

  • 不管是否发生异常,都要执行。
  • 通常在finally中挂壁已打开的资源。

try-catch-finally 语句块的执行过程详细分析:

程序首先执行可能发生异常的 try 语句块。如果 try 语句没有出现异常则执行完后跳至 finally 语句块执行;如果 try 语句出现异常,则中断执行并根据发生的异常类型跳至相应的 catch 语句块执行处理。catch 语句块可以有多个,分别捕获不同类型的异常。catch 语句 块执行完后程序会继续执行 finally 语句块。finally 语句是可选的,如果有的话,则不管是 否发生异常,finally 语句都会被执行。

在IDEA 中,使用:ctrl+alt+t 快捷键自动增加try-catch代码块。

throws(声明异常)
  1. CheckedException产生时,不一定要立刻处理它,可以把异常throws,由调用者处理。
  2. 一个方法抛出多个已检查异常,就必须在方法首部列出所有异常。

【示例】异常处理的典型代码(声明异常抛出 throws)

package com.bjsxt;
        import java.io.FileNotFoundException;
        import java.io.FileReader;
        import java.io.IOException;
public class Test9 {
    public static void main(String[ ] args) {
        try {
            readFile("joke.txt");
        } catch (FileNotFoundException e) {
            System.out.println("所需文件不存在!");
        } catch (IOException e) {
            System.out.println("文件读写错误!");
        }
    }
    public static void readFile(String fileName) throws
            FileNotFoundException, IOException {
        FileReader in = new FileReader(fileName);
        int tem = 0;
        try {
            tem = in.read();
            while (tem != -1) {
                System.out.print((char) tem);
                tem = in.read();
            }
        } finally {
            if(in!=null) {
                in.close();
            }
        }
    }
}

注意:

方法重写中声明异常原则:子类重写父类方法时,如果父类方法有声明异常,那么子类 声明的异常范围不能超过父类声明的范围。

try-with-resource 自动关闭 AutoClosable 接口的资源

JAVA 中,JVM 的垃圾回收机制可以对内部资源实现自动回收,给开发者带 来了极大的便利。但是 JVM 对外部资源(调用了底层操作系统的资源)的引用却 无法自动回收,例如数据库连接,网络连接以及输入输出 IO 流等。这些连接就 需要我们手动去关闭,不然会导致外部资源泄露,连接池溢出以及文件被异常占 用等。 JDK7 之后,新增了“try-with-resource”。它可以自动关闭实现了 AutoClosable 接口的类,实现类需要实现 close()方法。”try-with-resources 声明”,将 try-catch-finally 简化为 try-catch,这其实是一种语法糖,在编 译时仍然会进行转化为 try-catch-finally 语句。

自定义异常

在现实中我们难免会遇到各种异常,有些异常是标准异常类无法解决的,这时候就需要我们自定义异常类。

自定义异常类只需从Exception类或者它的子类派生一个子类即可。

自定义异常如果继承Exception类,则为CheckedException异常,必须对其进行处理;如果不想处理,则必须让自定义异常类继承运行时异常RunTimeException类。

习惯上,自定义异常类应该包含两个构造器,一个是默认构造器,另一个是带有详细信息的构造器。

【示例】自定义异常类

/**IllegalAgeException:非法年龄异常,继承 Exception 类*/
public class IllegalAgeException extends Exception {
	//默认构造器
	public IllegalAgeException() {
        
	}
	//带有详细信息的构造器,信息存储在 message 中
	public IllegalAgeException(String message) {
		super(message);
	}
}

【示例】自定义异常类的使用

class Person {
    private String name;
    private int age;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) throws IllegalAgeException {
        if (age < 0) {
            throw new IllegalAgeException("人的年龄不应该为负数");
        }
        this.age = age;
    }
    public String toString() {
        return "name is " + name + " and age is " + age;
    }
}
public class TestMyException {
    public static void main(String[ ] args) {
        Person p = new Person();
        try {
            p.setName("Lincoln");
            p.setAge(-1);
        } catch (IllegalAgeException e) {
            e.printStackTrace();
        }
        System.out.println(p);
    }
}

执行结果:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值