Java语言基础
初识计算机和Java语言
计算机体系结构
计算机的基本概念
计算机是一种用于高级计算,使用广泛的设备,主要由计算机硬件和计算机软件两个部分组成
硬件
常见的主要硬件
计算机硬件(Computer Hardware)主要包括:中央处理器(CPU)、内存、硬盘、输入输出设备、主板、机箱和电源等辅助设备
主要硬件详解
CPU:中央处理器(Central Processing Unit),功能主要是解释计算机指令以及处理计算机软件中的数据
内存:内存储器(Memory),用于暂时存放CPU中的运算数据以及与硬盘外部存储器交换的数据。优点是内存中的数据能被CPI直接访问,效率比较高。缺点是内存容量比较小且不能永久存储,一旦断电会造成数据的丢失,因此要记住ctrl+s快捷键进行及时保存
硬盘:主要用于永久存放数据内容,容量大且断电不丢失。CPU不能直接访问硬盘中数据,若希望访问则需要先加载到内存中
输入输出设备
软件
常见的主要软件
计算机软件(Computer Software)可分为系统软件和应用软件,系统软件就是操作系统,是其他软件的基础。主流的操作系统有Win/Unix(收费)/Linux(免费、开源)/IOS/Android
计算机的体系结构
Java语言概述
计算机语言的发展
第一代计算机语言:机器语言,二进制代码,最开始用穿孔卡片
第二代计算机语言:汇编语言,使用助记符表示一条机器指令,如:ADD、SUB等
第三代计算机语言:高级语言,C++、Java等
Java语言的发展
sun公司 詹姆斯.高斯里 喝咖啡 爪洼岛 Java
Java语言的主要版本
桌面应用:
JavaSE(Java Platform,Standard Edition)称之为"Java平台的标准版",是Java平台的基础。JavaSE包含了运行Java应用所需要的基础环境和核心类库。JavaSE定义的基于桌面应用的基础类库可以编写出类似于Office那样丰富多彩的桌面应用。
企业应用:
JavaEE(Java Platform,Enterprise Edition)称之为”Java平台企业版“。JavaEE构建在JavaSE基础之上,用于构建企业级应用。
移动应用:
JavaME(Java Platform,Micro Edition)称之为Java平台微型版。最开始为机顶盒、移动电话和PDA等嵌入式消费电子设备提供Java解决方案。随着以Java为核心编程语言的Android智能平台的迅速普及,JavaME已经走向淘汰。
开发环境的搭建和使用
开发工具的下载和安装
下载:www.sun.com官网下载或百度搜索官网下载JDK即可(不推荐下载或使用最新版本)
安装:若下载的是安装版,则一路点击下一步安装即可;若下载的是绿色版,则直接解压即可。注意无论安装版还是绿色版的路径都不要有中文!
相关概念
几个相关概念
JDK(Java Development Kit):称为Java开发工具包,Java开发人士需要下载和安装JDK,目前主流版本为JDK11
JRE(JavaSE Runtime Environment):称为JavaSE运行时环境,提供了运行Java应用程序所必须的软件环境等。无论开发还是运行Java应用都必须安装
javac.exe:编译器,主要用于将高级Java源代码翻译成字节码文件
java.exe:解释器,主要用于启动JVM对字节码文件进行解释并执行
JDK、JRE、JVM之间的关系
JDK的目录结构
bin目录 - 该目录下主要存放JDK的各种工具命令
conf目录 - 该目录下主要存放JDK的相关配置文件
include目录 - 该目录下主要存放一些平台的头文件
jmods目录 - 该目录下主要存放JDK的各种模块
legal目录 - 该目录主要存放了JDK各模块的授权文档
lib目录 - 该目录下主要存放了JDK工具的一些补充jar包和源代码(源代码在src.zip压缩包中)
编写Java程序的流程
Java开发工具
文本编辑器(TE,Text Editor)
集成开发环境(IDE,Integrated Development Environment)
用文本编辑器(TE)编写Java程序的流程
step0、使用Notepad++记得要在设置–>首选项–>新建中将编码改为和DOS属性中一样的编码(默认语言也可以在这里改)
step1、新建文本文档,将文件扩展名由xxx.txt修改为xxx.java
step2、使用文本编辑器的方式打开文件,编写Java代码后进行保存
step3、启动dos窗口,并切换到.java文件所在的路径中(启动dos窗口可以win+R键输入cmd进入后用cd指令进入.java文件所在路径后编译运行;也可以在所在文件夹的路径框中直接输cmd即可打开dos窗口并进入当前所在文件夹)
step4、使用javac xxx.java进行编译(注意这里不能省去后缀),生成xxx.class的字节码文件
step5、使用java xxx进行解释执行(这里不需要文件后缀了),打印最终运行结果
常见的错误和简化的编译运行
常见的错误
1、拼写错误
2、非法字符:’\uff1b’ —> 通常是因为出现了中文标点符号,要用英文标点符号
简化的编译运行
Java11新特性之简化的编译运行
在dos窗口中进去.java文件夹后,可以直接使用java xxx.java进行编译运行,打印最终结果(慎用)
常用的快捷键和注释
常用快捷键
ctrl+s保存、ctrl+c复制、ctrl+v粘贴、ctrl+a全选、ctrl+x剪切、ctrl+z撤销、ctrl+f搜索
windows+d回到桌面、windows+e打开计算机、windows+l锁屏、windows+r打开运行,输入cmd后回车就会启动dos窗口、windows+tab切换任务、alt+tab切换任务(用这个)、clt+alt+delete选择启动任务管理器、ctrl+shift+esc直接启动任务管理器
注释
// 单行注释
/* */ 多行注释,注意:多行注释不允许嵌套使用
/** */ 文档注释,是一种支持提取的注释
环境变量的配置
环境变量基本概念:通常情况下可执行文件(如java.exe)只能在该文件所在的路径中使用,为了使得该可执行文件可以在任意路径中使用,则需要将该文件所在的路径信息配置到环境变量Path中(若我们不配置环境变量要使用javac.exe和java.exe编译执行java代码文件,需将java代码文件放在和这两个可执行文件相同文件夹中)。
配置方式:百度一下,你就知道
注意事项:切记Path变量原来的变量值不要删除(寻找顺序会从前往后依次寻找),配置完毕后记得重启dos窗口
新建名为JAVA_HOME的系统变量:新建变量路径为到bin目录之前,这样是为了以后更新JDK时我们只需要修改JAVA_HOME这个系统变量的路径即可(bin之后的路径所有版本的JDK都是一样的)
跨平台原理
Java"一次编译,到处运行"的特点是由JavaVM(Java虚拟机)实现的,通过Java虚拟机,可以实现相同的字节码文件编译成不同的机器指令在不同的系统上运行
变量和数据
变量的概念和基本使用
变量的基本概念
变量即“变化的量”。变量可以存放单一数据。声明一个变量的本质是在内存中申请一个存储单元,由于该存储单元中的数据内容可以发生改变,因此称为变量。
定义变量时需要指定变量存储的数据是何种数据类型,不同数据类型的变量在内存中开辟出的内存空间不同。
变量的声明和使用
变量的声明方式
数据类型 变量名 = 初始值;
使用变量的注意事项
- 使用变量之前要先声明
- 使用变量之前需要初始化
- 变量不能重复声明
标识符的命名规则
- 由数字、字母、下划线和$组成,其中数字不能开头
- 不能使用Java语言的关键字(Java语言用于表示特殊含义的单词)
- 区分大小写,长度没有限制但不宜过长
- 尽量做到见名知意,支持中文但不推荐使用
- 标识符可以给类、变量、属性、方法、包起名字
变量输入输出的案例实现
代码实现
/*
编程实现变量的输入和输出
*/
import java.util.Scanner;
public class VarIOTest {
public static void main(String[] args) {
//1、声明两个变量
int age;
String name;
//2、创建一个扫描器来扫描键盘输入的内容,System.in代表键盘输入
Scanner sc = new Scanner(System.in);
//3、通过扫描器读取一个字符串数据放入变量name中
name = sc.next();
//4、通过扫描器读取一个整数放入变量age中
age = sc.nextInt();
//5、打印变量
System.out.println("name = " + name);
System.out.println("age = " + age);
}
}
输入输出案例的优化和手册介绍
1.代码的优化
-
变量随使用随声明(避免先声明变量占用内存空间)
String name = sc.next();
int age = sc.nextInt();
-
尽可能减少重复代码
System.out.println("name = " + name + "age = " + age);
2.官方库的使用:JDK中带有大量的API类,这些是Java系统带来的工具库,使用这些可以大大简化编程,提高开发效率,具体的API类功能可以参阅Java的参考手册。
数据类型的分类
Java中数据类型主要有两大类
-
基本数据类型(记住)
byte、short、int、long、float、double、boolean、char
-
引用数据类型
数组、类、接口、枚举、标注
进制
常见的进制
十进制(逢十进一,权重为10)、二进制(逢二进一,权重为2,最高位用于表示符号位,最高位为0表示非负数,最高位为1表示负数)、八进制和十六进制(其实都是二进制的简写)
Java中进制的表示方法
- 二进制:在二进制数的前面加0b表示此数为二进制数,如45=0b101101或0B101101
- 八进制:在八进制数的前面加0表示此数为八进制数
- 十六进制:在十六进制数的前面加0x表示此数为十六进制数
进制之间的转换
- 正数的十进制转换为二进制
- 方式一:整数部分用除2取余法(写法注意从左到右对应从下到上),小数部分用乘2取整法
- 方式二:拆分法,将十进制整数拆分为若干个二进制权重的和,有该权重下面写1,否则写0。如,45=32+8+4+1=0b 00101101
- 正数的二进制转换为十进制
- 加权法
- 负数的十进制转换为二进制
- 将十进制数的绝对值转换为二进制
- 按位取反(得到反码)再加1(得到补码)
- 负数的二进制转换为十进制
- 负数的十进制转换为二进制的逆过程
- 八进制、十六进制与二进制之间的转换
- 记住:二进制每三位对应的十进制数是一位八进制数,高位不够用0补齐;二进制每四位对应的十进制数(10-15用A-F表示)是一位十六进制数
- 八进制、十六进制与十进制之间的转换
- 方式一:转换为二进制再转换为十进制
- 方式二:同二进制,取余法与加权法
单个字节所能表示的整数范围
非负数表示范围:0000 0000 ~ 0111 1111 => 0 ~ 127 => 0 ~ 2^7-1
负数表示范围:1000 0000 ~ 1111 1111 => -128 ~ -1 => -2^7 ~ -2^0
单个字节表示的整数范围是:-2^7 ~ 2^7 - 1,也就是-128 ~ 127
几大基本数据类型
整型
整数类型的概念
Java语言中描述整数数据的类型有:byte(占1个字节,表示范围-2^7 ~ 27-1)、short(占2个字节,表示范围-215 ~ 215-1)、int(占4个字节,表示范围-231 ~ 231-1)、long(占8个字节,表示范围-263 ~ 2^63-1),推荐使用int类型
整数类型的使用
- 变量的数据值超过声明的整数类型范围的,会报错:不兼容的类型
- Java中的整数常量默认都是int类型,若写一个常量超过int类型范围,会报错:整数太大。若希望表达更大的常量,则需要在常量后面加上l或者L,推荐L(因为l和1类似怕歧义)。如long l = 2502505006L;
- 若描述比long类型还大的数据则使用java.math.BigInteger类型。
整数类型的笔试考点
-
变量的命名不能是数字开头的,否则可能出现2502505006L这种命名,这是long类型的常量,会混淆。
-
大的类型转换为小的类型会造成数值损失
int i = 25;
byte b = i; //错误:不兼容的类型:从int转换到byte可能会有损失
浮点型
浮点类型的概念
浮点类型就是Java中用于描述小数数据的类型:float和double,推荐double类型。
- float类型在内存空间中占4个字节,叫做单精度浮点数,可以表示7位有效数字,范围:-3.403E38 ~ 3.403E38
- double类型在内存空间中占8个字节,叫做双精度浮点数,可以表示15位有效数字,范围:-1.798E308 ~ 1.798E308
浮点类型的使用
- 变量的数据值超过声明的浮点数类型范围的,会报错:不兼容的类型
- Java中的浮点数常量默认都是double类型,若写一个常量超过double类型范围,会报错:不兼容的类型。若希望表达float类型的浮点数常量,则需要在常量后面加上f或者F。如float f = 3.1415926f; //会输出3.1415925,因为float类型默认7位有效数字
笔试考点
System.out.println(0.1 + 0.2);/*会输出0.30000000000000004,float和double类型运算时可能会有误差,若希望实现精确运算则需要借助java.math.BigDecimal类型*/
布尔型
布尔类型的概念
- Java语言中用于描述真假信息类型的就是布尔类型(boolean),数值只有true和false
- 布尔类型在内存空间中所占大小没有明确规定,可以认为是1个字节
- 注意:Java语言中int无法转换为boolean
字符类型
字符类型的概念
- Java语言中用于描述单个字符的数据类型:char类型。如’a’,'中’等
- char类型在内存空间中占2个字节且没有符号位,表示范围为0~65535。开发工作中更多的是用String类型
- 字符类型的数据在计算机中的存储是用对应的编号存储的(因为计算机只能识别0和1的二进制序列),该编号系统称为ASCII码。要求掌握的ASCII码有:‘0’ - 48;‘A’ - 65;‘a’ - 97;空格 - 32;换行符 - 10
字符类型和编号的使用
- 可以使用强转将字符转换为ASCII码:(int)c1
- 将整数编号直接赋值给char类型变量,输出该变量将会得到整数编号对应的字符
Unicode字符集的概念和使用
-
Java字符类型采用Unicode字符集编码。Unicode是世界通用的定长字符,所有的字符都是16位。
-
使用Unicode字符集表示一下“中国”(对应编号\u4e2d \u56fd)两个字:
char c1 = '\u4e2d';
char c2 = '\u56fd';
System.out.println("最终结果是:" + c1 + c2);
转义字符的概念和使用
- 转义字符的概念:就是将字符转换为原有的含义,用 \ 实现
- 要求掌握的转义字符有:\”,\’,\\,\t - 制表符,\n - 换行符
基本数据类型之间的转换
自动类型转换
自动类型转换概念
自动类型转换主要指从小类型到大类型之间的转换
举例
byte b = 10;
short s = 20;
s = b; //小类型变量b赋值给大类型变量s,实现自动转换
强制类型转换
强制类型转换概念
强制类型转换主要指从大类型到小类型之间的转换,语法格式如下:目标类型 变量名 = (目标类型)源类型变量名;
举例
byte b = 10;
short s = 20;
b = (byte)s;
//注意:若s的数据超过byte类型能表示的范围,会造成数据损失(因为只会保留小类型内存空间对应的二进制序列)
运算符
算术运算符
算术运算符的概念和使用
- 算术运算符的概念:+ - * / %(取模/取余运算符)
- 使用:进行算数运算,是双目运算符
算术运算符的注意事项
-
当两个整数相除时,结果只保留整数部分,丢弃小数部分;
-
若希望保留小数部分如何处理?
-
处理方式一:使用强制类型转换将其中一个操作数转换为double类型再运算即可
System.out.println((double)5 / 2));
-
处理方式二:让其中一个操作数乘以1.0即可(乘以1.0而不写x.0是因为变量也适用,更通用)
System.out.println(5 * 1.0 / 2);
-
-
0不能作除数。
算术运算符实现时间拆分
import java.util.Scanner;
public class ArithmeticTimeTest {
public static void main(String[] args) {
//1、提示用户输入秒数,获取该数
System.out.println("请输入一个秒数:");
Scanner sc = new Scanner(System.in);
int time = sc.nextInt();
//2、将正整数的秒数拆分为时分秒后并使用变量记录
int hour = time / 3600;
int min = time % 3600 / 60;
int sec = time % 60;
//3、输出对应的时分秒
System.out.println(time + "秒 = " + hour + "时" + min + "分" + sec + "秒");
}
}
字符串连接运算符 +
- +既可以作为加法运算符,又可以作为字符串连接符
- 只要+两边有一个操作数是字符串类型,则该+就被当作字符串连接符处理,否则当作加法运算符处理
关系运算符
- 关系/比较运算符:> >= < <= == !=
- 关系运算符作为最终运算的表达式结果一定是boolean类型
自增/自减运算符
自增减运算符概念与使用
-
概念:++表示自增运算符,使得当前变量自身的数值加1;–表示自减运算符,使得当前变量自身的数值减1
-
使用
- 自增减运算符放变量前面:先自增后使用(后作为整个表达式的结果)
- 自增减运算符放变量后面:先使用(先作为整个表达式的结果)后自增
-
使用举例
int i = 10;
System.out.println(i++); //此时输出10
System.out.println(i); //此时输出11
System.out.println(++i); //此时输出12
System.out.println(i); //此时输出12
-
注意:自增减运算符只能用于变量,常数不可以
自增减运算符的笔试考点
前自增/自减和后自增/自减的运算结果
逻辑运算符
逻辑运算符的概念和使用
- 逻辑运算符:&&(逻辑与) ||(逻辑或) !(逻辑非)
- 逻辑运算符的操作数均为boolean表达式
- 逻辑运算符主要用于连接多个关系运算符作为最终运算的表达式:如num >= 100 && num <= 999
逻辑运算符的短路特性
- 对于逻辑与运算,第一个表达式为假则结果为假,跳过判断第二个表达式
- 对于逻辑或运算,第一个表达式为真则结果为真,跳过判断第二个表达式
三目运算符
三目运算符的概念和使用
-
格式:条件表达式? 表达式1: 表达式2
-
使用:判断条件表达式是否成立,成立则执行表达式1,否则执行表达式2
赋值运算符
赋值运算符的概念和使用
- =表示赋值运算符,用于将=右边的数据赋值给=左边的变量,覆盖变量原来的数值
- 赋值表达式本身也有值,其本身之值即为所赋之值
- 复合赋值运算符:+= -= *= /=…
赋值运算符的考点
-
考点一:byte + byte结果还是int类型,这是编译器优化的结果——因为byte + byte很可能直接就超过能表示的范围了,所以编译器直接让它们的结果是int
byte b = 10;
System.out.println("b = " + b); //b = 10;
b = b + 2; //错误:不兼容的类型:从int转换到byte可能会有损失 byte + int 相加还是int类型
b = b + (byte)2; //错误:不兼容的类型:从int转换到byte可能会有损失 byte + byte相加结果还是int //类型,编译器优化
b = (byte)(b + 2); //正确,强制类型转换为byte后再赋值
b += 2; //无报错,真正等价于b = (byte)(b + 2)
System.out.println("b = " + b); //b = 12
-
考点二
i == 2; //表示判断变量i的数值是否等于2
2 == i; //表示判断2是否等于变量i的数值,结果上来说等价,推荐该方式,因为当我们将==写成=会立刻报错
i = 2; //表示将2赋值给变量i,覆盖变量i原来的数值
2 = i; //编译报错,错误:意外的类型
移位运算符(了解)
移位运算符的概念
- << 左移运算符,用于将数据的二进制位向左移动,右边用0补充
- 左移n位相当于当前整数的数值*2n
- >> 右移运算符,用于将数据的二进制位向右移动,左边使用符号位补充
- 右移n位相当于当前整数的数值/2n(结果舍弃小数)
- >>> 表示逻辑右移运算符,用于将数据的二进制位向右移动,左边使用0补充(由于移位未涉及符号,又称为无符号右移)
移位运算符的使用
左移运算符
byte b1 = 13
//b2 = 0000 1101向左移动一位,结果是:0001 1010=26
/*byte b2 = b1 << 1; //错误:不兼容的类型:从int转换到byte可能会有损失 自动提升为int类型,也就是32位二进制*/
byte b2 = (byte)(b1 << 1); //b2 = 26
右移运算符
byte b1 = 13
//b2 = 0000 1101向右移动一位,结果是:0000 0110=6
byte b2 = b1 >> 1; //b2 = 6
逻辑右移运算符
byte b2 = b1 >>> 1; //b2 = 6,对于非负数来说,逻辑右移与右移效果一致
位运算符(了解)
位运算符的概念
- &表示按位与运算符,按照二进制位进行与运算,同1为1,一0为0。
- | 表示按位或运算符,按照二进制位进行或运算,一1为1,同0为0。
- ~ 表示按位取反运算符,按照二进制位进行取反,1为0,0为1。
- ^ 表示按位异或运算符,按照二进制位进行异或运算,同为0,不同为1。
位运算符的使用
byte b1 = 11;
byte b2 = 13;
/*
b1的二进制为:0000 1011
b2的二进制为:0000 1101
*/
System.out.println(b1 & b2); //按位与后的二进制为:0000 1001 =>十进制是9
System.out.println(b1 | b2); //按位或后的二进制为:0000 1111 =>十进制是15
System.out.println(b1 ^ b2); //按位异或后的二进制为:0000 0110 =>十进制是6
System.out.println(~ b1); //按位取反后的二进制数为1111 0100 =>负数二进制转为十进制:先减1得
//1111 0011,再取反0000 1100 =>12,添加符号为-12
运算符的优先级
- ()的优先级极高。
- =的优先级极低。
- 若无法确认优先级,则使用()来确保即可。
流程控制语句
分支结构
分支结构的概念
当需要进行条件判断并做出选择时,使用分支结构。
if分支结构
if分支结构的概念和使用
if(条件表达式) { 语句块; }
- 若条件表达式成立则执行语句块;否则不执行。
if分支结构查找最大值
int ia = sc.nextInt();
int ib = sc.nextInt();
int max = ia;
if(ib > max) { //推荐使用这种方式:具有通用性
max = ib;
}
System.out.println("最大值是:" + max);
if-else分支结构
if-else分支结构的概念和使用
-
if(条件表达式) { 语句块1; } else { 语句块2; }
-
条件表达式成立,则执行语句块1;否则执行语句块2。
if-else if-else分支结构的概念和使用
-
if(条件表达式1) { 语句块1; } else if(条件表达式2) { 语句块2; } ....... else { 语句块n; }
-
满足何种条件就执行哪个语句块;否则执行else中的语句块。
switch-case分支结构
switch-case分支结构的概念和使用
-
switch(变量/表达式) { case 字面值1: 语块1; break; case 字面值2: 语块1; break; ... default: 语块n; }
-
把变量/表达式拿来匹配字面值,匹配到就执行对应语句,遇到break则结束匹配;否则执行default中的语句。
-
注意:switch()中的是变量/表达式!应该是一个具体值!switch()中支持的数据类型有:byte、short、char以及int类型,从jdk1.5开始支持枚举类型,从jdk1.7开始支持String类型。
案例
- switch-case实现等级判断:百分制分数/10就能得到对应的一个数值当作匹配的case字面值(等级)。
循环结构
循环结构的概念
-
循环结构:重复执行某一个步骤(代码),就需要使用循环结构。
-
任何复杂的程序逻辑都可以通过顺序、分支、循环三种程序结构实现。
for循环
for循环的概念和使用
for(初始化表达式; 条件表达式; 修改初始值表达式) {
循环体;
}
案例
-
三种方式实现循环打印奇数
//方式一:根据奇数概念打印 for(int i = 1; i <= 100; i++) { if(i % 2 != 0) { System.out.println("i = " + i); } } System.out.println("-----------------------------------------"); //方式二:根据等差数列的概念来打印,每两个数据之间相差2 for(int i = 1; i <= 100; i += 2) { System.out.println("i = " + i); } System.out.println("-----------------------------------------"); //方式三:根据奇数的通项公式规则来打印 2*i-1 for(int i = 1; i <= 50; i++) { System.out.println("i = " + (2 * i - 1)); }
-
for循环实现三位数中全部水仙花数的打印
//1、使用for循环打印所有三位数 for(int i = 100; i <= 999; i++) { //2、拆分三位数上各个数位的数字 int ia = i / 100; int ib = i % 100 / 10; int ic = i % 10; //3、判断是否是水仙花数,是则打印,否则不打印 if(ia * ia * ia + ib * ib * ib + ic * ic * ic == i) { System.out.println(i); } }
双层for循环
- for循环内部又嵌套了一层for循环。
- 在以后的开发中需要打印多行多列时,需要使用双重循环。
- 多重循环不宜嵌套太多层,否则效率很低。一般到三重循环即可,最常见的就是双重循环。
continue和break关键字
continue关键字
- continue语句用在循环体中,用于结束本次循环而开始下一次循环。
break关键字
-
break用于退出当前语句块,break用在循环体中用于退出循环。
-
break关键字一般用在无限循环,也叫做“死循环”。
-
注意:break只能跳出当前循环,若要退出外层循环体,需要使用标号的方式。
for() {
for() {
break; //只能跳出当前循环
}
}
outer: for() {
for() {
break outer; //能跳出外层outer循环
}
}
while循环
while循环的概念和格式
-
while(条件表达式) {
循环体;
}
-
判断条件表达式是否成立
- 若成立,则执行循环体
- 若不成立,则循环结束
while循环和for循环比较
- while循环和for循环完全可以互换,推荐使用for循环。
- while循环更适用于明确循环条件但不明确循环次数的场合中。
- for循环更适用于明确循环次数或范围的场合中(绝大多数场合都使用for循环)。
- while(true)等价于for( ; ; ),都表示无限循环。
do-while循环
do-while循环的概念和格式
-
do{
循环体;
} while(条件表达式); //注意while()后面有分号
-
先执行循环体,之后判断条件表达式是否成立
- 若成立,则执行循环体
- 若不成立,则循环结束
do-while和while循环区别
- do-while循环中的循环体至少执行一次,while和for循环可能一次都不执行。
循环的笔试考点
- 注意只有do-while循环的while()后面有加分号。
int i = 1;
while(i <= 1000); { //这里加分号相当于while循环体为空语句,程序陷入死循环
System.out.println("I Love You");
i++;
}
数组及应用
一维数组
一维数组的基本概念
- 当需要在Java程序中记录单个数据内容时,则声明一个变量即可;当需要在Java程序中记录多个类型相同的数据内容时,则声明一个一维数组即可,一维数组本质上就是在内存空间中申请一段连续的存储单元。
- 数组是相同数据类型的多个元素的容器,元素按线性顺序排列,在Java语言中体现为一种引用数据类型。
一维数组的声明方式
- 数据类型[] 数组名称 = new 数据类型[数组的长度];
一维数组的使用
- 调用数组的length属性可以获取数组的长度:int len = arr.length;
- 可以通过下标的方式访问数组中的每一个元素。需要注意的是:数组的下标从0开始,对于长度为n的数组,下标的范围是0~n-1
一维数组的初始化
-
基本类型的数组(数据元素为基本类型)创建后,其元素的初始值:byte、short、char、int、long为0;float和double为0.0;boolean为false
-
两种方式进行数组的初始化
-
静态方式:可以在数组声明的同时进行初始化,具体如下
数据类型[] 数组名称 = {初始值1,初始值2,…};
int[] arr1 = {1, 2}; //静态方式的简化版
int[] arr2 = new arr2[]{1, 2}; //静态方式的完整版,注意静态声明完整版后面[]不要声明数组长度!
-
动态方式:使用循环遍历数组一个一个进行赋值初始化
-
内存结构分析
内存中的栈区
- 栈用于存放程运行过程中所有的局部变量。一个运行的Java程序从开始到结束会有多次变量的声明
内存中的堆区
- JVM会在其内存空间中开辟一个称为“堆”的存储空间,这部分空间用于存储使用new关键字创建的数组和对象
内存结构图
基本数据类型将值直接放在栈区域;而引用数据类型在栈区域中存储的是地址,由该地址索引到堆区域中对应地址的值才是引用数据类型的值,也因此输出数组名输出的是栈区中存储的地址而不是具体值。
一维数组的增删改查操作
//1、增加:创建一个长度为5的一维数组,给数组的前4个元素分别赋值11 22 33 44
int[] arr = new int[5];
for(int i = 0; i < arr.length - 1; i++) {
arr[i] = (i + 1) * 11; //11 22 33 44 0
}
//2、插入:在0位置插入一个数字55(其实就是顺序表的插入操作)
for(int i = 4; i > 0; i--) {
arr[i] = arr[i - 1];
}
arr[0] = 55;
//3、删除:将元素55从数组删除,删除方式为后续元素向前移动,最后位置置为0并打印(其实就是顺序表的 //删除操作)
for(int i = 1; i < arr.length ; i++) {
arr[i - 1] = arr[i];
}
arr[arr.length - 1] = 0;
//4、查找:查找数组中是否存在元素22,若存在则修改为220后再次打印所有元素
for(int i = 0; i < arr.length; i++) {
if(arr[i] == 22) {
arr[i] = 220;
break; //只要找到就可以退出循环
}
}
//输出数组
for(int i = 0; i < arr.length; i++) {
System.out.println("arr[" + i + "] = " + arr[i]);
}
一维数组的优缺点
- 可以通过下标(或索引)的方式访问指定位置的元素,速度很快
- 数组要求要求所有元素的类型相同
- 数组要求内容空间必须连续,并且长度一旦确定就不能修改(一旦容量不够只能申请个更大的数组后将原数组的元素拷贝到大的新数组中)
- 增加和删除元素时可能移动大量元素,效率低
一维数组的拷贝
-
方式一:自己写
for(int i = 0; i < brr.length; i++) { brr[i] = arr[i + 1]; //将arr从位置1开始的元素拷贝到brr数组中 }
-
方式二:使用java.base.lang.System中的arraycopy()方法
System.arraycopy(arr, 1, brr, 0, 3); //实现结果同方法一
-
一维数组之间拷贝的笔试考点
brr = arr; //将arr数组栈内存中存储的地址赋值给brr,brr就指向了arr数组在堆内存中存储的元素堆
数组工具类
数组工具类的概念
- java.util.Arrays类可以实现对数组中元素的遍历、查找、排序等操作
常见的数组工具类
-
static String toString(int[] a) //输出数组中的内容
- 使用举例:
System.out.println("第一个数组中的元素有:" + Arrays.toString(arr));
- 使用举例:
-
static void fill(int[] a, int val) //将val赋值给数组中所有元素
- 使用举例:
Arrays.fill(arr2, 10); //将arr2中的所有数组元素填充为10
- 使用举例:
-
static boolean equals(boolean[] a, boolean[] a2) //判断两个数组元素内容和次序是否相同
- 使用举例:
System.out.println(Arrays.equals(arr2, arr3)); //判断arr2与arr3是否完全相同,若完全相同则打印true,否则打印false。(注意内容和次序都要完全相同)
- 使用举例:
-
static void sort(int[] a) //对数组中的元素进行从小到大排序
- 使用举例:
Arrays.sort(arr); //从小到大排序
- 使用举例:
-
static int binarySearch(int[] a, int key) //从数组中查找值为key的元素所在位置,不存在则返回一个越界下标(负数)
- 使用举例:
System.out.println(Arrays.binarySearch(arr, 3)); //查找元素值为3所在下标
- 使用举例:
二维数组
二维数组的基本概念
- 二维数组本质就是多个一维数组摞在一起组成的数组:二维数组里面的每个数组元素都是一维数组,而一维数组中的每个元素才是数据内容
二维数组的声明和使用
二维数组的声明
- 数据类型[][] 数组名称 = new 数据类型[行数][列数];
二维数组的使用
-
关于数组的长度
- arr.length代表一维数组的长度,也就是元素的个数
- brr.length代表二维数组brr的长度,也就是元素的个数,也就是一维数组的个数,也就是行数
- brr[0].length代表二维数组中第一个元素的长度,也就是一维数组的长度,也就是第一行的列数
-
遍历二维数组:使用外层for循环遍历二维数组的行,内层for循环遍历二维数组的列
for(int i = 0; i < arr.length; i++) { for(int j = 0; j < arr[i].length; j++) { ...; //循环体内可以进行初始化及其他一些对数组元素的操作 } }
-
二维数组的初始化
- 静态方式:数据类型[][] 数组名称 = {{元素1, 元素2, …}, {…}, …};
- 动态方式:使用双重for循环进行遍历赋值
二维数组案例:杨辉三角
//1、提示用户输入一个行数并用变量记录
System.out.println("请输入杨辉三角需要的行数:");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
//2、根据用户输入的行数来声明对应的二维数组存储杨辉三角
int[][] yanghui = new int[num][];
//3、根据杨辉三角的特点对二维数组进行初始化
for(int i = 0; i < yanghui.length; i++) {
//注意:要记得针对数组中的每一行进行内存空间的申请
yanghui[i] = new int[i + 1];
//使用内层for循环控制二维数组的列下标
for(int j = 0; j <= i; j++) {
//当列下标为0或者和行相同时,
if(j == 0 || j == i) {
yanghui[i][j] = 1;
} else {
//否则对应位置元素的值就是上一行当前列的值加上上一行前一列的值
yanghui[i][j] = yanghui[i - 1][j] + yanghui[i - 1][j - 1];
}
}
}
//4、输出杨辉三角查看效果
for(int i = 0; i < yanghui.length; i++) {
for(int j = 0; j <= i; j++) {
System.out.print(yanghui[i][j]);
}
System.out.println();
}
外层for循环遍历二维数组的行,内层for循环遍历二维数组的列
for(int i = 0; i < arr.length; i++) {
for(int j = 0; j < arr[i].length; j++) {
...; //循环体内可以进行初始化及其他一些对数组元素的操作
}
}
-
二维数组的初始化
- 静态方式:数据类型[][] 数组名称 = {{元素1, 元素2, …}, {…}, …};
- 动态方式:使用双重for循环进行遍历赋值
二维数组案例:杨辉三角
//1、提示用户输入一个行数并用变量记录
System.out.println("请输入杨辉三角需要的行数:");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
//2、根据用户输入的行数来声明对应的二维数组存储杨辉三角
int[][] yanghui = new int[num][];
//3、根据杨辉三角的特点对二维数组进行初始化
for(int i = 0; i < yanghui.length; i++) {
//注意:要记得针对数组中的每一行进行内存空间的申请
yanghui[i] = new int[i + 1];
//使用内层for循环控制二维数组的列下标
for(int j = 0; j <= i; j++) {
//当列下标为0或者和行相同时,
if(j == 0 || j == i) {
yanghui[i][j] = 1;
} else {
//否则对应位置元素的值就是上一行当前列的值加上上一行前一列的值
yanghui[i][j] = yanghui[i - 1][j] + yanghui[i - 1][j - 1];
}
}
}
//4、输出杨辉三角查看效果
for(int i = 0; i < yanghui.length; i++) {
for(int j = 0; j <= i; j++) {
System.out.print(yanghui[i][j]);
}
System.out.println();
}