java基础
java基础知识图解
软件开发
软件开发
软件,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统软件和应用软件之分。
人机交互方式
图形化界面(Graphical User Interface GUI):这种方式简单直观,使用者易于接受,容易上手操作。
命令行方式(Command Line Interface CLI):需要有一个控制台,输入特定的指令,让计算机完成一些操作。
应用程序=算法+数据结构
命令行
常用的DOS命令
dir : 列出当前目录下的文件以及文件夹
md : 创建目录
rd : 删除目录
cd : 进入指定目录
cd.. : 退回到上一级目录
cd: 退回到根目录
del : 删除文件
exit : 退出 dos 命令行
补充:echo javase>1.doc 创建新文件
常用快捷键
← →:移动光标
↑ ↓:调阅历史操作命令
Delete和Backspace:删除字符
java语言
java版本历史迭代
SUN(Stanford University Network,斯坦福大学网络公司 ) 1995年推出的一门高级编程语言。
1991年 Green项目,开发语言最初命名为Oak (橡树)
1996年,发布JDK 1.0,约8.3万个网页应用Java技术来制作
2004年,发布里程碑式版本:JDK 1.5,为突出此版本的重要性,更名为JDK 5.0
2009年,Oracle公司收购SUN,交易价格74亿美元
2014年,发布JDK 8.0,是继JDK 5.0以来变化最大的版本
Java技术体系平台
Java在各领域的应用
企业级应用:主要指复杂的大企业的软件系统、各种类型的网站。Java的安全机制以及它的跨平台的优势,使它在分布式系统领域开发中有广泛应用。应用领域包括金融、电信、交通、电子商务等。
Android平台应用:Android应用程序使用Java语言编写。Android开发水平的高低很大程度上取决于Java语言核心能力是否扎实。
大数据平台开发:各类框架有Hadoop,spark,storm,flink等,就这类技术生态圈来讲,还有各种中间件如flume,kafka,sqoop等等 ,这些框架以及工具大多数是用Java编写而成,但提供诸如Java,scala,Python,R等各种语言API供编程。
移动领域应用:主要表现在消费和嵌入式领域,是指在各种小型设备上的应用,包括手机、PDA、机顶盒、汽车通信设备等。
Java语言运行机制及运行过程
Java语言的特点
跨平台性
Java两种核心机制
Java虚拟机 (Java Virtal Machine)
垃圾收集机制 (Garbage Collection)
核心机制—Java虚拟机
JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指 令,管理数据、内存、寄存器。
对于不同的平台,有不同的虚拟机。
只有某平台提供了对应的java虚拟机,java程序才可在此平台运行
Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”
核心机制—垃圾回收
不再使用的内存空间应回收—— 垃圾回收。
1、在C/C++等语言中,由程序员负责回收无用内存。
2、Java 语言消除了程序员回收无用内存空间的责任:它提供一种系统级线程跟踪存储空间的分配情况。并在JVM空闲时,检查并释放那些可被释放的存储空间。
垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预。
Java程序还会出现内存泄漏和内存溢出问题吗?Yes!
Java语言的环境搭建
什么是JDK,JRE
下载并安装JDK
官方网址:www.oracle.com
配置环境变量
开发HelloWorld
开发HelloWorld
1.将编写的java代码保存在以‘.java’结尾的源文件中
class HelloChina{
public static void main(String[] args){
//args:arguments参数;可以更改
System .out.println("Hello,World!");
//输出语句System .out.println()先输出后换行
}
}
2.通过编译工具javac.exe编译为字节码文件,格式为javac 源文件名.java
3.通过java.exe运行字节码文件,格式为java 字节码文件名
注意:
1、在一个源文件中可以声明多个类(class),但是只能最多有一个类声明为public的。而且,要求声明为public的类的类名必须与源文件名相同。
2、程序的入口是main()方法,格式是固定的。
3、每一个执行语句都以分号;结束。
4、编译以后会生成一个多个字节码文件,字节码文件名与源文件中声明的类名相同。
注释(Comment)
用于注解说明解释程序的文字就是注释。
/*
1、java规定了三种注释:
单行注释 多行注释 文档注释(java特有)
2、 单行注释和多行注释的作用:
a.对所写的程序进行解释说明,增强可读性。 b.可以调试所写的代码
3、特点:
单行注释和多行注释的内容不参与编译。(编译后生成的 字节码文件不包含注释信息。)
4、多行注释不可以嵌套使用。
*/
class HelloJava {
/*
多行注释:
如下的main方法是程序的入口!
main的格式是固定的!
*/
public static void main(String[] args) {
//单行注释:如下的语句表示输出到控制台
System.out.println("Hello World!");
}
}
文档注释
文档注释的作用:
注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形 式体现的该程序的说明文档。
使用/*文档注释/的格式
使用javadoc.exe解析
dos命令行解析方法:
javadoc -d myHello -author -version HelloJava.java 其中的myHello为文件名,HelloJava.java为源文件名
Java API文档
API (Application Programming Interface,应用程序编程接口)是 Java 提供 的基本编程接口)(类库)。
下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html
关键字与保留字
关键字
定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)
特点:关键字中所有字母都为小写
官方地址:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html
保留字
Java保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使用。自己命名标识符时要避免使用这些保留字。
goto 、const
标识符(Identifier)
Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符。
凡是自己可以起名字的地方都叫标识符。
定义合法标识符规则:
- 由26个英文字母大小写,0-9 ,_或 $ 组成
- 数字不可以开头。
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java中严格区分大小写,长度无限制。
- 标识符不能包含空格。
Java中的名称命名规范
包名:多单词组成时所有字母都小写:xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
注意1:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
注意2:java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。
变量
变量的概念:
内存中的一个存储区域
该区域的数据可以在同一类型范围内不断变化
变量是程序中最基本的存储单元。包含变量类型、变量名和存储的值 。
变量的作用:
用于在内存中保存数据
使用变量注意:
- Java中每个变量必须先声明,后使用
- 使用变量名来访问这块区域的数据
- 变量的作用域:其定义所在的一对{ }内
- 变量只有在其作用域内才有效
- 同一个作用域内,不能定义重名的变量
声明变量
语法:<数据类型> <变量名称>
例如:int var;
变量的赋值
语法:<变量名称> = <值>
例如:var = 10;
声明和赋值变量
语法: <数据类型> <变量名> = <初始化值>
例如:int var = 10;
class VariableTest {
public static void main(String[] args){
//变量的定义
int myAge=12;
//变量的使用
System.out.println(myAge);
//变量的声明intmyNumber;
//变量的赋值
myNumber=1997;
//变量的使用
System.out.println(myNumber);
}
}
输出
12
1997
变量的分类-按数据类型
对于每一种数据都定义了明确的具体数据类型(强类型语言),在内存中分配了不同大小的内存空间。
整数类型:byte、short、int、long
Java各整数类型有固定的表数范围和字段长度,不受具体OS的影响,以保 证java程序的可移植性。
java的整型常量默认为 int 型,声明long型常量须后加‘l’或‘L’
java程序中变量通常声明为int型,除非不足以表示较大的数,才使用long
class VariableTest1{
public static void main(String[] args){
byte b1=24;
byte b2=-128;
//b2=128; 编译不通过,byte类型-128~127
System.out.println(b1);
System.out.println(b2);
short s1=128;
int s2=1234;
long s3=12345678L;
System.out.println(s1);
System.out.println(s2);
//声明:long型变量,必须以“l”或“L”结尾
System.out.println(s3);
}
}
输出
24
-128
128
1234
12345678
浮点类型:float、double
与整数类型类似,Java 浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响。
浮点型常量有两种表示形式:
十进制数形式:如:5.12 512.0f .512 (必须有小数点)
科学计数法形式:如:5.12e2 512E2 100E-2
float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。
double:双精度,精度是float的两倍。通常采用此类型。
class VariableTest1{
public static void main(String[] args){
double s1=123.3;
System.out.println(s1);
//定义float类型值的末尾要以‘f’或‘F’结尾
float s2=12.3F;
System.out.println(s2);
}
}
输出
123.3
12.3
字符类型:char
char 型数据用来表示通常意义上“字符”(2字节)
Java中的所有字符都使用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符。
字符型变量的三种表现形式:
- 字符常量是用单引号
‘ ’
括起来的单个字符。例如:char c1 = ‘a’; char c2= ‘中’; char c3 = ‘9’; - Java中还允许使用转义字符
\
来将其后的字符转变为特殊字符型常量。例如:char c3 = ‘\n’;\n
表示换行符。 - 直接使用 Unicode 值来表示字符型常量:‘\uXXXX’。其中,XXXX代表一个十六进制整数。如:\u000a 表示\n。
char类型是可以进行运算的。因为它都对应有Unicode码。
class VariableTest1{
public static void main(String[] args){
char c1='a';
System.out.println(c1);
char c2='中';
System.out.println(c2);
}
}
输出a中
转义字符
布尔类型:boolean
boolean类型数据只允许取值true和false,无null。
boolean 类型用来判断逻辑条件,一般用于程序流程控制:
if条件控制语句;
while循环控制语句;
do-while循环控制语句;
for循环控制语句;
变量的分类-按声明的位置的不同
在方法体外,类体内声明的变量称为成员变量。
在方法体内部声明的变量称为局部变量。
二者在初始化值方面的异同:
同:都有生命周期
异:局部变量除形参外,需显式初始化。
基本数据类型转换
前提:boolean类型不能与其它数据类型运算。
自动类型提升
容量小的类型自动转换为容量大的数据类型。数据类型按容 量大小排序为:(容量的大小表示数的范围的大小)
byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。
class VariableTest2{
public static void main(String[] args) {
byte b1=2;
int i1=129;
//byte i2 = b1 + i1; 解析不通过
inti2=b1+i1; //使用容量较大的类型int
System.out.println(i2);
}
}
输出
131
强制类型转换
自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符:(),但可能造成精度降低或溢出,格外要注意。
class VariableTest2{
public static void main(String[] args) {
double d1=12.3;
int i1= (int)d1; //使用强转符(),截断操作损失精度
System.out.println(i1);
}
}
输出
12
字符串类型:String
String不是基本数据类型,属于引用数据类型,声明String变量的是,使用一对双引号””
一个字符串可以串接另一个字符串,也可以直接串接其他类型的数据。例如:
str=str+“xyz";
intn=100;
str=str+n;
String类型可以和八种基本数据类型进行运算,且只能是连接运算,运算结果仍然是String类型。
class VariableTest2{
public static void main(String[] args){
String numberStr="学号:";
int number=1997;
String info=numberStr + number;
System.out.println(numberStr + number);
}
}
输出学号:1997
String类型索引、长度
String a="12345";
//获取字符串的长度
int long=a.length();
//索引指定位置的字符
char b=a.charAt(0);
获取指定索引的数字,并且用来运算
可以使用字符减去字符0,获得编码的差
String a="12345";
char b=a.charAt(2);
int num=b - '0';
输出
3
比较字符串是否相等
String a="yes";
String b="no";
boolean c=a.equals(b);
System.out.println(c);
输出
false
进制
所有数字在计算机底层都以二进制形式存在。
二进制(binary):0,1 ,满2进1.以0b或0B开头。
十进制(decimal):0-9 ,满10进1。
八进制(octal):0-7 ,满8进1. 以数字0开头表示。
十六进制(hex):0-9及A-F,满16进1. 以0x或0X开头表示。此处的A-F不区分大小写。 如:0x21AF +1= 0X21B0
二进制转换十进制,以及负数的原码、反码、补码
计算机底层都以补码的方式存储数据!
运算符
运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
算术运算符
赋值运算符
符号:=
当“=”两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理。
支持连续赋值。
比较运算符
逻辑运算符
变量的类型全部都是boolean类型的
区分&和&&:
相同点:
1、&和&&运算的结果相同
2、当符号左边是true时,都会运行符号右边的运算
不同点:
当符号左边是false时,&符号右边的运算继续执行,&&右边的运算不再执行。
区别 |和||:
相同点:
1、|和||运算的结果相同
2、当符号左边是false时,都会执行符号右边的运算
不同点:
当符号左边是true时,|符号右边的运算继续执行,||右边的运算不再执行。
位运算符
结论:
1、位运算符操作的都是整形数据
1、<<每向左移一位,相当于*2
2、>>每向右移一位,相当于/2
面试题:
1、最高效的计算2*8? 答:2 << 3或8 << 1
2、int num1 = 10;
int num2 = 20; // 交换两个变量的值。
class AriTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
int temp = num1;
num1 = num2;
num2 = temp;
System.out.println("num1 = " + num1 + ",num2 = " + num2);
}
}
输出
num1 = 20,num2 = 10
或者使用位运算符^,k = m^n,m= k^n = (m^n)^n
class AriTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
inttemp = num1 ^ num2;
num2 = temp ^ num2;
num1 = temp ^ num1;
System.out.println("num1 = " + num1 + ",num2 = " + num2);
}
}
输出
num1 = 20,num2 = 10
三元运算符
格式:
说明:
1、条件表达式的结果为boolean类型
2、根据条件表达式的结果为ture或false,决定执行表达式1,还是表达式2
如果结果为true,则执行表达式1
如果结果为false,则执行表达式2
3、表达式1和表达式2类型要求是一致的
4、三元运算符可以嵌套使用
5、凡是可以使用三元运算符的地方,都可以改写为if…else…
运算符的优先级
! > & > | > && > ||
程序流程控制
从键盘获取不同类型的变量:
使用Scanner类,具体实现步骤:
1、导包:import java.util.Scanner;
2、Scanner的实例化:Scanner scan = new Scanner(System.in);
3、调用Scanner类的相关方法,来获取指定类型的变量
注意:需要根据相应的方法输入指定类型的值,如果输入的数据类型与要求的数据类型不匹配时,会有异常:==InputMisMatchException==。
使用Scanner类获取int类型的变量:
import java.util.Scanner;
class ScannerTest{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
System.out.println(num);
}
}
随机数的生成
double value = Math.random();
生成的随机数double类型value的范围是:【0.0,1.0)
公式:求【a,b】-->(int)(Math.random()*(b - a + 1) + a)
顺序结构
分支结构
分支语句1: if-else结构
if-else使用说明
1、条件表达式必须是布尔表达式(关系表达式或逻辑表达式)、布尔变量
2、语句块只有一条执行语句时,一对{}可以省略,但建议保留
3、if-else语句结构,根据需要可以嵌套使用
4、当if-else结构是“多选一”时,最后的else是可选的,根据需要可以省略
5、当多个条件是“互斥”关系时,条件判断语句及执行语句间顺序无所谓
当多个条件是“包含”关系时,“小上大下 / 子上父下”
分支语句2: switch-case结构
说明
1、根据switch中表达式中的值,依次匹配各个case的常量,一旦匹配成功,则进入相应的case结构中,调用其执行语句,当调用完执行语句完以后,则仍然继续向下执行其他case语句中的执行语句,直到遇到break关键字或者switch-case结构末尾结束为止。
2、break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构。
3、switch-case结构中的表达式,只能是如下的六种数据类型之一:byte、short、char、int、枚举类型(JDK5.0新增)、String类型(JDK7.0新增)。
4、case之后只能声明常量,不可以声明范围。
5、break关键字在switch结构中是可选的。
6、default类似与if-else中的else
default结构是可选的。
7、如果switch中的多个case的执行语句相同,那么可以考虑合并。
switch-case和if-else的选择:
1、凡是可以使用switch-case的结构,都可以使用if-else
2、当既可以使用switch(表达式取值情况不多时)又可以使用if时,优先选择switch。
循环结构
在某些条件满足的情况下,反复执行特定代码的功能
for循环
while循环
初始化部分出了while循环以后仍可以调用
do-while循环
说明:
1、运行的时候会先执行一次循环体部分和迭代部分。
无限循环格式
for循环
for ( ; ; ){ }
while循环
while(true){ }
do-while循环
do{ }while(true);
结束循环的两种方式
1、循环条件部分返回false
2、在循环体中,执行break
嵌套循环(多重循环)
将一个循环放在另一个循环体内,就形成了嵌套循环。
设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
外层循环控制行数,内层循环控制列数
优化:计算程序运行时间
衡量功能代码的优劣:
1、保证代码的功能正确性;
2、代码的可读性;
3、健壮性;
4、高效率与低存储(算法的好坏):时间复杂度、空间复杂度
//获取当前开始时间距离1970-01-01 00:00:00的毫秒数
long start = System.currentTimeMillis();
....
//获取当前结束时间距离1970-01-01 00:00:00的毫秒数
long end= System.currentTimeMillis();
//时间差计算程序运行时间
System.out.println("所花费的时间:" + (end - start));
关键字break、continue
continue和break的后面不可以声明执行语句。
数组的概述
数组的特点:数组是有序排列的。
1、数组属于引用数据类型的变量。数组的元素既可以是基本数据类型也可以是引用数据类型。
2、创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址。
3、数组的长度一旦确定,就不能修改。
数组的分类
==按照维度==:一维数组、二维数组、三维数组、…
==按照元素的数据类型==:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
一维数组的使用
一维数组的声明和初始化
数组一旦初始化完成,其长度就确定了。
数组元素的引用
==数组的角标从0开始,到数组的长度减一结束。==
使用属性length可以获取数组的长度。
增强遍历一维数组(foreach)
int[] a=new int[]{1,2,3,4};
for(int e : a){
System.out.println(e);
}
//--------------------------------------
String[] names = {"beibei", "jingjing"};
for (String name : names) {
System.out.println(name);
}
数组元素的默认初始化值
内存解析
内存的简化结构
一维数组的内存解析
多维数组的使用
对于二维数组的理解,我们可以看成是一维数组 array1又作为另一个一维数组array2的元素而存在。==其实,从数组底层的运行机制来看,其实没有多维数组。==
二维数组的声明和初始化
二维数组的引用
int[][] arr=new int[][]{{1,2,3,},{4,5},{6,7,8}};
System.out.println(arr[0][1]);
输出
2
二维数组的长度
int[][] arr=new int[][]{{1,2,3,},{4,5},{6,7,8}}; System.out.println(arr.length);
输出
3
System.out.println(arr[1].length);
输出
2
遍历二维数组
使用嵌套循环
int[][] arr=new int[][]{{1,2,3,},{4,5},{6,7,8}};
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr[i].length;j++){
System.out.print(arr[i][j]);
}
} 输出
12345678
二位数组的元素默认初始值
int[][] arr=new int[4][3];
System.out.println(arr[0]);
System.out.println(arr[0][1]);
输出
[I@15db9742
//外层数组默认值为地址值
0
//内层数组默认值为对应类型一维默认值
二维数组的内存解析
数组中涉及到的常见算法
二分法查找
要求:要查找的数组必须有序
设置首位索引的方法,不断地取中间值。
/**
* 二分查找法
*/
public static int binSearch(int[] a,int b){
int low = 0;
int high = a.length - 1;
while (low <= high){
int mid = (low + high)/2;
if (a[mid] < b){
low = mid + 1;
}else if(a[mid] == b){
return mid;
}else {
high = mid - 1;
}
}
return -1;
}
Arrays工具类的使用
数组常见异常
冒泡排序
思想:
1、一个无序的数组,n个元素,一共需要排序n-1轮
2、在每一轮中,从数组第0位开始,比较相邻两个元素,如果与需求逆序,就交换这两个元素,在每一轮中,可以将当前最大(最小)的元素交换到最后,
3、直到执行完n-1轮,没有需要比较的元素为止。
代码实现:
public static void bubSort(int[] a){
for (int i = 0;i < a.length - 1;i++){
for (int j = 0;j < a.length - 1 - i;j++){
if (a[j] > a[j + 1]){
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
快速排序
思想:
代码实现:
选择排序
思想:
1、一个无序的数组,一共有n个元素,需要排序n-1轮
2、在每一轮的排序中,需要将当前轮的第0个元素的角标记录下来,然后依次与后面的元素进行比较,如果发现比当前轮第0个元素更小(更大)的元素,就记录角标,当比较完数组最后一个元素,如果最小的元素不是当前轮第0个元素,进行交换。
3、直到执行完n-1轮,所有元素都按顺序排好
代码实现:
public static void seletSort(int[] a){
//外层控制比较的轮数
for (int i = 0;i < a.length - 1;i++){
//声明一个变量,用于存储最小元素的角标,起始位轮数的第一个元素的角标
int minNum = i;
//内层循环控制每一轮的比较,第0个元素从当前轮第一个元素开始比较
for (int j = i + 1;j < a.length;j++){
minNum = (a[minNum] < a[j])?minNum : j;
}
//一轮比完以后,将第0位的元素,如果当前轮最小元素不是第0位,就交换位置
if (minNum != i){
int temp = a[minNum];
a[minNum] = a[i];
a[i] = temp;
}
}
}
插入排序
思想:
1、一个无序的数组,n个元素,一共执行n-1轮
2、在每一轮中,需要将当前有序区边界后一个元素,依次与有序区元素右后向前进行比较,如果是逆序的,就将有序区的该元素向后移一位,直到遇到顺序的有序区元素,就停止比较,进行下一轮,并将该元素插入该有序区元素的后面。
3、直到执行完n-1轮,将所有元素按照顺序排好
代码实现:
public static void insertSort(int[] a){
//外层循环控制轮数
for (int i = 1;i < a.length;i++){
//每一轮中进行比较的元素为a[i]
//变量j为有序区最后一个元素的角标
int num = a[i];
int j = i - 1;
/*内层循环控制每一轮比较的细节 q
要插入的元素一次向前比较,如果小于前面的元素就将比较的元素向后移动
直到数组的第0位
*/
while (j >= 0 && num < a[j]){
a[j + 1] = a[j];
j--;
}
//如果到了数组的第0位或插入的元素比要比较的元素大的情况下,就在比较的元素后面插入
a[j + 1] = num;
}
}
排序算法性能对比
面向对象内容的三条主线
1.Java类及类的成员:属性、方法、构造器;代码块、内部类
2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)
3.其它关键字:this、super、static、final、abstract、interface、package、import等
面向对象的思想概述
Java语言的基本元素: 类和对象
类的设计原则:
- 尽量要将数据(成员变量)设为私有
- 要对数据(成员变量)初始化
- 不要在类中使用过多的基本类型
- 不是所有的属性都需要独立的访问器和更改器
- 格式要统一
- 将职责过多的类进行分解
- 类名与方法名应体现它们的功能(望文生义)
对象
==没有完全相同的两个对象,对象具有唯一性!==
类和对象的使用(面向对象思想落地的实现)
1、创建类、设计类的成员
2、创建类的对象(new)
3、通过“对象.属性”或“对象.方法()”调用对象的结构
==如果创建了一个类的多个对象,对于类中定义的属性,每个对象都拥有各自的一套副本,且互不干扰。意味着:如果修改一个对象的属性A,则不影响另一个对象的属性A的值。==
对象的产生的内存解析
类的结构中一:属性
属性(成员变量)vs局部变量
相同点
1、定义变量的格式是一样的:数据类型 变量名 = 变量值
2、先声明,后使用
3、变量都有其对应的作用域
不同点
1、==在类中声明位置的不同==
属性:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2、==关于权限修饰符的不同==
属性:可以在声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:private(私有)、pubilc、缺省、protected
局部变量:不可以使用权限修饰符
3、==默认初始化值的情况==
属性:类的属性,根据其类型,都有默认初始化值
整形(byte、short、int、long):0
浮点型(double、float):0.0
字符型(char):0(或’\u0000’)
布尔型(boolean):false
引用数据类型:null
局部变量:没有默认初始化值
意味着,调用局部变量之前,一定要显示赋值
特别的,形参在调用时,赋值即可
4、==在内存中加载的位置不同==
属性:加载在堆空间中
局部变量:加载在栈空间
类的结构二:方法
==方法:描述类应该具有的功能,比如:==
Math类:sqrt()\random()…
Scanner类:nextXxx()…
Arrays类:sort()\binarySearch()\toString()\equals()…
方法的分类:
方法的声明格式:
public static void main (String[] args){ // 方法体 }
权限修饰符:private \缺省 \protected \pubilc ---->封装性
修饰符:static \ final \ abstract \native 可以用来修饰方法
返回值类型: 无返回值 / 有返回值 -->return
方法名:需要满足标识符命名的规则、规范;"见名知意"
形参列表:重载 vs 重写;参数的值传递机制;体现对象的多态性
方法体:来体现方法的功能
return关键字的使用:
使用范围:使用在方法体中
作用:
1、结束方法
2、针对于有返回值类型的方法,使用”return 数据”方式,返回需要的数据
注意点:return关键字后面不可以声明执行语句
注意
方法的使用中,可以调用当前类的属性和方法。
方法中不可以定义方法。
方法的重载
定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
总结:”两同一不同”:类、相同方法名,相同
参数列表:参数个数、参数类型,参数顺序,不同
==方法的重载和方法的权限修饰符、返回值类型、形参变量名、方法体都没关系!==
可变个数的形参(JDK5.0新增)
声明格式:
==方法名(参数的类型名 …参数名)==
例如:public void st(String … a);
1、可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
2、可变个数形参的方法与同名的方法之间,彼此构成重载
3、可变参数方法的使用与方法参数部分使用数组是一致的
4、方法的参数部分有可变形参,需要放在形参声明的最后
5、在一个方法的形参位置,最多只能声明一个可变个数形参
方法参数的值传递机制
方法,必须由其所在类或对象调用才有意义。若方法含有参数:
==形参==:方法声明时的参数
==实参==:方法调用时实际传给形参的参数值
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本 (复制品)传入方法内,而参数本身不受影响。
>形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
>形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
方法的递归
递归方法:一个方法体内调用它自身。
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
面向对象特征一:封装性
概念:
隐藏复杂,暴露简单
隐藏对象的属性和实现细节,仅对外提供公共访问方式,将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来对隐藏的信息进行操作和访问。
好处:
(1)只能通过规定的方法访问数据,可以有效的保护数据
(2)隐藏类的实现细节,方便修改和实现。
封装的实现步骤
(1)修改属性的可见性设为(private)
(2)创建getter/setter方法(用于属性的读写)(通过这两种方法对数据进行获取和设定,对象通过调用这两种发方法实现对数据的读写)
(3)在getter/setter方法中加入属性控制语句(对属性值的合法性进行判断)(不是必须要加)
四种访问权限修饰符
权限从小到大顺序为:private < 缺省 < protected < public
修饰范围:
==4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类==
==修饰类的话,只能使用:缺省、public==
对象数组的内存解析:
类的结构三:构造器
构造器(或构造方法):Constructor
构造器的作用:
1.创建对象
2.初始化对象的信息
说明:
- 如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
- 定义构造器的格式:权限修饰符 类名(形参列表){}
- 一个类中定义的多个构造器,彼此构成重载
- 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
- ==一个类中,至少会有一个构造器。==
创建对象的执行顺序:
1、给属性开辟空间,给属性赋默认值
2、如果声明属性的时候给属性赋初始值,那么接下来会给属性赋初始值
3、如果类中有代码块,会执行代码块(每创建一次对象都会执行一次)
4、调用构造方法
JavaBean
所谓JavaBean,是指符合如下标准的Java类:
>类是公共的
>一个无参的公共的构造器
>属性,且对应的get、set方法
关键字:this
概念
this理解为:当前对象 或 当前正在创建的对象
在类的方法中,我们可以使用”this.属性”或”this.方法”的方式,调用当前对象属性或方法。但是,通常情况下,我们都择省略”this.”。
特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用”this.变量”的方式,表明此变量是属性,而非形参。
this调用构造器:
① 我们在类的构造器中,可以显式的使用”this(形参列表)”方式,调用本类中指定的其他构造器
② 构造器中不能通过”this(形参列表)”方式调用自己
③ 如果一个类中有n个构造器,则最多有 n – 1构造器中使用了”this(形参列表)”
④ 规定:”this(形参列表)”必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个”this(形参列表)”,用来调用其他的构造器
关键字:final
作用范围:
可以用来修饰:类、方法、变量
说明:
final 用来修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类
final 用来修饰方法:表明此方法不可以被重写
比如:Object类中getClass();
final 用来修饰变量:此时的”变量”就称为是一个常量
-
final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化,但是只可以三选一,final修饰的属性不可以提供setter方法!
-
final修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:全局常量
包(package)
概念:
1.为了更好的实现项目中类的管理,提供包的概念
2.使用package声明类或接口所属的包,必须声明在源文件的首行
3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
4.每”.”一次,就代表一层文件目录。
JDK中的主要包:
import的使用:
import:导入
- 在源文件中显式的使用import结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构,则并列写出即可
- 可以使用”xxx.*”的方式,表示可以导入xxx包下的所结构
- 如果使用的类或接口是java.lang(java的默认导入包)包下定义的,则可以省略import结构
- 如果使用的类或接口是本包下定义的,则可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示。
- 使用”xxx.*”方式表明可以调用xxx包下的所结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
- import static:导入指定类或接口中的静态结构:属性或方法。
出血模型
定义:
简单来说,失血模型就是一个类,只有私有属性以及属性的getter/setter的纯数据类,所有业务逻辑都由Business Object来完成。
优势:
实现了业务与数据的完全分离,降低了代码之间的耦合
单一职责
一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离
类的结构四:代码块
代码块的作用:
用来初始化类、对象的信息
==代码块要是使用修饰符,只能使用static==
关键字:static(静态)
关键字:static(静态)
作用范围
可以用来修饰的结构:主要用来修饰类的内部结构
属性、方法、代码块、内部类
static修饰属性:
==静态变量(或类变量)==
静态属性 vs 非静态属性:
属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
l 实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
l 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
static修饰属性的其他说明:
① 静态变量随着类的加载而加载。可以通过”类.静态变量”的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则==静态变量在内存中也只会存在一份:存在方法区的静态域中==
内存解析:
static修饰方法:
① 随着类的加载而加载,可以通过”类.静态方法”的方式进行调用
② 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
static修饰代码块:
静态代码块:
>内部可以输出语句
随着类的加载而执行,而且只执行一次
>作用:初始化类的信息
>如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
>静态代码块的执行要优先于非静态代码块的执行
>静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块:
>内部可以输出语句
随着对象的创建而执行
>每创建一个对象,就执行一次非静态代码块
>作用:可以在创建对象时,对对象的属性等进行初始化
>如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
>非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
static的注意点:
==在静态的方法内,不能使用this关键字、super关键字==
什么时候使用static
属性:
- 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
- 类中的常量也常常声明为static
方法:
- 操作静态属性的方法,通常设置为static的
- 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections
main方法
- 作为程序的入口出现
- main()方法也是一个普通的静态方法
- main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
面向对象特征二:继承性
extends:延展、扩展
格式:
class A extends B{}
l A:子类、派生类、subclass
l B:父类、超类、基类、superclass
子类继承父类以后有哪些不同?
- 体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的==所有的属性和方法==。
- 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。
- 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
- 子类和父类的关系,不同于子集和集合的关系。
Java中继承性的说明
- 一个类可以被多个子类继承。
- Java中类的单继承性:一个类只能有一个父类
- 子父类是相对的概念。
- 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
- 子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法
方法重写:
override 或 overwrite
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
例如
class Circle{
public double findArea(){}//求面积
}
class Cylinder extends Circle{
public double findArea(){}//求表面积
}
**************************************
class Account{
public boolean withdraw(double amt){}
}
class CheckAccount extends Account{
public boolean withdraw(double amt){}
}
重写的规则:
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② ==子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符==
- 特殊情况:==子类不能重写父类中声明为private、static、final的方法==
③ 返回值类型:
- 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
- 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
super关键字
super 关键字可以理解为:父类的
可以用来调用的结构:==属性、方法、构造器==
super调用属性、方法:
- 我们可以在子类的方法或构造器中。通过使用”super.属性”或”super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略”super.”
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用”super.属性”的方式,表明调用的是父类中声明的属性。
- 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用”super.方法”的方式,表明调用的是父类中被重写的方法。
super调用构造器:
- 我们可以在子类的构造器中显式的使用”super(形参列表)”的方式,调用父类中声明的指定的构造器
- ==”super(形参列表)”的使用,必须声明在子类构造器的首行!==
- 我们在类的构造器中,==针对于”this(形参列表)”或”super(形参列表)”只能二选一==,不能同时出现
- 在构造器的首行,没显式的声明”this(形参列表)”或”super(形参列表)”,则默认调用的是父类中空参的构造器:super()
- 在类的多个构造器中,至少一个类的构造器中使用了”super(形参列表)”,调用父类中的构造器
类的初始化顺序:
- 加载父类(给父类的静态属性开辟空间、赋默认值、赋初始值,执行静态块)
- 加载子类(给子类的静态属性开辟空间、赋默认值、赋初始值,执行静态块)
以下是在创建子类对象的时候才会发生:
- 对父类中定义的非静态属性分配空间,赋默认值,做初始化(执行父类中定义的初始化块,执行父类的构造方法)
注意:默认执行的是父类的无参数的构造方法,如果在子类中使用super显示的调用了父类的其他构造方法,无参的就不会再次被调用
- 对子类中定义的非静态属性进行初始化(执行子类中定义的初始化块,执行子类中定义的构造方法,创建子类对象)
关键字:abstract(抽象的)
可以用来修饰:==类、方法==
==不可以和private、static、final、一起使用,不可以修饰构造器==
abstract修饰类:抽象类
- ==此类不能实例化==
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性
abstract修饰方法:抽象方法
- 抽象方法只有方法的声明,没方法体
- ==包含抽象方法的类,一定是一个抽象类。==反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所的抽象方法后,此子类方可实例化
- 若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
面向对象特征三:多态性
可以理解为一个事物的多种形态。
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
多态性的使用:虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
- ==总结:编译,看左边;运行,看右边。==
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
向上转型:
==多态性==
向下转型:
为什么使用向下转型:
- 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。
如何实现向下转型:
- 使用强制类型转换符:()
使用时的注意点:
- 使用强转时,可能出现==ClassCastException==的异常。
- 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
instanceof 的使用:
- a instanceof A : 判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
- 类B是类A的父类:a instanceof A返回true,a instanceof B也返回true
interface:接口
接口,实际上可以看做是一种规范
说明
- 接口使用interface来定义
- Java中,接口和类是并列的两个结构
- 如何定义接口:定义接口中的成员
- JDK7及以前:只能定义全局常量和抽象方法
- ==全局常量:默认是public static final的.但是书写时,可以省略不写==
- ==抽象方法:默认是public abstract的==
- JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法(static)、默认方法(default)
- ==接口中不能定义构造器的!意味着接口不可以实例化==
- Java开发中,接口通过让类去实现(implements)的方式来使用
- 如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
- 如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类
- ==Java类可以实现多个接口== —>弥补了Java单继承性的局限性
- 格式:class AA extends BB implements CC,DD,EE
- 接口与接口之间可以继承,而且可以多继承
- 接口的具体使用,体现多态性
Java8中关于接口的新规范
- 知识点1:接口中定义的静态方法,只能通过接口来调用。
- 知识点2:通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
- 知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的是父类中的同名同参数的方法。–>类优先原则
- 知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没重写此方法的情况下,报错。–>接口冲突。这就需要我们必须在实现类中重写此方法
- 知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
接口和抽象类的区别:
- 接口中不能写实例属性,但是抽象类中可以写实例属性,如果说在父类中想定义一些实例属性体现所有子类通用的属性,那么只能选择使用抽象类,如果没有上述需求,接口和抽象类都可以,那么优先使用接口,因为接口会更灵活一些。
- 抽象类中可以写构造方法,接口没有构造器
- 接口和类之间的关系是实现关系,不一定满足is a的原则,但是抽象类是属于继承体系,需要满足is a的原则
- 接口和接口之间可以有继承关系,并且是多继承,类和类之间的继承关系是单继承
类的结构五:内部类
定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
内部类的分类:
成员内部类(静态、非静态 )
一方面,作为外部类的成员:
- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰
另一方面,作为一个类:
- 内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以被abstract修饰
创建成员内部类的对象
- 静态
Person.Dog dog = new Person.Dog();
- 非静态(需要使用外部类的实例调用)
Person p = new Person();
Person.Bird bird = p.new Bird();
局部内部类(方法内、代码块内、构造器内)
一般使用在方法内,如:实现了Comparable接口的类的对象
字节码文件的区别
成员内部类和局部内部类,在编译以后,都会生成字节码文件。格式:
- 成员内部类:外部类$内部类名.class
- 局部内部类:外部类$数字 内部类名.class
关键字:native
使用native关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非 Java 语言实现的,并且被编译成了DLL,由java去调用
- 为什么要用native方法
- java使用起来非常方便,然而有些层次的任务用 java 实现起来不容易,或者我们对程序的效率很在意时,问题就来了
- 例如:==有时java应用需要与java外面的环境交互。==这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制: 它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节
- native声明的方法,对于调用者,可以当做和其他Java方法一样使用
- 一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制
- native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM将控制调用本地方法的所有细节
- 如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用java语言重写这个方法(如果需要的话)
异常的体系结构
- java.lang.Throwable
- |—–java.lang.Error:一般不编写针对性的代码进行处理。
- |—–java.lang.Exception:可以进行异常的处理
- |——编译时异常(==checked受检异常==)
- |—–IOException
- |—–FileNotFoundException
- |—–ClassNotFoundException
- |—–IOException
- |——运行时异常(==unchecked非受检异常==,RuntimeException)
- |—–NullPointerException
- |—–ArrayIndexOutOfBoundsException
- |—–ClassCastException
- |—–NumberFormatException
- |—–InputMismatchException
- |—–ArithmeticException
- |——编译时异常(==checked受检异常==)
==java.lang.Exception是所有异常的根父类==
运行时异常和非运行时异常的区别
1.运行时异常
- ==是指编译器不要求强制处置的异常。==一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子 类都是运行时异常。
- 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
2.非运行时异常
- ==是指编译器要求必须处置的异常。==即程序在运行时由于外界因素造成的一 般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
- 对于这类异常,如果程序不处理,可能会带来意想不到的结果。
异常的处理
java异常处理的抓抛模型
过程一:”抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。
一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)
过程二:”抓”:可以理解为异常的处理方式:① try-catch-finally ② throws
异常处理方式一:
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
....
finally{
//一定会执行的代码
}
说明:
-
==finally是可选的。==
-
使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
-
一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码)
-
catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,会报错
-
常用的异常对象处理的方式: ①String getMessage()—获取异常信息
② void printStackTrace()—打印异常堆栈信息
-
在try结构中声明的变量,再出了try结构以后,就不能再被调用
-
try-catch-finally结构可以嵌套
finally说明:
- finally是可选的
- finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况。
- 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
异常处理方式二:
public void 方法名() throws 声明异常类型{
if(何时异常){
throw 异常对象;
}
}
==一个方法可以声明多个类型的异常;==
“throws + 异常类型”写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
throw 和 throws区别:
- throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
- throws 属于异常处理的一种方式,声明在方法的声明处。
对比两种处理方式
try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。
自定义异常类
- 继承于现有的异常结构:RuntimeException 、Exception
- 提供全局常量:serialVersionUID
- 提供重载的构造器
Java中的JUnit单元测试
步骤:
- 选中当前工程 – 右键择:build path – add libraries – JUnit 4 – 下一步(eclipse)
- 创建Java类,进行单元测试。
- 此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器
- 此类中声明单元测试方法。
- 此时的单元测试方法:方法的权限是public,没返回值,没形参
- 此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
- 声明好单元测试方法以后,就可以在方法体内测试相关的代码。
- 写完代码以后,左键双击单元测试方法名,右键:run as – JUnit Test
说明:
- 如果执行结果没任何异常:绿条
- 如果执行结果出现异常:红条
注解
@Test:把一个方法标记为测试方法
@Before:每一个测试方法执行前自动调用一次
@After:每一个测试方法执行完自动调用一次
@BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
@AfterClass:所有测试方法执行完执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
@Ignore:暂不执行该测试方法
异常测试
可以在@Test后加==(expected = 可能出现的异常类.class)==进行捕获异常
断言
判断b的值是否是10,如果不是的话,那么就会运行失败
@Test
public void test(){
int b = 20;
Assert.assertEquals(10,b)
}