变量和常量
常量
常量:变量的量;java中使用final修饰的变量就是常量;命名规则:全部大写使用_分割;
变量
变量:可以变化的量;
分类:全局变量;局部变量;
全局变量:类的成员变量,(java中没有全局变量的概念)
局部变量:定义在方法、语句块、方法参数中类表中的量;
局部变量作用域和生命周期:从变量定义位置开始到变量所在语句块结束位置为止;
局部变量的语法:
变量类型 变量名 = 初始值;
或者:
变量类型 变量名;
变量名 = 初始值;
注意:
局部变量在使用之前必须进行初始化;
数据类型
扩展基础知识
二进制、8进制、十六进制、十进制之间的换算(包括整数和浮点数);
原码、反码、补码的计算方式;
结论:
计算机中所有的数都是以补码的形式存在的;【计算机中所有的负数都是以补码的形式存在的】【正数的原码、反码、补码是一样的】
数据类型的分类(8种基本类型)
整数类型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
浮点类型:float(4字节)、double(8字节)
字符类型:char(2字节)
布尔类型:boolean(根据编译器而定)
-
注意:
整数的数值默认类型是int类型;也就是一个数字如果是整数,则默认类型是int类型; 如果给一个long类型的变量进行赋值,则一般在数值后面添加后缀 L 或者 l,告诉编译器这是一个long类型,而不要默认为int类型; 浮点类型默认类型是double类型; 同理:如果给一个float类型的变量进行赋值;则一般在数值后面添加后缀 F 或者 f,告诉编译器这是一个float类型,而不要默认为double类型; char类型:表示一个字符且只能表示一个字符,使用 ''引出;这一个字符可以是英文、中文、Unicode码、转义字符(\n \t \r \' \\); char类型可以作为整型数值进行使用;使用的值为该字符的 ascii码的值; 语法: char chVal1 = 'a'; // 英文 char chVal2 = '小'; // 中文 char chVal3 = '\u00ac'; // Unicode码
数据类型转换
数据类型转换概念
不同数据类型之间的相互转换;
分为:默认转换(自动转换)、强制转换
默认类型转换(自动类型转换):从低级(范围小)转换为高级(范围大)的类型;(一般没有精度损失)
强制类型转换:从高级(范围大)转换为低级(范围小)的类型;(可能会造成精度损失)
默认类型转换(自动类型转换)
整型范围从小到大: byte short int long
浮点型范围从小到大:float double
8种基本类型中的默认类型转换规则:
整型、浮点型内部从小到大进行转换;
整型整体向浮点型进行默认转换;
byte和char之间不存在转换关系;
char和short之间不存在转换关系;
如果一个表达式中有多种类型,则该表达式的计算结果的类型为表达式中的最高类型;
特别注意:如果表达式中只有byte、char、short三种类型,则表达式的结果类型固定为int类型;[思考一下为什么?]
强制类型转换
从高级(范围大)转换为低级(范围小)的类型;(可能会造成精度损失)
语法:
[变量类型] 变量名 = (目标类型)被转化的变量;
注意:
小括号中的目标类型和等号左侧的变量名之间的类型是一致的;
一致的含义:如果转换的目标类型,可以默认转换为等号左侧的变量类型也可以;
运算符
运算符(操作符)
操作符也称为运算符,是一种特殊的符号,用来将一个或多个操作数连接成执行性语句(表达式),以实现特定功能。
运算符适用于连接操作数的符号;根据运算符连接的操作数的个数可以分为:
一元运算符:只有一个操作数; +(正) -(负) ! ~ ++ --
二元运算符:连接两个操作数; +(加) -(减) * / % & | && || >、>=、<、<=、==、!=
三元运算符:条件表达式; ? :
运算符特性:
结合性;【从左往右、从右往左】
优先级;例如:先乘除后加减; 优先级可以通过 () 来改变;
算术运算
常用的运算有:+、-、*、/、%(求余数)
注意:
运算符操作数之间的类型的默认转换;
运算结果是操作数中的最高类型;
位运算
结论:计算机中所有的数都是以补码的形式存在的;【计算机中所有的负数都是以补码的形式存在的】【正数的原码、反码、补码是一样的】★★★★★
常用的运算符:
~(按位取反)、&(按位与)、|(按位或)、^(异或)、<<(按位左移)、>>(按位不带符号右移)、>>>(按位带符号右移)
运算规则:
~取反运算:对于操作数的二进制按位进行取反运算;规则:1变0;0变1;
&与运算:对于操作数的二进制按位进行与运算;规则:1和1与为1;其他为0;
1&1=1 1&0=0 0&1=0 0&0=0
|或运算:对于操作数的二进制按位进行或运算;规则:0和0或为0;其他为1;
1|1=1 1|0=1 0|1=1 0|0=0
^异或运算:对于操作数的二进制按位进行异或运算;规则:相同异或为0;不相同异或为1;
1^1=0 1^0=1 0^1=1 0^0=0
<<按位左移:对于操作数的二进制按位向左移动n位,左侧最高位的符号位不动,其他各位向左移动n位,向左移动移出范围的位会被舍弃,右侧空余位补0;
一个数左移n位等价于这个数*(2的n次方)
>>不带符号右移:对于操作数的二进制按位向右移动n位,左侧最高位符号位不移动(除符号位之外的数右移),左侧空余位补最高位的符号位;
例如:1010 1100>>2 ===> 1110 1011
>>>带符号右移:对于操作数的二进制按位向右移动n位,左侧空余位补0;
例如:1010 1100>>>2 ===> 0010 1011
带符号的右移运算只对正数有意义,对负数没有意义;
注意:
位移运算(左移、不带符号右移、带符号右移)都是按位移动,移除范围的位会被舍弃;
比较运算(关系运算)
进行操作数大小相等关系比较的运算;
操作符:>、>=、<、<=、==(相等比较)、!=(不相等比较)
结论:比较运算的结果是布尔类型,也就是比较运算的结果不是 true就是false;
逻辑运算
进行逻辑判定的运算;
! 非(否定关系) && 与(且关系) || 或(或关系)
特性:逻辑运算的操作数必须是布尔类型;并且逻辑运算的结果也是布尔类型;
因为比较运算的结果是布尔类型,所有逻辑运算经常和比较运算混合使用;
运算规则:
! 非:true变false;false变true;
!true=false !false=true
&& 与:对于操作数进行与运算;规则:true和true与为true;其他为false;(连续的与运算表达式中只要有一个false,则结果为false)
true&&true=true true&&false=false false&&true=false false&&false=false
|| 或:对于操作数进行或运算;规则:false和false或为false;其他为true;(连续的或运算表达式中只要有一个true,则结果为true)
true||true=true true||false=true false||true=true false||false=false
逻辑短路:
因为逻辑&&和逻辑||运算有如下特点:
逻辑与:连续的与运算表达式中只要有一个false,则结果为false;
逻辑或:连续的或运算表达式中只要有一个true,则结果为true
规律:
逻辑与短路:
如果是多个连续的逻辑与运算,则从表达式的左侧开始,如果有一个表达式的结果为false,则可以判定这个表达式的结果为false;从该位置
往后的表达式则不会再被计算;这就是逻辑与短路;
逻辑或短路:
如果是多个连续的逻辑或运算,则从表达式的左侧开始,如果有一个表达式的结果为true,则可以判定这个表达式的结果为true;从该位置
往后的表达式则不会再被计算;这就是逻辑或短路;
混合逻辑短路:逻辑与和逻辑或混合运算也会产生逻辑短路现象。【课下扩展】
赋值运算
给变量进行赋值的操作;
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、>>>=
+=赋值运算是将+的加法和=的赋值运算进行了合并;
例如: i+=2; 等效于: i= i+2;
注意:
类似于+=的运算不能和声明一起使用;例如 : int iVal10 +=2; // 非法使用
自增自减运算
变量自身进行自动+1或者-1运算;经常用做计数因子;
运算符: ++ --
一元运算符、可以在变量之前,也可以在变量之后
语法:
变量之前的表达式:++变量名; --变量名;
变量之后的表达式:变量名++;变量名--;
解析:自增自减运算是一个表达式;表达式有自身的运算结果值;
表达式中含有变量;变量也有自身的值;
运算规律:
1)自增自减运算中含有两种值:自增自减表达式自身运算的结果值、自增自减表达式中含有的变量本身的值;
2)运算规则:
运算符在变量之前的先增、先减运算:
表达式的值+1(-1) 变量自身的值+1(-1)
运算符在变量之后的后增、后减运算:
表达式的值是变量运算之前的值(不进行+1或者-1运算) 变量自身的值+1(-1)
3)先增运算表达式的值需要+1操作;后增运算表达式的值不需要+1运算(使用变量的运算之前的值)
无论是先增还是后增运算,变量自身的值都是+1的;
自减运算亦同;
4)总结:
先增运算表达式的值是+1的;后增运算表达式的值是不+1的;变量的值永远+1;
先减运算表达式的值是-1的;后减运算表达式的值是不-1的;变量的值永远-1;
流程控制
程序语句分类
根据程序语句执行的方式分为:
顺序语句;按照执行代码的先后顺序依次执行;
分支语句;根据分支判定条件,确定不同的分支进行执行;
循环语句;对于特定语句块多次重复执行(根据停止条件结束重复动作)
分支语句
if-else语句实现分支语句的执行;
根据分支的个数:单分支、双分支、多分支
语法:
单分支:
if(判定条件){
语句体;
}
// 后续代码
执行流程:如果判定条件为true,则执行语句体;否跳过该语句,执行if语句后面其他的代码;
双分支:
if(判定条件){
if语句体;
}else{
else语句体;
}
执行流程:如果判定条件为true,则执行if语句体;否执行else语句体;
多分支:
if(判定条件){
语句体1;
}else if(判定条件2){
语句体2;
}else if(判定条件n){
语句体n;
}else{
else语句体;
}
// 其他代码
执行流程:从开始到结束依次执行判定条件语句,如果按个判定条件语句为true,则执行对应的语句体;
如果所有的判定条件都不成立,则执行else语句;
注意:
1)分支语句具有排斥性,就是一个if-else语句块中,至多只执行一个分支,该分支执行之后,无论后面有没有其他的分支,
就直接跳出if-else语句块,执行其后的其他代码;
2) 分支语句和嵌套的问号表达式之间是可以等价替换;根据编程规范:优先推荐使用if-else分支语句;
分支语句-switch-case
switch-case也是分支流程,也是根据条件执行对应的分支;
语法:
switch(判定标识表达式){
case 标识变量1:
case语句体1;
break;// 可选
case 标识变量2:
case语句体2;
break;// 可选
case 标识变量n:
case语句体n;
break;// 可选
default:
default语句体;
}
执行流程:
判定标识表达式的值会和case后面的标识变量的值进行等值匹配,如果匹配成功,则执行对应的case语句体;
进入了case语句体进行执行之后,如果该case语句块末尾没有break语句,则继续执行后续的case语句块,
直到遇到break语句终止或者执行到最后的case语句块为止;
case语句块默认可以使用break语句进行终止,否会进入下一个case语句块继续执行;
default是当所有case语句块匹配不成功时进行执行的语句块;default语句块一般位于末尾,当然可以位于其他位置,
如果位于其他位置,注意是否需要添加break语句;
注意:
case 部分的标识值不能重复,否会引起二义性问题;
case 部分的标识值的类型可以是:byte、short、char、int、String(1.7+)
比较if语句和switch语句的异同点:
相同点:都是分支选择;都是根据条件进入对应的语句进行执行;
不同点:if语句是具有排斥性的,至多执行一个分支;
switch语句不具有排斥性,一次可以执行多个分支;可以结合break语句使其具有排斥性,此时等价于if语句;
流程控制-循环流程
概念
重复执行某一段代码,使用终止条件控制循环终止;
常用的循环流程:for循环、while循环、do-while循环
for循环
语法:
for(循环因子初始化;循环条件判定表达式;循环因子改变){
循环体;
}
执行流程:
1)执行循环因子初始化;
2)执行循环条件判定表达式,如果表达式结果为true,则执行循环体;否则,退出循环;
3)循环体执行完成之后,执行 循环因子改变 语句,转到2)继续执行;
注意:
循环条件判定表达式的结果只能是布尔类型;如果循环条件判定表达式的结果一直为true,则for循环陷入了死循环;
循环因子改变,是为了影响循环条件判定表达式的结果,使for循环在某种条件下可以结束;
循环因子改变可以放在循环体中完成;
循环因子初始化、循环条件判定表达式、循环因子改变三部分可以不写,但是 ;; 必须保留,这样就是死循环了;
while循环
语法:
while(循环条件判定表达式){
循环体;
// 循环因子改变语句
}
执行流程:
1)执行循环条件判定表达式,如果表达式结果为true,则执行循环体;否则终止while循环;
2)循环体执行完成之后,转到1)继续执行;
注意:
循环条件判定表达式如果恒为true,则陷入死循环;
while循环的循环体中会包含循环因子改变语句;
循环因子初始化工作一般在while开之前进行;
循环因子改变可以在循环体中完成,也可以在循环条件判定表达式中完成(借助于自增、自减运算);
do-while循环
语法:
do{
循环体;
}while(循环条件判定表达式);
执行流程:
1)执行循环体;
2)执行while后面的循环条件判定表达式,如果表达式结果为true,则跳转到1)继续执行;否则,结束do-while循环;
while循环和do-while循环的区别:
while循环是先判定条件再执行循环体;
do-while循环是先执行循环体,在判定循环条件;
do-while循环至少会执行一次循环体;while循环可能一次也不执行;
注意:
在使用for/whiel/do-while等循环处理时,一定要注意边界值的处理;
转移语句
java中能够实现转移的语句有三种: break;continue;return(放在后面讲); 注意,java中goto关键字是保留关键字,当前还未启用;
本章节只介绍:break和continue的使用;
break语句的使用:
break语句用于中断循环;break语句执行之后,循环语句整体结束,进入执行循环语句之后的其他代码;
continue语句的使用:
continue语句用于中断循环体;continue语句执行之后,循环语句整体没有结束,
只是当前本次正在执行的循环体结束,进入下一次循环处理;
数组
数组概念
数组:在内存中连续存储的类型相同的数据集合;
特点:
存储与内存中;
在内存中连续存储;
类型相同的数据;
数组中的元素有下标,用于标明每个元素的位置;下标从0开始;
读取数组中的元素采用下标的形式读取,例如:数组名[下标] 表示读取下标所指向的位置的数组元素的值;
如果试图读取数组下标范围之外的存储单元的值,会引起 数组下标越界异常【java.lang.ArrayIndexOutOfBoundsException】;
数组的长度是数组的最大下标+1;数组的最大下标=数组的长度-1;
数组的长度获取方式:数组名.length;
因为数组是连续的,且每个元素有下标标明位置,则可以使用循环语句进行数组元素的遍历;
数组初始化时使用了new关键进行存储空间的分配;new关键字的作用类似于C语言的molloc函数;
new关键字在java中适用于申请内存空间的;一般对象类型初始化时需要先试用new向内存(堆内存中)申请存储空间;
数据一旦申请了内存空间,则该存储单元会进行初始化,也就是初始化默认值;【对照基本类型:需要进行初始赋值】
也就是:数组申请存储空间之后,是有默认值的;
默认值: byte short int long float double char boolean String
0 0 0 0 0.0f 0.0 '\0' false null
数组一旦分配了内存空间,则数组的长度是不可以变化的,也就是数组长度确定之后,就是固定的,不允许动态扩展;
数据根据维度分为:
一维数组、二维数组、多维数组
数组也是数据类型中的一种;可以用来声明变量;使用方式和基本数据类型的使用方式是一样的;
数组中存储的数据可以任意的数据类型,包括基本类型、对象类型;
一维数组
一维数组表示的是一个维度的数据集合;
语法:
数据类型[] 变量名 = new 数据类型[len]; // 也可以分开
或者:
数据类型 变量名[] = new 数据类型[len]; // C语言习惯,java不推荐使用
或者:
数据类型[] 变量名;
变量名=new 数据类型[len];
或者:
数据类型[] 变量名 = new 数据类型[]{数值1,数值2,数值3,数值n}
或者:
数据类型[] 变量名;
变量名 = {数值1,数值2,数值3,数值n}
注意:给方式的初始化赋值不能和声明一起使用【8-201版本支持合并方式】
二维数组
二维数组表示的是两个维度的数据集合;
特点:
具有一维数组的特性;在内存中是连续存储的;
二维数组的读取和赋值是使用行下标和列下标进行定位的,例如:arr[2][3] 表示的元素位于二维数组的 行下标为2 列下标为3 的位置上;
在二维数组中,数组名 arr.length 表示数组的行数;
在二维数组中 arr[行标] 表示该行在内存中的地址入口;列下标表示对于该行的入口地址的偏移量;
在二维数组中,arr[行标].length 表示该行的元素个数(也就是该行的列数)
语法:
数据类型[][] 变量名 = new 数据类型[rowlen][collen]; // 也可以分开
或者:
数据类型[][] 变量名;
变量名=new 数据类型[rowlen][collen];
或者:
数据类型[][] 变量名 = new 数据类型[][]{{第一行数值1,第一行数值2,第一行数值n},{第n行数值1,第n行数值2,第n行数值n}}
或者:
数据类型[][] 变量名;
变量名 = {{第一行数值1,第一行数值2,第一行数值n},{第n行数值1,第n行数值2,第n行数值n}}
注意:给方式的初始化赋值不能和声明一起使用【8-201版本支持合并方式】
二维数组的遍历:
使用嵌套的双层for循环来进行遍历;外层遍历行;内层遍历列;
注意,每一行使用 arr[行下标].length 获取该行的列数;
不规则二维数组
由于二维数组初始化有很多种方式,有些方式,会初始化出来不规则的二维数组;
不规则的二维数组的语法和遍历同普通二维数组是一样;
注意:
不规则的二维数组每一行的列数可能不同;
不规则的二维数组,虽然有些行的列数会缺少(缺少的位置只能是行末),但是在内存还是连续存储的;
面向对象思想
面向对象概述
面向对象的编程思想:是接近于人类思维方式的一种编程思想;是体现高级语言的特点之一;
面向对象和面向过程的思想的差异:【思维方式的出发点】★★★★★
面向过程的思维方式:做什么事情,考虑的是如何去做;(关于“How”的问题)
“如何”就是过程
面向对象的思维方式:做什么事情,考虑的是找谁去做;(关于“Who”的问题)
“谁”就是对象
面向对象的核心:
抽象、封装、继承、多态【四大核心】【教材或考试:三大核心:封装、继承、多态】
面对象的扩展:
abstract、static、final【三大关键字:面向对象思想的补充】、interface(接口)
面向对象如何进行编程:
1)不管三七二十一,先创建一个类(class);
2)抽象这个类的共同的特性(属性)和共同的行为(方法),封装为类属性和行为方法;
3)创建这个类的实例进行调用使用;(创建一个个体进行调用)
面向对象的意义和目的、目标:【面向对象的目标---软件复用】
复用;【软件工程的终极目标】
面向对象的核心-抽象
抽象:是一个从个体到一般的过程;从这个群体的每一个个体中抽取出这个群体的共性,这就是抽象的过程;
从群体中抽取出来的共性包括两个方面:属性、行为;
抽象的结果就是获得了这个类群的共同的属性、和共同的行为;
共同的属性:描述这个类群特性的参数;例如:学生【姓名、年龄、性别、学号、系别、年级、学校、专业、成绩。。。】
共同的行为:描述这个群体的共同动作行为;例如:学生【学习、上课、考试、逃课、玩游戏。。。】
面向对象的核心-封装
封装:
1)把抽象的共同的属性和行为封装类中;【类的成员的构成】
封装的方式:
共同的属性----->类的成员变量
共同的行为----->类的方法(类的函数)
2)封装的概念还包括限定这个群体的共同的属性和行为,被外界访问的方式;【访问权限的控制】
封装的访问权限控制通过访问控制修饰符来实现:public 默认的 protected private
什么是类(class)?
类是面向对象的思想中,用于定义和规范对象;
也就是在面向对象中,使用类来定义对象的;【面向对象的编程需要先建立一个类】
类和对象的关系:
类是对象的定义和规范;【一般】
对象是类的实例;【个体】
成员变量
成员变量:又叫做实例变量;定义在类中的变量就是成员变量;
局部变量:定义在方法、语句块、方法参数列表中的变量;
总结:
成员变量:定义在类中,属于这个类的所有的实例对象所有;
生命周期和作用域范围:伴随该类一个实例对象的存在而存在;
不管定义在类的开头还是结尾,作用域范围都是该类的内部;【和定义位置无关】
成员变量有默认值,声明之后可以不用初始化,具有默认值;默认值和数组的默认值一样;
成员变量可以使用访问控制修饰符进行修饰;
局部变量:定义在方法中、语句块中、方法参数列表中;
生命周期和作用域范围:从定义位置开始,到所在的语句块、方法结束位置为止;【和定义位置有关】
局部变量没有默认值,在使用之前,必须先进行初始化才可以使用;
局部变量不可以使用访问控制修饰符修饰;
注意:
成员变量和局部变量可以同名;
同名局部变量的值会覆盖同名的成员变量的值;【范围最小优先原则】【就近原则】【强龙不压地头蛇】
在成员变量被同名的局部变量覆盖的情况下,如果还想使用被覆盖的成员变量,可以使用this关键字引出即可【后面讲解】
函数(方法)
函数的定义
函数就是用于完成一定功能的可以独立调用执行的代码的集合;
语法:
访问控制修饰符 其他修饰符 返回值类型 函数名(函数形参列表){
函数体;
// return 返回值
}
解析:
访问控制修饰符:封装中对于访问权限的控制;public protected 默认的 private
其他修饰符:static、final、synchronized、default(jdk8+)等
返回值类型:指明函数执行完成之后返回给上层调用者的结果类型;如果没有返回值,则使用void进行声明;
函数名:就是函数的调用名,其实函数名就是函数在内存中的调用地址(类似于数组名的作用);
函数的参数列表:也叫形参;就是函数声明的函数被调用时需要传入参数类表;使用 , 号分割,需要指明参数类型,
例如: int age,int score,String,name
函数体:就是函数的主体;
函数体return语句的使用:返回值需要使用return语句进行返回;函数体中return之后的顺序执行的语句不会再被执行;
void类型的函数也可以使用return,但是return关键字之后没有要返回的结果,只是为了返回而return;
注意:
函数的返回类型如果不是void,则必须使用return语句进行函数结果返回;
函数的调用是返回的结果需要使用变量进行接收;
形参和实参
在函数调用时会发生参数传值,是由实参的值传递给形参;
形参:就是函数声明时的参数参数列表;
实参:就是函数调用时传值的参数列表;
函数调用时的传值:实参值传递给形参;
根据形参的值发生变化之后是否会影响实参,在java中将函数传值分为两种:
按值传参;
按引用传参;
按值传参:形参值发生变化之后,实参值不会受影响,实参值不会发生变化;
8种基本类型、String类型;
按引用(地址)传参:形参值发生变化之后,实参值会受到影响,实参值也会跟着变化;
数组、对象类型(除String之外)
总结:
除8种基本类型和String类型是按值传参的,其他的类型都是按引用传参的。包括数组和自定义类;
形参和实参:实参的个数、类型顺序需要和形参的个数、类型、顺序要保持一致;
面向对象的核心-封装
访问控制修饰符:使用封装用于进行访问权限控制的;
访问权限范围:(从大到小)
public:所有的类都可以访问;
protected:同包的所有的类(包括同包子类),不同包的子类也可以
默认的:(什么修饰符都不写就是默认情况(C++:friendly))同包的类(包括同包子类),但是非同包子类不能访问
private:只能本类内部访问,外部一律不能访问
编程原则:【高内聚,低耦合】
在类进行封装时,成员变量一般使用private进行封装;并提供geter/setter接口【固定命名形式】;
成员方法一般使用public进行封装,方便外部调用该类的行为;
面向对象的核心-继承
继承:新创建的类使用已有的类的进行构建的方式就是继承;
继承的作用就是用已知的类创建未知的类;已知的类就是父类;未知的类就是子类;
继承就是使用父类创建子类的过程;
语法:通过使用extends关键字进行实现;
访问控制修饰符 其他修饰符 class 类名 extends 已知的类(父类){
// 类的主体
}
注意:
java只支持单继承;一个类只有一个直接父类;(C++是多继承)【体现了java的简单的特性】
继承受访问控制修饰符的限制,并不是父类所有的内容都可以继承给子类;
访问权限范围以及父子类之间的继承限定:
public:所有的类都可以访问;所有的子类都可以继承;
protected:同包的所有的类(包括同包子类),不同包的子类也可以;所有的子类都可以继承;
默认的:(什么修饰符都不写就是默认情况(C++:friendly))同包的类(包括同包子类),但是非同包子类不能访问;
只有同包的子类才能继承;
private:只能本类内部访问,外部一律不能访问;不能被继承;
java中Object类是所有类的直接或间接父类;Object类是祖宗类;java中所有的类都是Object的子类,包括自定义的类;
无论是否显示使用extends,所有的类都继承自Object的类;
构造方法(构造器)
构造方法:使用于在类创建时进行初始化设置的方法;作用就是创建类时,用于初始化的方法;
构造方法比较特殊,不能使用方法名直接调用,需要使用new进行调用;
构造方法是一种特殊的方法;
语法:
访问控制修饰符 类名(函数形参列表){
函数体;
}
解析:
构造方法是一种特殊的方法;
构造方法没有返回值类型,无void;
构造方法的方法名固定为类名,也就是构造方法的方法名和类型相同;
构造方法不能通过方法名直接调用;
构造方法可以构成重载;
构造方法不能被继承;
jvm会给所有的java类提供一个默认的构造方法,该构造方法需要显式写出;一般使用 new 类名(); 调用的就是默认的构造方法;
jvm提供的默认的构造方法也可以显式写出;
如果一个类中声明了一个自定义的构造方法,则jvm默认提供的构造方法就失效了,此时如果还需要调用默认的构造方法,则需要手动显式写出;
不带参数的构造方法就是jvm默认提供的构造方法的形式;
不能类的外部使用方法名直接调用,需要使用new进行调用;
构造方法之间可以相互调用,但是调用时不是使用方法名调用,而是使用 this(实参列表);形式调用;
在构造方法中,如果使用this()调用了其他的构造方法,则this()语句只能写在第一行;
如果在子类的初始化中会递归调用父类的对应的构造方法;也就是说,在子类的构造方法调用之前会先递归调用父类的构造方法,
递归到Object的构造方法结束;
在父子类的继承关系中,子类和父类的构造方法之间要保持一致:
子列有默认的构造方法,则父类必须提供默认的构造方法;
子类有自定义的构造方法,且该子类构造方法使用super调用父类的构造方法,则父类需要提供被调用的构造方法;
子类是一定会调用父类的构造方法的,如果没有显示使用super指明调用那个父类的构造方法,则默认调用父类默认的构造方法;
this关键字的使用:【指代当前类的实例对象】
this用于指代当前类的一个实例对象;【类似于古代皇帝自称“朕”指代自己】
this关键字可以引出被局部变量覆盖的同名的成员变量;
this(实参列表)可以在构造方法之间进行相互调用;【且该语句只能在第一行】
super关键字的使用:【指代当前类继承的父类的实例对象】
super用于指代当前类继承的父类的实例对象;
super在重写中调用父类的被覆盖的同名的方法或属性;【多态-重写】
super(实参列表)用于子类的构造方法调用父类的构造方法;【且该语句只能在第一行】
面向对象的核心-多态
多态:在类的继承中,子类和父类在行为方法上的多种形态的关系;这种多态关系体现在两个方面:
1)子类角度:子类将继承的父类的行为进行覆盖和扩展;【重写】
2)父类角度:父类指代子类;【溯型】
重写:在类的继承中,子类的同名的方法覆盖了父类的同名方法;
重写的条件:
方法名相同;
参数列表相同;
返回值类型相同或者子类方法的返回值是父类方法的返回值的子类类型;
子类的访问控制修饰符的范围要大于等于父类的访问控制修饰符的范围;
子类方法抛出的异常范围要小于等于父类方法抛出的异常范围;
注意:
构造方法不能重写;
静态方法不构成重写;
重写的方法可以使用@Override注解标明,也可以不写;
子类重写了父类的方法,则通过子类调用的是子类重写之后的方法,不会再调用父类的被覆盖的方法;
如果需要在子类重写的方法中继续调用父类被覆盖的方法,则需要使用super关键字调用即可;
重写和重载的异同点?
溯型:在类的继承中,父类类型变量和子类类型变量之间的转换关系;
向上溯型:父类类型的变量指代子类类型的变量;
父类类型 变量名 = new 子类类型();
向下溯型:父类类型的变量强制类型转换为子类类型;
子类类型 变量名 = (子类类型)父类类型变量;
注意:
向上溯型:父类指代子类,只能调用子类重写或者继承的父类的行为方法,不能调用子类新增的行为方法;
向下溯型:只能将向上溯型的父类,转换为子类,如果是直接new创建的父类,不能转换子类;
优点:父类代替所有子类;
软件工程原则:面向接口的编程【面向父类的编程】
9.7 面向对象两大特殊类-抽象类(abstract)
抽象类:使用abstract修饰的类就是抽象类;
特点:
使用了abstract修饰;
含有抽象方法;【含有抽象方法的类一定是抽象类,抽象类不一定含有抽象方法】
抽象类不能使用new进行初始化【因为抽象类含有未实现的方法,所有才分配内存时无法确定内存的大小】
抽象类可以含有属性和普通的行为方法;
子类如果继承了抽象类,子类要么通过重写实现父类的抽象方法;子类要么也声明为抽象类;
抽象类作用:
在类进行抽象、封装时,对于一些当前无法实现的又必须存在行为以抽象方法的形式定义,这样就是抽象类存在的价值;
抽象方法:
使用abstract修饰的方法就是抽象方法;
抽象方法没有方法体,只有方法声明;
语法:
访问控制修饰符 abstract 返回值类型 函数名(函数形参列表);
特点:
抽象方法在使用之前需要进行实现;通过子类重写的方式将抽象方法的方法体补充完成的过程,就是抽象方法的实现;
abstract关键字可以修饰类或者方法;
接口
接口:是一种特殊的抽象类;以至于特殊到使用 interface 关键字进行定义接口;
接口是一组行为的规范定义;【接口和抽象类的本质区别】
语法:
访问控制修饰符 interface 接口名{
// 接口变量 public static final
// 接口方法 public abstract
}
解析:
接口命名和类命名是一样的,一般在类命名的基础之上加一个I前缀;
访问控制修饰符只能是public或者默认类型;不能是protected或者private;
接口中定义的接口变量都是静态常量,也就是都是 public static final 类型的;
接口中定义的变量的 public static final 三个关键字可以全部或者部分省略;
接口中的方法都是公共的抽象方法,也即是 public abstract 类型,其中 public abstract可以全部或者部分省略;
接口是可以支持多继承的;作为java类只能单继承的一种补充;【类与类之间是单继承】
接口中定义的方法都是抽象方法,没有方法体;如果在接口中定义带有方法体的方法,则需要使用default关键字修饰(jdk8+支持)
接口不能直接使用new进行初始化处理;
接口的多继承:
类多继承接口,使用implements关键字;
一个类implements实现一个接口,则这个类需要实现接口的全部的抽象方法;
接口继承接口,使用extends关键字;
接口的价值和意义:
接口的多继承弥补java中只允许类单继承的局限性;
接口是一组行为规范集合体;
接口表示的是一组行为能力,一个类实现了某个接口,则表示这个类具有了某种行为能力;
static关键字
static关键字:表示的是静态,表示全类所有;
可以修饰对象:
成员变量、方法、语句块;
静态变量(类变量):由static修饰的成员变量叫做静态变量,也叫作类变量;普通的成员变量叫做实例变量;
静态变量属于全类所有,在类加载到jvm时就进行了初始化处理,并且只加载一次,而且是常驻内存的;
其他的普通成员变量是使用时进行初始化处理;
静态方法(类方法):static修饰方法就是静态方法,表示全类所有;
静态语句块:static修饰的语句块就是静态语句块;静态语句块在类加载到jvm是就会被执行,并且只执行一次,一般用于初始化处理;
注意:
static修饰的变量和方法都是静态的,可以通过类名直接访问;(普通成员变量、方法需要先创建类的实例对象变量,然后在访问)
static修饰的对象在类被加载到jvm时就进行初始化处理;(无论是否被调用到,都会进行初始化)
staitc修饰的静态方法只能访问static修饰的方法或变量;非静态的方法可以访问静态的或者非静态的方法或变量(都可以访问);
static修饰的静态语句块在类加载到jvm是就会被执行,并且只执行一次,一般用于初始化处理;类中的非静态语句块在类每次
创建一个实例对象时都会被执行;
static方法中不能使用this关键字;
static修饰的对象的生命周期是这个运行程序的声明周期,也就是从程序启动加载到jvm开始,到程序进程结束为止;
static只能修饰成员变量,不能修饰局部变量;
final关键字
final关键字是终止、终结的作用;
可以修饰:
变量(成员变量和局部变量)、方法、类
常量:final修饰的变量叫做常量,如果结合static关键字则叫做静态常量(static final修饰的成员变量);
final修饰的变量一经初始化则不允许在改变值;
终结方法:final修饰的方法是不允许子类重写的;final就把子类重写给终止了;【final对多态重写的限定】
终结类:final修饰的类是不允许子类继承的;【太监类】【final对继承的限定】
四大关键字(abstract、static、final、interface)
abstract、interface和final不能同时使用;
static方法中不能使用this关键字;
abstract和static也不能同时使用;
面向对象
掌握四大核心:抽象、封装、继承、多态;
掌握四大关键:abstract、static、final、interface 【对核心的补充和限定】
抽象类和接口的异同点?什么时候使用抽象类?什么时候使用接口?
内部类
内部类的概念
内部类:定义来一个类内部的类就是内部类;
内部类的分类:
根据内部类定义的位置可以分为:
成员内部类:定义在成员变量声明位置的内部类;使用方法和成员变量的使用方法是一样;
静态内部类:使用static修饰的成员内部类;使用方法和静态成员变量使用方法是一样;
局部内部类:定义在局部变量声明位置的内部类;使用方法和局部变量使用方法一样;
匿名内部:匿名的局部内部类;使用方法和匿名的局部变量是一样的;
解析:
各种内部类的生命周期和作用域范围与之声明的位置相关;例如:成员内部类的生命周期和作用域范围和成员变量一样;其他类推;
匿名内部类是在new的同时将抽象类或者是接口的抽象方法进行了立即实现,然后使用匿名变量的方式调用了行为方法;
什么时候使用匿名内部类?当我们只关注于动作的结果,而不关注于动作的执行者时,可以考虑使用匿名内部类;
好处:省略了抽象类或者接口的实现类,而是直接执行动作;
核心类
String工具类
String类是jdk提供的一个用于字符串处理的工具类;
String类提供的行为方法;
String类的特性;
参考api进行学习;
字符串在java中使用双引号引出,可以有任意个字符构成,字符char类型使用单引号引出,且只有一个字符;
String类
String类底层是字符数组存储;所有String类具有数组的特性;
String字符串有下标索引,且从0开始;可以通过下标访问对应字符元素;
String字符串一旦初始化之后,长度是固定的,是不能再扩展的;
String类的常用的方法
参阅api方法列表;
StringBuilder工具类
主要进行字符串的拼接操作处理;
String和StringBuilder的区别:
String是固定长度的,如果使用+连接多个字符串,则需要多次申请内存,效率较低;
StringBuilder是存储的字符串是可以变长的,多个字符串进行拼接时效率较高;初始化长度是16,当然也可以初始化是制定长度;
如果是字符串进行拼接操作,有限使用StringBuilder,尽量避免使用+拼接;
StringBuffer和StringBuilder一样,都是字符串处理的工具类;二者的使用是一样的;
StringBuffer是线程安全的;StringBuilder是线程不安全的;
Object工具类
Object类是所有类的直接或者间接父类;
注意:子类从Object父类继承的方法以及子类可以重写的方法;
equals方法在Object类中判定两个对象的引用地址是否相同;(Object判定方式:使用== 判定两个对象的地址引用的hashcode码是否相同)
但是在Object的子类中,所有的子类都重写了equals方法,equals方法的功能就转变:判定两个对象的内容是否相同;
== 和 equals方法:
== 判定两个对象的地址引用的hashcode码是否相同(是不是同一个对象)【地址引用】
equals方法判定两个对象内容是否相同;【对象内容】
Math类
数学运算工具类;
课下练习:将一个double类型的数字保留1位小数?如何实现?
Arrays工具类
主要是进行数组的处理;
使用数组的拷贝方法接口;用于数组的扩容。
Scanner工具
主要进行控制台输入;简单文本扫描器,也就是文本输入器;
包装类
包装类:将8种基本类型使用面向对象的思想进行封装,使8种基本类型支持面向对象的思想;
基本类型: byte short int long float double char Boolean 【不支持面向对象】
包装类: Byte Short Integer Long Float Double Character Boolean 【支持面向对象】 默认值都是null
拆箱:将包装类转变为基本类型;
装箱:将基本类型转变为包装类类型;
注意:
包装类类型和基本类型以及String类型之间的相互转换;
包装类就是把8种基本类型纳入面向对象的知识体系;
包装类的默认值为null,基本类型的默认值不为null;
软件工程原则:在抽象封装类的成员变量时,如无特殊要求,则使用包装类类型;【包装类类型默认值是null】
异常
异常概念
异常,就是发生非正常的情况;
错误:错误发生时程序是无法继续运行的;
异常:异常发生时程序可以继续运行;
异常和错误都继承了Throwable类;
异常的父类是Exception,错误的父类是Error类型,他们都继承自Throwable类;
异常的分类
Exception类是所有异常类的直接或间接父类;
根据异常在编译阶段是否进行处理:
检查性异常:要求编程时就需要显式进行异常处理;
非检查性异常:编程时不需要显式进行处理,可以通过编程逻辑严密性避免发生异常;
根据异常发生的时机:
编译时异常:就是检查性异常;
运行时异常:非检查性异常;
异常的处理机制
在编程时对异常进行显式的处理;
try-catch-finally机制
语法:
try{
// 可能发生异常的语句块
}catch(发生的异常类型 变量名){
// 进行异常处理
}catch(发生的异常类型2 变量名2){
// 进行异常处理
}catch(发生的异常类型n 变量名n){
// 进行异常处理
}finally{
// 收尾工作
}
解析:
try中是可能发生异常的语句块,或者是必须进行异常处理的语句块;
catch是用于对发生的异常的处理逻辑;括号中是声明当前catch块可以处理的异常类型;
catch可以有多个;从前往后多个catch处理的异常类型的范围是从小到大的;【先子类类型的异常,后父类类型的异常】
多个catch块之间是互斥的,发生异常之后,只执行其中一个,其他不会被执行;【类似于if-else的互斥关系】
Exception是所有异常的父类,一般放在多个catch的最后位置;
finally是进行收尾工作处理的;
finally语句块可有可无,如果有finally语句块,无论是否发生异常,则都会被执行;
catch (FileNotFoundException|NullPointerException e)变形的写法,要求 | 前后的异常类不能是父子类的包含关系;
throws处理机制
在方法声明的同时使用throws将方法体内的代码块可能发生的异常进行抛出;抛出之后由方法的上层调用者来进行处理;
如果一直向上抛出异常,则最后由jvm来处理;
throw的用法-手动制造异常
使用throw可以在编码时,主动抛出一个异常,一般用于方法调用之间的一些通信处理;
语法:
throw new 抛出的异常类(构造参数列表);
自定义异常
自定义异常通过继承Exception类或其子类,通过重写的方式,实现自定义异常类;
集合
集合概念
集合:就是一组数据的集合体;这组数据可能类型相同,也可以能类型不同;可能连续,也可能不连续;
常用的集合由Collection和Map接口衍变而来;
学习集合从以下几个方法进行对比学习:
是否重复? 集合中的元素是否可以重复
是否为null?集合中的元素是否可以存储null值
是否有序?集合中存入时元素的顺序和遍历取出时的顺序是否一致,一致成为有序;不一致成为无序
是否排序?集合中的元素在遍历取出时是否按照自然排序规则进行了排序? 【自然数排序、字典排序】
Collocation接口的实现类
主要的实现类或者子接口:Set、List、Queue
Set接口
Set接口中的集合是一个不包含重复元素的集合;
Set集合中的元素没有下标,不能像数组一样采用下标方式进行遍历了;
HashSet:底层存储结构是Hash表;
是否重复? 否
是否为null?是
是否有序?否
是否排序?否
遍历方式:foreach遍历、Iterator迭代器遍历
LinkedHashSet:底层存储结构是双向链表;
是否重复? 否
是否为null?是
是否有序?是
是否排序?否
遍历方式:foreach遍历、Iterator迭代器遍历
TreeSet:底层存储结构是树型存储(红黑树、b+树的一种);
是否重复? 否
是否为null?否
是否有序?否
是否排序?是
遍历方式:foreach遍历、Iterator迭代器遍历
List接口
主要的实现类:ArrayList、LinkedList、Vector
List是一个有序序列;
List的元素有下标,下标从0开始;
List的元素可以像数组一样通过下标进行访问;
ArrayList:底层实现是数组结构;默认初始化长度是10;
是否重复? 是
是否为null?是
是否有序?是
是否排序?否
遍历方式:for循环【while/do-while】、foreach遍历、Iterator迭代器遍历
LinkedList:底层是使用双向链表实现。
是否重复? 是
是否为null?是
是否有序?是
是否排序?否
遍历方式:for循环【while/do-while】、foreach遍历、Iterator迭代器遍历
ArrayList和LinkedList的特性:
ArrayList是数组实现,具有数组的特性:读取、赋值速度快;增加、删除速度慢;
LinkedList是链表实现,具有链表的特性:读取、赋值速度慢;增加、删除速度快;
Vector:底层是数组实现;是List接口中的线程安全类;
课堂作业:熟悉数据结构的Hash表、链表、树等数据结构,扩展认识栈、队列、堆(大根堆、小根堆);
Map接口
主要是实现类或子接口:HashMap、TreeMap、LinkedHashMap
Map接口中存储的元素是以键值对的形式存在。例如:描述学生 {姓名:学号} {姓名:性别} {学科:成绩}
在Map中使用一个名为Entry的内部类来表示Map中的键值对; key-value表示一个键值
Map中对于键值对元素的操作是以键值对中的键名(key值)为依据的,Value值是伴随键值存在的;
HashMap:底层存储结构是Hash表;
键名(key值)是否重复? 否
键名(key值)是否为null?是
键名(key值)是否有序?否
键名(key值)是否排序?否
遍历方式:
Map的遍历转变对Entry键值对的Set集合的遍历;【Set集合的遍历】
Map的遍历转变对Key值的Set集合的遍历;【Set集合的遍历】
LinkedHashMap:底层存储结构是双向链表;
键名(key值)是否重复? 否
键名(key值)是否为null?是
键名(key值)是否有序?是
键名(key值)是否排序?否
遍历方式:
Map的遍历转变对Entry键值对的Set集合的遍历;【Set集合的遍历】
Map的遍历转变对Key值的Set集合的遍历;【Set集合的遍历】
TreeMap:底层存储结构是红黑树;
键名(key值)是否重复? 否
键名(key值)是否为null?否
键名(key值)是否有序?否
键名(key值)是否排序?是
遍历方式:
Map的遍历转变对Entry键值对的Set集合的遍历;【Set集合的遍历】
Map的遍历转变对Key值的Set集合的遍历;【Set集合的遍历】
总结:
集合的特性和其实现子类的底层的数据结构相关:
Hash表存储结构:不重复、无序、可以为null、不排序
双向链表存储结构:不重复、有序、可以为null、不排序
红黑树存储结构:不重复、无序、不可以为null、排序
泛型
泛型的概念
从JDK 5.0开始,Java引入“参数化类型(parameterized type)”的概念,这种参数化类型称为“泛型(Generic)”。
泛型是将数据类型参数化,即在编写代码时将数据类型定义成参数,这些类型参数在使用之前再进行指明具体类型。
泛型提高了代码的重用性,使得程序更加灵活、安全和简洁。【使用泛型可以避免方法重载需要写多个同名的方法】
在JDK 5.0之前,为了实现参数类型的任意化,都是通过Object类型来处理。但这种处理方式所带来的缺点是需要进行强制类型转换,
此种强制类型转换不仅使代码臃肿,而且要求程序员必须对实际所使用的参数类型已知的情况下才能进行,
否则容易引起ClassCastException异常。
从JDK 5.0开始,Java增加对泛型的支持。使用泛型之后就不会出现上述问题。泛型的好处是在程序编译期会对类型进行检查,
捕捉类型不匹配错误,以免引起ClassCastException异常;而且泛型不需要进行强制转换,数据类型都是自动转换的。
泛型经常使用在类、接口和方法的定义中,分别称为泛型类、泛型接口和泛型方法。泛型类是引用类型,在内存堆中。
泛型的通配符
不做深入讲解;
泛型的编程规范
在能使用泛型的地方尽可能使用泛型;
集合工具类
Collections集合工具类
进行集合处理的工具类;
注意常用方法的使用;
尤其是sort方法的使用;
sort方法的使用要求
集合工具类的sort方法可以给集合的元素按自然顺序进行排序;
要求:
被排序的集合元素实现Comparable接口【具有比较的行为能力】,
jdk提供的8种包装类类型、String类型已经实现了Comparable接口,可以直接使用;
如果被排序的集合没有实现Comparable接口,也可以通过提供一个比较器(Comparator接口)来进行排序
泛型
泛型的概念
从JDK 5.0开始,Java引入“参数化类型(parameterized type)”的概念,这种参数化类型称为“泛型(Generic)”。
泛型是将数据类型参数化,即在编写代码时将数据类型定义成参数,这些类型参数在使用之前再进行指明具体类型。
泛型提高了代码的重用性,使得程序更加灵活、安全和简洁。【使用泛型可以避免方法重载需要写多个同名的方法】
在JDK 5.0之前,为了实现参数类型的任意化,都是通过Object类型来处理。但这种处理方式所带来的缺点是需要进行强制类型转换,
此种强制类型转换不仅使代码臃肿,而且要求程序员必须对实际所使用的参数类型已知的情况下才能进行,
否则容易引起ClassCastException异常。
从JDK 5.0开始,Java增加对泛型的支持。使用泛型之后就不会出现上述问题。泛型的好处是在程序编译期会对类型进行检查,
捕捉类型不匹配错误,以免引起ClassCastException异常;而且泛型不需要进行强制转换,数据类型都是自动转换的。
泛型经常使用在类、接口和方法的定义中,分别称为泛型类、泛型接口和泛型方法。泛型类是引用类型,在内存堆中。
泛型的通配符
不做深入讲解;
泛型的编程规范
在能使用泛型的地方尽可能使用泛型;
集合工具类
Collections集合工具类
进行集合处理的工具类;
注意常用方法的使用;
尤其是sort方法的使用;
sort方法的使用要求
集合工具类的sort方法可以给集合的元素按自然顺序进行排序;
要求:
被排序的集合元素实现Comparable接口【具有比较的行为能力】,
jdk提供的8种包装类类型、String类型已经实现了Comparable接口,可以直接使用;
如果被排序的集合没有实现Comparable接口,也可以通过提供一个比较器(Comparator接口)来进行排序
这个比较器一般可以使用匿名内部类实现;