导图社区 javaSE
java基础知识整理,简单易学;面向对象(封装,继承,多态);平台无关性( Java 虚拟机实现平台无关性);可靠性;安全性;支持多线程( C 语言没有内置的多线程机制,因此必须调用操作系统的线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持);支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);编译与解释并存;
编辑于2022-05-07 10:43:24Thymeleaf提供了一个用于整合Spring MVC的可选模块,在应用开发中,你可以使用Thymeleaf来完全代替JSP或其他模板引擎,如Velocity、FreeMarker等。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。
这是一篇关于Nginx的思维导图,主要内容有1.目标、2.Nginx的安装与启动、3.Nginx静态网站部署、4.Nginx反向代理与负载均衡。
这是一篇关于json的思维导图,主要内容有概念、语法、fastjson。喜欢的小伙伴们可以点赞收藏哦!
社区模板帮助中心,点此进入>>
Thymeleaf提供了一个用于整合Spring MVC的可选模块,在应用开发中,你可以使用Thymeleaf来完全代替JSP或其他模板引擎,如Velocity、FreeMarker等。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。
这是一篇关于Nginx的思维导图,主要内容有1.目标、2.Nginx的安装与启动、3.Nginx静态网站部署、4.Nginx反向代理与负载均衡。
这是一篇关于json的思维导图,主要内容有概念、语法、fastjson。喜欢的小伙伴们可以点赞收藏哦!
javaSE
1. 注释(理解)
注释是对代码的解释和说明文字,可以提高程序的可读性,因此在程序中添加必要的注释文字十分重要。Java中的注释分为三种:
单行注释。单行注释的格式是使用//,从//开始至本行结尾的文字将作为注释文字。
多行注释。多行注释的格式是使用/* 和 */将一段较长的注释括起来。
文档注释。文档注释以`/**`开始,以`*/`结束。
2. 关键字(理解)
关键字是指被java语言赋予了特殊含义的单词。
关键字的特点:
关键字的字母全部小写。
常用的代码编辑器对关键字都有高亮显示,比如现在我们能看到的public、class、static等。
3. 常量(应用)
常量:在程序运行过程中,其值不可以发生改变的量。
Java中的常量分类:
字符串常量
用双引号括起来的多个字符(可以包含0个、一个或多个),例如"a"、"abc"、"中国"等
整数常量
整数,例如:-10、0、88等
小数常量
小数,例如:-5.5、1.0、88.88等
字符常量
用单引号括起来的一个字符,例如:'a'、'5'、'B'、'中'等
布尔常量
布尔值,表示真假,只有两个值true和false
空常量
一个特殊的值,空值,值为null
除空常量外,其他常量均可使用输出语句直接输出。
例如
public class Demo { public static void main(String[] args) { System.out.println(10); // 输出一个整数 System.out.println(5.5); // 输出一个小数 System.out.println('a'); // 输出一个字符 System.out.println(true); // 输出boolean值true System.out.println("程序员wry的后端开发奇妙之旅"); // 输出字符串 } }
4. 数据类型(记忆、应用)
计算机存储单元
我们知道计算机是可以用来存储数据的,但是无论是内存还是硬盘,计算机存储设备的最小信息单元叫“位(bit)”,我们又称之为“比特位”,通常用小写的字母”b”表示。而计算机中最基本的存储单元叫“字节(byte)”,
通常用大写字母”B”表示,字节是由连续的8个位组成。
除了字节外还有一些常用的存储单位,其换算单位如下:
1B(字节) = 8bit
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
Java中的数据类型
Java是一个强类型语言,Java中的数据必须明确数据类型。在Java中的数据类型包括基本数据类型和引用数据类型两种。
Java中的基本数据类型:
说明:
e+38表示是乘以10的38次方,同样,e-45表示乘以10的负45次方。
在java中整数默认是int类型,浮点数默认是double类型
5. 变量(应用)
变量的定义
变量:在程序运行过程中,其值可以发生改变的量。
从本质上讲,变量是内存中的一小块区域,其值可以在一定范围内变化。
变量的定义格式:
数据类型 变量名 = 初始化值; // 声明变量并赋值 int age = 18; System.out.println(age);
或者
// 先声明,后赋值(使用前赋值即可) 数据类型 变量名; 变量名 = 初始化值; double money; money = 55.5; System.out.println(money);
还可以在同一行定义多个同一种数据类型的变量,中间使用逗号隔开。但不建议使用这种方式,降低程序的可读性。
int a = 10, b = 20; // 定义int类型的变量a和b,中间使用逗号隔开 System.out.println(a); System.out.println(b); int c,d; // 声明int类型的变量c和d,中间使用逗号隔开 c = 30; d = 40; System.out.println(c); System.out.println(d);
变量的使用:通过变量名访问即可。
使用变量时的注意事项
在同一对花括号中,变量名不能重复。
变量在使用之前,必须初始化(赋值)。
定义long类型的变量时,需要在整数的后面加L(大小写均可,建议大写)。因为整数默认是int类型,整数太大可能超出int范围。
定义float类型的变量时,需要在小数的后面加F(大小写均可,建议大写)。因为浮点数的默认类型是double, double的取值范围是大于float的,类型不兼容。
6. 标识符(记忆、理解)
标识符是用户编程时使用的名字,用于给类、方法、变量、常量等命名。
Java中标识符的组成规则:
由字母、数字、下划线“_”、美元符号“$”组成,第一个字符不能是数字。
不能使用java中的关键字作为标识符。
标识符对大小写敏感(区分大小写)。
Java中标识符的命名约定:
小驼峰式命名:变量名、方法名
首字母小写,从第二个单词开始每个单词的首字母大写。
大驼峰式命名:类名
每个单词的首字母都大写。
另外,标识符的命名最好可以做到见名知意
例如:username、studentNumber等。
7. 类型转换(理解)
在Java中,一些数据类型之间是可以相互转换的。分为两种情况:自动类型转换和强制类型转换。
自动类型转换:
把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量。这种转换方式是自动的,直接书写即可。
例如:
double num = 10; // 将int类型的10直接赋值给double类型 System.out.println(num); // 输出10.0
强制类型转换:
把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量。
强制类型转换格式:目标数据类型 变量名 = (目标数据类型)值或者变量;
例如:
double num1 = 5.5; int num2 = (int) num1; // 将double类型的num1强制转换为int类型 System.out.println(num2); // 输出5(小数位直接舍弃)
说明:
char类型的数据转换为int类型是按照码表中对应的int值进行计算的。比如在ASCII码表中,'a'对应97。
int a = 'a'; System.out.println(a); // 将输出97
整数默认是int类型,byte、short和char类型数据参与运算均会自动转换为int类型
byte b1 = 10; byte b2 = 20; byte b3 = b1 + b2; // 第三行代码会报错,b1和b2会自动转换为int类型,计算结果为int,int赋值给byte需要强制类型转换。 // 修改为: int num = b1 + b2; // 或者: byte b3 = (byte) (b1 + b2);
boolean类型不能与其他基本数据类型相互转换。
8. 运算符
算术运算符(理解)
运算符和表达式
运算符:对常量或者变量进行操作的符号
表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式。
不同运算符连接的表达式体现的是不同类型的表达式。
举例说明:
int a = 10; int b = 20; int c = a + b;
+:是运算符,并且是算术运算符。
a + b:是表达式,由于+是算术运算符,所以这个表达式叫算术表达式。
算术运算符
注意:
/和%的区别:两个数据做除法,/取结果的商,%取结果的余数。
整数操作只能得到整数,要想得到小数,必须有浮点数参与运算。
例如
int a = 10; int b = 3; System.out.println(a / b); // 输出结果3 System.out.println(a % b); // 输出结果1
字符的“+”操作
char类型参与算术运算,使用的是计算机底层对应的十进制数值。需要我们记住三个字符对应的数值:
'a' -- 97
a-z是连续的,所以'b'对应的数值是98,'c'是99,依次递加
'A' -- 65
A-Z是连续的,所以'B'对应的数值是66,'C'是67,依次递加
'0' -- 48
0-9是连续的,所以'1'对应的数值是49,'2'是50,依次递加
例如
// 可以通过使用字符与整数做算术运算,得出字符对应的数值是多少 char ch1 = 'a'; System.out.println(ch1 + 1); // 输出98,97 + 1 = 98 char ch2 = 'A'; System.out.println(ch2 + 1); // 输出66,65 + 1 = 66 char ch3 = '0'; System.out.println(ch3 + 1); // 输出49,48 + 1 = 49
算术表达式中包含不同的基本数据类型的值的时候,整个算术表达式的类型会自动进行提升。
提升规则:
byte类型,short类型和char类型将被提升到int类型,不管是否有其他类型参与运算。
整个表达式的类型自动提升到与表达式中最高等级的操作数相同的类型
等级顺序:byte,short,char --> int --> long --> float --> double
例如
byte b1 = 10; byte b2 = 20; // byte b3 = b1 + b2; // 该行报错,因为byte类型参与算术运算会自动提示为int,int赋值给byte可能损失精度 int i3 = b1 + b2; // 应该使用int接收 byte b3 = (byte) (b1 + b2); // 或者将结果强制转换为byte类型 ------------------------------- int num1 = 10; double num2 = 20.0; double num3 = num1 + num2; // 使用double接收,因为num1会自动提升为double类型
tips:正是由于上述原因,所以在程序开发中我们很少使用byte或者short类型定义整数。也很少会使用char类型定义字符,而使用字符串类型,更不会使用char类型做算术运算。
字符串的“+”操作
当“+”操作中出现字符串时,这个”+”是字符串连接符,而不是算术运算。
例如
System.out.println("wry"+ 666); // 输出:wry666
在”+”操作中,如果出现了字符串,就是连接运算符,否则就是算术运算。当连续进行“+”操作时,从左到右逐个执行。
例如
System.out.println(1 + 99 + "年计划"); // 输出:100年计划 System.out.println(1 + 2 + "jn" + 3 + 4); // 输出:3jn34 // 可以使用小括号改变运算的优先级 System.out.println(1 + 2 + "it" + (3 + 4)); // 输出:3it7
赋值运算符(应用)
赋值运算符的作用是将一个表达式的值赋给左边,左边必须是可修改的,不能是常量。
注意:
扩展的赋值运算符隐含了强制类型转换。
例如
short s = 10; s = s + 10; // 此行代码报出,因为运算中s提升为int类型,运算结果int赋值给short可能损失精度 s += 10; // 此行代码没有问题,隐含了强制类型转换,相当于 s = (short) (s + 10);
自增自减运算符(理解)
注意事项:
++和-- 既可以放在变量的后边,也可以放在变量的前边。
单独使用的时候, ++和-- 无论是放在变量的前边还是后边,结果是一样的。
参与操作的时候,如果放在变量的后边,先拿变量参与操作,后拿变量做++或者--。
参与操作的时候,如果放在变量的前边,先拿变量做++或者--,后拿变量参与操作。
最常见的用法:单独使用。
例如
int i = 10; i++; // 单独使用 System.out.println("i:" + i); // i:11 int j = 10; ++j; // 单独使用 System.out.println("j:" + j); // j:11 int x = 10; int y = x++; // 赋值运算,++在后边,所以是使用x原来的值赋值给y,x本身自增1 System.out.println("x:" + x + ", y:" + y); // x:11,y:10 int m = 10; int n = ++m; // 赋值运算,++在前边,所以是使用m自增后的值赋值给n,m本身自增1 System.out.println("m:" + m + ", m:" + m); // m:11,m:11
练习
int x = 10; int y = x++ + x++ + x++; System.out.println(y); // y的值是多少? /* 解析,三个表达式都是++在后,所以每次使用的都是自增前的值,但程序自左至右执行,所以第一次自增时,使用的是10进行计算,但第二次自增时,x的值已经自增到11了,所以第二次使用的是11,然后再次自增。。。 所以整个式子应该是:int y = 10 + 11 + 12; 输出结果为33。 */ 注意:通过此练习深刻理解自增和自减的规律,但实际开发中强烈建议不要写这样的代码!小心挨打!
关系运算符(应用)
关系运算符有6种关系,分别为小于、小于等于、大于、等于、大于等于、不等于。
注意事项:
关系运算符的结果都是boolean类型,要么是true,要么是false。
千万不要把“==”误写成“=”,"=="是判断是否相等的关系,"="是赋值。
例如
int a = 10; int b = 20; System.out.println(a == b); // false System.out.println(a != b); // true System.out.println(a > b); // false System.out.println(a >= b); // false System.out.println(a < b); // true System.out.println(a <= b); // true // 关系运算的结果肯定是boolean类型,所以也可以将运算结果赋值给boolean类型的变量 boolean flag = a > b; System.out.println(flag); // 输出false
逻辑运算符(应用)
逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。
例如
//定义变量 int i = 10; int j = 20; int k = 30; //& “与”,并且的关系,只要表达式中有一个值为false,结果即为false System.out.println((i > j) & (i > k)); //false & false,输出false System.out.println((i < j) & (i > k)); //true & false,输出false System.out.println((i > j) & (i < k)); //false & true,输出false System.out.println((i < j) & (i < k)); //true & true,输出true System.out.println("--------"); //| “或”,或者的关系,只要表达式中有一个值为true,结果即为true System.out.println((i > j) | (i > k)); //false | false,输出false System.out.println((i < j) | (i > k)); //true | false,输出true System.out.println((i > j) | (i < k)); //false | true,输出true System.out.println((i < j) | (i < k)); //true | true,输出true System.out.println("--------"); //^ “异或”,相同为false,不同为true System.out.println((i > j) ^ (i > k)); //false ^ false,输出false System.out.println((i < j) ^ (i > k)); //true ^ false,输出true System.out.println((i > j) ^ (i < k)); //false ^ true,输出true System.out.println((i < j) ^ (i < k)); //true ^ true,输出false System.out.println("--------"); //! “非”,取反 System.out.println((i > j)); //false System.out.println(!(i > j)); //!false,,输出true
短路逻辑运算符
在逻辑与运算中,只要有一个表达式的值为false,那么结果就可以判定为false了,没有必要将所有表达式的值都计算出来,短路与操作就有这样的效果,可以提高效率。同理在逻辑或运算中,一旦发现值为true,右边的表达式将不再参与运算。
逻辑与&,无论左边真假,右边都要执行。
短路与&&,如果左边为真,右边执行;如果左边为假,右边不执行。
逻辑或|,无论左边真假,右边都要执行。
短路或||,如果左边为假,右边执行;如果左边为真,右边不执行。
例如
int x = 3; int y = 4; System.out.println((x++ > 4) & (y++ > 5)); // 两个表达都会运算 System.out.println(x); // 4 System.out.println(y); // 5 System.out.println((x++ > 4) && (y++ > 5)); // 左边已经可以确定结果为false,右边不参与运算 System.out.println(x); // 4 System.out.println(y); // 4
三元运算符(理解)
三元运算符语法格式:
关系表达式 ? 表达式1 : 表达式2;
解释:问号前面的位置是判断的条件,判断结果为boolean型,为true时调用表达式1,为false时调用表达式2。其逻辑为:如果条件表达式成立或者满足则执行表达式1,否则执行第二个。
例如
int a = 10; int b = 20; int c = a > b ? a : b; // 判断 a>b 是否为真,如果为真取a的值,如果为假,取b的值
案例:
1
需求
动物园里有两只老虎,已知两只老虎的体重分别为180kg、200kg,请用程序实现判断两只老虎的体重是否相同。
代码
public class OperatorTest01 { public static void main(String[] args) { //1:定义两个变量用于保存老虎的体重,单位为kg,这里仅仅体现数值即可。 int weight1 = 180; int weight2 = 200; //2:用三元运算符实现老虎体重的判断,体重相同,返回true,否则,返回false。 boolean b = weight1 == weight2 ? true : false; //3:输出结果 System.out.println("b:" + b); } }
2
需求
一座寺庙里住着三个和尚,已知他们的身高分别为150cm、210cm、165cm,请用程序实现获取这三个和尚的最高身高。
代码
public class OperatorTest02 { public static void main(String[] args) { //1:定义三个变量用于保存和尚的身高,单位为cm,这里仅仅体现数值即可。 int height1 = 150; int height2 = 210; int height3 = 165; //2:用三元运算符获取前两个和尚的较高身高值,并用临时身高变量保存起来。 int tempHeight = height1 > height2 ? height1 : height2; //3:用三元运算符获取临时身高值和第三个和尚身高较高值,并用最大身高变量保存。 int maxHeight = tempHeight > height3 ? tempHeight : height3; //4:输出结果 System.out.println("maxHeight:" + maxHeight); } }
9. 数据输入(应用)
我们可以通过 Scanner 类来获取用户的输入。使用步骤如下:
1、导包。Scanner 类在java.util包下,所以需要将该类导入。导包的语句需要定义在类的上面。
import java.util.Scanner;
2、创建Scanner对象。
Scanner sc = new Scanner(System.in);// 创建Scanner对象,sc表示变量名,其他均不可变
3、接收数据
int i = sc.nextInt(); // 表示将键盘录入的值作为int数返回。
例如
改写三个和尚案例,数据使用键盘录入。
import java.util.Scanner; public class ScannerTest { public static void main(String[] args) { //身高未知,采用键盘录入实现。首先导包,然后创建对象。 Scanner sc = new Scanner(System.in); //键盘录入三个身高分别赋值给三个变量。 System.out.println("请输入第一个和尚的身高:"); int height1 = sc.nextInt(); System.out.println("请输入第二个和尚的身高:"); int height2 = sc.nextInt(); System.out.println("请输入第三个和尚的身高:"); int height3 = sc.nextInt(); //用三元运算符获取前两个和尚的较高身高值,并用临时身高变量保存起来。 int tempHeight = height1 > height2 ? height1 : height2; //用三元运算符获取临时身高值和第三个和尚身高较高值,并用最大身高变量保存。 int maxHeight = tempHeight > height3 ? tempHeight : height3; //输出结果。 System.out.println("这三个和尚中身高最高的是:" + maxHeight +"cm"); } }
10. 流程控制语句(应用)
在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,我们必须清楚每条语句的执行流程。而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能。
流程控制语句分类
顺序结构
分支结构(if, switch)
循环结构(for, while, do…while)
顺序结构
顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。
顺序结构执行流程图:
分支结构之if语句
1
格式
执行流程:
①首先计算关系表达式的值
②如果关系表达式的值为true就执行语句体
③如果关系表达式的值为false就不执行语句体
④继续执行后面的语句内容
例如
public class IfDemo { public static void main(String[] args) { System.out.println("开始"); //定义两个变量 int a = 10; int b = 20; //需求:判断a和b的值是否相等,如果相等,就在控制台输出:a等于b if(a == b) { System.out.println("a等于b"); } //需求:判断a和c的值是否相等,如果相等,就在控制台输出:a等于c int c = 10; if(a == c) { System.out.println("a等于c"); } System.out.println("结束"); } }
2
格式
执行流程:
①首先计算关系表达式的值
②如果关系表达式的值为true就执行语句体1
③如果关系表达式的值为false就执行语句体2
④继续执行后面的语句内容
例如
public class IfDemo02 { public static void main(String[] args) { System.out.println("开始"); //定义两个变量 int a = 10; int b = 20; b = 5; //需求:判断a是否大于b,如果是,在控制台输出:a的值大于b,否则,在控制台输出:a的值不大于b if(a > b) { System.out.println("a的值大于b"); } else { System.out.println("a的值不大于b"); } System.out.println("结束"); } }
案例
奇偶数
需求
任意给出一个整数,请用程序实现判断该整数是奇数还是偶数,并在控制台输出该整数是奇数还是偶数。
分析
①为了体现任意给出一个整数,采用键盘录入一个数据
②判断整数是偶数还是奇数要分两种情况进行判断,使用if..else结构
③判断是否偶数需要使用取余运算符实现该功能 number % 2 == 0
④根据判定情况,在控制台输出对应的内容
代码
import java.util.Scanner; public class IfTest01 { public static void main(String[] args) { //为了体现任意给出一个整数,采用键盘录入一个数据。(导包,创建对象,接收数据) Scanner sc = new Scanner(System.in); System.out.println("请输入一个整数:"); int number = sc.nextInt(); //判断整数是偶数还是奇数要分两种情况进行判断,使用if..else结构 //判断是否偶数需要使用取余运算符实现该功能 number % 2 == 0 //根据判定情况,在控制台输出对应的内容 if(number%2 == 0) { System.out.println(number + "是偶数"); } else { System.out.println(number + "是奇数"); } } }
3
格式
执行流程:
①首先计算关系表达式1的值
②如果值为true就执行语句体1;如果值为false就计算关系表达式2的值
③如果值为true就执行语句体2;如果值为false就计算关系表达式3的值
④…
⑤如果没有任何关系表达式为true,就执行语句体n+1。
例如
键盘录入一个星期数(1,2,...7),输出对应的星期一,星期二,...星期日
import java.util.Scanner; public class IfDemo03 { public static void main(String[] args) { System.out.println("开始"); // 需求:键盘录入一个星期数(1,2,...7),输出对应的星期一,星期二,...星期日 Scanner sc = new Scanner(System.in); System.out.println("请输入一个星期数(1-7):"); int week = sc.nextInt(); if(week == 1) { System.out.println("星期一"); } else if(week == 2) { System.out.println("星期二"); } else if(week == 3) { System.out.println("星期三"); } else if(week == 4) { System.out.println("星期四"); } else if(week == 5) { System.out.println("星期五"); } else if(week == 6) { System.out.println("星期六"); } else { System.out.println("星期日"); } System.out.println("结束"); } }
案例
需求
小明快要期末考试了,小明爸爸对他说,会根据他不同的考试成绩,送他不同的礼物,假如你可以控制小明的得分,请用程序实现小明到底该获得什么样的礼物,并在控制台输出。
分析
①小明的考试成绩未知,可以使用键盘录入的方式获取值
②由于奖励种类较多,属于多种判断,采用if...else...if格式实现
③为每种判断设置对应的条件
④为每种判断设置对应的奖励
代码
import java.util.Scanner; public class IfTest02 { public static void main(String[] args) { //小明的考试成绩未知,可以使用键盘录入的方式获取值 Scanner sc = new Scanner(System.in); System.out.println("请输入一个分数:"); int score = sc.nextInt(); //由于奖励种类较多,属于多种判断,采用if...else...if格式实现 //为每种判断设置对应的条件 //为每种判断设置对应的奖励 //数据测试:正确数据,边界数据,错误数据 if(score>100 || score<0) { System.out.println("你输入的分数有误"); } else if(score>=95 && score<=100) { System.out.println("山地自行车一辆"); } else if(score>=90 && score<=94) { System.out.println("游乐场玩一次"); } else if(score>=80 && score<=89) { System.out.println("变形金刚玩具一个"); } else { System.out.println("胖揍一顿"); } } }
11. switch语句
switch语句结构(掌握)
格式
执行流程:
首先计算出表达式的值
其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结 束。
最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉。
如果switch中得case,没有对应break的话,则会出现case穿透的现象。
switch语句练习-春夏秋冬(应用)
需求
一年有12个月,分属于春夏秋冬4个季节,键盘录入一个月份,请用程序实现判断该月份属于哪个季节,并输出。
运行结果
例如
public class Demo1 { public static void main(String[] args) { //键盘录入月份数据,使用变量接收 Scanner sc = new Scanner(System.in); System.out.println("请输入一个月份:"); int month = sc.nextInt(); //case穿透 switch(month) { case 1: case 2: case 12: System.out.println("冬季"); break; case 3: case 4: case 5: System.out.println("春季"); break; case 6: case 7: case 8: System.out.println("夏季"); break; case 9: case 10: case 11: System.out.println("秋季"); break; default: System.out.println("你输入的月份有误"); } } }
12. for循环
for循环结构(掌握)
循环
循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复 执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形 成死循环。
for循环格式
格式解释
初始化语句: 用于表示循环开启时的起始状态,简单说就是循环开始的时候什么样
条件判断语句:用于表示循环反复执行的条件,简单说就是判断循环是否能一直执行下去
循环体语句: 用于表示循环反复执行的内容,简单说就是循环反复执行的事情
条件控制语句:用于表示循环执行中每次变化的内容,简单说就是控制循环是否能执行下去
执行流程
①执行初始化语句
②执行条件判断语句,看其结果是true还是false
如果是false,循环结束
如果是true,继续执行
③执行循环体语句
④执行条件控制语句
⑤回到②继续
for循环练习-输出数据(应用)
需求
在控制台输出1-5和5-1的数据
例如
public class ForTest01 { public static void main(String[] args) { //需求:输出数据1-5 for(int i=1; i<=5; i++) { System.out.println(i); } System.out.println("--------"); //需求:输出数据5-1 for(int i=5; i>=1; i--) { System.out.println(i); } } }
for循环练习-求和(应用)
需求
求1-5之间的数据和,并把求和结果在控制台输出
要点
今后遇到的需求中,如果带有求和二字,请立即联想到求和变量
求和变量的定义位置,必须在循环外部,如果在循环内部则计算出的数据将是错误的
例如
public class ForTest02 { public static void main(String[] args) { //求和的最终结果必须保存起来,需要定义一个变量,用于保存求和的结果,初始值为0 int sum = 0; //从1开始到5结束的数据,使用循环结构完成 for(int i=1; i<=5; i++) { //将反复进行的事情写入循环结构内部 // 此处反复进行的事情是将数据 i 加到用于保存最终求和的变量 sum 中 sum += i; /* sum += i; sum = sum + i; 第一次:sum = sum + i = 0 + 1 = 1; 第二次:sum = sum + i = 1 + 2 = 3; 第三次:sum = sum + i = 3 + 3 = 6; 第四次:sum = sum + i = 6 + 4 = 10; 第五次:sum = sum + i = 10 + 5 = 15; */ } //当循环执行完毕时,将最终数据打印出来 System.out.println("1-5之间的数据和是:" + sum); } }
for循环练习-求偶数和(应用)
需求
求1-100之间的偶数和,并把求和结果在控制台输出
例如
public class ForTest03 { public static void main(String[] args) { //求和的最终结果必须保存起来,需要定义一个变量,用于保存求和的结果,初始值为0 int sum = 0; //对1-100的数据求和与1-5的数据求和几乎完全一样,仅仅是结束条件不同 for(int i=1; i<=100; i++) { //对1-100的偶数求和,需要对求和操作添加限制条件,判断是否是偶数 if(i%2 == 0) { sum += i; } } //当循环执行完毕时,将最终数据打印出来 System.out.println("1-100之间的偶数和是:" + sum); } }
for循环练习-水仙花(应用)
需求
在控制台输出所有的“水仙花数”
解释
什么是水仙花数?
水仙花数,指的是一个三位数,个位、十位、百位的数字立方和等于原数
例如`153 3*3*3 + 5*5*5 + 1*1*1 = 153`
思路
1. 获取所有的三位数,准备进行筛选,最小的三位数为100,最大的三位数为999,使用for循环获取
2. 获取每一个三位数的个位,十位,百位,做if语句判断是否是水仙花数
例如
public class ForTest04 { public static void main(String[] args) { //输出所有的水仙花数必然要使用到循环,遍历所有的三位数,三位数从100开始,到999结束 for(int i=100; i<1000; i++) { //在计算之前获取三位数中每个位上的值 int ge = i%10; int shi = i/10%10; int bai = i/10/10%10; //判定条件是将三位数中的每个数值取出来,计算立方和后与原始数字比较是否相等 if(ge*ge*ge + shi*shi*shi + bai*bai*bai == i) { //输出满足条件的数字就是水仙花数 System.out.println(i); } } } }
for循环练习-统计水仙花数个数(应用)
需求
统计“水仙花数”一共有多少个,并在控制台输出个数
要点
今后如果需求带有统计xxx,请先想到计数器变量
计数器变量定义的位置,必须在循环外部
例如
public class ForTest05 { public static void main(String[] args) { //定义变量count,用于保存“水仙花数”的数量,初始值为0 int count = 0; //输出所有的水仙花数必然要使用到循环,遍历所有的三位数,三位数从100开始,到999结束 for(int i=100; i<1000; i++) { //在计算之前获取三位数中每个位上的值 int ge = i%10; int shi = i/10%10; int bai = i/10/10%10; //在判定水仙花数的过程中,满足条件不再输出,更改为修改count的值,使count+1 if(ge*ge*ge + shi*shi*shi + bai*bai*bai == i) { count++; } } //打印输出最终结果 System.out.println("水仙花共有:" + count + "个"); } }
13. while循环
while结构(掌握)
格式
流程
①执行初始化语句
②执行条件判断语句,看其结果是true还是false
如果是false,循环结束
如果是true,继续执行
③执行循环体语句
④执行条件控制语句
⑤回到②继续
例如
public class WhileDemo { public static void main(String[] args) { //需求:在控制台输出5次"HelloWorld" //for循环实现 for(int i=1; i<=5; i++) { System.out.println("HelloWorld"); } System.out.println("--------"); //while循环实现 int j = 1; while(j<=5) { System.out.println("HelloWorld"); j++; } } }
while循环练习-珠穆朗玛峰(应用)
需求
世界最高山峰是珠穆朗玛峰(8844.43米=8844430毫米),假如我有一张足够大的纸,它的厚度是0.1毫米。请问,我折叠多少次,可以折成珠穆朗玛峰的高度?
例如
public class WhileTest { public static void main(String[] args) { //定义一个计数器,初始值为0 int count = 0; //定义纸张厚度 double paper = 0.1; //定义珠穆朗玛峰的高度 int zf = 8844430; //因为要反复折叠,所以要使用循环,但是不知道折叠多少次,这种情况下更适合使用while循环 //折叠的过程中当纸张厚度大于珠峰就停止了,因此继续执行的要求是纸张厚度小于珠峰高度 while(paper <= zf) { //循环的执行过程中每次纸张折叠,纸张的厚度要加倍 paper *= 2; //在循环中执行累加,对应折叠了多少次 count++; } //打印计数器的值 System.out.println("需要折叠:" + count + "次"); } }
14. 循环细节
do...while循环结构(掌握)
格式
流程
① 执行初始化语句
② 执行循环体语句
③ 执行条件控制语句
④ 执行条件判断语句,看其结果是true还是false
如果是false,循环结束
如果是true,继续执行
⑤ 回到②继续
例如
public class DoWhileDemo { public static void main(String[] args) { //需求:在控制台输出5次"HelloWorld" //for循环实现 for(int i=1; i<=5; i++) { System.out.println("HelloWorld"); } System.out.println("--------"); //do...while循环实现 int j = 1; do { System.out.println("HelloWorld"); j++; }while(j<=5); } }
三种循环的区别(理解)
三种循环的区别
for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
do...while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)
for循环和while的区别
条件控制语句所控制的自增变量,因为归属for循环的语法结构中,在for循环结束后,就不能再次被访问到了
条件控制语句所控制的自增变量,对于while循环来说不归属其语法结构中,在while循环结束后,该变量还可以继续使用
死循环(无限循环)的三种格式
1. for(;;){}
2. while(true){}
3. do {} while(true);
跳转控制语句(掌握)
跳转控制语句(break)
跳出循环,结束循环
跳转控制语句(continue)
跳过本次循环,继续下次循环
注意: continue只能在循环中进行使用!
循环嵌套(理解)
概述
在循环中,继续定义循环
理解
请反复理解这句话(整个内循环,就是外循环的一个循环体,内部循环体没有执行完毕,外循环是不会继续向下执行的)
结论
外循环执行一次,内循环执行一圈
例如
public static void main(String[] args) { //外循环控制小时的范围,内循环控制分钟的范围 for (int hour = 0; hour < 24; hour++) { for (int minute = 0; minute < 60; minute++) { System.out.println(hour + "时" + minute + "分"); } System.out.println("--------"); } }
15. Random
Random产生随机数(掌握)
概述
Random类似Scanner,也是Java提供好的API,内部提供了产生随机数的功能
步骤
1. 导入包
import java.util.Random;
2. 创建对象
Random r = new Random();
3. 产生随机数
int num = r.nextInt(10);
解释
10代表的是一个范围,如果括号写10,产生的随机数就是0-9,括号写20,参数的随机数则是0-19
例如
import java.util.Random; public class RandomDemo { public static void main(String[] args) { //创建对象 Random r = new Random(); //用循环获取10个随机数 for(int i=0; i<10; i++) { //获取随机数 int number = r.nextInt(10); System.out.println("number:" + number); } //需求:获取一个1-100之间的随机数 int x = r.nextInt(100) + 1; System.out.println(x); } }
Random练习-猜数字(应用)
需求
程序自动生成一个1-100之间的数字,使用程序实现猜出这个数字是多少?
当猜错的时候根据不同情况给出相应的提示
A. 如果猜的数字比真实数字大,提示你猜的数据大了
B. 如果猜的数字比真实数字小,提示你猜的数据小了
C. 如果猜的数字与真实数字相等,提示恭喜你猜中了
例如
import java.util.Random; import java.util.Scanner; public class RandomTest { public static void main(String[] args) { //要完成猜数字的游戏,首先需要有一个要猜的数字,使用随机数生成该数字,范围1到100 Random r = new Random(); int number = r.nextInt(100) + 1; while(true) { //使用程序实现猜数字,每次均要输入猜测的数字值,需要使用键盘录入实现 Scanner sc = new Scanner(System.in); System.out.println("请输入你要猜的数字:"); int guessNumber = sc.nextInt(); //比较输入的数字和系统产生的数据,需要使用分支语句。 //这里使用if..else..if..格式,根据不同情况进行猜测结果显示 if(guessNumber > number) { System.out.println("你猜的数字" + guessNumber + "大了"); } else if(guessNumber < number) { System.out.println("你猜的数字" + guessNumber + "小了"); } else { System.out.println("恭喜你猜中了"); break; } } } }
16. 数组
什么是数组【理解】
数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致。
数组定义格式【记忆】
第一种
数据类型[] 数组名
例如
第二种
数据类型 数组名[]
例如
数组动态初始化【应用】
定义
数组动态初始化就是只给定数组的长度,由系统给出默认初始化值
格式
例如
格式详解
等号左边:
int:数组的数据类型
[]:代表这是一个数组
arr:代表数组的名称
等号右边:
new:为数组开辟内存空间
int:数组的数据类型
[]:代表这是一个数组
5:代表数组的长度
数组元素访问【应用】
定义
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始。
这个自动编号称为数组索引(index),可以通过数组的索引访问到数组中的元素。
格式
例如
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[3]; //输出数组名 System.out.println(arr); //[I@880ec60 //输出数组中的元素 System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); } }
内存分配【理解】
概述
内存是计算机中的重要原件,临时存储区域,作用是运行程序。
我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的。
必须放进内存中才能运行,运行完毕后会清空内存。
Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。
java中的内存分配
目前我们只需要记住两个内存,分别是:栈内存和堆内存
数组静态初始化【应用】
定义
在创建数组时,直接将元素确定
格式
完整版格式
简化版格式
例如
public class ArrayDemo { public static void main(String[] args) { //定义数组 int[] arr = {1, 2, 3}; //输出数组名 System.out.println(arr); //输出数组中的元素 System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); } }
数组操作的两个常见小问题【应用】
索引越界异常
原因
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[3]; System.out.println(arr[3]); } }
数组长度为3,索引范围是0~2,但是我们却访问了一个3的索引。
程序运行后,将会抛出ArrayIndexOutOfBoundsException 数组越界异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。
解决
将错误的索引修改为正确的索引范围即可!
空指针异常
原因
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[3]; //把null赋值给数组 arr = null; System.out.println(arr[0]); } }
arr = null 这行代码,意味着变量arr将不会在保存数组的内存地址,也就不允许再操作数组了,因此运行的时候会抛出 NullPointerException 空指针异常。在开发中,空指针异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。
解决
给数组一个真正的堆内存空间引用即可!
数组遍历【应用】
定义
就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。
例如
public class ArrayTest01 { 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 class ArrayTest01 { public static void main(String[] args) { //定义数组 int[] arr = {11, 22, 33, 44, 55}; //使用通用的遍历格式 for(int x=0; x<arr.length; x++) { System.out.println(arr[x]); } } }
数组最值【应用】
需求
最大值获取:从数组的所有元素中找出最大值。
思路
定义变量,保存数组0索引上的元素
遍历数组,获取出数组中的每个元素
将遍历到的元素和保存数组0索引上值的变量进行比较
如果数组元素的值大于了变量的值,变量记录住新的值
数组循环遍历结束,变量保存的就是数组中的最大值
代码
public class ArrayTest02 { public static void main(String[] args) { //定义数组 int[] arr = {12, 45, 98, 73, 60}; //定义一个变量,用于保存最大值 //取数组中第一个数据作为变量的初始值 int max = arr[0]; //与数组中剩余的数据逐个比对,每次比对将最大值保存到变量中 for(int x=1; x<arr.length; x++) { if(arr[x] > max) { max = arr[x]; } } //循环结束后打印变量的值 System.out.println("max:" + max); } }
17. 方法
方法的概念(理解)
方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集
注意
方法必须先创建才可以使用,该过程成为方法定义
方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程成为方法调用
方法的定义和调用
无参数方法定义和调用(掌握)
定义格式
例如
调用格式
例如
注意
方法必须先定义,后调用,否则程序将报错
方法调用过程图解(理解)
总结
每个方法在被调用执行的时候,都会进入栈内存,并且拥有自己独立的内存空间,方法内部代码调用完毕之后,会从栈内存中弹栈消失。
无参数方法的练习(应用)
需求
设计一个方法用于打印两个数中的较大数
思路
①定义一个方法,用于打印两个数字中的较大数,例如getMax()
②方法中定义两个变量,用于保存两个数字
③使用分支语句分两种情况对两个数字的大小关系进行处理
④在main()方法中调用定义好的方法
代码
public class MethodTest { public static void main(String[] args) { //在main()方法中调用定义好的方法 getMax(); } //定义一个方法,用于打印两个数字中的较大数,例如getMax() public static void getMax() { //方法中定义两个变量,用于保存两个数字 int a = 10; int b = 20; //使用分支语句分两种情况对两个数字的大小关系进行处理 if(a > b) { System.out.println(a); } else { System.out.println(b); } } }
带参数方法定义和调用
带参数方法定义和调用(掌握)
定义格式
参数
由数据类型和变量名组成 - 数据类型 变量名
参数范例
int a
注意
方法定义时,参数中的数据类型与变量名都不能缺少,缺少任意一个程序将报错
方法定义时,多个参数之间使用逗号( ,)分隔
例如
调用格式
方法调用时,参数的数量与类型必须与方法定义中的设置相匹配,否则程序将报错
例如
形参和实参(理解)
形参
方法定义中的参数
等同于变量定义格式,例如:int number
实参
方法调用中的参数
等同于使用变量或常量,例如: 10 number
带参数方法练习(应用)
需求
设计一个方法用于打印两个数中的较大数,数据来自于方法参数
思路
①定义一个方法,用于打印两个数字中的较大数,例如getMax()
②为方法定义两个参数,用于接收两个数字
③使用分支语句分两种情况对两个数字的大小关系进行处理
④在main()方法中调用定义好的方法(使用常量)
⑤在main()方法中调用定义好的方法(使用变量)
代码
public class MethodTest { public static void main(String[] args) { //在main()方法中调用定义好的方法(使用常量) getMax(10,20); //调用方法的时候,人家要几个,你就给几个,人家要什么类型的,你就给什么类型的 //getMax(30); //getMax(10.0,20.0); //在main()方法中调用定义好的方法(使用变量) int a = 10; int b = 20; getMax(a, b); } //定义一个方法,用于打印两个数字中的较大数,例如getMax() //为方法定义两个参数,用于接收两个数字 public static void getMax(int a, int b) { //使用分支语句分两种情况对两个数字的大小关系进行处理 if(a > b) { System.out.println(a); } else { System.out.println(b); } } }
带返回值方法的定义和调用
带返回值方法定义和调用(掌握)
定义格式
例如
注意
方法定义时return后面的返回值与方法定义上的数据类型要匹配,否则程序将报错
调用格式
方法的返回值通常会使用变量接收,否则该返回值将无意义
例如
带返回值方法练习(应用)
需求
设计一个方法可以获取两个数的较大值,数据来自于参数
思路
①定义一个方法,用于获取两个数字中的较大数
②使用分支语句分两种情况对两个数字的大小关系进行处理
③根据题设分别设置两种情况下对应的返回结果
④在main()方法中调用定义好的方法并使用变量保存
⑤在main()方法中调用定义好的方法并直接打印结果
代码
public class MethodTest { public static void main(String[] args) { //在main()方法中调用定义好的方法并使用变量保存 int result = getMax(10,20); System.out.println(result); //在main()方法中调用定义好的方法并直接打印结果 System.out.println(getMax(10,20)); } //定义一个方法,用于获取两个数字中的较大数 public static int getMax(int a, int b) { //使用分支语句分两种情况对两个数字的大小关系进行处理 //根据题设分别设置两种情况下对应的返回结果 if(a > b) { return a; } else { return b; } } }
方法的注意事项
方法的注意事项(掌握)
方法不能嵌套定义
例如
void表示无返回值,可以省略return,也可以单独的书写return,后面不加数据
例如
方法的通用格式(掌握)
格式
解释
public static 修饰符,目前先记住这个格式 返回值类型 方法操作完毕之后返回的数据的数据类型 如果方法操作完毕,没有数据返回,这里写void,而且方法体中一般不写return 方法名 调用方法时候使用的标识 参数 由数据类型和变量名组成,多个参数之间用逗号隔开 方法体 完成功能的代码块 return 如果方法操作完毕,有数据返回,用于把数据返回给调用者
定义方法时,要做到两个明确
明确返回值类型:主要是明确方法操作完毕之后是否有数据返回,如果没有,写void;如果有,写对应的数据类型
明确参数:主要是明确参数的类型和数量
调用方法时的注意:
void类型的方法,直接调用即可
非void类型的方法,推荐用变量接收调用
方法重载
方法重载(理解)
定义
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载
多个方法在同一个类中
多个方法具有相同的方法名
多个方法的参数不相同,类型不同或者数量不同
注意
重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载
例如
正确
错误
方法重载练习(掌握)
需求
使用方法重载的思想,设计比较两个整数是否相同的方法,兼容全整数类型(byte,short,int,long)
思路
①定义比较两个数字的是否相同的方法compare()方法,参数选择两个int型参数
②定义对应的重载方法,变更对应的参数类型,参数变更为两个long型参数
③定义所有的重载方法,两个byte类型与两个short类型参数
④完成方法的调用,测试运行结果
代码
public class MethodTest { public static void main(String[] args) { //调用方法 System.out.println(compare(10, 20)); System.out.println(compare((byte) 10, (byte) 20)); System.out.println(compare((short) 10, (short) 20)); System.out.println(compare(10L, 20L)); } //int public static boolean compare(int a, int b) { System.out.println("int"); return a == b; } //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; } //long public static boolean compare(long a, long b) { System.out.println("long"); return a == b; } }
方法的参数传递
方法参数传递基本类型(理解)
代码
public class ArgsDemo01 { public static void main(String[] args) { int number = 100; System.out.println("调用change方法前:" + number); change(number); System.out.println("调用change方法后:" + number); } public static void change(int number) { number = 200; } }
结论
基本数据类型的参数,形式参数的改变,不影响实际参数
结论依据
每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失
方法参数传递引用类型(理解)
代码
public class ArgsDemo02 { public static void main(String[] args) { int[] arr = {10, 20, 30}; System.out.println("调用change方法前:" + arr[1]); change(arr); System.out.println("调用change方法后:" + arr[1]); } public static void change(int[] arr) { arr[1] = 200; } }
结论
对于引用类型的参数,形式参数的改变,影响实际参数的值
结论依据
引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果
数组遍历(应用)
需求
设计一个方法用于数组遍历,要求遍历的结果是在一行上的。例如:[11, 22, 33, 44, 55]
思路
①因为要求结果在一行上输出,所以这里需要在学习一个新的输出语句System.out.print(“内容”);
System.out.println(“内容”); 输出内容并换行
System.out.print(“内容”); 输出内容不换行
System.out.println(); 起到换行的作用
②定义一个数组,用静态初始化完成数组元素初始化
③定义一个方法,用数组遍历通用格式对数组进行遍历
④用新的输出语句修改遍历操作
⑤调用遍历方法
代码
public class MethodTest01 { public static void main(String[] args) { //定义一个数组,用静态初始化完成数组元素初始化 int[] arr = {11, 22, 33, 44, 55}; //调用方法 printArray(arr); } //定义一个方法,用数组遍历通用格式对数组进行遍历 /* 两个明确: 返回值类型:void 参数:int[] arr */ public static void printArray(int[] arr) { System.out.print("["); for(int x=0; x<arr.length; x++) { if(x == arr.length-1) { System.out.print(arr[x]); } else { System.out.print(arr[x]+", "); } } System.out.println("]"); } }
数组最大值(应用)
需求
设计一个方法用于获取数组中元素的最大值
思路
①定义一个数组,用静态初始化完成数组元素初始化
②定义一个方法,用来获取数组中的最大值,最值的认知和讲解我们在数组中已经讲解过了
③调用获取最大值方法,用变量接收返回结果
④把结果输出在控制台
代码
public class MethodTest02 { public static void main(String[] args) { //定义一个数组,用静态初始化完成数组元素初始化 int[] arr = {12, 45, 98, 73, 60}; //调用获取最大值方法,用变量接收返回结果 int number = getMax(arr); //把结果输出在控制台 System.out.println("number:" + number); } //定义一个方法,用来获取数组中的最大值 /* 两个明确: 返回值类型:int 参数:int[] arr */ public static int getMax(int[] arr) { int max = arr[0]; for(int x=1; x<arr.length; x++) { if(arr[x] > max) { max = arr[x]; } } return max; } }
18. Debug模式
什么是Debug模式【理解】
是供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试程序。
Debug模式操作流程【应用】
如何加断点
选择要设置断点的代码行,在行号的区域后面单击鼠标左键即可
如何运行加了断点的程序
在代码区域右键Debug执行
看哪里
看Debugger窗口
看Console窗口
点哪里
点Step Into (F7)这个箭头,也可以直接按F7
如何删除断点
- 选择要删除的断点,单击鼠标左键即可
- 如果是多个断点,可以每一个再点击一次。也可以一次性全部删除
19. 基础练习
减肥计划if版本【应用】
需求
输入星期数,显示今天的减肥活动
周一:跑步
周二:游泳
周三:慢走
周四:动感单车
周五:拳击
周六:爬山
周日:好好吃一顿
代码
/* 思路: 1:键盘录入一个星期数,用一个变量接收 2:对星期数进行判断,这里用 if 语句实现 3:在对应的语句控制中输出对应的减肥活动 */ public class Test01 { public static void main(String[] args) { //键盘录入一个星期数,用一个变量接收 Scanner sc = new Scanner(System.in); System.out.println("请输入一个星期数:"); int week = sc.nextInt(); //对星期数进行判断,这里用 if 语句实现 if (week < 1 || week > 7) { System.out.println("你输入的星期数有误"); } else if (week == 1) { System.out.println("跑步"); } else if (week == 2) { System.out.println("游泳"); } else if (week == 3) { System.out.println("慢走"); } else if (week == 4) { System.out.println("动感单车"); } else if (week == 5) { System.out.println("拳击"); } else if (week == 6) { System.out.println("爬山"); } else { System.out.println("好好吃一顿"); } } }
减肥计划switch版本【应用】
需求
输入星期数,显示今天的减肥活动
周一:跑步
周二:游泳
周三:慢走
周四:动感单车
周五:拳击
周六:爬山
周日:好好吃一顿
代码
/* 思路: 1:键盘录入一个星期数,用一个变量接收 2:对星期数进行判断,这里用 switch 语句实现 3:在对应的语句控制中输出对应的减肥活动 导包: 1:手动导包 import java.util.Scanner; 2:快捷键导包 Alt+Enter 3:自动导包 */ public class Test02 { public static void main(String[] args) { //键盘录入一个星期数,用一个变量接收 Scanner sc = new Scanner(System.in); System.out.println("请输入一个星期数:"); int week = sc.nextInt(); //对星期数进行判断,这里用 switch 语句实现 switch (week) { 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("你输入的星期数有误"); } } }
逢七跳过【应用】
需求
朋友聚会的时候可能会玩一个游戏:逢七过。
规则是:从任意一个数字开始报数,当你要报的数字包含7或者是7的倍数时都要说:过。
为了帮助大家更好的玩这个游戏,这里我们直接在控制台打印出1-100之间的满足逢七必过规则的数据。
这样,大家将来在玩游戏的时候,就知道哪些数据要说:过。
代码
/* 思路: 1:数据在1-100之间,用for循环实现数据的获取 2:根据规则,用if语句实现数据的判断:要么个位是7,要么十位是7,要么能够被7整除 3:在控制台输出满足规则的数据 */ public class Test03 { public static void main(String[] args) { //数据在1-100之间,用for循环实现数据的获取 for(int x=1; x<=100; x++) { //根据规则,用if语句实现数据的判断:要么个位是7,要么十位是7,要么能够被7整除 if(x%10==7 || x/10%10==7 || x%7==0) { //在控制台输出满足规则的数据 System.out.println(x); } } } }
不死神兔【应用】
需求
有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,
假如兔子都不死,问第二十个月的兔子对数为多少?
代码
/* 思路: 1:为了存储多个月的兔子对数,定义一个数组,用动态初始化完成数组元素的初始化,长度为20 2:因为第1个月,第2个月兔子的对数是已知的,都是1,所以数组的第1个元素,第2个元素值也都是1 3:用循环实现计算每个月的兔子对数 4:输出数组中最后一个元素的值,就是第20个月的兔子对数 */ public class Test04 { public static void main(String[] args) { //为了存储多个月的兔子对数,定义一个数组,用动态初始化完成数组元素的初始化,长度为20 int[] arr = new int[20]; //因为第1个月,第2个月兔子的对数是已知的,都是1,所以数组的第1个元素,第2个元素值也都是1 arr[0] = 1; arr[1] = 1; //用循环实现计算每个月的兔子对数 for(int x=2; x<arr.length; x++) { arr[x] = arr[x-2] + arr[x-1]; } //输出数组中最后一个元素的值,就是第20个月的兔子对数 System.out.println("第二十个月兔子的对数是:" + arr[19]); } }
百钱白鸡【应用】
需求
我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。
百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?
代码
/* 思路: 1:第1层循环,用于表示鸡翁的范围,初始化表达式的变量定义为 x=0,判断条件是x<=20 2:第2层循环,用于表示鸡母的范围,初始化表达式的变量定义为 y=0,判断条件是y<=33 3:这个时候,用于表示鸡雏的变量 z = 100 – x – y 4:判断表达式 z%3==0 和表达式 5*x + 3*y + z/3 = 100 是否同时成立,如果成立,输出对应的 x,y,z 的值,就是对应的鸡翁,鸡母,鸡雏的值 */ public class Test05 { public static void main(String[] args) { //第1层循环,用于表示鸡翁的范围,初始化表达式的变量定义为 x=0,判断条件是x<=20 for(int x=0; x<=20; x++) { //第2层循环,用于表示鸡母的范围,初始化表达式的变量定义为 y=0,判断条件是y<=33 for(int y=0; y<=33; y++) { //这个时候,用于表示鸡雏的变量 z = 100 – x – y int z = 100 - x - y; //判断表达式 z%3==0 和表达式 5*x + 3*y + z/3 = 100 是否同时成立 if(z%3==0 && 5*x+3*y+z/3==100) { System.out.println(x+","+y+","+z); } } } } }
数组元素求和【应用】
需求
有这样的一个数组,元素是{68,27,95,88,171,996,51,210}。求出该数组中满足要求的元素和,
要求是:求和的元素个位和十位都不能是7,并且只能是偶数
代码
/* 思路: 1:定义一个数组,用静态初始化完成数组元素的初始化 2:定义一个求和变量,初始值是0 3:遍历数组,获取到数组中的每一个元素 4:判断该元素是否满足条件,如果满足条件就累加 5:输出求和变量的值 */ public class Test06 { public static void main(String[] args) { //定义一个数组,用静态初始化完成数组元素的初始化 int[] arr = {68, 27, 95, 88, 171, 996, 51, 210}; //定义一个求和变量,初始值是0 int sum = 0; //遍历数组,获取到数组中的每一个元素 for(int x=0; x<arr.length; x++) { //判断该元素是否满足条件,如果满足条件就累加 if(arr[x]%10!=7 && arr[x]/10%10!=7 && arr[x]%2==0) { sum += arr[x]; } } //输出求和变量的值 System.out.println("sum:" + sum); } }
判断两个数组是否相同【应用】
需求
定义一个方法,用于比较两个数组的内容是否相同
代码
查找元素在数组中出现的索引位置【应用】
需求
已知一个数组 arr = {19, 28, 37, 46, 50}; 键盘录入一个数据,查找该数据在数组中的索引。
并在控制台输出找到的索引值。如果没有查找到,则输出-1
代码
/* 思路: 1:定义一个数组,用静态初始化完成数组元素的初始化 2:键盘录入要查找的数据,用一个变量接收 3:定义一个索引变量,初始值为-1 4:遍历数组,获取到数组中的每一个元素 5:拿键盘录入的数据和数组中的每一个元素进行比较,如果值相同,就把该值对应的索引赋值给索引变量,并结束循环 6:输出索引变量 */ public class Test08 { public static void main(String[] args) { //定义一个数组,用静态初始化完成数组元素的初始化 int[] arr = {19, 28, 37, 46, 50}; //键盘录入要查找的数据,用一个变量接收 Scanner sc = new Scanner(System.in); System.out.println("请输入要查找的数据:"); int number = sc.nextInt(); //调用方法 int index = getIndex(arr, number); //输出索引变量 System.out.println("index: " + index); } //查找指定的数据在数组中的索引 /* 两个明确: 返回值类型:int 参数:int[] arr, int number */ public static int getIndex(int[] arr, int number) { //定义一个索引变量,初始值为-1 int index = -1; //遍历数组,获取到数组中的每一个元素 for(int x=0; x<arr.length; x++) { //拿键盘录入的数据和数组中的每一个元素进行比较,如果值相同,就把该值对应的索引赋值给索引变量,并结束循环 if(arr[x] == number) { index = x; break; } } //返回索引 return index; } }
数组元素反转【应用】
需求
已知一个数组 arr = {19, 28, 37, 46, 50}; 用程序实现把数组中的元素值交换,
交换后的数组 arr = {50, 46, 37, 28, 19}; 并在控制台输出交换后的数组元素。
代码
评委打分【应用】
需求
在编程竞赛中,有6个评委为参赛的选手打分,分数为0-100的整数分。
选手的最后得分为:去掉一个最高分和一个最低分后 的4个评委平均值 (不考虑小数部分)。
代码
/* 思路: 1:定义一个数组,用动态初始化完成数组元素的初始化,长度为6 2:键盘录入评委分数 3:由于是6个评委打分,所以,接收评委分数的操作,用循环改进 4:定义方法实现获取数组中的最高分(数组最大值),调用方法 5:定义方法实现获取数组中的最低分(数组最小值) ,调用方法 6:定义方法实现获取数组中的所有元素的和(数组元素求和) ,调用方法 7:按照计算规则进行计算得到平均分 8:输出平均分 */ public class Test10 { public static void main(String[] args) { //定义一个数组,用动态初始化完成数组元素的初始化,长度为6 int[] arr = new int[6]; //键盘录入评委分数 Scanner sc = new Scanner(System.in); //由于是6个评委打分,所以,接收评委分数的操作,用循环改进 for(int x=0; x<arr.length; x++) { System.out.println("请输入第" + (x + 1) + "个评委的打分:"); arr[x] = sc.nextInt(); } //printArray(arr); //定义方法实现获取数组中的最高分(数组最大值),调用方法 int max = getMax(arr); //定义方法实现获取数组中的最低分(数组最小值) ,调用方法 int min = getMin(arr); //定义方法实现获取数组中的所有元素的和(数组元素求和) ,调用方法 int sum = getSum(arr); //按照计算规则进行计算得到平均分 int avg = (sum - max - min) / (arr.length - 2); //输出平均分 System.out.println("选手的最终得分是:" + avg); } /* 两个明确: 返回值类型:int 参数:int[] arr */ public static int getSum(int[] arr) { int sum = 0; for(int x=0; x<arr.length; x++) { sum += arr[x]; } return sum; } /* 两个明确: 返回值类型:int 参数:int[] arr */ public static int getMin(int[] arr) { int min = arr[0]; for(int x=1; x<arr.length; x++) { if(arr[x] < min) { min = arr[x]; } } return min; } /* 两个明确: 返回值类型:int 参数:int[] arr */ public static int getMax(int[] arr) { int max = arr[0]; for(int x=1; x<arr.length; x++) { if(arr[x] > max) { max = arr[x]; } } return max; } //遍历数组 public static void printArray(int[] arr) { System.out.print("["); for (int x = 0; x < arr.length; x++) { if (x == arr.length - 1) { System.out.print(arr[x]); } else { System.out.print(arr[x] + ", "); } } System.out.println("]"); } }
20. 类和对象
类和对象的理解【理解】
客观存在的事物皆为对象 ,所以我们也常常说万物皆对象。
类
类的理解
类是对现实生活中一类具有共同属性和行为的事物的抽象
类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
简单理解:类就是对现实事物的一种描述
类的组成
* 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
* 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
类和对象的关系
类:类是对现实生活中一类具有共同属性和行为的事物的抽象
对象:是能够看得到摸的着的真实存在的实体
简单理解:类是对事物的一种描述,对象则为具体存在的事物
类的定义【应用】
类的组成是由属性和行为两部分组成
* 属性:在类中通过成员变量来体现(类中方法外的变量)
* 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可)
类的定义步骤:
①定义类
②编写类的成员变量
③编写类的成员方法
例如
/* 手机类: 类名: 手机(Phone) 成员变量: 品牌(brand) 价格(price) 成员方法: 打电话(call) 发短信(sendMessage) */ public class Phone { //成员变量 String brand; int price; //成员方法 public void call() { System.out.println("打电话"); } public void sendMessage() { System.out.println("发短信"); } }
对象的使用【应用】
创建对象的格式:
类名 对象名 = new 类名();
调用成员的格式:
对象名.成员变量
对象名.成员方法();
例如
/* 创建对象 格式:类名 对象名 = new 类名(); 范例:Phone p = new Phone(); 使用对象 1:使用成员变量 格式:对象名.变量名 范例:p.brand 2:使用成员方法 格式:对象名.方法名() 范例:p.call() */ public class PhoneDemo { public static void main(String[] args) { //创建对象 Phone p = new Phone(); //使用成员变量 System.out.println(p.brand); System.out.println(p.price); p.brand = "小米"; p.price = 2999; System.out.println(p.brand); System.out.println(p.price); //使用成员方法 p.call(); p.sendMessage(); } }
学生对象-练习【应用】
需求
首先定义一个学生类,然后定义一个学生测试类,在学生测试类中通过对象完成成员变量和成员方法的使用
思路
* 成员变量:姓名,年龄…
* 成员方法:学习,做作业…
代码
class Student { //成员变量 String name; int age; //成员方法 public void study() { System.out.println("好好学习,天天向上"); } public void doHomework() { System.out.println("键盘敲烂,月薪过万"); } } /* 学生测试类 */ public class StudentDemo { public static void main(String[] args) { //创建对象 Student s = new Student(); //使用对象 System.out.println(s.name + "," + s.age); s.name = "林青霞"; s.age = 30; System.out.println(s.name + "," + s.age); s.study(); s.doHomework(); } }
21. 对象内存图
单个对象内存图【理解】
成员变量使用过程
成员方法调用过程
多个对象内存图【理解】
成员变量使用过程
成员方法调用过程
总结
多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份
多个对象指向相同内存图【理解】
总结
当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)
只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。
22. 成员变量和局部变量
成员变量和局部变量的区别【理解】
* 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
* 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
* 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,随着方法的调用完毕而消失)
* 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
23. 封装
private关键字【理解】
private是一个修饰符,可以用来修饰成员(成员变量,成员方法)
被private修饰的成员,只能在本类进行访问,针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作
提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰
提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰
例如
/* 学生类 */ class Student { //成员变量 String name; private int age; //提供get/set方法 public void setAge(int a) { if(a<0 || a>120) { System.out.println("你给的年龄有误"); } else { age = a; } } public int getAge() { return age; } //成员方法 public void show() { System.out.println(name + "," + age); } } /* 学生测试类 */ public class StudentDemo { public static void main(String[] args) { //创建对象 Student s = new Student(); //给成员变量赋值 s.name = "林青霞"; s.setAge(30); //调用show方法 s.show(); } }
private的使用【应用】
需求
定义标准的学生类,要求name和age使用private修饰,并提供set和get方法以及便于显示数据的show方法,测试类中创建对象并使用,最终控制台输出 林青霞,30
代码
/* 学生类 */ class Student { //成员变量 private String name; private int age; //get/set方法 public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } public void show() { System.out.println(name + "," + age); } } /* 学生测试类 */ public class StudentDemo { public static void main(String[] args) { //创建对象 Student s = new Student(); //使用set方法给成员变量赋值 s.setName("林青霞"); s.setAge(30); s.show(); //使用get方法获取成员变量的值 System.out.println(s.getName() + "---" + s.getAge()); System.out.println(s.getName() + "," + s.getAge()); } }
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; } public void show() { System.out.println(name + "," + age); } }
this内存原理【理解】
* this代表当前调用方法的引用,哪个对象调用的方法,this就代表哪一个对象
例如
图解
封装思想【理解】
封装概述
是面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
封装原则
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的getXxx()/setXxx()方法
封装好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
24. 构造方法
构造方法概述【理解】
定义
构造方法是一种特殊的方法
作用
创建对象 Student stu = **new Student();**
格式
功能
主要是完成对象数据的初始化
示例
class Student { private String name; private int age; //构造方法 public Student() { System.out.println("无参构造方法"); } public void show() { System.out.println(name + "," + age); } } /* 测试类 */ public class StudentDemo { public static void main(String[] args) { //创建对象 Student s = new Student(); s.show(); } }
构造方法的注意事项【理解】
构造方法的创建
如果没有定义构造方法,系统将给出一个默认的无参数构造方法
如果定义了构造方法,系统将不再提供默认的构造方法
构造方法的重载
如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
推荐的使用方式
无论是否使用,都手工书写无参数构造方法
重要功能!
可以使用带参构造,为成员变量进行初始化
例如
/* 学生类 */ class Student { private String name; private int age; public Student() {} public Student(String name) { this.name = name; } public Student(int age) { this.age = age; } public Student(String name,int age) { this.name = name; this.age = age; } public void show() { System.out.println(name + "," + age); } } /* 测试类 */ public class StudentDemo { public static void main(String[] args) { //创建对象 Student s1 = new Student(); s1.show(); //public Student(String name) Student s2 = new Student("林青霞"); s2.show(); //public Student(int age) Student s3 = new Student(30); s3.show(); //public Student(String name,int age) Student s4 = new Student("林青霞",30); s4.show(); } }
标准类制作【应用】
需求
定义标准学生类,要求分别使用空参和有参构造方法创建对象,空参创建的对象通过setXxx赋值,有参创建的对象直接赋值,并通过show方法展示数据。
例如
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 void show() { System.out.println(name + "," + age); } } /* 创建对象并为其成员变量赋值的两种方式 1:无参构造方法创建对象后使用setXxx()赋值 2:使用带参构造方法直接创建带有属性值的对象 */ public class StudentDemo { public static void main(String[] args) { //无参构造方法创建对象后使用setXxx()赋值 Student s1 = new Student(); s1.setName("林青霞"); s1.setAge(30); s1.show(); //使用带参构造方法直接创建带有属性值的对象 Student s2 = new Student("林青霞",30); s2.show(); } }
25. API
API概述【理解】
什么是API
API (Application Programming Interface) :应用程序编程接口
java中的API
指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们可以通过帮助文档来学习这些API如何使用。
如何使用API帮助文档【应用】
- 打开帮助文档
- 找到索引选项卡中的输入框
- 在输入框中输入Random
- 看类在哪个包下
- 看类的描述
- 看构造方法
- 看成员方法
26. String类
String类概述【理解】
String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!
String类的特点【理解】
字符串不可变,它们的值在创建后不能被更改
虽然 String 的值是不可变的,但是它们可以被共享
字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
String类的构造方法【记忆】
常用的构造方法
例如
public class StringDemo01 { public static void main(String[] args) { //public String():创建一个空白字符串对象,不含有任何内容 String s1 = new String(); System.out.println("s1:" + s1); //public String(char[] chs):根据字符数组的内容,来创建字符串对象 char[] chs = {'a', 'b', 'c'}; String s2 = new String(chs); System.out.println("s2:" + s2); //public String(byte[] bys):根据字节数组的内容,来创建字符串对象 byte[] bys = {97, 98, 99}; String s3 = new String(bys); System.out.println("s3:" + s3); //String s = “abc”; 直接赋值的方式创建字符串对象,内容就是abc String s4 = "abc"; System.out.println("s4:" + s4); } }
创建字符串对象两种方式的区别【理解】
通过构造方法创建
通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同
直接赋值方式创建
以“”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护
字符串的比较【理解】
==号的作用
- 比较基本数据类型:比较的是具体的值
- 比较引用数据类型:比较的是对象地址值
equals方法的作用
介绍
例如
public class StringDemo02 { public static void main(String[] args) { //构造方法的方式得到对象 char[] chs = {'a', 'b', 'c'}; String s1 = new String(chs); String s2 = new String(chs); //直接赋值的方式得到对象 String s3 = "abc"; String s4 = "abc"; //比较字符串对象地址是否相同 System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s3 == s4); System.out.println("--------"); //比较字符串内容是否相同 System.out.println(s1.equals(s2)); System.out.println(s1.equals(s3)); System.out.println(s3.equals(s4)); } }
用户登录案例【应用】
需求
已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示
例如
/* 思路: 1:已知用户名和密码,定义两个字符串表示即可 2:键盘录入要登录的用户名和密码,用 Scanner 实现 3:拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。字符串的内容比较,用equals() 方法实现 4:用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环 */ public class StringTest01 { public static void main(String[] args) { //已知用户名和密码,定义两个字符串表示即可 String username = "wry"; String password = "wry"; //用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环 for(int i=0; i<3; i++) { //键盘录入要登录的用户名和密码,用 Scanner 实现 Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String name = sc.nextLine(); System.out.println("请输入密码:"); String pwd = sc.nextLine(); //拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。字符串的内容比较,用equals() 方法实现 if (name.equals(username) && pwd.equals(password)) { System.out.println("登录成功"); break; } else { if(2-i == 0) { System.out.println("你的账户被锁定,请与管理员联系"); } else { //2,1,0 //i,0,1,2 System.out.println("登录失败,你还有" + (2 - i) + "次机会"); } } } } }
遍历字符串案例【应用】
需求
键盘录入一个字符串,使用程序实现在控制台遍历该字符串
例如
/* 思路: 1:键盘录入一个字符串,用 Scanner 实现 2:遍历字符串,首先要能够获取到字符串中的每一个字符 public char charAt(int index):返回指定索引处的char值,字符串的索引也是从0开始的 3:遍历字符串,其次要能够获取到字符串的长度 public int length():返回此字符串的长度 数组的长度:数组名.length 字符串的长度:字符串对象.length() 4:遍历字符串的通用格式 */ public class StringTest02 { public static void main(String[] args) { //键盘录入一个字符串,用 Scanner 实现 Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String line = sc.nextLine(); for(int i=0; i<line.length(); i++) { System.out.println(line.charAt(i)); } } }
统计字符次数案例【应用】
需求
键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
例如
/* 思路: 1:键盘录入一个字符串,用 Scanner 实现 2:要统计三种类型的字符个数,需定义三个统计变量,初始值都为0 3:遍历字符串,得到每一个字符 4:判断该字符属于哪种类型,然后对应类型的统计变量+1 假如ch是一个字符,我要判断它属于大写字母,小写字母,还是数字,直接判断该字符是否在对应的范围即可 大写字母:ch>='A' && ch<='Z' 小写字母: ch>='a' && ch<='z' 数字: ch>='0' && ch<='9' 5:输出三种类型的字符个数 */ public class StringTest03 { public static void main(String[] args) { //键盘录入一个字符串,用 Scanner 实现 Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String line = sc.nextLine(); //要统计三种类型的字符个数,需定义三个统计变量,初始值都为0 int bigCount = 0; int smallCount = 0; int numberCount = 0; //遍历字符串,得到每一个字符 for(int i=0; i<line.length(); i++) { char ch = line.charAt(i); //判断该字符属于哪种类型,然后对应类型的统计变量+1 if(ch>='A' && ch<='Z') { bigCount++; } else if(ch>='a' && ch<='z') { smallCount++; } else if(ch>='0' && ch<='9') { numberCount++; } } //输出三种类型的字符个数 System.out.println("大写字母:" + bigCount + "个"); System.out.println("小写字母:" + smallCount + "个"); System.out.println("数字:" + numberCount + "个"); } }
字符串拼接案例【应用】
需求
定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,
并在控制台输出结果。例如,数组为 int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]
例如
/* 思路: 1:定义一个 int 类型的数组,用静态初始化完成数组元素的初始化 2:定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回。 返回值类型 String,参数列表 int[] arr 3:在方法中遍历数组,按照要求进行拼接 4:调用方法,用一个变量接收结果 5:输出结果 */ public class StringTest04 { public static void main(String[] args) { //定义一个 int 类型的数组,用静态初始化完成数组元素的初始化 int[] arr = {1, 2, 3}; //调用方法,用一个变量接收结果 String s = arrayToString(arr); //输出结果 System.out.println("s:" + s); } //定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回 /* 两个明确: 返回值类型:String 参数:int[] arr */ public static String arrayToString(int[] arr) { //在方法中遍历数组,按照要求进行拼接 String s = ""; s += "["; for(int i=0; i<arr.length; i++) { if(i==arr.length-1) { s += arr[i]; } else { s += arr[i]; s += ", "; } } s += "]"; return s; } }
字符串反转案例【应用】
需求
定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果
例如,键盘录入 abc,输出结果 cba
例如
/* 思路: 1:键盘录入一个字符串,用 Scanner 实现 2:定义一个方法,实现字符串反转。返回值类型 String,参数 String s 3:在方法中把字符串倒着遍历,然后把每一个得到的字符拼接成一个字符串并返回 4:调用方法,用一个变量接收结果 5:输出结果 */ public class StringTest05 { public static void main(String[] args) { //键盘录入一个字符串,用 Scanner 实现 Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String line = sc.nextLine(); //调用方法,用一个变量接收结果 String s = reverse(line); //输出结果 System.out.println("s:" + s); } //定义一个方法,实现字符串反转 /* 两个明确: 返回值类型:String 参数:String s */ public static String reverse(String s) { //在方法中把字符串倒着遍历,然后把每一个得到的字符拼接成一个字符串并返回 String ss = ""; for(int i=s.length()-1; i>=0; i--) { ss += s.charAt(i); } return ss; } }
帮助文档查看String常用方法【记忆】
27. StringBuilder类
StringBuilder类概述【理解】
StringBuilder 是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是 StringBuilder 对象中的内容是可变的
StringBuilder类和String类的区别【理解】
- String类:内容是不可变的
- StringBuilder类:内容是可变的
StringBuilder类的构造方法【记忆】
常用的构造方法
例如
public class StringBuilderDemo01 { public static void main(String[] args) { //public StringBuilder():创建一个空白可变字符串对象,不含有任何内容 StringBuilder sb = new StringBuilder(); System.out.println("sb:" + sb); System.out.println("sb.length():" + sb.length()); //public StringBuilder(String str):根据字符串的内容,来创建可变字符串对象 StringBuilder sb2 = new StringBuilder("hello"); System.out.println("sb2:" + sb2); System.out.println("sb2.length():" + sb2.length()); } }
StringBuilder类添加和反转方法【记忆】
添加和反转方法
例如
public class StringBuilderDemo01 { public static void main(String[] args) { //创建对象 StringBuilder sb = new StringBuilder(); //public StringBuilder append(任意类型):添加数据,并返回对象本身 // StringBuilder sb2 = sb.append("hello"); // // System.out.println("sb:" + sb); // System.out.println("sb2:" + sb2); // System.out.println(sb == sb2); // sb.append("hello"); // sb.append("world"); // sb.append("java"); // sb.append(100); //链式编程 sb.append("hello").append("world").append("java").append(100); System.out.println("sb:" + sb); //public StringBuilder reverse():返回相反的字符序列 sb.reverse(); System.out.println("sb:" + sb); } }
StringBuilder和String相互转换【应用】
StringBuilder转换为String
public String toString():通过 toString() 就可以实现把 StringBuilder 转换为 String
String转换为StringBuilder
public StringBuilder(String s):通过构造方法就可以实现把 String 转换为 StringBuilder
例如
public class StringBuilderDemo02 { public static void main(String[] args) { /* //StringBuilder 转换为 String StringBuilder sb = new StringBuilder(); sb.append("hello"); //String s = sb; //这个是错误的做法 //public String toString():通过 toString() 就可以实现把 StringBuilder 转换为 String String s = sb.toString(); System.out.println(s); */ //String 转换为 StringBuilder String s = "hello"; //StringBuilder sb = s; //这个是错误的做法 //public StringBuilder(String s):通过构造方法就可以实现把 String 转换为 StringBuilder StringBuilder sb = new StringBuilder(s); System.out.println(sb); } }
字符串拼接升级版案例【应用】
需求
定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,
并在控制台输出结果。例如,数组为int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]
例如
/* 思路: 1:定义一个 int 类型的数组,用静态初始化完成数组元素的初始化 2:定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回。 返回值类型 String,参数列表 int[] arr 3:在方法中用 StringBuilder 按照要求进行拼接,并把结果转成 String 返回 4:调用方法,用一个变量接收结果 5:输出结果 */ public class StringBuilderTest01 { public static void main(String[] args) { //定义一个 int 类型的数组,用静态初始化完成数组元素的初始化 int[] arr = {1, 2, 3}; //调用方法,用一个变量接收结果 String s = arrayToString(arr); //输出结果 System.out.println("s:" + s); } //定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回 /* 两个明确: 返回值类型:String 参数:int[] arr */ public static String arrayToString(int[] arr) { //在方法中用 StringBuilder 按照要求进行拼接,并把结果转成 String 返回 StringBuilder sb = new StringBuilder(); sb.append("["); for(int i=0; i<arr.length; i++) { if(i == arr.length-1) { sb.append(arr[i]); } else { sb.append(arr[i]).append(", "); } } sb.append("]"); String s = sb.toString(); return s; } }
字符串反转升级版案例【应用】
需求
定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果
例如,键盘录入abc,输出结果 cba
例如
帮助文档查看StringBuilder常用方法【记忆】
28. 继承
1.1 继承的实现(掌握)
继承的概念
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
实现继承的格式
继承通过extends实现
格式:class 子类 extends 父类 { }
举例:class Dog extends Animal { }
继承带来的好处
继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。(是否可以使用默认修饰符的成员。考虑子类和父类是否在同一个包内)
示例代码
public class Fu { public void show() { System.out.println("show方法被调用"); } } public class Zi extends Fu { public void method() { System.out.println("method方法被调用"); } } public class Demo { public static void main(String[] args) { //创建对象,调用方法 Fu f = new Fu(); f.show(); Zi z = new Zi(); z.method(); z.show(); } }
1.2 继承的好处和弊端(理解)
继承好处
提高了代码的复用性(多个类相同的成员可以放到同一个类中)
提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
继承弊端
继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
继承的应用场景:
使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
29. 继承中的成员访问特点
2.1 继承中变量的访问特点(掌握)
在子类方法中访问一个变量,采用的是就近原则。
1. 子类局部范围找
2. 子类成员范围找
3. 父类成员范围找
4. 如果都没有就报错(不考虑父亲的父亲…)
示例代码
class Fu { int num = 10; } class Zi extends Fu{ int num = 20; public void show(){ int num = 30; System.out.println(num); } } public class Demo1 { public static void main(String[] args) { Zi z = new Zi(); z.show(); // 输出show方法中的局部变量30 } }
2.2 super(掌握)
this&super关键字:
this:代表本类对象的引用
super:代表父类存储空间的标识(可以理解为父类对象引用)
this和super的使用分别
成员变量:
this.成员变量 - 访问本类成员变量
super.成员变量 - 访问父类成员变量
成员方法:
this.成员方法 - 访问本类成员方法
super.成员方法 - 访问父类成员方法
构造方法:
this(…) - 访问本类构造方法
super(…) - 访问父类构造方法
2.3 继承中构造方法的访问特点(理解)
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()
问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 在父类中自己提供一个无参构造方法
推荐方案:
自己给出无参构造方法
2.4 继承中成员方法的访问特点(掌握)
通过子类对象访问一个方法
1. 子类成员范围找
2. 父类成员范围找
3. 如果都没有就报错(不考虑父亲的父亲…)
2.5 super内存图(理解)
对象在堆内存中,会单独存在一块super区域,用来存放父类的数据
2.6 方法重写(掌握)
1、方法重写概念
子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
2、方法重写的应用场景
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
3、Override注解
用来检测当前的方法,是否是重写的方法,起到【校验】的作用
2.7 方法重写的注意事项(掌握)
方法重写的注意事项
1. 私有方法不能被重写(父类私有成员子类是不能继承的)
2. 子类方法访问权限不能更低(public > 默认 > 私有)
示例代码
public class Fu { private void show() { System.out.println("Fu中show()方法被调用"); } void method() { System.out.println("Fu中method()方法被调用"); } } public class Zi extends Fu { /* 编译【出错】,子类不能重写父类私有的方法*/ @Override private void show() { System.out.println("Zi中show()方法被调用"); } /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */ @Override private void method() { System.out.println("Zi中method()方法被调用"); } /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */ @Override public void method() { System.out.println("Zi中method()方法被调用"); } }
2.8. Java中继承的注意事项(掌握)
Java中继承的注意事项
Java中类只支持单继承,不支持多继承
错误范例:class A extends B, C { }
Java中类支持多层继承
多层继承示例代码:
public class Granddad { public void drink() { System.out.println("爷爷爱喝酒"); } } public class Father extends Granddad { public void smoke() { System.out.println("爸爸爱抽烟"); } } public class Mother { public void dance() { System.out.println("妈妈爱跳舞"); } } public class Son extends Father { // 此时,Son类中就同时拥有drink方法以及smoke方法 }
30. 继承练习
3.1 老师和学生(应用)
需求:定义老师类和学生类,然后写代码测试;最后找到老师类和学生类当中的共性内容,抽取出一个父类,用继承的方式改写代码,并进行测试
步骤:
①定义老师类(姓名,年龄,教书())
②定义学生类(姓名,年龄,学习())
③定义测试类,写代码测试
④共性抽取父类,定义人类(姓名,年龄)
⑤定义老师类,继承人类,并给出自己特有方法:教书()
⑥定义学生类,继承人类,并给出自己特有方法:学习()
⑦定义测试类,写代码测试
示例代码:
class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class Teacher extends Person { public Teacher() {} public Teacher(String name,int age) { super(name,age); } public void teach() { System.out.println("用爱成就每一位学员"); } } class Student extends Person{ public Student() {} public Student(String name, int age) { super(name,age); } public void study(){ System.out.println("学生学习"); } } class PersonDemo { public static void main(String[] args){ //创建老师类对象并进行测试 Teacher t1 = new Teacher(); t1.setName("林青霞"); t1.setAge(30); System.out.println(t1.getName() + "," + t1.getAge()); t1.teach(); Teacher t2 = new Teacher("风清扬", 33); System.out.println(t2.getName() + "," + t2.getAge()); t2.teach(); // 创建学生类对象测试 Student s = new Student("张三",23); System.out.println(s.getName() + "," + s.getAge()); s.study(); } }
3.2 猫和狗( 应用)
需求:请采用继承的思想实现猫和狗的案例,并在测试类中进行测试
分析:
①猫:
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:get/set方法,抓老鼠()
②狗:
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:get/set方法,看门()
③共性:
成员变量:姓名,年龄;构造方法:无参,带参;成员方法:get/set方法
步骤:
1、定义动物类(Animal)
【成员变量:姓名,年龄】【 构造方法:无参,带参】【成员方法:get/set方法】
2、定义猫类(Cat),继承动物类
【构造方法:无参,带参】【成员方法:抓老鼠() 】
3、定义狗类(Dog),继承动物类
【构造方法:无参,带参】【成员方法:看门() 】
4、定义测试类(AnimalDemo),写代码测试
示例代码:
class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } public void catchMouse() { System.out.println("猫抓老鼠"); } } class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } public void lookDoor() { System.out.println("狗看门"); } } /* 测试类 */ public class AnimalDemo { public static void main(String[] args) { //创建猫类对象并进行测试 Cat c1 = new Cat(); c1.setName("加菲猫"); c1.setAge(5); System.out.println(c1.getName() + "," + c1.getAge()); c1.catchMouse(); Cat c2 = new Cat("加菲猫", 5); System.out.println(c2.getName() + "," + c2.getAge()); c2.catchMouse(); } }
31. 修饰符
package(了解)
1、包的概念
包就是文件夹,用来管理类文件的
2、包的定义格式
package 包名; (多级包用.分开)
例如:package com.wry.demo;
3、带包编译&带包运行
带包编译:javac –d . 类名.java
例如:javac -d . com.wry.demo.HelloWorld.java
带包运行:java 包名+类名
例如:java com.wry.demo.HelloWorld
import(理解)
导包的意义
使用不同包下的类时,使用的时候要写类的全路径,写起来太麻烦了
为了简化带包的操作,Java就提供了导包的功能
导包的格式
格式:import 包名;
范例:import java.util.Scanner;
示例代码(没有使用导包,创建的Scanner对象)
示例代码(使用导包后,创建的Scanner对象)
权限修饰符(理解)
final(应用)
final关键字的作用
final代表最终的意思,可以修饰成员方法,成员变量,类
final修饰类、方法、变量的效果
fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
final修饰方法:该方法不能被重写
final修饰变量:表明该变量是一个常量,不能再次赋值
final修饰局部变量(理解)
fianl修饰基本数据类型变量
final 修饰指的是基本类型的数据值不能发生改变
final修饰引用数据类型变量
final 修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的
举例:
static
static(应用)
static的概念
static关键字是静态的意思,可以修饰【成员方法】,【成员变量】
static修饰的特点
1. 被类的所有对象共享,这也是我们判断是否使用静态关键字的条件
2. 可以通过类名调用当然,也可以通过对象名调用【推荐使用类名调用】
示例代码:
class Student { public String name; //姓名 public int age; //年龄 public static String university; //学校 共享数据!所以设计为静态! public void show() { System.out.println(name + "," + age + "," + university); } } public class StaticDemo { public static void main(String[] args) { // 为对象的共享数据赋值 Student.university = "山东农业工程学院"; Student s1 = new Student(); s1.name = "林青霞"; s1.age = 30; s1.show(); Student s2 = new Student(); s2.name = "风清扬"; s2.age = 33; s2.show(); } }
static访问特点(掌握)
非静态的成员方法
能访问静态的成员变量
能访问非静态的成员变量
能访问静态的成员方法
能访问非静态的成员方法
静态的成员方法
能访问静态的成员变量
能访问静态的成员方法
总结成一句话就是:
静态成员方法只能访问静态成员
Java static关键字与static{}语句块
一、 类的加载特性与时机
在进入static之前,先补一下关于类的加载。
1、 类加载的特性
在JVM的生命周期里,每个类只会被加载一次。
类加载的原则:延迟加载,能少加载就少加载,因为虚拟机的空间是有限的。
2、 类加载的时机
1)第一次创建对象要加载类.
2)调用静态方法时要加载类,访问静态属性时会加载类。
2)调用静态方法时要加载类,访问静态属性时会加载类。
3)加载子类时必定会先加载父类。
4)创建对象引用不加载类.
5) 子类调用父类的静态方法时
(1)当子类没有覆盖父类的静态方法时,只加载父类,不加载子类
(2)当子类有覆盖父类的静态方法时,既加载父类,又加载子类
6)访问静态常量,如果编译器可以计算出常量的值,则不会加载类,例如:public static final int a =123;否则会加载类,例如:public static final int a = math.PI。
二、 static的三个常用
1、 修饰成员变量
2、 修饰成员方法
同C++是一样的概念。但是在JVM里面,JVM也会划分一个暂称静态存储区,用于存放方法的定义。实际上从更大的角度而言,它存放的是各种类的定义,当我们通过new来生成对象时,会根据这里定义的类的定义去创建对象。
下面观察两段代码的输出结果,加了static和没有加static的区别:
public class Person { String name; int age; public String toString() { return "Name:" + name + ", Age:" + age; } public static void main(String[] args) { Person p1 = new Person(); p1.name = "zhangsan"; p1.age = 10; Person p2 = new Person(); p2.name = "lisi"; p2.age = 12; System.out.println(p1); System.out.println(p2); } /**输出结果 * Name:zhangsan, Age:10 * Name:lisi, Age:12 */ }
public class Person { String name; // 给age加上static static int age; /* 其余代码不变... */ /**输出结果 * Name:zhangsan, Age:12 * Name:lisi, Age:12 */ }
结论:通过运行结果,可以看到 age都为12,只保存了最后一次给age赋的值。这是为什么呢,在内存里面发生了什么?
给age属性加了static关键字之后,Person对象就不再拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性,一个对象如果对age属性做了改变,其他的对象都会受到影响。
3、 静态块(static{})
(1) static关键字还有一个比较关键的作用,用来形成静态代码块(static{}(即static块))以优化程序性能。
(2) static块可以置于类中的任何地方,类中可以有多个static块。
(3) 在类初次被加载的时候执行且仅会被执行一次(这是优化性能的原因!!!),会按照static块的顺序来执行每个static块,一般用来初始化静态变量和调用静态方法。
下面通过两段代码,说明 static{} 为什么能优化程序性能。
/** * 每次调用isBornBoomer的时候 * 都会生成startDate和birthDate两个对象,造成了空间浪费 */ class Person{ private Date birthDate; public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { Date startDate = Date.valueOf("1997"); Date endDate = Date.valueOf("2019"); return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
/** * 这里使用了static块 * 只需要进行一次的初始化操作 * 节省内存空间,优化性能 */ class Person{ private Date birthDate; private static Date startDate,endDate; static{ startDate = Date.valueOf("1997"); endDate = Date.valueOf("2019"); } public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
32. 多态
多态的概述(记忆)
什么是多态
同一个对象,在不同时刻表现出来的不同形态
多态的前提
要有继承或实现关系
要有方法的重写
要有父类引用指向子类对象
多态中的成员访问特点(记忆)
成员访问特点
成员变量
编译看父类,运行看父类
成员方法
编译看父类,运行看子类
代码演示
动物类
public class Animal { public int age = 40; public void eat() { System.out.println("动物吃东西"); } }
猫类
public class Cat extends Animal { public int age = 20; public int weight = 10; @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
测试类
public class AnimalDemo { public static void main(String[] args) { //有父类引用指向子类对象 Animal a = new Cat(); System.out.println(a.age); // System.out.println(a.weight); a.eat(); // a.playGame(); } }
多态的好处和弊端(记忆)
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
弊端
不能使用子类的特有成员
多态中的转型(应用)
向上转型
父类引用指向子类对象就是向上转型
向下转型
格式:子类型 对象名 = (子类型)父类引用;
代码演示
动物类
public class Animal { public void eat() { System.out.println("动物吃东西"); } }
猫类
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
测试类
public class AnimalDemo { public static void main(String[] args) { //多态 //向上转型 Animal a = new Cat(); a.eat(); // a.playGame(); //向下转型 Cat c = (Cat)a; c.eat(); c.playGame(); } }
多态的案例(应用)
案例需求
请采用多态的思想实现猫和狗的案例,并在测试类中进行测试
代码实现
动物类
public class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void eat() { System.out.println("动物吃东西"); } }
猫类
public class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } }
狗类
public class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("狗吃骨头"); } }
测试类
public class AnimalDemo { public static void main(String[] args) { //创建猫类对象进行测试 Animal a = new Cat(); a.setName("加菲"); a.setAge(5); System.out.println(a.getName() + "," + a.getAge()); a.eat(); a = new Cat("加菲", 5); System.out.println(a.getName() + "," + a.getAge()); a.eat(); } }
扩展
父类引用指向子类对象指的是:
举个例子:一个父类Animal,其子类Cat,Dog。其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类。
即声明的是父类,实际指向的是子类的一个对象。
那我们从内存角度来理解试试.
假设
现在有一个父类Father,它里面的变量需要占用1M内存. 假设它的一个子类Son,里面的变量需要占用0.5M内存. 现在通过代码来看看内存的分配情况:
因为子类中有一个隐藏的引用super会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造函数.由于s中包含了父类的实例,所以s可以调用父类的方法.
f1只是指向了s中实例的父类实例对象, 所以f1只能调用父类的方法(存储在1M内存中), 而不能调用子类的方法(存储在0.5M内存中).
因为 f 中只有1M内存,而子类的引用都必须要有1.5M的内存,所以无法转换.
由于f1是由s转换过来的,所以它是有1.5M的内存的,只是它指向的只有1M内存.
如果能够理解对象在内存的分布,那我们接下来就介绍java的
多态性
方法的重写、重载和动态链接构成了java的多态性。
Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来的非常强大的功能,但是复杂的继承关系也给C++开发者带来了更大的麻烦,为了规避风险,Java只允许单继承,子类与父类间有is a的关系(即“猫”is a “动物”)。这样做虽然保证了继承关系的简单明了,但是势必在功能上有很大的限制,所以,Java引入了多态性的概念以弥补这点的不足。
理解多态性,首先要理解的就是
“向上转型”
它表示我定义了一个Animal类型的引用,指向新建的Cat类型的对象。由于Cat是继承自它的父类Animal,所以Animal类型的引用是可以指向Cat类型的对象的。这就是“向上转型”。
注意:java中“向上转型”是自动的。但是“向下转型”却不是自动的。需要我们用强制类型转化。
那么这样做有什么意义呢?
因为子类是对父类的改进和扩充。定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。 但是父类类型的引用可以调用父类中定义的所有属性和方法,而子类中定义而父类中没有的方法,父类引用是无法调用的,所以才有那句:
“编译看左边(父类),运行看右边(子类)”;
要做到父类引用调用子类的属性或者方法,就还要有动态连接。那什么是动态链接呢?
当父类中的一个方法只有在父类中定义而在子类中没有被重写的情况下,才可以被父类类型的引用调用; 对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法,这就是动态连接。
JAVA里没有多继承,一个类只能有一个父类。而继承的表现就是多态。一个父类可以有多个子类,而在子类里可以重写父类的方法,这样每个子类里重写的代码不一样,自然表现形式就不一样。这样用父类的变量去引用不同的子类,在调用这个相同名字的方法的时候得到的结果就不一样了,这就是多态,
下面看一个多态性的例子
结果:
上面程序调用同一个方法,去出现不同的结果。这就是多态。
对于多态性的实现有必要重视方法重载(overloading)和方法重写(override)区别
上面的程序是个很典型的多态的例子。子类Child继承了父类Father,并重载了父类的func1()方法。重载后的func1(int i)和func1()不再是同一个方法,由于父类中没有func1(int i),那么,父类类型的引用child就不能调用func1(int i)方法。
多态总结
1. 使用父类类型的引用指向子类的对象;
2. 该引用只能调用父类中定义的方法和变量;
3. 如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)
4. 变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。
33. 抽象类
抽象类的概述(理解)
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
抽象类的特点(记忆)
抽象类和抽象方法必须使用 abstract 关键字修饰
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
抽象类的成员特点(记忆)
成员的特点
成员变量
既可以是变量
也可以是常量
构造方法
空参构造
有参构造
成员方法
抽象方法
普通方法
例如
动物类
public abstract class Animal { private int age = 20; private final String city = "北京"; public Animal() {} public Animal(int age) { this.age = age; } public void show() { age = 40; System.out.println(age); // city = "上海"; System.out.println(city); } public abstract void eat(); }
猫类
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
测试类
public class AnimalDemo { public static void main(String[] args) { Animal a = new Cat(); a.eat(); a.show(); } }
抽象类的案例(应用)
需求
请采用抽象类的思想实现猫和狗的案例,并在测试类中进行测试
代码
动物类
public abstract class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public abstract void eat(); }
猫类
public class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } }
狗类
public class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("狗吃骨头"); } }
测试类
public class AnimalDemo { public static void main(String[] args) { //创建对象,按照多态的方式 Animal a = new Cat(); a.setName("加菲"); a.setAge(5); System.out.println(a.getName()+","+a.getAge()); a.eat(); System.out.println("--------"); a = new Cat("加菲",5); System.out.println(a.getName()+","+a.getAge()); a.eat(); } }
34. 接口
接口的概述(理解)
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
Java中的接口更多的体现在对行为的抽象!
接口的特点(记忆)
接口用关键字interface修饰
类实现接口用implements表示
接口不能实例化
接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态,抽象类多态,接口多态。
接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
接口的成员特点(记忆)
成员特点
成员变量
只能是常量
默认修饰符:public static final
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
例如
接口
public interface Inter { public int num = 10; public final int num2 = 20; // public static final int num3 = 30; int num3 = 30; // public Inter() {} // public void show() {} public abstract void method(); void show(); }
实现类
public class InterImpl extends Object implements Inter { public InterImpl() { super(); } @Override public void method() { System.out.println("method"); } @Override public void show() { System.out.println("show"); } }
测试类
public class InterfaceDemo { public static void main(String[] args) { Inter i = new InterImpl(); // i.num = 20; System.out.println(i.num); // i.num2 = 40; System.out.println(i.num2); System.out.println(Inter.num); } }
接口的案例(应用)
需求
对猫和狗进行训练,他们就可以跳高了,这里加入跳高功能。
请采用抽象类和接口来实现猫狗案例,并在测试类中进行测试。
代码
动物类
public abstract class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public abstract void eat(); }
跳高类
public interface Jumpping { public abstract void jump(); }
猫类
public class Cat extends Animal implements Jumpping { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } @Override public void jump() { System.out.println("猫可以跳高了"); } }
测试类
public class AnimalDemo { public static void main(String[] args) { //创建对象,调用方法 Jumpping j = new Cat(); j.jump(); System.out.println("--------"); Animal a = new Cat(); a.setName("加菲"); a.setAge(5); System.out.println(a.getName()+","+a.getAge()); a.eat(); // a.jump(); a = new Cat("加菲",5); System.out.println(a.getName()+","+a.getAge()); a.eat(); System.out.println("--------"); Cat c = new Cat(); c.setName("加菲"); c.setAge(5); System.out.println(c.getName()+","+c.getAge()); c.eat(); c.jump(); } }
类和接口的关系(记忆)
类与类的关系
继承关系,只能单继承,但是可以多层继承
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口的关系
继承关系,可以单继承,也可以多继承
抽象类和接口的区别(记忆)
成员区别
抽象类
变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口
常量;抽象方法
关系区别
类与类
继承,单继承
类与接口
实现,可以单实现,也可以多实现
接口与接口
继承,单继承,多继承
设计理念区别
抽象类
对类抽象,包括属性、行为
接口
对行为抽象,主要是行为
35. 综合案例
需求
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。
了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识分析,这个案例中有哪些具体类,哪些抽象类,哪些接口,并用代码实现。
代码
抽象人类
public abstract class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public abstract void eat(); }
抽象运动员类
public abstract class Player extends Person { public Player() { } public Player(String name, int age) { super(name, age); } public abstract void study(); }
抽象教练类
public abstract class Player extends Person { public Player() { } public Player(String name, int age) { super(name, age); } public abstract void study(); }
学英语接口
public interface SpeakEnglish { public abstract void speak(); }
蓝球教练
public class BasketballCoach extends Coach { public BasketballCoach() { } public BasketballCoach(String name, int age) { super(name, age); } @Override public void teach() { System.out.println("篮球教练教如何运球和投篮"); } @Override public void eat() { System.out.println("篮球教练吃羊肉,喝羊奶"); } }
乒乓球教练
public class PingPangCoach extends Coach implements SpeakEnglish { public PingPangCoach() { } public PingPangCoach(String name, int age) { super(name, age); } @Override public void teach() { System.out.println("乒乓球教练教如何发球和接球"); } @Override public void eat() { System.out.println("乒乓球教练吃小白菜,喝大米粥"); } @Override public void speak() { System.out.println("乒乓球教练说英语"); } }
乒乓球运动员
public class PingPangPlayer extends Player implements SpeakEnglish { public PingPangPlayer() { } public PingPangPlayer(String name, int age) { super(name, age); } @Override public void study() { System.out.println("乒乓球运动员学习如何发球和接球"); } @Override public void eat() { System.out.println("乒乓球运动员吃大白菜,喝小米粥"); } @Override public void speak() { System.out.println("乒乓球运动员说英语"); } }
篮球运动员
public class BasketballPlayer extends Player { public BasketballPlayer() { } public BasketballPlayer(String name, int age) { super(name, age); } @Override public void study() { System.out.println("篮球运动员学习如何运球和投篮"); } @Override public void eat() { System.out.println("篮球运动员吃牛肉,喝牛奶"); } }
36. 参数传递
类名作为形参和返回值(应用)
类名作为方法的形参
方法的形参是类名,其实需要的是该类的对象
实际传递的是该对象的【地址值】
类名作为方法的返回值
方法的返回值是类名,其实返回的是该类的对象
实际传递的,也是该对象的【地址值】
例如
class Cat { public void eat() { System.out.println("猫吃鱼"); } } class CatOperator { public void useCat(Cat c) { //Cat c = new Cat(); c.eat(); } public Cat getCat() { Cat c = new Cat(); return c; } } public class CatDemo { public static void main(String[] args) { //创建操作类对象,并调用方法 CatOperator co = new CatOperator(); Cat c = new Cat(); co.useCat(c); Cat c2 = co.getCat(); //new Cat() c2.eat(); } }
抽象类作为形参和返回值(理解)
抽象类作为形参和返回值
* 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
* 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
例如
abstract class Animal { public abstract void eat(); } class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } } class AnimalOperator { public void useAnimal(Animal a) { //Animal a = new Cat(); a.eat(); } public Animal getAnimal() { Animal a = new Cat(); return a; } } public class AnimalDemo { public static void main(String[] args) { //创建操作类对象,并调用方法 AnimalOperator ao = new AnimalOperator(); Animal a = new Cat(); ao.useAnimal(a); Animal a2 = ao.getAnimal(); //new Cat() a2.eat(); } }
接口名作为形参和返回值(理解)
接口作为形参和返回值
* 方法的形参是接口名,其实需要的是该接口的实现类对象
* 方法的返回值是接口名,其实返回的是该接口的实现类对象
例如
interface Jumpping { void jump(); } class JumppingOperator { public void useJumpping(Jumpping j) { //Jumpping j = new Cat(); j.jump(); } public Jumpping getJumpping() { Jumpping j = new Cat(); return j; } } class Cat implements Jumpping { @Override public void jump() { System.out.println("猫可以跳高了"); } } public class JumppingDemo { public static void main(String[] args) { //创建操作类对象,并调用方法 JumppingOperator jo = new JumppingOperator(); Jumpping j = new Cat(); jo.useJumpping(j); Jumpping j2 = jo.getJumpping(); //new Cat() j2.jump(); } }
37. 内部类
内部类的基本使用(理解)
内部类概念
在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
内部类定义格式
格式
例如
内部类的访问特点
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
例如
/* 内部类访问特点: 内部类可以直接访问外部类的成员,包括私有 外部类要访问内部类的成员,必须创建对象 */ public class Outer { private int num = 10; public class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } }
成员内部类(理解)
成员内部类的定义位置
在类中方法,跟成员变量是一个位置
外界创建成员内部类格式
格式
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
例如
Outer.Inner oi = new Outer().new Inner();
成员内部类的推荐使用方案
将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
例如
class Outer { private int num = 10; private class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } } public class InnerDemo { public static void main(String[] args) { //Outer.Inner oi = new Outer().new Inner(); //oi.show(); Outer o = new Outer(); o.method(); } }
局部内部类(理解)
局部内部类定义位置
局部内部类是在方法中定义的类
局部内部类方式方式
局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
示例代码
public class Outer { private int num = 10; public void method() { final int num2 = 20; class Inner { public void show() { System.out.println(num); System.out.println(num2); } } Inner i = new Inner(); i.show(); } }
public class Demo { public static void main(String[] args) { Outer o = new Outer(); o.method(); } }
匿名内部类(应用)
匿名内部类的前提
存在一个类或者接口,这里的类可以是具体类也可以是抽象类
匿名内部类的格式
格式
new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }
例如
匿名内部类的本质
本质:是一个继承了该类或者实现了该接口的子类匿名对象
匿名内部类的细节
匿名内部类可以通过多态的形式接受
匿名内部类直接调用方法
public interface Inter{ void method(); }
public class Demo { public static void main(String[] args){ new Inter(){ @Override public void method(){ System.out.println("我是匿名内部类"); } }.method(); // 直接调用方法 } }
匿名内部类在开发中的使用(应用)
匿名内部类在开发中的使用
当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
例如
public interface Jumpping { void jump(); }
public class Cat implements Jumpping { @Override public void jump() { System.out.println("猫可以跳高了"); } }
public class Dog implements Jumpping { @Override public void jump() { System.out.println("狗可以跳高了"); } }
public class JumppingOperator { public void method(Jumpping j) { //new Cat(); new Dog(); j.jump(); } }
public class Demo { public static void main(String[] args) { //需求:创建接口操作类的对象,调用method方法 JumppingOperator jo = new JumppingOperator(); Jumpping j = new Cat(); jo.method(j); Jumpping j2 = new Dog(); jo.method(j2); System.out.println("--------"); // 匿名内部类的简化 jo.method(new Jumpping() { @Override public void jump() { System.out.println("猫可以跳高了"); } }); // 匿名内部类的简化 jo.method(new Jumpping() { @Override public void jump() { System.out.println("狗可以跳高了"); } }); } }
38. 常用API
Math(应用)
Math类概述
Math 包含执行基本数字运算的方法
Math中方法的调用方式
Math类中无构造方法,但内部的方法都是静态的,则可以通过 **类名.进行调用**
Math类的常用方法
System(应用)
System类的常用方法
例如
需求
在控制台输出1-10000,计算这段代码执行了多少毫秒
代码
public class SystemDemo { public static void main(String[] args) { // 获取开始的时间节点 long start = System.currentTimeMillis(); for (int i = 1; i <= 10000; i++) { System.out.println(i); } // 获取代码运行结束后的时间节点 long end = System.currentTimeMillis(); System.out.println("共耗时:" + (end - start) + "毫秒"); } }
Object类的toString方法(应用)
Object类概述
Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类,换句话说,该类所具备的方法,所有类都会有一份
查看方法源码的方式
选中方法,按下Ctrl +左键(推荐)
选中方法,按下Ctrl + B
重写toString方法的方式
Alt + Insert 选择toString
在类的空白区域,右键 -> Generate -> 选择toString
toString方法的作用:
以良好的格式,更方便的展示对象中的属性值
示例代码:
public class Student extends Object { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
public class ObjectDemo { public static void main(String[] args) { Student s = new Student(); s.setName("林青霞"); s.setAge(30); System.out.println(s); System.out.println(s.toString()); } }
Object类的equals方法(应用)
equals方法的作用
用于对象之间的比较,返回true和false的结果
举例:s1.equals(s2); s1和s2是两个对象
重写equals方法的场景
不希望比较对象的地址值,想要结合对象属性进行比较的时候。
重写equals方法的方式
alt + insert 选择equals() and hashCode(),IntelliJ Default,一路next,finish即可
在类的空白区域,右键 -> Generate -> 选择equals() and hashCode(),后面的同上。
示例代码:
子主题
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { //this -- s1 //o -- s2 if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; //student -- s2 if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; } }
public class ObjectDemo { public static void main(String[] args) { Student s1 = new Student(); s1.setName("林青霞"); s1.setAge(30); Student s2 = new Student(); s2.setName("林青霞"); s2.setAge(30); //需求:比较两个对象的内容是否相同 System.out.println(s1.equals(s2)); } }
冒泡排序原理(理解)
冒泡排序概述
一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面,依次对所有的数据进行操作,直至所有数据按要求完成排序
如果有n个数据进行排序,总共需要比较n-1次
每一次比较完毕,下一次的比较就会少一个数据参与
冒泡排序代码实现(理解)
/* 冒泡排序: 一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面, 依次对所有的数据进行操作,直至所有数据按要求完成排序 */ public class Demo { public static void main(String[] args) { //定义一个数组 int[] arr = {24, 69, 80, 57, 13}; System.out.println("排序前:" + arrayToString(arr)); // 循环次数判断:每轮比较次数>0来决定一共多少轮,循环几次 // 这里减1,是控制每轮比较的次数 for (int x = arr.length - 1; x > 0; x--) { // i < x是为了避免索引越界 // 每轮比较:通过for循环进行每轮比较 for (int i = 0; i < x; i++) { if (arr[i] > arr[i + 1]) { int temp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = temp; } } } System.out.println("排序后:" + arrayToString(arr)); } //把数组中的元素按照指定的规则组成一个字符串:[元素1, 元素2, ...] public static String arrayToString(int[] arr) { StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < arr.length; i++) { if (i == arr.length - 1) { sb.append(arr[i]); } else { sb.append(arr[i]).append(", "); } } sb.append("]"); String s = sb.toString(); return s; } }
Arrays(应用)
Arrays的常用方法
工具类设计思想
构造方法用 private 修饰
成员用 public static 修饰
例如
39. 包装类
基本类型包装类(记忆)
基本类型包装类的作用
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
常用的操作之一:用于基本数据类型与字符串之间的转换
基本类型对应的包装类
Integer类(应用)
概述
包装一个对象中的原始类型 int 的值
构造方法
例如
public class IntegerDemo { public static void main(String[] args) { //public Integer(int value):根据 int 值创建 Integer 对象(过时) Integer i1 = new Integer(100); System.out.println(i1); //public Integer(String s):根据 String 值创建 Integer 对象(过时) Integer i2 = new Integer("100"); // Integer i2 = new Integer("abc"); //NumberFormatException System.out.println(i2); System.out.println("--------"); //public static Integer valueOf(int i):返回表示指定的 int 值的 Integer 实例 Integer i3 = Integer.valueOf(100); System.out.println(i3); //public static Integer valueOf(String s):返回一个保存指定值的Integer对象 String Integer i4 = Integer.valueOf("100"); System.out.println(i4); } }
int和String类型的相互转换(记忆)
int转换为String
转换方式
- 方式一:直接在数字后加一个空字符串
- 方式二:通过String类静态方法valueOf()
例如
public class Demo { public static void main(String[] args) { //int --- String int number = 100; //方式1 String s1 = number + ""; System.out.println(s1); //方式2 // Returns the string representation of the int argument. String s2 = String.valueOf(number); System.out.println(s2); System.out.println("--------"); } }
String转换为int
转换方式
- 方式一:先将字符串数字转成Integer,再调用valueOf()方法
- 方式二:通过Integer静态方法parseInt()进行转换
例如
public class Demo { public static void main(String[] args) { //String --- int String s = "100"; //方式1:String --- Integer --- int // an Integer object holding the value represented by the string argument. Integer i = Integer.valueOf(s); //Returns the value of this Integer as an int. int x = i.intValue(); System.out.println(x); //方式2 //Parses the string argument as a signed decimal integer. int y = Integer.parseInt(s); System.out.println(y); } }
字符串数据排序案例(应用)
需求
有一个字符串:“91 27 46 38 50”,请写程序实现最终输出结果是:“27 38 46 50 91”
代码
public class Demo { public static void main(String[] args) { //定义一个字符串 String s = "91 27 46 38 50"; //把字符串中的数字数据存储到一个int类型的数组中 // return the array of strings computed by splitting this string around matches of the given regular expression String[] strArray = s.split(" "); //定义一个int数组,把 String[] 数组中的每一个元素存储到 int 数组中 int[] arr = new int[strArray.length]; for(int i=0; i<arr.length; i++) { // Parses the string argument as a signed decimal integer. arr[i] = Integer.parseInt(strArray[i]); } //对 int 数组进行排序 // Sorts the specified array into ascending numerical order. Arrays.sort(arr); //把排序后的int数组中的元素进行拼接得到一个字符串,这里拼接采用StringBuilder来实现 StringBuilder sb = new StringBuilder(); for(int i=0; i<arr.length; i++) { if(i == arr.length - 1) { // Appends the string representation of the int argument to this sequence. sb.append(arr[i]); } else { sb.append(arr[i]).append(" "); } } // Returns a string containing the characters in this sequence in the same order as this sequence. String result = sb.toString(); //输出结果 System.out.println(result); } }
自动拆箱和自动装箱(理解)
自动装箱
把基本数据类型转换为对应的包装类类型
自动拆箱
把包装类类型转换为对应的基本数据类型
示例代码
40. 时间日期类
Date类(应用)
概述
Date 代表了一个特定的时间,精确到毫秒
构造方法
例如
public class Demo { public static void main(String[] args) { //public Date():分配一个 Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒 Date d1 = new Date(); System.out.println(d1); //public Date(long date):分配一个 Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数 long date = 1000*60*60; Date d2 = new Date(date); System.out.println(d2); } }
Date类常用方法(应用)
常用方法
例如
public class Demo { public static void main(String[] args) { //创建日期对象 Date d = new Date(); // returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object. System.out.println(d.getTime()); System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年"); // Returns the current time in milliseconds. long time = System.currentTimeMillis(); // Sets this Date object to represent a point in time that is time milliseconds after January 1, 1970 00:00:00 GMT. d.setTime(time); System.out.println(d); } }
SimpleDateFormat类(应用)
概述
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。
我们重点学习日期格式化和解析
构造方法
常用方法
格式化(从Date到String)
public final String format(Date date):将日期格式化成日期/时间字符串
解析(从String到Date)
public Date parse(String source):从给定字符串的开始解析文本以生成日期
例如
public class Demo { public static void main(String[] args) throws ParseException { // 格式化:从 Date 到 String Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); // Formats a Date into a date/time string. String s = sdf.format(d); System.out.println(s); System.out.println("--------"); // 从 String 到 Date String ss = "2048-08-09 11:11:11"; //ParseException SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 从给定字符串的开头解析文本以生成日期 Date dd = sdf2.parse(ss); System.out.println(dd); } }
日期工具类案例(应用)
需求
定义一个日期工具类(DateUtils),包含两个方法:把日期转换为指定格式的字符串;把字符串解析为指定格式的日期,然后定义一个测试类(DateDemo),测试日期工具类的方法
代码
工具类
public class DateUtils { private DateUtils() {} /* 把日期转为指定格式的字符串 返回值类型:String 参数:Date date, String format */ public static String dateToString(Date date, String format) { // 使用给定模式和默认FORMAT语言环境的默认日期格式符号构造SimpleDateFormat 。 SimpleDateFormat sdf = new SimpleDateFormat(format); // Formats a Date into a date/time string. String s = sdf.format(date); return s; } /* 把字符串解析为指定格式的日期 返回值类型:Date 参数:String s, String format */ public static Date stringToDate(String s, String format) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat(format); // 从给定字符串的开头解析文本以生成日期 Date d = sdf.parse(s); return d; } }
测试类
public class Demo { public static void main(String[] args) throws ParseException { //创建日期对象 Date d = new Date(); // 把日期转为指定格式的字符串 String s1 = DateUtils.dateToString(d, "yyyy年MM月dd日 HH:mm:ss"); System.out.println(s1); String s2 = DateUtils.dateToString(d, "yyyy年MM月dd日"); System.out.println(s2); String s3 = DateUtils.dateToString(d, "HH:mm:ss"); System.out.println(s3); System.out.println("--------"); String s = "2048-08-09 12:12:12"; // 把字符串解析为指定格式的日期 Date dd = DateUtils.stringToDate(s, "yyyy-MM-dd HH:mm:ss"); System.out.println(dd); } }
Calendar类(应用)
概述
Calendar 为特定瞬间与一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法
Calendar 提供了一个类方法 getInstance 用于获取这种类型的一般有用的对象。
该方法返回一个Calendar 对象。
其日历字段已使用当前日期和时间初始化:Calendar rightNow = Calendar.getInstance();
常用方法
例如
public class Demo { public static void main(String[] args) { //需求0 //获取日历类对象 // Gets a calendar using the default time zone and locale. Calendar c = Calendar.getInstance(); // Field number for get and set indicating the year. // Returns the value of the given calendar field. int year = c.get(Calendar.YEAR); // Field number for get and set indicating the month. The first month of the year in the Gregorian and Julian calendars is JANUARY which is 0 int month = c.get(Calendar.MONTH) + 1; // Field number for get and set indicating the day of the month. int date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日"); //需求1:3年前的今天 // Adds or subtracts the specified amount of time to the given calendar field, based on the calendar's rules. c.add(Calendar.YEAR,-3); year = c.get(Calendar.YEAR); month = c.get(Calendar.MONTH) + 1; date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日"); //需求2:10年后的10天前 c.add(Calendar.YEAR,10); c.add(Calendar.DATE,-10); year = c.get(Calendar.YEAR); month = c.get(Calendar.MONTH) + 1; date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日"); // 需求3 // Sets the values for the calendar fields YEAR, MONTH, and DAY_OF_MONTH. // Params:month – the value used to set the MONTH calendar field. Month value is 0-based. e.g., 0 for January. c.set(2050,10,10); year = c.get(Calendar.YEAR); month = c.get(Calendar.MONTH) + 1; date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日"); } }
需求0
需求1
需求2
需求3
二月天案例(应用)
需求
获取任意一年的二月有多少天
代码
public class Demo { public static void main(String[] args) { // 1.键盘录入任意的年份 Scanner sc = new Scanner(System.in); System.out.println("请输入年:"); // Scans the next token of the input as an int. int year = sc.nextInt(); // 2.设置日历对象的年、月、日 // Gets a calendar using the default time zone and locale. Calendar c = Calendar.getInstance(); // Sets the values for the calendar fields YEAR, MONTH, and DAY_OF_MONTH. // Params:month – the value used to set the MONTH calendar field. Month value is 0-based. e.g., 0 for January. c.set(year, 2, 1); // 3月1日往前推一天,就是2月的最后一天 // Field number for get and set indicating the day of the month. // Adds or subtracts the specified amount of time to the given calendar field, based on the calendar's rules. c.add(Calendar.DATE, -1); // Returns the value of the given calendar field. int date = c.get(Calendar.DATE); System.out.println(year + "年的2月份有" + date + "天"); } }
41. 异常
概述
异常就是程序出现了不正常的情况
体系结构
JVM默认处理异常的方式(理解)
如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理,处理方式有如下两个步骤:
把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
程序停止执行
try-catch方式处理异常(应用)
格式
流程
程序从 try 里面的代码开始执行
出现异常,就会跳转到对应的 catch 里面去执行
执行完毕之后,程序还可以继续往下执行
代码
public class Demo { public static void main(String[] args) { System.out.println("开始"); method(); System.out.println("结束"); } public static void method() { try { int[] arr = {1, 2, 3}; System.out.println(arr[3]); System.out.println("这里能够访问到吗"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("你访问的数组索引不存在,请回去修改为正确的索引"); // Prints this throwable and its backtrace to the standard error stream. e.printStackTrace(); } } }
Throwable成员方法(应用)
常用方法
例如
public class Demo { public static void main(String[] args) { System.out.println("开始"); method(); System.out.println("结束"); } public static void method() { try { int[] arr = {1, 2, 3}; //new ArrayIndexOutOfBoundsException(); System.out.println(arr[3]); System.out.println("这里能够访问到吗"); } catch (ArrayIndexOutOfBoundsException e) { // Returns the detail message string of this throwable. System.out.println(e.getMessage()); System.out.println("--------------------"); // Returns a short description of this throwable. System.out.println(e.toString()); System.out.println("--------------------"); // Prints this throwable and its backtrace to the standard error stream. e.printStackTrace(); } } }
编译时异常和运行时异常的区别(记忆)
编译时异常
都是Exception类及其子类
必须显示处理,否则程序就会发生错误,无法通过编译
运行时异常
都是RuntimeException类及其子类
无需显示处理,也可以和编译时异常一样处理
throws方式处理异常(应用)
格式
注意
这个throws格式是跟在方法的括号后面的
编译时异常必须要进行处理,两种处理方案:try...catch …或者 throws,如果采用 throws 这种方案,将来谁调用谁处理
运行时异常可以不处理,出现问题后,需要我们回来修改代码
例如
public class Demo { public static void main(String[] args) { System.out.println("开始"); try { method(); }catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); } System.out.println("结束"); } //运行时异常 public static void method() throws ArrayIndexOutOfBoundsException { int[] arr = {1, 2, 3}; // ArrayIndexOutOfBoundsException System.out.println(arr[3]); } }
throws和throw的区别(记忆)
自定义异常(应用)
自定义异常类
public class ScoreException extends Exception { public ScoreException() {} public ScoreException(String message) { super(message); } }
老师类
public class Teacher { public void checkScore(int score) throws ScoreException { if(score<0 || score>100) { throw new ScoreException("你给的分数有误,分数应该在0-100之间"); } else { System.out.println("成绩正常"); } } }
测试类
public class Demo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入分数:"); int score = sc.nextInt(); Teacher t = new Teacher(); try { t.checkScore(score); } catch (ScoreException e) { // Prints this throwable and its backtrace to the standard error stream. e.printStackTrace(); } } }
成绩正常
成绩不正常
42. Collection集合
集合体系结构【记忆】
集合类的特点
提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变
集合类的体系图
Collection集合概述和基本使用【应用】
概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
例如
public class Demo { public static void main(String[] args) { //创建Collection集合的对象 Collection<String> c = new ArrayList<>(); //添加元素:boolean add(E e) c.add("hello"); c.add("world"); c.add("java"); //输出集合对象 System.out.println(c); } }
Collection集合的常用方法【应用】
Collection集合的遍历【应用】
迭代器的介绍
迭代器,集合的专用遍历方式
Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
例如
public class Demo { public static void main(String[] args) { //创建集合对象 Collection<String> c = new ArrayList<>(); //添加元素 c.add("hello"); c.add("world"); c.add("java"); c.add("javaee"); // Returns an iterator over the elements in this collection. Iterator<String> it = c.iterator(); //用while循环改进元素的判断和获取 // Returns true if the iteration has more elements. while (it.hasNext()) { // Returns the next element in the iteration. String s = it.next(); System.out.println(s); } } }
集合使用步骤图解【理解】
集合的案例-Collection集合存储学生对象并遍历【应用】
需求
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
public class Demo { public static void main(String[] args) { //创建Collection集合对象 Collection<Student> c = new ArrayList<>(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); //把学生添加到集合 c.add(s1); c.add(s2); c.add(s3); //遍历集合(迭代器方式) Iterator<Student> it = c.iterator(); while (it.hasNext()) { Student s = it.next(); System.out.println(s.getName() + "," + s.getAge()); } } }
43. List集合
List集合概述和特点【记忆】
概述
- 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
特点
有索引
可以存储重复元素
元素存取有序
List集合的特有方法【应用】
集合的案例-List集合存储学生对象并遍历【应用】
需求
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
并发修改异常【应用】
出现的原因
迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致,则会出现:ConcurrentModificationException
解决的方案
用for循环遍历,然后用集合对象做对应的操作即可
例如
public class Demo { public static void main(String[] args) { //创建集合对象 List<String> list = new ArrayList<>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); //遍历集合,得到每一个元素,看有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现 // Iterator<String> it = list.iterator(); // while (it.hasNext()) { // String s = it.next(); // if(s.equals("world")) { // list.add("javaee"); // } // } for(int i=0; i<list.size(); i++) { String s = list.get(i); if(s.equals("world")) { list.add("javaee"); } } //输出集合对象 System.out.println(list); } }
列表迭代器【应用】
ListIterator介绍
通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任一方向遍历的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
例如
public class Demo { public static void main(String[] args) { //创建集合对象 List<String> list = new ArrayList<>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); //获取列表迭代器 // Returns a list iterator over the elements in this list (in proper sequence). ListIterator<String> lit = list.listIterator(); while (lit.hasNext()) { String s = lit.next(); // Compares this string to the specified object. if(s.equals("world")) { lit.add("javaee"); } } System.out.println(list); } }
增强for循环【应用】
格式
例如
public class Demo { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; for(int i : arr) { System.out.println(i); } System.out.println("--------"); String[] strArray = {"hello","world","java"}; for(String s : strArray) { System.out.println(s); } System.out.println("--------"); List<String> list = new ArrayList<>(); list.add("hello"); list.add("world"); list.add("java"); for(String s : list) { System.out.println(s); } System.out.println("--------"); //内部原理是一个Iterator迭代器 /* for(String s : list) { if(s.equals("world")) { list.add("javaee"); //ConcurrentModificationException } } */
例如
需求
求同学们的年龄和
代码
学生类
public class Student { private String name; private Integer age; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试类
子主题
public class Demo { public static void main(String[] args) { // 情况1:正常 //List<Student> students= new ArrayList<>(); //Student student1 = new Student(); //student1.setAge(2); //students.add(student1); //Student student2 = new Student(); //student2.setAge(3); //students.add(student2); // 情况2:list=null List<Student> students = null; // 情况3:age=null //List<Student> students= new ArrayList<>(); //Student student1 = new Student(); //students.add(student1); // 获得年龄合计 Integer totalNum = 0; if (students !=null && students.size()>0){ for (Student student : students) { if(student.getAge() == null){ student.setAge(0); } totalNum+=student.getAge(); } } System.out.println(totalNum); //return totalNum; } }
集合的案例-List集合存储学生对象三种方式遍历【应用】
需求
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
public class Demo { public static void main(String[] args) { //创建List集合对象 List<Student> list = new ArrayList<Student>(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); //把学生添加到集合 list.add(s1); list.add(s2); list.add(s3); //迭代器:集合特有的遍历方式 Iterator<Student> it = list.iterator(); while (it.hasNext()) { Student s = it.next(); System.out.println(s.getName()+","+s.getAge()); } System.out.println("--------"); //普通for:带有索引的遍历方式 for(int i=0; i<list.size(); i++) { Student s = list.get(i); System.out.println(s.getName()+","+s.getAge()); } System.out.println("--------"); //增强for:最方便的遍历方式 for(Student s : list) { System.out.println(s.getName()+","+s.getAge()); } } }
44. 数据结构
数据结构之栈和队列【记忆】
栈结构
先进后出
队列结构
先进先出
数据结构之数组和链表【记忆】
数组结构
查询快、增删慢
队列结构
查询慢、增删快
45. List集合的实现类
List集合子类的特点【记忆】
ArrayList集合
底层是数组结构实现,查询快、增删慢
LinkedList集合
底层是链表结构实现,查询慢、增删快
集合的案例-ArrayList集合存储学生对象三种方式遍历【应用】
需求
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
public class Demo { public static void main(String[] args) { //创建ArrayList集合对象 ArrayList<Student> array = new ArrayList<>(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); //把学生添加到集合 array.add(s1); array.add(s2); array.add(s3); //迭代器:集合特有的遍历方式 Iterator<Student> it = array.iterator(); while (it.hasNext()) { Student s = it.next(); System.out.println(s.getName() + "," + s.getAge()); } System.out.println("--------"); //普通for:带有索引的遍历方式 for(int i=0; i<array.size(); i++) { Student s = array.get(i); System.out.println(s.getName() + "," + s.getAge()); } System.out.println("--------"); //增强for:最方便的遍历方式 for(Student s : array) { System.out.println(s.getName() + "," + s.getAge()); } } }
LinkedList集合的特有功能【应用】
特有方法
46. Set集合
Set集合概述和特点【应用】
特点
元素存取无序
没有索引、只能通过迭代器或增强for循环遍历
不能存储重复元素
例如
哈希值【理解】
简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
如何获取
Object类中的public int hashCode():返回对象的哈希码值
特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int hashCode() { return 0; } }
测试类
public class Demo { public static void main(String[] args) { //创建学生对象 Student s1 = new Student("林青霞",30); //同一个对象多次调用hashCode()方法返回的哈希值是相同的 System.out.println(s1.hashCode()); //1060830840 System.out.println(s1.hashCode()); //1060830840 System.out.println("--------"); Student s2 = new Student("林青霞",30); //默认情况下,不同对象的哈希值是不相同的 //通过方法重写,可以实现不同对象的哈希值是相同的 System.out.println(s2.hashCode()); //2137211482 System.out.println("--------"); Student s3 = new Student("林青霞",66); System.out.println(s3.hashCode()); System.out.println("--------"); System.out.println("hello".hashCode()); //99162322 System.out.println("world".hashCode()); //113318802 System.out.println("java".hashCode()); //3254818 System.out.println("world".hashCode()); //113318802 System.out.println("--------"); System.out.println("重地".hashCode()); //1179395 System.out.println("通话".hashCode()); //1179395 } }
HashSet集合概述和特点【应用】
特点
底层数据结构是哈希表
对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以是不包含重复元素的集合
例如
public class Demo { public static void main(String[] args) { //创建集合对象 HashSet<String> hs = new HashSet<>(); //添加元素 hs.add("hello"); hs.add("world"); hs.add("java"); hs.add("world"); //遍历 for(String s : hs) { System.out.println(s); } } }
HashSet集合保证元素唯一性源码分析【理解】
HashSet集合保证元素唯一性的原理
1.根据对象的哈希值计算存储位置
如果当前位置没有元素则直接存入
如果当前位置有元素存在,则进入第二步
2.当前元素的元素和已经存在的元素比较哈希值
如果哈希值不同,则将当前元素进行存储
如果哈希值相同,则进入第三步
3.通过equals()方法比较两个元素的内容
如果内容不相同,则将当前元素进行存储
如果内容相同,则不存储当前元素
HashSet集合保证元素唯一性的图解
子主题
常见数据结构之哈希表【理解】
HashSet集合存储学生对象并遍历【应用】
需求
创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
注意
实体类要重写hash和equals,才能实现HashSet去重
思路
代码
学生类
子主题
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } }
测试类
LinkedHashSet集合概述和特点【应用】
特点
哈希表和链表实现的Set接口,具有可预测的迭代次序
由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
由哈希表保证元素唯一,也就是说没有重复的元素
例如
需求
存储字符串并遍历
学生类
子主题
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } }
测试类
public class Demo { public static void main(String[] args) { //创建集合对象 LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(); //添加元素 linkedHashSet.add("hello"); linkedHashSet.add("world"); linkedHashSet.add("java"); linkedHashSet.add("world"); //遍历集合 for(String s : linkedHashSet) { System.out.println(s); } } }
47. Set集合排序
TreeSet集合概述和特点【应用】
概述
元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator) :根据指定的比较器进行排序
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不包含重复元素的集合
例如
需求
存储整数并遍历
测试类
public class Demo { public static void main(String[] args) { //创建集合对象 TreeSet<Integer> ts = new TreeSet<>(); //添加元素 ts.add(10); ts.add(40); ts.add(30); ts.add(50); ts.add(20); ts.add(30); //遍历集合 for(Integer i : ts) { System.out.println(i); } } }
自然排序Comparable的使用【应用】(难)
需求
存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
思路
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
代码
学生类
package com.wry; /** * @author wry * @date 2022/4/2 * @apiNote */ public class Student implements Comparable<Student> { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override /** * Compares this object with the specified object for order. * Returns a negative integer, zero, or a positive integer as this object is less than, * equal to, or greater than the specified object. */ public int compareTo(Student s) { //按照年龄从小到大排序 int num = this.age - s.age; // int num = s.age - this.age; //年龄相同时,按照姓名的字母顺序排序 int num2 = num==0?this.name.compareTo(s.name):num; return num2; } }
子主题
public class Demo { public static void main(String[] args) { //创建集合对象 TreeSet<Student> ts = new TreeSet<Student>(); //创建学生对象 Student s1 = new Student("xishi", 29); Student s2 = new Student("wangzhaojun", 28); Student s3 = new Student("diaochan", 30); Student s4 = new Student("yangyuhuan", 33); Student s5 = new Student("linqingxia",33); Student s6 = new Student("linqingxia",33); //把学生添加到集合 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); //遍历集合 for (Student s : ts) { System.out.println(s.getName() + "," + s.getAge()); } } }
比较器排序Comparator的使用【应用】(难)
需求
- 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
思路
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
子主题
public class Demo { public static void main(String[] args) { //创建集合对象 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() { @Override /** *Comparator Compares its two arguments for order. Returns a negative integer, zero, or a positive integer * as the first argument is less than, equal to, or greater than the second. */ public int compare(Student s1, Student s2) { //this.age - s.age //s1,s2 int num = s1.getAge() - s2.getAge(); int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; return num2; } }); //创建学生对象 Student s1 = new Student("xishi", 29); Student s2 = new Student("wangzhaojun", 28); Student s3 = new Student("diaochan", 30); Student s4 = new Student("yangyuhuan", 33); Student s5 = new Student("linqingxia",33); Student s6 = new Student("linqingxia",33); //把学生添加到集合 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); //遍历集合 for (Student s : ts) { System.out.println(s.getName() + "," + s.getAge()); } } }
成绩排序案例【应用】(难)
需求
- 用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
- 要求:按照总分从高到低出现
代码
学生类
子主题
public class Student { private String name; private int chinese; private int math; public Student() { } public Student(String name, int chinese, int math) { this.name = name; this.chinese = chinese; this.math = math; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getChinese() { return chinese; } public void setChinese(int chinese) { this.chinese = chinese; } public int getMath() { return math; } public void setMath(int math) { this.math = math; } public int getSum() { return this.chinese + this.math; } }
测试类
子主题
public class Demo { public static void main(String[] args) { //创建TreeSet集合对象,通过比较器排序进行排序 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { //主要条件 int num = s2.getSum() - s1.getSum(); //次要条件 int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num; int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2; return num3; } }); //创建学生对象 Student s1 = new Student("林青霞", 98, 100); Student s2 = new Student("张曼玉", 95, 95); Student s3 = new Student("王祖贤", 100, 93); Student s4 = new Student("柳岩", 100, 97); Student s5 = new Student("风清扬", 98, 98); Student s6 = new Student("左冷禅", 97, 99); Student s7 = new Student("赵云", 97, 99); //把学生对象添加到集合 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); //遍历集合 for (Student s : ts) { System.out.println(s.getName() + "," + s.getChinese() + "," + s.getMath() + "," + s.getSum()); } } }
不重复的随机数案例【应用】
需求
编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
代码
子主题
public class Demo { public static void main(String[] args) { //创建Set集合对象 Set<Integer> set = new TreeSet<>(); //创建随机数对象 Random r = new Random(); //判断集合的长度是不是小于10 while (set.size()<10) { //产生一个随机数,添加到集合 int number = r.nextInt(20) + 1; set.add(number); } //遍历集合 for(Integer i : set) { System.out.println(i); } } }
48. 泛型
泛型概述和好处【理解】
泛型概述
是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
泛型定义格式
<类型>:指定一种类型的格式。这里的类型可以看成是形参
<类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
泛型类【应用】
定义格式
修饰符 class 类名<类型> { }
示例代码
泛型类
public class Generic<T> { private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } }
测试类
public class GenericDemo { public static void main(String[] args) { Generic<String> g1 = new Generic<String>(); g1.setT("林青霞"); System.out.println(g1.getT()); Generic<Integer> g2 = new Generic<Integer>(); g2.setT(30); System.out.println(g2.getT()); Generic<Boolean> g3 = new Generic<Boolean>(); g3.setT(true); System.out.println(g3.getT()); } }
泛型方法【应用】
定义格式
修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
示例代码
带有泛型方法的类
public class Generic { public <T> void show(T t) { System.out.println(t); } }
测试类
public class GenericDemo { public static void main(String[] args) { Generic g = new Generic(); g.show("林青霞"); g.show(30); g.show(true); g.show(12.34); } }
泛型接口【应用】
定义格式
修饰符 interface 接口名<类型> { }
示例代码
泛型接口
public interface Generic<T> { void show(T t); }
泛型接口实现类
public class GenericImpl<T> implements Generic<T> { @Override public void show(T t) { System.out.println(t); } }
测试类
public class GenericDemo { public static void main(String[] args) { Generic<String> g1 = new GenericImpl<String>(); g1.show("林青霞"); Generic<Integer> g2 = new GenericImpl<Integer>(); g2.show(30); } }
泛型详解
前言
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。
概述
泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。
什么是泛型?为什么要使用泛型?
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
一个栗子
一个被举了无数次的例子:
List arrayList = new ArrayList(); arrayList.add("aaaa"); arrayList.add(100); for(int i = 0; i< arrayList.size();i++){ String item = (String)arrayList.get(i); Log.d("泛型测试","item = " + item); }
毫无疑问,程序的运行结果会以崩溃结束:
ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。
我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。
List<String> arrayList = new ArrayList<String>(); ... //arrayList.add(100); 在编译阶段,编译器就会报错
特性
泛型只在编译阶段有效。看下面的代码:
https://blog.csdn.net/s10461/article/details/53941091?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164747649616782094881039%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164747649616782094881039&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-53941091.142^v2^pc_search_result_cache,143^v4^control&utm_term=%E6%B3%9B%E5%9E%8B&spm=1018.2226.3001.4187
类型通配符【应用】
作用
为了表示各种泛型List的父类,可以使用类型通配符
分类
类型通配符:<?>
List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
类型通配符上限:<? extends 类型>
List<? extends Number>:它表示的类型是Number或者其子类型
类型通配符下限:<? super 类型>
List<? super Number>:它表示的类型是Number或者其父类型
例如
public class Demo { public static void main(String[] args) { //类型通配符:<?> List<?> list1 = new ArrayList<Object>(); List<?> list2 = new ArrayList<Number>(); List<?> list3 = new ArrayList<Integer>(); System.out.println("--------"); //类型通配符上限:<? extends 类型> //List<? extends Number> list4 = new ArrayList<Object>(); List<? extends Number> list5 = new ArrayList<Number>(); List<? extends Number> list6 = new ArrayList<Integer>(); System.out.println("--------"); //类型通配符下限:<? super 类型> List<? super Number> list7 = new ArrayList<Object>(); List<? super Number> list8 = new ArrayList<Number>(); // List<? super Number> list9 = new ArrayList<Integer>(); } }
49. 可变参数
可变参数【应用】
介绍
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
格式
注意事项
这里的变量其实是一个数组
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
例如
public class Demo { public static void main(String[] args) { System.out.println(sum(10, 20)); System.out.println(sum(10, 20, 30)); System.out.println(sum(10, 20, 30, 40)); System.out.println(sum(10,20,30,40,50)); System.out.println(sum(10,20,30,40,50,60)); System.out.println(sum(10,20,30,40,50,60,70)); System.out.println(sum(10,20,30,40,50,60,70,80,90,100)); } // public static int sum(int b,int... a) { // return 0; // } public static int sum(int... a) { System.out.println(a); int sum = 0; for(int i : a) { sum += i; } return sum; } }
可变参数的使用【应用】
Arrays工具类中有一个静态方法
public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表
返回的集合不能做增删操作,可以做修改操作
例如
public class Demo { public static void main(String[] args) { //public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表 List<String> list1 = Arrays.asList("hello", "world", "java"); // list1.add("javaee"); //UnsupportedOperationException // list1.remove("world"); //UnsupportedOperationException // Replaces the element at the specified position in this list with the specified element (optional operation). list1.set(1,"javaee"); System.out.println(list1); } }
50. Map集合
Map集合概述和特点【理解】
概述
特点
键值对映射关系
一个键对应一个值
键不能重复,值可以重复。当键重复添加,后添加的值替换前添加的值。
元素存取无序
注意
如果map的key是对象。如果想要map的key不重复,需要重写该对象的equals和hashCode
例如
public class Demo { public static void main(String[] args) { //创建集合对象 Map<String,String> map = new HashMap<>(); // Associates the specified value with the specified key in this map (optional operation). map.put("001","林青霞"); map.put("002","张曼玉"); map.put("003","王祖贤"); map.put("003","柳岩"); //输出集合对象 System.out.println(map); } }
Map集合的基本功能【应用】
方法介绍
例如
public class Demo { public static void main(String[] args) { //创建集合对象 Map<String,String> map = new HashMap<String,String>(); //V put(K key,V value):添加元素 map.put("张无忌","赵敏"); map.put("郭靖","黄蓉"); map.put("杨过","小龙女"); // Removes the mapping for a key from this map if it is present (optional operation). //System.out.println(map.remove("郭靖")); //System.out.println(map.remove("郭襄")); // Removes all of the mappings from this map (optional operation). //map.clear(); // Returns true if this map contains a mapping for the specified key. //System.out.println(map.containsKey("郭靖")); //System.out.println(map.containsKey("郭襄")); // Returns true if this map contains no key-value mappings. //System.out.println(map.isEmpty()); // Returns the number of key-value mappings in this map System.out.println(map.size()); //输出集合对象 System.out.println(map); } }
Map集合的获取功能【应用】
方法介绍
例如
public class Demo { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<>(); //添加元素 map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); // 返回指定键映射到的值,如果此映射不包含该键的映射,则返回null 。 System.out.println(map.get("张无忌")); System.out.println(map.get("张三丰")); System.out.println("--------------------------------"); //Set<K> keySet():获取所有键的集合 Set<String> keySet = map.keySet(); for(String key : keySet) { System.out.println(key); } System.out.println("--------------------------------"); //Collection<V> values():获取所有值的集合 Collection<String> values = map.values(); for(String value : values) { System.out.println(value); } } }
Map集合的遍历(方式1)【应用】
遍历思路
我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
把所有的丈夫给集中起来
遍历丈夫的集合,获取到每一个丈夫
根据丈夫去找对应的妻子
步骤分析
获取所有键的集合。用keySet()方法实现
遍历键的集合,获取到每一个键。用增强for实现
根据键去找值。用get(Object key)方法实现
例如
public class Demo { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<>(); //Associates the specified value with the specified key in this map (optional operation). map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); // Returns a Set view of the keys contained in this map. Set<String> keySet = map.keySet(); //遍历键的集合,获取到每一个键。用增强for实现 for (String key : keySet) { // 返回指定键映射到的值,如果此映射不包含该键的映射,则返回null 。 String value = map.get(key); System.out.println(key + "," + value); } } }
Map集合的遍历(方式2)【应用】
遍历思路
我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
获取所有结婚证的集合
遍历结婚证的集合,得到每一个结婚证
根据结婚证获取丈夫和妻子
步骤分析
获取所有键值对对象的集合
Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
遍历键值对对象的集合,得到每一个键值对对象
用增强for实现,得到每一个Map.Entry
根据键值对对象获取键和值
用getKey()得到键
用getValue()得到值
例如
public class Demo { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<String, String>(); // main:31, Demo map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); // Returns a Set view of the mappings contained in this map. Set<Map.Entry<String, String>> entrySet = map.entrySet(); //遍历键值对对象的集合,得到每一个键值对对象 for (Map.Entry<String, String> me : entrySet) { // Returns the key corresponding to this entry. String key = me.getKey(); // Returns the value corresponding to this entry. String value = me.getValue(); System.out.println(key + "," + value); } } }
Map集合的案例【应用】
HashMap集合练习之键是String值是Student
需求
建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历
思路
1:定义学生类 2:创建HashMap集合对象 3:创建学生对象 4:把学生添加到集合 5:遍历集合 方式1:键找值 方式2:键值对对象找键和值
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
public class Demo { public static void main(String[] args) { //创建HashMap集合对象 HashMap<String, Student> hm = new HashMap<>(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); //把学生添加到集合 hm.put("001", s1); hm.put("002", s2); hm.put("003", s3); //方式1:键找值 // Returns a Set view of the keys contained in this map. Set<String> keySet = hm.keySet(); for (String key : keySet) { // Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. Student value = hm.get(key); System.out.println(key + "," + value.getName() + "," + value.getAge()); } System.out.println("--------"); //方式2:键值对对象找键和值 // Returns a Set view of the mappings contained in this map. Set<Map.Entry<String, Student>> entrySet = hm.entrySet(); for (Map.Entry<String, Student> me : entrySet) { // the key corresponding to this entry String key = me.getKey(); // Returns the value corresponding to this entry Student value = me.getValue(); System.out.println(key + "," + value.getName() + "," + value.getAge()); } } }
HashMap集合练习之键是Student值是String
需求
- 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
- 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } }
子主题
public class Demo { public static void main(String[] args) { //创建HashMap集合对象 HashMap<Student, String> hm = new HashMap<>(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); Student s4 = new Student("王祖贤", 33); //把学生添加到集合 hm.put(s1, "西安"); hm.put(s2, "武汉"); hm.put(s3, "郑州"); hm.put(s4, "北京"); //遍历集合 Set<Student> keySet = hm.keySet(); for (Student key : keySet) { String value = hm.get(key); System.out.println(key.getName() + "," + key.getAge() + "," + value); } } }
集合嵌套之ArrayList嵌套HashMap
需求
- 创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap
- 每一个HashMap的键和值都是String,并遍历
代码
public class Demo { public static void main(String[] args) { //创建ArrayList集合 ArrayList<HashMap<String, String>> array = new ArrayList<>(); //创建HashMap集合,并添加键值对元素 HashMap<String, String> hm1 = new HashMap<>(); hm1.put("孙策", "大乔"); hm1.put("周瑜", "小乔"); //把HashMap作为元素添加到ArrayList集合 array.add(hm1); HashMap<String, String> hm2 = new HashMap<>(); hm2.put("郭靖", "黄蓉"); hm2.put("杨过", "小龙女"); //把HashMap作为元素添加到ArrayList集合 array.add(hm2); HashMap<String, String> hm3 = new HashMap<>(); hm3.put("令狐冲", "任盈盈"); hm3.put("林平之", "岳灵珊"); //把HashMap作为元素添加到ArrayList集合 array.add(hm3); //遍历ArrayList集合 for (HashMap<String, String> hm : array) { Set<String> keySet = hm.keySet(); for (String key : keySet) { String value = hm.get(key); System.out.println(key + "," + value); } } } }
集合嵌套之HashMap嵌套ArrayList
需求
- 创建一个HashMap集合,存储三个键值对元素,每一个键值对元素的键是String,值是ArrayList
- 每一个ArrayList的元素是String,并遍历。
代码
public class Demo { public static void main(String[] args) { //创建HashMap集合 HashMap<String, ArrayList<String>> hm = new HashMap<>(); //创建ArrayList集合,并添加元素 ArrayList<String> sgyy = new ArrayList<>(); sgyy.add("诸葛亮"); sgyy.add("赵云"); //把ArrayList作为元素添加到HashMap集合 hm.put("三国演义",sgyy); ArrayList<String> xyj = new ArrayList<>(); xyj.add("唐僧"); xyj.add("孙悟空"); //把ArrayList作为元素添加到HashMap集合 hm.put("西游记",xyj); ArrayList<String> shz = new ArrayList<>(); shz.add("武松"); shz.add("鲁智深"); //把ArrayList作为元素添加到HashMap集合 hm.put("水浒传",shz); //遍历HashMap集合 Set<String> keySet = hm.keySet(); for(String key : keySet) { System.out.println(key); ArrayList<String> value = hm.get(key); for(String s : value) { System.out.println("\t" + s); } } } }
统计字符串中每个字符出现的次数
需求
- 键盘录入一个字符串,要求统计字符串中每个字符出现的次数。
- 举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”
思路
代码
子主题
public class Demo { public static void main(String[] args) { //键盘录入一个字符串 Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String line = sc.nextLine(); // 键不排序 // HashMap<Character, Integer> hm = new HashMap<Character, Integer>(); // 键排序 // Constructs a new, empty tree map, using the natural ordering of its keys. // The Character class wraps a value of the primitive type char in an object. TreeMap<Character, Integer> hm = new TreeMap<>(); //遍历字符串,得到每一个字符 for (int i = 0; i < line.length(); i++) { // Returns the char value at the specified index. An index ranges from 0 to length() - 1. char key = line.charAt(i); //拿得到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值 Integer value = hm.get(key); if (value == null) { //如果返回值是null:说明该字符在HashMap集合中不存在,就把该字符作为键,1作为值存储 // Associates the specified value with the specified key in this map. hm.put(key,1); } else { //如果返回值不是null:说明该字符在HashMap集合中存在,把该值加1,然后重新存储该字符和对应的值 value++; hm.put(key,value); } } //遍历HashMap集合,得到键和值,按照要求进行拼接 StringBuilder sb = new StringBuilder(); Set<Character> keySet = hm.keySet(); for(Character key : keySet) { Integer value = hm.get(key); sb.append(key).append("(").append(value).append(")"); } String result = sb.toString(); //输出结果 System.out.println(result); } }
51. Collections集合工具类
Collections概述和使用【应用】
作用
是针对集合操作的工具类
常用方法
代码
public class Demo { public static void main(String[] args) { //创建集合对象 List<Integer> list = new ArrayList<>(); //添加元素 list.add(30); list.add(20); list.add(50); list.add(10); list.add(40); //public static <T extends Comparable<? super T>> void sort(List<T> list):将指定的列表按升序排序 Collections.sort(list); //public static void reverse(List<?> list):反转指定列表中元素的顺序 //Collections.reverse(list); //public static void shuffle(List<?> list):使用默认的随机源随机排列指定的列表 //Collections.shuffle(list); System.out.println(list); } }
ArrayList集合存储学生并排序【应用】
需求
- ArrayList存储学生对象,使用Collections对ArrayList进行排序
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
思路
代码
学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
子主题
public class Demo { public static void main(String[] args) { //创建ArrayList集合对象 ArrayList<Student> array = new ArrayList<>(); //创建学生对象 Student s1 = new Student("linqingxia", 30); Student s2 = new Student("zhangmanyu", 35); Student s3 = new Student("wangzuxian", 33); Student s4 = new Student("liuyan", 33); //把学生添加到集合 array.add(s1); array.add(s2); array.add(s3); array.add(s4); //使用Collections对ArrayList集合排序 //sort(List<T> list, Comparator<? super T> c) Collections.sort(array, new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { //按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序 int num = s1.getAge() - s2.getAge(); int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; return num2; } }); //遍历集合 for (Student s : array) { System.out.println(s.getName() + "," + s.getAge()); } } }
斗地主案例
模拟斗地主案例-普通版本【应用】
需求
通过程序实现斗地主过程中的洗牌,发牌和看牌
代码
子主题
public class Demo { public static void main(String[] args) { //创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现 ArrayList<String> array = new ArrayList<>(); //往牌盒里面装牌 /* ♦2,♦3,♦4...♦K,♦A ♣2,... ♥2,... ♠2,... 小王,大王 */ //定义花色数组 String[] colors = {"♦", "♣", "♥", "♠"}; //定义点数数组 String[] numbers = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}; for (String color : colors) { for (String number : numbers) { array.add(color + number); } } array.add("小王"); array.add("大王"); //洗牌,也就是把牌打撒,用Collections的shuffle()方法实现 Collections.shuffle(array); // System.out.println(array); //发牌,也就是遍历集合,给三个玩家发牌 ArrayList<String> lqxArray = new ArrayList<>(); ArrayList<String> lyArray = new ArrayList<>(); ArrayList<String> fqyArray = new ArrayList<>(); ArrayList<String> dpArray = new ArrayList<>(); for (int i = 0; i < array.size(); i++) { // Returns the element at the specified position in this list. String poker = array.get(i); if (i >= array.size() - 3) { dpArray.add(poker); } else if (i % 3 == 0) { lqxArray.add(poker); } else if (i % 3 == 1) { lyArray.add(poker); } else if (i % 3 == 2) { fqyArray.add(poker); } } //看牌,也就是三个玩家分别遍历自己的牌 lookPoker("林青霞", lqxArray); lookPoker("柳岩", lyArray); lookPoker("风清扬", fqyArray); lookPoker("底牌", dpArray); } //看牌的方法 public static void lookPoker(String name, ArrayList<String> array) { System.out.print(name + "的牌是:"); for (String poker : array) { System.out.print(poker + " "); } System.out.println(); } }
模拟斗地主案例-升级版本【应用】
需求
通过程序实现斗地主过程中的洗牌,发牌和看牌。要求:对牌进行排序
代码
子主题
public class Demo { public static void main(String[] args) { //创建HashMap,键是编号,值是牌 HashMap<Integer, String> hm = new HashMap<>(); //创建ArrayList,存储编号 ArrayList<Integer> array = new ArrayList<>(); //创建花色数组和点数数组 String[] colors = {"♦", "♣", "♥", "♠"}; String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"}; //从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存储编号 int index = 0; for (String number : numbers) { for (String color : colors) { hm.put(index, color + number); array.add(index); index++; } } hm.put(index, "小王"); array.add(index); index++; hm.put(index, "大王"); array.add(index); //洗牌(洗的是编号),用Collections的shuffle()方法实现 Collections.shuffle(array); //发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收) TreeSet<Integer> lqxSet = new TreeSet<>(); TreeSet<Integer> lySet = new TreeSet<>(); TreeSet<Integer> fqySet = new TreeSet<>(); TreeSet<Integer> dpSet = new TreeSet<>(); for (int i = 0; i < array.size(); i++) { int x = array.get(i); if (i >= array.size() - 3) { dpSet.add(x); } else if (i % 3 == 0) { lqxSet.add(x); } else if (i % 3 == 1) { lySet.add(x); } else if (i % 3 == 2) { fqySet.add(x); } } //调用看牌方法 lookPoker("林青霞", lqxSet, hm); lookPoker("柳岩", lySet, hm); lookPoker("风清扬", fqySet, hm); lookPoker("底牌", dpSet, hm); } //定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌) public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) { System.out.print(name + "的牌是:"); for (Integer key : ts) { String poker = hm.get(key); System.out.print(poker + " "); } System.out.println(); } }
52. File类
53. 模板
知识
定义
格式
例如
bug
原因
解决
应用
需求
思路
代码
方法
定义格式
例如
调用格式
例如