JDK的安装与卸载
卸载
首先在控制面版上卸载---》需要在环境变量上删除配置。
安装
直接下载install版的jdk 直接安装即可
配置环境变量(当前电脑在任何位置都能使用jdk中bin目录的命令)
我的电脑--》属性--》高级属性设置--》环境变量--》path 添加jdk安装目录中的bin文件路径
C:\Program Files\Java\jdk-17\bin
环境变量配置
配置JAVA_HOME给其他软件使用java提供一个窗口。
在系统变量中新建一个变量
变量名:JAVA_HOME
变量值::\Program Files\Java\jdk-17
jdk jre jvm
jdk:java开发工具包
jre:java运行时环境
jvm:java虚拟机
jdk包含jre包含jvm
idea自定义快捷模板
File-->Setting-->Editor-->Live Templates
HelloWorld案例常见错误
当cmd命令行窗口的字符编码与.java源文件的字符编码不一致,如何解决?
1.在Notepad++等编辑器中,修改源文件的字符编码:
2.在EditPlus中可以将Java源文件另存为ANSI编码方式(中文操作系统下即为GBK字符集)
3.在使用javac命令式,可以指定源文件的字符编码 --> javac -encoding utf-8 Review01.java
4.一个java文件内有几个类就会生成几个class文件
5.一个java文件内有多个类如果采用动态编译则使用第一个类
6.如果一个类被public修饰则类名必须和它一致
一个java文件中只能有一个类被public修饰
7.java是严格区分大小写
8.java标点符号必须是英文半角
基本语法
- 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行。
注释和打印语句
作用: 对代码进行解释说明
分类: 单行 //
多行 /* * /
文档 /** * /
注意:
1. 多行和文档注释不能嵌套使用
1. 注释只存在于编译时
编译 .java--> .class
反编译 .class --> .java
关键字
被Java语言赋予了特殊含义,用做专门用途的字符串(或单词)
java标识符
标识符:java中对类 方法 变量等命名时采用的字符序列
命名规则:
1.字母 数字 下划线_ 美元符$ 组成
2.数字不能开头
3.不能使用java中的关键字和保留字
保留字:在当前的k版本内没有意义但是以后可能会使用
4.严格区分大小写
命名规范:
对类命名:大驼峰 例: UserTest
对变量 方法命名: 小驼峰 例: userName
对包命名:全部字母小写 例: chs
对常量命名:全部字母大写如果由多个单词组成 中间使用下划线连接 例: PI
两大数据类型
基本数据类型:四类八种
整数类型: byte short int long
浮点: float double
字符: char
布尔: boolean true false
引用数据类型:只要不是基本数据类型那么一定是引用数据类型
整数类型:
计算公式 [2(bit-1)~(2(bit-1))-1]
定义long类型的变量,赋值时需要以"l
"或"L
"作为后缀。
当赋值的值没有超过int界时,可以不用加 "L"
Java程序中变量通常声明为int型,除非不足以表示较大的数,才使用long。
Java的整型常量默认为 int 型
。
浮点类型:
-
浮点型常量有两种表示形式:
-
十进制数形式。如:5.12 512.0f .512 (必须有小数点)
-
科学计数法形式。如:5.12e2 512E2 100E-2
例:5e2==>5*10^2
-
-
float:
单精度
,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。 -
double:
双精度
,精度是float的两倍。通常采用此类型。 -
定义float类型的变量,赋值时需要以"
f
"或"F
"作为后缀。 -
Java 的浮点型
常量默认为double型
如果需要精确
数字计算或保留指定位数的精度,需要使用BigDecimal类
字符类型:
char 型数据用来表示通常意义上“字符
”(占2字节).
char类型是可以进行运算的。因为它都对应有Unicode码,可以看做是一个数值.
a:97 A:65 0:48
布尔类型:
boolean类型数据只有两个值:true、false,无其它 (一个字节)
计算机存储单位
字节(Byte):是计算机用于计量存储容量
的基本
单位,一个字节等于8 bit。
位(bit):是数据存储的最小
单位。二进制数系统中,每个0或1就是一个位,叫做bit(比特),其中8 bit 就称为一个字节(Byte)。
8 bit = 1 Byte
1024 Byte = 1 KB
1024 KB = 1 MB
1024 MB = 1 GB
1024 GB = 1 TB
变量
变量: 在程序执行时值可以发生改变的量
作用: 记录程序运行时产生的信息
注意:
方法内变量是局部变量 它在使用前必须进行初始化赋值就是初始化
在同一个方法内不能声明同名的局部变量
字符串可以与变量名进行拼接中间使用+连接
底层整数存储原理:所有数据在底层都是以2进制形式存储
byte 8bit [-128,127]
二进制最高位是符号位 0正数 1负数
36
源码:0010 0100
反码: 0010 0100
补码: 0010 0100
正整数三码合一
-36:
源码:最高位是1其余位是10进制转为2进制的值 1010 0100
反码: 符号位不变其余位1变00变1 1101 1011
补码: 反码+1 1101 1100
负整数
所有数据在底层是以补码的形式存储
数据类型转换
自动转换 (小转大)
如果有多个数据类型的值做运算结果是最高类型
强制类型转换(大转小)
公式:小的数据类型 变量名=(小的数据类型) 大的数据类型的值;
byte<short==char<int<long<flaot<double;
当强制类型转换大转小超出小的范围时会以2进制形式存储
例:
byte num=(byte)400; ====> num=-112
解:
源码:1 1001 0000 因为byte占8bit,所以只取最后8位
补码:1001 0000
反码:1000 1111
源码:1111 0000
-112
特殊的类型转换
byte short char三个类型的【变量】进行运算结果都是int类型
有时强制类型转换会小转大 (double)100
运算符
按照功能
分为:算术运算符、赋值运算符、比较(或关系)运算符、逻辑运算符、位运算符、条件运算符、Lambda运算符
分类 | 运算符 |
---|---|
算术运算符(7个) | +、-、*、/、%、++、-- |
赋值运算符(12个) | =、+=、-=、*=、/=、%=、>>=、<<=、>>>=、&=、|=、^=等 |
比较(或关系)运算符(6个) | >、>=、<、<=、==、!= |
逻辑运算符(6个) | &、|、^、!、&&、|| |
位运算符(7个) | &、|、^、~、<<、>>、>>> |
条件运算符(1个) | (条件表达式)?结果1:结果2 |
Lambda运算符(1个) | 后面讲解 |
- 按照
操作数个数
分为:一元运算符(单目运算符)、二元运算符(双目运算符)、三元运算符 (三目运算符)
分类 | 运算符 |
---|---|
一元运算符(单目运算符) | 正号(+)、负号(-)、++、--、!、~ |
二元运算符(双目运算符) | 除了一元和三元运算符剩下的都是二元运算符 |
三元运算符 (三目运算符) | (条件表达式)?结果1:结果2 |
算术运算符
“+”号的两种用法
- 第一种:对于
+
两边都是数值的话,+
就是加法的意思 - 第二种:对于
+
两边至少有一边是字符串的话,+
就是拼接的意思
符号:=
- 当“=”两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理。
- 支持
连续赋值
。
赋值运算符: +=、 -=、*=、 /=、%= (不会更改数据类型)
赋值运算符 | 符号解释 |
---|---|
+= |
将符号左边的值 和右边的值 进行相加 操作,最后将结果赋值给左边的变量 |
-= |
将符号左边的值 和右边的值 进行相减 操作,最后将结果赋值给左边的变量 |
*= |
将符号左边的值 和右边的值 进行相乘 操作,最后将结果赋值给左边的变量 |
/= |
将符号左边的值 和右边的值 进行相除 操作,最后将结果赋值给左边的变量 |
%= |
将符号左边的值 和右边的值 进行取余 操作,最后将结果赋值给左边的变量 |
比较(关系)运算符
-
比较运算符的结果都是boolean型,也就是要么是true,要么是false。
-
> < >= <= :只适用于基本数据类型(除boolean类型之外)
== != :适用于基本数据类型和引用数据类型
-
比较运算符“
==
”不能误写成“=
” -
instanceof 检查是否式类的对象 返回结果为true|false
用法: a instanceof 类型 s
逻辑运算符
-
& 和 &&:表示"且"关系,当符号左右两边布尔值都是true时,结果才能为true。否则,为false。
-
| 和 || :表示"或"关系,当符号两边布尔值有一边为true时,结果为true。当两边都为false时,结果为false
-
! :表示"非"关系,当变量布尔值为true时,结果为false。当变量布尔值为false时,结果为true。
-
^ :当符号左右两边布尔值不同时,结果为true。当两边布尔值相同时,结果为false。
-
区分“&”和“&&”: (&&:如果前面的表达式有一个false,就不再执行后面的表达式了)
-
相同点:如果符号左边是true,则二者都执行符号右边的操作
-
不同点:& : 如果符号左边是false,则继续执行符号右边的操作
&& :如果符号左边是false,则不再继续执行符号右边的操作
- 建议:开发中,推荐使用 &&
-
-
区分“|”和“||”:(||:如果前面的表达式有一个ture,就不再执行后面的表达式了)
-
相同点:如果符号左边是false,则二者都执行符号右边的操作
-
不同点:| : 如果符号左边是true,则继续执行符号右边的操作
|| :如果符号左边是true,则不再继续执行符号右边的操作
-
建议:开发中,推荐使用 ||
-
位运算符
位运算符的运算过程都是基于二进制的补码运算
左移:<<
运算规则:在一定范围内,数据每向左移动一位,相当于原数据*2。(正数、负数都适用)
【注意】当左移的位数n超过该数据类型的总位数时,相当于左移(n-总位数)位
右移:>>
运算规则:在一定范围内,数据每向右移动一位,相当于原数据/2。(正数、负数都适用)
无符号右移:>>>
运算规则:往右移动后,左边空出来的位直接补0。(正数、负数都适用)
条件运算符(可嵌套使用)
条件运算符格式:
(条件表达式)? 表达式1:表达式2
运算符优先级
优先级 | 运算符说明 | Java运算符 |
---|---|---|
1 | 括号 | () 、[] 、{} |
2 | 正负号 | + 、- |
3 | 单元运算符 | ++ 、-- 、~ 、! |
4 | 乘法、除法、求余 | * 、/ 、% |
5 | 加法、减法 | + 、- |
6 | 移位运算符 | << 、>> 、>>> |
7 | 关系运算符 | < 、<= 、>= 、> 、instanceof |
8 | 等价运算符 | == 、!= |
9 | 按位与 | & |
10 | 按位异或 | ^ |
11 | 按位或 | ` |
12 | 条件与 | && |
13 | 条件或 | ` |
14 | 三元运算符 | ? : |
15 | 赋值运算符 | = 、+= 、-= 、*= 、/= 、%= |
16 | 位赋值运算符 | &= 、` |
开发建议:
- 不要过多的依赖运算的优先级来控制表达式的执行顺序,这样可读性太差,尽量
使用()来控制
表达式的执行顺序。 - 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它
分成几步
来完成。例如:
(num1 + num2) * 2 > num3 && num2 > num3 ? num3 : num1 + num2;
分支语句
if-else条件判断结构
单结构分支
if(条件表达式){
语句块;
}
双分支条件判断
if(条件表达式) {
语句块1;
}else {
语句块2;
}
多分支条件判断:if...else if...else
if (条件表达式1) {
语句块1;
} else if (条件表达式2) {
语句块2;
}
...
}else if (条件表达式n) {
语句块n;
} else {
语句块n+1;
}
switch-case选择结构
switch(表达式){
case 常量值1:
语句块1;
//break;
case 常量值2:
语句块2;
//break;
// ...
[default:
语句块n+1;
break;
]
}
使用注意点:
-
switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);
-
case子句中的值必须是常量,不能是变量名或不确定的表达式值或范围;
-
同一个switch语句,所有case子句中的常量值互不相同;
-
break语句用来在执行完一个case分支后使程序跳出switch语句块;
如果没有break,程序会顺序执行到switch结尾;
-
default子句是可选的。同时,位置也是灵活的。当没有匹配的case时,执行default语句。
5.2.2 应用举例
利用case的穿透性
在switch语句中,如果case的后面不写break,将出现穿透现象,也就是一旦匹配成功,不会在判断下一个case的值,直接向后运行,直到遇到break或者整个switch语句结束,执行终止
switch的新写法
在 Java 14 中,新的 switch 表达式主要改变了两个方面:
- 支持箭头表达式返回;
- 支持 yied 返回值。
/*
* 需求:指定一个月份,输出该月份对应的季节。一年有四季:
* 3,4,5 春季
* 6,7,8 夏季
* 9,10,11 秋季
* 12,1,2 冬季
*/
// 改进版
switch(month) {
case 1:
case 2:
case 12:
System.out.println("冬季");
break;
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
default:
System.out.println("你输入的月份有误");
break;
}
//改进版2 (一个case后面可以有多个常量值)
int month =6;
switch(month){
case 1,2,12:System.out.println("冬季"); break;
case 3,4,5:System.out.println("春季"); break;
case 6,7,8:System.out.println("夏季") ;break;
case 9,10,11:System.out.println("秋季"); break;
}
//改进版3 (switch 可以有返回值)可以省略break;
/*
统计月份对应的季节
3 4 5 春天
6 7 8 夏天
9 10 11 秋天
12 1 2 冬天
*/
int month = 3;
String seasonName = switch (month) {
case 3,4,5 ->{
yield "春天"; //yield 属性 就是返回的值
}
case 6,7,8 -> "夏天";
case 9,10,11 ->"秋天";
case 12,1,2->"冬天";
default -> "您输入的月份有误";
};
System.out.println("seasonName = " + seasonName);
总结:switch和if分支
if适用范围判断和等值判断
switch只使用等值判断 但是等值判断的效率比If快很多
如何获取一个随机数
Math.random()
1、Math类的random()的调用,会返回一个[0,1)范围的一个double型值
2、Math.random() * 100 ---> [0,100)
(int)(Math.random() * 100) ---> [0,99]
(int)(Math.random() * 100) + 5 ----> [5,104]
3、如何获取[a,b]
范围内的随机整数呢?(int)(Math.random() * (b - a + 1)) + a
例
class MathRandomTest {
public static void main(String[] args) {
double value = Math.random();
System.out.println(value);
//[1,6]
int number = (int)(Math.random() * 6) + 1; //
System.out.println(number);
}
}
Random类
借助java.util.Random
类来产生一个随机数发生器,也是最常用的一种,构造函数有两个,Random()
和Random(long seed)
。
- 第一个就是以当前时间为默认种子
- 第二个是以指定的种子值进行。
[n,m] 公式: rand.nextInt(m-n+1)+n;
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < 10; i++) {
System.out.println(rand.nextInt(100) + 1); //[1,100]
}
}
循环语句
-
理解:循环语句具有在
某些条件
满足的情况下,反复执行
特定代码的功能。 -
循环结构分类:
- for 循环
- while 循环
- do-while 循环
-
循环结构
四要素
:- 初始化部分
- 循环条件部分
- 循环体部分
- 迭代部分
for循环
语法格式:
for (①初始化部分; ②循环条件部分; ④迭代部分){
③循环体部分;
}
执行过程:①-②-③-④-②-③-④-②-③-④-.....-②
说明:
- for(;;)中的两个;不能多也不能少
- ①初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔
- ②循环条件部分为boolean类型表达式,当值为false时,退出循环
- ④可以有多个变量更新,用逗号分隔
for(int i=0,j=0,i<=5;i++){
//循环体
}
while循环
语法格式:
①初始化部分
while(②循环条件部分){
③循环体部分;
④迭代部分;
}
执行过程:①-②-③-④-②-③-④-②-③-④-...-②
说明:
- while(循环条件)中循环条件必须是boolean类型。
- 注意不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
- for循环和while循环可以相互转换。二者没有性能上的差别。实际开发中,根据具体结构的情况,选择哪个格式更合适、美观。
- for循环与while循环的区别:初始化条件部分的作用域不同。
class WhileTest1 {
public static void main(String[] args) {
int i = 1;
while(i <= 5){
System.out.println("Hello World!");
i++;
}
}
}
do-while循环
语法格式:
①初始化部分;
do{
③循环体部分
④迭代部分
}while(②循环条件部分);
执行过程:①-③-④-②-③-④-②-③-④-...-②
说明:**
- 结尾while(循环条件)中循环条件必须是boolean类型
- do{}while();最后有一个分号
- do-while结构的循环体语句是至少会执行一次,这个和for和while是不一样的
- 循环的三个结构for、while、do-while三者是可以相互转换的。
输出1~200能被5整除的数,5个为一行
public class DoWhileExam {
public static void main(String[] args) {
int i=1;
int count=0;
do{
if(i%5==0){
count++;
System.out.print(i+"\t");
if(count%5==0){
System.out.println();
}
}
i++;
}while (i<=200);
}
}
对比三种循环结构
如何选择
- 遍历有明显的循环次数(范围)的需求,选择for循环
- 遍历没有明显的循环次数(范围)的需求,选择while循环
- 如果循环体语句块至少执行一次,可以考虑使用do-while循环
- 本质上:三种循环之间完全可以互相转换,都能实现循环的功能
"无限"循环
最简单"无限"循环格式:while(true)
, for(;;)
关键字break和continue和return的使用
适用范围 在循环结构中使用的作用 相同点
break switch-case
循环结构 一旦执行,就结束(或跳出)当前循环结构 此关键字的后面,不能声明语句
continue 循环结构 一旦执行,就结束(或跳出)当次循环结构 此关键字的后面,不能声明语句
return 任何地方 一旦执行,结束当前方法 此关键字的后面,不能声明语句
break:如果存在标签,跳出标签所在层的循环
continue:如果存在标签,结束标签所在层的本次循环
买可乐
/*
1. 输出1~100内的数 没有 3的倍数 以及个位是3的
2. 20元买可乐 可乐3元一瓶 瓶子可以换1块钱 最多买几瓶?
思路一: 一次购买当前钱数的最大瓶数
总钱数 单价 单次购买的可乐数 剩余钱数
20 3 6 2
8 3 2 2
4 3 1 1
2
思路二: 一次购买一瓶
总钱数 单价 单次购买的可乐数 剩余钱数
20 3 1 17
18 3 1 15
16 3 1 13
....
*/
class LoopExer3{
public static void main(String[] args) {
int money =20;
int price = 3;
int count = 0;
while (money>=price){
//买一瓶可乐
count++;
money = money-3+1;
}
System.out.println("count = " + count);
}
}
class LoopExer2{
public static void main(String[] args) {
//总钱数
int money = 20;
//单价
int price = 3;
//todo 定义变量 记录每次购买的瓶子数
int count = 0;
//循环购买 总钱数>=单价
while (money>=price){
//单次购买的可乐数
int bottle = money / price;
//todo 将每次购买的可乐数 加到一起
count+=bottle;
//剩余钱数
int freeMoney = money % price;
//更改总钱数 = 可乐数 + 剩余钱数
money = bottle+freeMoney;
}
System.out.println("count = " + count);
}
}
public class Test {
public static void main(String[] args) {
for(int i = 1;i<=100;i++){
if(i%3==0|| i%10==3){
continue;
}
System.out.print(i+"\t");
}
}
}
数组
- 数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
- 存储相同类型数据的有序集合.
- 数组中的概念
- 数组名
- 下标(或索引)
- 元素
- 数组的长度
数组的声明
//推荐
元素的数据类型[] 一维数组的名称;
//不推荐
元素的数据类型 一维数组名[];
数组的声明,需要明确:
(1)数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。
(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。
(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
数组的初始化
静态初始化
1.
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,...};
或
数据类型[] 数组名;
数组名 = new 数据类型[]{元素1,元素2,元素3,...};
2.
数据类型[] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分成两个语句写
-
如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。
-
静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。
动态初始化
数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
或
数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
-
[长度]:数组的长度,表示数组容器中可以最多存储多少个元素。
-
注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。
数组元素的引用
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标
,可以通过数组的索引/下标访问到数组中的元素。
数组名[索引/下标]
数组的下标范围?
Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]
数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
数组遍历
1.普通for :通过下标,遍历数组元素展示数据
2.增强for:for(数组元素类型 变量名: 数组名){
变量名(数组内的每个值);}
数组元素的默认值
Java虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
区域名称 | 作用 |
---|---|
虚拟机栈 | 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。 |
堆内存 | 存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
方法区 (元空间) |
存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。 |
本地方法栈 | 当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
程序计数器 | 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
数组的扩容与缩容数组的扩容与缩容
数组的扩容
题目:现有数组 int[] arr = new int[]{1,2,3,4,5}; ,现将数组长度扩容1倍,并将10,20,30三个数据添加到arr数组中,如何操作?
public class ArrTest1 {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
int[] newArr = new int[arr.length << 1];
for(int i = 0;i < arr.length;i++){
newArr[i] = arr[i];
}
newArr[arr.length] = 10;
newArr[arr.length + 1] = 20;
newArr[arr.length + 2] = 30;
arr = newArr;
//遍历arr
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
数组的缩容
题目:现有数组 int[] arr={1,2,3,4,5,6,7}。现需删除数组中索引为4的元素。
public class ArrTest2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7};
//删除数组中索引为4的元素
int delIndex = 4;
//方案1:
/*//创建新数组
int[] newArr = new int[arr.length - 1];
for (int i = 0; i < delIndex; i++) {
newArr[i] = arr[i];
}
for (int i = delIndex + 1; i < arr.length; i++) {
newArr[i - 1] = arr[i];
}
arr = newArr;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}*/
//方案2:
for (int i = delIndex; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
arr[arr.length - 1] = 0;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
数组元素排序
冒泡排序(依次前后两个元素进行比较,根据排序规则确定较大(或较小)的元素向后移动(或向后移动)。最后直到最大(或最小)元素移动到最后一位(或第一位))
- 时间复杂度:平均 O(n²),最坏 O(n²),最好 O(n)
- 稳定排序,通常用于教学和简单场景。
//冒泡
public class ArraySort{
public static void main(String[] args) {
int[] arr ={3,9,-1,10,-2,6};
//优化冒泡算法
boolean flag = false;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if(arr[j]>arr[j+1]){
flag=true;
int temp = arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
System.out.println(Arrays.toString(arr));
if(!flag){
break;
}
}
}
}
选择排序(首先选择第“i”个元素下标当作是最小(或是最大)依次去跟后面的元素比较记录下最小(或最大)元素的下标,然后第“i”个元素于记录的下标元素交换位置)
- 时间复杂度:O(n²)
- 简单易懂,不稳定,适合小规模数据。
//选择排序
class ArraySort2 {
public static void main(String[] args) {
int[] arr = {3, 9, -1, 10, -2, 6};
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
//记录最小数字的下标
minIndex = j;
}
}
//最小和第一个元素交换位置
if (minIndex != i) { //优化 如果最小值的下标没有变化了,就说明已经排好序了
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
else break;
System.out.println(Arrays.toString(arr));
}
}
}
插入排序(把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表)
- 时间复杂度:平均 O(n²),最坏 O(n²),最好 O(n)
- 简单且适合小规模数据,稳定排序。
//插入排序
class ArraySort3 {
public static void main(String[] args) {
int[] arr = {3, 9, -1, 10, -2, 6};
for (int i = 1; i < arr.length; i++) {
//记录待插入的数
int insertVal = arr[i];
//记录待插入的数,插入的前一个有序元素的下标
int insertIndex = i - 1;
//防止下标越界和(定位该元素插入有序列表的下标数)
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex]; //将比待插入大的元素都后移一位
insertIndex--;
}
//循环结束后得到了待插入元素插入的位置
arr[insertIndex+1]=insertVal;
System.out.println(Arrays.toString(arr));
}
}
}
快速排序(通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列)
- 时间复杂度:平均 O(n log n),最坏 O(n²)
- 原地排序,适合大多数情况,效率高。
//快速排序
class ArraySort4 {
public static void main(String[] args) {
int[] arr = {3, 9, -1, 10, -2, 6};
//int[] arr = {3, 6, 8, 10, 1, 2, 1};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int low, int high) {
if (arr == null || arr.length == 0){
return;
}
if (low >= high){
return;
}
int middle = low + (high - low) / 2; // 这样永远都是一段数组的"最中间值(向下取整)" 例子 low =0 high =3 mid = 0+3/2 =1
int pivot = arr[middle];
int l = low, r = high; //记录当前开始下标和当前结束下标
while (l <= r){
//从当前开始下标开始查找比中间值 大的值的下标
while (arr[l] < pivot){
l++;
}
//从当前结束下标开始查找比中间值 小的值的下标
while (arr[r] > pivot){
r--;
}
//如果找到的下标满足当前开始下标小于等于当前结束下标,就互相交换值 ,并当前开始下标后移1,当前结束下标前移1
if (l <= r){
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
l++;
r--;
}
}
if (low < r){ //如果开始下标小于当前结束下标,向左递归(开始下标不变,结束下标变成,当前结束下标 r) 【1,3,4] ,2,1】
quickSort(arr, low, r);
}
if (high > l){ //如果结束下标大于当前开始下标,向右递归(结束下标不变,开始下标变成,当前开始下标 l) 【1,3, [4,2,1】
quickSort(arr, l, high);
}
}
}
归并排序(是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之))
- 时间复杂度:O(n log n)
- 稳定排序,适合处理大数据量,尤其是链表。
二维数组
分类
- 二维数组:
- 所谓二维数组其实就是多个一维数组作为元素,存储在一个一维数组中
- 三维数组
- 所谓三维数组其实就是多个二维数组作为元素,存储在一个一维数组中
- 四维数组
- 所谓三维数组其实就是多个三维数组作为元素,存储在一个一维数组中
声明与初始化
声明
//推荐
元素的数据类型[][] 二维数组的名;
//不推荐
元素的数据类型 二维数组名[][];
//不推荐
元素的数据类型[] 二维数组名[]
静态初始化
int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//声明与初始化必须在一句完成
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
int[][] arr;
arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//错误,静态初始化右边new 数据类型[][]中不能写数字
动态初始化
/**动态1*/
//(1)确定行数和列数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
例:int[][] arr = new int[3][2];
//其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
//其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格
//此时创建完数组,行数、列数确定,而且元素也都有默认值
//(2)再为元素赋新值
二维数组名[行下标][列下标] = 值;
/**动态2*/
//(1)先确定总行数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][];
//此时只是确定了总行数,每一行里面现在是null
//(2)再确定每一行的列数,创建每一行的一维数组
二维数组名[行下标] = new 元素的数据类型[该行的总列数];
//此时已经new完的行的元素就有默认值了,没有new的行还是null
//(3)再为元素赋值
二维数组名[行下标][列下标] = 值;
遍历二维数组(多位也一样)
普通for
for(int i=0; i<二维数组名.length; i++){ //二维数组对象.length
for(int j=0; j<二维数组名[i].length; j++){//二维数组行对象.length
System.out.print(二维数组名[i][j]);
}
System.out.println();
}
增强for
for(一维数组类型[] 变量名1:二维数组变量){
for(数据类型 变量名2 : 变量名1){
变量名(数组内的每个值);
}
}
空指针异常:
public class TestNullPointerException {
public static void main(String[] args) {
//定义数组
int[][] arr = new int[3][];
System.out.println(arr[0][0]);//NullPointerException
}
}
因为此时数组的每一行还未分配具体存储元素的空间,此时arr[0]是null,此时访问arr[0][0]会抛出NullPointerException
空指针异常。
面向对象思想编程
方法(对功能进行封装,实现代码的复用)
方法
是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数
或过程
。- 将功能封装为方法的目的是,可以
实现代码重用,减少冗余,简化代码
- Java里的方法
不能独立存在
,所有的方法必须定义在类里。
方法必须声明在类中 且不能嵌套使用
类{
方法1(){
}
方法2(){
}
}
定义方法:
修饰符 返回值类型
public static void 方法名([形参列表]){
//方法的内容
}
//public static 是方法的修饰符 公共的 静态的
//返回值类型:表示方法运行的结果的数据类型,方法执行后将结果返回到调用者
//方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字
//形参列表:表示完成方法体功能时需要外部提供的数据列表
//方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码
方法调用:
public class Method {
public static void main(String[] args) {
System.out.println("调用方法");
eat();
}
public static void eat(){
System.out.println("吃饭了!!!");
}
}
方法的形参和返回值
形参和实参:
方法的形参:
1.方法的声明处
2.规定方法实参的数量和类型
方法的实参:
方法调用者传递的实际的值
例:
public class MethodTest3 {
//方法的声明
public static void sum(int a,int b){//形参
System.out.println(a+b);
}
public static void main(String[] args) {
sum(10,30);//实参
}
}
方法的返回值:
有的时候需要方法执行的结果作为下次执行的条件就需要带返回值的方法
public static 返回值类型 方法名([形参列表]){
方法体的功能代码
}
返回值类型:
void:没有返回值
数据类型:必须通过 return 关键字 返回一个该类型匹配的值
- return语句的作用是结束方法的执行,并将方法的结果返回去
- 如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
- 如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。
- return语句后面就不能再写其他代码了,否则会报错:Unreachable code
例:
public class MethodTest4 {
//方法的声明
public static int sum(int a,int b){//形参
return a+b;
}
public static void main(String[] args) {
int result = sum(10,30);//实参
System.out.println("result = "+ result);
}
}
注意:
方法只声明不调用 不会执行"
方法与方法是兄弟关系
class 类{
方法a(){}
方法b(){}
}
方法执行完毕 回到方法调用处
方法的重载(在同一类中使用相同的方法名表示不同细节实现的方法)
新:本类或者父子类中有相同的方法名 不同的形参列表(数量 顺序 类型) 就是重载
要求: 两同一不同
两同:同一类中同一方法实现
不同:参数列表不同: 数量、类型、顺序
//找两个数的最大值
public class MethodsOverload {
public static void main(String[] args) {
System.out.println((char)Compare('a','b'));
System.out.println(Compare(10,2));
System.out.println(Compare(6.1,5.1));
}
public static int Compare(int a, int b){
return a>b?a:b;
}
public static int Compare(double a, double b){
return (char)(a>b?a:b);
}
public static int Compare(char a, char b){
return a>b?a:b;
}
}
可变参数
当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变参数
声明可变参数:
public 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型... 形参名){
}
可变参数的特点
(1)一个方法最多只能有一个可变参数(语法规定)
(2)如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个
(3)在声明它的方法中,可变参数当成数组使用
(4)可变参数中实参的数量[0,n]
public class MethodVariableParams {
public static void main(String[] args) {
System.out.println(joins('-',"c","h","s"));
}
//字符于字符串拼接
public static String joins(char c,String ...s){
String temp="";
for (int i = 0; i < s.length; i++) {
if(i==s.length-1) temp+=s[i];
else temp+=s[i]+c;
}
return temp;
}
方法的值传递(画图)
基本类型数据的值:传递的是值的副本
数组作为参数:传递的是地址值
标签:arr,int,数据类型,运算符,数组,javaSE,public From: https://www.cnblogs.com/21CHS/p/18365558