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所需要依赖的模块。

posted @ 2021-06-22 21:53  Lucky_龍  阅读(97)  评论(0)    收藏  举报