一.Java开发环境的搭建
1. 单位换算
1TB = 1024GB
1GB = 1024MB
1MB = 1024KB
1KB = 1024Byte (字节)
1Byte = 8 bit(位)
注意:一个字节占8位
2. DOS命令
DOS : Disk Operation System 磁盘操作系统 即用于操作本地磁盘的系统
命令 | 操作符号 |
---|---|
盘符切换命令 | 盘符名: |
查看当前文件夹内容 | dir |
进入文件夹命令 | cd 文件夹名 |
退出文件夹命令 | cd .. |
退出到磁盘根目录 | cd \ |
清屏 | cls |
退出 | exit |
3. 名词解释
-
JVM(Java Virtual Machine ): Java虚拟机 JVM相当于一个软件 在不同的平台模拟相同的环境 以实现跨平台的效果
-
JRE(Java Runtime Environment) : Java运行环境,包含
JVM
和运行时所需要的核心类库
。 -
JDK(Java Development Kit) : Java开发工具包 ,包含
JRE
和开发人员使用的工具。
三者关系:JDK包含 JRE JRE包含JVM
4. 安装JDK
5.配置环境变量
右键此电脑===》高级系统设置===》高级====》环境变量
系统变量====》新建变量
名:JAVA_HOME
值: C:\Program Files\Java\jdk1.8.0_131
找到Path变量===》编辑 ====》新建
直接写值:%JAVA_HOME%\bin
所有已打开的窗口全部确定即可
用户变量 vs 系统变量
用户变量 :当前用户自己独有的变量,切换到其他用户后就不能使用来了
系统变量:当前系统中,所有的用户都共享的变量
6.测试
徽标 + R 输入 CMD
javac -version
java -version
以上两个命令都出现版本号表示安装JDK并且环境变量配置成功
7.关于配置环境变量问题补充
1.为什么不直接在Path变量中配置C:\Program Files\Java\jdk1.8.0_131\bin
直接在Path变量中写为C:\Program Files\Java\jdk1.8.0_131\bin 这样的格式目前也是没有问题的 也可以使用
但是后续我们使用的一些工具将默认查找JAVA_HOME变量 比如 tomcat maven等等
2.为什么要配置环境变量?
配置环境变量相当于把bin目录下的命令通知给操作系统 便于我们使用当前目录下对应的命令
3.为什么不配置classpath ?
classpath表示类路径 (即JVM加载类会寻找的路径)
早期(JDK1.5之前)的JDK都需要配置此环境变量 从JDK1.5开始 不需要人为的配置此变量
如果配置了,一定删掉!!!
二.第一个Java程序
2.1 源代码
public class HelloWorld{
public static void main(String [] args){
System.out.print("hello world 0724");
}
}
2.2 单词解释
public 公开的
class 类
static 静态的
void 无效的 空的
main 主要的
String 字符串
args arguments 参数 复数形式
System 系统
out 出
print 打印
2.3 注意事项
1.文件名必须与类名完全保持一致 包括大小写
2.注意单词大小写
3.代码中的标点符号默认都是英文模式的
4.注意合适的缩进 每遇到一个大括号就缩进一个tab键的距离
3. 测试运行
在java源文件上方的地址栏中输入CMD 打开DOS命令窗口
1.输入 javac + java文件名 编译 ,将java文件编译为class文件(c 表示单词 Complier 即编译的意思)
2.输入 java + class文件名 执行class文件
4.关键字
-
1.什么是关键字?
-
被Java官方赋予了特殊含义的单词 全部小写
-
-
2.注意事项:
-
关键字众多,不需要记忆,注意后续取名字避免和关键字冲突即可
-
第一个Java程序的扩展
1.详解代码
/**
*/
//公有的类叫做Hello
public class Hello{
//这种固定写法的方法叫做主方法
public static void main(String[] args){
//打印语句
System.out.println("HelloWorld...1");
System.out.println("HelloWorld...2");
System.out.println("HelloWorld...3");
}
}
/*
public-公有的 class-类 Hello-类名
static-静态的 void-无返回值的 main-方法名
String-字符串类型 ps:"用良心做教育"
String[]-字符串类型的数组 ps:["小红","小绿","小黄","明日花绮罗"]
args-数组名
*/
2.代码细节
public修饰的类名必须和文件名一致
Java是一门大小写敏感的语言
括号都是成双成对的
方法里的代码都是从上往下执行-顺序执行
方法中的代码又叫做执行语句,执行语句用分号结束
2、Java代码规范
1.书写格式
经验:灵活使用tab(缩进键)
优先级别:类 > 方法 > 执行语句
2.代码注释
注释 描述 //注释内容 单行注释 /*注释内容*/ 多行注释 /**注释内容*/ 文档注释
编译的过程:Hello.java -> Hello.class
反编译的过程:Hello.class -> Hello.java
注意:
使用Xjad工具进行反编译
注释不会随着编译,而编译到class文件中
3、Path的作用
%JAVA_HOME%/bin --> javac.exe 和 java.exe
理解:在path中配置了某个目录,代表在其他任何目录里都可以打开配置的目录中的文件
见 path的作用.png
4、Java的扩平台特性
见 Java跨平台特性.png
三.Java 基本数据类型与算数运算符
1. 基本数据类型的深入
取值范围顺序
Java 中基本数据类型的取值范围从小到大依次为:byte < short < int < long < float < double
。
其中,
byte
占 1 个字节(8 位),取值范围是 -128 到 127;
short
占 2 个字节(16 位);
int
占 4 个字节(32 位);
long
占 8 个字节(64 位);
float
占 4 个字节(32 位),用于表示单精度浮点数;
double
占 8 个字节(64 位),用于表示双精度浮点数。
特殊情况
-
char
类型:char
类型用于表示单个字符,它可以转换为int
类型,从而获取字符对应的 ASCII 值。例如:
char c1 = 'A';
char c2 = 'a';
int i1 = c1;
int i2 = c2;
System.out.println(i1); // 输出 65
System.out.println(i2); // 输出 97
-
boolean
类型:boolean
类型只有两个值:true
和false
,它不能与其他基本数据类型进行转换。例如:
boolean bool = true;
// 以下代码会编译错误
// System.out.println((int)bool);
字面量赋值
-
byte
和short
类型:如果赋值的字面量在其取值范围内,可以直接赋值。例如:byte b1 = 1;
。如果字面量超出了其取值范围,需要进行强制类型转换。例如:byte b2 = (byte)128;
,此时会发生溢出,b2
的值为 -128。这是因为128
的二进制表示(32 位)为0000 0000 0000 0000 0000 0000 1000 0000
,强制转换为byte
类型(8 位)后变为1000 0000
,在补码表示中,这表示 -128。 -
long
类型:如果赋值的字面量超出了int
类型的取值范围,需要在字面量后面加上L
或l
。例如:long l2 = 12345678901L;
。 -
float
类型:int
类型可以自动转换为float
类型。例如:float f1 = 1;
。double
类型转换为float
类型需要进行强制类型转换或使用f
后缀。例如:float f2 = (float)1.0;
或float f2 = 1.0f;
。
2. 基本数据类型的转型(注意特殊点)
自动转型
取值范围小的类型会自动转换为取值范围大的类型,这是由 Java 编译器自动完成的,无需手动干预。例如:
byte b = 100;
short s = b;
int i = s;
long l = i;
float f = l;
double d = f;
System.out.println(d); // 输出 100.0
强制转型
取值范围大的类型转换为取值范围小的类型时,需要使用强制类型转换符 (type)
。例如:
double d = 123.123;
float f = (float)d;
long l = (long)f;
int i = (int)l;
short s = (short)i;
byte b = (byte)s;
System.out.println(b); // 输出 123
特殊点
-
类型提升:在进行运算时,如果操作数的类型不同,会自动将取值范围小的类型提升为取值范围大的类型。例如,
byte
和short
类型进行运算时,会自动转换为int
类型,结果也为int
类型。如果需要将结果赋值给byte
或short
类型,需要进行强制类型转换。例如:
byte b1 = 10;
byte b2 = 20;
byte result = (byte)(b1 + b2);
System.out.println(result);
-
溢出问题:强制类型转换可能会导致数据溢出。例如,将一个超出目标类型取值范围的数进行强制转换,会得到一个错误的结果。如上面的
byte b2 = (byte)128;//-128
示例。
3. 算数运算符的使用
基本算数运算符
Java 中的基本算数运算符包括 +
(加法)、-
(减法)、*
(乘法)、/
(除法)、%
(取模,即取余数)。例如:
int a = 10;
int b = 3;
int result = a + b;
System.out.println(result); // 输出 13
System.out.println(a + b); // 输出 13
System.out.println(10 + 3); // 输出 13
System.out.println(10 - 3); // 输出 7
System.out.println(10 * 3); // 输出 30
System.out.println(10 / 3); // 输出 3,整数除法会舍去小数部分
System.out.println(10 % 3); // 输出 1
自增自减运算符
-
++
:自增 1,分为前缀自增(先自增,后使用)和后缀自增(先使用,后自增)。例如: -
底层实现需要考虑byte或short:++a 等价于 (type)(a+1)
int a = 10;
System.out.println(++a); // 先将 a 自增为 11,然后输出 11
System.out.println(a); // 输出 11
int b = 10;
System.out.println(b++); // 先输出 b 的值 10,然后 b 自增为 11
System.out.println(b); // 输出 11
-
--
:自减 1,分为前缀自减(先自减,后使用)和后缀自减(先使用,后自减)。例如:
int c = 10;
System.out.println(--c); // 先将 c 自减为 9,然后输出 9
System.out.println(c); // 输出 9
int d = 10;
System.out.println(d--); // 先输出 d 的值 10,然后 d 自减为 9
System.out.println(d); // 输出 9
4. 算数运算符的深入(注意特殊点和面试题)
特殊点
-
byte
和short
运算:byte
和short
类型进行运算时,会自动转换为int
类型,结果也为int
类型。如果需要将结果赋值给byte
或short
类型,需要进行强制类型转换。例如:
byte b1 = 10;
byte b2 = 20;
byte result = (byte)(b1 + b2);
System.out.println(result);
-
浮点数运算:
float
和double
类型进行运算时可能会出现精度丢失的问题。这是因为浮点数在计算机中是以二进制形式存储的,有些十进制小数无法精确表示为二进制小数。例如:
double d1 = 0.5d;
double d2 = 0.4d;
System.out.println(d1 - d2); // 输出 0.0999999998,而不是 0.1
-
字符参与运算:字符在参与运算时,会使用其对应的 ASCII 值。例如:
char c = 'a'; // 'a' 的 ASCII 值为 97
System.out.println(c + 1); // 输出 98
面试题
-
复杂表达式计算:
int a = 8;
int b = (a++) + (++a) + (a * 10);
// 首先,a++ 先使用 a 的值 8,然后 a 变为 9
// 接着,++a 先将 a 自增为 10,然后使用 a 的值 10
// 最后,a * 10 即 10 * 10 = 100
// 所以 b = 8 + 10 + 100 = 118
System.out.println(b); // 输出 118
-
自增赋值问题:
int i = 0;
i = ++i;
// ++i 先将 i 自增为 1,然后将 1 赋值给 i
System.out.println(i); // 输出 1
int j = 0;
j = j++;
// 首先,j++ 先使用 j 的值 0,将 0 保存到一个临时变量中
// 然后,j 自增为 1
// 最后,将临时变量中的 0 赋值给 j
System.out.println(j); // 输出 0
四.Java 基础语法与运算符详解
1.赋值运算符
符号
-
注意:=为赋值号,将赋值号右侧的数据赋值给左侧的变量
-
=
,+=
,-=
,*=
,/=
,%=
复合赋值运算符会自动进行类型转换:
short s = 1; // 等价于 s = (short)(1);
s += 1; // 等价于 s = (short)(s + 1);
赋值规则:
s += 1 s = (T)((s) + (1))
复合赋值 E1 op= E2等价于简单赋值 E1 = (T)((E1) op (E2)),
其中T是E1的类型。
2.字符串拼接符
规则
-
+
两侧均为数值时,执行算术运算。 -
任意一侧为字符串时,执行字符串拼接。
示例
System.out.println(1 + 2 + "abc" + "def" + 1 + 2);
// 运算顺序:3 → "3abc" → "3abcdef" → "3abcdef1" → "3abcdef12"
3.Scanner 输入类
使用步骤
-
导包:
import java.util.Scanner;
-
实例化:
Scanner scan = new Scanner(System.in);
-
读取输入:注意:没有char类型的数据输入
int i = scan.nextInt(); // 读取整数 double d = scan.nextDouble();// 读取浮点数 String str = scan.next(); // 读取字符串(空格分隔)
4.关系运算符
符号
-
==
,!=
,>
,>=
,<
,<=
注意事项
-
==
比较值是否相等,=
是赋值。 -
结果始终为
boolean
类型。
经典面试题
int x = 10, y = 10;
boolean flag = (x = y); // 编译错误:int 不能赋给 boolean
import java.util.Scanner;
public class Test04{
/**
知识点:关系运算符
符号:==、!=、>、>=、<、<=
注意:
1.=是赋值运算符,将右侧的值赋值给左侧的变量
2.==是关系运算符,判断两个值是否相等
3.!=是关系运算符,判断两个值是否不相等
4.关系运算符的结果必须是boolean类型
*/
public static void main(String[] args){
boolean bool = 10 == 20;
System.out.println(bool);//false
System.out.println(10 != 20);//true
System.out.println(10 > 20);//false
System.out.println(10 >= 20);//false
System.out.println(10 < 20);//true
System.out.println(10 <= 20);//true
需求:在控制台输入两个int类型的数字,比较大小
Scanner scan = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = scan.nextInt();
System.out.println("请输入第二个数字:");
int b = scan.nextInt();
boolean bool = a > b;
System.out.println("第一个数字是否比第二个数字大:" + bool);
经典面试题一:输出结果为?
int x = 10;
int y = 10;
boolean flag = (x == y);
System.out.println(flag);//true
flag = (x = y);//会报错,原因boolean类型不能与其他类型兼容
System.out.println(flag);
经典面试题二:输出结果为?
boolean b1 = true;
boolean b2 = false;
boolean b3 = (b1 == b2);
System.out.println(b3);//false -- 比较是否相同
boolean b4 = (b1 = b2);
System.out.println(b4);//false -- 连续赋值
}
}
5.逻辑运算符
符号与特性
运算符 | 名称 | 特性 |
---|---|---|
& | 逻辑与 | 两侧均执行 |
&& | 短路与 | 左侧为 false 则短路 |
| | 逻辑或 | 逻辑或两侧均执行 |
|| | 短路或 | 左侧为true 则短路 |
^ | 异或 | 两侧不同为 true |
! | 非 | 取反 |
应用示例
boolean inRange = num > 50 && num < 100; // 判断数字是否在区间内
System.out.println(false && 10/0 > 5); // 不报错(短路特性)
import java.util.Scanner;
public class Test05{
/**
知识点:逻辑运算符
符号:
&与 &&短路与
|或 ||短路或
^异或
!非
注意:
1.逻辑运算符运行结果都是boolean类型
2.&、&&、|、||、^ 两侧都是boolean类型数据
*/
public static void main(String[] args){
//&与:前后两侧都接boolean值,两侧同时成立(true),结果才为true
System.out.println(true & true);//true
System.out.println(false & true);//false
System.out.println(true & false);//false
System.out.println(false & false);//false
//&&短路与:前后两侧都接boolean值,两侧同时成立(true),结果才为true
System.out.println(true && true);//true
System.out.println(false && true);//false
System.out.println(true && false);//false
System.out.println(false && false);//false
//&与:判断前者为false后,还会判断后者
//&&短路与:判断前者为false后,不会判断后者,效率更高
//ArithmeticException -- 算数异常
System.out.println(false && 10/0>5);
//|或:前后两侧都接boolean值,两侧有一侧成立(true),结果就为true
System.out.println(true | true);//true
System.out.println(false | true);//true
System.out.println(true | false);//true
System.out.println(false | false);//false
//||短路或:前后两侧都接boolean值,两侧有一侧成立(true),结果就为true
System.out.println(true || true);//true
System.out.println(false || true);//true
System.out.println(true || false);//true
System.out.println(false || false);//false
//|或:判断前者为true后,还会判断后者
//||短路或:判断前者为true后,不会判断后者,效率更高
System.out.println(true || 10/0>5);
//^异或:前后两侧都接boolean值,两侧不同为true,相同为false
System.out.println(true ^ true);//false
System.out.println(false ^ true);//true
System.out.println(true ^ false);//true
System.out.println(false ^ false);//false
//!非:置反,true变为false,false变为true
boolean bool1 = true;
System.out.println(!bool1);//false
boolean bool2 = false;
System.out.println(!bool2);//true
需求:在控制台输入一个int类型的数字,判断是否在50~100的区间内
Scanner scan = new Scanner(System.in);
System.out.println("请输入int类型的数字:");
int num = scan.nextInt();
boolean bool = num>50 && num<100;
System.out.println("该数字是否在50~100的区间内:" + bool);
}
}
6.三元运算符
语法
变量 = (条件) ? 值1 : 值2;
返回值类型规则
-
类型相同:直接返回该类型。
-
基本类型兼容:自动类型提升。
-
混合类型(基本与包装类):自动拆箱/装箱。
-
常量与变量:按兼容性决定类型。
常量的值是否在变量所属类型的取值范围里
在 - 按照变量的类型返回数据
不在 - 按照常量的类型返回数据
import java.util.Scanner;
public class Test06{
/**
知识点:表达式
5 + 6 -- 算数表达式
5 > 6 -- 关系表达式
知识点:三目运算符/三元运算符
语法规则:变量 = (表达式)?值1:值2;
理解:表达式的结果必须是boolean
true - 将值1赋值给变量
false- 将值2赋值给变量
做实验:
*/
public static void main(String[] args){
int num = (false)?10:20;
System.out.println(num);
需求1:在控制台输入2个int类型的数字,比较大小,输出最大值
Scanner scan = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = scan.nextInt();
System.out.println("请输入第二个数字:");
int b = scan.nextInt();
int max = (a>b)?a:b;
System.out.println("最大值为:" + max);
需求2:在控制台输入2个int类型的数字,比较大小,输出最小值
Scanner scan = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = scan.nextInt();
System.out.println("请输入第二个数字:");
int b = scan.nextInt();
int min = (a<b)?a:b;
System.out.println("最小值为:" + min);
需求3:在控制台输入3个int类型的数字,比较大小,输出最大值
Scanner scan = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = scan.nextInt();
System.out.println("请输入第二个数字:");
int b = scan.nextInt();
System.out.println("请输入第三个数字:");
int c = scan.nextInt();
int max = (a>b)?a:b;
max = (max>c)?max:c;
System.out.println("最大值为:" + max);
需求4:在控制台输入3个int类型的数字,比较大小,由小到大输出(10<20<30)
Scanner scan = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = scan.nextInt();
System.out.println("请输入第二个数字:");
int b = scan.nextInt();
System.out.println("请输入第三个数字:");
int c = scan.nextInt();
//最大值
int max = (a>b)?a:b;
max = (max>c)?max:c;
//最小值
int min = (a<b)?a:b;
min = (min<c)?min:c;
//中间值
int mid = a+b+c-max-min;
System.out.println(min + "<" + mid + "<" + max);
扩展面试题1:
int a = 5;
System.out.println((a<5)?10.9:9);//9.0
扩展面试题2:
char x = 'x';//码值 - 120
int i = 10;
System.out.println(false?i:x);//120
扩展面试题3:
char x = 'x';//码值 - 120
System.out.println(false?100:x);//x
System.out.println(false?100000:x);//120
扩展 -- 常量的概念:程序执行过程中不可发生改变的量
注意:数字字面量是常量之一
返回值规则:
值1和值2都是常量时,按照取值范围大的类型返回数据
值1和值2都是变量时,按照取值范围大的类型返回数据
值1和值2一个是常量一个是变量,判断常量的值是否在变量所属类型的取值范围里
在 - 按照变量的类型返回数据
不在 - 按照常量的类型返回数据
}
}
扩展面试题 ::注意这里的char类型的常量为规则变量跟常量,暂时把'X'看做是个变量
System.out.println(false ? 100 : 'x'); // 输出 'x'(char 类型)
System.out.println(false ? 100000 : 'x');// 输出 120(char 提升为 int)
7.位运算符
符号与功能
运算符 | 名称 | 功能 |
---|---|---|
& | 位与 | 两位为 1 则结果为 1 |
| | 位或 | 任意一位为 1 则结果为 1 |
^ | 异或 | 两位不同为 1 |
<< | 左移 | 低位补 0,相当于乘以 2 |
>> | 右移 | 高位补符号位,相当于除以 2 |
>>> | 无符号右移 | 高位补 0 |
示例
int a = 19; // 0001,0011
int b = 25; // 0001,1001
System.out.println(a & b); // 17 (0001,0001)
System.out.println(a << 3); // 8192 (左移 3 位)
System.out.println(-1024 >> 3); // -128
System.out.println(-1024 >>> 3); // 536870784
import java.util.Scanner;
public class Test07{
/**
知识点:位运算符
理解:将十进制的数据转换为二进制做运算
符号:&、|、^、<<、>>、>>>
*/
public static void main(String[] args){
//&与:同位比较,两位为1,结果才为1
byte a = 19;//0001,0011
byte b = 25;//0001,1001
//0001,0001
//a - byte:0001,0011
// (int):0000,0000,0000,0000,0000,0000,0001,0011
//b - byte:0001,1001
// (int):0000,0000,0000,0000,0000,0000,0001,1001
// 结果:0000,0000,0000,0000,0000,0000,0001,0001
// (byte):0001,0001
byte result = (byte)(a & b);
System.out.println(result);//17 - 0001,0001
//|或:同位比较,两者有1,结果就为1
int a = 19;//0000,0000,0000,0000,0000,0000,0001,0011
int b = 25;//0000,0000,0000,0000,0000,0000,0001,1001
//0000,0000,0000,0000,0000,0000,0001,1011
System.out.println(a | b);//27
//^异或:同位比较,两者不同为1,相同为0
int a = 19;//0000,0000,0000,0000,0000,0000,0001,0011
int b = 25;//0000,0000,0000,0000,0000,0000,0001,1001
//0000,0000,0000,0000,0000,0000,0000,1010
System.out.println(a ^ b);//10
注意:
&、|、^:前后两侧为数值,该符号为位运算符
&、|、^:前后两侧为boolean,该符号为逻辑运算符
//<<左移:整体向左移动n位,就用n个0补位,补到最低位
//经验:向左移动1位,相当于乘以2
int a = 1024;//0000,0000,0000,0000,0000,0100,0000,0000
//0000,0000,0000,0000,0010,0000,0000,0000
System.out.println(a << 3);//8192
//>>右移:整体向右移动n位,就用n个最高位补位(符号位),补到最高位
//注意:正数右移用0补位,负数右移用1补位
//经验:向右移动1位,相当于除以2
int a = 1024;//0000,0000,0000,0000,0000,0100,0000,0000
//0000,0000,0000,0000,0000,0000,1000,0000
System.out.println(a >> 3);//128
int b = -1024;//1111,1111,1111,1111,1111,1100,0000,0000
//1111,1111,1111,1111,1111,1111,1000,0000
System.out.println(b >> 3);//-128
//>>>无符号位右移:整体向右移动n位,就用n个0补位,补到最高位
//注意:无符号位右移和右移处理正数都是一样的,都用0补位
int a = 1024;//0000,0000,0000,0000,0000,0100,0000,0000
//0000,0000,0000,0000,0000,0000,1000,0000
System.out.println(a >>> 3);//128
int b = -1024;//1111,1111,1111,1111,1111,1100,0000,0000
//0001,1111,1111,1111,1111,1111,1000,0000
System.out.println(b >>> 3);//536870784
}
}
8.扩展知识点
1. 三元运算符的特殊场景
-
null
处理:若一个表达式为null
,返回值类型由另一个表达式决定。Object result = (true) ? "Hello" : null; // 类型为 String
-
自动拆箱陷阱:包装类为
null
时可能抛出NullPointerException
。Integer b = null; int result = (false) ? 10 : b; // 运行时异常
2. 位运算符应用
-
交换两个数(无需临时变量):
a = a ^ b; b = a ^ b; a = a ^ b;
-
快速计算:左移/右移实现乘除 2 的幂次。
3. 常量与变量混合运算
-
若常量值在变量类型范围内,按变量类型返回;否则按常量类型返回。
示例:
int a = 5;
System.out.println((a < 5) ? 10.9 : 9); // 输出 9.0(int 提升为 double)
总结: 运算符的优先级和类型转换规则是 Java 基础中的核心内容,理解这些规则可避免常见的编译错误和逻辑问题。建议通过实际编码验证不同场景下的行为,加深记忆。
五. 流程控制
Java 基础知识点总结
1. 转义字符
1.1 概念
在 Java 中,转义字符用于表示一些特殊的字符,例如双引号、单引号、反斜杠等。通过转义字符,可以在字符串中使用这些特殊字符而不产生语法错误。
1.2 常见转义字符及示例
转义字符 | 含义 | 示例代码 | 输出结果 |
---|---|---|---|
\" | 表示一个双引号字符 | System.out.println("老师说:\"好好学习\""); | 老师说:"好好学习" |
\' | 表示一个单引号字符 | System.out.println("老师说:\'好好学习\'"); | 老师说:' 好好学习' |
\\ | 表示一个反斜杠字符 | System.out.println("老师说:\\好好学习\\"); | 老师说:\ 好好学习 \ |
\n | 表示换行 | System.out.print("好好学习\n"); | 好好学习(换行) |
\t | 表示水平制表符 | System.out.println("3*3=9\t3*4=12\t3*5=15"); | 33=9 34=12 3*5=15 |
1.3 打印方法区别
println():打印内容后换行。
print():只打印内容,不换行。
示例代码:
java
System.out.print("好好学习\n");
System.out.print("天天向上");
输出结果:
好好学习 天天向上
2. 常量
2.1 数字字面量
-
整数默认类型为
int
。 -
小数默认类型为
double
。
2.2 字面值常量
-
双引号括起来的内容
2.3 final 修饰的常量
使用 final
关键字修饰的变量为常量,一旦赋值后不能再修改。
示例代码:
final int num = 100;
System.out.println(num++);//报错 无法为 final 变量 num 分配值
3. Java 执行流程
3.1 顺序结构
程序按照代码的书写顺序依次执行。
3.2 选择结构
3.2.1 if 语句
-
语法结构:
if(条件表达式){
// 语句块
}
-
执行流程:条件表达式的结果为
boolean
类型,如果为true
,则执行语句块;如果为false
,则跳过 if 语句,继续执行后面的代码。
示例代码:
Scanner scan = new Scanner(System.in);
System.out.println("请输入成绩:");
double score = scan.nextDouble();
if(score > 98){
System.out.println("恭喜你,一个奖励大娃娃");
}
3.3 循环结构
包括 for
循环、while
循环、do-while
循环。
3.4 跳转语句
包括 break
、return
、continue
、lable
。
4. 命名规范(Test04.java)
4.1 大驼峰命名法
用于类名,首字母大写,后续每个单词首字母也大写。例如:HelloWorld
。
4.2 小驼峰命名法(推荐)
用于变量名和方法名,第一个单词首字母小写,后续每个单词首字母大写。例如:javaScore
。
5. if...else... 语句
5.1 语法结构
if(条件表达式){
// 语句块 1
} else {
// 语句块 2
}
5.2 执行流程
条件表达式的结果必须为 boolean
类型,如果为 true
,则执行语句块 1;如果为 false
,则执行语句块 2。
示例代码:
Scanner scan = new Scanner(System.in);
System.out.println("请输入成绩:");
double score = scan.nextDouble();
if(score > 98){
System.out.println("恭喜你,奖励一个大娃娃");
} else {
System.out.println("很遗憾,继续努力");
}
6. 多重 if 语句
6.1 语法结构
if(条件表达式 1){
// 语句块 1
} else if(条件表达式 2){
// 语句块 2
} else if(条件表达式 n){
// 语句块 n
} else {
// 语句块 else
}
6.2 执行流程
依次判断条件表达式,如果某个条件表达式为 true
,则执行对应的语句块,然后跳出多重 if 语句;如果所有条件表达式都为 false
,则执行 else
语句块(如果有)。
示例代码:
Scanner scan = new Scanner(System.in);
System.out.println("请输入成绩:");
double score = scan.nextDouble();
if(score >= 90 && score <= 100){
System.out.println("A");
} else if(score >= 80 && score < 90){
System.out.println("B");
} else if(score >= 70 && score < 80){
System.out.println("C");
} else if(score >= 60 && score < 70){
System.out.println("D");
} else if(score >= 0 && score < 60){
System.out.println("E");
} else {
System.out.println("成绩错误");
}
7. 嵌套 if 语句
在一个 if 语句中可以嵌套另一个 if 语句,用于处理更复杂的条件判断。
小结:if可以无限层嵌套
经验:if嵌套最好不要超过3层(考虑到可读性)
示例代码:
Scanner scan = new Scanner(System.in);
System.out.println("请输入考试成绩:");
double score = scan.nextDouble();
if(score >= 0 && score < 16){
System.out.println("可以报名");
String sex = scan.next();
if(sex.equals("男")){
System.out.println("欢迎来到男子组");
} else if(sex.equals("女")){
System.out.println("欢迎来到女子组");
} else {
System.out.println("报名错误");
}
} else if(score >= 16){
System.out.println("很遗憾,你不能参加...");
} else {
System.out.println("成绩错误");
}
8. switch 语句
8.1 语法结构
switch(表达式){
case 值 1:
// 语句块 1
break;
case 值 2:
// 语句块 2
break;
case 值 n:
// 语句块 n
break;
default:
// 语句块 default
break;
}
8.2 执行流程
表达式的值与各个 case
后面的值进行比较,如果相等,则执行对应的语句块,直到遇到 break
语句跳出 switch
语句;如果所有 case
值都不匹配,则执行 default
语句块(如果有)。
8.3 注意事项
-
case
后面的值不能重复。 -
default
语句可以省略。 -
break
语句可以省略,如果省略,会继续执行下一个case
语句块,直到遇到break
或switch
语句结束。 -
表达式的值可以是
byte
、short
、int
、char
、String
(JDK1.7 及以上)、枚举(JDK1.5 及以上)。 -
switch底层逻辑是什么?
-
将表达式的值转化为int类型
输入类型 转化类型 方式 byte int 自动转型 short int 自动转型 int int 直接使用 char int 获取码值 String int 获取hash值 枚举 int 获取枚举对象的编号 -
获取hash值:
-
(String的hash值就是获取的字符的码值 + 散列算法,算出来的一个int值)
-
-
示例代码
Scanner scan = new Scanner(System.in);
System.out.println("请输入抽奖序号:");
String str = scan.next();
switch(str){
case "一等奖":
System.out.println("获得一台华为电脑");
break;
case "二等奖":
System.out.println("获得一部苹果手机");
break;
case "三等奖":
System.out.println("获得一个移动硬盘");
break;
default:
System.out.println("老师会详细讲解抽奖规则");
break;
}
案例
-
需求:输入年和月,输出当月的天数
import java.util.Scanner;
public class Test09{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("请输入年:");
int year = scan.nextInt();
System.out.println("请输入月:");
int month = scan.nextInt();
int day = 0;
switch(month){
case 1:case 3:case 5:case 7:case 8:case 10:case 12:
day = 31;
break;
case 4:case 6:case 9:case 11:
day = 30;
break;
case 2:
if(year%4==0 && year%100!=0 || year%400==0){
day = 29;
}else{
day = 28;
}
break;
}
System.out.println(year + "年" + month + "月当月的天数为:" + day + "天");
}
}
switch判断区间
int num = 3;
switch (num) {
case 1:
case 2:
case 3:
System.out.println("1-3");
break;
case 4:
case 5:
System.out.println("4-5");
break;
default:
System.out.println("其他");
}
8.4 if 与 switch 的比较
-
表达式类型:
-
if
的条件表达式为boolean
类型; -
switch
的表达式可以是byte
、short
、int
、char
、String
、枚举。
-
-
应用场景:
-
if
适用于复杂的条件判断; -
switch
适用于判断值的情况。
-
六.循环语句
1. for 循环
1.1 语法结构
for(表达式 1; 表达式 2; 表达式 3){
// 语句块
}
理解:
表达式1 -- 初始化变量
表达式2 -- 判断条件
表达式3 -- 更新变量
1.2 执行流程
-
执行表达式 1,进行初始化操作。
-
执行表达式 2,进行条件判断,结果为boolean类型。
-
如果为
true
,则执行语句块,然后执行表达式 3,再回到步骤 2 继续判断。 -
如果为
false
,则跳出循环。
-
示例代码:
for(int i = 1; i <= 10; i++){
System.out.println(i);
}
1.3 常见应用场景
-
打印一定范围内的数字。
-
循环输入数据并进行求和、求平均值、找最大值等操作。
示例代码(求 5 个整数的和):
Scanner scan = new Scanner(System.in);
int sum = 0;
for(int i = 1; i <= 5; i++){
System.out.println("请输入第" + i + "个数字:");
int num = scan.nextInt();
sum += num;
}
System.out.println("总和为:" + sum);
知识点
-
用于已知循环次数的情况。
-
语法结构:
for(初始化语句; 判断条件; 迭代语句){ 循环体 }
示例代码
// 打印矩形图案
for(int i = 0;i<3;i++){
for(int j = 0;j<4;j++){
System.out.print("*");
}
System.out.println();
}
代码解释
-
外层循环控制行数,内层循环控制每行的列数。通过嵌套循环可以打印出各种图案。
2. while 循环
知识点
-
用于循环次数不确定,但有明确判断条件的情况。
-
语法结构:
while(判断条件){ 循环体 }
示例代码
int i = 0;
while(i<5){
System.out.println("循环执行中");
i++;
}
代码解释
-
先判断条件,条件为
true
则执行循环体,执行完后再次判断条件,直到条件为false
。
3. do - while 循环
知识点
-
先执行一次循环体,再判断条件。保证循环体至少执行一次。
-
语法结构:
do{ 循环体 }while(判断条件);
示例代码
Scanner scan = new Scanner(System.in);
String str;
do{
System.out.println("请输入内容");
str = scan.next();
}while(str.equals("继续"));
代码解释
-
无论条件是否成立,循环体都会先执行一次,然后再根据条件决定是否继续循环。
4. 循环控制语句
4.1 break
知识点
-
在循环中使用,用于终止当前循环。
示例代码
Scanner scan = new Scanner(System.in);
boolean flag = true;
double sum = 0;
for(int i = 1;i<=5;i++){
System.out.println("请输入第" + i + "个成绩");
double score = scan.nextDouble();
if(score < 0){
flag = false;
break;
}
sum += score;
}
代码解释
-
当输入的成绩为负数时,使用
break
语句终止循环。
4.2 continue
Scanner scan = new Scanner(System.in);
int count = 0;
for(int i = 1;i<=5;i++){
System.out.println("请输入第" + i + "个学生的成绩");
double score = scan.nextDouble();
if(score < 80){
continue;
}
count++;
}
知识点
-
在循环中使用,用于跳过本次循环,直接进入下一次循环。
代码解释
-
当输入的成绩小于 80 分时,使用
continue
语句跳过本次循环,不执行count++
。
4.3 return
知识点
-
在方法中使用,用于返回方法的结果,并终止当前方法的执行。
示例代码
public static void main(String[] args) {
System.out.println("111");
System.out.println("222");
if(true){
return;
}
System.out.println("333");
}
代码解释
-
当条件为
true
时,使用return
语句终止方法的执行,后面的System.out.println("333");
不会执行。
4.4 label
知识点
-
用于标记循环,可配合
break
和continue
语句跳出指定的循环。
示例代码
a: for(int i = 1;i<=5;i++){
for(int j = 1;j<=3;j++){
if(i == 3){
break a;
}
System.out.println(i + " -- " + j);
}
}
代码解释
-
当
i
等于 3 时,使用break a;
语句跳出标记为a
的外层循环。
5. 静态方法
知识点
-
用
static
关键字修饰的方法,可通过类名直接调用。 -
语法结构:
public static 返回值类型 方法名(参数列表){ 方法体 }
方法的调用
- 在需要调用方法的位置直接书写方法名()即可调用。
示例代码
public static void printStar(){
for(int i = 0;i<5;i++){
for(int j = 0;j<=i;j++){
System.out.print("*");
}
System.out.println();
}
}
代码解释
-
printStar
方法是一个静态方法,可通过Test10.printStar();
调用。
6. 日期计算与日历打印
知识点
-
涉及日期计算,判断闰年,计算指定年月的第一天是星期几,以及打印日历。
示例代码
Scanner scan = new Scanner(System.in);
System.out.println("请输入年份:");
int year = scan.nextInt();
System.out.println("请输入月份:");
int month = scan.nextInt();
// 计算指定年份前的总天数
int allDayOfYear = 0;
for(int i = 1900;i<year;i++){
if(i%4==0 && i%100!=0 || i%400==0){
allDayOfYear += 366;
} else {
allDayOfYear += 365;
}
}
代码解释
-
首先获取用户输入的年份和月份,然后计算从 1900 年到指定年份前一年的总天数,根据闰年规则判断每年的天数。
七.方法的重载递归
方法的重载
知识点
-
条件:
-
同一类中,方法名相同,参数列表不同(类型或数量或顺序)。
-
与返回值无关以及访问权限修饰符无关 。
-
-
优点:根据实参类型自动匹配方法。
代码示例
public class Test03 {
public static void main(String[] args) {
System.out.println(getMax(10, 20, 30.3));
}
// 重载示例
public static int getMax(int a, int b) { /*...*/ }
public static int getMax(int a, int b, int c) { /*...*/ }
public static double getMax(double a, double b) { /*...*/ }
public static double getMax(double a, double b, double c) { /*...*/ }
}
4. 方法的递归(错误示例)
知识点
-
递归错误:死循环导致栈溢出(
StackOverflowError
)。 -
原因:每次递归调用都会在栈中开辟新空间,无限调用导致内存耗尽。
代码示例
public class Test04 {
public static void main(String[] args) {
method(); // 错误示例
}
public static void method() {
method(); // 无限递归
}
}
总结
-
方法:注重参数传递、返回值、重载与递归的合理使用。
-
数组:掌握静态/动态初始化、遍历方式及常见操作(如求最值)。
-
递归:必须定义出口,避免栈溢出。
-
代码规范:方法功能单一,变量命名清晰,逻辑分层明确。
5. 递归实现阶乘
知识点
-
递归要点:
-
规律:
n! = (n-1)! * n
-
出口:
1! = 1
-
代码示例
public class Test05 {
public static void main(String[] args) {
System.out.println(method(5)); // 输出120
}
public static int method(int n) {
return (n == 1) ? 1 : method(n - 1) * n;
}
}
6. 方法版本的万年历
知识点
-
功能分解:
-
计算总天数、判断闰年、获取当月天数、打印日历。
-
-
关键方法:
-
getAllDay
:计算从基准年(1900)到目标年的总天数。 -
getWeek
:计算某月第一天的星期。
-
代码结构
//封装方法万历表
import java.util.Scanner;
public class Test06{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("请输入年:");
int year = scan.nextInt();
System.out.println("请输入月:");
int month = scan.nextInt();
//获取星期
int week = getWeek(year,month);
//获取当月的天数
int day = getDay(year,month);
//打印日历
printCalendar(year,month,day,week);
}
//打印日历
public static void printCalendar(int year,int month,int day,int week){
System.out.println(year + "年" + month + "月");
System.out.println("一\t二\t三\t四\t五\t六\t日");
int count = 0;
for(int i = 1;i<week;i++){
System.out.print("\t");
count++;
}
for(int i = 1;i<=day;i++){
System.out.print(i + "\t");
count++;
if(count % 7 == 0){
System.out.println();//换行
}
}
}
//获取星期
public static int getWeek(int year,int month){
int week = getAllDay(year,month)%7;
if(week == 0){
week = 7;
}
return week;
}
//获取总天数
public static int getAllDay(int year,int month){
int allDay = getAllDayOfMonth(year,month) + getAllDayOfYear(year)+1;
return allDay;
}
//获取月的总天数
public static int getAllDayOfMonth(int year,int endMonth){
int AllDayOfMonth = 0;
for(int i = 1;i < endMonth;i++){
AllDayOfMonth += getDay(year,i);
}
return AllDayOfMonth;
}
//获取当月的天数
public static int getDay(int year,int month){
int day = 0;
switch(month){
case 1:case 3:case 5:case 7:case 8:case 10:case 12:
day = 31;
break;
case 4:case 6:case 9:case 11:
day = 30;
break;
case 2:
if(isLeapYear(year)){
day = 29;
}else{
day = 28;
}
break;
}
return day;
}
//获取年的总天数
public static int getAllDayOfYear(int year){
int allDayOfYear =0;
for(int i = 1900;i < year; i++){
if(isLeapYear(i)){
allDayOfYear += 366;
}else{
allDayOfYear += 365;
}
}
return allDayOfYear;
}
//判断是否是闰年
public static boolean isLeapYear(int year){
if(year % 4 == 0 && year % 100 !=0 || year % 400 == 0){
return true;
}else{
return false;
}
}
}
八.数组
1.数组的概念:
一组`连续`的存储空间,存储多个`相同`数据类型的值,长度是`固定`的。
2. 数组的定义
先声明、再分配空间: 数据类型[] 数组名; 数组名 = new 数据类型[长度];
声明并分配空间: 数据类型[] 数组名 = new 数据类型[长度];
声明并赋值(繁): 数据类型[] 数组名 = new 数据类型[]{value1,value2,value3,...};
声明并赋值(简): 数据类型[] 数组名 = {value1,value2,value3,...};
3. 数组的使用
数组的元素:数组中的每个数据称之为数组中的元素
数组的访问:对数组中的元素赋值以及取值的操作 统称为数组的访问
下标、索引、角标、index : 下标自动生成 从0开始 往后依次+1
访问数组通过下标访问:
赋值 数组名[下标] = 值;
取值 System.out.println(数组名[下标]);
访问不存在的下标 将会导致数组下标越界异常 ArrayIndexOutOfBoundsException
一维数组
知识点
-
静态初始化:
String[] names = {"A", "B", "C"};
-
遍历方式:
-
for
循环(需下标时使用)。 -
foreach
循环(无需下标时使用)。
-
代码示例
public class Test07 {
public static void main(String[] args) {
String[] names = {"麻生希", "椎名空", "王舟毅"};
for (String name : names) {
System.out.println(name);
}
}
}
4. 一维数组(动态初始化)
知识点
-
动态初始化:
String[] names = new String[5];
-
默认值:
-
int
:0,double
:0.0,boolean
:false,引用类型:null
。
-
代码示例
public class Test08 {
public static void main(String[] args) {
String[] names = new String[5];
names[0] = "白鸽";
}
}
//{白鸽,null,null,null,null}
5. 数组应用:最大值查找
知识点
-
步骤:
-
动态初始化数组并录入数据。
-
遍历比较求最大值。
-
代码示例
public class Test09 {
public static void main(String[] args) {
int[] arr = new int[5];
// 录入数据...
int max = arr[0];
for (int num : arr) {
if (num > max) max = num;
}
System.out.println("最大值:" + max);
}
}
6.数组的扩容
数组的扩容:
实现步骤:
1.创建比原数组更长的新数组
2.将原数组中的元素依次复制到新数组中
3.将新数组的地址赋值给原数组
数组作为引用数据类型 其数组名中保存的是指向堆中的地址 所以 当我们把一个数组 赋值 给 另外一个数组
赋值的是地址
package com.atguigu.test4;
import java.util.Arrays;
/**
* 数组的扩容:
* 实现步骤:
* 1.创建比原数组更长的新数组
* 2.将原数组中的元素依次复制到新数组中
* 3.将新数组的地址赋值给原数组
*/
public class TestArrayGrow {
public static void main(String[] args) {
int [] oldArr = {1,2,3,4,5};
int [] newArr = new int[oldArr.length * 2];
for(int i = 0;i < oldArr.length ;i++){
newArr[i] = oldArr[i];
}
System.out.println(Arrays.toString(newArr));
// 数组作为引用数据类型 其数组名中保存的是指向堆中的地址
// 所以 当我们把一个数组 赋值 给 另外一个数组
// 赋值的是地址
System.out.println(oldArr);
System.out.println(newArr);
oldArr = newArr;
System.out.println("==================地址赋值以后===================");
System.out.println(oldArr);
System.out.println(newArr);
System.out.println(oldArr.length);
System.out.println(Arrays.toString(oldArr));
System.out.println("------------------------------------------------------------------");
newArr[9] = 666;
System.out.println(oldArr[9]); // 0? 666?
}
}
7.数组的复制
复制数组的三种方式:
循环将原数组中所有元素逐一赋值给新数组。
System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度);
java.util.Arrays.copyOf(原数组, 新长度);//返回带有原值的新数组。
package com.atguigu.test4;
import java.sql.SQLOutput;
import java.util.Arrays;
/**
* @author WHD
* @description TODO
* @date 2023/8/2 14:23
* 复制数组的三种方式:
* 循环将原数组中所有元素逐一赋值给新数组。
* System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度);
* java.util.Arrays.copyOf(原数组, 新长度);//返回带有原值的新数组。
*/
public class TestArrayCopy {
public static void main(String[] args) {
int [] oldArr = {1,2,3,4,5};
int [] newArr = new int[10];
System.arraycopy(oldArr, 1, newArr, 2, 2);
System.out.println(Arrays.toString(newArr));
System.out.println("-------------------------------------------------");
int [] nums = {11,22,33,44,55};
// 第一个参数 原数组
// 第二个参数 新长度 新长度可以小于等于或者大于原数组的长度
int [] newNums = Arrays.copyOf(nums, 5);
System.out.println(Arrays.toString(newNums));
}
}
8.面试题
值传递和引用传递的区别?
Java官方明确指出 Java中只有值传递
基本数据类型属于'值传递',传递的就是值的副本,值的拷贝,在方法中对参数的修改不会影响原变量
引用数据类型属于'引用传递' 传递的是地址,也就是引用,在方法中对参数的修改会影响原变量
String类型是特殊的引用数据类型 作为参数不会影响原变量 因为String是不可变
9.可变长参数
可变长参数:可接收多个同类型实参,个数不限,使用方式与数组相同。
要求:
1.整个形参列表中只能有一个可变长参数
2.必须书写在形参列表的末尾
格式:数据类型...参数名
package com.atguigu.test6;
/**
* 可变长参数:可接收多个同类型实参,个数不限,使用方式与数组相同。
* 要求:
* 1.整个形参列表中只能有一个可变长参数
* 2.必须书写在形参列表的末尾
* 格式:数据类型...参数名
*/
public class TestChangedParam {
public static void m1(int ... args){
System.out.println("m1方法开始执行");
for(int i =0;i < args.length;i++){
System.out.println(args[i]);
}
System.out.println("m1方法执行完毕");
}
public static void m2(int [] args){
System.out.println("m2方法开始执行");
for(int i =0;i < args.length;i++){
System.out.println(args[i]);
}
System.out.println("m2方法执行完毕");
}
public static void main(String[] args) {
m1(1,2,3,4,5,6);
int [] nums = new int[4];
m2(nums);
}
}
10. Arrays工具类
java.util.Arrays 是JDK提供的一个用于操作数组的工具类
toString(数组名) : 将数组中的元素转换为字符串
copyOf(数组名,新长度) : 复制数组
sort(数组名) : 将数组按照升序排序 属于快速排序 实现原理为 递归
fill(数组名,填充元素) : 将数组按照指定元素进行填充
binarySearch(数组名,元素) : 使用二分查找法查找某个元素在数组中的下标
核心方法
方法 | 功能说明 | 注意事项 |
---|---|---|
Arrays.sort(arr) | 数组排序(升序) | 修改原数组 |
Arrays.binarySearch(arr, key) | 二分查找元素下标 | 必须已排序 |
Arrays.copyOf(arr, newLength) | 拷贝数组并指定新长度 | 新长度可大于原数组长度 |
Arrays.fill(arr, value) | 用指定值填充数组 | 可指定填充区间 (from, to) |
Arrays.toString(arr) | 将数组转换为字符串 | 输出格式:[e1, e2, e3] |
代码示例
int[] arr = {67,81,37,72,41,18,88,22,44,66,11};
Arrays.sort(arr); // 排序后:[11, 18, 22, 37, 41, 44, 66, 67, 72, 81, 88]
int index = Arrays.binarySearch(arr, 37); // 返回3
int[] copy = Arrays.copyOf(arr, 15); // 新数组长度15,超出部分补默认值
package com.atguigu.test3;
import java.util.Arrays;
/**
* @author WHD
* @description TODO
* @date 2023/8/4 14:05
* toString(数组名) : 将数组中的元素转换为字符串
* copyOf(数组名,新长度) : 复制数组
* sort(数组名) : 将数组按照升序排序
* fill(数组名,填充元素) : 将数组按照指定元素进行填充
* binarySearch(数组名,元素) : 使用二分查找法查找某个元素在数组中的下标
*/
public class TestArrays {
public static void main(String[] args) {
int [] nums = {56,12,22,41,85,99};
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
int [] newArr = new int[nums.length];
for(int i = 0,j = nums.length -1;i < nums.length;i++,j--){
newArr[i] = nums[j];
}
System.out.println(Arrays.toString(newArr));
System.out.println("--------------------------------------------");
Arrays.fill(newArr, 666);
System.out.println(Arrays.toString(newArr));
System.out.println("--------------------------------------------");
int index = Arrays.binarySearch(nums, 999);
System.out.println("index = " + index);
}
}
11. 数组插入元素
编写方法实现在数组中插入元素
扩展:可以使用System.arraycopy方法 实现插入效果
package com.atguigu.test3;
import java.util.Arrays;
/**
* @author WHD
* @description TODO
* @date 2023/8/4 14:16
* 编写方法实现在数组中插入元素
* 扩展:可以使用System.arraycopy方法 实现插入效果
*/
public class TestArrayInsert {
public static void main(String[] args) {
int [] nums = {1,2,3,4,5};
int[] newArray = insert(nums, -8, 666);
System.out.println(Arrays.toString(newArray));
}
/**
*
* @param oldArray 操作的数据
* @param index 插入元素的位置
* @param num 插入的元素
* @return 新的数组
*/
public static int[] insert(int [] oldArray,int index,int num){
// 下标不能小于0 或者 不能大于数组的长度
if(index < 0 || index > oldArray.length){
System.out.println("下标不合法");
// 如果下标不合法 则将传入的数组 直接返回 表示没有做任何操作
return oldArray;
}
// 代码执行到这里 表示下标没问题
// 准备一个长度+1的数组 用于插入元素
int [] newArray = new int[oldArray.length + 1];
// 遍历开始移动元素
for(int i = 0;i < oldArray.length;i++){
// 情况1 小于插入下标 则直接复制到新数组中
if(i < index){
newArray[i] = oldArray[i];
}else{ // 情况2 大于或者等于插入下标 则移动到新数组的+1位置
newArray[i + 1] = oldArray[i];
}
}
// 空缺位插入指定的元素
newArray[index] = num;
return newArray; // 将新数组返回
}
}
12.数组删除元素
编写方法实现删除数组中的元素
扩展:可以使用System.arraycopy方法 实现删除效果
package com.atguigu.test3;
import java.util.Arrays;
/**
* @author WHD
* @description TODO
* @date 2023/8/4 14:31
* 编写方法实现删除数组中的元素
* 扩展:可以使用System.arraycopy方法 实现删除效果
*/
public class TestArrayDelete {
public static int[] delete(int [] oldArray,int index){
// 判断下标 如果小于0 或者 大于等于数组长度 表示不合法 因为删除 只能删除有效范围以内的
if(index < 0 || index >= oldArray.length){
System.out.println("下标不合法");
return oldArray; // 直接将传入的原数组返回 表示没有做任何操作
}
// 准备一个长度-1的数组 用于删除之后复制元素
int [] newArray = new int[oldArray.length - 1];
// 循环遍历开始复制元素
for(int i = 0;i < newArray.length;i++){
if(i < index){ // 情况1 小于删除的下标 直接复制
newArray[i] = oldArray[i];
}else{ // 情况2 大于等于删除下标 则直接将原数组的+1位置 移动到新数组的后续位置 属于向左移动覆盖
newArray[i] = oldArray[i +1];
}
}
return newArray; // 将新数组返回
}
public static void main(String[] args) {
int [] nums = {1,2,3,4,5};
int[] delete = delete(nums, -5);
System.out.println(Arrays.toString(delete));
}
}
13.二维数组
二维数组:数组中的元素还是数组
package com.atguigu.test3;
/**
* @author WHD
* @description TODO
* @date 2023/8/4 15:30
* 二维数组:数组中的元素还是数组
*/
public class Test2DArray {
public static void main(String[] args) {
int [] arr1 = {1,2,3,4,5};
int [] [] arr2 = { {1,2,4} , {55,88,66} , {666} , {999,888,777}};
System.out.println(arr2[0]);
System.out.println(arr2[0][0]);
System.out.println(arr2[0][1]);
System.out.println(arr2[0][2]);
System.out.println("----------------------------------");
System.out.println(arr2[1][0]);
System.out.println(arr2[1][1]);
System.out.println(arr2[1][2]);
System.out.println("----------------------------------");
System.out.println(arr2[2][0]);
System.out.println("----------------------------------");
System.out.println(arr2[3][0]);
System.out.println(arr2[3][1]);
System.out.println(arr2[3][2]);
System.out.println("----------------------------------");
for(int i = 0;i < arr2.length;i++){
System.out.println(arr2[i]);
for(int j = 0;j < arr2[i].length;j++){
System.out.print(arr2[i][j] + "\t");
}
System.out.println();
}
}
}
二维数组定义:和一维数组定义方式大致相同
二维开辟空间 高维度(第一个中括号)长度必须指定 低维度(第二个中括号)长度可以后续单独指定
package com.atguigu.test3;
/**
* @author WHD
* @description TODO
* @date 2023/8/4 15:38
* 二维数组定义:和一维数组定义方式大致相同
* 二维开辟空间 高维度(第一个中括号)长度必须指定 低维度(第二个中括号)长度可以后续单独指定
*/
public class Test2DArrayDefine {
public static void main(String[] args) {
// 方式1
int [][] arr1;
arr1 = new int[3][4];
// 方式2
// 数组的默认值 二维数组每个元素为一维数组 属于引用数据类型 其默认值为null
// 当我们创建一个长度为2的二维数组 表示在内存中 有两个一维数组 都指向为null
int [][] arr2 = new int[2][];
System.out.println(arr2[0]);
System.out.println(arr2[1]);
arr2[0] = new int[3];
arr2[0][0] = 11;
arr2[0][1] = 11;
arr2[0][2] = 11;
arr2[1] = new int[2];
arr2[1][0] = 34;
arr2[1][1] = 56;
// 方式3
int [][] arr3 = new int[][]{{1},{22,33,44},{55,55,666}};
// 方式4
int [][] arr4 = {{1},{22,33,44},{55,55,666}};
}
}
二维数组实现:杨辉三角
package com.atguigu.test3;
/**
* @author WHD
* @description TODO
* @date 2023/8/4 15:47
* 杨辉三角
* 1
* 1 1
* 1 2 1
* 1 3 3 1
* 1 4 6 4 1
* 1 5 10 10 5 1
*/
public class TestYangHuiTriangle {
public static void main(String[] args) {
int [][] yh = new int[6][6];
for(int i = 0;i < yh.length;i++){
yh[i][0] = 1; // 每一行的第一列为1
yh[i][i] = 1;
if(i > 1){ // 表示从第3行开始 赋值有具体的计算
for(int j = 1;j < i;j++){ // 因为每一行的第一列 和 最后一列都有值了 为0 所以 我们只需要赋值中间的元素
yh[i][j] = yh[i -1][j] + yh[i -1][j -1];
}
}
}
for(int i = 0;i < yh.length;i++){
for(int j = 6;j >= i;j--){
System.out.print(" ");
}
for(int j = 0;j <= i;j++){
System.out.print(yh[i][j] + " ");
}
System.out.println();
}
}
}
九.面相对象
一、类与对象基础
1.1 类定义规范
//文档1基础类
public class Person {
//成员变量(未封装)
String name;
char sex;
int age;
//成员方法
public void eat(){...}
}
//文档4改进类
public class Person {
//成员变量(封装改进)
String name;
char sex;
int age;
//使用this的成员方法
public void eat(){
System.out.println(this.name + " eating");
}
}
核心知识点:
- 类包含成员变量和成员方法
- 默认访问修饰符为包级私有(未显式声明时)
- this关键字用于明确访问当前对象成员
- 成员变量未初始化时系统默认赋值(String->null, int->0等)
1.2 对象创建与使用
//文档2测试类
Person p = new Person();
p.name = "张三";
p.eat();
//文档3多对象测试
Person p1 = new Person();
Person p2 = new Person();
p1.age = 23;
p2.age = 18;
对象特性:
- new关键字触发堆内存分配
- 每个对象独立存储成员变量副本
- 对象引用存储的是堆内存地址
- 垃圾回收机制自动管理内存
二、封装与访问控制
2.1 private封装
//文档5私有化示例
public class A {
private String str = "";
private void method01(){...}
public void method02(){
method01(); //内部调用私有方法
}
}
//文档6测试类
A a = new A();
a.method02(); //通过公有方法间接访问私有方法
封装原则:
- 私有化字段(private修饰)
- 提供公有访问方法(getter/setter)
- 方法内部可添加业务逻辑控制
2.2 标准封装实现
//文档7完整封装类
public class User {
private String username;
private double money;
//带验证的setter
public void setMoney(double money) {
System.out.println(LocalDateTime.now() + " 修改金额");
this.money = money;
}
//只读属性
public double getMoney() { return money; }
}
封装优势:
- 数据访问可控
- 支持数据验证
- 方便添加日志/通知等横切关注点
- 保持接口稳定性
三、构造方法与对象初始化
3.1 构造方法类型
//文档10构造方法示例
public class Person {
//无参构造
public Person() {
System.out.println("默认构造");
}
//全参构造
public Person(String name, char sex, int age) {
System.out.println("全参构造");
this.name = name;
this.sex = sex;
this.age = age;
}
}
构造方法特点:
- 方法名必须与类名相同
- 无返回类型声明
- 支持方法重载
- 默认提供无参构造(当无自定义构造时)
- this()可调用同类其他构造方法
3.2 对象初始化顺序
//文档9构造方法测试
Person p = new Person("李四", 'M', 25);
// 输出顺序:
// 1. 父类构造(如果有)
// 2. 成员变量初始化
// 3. 构造方法体执行
初始化顺序:
- 静态初始化块(类加载时)
- 实例初始化块(每次new时)
- 构造方法执行
- 父类构造优先于子类
四、高级特性补充
4.1 静态成员
public class Person {
//静态计数器
private static int count = 0;
public Person() {
count++;
}
public static int getCount() {
return count;
}
}
静态特性:
- 类级别共享数据
- 通过类名直接访问
- 静态方法不能访问非静态成员
- 静态初始化块用于复杂初始化
4.2 继承与多态
//补充继承示例
public class Student extends Person {
private String school;
@Override
public void eat() {
System.out.println("学生在食堂吃饭");
}
}
继承要点:
- extends实现继承
- super调用父类成员
- 方法重写规则(@Override)
- 里氏替换原则
4.3 接口与实现
//补充接口示例
public interface Walkable {
void walk(int speed);
}
public class Athlete extends Person implements Walkable {
@Override
public void walk(int speed) {
System.out.println("运动员竞走速度:" + speed);
}
}
接口特性:
- 完全抽象(JDK8前)
- 多实现机制
- 默认方法(default)
- 静态接口方法
五、面向对象设计原则
5.1 SOLID原则
- 单一职责:User类不应包含支付逻辑
- 开闭原则:通过继承扩展功能而非修改类
- 里氏替换:子类可完全替代父类
- 接口隔离:多个专用接口优于单一总接口
- 依赖倒置:高层模块不应依赖低层细节
5.2 设计模式示例
//简单工厂模式
public class PersonFactory {
public static Person createPerson(String type) {
switch(type) {
case "student": return new Student();
case "teacher": return new Teacher();
default: return new Person();
}
}
}
六、异常处理补充
//封装中的异常处理
public void setAge(int age) throws IllegalArgumentException {
if(age < 0 || age > 150) {
throw new IllegalArgumentException("无效年龄");
}
this.age = age;
}
最佳实践:
- 在setter中进行参数校验
- 使用自定义异常类型
- 保持对象状态有效性
十、this
关键字相关
一. this
的三大核心作用
public class Person {
public Person(String name, char sex, int age) {
this.name = name; // 1. 区分成员变量与局部变量
this.sex = sex;
this.age = age;
}
public void study() {
this.eat(); // 2. 调用同类其他方法
this.writerCode();
}
}
public class User {
public User() {
this("default", "000000"); // 3. 调用其他构造方法(必须首行)
}
}
核心知识点
- 成员变量遮蔽:当形参与成员变量同名时,用
this.变量名
明确指向对象属性 - 方法间调用:在实例方法中,
this
可调用本类其他方法(非必须但增强可读性) - 构造方法链:通过
this(参数)
重用构造逻辑,需在构造方法首行使用
二、static
关键字相关
1. 静态变量 vs 实例变量
描述 | 局部变量 | 实例变量 |
---|---|---|
定义位置 | 方法或方法内的结构当中 | 类的内部,方法的外部 |
默认值 | 无默认值 | 有默认值(与数组相同) |
使用范围 | 从定义行到包含其结构结束 | 本类有效 |
命名冲突 | 不允许重名 | 可与局部变量重名,局部变量优先 |
存储位置 | 基本数据类型存在栈中,引用数据类型名字在栈,值在堆 | 因为实例属性保存在对象中,而对象存在堆中,全部存储在堆中 |
生命周期 | 随着方法的入栈而生效,随着方法的出栈而死亡 | 随着对象的创建而存在 随着对象被垃圾回收(GC)而死亡 |
public class A {
String attr01; // 实例变量(对象级)
static String attr02; // 静态变量(类级)
}
核心知识点
- 存储位置:静态变量在方法区,实例变量在堆内存
- 生命周期:静态变量随类加载存在,实例变量随对象创建/销毁
- 访问方式:
A.attr02 = "value"; // 推荐类名访问 new A().attr01 = "val"; // 必须通过对象
2. 类加载与初始化顺序
// 文档6:static A a = new A() 先执行
static A a = new A(); // 触发构造方法执行
static int value1;
static int value2 = 0;
// 构造方法中对静态变量自增
public A() {
value1++; // 结果:value1=1
value2++; // 先变成1,后被显式初始化为0
}
执行顺序解析
- 准备阶段:静态变量默认初始化(
a=null
,value1=0
,value2=0
) - 初始化阶段:
- 按代码顺序执行:
new A()
→ 构造方法使value1=1
,value2=1
value2=0
覆盖为0
- 按代码顺序执行:
- 最终结果:
value1=1
,value2=0
三、代码块执行顺序
1. 代码块类型与特性
public class A {
static { // 静态代码块(类加载时执行一次)
attr02 = "CCC";
}
{ // 实例代码块(每次new对象时执行,先于构造方法)
attr01 = "bbb";
}
public A() { // 构造方法最后执行
attr01 = "aaa";
}
}
执行顺序验证
new A(); // 输出顺序:
// 1. 静态代码块输出 → "CCC"
// 2. 实例代码块输出 → "bbb -- BBB"
// 3. 构造方法输出 → "aaa -- AAA"
四、工具类设计模式
1. 工具类最佳实践
public class MyArrays {
private MyArrays() {} // 禁止实例化
public static int[] copyOf(int[] arr, int newLength) {
// 实现数组复制逻辑
}
public static String toString(int[] arr) {
// 实现数组转字符串
}
}
设计要点
- 私有构造方法:防止通过
new
创建实例 - 全静态方法:通过类名直接调用(如
MyArrays.copyOf()
) - 功能完整性:
- 处理不同长度需求(扩容/截断)
- 格式规范化输出(模仿
Arrays.toString()
)
五、补充关键知识点
1. 类加载机制
- 加载 → 验证 → 准备 → 解析 → 初始化
- 静态变量准备阶段赋默认值,初始化阶段赋真实值
2. 单例模式与静态变量
class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
}
3. 方法设计建议
- 参数校验:工具方法应添加
if (arr == null) throw...
- 异常处理:考虑添加
IllegalArgumentException
六、易错点总结
场景 | 常见错误 | 正确做法 |
---|---|---|
构造方法链 | this() 不在首行 | 必须作为构造方法第一条语句 |
静态变量访问 | 通过对象访问静态变量 | 使用类名访问 |
代码块顺序 | 混淆静态/实例代码块执行顺序 | 记住静态→父类→实例→构造方法 |
十一、继承基础
1.1 继承结构
// 文档1:Person基类
public class Person {
String name;
char sex;
int age;
public Person() {
System.out.println("Person构造");
}
public void eat(){}
public void sleep(){}
}
// 文档3:Chinese子类
public class Chinese extends Person{
String id;
public Chinese() {
System.out.println("Chinese构造");
}
public void playTaiJi(){}
}
1.2 核心知识点
- 继承机制:子类继承父类非私有成员(字段+方法)
- 构造方法执行顺序:
- 父类构造方法先于子类执行
- 默认调用
super()
(隐式调用父类无参构造)
- 继承链:
Person构造 -> Chinese构造
二、构造方法链
2.1 构造方法示例
// 文档10:带参数的构造方法
public Chinese(String name, char sex, int age, String id) {
super(name, sex, age); // 显式调用父类构造
this.id = id;
}
2.2 关键要点
- super()规则:
- 必须作为子类构造方法的第一条语句
- 父类若无无参构造,必须显式调用其他构造
- 构造方法执行顺序:
- 父类字段初始化 -> 父类构造方法体 -> 子类字段初始化 -> 子类构造方法体
三、访问权限控制
3.1 权限修饰符对比
修饰符 | 同类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
private | ✔️ | ✖️ | ✖️ | ✖️ |
default | ✔️ | ✔️ | ✖️ | ✖️ |
protected | ✔️ | ✔️ | ✔️ | ✖️ |
public | ✔️ | ✔️ | ✔️ | ✔️ |
3.2 典型示例
// 文档6:A类
public class A {
private String attr = "A属性";
protected void method02(){}
}
// 文档7:B类(子类)
public class B extends A {
public void test() {
// super.attr; // 错误!不能访问父类私有成员
super.method02(); // 允许访问protected方法
}
}
四、方法重写(Override)
4.1 重写规则
- 方法签名相同
- 访问权限不能更严格
- 返回类型相同
- 异常不能扩大
4.2 注解使用
// 文档15:eat方法重写
@Override
public void eat() {
System.out.println(super.getName() + "吃中餐");
}
4.3 特殊方法重写
- equals():
// 文档24:User类重写 @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj instanceof User){ return this.username.equals(((User)obj).username); } return false; }
- toString():
@Override public String toString() { return username + "--" + password; }
五、Object类核心方法
5.1 重要方法解析
方法 | 说明 |
---|---|
equals() | 默认比较对象地址,需重写实现内容比较 |
hashCode() | 返回对象哈希码,重写equals时必须同时重写 |
toString() | 返回对象字符串表示,默认格式:类名@十六进制哈希码 |
getClass() | 返回运行时类对象 |
clone() | 创建并返回对象副本(需实现Cloneable接口) |
5.2 典型应用
// 文档27:自定义字符串比较
public class MyString {
@Override
public boolean equals(Object obj) {
// 内容比较逻辑
}
}
六、封装与多态
6.1 封装实现
// 文档8:Person类封装
public class Person {
private String name;
// 通过getter/setter访问
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
6.2 多态应用
// 文档12:方法重写实现多态
public class Japanese extends Person {
@Override
public void eat() {
System.out.println("吃日料");
}
}
七、特殊语法特性
7.1 super关键字
- 访问父类成员:
super.getName();
- 调用父类构造:
super(name, sex, age);
7.2 final关键字
- 修饰类:不可继承
- 修饰方法:不可重写
- 修饰变量:常量
八、字符串处理
8.1 String特殊性
- 不可变性:任何修改都会创建新对象
- 字符串池:字面量共享机制
- 比较方法:
String s1 = new String("abc"); String s2 = "abc"; System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // true
九、综合案例解析
9.1 用户系统实现
// 文档24-25:用户类及测试
public class User {
// 字段封装
// equals/toStirng重写
}
public class Test01 {
public static void main(String[] args) {
User u1 = new User("user1", "123", "张三", 5000);
User u2 = new User("user1", "456", "李四", 3000);
System.out.println(u1.equals(u2)); // true(用户名相同)
}
}