一、java语言概述
1、适用的领域
- Java Web开发:后台开发
- 大数据开发
- Android应用程序开发:客户端开发
2、特点
-
面向对象
-
两个要素:类、对象
-
三个特征:封装、继承、多态
-
-
健壮性①去除了C语言中的指针②自动的垃圾回收机制—>仍然会出现溢出、内存溢漏
-
跨平台性:一次编译,到处运行
3、环境搭建
二、基本语法
1、关键字和标识符
1.1关键字
1.定义:定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)
2.特点:关键字中所有字母都为小写
1.2保留字
1.举例:goto 、cons
2.注意:明明标识符时要避免保留字
1.3标识符
1.定义:凡是自己可以起名字的地方都叫标识符。
2、涉及到的结构:包名、类名、接口名、变量名、方法名、常量名
3、命名规则:
- 由26个英文字母大小写,0-9 ,_或 $ 组成
- 数字不可以开头
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java中严格区分大小写,长度无限制
- 标识符不能包含空格
4、规范:
- 包名:多单词组成时所有字母都小写:xxxyyyzzz
- 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
- 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个 单词首字母大写:xxxYyyZzz
- 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
5、注意:
-
在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
-
Java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。
2、变量的使用
2.1分类
(1)按数据类型分类
-
基本数据类型
- 数值型
- 整数类型(byte,short,int,long)
- 浮点类型(float,double)
- 字符型(char)
- 布尔型(boolean)
- 数值型
-
引用数据类型
- 类(class)<------字符串
- 接口(interface)
- 数组([ ])
①整型:byte(1字节= 8bit),short(2字节),int(4字节),long(8字节)
- byte范围:-128~127
- 声明long型类型,必须以"l"或"L"结尾
- 通常,定义整型变量时,使用int类型
- 整型的常量,默认类型是:int型
②浮点型:float(4字节),double(8字节)
- 浮点型:表示带小数点的数值
- float表示数值的范围比long还大
- 定义float类型变量时,变量要以"f"或"F"结尾
- 通常定义浮点类型变量时,使用double型
- 浮点类型的常量,默认类型为:double
③字符型(char=2字节)
- 定义char型变量,通常使用一对’',内部只能写一个字符
- 表示方式:声明一个字符;转义字符;直接使用Unicode值来表示字符型常量
④布尔型(boolean)
-
只能取两个值之一:true,false
-
常常在条件判断、循环结构中使用
(2)按声明的位置分类
-
成员变量
- 实例变量(不以static修饰)
- 类变量(以static修饰)
-
局部变量
- 形参(方法、构造器中定义的变量)
- 方法局部变量(在方法内定义)
- 代码块局部变量(在代码块内定义)
2.2定义变量的格式
数据类型 变量名 = 变量值;
或
数值类型 变量名;
变量名 = 变量值;
2.3注意
(1)变量必须先声明后使用
(2)变量都定义在其作用域内,在作用域内有效,不在作用域内则失效
(3)同一个作用域内不能声明两个同名的变量
2.4基本数据类型变量间的运算规则
(1)涉及到除boolean之外的其他7种基本数据类型
(2)自动类型转换(7种基本数据类型)
当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型。
byte、char、short -------> int ------>long ------>float ----->double
特别的:当byte、char、short 三种类型的变量做运算时,结果为int型
说明:此时的容量大小指的是:表示数的范围的大和小。比如float容量大于long容量
(3)强制类型转换(7种基本数据类型):自动类型提升的逆运算
①需要使用强转符:()
②注意:强制类型转换,可能导致精度损失
(4)String与8种基本数据类型间的运算
①String属于引用数据类型,翻译为:字符串
②声明String类型变量时,使用一对""
③String可以和8种基本数据类型变量做运算,且运算只能是链接运算:+
④运算的结果仍为String类型
3、进制
3.1进制及表示方式:
对于整数,有四种表示方式:
-
二进制(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不区分大小写。
3.2二进制使用说明
1.所有数字在计算机底层都以二进制形式存在。
2.二进制数据的存储方式:所有的数值,不管正负,底层都以补码的方式存储
3.原码、反码、补码:
- 正数:三码合一
- 负数:
- 原码:直接将一个数值换成二进制数。最高位是符号位
- 反码:是对原码按位取反,只是最高位(符号位)确定为1。
- 补码:其反码加1。
3.3进制间的转换
4、运算符
4.1算术运算符
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
+ | 正号 | +3 | 3 |
- | 负号 | b=4;-b | -4 |
+ | 加 | 5+5 | 10 |
- | 减 | 6-4 | 2 |
* | 乘 | 3*4 | 12 |
/ | 除 | 5/5 | 1 |
% | 取余(取模) | 7%5 | 2 |
++ ++ | 自增(前):先运算后取值 自增(后):先取值后运算 | a=2;b++; a=2;++b; | a=3;b=3 a=3;b=2; |
– – | 自减(前):先运算后取值 自减(后):先取值后运算 | a=2;b–; a=2;–b; | a=1;b=1; a=1;b=2; |
+ | 字符串连接 | “He”+“llo” | “hello” |
1.前++:先自增1,后运算
后++:先运算,后自增1
2.前–:先自减1,后运算
后–:先运算,后自减1
3.连接符:+ 只能使用在String与其他数据类型之间
4.2赋值运算符
1.符号:=
2.扩展赋值运算符: +=, -=, *=, /=, %=
3.运算的结果不会改变变量本身的数据类型
4.3比较运算符(关系运算符)
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
== | 等于 | 4==3 | false |
!= | 不等于 | 4!=3 | true |
< | 小于 | 4<3 | false |
> | 大于 | 4>3 | true |
<= | 小于等于 | 4<=3 | false |
>= | 大于等于 | 4>=3 | true |
instanceof | 检查是否是类的对象 | “hello” instanceof String | true |
1.比较运算符的结果是boolean型:true或false。
2.> < >= <=:只能使用在数值类型的数据之间
3.==:既可以使用在数值类型数据之间,也可以使用在引用类型变量之间。
4.4逻辑运算符
&—逻辑与 | —逻辑或 !—逻辑非 && —短路与 || —短路或 ^ —逻辑异或
1.逻辑运算符操作的都是boolean类型的变量,而且结果也是boolean类型
4.5位运算符
运算符 | 运算 |
---|---|
<< | 左移 |
>> | 右移 |
>>> | 无符号右移 |
& | 与运算 |
| | 或运算 |
^ | 异或运算 |
~ | 取反运算 |
1.位运算操作的都是整型的数据
2.<<:在一定范围内,每向左移一位,相当于*2;>>:在一定范围内,每向右移一位,相当于/2。
4.6三元运算符
1.结构:(条件表达式)?表达式1 :表达式2
2.说明:
(1)条件表达式的结果为boolean类型
(2)根据条件表达式真假,决定执行表达式1,还是表达式2;如果表达式为true,则执行表达式1;如果表达式为false,则执行表达式2.
(3)表达式1和表达式2要求是一致的
(4)三元运算符可以嵌套使用
3.凡是可以用三元运算符的地方都可以改成if-else,反之不成立
4.如果程序及可以使用三元运算符,又可以使用if-else,优先选择三元运算符。原因:简洁、执行效率高。
5、流程控制
5.1顺序结构
1.程序从上到下逐行地执行,中间没有任何判断和跳转。
5.2分支结构
1.if-else
(1)结构
- 结构一
if(条件表达式){
执行代码块;
}
- 结构二:二选一
if(条件表达式){
执行代码块1;
}
else{
执行代码块2;
}
- 结构三:多选一
if(条件表达式1){
执行代码块1;
}
else if (条件表达式2){
执行代码块2;
}
……
else{
执行代码块n;
}
①else结构是可选的
②针对于条件表达式:
- 如果多个表达式之间是"互斥"关系(或没有交集的关系),则判断和执行语句声明的先后无所谓。
- 如果多个表达式之间有交集的关系,需要根据实际情况,考虑那个声明相爱上面。
- 如果多个表达式之间有包含的关系,通常情况下,将范围小的生命在范围大的上面,否则,范围小的不会被执行。
③if-else结构是可以相互嵌套的
④如果if-else结构中的执行语句只有一行,对应的()可以省略,但是不建议省略。
2.switch-case
(1)格式
switch(表达式){
case 常量1:
语句1;
// break;
case 常量2:
语句2;
// break;
… …
case 常量N:
语句N;
// break;
default:
语句;
// break;
}
①根据switch表达式中的值,依次匹配各个case中的常量,一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,知道遇到break关键字或此switch-case结构末尾结束为止。
②break可以使用在switch-case结构中,表示执行到此关键字,就跳出switch-case结构
③switch结构中的表达式,只能是一下6种数据类型之一:byte,short, char,int,枚举 (jdk 5.0),String (jdk 7.0);
④case之后只能声明常量,不能声明范围
⑤break关键字是可选的
⑥default:相当于if-else结构中的else
default结构是可选的,而且位置是灵活的
5.3循环结构
1.循环结构的四要素
①初始化条件
②循环条件—>boolean类型
③循环体
④迭代条件
通常情况下,循环结束是因为循环条件中返回false
2.三种循环结构
(1)for循环结构
for (①初始化部分; ②循环条件部分; ④迭代部分){
③循环体部分;
}
执行过程:①-②-③-④-②-③-④-②-③-④-…-②
(2)while循环结构
①初始化部分
while(②循环条件部分){
③循环体部分;
④迭代部分;
}
执行过程:①-②-③-④-②-③-④-②-③-④-…-②
while循环丢了迭代条件后可能导致死循环
(3)for和while循环总结
①开发中,基本上从.for循环和while循环中进行选择,实现循环结构。
②for循环和while循环可以相互转换
区别:for循环和while循环的初始化条件部分的作用范围不同。
③避免出现死循环。
(4)do-while循环结构
①初始化部分;
do{
③循环体部分
④迭代部分
}while(②循环条件部分);
执行过程:①-③-④-②-③-④-②-③-④-…②
说明:
①do-while循环至少会执行一次循环体。
②开发中更多的使用for和while循环
③"无限循环"结构:while(true)或for(;;)
如何结束一个循环结构?
- 当循环条件是false
- 在循环体内执行break
(5)嵌套循环
①嵌套循环:将一个循环结构A声明在另一个循环结构B的循环体中,就构成了嵌套循环。
外层循环:循环结构B
内层循环:循环结构A
②说明
- 内层循环结构遍历一遍,相当于外层循环循环体执行了一次
- 假设外层循环需要执行m次,内层循环需要执行n次,此时内层循环的循环体一共执行了n*m次
- 外层循环控制行数,内层循环控制列数
5.4关键字:break和continue
使用范围 | 循环中使用的作用(不同点) | 相同点 | |
---|---|---|---|
break | switch-case循环结构中 | 结束当前循环 | 关键字后面不能声明执行语句 |
continue | 循环结构中 | 结束当次循环 | 关键字后面不能声明执行语句 |
三、数组
1、数组的概述
1.数组(Array),十多个相同数据类型一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
2.数组的相关概念:
-
数组名
-
元素
-
角标、下标、索引
-
数组的长度:元素的个数
3.数组的特点:
(1)数组是序排列的
(2)数组属于引用数据类型变量,数组的元素既可以是基本数据类型也可以是引用数据类型后
(3)创建数组对象会在内存中开辟一整块连续的空间
(4)数组的长度一旦确定,不能更改
4.数组的分类
(1)按维数:一维数组、二维数组……
(2)按数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组
2、一维数组
1.一维数组的声明与初始化
//1.一维数组的声明和初始化
int num;//声明
num = 10;//初始化
int id = 1001;//声明+初始化
int[] ids;//声明
//1.1静态初始化:数组的初始化和数组元素的赋值操作同时进行
ids = new int[]{1001,1002,1003,1004};
//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行
String[] names = new String[5];
int[] arr = {1,2,3,4,5};//类型推断
2.一维数组元素的引用:通过角标的方式进行调用。
数组的角标(或索引)从0开始,到数组长度-1结束。
3.数组的属性:length
(1)数组一旦初始化,其长度确定。arr.length
(2)数组长度一旦确定,就不可更改
4.一维数组的遍历
for(int i = 0;i < name.length;i++){
System.out.println(name[i]);
}
5.一维数组元素的默认初始化值
-
数组元素是整型:0
-
数组元素是浮点型:0.0
-
数组元素是char型:0或’\u0000’
-
数组元素是boolean型:false
-
数组元素是引用数据类型:null
6.一维数组的内存解析
3、二维数组
1.如果一个一维数组A的元素还是一个一维数组类型的,则此数组A称为二维数组。
2.二维数组的声明与初始化
int[] arr = new int[]{1,2,3};//一维数组
//静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
//动态初始化1
String[][] arr2 = new String[3][2];
//动态初始化2
String[][] arr3 = new String[3][];
int[] arr4[] = new int[][]{{1,2,3},{4,5},{6,7,8}};
int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};
3.二维数组元素的引用
System.out.println(arr1[0][1]);
System.out.println(arr2[1][1]);
arr3[1] = new String[4];
System.out.println(arr3[1][0]);
4.二维数组的属性
System.out.println(arr4.length);//3
System.out.println(arr4[0].length);//3
System.out.println(arr4[1].length);//4
5.二维数组的遍历
for (int i = 0; i < arr4.length;i++) {
for(int j = 0; j < arr4[i].length;j++) {
System.out.println(arr4[i][j] + " ");
}
System.out.println();
}
6.二维数组元素的默认初始化值
(1)二维数组分为外层数组的元素、内层数组的元素
int[][] arr = new int[4][3];
//外层元素:arr[0],arr[1]
//内层元素:arr[0][0],arr[1][2]
(2)数组元素的默认初始化值
方式一
int[][] arr = new int[4][3];
//外层元素的初始化值为:地址值
//内层元素的初始化值为:与一维数组初始化情况相同
方式二
int[][] arr = new int[4][];
//外层元素的初始化值为:null
//内层元素的初始化值为:不能调用,否则报错
7.二维数组的内存解析
4、数组的常见算法
1.数组的创建与元素赋值
(1)杨辉三角
public class day {
public static void main(String[] args) {
//声明并初始化
int[][] yanghui=new int[10][];
//赋值
for(int i = 0; i< yanghui.length;i++) {
yanghui[i] = new int[i + 1];
//首末元素赋值
yanghui[i][0] = 1;
yanghui[i][i] = 1;
//其他位置赋值
if (i > 1) {
for(int j = 1;j<yanghui[i].length-1;j++) {
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
}
}
}
//遍历
for (int i = 0;i < yanghui.length;i++) {
for (int j = 0;j < yanghui[i].length;j++) {
System.out.print(yanghui[i][j] + " ");
}
System.out.println();
}
}
}
(2)回形数
public class ArrayExer3 {
public static void main(String[] args) {
int num = 5;
int[][] arr = new int[num][num];
int count = 0; // 要显示的数据
int maxX = num - 1,maxY = num - 1; // x横轴的最大下标, Y纵轴的最大下标
int minX = 0,minY = 0; // x横轴的最小下标,Y纵轴的最小下标
while (minX <= maxX) { //直到X横轴线最小下标比最大下标还大时就退出循环 说明已经把值赋到最后一个了
for (int x = minX; x <= maxX; x++) { //向右 从最小到最大下标递增
arr[minY][x] = ++count; //必须++count 不然count++第一次会赋值为0
}
minY++; //说明Y纵轴的最小下标+1 向右已经赋值完一行了
for (int y = minY; y <= maxY; y++) { //向下 从最小到最大下标递增
arr[y][maxX] = ++count;
}
maxX--; //以上同理不再赘述
for (int x = maxX; x >= minX; x--) { //向左 从最小到最大下标递减
arr[maxY][x] = ++count;
}
maxY--;
for (int y = maxY; y >= minY; y--) { //向上 从最小到最大下标递减
arr[y][minX] = ++count;
}
minX++;
}
// 遍历
for (int[] a : arr) {
for (int b : a) {
System.out.print(b + "\t");
}
System.out.println();
}
}
}
public class ArrayExer4 {
private static final int RIGHT = 1; //向右
private static final int DOWN = 2; //向下
private static final int LEFT = 3; //向左
private static final int ON = 4; //向上
/*
* k = 1:向右 k = 2:向下 k = 3:向左 k = 4:向上
*/
public static void main(String[] args) {
int num = 5;
int[][] huiXingShu = new int[num][num];
int count = num * num; //s表示该矩阵的总数目
int k = RIGHT; //k变量表示往哪个方向赋值 初始化从向右开始
int i = 0, j = 0; //i表示数组的纵坐标 j表示数组的横坐标
for (int m = 1; m <= count; m++) { //循环的次数刚好是矩阵的总数目
if (k == RIGHT) { //向右 赋值限制条件:如果横坐标小于输入的矩阵纵横数并且值是默认值0就给它赋值
if (j < num && huiXingShu[i][j] == 0) {
huiXingShu[i][j++] = m;
} else { //如果不是
k = DOWN; //表示改变方向赋值 向下
i++; //每次方向赋值完 要换一行方便下个判断赋值 所以i自增1 保证下次循环i下标是正确的
j--; //每次方向赋值完 要帮上面j++多运行的一次自减1 保证下次循环j下标是正确的
m--; //每次方向赋值完 进入else语句都要自减1 保证下次循环m的赋值数是正确的
}
} else if (k == DOWN) { //向下 以下同理
if (i < num && huiXingShu[i][j] == 0) {
huiXingShu[i++][j] = m;
} else {
k = LEFT;
i--;
j--;
m--;
}
} else if (k == LEFT) { //向左
if (j >= 0 && huiXingShu[i][j] == 0) {
huiXingShu[i][j--] = m;
} else {
k = ON;
i--;
j++;
m--;
}
} else if (k == ON) { //向上
if (i >= 0 && huiXingShu[i][j] == 0) {
huiXingShu[i--][j] = m;
} else {
k = RIGHT;
i++;
j++;
m--;
}
}
}
// 遍历
for (int[] anArr : huiXingShu) {
for (int anAnArr : anArr) {
System.out.print(anAnArr + "\t");
}
System.out.println();
}
}
}
(3)最大值、最小值、总和、平均数
public class ArrayExer5 {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i = 0; i < arr.length;i++) {
arr[i] = arr[i] = (int) (Math.random() * 90 + 10);
}
//遍历
for(int i = 0; i < arr.length;i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
//max
int maxValue = arr[0];
for(int i = 1;i < arr.length;i++) {
if(maxValue < arr[i]) {
maxValue = arr[i];
}
}
System.out.println("max:" + maxValue);
//min
int minValue = arr[0];
for(int i = 1;i < arr.length;i++) {
if(minValue > arr[i]) {
minValue = arr[i];
}
}
System.out.println("min:" + minValue);
//sum
int sum = 0;
for(int i = 0;i < arr.length;i++) {
sum += arr[i];
}
System.out.println("sum:" + sum);
//avg
int avgValue = sum / arr.length;
System.out.println("avg:" + avgValue);
}
}
2.数组的复制与赋值
int[] array1,array2;
array1 = new int[]{1,2,3,4};
2.1赋值:
array2 = array1;
将array1保存的数组的地址值赋给了array2,使得array1 和array2共同指向堆空间中的同一个数组实体。
2.2复制:
array2 = new int[array1.length];
for(int i = 0;i < array2.length;i++){
array2[i] = array1[i];
}
通过new的方式,给array2在堆空间中开辟一个新的数组的空间。将array1数组中的元素值一个一个的赋值到array2数组中。
3.数组元素的反转
//方法一
for(int i = 0; i < arr.length;i++) {
String temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i -1] = temp;
}
//方法二
for(int i = 0,j = arr.length -1; i < j;i++,j--) {
String temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
4.数组中制定元素的查找:搜索、检索
(1)线性查找:通过遍历的方式,一个一个数据进行比较查找,具有普遍适用性
(2)二分法查找:每次比较中间值,折半的方式检索。前提:数组必须有序
5.数组的排序算法
(1)十大内部排序算法
-
选择排序
- 直接选择排序
- 堆排序
-
交换排序
- 冒泡排序
- 快速排序
-
插入排序
- 直接插入排序
- 折半插入排序
- Shell排序
-
归并排序
-
桶式排序
-
基数排序
(2)理解
①衡量排序算法的优劣:时间复杂度、空间复杂度、稳定性
②排序的分类:内部排序、外部排序(借助磁盘)
③冒泡排序
public static void main(String[] args) {
int[] arr = new int[] {43,32,76,-98,0,64,33,-21,32,99};
//遍历
for(int i = 0;i < arr.length;i++) {
System.out.print(arr[i] + "\t");
}
System.out.println();
//冒泡排序:从小到大排序
for(int i = 0;i < arr.length - 1;i++) {//i为冒泡排序次数,j为数组内对象值
for(int j = 0;j < arr.length - 1 - i;j++) {
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
//遍历
for(int i = 0;i < arr.length;i++) {
System.out.print(arr[i] + "\t");
}
}
5、Arrays工具类的使用
1.定义在java.util.Arrays包下
2.Arrays:提供了很多操作数组的方法
6、数组的常见异常
1.数组脚标越界异常(ArrayIndexOutOfBoundsException)
2.空指针异常(NullPointerException)
3.一旦程序出现异常,未处理时,就终止执行。
四、面向对象-上
(1)面向对象学习的三条主线
①Java类及类的成员 :属性、方法、构造器;代码块、内部类
②面向对象的三大特征 :封装性、继承性、多态性(抽象性)
③其它关键字:this、super、static、final、abstract、interface、package、import
(2)面向对象与面向过程
①面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
②面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
③把大象装冰箱:
- 面向过程:1.打开冰箱 2.把大象装进冰箱 3.把冰箱门关住
- 面向对象:
人{
打开(冰箱){
冰箱.开门();
}
操作(大象){
大象.进入(冰箱);
}
关闭(冰箱){
冰箱.关门();
}
}
冰箱{
开门(){ }
关门(){ }
}
大象{
进入(冰箱){ }
}
1、类与对象
1.面向对象的两个重要概念:
- 类:对一类食物的描述,抽象的、概念上的定义
- 对象:实际存在的该类事物的每个个体,也成为实例
- 面向对象程序设计的重点是类的设计
- 设计类就是设计类的成员
对象是由类new出来的,派生出来的。
2.设计类就是设计类的成员
属性 = 成员变量 = field = 域、字段
方法 = 成员变量 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类
3.类和对象的使用
①创建类,设计类的成员
②创建类的对象
③通过"对象.属性"或"对象.方法"调用对象的结构
4.如果创建了一个类的多个对象,则每个对象都独立的拥有一套累的属性(非static的)
意味着:修改一个对象的属性a,不影响另一个对象属性a的值。
5.匿名对象:我们创建的对象,没有显示的赋给一个变量名,即为匿名对象
匿名对象只能调用一次
6.“万事万物皆对象”
(1)java中,我们将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
-
String、Scanner等
-
文件:file
-
网络资源:URL
(2)涉及到java语言和前端Html、后端数据库交互时,前后端的结构在java层面交互时,都体现为类、对象。
2、类的结构之一:属性
1.变量的分类:属性(成员变量)与局部变量
(1)相同点
①定义变量的格式:数据类型 变量名 = 变量值
②先声明,后使用
③变量有其对应的作用域
(2)不同点
①在类中声明的位置不同
-
属性:直接定义在类的{}内
-
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
②权限修饰符不同
- 属性:可以在声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:private、public、缺省、protected---->封装性
- 局部变量:不可以使用权限修饰符
③默认初始化不同
-
属性:根据其类型有对应的默认初始化值
-
整型(byte、short、int、long):0
-
浮点型(float、double):0.0
-
字符型(char):0或’\u0000’
-
布尔型(boolean):fals引用数据类型(类、数组、接口):null
-
-
局部变量:没有默认初始化值
在调用局部变量之前一定要赋值。形参在调用时赋值即可。
④在内存中加载的位置不同
- 属性:加载到堆空间(非static的)
- 局部变量:加载到栈空间
3、类的结构值二:方法
1.方法的声明
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
2.说明:
(1)关于权限修饰符:默认方法的权限修饰符使用public
修饰符:public,缺省,private, protected等
(2)返回值类型:有返回值、无返回值
如果方法有返回值类型,则必须在方法声明时,指定返回值的类型,同时,方法中,需要使用return关键字来返回制定类型的变量或常量:“return 数据”
如果方法没有返回值,则方法声明时,使用void来表示,通常,没返回值的方法中,就不需要使用return,如果使用的话,只能用return表示结束方法的意思
(3)方法名:属于标识符,遵循标识符的规则和规范,“见名知意”
(4)形参列表:方法可以声明0个、1个或多个形参
格式:参数类型 形参1, 参数类型 形参2, ……
(5)方法体:方法功能的体现
3.方法的使用中可以调用当前类的属性或方法
方法A中又调用了方法A:递归方法
方法中不可以定义方法
3.1关键字:return
1.使用范围:使用在方法体内
2.作用:①结束方法②针对于返回值类型,使用“return 数据”方法返回所要的数据
3.注意点:return后关键字后面不可以声明执行语句
3.2方法的重载
1.定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数 类型不同即可。
“两同一不同”:同一个类、相同方法名;参数列表不同:参数个数不同,参数类型不同
2.构成重载的举例:Arrays类中重载的sort()/binarySeach()
3.如何判断方法是否重载:“两同一不同”
与方法的权限修饰符、返回值类型、形参变量名、方法体都无关。
4.在通过对象调用方法时,如何确定某一个指定的方法
方法名—>形参列表
3.3可变个数形参的方法
1.jdk5.0新增
2.具体使用
(1)声明格式:方法名(参数的类型名 …参数名)
(2)可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
(3)可变个数形参的方法与本类中方法名相同,形参不同的方法彼此构成重载
(4)可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,二者不能共存
(5)可变个数形参int方法的形参中,必须声明在末尾
(6)可变个数形参在方法的形参中,最多只能声明一个可变形参
3.4java的值传递机制
1.针对于方法内变量的赋值
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
2.针对于方法的参数概念
形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
3.java中参数传递机制:值传递
(1)规则:
如果变量是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
如果变量是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
(2)推广:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
3.5递归方法
1.定义:一个方法体内调用它自身
2.理解
方法递归包含了一种隐藏的循环,他会重复执行某段代码,但这种重复执行无需循环控制。
递归一定要向已知方向递归,否则地归变成无穷递归,类似于死循环。
4、面向对象特征一:封装性
1.我们程序设计追求“高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提 高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露 的暴露出来。这就是封装性的设计思想。
2.封装性的体现:
(1)将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)
(2)不对外暴露的私有的方法
(3)单例模式(将构造器私有化)
(4)不希望类在包外被调用,可以将类设置为缺省的
3.权限修饰符
(1)java的4种权限(从小到大):private、缺省、protrcted、public
(2)具体的修饰范围
(3)4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类:只能用缺省、public
5、类的结构之三:构造器(构造方法、constructor)
5.1构造器
1.构造器的作用:
(1)创建对象
(2)初始化对象的信息
2.说明
(1)如果没有显示的定义类的构造器,则默认系统提供一个空参的构造器
(2)定义构造器的格式:权限修饰符 类名(形参列表){}
(3)一个类中定义的多个构造器,彼此构成重载
(4)一旦显示的定义了类的构造器之后,系统不再提供默认的空参构造器
(5)一个类中,至少有一个构造器
5.2属性赋值顺序
(1)默认初始化
(2)显式初始化
(3)构造器中初始化
(4)有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
执行的先后顺序:(1)-(2)-(3)-(4)
5.3JavaBean
1.JavaBean是一种java语言写成的可重用组件
2.所谓JavaBean,是指符合标准的java类
-
类是公共的
-
有一个无参的公共的构造器
-
有属性,且有对应的get、set方法
6、关键字:this
1.this可以用来修饰、调用:属性、方法、构造器
2.this修饰属性和方法:
this理解为:当前对象或当前正在创建的对象
(1)在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。通常情况下,省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表明此变量是属性,而非形参。
(2)在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。通常情况下,省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表明此变量是属性,而非形参。
3.this调用构造器
(1)在类的构造器中,可以显示的使用"this(形参列表)"方式,调用本类中指定的其他构造器
(2)构造器中不能通过"this(形参列表)"方式调用自己
(3)如果一个类中有n个构造器,则最多有n-1构造器中使用了"this(形参列表)"
(4)规定:"this(形参列表)"必须声明在当前构造器的首行
(5)构造器内部最多只能声明一个"this(形参列表)",用来调用其他的构造器
7、关键字:package、import
7.1package
1.package:包
2.使用package声明类或接口所属的包,声明在源文件的首行
3.包,属于标识符,遵循标识符的命名原则、规范(xxxyyyzzz)、“见名知意”
4.每"."一次,代表一层文件目录
补充:同一个包下,不能定义同名的接口、类
不同的包下,可以明明同名的接口、类
7.2import
import:导入
1.在源文件中显示的使用import结构导入制定包下的类、接口
2.声明在包的声明和类的声明之间
3.如果需要导入多个结构,则并列写出即可
4.可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构
5.如果使用的类或接口是java.long包下定义的,则可以省略import
6.如果使用的类或接口是本包下定义的,则可以省略import
7.如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示
8.使用"xxx.*"方式表明可以调用xxx包下的所有结构,但如果使用的是xxx子包下的结构,则仍需要显示的导入
9.import static:导入指定类或接口中的静态结构:属性或方法
五、面向对象-中
1、面向对象特征二:继承性
1.继承性的好处:
(1)减少了代码的冗余
(2)便于功能的扩展
(3)为多态性的使用提供了前提
2.继承性的格式: class A extends B{}
extends:延展、扩展
A :子类、派生类、subclass
B:父类、超类、基类、superclass
(1)一旦子类继承父类以后,子类A就获取了父类B中声明的所有的属性和方法。
特别的:父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。欣慰封装性的影响,是的子类不能直接调用父类的结构。
(2)子类继承父类后,可以声明自己特有的属性或方法:实现功能的拓展。
3.java中关于继承性的规定:
(1)一个类可以被多个子类继承
(2)java中类的单继承性:一个类只能有一个父类
(3)子父类是相对的概念
(4)子类直接继承的父类称为:直接父类。间接继承的父类称为:间接父类。
(5)子类继承父类后,就获取了直接父类以及所有间接父类中声明的属性和方法。
4.java.long.Object
(1)如果没有显示的声明一个类的父类,则此类继承于java.long.Object类
(2)所有的类(除了java.long.Object之外)都直接或间接的继承于java.long.Object类
(3)所有的java类都具有java.long.Object类声明的功能
2、方法的重写
1.定义:子类继承父类后,可以对父类中同名同参书的方法,进行覆盖操作。
2.应用:重写以后,当创建子类对象以后,通过子类对象调用父类中同名同参数的方法时,实际执行的是子类重写父类的方法。
3.重写的规则:
方法的声明:
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
约定俗成:子类中的叫重写的方法,父类中的叫被重写的方法
(1)子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
(2)子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊的:子类不能冲洗父类中声明为private权限的方法
(3)返回值类型:
-
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
-
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
-
父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须是double)
(4)子类重写的父类的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
子类和父类中的同名同参数的方法要么都声明为非static的,要么都声明为static的
3、关键字:super
1.super:父类的
2.super可以用来调用:属性、方法、构造器
3.super调用属性、方法
(1)在子类的方法或构造器中,可以通过使用"super.属性"或"super.方法"的方式,显示的调用父类中声明的属性或方法。通常情况下,省略"super."
(2)特殊的:当子类和父类中定义了同名的属性时,如果想在子类中调用父类中生命的属性,必须是显示的使用"super.",表明调用的是父类中声明的属性。
(3)特殊的:当子类重写了父类的方法时,如果想在子类的方法中调用父类中被重写的方法时,必须是显示的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4.super调用构造器
(1)我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
(2) "super(形参列表)"的使用,必须声明在子类构造器的首行!
(3)我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
(4)在构造器的首行,没有显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器: super()
(5)在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
4、子类对象实例化全过程
1.从结果上看
(1)子类继承父类以后,就获取了父类中声明的属性或方法。
(2)创建子类的对象,在堆空间中,就会加载所父类中声明的属性。
2.从过程上看:
当通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。
5、面向对象特征三:多态性
1.多态性的理解:可以理解为一个事物的多种形态
2.何为多态性?
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
举例:
Person p = new Man();
Object obj = new Date();
3.多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译看左边;运行看右边。
4.多态性的使用前提:①类的继承关系 ②方法的重写
5.多态性的应用举例:
public void func(Animal animal){//Animal animal = new Dog();
animal.eat();
animal.shout();
}
public void method(Object obj){
}
6.多态性使用的注意点:
对象的多态性,只使用于方法,不适用于属性(编译和运行都看左边)
7.关于向上转型与向下转型
7.1向上转型:多态
7.2向下转型:
-
为什么使用向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
如何才能调用子类特有的实行和方法?使用向下转型:使用强制类型转换符。
-
如何实现向下转型?用强制类型转换符:()
-
使用时的注意点:
- 使用强转时,可能出现ClassCastException的异常
- 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
-
instanceof的使用:
- a instanceof A:判断对象a是否是A的实例。如果是,返回true;不是,返回false。
- 如果a instanceof A返回true,则a instanceof B也返回true。其中B是A的父类。
- 要求A所属的类与类A必须是子类和父类的关系,否则编译错误。
- 使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
6、Object类的使用
1.java.lang.Object类的说明
(1)Object类是所有java类的跟父类
(2)如果在累的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
(3)Object类中的功能(属性、方法)具有通用性
属性:无
方法:equals()/toString()/getClass()/hashCode()/clone()/finalize()/wait()/notify()/notifyAll()
(4)Object类只声明了一个空参的构造器
2.equals()方法
- equals()方法的使用
(1)是一个方法,而非运算符
(2)只适用于引用数据类型
(3)object类中equals()的定义
public boolean equals(Object obj){
return (this == obj);
}
说明:Object类中定义的equals()和==的作用相同:比较的是两个对象的地址值是否相等,即是否引用同一个对象实体
(4)String、Date、File、包装类等都重写了Object类中的equals()方法。
重写后比较的不是两个引用的地址值是否相同,而是比较两个对象的“实体内容”是否相同。
(5)通常情况下,自定义的类如果使用equals(),是比较两个对象的“实体内容”是否相同,需要对object类中的equals()进行重写。
重写的规则:比较两个对象的实体内容是否相同。
-
如何重写equals()方法
-
开发中自动生成的
-
手动重写
class User{ String name; int age; //重写equals() public boolean equals(Object obj){ if(obj == this){ return true; } if(obj instanceof User){ User u = (User)obj; return this.age == u.age && this.name.equals(u.name); } return false; } }
-
-
回顾==运算符的使用
==:运算符
(1.)可以使用在基本数据类型和引用数据类型变量中
(2.)如果比较的是基本数据类型变量,比较的是两个变量保存的数值是否相等(类型不一定相同)
如果比较的是引用数据类型变量,比较的是两个对象的地址值是否相等,即是否引用同一个对象实体
(3)补充:==符号使用时,必须保证符号左右两边的变量类型一致。
3.toString()方法
- toString()方法的使用
(1)当我们输出一个对象的引用时,实际上就是调用当前对象的toString()[前提:不能为空]
@Test
public void test() {
String s = "abc";
s = null;
System.out.println(s);//null
System.out.println(s.toString());//出现NullPointerException
}
(2)Object类中toString()的定义
Public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
(3)String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回“实体内容”信息。
(4)自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”
- toString()方法的重写:自动实现
7、单元测试方法
步骤:
1.选中当前工程-右键:build path -add libraries - JUnit4 - 下一步
2.创建java类,进行单元测试
此时的java类要求:①此类是public的 ②此类提供公共的午餐的构造器
3.此类中声明单元测试方法
此类的单元测试方法,方法的权限是public,没有返回值,没有形参
4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入import org.junit.Test;
5.声明好单元测试方法后,就可以在方法体内测试相关的代码
6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Tesy
说明:
1.如果执行没有任何异常:绿条
2.如果执行出现异常:红条
8、包装类的使用
1.为了使基本数据类型的变量具有类的特征,引入包装类。
2.基本数据类型与对应的包装类:
3.基本数据类型、包装类、String之间的转换
基本数据类型<-------->包装类:自动拆箱与自动装箱
基本数据类型、包装类-------->String:调用String重载的valueOf(Xxx xxx)
String-------->基本数据类型、包装类:调用包装类的parseXxx(String s)
注意:转换时,可能会报NumberFormatException
4.应用场景举例
(1)Vector类中关于添加元素,只定义了形参为Object类型的方法。
package com.xiaoli.exer;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Vector;
public class ScoreTest {
public static void main(String[] args) {
//1.实例化Scanner,用于从键盘获取学生成绩
Scanner scan = new Scanner(System.in);
//2.创建Vector对象,Vector v = new Vector();相当于原来的数组
Vector v = new Vector();
//3.通过for (;;)或while(true)方式,给Vector中添加数组
int maxScore = 0;
for (;;) {
System.out.println("请输入成绩(1-100负数结束)");
int score = scan.nextInt();
if (score < 0) {
break;
}
if (score > 100) {
System.out.println("请重新输入");
continue;
}
//3.1添加操作:v.addElement(Object obj);
v.addElement(score);//自动装箱
//4.获取学生成绩的最大值
if(maxScore < score) {
maxScore = score;
}
}
//5.遍历Vector,得到每个学生的成绩,并与最大成绩比较,得到每个学生的等级。
char level;
for (int i = 0;i < v.size();i++) {
Object obj = v.elementAt(i);
int score = (int)obj;
if (maxScore - score <= 10) {
level = 'A';
}else if(maxScore - score <= 20){
level = 'B';
}else if(maxScore - score <= 30){
level = 'C';
}else {
level = 'D';
}
System.out.println("student" + i + " score is" + score +",level is" + level);
}
}
}
六、面向对象-下
1、关键字:static
1.static:静态的
2.static可以用来修饰:属性、方法、代码块、内部类
3.使用static修饰属性:静态变量(类变量)
(1)属性:按照是否使用static修饰,分为:静态属性 vs 非静态属性(实例变量)
实例变量:(没有被static修饰的)我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的改变。
静态变量:(被static修饰的)我们创建了类的多个对象,多个对象共享一个静态变量。当修改其中一个对象中的非静态属性时,会导致其他对象调用此静态变量时,是修改后的。
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 42;
c1.nation = "CHN";
Chinese c2 = new Chinese();
c2.name = "马龙";
c2.age = 34;
c2.nation = "China";
System.out.println(c1.nation);//china
}
}
class Chinese{
String name;
int age;
static String nation;
}
内存解析:
(2)static修饰属性的其他说明:
①静态变量随着类的加载而加载,可以通过"类.静态变量"的方式进行调用。
public static void main(String[] args) {
Chinese.nation = "中国";
}
class Chinese{
static String nation;
}
②静态变量的加载早于对象的创建。
③由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。
④
类变量 | 实例变量 | |
---|---|---|
类 | yes(生命周期一样) | no(实例变量归具体对象所有,不能通过类调用) |
对象 | yes(静态变量的加载早于对象的创建) | yes(同时出生) |
(3)静态属性举例:System.out;Math.PI;
4.使用static修饰方法:静态方法、类方法
(1)随着类的加载而加载,可以通过"类.静态方法"的方式进行调用。
(2)
静态方法 | 非静态方法 | |
---|---|---|
类 | yes | no |
对象 | yes | yes |
(3)静态方法中,只能调用静态方法或属性
非静态方法中,既可以调用静态的方法或属性,也可以调用非静态的方法或属性。
5.注意:在静态的方法内,不能使用this关键字、super关键字
class Chinese{
String name;
int age;
static String nation;
public void eat() {
System.out.println("中国人吃中餐");
}
public static void walk() {
}
public static void show() {
System.out.println("我是一个中国人");
//不能调用非静态的结构
// eat();
// name = "Tom";
//可以调用静态的结构
System.out.println(nation);//省略Chinese.
System.out.println(Chinese.nation);
walk();
}
}
6.(1)开发中,如何确定一个属性是否要声明为static?
-
属性是可以被多个对象所共享,不会随着对象的不同而不同。
-
类中的常量也常常声明为static。
(2)开发中,如何确定一个方法是否要声明为static?
-
操作静态属性的方法,通常设置为static
-
工具类中的方法,习惯上那个声明为static的。比如:Math、Arrays、Collections等工具类
2、理解main方法的语法
1.main()方法作为程序的入口出现
2.main()方法是一个静态方法
public static void main(String[] args) {}
3.main()方法可以作为与控制台交互的方式。
4.如何将控制台获取的数据传给形参:String[] args?
运行时:java 类名 “Tom” “Jerry” “123” “true”
sysout(arg[0]);//“Tom”
sysout(arg[3]);//“true”------>Boolean.parseBoolean(arg[3]);
sysout(arg[4]);//异常
5.总结:一叶知秋
public static void main(String[] args) {//方法体}
权限修饰符:private 缺省 protected public —>封装性
修饰符:static \ final \ abstract \ native 可以用来修饰方法
返回值类型:无返回值 / 有返回值–>return
方法名:需要满足便是福命名的规则、规范:“见名知意”
形参列表:重载 / 重写:参数的值传递机制:体现对象的多态性
方法体:体现方法的功能
3、类的成员之四:代码块(初始化块)
1.作用:初始化类、对象
2.只能用static修饰
3.分类:静态代码块、非静态代码块
4.静态代码块
-
内部可以有输出语句
-
随着类的加载而执行,而且只执行一次
-
作用:初始化类的信息
-
如果一个类中定义了多个静态代码块,按照声明的先后顺序执行
-
静态代码块的执行优先于非静态代码块
-
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
5.非静态代码块
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一个对象就执行一次
- 作用:在创建对象时,对对象的属性等进行初始化。
- 如果一个类中定义了多个非静态代码块,按照声明的先后顺序执行
- 非静态代码块内可以调用静态的属性、静态的方法,或调用非静态的属性、非静态的方法
6.实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:由父及子,静态先行
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
//new Leaf();
}
}
类的属性赋值的位置:
(1)默认初始化
(2)显式初始化
(3)构造器中初始化
(4)有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
(5)在代码块中赋值
执行的先后顺序:(1)-(2)/(5)-(3)-(4)
4、关键字:final
1.final:最终的
2.final可以用来修饰类、方法、变量
3.final修饰类:此类不能被其他类继承(不能有子类)
比如:String类、System类、StringBuffer类
4.final修饰方法:此方法不能被重写
比如:Object类中的getClass();
5.final修饰变量:此时的"变量"称为常量
(1)final修饰属性:可以赋值的位置:显式初始化、在代码块中初始化、构造器中初始化
(2)final修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量,但我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final用来修饰属性:全局常量
5、抽象类与抽象方法
5.1abstract关键字的使用
1.abstract:抽象的
2.abstract可以用来修饰的结构:类、方法
3.abstract修饰类:抽象类
-
此类不能实例化(不能造对象)
-
抽象类中一定有构造器,子类实例化时调用
-
开发中,会提供抽象类的子类,让对象实例化,完成相关操作
4.abstract修饰方法:抽象方法
-
抽象方法只有方法的声明,没有方法体
-
包含抽象方法的类一定是抽象类,反之,抽象类中不一定有抽象方法。
-
若子类重写了父类中的所有的抽象方法后,此子类方可实例化。
若子类没有重写父类中的所有的抽象方法,则此子类也是抽象类,需要用abstract修饰。
5.注意:
(1)abstract不能用来修饰:属性、构造器等结构
(2)abstract不能用来修饰私有方法、静态方法、final的方法、final的类
6、关键字:interface
接口
1.定义:使用interface定义
2.在java中接口和类是并列的结构
3.如何定义接口:定义接口中的成员
(1)JDK7及以前:只能定义全局常量和抽象方法
全局常量:publuc static final的,可以省略
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
}
}
interface Flyable{
///全局常量
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;//省略了public static final
}
抽象方法:public abstract
public abstract void fly();
void stop();//省略public abstract
(2)JDK8:除了定义全局常量和抽象方法外,还可以定义静态方法、默认方法
①接口中定义的静态方法,只能通过接口来调用
②通过实现类的对象,可以调用该接口中的默认方法
如果实现类重写了借口中的默认方法,调用时,调用的是重写后的方法
③子类(实现类)继承的父类和实现的接口类中声明了同名同参数的方法,如果子类没有重写此方法,默认调用的是父类中的同名同参数的方法----->类优先原则
class SubClass extends SuperClass implements Compara,ComparaB{
public void method2() {
System.out.println("aaa");
}
}
④实现类实现了多个接口,而多个接口中定义了同名同参数的默认方法,如果在实现类中没有重写该方法,则报错----->接口冲突
必须在实现类中重写此方法
class SubClass implements Compara,ComparaB{
public void method2() {
System.out.println("aaa");
}
public void method3() {
System.out.println("sss");
}
}
⑤在子类(实现类)中调用父类、接口中被重写的方法
public void myMethod() {
method3();//调用自己重写的方法
super.method3();//调用父类中声明的
//调用接口中的默认方法
Compara.super.method3();
ComparaB.super.method3();
}
4.接口中不能定义构造器,接口不可以实例化!
5.java开发中,接口通过类去实现(implements)的方式来使用。
如果实现类覆盖了接口中的所有抽象方法,则此实现类可以实例化;
如果实现类没有覆盖了接口中的所有抽象方法,则此实现类仍为抽象类。
6.java类可以实现多个接口---->弥补了java单继承性的局限性
格式:class AA extends BB implements CC,DD
7.接口与接口之间是继承,接口可以多继承。
类与类之间是继承,类是单继承。
接口与类之间是实现。
8.接口的具体使用
(1)满足多态性
(2)接口实际上是定义了一种规范
(3)开发中,面向接口编程
我们在应用程序中,调用的结构都是JDBC中定义的接口,不会出现具体某一个数据库厂商的API。
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//创建了接口的匿名实现类的非匿名对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//创建了接口的匿名实现类的匿名对象
com.transferData(new USB() {
@Override
public void start() {
System.out.println("mp3开始工作");
}
@Override
public void stop() {
System.out.println("mp3结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb) {//USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB {
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("u盘开始工作");
}
@Override
public void stop() {
System.out.println("U盘停止工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void stop() {
System.out.println("打印机停止工作");
}
}
9.接口的应用:
(1)代理模式(Proxy)
-
应用场景:安全代理、远程代理、延迟代理
-
分类:静态代理、动态代理
(2)工厂模式
7、类的成员之五:内部类
1.定义:java中允许讲一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。
2.分类:成员内部类(静态、非静态)、局部内部类(方法内、代码块内、构造器类)
3.成员内部类:
- 作为外部类的成员
- 可以调用外部类的结构
- 可以被static修饰
- 可以被四种不同的权限修饰
- 作为一个类:
- 类中可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承;没有被final修饰,表示此类可以被继承
- 可以被absteact修饰
4.注意
(1)如何实例化成员内部类的对象(静态的、非静态的)
//创建静态的Dog实例(静态的成员内部类)
Animal.Dog dog = new Animal.Dog();
//创建非静态的Bird实例(非静态的成员内部类)
Animal a = new Animal();
Animal.Bird bird = a.new Bird();
(2)如何在成员内部类中区分调用外部类的结构
class Animal{
String name = "动物";
//非静态成员内部类
class Bird{
String name = "杜鹃";
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
}
(3)开发中使用局部内部类
①方法一
//返回一个实现了Comparable接口类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
class MyComparable implements Comparable{
@Override
public int comparaTo(Object obj){
retuan 0;
}
}
retuan new MyComparable();
}
②方法二
public Comparable getComparable(){
@Override
public int comparaTo(Object o){
retuan 0;
}
};
③注意:在局部内部类的方法中(show)如果调用局部内部类所声明的方法(method)中的局部变量(num),要求此变量声明为final。
jdk7 及以前要求此局部变量显示的声明为final
jdk8 及以后可以省略final
public void method(){
//局部变量
int num = 10;
class AA{
public oid show(){
//num = 20;
System.out.println(num);
}
}
}
④总结:成员内部类和局部内部类在编译后,都会生成字节码文件。
格式:成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class
七、异常处理
1、异常概述与异常体系结构
1.Error:java虚拟机无法解决的问题.如:JVM系统内部错误、资源耗尽等严重情况
//1.栈溢出:java.lang.StackOverflowError
// main(args);
//2.堆溢出:java.lang.OutOfMemoryError
// Integer[] arr = new Integer[1024*1024*1024];
2.Exception
(1)空指针
(2)试图读取不存在的文件
(3)网络连接中断
(4)数组角标越界
2、常见异常
2.1异常分类:编译时异常、运行时异常
2.2java.lang.Throwable
-
java.long.Error:一般不编写针对性的代码进行处理
-
java.long.Exception:
- 编译时异常(checked):执行javac.exe命名时,可能出现的异常
- IOException
- FileNotFoundException:文件找不到
- ClassNotFoundException:类找不到
- IOException
- 运行时异常(unchecked):执行java.exe命名时,出现的异常
- NullPointerException:空指针异常
- ArrayIndexOutOfBoundsException:数组角标越界
- ClassCastException:类型转换异常
- NuberFormatException:数字格式化异常
- InputMismathchException:输入不匹配
- ArithmeticException:算术异常
- 编译时异常(checked):执行javac.exe命名时,可能出现的异常
3、异常处理机制一:try-catch-finally
3.1异常的处理:抓抛模型
过程一:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并在此处将对象抛出
一旦抛出对象后,后面的代码不再执行
关于异常对象的产生:①系统自动产生②手动生成异常对象并抛出(throw)
过程二:“抓”:异常的处理方式:①try- catch- finally ②throws
3.2try-catch-finally的使用
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常方式1
}
catch(异常类型2 变量名2){
//处理异常方式2
}
catch(异常类型3 变量名3){
//处理异常方式3
}finally{
//一定会执行的代码
}
(1)finally是可选的
(2)使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
(3)一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常处理,一旦处理完成,就跳出当前的try-catch结构(在没有写finally情况下),继续执行之后的代码。
(4)如果catch中的异常类型不存在子父类关系,则声明顺序无所谓;如果catch中的异常类型存在子父类关系,则要求子类声明在父类的上面,否则报错。
(5)常用的异常对象处理方法:①String getMessage()②printStackTrace()
(6)在try结构中声明的变量,在try结构外面不能被调用
(7)try-catch-finally可以嵌套
总结:
(1)使用try-catch-finally处理编译异常,使得程序编译时不再报错,但是运行时仍可能报错。相当于使用try-catch-finally将编译时可能出错的异常,延迟到运行时出现。
(2)开发中,由于运行时异常比较常见,所以通常不针对运行时异常编写try-catch-finally;针对编译时异常,一定要考虑异常的处理。
3.3finally
(1)finally是可选的
(2)finally中声明的代码一定会被执行,即使catch中出现异常了,try中有return语句,catch中有return语句等情况。
(3)像数据库连接、输入输出流、网络编程中的Socket等资源,JVM不能自动回收,需要手动释放资源。此时的资源释放,需要声明在finally中。
4、异常处理机制二:throws
4.1throws+异常类型
1."throws+异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。一旦方法体执行时,出现异常,仍会在异常代码处生成异常类的对象,此对象满足throws后一场类型时,就会被抛出。异常代码后续代码不被执行。
2.try-catch-finally:真正的将异常处理掉
throws:将异常抛给方法的调用者,并没有将异常真正的处理掉
补充:方法重写的规则之一:
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常。
4.2异常处理机制的选择
(1)父类中被重写的方法没有throws方法处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方法处理。
(2)执行的方法A中,又先后调用了几个方法,且这几个方法是递进关系执行的。则这几个方法使用throws的方法进行处理,而执行的方法A可以考虑使用try-catch-finally方法进行处理。
5、手动抛出异常:throw
1.在程序执行中,处理自动抛出异常对象外,也可以手动的throw抛出异常类对象
2.throw和throws的区别:
throw:表示抛出一个异常类的对象,生成异常对象的过程。生命在方法体内。
throws:属于异常处理的一种方式,生命在方法的声明处。
6、用户定义异常类
1.继承于现有的异常结构:RuntiomException、Exception
2.提供全局常量:serialVersionUID
3.提供重载的构造器
public class Ecdef extends Exception{
static final long serialVersionUID = -338516993124229948L;
public Ecdef() {
}
public Ecdef(String msg) {
super(msg);
}
}