java基础笔记总结
前世今生
Java语言是什么?
一种计算机编程语言。名字取自咖啡。
Java语言发展简史
Java语言之父:James Gosling
l SUN (Stanford University Network斯坦福大学网络公司)
l 1995年5月23日 Java语言诞生
l 1996年1月 JDK1.0
l 1997年2月 JDK1.1
l 1998年12月 JDK1.2(细分为J2SE标准版、J2EE企业版、J2ME移动端开发 用的少)
l 2000年5月 J2SE1.3
l 2002年2月 J2SE1.4
l 2004年10月 J2SE1.5(改名JavaSE5.0、JavaEE5.0、JavaME5.0)
l 2006年12月 Java SE 6
l 2009年4月20日 甲骨文(Oracle)74亿美元收购Sun
l 2011年7月 Java SE 7
l 2014年3月 Java SE 8(市场主流版本)
l 2017年9月 Java SE 9
Java语言流行程度
概念与环境
JDK、JRE与JVM
l JDK(Java Development Kit,Java开发工具包):
包含JRE以及开发工具,面向开发人员使用。(PS:开发用的)
l JRE(Java Runtime Environment,Java运行时环境):
包含JVM以及运行类库,面向运行人员使用。(PS:运行用的)
l JVM(Java Virtual Machine,Java虚拟机):
包含字节码等核心内容。(PS:核心所在)
JDK的下载获取
官方网站地址: https://www.oracle.com/
JDK的安装
注意两点问题:
\1. 既然JDK已经包含了JRE,所以没有必要单独安装JRE。
\2. 安装路径推荐不包含中文或空格。
第一段代码
命令提示符
l 启动: 开始 + R,输入cmd
l 切换盘符: 盘符名称:
l 进入文件夹: cd 文件夹名称
l 进入多级文件夹: cd 文件夹1\文件夹2\文件夹3
l 回到上一级: cd ..
l 回到盘符根路径: cd \
l 查看内容: dir
l 清屏: cls
l 退出: exit
HelloWorld代码编写
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
HelloWorld编译运行
编译命令:JDK安装路径下的bin**javac**.exe
运行命令:JDK安装路径下的bin**java**.exe
使用编译命令,例如:
D:\Java\jdk-9.0.1\bin\javac HelloWorld.java(有后缀.java)
使用运行命令,例如:
D:\Java\jdk-9.0.1\bin\java HelloWorld(没有后缀.class)
HelloWorld常见问题
\1. 文件扩展名错误。
\2. 大小写错误。
\3. 中英文符号错误。
\4. 忘记大括号或者分号。
\5. 拼写错误,例如main写成mian等。
\6. javac命令必须有.java后缀;java命令不能有.class后缀。
配置环境变量
JAVA_HOME:设置为JDK的安装路径,指向bin的上一级文件夹。
Path:最前方加入“%JAVA_HOME%\bin;”,注意有分号分隔。
Notepad++
官方网站地址: https://notepad-plus-plus.org/
需要进行两个设置:
\1. 选择菜单“设置”,点击“首选项”。左侧点击“新建”,将默认语言设置为“Java”。
\2. 同样地,将右侧的编码设置为“ANSI”。
Java基础语法
程序注释
注释:程序源代码中解释说明性的文字,对程序运行没有影响。
单行注释:以“//”开头,直到本行结束为止。(Comment)
多行注释:以“/*”开头,以“*/”结尾,中间的内容都属于注释。(Comment)
文档注释:以“/*”开头,以“/”结尾,中间的内容都属于注释。(Javadoc)
HelloWorld代码解释
l 第一行代码:public class HelloWorld {...}。代表创建一个名称为HelloWorld的类,类是Java中代码组织的基本单位。注意第三个单词必须和文件名完全一样。
l 第二行代码:public static void main(String[] args) {...}。这是一行固定写法,是程序执行的起点位置。
l 第三行代码:System.out.println(“Hello, World!”);。这是一行输出语句,也叫打印语句,也就是在屏幕当中显示一行内容。英文双引号中间是需要显示的内容。
关键字
l 概念:关键字是被Java语言赋予特殊含义,具有专门用途的单词。
比如之前接触的class,public,static,void均为Java已经预设好的。
l 特点:
\1. 完全由小写字母组成;
\2. 在增强版记事本(例如Notepad++)当中有特殊颜色。
l Java中所有的关键字列表:(现阶段不要背,随着学习内容逐步深入,慢慢了解含义)
常量
l 概念:程序运行期间,固定不变的量。
l 分类:
\1. 字符串常量,使用英文双引号引起来的内容。
\2. 整数常量,例如100、120、-250等。
\3. 浮点数(小数)常量,例如3.14、-2.5等。
\4. 字符常量,使用英文单引号引起来的单个字符,例如’A’、’b’、’中’等。必须有且仅有一个字符。
\5. 布尔常量,只有两种取值:true和false。通常用于条件判断和流程控制。
l 代码:
public class DemoConst {
**public static void** main(String[] args) {
// 字符串常量
System.out.println("Hello, World!");
// 整数常量
System.out.println(100);
System.out.println(-150);
// 浮点数常量
System.out.println(3.14);
System.out.println(-2.5);
// 字符串常量使用双引号,中间可以有0-n个字符都行
System.***out\***.println(""); // 中间没有内容,但是为双引号字符串,所以也可以
// 字符常量
System.out.println('A');
System.out.println('b');
System.out.println('*');
System.out.println('中');
// System.out.println(''); // 中间什么内容都没有,错误写法!
// System.out.println('ab'); // 单引号中间有多个字符,错误写法!
// 布尔常量
System.out.println(true); // 真,对
System.out\.println(false); // 假,错
}
}
变量与数据类型
变量的概念
常量是指程序运行期间,内容不可发生改变的量。
变量是指程序运行期间,内容在一定范围内可以改变的量。
变量其实是用来存放数据的内存当中的一小块空间,特点:
\1. 用来存放数据的;
\2. 内容可以发生改变;
\3. 一次只能存放一个数据,如果放入新数据,将会替换掉老数据;
\4. 可以通过变量名称使用其中的数据;
\5. 数据一定要符合数据类型的要求。
变量的格式
l 方案一(两个步骤)
\1. 创建一个变量,格式: 数据类型 变量名称;
\2. 放入一些数据,格式: 变量名称 = 数据值;
l 方案二(一个步骤)
\1. 创建变量的同时立刻放入数据,格式: 数据类型 变量名称 = 数据值;
标识符
l 作用:给类、变量、包、方法等起名字。
l 组成规则(硬性规定)
\1. 只能由字符、下划线_、美元符$组成。
这里的字符包括大小写字母、中文字符、数字字符等,但符号只能有两个:下划线_和美元符$;
\2. 不能以数字开头;
\3. 不能是Java中的关键字。
l 命名规则(软性建议)
\1. 基本要求:见名知意。
\2. 建议只使用英文字母和数字。
\3. 常见命名的规则:
a) 类
每个单词首字母大写,例如Student、HelloWorld。
b) 变量
第一个单词完全小写,后续更多单词首字母大写,例如age、ageOfMyGirlfriend。
c) 方法
与变量规则一样,例如show()、getAge()。
d) 包(其实就是文件夹,用于对类进行管理)
全部小写,多级包用点隔开。公司域名的反写。
cn.itcast (相当于两级文件夹:cn\itcast)
com.itheima (相当于两级文件夹:com\itheima)
类名称
HelloWorld正确,很好;Hello123正确,很好;123Hello错误,数字不能开头;helloworld正确,很不好。
变量名称
age正确,很好;ageOfMyFather正确,很好;year100正确,很好;100year错误,数字不能开头;ageOfMymother正确,不好。
数据类型
在Java 9(2017年9月发布)或更早版本的JDK中,数据类型只有两种:
l 基本类型,包括8种:byte、short、int、long、float、double、char、boolean。
l 引用类型(除了基本,都算引用),包括:数组、类、接口、Lambda等。
其中的8种基本数据类型:
| 四类 | 八种 | 字节数 | 数据表示范围 |
|---|---|---|---|
| 整型 | byte | 1 | -128~127 |
| short | 2 | -32768~32767 | |
| int(默认) | 4 | -2147483648~2147483647 | |
| long | 8 | -263~263-1 | |
| 浮点型 | float | 4 | -3.403E38~3.403E38 |
| double(默认) | 8 | -1.798E308~1.798E308 | |
| 字符型 | char | 2 | 表示一个字符,如('a','A','0','家') |
| 布尔型 | boolean | 1 | 只有两个值true与false |
注意事项
\1. 整数类型有4种,默认为int类型。
\2. 浮点数类型有2种,默认为double类型。
\3. 定义一个long型数据,在数值后面要用字母L作为后缀(大小写均可,推荐大写)。
5200000000L
\4. 定义一个float型数据,在数值后面要用字母F作为后缀(大小写均可,推荐大写)。
3.14F
\5. 字符char型数据,是可以包含中文的。
变量的基本使用
按照变量的格式,指定不同的数据类型,并且赋予不同的数值即可。
请参考对应的源代码。
变量的注意事项
\1. 创建的多个变量不能重名。
\2. 变量一定要赋值之后才能使用。
\3. 变量的作用域问题。
变量定义在哪个大括号中,就只能用于哪个大括号中。超出直接所属的大括号,将无法使用。
\4. 可以在一个语句中同时定义类型相同的多个变量。
运算符
概述
运算符:对常量或变量进行操作的符号,叫做运算符。例如“+”。
表达式:用运算符将多个常量或变量连起来的式子,叫做表达式。“例如a + b”。
常用的运算符分类有:
l 算术运算符
l 赋值运算符
l 比较运算符
l 逻辑运算符
算术运算符
四则运算
加(+)减(-)乘(*)除(/),与数学中的用法基本一致。但是有两点注意事项:
\1. 对于整数来说,除法是进行“整除”,只看商,不看余数。
\2. 一旦有浮点数参与运算,那么结果就是浮点数。
取模运算
如果对于整数的除法,希望得到余数而不是商,那么可以使用取模运算(%)。
注意,只有对整数使用取模运算,才有余数的数学意义。
字符串连接
如果将加号(+)用于字符串,那么将不再是数学运算,而是字符串连接。
任何数据类型与字符串进行连接,结果都将是字符串类型。
自增自减运算
自增运算符 ++ 在原有的基础上,累加一个数字1
自减运算符 -- 在原有的基础上,累减一个数字1
使用格式:
前++:在变量的名称之前,写上++符号。例如:++num
后++:在变量的名称之后,写上++符号。例如:num++
使用方式:
单独使用:自己独立成为一个步骤,不和其他操作混合。
混合使用:和打印语句、赋值语句等各种其他操作混合使用。
前后++的区别:
\1. 在单独使用的时候,前++和后++没有任何区别。
\2. 在混合使用的时候,前++和后++存在【重大区别】:
a) 如果是【前++】,那么变量立刻马上+1,然后拿着结果进行使用。【先加后用】
b) 如果是【后++】,那么首先使用变量当前本来的数值,然后变量再+1。【先用后加】
注意:自增自减运算符只能用于变量,不能用于常量。
赋值运算符
l 基本赋值运算符:等号(=),代表将右侧的数据交给左侧的变量。
l 复合赋值运算符:
\1. a += 3 相当于 a = a + 3
\2. b -= 4 相当于 b = b - 4
\3. c *= 5 相当于 c = c * 5
\4. d /= 6 相当于 d = d / 6
\5. e %= 7 相当于 e = e % 7
注意:赋值运算符左侧必须是变量,不能是常量。
比较运算符
用于比较两个数据的关系,运算结果一定是boolean型数据。
\1. 大于: >
\2. 小于: <
\3. 大于等于: >=
\4. 小于等于: <=
\5. 等于: ==
\6. 不等于: !=
注意:两个等号连写,才是数学中相等的含义。
逻辑运算符
基本使用
与(并且) & 全都是true才是true;否则是false。
或(或者) | 有一个true就是true;全都false才是false。
亦或 ^ 相同就是false;不同就是true。
非(取反) ! 本来是true变成false;本来是false变成true。
短路使用
短路与 &&
短路或 ||
如果表达式左边已经可以确定最终结果,那么右边的代码将不再执行,提高程序效率。
Scanner键盘输入
使用引用类型
在Java 9或更早的版本中,除了8种基本类型,其他类型都属于引用类型。
如果希望使用引用类型中的“类”,那么典型用法的一般步骤为:
\1. 导包。
指定需要使用的目标在什么位置。在public class之前一行写代码:
import 包路径名;
\2. 创建。
通常需要创建之后才能使用,格式:
数据类型 变量名称 = new 数据类型();
\3. 使用。
需要使用什么功能,就点儿一个功能名称(方法名),格式:
变量名称.方法名();
使用Scanner
Scanner就是一种引用类型,JDK已经写好了这个类供我们直接使用。步骤有三个:
\1. 导包。
import java.util.Scanner;
\2. 创建。
Scanner sc = new Scanner(System.in);
** PS:其中System.in代表从键盘输入。
\3. 使用。
a) 获取键盘输入的int数字:
int num = sc.nextInt();
b) 获取键盘输入的字符串:
String str = sc.next();
流程概述
概念:流程是指程序步骤执行的先后顺序,先做什么,后做什么。
分类:
\1. 顺序结构:从上到下,从前向后,顺序执行。
\2. 选择结构:执行路线分叉,做这个,或者做那个,也叫分支结构。
\3. 循环结构:重复做一些事情。
顺序结构
程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。
总地来说:写在前面的先执行,写在后面的后执行。
选择结构
Java中主要通过if语句来实现选择分支流程,if语句的格式有常用的三种:
l 单if语句:执行某些步骤,或者不执行它们。
l 标准的if-else语句:在2套步骤方案中选择一种执行。
l 扩展的if-else语句:在3套或更多步骤方案中选择一种执行。
单if语句
效果:根据条件判断,成立则执行一些代码,不成立则不执行这些代码。
格式:
if (布尔表达式) {
语句体
}
执行流程:
\1. 首先判断小括号之内的条件是true还是false。
\2. 如果是true,则执行大括号之内的语句体,即一些代码。
\3. 如果是false,则不执行大括号之内的语句体。
标准的if-else语句
效果:根据条件判断,成立则执行if中的代码,不成立则执行else中的代码。
格式:
if (布尔表达式) {
语句体A
} else {
语句体B
}
执行流程:
\1. 首先判断小括号之内的条件是true还是false。
\2. 如果是true,那么执行if大括号内的语句体A,而else的内容将不再执行。
\3. 如果是false,那么执行else大括号内的语句体B,而if的内容将不再执行。
扩展的if-else语句
作用:在N个语句体当中选择其中的一个执行。
格式:
if (条件判断1) {
语句体1
} else if (条件判断2) {
语句体2
}
……
else if (条件判断N) {
语句体N
} else {
语句体N+1
}
执行流程:
\1. 如果条件判断1成立,执行语句体1,后续代码不再执行。
\2. 如果条件判断1不成立,跳过语句体1,继续执行条件判断2。
\3. 如果条件判断2成立,执行语句体2,后续代码不再执行。
\4. 如果条件判断2不成立,跳过语句体2,继续执行条件判断N。
\5. 如果条件判断N成立,执行语句体N,后续代码不再执行。
\6. 如果条件判断N不成立,跳过语句体N,执行else中的代码。
练习1:考试成绩划分
提示:总共有5种情况,5者选其一。成绩划分的标准为:
l 优秀:90-100分
l 良好:80-89分
l 中等:70-79分
l 及格:60-69分
l 不及格:0-59分
练习2:求两者最大值
提示:有两个数字a和b,要么a大,要么一定是b大,所以应该使用标准的if-else语句,二者选其一。对于a和b一样大的情况,可以归并为两种情况中的任何一种。
练习3:求三者最大值
提示:已经掌握了两者最大值,则可以将三个数字的大小比较,拆分为两两比较。
循环结构
组成部分
一个循环结构的典型组成部分有:
\1. 初始化语句(准备工作)。这部分内容最先执行,而且仅执行一次。
\2. 条件判断(布尔结果)。如果成立,则循环继续;如果不成立,则循环退出。
\3. 循环体(重复的内容)。每次循环都将重复执行循环体的代码内容。
\4. 步进语句(扫尾工作)。每次循环体执行后,都会执行一次步进语句。
for循环
通常用于控制次数的场景,格式为:
for (①初始化语句; ②条件判断; ④步进语句) {
③循环体
}
执行顺序: ①②③④=>②③④=>②③④……直到②不满足为止。
①负责完成循环变量初始化
②负责判断是否满足循环条件,不满足则跳出循环
③具体执行的语句
④循环后,循环条件所涉及变量的变化情况
练习:求出1-100的偶数和
使用for循环,从1一直到100逐一判断,如果是偶数,则累加起来。最终输出结果。
while循环
通常用于控制条件的场景,格式有两种。
标准格式
while (①条件判断) {
②循环体
}
执行顺序: ①②=>①②=>①②……直到①不满足为止。
①负责判断是否满足循环条件,不满足则跳出循环
②具体执行的语句
扩展格式
①初始化语句
while (②条件判断) {
③循环体
④步进语句
}
执行顺序: ①②③④=>②③④=>②③④……直到②不满足为止。
①负责完成循环变量初始化
②负责判断是否满足循环条件,不满足则跳出循环
③具体执行的语句
④循环后,循环条件所涉及变量的变化情况
练习:求出1-100的奇数和
使用while循环,从1一直到100逐一判断,如果是偶数,则累加起来。最终输出结果。
for与while的区别
大多数时候,for循环与while循环可以等效替换。但是二者存在几点区别:
\1. for循环格式固定,控制次数更方便;而while循环格式灵活,不太在意循环次数。所以次数确定使用for较多;次数不确定,使用while较多。
\2. for循环小括号内定义的变量,循环内可用,循环外不可用;while循环的初始化表达式本来就在外面,仍然可用。
\3. 跳转语句continue的效果不同。(稍后介绍)
跳转控制语句
循环当中可以使用两种跳转控制语句:
l break语句
l continue语句
break语句
如果希望当前循环立刻结束,可以使用break语句:
break;
一旦执行这个语句,整个循环立刻结束。
continue语句
如果希望循环只是跳过当前次,后续还要继续,那么可以使用continue语句:
continue;
一旦执行这个语句,当前次循环剩余内容立刻跳过,马上开始下一次循环。
break与continue的区别
l break会将整个循环彻底打断,剩余多少次全都没有了;
l continue只是跳过当前次,剩余的更多次仍然还有。
死循环
概念:永远无法退出的循环,叫做死循环。
如果希望手写一个死循环,那么可以使用格式:
标准格式:
while (true) {
循环体
}
还有另外一种等效的格式:
for (;;) {
循环体
}
PS:死循环当中仍然可以根据实际需要使用break或continue语句。
循环嵌套
可以在一个循环的内部,套用又一个循环,行程内外层嵌套的结构。
在循环嵌套中,外层循环的每一次执行,都对应这内层循环的完整多次执行。
练习:打印一天的分钟时刻
提示:外层控制小时,内层控制分钟。
循环嵌套中的跳转语句
break或continue只能作用于当前所属层循环。如果希望作用于外层循环,可以使用标签的写法:
标签名称: for (...) { // 外层循环
for (...) { // 内层循环
break 标签名称;
}
}
Eclipse IDE
概述
1. 软件介绍
Eclipse是一个专门针对Java的集成开发工具(IDE),原本是IBM公司的产品。免费、开源、主要由Java语言编写,所以需要有JRE运行环境并配置好环境变量。它可以极大地提升我们的开发效率,可以自动编译,检查错误。目前在免费的各种IDE中,使用最多的就是Eclipse。
2. 软件特点
a) 完全免费
b) 完全开源
c) Java语言编写
d) 绿色软件
e) 扩展性强
下载、安装与卸载
官网地址:http://www.eclipse.org 。
安装步骤:解压缩到任意文件夹即可直接使用,路径中建议不包含中文或空格。
卸载步骤:直接删除安装路径的文件夹即可。
基本使用
\1. 新建一个项目(Project);
\2. 在src下新建一个包(Package);
\3. 在包下新建一个类(Class);
\4. 在类中编写源代码;
\5. 运行代码,右键选择“Run As”,然后点击“Java Application”。
基本配置
\1. 调整字体:Window --> Preferences --> General --> Appearance --> Colors and Fonts --> Basic --> Text Font --> Edit
\2. 显示行号:在行号的位置点击右键--> Show Line Numbers
\3. 重置界面:Window --> Perspective --> Reset Perspective
\4. 找回控制台:Window --> Show View --> Console
项目的删除与导入
\1. 删除项目或其内容
a) 选中需要删除的内容,右键选择“Delete”即可。注意,如果删除项目中的内容,那么会直接删除,不会进入回收站。
b) 删除整个项目文件夹,那么删除时勾选“Delete project contents on disk”将彻底删除整个项目,且无法撤销还原。如果不勾选,那么项目在硬盘中仍然存在。
\2. 导入已存在的项目
a) 建议将项目文件夹放入Workspace中。
左侧空白处右键选择“Import”,然后通过Browse按钮找到项目文件夹。
常用快捷键
单行注释:Ctrl + / 取消:Ctrl + /
多行注释:Ctrl + Shift + / 取消:Ctrl + Shift + \
向下复制一行:Ctrl + Shift + 下
向上复制一行:Ctrl + Shift + 上
向下移动一行:Ctrl + 下
向上移动一行:Ctrl + 上
删除当前行:Ctrl + D
格式化代码:Ctrl + Shift + F
智能提示:Alt + /
数组
概述
数组是一种引用类型。变量只可以存放一个数据,数组则可以存放多个类型统一的数据,可以存放基本类型,也可以存放引用类型。
如果需要存储的数据很多,那么定义多个变量很麻烦:
int score1 = 100;
int score2 = 95;
int score3 = 98;
...
int score80 = 100;
多个变量的数据类型是统一的,则可以使用数组进行存储。
数组定义格式与初始化
\1. 定义一个数组的格式为:**数据类型[] 数组名称;
** 数据类型代表数组中保存的数据全都是统一的哪种类型。
通过数组名称可以使用其中的多个数据。
\2. 数组一定要初始化之后才可以使用。初始化是指:为数组在内存当中开辟内存空间,用来保存数据,并且设置默认值。没有初始化的数组无法使用。
数组的初始化方式分成两种:
a) 动态初始化,指定长度;
b) 静态初始化,指定内容。
动态初始化
数组的动态初始化是直接指定数组的长度,并且为数组中的每一个元素赋予一个默认值。
动态初始化的格式为:
数据类型[] 数组名称 = new 数据类型[长度];
或者:
数据类型[] 数组名称;
数组 = new 数据类型[长度];
数组元素的默认值规则为:
整数默认为0,浮点数默认为0.0,字符默认为’\u0000’,布尔值默认为false,字符串等引用类型默认为null。
访问数组元素
直接打印数组的名称,得到的是“内存地址值”(的哈希值)。要想访问数组中的元素,需要使用格式:
数组名称[索引编号]
其中索引编号是数组中元素的int型数字编号,从0开始,一直到数组的长度-1为止。
获取数组长度
无论是动态还是静态初始化,数组一定是具有长度的。获取长度的格式:
数组名称.length
这将得到一个int数字代表长度。
注意,数组在内存当中一旦被创建,那么长度不可改变。
数组索引越界异常
如果访问的数组元素索引并不存在,那么将会发生异常:
java.lang.ArrayIndexOutOfBoundsException。
注意,数组元素索引编号从0开始,一直到“数组长度-1”为止。
Java内存分配
\1. 栈(Stack):主要用来存放局部变量。
\2. 堆(Heap):凡是new出来的东西,都在堆当中。堆当中的数据有默认值规则:
a) 如果是整数,默认是0;
b) 如果是浮点数,默认是0.0;
c) 如果是字符,默认是’\u0000’(Unicode写法);
d) 如果是布尔值,默认为false;
e) 如果是引用类型(包含字符串),默认为null(空常量)。
\3. 方法区(Method Area):存放与.class相关的信息。
\4. 本地方法区(Native Method Area):与操作系统相关。
\5. 寄存器(pc Register):与CPU相关,性能极高。
练习
第一题:求数组元素最值
一个数组当中有很多个元素,求出其中的最大值,再求出其中的最小值。
第二题:求数组元素和
设置一个长度为6的数组,存入任意6个数字,然后求出所有元素的总和。
方法
概述
Java语言中的“方法”(Method)在其他语言当中也可能被称为“函数”(Function)。对于一些复杂的代码逻辑,如果希望重复使用这些代码,并且做到“随时任意使用”,那么就可以将这些代码放在一个大括号“{}”当中,并且起一个名字。使用代码的时候,直接找到名字调用即可。
定义格式
首先了解关于方法的两个概念:
\1. 参数:是指进入方法中的数据,有了这些数据,方法才能执行逻辑。
\2. 返回值:是指从方法中出来的数据,也就是方法执行之后的最终结果数据。
目前定义方法的基本格式:
修饰符 返回值类型 方法名称(参数类型 参数名称) {
方法体
return 返回值;
}
对于定义格式的解释:
l 修饰符:现阶段固定为public static两个关键字。
l 返回值类型:方法最终产生的结果数据是什么类型。
l 方法名称:自定义的名称,命名规则和变量一样。
l 参数类型:进入方法的数据是什么类型。
l 参数名称:进入方法的数据对应的变量名称。
l 方法体:方法内部执行的若干行代码。
l return:结束方法的执行,并且将返回值返还给调用处。
l 返回值:方法最终产生的结果数据。
注意:
\1. 返回值必须和返回值类型对应。
\2. 参数如果有多个,需要使用逗号分隔。
\3. 参数如果没有,小括号则可以留空。
定义代码
public class Demo02MethodDefine {
/*
\* 定义方法的三要素
\* 返回值类型:最终结果肯定是一个int类型
\* 方法名称:sum
\* 参数列表:两个int数值
*/
public static int sum(int a, int b) {
System.out.println("方法执行啦!");
int result = a + b; // 方法体
return result;
}
public static void main(String[] args) {
}
注意事项:
\1. 多个方法的定义先后顺序无所谓。
\2. 不能在一个方法内部定义方法。
\3. 方法定义之后,没有调用就不会执行;要想执行,一定要调用它。
调用方式
调用方法的方式有常见的三种:
\1. 单独调用。这种方式无法使用方法的返回值。格式:
方法名称(参数值);
\2. 打印调用。这种方式可以将方法的返回值直接打印。格式:
System.out.println(****方法名称(参数值));
\3. 赋值调用。这种方式可以将方法的返回值赋值给一个变量,注意变量的数据类型必须和方法的返回值类型对应。格式:
数据类型 变量名称 = 方法名称(参数值);
重名问题
\1. 变量的名称是否可以与方法名称重名?可以。
\2. 两个不同的方法中,能否各自有一个重名的变量?可以。
无返回值的方法
\1. 概念:如果方法只是执行方法体操作,并没有最终结果必须交还给调用处,那么这就是一个无返回值的方法。
\2. 有返回值:大哥叫小弟去杀人,杀了之后提人头来见,人头就是返回值。
\3. 无返回值:大哥叫小弟去杀人,杀了之后回报一声即可,不要人头,这就没有返回值。
定义格式
修饰符 void 方法名称(参数类型 参数名称) {
方法体
return;
}
修饰符:与普通方法一样
返回值类型:固定为void
方法名称:与普通方法一样
参数类型:与普通方法一样
参数名称:与普通方法一样
方法体:与普通方法一样
return:后面不能写返回值,直接分号结束。而且最后一行return通常情况下会省略不写。
代码使用
public class Demo01MethodVoid {
public static void main(String[] args) {
// 单独调用
printHelloWorld();
System.out.println("===============");
// 单独调用
printHelloWorldCount(15);
System.***out\***.println("===============");
// 错误写法!void的方法不能使用打印调用
// System.out.println(printHelloWorld());
// System.out.println(void);
// 错误写法!void的方法不能使用赋值调用
// int num = printHelloWorld();
// int num = void;
}
/*
\* 定义一个方法,用来打印输出固定10次HelloWorld。
\* 三要素
\* 返回值类型:只是希望进行打印输出,屏幕显示而已,并没有最终的数据结果产生,所以使用void
\* 方法名称:printHelloWorld
\* 参数列表:不需要任何条件数据,就可以完成任务。所以参数留空。
*/
public static void printHelloWorld() {
for (int i = 0; i < 10; i++) {
System.out.println("Hello, World! " + (i + 1));
}
// return; // 最后一行return语句可以省略
// return 100; // 错误写法!void就不能有具体的返回值
}
/*
\* 定义一个方法,用来打印输出指定次数的HelloWorld。
\* 三要素
\* 返回值类型:仍然只是进行打印输出,仍然没有最终的数据结果产生,所以还是使用void
\* 方法名称:printHelloWorldCount
\* 参数列表:需要知道到底是几次,才能打印。需要一个int数字代表次数。
*/
public static void printHelloWorldCount(int count) {
**for** (**int** i = 0; i < count; i++) {
System.out.println("Hello, World! " + (i + 1));
}
}
}
遍历数组
遍历数组,是指对数组当中的每一个数据元素进行逐一、挨个儿处理。默认的遍历就是打印输出。
public class Demo02PrintArray {
public static void main(String[] args) {
// 1. 首先准备一个数组
int[] array = { 10, 20, 30, 40, 50, 65, 75 };
// 3. 调用方法,传入参数数据(一个数组)
printArray(array);
}
/*
\* 2. 定义一个方法,目的是遍历数组
\* 三要素
\* 返回值类型:只是进行屏幕的打印显示而已,没有最终数据结果要还给调用处,所以用void
\* 方法名称:printArray
\* 参数列表:一定要得到一个数组,才能打印。int[]
*/
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
扩展成为指定格式:
public class Demo03PrintArrayFormat {
public static void main(String[] args) {
int[] array1 = { 10, 20, 30, 40, 50 };
printArrayFormat(array1);
int[] array2 = { 5, 15, 25, 35, 45 };
printArrayFormat(array2);
}
public static void printArrayFormat(int[] array) {
System.out.print("["); // 1. 首先打印左括号
for (int i = 0; i < array.length; i++) {
// 2. 判断一下当前元素是不是最后一个?
if (i == array.length - 1) { // 如果是最后一个
System.out.println(array[i] + "]");
} else { // 如果不是最后一个
System.out.print(array[i] + ", ");
}
}
}
}
对比返回值的有无
public class Demo04VoidVsReturn {
public static void main(String[] args) {
int[] array = { 1, 2, 7, 12, 80, -14 };
// 有返回值的调用
int result = sumA(array);
System.out.println("结果是:" + result);
// 无返回值的调用
sumB(array);
}
// 求出数组当中元素的总和,有返回值
public static int sumA(**int**[] array) {
int sum = 0; // 存钱罐,这个变量用来进行累加
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
// 打印数组当中元素的总和,只是打印,就没有返回值
public static void sumB(int[] array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
System.out.println("总和:" + sum);
}
}
方法重载
概述
对于参数列表不一样的多个方法,如果设置不一样的方法名称会很麻烦,所以引入了方法重载(Overload)的技术。
方法重载:方法名称相同,但是参数列表不同。
参数列表的不同包括:
\1. 参数的个数不同;
\2. 参数的类型不同;
\3. 参数的多类型顺序不同。
重载与下列因素无关:
\1. 方法的返回值;
\2. 参数的名称。
基本使用
public class Demo01MethodOverload {
public static void main(String[] args) {
// System.out.println(sumTwo(10, 20)); // 30
// System.out.println(sumThree(10, 20, 30)); // 60
// System.out.println(sumFour(10, 20, 30, 40)); // 100
System.out.println(*sum*(10, 20)); // 30
System.out.println(*sum*(10, 20, 30)); // 60
System.out.println(*sum*(10, 20, 30, 40)); // 100
// System.out.println(sum(10, 20, 30, 40, 50)); // 错误的写法!因为没有任何一种重载形式能对上
System.out.println("===============");
System.out.println(sum(10, 20)); // int
System.out.println(sum(10.5, 20.5)); // double
System.out.println("===============");
System.out.println(sum(2.5, 10)); // 先double后int
System.out.println(sum(10, 2.5)); // 先int后double
}
public static double sum(int a, double b) {
System.out.println("先int后double的方法执行!");
return a + b; // 注意!不同数据类型的数值相加,谁的范围大,结果就是谁
}
public static double sum(double a, int b) {
System.out.println("先double后int的方法执行!");
return a + b; // 注意!不同数据类型的数值相加,谁的范围大,结果就是谁
}
public static double sum(double a, double b) {
System.out.println("有2个double参数的方法执行!");
return a + b;
}
public static int sum(int a, int b) {
System.out.println("有2个int参数的方法执行!");
return a + b;
}
// 错误写法!与参数的名称无关
// public static int sum(int x, int y) {
// System.out.println("有2个int参数的方法执行!");
// return x + y;
// }
// 错误写法!重载与返回值无关!
// public static double sum(int a, int b) {
// System.out.println("有2个int参数的方法执行!");
// return a + b + 0.0;
// }
public static int sum(int a, int b, int c) {
System.out.println("有3个int参数的方法执行!");
return a + b + c;
}
public static int sum(int a, int b, int c, int d) {
System.out.println("有4个int参数的方法执行!");
return a + b + c + d;
}
}
参数传递
形式参数:在“定义方法”的时候,写在小括号之内的变量,就叫形式参数。
实际参数:在“调用方法”的时候,真正传入方法里的数据,叫做实际参数。
两条规则:
\1. 对于基本类型(以及String)来说,形式参数的操作【不会】影响实际参数。
\2. 对于引用类型(除了String)来说,形式参数的操作【会】影响实际参数。
基本类型传参
public class Demo01BasicParam {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("a=" + a); // 10
System.out.println("b=" + b); // 20
System.out.println("======================");
change(a, b); // 实际参数
System.out.println("======================");
System.out.println("a=" + a); // 10
System.out.println("b=" + b); // 20
}
public static void change(int x, int y) { // 形式参数
x *= 10; // 扩大十倍
y *= 10; // 扩大十倍
System.out.println("x=" + x); // 100
System.out.println("y=" + y); // 200
}
}
引用类型传参
public class Demo02RefParam {
public static void main(String[] args) {
int[] array = { 10, 20, 30 };
System.out.println(array[0]); // 10
System.out.println(array[1]); // 20
System.out.println(array[2]); // 30
System.out.println("====================");
change(array);
System.out.println("====================");
System.out.println(array[0]); // 100
System.out.println(array[1]); // 200
System.out.println(array[2]); // 300
}
public static void change(int[] arr) {
arr[0] *= 10;
arr[1] *= 10;
arr[2] *= 10;
System.out.println(arr[0]); // 100
System.out.println(arr[1]); // 200
System.out.println(arr[2]); // 300
}
}
面向对象
概述
如今主流的软件开发思想有两种:一个是面向过程,另一个是面向对象。面向过程出现得较早,典型代表为C语言,开发中小型项目的效率很高,但是很难适用于如今主流的大中型项目开发场景。面向对象则出现得更晚一些,典型代表为Java或C++等语言,更加适合用于大型开发场景。两种开发思想各有长短。
对于面向过程的思想:当需要实现一个功能的时候,看中的是每一个步骤怎么做,整体的过程是怎样的。每一个操作都需要自己亲力亲为。
对于面向对象的思想:当需要实现一个功能的时候,不看重具体的过程和步骤是怎样的,而是关心“谁能帮我做这件事情”。(偷懒儿,找人帮我做事儿。)
面向对象的三大特征有:封装性、继承性、多态性。
生活举例
\1. 洗衣服
l 面向过程(手洗):脱衣服、找一个盆、加水、加洗衣粉、浸泡30分钟、搓洗、拧衣服、倒掉水、再加水、漂洗、拧衣服、倒掉水、晾衣服。
l 面向对象(机洗):脱衣服、放入洗衣机、按下开关、拿出衣服晾晒。
\2. 买电脑
l 面向过程(自己买):需要电脑、查询参数信息、横向比较机型、了解打折信息、与店家讨价还价、下单、收快递、开机验货、确认收货。
l 面向对象(找人买):需要电脑、找秘书帮我买、收电脑。
\3. 吃饭
l 面向过程(自己做):我饿了、买菜、洗菜择菜、切菜、起火、炒菜、盛菜、吃饭、刷碗。
l 面向对象(出去买):我饿了、买饭、吃饭。
代码体验
import java.util.Arrays;
/*
\* 面向过程:
\* 当需要实现一个功能的时候,所有的步骤都要亲力亲为,处理每一个代码细节。
\*
\* 面向对象:
*/
public lass Demo01PrintArray {
public static void main(String[] args) {
int[] array = { 10, 20, 30, 40, 50 };
// 首先使用面向过程的思想,每一个步骤细节都要亲自处理
System.out.print("[");
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
System.out.println(array[i] + "]");
} else {
System.out.print(array[i] + ", ");
}
}
System.out.println("==================");
// 使用面向对象的思想,达到同样的效果。
// 自己不处理详细的步骤,而是找人帮我做事儿。
// 在JDK当中已经给我们提供好了一个工具,叫做Arrays
// 通过这个工具就可以将数组转换成为指定格式的字符串
// 我自己不做,而是找Arrays帮我把数组转换成为指定格式的字符串
System.out.println(Arrays.toString(array));
}
}
类与对象的关系
我们学习编程语言,就是为了模拟现实世界中的事物,实现信息化来提高工作效率。例如银行的业务系统、超市的结账系统等,都是如此。那么现实世界的事物通常分成两个组成部分:
\1. 属性:事物的特征描述信息,“是什么”。
\2. 行为:事物的能力行动方案,“能做什么”。
面向对象的语言当中,“类”就是用来模拟现实事物的。类中也有属性、行为两个组成部分,而“对象”是类的具体实例。例如:
\1. 类:抽象的,是一张“手机设计图”。
\2. 对象:具体的,是一个“真正的手机实例”。
类的定义
类是用来模拟现实事物的代码手段,事物分成属性、行为两个部分,类当中也对应地包含两个部分:
\1. 成员变量(属性):将变量位置直接定义在类中,在方法外,即为成员变量。
\2. 成员方法(行为):将普通的方法去掉static关键字,即为成员方法。
public class Student {
// 成员变量
String name; // 姓名
int age; // 年龄
// 成员方法
public void eat() {
System.out.println("吃饭饭!");
}
public void sleep() {
System.out.println("睡觉觉!");
}
public void study() {
System.out.println("学习!");
}
}
创建对象
类如同一张设计图纸,通常情况下不能直接使用;要想使用,则需要根据类创建一个真正的对象,然后使用对象中的功能。根据类创建对象的格式为:
类名称 对象名 = new 类名称();
public class Demo02Student {
public static void main(String[] args) {
// 希望根据Student类来创建一个学生对象
// 格式:类名称 对象名 = new 类名称();
// 创建一个对象,是一个学生,对象名称叫stu1
// 对象的名称stu1也叫做对象的“引用名”。
Student stu1 = new Student();
Student stu2 = new Student(); // 创建了第二个对象
Student stu3 = new Student(); // 创建更多学生对象
Student stu4 = new Student(); // 创建更多学生对象
Student stu5 = new Student(); // 创建更多学生对象
}
}
对象的基本使用
对象是根据类创建出来的,所以内部包含成员变量、成员方法两个组成部分。使用它们的格式如下:
\1. 使用成员变量:对象名.成员变量名
\2. 调用成员方法:对象名.成员方法名(参数)
简单来说,“想用谁就点儿谁”。
public class Demo03Student {
public static void main(String[] args) {
Student stu = new Student(); // 创建了一个学生对象
System.out.println(stu.name); // null
System.out.println(stu.age); // 0
System.out.println("==============");
// 改变成员变量的数据值
stu.name = "鹿晗";
stu.age = 20;
System.out.println(stu.name); // 鹿晗
System.out.println(stu.age); // 20
System.out.println("==============");
// 将对象当中的成员变量,交给name变量
String name = stu.name;
System.out.println(name); // 鹿晗
System.out.println("==============");
// 使用对象当中的成员方法
stu.eat(); // 调用吃饭的成员方法
stu.sleep(); // 调用睡觉的成员方法
stu.study(); // 调用学习的成员方法
}
}
一个对象
对象的名称其实就是一个局部变量,里面存放的是地址值。真正new出来的对象在堆内存当中,而且成员方法保存的是方法区中的地址。
两个对象
两个对象的情况与一个对象是类似的,对象名称作为局部变量保存地址值,对象中的成员方法是方法区中的地址值。
同一个对象
如果使用“Phone two = one;”的代码写法,那么是将one当中的地址值赋值给two,于是one和two的地址相同,指向了同一个对象。
区分局部变量与成员变量
\1. 定义的位置不同:
a) 局部变量定义在方法内
b) 成员变量定义在方法外,直接在类中
\2. 内存的位置不同:
a) 局部变量是在栈内存中的
b) 成员变量是在堆内存中的
\3. 生命周期不同:
a) 局部变量随着方法进栈而出现,随着方法出栈而消失
b) 成员变量随着对象被创建而出现,随着对象被回收而消失
\4. 默认值不同:
a) 局部变量没有默认值,必须赋值之后才能使用
b) 成员变量如果没有赋值,那么会有默认值
private关键字
如果类当中有一些成员不希望被外界直接访问,那么可以使用private关键字进行修饰,从而达到确保数据安全的目的。
private关键字是面向对象三大特征(封装、继承、多态)之一封装性的重要体现。
public class Person {
// 成员变量
String name; // 姓名
private int age; // 年龄
// 成员方法
public void show() {
System.out.println("我叫" + name + ",今年" + age + "岁。");
}
// 专门定义了一个成员方法用来设置成员变量的数据,Setter方法
public void setAge(int num) {
// 设置条件判断进行数据合理性检测,把关!
if (num < 0 || num > 200) {
System.*out*.println("数据错误!");
} else {
age = num;
}
}
// 专门定义了一个成员方法用来获取成员变量的数据,Getter方法
public int getAge() {
return age;
}
}
this关键字
如果成员变量和局部变量重名,那么方法中会根据就近原则优先使用局部变量。此时可以通过this关键字进行二者的区分:
this.成员变量名
public class Person {
String name; // 我自己的名字
public void sayHello(String name) { // 对方的名字
System.out.println(name + ",你好。我是" + this.name + "。");
}
}
构造方法
构造方法是指在类当中专门用于创建对象的方法,定义格式:
修饰符 构造方法名(参数类型 参数名称) {
方法体
// return; // 最后一行return通常省略
}
当通过new关键字创建对象的时候,实际上就是在调用构造方法。
注意事项:
\1. 构造方法根本不写返回值类型,连void都不写。
\2. 构造方法的名称和所在的类名称必须完全一样,连大小写都要一样
\3. 构造方法也是可以进行重载的。
\4. 构造方法如果没有自定义,那么将会默认赠送一个。如果自定义了至少一个,那么就不再赠送默认的。
练习:两个对象的邂逅
定义一个类代表“人”,含有姓名和年龄,而且具有打招呼的功能。根据类创建两个对象并且赋值。让两个对象分别和对方打招呼。
字符串
字符串也是对象
虽然字符串的基本使用很简单,但它属于引用数据类型。无论通过何种方式创建了字符串,其实都是一个java.lang.String类的对象。那为什么不需要import导包语句,就可以直接使用字符串类型String呢?
所有的类在使用的时候都需要导包,除了以下两种情况:
\1. 需要使用的目标类和当前类位于同一个包下;
\2. 需要使用的目标类直接位于java.lang包下(不含其子包)。
由于字符串类型的数据特别常用,所以代码中直接定义一个双引号的数据,就自动创建了字符串对象,通常不需要手动进行new操作。请注意,直接写上双引号就是字符串对象。
API文档
API(Application Programming Interface,应用程序编程接口)其实就是一本字典,帮我们列出了JDK中所有的类、所有的方法,是一份使用说明书。
使用步骤为:
\1. 打开CHM文档,点击左上角“显示”按钮。
\2. 在“索引”当中输入需要查找的类名称。
\3. 根据需要阅读右侧的说明信息:包、构造方法、普通方法。
构造方法
字符串String的构造方法很多,仅记住常用的2+1种创建方式即可,其余不常用的方式可以查询API文档。创建字符串的常用方式有:
\1. 直接使用双引号。
\2. 构造方法:public String(char[] array),使用字符数组来创建字符串。
\3. 构造方法:public String(char[] array, int offset, int count),使用字符数组的一部分创建字符串。
public class Demo03StringInit {
public static void main(String[] args) {
char[] array = { 'H', 'e', 'l', 'l', 'o' };
// 根据字符数组来创建字符串
// 类名称 对象名 = new 类名称(构造参数);
String str1 = new String(array);
System.out.println(str1); // Hello
// 根据字符数组的一部分来创建字符串
String str2 = new String(array, 2, 3);
System.out.println(str2); // llo
// 如果指定的索引或者个数超出了合理范围,那么将会发生索引越界异常,错误写法:
// String str3 = new String(array, 100, 200);
// 直接赋值
String str4 = "Hello, World!";
System.***out\***.println(str4); // Hello, World!
}
}
字符串池
由于字符串是对象,而又特别常用,所以为了解决内存中存在大量重复字符串对象的问题,引入了“字符串池”的设计来节省内存。关于字符串池的几点描述:
l 字符串池位于堆当中,里面保存了字符串的若干地址信息。
l 字符串池中绝对不会存放多个重复的字符串。
l 直接使用双引号的字符串默认在池中,而new出来的字符串默认不在池中。
public class Demo04StringPool {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true
char[] array = { 'H', 'e', 'l', 'l', 'o' };
String str3 = new String(array);
String str4 = new String(array);
System.out.println(str3 == str4); // false
System.out.println(str1 == str3); // false
}
}
字符串内容不可变
字符串对象一旦被创建,内容不可改变。下面的代码中,改变的是字符串对象名(其实是一个局部变量)中保存的地址值。
public class Demo05StringEternal {
public static void main(String[] args) {
String str = "Hello";
System.out.println(str); // Hello
str = "World";
System.out.println(str); // World
String str2 = "Love";
str2 += "Java";
System.out.println(str2); // LoveJava
}
}
比较方法
要想进行字符串的内容比较,不能使用==进行地址比较,应该使用两个方法:
l public boolean equals(Object obj):区分大小写的内容比较,参数是需要比较的另一个对象(和谁比)。备注:Object类型代表可以接收任意类型,包括字符串。
l public boolean equalsIgnoreCase(String str):忽略大小写的内容比较。
public class Demo01StringEquals {
public static void main(String[] args) {
char[] array = { 'a', 'b', 'c' };
String str1 = new String(array);
String str2 = "abc";
System.out.println(str1 == str2); // false
// 进行内容比较,应该使用equals方法
// 拿着str1和str2进行内容比较。
boolean same = str1.equals(str2);
System.out.println(same); // true
System.out.println(str2.equals(str1)); // true
System.out.println("================");
String str3 = null;
System.out.println("abc".equals(str3)); // false,正确,没有异常
// System.out.println(str3.equals("abc")); // 错误写法!发生异常!
System.out.println("================");
String str4 = "Hello";
String str5 = "hello";
System.out.println(str4.equals(str5)); // false
System.out.println("Hello".equals("hello")); // false
System.out.println(str4.equalsIgnoreCase(str5)); // true
}
}
练习:模拟登录
使用equals和equalsIgnoreCase方法,即可模拟简单的登录场景。注意两点问题:
l 用户名忽略大小写
l 密码区分大小写
public class Demo02Login {
public static void main(String[] args) {
// 首先需要注册的时候,指定一个正确的用户名,和正确的密码
String registUsername = "admin"; // 注册时候正确的用户名
String registPassword = "123456abc"; // 注册时候正确的密码
// 需要键盘输入两个字符串
Scanner sc = new Scanner(System.in); // 创建
System.out.println("请输入用户名:");
String inputUsername = sc.next(); // 获取键盘输入的用户名字符串
System.out.println("请输入密码:");
String inputPassword = sc.next(); // 获取键盘栓书的密码字符串
// 判断一下,两个条件:
// A. 输入的用户名和注册时候的正确用户名必须一样
// (用户名一般不区分大小写,内容判断应该使用equalsIgnoreCase)
// B. 输入的密码和注册时候的正确密码也要一样
// (密码一定是严格区分大小写,所以内容判断应该使用equals)
// A和B两个条件都要同时满足才行:&&
// 既然要判断,肯定使用选择结构:if
if (inputUsername.equalsIgnoreCase(registUsername) &&
inputPassword.equals(registPassword)) {
System.out.println("欢迎使用!");
} else {
System.out.println("登录失败!退出!");
}
}
}
练习:模拟登录(重试)
如果加入重试次数限制为3次的功能,则需要使用循环:
public class Demo03LoginRetry {
public static void main(String[] args) {
// 首先需要注册的时候,指定一个正确的用户名,和正确的密码
String registUsername = "admin"; // 注册时候正确的用户名
String registPassword = "123456abc"; // 注册时候正确的密码
// 需要键盘输入两个字符串
Scanner sc = new Scanner(System.in); // 创建
for (int i = 1; i <= 3; i++) {
System.out.println("请输入用户名:");
String inputUsername = sc.next(); // 获取键盘输入的用户名字符串
System.out.println("请输入密码:");
String inputPassword = sc.next(); // 获取键盘栓书的密码字符串
if (inputUsername.equalsIgnoreCase(registUsername) &&
inputPassword.equals(registPassword)) {
System.out.println("登录成功!欢迎使用!");
// 如果登录成功,不应该再循环,应该退出喜欢
break;
} else { // 如果登录失败
// 区分一下是不是最后一次
// 如果是最后一次,那么就提示重试次数耗尽
// 如果不是最后一次,那么提示剩余次数
if (i == 3) { // 如果是最后一次
System.out.println("重试次数耗尽,你是盗号的吧?哥屋恩!");
} else { // 如果不是最后一次
System.out.println("剩余次数" + (3 - i) + "次,请重试。");
}
}
} // for
}
}
替换方法
如果希望替换字符串当中的内容,则可以使用方法:
l public String replace(CharSequence oldStr, CharSequence newStr)
其中CharSequence现阶段可以简单地认为就是String字符串。而oldStr代表需要被替换的老字符串,newStr代表需要替换成为的新字符串。
注意,replace方法会将结果作为一个新的字符串返回。
public class Demo04StringReplace {
public static void main(String[] args) {
String str1 = "How do you do?";
String str2 = str1.replace("o", "*");
System.out.println(str1); // How do you do?
System.out.println(str2); // H*w d* y*u d*?
System.out.println("===============");
String str3 = "会不会玩儿啊!你大爷!真是服了!";
String str4 = str3.replace("你大爷", "***");
System.out.println(str4); // 会不会玩儿啊!***!真是服了!
}
}
切割方法
如果希望将字符串按照指定的标记切分成为若干段儿,可以使用方法:
l public String[] split(String regex):参数regex代表切割标记,返回值是字符串切割后成为的字符串数组。
注意,强烈推荐现阶段使用英文逗号作为切割标记,不要使用英文句点,因为英文句点在正则表达式(今后学习)中有特殊含义。
public class Demo05StringSplit {
public static void main(String[] args) {
String str1 = "AAA,BBB,CCC";
String[] array1 = str1.split(",");
System.out.println("数组的长度:" + array1.length);
for (int i = 0; i < array1.length; i++) {
System.out.println(array1[i]);
}
System.out.println("================");
String str2 = "aaa.bbb.ccc";
String[] array2 = str2.split("\\.");
System.out.println("数组的长度:" + array2.length);
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
}
}
集合
概述
集合与数组类似,也是一种引用类型,也可以存放很多个数据。但是数组的长度不可改变,而集合的长度是可变的,所以使用更加方便。
集合有很多种,现阶段我们学习最为简单的java.util.ArrayList集合。
构造方法
创建一个ArrayList,最常用的就是默认无参数的构造方法:
l public ArrayList()
创建集合的时候要注意尖括号中要指定泛型,也就是里面存放的都是统一的什么数据类型。注意,泛型只能是引用类型,不能是基本类型。
public class Demo01ArrayList {
public static void main(String[] args) {
// 创建一个集合,存放的全都是String字符串类型的数据
ArrayList<String> list1 = new ArrayList<>();
// 泛型只能写引用类型,不能写基本类型,下面是错误写法!
// ArrayList<int> list2 = new ArrayList<>();
}
}
常用方法
集合主要是用来存储数据的,所以常用的方法有:
l public boolean add(E element):向集合中放入一个数据对象,参数是被放入的对象,返回值一定为true。
l public E get(int index):从集合中取出一个对象,参数是对象的索引值(从0开始),返回值是获取的对象。
l public int size():获取集合的长度,返回值是集合的长度int数字,是可以变化的。
public class Demo02ArrayListMethod {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
System.out.println(list); // []
// 向集合当中添加元素:add
list.add("鹿晗");
list.add("吴亦凡");
list.add("张艺兴");
System.out.println(list); // [鹿晗, 吴亦凡, 张艺兴]
boolean success = list.add("关晓彤");
System.out.println("添加元素是否成功:" + success); // true
System.out.println(list); // [鹿晗, 吴亦凡, 张艺兴, 关晓彤]
System.out.println("====================");
String name = list.get(1); // 获取第1号元素
System.out.println(name); // 吴亦凡
System.out.println(list); // [鹿晗, 吴亦凡, 张艺兴, 关晓彤]
System.out.println("====================");
System.out.println("集合的长度:" + list.size()); // 4
list.add("赵丽颖");
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("玛尔扎哈");
System.out.println("集合的长度:" + list.size()); // 8
}
}
练习:遍历集合字符串
public class Demo03ArrayListEach {
public static void main(String[] args) {
ArrayList<String> list = **new** ArrayList<>();
// 添加:add
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("玛尔扎哈");
System.out.println(list);
// 遍历输出每一个字符串对象
// 集合的长度获取:size()
for (int i = 0; i < list.size(); i++) {
// 获取一个当前元素:get(int)
String name = list.get(i);
System.out.println(name);
}
}
}
存储基本类型
集合的泛型必须是引用类型,不能是基本类型。如果一定要存储基本类型,需要使用对应的包装类(都位于java.lang包下)。基本类型与包装类的对应关系如下:
l byte Byte
l short Short
l int Integer
l long Long
l float Float
l long Long
l char Character
l boolean Boolean
public class Demo04ArrayListWrapper {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
list.add(300);
int result = list.get(1);
System.out.println(result); // 200
}
}
存储自定义类型
集合可以存储任何引用类型,包括自定义的Person、Student等各种类。
public class Demo05ArrayListCustom {
public static void main(String[] args) {
// 首先创建几个Person对象
Person one = new Person("迪丽热巴", 18);
Person two = new Person("古力娜扎", 19);
Person three = new Person("玛尔扎哈", 200);
// 准备一个集合,用来存放多个Person对象
ArrayList<Person> list = new ArrayList<>();
// 将对象添加到集合当中
list.add(one);
list.add(two);
list.add(three);
// 遍历集合
for (int i = 0; i < list.size(); i++) {
Person per = list.get(i);
System.out.println("姓名:" + per.getName() + ",年龄:" + per.getAge());
}
}
}
练习:王者荣耀阵容展示
创建一个Hero类代表一位英雄,其中包含有名字和战斗力两个成员变量。使用一个集合存储5位英雄,然后遍历集合展示所有英雄的信息。
public class Demo01Hero {
public static void main(String[] args) {
// 创建一个集合,代表一个队伍,里面有5个英雄
ArrayList<Hero> team = new ArrayList<>();
// 创建5个英雄对象
Hero one = new Hero("蔡文姬", 200);
Hero two = new Hero("貂蝉", 250);
Hero three = new Hero("狄仁杰", 360);
Hero four = new Hero("东皇太一", 150);
Hero five = new Hero("韩信", 220);
// 将5个英雄对象添加到集合中
team.add(one);
team.add(two);
team.add(three);
team.add(four);
team.add(five);
// 遍历集合,进行阵容展示
for (int i = 0; i < team.size(); i++) {
// 获取当前英雄
Hero hero = team.get(i);
System.out.println("英雄名称:" + hero.getName() + ",攻击力:" + hero.getAttack());
}
}
}
练习:王者荣耀平均战斗力
仍然是一个集合当中存储5个英雄对象,但是要求不进行遍历,而是进行整个队伍平均战斗力的计算。
public class Demo02HeroAvg {
public static void main(String[] args) {
// 创建一个集合,代表一个队伍,里面有5个英雄
ArrayList<Hero> team = new ArrayList<>();
// 创建5个英雄对象
Hero one = new Hero("蔡文姬", 200);
Hero two = new Hero("貂蝉", 250);
Hero three = new Hero("狄仁杰", 360);
Hero four = new Hero("东皇太一", 150);
Hero five = new Hero("韩信", 220);
// 将5个英雄对象添加到集合中
team.add(one);
team.add(two);
team.add(three);
team.add(four);
team.add(five);
// 需要计算集合当中5个英雄平均战斗力。
// 如何计算战斗力的平均值?
// 1. 需要求出总和,也就是5个战斗力求和
// 2. 将战斗力的总和除以5,得到平均战斗力
int total = 0; // 战斗力总和
for (int i = 0; i < team.size(); i++) {
// 获取当前英雄
Hero hero = team.get(i);
int attack = hero.getAttack(); // 获取当前英雄的战斗力
total += attack; // 将战斗力累加到total当中
}
int avg = total / 5;
System.out.println("平均战斗力:" + avg);
}
}
IO流
概述与分类
Java中的IO流技术主要用于传输数据。典型的应用场景有:读写本地文件、上传下载文件等。按照数据传输的方向可以分为两种:
l 输入流(Input):即让数据进入应用程序中。
l 输出流(Output):即让数据离开应用程序。
按照数据的种类还可以有另外一种分法:
l 字节流:可以处理任何一种数据。
l 字符流:只能处理纯文本数据。
现阶段我们主要学习:字符输入流、字符输出流,来实现读写文件的功能。
FileWriter写文件
一般步骤
使用java.io.FileWriter类可以将数据写到文本文件当中,使用步骤为三步:
\1. 调用构造方法来创建一个FileWriter对象:
public FileWriter(String fileName):构造参数为文件路径名。
备注:创建对象时,如果文件不存在,则会自动创建该文件。
\2. 调用写数据的成员方法向文件中写入数据:
public void write(String text):参数为需要写入的字符串。
\3. 调用关闭的成员方法释放相关资源:
public void close()
三步走:“创、写、关”。
基本代码
public class Demo01FileWriter {
public static void main(String[] args) throws IOException {
// 1. 创建一个FileWriter对象
FileWriter fw = new FileWriter("file01.txt");
// 2. 调用write方法写数据
fw.write("Hello, World!");
// 3. 调用close方法关闭流
fw.close();
}
}
覆盖与追加
在使用FileWriter写文件的时候,如果指定的文件不存在,则会自动创建;如果文件已经存在,则会覆盖写入;如果不希望覆盖写入,实现追加写入的效果,则需要使用另一种重载的构造方法:
public FileWriter(String fileName, boolean append)
当第2个参数为true时,将会实现追加写入的效果。
public class Demo02FileWriter {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("file02.txt", true); // 创
fw.write("Java"); // 写
fw.close(); // 关
}
}
换行符
不同的平台使用不同的字符来代表换行:
l Windows操作系统:\r\n(两个字符)
l macOS操作系统:\r(早期)或者\n(现在)
l Linux操作系统:\n
public class Demo03FileWriterLine {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("file03.txt");
fw.write("迪丽热巴\r\n");
fw.write("\r\n"); // 代表一个Windows当中的换行符
fw.write("古力娜扎");
fw.close();
}
}
数字与文字的转换
其实所有数据在计算机底层都是二进制数字,所以一切数据都是数字。文件当中存放的其实并不是文字图形,而是数字序列。之所以打开文件可以看到文字图形,是因为软件对文件当中的数字进行了“翻译”。而翻译就需要一个对照词典,如:ASCII码表或Unicode码表。
ASCII当中的字符种类比较少;而Unicode包含了ASCII的内容,还扩展了数以万计的字符,可以涵盖全世界所有的文字。
FileWriter提供了一种重载形式的write方法,可以省去将文字翻译成为数字的步骤,直接在文件当中写对应的数字:
public void write(int ch):参数就是ASCII或Unicode表当中的数字,最小为0。
public class Demo04FileWriterCharacter {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("file04.txt");
fw.write("Hello");
fw.write(20013); // 代表“中”
fw.write("国");
fw.close();
}
}
write方法的重载
FileWriter中写数据的方法write其实有5种重载形式,可以在写字符到文件中时提供更加灵活的选择:
l public void write(int ch):参数为单个文字对应的数字(参考对照码表)。
l public void write(String str):写完整字符串。
l public void write(String str, int offset, int count):写字符串的一部分。
l public void write(char[] array):写完整字符数组。
l public void write(char[] array, int offset, int count):写字符数组的一部分。
public class Demo05FileWriterOverload {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("file05.txt");
// 写单个字符
fw.write(65); // A
fw.write(66); // B
fw.write(67); // C
fw.write(48); // 0
fw.write(97); // a
// ===================
// 写一个完整的字符串
fw.write("World\r\n");
fw.write("Hello");
// ===================
// 写一个字符串当中的部分内容
String str = "生前何必久睡,死后自会长眠。";
fw.write(str, 7, 6); // 死后自会长眠
// ===================
char[] array = { 'J', 'a', 'v', 'a', '!' };
// 写一个完整的字符数组
fw.write(array);
// 写一个字符数组的部分内容
fw.write(array, 1, 3);
fw.close();
}
}
FileReader读文件
一般步骤
使用java.io.FileReader类可以读取文本文件当中的字符(对应的ASCII或Unicode值)。使用步骤为:
\1. 调用构造方法来创建一个FileReader对象:
public FileReader(String fileName):参数仍然为文件的路径名。
备注:如果文件不存在,不会自动创建,而会发生异常。
\2. 调用读取数据的read方法获取单个字符:
public int read():返回值为单个字符对应的ASCII或Unicode值。
备注:每次调用都会读取下一个字符,如果没有更多,将返回-1值。
\3. 调用close方法关闭流:
public void close():释放相关资源。
由于并不确定文件当中包含多少个字符,所以次数不确定的重复代码,应该使用while循环。当得到-1值时退出循环。
读取单个字符
public class Demo02FileReaderWhile {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("file06.txt"); // 创建
// 下面的错误!因为每次read方法执行,都会读取一个新字符,直到-1为止。
// while (fr.read() != -1) {
// System.out.println(fr.read());
// }
int ch; // 代表一个单个字符(对应的ASCII/Unicode)
while ((ch = fr.read()) != -1) {
System.out.println((char) ch);
}
// (x + y) > z
fr.close(); // 关闭
}
}
读取字符数组
一个字一个字地读取,很明显效率很低。如果能够五个五个地批量读取,那么效率会有大幅度的提升。FileReader中提供另一种重载形式的read方法:
public int read(char[] buf):参数buf是用来装载多个字符结果的数组,返回值代表数组当中读取到的字符有效个数。
public class Demo04FileReaderArrayWhile {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("file07.txt");
char[] buf = new char[2];
int len; // 代表有效个数
while ((len = fr.read(buf)) != -1) {
String str = new String(buf, 0, len);
System.out.println(str);
}
fr.close();
}
}
读写数据的过程
Java程序并不会直接去操作硬盘,而是交给JVM;JVM再与操作系统交互;操作系统去操作硬盘。然而硬盘的速度比内存慢很多,所以如果每一个单个字符都去操作一次硬盘,性能非常低下。为了提高性能,应该使用数组来达到批量操作,缓冲的目的。
BufferedWriter
为了提高写文件的效率,java.io.BufferedWriter类内部包含了一个缓冲数组,可以大幅度提高FileWriter写数据的性能。
缓冲原理
BufferedWriter类内部包含一个长度为8192的char[]字符数组,每次写数据都是在向数组当中添加字符。如果数组满了,将会整体写到硬盘中;如果数组没满,那么在调用close方法的时候,也会将最后剩余的有效部分写到硬盘中。
基本使用
BufferedWriter类的构造方法可以将一个普通的FileWriter作为参数,而使用起来与FileWriter基本一样,性能却大幅度提高。构造方法:
public BufferedWriter(FileWriter fw):构造参数就是普通的FileWriter对象。
public class Demo01BufferedWriter {
public static void main(String[] args) throws IOException {
// 首先创建一个普通的FileWriter
FileWriter fw = new FileWriter("file08.txt");
// 将这个普通的FileWriter对象传递给BufferedWriter构造方法即可。
BufferedWriter bw = new BufferedWriter(fw);
// 后面bw的时候,和fw基本没有区别。
bw.write("Hello");
// 关闭流
bw.close();
}
}
newLine方法
除了性能比FileWriter更高之外,BufferedWriter还提供一个换行的方法:
public void newLine():该方法会根据操作系统的不同,自动选择写入\r\n或者\r或者\n。
public class Demo02NewLine {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("file09.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("Hello");
// bw.write("\r\n"); // 这种写法不太好,一旦换了操作系统,那么就要修改代码
bw.newLine(); // 自动根据操作系统选择对应的换行符
bw.write("World");
bw.close();
}
}
BufferedReader
缓冲原理
类似地,BufferedReader类内部包含一个长度为8192的char[]字符数组,读数据的时候将会一次最多读取8192个字符放在数组中,而read方法只是从数组中取出字符使用。如果数组被“取空”,会自动再次读取最多8192个字符供使用。从而达到减少硬盘读取次数的目的。
基本使用
BufferedReader类的构造方法可以将一个普通的FileReader作为参数,而使用起来与FileReader基本一样,性能却大幅度提高。构造方法:
public BufferedReader(FileReader fr):构造参数就是普通的FileReader对象。
readLine方法
除了性能比FileReader更高之外,BufferedReader还额外提供一个方法,可以读取一整行字符串:
public String readLine():返回值为一整行字符串内容,不包含换行符。
public class Demo04ReadLine {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("file11.txt");
BufferedReader br = new BufferedReader(fr);
// String line = br.readLine();
// System.out.println(line); // Hello
//
// line = br.readLine();
// System.out.println(line); // World
//
// line = br.readLine();
// System.out.println(line); // Java
//
// line = br.readLine();
// System.out.println(line); // null
// System.out.println(line == null); // true
String line; // 代表一行字符串
while ((line = br.readLine()) != **null**) {
System.out.println(line);
}
br.close();
}
}
练习
将集合中的字符串写到文件中
定义一个集合用于存储多个字符串,向其中添加一些字符串,然后将集合的所有字符串内容写到文件中。要求每个字符串独占一行。
public class Demo01FromListToFile {
public static void main(String[] args) throws IOException {
ArrayList<String> list = new ArrayList<>(); // 创建集合
// 加入字符串
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("玛尔扎哈");
BufferedWriter bw = new BufferedWriter(new FileWriter("练习1.txt"));
// 遍历集合
for (int i = 0; i < list.size(); i++) {
String str = list.get(i); // 当前字符串
bw.write(str);
bw.newLine(); // 不要忘记换行
}
bw.close(); // 不要忘记关闭流
}
}
将文件中的字符串读到集合中
与上一题相反,将文件中的字符串文本读取到集合当中,并且每一行文本作为集合中的一个字符串元素。
public class Demo02FromFileToList {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("练习1.txt"));
ArrayList<String> list = new ArrayList<>();
String line; // 代表一行字符串
while ((line = br.readLine()) != **null**) {
list.add(line); // 将读取得到的一行字符串绝蛮王到集合当中
}
br.close(); // 关闭流
// 遍历集合
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
反转藏头情诗
将文件当中的每一行字符串读取到集合当中,然后倒序写到另一个文件中,实现行与行之间的反转。
public class Demo03Reverse {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("练习2-表白信-之前.txt"));
ArrayList<String> list = new ArrayList<>();
String line; // 代表一行字符串
while ((line = br.readLine()) != null) {
list.add(line);
}
br.close(); // 关闭输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("练习2-表白信-之后.txt"));
for (int i = list.size() - 1; i >= 0 ; i--) {
bw.write(list.get(i));
bw.newLine();
}
bw.close();
}
}
方法参数与返回值
方法的参数和返回值可以是任何类型,无论是基本类型,还是引用类型中的数组、字符串甚至是自定义的类,都可以作为方法的参数和返回值。
参数
public class Demo01MethodParam {
public static void main(String[] args) {
method1(100);
System.out.println("============");
int[] arr = { 10, 20, 30 };
method2(arr);
System.out.println("============");
method3("How do you do?");
System.out.println("============");
Student xueSheng = new Student("赵丽颖", 20);
method4(xueSheng);
}
// 使用基本类型作为方法的参数,可以
public static void method1(int num) {
num += 20;
System.out.println(num); // 120
}
// 使用数组作为方法的参数,也可以
public static void method2(**int**[] array) {
System.out.println(array[0]); // 10
System.out.println(array[1]); // 20
System.out.println(array[2]); // 30
}
// 使用字符串作为方法的参数,还可以
public static void method3(String str) {
String result = str.replace("o", "*");
System.out.println(result); // H*w d* y*u d*?
}
// 使用自定义的类作为方法的参数,照样可以!
public static void method4(Student stu) {
// 姓名:赵丽颖,年龄:20
System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge());
}
}
返回值
public class Demo02MethodReturn {
public static void main(String[] args) {
int result1 = method1();
System.out.println(result1); // 100
System.out.println("==================");
int[] result2 = method*();
System.out.println(result2[0]); // 10
System.out.println(result2[1]); // 20
System.out.println("==================");
String result3 = method3();
System.out.println(result3.replace("o", "*")); // H*w d* y*u d*?
System.out.println("==================");
Student result4 = method4();
// 姓名:赵丽颖,年龄:20
System.out.println("姓名:" + result4.getName() + ",年龄:" + result4.getAge());
}
// 使用基本类型作为方法的返回值,可以
public static int method1() {
int num = 100;
return num;
}
// 使用数组类型作为方法的返回值,也可以
public static int[] method2() {
int[] array = { 10, 20 };
return array;
}
// 使用字符串作为方法的返回值,还可以
public static String method3() {
String str = "How do you do?";
return str;
}
// 使用自定义的类作为方法的返回值,仍然可以
public static Student method4() {
Student stu = new Student("赵丽颖", 20);
return stu;
}
}
综合案例
此前我们已经练习了根据集合当中的字符串对象读写文件,而本综合案例主要练习根据集合当中的自定义对象来读写文件。
场景介绍
很多网络游戏当中都有组队模式,例如魔兽世界、DotA、英雄联盟(LOL)、王者荣耀等,均为5人组队进行游戏。即使在现实生活中,打麻将、斗地主也都是多人进行的游戏。那么对于多人组队的游戏,典型的场景为:
\1. 有5个人都在玩游戏;
\2. 这5个人组成一个队伍;
\3. 这支队伍进行游戏;
\4. 队伍成员彼此欣赏,决定以后还要组队一起玩;
\5. 一段时间后大家再次组队游戏。
整体思路
\1. 看看有没有已经认识的老朋友;
\2. 如果没有,则找到5个新朋友,组成队伍;如果有,则队伍中直接为5个老朋友;
\3. 开始游戏,例如阵容展示、战斗力计算等;
\4. 如果这是新队伍,则保存5个新朋友的信息;如果这是老队伍,则无需操作;
\5. 结束游戏。
代码实现
具体的实现请参考相关源代码。但请注意,一定要首先把握整体流程,编写框架代码之后,再逐步编写具体的逻辑实现。
接口
现实生活中的接口举例
电源接口、USB接口,都具有一定范围之内的通用性。只要符合接口的标准,那么所有的设备都无需互相商量,可以享受通用性带来的遍历。
面向对象中的接口思想
接口是一种针对对象的公共约束力,可以将对象中的成员方法“剥离”出来,于是通过接口就可以只关心特定的一部分方法,而不关心接口之外的其他方法。
接口的格式与组成部分
在Java 9版本中,接口的定义格式为:
public interface 接口名称 {
// ...
}
其中可以包含的组成部分有:
\1. 抽象方法
\2. 常量
\3. 默认方法(Java 8)
\4. 静态方法(Java 8)
\5. 私有方法(Java 9)
今天我们主要学习接口中的抽象方法,其他的组成部分今后学习。
接口的抽象方法定义
抽象方法的定义格式:
public abstract 返回值类型 方法名称(参数类型 参数名称);
注意:
\1. 接口中的抽象方法,修饰如果写必须是:public abstract
\2. 接口中的抽象方法,修饰符可以省略不写,默认就是:public abstract
\3. 抽象方法只有方法头,不能有方法体大括号。
接口实现类的定义
格式:
public class 实现类名称 implements 接口名称 {
//覆盖重写所有抽象方法
}
覆盖重写抽象方法的步骤:
\1. 将接口当中的抽象方法抄写过来
\2. 去掉abstract关键字
\3. 写上大括号方法体
接口与实现类的使用
创建格式:
接口名称 引用名 = new 实现类名称();
调用格式:
引用名.抽象方法名(参数);
注意事项:
\1. 左边是接口类型,那么只能调用接口当中定义好的内容,不能调用右侧实现类当中特有的内容。(接口隔离)
\2. 当调用接口当中的抽象方法时,真正进行运行的是右侧new的时候类的具体方法内容。
\3. 总结记住一句话:调用的时候看左边,运行的时候看右边。
Lambda
冗余的接口实现类
接口在Java开发当中的作用非常重要。但有的时候为了使用接口而不得不去创建一个实现类对象,这种操作其实没有必要。
例如接口:
public interface Calculator {
int sum(int a, int b);
}
对应的实现类:
public class CalculatorImpl implements Calculator {
public int sum(int a, int b) {
int result = a + b;
return result;
}
}
使用接口的时候需要创建实现类对象才能使用:
public class Demo01Calc {
public static void main(String[] args) {
// 首先使用接口的格式来创建了一个计算器对象
Calculator calculator = new CalculatorImpl();
// 将计算器对象交给method方法去使用
method(calculator);
}
// 参数是接口类型:计算器接口
public static void method(Calculator calculator) {
int result = calculator.sum(10, 20);
System.out.println("结果是:" + result);
}
}
体验Lambda表达式
使用Lambda,在接口不变的情况下,不需要实现类,直接就可以使用接口,而且写法非常简单:
public class Demo02CalcLambda {
public static void main(String[] args) {
method((x, y) -> x + y);
}
public static void method(Calculator calculator) {
int result = calculator.sum(100, 200);
System.out.println("结果是:" + result);
}
}
理解Lambda的语义
Lambda表达式:(x, y) -> x + y。方法需要一个Calculator接口类型的参数,而Lambda表达式就是充当了Calculator接口类型的参数。理解:
\1. Lambda表达式前面的小括号,其实就是接口抽象方法的小括号。
\2. 箭头代表拿着小括号的数据做什么事情,是一个指向的动作。
\3. 箭头后面就代表拿到了参数之后做什么事情。
函数式接口
Java当中使用Lambda表达式的前提是:必须有“函数式接口”。
概念:有且仅有一个抽象方法的接口,叫做函数式接口。
可以使用可选的@FunctionalInterface来检测接口是否为函数式接口,但是有没有这个注解并不影响函数式接口的定义,这只是一种可选的检测手段。
@FunctionalInterface
public interface Calculator {
int sum(int a, int b);
}
Lambda的标准格式
Lambda的格式就是为了将抽象方法,翻译成为以下三点:
\1. 一些参数(方法参数)
\2. 一个箭头
\3. 一些代码(方法体,大括号)
例如将抽象方法:public abstract int sum(int a, int b);
翻译成为:(int a, int b) -> { return a + b; }
Lambda的上下文推断
Lambda表达式要想使用,一定要有函数式接口的推断环境:
\1. 要么通过方法的参数类型来确定是哪个函数式接口
\2. 要么通过赋值操作来确定是哪个函数式接口
public class Demo03Lambda {
public static void main(String[] args) {
// 调用方法的时候,参数类型是函数式接口,所以Lambda可以推断出来是哪个接口
method( (int a, int b) -> { return a + b;} );
System.out.println("======================");
// 也可以根据赋值语句左侧的类型来进行Lambda上下文推断
Calculator param = (int a, int b) -> { return a + b; };
method(param);
// 错误写法!没有上下文环境,Lambda就无法推断是哪个函数式接口
// (int a, int b) -> { return a + b; };
}
public static void method(Calculator calculator) {
int result = calculator.sum(100, 200);
System.out.println("结果是:" + result);
}
}
Lambda的简便格式
在Lambda表达式当中,凡是可以推导的,都是可以省略的。
\1. Lambda表达式当中的参数类型可以省略不写。
\2. 如果参数有且只有一个,那么小括号可以省略。
\3. 如果语句只有一个,那么大括号和return也可以省略。
public class DemoLambdaSimple {
public static void main(String[] args) {
// 标准格式:
method( (int x) -> { return ++x; } );
// 省略参数类型:
method( (x) -> { return ++x; } );
// 省略参数小括号:
method( x -> { return ++x; } );
// 省略大括号和return关键字:
method( x -> ++x );
}
public static void method(MyInter inter) {
int result = inter.singlePlus(10);
System.out.println("结果:" + result);
}
}
复习
静态方法与成员方法
我们曾经学习过两种方法的定义,一种是静态方法,格式为:
public static 返回值类型 方法名称(参数类型 参数名称) {
// 方法体
}
另一种为成员方法,也就是去掉了static关键字,格式为:
public返回值类型 方法名称(参数类型 参数名称) {
// 方法体
}
这两种方法的区别是什么?现阶段我们主要了解方法的调用方式不同:
l 成员方法的调用必须要创建一个对象才可以,格式为:
类名称 对象名 = new 类名称();
对象名.成员方法名(参数);
l 而静态方法的调用不需要借助对象,可以直接通过类名称来调用,格式为:
类名称.静态方法名(参数);
方法引用
冗余的Lambda
在某些场景之下,Lambda表达式要做的事情,其实在另外一个地方已经写过了,那么此时如果通过Lambda表达式重复编写相同的代码,就是浪费。例如已经有了一个厨师类:
public class Cook {
// 这是一个静态方法,可以通过类名称进行调用
public static void makeFood(String food) {
System.out.println("将" + food + "做成可口的食物。");
}
}
还有一个函数式接口:
// 保姆接口
@FunctionalInterface
public interface Sitter {
// 保姆的工作,就是生米煮成熟饭
void work(String food);
}
但是在使用函数式接口的时候,Lambda的内容其实和厨师Cook类的方法体重复了:
public class Demo03Lambda {
public static void main(String[] args) {
hireSitter(food -> System.out.println("将"+food+"做成可口的食物。"));
}
// 雇佣一个保姆,并且让他去做饭
public static void hireSitter(Sitter sitter) {
sitter.work("白菜");
}
}
体验方法引用
此时Lambda所做的事情,其实在Cook类的方法当中已经做过了,那么就可以使用方法引用。例如:
public class Demo03Lambda {
public static void main(String[] args) {
hireSitter( Cook::makeFood );
}
// 雇佣一个保姆,并且让他去做饭
public static void hireSitter(Sitter sitter) {
sitter.work("白菜");
}
}
类名称引用静态方法
通过类名称引用静态方法的格式为:
类名称::静态方法名
这将可以作为Lambda表达式的替代者,更加简单。
对象名引用成员方法
另一种通过对象名称来引用成员方法的格式为:
对象名::成员方法名
例如Cook类当中有一个成员方法(并非静态方法):
public class Cook {
// 这是一个成员方法,必须要有对象才能调用
public void makeFood(String food) {
System.out.println("将" + food + "做成可口的饭菜!");
}
}
那么引用其中的成员方法时,必须要有一个对象才行。例如:
public class Demo02MethodRef {
public static void main(String[] args) {
Cook cook = new Cook(); // 创建了一个厨师对象
// 引用了cook对象当中的成员方法makeFood
method( cook::makeFood );
}
public static void method(Sitter sitter) {
sitter.work("土豆");
}
}
Stream流
冗余的集合for遍历
在使用集合进行某些操作时,一次又一次的for循环遍历虽然简单,但是步骤冗余:
public class Demo01ArrayList {
public static void main(String[] args) {
// 首先创建一个集合,然后放入指定格式的字符串
ArrayList<String> recordList = new ArrayList<>();
recordList.add("赵丽颖,98");
recordList.add("鹿晗,95");
recordList.add("宋小宝,87");
// 应该拆分一下每个字符串,只要逗号后面的
ArrayList<String> scoreList = new ArrayList<>(); // 保存的多个字符串是:"98"、"95"、"87"
for (int i = 0; i < recordList.size(); i++) {
String record = recordList.get(i); // 当前字符串:"赵丽颖,98"
String[] array = record.split(",");
String score = array[1]; // "98"
scoreList.add(score);
}
// 将字符串"98"转换成为int数字98
ArrayList<Integer> numList = new ArrayList<>(); // 保存的多个数字是:98、95、87
for (int i = 0; i < scoreList.size(); i++) {
String score = scoreList.get(i); // "98"
int num = Integer.parseInt(score);
numList.add(num);
}
// 过滤一下,筛选,只要大于90的,小于等于90的不要
ArrayList<Integer> resultList = new ArrayList<>(); // 最终结果集合
for (int i = 0; i < numList.size(); i++) {
int num = numList.get(i); // 98
if (num > 90) {
resultList.add(num);
}
}
// 最后遍历最终结果集合,打印输出
for (int i = 0; i < resultList.size(); i++) {
int result = resultList.get(i);
System.out.println(result);
}
}
}
体验流式写法
如果使用Java 8当中新加入的Stream API,代码篇幅将会减少很多:
public class Demo02ArrayListStream {
public static void main(String[] args) {
// 首先创建一个集合,然后放入指定格式的字符串
ArrayList<String> recordList = new ArrayList<>();
recordList.add("赵丽颖,98");
recordList.add("鹿晗,95");
recordList.add("宋小宝,87");
// Stream API更优写法!
recordList.stream().map(s -> s.split(",")[1]).map(Integer::parseInt*).filter(n -> n > 90).forEach(System.out::println);
}
}
流式思想概述
Stream API的思想就是流水线加工
获取流
一个流,就是一个java.util.stream.Stream
集合名称.stream()
也可以通过一个数组(元素必须为引用类型)来获取一个流,格式为:
Stream.of(数组名称)
例如:
public class Demo03GetStream {
public static void main(String[] args) {
// 1. 根据集合获取流
ArrayList<String> list = new ArrayList<>();
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("玛尔扎哈");
Stream<String> streamA = list.stream();
// 2. 根据数组获取流,数组当中的元素必须是引用类型才行
String[] arrayStr = { "Hello", "World", "Java" };
Stream<String> streamB = Stream.of(arrayStr);
Integer[] arrayInteger = { 10, 20, 30 };
Stream<Integer> streamC = Stream.of(arrayInteger);
}
}
映射map方法
获取流之后,可以使用映射方法:map(用于转换的Lambda表达式)。映射:就是将一个对象转换成为另一个对象,把老对象映射到新对象上。
public class Demo04StreamMap {
public static void main(String[] args) {
// 这个集合当中存放的是字符串类型
ArrayList<String> list = new ArrayList<>();
list.add("100");
list.add("200");
list.add("300");
Stream<Integer> streamA = list.stream().map((String str) -> {
int num = Integer.parseInt(str);
return num;
});
Stream<Integer> streamB = list.stream().map(str -> {
int num = Integer.parseInt(str);
return num;
});
Stream<Integer> streamC = list.stream().map(str -> {
return Integer.parseInt(str);
});
Stream<Integer> streamD = list.stream().map(Integer::parseInt);
System.out.println("========================");
ArrayList<String> list2 = **new** ArrayList<>();
list2.add("赵丽颖,98");
list2.add("鹿晗,95");
list2.add("宋小宝,87");
Stream<String> stream1 = list2.stream().map((String str) -> {
String[] array = str.split(",");
String result = array[1];
return result;
});
Stream<String> stream2 = list2.stream().map(s -> {
String[] array = s.split(",");
String result = array[1];
return result;
});
Stream<String> stream3 = list2.stream().map(s -> {
String[] array = s.split(",");
return array[1];
});
Stream<String> stream4 = list2.stream().map(s -> {
return s.split(",")[1];
});
Stream<String> stream5 = list2.stream().map(s -> s.split(",")[1]);
}
}
过滤filter方法
如果希望对流当中的元素进行过滤,可以使用过滤方法:
filter(能产生boolean结果的Lambda):如果参数Lambda产生了true,则要元素;如果产生了false,则不要元素。
public class Demo05StreamFilter {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(90);
list1.add(85);
list1.add(60);
Stream<Integer> stream1 = list1.stream().filter((Integer num) -> {
boolean b = num > 80;
return b;
});
Stream<Integer> stream2 = list1.stream().filter(num -> {
boolean b = num > 80;
return b;
});
Stream<Integer> stream3 = list1.stream().filter(num -> {
return num > 80;
});
Stream<Integer> stream4 = list1.stream().filter(n -> n > 80);
System.out.println("==================");
ArrayList<String> list2 = new ArrayList<>();
list2.add("赵丽颖");
list2.add("赵丽颖");
list2.add("宋小宝");
Stream<String> streamA = list2.stream().filter((String str) -> {
// boolean b = "赵丽颖".equals(str);
boolean b = str.equals("赵丽颖");
return b;
});
Stream<String> streamB = list2.stream().filter(s -> {
boolean b = s.equals("赵丽颖");
return b;
});
Stream<String> streamC = list2.stream().filter(s -> {
return s.equals("赵丽颖");
});
Stream<String> streamD = list2.stream().filter(s -> s.equals("赵丽颖"));
}
}
遍历forEach方法
如果希望在流当中进行元素的遍历操作,可以使用forEach方法:
forEach(Lambda表达式):意思是,对流当中的每一个元素都要进行操作。参数Lambda表达式必须是一个能够消费一个参数,而且不产生数据结果的Lambda。
public class Demo06StreamForEach {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("玛尔扎哈");
Stream<String> streamA = list.stream();
streamA.forEach((String str) -> {
System.out.println(str);
});
System.out.println("==============");
list.stream().forEach((String str) -> {
System.out.println(str);
});
System.out.println("==============");
list.stream().forEach(str -> {
System.out.println(str);
});
System.out.println("==============");
list.stream().forEach(System.out::println);
}
}
并发流
流当中的元素如果特别多,那么只有一个人在逐一、挨个儿处理,肯定比较慢,费劲。如果对流当中的元素,使用多个人同时处理,这就是“并发”。
获取并发流有两种方式:
\1. 直接获取并发流:.parallelStream()
\2. 已经获取了普通流,然后升级成为并发流:.stream().parallel()
public class Demo07StreamParallel {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
list.add("Hello-" + i);
}
// 这是只有一个人在做打印输出的操作
// list.stream().forEach(System.out::println);
// 获取一个并发流
list.parallelStream().forEach(System.out::println);
list.stream().parallel().forEach(System.out::println);
}
}
模块化
思想概述
Java 9中引入的模块化技术可以达到两种效果:
\1. 所有资源都在一起,体积臃肿,通常并不需要其中所有的内容。有了模块化,拆分成为若干个小模块,可以只选择需要的。
\2. 可以精确控制package包级别的访问权限,控制模块之外的访问情况。
认识module-info.java文件
在Java的一个模块中,需要使用module-info.java文件描述模块信息。
模块之前的依赖关系以及权限访问控制情况为:
模块的基本使用
可以通过下面的操作将Eclipse的Java Project改造成一个Java 9的模块:
\1. 点击项目名称,右键选择Configure,点击Create module-info.java选项。
\2. 在生成的module-info.java文件中编写导出exports和依赖requires等信息。
\3. 点击项目名称,右键选择Build Path,点选最后一项Configure Build Path。在Projects标签中选中Modulepath,然后右侧Add所需要依赖的模块。

浙公网安备 33010602011771号