Java知识记忆
- Java技术体系
- JDK
- IDEA
- Java基础语法
- Java基础语法
- 1.类型转换问题
- 2.运算符
- 3. Java数据输入(应用)
- 4、流程控制语句(应用)
- 5.数组
- Java 方法
- 面向对象
- 常用API(string,ArrayList)
- IDEA项目结构
- 面向对象进阶
- 常用API
- 包装类
- Arrays类
- Lambda表达式
- 集合
- Stream流
- 异常处理
- 日志框架
- File
- IO流
- 多线程
- 网络编程
- 单元测试
- 反射
- 注解
- XML
- 设计模式:工厂模式
- 设计模式:装饰模式
Java技术体系
- Java SE(Java Standard Edition) 标准版
Java技术的核心和基础,是学习Java EE,JavaME的基础,支持开发桌面级应用(如Windows下的应用程序)的Java平台 - Java EE(Java Enterprise Edition)企业版
为开发企业环境下的应用程序提供的一套解决方案,主要针对于Web应用程序开发。多用于大型网站开发。 - Java ME(Java Micro Edition)小型版
是为机顶盒、移动电话和PDA之类嵌入式消费电子设备提供的Java语言平台,但现在移动终端基本上都是使用Android和IOS的技术平台了 - Java Card
支持一些Java小程序(Applets)运行在小内存设备(如智能卡)上的平台 ,此技术也被广泛运用在SIM卡、提款卡上。
JDK
Java语言的产品是 JDK(Java Development Kit :Java开发者工具包) ,必须安装JDK才能使用Java语言。
javac是编译工具,java是执行工具。java执行javac编译的.class文件。
- java代码编写的基本要求:
- 文件名称的后缀必须是java结尾。
- 文件名称必须与代码的类名称一致。
- 必须使用英文模式下的符号
- jvm :Java虚拟机, 真正运行Java程序的地方。
- JRE(Java Runtime Environment): Java运行时环境,只包含java虚似机、以及java帮我们写好的程序等java运行所需的东西。
- . Java的跨平台是什么含义,如何实现的?
- 一次编译、处处可用
- 不同平台有对应的JRE环境
- JAVA_HOME:告诉操作系统JDK安装在了哪个位置(未来其他技术要通过这个找JDK)
- JAVA_HOME C:\soft\java\jdk-17
- Path:告诉操作系统JDK提供的javac(编译)、java(执行)命令安装到了哪个位置
- Path C:\soft\java\jdk-17\bin
或者 - Path %JAVA_HOME%\bin
- Path C:\soft\java\jdk-17\bin
IDEA
-
IDEA的结构都是什么样的?
⚫ project - module - package – class
⚫ project中可以创建多个module
⚫ module中可以创建多个package
⚫ package中可以创建多个class -
新建都是哪些操作
⚫ New project/module/package/class -
主题配置
-
字体配置
-
常用快捷键
-
导入模块
导入已存在的module的,选择要导入法人这个黑点点确定,即可导入模块。
也可以将src的代码复制。
Java基础语法
- 注释:
- 单行注释://注释内容,只能写一行
- 多行注释: /* */
- 文档注释: /** */
- Ctrl + / 单行注释。
- Ctrl + Shift + / 选中代码进行多行注释
- 字面量
⚫ 告诉程序员,数据在程序中的书写格式。
注意:
⚫ 字符必须单引号围起来,有且仅能一个字符。
⚫ 字符串必须用双引号围起来。
⚫ 布尔类型只有2个值:false 、true - 变量
变量就是用来存储一个数据的内存区域(可以理解成盒子),且里面存储的数据可以变化。
- 变量定义:
数据类型 变量名称 = 初始值;
double money = 6.0;
- 数据类型
数据类型 | 关键字 | 内存占用 | 取值范围 |
---|---|---|---|
整数 | byte | 1 | 负的2的7次方 ~ 2的7次方-1(-128~127) |
short | 2 | 负的2的15次方 ~ 2的15次方-1(-32768~32767) | |
int | 4 | 负的2的31次方 ~ 2的31次方-1 | |
long | 8 | 负的2的63次方 ~ 2的63次方-1 | |
浮点数 | float | 4 | 1.401298e-45 ~ 3.402823e+38 |
double | 8 | 4.9000000e-324 ~ 1.797693e+308 | |
字符 | char | 2 | 0-65535 |
布尔 | boolean | 1 | true,false |
- 命名规范
变量名称:满足标识符规则,建议全英文、有意义、首字母小写,满足“驼峰模式”,例如:int studyNumber = 59。
类名称: 满足标识符规则,建议全英文、有意义、首字母大写,满足“驼峰模式”,例如:HelloWorld.java。
Java基础语法
1.类型转换问题
自动类型转换
类型范围小的变量,可以直接赋值给类型范围大的变量。
把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量。这种转换方式是自动的,直接书写即可。例如:
double num = 10; // 将int类型的10直接赋值给double类型
System.out.println(num); // 输出10.0
byte a = 12 ;
int b = a;
System.out.println(b); // 12
表达式的自动类型转换
在表达式中,小范围类型的变量会自动转换成当前较大范围的类型再运算。
注意事项:
表达式的最终结果类型由表达式中的最高类型决定。
在表达式中,byte、short、char 是直接转换成int类型参与运算的。
强制类型转换
类型范围大的数据或者变量,不能直接赋值给类型范围小的变量,会报错,把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量必须进行强制类型转换。
强制类型转换格式:目标数据类型 变量名 = (目标数据类型)值或者变量;
例如:
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类型不能与其他基本数据类型相互转换。
2.运算符
2.1 算术运算符(理解)
2.1.1 运算符和表达式
运算符:对常量或者变量进行操作的符号
表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式。
不同运算符连接的表达式体现的是不同类型的表达式。
举例说明:
int a = 10;
int b = 20;
int c = a + b;
+:是运算符,并且是算术运算符。
a + b:是表达式,由于+是算术运算符,所以这个表达式叫算术表达式。
2.1.2 算术运算符
符号 | 作用 | 说明 |
---|---|---|
+ | 加 | 参看小学一年级 |
- | 减 | 参看小学一年级 |
* | 乘 | 参看小学二年级,与“×”相同 |
/ | 除 | 参看小学二年级,与“÷”相同 |
% | 取余 | 获取的是两个数据做除法的余数 |
注意:
/和%的区别:两个数据做除法,/取结果的商,%取结果的余数。
整数操作只能得到整数,要想得到小数,必须有浮点数参与运算。
int a = 10;
int b = 3;
System.out.println(a / b); // 输出结果3
System.out.println(a % b); // 输出结果1
2.1.3 字符的“+”操作
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类型做算术运算。
2.1.4 字符串的“+”操作
当“+”操作中出现字符串时,这个”+”是字符串连接符,而不是算术运算。
System.out.println("itheima"+ 666); // 输出:itheima666
在”+”操作中,如果出现了字符串,就是连接运算符,否则就是算术运算。当连续进行“+”操作时,从左到右逐个执行。
System.out.println(1 + 99 + "年黑马"); // 输出:199年黑马
System.out.println(1 + 2 + "itheima" + 3 + 4); // 输出:3itheima34
// 可以使用小括号改变运算的优先级
System.out.println(1 + 2 + "itheima" + (3 + 4)); // 输出:3itheima7
2.2 赋值运算符(应用)
赋值运算符的作用是将一个表达式的值赋给左边,左边必须是可修改的,不能是常量。
符号 | 作用 | 说明 |
---|---|---|
= | 赋值 | a=10,将10赋值给变量a |
+= | 加后赋值 | a+=b,将a+b的值给a |
-= | 减后赋值 | a-=b,将a-b的值给a |
*= | 乘后赋值 | a*=b,将a×b的值给a |
/= | 除后赋值 | a/=b,将a÷b的商给a |
%= | 取余后赋值 | a%=b,将a÷b的余数给a |
注意:
扩展的赋值运算符隐含了强制类型转换。
short s = 10;
s = s + 10; // 此行代码报出,因为运算中s提升为int类型,运算结果int赋值给short可能损失精度
s += 10; // 此行代码没有问题,隐含了强制类型转换,相当于 s = (short) (s + 10);
2.3 自增自减运算符(理解)
符号 | 作用 | 说明 |
---|---|---|
++ | 自增 | 变量的值加1 |
– | 自减 | 变量的值减1 |
注意事项:
++和-- 既可以放在变量的后边,也可以放在变量的前边。
单独使用的时候, ++和-- 无论是放在变量的前边还是后边,结果是一样的。
参与操作的时候,如果放在变量的后边,先拿变量参与操作,后拿变量做++或者–。
参与操作的时候,如果放在变量的前边,先拿变量做++或者–,后拿变量参与操作。
最常见的用法:单独使用。
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。
*/
注意:通过此练习深刻理解自增和自减的规律,但实际开发中强烈建议不要写这样的代码!小心挨打!
2.4 关系运算符(应用)
关系运算符有6种关系,分别为小于、小于等于、大于、等于、大于等于、不等于。
符号 | 说明 |
---|---|
== | a==b,判断a和b的值是否相等,成立为true,不成立为false |
!= | a!=b,判断a和b的值是否不相等,成立为true,不成立为false |
> | a>b,判断a是否大于b,成立为true,不成立为false |
>= | a>=b,判断a是否大于等于b,成立为true,不成立为false |
< | a<b,判断a是否小于b,成立为true,不成立为false |
<= | a<=b,判断a是否小于等于b,成立为true,不成立为false |
注意事项:
关系运算符的结果都是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
2.5 逻辑运算符(应用)
逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。
符号 | 作用 | 说明 |
---|---|---|
& | 逻辑与 | a&b,a和b都是true,结果为true,否则为false |
| | 逻辑或 | a|b,a和b都是false,结果为false,否则为true |
^ | 逻辑异或 | a^b,a和b结果不同为true,相同为false |
! | 逻辑非 | !a,结果和a的结果正好相反 |
//定义变量
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
2.6 三元运算符(理解)
三元运算符语法格式:
关系表达式 ? 表达式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);
}
}
3. Java数据输入(应用)
我们可以通过 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 ScannerDemo {
public static void main(String[] args) {
//创建对象
Scanner sc = new Scanner(System.in);
//接收数据
int x = sc.nextInt();
//输出数据
System.out.println("x:" + x);
}
}
改写三个和尚案例,数据使用键盘录入。
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");
}
}
3.2 键盘输入一个数组
- 第一种方法:(不限制输入数组的长度)
System.out.println("请输入几个数并用逗号隔开:");
Scanner sc = new Scanner(System.in);
String str = sc.next().toString();
String[] arr = str.split(",");
int[] b = new int[arr.length];
for(int j = 0; j<b.length;j++) {
b[j] = Integer.parseInt(arr[j]);
System.out.println(b[j]+" ");
}
- 第二种方法:(限制输入的个数)
System.out.println("请输入三个数:");
Scanner in = new Scanner(System.in);
int[] b=new int[3];
for(int i=0;i<b.length;i++){
b[i]=in.nextInt();
}
- Scanner类中next()与nextLine()方法的区别:next()与nextLine()区别很明确,next() 方法遇见第一个有效字符(不是空格和换行符)时,开始扫描,当遇见第一个分隔符或结束符(空格或换行符)时,结束扫描,获取扫描到的内容,也就是说使用next()方法获得的是不含空格和换行符的单个字符串。
而使用nextLine()时,则可以扫描到一行内容并作为一个字符串而被获取到。
4、流程控制语句(应用)
在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,我们必须清楚每条语句的执行流程。而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能。
4.1 流程控制语句分类
顺序结构
分支结构(if, switch)
循环结构(for, while, do…while)
4.2 顺序结构
顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。
顺序结构执行流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TqBrNgJv-1645756808277)(day03-switch&循环语句.assets\1545615769372.png)]
4.3 分支结构:if语句
if语句格式1
格式:
if (关系表达式) {
语句体;
}
执行流程:
①首先计算关系表达式的值
②如果关系表达式的值为true就执行语句体
③如果关系表达式的值为false就不执行语句体
④继续执行后面的语句内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-om238h9T-1645756808289)(day03-switch&循环语句.assets\1545616039363.png)]
示例:
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("结束");
}
}
if语句格式2
格式:
if (关系表达式) {
语句体1;
} else {
语句体2;
}
执行流程:
①首先计算关系表达式的值
②如果关系表达式的值为true就执行语句体1
③如果关系表达式的值为false就执行语句体2
④继续执行后面的语句内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qlEJJEd-1645756808290)(day03-switch&循环语句.assets\1545616221283.png)]
示例:
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语句案例:奇偶数
需求:任意给出一个整数,请用程序实现判断该整数是奇数还是偶数,并在控制台输出该整数是奇数还是偶数。
分析:
①为了体现任意给出一个整数,采用键盘录入一个数据
②判断整数是偶数还是奇数要分两种情况进行判断,使用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 + "是奇数");
}
}
}
if语句格式3
格式:
if (关系表达式1) {
语句体1;
} else if (关系表达式2) {
语句体2;
}
…
else {
语句体n+1;
}
执行流程:
①首先计算关系表达式1的值
②如果值为true就执行语句体1;如果值为false就计算关系表达式2的值
③如果值为true就执行语句体2;如果值为false就计算关系表达式3的值
④…
⑤如果没有任何关系表达式为true,就执行语句体n+1。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hx2Zl99N-1645756808292)(day03-switch&循环语句.assets\1545616667104.png)]
示例:键盘录入一个星期数(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语句格式3案例:
需求:小明快要期末考试了,小明爸爸对他说,会根据他不同的考试成绩,送他不同的礼物,假如你可以控制小明的得分,请用程序实现小明到底该获得什么样的礼物,并在控制台输出。
分析:
①小明的考试成绩未知,可以使用键盘录入的方式获取值
②由于奖励种类较多,属于多种判断,采用if…else…if格式实现
③为每种判断设置对应的条件
④为每种判断设置对应的奖励
4.4 分支结构:switch语句
switch语句结构(掌握)
-
格式
switch (表达式) { case 1: 语句体1; break; case 2: 语句体2; break; ... default: 语句体n+1; break; }
-
执行流程:
- 首先计算出表达式的值
- 其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结 束。
- 最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉。
switch语句练习-春夏秋冬(应用)
- 需求:一年有12个月,分属于春夏秋冬4个季节,键盘录入一个月份,请用程序实现判断该月份属于哪个季节,并输出。
- 运行结果:
春:3、4、5
夏:6、7、8
秋:9、10、11
冬:1、2、12
- 示例代码:
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("你输入的月份有误");
}
}
}
- 注意:如果switch中得case,没有对应break的话,则会出现case穿透的现象。
4.5 循环结构:for循环
for循环结构(掌握)
-
循环:
循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复 执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形 成死循环。
-
for循环格式:
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
- 例如
- 水仙花数,指的是一个三位数,个位、十位、百位的数字立方和等于原数
- 思路:
- 获取所有的三位数,准备进行筛选,最小的三位数为100,最大的三位数为999,使用for循环获取
- 获取每一个三位数的个位,十位,百位,做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循环练习-统计水仙花数个数(应用)
- 需求:统计“水仙花数”一共有多少个,并在控制台输出个数
- 示例代码:
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 + "个");
}
}
- 本题要点:
- 今后如果需求带有统计xxx,请先想到计数器变量
- 计数器变量定义的位置,必须在循环外部
4.6 循环结构: while循环
while结构(掌握)
-
while循环完整格式:
初始化语句; 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 + "次");
}
}
4.7 循环结构:do-while循环
do…while循环结构(掌握)
-
完整格式:
初始化语句; 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);
}
}
4.8 三种循环的区别(理解)
- 三种循环的区别
- for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
- do…while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)
- for循环和while的区别
- 条件控制语句所控制的自增变量,因为归属for循环的语法结构中,在for循环结束后,就不能再次被访问到了
- 条件控制语句所控制的自增变量,对于while循环来说不归属其语法结构中,在while循环结束后,该变量还可以继续使用
- 死循环(无限循环)的三种格式
- for(;😉{}
- while(true){}
- do {} while(true);
4.9 跳转控制语句(掌握)
- 跳转控制语句(break)
- 跳出循环,结束循环
- 跳转控制语句(continue)
- 跳过本次循环,继续下次循环
- 注意: continue只能在循环中进行使用!
4.10 循环嵌套(理解)
-
循环嵌套概述:在循环中,继续定义循环
-
示例代码:
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("--------"); } }
-
理解:
- 请反复理解这句话(整个内循环,就是外循环的一个循环体,内部循环体没有执行完毕,外循环是不会继续向下执行的)
-
结论:
- 外循环执行一次,内循环执行一圈
4.11 Random
Random产生随机数(掌握)
-
概述:
- Random类似Scanner,也是Java提供好的API,内部提供了产生随机数的功能
- API后续课程详细讲解,现在可以简单理解为Java已经写好的代码
- Random类似Scanner,也是Java提供好的API,内部提供了产生随机数的功能
-
使用步骤:
-
导入包
import java.util.Random;
-
创建对象
Random r = new Random();
-
产生随机数
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;
}
}
}
}
5.数组
5.1什么是数组【理解】
数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致。
5.2数组定义格式【记忆】
5.2.1第一种
数据类型[] 数组名
示例:
int[] arr;
double[] arr;
char[] arr;
5.2.2第二种
数据类型 数组名[]
示例:
int arr[];
double arr[];
char arr[];
5.3数组动态初始化【应用】
5.3.1什么是动态初始化
数组动态初始化就是只给定数组的长度,由系统给出默认初始化值。
5.3.2动态初始化格式
数据类型[] 数组名 = new 数据类型[数组长度];
int[] arr = new int[3];
5.3.3动态初始化格式详解
-
等号左边:
-
int:数组的数据类型
-
[]:代表这是一个数组
-
arr:代表数组的名称
-
-
等号右边:
-
new:为数组开辟内存空间
-
int:数组的数据类型
-
[]:代表这是一个数组
-
3:代表数组的长度
-
5.4数组元素访问【应用】
5.4.1什么是索引
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始。
这个自动编号称为数组索引(index),可以通过数组的索引访问到数组中的元素。
5.4.2访问数组元素格式
数组名[索引];
5.4.3示例代码
public class ArrayDemo {
public static void main(String[] args) {
int[] arr = new int[3];
//输出数组名
System.out.println(arr); //
//输出数组中的元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
5.5内存分配【理解】
5.5.1内存概述
内存是计算机中的重要原件,临时存储区域,作用是运行程序。
我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的。
必须放进内存中才能运行,运行完毕后会清空内存。
Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。
5.5.2 Java中的内存分配
- 目前我们只需要记住两个内存,分别是:栈内存和堆内存
区域名称 | 作用 |
---|---|
寄存器 | 给CPU使用,和我们开发无关。 |
本地方法栈 | JVM在使用操作系统功能的时候使用,和我们开发无关。 |
方法区 | 存储可以运行的class文件。 |
堆内存 | 存储对象或者数组,new来创建的,都存储在堆内存。 |
方法栈 | 方法运行时使用的内存,比如main方法运行,进入方法栈中执行。 |
5.6单个数组的内存图【理解】
5.7多个数组的内存图【理解】
5.8多个数组指向相同内存图【理解】
5.9数组静态初始化【应用】
5.9.1什么是静态初始化
在创建数组时,直接将元素确定
5.9.2静态初始化格式
-
完整版格式
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,...};
-
简化版格式
数据类型[] 数组名 = {元素1,元素2,...};
5.9.3示例代码
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]);
}
}
5.10数组操作的两个常见小问题【应用】
5.10.1索引越界异常
-
出现原因
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[3]; System.out.println(arr[3]); } }
数组长度为3,索引范围是0~2,但是我们却访问了一个3的索引。
程序运行后,将会抛出ArrayIndexOutOfBoundsException 数组越界异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。
-
解决方案
将错误的索引修改为正确的索引范围即可!
5.10.2空指针异常
-
出现原因
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 空指针异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。
-
解决方案
给数组一个真正的堆内存空间引用即可!
5.11数组遍历【应用】
-
数组遍历:就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。
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]); } } }
5.12数组最值【应用】
-
最大值获取:从数组的所有元素中找出最大值。
-
实现思路:
- 定义变量,保存数组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); } }
Java 方法
1. 方法概述
1.1 方法的概念(理解)
方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集
- 注意:
- 方法必须先创建才可以使用,该过程成为方法定义
- 方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程成为方法调用
2. 方法的定义和调用
2.1 无参数方法定义和调用(掌握)
-
定义格式:
public static void 方法名 ( ) { // 方法体; }
-
范例:
public static void method ( ) { // 方法体; }
-
调用格式:
方法名();
-
范例:
method();
-
注意:
方法必须先定义,后调用,否则程序将报错
2.2 方法调用过程图解(理解)
- 总结:每个方法在被调用执行的时候,都会进入栈内存,并且拥有自己独立的内存空间,方法内部代码调用完毕之后,会从栈内存中弹栈消失。
2.3 无参数方法的练习(应用)
- 需求:设计一个方法用于打印两个数中的较大数
- 思路:
- ①定义一个方法,用于打印两个数字中的较大数,例如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);
}
}
}
3. 带参数方法定义和调用
3.1 带参数方法定义和调用(掌握)
-
定义格式:
参数:由数据类型和变量名组成 - 数据类型 变量名
参数范例:int a
public static void 方法名 (参数1) { 方法体; } public static void 方法名 (参数1, 参数2, 参数3...) { 方法体; }
-
范例:
public static void isEvenNumber(int number){ ... } public static void getMax(int num1, int num2){ ... }
-
注意:
方法定义时,参数中的数据类型与变量名都不能缺少,缺少任意一个程序将报错 方法定义时,多个参数之间使用逗号( ,)分隔
-
-
调用格式:
方法名(参数); 方法名(参数1,参数2);
-
范例:
isEvenNumber(10); getMax(10,20);
- 方法调用时,参数的数量与类型必须与方法定义中的设置相匹配,否则程序将报错
3.2 形参和实参(理解)
- 形参:方法定义中的参数
等同于变量定义格式,例如:int number
- 实参:方法调用中的参数
等同于使用变量或常量,例如: 10 number
3.3 带参数方法练习(应用)
- 需求:设计一个方法用于打印两个数中的较大数,数据来自于方法参数 }
- 思路:
- ①定义一个方法,用于打印两个数字中的较大数,例如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);
}
}
}
4. 带返回值方法的定义和调用
4.1 带返回值方法定义和调用(掌握)
-
定义格式
public static 数据类型 方法名 ( 参数 ) { return 数据 ; }
-
范例
public static boolean isEvenNumber( int number ) { return true ; } public static int getMax( int a, int b ) { return 100 ; }
- 注意:
- 方法定义时return后面的返回值与方法定义上的数据类型要匹配,否则程序将报错
- 注意:
-
调用格式
方法名 ( 参数 ) ; 数据类型 变量名 = 方法名 ( 参数 ) ;
-
范例
isEvenNumber ( 5 ) ; boolean flag = isEvenNumber ( 5 );
- 注意:
- 方法的返回值通常会使用变量接收,否则该返回值将无意义
- 注意:
4.2 带返回值方法练习(应用)
-
需求:设计一个方法可以获取两个数的较大值,数据来自于参数
-
思路:
- ①定义一个方法,用于获取两个数字中的较大数
- ②使用分支语句分两种情况对两个数字的大小关系进行处理
- ③根据题设分别设置两种情况下对应的返回结果
- ④在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; } } }
5. 方法的注意事项
5.1 方法的注意事项(掌握)
-
方法不能嵌套定义
-
示例代码:
public class MethodDemo { public static void main(String[] args) { } public static void methodOne() { public static void methodTwo() { // 这里会引发编译错误!!! } } }
-
-
void表示无返回值,可以省略return,也可以单独的书写return,后面不加数据
-
示例代码:
public class MethodDemo { public static void main(String[] args) { } public static void methodTwo() { //return 100; 编译错误,因为没有具体返回值类型 return; //System.out.println(100); return语句后面不能跟数据或代码 } }
-
5.2 方法的通用格式(掌握)
-
格式:
public static 返回值类型 方法名(参数) { 方法体; return 数据 ; }
-
解释:
-
public static 修饰符,目前先记住这个格式
返回值类型 方法操作完毕之后返回的数据的数据类型
如果方法操作完毕,没有数据返回,这里写void,而且方法体中一般不写return
方法名 调用方法时候使用的标识
参数 由数据类型和变量名组成,多个参数之间用逗号隔开
方法体 完成功能的代码块
return 如果方法操作完毕,有数据返回,用于把数据返回给调用者
-
-
定义方法时,要做到两个明确
- 明确返回值类型:主要是明确方法操作完毕之后是否有数据返回,如果没有,写void;如果有,写对应的数据类型
- 明确参数:主要是明确参数的类型和数量
-
调用方法时的注意:
- void类型的方法,直接调用即可
- 非void类型的方法,推荐用变量接收调用
6. 方法重载
6.1 方法重载(理解)
-
方法重载概念
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同(这是核心)
-
注意:
- 重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
- 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载
-
正确范例:
public class MethodDemo { public static void fn(int a) { //方法体 } public static int fn(double a) { //方法体 } } public class MethodDemo { public static float fn(int a) { //方法体 } public static int fn(int a , int b) { //方法体 } }
-
错误范例:
public class MethodDemo { public static void fn(int a) { //方法体 } public static int fn(int a) { /*错误原因:重载与返回值无关*/ //方法体 } } public class MethodDemo01 { public static void fn(int a) { //方法体 } } public class MethodDemo02 { public static int fn(double a) { /*错误原因:这是两个类的两个fn方法*/ //方法体 } }
6.2 方法重载练习(掌握)
-
需求:使用方法重载的思想,设计比较两个整数是否相同的方法,兼容全整数类型(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; } }
7. 方法的参数传递
7.1 方法参数传递基本类型(理解)
-
测试代码:
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; } }
-
结论:
- 基本数据类型的参数,形式参数的改变,不影响实际参数
-
结论依据:
-
每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失
-
7.2 方法参数传递引用类型(理解)
-
测试代码:
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; } }
-
结论:
- 对于引用类型的参数,形式参数的改变,影响实际参数的值
-
结论依据:
-
引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果
-
7.3 数组遍历(应用)
-
需求:设计一个方法用于数组遍历,要求遍历的结果是在一行上的。例如:[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("]"); } }
7.4 数组最大值(应用)
-
需求:设计一个方法用于获取数组中元素的最大值
-
思路:
- ①定义一个数组,用静态初始化完成数组元素初始化
- ②定义一个方法,用来获取数组中的最大值,最值的认知和讲解我们在数组中已经讲解过了
- ③调用获取最大值方法,用变量接收返回结果
- ④把结果输出在控制台
-
代码:
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; } }
面向对象
1. 类和对象
1.0 类定义的注意事项
- java中可以有多个类,但是最多只有一个类的类名和文件名相同
- 如果一个类被public修饰,那该类的类名必须和文件名相同
- 一个java文件中最多只有一个类被public修饰
- 主方法所在类的类名一定要与文件名一致
1.1 类和对象的理解
客观存在的事物皆为对象 ,所以我们也常常说万物皆对象。
- 类
- 类的理解
- 类是对现实生活中一类具有共同属性和行为的事物的抽象
- 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
- 简单理解:类就是对现实事物的一种描述
- 类的组成
- 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
- 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
- 类的理解
- 类和对象的关系
- 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
- 对象:是能够看得到摸的着的真实存在的实体
- 简单理解:类是对事物的一种描述,对象则为具体存在的事物
1.2 类的定义
类的组成是由属性和行为两部分组成
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可)
类的定义步骤:
①定义类
②编写类的成员变量
③编写类的成员方法
public class 类名 {
// 成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2;
…
// 成员方法
方法1;
方法2;
}
示例代码:
/*
手机类:
类名:
手机(Phone)
成员变量:
品牌(brand)
价格(price)
成员方法:
打电话(call)
发短信(sendMessage)
*/
public class Phone {
//成员变量
String brand;
int price;
//成员方法
public void call() {
System.out.println("打电话");
}
public void sendMessage() {
System.out.println("发短信");
}
}
1.3 对象的使用
- 创建对象的格式:
- 类名 对象名 = 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();
}
}
1.4 学生对象-练习
- 需求:首先定义一个学生类,然后定义一个学生测试类,在学生测试类中通过对象完成成员变量和成员方法的使用
- 分析:
- 成员变量:姓名,年龄…
- 成员方法:学习,做作业…
- 示例代码:
![1](C:\TeachRes\Java2019\JavaSe01\day07\4-笔记\笔记图片资源包\1.png)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();
}
}
2. 对象内存图
-
变量,函数在栈内存;new的对象在堆内存。
-
对象存放在堆内存中
-
当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)
只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。
3. 成员变量和局部变量
3.1 成员变量和局部变量的区别
4. 封装
4.1 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(); } }
4.2 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()); } }
4.3 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);
}
}
4.4 this内存原理
-
this代表当前调用方法的引用,哪个对象调用的方法,this就代表哪一个对象
-
示例代码:
public class StudentDemo { public static void main(String[] args) { Student s1 = new Student(); s1.setName("林青霞"); Student s2 = new Student(); s2.setName("张曼玉"); } }
-
图解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BhFbt5LK-1645971216244)(images\6.png)]
4.5 封装思想
- 封装概述
是面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的 - 封装原则
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的getXxx()/setXxx()方法 - 封装好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
5. 构造方法(构造器)
5.1 构造方法概述
构造方法是一种特殊的方法
-
作用:创建对象 Student stu = new Student();
-
格式:
public class 类名{
修饰符 类名( 参数 ) {
}
}
-
功能:主要是完成对象数据的初始化
-
示例代码:
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();
}
}
5.2 构造方法的注意事项
- 构造方法的创建
如果没有定义构造方法,系统将给出一个默认的无参数构造方法
如果定义了构造方法,系统将不再提供默认的构造方法,此时就需要自己写一个无参数构造器
- 构造方法的重载
如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
- 推荐的使用方式
无论是否使用,都手工书写无参数构造方法
- 重要功能!
可以使用带参构造,为成员变量进行初始化
- 示例代码
/*
学生类
*/
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();
}
}
5.3 标准类制作
- 需求:定义标准学生类,要求分别使用空参和有参构造方法创建对象,空参创建的对象通过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();
}
}
标准JAVABean
- 也可以理解成实体类,其对象可以用于在程序中封装数据。
- 标准JavaBean须满足如下要求:
⚫ 成员变量使用 private 修饰。
⚫ 提供每一个成员变量对应的 setXxx() / getXxx()。
⚫ 必须提供一个无参构造器。
常用API(string,ArrayList)
1.API
1.1API概述
-
什么是API
API (Application Programming Interface) :应用程序编程接口
-
java中的API
指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们可以通过帮助文档来学习这些API如何使用。
1.2如何使用API帮助文档
- 打开帮助文档
- 找到索引选项卡中的输入框
- 在输入框中输入Random
- 看类在哪个包下
- 看类的描述
- 看构造方法
- 看成员方法
2.String类
2.1String类概述
String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!
2.2String类的特点
- 字符串不可变,它们的值在创建后不能被更改(修改后会指向新对象,原来的对象不会改变)
- 虽然 String 的值是不可变的,但是它们可以被共享
- 字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
2.3String类的构造方法
-
常用的构造方法
方法名 说明 public String() 创建一个空白字符串对象,不含有任何内容 public String(char[] chs) 根据字符数组的内容,来创建字符串对象 public String(byte[] bys) 根据字节数组的内容,来创建字符串对象 String s = “abc”; 直接赋值的方式创建字符串对象,内容就是abc -
示例代码
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); } }
2.4创建字符串对象两种方式的区别
-
通过构造方法创建
char[] chs = {'a', 'b', 'c'}; String s2 = new String(chs);
通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同,new出来在堆内存。不同对象的地址不同。
-
直接赋值方式创建
String name = "阿柠";
以“ ”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护。(字符串常量池,也是在堆内存中)。不同对象的地址相同。
2.5字符串的内容比较
2.5.1==号的作用
- 比较基本数据类型:比较的是具体的值
- 比较引用数据类型:比较的是对象地址值
2.5.2equals方法的作用(比较字符串)
-
方法介绍
public boolean equals(String s) 比较两个字符串内容是否相同、区分大小写 public boolean equalsIgnoreCase (String anotherString) 将此字符串与指定对象进行比较,忽略大小写比较字符串。只关心字符内容是否一致!
-
示例代码
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)); } }
2.6用户登录案例
2.6.1案例需求
已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示
2.6.2代码实现
/*
思路:
1:已知用户名和密码,定义两个字符串表示即可
2:键盘录入要登录的用户名和密码,用 Scanner 实现
3:拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。字符串的内容比较,用equals() 方法实现
4:用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环
*/
public class StringTest01 {
public static void main(String[] args) {
//已知用户名和密码,定义两个字符串表示即可
String username = "itheima";
String password = "czbk";
//用循环实现多次机会,这里的次数明确,采用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) + "次机会");
}
}
}
}
}
2.7遍历字符串案例
2.7.1案例需求
键盘录入一个字符串,使用程序实现在控制台遍历该字符串
2.7.2代码实现
/*
思路:
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));
}
}
}
2.8统计字符次数案例
2.8.1案例需求
键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
2.8.2代码实现
/*
思路:
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 + "个");
}
}
2.9字符串拼接案例
2.9.1案例需求
定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,
并在控制台输出结果。例如,数组为 int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]
2.9.2代码实现
/*
思路:
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;
}
}
2.10字符串反转案例
2.10.1案例需求
定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果
例如,键盘录入 abc,输出结果 cba
2.10.2代码实现
/*
思路:
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;
}
}
2.11帮助文档查看String常用方法
方法名 | 说明 |
---|---|
public boolean equals(Object anObject) | 比较字符串的内容,严格区分大小写(用户名和密码) |
public char charAt(int index) | 返回指定索引处的 char 值 |
public int length() | 返回此字符串的长度 |
public char[] toCharArray() | 将当前字符串转换成字符数组返回 |
public String substring(int beginIndex, int endIndex) | 根据开始和结束索引进行截取,得到新的字符串(包前不包后) |
public String substring(int beginIndex) | 从传入的索引处截取,截取到末尾,得到新的字符串 |
public String replace(CharSequence target, CharSequence replacement) | 使用新值,将字符串中的旧值替换,得到新的字符串 |
public String[] split(String regex) | 根据传入的规则切割字符串,得到字符串数组返回(字符串按逗号分割啊之类的) |
public boolean cintains(charsequences s) | 是否包含某一序列 |
3.ArrayList
3.0 泛型
- 泛型概述
⚫ ArrayList< E >:其实就是一个泛型类,可以在编译阶段约束集合对象只能操作某种数据类型。 - 举例:
⚫ ArrayList< String > :此集合只能操作字符串类型的元素。
⚫ ArrayList< Integer >:此集合只能操作整数类型的元素。
注意:集合中只能存储引用类型,不支持基本数据类型。
3.1ArrayList类概述
-
什么是集合
提供一种存储空间可变的存储模型,存储的数据容量可以发生改变。(数组定义完成后,类型确定,长度固定)
-
ArrayList集合的特点
底层是数组实现的,长度可以变化
-
泛型的使用
用于约束集合中存储元素的数据类型
3.2ArrayList类常用方法
3.2.1构造方法
方法名 | 说明 |
---|---|
public ArrayList() | 创建一个空的集合对象 |
3.2.2成员方法
方法名 | 说明 |
---|---|
public boolean remove(Object o) | 删除指定的元素,返回删除是否成功 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
public boolean add(E e) | 将指定的元素追加到此集合的末尾 |
public void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
3.2.3示例代码
public class ArrayListDemo02 {
public static void main(String[] args) {
//创建集合
ArrayList<String> array = new ArrayList<String>();
//ArrayList<Integer> array = new ArrayList<>(); 写int是不支持的
//添加元素
array.add("hello");
array.add("world");
array.add("java");
//public boolean remove(Object o):删除指定的元素,返回删除是否成功
// System.out.println(array.remove("world"));
// System.out.println(array.remove("javaee"));
//public E remove(int index):删除指定索引处的元素,返回被删除的元素
// System.out.println(array.remove(1));
//IndexOutOfBoundsException
// System.out.println(array.remove(3));
//public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
// System.out.println(array.set(1,"javaee"));
//IndexOutOfBoundsException
// System.out.println(array.set(3,"javaee"));
//public E get(int index):返回指定索引处的元素
// System.out.println(array.get(0));
// System.out.println(array.get(1));
// System.out.println(array.get(2));
//System.out.println(array.get(3)); //?????? 自己测试
//public int size():返回集合中的元素的个数
System.out.println(array.size());
//输出集合
System.out.println("array:" + array);
}
}
3.3ArrayList存储字符串并遍历
3.3.1案例需求
创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
3.3.2代码实现
/*
思路:
1:创建集合对象
2:往集合中添加字符串对象
3:遍历集合,首先要能够获取到集合中的每一个元素,这个通过get(int index)方法实现
4:遍历集合,其次要能够获取到集合的长度,这个通过size()方法实现
5:遍历集合的通用格式
*/
public class ArrayListTest01 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> array = new ArrayList<String>();
//往集合中添加字符串对象
array.add("刘正风");
array.add("左冷禅");
array.add("风清扬");
//遍历集合,其次要能够获取到集合的长度,这个通过size()方法实现
// System.out.println(array.size());
//遍历集合的通用格式
for(int i=0; i<array.size(); i++) {
String s = array.get(i);
System.out.println(s);
}
}
}
3.4ArrayList存储学生对象并遍历
3.4.1案例需求
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
3.4.2代码实现
/*
思路:
1:定义学生类
2:创建集合对象
3:创建学生对象
4:添加学生对象到集合中
5:遍历集合,采用通用遍历格式实现
*/
public class ArrayListTest02 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> array = new ArrayList<>();
//创建学生对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("风清扬", 33);
Student s3 = new Student("张曼玉", 18);
//添加学生对象到集合中
array.add(s1);
array.add(s2);
array.add(s3);
//遍历集合,采用通用遍历格式实现
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
}
3.5ArrayList存储学生对象并遍历升级版
3.5.1案例需求
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
学生的姓名和年龄来自于键盘录入
3.5.2代码实现
/*
思路:
1:定义学生类,为了键盘录入数据方便,把学生类中的成员变量都定义为String类型
2:创建集合对象
3:键盘录入学生对象所需要的数据
4:创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
5:往集合中添加学生对象
6:遍历集合,采用通用遍历格式实现
*/
public class ArrayListTest {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> array = new ArrayList<Student>();
//为了提高代码的复用性,我们用方法来改进程序
addStudent(array);
addStudent(array);
addStudent(array);
//遍历集合,采用通用遍历格式实现
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
/*
两个明确:
返回值类型:void
参数:ArrayList<Student> array
*/
public static void addStudent(ArrayList<Student> array) {
//键盘录入学生对象所需要的数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生姓名:");
String name = sc.nextLine();
System.out.println("请输入学生年龄:");
String age = sc.nextLine();
//创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
Student s = new Student();
s.setName(name);
s.setAge(age);
//往集合中添加学生对象
array.add(s);
}
}
IDEA项目结构
面向对象进阶
1. static
- static是静态的意思,可以修饰成员变量和成员方法。
- static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。static是静态的意思,可以修饰成员变量和成员方法。
1.1 成员变量分类
- 静态成员变量(有static修饰,属于类,内存中加载一次): 常表示如在线人数信息、等需要被共享的信息,可以被共享访问。
public class User {
// 静态成员变量
public static String onlineNumber= 161;}
//类名.静态成员变量。(推荐)用对象去访问不推荐
- 实例成员变量(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。
public class User {
public static String onlineNumber= 161;
// 实例成员变量
private String name;
private int age;
…}
//对象.实例成员变量。
1.2 static修饰成员变量的内存原理
1.3 static修饰成员方法的基本用法
-
成员方法的分类:
- 静态成员方法(有static修饰,属于类),建议用类名访问,也可以用对象访问。
- 实例成员方法(无static修饰,属于对象),只能用对象触发访问。
-
使用场景
表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。 -
静态方法需要类名去访问,但是同一个类中访问静态成员,可以省略类名不写。
1.4 static修饰成员方法的内存原理
1.5 static访问注意实现
- 静态方法只能访问静态的成员,不可以直接访问实例成员。(可以创建对象去访问)
- 实例方法可以访问静态的成员,也可以访问实例成员。
- 静态方法中是不可以出现this关键字的。
1.6 static应用知识:代码块
-
代码块概述
- 代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。
- 在Java类下,使用 { } 括起来的代码被称为代码块 。
-
代码块分为
- 静态代码块:
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。 - 构造代码块(了解,用的少):
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
使用场景:初始化实例资源。
- 静态代码块:
-
静态代码块的作用是什么?
如果要在启动系统时对数据进行初始化。
建议使用静态代码块完成数据的初始化操作,代码优雅。
1.7 static应用知识:单例设计模式
-
什么是设计模式(Design pattern)
- 开发中经常遇到一些问题,一个问题通常有n种解法的,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
- 设计模式有20多种,对应20多种软件开发中会遇到的问题,学设计模式主要是学2点:
- 第一:这种模式用来解决什么问题。
- 第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
-
单例模式
- 可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
- 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。
-
饿汉单例设计模式
- 在用类获取对象的时候,对象已经提前为你创建好了。
/** a、定义一个单例类 * /public class SingleInstance { /** c.定义一个静态变量存储一个对象即可 :属于类,与类一起加载一次 */ public static SingleInstance instance = new SingleInstance ();//静态的量在内存中只会加载一次 /** b.单例必须私有构造器*/ private SingleInstance (){ System.out.println("创建了一个对象"); }}
- 设计步骤:
定义一个类,把构造器私有。
定义一个静态变量存储一个对象。
-
懒汉单例模式
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
/** 定义一个单例类 */
class SingleInstance{
/** 定义一个静态变量存储一个对象即可 :属于类,与类一起加载一次 */
public static SingleInstance instance ; // null
/** 单例必须私有构造器*/
private SingleInstance(){}
/** 必须提供一个方法返回一个单例对象 */
public static SingleInstance getInstance(){
if(instance = null){
instance = new SingleInstance;
}
return instance;
}}
- 设计步骤:
- 定义一个类,把构造器私有。
- 定义一个静态变量存储一个对象。
- 提供一个返回单例对象的方法
2.继承
2.1 什么是继承?
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
public class Student extends People {}
Student称为子类(派生类),People称为父类(基类 或超类)。
-
使用继承的好处
当子类继承父类后,就可以直接使用父类公共的属性和方法了。因此,用好这个技术可以很好的我们提高代码的复用性
2.2继承设计规范
子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的的属性和行为应该定义在子类自己里面。
2.3 继承的特点
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
- 子类可以继承父类的私有成员,但不能直接访问。
- 子类是否可以继承父类的静态成员:有争议
- Java是单继承模式:一个类只能继承一个直接父类。
- Java不支持多继承、但是支持多层继承。
- Java中所有的类都是Object类的子类。
2.4继承后:成员变量、成员方法的访问特点
-
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错。
-
如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
可以通过super关键字,指定访问父类的成员。
2.5 继承后:方法重写
- 什么是方法重写?
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。 - 方法重写的应用场景
当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
子类可以重写父类中的方法。 - @Override重写注解
- @Override是放在重写后的方法上,作为重写是否正确的校验注解。
- 加上该注解后如果重写错误,编译阶段会出现错误提示。
- 建议重写方法都加@Override注解,代码安全,优雅!
- 方法重写注意事项和要求:
- 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
- 私有方法不能被重写。
- 子类重写父类方法时,访问权限必须大于或者等于父类 (暂时了解 :缺省 < protected < public)
- 子类不能重写父类的静态方法,如果重写会报错的。
2.6 继承后:子类构造器的特点
-
子类继承父类后构造器的特点:
子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。 -
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。 -
怎么调用父类构造器的?
子类构造器的第一行语句默认都是:super(),不写也存在。(创建子类的时候会先调用父类的无参构造器,再调用子类的无参构造器)
2.7 继承后:子类构造器访问父类有参构造器
- super调用父类有参数构造器的作用:
初始化继承自父类的数据。 - 如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?
会报错。因为子类默认是调用父类无参构造器的。 - 如何解决?
子类构造器中可以通过书写 super(…),手动调用父类的有参数构造器
2.8 this、super使用总结
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量(访问本类成员变量) | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构器 |
super | super.成员变量(访问父类成员变量) | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造器 |
3. 包
-
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护。
- 建包的语法格式:package 公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
package com.itheima.javabean; public class Student { }
- 建包语句必须在第一行,一般IDEA工具会帮助创建
-
导包
- 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:import 包名.类名;
- 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
4. 权限修饰符
-
什么是权限修饰符?
权限修饰符:是用来控制一个成员能够被访问的范围的。
可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。 -
权限修饰符的分类和具体作用范围:
权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected - > public )
修饰符 同一 个类中 同一个包中其他类 不同包下的子类 不同包下的无关类 private √ 缺省 √ √ protected √ √ √ public √ √ √ √ -
自己定义成员(方法,成员变量,构造器等)一般满足如下要求:
- 成员变量一般私有。
- 方法一般公开。
- 如果该成员只希望本类访问,使用private修饰。
- 如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。
5. final
-
final的作用
final 关键字是最终的意思,可以修饰(方法,变量,类)
修饰方法:表明该方法是最终方法,不能被重写。
修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
修饰类:表明该类是最终类,不能被继承。 -
final修饰变量的注意
final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的
6.常量
-
常量是使用了public static final修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。
-
常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
-
常量命名规范:英文单词全部大写,多个单词下划线连接起来。
-
常量的执行原理
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
7. 枚举
-
枚举的概述
- 枚举是Java中的一种特殊类型
- 枚举的作用:“是为了做信息的标志和信息的分类”。
-
定义枚举类的格式:
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称。
}
enum Season{
SPRING , SUMMER , AUTUMN , WINTER;
}
- 枚举的特征:
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 枚举类相当于是多例模式。
8.抽象类
- 在Java中abstract是抽象的意思,如果一个类中的某个方法的具体实现不能确定,就可以申明成abstract修饰的抽象方法(不能写方法体了),这个类必须用abstract修饰,被称为抽象类。
public abstract class Animal{
public abstract void run();
}
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表);
}
-
抽象的使用总结与注意事项:
- 抽象类可以理解成类的不完整设计图,是用来被子类继承的。
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
-
抽象类的特征、注意事项小结
- 特征:
- 有得有失: 得到了抽象方法,失去了创建对象的能力。
- 抽象类为什么不能创建对象?
- 抽象类尚未实现方法,所以不能创建对象。
- 类有的成员(成员变量、方法、构造器)抽象类都具备
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
- 不能用abstract修饰变量、代码块、构造器。
- 特征:
-
final和abstract是什么关系?
- 互斥关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
- 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写
-
抽象类的应用知识:模板方法模式
- 使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
- 模板方法模式实现步骤
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码。
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现。
- 模板方法我们是建议使用final修饰的,这样会更专业
- 模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法就失效了。
- 模板方法模式解决了什么问题?
- 极大的提高了代码的复用性
- 模板方法已经定义了通用结构,模板不能确定的定义成抽象方法。
- 使用者只需要关心自己需要实现的功能即可。
9. 接口
9.1 接口概述
接口就是体现规范,其中用抽象方法定义的一组行为规范,接口是更加彻底的抽象
9.2 接口定义
接口的格式如下:
接口用关键字interface来定义public interface 接口名 { // 常量 // 抽象方法}
- JDK8之前接口中只能是抽象方法和常量,没有其他成分了。
- 接口不能实例化。
- 接口中的成员都是public修饰的,写不写都是,因为规范的目的是为了公开化
9.3 接口的基本使用
- 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {}实现的关键字:implements从上面可以看出,接口可以被类单实现,也可以被类多实现。
-
接口实现的注意事项:
一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
9.4 接口与接口的关系:多继承
- 类和类的关系:单继承。
- 类和接口的关系:多实现。
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
9.5 JDK8开始接口新增方法
-
允许接口中直接定义带有方法体的方法:
-
第一种:默认方法
-
类似之前写的普通实例方法:必须用default修饰
默认会public修饰。需要用接口的实现类的对象来调用default void run(){ System.out.println("--开始跑--");}
-
-
第二种:静态方法
-
默认会public修饰,必须static修饰。
注意:接口的静态方法必须用本身的接口名来调用。static void inAddr(){ System.out.println("我们都在快乐的学习Java!");}
-
-
第三种:私有方法
-
就是私有的实例方法:,必须使用private修饰,从JDK 1.9才开始有的。
只能在本类中被其他的默认方法或者私有方法访问。private void go(){ System.out.println("--准备--");}
-
-
-
总结:
- JDK8开始后新增了那些方法?
- 默认方法:default修饰,实现类对象调用。
- 静态方法:static修饰,必须用当前接口名调用
- 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
他们都会默认被public修饰。
- JDK8开始后新增了那些方法?
9.6 接口的注意事项
1、接口不能创建对象
2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。
3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
4、一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
10.多态
-
什么是多态?
同类型的对象,执行同一个行为,会表现出不同的行为特征。 -
多态的常见形式
父类类型 对象名称 = new 子类构造器;接口 对象名称 = new 实现类构造器;
-
多态中成员访问特点
方法调用:编译看左边,运行看右边。
变量调用:编译看左边,运行也看左边。(多态侧重行为多态) -
多态的前提
有继承/实现关系;有父类引用指向子类对象;有方法重写。 -
多态的优势
- 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
- 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
-
多态下会产生的一个问题:
- 多态下不能使用子类的独有功能
-
多态下引用数据类型的类型转换
- 自动类型转换(从子到父):子类对象赋值给父类类型的变量指向
- 强制类型转换(从父到子):此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量
- 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
11.内部类
- 内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。
public class People{ // 内部类 public class Heart{ }}
- 内部类的使用场景、作用
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。
- 内部类通常可以方便访问外部类的成员,包括私有的成员。
- 内部类提供了更好的封装性,内部类本身就可以用private protectecd等修饰,封装性可以做更多控制。
11.1 匿名内部类
-
匿名内部类:
本质上是一个没有名字的局部内部类,定义在方法中、代码块中、等。
作用:方便创建子类对象,最终目的为了简化代码编写。 -
格式
new 类|抽象类名|或者接口名() { 重写方法;};Animal a = new Animal() { public void run() { }//不需要去创建子类,直接new抽象父类,把抽象方法重写即可运行。};
-
特点总结:
匿名内部类是一个没有名字的内部类。
匿名内部类写出来就会产生一个匿名内部类的对象。
匿名内部类的对象类型相当于是当前new的那个的类型的子类类型。
常用API
1.object类
-
Object类的作用:
Object类的方法是一切子类对象都可以直接使用的,所以我们要学习Object类的方法。
一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类。
1.1 tostring()
方法名 | 说明 |
---|---|
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址 |
- toString存在的意义
父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息!!
1.2 equals方法()
方法名 | 说明 |
---|---|
public boolean equals(Object o) | 默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false |
- equals存在的意义
父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则。
2.objects类
-
官方在进行字符串比较时,没有用字符串对象的的equals方法,而是选择了Objects的equals方法来比较。
使用Objects的equals方法在进行对象的比较会更安全。
Objects的常见方法:
方法名 | 说明 |
---|---|
public static boolean equals(Object a, Object b) | 比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常。再进行equals比较 |
public static boolean isNull(Object obj) | 判断变量是否为null ,为null返回true ,反之 |
3.StringBuilder
StringBuilder概述:
-
StringBuilder是一个可变的字符串类,我们可以把它看成是一个对象容器。
作用:提高字符串的操作效率,如拼接、修改等。 -
StringBuilder 构造器:
名称 | 说明 |
---|---|
public StringBuilder() | 创建一个空白的可变的字符串对象,不包含任何内容 |
public StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
- StringBuilder常用方法
方法名称 | 说明 |
---|---|
public StringBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
public int length() | 返回对象内容长度 |
public String toString() | 通过toString()就可以实现把StringBuilder转换为String |
4. math
-
Math类
包含执行基本数字运算的方法,Math类没有提供公开的构造器。
如何使用类中的成员呢?看类的成员是否都是静态的,如果是,通过类名就可以直接调用 -
Math 类的常用方法
方法名 | 说明 |
---|---|
public static int abs(int a) | 获取参数绝对值 |
public static double ceil(double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round(float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int值中的较大值 |
public static double pow(double a,double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double的随机值,范围[0.0,1.0) |
5.system
-
System 类概述
System也是一个工具类,代表了当前系统,提供了一些与系统相关的方法。
-
System 类的常用方法
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的 Java 虚拟机,非零表示异常终止 |
public static long currentTimeMillis() | 返回当前系统的时间毫秒值形式 |
public static void arraycopy(数据源数组, 起始索引, 目的地数组, 起始索引, 拷贝个数) | 数组拷贝 |
6. BigDecimal
-
BigDecimal作用
用于解决浮点型运算精度失真的问题 -
使用步骤
创建对象BigDecimal封装浮点型数据(最好的方式是调用方法) -
public static BigDecimal valueOf(double val): 包装浮点数成为BigDecimal对象。
-
BigDecima常用API
方法名 | 说明 |
---|---|
public BigDecimal add(BigDecimal b) | 加法 |
public BigDecimal subtract(BigDecimal b) | 减法 |
public BigDecimal multiply(BigDecimal b) | 乘法 |
public BigDecimal divide(BigDecimal b) | 除法 |
public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式) | 除法 |
7.data
-
Date 类概述
Date类代表当前所在系统的日期时间信息。
-
Date的构造器
名称 | 说明 |
---|---|
public Date() | 创建一个Date对象,代表的是系统当前此刻日期时间。 |
- Date的常用方法
名称 | 说明 |
---|---|
public long getTime() | 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数 |
8.SimpleDateFormat
-
作用
可以去完成日期时间的格式化操作
构造器 | 说明 |
---|---|
public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat,使用指定的格式(pattern里设置格式) |
格式化方法 | 说明 |
---|---|
public final String format(Date date) | 将日期格式化成日期/时间字符串 |
public final String format(Object time) | 将时间毫秒值式化成日期/时间字符串 |
9.calendar
-
Calendar概述
Calendar代表了系统此刻日期对应的日历对象。
Calendar是一个抽象类,不能直接创建对象。 -
Calendar日历类创建日历对象的方法:
方法名 | 说明 |
---|---|
public static Calendar getInstance() | 获取当前日历对象 |
- Calendar常用方法
方法名 | 说明 |
---|---|
public int get(int field) | 取日期中的某个字段信息。 |
public void set(int field,int value) | 修改日历的某个字段信息。 |
public void add(int field,int amount) | 为某个字段增加/减少指定的值 |
public final Date getTime() | 拿到此刻日期对象。 |
public long getTimeInMillis() | 拿到此刻时间毫秒值 |
包装类
- 其实就是8种基本数据类型对应的引用类型。
基本数据类型 | 引用数据类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
-
为什么提供包装类?
Java为了实现一切皆对象,为8种基本类型提供了对应的引用类型。
后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型。 -
自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量。
-
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量。
-
包装类的特有功能:
包装类的变量的默认值可以是null,容错率更高。
-
可以把基本类型的数据转换成字符串类型(用处不大)
- 调用toString()方法得到字符串结果。
- 调用Integer.toString(基本类型的数据)。
-
可以把字符串类型的数值转换成真实的数据类型(真的很有用)
- Integer.parseInt(“字符串类型的整数”) //valueof也可以实现
- Double.parseDouble(“字符串类型的小数”)。
-
Arrays类
-
数组操作工具类,专门用于操作数组元素的。
-
Arrays类的常用API:
方法名 | 说明 |
---|---|
public static String toString(类型[] a) | 返回数组的内容(字符串形式) |
public static void sort(类型[] a) | 对数组进行默认升序排序 |
public static < T > void sort(类型[] a, Comparator<? super T> c) | 使用比较器对象自定义排序 |
public static int binarySearch(int[] a, int key) | 二分搜索数组中的数据(数组必须提前有序),存在返回索引,不存在返回-1 |
-
Arrays类对于Comparator比较器的支持
-
Arrays类的排序方法
方法名 说明 public static void sort(类型[] a) 对数组进行默认升序排序 public static < T > void sort(类型[] a, Comparator<? super T> c) 使用比较器对象自定义排序
-
Integer[] ages1 = {34, 12, 42, 23}; /** 参数一:被排序的数组 必须是引用类型的元素 参数二:匿名内部类对象,代表了一个比较器对象。 */ Arrays.sort(ages1, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // 制定比较规则。升序// if(o1 > o2){// return 1;// }else if(o1 < o2){// return -1;// }// return 0; // return o1 - o2; // 默认升序 return o2 - o1; // 降序 } });
Student[] students = new Student[3]; students[0] = new Student("吴磊",23 , 175.5); students[1] = new Student("谢鑫",18 , 185.5); students[2] = new Student("王亮",20 , 195.5); System.out.println(Arrays.toString(students)); // Arrays.sort(students); // 直接运行奔溃 Arrays.sort(students, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // 自己指定比较规则 // return o1.getAge() - o2.getAge(); // 按照年龄升序排序! // return o2.getAge() - o1.getAge(); // 按照年龄降序排序!! // return Double.compare(o1.getHeight(), o2.getHeight()); // 比较浮点型可以这样写 升序 return Double.compare(o2.getHeight(), o1.getHeight()); // 比较浮点型可以这样写 降序 } }); System.out.println(Arrays.toString(students));
Lambda表达式
-
Lambda概述
Lambda表达式是JDK 8开始后的一种新语法形式。
作用:简化匿名内部类的代码写法。
(匿名内部类被重写方法的形参列表) -> {被重写方法的方法体代码。}注:-> 是语法形式,无实际含义
- Lambda表达式只能简化函数式接口的匿名内部类的写法形式
public class LambdaDemo1 { public static void main(String[] args) { goSwimming( new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧~"); } } ); } public static void goSwimming(Swimming swimming) { swimming.swim(); }}public class LambdaDemo1 { public static void main(String[] args) { goSwimming( () -> { System.out.println("铁汁, 我们去游泳吧~") } ); }public static void goSwimming(Swimming swimming) { swimming.swim(); }}
public static void main(String[] args) { Integer[] ages = {66, 33, 22, 55, 88}; Arrays.sort(ages, new Comparator<Integer>(){ @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); System.out.println("内容:" + Arrays.toString(ages));}Arrays.sort(ages, (Integer o1, Integer o2) -> { return o2 - o1;});
JButton btn = new JButton("登录"); // 给登录按钮绑定点击事件监听器 btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("登录一下~~~"); } });btn.addActionListener( (ActionEvent e) -> { System.out.println("登录一下~~~");});
- Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
参数类型可以省略不写。
如果只有一个参数,参数类型可以省略,同时()也可以省略。
如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号!
如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"不写
集合
1.集合概述
集合和数组都是容器
-
数组的特点
数组定义完成并启动后,类型确定、长度固定
适合元素的个数和类型确定的业务场景,不适合做需要增删数据操作。
1、数组和集合的元素存储的个数问题。
数组定义后类型确定,长度固定
集合类型可以不固定,大小是可变的。
2、数组和集合存储元素的类型问题。
数组可以存储基本类型和引用类型的数据。
集合只能存储引用数据类型的数据。
3、数组和集合适合的场景
数组适合做数据个数和类型确定的场景。
集合适合做数据个数不确定,且要做增删元素的场景。 -
集合类体系结构
- Collection单列集合,每个元素(数据)只包含一个值。
- Map双列集合,每个元素包含两个值(键值对)。
-
Collection集合特点
List系列集合:添加的元素是有序、可重复、有索引。
ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
HashSet: 无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。
- 集合对于泛型的支持
集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>();Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写
2.Collection集合
-
Collection集合
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。 -
Collection API如下:
方法名称 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
2.0 Collection集合的遍历方式
2.0.1 迭代器
遍历就是一个一个的把容器中的元素访问一遍。
迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
- Collection集合获取迭代器
方法名称 | 说明 |
---|---|
Iterator iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引 |
- Iterator中的常用方法
方法名称 | 说明 |
---|---|
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界。 |
Iterator<String> it = lists.iterator(); while(it.hasNext()){ String ele = it.next(); System.out.println(ele);}
2.0.2 foreach/增强for循环
- 增强for循环
for(元素数据类型 变量名 : 数组或者Collection集合) { //在此处使用变量即可,该变量就是元素}
增强for循环:既可以遍历集合也可以遍历数组。
Collection<String> list = new ArrayList<>();for(String ele : list) {System.out.println(ele);}
2.0.3 lambda表达式
- 得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。
Collection结合Lambda遍历的API
方法名称 | 说明 |
---|---|
default void forEach(Consumer<? super T> action): | 结合lambda遍历集合 |
Collection<String> lists = new ArrayList<>();...lists.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});lists.forEach(s -> {System.out.println(s); });
2.1Collection集合存储自定义类型的对象
public class Movie {private String name;private double score;private String acotr;public Movie(String name, double score, String acotr) {this.name = name;this.score = score;this.acotr = acotr;}// ... getter + setter}
public class SystemDemo { public static void main(String[] args) { Collection <Movie> movies = new ArrayList<>(); movies.add(new Movie(“《肖生克的救赎》”, 9.7 , “罗宾斯”)); movies.add(new Movie(“《霸王别姬》”, 9.6 , “张国荣、张丰毅”)); movies.add(new Movie(“《阿甘正传》”, 9.5 , “汤姆.汉克斯")); System.out.println(movies); for (Movie movie : movies) { System.out.println("片名:" + movie.getName()); System.out.println("评分:" + movie.getScore()); System.out.println("主演:" + movie.getAcotr()); } }}
2.2 List
-
List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复 -
List集合特有方法
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
-
List的实现类的底层原理
ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢。
LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的 -
LinkedList的特点
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API -
LinkedList集合的特有功能
方法名称 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
2.3 set
-
Set系列集合特点
- 无序:存取顺序不一致(只会无序一次)
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
-
Set集合实现类特点
- HashSet : 无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:排序、不重复、无索引。
-
HashSet底层原理
- 同样的内容只会添加一个,不会重复添加。
- HashSet集合底层采取哈希表存储的数据。
- 哈希表是一种对于增删改查数据性能都较好的结构。
-
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。 -
LinkedHashSet集合概述和特点
有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。 -
TreeSet集合概述和特点
不重复、无索引、可排序
可排序:按照元素的大小默认升序(有小到大)排序。
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
3. MAP集合
3.1MAP集合概述
Map集合是一种双列集合,每个元素包含两个数据。
Map集合的每个元素的格式:key=value(键值对元素)。
Map集合也被称为“键值对集合”。
Collection集合的格式: [元素1,元素2,元素3..]Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , ...}
3.2MAP集合特点
- Map集合体系特点
Map集合的特点都是由键决定的。
Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
Map集合后面重复的键对应的值会覆盖前面重复键的值。
Map集合的键值对都可以为null。
- Map集合实现类特点
HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
TreeMap:元素按照建是排序,不重复,无索引的,值不做要求。
3.3 MAP集合的常用API
-
Map集合
Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的。 -
Map API如下
方法名称 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
3.4 Map集合的遍历方式一:键找值
-
先获取Map集合的全部键的Set集合。
遍历键的Set集合,然后通过键提取对应值。 -
键找值涉及到的API:
方法名称 | 说明 |
---|---|
Set< K > keySet() | 获取所有键的集合 |
V get(Object key) | 根据键获取值 |
// 1、键找值:第一步:先拿到集合的全部键。 Set<String> keys = maps.keySet(); // 2、第二步:遍历每个键,根据键提取值 for (String key : keys) { int value = maps.get(key); System.out.println(key + "===>" + value); }
3.5 Map集合的遍历方式二:键值对
-
先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。
遍历Set集合,然后提取键以及提取值。 -
键值对涉及到的API:
方法名称 | 说明 |
---|---|
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
K getKey() | 获得键 |
V getValue() | 获取值 |
// 1、把Map集合转换成Set集合 Set<Map.Entry<String, Integer>> entries = maps.entrySet(); // 2、开始遍历 for(Map.Entry<String, Integer> entry : entries){ String key = entry.getKey(); int value = entry.getValue(); System.out.println(key + "====>" + value); }
3.6 Map集合的遍历方式三:Lambda
- Map结合Lambda遍历的API
方法名称 | 说明 |
---|---|
default void forEach(BiConsumer<? super K, ? super V> action) | 结合lambda遍历Map集合 |
public class MapDemo03 { public static void main(String[] args) { Map<String , Integer> maps = new HashMap<>(); // 1.添加元素: 无序,不重复,无索引。 maps.put("娃娃",30); maps.put("iphoneX",100);// Map集合后面重复的键对应的元素会覆盖前面重复的整个元素! maps.put("huawei",1000); maps.put("生活用品",10); maps.put("手表",10); System.out.println(maps); // maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}// maps.forEach(new BiConsumer<String, Integer>() {// @Override// public void accept(String key, Integer value) {// System.out.println(key + "--->" + value);// }// }); maps.forEach((k, v) -> { System.out.println(k + "--->" + v); }); }}
3.7 Map集合的实现类HashMap
- HashMap的特点
HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引
没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
3.8 Map集合的实现类LinkedHashMap
- LinkedHashMap集合概述和特点
由键决定:有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
3.9 Map集合的实现类TreeMap
- TreeMap集合概述和特点
由键决定特性:不重复、无索引、可排序
可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
TreeMap跟TreeSet一样底层原理是一样的。
- TreeMap集合自定义排序规则有2种
类实现Comparable接口,重写比较规则。
集合自定义Comparator比较器对象,重写比较规则。
4. 不可变集合
-
什么是不可变集合?
不可变集合,就是不可被修改的集合。
集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错。 -
为什么创建不可变集合?
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
或者当集合对象被不可信的库调用时,不可变形式是安全的。 -
如何创建不可变集合?
在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
方法名称 | 说明 |
---|---|
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K , V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
这个集合不能添加,不能删除,不能修改。
Stream流
-
什么是Stream流?
在Java 8中,得益于Lambda所带来的函数式编程, 引入了一个全新的Stream流概念。
目的:用于简化集合和数组操作的API。
异常处理
-
什么是异常?
异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
比如:数组索引越界、空指针异常、 日期格式化异常,等… -
异常体系
-
Error:
系统级别问题、JVM退出等,代码无法控制。 -
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
RuntimeException及其子类:运行时异常,编译阶段不会报错。 (空指针异常,数组索引越界异常)
除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。 (日期格式化异常)
日志框架
- 日志技术具备的优势
可以将系统执行的信息选择性的记录到指定的位置(控制台、文件中、数据库中)。
可以随时以开关的形式控制是否记录日志,无需修改源代码。
输出语句 | 日志技术 | |
---|---|---|
输出位置 | 只能是控制台 | 可以将日志信息写入到文件或者数据库中 |
取消日志 | 需要修改代码,灵活性比较差 | 不需要修改代码,灵活性比较好 |
多线程 | 性能较差 | 性能较好 |
-
日志的规范是什么,常见的有几种形式。
日志规范大多是一些接口,提供给实现框架去设计的。
常见的规范是:
Commons Logging
Simple Logging Facade for Java -
日志的实现框架有哪些常见的?
Log4J
Logback(我们重点学习的,其他的都大同小异)
Logback
-
Logback主要分为三个技术模块:
logback-core: logback-core 模块为其他两个模块奠定了基础,必须有。
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API。
logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能 -
使用Logback需要使用哪几个模块,各自的作用是什么。
slf4j-api:日志规范
logback-core:基础模块。
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API
File
- File类创建对象
方法名称 | 说明 |
---|---|
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 从父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 |
- File对象可以定位文件和文件夹
File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。
File file1 = new File(“D:\\itheima\\a.txt”);
- File类的常用API
File类的判断文件类型、获取文件信息功能
方法名称 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为文件夹 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或文件夹的名称 |
public long lastModified() | 返回文件最后修改的时间毫秒值 |
- File类创建文件的功能
方法名称 | 说明 |
---|---|
public boolean createNewFile() | 创建一个新的空的文件 |
public boolean mkdir() | 只能创建一级文件夹 |
public boolean mkdirs() | 可以创建多级文件夹 |
- File类删除文件的功能
方法名称 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或空文件夹 |
-
delete方法默认只能删除文件和空文件夹。
delete方法直接删除不走回收站 -
File类的遍历功能
方法名称 | 说明 |
---|---|
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 |
public File[] listFiles()(常用) | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) |
-
listFiles方法注意事项:
当调用者不存在时,返回null
当调用者是一个文件时,返回null
当调用者是一个空文件夹时,返回一个长度为0的数组
当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
当调用者是一个需要权限才能进入的文件夹时,返回null
IO流
多线程
线程(thread)是一个程序内部的一条执行路径。
-
我们之前启动程序执行后,main方法的执行其实就是一条单独的执行路径。
-
多线程是指从软硬件上实现多条执行流程的技术。
1.多线程的创建
方式一:继承Thread类
-
Thread类
Java是通过java.lang.Thread 类来代表线程的。
按照面向对象的思想,Thread类应该提供了实现多线程的方式。 -
多线程的实现方案一:继承Thread类
定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
创建MyThread类的对象
调用线程对象的start()方法启动线程(启动后还是执行run方法的) -
为什么不直接调用了run方法,而是调用start启动线程。
直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
只有调用start方法才是启动一个新的线程执行。 -
把主线程任务放在子线程之前了。
这样主线程一直是先跑完的,相当于是一个单线程的效果了
-
方式一是如何实现多线程的?
-
继承Thread类
-
重写run方法
-
创建线程对象
-
调用start()方法启动。
-
-
优缺点是什么?
优点:编码简单
缺点:存在单继承的局限性,线程类继承Thread后,不能继承其他类,不便于扩展。
/** 目标:多线程的创建方式一:继承Thread类实现。 */public class ThreadDemo1 { public static void main(String[] args) { // 3、new一个新线程对象 Thread t = new MyThread(); // 4、调用start方法启动线程(执行的还是run方法) t.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程执行输出:" + i); } }}/** 1、定义一个线程类继承Thread类 */class MyThread extends Thread{ /** 2、重写run方法,里面是定义线程以后要干啥 */ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("子线程执行输出:" + i); } }}
方式二:实现Runnable接口
- 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable任务对象
- 把MyRunnable任务对象交给Thread处理。
- 调用线程对象的start()方法启动线程
- Thread的构造器
构造器 | 说明 |
---|---|
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
public Thread(Runnable target ,String name ) | 封装Runnable对象成为线程对象,并指定线程名称 |
package com.itheima.d1_create;/** 目标:学会线程的创建方式二,理解它的优缺点。 */public class ThreadDemo2 { public static void main(String[] args) { // 3、创建一个任务对象 Runnable target = new MyRunnable(); // 4、把任务对象交给Thread处理 Thread t = new Thread(target); // Thread t = new Thread(target, "1号"); // 5、启动线程 t.start(); for (int i = 0; i < 10; i++) { System.out.println("主线程执行输出:" + i); } }}/** 1、定义一个线程任务类 实现Runnable接口 */class MyRunnable implements Runnable { /** 2、重写run方法,定义线程的执行任务的 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("子线程执行输出:" + i); } }}
- 第二种方式的优点
优点:线程任务类只是实现了Runnale接口,可以继续继承和实现。
缺点:如果线程有执行结果是不能直接返回的。 - 匿名内部类形式:
package com.itheima.d1_create;/** 目标:学会线程的创建方式二(匿名内部类方式实现,语法形式) */public class ThreadDemo2Other { public static void main(String[] args) { Runnable target = new Runnable() {//匿名内部类实现 @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("子线程1执行输出:" + i); } } }; Thread t = new Thread(target); t.start(); new Thread(new Runnable() {//上面代码的简化 @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("子线程2执行输出:" + i); } } }).start(); new Thread(() -> {//兰姆达表达式 for (int i = 0; i < 10; i++) { System.out.println("子线程3执行输出:" + i); } }).start(); for (int i = 0; i < 10; i++) { System.out.println("主线程执行输出:" + i); } }}
方式三:JDK 5.0新增:实现Callable接口
-
前2种线程创建方式都存在一个问题:
他们重写的run方法均不能直接返回结果。
不适合需要返回线程执行结果的业务场景。 -
怎么解决这个问题呢?
JDK 5.0提供了Callable和FutureTask来实现。
这种方式的优点是:可以得到线程执行的结果。 -
1、得到任务对象
定义类实现Callable接口,重写call方法,封装要做的事情。
用FutureTask把Callable对象封装成线程任务对象。
2、把线程任务对象交给Thread处理。
3、调用Thread的start方法启动线程,执行任务
4、线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果
package com.itheima.d1_create;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;/** 目标:学会线程的创建方式三:实现Callable接口,结合FutureTask完成。 */public class ThreadDemo3 { public static void main(String[] args) { // 3、创建Callable任务对象 Callable<String> call = new MyCallable(100); // 4、把Callable任务对象 交给 FutureTask 对象 // FutureTask对象的作用1: 是Runnable的对象(实现了Runnable接口),可以交给Thread了 // FutureTask对象的作用2: 可以在线程执行完毕之后通过调用其get方法得到线程执行完成的结果 FutureTask<String> f1 = new FutureTask<>(call); // 5、交给线程处理 Thread t1 = new Thread(f1); // 6、启动线程 t1.start(); Callable<String> call2 = new MyCallable(200); FutureTask<String> f2 = new FutureTask<>(call2); Thread t2 = new Thread(f2); t2.start(); try { // 如果f1任务没有执行完毕,这里的代码会等待,直到线程1跑完才提取结果。 String rs1 = f1.get(); System.out.println("第一个结果:" + rs1); } catch (Exception e) { e.printStackTrace(); } try { // 如果f2任务没有执行完毕,这里的代码会等待,直到线程2跑完才提取结果。 String rs2 = f2.get(); System.out.println("第二个结果:" + rs2); } catch (Exception e) { e.printStackTrace(); } }}/** 1、定义一个任务类 实现Callable接口 应该申明线程任务执行完毕后的结果的数据类型 */class MyCallable implements Callable<String>{ private int n; public MyCallable(int n) { this.n = n; } /** 2、重写call方法(任务方法) */ @Override public String call() throws Exception { int sum = 0; for (int i = 1; i <= n ; i++) { sum += i; } return "子线程执行的结果是:" + sum; }}
总结对比
方式 | 优点 | 缺点 |
---|---|---|
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 扩展性较差,不能再继承其他的类,不能返回线程执行的结果 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。 | 编程相对复杂,不能返回线程执行的结果 |
实现Callable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果 | 编程相对复杂 |
Thread的常用方法
-
当有很多线程在执行的时候,我们怎么去区分这些线程呢?
此时需要使用Thread的常用方法:getName()、setName()、currentThread()等。 -
Thread常用方法、构造器
方法名称 | 说明 |
---|---|
String getName() | 获取当前线程的名称,默认线程名称是Thread-索引 |
void setName(String name) | 设置线程名称 |
public static Thread currentThread(): | 返回对当前正在执行的线程对象的引用 |
public static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒。 |
public void run() | 线程任务方法 |
public void start() | 线程启动方法 |
构造器 | 说明 |
---|---|
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 把Runnable对象交给线程对象 |
public Thread(Runnable target ,String name ) | 把Runnable对象交给线程对象,并指定线程名称 |
package com.itheima.d2_api;/** 目标:线程的API */public class ThreadDemo01 { // main方法是由主线程负责调度的 public static void main(String[] args) { Thread t1 = new MyThread("1号"); // t1.setName("1号"); t1.start(); System.out.println(t1.getName()); Thread t2 = new MyThread("2号"); // t2.setName("2号"); t2.start(); System.out.println(t2.getName()); // 哪个线程执行它,它就得到哪个线程对象(当前线程对象) // 主线程的名称就叫main Thread m = Thread.currentThread(); System.out.println(m.getName()); m.setName("最牛的线程"); for (int i = 0; i < 5; i++) { System.out.println( m.getName() + "输出:" + i); } }}
线程安全
- 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。
线程同步
-
线程同步的核心思想
加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来
方式一:同步代码块
作用:把出现线程安全问题的核心代码给上锁。
原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。
synchronized(同步锁对象) { 操作共享资源的代码(核心代码)}
package com.itheima.d4_thread_synchronized_code;/** 账户类:余额,卡号 */public class Account { private String cardId; private double money; // 余额 关键信息 public Account() { } public Account(String cardId, double money) { this.cardId = cardId; this.money = money; } public String getCardId() { return cardId; } public void setCardId(String cardId) { this.cardId = cardId; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; }// // 100个线程人// public static void run(){// synchronized (Account.class){ }// } /** 小明 小红 */ public void drawMoney(double money) { // 1、拿到是谁来取钱 String name = Thread.currentThread().getName(); // 同步代码块 // 小明 小红 // this == acc 共享账户 synchronized (this) { // 2、判断余额是否足够 if(this.money >= money){ // 钱够了 System.out.println(name+"来取钱,吐出:" + money); // 更新余额 this.money -= money; System.out.println(name+"取钱后,余额剩余:" + this.money); }else{ // 3、余额不足 System.out.println(name+"来取钱,余额不足!"); } } }}
-
锁对象用任意唯一的对象好不好呢?
不好,会影响其他无关线程的执行。因为整个系统不是一家人用,你这样就一把锁锁住了全部人,影响无关线程了。 -
锁对象的规范要求
规范上:建议使用共享资源作为锁对象。
对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码(类名.class)对象作为锁对象。-
1.同步代码块是如何实现线程安全的?
对出现问题的核心代码使用synchronized进行加锁
每次只能一个线程占锁进入访问-
同步代码块的同步锁对象有什么要求?
对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码(类名.class)对象作为锁对象。
-
-
方式二:同步方法
作用:把出现线程安全问题的核心方法给上锁。
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
修饰符 synchronized 返回值类型 方法名称(形参列表) {//方法上加一个关键字即可操作共享资源的代码}
package com.itheima.d5_thread_synchronized_method;/** 账户类:余额,卡号 */public class Account { private String cardId; private double money; // 余额 关键信息 public Account() { } public Account(String cardId, double money) { this.cardId = cardId; this.money = money; } public String getCardId() { return cardId; } public void setCardId(String cardId) { this.cardId = cardId; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } /** 小明 小红 this == acc */ public synchronized void drawMoney(double money) { // 1、拿到是谁来取钱 String name = Thread.currentThread().getName(); // 2、判断余额是否足够 // 小明 小红 if(this.money >= money){ // 钱够了 System.out.println(name+"来取钱,吐出:" + money); // 更新余额 this.money -= money; System.out.println(name+"取钱后,余额剩余:" + this.money); }else{ // 3、余额不足 System.out.println(name+"来取钱,余额不足!"); } }}
-
同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
如果方法是实例方法:同步方法默认用this作为的锁对象。但是代码要高度面向对象!
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。 -
同步代码块锁的范围更小,同步方法锁的范围更大。
方式三:Lock锁
网络编程
网络编程可以让程序与网络上的其他设备中的程序进行数据交互。
-
网络通信基本模式
常见的通信模式有如下2种形式:Client-Server(CS) 、 Browser/Server(BS)
-
实现网络编程关键的三要素
IP地址:设备在网络中的地址,是唯一的标识。
端口:应用程序在设备中唯一的标识。
协议: 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
-
IP地址操作类-InetAddress
名称 | 说明 |
---|---|
public static InetAddress getLocalHost() | 返回本主机的地址对象 |
public static InetAddress getByName(String host) | 得到指定主机的IP地址对象,参数是域名或者IP地址 |
public String getHostName() | 获取此IP地址的主机名 |
public String getHostAddress() | 返回IP地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连通该IP地址对应的主机,连通返回true |
- 端口号
端口号:标识正在计算机设备上运行的进程(程序),被规定为一个 16 位的二进制,范围是 0~65535
-
端口类型
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占 用8080,MySQL占用3306)
动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。 -
通信协议
连接和通信数据的规则被称为网络通信协议
OSI参考模型 | TCP/IP参考模型 | 各层对应 | 面向操作 |
---|---|---|---|
应用层 | 应用层 | HTTP、FTP、DNS、SMTP… | 应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发 |
表示层 | |||
会话层 | |||
传输层 | 传输层 | TCP、UDP… | 选择使用的TCP , UDP协议 |
网络层 | 网络层 | IP、ICMP… | 封装源和目标IP,进行路径选择 |
数据链路层 物理层 | 数据链路层+物理 | 物理寻址、比特流… | 物理设备中传输 |
单元测试
反射
-
反射概述
反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。 -
反射的关键:
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
HelloWorld.java -> javac -> HelloWorld.classClass c = HelloWorld.class;
- 反射的第一步:获取Class类的对象
public class Test { public static void main(String[] args) throws Exception { // 1、Class类中的一个静态方法:forName(全限名:包名 + 类名) Class c = Class.forName("com.itheima.d2_reflect_class.Student"); System.out.println(c); // Student.class // 2、类名.class Class c1 = Student.class; System.out.println(c1); // 3、对象.getClass() 获取对象对应类的Class对象。 Student s = new Student(); Class c2 = s.getClass(); System.out.println(c2); }} */public class Test { public static void main(String[] args) throws Exception { // 1、Class类中的一个静态方法:forName(全限名:包名 + 类名) Class c = Class.forName("com.itheima.d2_reflect_class.Student"); System.out.println(c); // Student.class // 2、类名.class Class c1 = Student.class; System.out.println(c1); // 3、对象.getClass() 获取对象对应类的Class对象。 Student s = new Student(); Class c2 = s.getClass(); System.out.println(c2); }}
-
反射获取构造器对象
Class类中用于获取构造器的方法
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor getConstructor(Class<?>… parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 返回单个构造器对象,存在就能拿到 |
Constructor类中用于创建对象的方法
符号 | 说明 |
---|---|
T newInstance(Object… initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
package com.itheima.d3_reflect_constructor;import org.junit.Test;import java.lang.reflect.Constructor;/** 目标:反射_获取Constructor构造器对象. 反射的第一步是先得到Class类对象。(Class文件) 反射中Class类型获取构造器提供了很多的API: 1. Constructor getConstructor(Class... parameterTypes) 根据参数匹配获取某个构造器,只能拿public修饰的构造器,几乎不用! 2. Constructor getDeclaredConstructor(Class... parameterTypes) 根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用! 3. Constructor[] getConstructors() 获取所有的构造器,只能拿public修饰的构造器。几乎不用!!太弱了! 4. Constructor[] getDeclaredConstructors() 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!! 小结: 获取类的全部构造器对象: Constructor[] getDeclaredConstructors() -- 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!! 获取类的某个构造器对象:Constructor getDeclaredConstructor(Class... parameterTypes) -- 根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用! */public class TestStudent01 { // 1. getConstructors: // 获取全部的构造器:只能获取public修饰的构造器。 // Constructor[] getConstructors() @Test public void getConstructors(){ // a.第一步:获取类对象 Class c = Student.class; // b.提取类中的全部的构造器对象(这里只能拿public修饰) Constructor[] constructors = c.getConstructors(); // c.遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } } // 2.getDeclaredConstructors(): // 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。 @Test public void getDeclaredConstructors(){ // a.第一步:获取类对象 Class c = Student.class; // b.提取类中的全部的构造器对象 Constructor[] constructors = c.getDeclaredConstructors(); // c.遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } } // 3.getConstructor(Class... parameterTypes) // 获取某个构造器:只能拿public修饰的某个构造器 @Test public void getConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器 只能拿public修饰的某个构造器) Constructor cons = c.getConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); } // 4.getConstructor(Class... parameterTypes) // 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。 @Test public void getDeclaredConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器) Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); // c.定位某个有参构造器 Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount()); }}
1.利用反射技术获取构造器对象的方式
getDeclaredConstructors()
getDeclaredConstructor (Class<?>… parameterTypes)
2.反射得到的构造器可以做什么?
依然是创建对象的
public newInstance(Object… initargs)
如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象
setAccessible(boolean)
反射可以破坏封装性,私有的也可以执行了。
- 反射获取成员变量对象
Class类中用于获取成员变量的方法
方法 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
Field类中用于取值、赋值的方法
符号 | 说明 |
---|---|
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值。 |
-
利用反射技术获取成员变量的方式
-
获取类中成员变量对象的方法
getDeclaredFields()
getDeclaredField (String name)2.反射得到成员变量可以做什么?
依然是在某个对象中取值和赋值。
void set(Object obj, Object value):
Object get(Object obj)
如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值
setAccessible(boolean)
-
-
反射获取方法对象
Class类中用于获取成员方法的方法
方法 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
Method类中用于触发执行的方法
符号 | 说明 |
---|---|
Object invoke(Object obj, Object… args) | 运行方法参数一:用obj对象调用该方法参数二:调用方法的传递的参数(如果没有就不写)返回值:方法的返回值(如果没有就不写) |
1.利用反射技术获取成员方法对象的方式
获取类中成员方法对象
getDeclaredMethods()
getDeclaredMethod (String name, Class<?>… parameterTypes)
2.反射得到成员方法可以做什么?
依然是在某个对象中触发该方法执行。
Object invoke(Object obj, Object… args)
如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行
setAccessible(boolean)
-
反射的作用-绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。(就能往数组里添加字符串这样的骚操作)
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了
-
反射的作用-通用框架的底层原理
注解
-
注解概述、作用
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。 -
注解的作用是什么呢?
对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。
例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。 -
自定义注解
public @interface 注解名称 {public 属性类型 属性名() default 默认值 ;}
-
元注解
元注解:就是注解注解的注解。
元注解有两个:
@Target: 约束自定义注解只能在哪些地方使用,
@Retention:申明注解的生命周期
XML
- XML概述
XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。
-
XML的几个特点和使用场景
一是纯文本,默认使用UTF-8编码;二是可嵌套;
如果把XML内容存为文件,那么它就是一个XML文件。
XML的使用场景:XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的信息。 -
XML是什么?
XML的全称为(EXtensible Markup Language),是一种可扩展的标记语言。
它是是一种数据表示格式,可以用于自定义数据格式。 -
XML的作用是什么?
用于进行存储数据和传输数据
作为软件的配置文件 -
创建xml文档
XML文件的后缀名为:xml
文档声明必须是第一行:<?xml version="1.0" encoding="UTF-8" ?>version:XML默认的版本号码、该属性是必须存在的encoding:本XML文件的编码
XML的标签(元素)规则
标签由一对尖括号和合法标识符组成: ,必须存在一个根标签,有且只能有一个。
标签必须成对出现,有开始,有结束:
特殊的标签可以不成对,但是必须有结束标记,如:
标签中可以定义属性,属性和标签名空格隔开,属性值必须用引号引起来<student id = “1">
标签需要正确的嵌套
设计模式:工厂模式
-
什么是工厂设计模式?
之前我们创建类对象时, 都是使用new 对象的形式创建,在很多业务场景下也提供了不直接new的方式 。
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一, 这种类型的设计模式属于创建型模式,它提供了一种获取对象的方式。 -
工厂设计模式的作用:
工厂的方法可以封装对象的创建细节,比如:为该对象进行加工和数据注入。
可以实现类与类之间的解耦操作(核心思想)。 -
工厂设计模式的作用
对象通过工厂的方法创建返回,工厂的方法可以为该对象进行加工和数据注入
可以实现类与类之间的解耦操作(核心思想)
public class FactoryDemo { public static void main(String[] args) { Computer c1 = FactoryPattern.createComputer("huawei"); c1.start(); Computer c2 = FactoryPattern.createComputer("mac"); c2.start(); }}
package com.itheima.d3_factory_pattern;public class FactoryPattern { /** 定义一个方法,创建对象返回 */ public static Computer createComputer(String info){ switch (info){ case "huawei": Computer c = new Huawei(); c.setName("huawei pro 16"); c.setPrice(5999); return c; case "mac": Computer c2 = new Mac(); c2.setName("MacBook pro"); c2.setPrice(11999); return c2; default: return null; } }}
设计模式:装饰模式
-
什么是装饰设计模式?
创建一个新类,包装原始类,从而在新类中提升原来类的功能。
-
装饰设计模式的作用:
作用:装饰模式指的是在不改变原类的基础上, 动态地扩展一个类的功能。
-
设计过程说明:
定义父类。
定义原始类,继承父类,定义功能。
定义装饰类,继承父类,包装原始类,增强功能!!InputStream(抽象父类) FileInputStream(实现子类,读写性能较差) BufferedInputStream(实现子类,装饰类,读写性能高)//不改变上面那个类的功能,但把它的性能增强了。