目录
4.2.3 - 判断语句3 - if..else if ... else
10 - Scanner类、Random类、ArrayList类
11 - String类、static关键字、Arrays类、Math类
第一节:HelloWorld
1.1 - 程序开发步骤说明
Java程序开发三步骤:编写、编译、运行。
1.2 - 编写java源程序
- 在d:\day01目录下新建文本文件,完整的文件名修改为HelloWorld.java,其中文件名称的后缀必须为.java。
- 使用notpad++记事本软件打开刚才新建的文本文件。
- 在文件中键入文本并保存,代码如下:
public class HelloWorld{
public static void main(String[] args){
system.out.print("HelloWorld");
}
}
第一个HelloWorld源程序就编写完成了,但是这个文件是程序员编写的,JVM是看不懂的,也就不能运行,因此我们必须将编写好的Java源文件编译成JVM可以看懂的字节码文件。
1.3 - 编译java源文件
在DOS命令行中,进入java源文件的目录,使用javac命令进行编译。
命令:
javac java源文件名.后缀名
举例:
javac HelloWorld.java
编译成功后,命令行没有任何提示,打开d:\day01目录,发现产生了一个新文件HelloWorld.class,该文件就是编译后的文件,是Java的可运行文件,被称为字节码文件,有了字节码文件,就可以运行程序了。
java源文件的编译工具javac.exe,在JDK安装目录的bin目录下,但是由于配置了环境变量,可以在任意目录下使用。
1.4 - 运行Java程序
在DOS命令中,进入java源文件的目录,使用java命令进行运行。
命令:
java 类名字
举例:
java HelloWorld
java HelloWorld 不要写 .class!!!!
1.5 - 入门程序说明
编译和运行是两回事
编译:是指我们编写的java源文件翻译成JVM认识的class文件,在这个过程中,javac编译器会检查我们的程序是否有错误,有错误就会提示出来,如果没有错误就会编译成功。
运行:支持将class文件交给JVM去运行,此时JVM就会去执行我们编写的程序了。
关于main方法
main方法:称为主方法。写法是固定格式不可以更改。main方法是程序的入口点或者起始点,无论我们编写多少程序,JVM在运行的时候,都会从main方法这里开始执行。
1.6 - 添加注释comment
注释:就是对代码的解释和说明,其目的是让人们能够更加轻松的了解代码,为代码添加注释是十分必要的,它不影响程序的编译和运行。
Java中有单行注释和多行注释。单行注释以 // 开头,换行结束。 多行注释以 /* 开头 以 */ 结束
1.7 - 关键字keywords
关键字:是指在程序中,java已经定义好的单词,具有特殊含义。关键字比较多,不能死记硬背,学到哪里记到哪里即可。
1.8 - 标识符
标识符:是指在程序中,我们自己定义的内容,比如类的名字、方法的名字和变量的名字等等。
命名规则:硬性要求!
- 标识符可以包含 英文字母26个(区分大小写) 、 0-9数字 、 $(美元符号) 和 _(下划线) 。
- 标识符不能以数字开头。
- 标识符不能是关键字。
命名规范:软性建议
-
类名规范:首字母大写,后面每个单词首字母大写(大驼峰式)。
-
方法名规范: 首字母小写,后面每个单词首字母大写(小驼峰式)。
-
变量名规范:全部小写。
2 - 常量、变量和数据类型
2.1 - 常量
2.1.1 - 常量概述
常量:是指在java程序中固定不变的数据。
2.1.2 - 常量分类
类型 | 含义 | 数据举例 |
整数常量 | 所有的整数 | 0,1,2,3,4,5 |
小数常量 | 所有的小数 | 0.0,-0.1,2.55 |
字符常量 | 单引号引起来,只能写一个字符,必须有内容 | ‘a’,' ','好' |
字符串常量 | 双引号引起来,可以写多个字符,也可以不写 | “A”,“Hello”,“你好” |
布尔常量 | 只有两个值 | true,false |
空常量 | 只有一个值 | null |
2.2 - 变量
2.2.1 - 变量概述
变量:常量是固定不变的数据,那么在程序中可以变化的量被称为变量。
java中要求,一个变量每次只能保存一个数据,必须要明确保存的数据类型。
2.2.2 - 数据类型
java的数据类型分为两大类:基本数据类型(整数、浮点数、字符、布尔)和引用数据类型(类、数组、接口)。
数据类型 | 关键字 | 内存占用 |
字节型 | byte | 1个字节 |
短整型 | short | 2个字节 |
整形 | int(默认) | 4个字节 |
长整型 | long | 8个字节 |
单精度浮点数 | float | 4个字节 |
双精度浮点数 | double(默认) | 8个字节 |
字符型 | char | 2个字节 |
布尔类型 | boolean | 1个字节 |
2.2.3 - 变量的定义
格式:
数据类型 变量名 = 数据值;
public class Variable{
public static void main(String[] args){
byte b = 100; // 定义字节型变量
System.out.println(b);
short s = 1000; // 定义短整型变量
Ststem.out.println(s);
int i = 123456; // 定义整形变量
Ststem.out.println(i);
long l = 1233245L; // 定义长整型变量
Ststem.out.println(l);
float f = 5.5f; // 定义单精度浮点型变量
Ststem.out.println(f);
double d = 8.5; // 定义双精度浮点型变量
Ststem.out.println(d);
boolean bool = false; // 定义布尔型变量
Ststem.out.println(bool);
char c = 'A'; // 定义字符型变量
Ststem.out.println(c);
}
}
变量名称:在同一个大括号范围内,变量的名字不可以相同。
变量赋值:定义的变量,不赋值不能使用。
3 - 数据类型转换、运算符、方法入门
3.1 - 数据类型转换
java程序要求参与的计算的数据必须要保持数据的一致性,如果数据类型不一致将发生类型的转换。
3.1.1 - 自动转换
一个int类型变量和byte类型变量进行加法运算,运算结果,变量的类型将会是int类型,这就是出现了数据类型自动转换现象。
int i = 1;
byte b = 2;
自动转换:将取值范围小的类型自动提升为取值范围大的类型。
public static void main(String[] args){
int i = 1;
byte b = 2;
int j = b + i; // byte x = b + i; 报错
System.out.println(j); // int类型和byte类型运算,结果是int类型
}
byte类型内存占有1个字节,在和int类型运算时会被提升为int类型,自动补充3个字节,因此计算结果最后还是int类型。
同样道理,一个int类型变量和一个double变量运算时,int类型会自动提升为double类型进行运算。
转换原则:范围小的类型向范围大的类型提升,byte、short、char运算时直接提升为int。
3.1.2 - 强制转换
将1.5赋值到int类型变量会产生编译失败,无法赋值。
int i = 1.5; // 错误
double类型内存8个字节,int类型内存4个字节,1.5是double类型,取值范围大于int。想要赋值成功,只有通过强制类型转换,将double类型强制转换成int类型才能赋值。
// double类型数据强制转成int,直接去掉小数点
int i = (int)1.5;
同样道理,一个short类型与1相加,我们知道会类型提升,但是还想给结果赋值给short类型变量,就需要强制类型转换。
public static void main(String[] args){
// short类型变量,内存中2个字节
short s = 1;
// 编译失败
s = s + 1;
/*
出现编译失败
s 和 1 做运算的时候,1是int类型,s 会被提升为int类型
s + 1 后的结果是int类型,将结果再赋值给short类型时发生错误
short内存中2个字节,int类型4个字节
必须将int强制转成short才能完成赋值
*/
s = (short)(s + 1); // 编译成功
}
需要注意!
浮点转换成整数,直接取消小数点,可能造成数据损失精度。
int 强制转成 short 砍掉两个字节,可能造成数据丢失。
3.1.3 - ASCII编码表
public static void main(String[] args){
char c = 'a'; // 字符类型变量
int i = 1; // 字符类型和int类型计算
System.out.println(c + i); // 输出结果是98
}
在计算机的内部都是二进制的0、1数据,如何让计算机可以直接识别人类的文字呢?就产生了编码表的概念。
编码表:就是将人类的文字和一个十进制数字进行对应起来组成一张表格。
字符 | 数值 |
0 | 48 |
9 | 57 |
A | 65 |
Z | 90 |
a | 97 |
z | 122 |
将所有的英文字母,数字,符号和十进制进行了对应,因此产生了世界上第一张编码表。
3.2 - 运算符
3.2.1 - 算术运算符
算数运算符包括: | |
---|---|
+ | 加法运算,字符串连接运算 |
- | 减法运算 |
* | 乘法运算 |
/ | 除法运算 |
% | 取模运算,两个数字相除取余数 |
++、-- | 自增自减运算 |
java中,整数使用以上运算符,无论怎么激素按,也不会得到小数。
public static void main(String[] args){
int i = 1234;
System.out.println(i / 1000 * 1000); // 计算结果是1000
}
++运算,变量自己增长1。反之,--运算,变量自己减少1,用法与++一致。
独立运算:
变量在独立运算时,前++和后++没有区别。
变量前++:例如++i。
变量后++:例如i++。
混合运算:
和其他变量放在一起,前++和后++就产生了不同。
变量前++:变量a自己加1,将加1后的结果赋值给变量b,也就是说a先计算,a和b的结果都是2。
public static void main(String[] args){
int a = 1;
int b = ++a;
System.out.println(a); //计算结果是2
System.out.println(b); //计算结果是2
}
变量后++:变量a先把自己的值1,赋值给变量b,此时变量b的值就是1,变量a自己再加1,a的结果是2,b的结果是1。
public static void main(String[] args){
int a = 1;
int b = a++;
System.out.println(a); //计算结果是2
System.out.println(b); //计算结果是1
}
+ 符号在字符串中的操作:
+ 符号在遇到字符串时,表示连接、拼接的含义。
"a" + "b" 的结果是"ab",连接含义。
public static void main(String[] args){
System.out.println("5+5="+5+5); // 输出5+5=55
}
3.2.2 - 赋值运算符
赋值运算符包括: | |
---|---|
= | 等于号 |
+= | 加等于 |
-= | 减等于 |
*= | 乘等于 |
/= | 除等于 |
%= | 取模等 |
赋值运算符,就是将符号右边的值,付给左边的变量。
public static void main(String[] args){
int i = 5;
i += 5; // 计算方式 i = i + 5 变量i先加5,再赋值变量1
System.out.println(i); // 输出结果是10
}
3.2.3 - 比较运算符
比较运算符包括: | |
---|---|
== | 比较符号两边数据是否相等,想等结果是true。 |
< | 比较符号左边的数据是否小于右边的数据,如果小于结果是true。 |
> | 比较符号左边的数据是否大于右边的数据,如果大于结果是true。 |
<= | 比较符号左边的数据是否小于或者等于右边的数据,如果小于结果是true。 |
>= | 比较符号左边的数据是否大于或者等于右边的数据,如果大于结果是true。 |
!= | 不等于符号,如果符号两边的数据不相等,结果是true。 |
比较运算符,是两个数据之间进行比较,运算结果都是布尔值true或者false。
public static void main(String[] args){
System.out.println(1 == 1); // true
System.out.println(1 < 2); // true
System.out.println(3 > 4); // false
System.out.println(3 <= 4); // true
System.out.println(3 >= 4); // false
System.out.println(3 != 4); // true
}
3.2.4 - 逻辑运算符
逻辑运算符包括: | |
---|---|
&& 短路与 | 1.两边都是true,结果是true 2.一边是false,结果是false 短路特点:符号左边是false,右边不再运算 |
|| 短路或 | 1.两边都是false,结果是false 2.一边是true,结果是true 短路特点:符号左边是true,右边不再运算 |
! 取反 | 1. !true 结果是 false 2. !false 结果是 true |
逻辑运算符,是用来连接两个布尔类型结果的运算符,运算结果都是布尔值true或者false。
public static void main(String[] args){
System.out.println(true && true); // true
System.out.println(true && false); // false
System.out.println(false && true); // false, 右边不计算
System.out.println(false || false); // false
System.out.println(false || true); // true
System.out.println(true || false); // true
System.out.println(!false); // true
}
3.2.5 - 三元运算符
三元运算符格式:
数据类型 变量名 = 布尔类型表达式 ? 结果1 : 结果2
三元运算符计算方式:
布尔类型表达式结果是true,三元运算符整体结果为结果1,赋值给变量。
布尔类型表达式结果是false,三元运算符整体结果为结果2,赋值给变量。
public static void main(String[] args){
int i = (1 == 2 ? 100 : 200);
System.out.println(i); // 200
int j = (3 <= 4 ? 500 : 600);
System.out.println(j); // 500
}
3.3 - 方法入门
3.3.1 - 概述
我们在学习运算符的时候,都为每个运算符单独创建一个新的类和main方法,我们会发现这样编写代码非常的繁琐,重复的代码过多,所以就需要使用方法来实现。
方法:就是将一个功能抽取出来,把代码单独定义在一个大括号内,形成一个单独的功能。
当我们需要这个功能的时候,就可以去调用。这样即实现了代码的复用性,也解决了代码冗余的现象。
3.3.2 - 方法的定义
定义格式:
修饰符 返回值类型 方法名 ( 参数列表 ) {
代码 ...
return;
}
定义格式解释:
修饰符:目前固定写法 public static。
返回类型:目前固定写法 void ,其他返回值类型会在后面讲解。
方法名:为我们定义的方法起名,满足标识符的规范,用来调用方法。
参数列表:目前无参数,带有参数的方法在后面讲解。
return:方法结束,因为返回类型是void,方法大括号内的return可以不写。
public static void methodName(){
System.out.println("这是一个方法");
}
3.3.3 - 方法的调用
方法在定义完毕后,方法不会自己运行,必须被调用才能执行,我们可以在主方法main中来调用我们自己定义好的方法。在主方法中,直接写要调用的方法名字就可以调用了。
public static void main(String[] args){
method(); // 调用定义的方法method
}
// 定义方法,被main方法调用
public static void method(){
System.out.println("自己定义的方法,需要被main调用运行");
}
3.3.4 - 注意事项
方法必须定义在一类中方法外。
方法不能定义在另一个方法的里面。
public class Demo(){
public static void main(String[] args){
}
// 正确写法,类中,main方法外面可以定义方法
public static void method(){}
}
public class Demo(){
public static void main(String[] args){
// 错误写法:一个方法不能定义在另一个方法内部
public static void method(){}
}
}
4 - 流程控制语句
4.1 - 流程控制
4.1.1 - 概述
在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。也就是说,程序的流程对运行结果
有直接的影响。所以,我们必须清楚每条语句的执行流程。而且,很多时候我们要通过控制语句的执行顺序来实现
我们要完成的功能。
4.1.2 - 顺序结构
public static void main(String[] args){
//顺序执行,根据编写的顺序,从上到下运行
System.out.println(1);
System.out.println(2);
System.out.println(3);
}
4.2 - 判断语句
4.2.1 - 判断语句1 - if
if(关系表达式){
语句体;
}
执行流程:
首先判断关系表达式看其结果是true还是false。
如果是true就执行语句体。
如果是false就不执行语句体。
public static void main(String[] args){
System.out.println("开始");
// 定义两个变量
int a = 10;
int b = 20;
//变量使用if判断
if (a == b){
System.out.println("a等于b");
}
int c = 10;
if(a == c){
System.out.println("a等于c");
}
System.out.println("结束");
}
4.2.2 - 判断语句2 - if...else
if(关系表达式) {
语句体1;
}else {
语句体2;
}
执行流程:
首先判断关系表达式看其结果是true还是false
如果是true就执行语句体1
如果是false就执行语句体2
public static void main(String[] args){
// 判断给定的数据是奇数还是偶数
// 定义变量
int a = 1;
if(a % 2 == 0) {
System.out.println("a是偶数");
} else{
System.out.println("a是奇数");
}
System.out.println("结束");
}
4.2.3 - 判断语句3 - if..else if ... else
if (判断条件1) {
执行语句1;
} else if (判断条件2) {
执行语句2;
}
...
}else if (判断条件n) {
执行语句n;
} else {
执行语句n+1;
}
执行流程:
首先判断关系表达式1看其结果是true还是false
如果是true就执行语句体1
如果是false就继续判断关系表达式2看其结果是true还是false
如果是true就执行语句体2
如果是false就继续判断关系表达式…看其结果是true还是false
…
如果没有任何关系表达式为true,就执行语句体n+1。
public static void main(String[] args) {
// x和y的关系满足如下:
// x>=3 y = 2x + 1;
//‐1<=x<3 y = 2x;
// x<=‐1 y = 2x – 1;
// 根据给定的x的值,计算出y的值并输出。
// 定义变量
int x = 5;
int y;
if (x>= 3) {
y = 2 * x + 1;
} else if (x >= ‐1 && x < 3) {
y = 2 * x;
} else {
y = 2 * x ‐ 1;
}
System.out.println("y的值是:"+y);
}
4.2.4 - if语句和三元运算符的互换
在某些简单的应用中,if语句是可以和三元运算符互换使用的。
public static void main(String[] args) {
int a = 10;
int b = 20;
//定义变量,保存a和b的较大值
int c;
if(a > b) {
c = a;
} else {
c = b;
}
//可以上述功能改写为三元运算符形式
c = a > b ? a:b;
}
4.3 - 选择语句
4.3.1 - 选择语句 - switch
switch语句格式:
switch(表达式) {
case 常量值1:
语句体1;
break;
case 常量值2:
语句体2;
break;
...
default:
语句体n+1;
break;
}
执行流程:
首先计算出表达式的值
其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结
束。
最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉
public static void main(String[] args) {
//定义变量,判断是星期几
int weekday = 6;
//switch语句实现选择
switch(weekday) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期日");
break;
default:
System.out.println("你输入的数字有误");
break;
}
}
switch语句中,表达式的数据类型,可以是byte,short,int,char,enum(枚举),JDK7后可以接收字符串
4.3.2 - case的穿透性
在switch语句中,如果case的后面不写break,将出现穿透现象,也就是不会在判断下一个case的值,直接向后运
行,直到遇到break,或者整体switch结束。
public static void main(String[] args) {
int i = 5;
switch (i){
case 0:
System.out.println("执行case0");
break;
case 5:
System.out.println("执行case5");
case 10:
System.out.println("执行case10");
default:
System.out.println("执行default");
}
}
上述程序中,执行case5后,由于没有break语句,程序会一直向后走,不会在判断case,也不会理会break,直接
运行完整体switch。
由于case存在穿透性,因此初学者在编写switch语句时,必须要写上break
4.4 - 循环语句
4.4.1 - 循环概述
循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复
执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形
成死循环。
4.4.2 - 循环语句1 - for
for(初始化表达式①; 布尔表达式②; 步进表达式④){
循环体③
}
执行流程:
执行顺序:①②③④>②③④>②③④…②不满足为止。
①负责完成循环变量初始化
②负责判断是否满足循环条件,不满足则跳出循环
③具体执行的语句
④循环后,循环条件所涉及变量的变化情况
public static void main(String[] args) {
//控制台输出10次HelloWorld,不使用循环
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐");
//用循环改进,循环10次
//定义变量从0开始,循环条件为<10
for(int x = 0; x < 10; x++) {
System.out.println("HelloWorld"+x);
}
}
4.2.3 - 循环语句2 - while
初始化表达式①
while(布尔表达式②){
循环体③
步进表达式④
}
执行流程:
执行顺序:①②③④>②③④>②③④…②不满足为止。
①负责完成循环变量初始化。
②负责判断是否满足循环条件,不满足则跳出循环。
③具体执行的语句。
④循环后,循环变量的变化情况。
public static void main(String[] args) {
//while循环实现打印10次HelloWorld
//定义初始化变量
int i = 1;
//循环条件<=10
while(i<=10){
System.out.println("HelloWorld");
//步进
i++;
}
}
4.2.4 - 循环语句3 - do...while
初始化表达式①
do{
循环体③
步进表达式④
}while(布尔表达式②);
执行流程:
执行顺序:①③④>②③④>②③④…②不满足为止。
①负责完成循环变量初始化。
②负责判断是否满足循环条件,不满足则跳出循环。
③具体执行的语句
④循环后,循环变量的变化情况
public static void main(String[] args) {
int x=1;
do {
System.out.println("HelloWorld");
x++;
}while(x<=10);
}
do...while循环的特点:无条件执行一次循环体,即使我们将循环条件直接写成false,也依然会循环一次。这样的
循环具有一定的风险性,因此初学者不建议使用do...while循环。
4.2.5 - 循环语句区别
for 和 while 的小区别:
控制条件语句所控制的那个变量,在for循环结束后,就不能再被访问到了,而while循环结束还可以继
续使用,如果你想继续使用,就用while,否则推荐使用for。原因是for循环结束,该变量就从内存中消
失,能够提高内存的使用效率。
在已知循环次数的时候使用推荐使用for,循环次数未知的时推荐使用while
4.2.6 - 跳出语句
break
使用场景:终止switch或者循环
public static void main(String[] args) {
for (int i = 1; i<=10; i++) {
//需求:打印完两次HelloWorld之后结束循环
if(i == 3){
break;
}
System.out.println("HelloWorld"+i);
}
}
continue
使用场景:结束本次循环,继续下一次循环
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
//需求:不打印第三次HelloWorld
if(i == 3){
continue;
}
System.out.println("HelloWorld"+i);
}
}
4.5 - 扩展知识
4.5.1 - 死循环
死循环:也就是循环中的条件永远为true,死循环的是永不结束的循环。例如:while(true){}。
4.5.2 - 嵌套循环
所谓嵌套循环,是指一个循环的循环体是另一个循环。比如for循环里面还有一个for循环,就是嵌套循环。总
共的循环次数=外循环次数*内循环次数
for(初始化表达式①; 循环条件②; 步进表达式⑦) {
for(初始化表达式③; 循环条件④; 步进表达式⑥) {
执行语句⑤;
}
}
嵌套循环执行流程:
执行顺序:①②③④⑤⑥>④⑤⑥>⑦②③④⑤⑥>④⑤⑥
外循环一次,内循环多次。
比如跳绳:一共跳5组,每组跳10个。5组就是外循环,10个就是内循环。
public static void main(String[] args) {
//5*8的矩形,打印5行*号,每行8个
//外循环5次,内循环8次
for(int i = 0; i < 5; i++){
for(int j = 0; j < 8; j++){
//不换行打印星号
System.out.print("*");
}
//内循环打印8个星号后,需要一次换行
System.out.println();
}
}
5 - 开发工具Intelij IDEA
5.1 - 开发工具概述
IDEA是一个专门针对Java的集成开发工具(IDE),由Java语言编写。所以,需要有JRE运行环境并配置好环境变量。
它可以极大地提升我们的开发效率。可以自动编译,检查错误。在公司中,使用的就是IDEA进行开发。
5.2 - IDEA软件首次驱动
5.3 - 创建包和类
5.4 - 字体设置
5.5 - IDEA的项目目录
我们创建的项目,在d:\ideawork目录的demo下
.idea 目录和 demo.iml 和我们开发无关,是IDEA工具自己使用的
out 目录是存储编译后的.class文件
src 目录是存储我们编写的.java源文件
5.6 - IDEA常用快捷键
快捷键 | 功能 |
Alt+Enter | 导入包,自动修正代码 |
Ctrl+Y | 删除光标所在行 |
Ctrl+D | 复制光标所在行的内容,插入光标位置下面 |
Ctrl+Alt+L | 格式化代码 |
Ctrl+/ | 单行注释 |
Ctrl+Shift+/ | 选中代码注释,多行注释,再按取消注释 |
Alt+Ins | 自动生成代码,toString,get,set等方法 |
Alt+Shift+上下箭头 | 移动当前代码行 |
6 - 方法
6.1 - 定义方法的格式详解
修饰符 返回值类型 方法名(参数列表){
//代码省略...
return 结果;
}
修饰符: public static 固定写法
返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者
参数列表:方法在运算过程中的未知数据,调用者调用方法时传递
return:将方法执行后的结果带给调用者,方法执行到 return ,整体方法运行结束
6.2 - 定义方法的两个明确
需求:定义方法实现两个整数的求和计算。
明确返回值类型:方法计算的是整数的求和,结果也必然是个整数,返回值类型定义为int类型。
明确参数列表:计算哪两个整数的和,并不清楚,但可以确定是整数,参数列表可以定义两个int类型的
变量,由调用者调用方法时传递
public class Method_Demo2 {
public static void main(String[] args) {
// 调用方法getSum,传递两个整数,这里传递的实际数据又称为实际参数
// 并接收方法计算后的结果,返回值
int sum = getSum(5, 6);
System.out.println(sum);
}
/* 定义
计算两个整数和的方法
返回值类型,计算结果是int
参数:不确定数据求和,定义int参数.参数又称为形式参数
*/
public static int getSum(int a, int b) {
return a + b;
}
}
程序执行,主方法 main 调用 getSum 方法,传递了实际数据 5和6 ,两个变量 a和b 接收到的就是实际参数,并
将计算后的结果返回,主方法 main 中的变量 sum 接收的就是方法的返回值。
6.3 - 定义方法的注意事项
定义位置:类中方法外面。
返回值类型:必须要和return语句返回的类型相同,否则编译失败。
// 返回值类型要求是int
public static int getSum() {
return 5;// 正确,int类型
return 1.2;// 错误,类型不匹配
return true;// 错误,类型不匹配
}
不能在 return 后面写代码, return 意味着方法结束,所有后面的代码永远不会执行,属于无效代码。
public static int getSum(int a,int b) {
return a + b;
System.out.println("Hello");// 错误,return已经结束,这里不会执行,无效代码
}
6.4 - 调用方法的三种形式
直接调用:直接写方法名调用
public static void main(String[] args) {
print();
}
public static void print() {
System.out.println("方法被调用");
}
赋值调用:调用方法,在方法前面定义变量,接受方法返回值。
public static void main(String[] args) {
int sum = getSum(5,6);
System.out.println(sum);
}
public static int getSum(int a,int b) {
return a + b;
}
输出语句调用:在输出语句中调用方法,System.out,println(方法名())。
public static void main(String[] args) {
System.out.println(getSum(5,6));
}
public static int getSum(int a,int b) {
return a + b;
}
不能用输出语句调用void类型方法,因为方法执行后没有结果,也就打印不出任何内容。
6.5 - 方法重载
指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返
回值类型无关。
参数列表:个数不同,数据类型不同,顺序不同。
重载方法调用:JVM通过方法的参数列表,调用不同的方法
public class Method_Demo6 {
public static void main(String[] args) {
//定义不同数据类型的变量
byte a = 10;
byte b = 20;
short c = 10;
short d = 20;
int e = 10;
int f = 10;
long g = 10;
long h = 20;
// 调用
System.out.println(compare(a, b));
System.out.println(compare(c, d));
System.out.println(compare(e, f));
System.out.println(compare(g, h));
}
// 两个byte类型的
public static boolean compare(byte a, byte b) {
System.out.println("byte");
return a == b;
}
// 两个short类型的
public static boolean compare(short a, short b) {
System.out.println("short");
return a == b;
}
// 两个int类型的
public static boolean compare(int a, int b) {
System.out.println("int");
return a == b;
}
// 两个long类型的
public static boolean compare(long a, long b) {
System.out.println("long");
return a == b;
}
}
7 - 数组
7.1 - 数组定义和访问
7.1.1 - 容器概述
容器概念:是将多个数据存储到一起,每个数据称为该容器的元素。
数组概念:数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致。
7.1.2 - 数组的定义方式
方式一:
数组存储的数据类型[] 数组名字 = new 数组存储的数据类型[长度];
数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
[] : 表示数组。
数组名字:为定义的数组起个变量名,满足标识符规范,可以使用名字操作数组。
new:关键字,创建数组使用的关键字。
数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
[长度]:数组的长度,表示数组容器中可以存储多少个元素。
// 定义可以存储三个整数的数组容器,代码如下:
int[] arr = new int[3];
注意!数组有定长特性,长度一旦指定,不可更改。
方式二:
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};
// 定义存储1,2,3,4,5整数的数组容器
int[] arr = new int[]{1,2,3,4,5};
方式三:
数据类型[] 数组名 = {元素1,元素2,元素3...};
//定义存储1,2,3,4,5整数的数组容器
int[] arr = {1,2,3,4,5};
7.1.3 - 数组的访问
索引: 每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引
(index),可以通过数组的索引访问到数组中的元素。
数组名[索引]
数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的
长度,语句为: 数组名.length ,属性length的执行结果是数组的长度,int类型结果。由次可以推断出,数
组的最大索引值为 数组名.length-1 。
public static void main(String[] args){
int[] arr = new int[]{1,2,3,4,5};
System.out.println(arr.length);
}
索引访问数组中的元素:
数组名[索引]=数值,为数组中的元素赋值
变量=数组名[索引],获取出数组中的元素
public static void main(String[] args){
int[] arr = {1,2,3,4,5}; // 定义存储int类型数组,赋值元素1,2,3,4,5
arr[0] = 6; // 为0索引元素赋值为6
int i = arr[0]; // 获取数组0索引上的元素
System.out.println(i);
System.out.println(arr[0]); // 直接输出数组0索引元素
}
7.2 - 数组原理内存
7.2.1 - 内存概述
内存是计算机中的重要原件,临时存储区域,作用是运行程序。我们编写的程序是存放在硬盘中的,在硬盘中的程
序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。
Java虚拟机要运行程序,必须要对内存进行空间的分配和管理
7.2.2 - Java虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
区域名称 | 作用 |
寄存器 | 给CPU使用,和我们开发无关。 |
本地方法栈 | JVM在使用操作系统功能的时候使用,和我们开发无关。 |
方法区 | 存储可以运行的class文件。 |
堆内存 | 存储对象或者数组,new来创建的,都存储在堆内存。 |
方法栈 | 方法运行时使用的内存,比如main方法运行,进入方法栈中执行。 |
7.2.3 - 数组在内存中的存储
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr);//[I@5f150435
}
以上方法执行,输出的结果是[I@5f150435,这个是什么呢?是数组在内存中的地址。new出来的内容,都是在堆
内存中存储的,而方法中的变量arr保存的是数组的地址。
输出arr[0],就会输出arr保存的内存地址中数组中0索引上的元素。
public static void main(String[] args) {
int[] arr = new int[3];
int[] arr2 = new int[2];
System.out.println(arr);
System.out.println(arr2);
}
public static void main(String[] args) {
// 定义数组,存储3个元素
int[] arr = new int[3];
//数组索引进行赋值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//输出3个索引上的元素值
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//定义数组变量arr2,将arr的地址赋值给arr2
int[] arr2 = arr;
arr2[1] = 9;
System.out.println(arr[1]);
}
7.3 - 数组的常见操作
7.3.1 - 数组越界异常
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println(arr[3]);
}
创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运
行后,将会抛出 ArrayIndexOutOfBoundsException 数组越界异常。在开发中,数组的越界异常是不能出现的,一
旦出现了,就必须要修改我们编写的代码。
7.3.2 - 数组空指针异常
public static void main(String[] args) {
int[] arr = {1,2,3};
arr = null;
System.out.println(arr[0]);
}
arr = null 这行代码,意味着变量arr将不会在保存数组的内存地址,也就不允许再操作数组了,因此运行的时候
会抛出 NullPointerException 空指针异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修
改我们编写的代码
7.3.3 - 数组遍历【重点】
数组遍历: 就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);
System.out.println(arr[4]);
}
以上代码是可以将数组中每个元素全部遍历出来,但是如果数组元素非常多,这种写法肯定不行,因此我们需要改
造成循环的写法。数组的索引是 0 到 lenght-1 ,可以作为循环的条件出现。
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
7.3.4 - 数组获取最大值元素
最大值获取:从数组的所有元素中找出最大值。
实现思路:
1 - 定义变量,保存数组0索引上的元素
2 - 遍历数组,获取出数组中的每个元素
3 - 将遍历到的元素和保存数组0索引上值的变量进行比较
4 - 如果数组元素的值大于了变量的值,变量记录住新的值
5 - 数组循环遍历结束,变量保存的就是数组中的最大值
public static void main(String[] args) {
int[] arr = { 5, 15, 2000, 10000, 100, 4000 };
//定义变量,保存数组中0索引的元素
int max = arr[0];
//遍历数组,取出每个元素
for (int i = 0; i < arr.length; i++) {
//遍历到的元素和变量max比较
//如果数组元素大于max
if (arr[i] > max) {
//max记录住大值
max = arr[i];
}
}
System.out.println("数组最大值是: " + max);
}
7.3.5 - 数组反转
数组的反转: 数组中的元素颠倒顺序,例如原始数组为1,2,3,4,5,反转后的数组为5,4,3,2,1
实现思想:
数组最远端的元素互换位置。
实现反转,就需要将数组最远端元素位置交换
定义两个变量,保存数组的最小索引和最大索引
两个索引上的元素交换位置
最小索引++,最大索引--,再次交换位置
最小索引超过了最大索引,数组反转操作结束
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
/*
循环中定义变量min=0最小索引
max=arr.length‐1最大索引
min++,max‐‐
*/
for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) {
//利用第三方变量完成数组中的元素交换
int temp = arr[min];
arr[min] = arr[max];
arr[max] = temp;
}
// 反转后,遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
7.4 - 数组作为方法参数和返回值
7.4.1 - 数组作为方法参数
数组作为方法参数传递,传递的参数是数组内存的地址。
public static void main(String[] args) {
int[] arr = { 1, 3, 5, 7, 9 };
//调用方法,传递数组
printArray(arr);
}
/*
创建方法,方法接收数组类型的参数
进行数组的遍历
*/
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
7.4.2 - 数组作为方法返回值
数组作为方法的返回值,返回的是数组的内存地址。
public static void main(String[] args) {
//调用方法,接收数组的返回值
//接收到的是数组的内存地址
int[] arr = getArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
/*
创建方法,返回值是数组类型
return返回数组的地址
*/
public static int[] getArray() {
int[] arr = { 1, 3, 5, 7, 9 };
//返回数组的地址,返回到调用者
return arr;
}
7.4.3 - 方法的参数类型区别
方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值。
8 - 类与对象
8.1 - 面向对象思想概述
8.1.1 - 概述
Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,
使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面
向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算
机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去
操作实现。
8.1.2 - 特点
面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。
面向对象的语言中,包含了三大基本特征,即封装、继承和多态。
8.2 - 类和对象
8.2.1 - 什么是类
类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该
类事物。
8.2.2 - 什么是对象
对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性
和行为
8.2.3 - 类与对象的关系
类是对一类事物的描述,是抽象的。
对象是一类事物的实例,是具体的。
类是对象的模板,对象是类的实体。
8.3 - 类的定义
8.3.1 - 事物与类的对比
属性:事物的状态信息。 行为:事物能够做什么
成员变量:对应事物的属性 成员方法:对应事物的行为
8.3.2 - 类的定义格式
public class ClassName{
// 成员变量
// 成员方法
}
定义类:就是定义类的成员,包括成员变量和成员方法。
成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
成员方法:和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面课程中再详细
讲解。
public class Student{
// 成员变量
String name; // 姓名
int age; // 年龄
// 成员方法
public void study(){
System.out.println("好好学习,天天向上.");
}
public void eat(){
System.out.println("吃饭");
}
}
8.4 - 对象的使用
8.4.1 - 对象的使用格式
// 创建对象
类名 对象名 = new 类名();
// 使用对象访问类中成员
对象名.成员变量;
对象名.成员方法();
// 对象的使用格式举例
public void Test01_Student(){
public static void main(String[] args){
// 创建对象格式: 类名 对象名 = new 类名();
Student s = new Student();
System.out.println("s:" + s);
// 直接输出成员变量值
System.out.println("姓名:" + s.name); // null
System.out.println("年龄:" + s.age); // 0
System.out.println("---------------");
// 给成员变量赋值
s.name = "赵丽颖";
s.age = 18;
// 再次输出成员变量
System.out.println("姓名:" + s.name); // 赵丽颖
System.out.println("年龄:" + s.age); // 18
System.out.println("---------------");
// 调用成员方法
s.study();
s.eat();
}
}
8.4.2 - 成员变量的默认值
数据类型 | 默认值 | |
---|---|---|
基本类型 | 整数(byte,short,int,long) | 0 |
浮点数(float,double) | 0.0 | |
字符(char) | ’\u0000‘ | |
布尔(boolean) | false | |
引用类型 | 数组、类、接口 | null |
8.5 - 对象内存
8.5.1 - 一个对象,调用一个方法内存图
通过上图,我们可以理解,在栈内存中运行的方法,遵循"先进后出,后进先出"的原则。变量p指向堆内存中
的空间,寻找方法信息,去执行该方法。
8.5.2 - 两个对象,调用同一个方法内存图
对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息
只保存一份,节约内存空间。
8.5.3 - 一个引用,作为参数传递到方法中内存图
引用类型作为参数,传递的是地址值。
8.6 - 成员变量和局部变量的区别
在类中的位置不同
成员变量:类中,方法外
局部变量:方法中或者方法声明上(形式参数)
作用范围不一样
成员变量:类中
局部变量:方法中
初始化值的不同
成员变量:有默认值
局部变量:没有默认值。必须先定义,赋值,最后使用
在内存中的位置不同
成员变量:堆内存
局部变量:栈内存
生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
9 - 封装
9.1 - 封装概述
9.1.1 - 概述
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。
封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的
方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
9.1.2 - 原则
将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。
9.2 - 封装的步骤
- 使用private关键字来修饰成员变量
- 对需要访问的成员变量,提供一对对应的getXxx方法、setXxx方法。
9.3 - 封装的操作 —— private关键字
9.3.1 - private的含义
- private是一个权限修饰符,代表最小权限
- 可以修饰成员变量和成员方法
- 被private修饰后的成员变量和成员方法,只在本类中才能访问
9.3.2 - private的使用格式
pirvate 数据类型 变量名;
// 使用private修饰成员变量
public class Student{
private String name;
private int age;
}
// 提供getXxx方法和setXxx方法,可以访问成员变量
public class Student{
private String name;
private int age;
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
public void setAge(int a){
age = a;
}
public int getAge(){
return age;
}
}
9.4 - 封装优化1 —— this关键字
9.4.1 - this的含义
this代表所在类的当前对象的引用(地址值),即对象自己的引用。
方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。
9.4.2 - this的使用格式
// this的使用格式
this.成员变量名;
// 使用this修饰方法中的变量,解决成员变量被隐藏的问题
public class Student{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age
}
}
//方法中只有一个变量名时,默认也是使用this修饰,可以省略不写
9.5 - 封装优化2 —— 构造方法
当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。
9.5.1 - 构造方法的定义格式
// 构造方法的定义格式
修饰符 构造方法名(参数列表){
方法体
}
构造方法的写法上,方法名与它所在的类名相同。它没有返回值,所以不需要返回值类型,甚至不需要void。
public class Student {
private String name;
private int age;
// 无参数构造方法
public Student() {}
// 有参数构造方法
public Student(String name,int age) {
this.name = name;
this.age = age;
}
}
9.5.2 - 注意事项
- 如果你不提供构造方法,系统会给出无参数构造方法。
- 如果你提供了构造方法,系统将不再提供无参数构造方法。
- 构造方法是可以重载的,既可以定义参数,也可以不定义参数。
9.6 - 标准代码 —— JavaBean
JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的,并且具有无
参数的构造方法,提供用来操作成员变量的 set 和 get 方法。
public class ClassName{
//成员变量
//构造方法
//无参构造方法【必须】
//有参构造方法【建议】
//成员方法
//getXxx()
//setXxx()
}
编写符合 JavaBean 规范的类,以学生类为例,标准代码如下:
public class Student {
//成员变量
private String name;
private int age;
//构造方法
public Student() {}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
//成员方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
10 - Scanner类、Random类、ArrayList类
10.1 - Scanner类
10.1.1 - 什么是Scanner类
一个可以解析基本类型和字符串的简单文本扫描器。
Scanner sc = new Scanner(System.in); // System.in 系统输入指的是通过键盘录入数据
int i = sc,nextInt();
10.1.2 - 引用类型使用步骤
导包
使用import关键字导包,在类的所有代码之前导包,引入要使用的类型,java.lang包下的所有类无需导入。
import 包名.类名;
创建对象
使用该类的构造方法,创建一个该类的对象。
数据类型 变量名 = new 数据类型(参数列表);
Scanner sc = new Scanner(System.in);
调用方法
调用该类的成员方法,完成指定的功能。
变量名.方法名();
int i = sc.nextInt(); // 接受一个键盘录入的整数
10.1.3 - Scanner使用步骤
查看类
java.util.Scanner 该类需要import导入后使用
查看构造方法
public Scanner(InputStream source) 构造一个新的Scanner,它生成的值时从指定的输入流扫描的。
查看成员方法
public int nextInt(); 将输入信息的下一个标记扫描为一个int值。
使用Scanner类,完成接受键盘录入数据的操作。
import java.util.Scanner;
public class Demo01_Scanner{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请录入一个整数:");
int i = sc.nextInt();
System.out.println("i:" + i);
}
}
10.1.4 - 练习
// 键盘录入两个数据并求和
import java.util.Scanner;
public class Test01_Scanner{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数据:");
int a = sc.nextInt();
System.out.println("请输入第二个数据:");
int b = sc.nextInt();
int sum = a + b;
System.out.println("sum: " + sum);
}
}
// 键盘录入三个数据并获取最大值
import java.util.Scanner;
public class Test02_Scanner{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数据:");
int a = sc.nextInt();
System.out.println("请输入第二个数据:");
int b = sc.nextInt();
System.out.println("请输入第三个数据:");
int c = sc.nextInt();
int temp = (a > b ? a : b);
int max = (temp > c ? temp : c);
System.out.println("max: " + max);
}
}
10.1.5 - 匿名对象
创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。虽然是创建对象的简化写法,但是应用
场景非常有限。
匿名对象:没有变量名的对象。
new 类名(参数列表);
new Scanner(System.in);
// 1.创建匿名对象直接调用方法,没有变量名
new Scanner(System.in).nextInt();
// 2.一旦调用两次方法,就是创建了两个对象,造成了浪费
new Scanner(System.in).nextInt();
new Scanner(System.in).nextInt();
// 注意! 一个匿名对象,只能使用一次。
// 3.匿名对象可以作为方法的参数和返回值
// 作为参数
class Test {
public static void main(String[] args) {
// 普通方式
Scanner sc = new Scanner(System.in);
input(sc);
//匿名对象作为方法接收的参数
input(new Scanner(System.in));
}
public static void input(Scanner sc){
System.out.println(sc);
}
}
// 作为返回值
class Test2 {
public static void main(String[] args) {
// 普通方式
Scanner sc = getScanner();
}
public static Scanner getScanner(){
//普通方式
//Scanner sc = new Scanner(System.in);
//return sc;
//匿名对象作为方法返回值
return new Scanner(System.in);
}
}
10.2 - Random类
10.2.1 - 什么是Random类
此类的实例用于生成伪随机数。
Random r = new Random();
int i = r.nextInt();
10.2.2 - Random使用步骤
查看类:
java.util.Random; // 该类需要import导入后使用
查看构造方法:
public Random(); // 创建一个新的随机数生成器
查看成员方法:
public int nextInt(int n);
// 返回一个伪随机数,范围在0(包括)和指定值n(不包括)之间的int值。
// 使用Random类,完成生成3个10以内的随机整数的操作
import java.util.Random;
public class Demo01_Random{
public static void main(String[] args){
Random r = new Random();
for(int i = 0; i < 3; i++){
int number = r.nextInt(10);
System.out.println("number: " + number);
}
}
}
10.2.3 - 练习
// 获取 1 - n 之间的随机数
import java.util.Random
public class Test01_Random{
public static void main(String[] args){
int n = 50;
Random r = new Random();
int number = r.nextInt(n) + 1;
System.out.println("number: " + number);
}
}
// 游戏开始时,会随机生成一个1-100之间的整数 number 。玩家猜测一个数字 guessNumber ,会与 number 作比较,系统提示大了或者小了,直到玩家猜中,游戏结束。
import java.util.Random;
import java.util.Scanner;
public class Test02_Random{
public static void main(String[] args){
Random r = new Random();
int number = r.nextInt(100) + 1;
while(true){
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要猜的数字(1-100):");
int guessNumber = sc.nextInt();
if(guessNumber > number){
System.out.println("你猜的数据 " + guessNumber + " 大了");
} else if(guessNumber < number){
System.out.println("你猜的数据 " + guessNumber + " 小了");
} else {
System.out.println("恭喜你,猜中你");
break;
}
}
}
}
10.3 - ArrayList类
10.3.1 - 引入——对象数组
// 使用学生数据,存储三个学生对象
public class Student{
private String name;
private int age;
public Student(){};
public Student(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
public class Test01_Student{
public static void main(String[] args){
Student[] students = new Student[3];
Student s1 = new Student("曹操",40);
Student s2 = new Student("刘备",35);
Student s3 = new Student("孙权",30);
students[0] = s1;
students[1] = s2;
students[2] = s3;
for(int i = 0; i < students.length; i++){
Student s = students[i];
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
// 数组的长度是固定的,无法适应数据变化的需求
// Java提供了另一个容器 java.util.ArrayList 集合类,让我们可以更便捷的存储和操作对
象数据.
10.3.2 - 什么是ArrayList类
java.util.ArrayList 是大小可变的数组的实现,存储在内的数据称为元素。此类提供一些方法来操作内部存储的元素。 ArrayList 中可不断添加元素,其大小也自动增长。
10.3.3 - ArrayList使用步骤
查看类:
// java.util.ArrayList <E> :该类需要 import导入使后使用。
// <E> ,表示一种指定的数据类型,叫做泛型。 E ,取自Element(元素)的首字母。在出现 E 的地方,我们使用一种引用数据类型将其替换即可,表示我们将存储哪种引用类型的元素。
ArrayList<String>,ArrayList<Student>
查看构造方法:
// public ArrayList() :构造一个内容为空的集合。
ArrayList<String> list = new ArrayList<String>();
查看成员方法:
// public boolean add(E e) : 将指定的元素添加到此集合的尾部。
// 参数 E e ,在构造ArrayList对象时, <E> 指定了什么数据类型,那么 add(E e) 方法中,只能添加什么数据类型的对象。
// 使用ArrayList类,存储三个字符串元素。
public class Test02_Student_ArrayList{
public static void main(String[] args){
AraayList<String> list = new ArrayList<String>();
String s1 = "曹操";
String s2 = "刘备";
String s3 = "孙权";
System.out.println(list);
list.add(s1);
list.add(s2);
lise.add(s3);
System.out.println(list);
}
}
10.3.4 - 常用方法和遍历
// 对于元素的操作,基本体现在——增、删、查。常用的方法有:
// public boolean add(E e) :将指定的元素添加到此集合的尾部。
// public E remove(int index) :移除此集合中指定位置上的元素。返回被删除的元素
// public E get(int index) :返回此集合中指定位置上的元素。返回获取的元素
// public int size() :返回此集合中的元素数。遍历集合时,可以控制索引范围,防止越界
public class Demo01_ArrayList_Method{
public static void main(String[] args){
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("java");
System.out.println("get: " + list.get(0));
System.out.println("get: " + list.get(1));
System.out.println("get: " + list.get(2));
System.out.println("size: " + list.size());
System.out.println("remove: " + list.remove(0));
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
}
10.3.5 - 如何存储基本数据类型
ArrayList对象不能存储基本类型,只能存储引用类型的数据。类似 <int> 不能写,但是存储基本数据类型对应的
包装类型是可以的。所以,想要存储基本类型数据, <> 中的数据类型,必须转换后才能编写。
基本类型 | 基本类型包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
只有 Integer 和 Character 需要特殊记忆,其他基本类型只是首字母大写即可。
public class Demo2_ArrayList_Method{
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list);
}
}
10.3.6 - ArrayList练习
// 获取所有偶数元素集合的方法
public class Test_ArrayList{
public static void main(String[] args){
Random random = new Random();
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0; i < 20; i++){
int r = random.nextInt(1000) + i;
list.add(r);
}
ArrayList<Integer> arrayList = getArrayList(list);
System.out.println(arrayList);
}
public static ArrayList<Integer> getArrayList(ArrayList<Integer> list){
ArrayList<Integer> smallList = new ArrayList<Integer>();
for(int i = 0; i < list.size(); i++){
Integer num = list.get(i);
if(num % 2 == 0){
smallList.add(num);
}
}
return smallList;
}
}
11 - String类、static关键字、Arrays类、Math类
11.1 - String类
11.1.1 - 概述及特点
概述:
java.lang.String 类代表字符串。Java程序中所有的字符串文字(例如 "abc" )都可以被看作是实现此类的实
例。
类 String 中包括用于检查各个字符串的方法,比如用于比较字符串,搜索字符串,提取子字符串以及创建具有翻
译为大写或小写的所有字符的字符串的副本。
特点:
1. 字符串不变:字符串的值在创建后不能被更改。
String s1 = 'abc';
s1 += "d";
System.out.println(s1); // "abcd"
// 内存中有"abc","abcd"两个对象,s1从指向"abc",改变指向,指向了"abcd"
2. 因为String对象时不可变的,所以它们可以被共享。
String s1 = "abc";
String s2 = "abc";
// 内存中只有一个"abc"对象被创建,同时被s1和s2共享
3. “abc”等效于"char[] data = {'a','b','c'}"
例如:
String str = "abc";
相当于:
char data[] = {'a','b','c'};
String str = new String(data);
// String底层是靠字符数组实现的
11.1.2 - 使用步骤
查看类:
java.lang.String :此类不需要导入。
查看构造方法:
// public String() :初始化新创建的 String对象,以使其表示空字符序列。
// public String(char[] value) :通过当前参数中的字符数组来构造新的String。
// public String(byte[] bytes) :通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String。
// 无参构造
String str = new String();
// 通过字符数组构造;
String chars[] = {'a','b','c'};
String str2 = new String(chars);
// 通过字节数组构造
byte bytes[] = {97,98,99};
String str3 = new String(bytes);
11.1.3 - 常用方法
判断功能的方法:
// public boolean equals(Object anObject) 将此字符串与指定对象进行比较
// public boolean equalsIgnoreCase(String antherString) 将此字符串与指定对象进行比较,忽略大小写
public class String_Demo01{
public static void main(String[] args){
String s1 = "hello";
String s2 = "hello";
String s3 = "HELLO";
System.out.println(s1.equals(s2));
System.out.println(s2.equals(s3));
System.out.println(" - - - - - - - -")
System.out.println(s1.equalsIgnoreCase(s2));
System.out.println(s1.equalsIgnoreCase(s3));
}
}
获取功能的方法:
// public int length () :返回此字符串的长度。
// public String concat (String str) :将指定的字符串连接到该字符串的末尾。
// public char charAt (int index) :返回指定索引处的 char值。
// public int indexOf (String str) :返回指定子字符串第一次出现在该字符串内的索引。
// public String substring (int beginIndex) :返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。
// public String substring (int beginIndex, int endIndex) :返回一个子字符串,从beginIndex到endIndex截取字符串。含beginIndex,不含endIndex
public class String_Demo02 {
public static void main(String[] args) {
//创建字符串对象
String s = "helloworld";
// int length():获取字符串的长度,其实也就是字符个数
System.out.println(s.length());
System.out.println("‐‐‐‐‐‐‐‐");
// String concat (String str):将将指定的字符串连接到该字符串的末尾.
String s = "helloworld";
String s2 = s.concat("**hello itheima");
System.out.println(s2); // helloworld**hello itheima
// char charAt(int index):获取指定索引处的字符
System.out.println(s.charAt(0));
System.out.println(s.charAt(1));
System.out.println("‐‐‐‐‐‐‐‐");
// int indexOf(String str):获取str在字符串对象中第一次出现的索引,没有返回‐1
System.out.println(s.indexOf("l"));
System.out.println(s.indexOf("owo"));
System.out.println(s.indexOf("ak"));
System.out.println("‐‐‐‐‐‐‐‐");
// String substring(int start):从start开始截取字符串到字符串结尾
System.out.println(s.substring(0));
System.out.println(s.substring(5));
System.out.println("‐‐‐‐‐‐‐‐");
// String substring(int start,int end):从start到end截取字符串。含start,不含end。
System.out.println(s.substring(0, s.length()));
System.out.println(s.substring(3,8));
}
}
转换功能的方法:
// public char[] toCharArray () :将此字符串转换为新的字符数组。
// public byte[] getBytes () :使用平台的默认字符集将该 String编码转换为新的字节数组。
// public String replace (CharSequence target, CharSequence replacement) :将与target匹配的字符串使用replacement字符串替换。
public class String_Demo03 {
public static void main(String[] args) {
//创建字符串对象
String s = "abcde";
// char[] toCharArray():把字符串转换为字符数组
char[] chs = s.toCharArray();
for(int x = 0; x < chs.length; x++) {
System.out.println(chs[x]);
}
System.out.println("‐‐‐‐‐‐‐‐‐‐‐");
// byte[] getBytes ():把字符串转换为字节数组
byte[] bytes = s.getBytes();
for(int x = 0; x < bytes.length; x++) {
System.out.println(bytes[x]);
}
System.out.println("‐‐‐‐‐‐‐‐‐‐‐");
// 替换字母it为大写IT
String str = "itcast itheima";
String replace = str.replace("it", "IT");
System.out.println(replace); // ITcast ITheima
System.out.println("‐‐‐‐‐‐‐‐‐‐‐");
}
}
分割功能的方法:
// public String[] split(String regex) :将此字符串按照给定的regex(规则)拆分为字符串数组
public class String_Demo03 {
public static void main(String[] args) {
//创建字符串对象
String s = "aa|bb|cc";
String[] strArray = s.split("|"); // ["aa","bb","cc"]
for(int x = 0; x < strArray.length; x++) {
System.out.println(strArray[x]); // aa bb cc
}
}
}
11.1.4 - 练习
// 定义一个方法,把数组{1,2,3}按照指定个格式拼接成一个字符串。格式参照如下:[word1#word2#word3]。
public class StringTest1{
public static void main(String[] args){
int[] arr = {1,2,3};
String s = arrayToString(arr);
System.out.println("s: " + s);
}
public static String arrayToString(int[] arr){
String s = new String("[");
for(int i = -; i < arr.length; i++){
if(x == arr.length - 1){
s = s.concat(arr[i] + "]");
} else {
s = s.concat(arr[i] + "#");
}
}
return s;
}
}
// 键盘录入一个字符,统计字符串中大小写字母以及数字字符个数
public class StringTest2{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串数据:");
String s = sc.nextLine();
int bigCount = 0;
int smallCount = 0;
int numberCount = 0;
for(int i = 0; i < s.length(); i++){
char ch = s.charAt(i);
if(ch >= 'A' && ch <= 'Z'){
bigCount++;
} else if(ch >= 'a' && ch <= 'z'){
smallCount++;
} else if(ch >= '0' && ch <= '9'){
numberCount++;
} else {
System.out.printInt("该字符 " + ch + " 非法");
}
}
System.out.printInt("大写字符:" + bigCount + " 个");
System.out.printInt("小写字符:" + smallCount + " 个");
System.out.printInt("数字字符:" + numberCount + " 个");
}
}
11.2 - static关键字
11.2.1 - 概述
关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属
于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。
11.2.2 - 定义和使用方式
类变量
当static修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
类变量:使用static关键字修饰的成员变量。
// 格式:
// static 数据类型 变量名;
// 举例:
static int numberID;
静态方法
当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要
创建类的对象。调用方式非常简单。
类方法:使用static关键字修饰的成员方法,习惯称为静态方法。
// 格式:
修饰符 static 返回值类型 方法名(参数列表){
// 执行语句
}
// 举例:
public static void showNum(){
System.out.println("num: " + numberOfStudent);
}
静态方法调用的注意事项:
- 静态方法可以直接访问类变量和静态方法
- 静态方法不能直接访问普通成员变量或者成员方法。反之,成员方法可以直接访问类变量或者静态方法。
- 静态方法中,不能使用this关键字。
- 静态方法只能访问静态成员
被static修饰的成员可以并且建议通过类名直接访问。虽然也可以通过对象名访问静态成员,原因即多个对象均属
于一个类,共享使用同一个静态成员,但是不建议,会出现警告信息。
// 格式:
// 访问类变量
类名.类变量名;
// 调用静态方法
类名.静态方法名(参数);
// 举例
public class StuDemo2{
public static void main(String[] args){
System.out.println(Student.numberOfStudent);
Student.showNum();
}
}
11.2.3 - 静态原理图解
static修饰的内容:
- 是随着类的加载而加载的,且只加载一次。
- 存储于一块固定的内存区域,所以,可以直接被类名调用。
- 它优先于对象存在,所以,可以被所有对象共享。
11.2.4 - 静态代码块
静态代码块:定义在成员位置,使用static修饰的代码块。
位置:类中方法外。
执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
public class ClassName{
static{
// 执行语句
}
}
作用:给类变量进行初始化赋值
public class Game{
public static int number;
public static ArrayList<String> list;
static{
number = 2;
list = new ArrayList<String>();
list.add("张三");
list.add("李四");
}
}
// static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。下面将介绍两个工具类,来体现static方法的便利。
11.3 - Arrays类
11.3.1 - 概述
java.util.Arrays 此类包含用来操作数组的各种方法,比如排序和搜索等。其所有方法均为静态方法,调用起来
非常简单。
11.3.2 - 操作数组的方法
// public static String toString(int[] a) 返回指定数组内容的字符串表示形式
public static void main(String[] args){
int[] arr = {2,34,35,4,657,8,69,9};
System.out,println(arr);
String s = Arrays.toString(arr);
System.out.println(s); // [2,34,35,4,657,8,69,9]
}
// public static void sort(int[] a) 对指定的int类型数组按数字升序进行排序
public static void main(String[] args){
int[] arr = {24,7,5,48,4,46,35,11,6,2};
System.out.println("排序前:" + Arrays.toString(arr));
Arrays.sort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
11.3.3 - 练习
// 使用 Arrays 相关的API,将一个随机字符串中的所有字符升序排列,并倒序打印。
public class ArraysTest{
public static void main(String[] args){
String line = "asdfklasjdhfklasj";
char[] chars = line.toCharArray();
Arrays.sort(chars);
for(int i = chars.length - 1; i >= 0; i++){
System.out.print(chars[i] + " ");
}
}
}
11.4 - Math类
11.4.1 - 概述
java.lang.Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具
类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。
11.4.2 - 基本运算方法
// public static double abs(double a) 返回double值的绝对值
double d1 = Math.abs(-5);
double d2 = Math.abs(5);
// public static double ceil(double a) 返回大于等于参数的最小整数
double d1 = Math.ceil(-3.3); // -3.0
double d2 = Math.ceil(5.1); // 6.0
// public static double floor(double a) 返回小于等于参数最大的整数
double d1 = Math.floor(3.3); // 3.0
double d2 = Math.floor(-3.3); // -4.0
// public static long round(double a) 返回最接近参数的long(相当于四舍五入)
long d1 = Math.round(5.5); // 6.0
loong d2 = Math.round(5.4); // 5.0
11.4.3 - 练习
// 使用 Math 相关的API,计算在 -10.8 到 5.9 之间,绝对值大于 6 或者小于 2.1 的整数有多少个
public class MathTest{
public static void main(String[] args){
double min = -10.8;
double max = 5.9;
int count = 0;
for(double i = Math.ceil(min); i <= max; i++){
if(Math.abs(i) > 6 || Math.abs(i) < 2.1){
count++;
}
}
System.out.println("个数为:" + count + " 个");
}
}
12 - 继承、抽象类
12.1 - 继承
12.1.1 - 概述
由来:
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要
继承那一个类即可。
定义:
继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接
访问父类中的非私有的属性和行为
好处:
-
提高了代码的复用性
-
类与类之间产生了关系,是多态的前提。
12.1.2 - 继承的格式
通过 extends 关键字,可以声明一个子类继承另外一个父类
class 父类{
...
}
class 子类 extends 父类{
...
}
// 举例
class Employee{
String name;
public void work(){
System.out.println("工作");
}
}
class Teacher extends Employee{
public void printName(){
System.out.println("name= " + name);
}
}
public class ExtendDemo01{
public static void main(String[] args){
Teacher t = new Teacher();
t.name = "小明";
t.printName();
t.work();
}
}
12.1.3 - 继承后的特点 —— 成员变量
成员变量不重名:
如果子类父类中出现不重名的成员变量,这时的访问时没有影响的。
class Fu{
int num = 5;
}
class Zi extends Fu{
int num2 = 6;
public void show(){
System.out.println("Fu num = " + num);
System.out.println("Zi num2 = " + num2);
}
}
class ExtendDemo2{
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
// Fu num = 5
// Zi num2 = 6
成员变量重名:
如果子类父类中出现重名的成员变量,这时的访问是有影响的。
class Fu{
int num = 5;
}
class Zi extends Fu{
int num = 6;
public void show(){
System.out.println("Fu num = " + num);
System.out.println("Zi num = " + num2);
}
}
class ExtendDemo2{
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
// Fu num = 6
// Zi num = 6
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰
父类成员变量,类似于之前学过的 this。
// 格式
super.父类成员变量名
// 子类方法需要修改
class Zi extends Fu{
int num = 6;
public void show(){
System.out.println("Fu num = " + super.num);
System.out.println("Zi num = " + this.num);
}
}
// Fu num = 5
// Zi num = 6
// Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能
直接访问的。
// 通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
12.1.4 - 继承后的特点 —— 成员方法
成员方法不重名:
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对
应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
class Fu{
public void show(){
System.out.println("Fu类中的show方法执行");
}
}
class Zi extends Fu{
public void show2(){
System.out.println("Zi类中的show2方法执行");
}
}
public class ExtendsDemo04{
public static void main(String[] args) {
Zi z = new Zi();
//子类中没有show方法,但是可以找到父类方法去执行
z.show();
z.show2();
}
}
成员方法重名——重写(Override):
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效
果,也称为重写或者复写。声明不变,重新实现。
class Fu {
public void show() {
System.out.println("Fu show");
}
}
class Zi extends Fu {
//子类重写了父类的show方法
public void show() {
System.out.println("Zi show");
}
}
public class ExtendsDemo05{
public static void main(String[] args) {
Zi z = new Zi();
// 子类中有show方法,只执行重写后的show方法
z.show(); // Zi show
}
}
重写的应用:
子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从
而进行扩展增强。
class Phone {
public void sendMessage(){
System.out.println("发短信");
}
public void call(){
System.out.println("打电话");
}
public void showNum(){
System.out.println("来电显示号码");
}
}
//智能手机类
class NewPhone extends Phone {
//重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
//调用父类已经存在的功能使用super
super.showNum();
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}
public class ExtendsDemo06 {
public static void main(String[] args) {
// 创建子类对象
NewPhone np = new NewPhone();
// 调用父类继承而来的方法
np.call();
// 调用子类重写的方法
np.showNum();
}
}
// 这里重写时,用到super.父类成员方法,表示调用父类的成员方法。
注意事项:
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
12.1.5 - 继承后的特点 —— 构造方法
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构
造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
class Fu{
private int n;
Fu(){
System.out.println("Fu()");
}
}
class Zi extends Fu{
Zi(){
// super() 调用父类构造方法
super();
System.out.println("Zi()");
}
}
public class ExtendsDemo07{
public static void main(String[] args){
Zi zi = new Zi();
}
}
// Fu()
// Zi()
12.1.6 - super 和 this
父类空间优先于子类对象产生
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空
间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构
造方法调用时,一定先调用父类的构造方法。
super和this的含义
-
super 代表父类的存储空间标识(可以理解为父类的引用)
-
this 代表当前对象的引用(谁调用就代表谁)
super和this的用法
// 访问成员
this.成员变量 ‐‐ 本类的
super.成员变量 ‐‐ 父类的
this.成员方法名() ‐‐ 本类的
super.成员方法名() ‐‐ 父类的
// 用法演示
class Animal {
public void eat() {
System.out.println("animal : eat");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("cat : eat");
}
public void eatTest() {
this.eat(); // this 调用本类的方法
super.eat(); // super 调用父类的方法
}
}
public class ExtendsDemo08 {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Cat c = new Cat();
c.eatTest();
}
}
// animal : eat
// cat : eat
// animal : eat
// 访问构造方法
this(...) ‐‐ 本类的构造方法
super(...) ‐‐ 父类的构造方法
// 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
// super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
12.1.7 - 继承的特点
// java只支持单继承,不支持多继承
// 一个类只能有一个父类,不可以有多个父类
class C extends A{} // ok
class C extends A,B... // error
// java支持多层继承(继承体系)
// 顶层父类时Object类,所有的类默认继承Ojbect,作为父类
class A{}
class B extends A{}
class C extends B{}
// 父类和子类是一种相对的概念。
12.2 - 抽象类
12.2.1 - 概述
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有
意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法
的类就是抽象类。
抽象方法 : 没有方法体的方法。
抽象类:包含抽象方法的类。
12.2.2 - abstract使用格式
抽象方法:
使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
// 格式
修饰符 abstract 返回值类型 方法名(参数列表);
// 举例
public abstract void run();
抽象类:
如果一个类包含抽象方法,那么该类必须是抽象类。
// 格式
abstract class 类名字{
...
}
// 举例
public abstract class Animal{
public abstract void run();
}
抽象的使用:
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父
类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
public class Cat extends Animal {
public void run (){
System.out.println("小猫在墙头走~~~");
}
}
public class CatTest {
public static void main(String[] args) {
// 创建子类对象
Cat c = new Cat();
// 调用run方法
c.run();
}
}
// 输出结果:
// 小猫在墙头走~~~
// 此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法
12.2.3 - 注意事项
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设
计。
4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象
类
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有
意义。
12.2.4 - 综合案例
// 群主发普通红包。某群有多名成员,群主给成员发普通红包。普通红包的规则:
// 1. 群主的一笔金额,从群主余额中扣除,平均分成n等份,让成员领取。
// 2. 成员领取红包后,保存到成员余额中。
// 请根据描述,完成案例中所有类的定义以及指定类之间的继承关系,并完成发红包的操作。
// 用户类
public class User{
private String username;
private double leftMoney;
public User(){}
public User(String username, double leftMoney){
this.username = username;
this.leftMoney = leftMoney;
}
public void setUsername(String username){
this.username = username;
}
public String getUseranme(){
return username;
}
public void setLeftMoney(double leftMoney){
this.leftMoney = leftMoney
}
public double getLeftMoney(){
return leftMoney;
}
public void show(){
System.out.println("用户名: " + username " ,余额为:" + leftMoney + " 元");
}
}
// 定义群主类
public class QunZhu extends User{
public QunZhu(){}
public QunZhu(String username, double leftMoney){
super(useranme, leftMoney);
}
public ArrayList<Double> send(int money, int count){
double leftMoney = getLeftMoney();
if(money > leftMoney){
return null;
}
setLeftMoney(leftMoney - money);
ArrayList<Double> list = new ArrayList<>();
money = money * 100;
int m = money / count;
int l = money % count;
for(int i = 0; i < count - 1; i++){
list.add(m / 100.0);
}
if(l == 0){
list.add(m / 100.0);
} else {
list.add((m + 1) / 100.0);
}
return list;
}
}
// 成员类
public class Member extends User{
public Member(){}
public Member(String username, double leftMoney){
super(username,leftMoney);
}
public void openHongbao(ArrayList<Double> list){
Random r = new Random();
int index = r.nextInt(list.size());
Double money = list.remove(index);
setLeftMoney(money);
}
}
// 测试类
public class Test{
public static void main(String[] args){
QunZhu qz = new QunZhu("群主", 200);
Scanner sc = new Scanner(System.in);
System.out.println("请输入金额:");
int money = sc.nextInt();
System.out.println("请输入个数:");
int count = sc.nextInt();
ArrayList<Double> sendList = s.send(money,count);
if(sendList == null){
System.out.println("余额不足...");
return;
}
Member m = new Member();
Member m2 = new Member();
Member m3 = new Member();
m.openHongbao(sendList);
m2.openHongbao(sendList);
m3.openHongbao(sendList);
qz.show();
m.show();
m2.show();
m3.show();
}
}
13 - 接口、多态
13.1 - 接口
13.1.1 - 概述
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么
接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法
(JDK 9)。
接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并
不是类,而是另外一种引用数据类型。
引用数据类型:数组,类,接口。
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做
是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象
类。
13.1.2 - 定义格式
public interface 接口名称{
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
含有抽象方法:
// 抽象方法:使用abstract关键字修饰,可以省略,没有方法体,该方法体子类实现使用。
public interface InterfaceName(){
public abstract void method();
}
含有默认方法和静态方法:
// 默认方法:使用default修饰,不可省略,供子类调用或者子类重写。
// 静态方法:使用static修饰,供接口直接调用。
public interface InterfaceName{
public default void method{
// 执行语句
}
public static void method2{
// 执行语句
}
}
含有私有方法和私有静态方法:
// 私有方法:使用private修饰,供接口中的默认方法或者静态方法调用
public interface InterfaceName{
private void method(){
// 执行语句
}
}
13.1.3 - 基本的实现
实现的概述:
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类
似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。
非抽象子类实现接口:
1.必须重写接口中所有的抽象方法。
2.继承了接口的默认方法,即可以直接调用,也可以重写。
class 类名 implements 接口名{
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【可选】
}
抽象方法的使用:
// 定义接口
public interface LiveAble{
// 定义抽象方法
public abstract void eat();
public abstract void sleep();
}
// 定义实现类
public class Animal implements LiveAble{
@Override
public void eat(){
System.out.println("吃东西");
}
@Override
public void sleep(){
System.out.println("晚上睡");
}
}
// 测试类
public class InterfaceDemo{
public static void main(String[] args){
// 创建子类对象
Animal a = new Animal();
// 调用实现后的方法
a.eat();
a.sleep();
}
}
// 输出结果:
// 吃东西
// 晚上睡
默认方法的使用:
/****************
*
* 1. 继承默认方法
*
*****************/
// 定义接口
public interface LiveAble{
public default void fly(){
System.out.println("天上飞");
}
}
// 定义实现类
public class Animal implements LiveAble{
// 继承,什么都不用写,直接调用
}
// 定义测试类
public class InterfaceDemo{
public static void main(String[] args){
//创建子类对象
Animal a = new Animal();
// 调用默认方法
a.fly();
}
}
// 输出结果
// 天上飞
/****************
*
* 2.重写默认方法
*
*****************/
// 定义接口
public interface LiveAble{
public default void fly(){
System.out.println("天上飞");
}
}
// 定义实现类
public class Animal implements LiveAble{
@Override
public void fly(){
System.out.println("自由自在的飞");
}
}
// 定义测试类
public class InterfaceDemo{
public static void main(String[] args){
//创建子类对象
Animal a = new Animal();
// 调用默认方法
a.fly();
}
}
// 输出结果
// 自由自在的飞
静态方法的使用:
// 静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用
// 定义接口
public interface LivaAble{
public static void run(){
System.out.println("跑起来");
}
}
// 定义实现类
public class Animal implements LiveAble{
// 无法重写静态方法
}
// 定义测试类
public class InterfaceDemo{
public static void main(String[] args){
// Animal.run(); 【错误】 无法继承方法,也无法调用
LiveAble.run();
}
}
// 输出结果:
// 跑起来
私有方法的使用:
- 私有方法:只有默认方法可以调用。
- 静态方法:默认方法和静态方法可以调用。
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法
去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。同学们在已学技术的基础上,可以自行测
试。
public interface LiveAble{
default void func(){
func1();
func2();
}
private void func1(){
System.out.println("跑起来");
}
private void func2(){
System.out.println("跑起来");
}
}
13.1.4 - 接口的多实现
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接
口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
class 类名[extends 父类名] implements 接口名1,接口名2,接口名3... {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【不重名时可选】
}
抽象方法:
接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。
// 定义多个接口
interface A{
public abstract void showA();
public abstract void show();
}
interface B{
public abstract void showB{};
public abstract void show();
}
// 定义实现类
public class C implements A,B{
@Override
public void showA(){
System.out.println("showA");
}
@Override
public void showB(){
System.out.println("showB");
}
@Override
public void show(){
System.out.println("show");
}
}
默认方法:
接口中,有多个默认方法时,实现类都可以继承使用,如果默认方法有重名的,必须重写一次。
// 定义多个接口
interface A{
public default void methodA(){}
public default void method(){}
}
interface B{
public default void methodB(){}
public default void method(){}
}
// 定义实现类
public class C implements A,B{
@Override
public void method(){
System.out.println("method");
}
}
静态方法:
接口中,存在同名的静态方法并不会冲突,原因是只能通过各自的接口名访问静态方法。
优先级的问题:
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。
// 定义接口
interface A{
public default void methodA(){
System.out.println("AAAAAAA");
}
}
// 定义父类
class D{
public void methodA(){
System.out.println("DDDDDDDDD");
}
}
// 定义子类
class C extends D implements A{
// 未重写方法
}
// 测试类
public class Test{
public static void main(String[] args){
C c = new C();
C.methodA();
}
}
// 输出结果
// DDDDDDDDD
13.1.5 - 接口的多继承
一个接口能够继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。
// 父接口
interface A{
public default void method(){
System.out.println("AAAAA");
}
}
interface B{
public default void method(){
System.out.println("BBBBB");
}
}
// 子接口
interface D extends A,B{
@Override
public default void method(){
System.out.println("DDDDD");
}
}
// 子接口重写默认方法时,default关键字可以保留
// 子类重写默认方法时,default关键字不可以保留
13.1.6 - 其他成员特点
- 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
- 接口中,没有构造方法,不能创建对象。
- 接口中,没有静态代码块。
13.2 - 多态
13.2.1 - 概述
多态是继封装、继承之后,面向对象的第三大特性。
多态:是指同一行为,具有多个不同的表现形式。
继承或者实现【二选一】,方法的重写【意义体现:不重写,无意义】,父类引用指向子类对象【格式体现】
13.2.2 - 多态的体现
// 格式
// 父类类型:指子类对象继承的父类类型,或者实现的父接口类型
父类类型 变量名 = new 子类对象;
变量名.方法名();
// 父类类型:子类对象继承的父类类型,或者实现的父接口类型。
Fu f = new Zi();
f.method();
// 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的时子类重写后方法。
// 定义父类
public abstract class Ainmal{
public abstract void eat();
}
// 定义子类
class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("吃骨头");
}
}
// 定义测试类
public class Test{
public static void main(String[] args){
// 多态形式,创建对象
Animal a1 = new Cat();
// 调用的时 Cat 的 eat
// 多态形式,创建对象
Animal a2 = new Dog();
// 调用的时 Dog 的 eat
a2.eat();
}
}
13.2.3 - 多态的好处
实际开发过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展与便利。
// 定义父类
public abstract class Animal{
public abstract void eat();
}
// 定义子类
class Cat extends Animal{
public void eat(){
System.out,println("吃鱼");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("吃骨头");
}
}
// 定义测试类
public class Test{
public static void main(String[] args){
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法,均可以被showAnimalEat(Animal a)方法所代替
执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void ShowCatEat(Cat c){
c.eat();
}
public static void ShowDogEat(Dog d){
d.eat();
}
public static void showAinmalEat(Animal a){
a.eat();
}
}
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当
然可以把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,
所以showAnimalEat完全可以替代以上两方法。
不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用
showAnimalEat都可以完成。
所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。
13.2.4 - 引用类型转换
多态的转型分为向上转型和向下转型两种:
向上转型:
多态本身时子类类型向父类类型向上转换的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
父类类型 变量名= new 子类类型();
Aimal a = new Cat();
向下转型:
父类类型向子类类型向下转换得到过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
子类类型 变量名 = (子类类型)父类变量名;
Cat c = (Cat) a;
为什么要转型:
当使用多态方式调用方式时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别提运行了。这也是多态给我们带来的一点“小麻烦”。所以,想要调用子类特有的方法,必须向下转型。
// 定义类
abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
public void catchMouse(){
System.out.pritnln("抓老鼠");
}
}
class Dog extends Animal(){
public void eat(){
System.out.println("吃骨头");
}
public void watchHouse(){
System.out.println("看家");
}
}
// 定义测试类
public class Test{
public static void main(String[] args){
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat) a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
转型的异常:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了
Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验。
// 变量名 instanceof 数据类型
// 如果变量属于该数据类型,返回true
// 如果变量不属于该数据类型,返回false
public class Test(){
public static void main(String[] args){
// 向上转型
Aniaml a = new Cat();
// 向下转型
if(a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse();
} else if(a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse();
}
}
}
13.3 - 接口多态的综合案例
// USB接口
interface USB{
void open(); // 开启功能
void close(); // 关闭功能
}
// 鼠标类
class Mouse implements USB{
public void open(){
System.out.println("鼠标开启,红灯亮");
}
public void close(){
System.out.println("鼠标关闭,红灯熄灭");
}
public void click(){
System.out.println("鼠标单击");
}
}
// 键盘类
class KeyBorad implements USB{
public void open(){
System.out.println("键盘开启,绿灯亮");
}
public void close(){
System.out.println("键盘关闭,绿灯灭");
}
public void type(){
System.out.println("键盘打字");
}
}
class Laptop{
// 笔记本开启功能
public void run(){
System.out.println("笔记本运行");
}
// 笔记本使用USB设备,这时当笔记本对象调用这个功能时,必须给其传递一个符合USB规则的USB设备
public void useUSB(USB usb){
if(usb != null){
usb.open();
// 类型转换,调用特有方法
if(usb instanceof Mouse){
Mouse m = (Mouse)usb;
m.click();
} else if(usb instanceof KeyBorad){
KeyBorad kb = (KeyBorad)usb;
kb.type();
}
usb.close();
}
}
public void shutDown(){
System.out.println("笔记本关闭");
}
}
public class Test{
public static void main(String[] args){
// 创建笔记本对象
Laptop lt = new Laptop();
// 笔记本开启
lt.run();
// 创建鼠标实体对象
USB u = new Mouse();
// 笔记本使用鼠标
lt.useUSB(u);
// 创建键盘实体对象
KeyBorad kb = new KeyBorad();
// 笔记本使用键盘
lt.useUSB(kb);
// 笔记本关闭
lt.shutDown();
}
}
14 - final、权限、内部类、引用类型
14.1 - final关键字
14.1.1 - 概述
子类可以在父类的基础上改写父类内容,为了避免随意的继承API中提供的类,改写其内容,java提供了关键字final,用于修饰不可改变内容。
final:不可改变,可以用于修饰类、方法和变量。被修饰的类,不能被继承。被修饰的方法,不能被重写。被修饰的变量,不能被重新赋值。
14.1.2 - 使用方式
// 修饰类
final class 类名{
}
// 修饰方法
修饰符 final 返回值类型 方法名(参数列表){
// 方法体
}
// 修饰变量 1.局部变量——基本类型
public class FinalDemo1{
public static void main(String[] args){
final in a;
a = 10;
a = 20; // 第二次赋值,报错,不可重新赋值
}
}
// 修饰变量 2.局部变量——引用类型
// 引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改,但是不影响对象内部的成员变量值的修改
public class FinalDemo2{
public static void main(String[] args){
// 创建User对象
final User u = new User();
// 创建 另一个User对象
u = new User(); // 报错,指向了新的对象,地址值该百年
// 调用setName方法
u.setName("张三"); // 可以修改
}
}
// 成员变量 成员变量涉及到初始化的问题,初始化方式有两种,只能二选一
// 1.显示初始化
public class User{
final String USERNAME="张三";
private int age;
}
// 2.构造方法初始化
public class User{
final String USERNAME;
private int age;
public User(String username, int age){
this.USERNAME = username;
this.age = age;
}
}
14.2 - 权限修饰符
14.2.1 - 概述
在java中提供了四种访问权限,使用不同的访问修饰符时,被修饰的内容会有不同的访问权限。
- public:公共的。
- protected:受保护的。
- default:默认的。
- private:私有的。
14.2.2 - 不同权限的访问能力
public | protected | default | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用private,隐藏细节。
- 构造方法使用public,方便创建对象。
- 成员方法使用public,方便调用方法。
- 不加权限修饰符,其访问能力与default修饰符相同。
14.3 - 内部类
14.3.1 - 概述
将类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
// 成员内部类:定义在类中方法外的类。
class 外部类{
class 内部类{
}
}
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。
访问特点:
内部类可以直接访问外部类的成员,包括私有成员。
外部类药访问内部类的成员,必须药创建内部类的对象。
// 创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
// 定义类
public class Person{
private boolean live = true;
clas Heart{
public void jump(){
// 直接访问外部类成员
if(live){
System.out.println("心脏在跳动");
} else {
System.out.println("心脏不跳了");
}
}
}
public boolean isLive(){
return live;
}
public void setLive(boolean live){
this.live = live;
}
}
public class InnerDemo(){
public static void main(String[] args){
Person p = new Person();
Heart heart = p.new.Heart();
heart.jump();
p.setLive(false);
heart.jump();
}
}
// 输出结果:
// 心脏在跳动
// 心脏不跳了
// 内部类仍然时一个独立的类,在编译后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号
14.3.2 - 匿名内部类
匿名内部类时内部类的简化写法。它的本质时一个【带具体实现的】【父类或者父接口的】【匿名的】子类对象。
开发中,最常用的内部类就是匿名内部类。以接口举例,当你使用一个接口时,似乎需要做如下几步操作:
- 定义子类
- 接口中的方法
- 创建子类对象
- 调用重写后的方法
我们的目的,最终只是为了调用方法,匿名内部类就是这样做的快捷方式,将上面的四步合成一步。
匿名内部类必须继承一个父类或者实现一个父接口。
// 格式
new 父类名或者接口名(){
// 方法重写
@Override
public void method(){
// 执行语句
}
};
// 定义接口
public abstract class FlyAble{
public abstract void fly();
}
// 创建匿名内部类,并调用:
public class InnerDemo{
public static void main(String[] args){
/*
1. 等号右边:匿名内部类,定义并创建该接口的子类对象
2. 等号左边:多态赋值,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
public void fly(){
System.out.println("起飞");
}
};
// 调用fly方法,执行重写后的方法
f.fly();
}
}
// 通常在方法的形式参数时接口或者抽象类时,也可以将匿名内部类作为参数传递
public class InnerDemo2{
public static void main(String[] args){
FlyAble f = new FlyAble(){
public void fly(){
System.out.println("起飞");
}
};
showFly(f);
}
public static void showFly(FlyAble f){
f.fly();
}
}
// 以上的两步也可以简化为一步
public class InnerDemo3(){
public stativ void main(String[] args){
showFly(FlyAble f = new Flyable(){
public void fly(){
System.out.println("起飞");
}
});
}
public static void showFly(FlyAble f){
f.fly();
}
}
14.4 - 引用类型用法总结
14.4.1 - class作为成员变量
class Weapon{
String name; // 武器名称
int hurt; // 伤害值
Weapon(String name, int hurt){
this.name = name;
this.hurt = hurt;
}
public String getName(){
return name;
}
public int getHurt(){
return hurt;
}
}
class Armour{
String name; // 装备名称
int protect; // 防御值
Armour(String name, int protect){
this.name = name;
this.protect = protect;
}
public String getName(){
return name;
}
public int getProtect(){
return protect;
}
}
class Role{
int id;
int blood;
String name;
// 添加武器属性
Weapon wp;
// 添加盔甲属性
Armour ar;
// 提供get/set方法
public Weapon getWp(){
return wp;
}
public void setWeapon(Weapon wp){
this.wp = wp;
}
public Armour getArmour(){
return ar;
}
public void setArmour(Armour ar){
this.ar = ar;
}
// 攻击方法
public void attack(){
System.out.println("使用" + wp.getName() + ",造成" + wp.getHurt() + "点伤害");
}
// 穿戴盔甲
public void wear(){
this.blood += ar.getProtect();
System.out.println("穿上" + ar.getName() + ", 生命值增加" + ar.getProtect());
}
}
public class Test{
public static void main(String[] args){
// 创建Weapon 对象
Weapon wp = new Weapon("屠龙刀" , 999999);
// 创建Armour 对象
Armour ar = new Armour("麒麟甲",10000);
// 创建Role 对象
Role r = new Role();
// 设置武器属性
r.setWeapon(wp);
// 设置盔甲属性
r.setArmour(ar);
// 攻击
r.attack();
// 穿戴盔甲
r.wear();
}
}
// 类作为成员变量时,对它进行赋值的操作,实际上,时赋给它该类的一个对象。
14.4.2 - interface作为成员变量
interface FaShuSkill{
public abstract void faShuAttack();
}
class Role{
FaShuSkill fs;
public void setFaShuSkill(FaShuSkill fs){
this.fs = fs;
}
// 法术攻击
public void faShuSkillAttack(){
System.out.print("发动法术攻击:");
fs.faShuAttack();
System.out.println("攻击完毕");
}
}
public class Test{
public stativ void main(String[] args){
Role role = new Role();
role.setFaShuSkill(new FaShuSkill(){
@Override
public void faShuAttack(){
System.out.println("纵横天下");
}
});
// 发动法术攻击
role.faShuSkillAttack();
// 更换技能
role.setFaShuSkill(new FaShuSkill(){
@Override
public void faShuAttack(){
System.out.println("逆转乾坤");
}
});
// 发动法术攻击
role.faShuSkillAttack();
}
}
// 使用一个接口,作为成员变量,以便随时能更换技能,这样的设计更加灵活,增加了程序的扩展性
// 接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象。
14.4.3 - interface作为方法参数和返回值
当接口作为方法参数,或者是返回值类型时,需要传递的或者返回的都是它的子类对象。
// 获取某集合中所有的偶数
public static List<Integer> getEvenNum(List<Integer> list) {
// 创建保存偶数的集合
ArrayList<Integer> evenList = new ArrayList<>();
// 遍历集合list,判断元素为偶数,就添加到evenList中
for (int i = 0; i < list.size(); i++) {
Integer integer = list.get(i);
if (integer % 2 == 0) {
evenList.add(integer);
}
}
/*
返回偶数集合
因为getEvenNum方法的返回值类型是List,而ArrayList是List的子类,
所以evenList可以返回
*/
return evenList;
}
public class Test {
public static void main(String[] args) {
// 创建ArrayList集合,并添加数字
ArrayList<Integer> srcList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
srcList.add(i);
}
/*
获取偶数集合
因为getEvenNum方法的参数是List,而ArrayList是List的子类,
所以srcList可以传递
*/
List list = getEvenNum(srcList);
System.out.println(list);
}
}