目录
程序:计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合。
Java技术体系平台
- Java SE(Java Standard Edition)标准版。支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API。此版本以前称为J2SE。
- Java EE(Java Enterprise Edition)企业版。是为开发企业环境下的应用程序提供的一套解决方案。该技术体系中包含的技术如:Servlet、JSP等,主要是针对于Web应用程序开发。此版本以前称为J2EE。
- Java ME(Java Micro Edition)小型版。支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了针对移动终端的支持。此版本以前称为J2ME。
Java语言的特点
- Java语言是面向对象的(oop)。
- Java语言是健壮的。
- Java语言是跨平台性的。
- .java文件编译后生成的.class文件可以在不同的操作系统下运行。
- .class文件可以在不同的操作系统下运行的原因是每个不同的操作系统中都安装有JVM,JVM必不可少。
- Java语言是解释型的。
- 解释型语言:JavaScript,PHP,Java
- 编译型语言:C/C++
- 解释型语言:编译后的代码不能直接被机器执行,需要解释器来执行。
- 编译型语言:编译后的代码,可以直接被机器执行。
Java虚拟机(JVM Java Virtual Machine)
- JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。
- JVM包含在JDK中。
- 对于不同的操作系统,有不同的JVM。
- JVM机制屏蔽了底层运行平台的差别,实现了“一次编译、到处运行”。
Java开发工具包(JDK Java Development Kit)
- JDK = JRE + Java的开发工具
- Java的开发工具:java,javac,javadoc,javap等。
- JDK是提供给Java开发人员使用的,其中包含了JRE和Java的开发工具。
Java运行环境(JRE Java Runtime Environment)
- JRE = JVM + Java的核心类库
- 想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
三者关系:JDK > JRE > JVM
如果只想运行开发好的.class文件,只需要JRE即可。
编写一个输出“Hello,World!”的程序
//public class HelloWorld 表示HelloWorld是一个类,是一个public的类。
//HelloWorld{} 表示一个类的开始和结束
//public static void main(String[] args) 表示一个主方法,是程序的入口。
//main(String[] args) {} 方法的开始和结束
//System.out.println("Hello,World!") 输出HelloWorld到屏幕上
//;表示一条语句的结束
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello,World!");
}
}
.java文件是源文件,.class文件是字节码文件。
编译
- 有了Java源文件,通过编译器将其编译成JVM可以识别的字节码文件。
- 在该源文件目录下,通过javac编译工具对.java文件进行编译。
- 如果程序没有错误,没有任何提示,但在当前目录下会出现一个.class文件,该文件称为字节码文件,也是可以执行的Java的程序。
运行
- 有了可执行的Java程序。
- 通过运行工具对字节码文件进行执行,本质就是.class装载到JVM机执行。
Java程序开发注意事项
- 想要运行修改后的Java文件,需要对源文件进行重新编译,生成新的字节码文件后,再执行,才能运行出修改后的。否则运行出的只能是修改之前的。
- Java应用程序的执行入口是main()方法。
- Java语言严格区分大小写。
- 每条语句以;结束。
- {}成对出现,缺一不可。
- 一个源文件中只能有一个public修饰的类,其它类的个数不限。
- 每一个类编译后,都对应一个.class文件。
- 如果源文件包含一个public类,则文件名必须按该类命名。
- 可以将main()方法写在非public类中,然后指定运行该类,这样入口方法就是非public的main方法。
Java转义字符
- \t:制表位,实现对齐功能。
- \n:换行
- \\:输出\
- \":输出"
- \':输出'
- \r:回车。如果不在\r之前加入\n,那么就会导致\r之后的第一位将顶到该行第一位,第二位顶到第二位,直至完成。
Java中的注释类型
- 单行注释: //
- 多行注释:/*........*/
- 文档注释:/**...........*/
- 注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档,一般写在类。
/** * * @author 小焰 * @version 1.0 * */
-
使用如下Dos指令,可以对应生成文档。
javadoc -d 文件夹名 -author -version xx.java
- 注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档,一般写在类。
- 被注释的文字,不会被JVM解释执行。
- 多行注释里不能再嵌套多行注释。
Java代码规范
- 选中想要规范的代码块,Tab键整体向右移,Shift+Tab整体向左移。
- 运算符和=号两边各加一个空格。
- 行宽不要超过80个字符。
- 次行风格
if () { } else { }
- 行尾风格
if () { } else { }
常用的Dos命令
- dir:查看当前目录有什么内容。
- cd..:切换到上一级目录。
- md:创建目录。
- rd:删除目录。
- tree:查看指定目录下所有的子级目录
- cls:清屏。
- exit:退出Dos。
- copy:拷贝文件。
- del:删除文件。
相对路径:从当前目录开始定位,形成的一个路径。
绝对路径:从顶级目录开始定位,形成的一个路径。
变量是程序的基本组成单位。
变量三要素:类型、名称和值。
变量相当于内存中一个数据存储空间的表示。
变量使用注意事项
- 变量必须先声明,后使用。
- 可以在同一类型范围内不断变化。
- 变量在同一作用域内不能重名。
程序中“+”号的使用
- 当左右两边都是数值型时,做加法运算。
- 当左右两边有一方为字符串时,做拼接运算。(程序是从左到右,也就是说,倘若是“Hello”+100+3那么输出结果就是Hello1003)。
数据类型
整型类型
byte(字节 1字节)short(短整型 2字节)int(整型 4字节)long(长整型 8字节)
整型的使用细节
- Java各整数类型有固定的范围和字段长度,不受具体操作系统的影响,以保证Java的可移植性。
- Java的整型常量默认为int型,声明long型常量需要在后面加“l”或“L”。
- Java程序中变量常声明为int型,除非int型不足以表示想要表示的数,才使用long型。
- bit:计算机中最小的存储单位。
- byte:计算机中基本存储单元。
- 1byte = 8 bit
浮点类型
float(单精度 4字节)双精度(double 8字节)
浮点数 = 符号位 + 指数位 + 尾数位
尾数部分可能丢失,造成精度损失(小数都是近似值)。
浮点类型的使用细节
- Java各浮点类型有固定的范围和字段长度,不受具体操作系统的影响,以保证Java的可移植性。
- Java的浮点类型默认为double型,声明float型常量需要在后面加“f”或“F”。
- 十进制数形式:例如 5.12 512.0f .512(必须有小数点 但是小数点前是0的话0可以不写)。
- 科学计数法形式:例如 5.12e2(5.12 * 10的2次方) 5.12E-2(5.12 / 10的2次方)。
- 通常情况下我们使用double类型,因为比float类型更加的精确。如下图示例,可以看出float类型要四舍五入尾数的部分,double类型的精度更高一些。
- 当我们对运算结果是小数的进行相等判断时,要小心。正确的写法如下:
- 如果是直接查询得的小数或者直接复制,是可以判断相等的,对于运算的就要小心。
Java API文档
- API(Application Programming Interface 应用程序编程接口)是Java提供的基本编程接口(Java提供的类欢迎相关的方法)。
- Java 8 中文版 - 在线API手册 - 码工具 (matools.com)
- Java语言提供了大量的基础类,因此Oracle公司也为这些基础类提供了相应的API文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法。
- Java类的组织形式
字符类型
char(可以存放单个字符 2字节)
多个字符的字符类型用字符串String。
字符类型的使用细节
- 字符常量是用单引号(‘’)括起来的单个字符。
- Java中还允许使用转义字符‘\’为其后的字符转变为特殊字符型常量。
- 在Java中,char的本质是一个整数,在输出时,是Unicode码对应的字符。
- 可以直接给char赋一个整数,在输出时,会按照整数去对应Unicode码对应的字符输出。
- char类型都是可以进行运算的,相当于一个整数,因为它的本质是一个整数,都对应的有Unicode码。
字符编码表:
-
ASCII:ASCII编码表,用一个字节表示,一共有128个字符,实际上一个字节可以表示256个字符,但是只用了128个字符。
-
Unicode:Unicode编码表,固定大小的编码,使用两个字节来表示字符,字母和汉字统一都是占用两个字节,浪费空间。
-
UTF-8:UTF-8编码表,大小可变的编码,字母使用一个字节,汉字使用三个字节。
-
GBK:GBK编码表,可以表示汉字并且范围广,字母使用一字节,汉字使用两个字节。
-
GB2312:GB2312编码表,可以表示汉字,使用较少。
-
BIG5:BIG5编码表,可以使用繁体中文,适用于台湾,香港。
Unicode编码兼容ASCII编码。
UTF-8是在互联网上使用最广的一种Unicode的实现方式(改进)。
布尔类型
- 布尔类型也叫Boolean类型,Boolean类型数据只允许取值true和false,无null。
- Boolean类型占1个字节。
- Boolean类型适于逻辑运算,一般用于程序流程控制。
布尔类型的使用细节
不可以用0或非0的整数替代false和true,这点和C语言不同。
基本数据类型转换
自动类型转换
当Java程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型。
(byte可以转换成short,short可以转换成int。。。)
自动类型转换的使用细节
- 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的数据类型,然后再进行计算。
- 当我们把精度大的数据类型赋值给精度小的数据类型时,就会报错,反之就会进行自动类型转换。
- (byte,short)和char之间不会相互自动转换。但是当byte被赋予一个在取值范围内具体的数字时就可以,被赋予一个带有数据类型的变量值就不行。
byte = 10 //正确 int a = 10; byte b = a; //错误
- byte,short,char三者可以计算,在计算时首先转换为int类型。
- Boolean不参与自动类型转换。
- 自动提升原则:表达式结果的类型自动提升为操作数中最大的类型。
强制类型转换
自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。
使用时要加上强制转换符(),但可能造成精度降低或溢出,需要格外注意。
int n = (int)1.9; //输出值为1 无四舍五入
强制类型转换的使用细节
- 当需要从精度大的数据类型转换为精度小的数据类型时,就需要进行强制类型转换。
- 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级。
int a = (int)10 * 3.5 + 6 * 1.5; //错误 int a = (int)(10 * 3.5 + 6 * 1.5); //正确
- char类型可以保存int的常量值,但不能保存int的变量值,需要强转。
char a1 = 100; int b1 = 100; char a2 = b1; //错误 char a3 = (char)b1; //正确
- byte类型和short类型在进行运算时,当作int类型处理。
基本数据类型和String类型的转换
在程序开发中,经常需要将基本数据类型转换成String类型,或者将String类型转换成基本数据类型。
基本数据类型转String类型
将基本数据类型的值+“”即可。
int n = 100;
String s = n + "";
String类型转基本数据类型
通过基本数据类型的包装类调用parsexx方法即可。
在Java中每一个数据类型都对应一个包装类。
String n = "123";
int b = Integer.parseInt(n);
double c = Double.parseDouble(n);
boolean d = Boolean.parseBoolean("true");
...
基本数据类型和String类型的转换的使用细节
- 在将String类型转成基本数据类型时,要确保String类型能够转成有效的数据,比如,我们可以把“123”转换成一个整数,但是不能把“Hello”转换成一个整数。
- 如果格式不正确,就会抛出异常,程序就会终止。
运算符
运算符是一种特殊的符号,用以表示数据的运算,赋值和比较等。
算术运算符
算术运算符是对数值类型的变量进行运算。
运算符 | 运算 | 范例 | 结果 |
+ | 正号 | +7 | 7 |
- | 负号 | b = 11; -b; | -11 |
+ | 加 | 9 + 9 | 18 |
- | 减 | 10 - 8 | 2 |
* | 乘 | 7 * 8 | 56 |
/ | 除 | 9 / 9 | 1 |
% | 取模(取余) | 11 % 9 | 2 |
++ ++ | (前)自加:先运算后取值 (后)自加:先取值后运算 | a = 2 ; b = ++a; a = 2 ; b = a++; | a = 3 ; b = 3 a = 3 ; b = 2 |
-- -- | (前)自减:先运算后取值 (后)自减:先取值后运算 | a = 2 ; b = --a; a = 2 ; b = a--; | a = 1 ; b = 1 a = 1 ; b = 2 |
+ | 字符串相加 | "huo"+"yan" | "huoyan" |
在Java中,取余的本质是 a % b = a - a / b * b,例如-10 % 3 = -1,10 % -3 = 1。
++ or -- (均可参考)
- 作为独立语句使用,++在前在后都是等价于i = i + 1。
- 作为表达式使用(简单记法:++在前为先+1,++在后为后+1,记++在前在后的位置即可):
- 前++:先+1后再赋值。
public class Example { public static void main(String[] args) { int a = 8; int k = ++a; //a = a + 1 = 9 //int k = a = 9 System.out.println("a = "+ a + ",k = "+ k); } //输出结果为:a = 9,k = 9
- 后++:先赋值后+1。
public class Example { public static void main(String[] args) { int a = 8; int k = a++; //int k = a = 8 //a = a + 1 = 9 System.out.println("a = "+ a + ",k = "+ k); } //输出结果为:a = 9,k = 8
- 前++:先+1后再赋值。
关系运算符(比较运算符)
关系运算符的结果都是Boolean型,也就是说要么是true,要么是false。
关系表达式经常用在if结构或者循环结构的条件中。
运算符 | 运算 | 范例 | 结果 |
== | 相等于 | 8 == 7 | false |
!= | 不等于 | 8 != 7 | true |
< | 小于 | 8 < 7 | false |
> | 大于 | 8 > 7 | true |
<= | 小于等于 | 8 <= 7 | false |
>= | 大于等于 | 8 >= 7 | true |
instanceof | 检查是否是类的对象 | "yan" instanceof String | true |
关系运算符的使用细节
- 关系运算符的结果都是Boolean型,也就是说要么是true,要么是false。
- 关系运算符组成的表达式,我们称之为关系表达式。例如a > b。
- 比较运算符“==”注意不能写成“=”。
逻辑运算符
用于连接多个条件(多个关系表达式),最终的结果也是一个Boolean型。
短路与:&& 短路或:|| 取反:!
a | b | a && b | a || b | !a |
true | true | true | true | false |
true | false | false | true | false |
false | true | false | true | true |
false | false | tfalse | false | true |
短路与:当a,b同时为true时,结果才为true。
短路或:当a,b中只要有一个为true,结果就是true。
取反:当a为true,结果为false,当a为false,结果为true。
逻辑与:& 逻辑或:| 逻辑异或:^
a | b | a & b | a | b | a ^ b |
true | true | true | true | false |
true | false | false | true | true |
false | true | false | true | true |
false | false | false | false | false |
逻辑与:当a,b同时为true时,结果才为true。
逻辑或:当a,b中只要有一个为true,结果就是true。
逻辑异或:当a,b不同的时候,结果为true,否则就是false。
一个地址符为逻辑x,两个地址符为短路x。
短路与&&与逻辑与&的区别(其它类似)
- 短路与&&:如果第一个条件为false,则第二个条件不会执行,最终结果就是false。
- 逻辑与&:不管第一个条件是不是false,第二个条件都要判断。
- 逻辑与&的效率比较低。
- 一般开发中,我们使用短路与&&。
赋值运算符
赋值运算符就是将某个运算后的值,赋给指定的变量。
基本赋值运算符 =
复合赋值运算符 a + = b =====> a = a + b(简化的写法)
赋值运算符的使用细节
- 运算顺序从右往左,先把等式右边的变量算出来再赋给左边。
- 赋值运算符的左边只能是变量。
- 赋值运算符的右边可以是变量、表达式、常量值。
- 复合赋值运算符等价于a += 3 ======> a = a + 3
- 复合赋值运算符会进行类型转换,++也有。
byte a = 3; b += 2; //等价于b = (byte)(b + 2)
三元运算符
条件表达式?表达式1:表达式2
- 如果条件表达式为true,则运算后的结果为表达式1。
- 如果条件表达式为false,则运算后的结果为表达式2。
三元运算符的使用细节
- 表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换)。
- 三元运算符可以转换成if else语句。
运算符优先级
- 运算符有不同的优先级,所谓优先级就是表达式运算中的运算顺序。
- 只有单目运算符、赋值运算符是从右向左进行运算的。
- 单目运算:只对一个数进行操作。
- 优先级排序:
- 单目运算符
- 算术运算符
- 位移运算符
- 比较运算符
- 逻辑运算符
- 三元运算符
- 赋值运算符
标识符
Java对各种变量、方法和类等命名时使用的字符序列称为标识符。
标识符的命名规则
- 由26个英文字母大小写,0-9,_或者是$组成。
- 数字不可以作为开头。
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java中严格区分大小写,长度无限制。
- 标识符不能含有空格。
标识符的命名规范
- 包名:多单词组成时所有字母都小写。
- 类名,接口名:多单词组成时,所有单词的首字母大写。
- 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。
- 常量名:所有字母都大写,多单词时每个单词用下划线连接。
关键字
被Java语言赋予了特殊含义,用做专门用途的字符串。
保留字
现有Java版本尚未使用,但以后版本可能会作为关键字使用。
键盘输入语句
在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。
步骤
- 引入Scanner类
import java.util.Scanner;
- 创建Scanner对象实例
Scanner scanner = new Scanner(System.in); //创建名为scanner的Scnaner对象 //new是创建一个Scanner对象的意思 //System.in为用户从键盘输入的意思 //整条语句的意思就是创建一个名为scanner的Scanner的对象, //该对象为用户从键盘输入的Scanner对象
- 调用里面的功能
String name = scanner.next(); //接收用户的输入 int age = scanner.nextInt(); double salary = scanner.nextDouble(); //不同的数据类型有不同的scanner输入语句 //当程序执行到next方法时,会等待用户输入,倘若不输入,就会停留在此处
进制
对于整数,有四种表示方式
- 二进制:0,1
- 满2进1,以0b或者0B开头。
- 满2进1的意思是说
- 1(1)
- 2(此时为2,要进一位,那么就是10)
- 3(数字2的尾数为0,0+1=1,再带上数字2的前面几位的数字,那么就是11),
- 4(数字三为11,尾数为1,1+1=2,所以要进一位,数字三的倒数第二位也是1,1再+1,那么就是0,将加到的数字1提到倒数第二位的前面,那么就是110)
- 其他数字依次类推。
- 十进制:0-9
- 满10进1。
- 当作日常生活中使用的数字来看即可。
- 八进制:0-7
- 满8进1,以数字0开头。
- 满8进1的意思是说
- 7(7)
- 8(进一位,那么就是10)
- 9(10进一位,11)
- 10(12)
- ...
- 15(17)
- 16(17进一位,20)
- 十六进制:0-9及A(10)-F(15)
- 满16进1,以0x或者0X开头。
- 此处的A-F不区分大小写。
- a10、b11、c12、d13、e14、f15。
- 16(10)
进制的转换
二进制转十进制
从最低位(右边)开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和。
例如将0b1101转成十进制的数字。
0b1101 = 1 * 2的(1-1)次方 + 0 * 2的(2-1)次方 + 1 * 2的(3-1)次方 + 1 * 2的(4-1)次方 = 1 + 2 + 0 + 8 = 11
八进制转十进制
从最低位(右边)开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和。
例如将0234转为十进制的数字。
0234 = 4 * 8的(1-1)次方 + 3 * 8的(2-1)次方 + 2 * 8的(3-1)次方 = 4 + 24 + 128 = 156
十六进制转十进制
从最低位(右边)开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和。
例如将0x23A转成十进制的数字。
0x23A = 10(A) * 16的(1-1)次方 + 3 * 16的(2-1)次方 + 2 * 16(3-1)次方 = 10 + 48 + 512 = 570
十进制转二进制
将该数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制数。
例如将34转成二进制的数字。
就是0b100010,这是六位,要写成八位,所以写成0b00100010。(0b为二进制的代表表示)
十进制转八进制
将该数不断除以8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的八进制数。
例如将131转成八进制的数字。
就是0203(0为八进制的代表表示)
十进制转十六进制
将该数不断除以16,直到商为0为止,然后将每步得到的余数倒过来,就是对应的十六进制数。
例如将237转成十六进制的数字。
就是0xed或者是0xED(0x为十六进制的代表表示)
二进制转八进制
从低位开始,将二进制数每三位一组,转成对应的八进制数即可。
例如将0b11010101转成八进制的数字。
0b11010101 = 0325(0是八进制的代表表示)
二进制转十六进制
从低位开始,将二进制数每四位一组,转成对应的十六进制数即可。
例如将0b11010101转成十六进制的数字。
0b11010101 = 0xD5(0x是十六进制的代表表示)
八进制转二进制
将八进制的每一位,转成对应的一个三位的二进制数即可。
例如将0237转成二进制的数字。
0237 = 0b010011111(0b是二进制的代表表示)
十六进制转二进制
将八进制的每一位,转成对应的一个四位的二进制数即可。
例如将0x23B转成二进制的数字。
0x23B = 0b001000111011
二进制在运算中的说明
二进制是逢2进位的进位制,0、1是基本算符。
现代的电子计算机技术全部采用的是二进制,因为它只使用0、1两个数字符号,非常简单方便易于用电子方式实现。计算机内部处理的信息,都是采用二进制位数来表示的。二进制(Binary)数用0和1两个数字及其组合来表示任何数。进位规则是“逢2进1”,数字1在不同的位上代表不同的值,按从右至左的次序,这个值以二倍递增。
原码、反码和补码
对于有符号的而言
- 二进制的最高位是符号位:0表示正数,1表示负数(正0负1)。
- 正数的原码,反码和补码都一样(三码合一)。
- 负数的反码 = 它的原码符号位不变,其它位取反(1->0,0->1)。
- 负数的补码 = 它的反码 + 1
- 负数的反码 = 它的补码 - 1
- 0的反码,补码都是0。
- Java没有无符号数,换而言之,Java中的数都是有符号的。
- 在计算机运算的时候,都是以补码的方式来运算的。
- 当我们看运算结果的时候,要看它的原码。
位运算符
Java中有7个位运算符,都是指的码值的运算,并不是直接的值。
- 按位与&
- 两位全为1,结果为1,否则为0。
public class Example{ public static void main(String[] args) { //2的原码:00000000 00000000 00000000 00000010 //2的补码:00000000 00000000 00000000 00000010(正数三码合一) //3的原码:00000000 00000000 00000000 00000011 //3的补码:00000000 00000000 00000000 00000011(正数三码合一) //按位& 全为1才为1,否则为0 //则有 //00000000 00000000 00000000 00000010 & //00000000 00000000 00000000 00000011 = //00000000 00000000 00000000 00000010(此处为补码,要转换为原码) //原码:00000000 00000000 00000000 00000010 (正0负1,三码合一) //结果为:2 System.out.println(2&3); }
- 两位全为1,结果为1,否则为0。
- 按位或|
- 两位有一个1,结果为1,否则为0。
- 按位异或^
- 两位有一个为0,一个为1,结果为1,否则为0。
- 按位取反~
- 1变0
- 0变1
- >>,<<,>>>
- >>:算术右移,低位溢出,符号位不变,并用符号位补溢出的高位。
- 例如 a>>2 -----> 00000001 -> 00000000 01
- <<:算术左移,符号位不变,低位补0。
- 例如a>>2 -----> 00000001 -> 00000100
- >>>:逻辑右移也叫无符号右移,运算规则是:低位溢出,高位补0。
- 特别注意的是没有<<<符号。
- >>:算术右移,低位溢出,符号位不变,并用符号位补溢出的高位。
程序流程控制
在程序中,程序运行的流行控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。
顺序控制
程序从上到下逐行地执行,中间没有任何判断和跳转。
Java中定义变量时采用合法的前向引用。
分支控制
让程序有选择的执行。
分支语句是可以嵌套使用的。
单分支
if(条件表达式){
代码块;(可以支持多条语句)
}
当条件表达式为true时,就会执行{}内的代码块。
当条件表达式为false时,就不执行{}内的代码块,执行该if语句之后的代码。
如果{}内只有一条语句,可以不用写{},但当有两条及以上语句时,则必须写{}。
双分支
if(条件表达式){
代码块1;
}
else {
代码块2;
}
当条件表达式成立时,就执行代码块1,否则执行代码块2。
如果{}内只有一条语句,可以不用写{},但当有两条及以上语句时,则必须写{}。
多分支
if(条件表达式1){
代码块1;
}else if(条件表达式2){
代码块2;
}
....
else{
代码块n;
}
当条件表达式1成立时,执行代码块1。
如果条件表示1不成立,才去判断条件表达式2是否成立。
条件表达式2成立,就执行代码块2。
以此类推,如果所有的条件表达式都不成立,就执行else语句里的代码块n。
只能有一个执行入口。
多分支语句可以没有else。
如果所有的条件表达式都不成立,则一个执行入口都没有。
嵌套分支
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层分支,外面的分支结构称为外层分支,最好是不要超过三层,可读性不好。
if(){
if(){
}else{
}
} else{
}
switch
switch (表达式){
case 常量1:
代码块1;
break;
case 常量2:
代码块2;
break;
...
case 常量n:
代码块n;
break;
default:
代码块;
break;
}
表达式对应一个值。
在Java中只要有值返回,就是一个表达式。
case 常量1:当表达式的值等于常量1,就执行代码块1。
break:表达退出swtich。
如果和case常量1匹配,就执行语句块1,如果没有匹配,就继续匹配case常量2。
如果一个都没有匹配上,就执行default。
switch语句的使用细节
- 表达式数据类型,应和case后的常量类型保持一致,或者是可以自动转成可以相互比较的类型。
- switch(表达式)中表达式的返回值必须是(byte,short,int,char,enum,String)
- case子句中的值必须是常量或者常量表达式(1+1),而不能是变量。
- default子句是可选的,当没有匹配的case时,执行default。
- break语句用来执行完一个case分支后使程序跳出switch语句,如果没有写break,程序会顺序执行到switch结尾,除非遇到break。
穿透
根据指定月份,打印该月份所属的季节。3,4,5春季,6,7,8夏季,9,10,11秋季,12,1,2冬季。
import java.util.Scanner;
public class Example{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入月份:");
int mouth = scanner.nextInt();
switch (mouth){
case 3:
case 4:
case 5:
System.out.println(mouth + "月是春季");
break;
case 6:
case 7:
case 8:
System.out.println(mouth + "月是夏季");
break;
case 9:
case 10:
case 11:
System.out.println(mouth + "月是秋季");
break;
case 12:
case 1:
case 2:
System.out.println(mouth + "月是冬季");
break;
default:
System.out.println("请输入正确的月份!");
}
}
}
if语句和switch语句的比较
- 对于区间判断,对于结果为Boolean类型判断,推荐使用if语句。
- 如果判断的具体数值不多,而且符合byte,short,int,char,enum,String这六种类型,推荐使用switch语句。
循环控制
让代码可以循环的进行。
for
for(循环变量初始化;循环条件;循环变量迭代){
循环操作(语句);
}
for的四要素
- 循环变量初始化。
- 循环条件。
- 循环操作。
- 循环变量迭代。
循环操作可以有多条语句,也就是我们要循环执行的代码。
如果循环操作语句只有一条,可以省略{},但是最好还是不要省略为好。
for循环的使用细节
- 循环条件是返回一个布尔值的表达式。
- for中的初始化和变量迭代可以写到其它地方,但是两边分号不能省略。
public class Example { public static void main(String[] args) { int i = 1; for(;i <= 10;){ System.out.println("你好!小焰"); i++; } System.out.println(i); } } //for(;;)表示无限循环
- 循环初始值可以由多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开。
public class Example { public static void main(String[] args) { int count = 3; for(int i = 0,j = 0;i < count;i++,j+=2){ System.out.println("i = " + i + " j = " + j); } } }
例题
//打印1-100之前所有是9的倍数的整数,统计个数及总和。
//(1)完成输出1-100的值
//(2)在输出过程中进行过滤,只输入9的倍数 i%9 == 0
//(3)统计个数,int count = 0,当条件满足时count++
//(4)总和,int sum = 0,当条件满足时sum += i
//为了更好的适应需求,可以对各个可以当成变量的值进行修改
public class Example {
public static void main(String[] args) {
int start = 1;
int end = 100;
int n = 9;
int count = 0;
int sum = 0;
for(int i = start;i <= end;i++){
if(i % n == 0){
System.out.println("i = " + i);
count++;
sum += i;
}
}
System.out.println("count = " + count);
System.out.println("sum = " + sum);
}
}
//(1)先输出0-5
//(2)后面的+是5-i
//为了使代码更加灵活,适当修改
public class Example {
public static void main(String[] args) {
int n = 5;
for(int i = 0;i <= n;i++){
System.out.println(i + " + " + (n - i) + " = " + n);
}
}
}
while
while(循环条件){
循环体;
循环变量迭代;
}
与for循环一样有四要素,但是四要素所处的位置不一样。
public class Example {
public static void main(String[] args) {
int i = 1;
while(i <= 10){
System.out.println("你好!小焰");
i++;
}
}
}
while循环的使用细节
- 循环条件是返回一个布尔值的表达式。
- while循环是先判断再执行语句。
do...while循环
do{
循环体;
循环变量迭代;
}while(循环条件);
也有循环四要素,只是位置不一样。
先执行,再判断,也就是说,一定会执行一次。
一定要注意的是最后有一个;号。
public class Example {
public static void main(String[] args) {
int i = 1;
do{
System.out.println("你好!小焰");
i++;
}while(i <= 10);
}
}
do...while循环的使用细节
- 循环条件是返回一个布尔值的表达式。
- do...while循环是先执行,再判断,因此它至少执行一次。
while循环和do...while循环的区别
while循环是先判断条件再执行。
do...while循环是先执行再判断。
多重循环控制
将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do...while均可以作为外层循环和内层循环。
建议一般使用两层,最多不要超过三层,否则,代码的可读性很差。
实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的循环。
设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
例题
//统计三个班的成绩情况,每个班有五名同学,求出各个班的平均分和所有班级的平均分
//学生信息从键盘输入
//统计三个班的及格人数,每个班有五名同学。
//先计算一个班,五个学生的成绩。使用一个for循环即可。
//创建Scanner对象然后接收用户的输入。
//得到该班级的平均分
//统计三个班(每个班五个学生)的平均分
//所有班级的平均分
//定义一个变量,double total累计所有学生的成绩
//当多重循环结束后,total / (3 * 5)
//定义一个变量,int passNum累计所有及格学生的人数
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); //创建Scanner对象
double total = 0;
int passNum = 0;
for(int i = 1;i <= 3;i++){
double sum = 0;
for(int j = 1;j <= 5;j++){
System.out.println("请输入第" + i + "个班的第" + j + "个学生的成绩:");
double score = scanner.nextDouble();
if(score >= 60){
passNum++;
}
System.out.println("成绩为" + score);
sum += score;
}
System.out.println("sum = " + sum + " average = " + (sum / 5));
System.out.println("========================");
total += sum;
}
System.out.println("三个班的总分为:" + total + " 三个班的平均分为:" + total / (3 * 5));
System.out.println("三个班的及格人数有" + passNum + "人");
}
}
//打印一个九九乘法表
//*后的数字保持不变,故知道是循环外层->当内层循环结束后才可以进行外层循环,所以是for(int i = 1;i <= 9;i++)
//*前的数字即是内层循环,每次都要+1,for(int j = 1;j <= i;j++)
//将内层循环嵌套在外层循环内
//注意里面的输出语句不是println而是print,println带有换行,而print没有。
//每一行的最后一个乘法都是 i 和 j 相同,故嵌套if语句,当 i == j 时,换行
//注意:是每一行后使用if语句,所以if语句在内层循环内
public class Example {
public static void main(String[] args) {
for(int i = 1;i <= 9;i++){
for(int j = 1;j <= i;j++){
System.out.print(j + " * " + i + " = " + j * i + "\t");
if(i == j){
System.out.println();
}
}
}
}
}
/*
打印一个空心金字塔
先打印一个矩形五行五列
*****
*****
*****
*****
*****
for(int i = 1;i <= 5;i++){
for(int j = 1;j <= 5;j++){
System.out.print("*");
}
System.out.println();
打印半个金字塔,嵌套在内,结束每一行都需要换行
*
**
***
****
*****
for(int i = 1;i <= 5;i++){
for(int j = 1;j <= i;j++){
System.out.print("*");
}
System.out.println();
}
打印整个金字塔
*
***
*****
*******
*********
for(int k = 1;k <= 5 - i;k++){
System.out.print(" ");
}
打印空心金字塔
*
* *
* *
* *
*********
*/
public class Example {
public static void main(String[] args) {
for(int i = 1;i <= 5;i++){
for(int k = 1;k <= 5 - i;k++){
System.out.print(" ");
}
for(int j = 1;j <= 2 * i - 1;j++){
if(j == 1 || j == 2 * i - 1 || i == 5){
System.out.print("*");
}else{
System.out.print(" ");
}
}
System.out.println();
}
}
}
跳转控制语句
当某个条件满足时,终止循环,通过该需求可以说明其它流程控制的必要性。
break
break语句用于种植某个语句块的执行,一般是用在switch或者循环中。
{
...
break;
...
}
break语句的使用细节
break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块。
public class Example {
public static void main(String[] args) {
lable1:
for(int j = 0;j <= 4;j++){
lable2:
for(int i = 0;i <= 4;i++){
if(i == 2){
break lable1;
}
System.out.println("i = " + i);
}
}
}
}
但是最好不要使用标签,可读性会变差,了解即可。
如果没有指定的break,默认退出最近的循环体。
例题
/*
1-100以内的数求和,求出当和第一次大于20的当前数
循环1-100,求和为变量sum
当 sum > 20 时,记录当前数字,然后break
在for循环外部定义变量n,把当前的i赋给n
*/
public class Example {
public static void main(String[] args) {
int n = 0;
int sum = 0;
for(int i = 1;i <= 100;i++){
sum += i;
if(sum > 20){
n = i;
break;
}
}
System.out.println("i = " + n);
System.out.println("sum = " + sum);
}
}
/*
实现登录验证,一共有三次机会,如果用户名为“小焰”,密码为“666”提示登录成功,否则提示还有几次机会。
实现登录验证->用户登录->从用户进行输入->使用输入语句->分别键入用户名和密码。
比较使用的方法是String的equals方法,将equals前的与equals()内的数相比较
用户名为“小焰”,密码为“666”->两个变量的条件都要满足才能提示登录成功,即用&&,输出后进行break语句。
用一次机会少一次->i-1->当i=3时,有0次机会,输出还有0次机会,直接输出登录结束即可->用if...else语句。
*/
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
String username;
String password;
for(int i = 1;i <= 3;i++) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
username = scanner.next();
System.out.println("请输入密码");
password = scanner.next();
if ("小焰".equals(username) && "666".equals(password)) {
System.out.println("登录成功!");
break;
}
if(i == 3){
System.out.println("最后的机会已用完,登录失败!");
}else{
System.out.println("登录失败,还有" + (3 - i) + "次机会!");
}
}
}
}
continue
continue语句用于结束本次循环,继续执行下一次循环。
continue语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环,这个和前面的标签的使用的规则是一样的。
{
...
continue;
...
}
break语句与continue语句的区别
break是直接退出该循环,continue是结束此次条件的循环,continue下面的语句不再执行,返回去执行该循环的下一个条件。
break退出此次循环。
public class Example {
public static void main(String[] args) {
int i = 1;
while(i <= 4){
i++;
if(i == 2){
break;
}
System.out.println("i = " + i);
}
System.out.println("循环结束");
}
}
/*
循环结束
*/
continue不退出此次循环。
public class Example {
public static void main(String[] args) {
int i = 1;
while(i <= 4){
i++;
if(i == 2){
continue;
}
System.out.println("i = " + i);
}
System.out.println("循环结束");
}
}
/*
i = 3
i = 4
i = 5
循环结束
*/
return
当return用在方法时,表示跳出方法,如果使用在main,则表示退出程序。
例题
/*
某人有100000元,每经过一次路口,需要缴费,规则如下:
当现金>50000元时,每次需要缴费0.05,当现金<=50000时,每次需要缴费1000
编程计算该人可以经过多少次路口?(使用while break)
根据题目要求,得出三种情况
money > 50000
money >= 1000 && money <= 50000
money < 1000
使用多分支语句
money = money - money * 0.05 -> money *= 0.95
*/
public class Example {
public static void main(String[] args) {
double money = 100000;
int count = 0;
while (true){
if(money > 50000){
money *= 0.95;
count++;
}else if(money >= 1000 && money <= 50000){
money -= 1000;
count++;
}else{
break;
}
}
System.out.println("count = " + count);
}
}
/*
判断一个整数是否是水仙花数。
所谓水仙花数是指一个三位数,及其个位上的数字立方和等于其本身。
例如 153 = 1 * 1 * 1 + 5 * 5 * 5 + 3 * 3 * 3
int n = 153
先得到n的百位、十位、个位的数字。
n的百位 n / 100
n的十位 n % 100 / 10
n的个位 n % 10
判断
*/
public class Example {
public static void main(String[] args) {
int n = 153;
int n1 = n / 100;
int n2 = n % 100 / 10;
int n3 = n % 10;
if(n1 * n1 * n1 + n2 * n2 * n2 + n3 * n3 * n3 == n){
System.out.println(n + "是水仙花数");
}else{
System.out.println("不是水仙花数");
}
}
}
/*
输出1-100间不能被5整除的数,每五个一行
先输出1-100的数字
在进行过滤出不能被5整除的数
每五个数字一行,用count计数,当count为5时,进行换行操作,再对count赋0。
*/
public class Example {
public static void main(String[] args) {
int count = 0;
for(int i = 1;i <= 100;i++){
if(i % 5 != 0){
System.out.print(i + "\t");
count++;
}
if(count == 5){
System.out.println();
count = 0;
}
}
}
}
/*
输出小写的a-z和大写的Z-A
考察了对于编码的认识
'b' = 'a' + 1
'c' = 'a' + 2
...
'Y' = 'Z' - 1
...
*/
public class Example {
public static void main(String[] args) {
for(char c1 = 'a';c1 <= 'z';c1++){
System.out.print(c1 + "\t");
}
System.out.println();
for(char c1 = 'Z';c1 >= 'A';c1--){
System.out.print(c1 + "\t");
}
}
}
数组
可以存放多个同一类型的数据,数组也是一种数据类型,是引用类型。
数据就是一种数据。
/*
一个养鸡场有六只鸡,它们的体重分别是3kg,5kg,1kg,3.4kg,2kg,50kg。
请问这六只鸡的总体中是多少?平均体重是多少?
定义一个数组。
double[]是double类型的数组,数组名是hens。
数组可以通过for循环遍历。
可以通过hens[i]来访问数组的元素,i是下标,下标是从0开始编号的,之后依此类推。
通过for循环就可以循环的访问数组的元素。
使用一个变量total将各个元素累计。
*/
public class Example {
public static void main(String[] args) {
double[] hens = {3,5,1,3.4,2,50};
double total = 0;
for(int i = 0;i <= 5;i++){
total += hens[i];
}
System.out.println("总体重为" + total);
System.out.println("平均体重为" + (total / 6));
}
}
可以通过数组名.length来获取数据的大小/长度。
数组的使用
动态初始化
数组类型 数组名[] = new 数组类型[大小];
int a[] = new int[5];
//创建了一个名为a的数组,存放的有5个int。
数组类型[] 数组名;
数组名 = new 数组类型[大小]
int[] b;
b = new int[5];
//先声明数组,再分配内存空间用来存放数据。
数组元素的引用利用下标,下标是从0开始的。
/*
循环输入五个成绩,保存到double数组,并输出。
*/
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double[] scores = new double[5];
//double[] scores;
//scores = new double[5]
for(int i = 0;i < scores.length;i++){
System.out.println("请输入第" + (i + 1) + "个元素的值");
scores[i] = scanner.nextDouble();
}
System.out.println("===========================");
for(int i = 0;i < scores.length;i++){
System.out.println("第" + (i + 1) + "个元素的值为" + scores[i]);
}
}
}
静态初始化
数据类型 数组名[] = {元素值1,元素值2,元素值3,...};
int a[] = {1,2,3,4,5,6,7,8};
数组的使用细节
- 数组可以是多个相同类型数据的集合,实现对这些数据的统一管理。数组也是可以进行自动类型转换的。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。
- 数组创建后,如果没有赋值,就有默认值。
- int 0
- short 0
- byte 0
- long 0
- float 0.0
- double 0.0
- char \u0000
- boolean false
- String null
- 使用数组的步骤
- 声明数组并开辟空间。
- 给数组各个元素赋值 。
- 使用数组。
- 数组的下标是从0开始的,下标的最大值为长度-1。
- 数组下标必须在指定范围内使用,否则报:下标越界异常。
- 数组属于引用类型。
例题
/*
请求出一个数组int[]的最大值{4,-1,9,10,23},并得到对应的下标。
定义一个int数组,int[] arr = {4,-1,9,10,23}。
假定arr[0]为最大值,即max = arr[0],maxIndex = 0。
从下标1开始遍历数组arr,如果max < 当前元素,说明max不是最大值,需要进行交换,max = 当前元素,maxIndex = 当前元素的下标
当我们遍历这个数组arr后,max就是真正的最大值,maxIndex就是最大值的下标。
*/
public class Example {
public static void main(String[] args) {
int[] arr = {4,-1,9,10,23};
int max = arr[0];
int maxIndex = 0;
for(int i = 1;i < arr.length;i++){
if(max < arr[i]){
max = arr[i];
maxIndex = i;
}
}
System.out.println("max = " + max + "\tmaxIndex = " + maxIndex);
}
}
数组赋值机制
基本数据类型赋值,赋值方式为值拷贝。
不会影响到原来的值。
数组在默认情况下是引用传递,赋的值是地址,赋值方式为引用传达。
是一个地址,会影响到原来的。
数组拷贝
/*
将 int[] arr1 = {10,20,30} 拷贝到arr2数组,要求数据空间是独立的
创建一个新的数组arr2,开辟新的数据空间。
大小为arr1.length
遍历arr1,把每个元素拷贝到对应的位置
*/
public class Example {
public static void main(String[] args) {
int[] arr1 = {10,20,30};
int[] arr2 = new int[arr1.length];
for(int i = 0;i < arr1.length;i++){
arr2[i] = arr1[i];
}
System.out.print("arr2的元素为");
for(int i = 0;i < arr2.length;i++){
System.out.print(arr2[i] + " ");
}
System.out.println();
}
}
数组反转
/*
把数组int[] arr = {11,22,33,44,55,66}的内容反转
定义数组,int[] arr = {11,22,33,44,55,66}。
把arr[0]和arr[5]进行交换,{66,22,33,44,55,11}。
把arr[1]和arr[4]进行交换,{66,55,33,44,22,11}。
把arr[2]和arr[3]进行交换,{66,55,44,33,22,11}。
一共交换三次 = arr.length / 2。
下标 0->1->2 5->4->3。
即 arr[i]和arr[arr.length - 1 - i]进行交换。
temp变成了arr[arr.length - 1 - i]被赋给了arr[i]。
arr[arr.length - 1 - i]变为了arr[i]。
*/
public class Example {
public static void main(String[] args) {
int[] arr = {11,22,33,44,55,66};
for(int i = 0;i <arr.length / 2;i++){
int temp = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = arr[i];
arr[i] = temp;
}
System.out.println("arr反转后的数组元素为:");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
/*
把数组int[] arr = {11,22,33,44,55,66}的内容反转
先创建一个新的数组arr2,大小arr1.length
逆序遍历arr1,将每个元素拷贝到arr2的元素中(顺序拷贝)
建议增加一个循环遍历 j->0->5
当for循环结束后,arr2就是一个逆序的数组
让arr1指向arr2的数据空间,arr1原来的数据空间就没有被引用,就被当做垃圾销毁
*/
public class Example {
public static void main(String[] args) {
int[] arr1 = {11,22,33,44,55,66};
int[] arr2 = new int[arr1.length];
for(int i = arr1.length - 1,j = 0;i >= 0;i--,j++){
arr2[j] = arr1[i];
}
arr1 = arr2;
System.out.println("arr2的元素为" );
for(int i = 0;i < arr1.length;i++){
System.out.print(arr1[i] + " ");
}
System.out.println();
}
}
数组添加
/*
实现动态的给数组添加元素效果,实现对数组的扩容
原始数组使用静态分配 int[] arr = {1,2,3}
增加元素4,直接放在数组的最后arr = {1,2,3,4}
用户可以通过如下方法来决定是否继续添加,添加成功,是否继续?Y\N
定义初始数组 int[] arr = {1,2,3}
定义一个新的数组 int[] arrnew = new int[arr.length + 1]
遍历arr数组,依次将arr的元素拷贝到arrnew数组
将4赋给arrnew[arr.length - 1] = 4,赋给arrnew的最后一个元素
让arr指向arrnew。原来的arr数组被销毁,成为垃圾
创建一个Scanner对象,接收用户想要添加的元素和回答。
因为用户什么时候退出不确定,需要一个循环,do...while和break。
*/
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] arr = {1,2,3};
do {
int[] arrnew = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
arrnew[i] = arr[i];
}
System.out.println("请输入想要添加进入的元素:");
int add = scanner.nextInt();
arrnew[arrnew.length - 1] = add;
arr = arrnew;
System.out.println("arrnew数组的元素为");
for (int i = 0; i < arrnew.length; i++) {
System.out.print(arrnew[i] + " ");
}
System.out.println();
System.out.println("添加成功,是否继续?Y\\N");
char talk = scanner.next().charAt(0);
if(talk == 'n'){
break;
}
}while(true);
System.out.println("程序结束");
}
}
数组缩减
/*
有一个数组{1,2,3,4,5},可以将该数组进行缩减,提示用户是否继续缩减,每次缩减最后那个元素。
当只剩下最后一个元素,提示,不能再进行缩减。
定义初始数组int[] arr = {1, 2, 3, 4, 5}
定义一个新的数组 int[] arrnew = new int[arr.length - 1]
遍历arr数组,依次将arr的元素拷贝到arrnew数组
用do...while循环,循环的是arr.length,每循环一次都会变少。
故arrnew的输出循环语句为for (int i = 0; i < arr.length - 1; i++)
所以一直到arr.length==1时,则不能再进行缩减。
*/
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] arr = {1, 2, 3, 4, 5};
do {
int[] arrnew = new int[arr.length - 1];
for (int i = 0; i < arr.length - 1; i++) {
arrnew[i] = arr[i];
}
System.out.println("arrnew数组的元素为");
for (int i = 0; i < arr.length - 1; i++) {
System.out.print(arrnew[i] + " ");
}
System.out.println();
arr = arrnew;
System.out.println("缩减成功,是否继续?Y\\N");
char talk = scanner.next().charAt(0);
if (talk == 'n') {
break;
} else if (arr.length == 1) {
System.out.println("不能再继续缩减");
break;
}
}while(true);
System.out.println("程序结束");
}
}
排序
排序是将多个数据,依指定的顺序进行排列的过程。
排序的分类
- 内部排序。
- 指将需要处理的所有数据都加载到内部存储器中进行排序。
- 包括
- 交换式排序法。
- 选择式排序法。
- 插入式排序法。
- 外部排序
- 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。
- 包括
- 合并排序法。
- 直接合并排序法。
冒泡排序
通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底下的气泡一样网上冒。
冒泡排序是从小到大的顺序排列。
分析冒泡排序
数组[24,69,80,57,13]
- 第一轮排序:目标是把最大数放在最后
- 第一次比较[24,69,80,57,13]
- 第二次比较[24,69,80,57,13]
- 第三次比较[24,69,57,80,13]
- 第四次比较[24,69,57,13,80]
- 第二轮排序:目标是把第二大的数放在倒数第二位
- 第一次比较[24,69,57,13,80]
- 第二次比较[24,57,69,13,80]
- 第三次比较[24,57,13,69,80]
- 第三轮排序:目标是把第三大的数放在倒数第三位
- 第一次比较[24,57,13,69,80]
- 第二次比较[24,13,57,69,80]
- 第四轮排序:目标是把第四大的数放在倒数第四位
- 第一次比较[13,24,57,69,80]
冒泡排序的特点
数组[24,69,80,57,13]
- 有五个元素。
- 一共进行了四轮排序。可以看成外层循环。
- 每一轮排序可以确定一个数的位置,比如第一轮排序可以确定最大数,第二轮排序可以确定第二大的数,以此类推。
- 当进行比较时,如果前面的数字大于后面的数字,就交换。
- 每一轮的比较都在逐渐减少。4 -> 3 -> 2 -> 1
冒泡排序的实现
简单但复杂版
/*
将五个无序,24,69,80,57,13使用冒泡排序法将其排成一个从小到大的有序数列。
*/
public class Example {
public static void main(String[] args) {
int[] arr = {24,69,80,57,13};
int temp = 0; //用于交换
/*分析冒泡排序
数组[24,69,80,57,13]
1.第一轮排序:目标是把最大数放在最后
第一次比较[24,69,80,57,13]
第二次比较[24,69,80,57,13]
第三次比较[24,69,57,80,13]
第四次比较[24,69,57,13,80]
*/
for(int j = 0;j < 4;j++){ //第一轮排序进行了四次比较
//如果前面的数字大于后面的数字,就交换
if(arr[j] > arr[j + 1]){
temp = arr[j]; //前面的数字赋给前面的数字
arr[j] = arr[j + 1]; //将后面的数字赋给前面的数字
arr[j + 1] = temp; //temp赋给后面的数字->前面的数字被赋给后面的数字
}
}
System.out.println("第一轮排序后:");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
for(int j = 0;j < 3;j++){ //第二轮排序进行了四次比较
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
System.out.println("第二轮排序后:");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
for(int j = 0;j < 2;j++){ //第三轮排序进行了二次比较
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
System.out.println("第三轮排序后:");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
for(int j = 0;j < 1;j++){ //第四轮排序进行了1次比较
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
System.out.println("第四轮排序后:");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
简单且易于理解版
/*
将五个无序,24,69,80,57,13使用冒泡排序法将其排成一个从小到大的有序数列。
*/
public class Example {
public static void main(String[] args) {
int[] arr = {24,69,80,57,13};
int temp = 0; //用于交换
//将多轮排序使用外层循环包括起来即可
for(int i = 0;i < 4;i++){ //一共进行了四轮比较
for(int j = 0;j < 4 - i;j++){ //第一轮排序进行了四次比较
//如果前面的数字大于后面的数字,就交换
if(arr[j] > arr[j + 1]){
temp = arr[j]; //前面的数字赋给前面的数字
arr[j] = arr[j + 1]; //将后面的数字赋给前面的数字
arr[j + 1] = temp; //temp赋给后面的数字->前面的数字被赋给后面的数字
}
}
System.out.println("第" + (i + 1) + "轮排序后:");
for(int k = 0;k < arr.length;k++){
System.out.print(arr[k] + " ");
}
System.out.println();
}
}
}
最终版
/*
将五个无序,24,69,80,57,13使用冒泡排序法将其排成一个从小到大的有序数列。
*/
public class Example {
public static void main(String[] args) {
int[] arr = {24,69,80,57,13};
int temp = 0; //用于交换
//将多轮排序使用外层循环包括起来即可
for(int i = 0;i < arr.length - 1;i++){ //一共进行了四轮比较 数组的长度-1
for(int j = 0;j < arr.length - 1 - i;j++){ //第一轮排序进行了四次比较
//如果前面的数字大于后面的数字,就交换
if(arr[j] > arr[j + 1]){
temp = arr[j]; //前面的数字赋给前面的数字
arr[j] = arr[j + 1]; //将后面的数字赋给前面的数字
arr[j + 1] = temp; //temp赋给后面的数字->前面的数字被赋给后面的数字
}
}
System.out.println("第" + (i + 1) + "轮排序后:");
for(int k = 0;k < arr.length;k++){
System.out.print(arr[k] + " ");
}
System.out.println();
}
}
}
查找
在Java中,我们常用的查找方式有两种
- 顺序查找
/* 有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王猜数游戏。 从键盘中任意输入一个名称,判断数列中是否包含此名称(顺序查找) 要求:如果找到了,就提示找到,并且给出其下标。 定义一个数组 接收用户输入,遍历数组,逐一比较,如果有,则提示信息,并退出。 定义index = -1,找到后index定义为i,即index为i时找到,index为-1时则没有找到。 */ import java.util.Scanner; public class Example { public static void main(String[] args) { String[] names = {"白眉鹰王","白眉鹰王","紫衫龙王","青翼蝠王"}; Scanner scanner = new Scanner(System.in); System.out.println("请输入想要查找的名字:"); String findName = scanner.next(); int index = -1; for(int i = 0;i < names.length;i++){ if(findName.equals(names[i])){ System.out.println("恭喜你!找到了" + findName); System.out.println("下标为:" + i); index = i; break; } } if(index == -1){ System.out.println("很遗憾,没有找到" + findName); } } }
- 二分查找
多维数组
二维数组
从定义形式上看 int[][]
原来的一堆数组的每个元素是一对数组,就构成二维数组。
二维数组的使用
静态初始化
/*
请用二维数组输出如下图形
000000
001000
020300
000000
*/
public class Example {
public static void main(String[] args) {
int[][] arr = {{0,0,0,0,0,0},{0,0,1,0,0,0},{0,2,0,3,0,0},{0,0,0,0,0,0}};
for(int i = 0;i < arr.length;i++){ //遍历二维数组的每个元素
for(int j = 0;j < arr[i].length;j++){ //arr[i].length得到对应的每个一维数组的长度
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
}
}
/*
int arr[][] = {{4,6},{1,4,5,7},{-2}},遍历该二维数组,并得到和。
遍历二维数组,并将各个值累计到int sum,遍历结束输出和
*/
public class Example {
public static void main(String[] args) {
int arr[][] = {{4,6},{1,4,5,7},{-2}};
int sum = 0;
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr[i].length;j++){
sum += arr[i][j];
}
}
System.out.println(sum);
}
}
动态初始化
类型[][] 数组名 = new 类型[大小][大小];
int[][] arr = new int[3][4];
类型 数组名[][]; //先声明
数组名 = new 类型[大小][大小]; //再定义
int arr[][]; //先声明
arr = new int[3][4]; //再定义
Java的二维数组中允许一维数组的元素个数不一样。
/*
动态创建下面的数组,并输出
1
2 2
3 3 3
一共有三个一维数组,每个一维数组的元素是不一样的
因为第0个一维数组有1个元素,故arr[i] = new int[i + 1]。以此类推
因为第0个一维数组的元素是1,故arr[i][j] = i + 1。以此类推
*/
public class Example {
public static void main(String[] args) {
//创建一个二维数组,但是只是确定一维数组的个数
int[][] arr = new int[3][]; //因为一维数组的元素个数不同,所以这里没有定义后面的大小
for(int i = 0;i < arr.length;i++){ //遍历arr的每个一维数组
arr[i] = new int[i + 1]; //给每个一维数组开空间 new,如果没有给一维数组开空间,那么arr[i]就是null
for(int j = 0;j < arr[i].length;j++){ //遍历一维数组,并给一维数组的每个元素赋值
arr[i][j] = i + 1;
}
}
//遍历arr输出
for(int i = 0;i < arr.length;i++){
//输出arr的每个一维数组
for(int j = 0;j < arr[i].length;j++){
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
例题
/*
使用二维数组打印一个10行杨辉三角。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
第1行有1个元素,第n行有n个元素。
每一行的第1个元素和最后1个元素都是1。
从第3行开始,对于非第1个元素和最后1个元素的元素的值arr[i][j]
a[i][j] = arr[i - 1][j] + arr[i - 1][j - 1]
*/
public class Example {
public static void main(String[] args) {
int[][] YangHui = new int[10][];
for(int i = 0;i < YangHui.length;i++){
YangHui[i] = new int[i + 1];
for(int j = 0;j < YangHui[i].length;j++){
if(j == 0 || j == YangHui[i].length - 1){
YangHui[i][j] = 1;
}else {
YangHui[i][j] = YangHui[i - 1][j] + YangHui[i - 1][j - 1];
}
}
}
for(int i = 0;i < YangHui.length;i++){
for(int j = 0;j < YangHui[i].length;j++){
System.out.print(YangHui[i][j] + "\t");
}
System.out.println();
}
}
}
/*
已知有个升序的数组,要求插入一个元素,该数组顺序依然是升序,比如{10,12,45,90},添加23后,数组为{10,12,23,45,90}
本质是数组扩容+定位
我们先确定 添加数应该插入到哪个索引 然后扩容
先定义原数组
*/
public class Example {
public static void main(String[] args) {
int[] arr = {10,12,45,90};
int insertNum = 23;
int index = -1; //index就是要插入的位置
//遍历arr数组
//如果发现 insertNum <= arr[i] 说明i就是要插入的位置
//使用index保留index = i
//如果遍历完后没有发现 insertNum <= arr[i] 则说明index = arr.length 即添加到arr的最后
for(int i = 0;i < arr.length;i++){
if(insertNum <= arr[i]){
index = i;
break;
}
}
if(index == -1){ //说明没有找到位置
index = arr.length;
}
//扩容 先创建一个新的数组 大小arr.length + 1
int[] arrNew = new int[arr.length + 1];
for(int i = 0,j = 0;i < arrNew.length;i++){
if(i != index){ //说明可以把arr的元素拷贝到arrNew
arrNew[i] = arr[j];
j++;
} else { //如果相等 就把这个位置的数替换成插入的数
arrNew[i] = insertNum;
}
}
for(int i = 0;i < arrNew.length;i++){
System.out.print(arrNew[i] + " ");
}
}
}
/*
随机生成10个整数(1-100的范围)保存到数组,并倒序打印以及求平均值、求最大值和最大值的下标,并查找里面是否有8
(int)(Math.random() * 100) + 1 生产随机数 1-100
*/
public class Example {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i = 0; i < arr.length;i++){
arr[i] = (int)(Math.random() * 100) + 1;
}
System.out.println("arr的元素情况");
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("===========================");
System.out.println("arr的元素情况(倒序)");
for(int i = arr.length - 1;i >= 0;i--){
System.out.print(arr[i] + " ");
}
System.out.println();
double sum = arr[0];
int max = arr[0];
int maxIndex = 0;
for(int i = 1;i < arr.length;i++){
if(max < arr[i]){
max = arr[i];
maxIndex = i;
}
sum += arr[i];
}
System.out.println("===========================");
System.out.println("arr的平均值");
System.out.println(sum / arr.length);
System.out.println("===========================");
System.out.println("arr的最大值");
System.out.println(max);
System.out.println("===========================");
System.out.println("arr的最大值下标");
System.out.println(maxIndex);
System.out.println("===========================");
//并查找里面是否有8
int findNum = 8;
int index = -1;
for(int i = 0;i < arr.length;i++){
if(findNum == arr[i]){
System.out.println("查找到含有" + findNum + "下标为" + i);
index = 1;
break;
}
}
if(index == -1){
System.out.println("没有找到" + findNum);
}
}
}
/*
写一个冒泡排序
*/
public class Example {
public static void main(String[] args) {
int[] arr = {20,-1,89,2,890,7};
int temp;
for(int i = 0; i < arr.length - 1;i++){ //外层循环
for(int j = 0;j < arr.length - 1 - i;j++){ //每轮的比较次数
//如果是从小到大arr[j] > arr[j + 1]
//如果是从大到小arr[j] < arr[j + 1]
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for(int i = 0;i < arr.length;i ++){
System.out.print(arr[i] + " ");
}
}
}