第一部分:数据存储原理与进制
1. 计算机数据存储的最小单元
Q: 计算机底层存储数据的最小单元是什么?1字节等于多少位?
A:
- 最小单元:字节(Byte)
- 1字节(B)= 8个二进制位(bit)
- 数据在计算机底层都是采用二进制(0和1)存储,逢2进1
// 验证字节与位的关系
// 1字节 = 8位,能表示的范围:
// 无符号:0 ~ 255 (2^8 - 1)
// 有符号:-128 ~ 127 (-2^7 ~ 2^7 - 1)
byte b = 127; // ✓ 最大值
// byte b2 = 128; // ✗ 编译报错,超出byte范围
2. 字符数据的存储原理
Q: 字符数据(如'A')在计算机中是如何存储的?
A: 字符存储的是ASCII码表中对应的数字的二进制形式。
常见ASCII码:
| 字符 | ASCII码(十进制) |
|---|---|
'0' |
48 |
'A' |
65 |
'a' |
97 |
// 字符本质是数字
char c1 = 'A'; // 存储的是65的二进制
char c2 = 65; // 直接用数字赋值,与上面等价
System.out.println(c1); // 输出:A
System.out.println(c2); // 输出:A
System.out.println(c1 == c2); // 输出:true
// 字符参与运算时自动转为ASCII码
int result = 'A' + 10; // 65 + 10 = 75
System.out.println(result); // 输出:75
// 常用ASCII码记忆
System.out.println('0' + 0); // 48 (字符'0'的ASCII)
System.out.println('A' + 0); // 65
System.out.println('a' + 0); // 97
3. 不同进制的写法与转换
Q: Java中如何书写二进制、八进制、十进制、十六进制?
A:
| 进制 | 前缀 | 示例 | 十进制值 |
|---|---|---|---|
| 二进制 | 0b或 0B |
0b111 |
7 |
| 八进制 | 0 |
07 |
7 |
| 十进制 | 无(默认) | 7 |
7 |
| 十六进制 | 0x或 0X |
0xABC |
2748 |
public class RadixDemo {
public static void main(String[] args) {
// 二进制:0b/0B开头
int a = 0b111; // 1*4 + 1*2 + 1*1 = 7
System.out.println(a); // 7
// 八进制:0开头
int b = 07; // 7
System.out.println(b); // 7
// 十进制:默认
int c = 7;
System.out.println(c); // 7
// 十六进制:0x/0X开头(0-9, A-F)
int d = 0x7; // 7
int e = 0xABC; // 10*256 + 11*16 + 12 = 2748
System.out.println(d); // 7
System.out.println(e); // 2748
}
}
4. 进制转换方法(面试重点)
Q: 二进制如何转换为十进制、八进制、十六进制?十进制如何转二进制?
A:
二进制 → 十进制:每位乘以2的对应次幂求和
0b101 = 1×2² + 0×2¹ + 1×2⁰ = 4 + 0 + 1 = 5
二进制 → 八进制:每3位一组(0-7)
0b01100001 → 001 100 001 → 1 4 1 → 0141(八进制)
二进制 → 十六进制:每4位一组(0-15,用0-9,A-F表示)
0b01100001 → 0110 0001 → 6 1 → 0x61
0b11111010 → 1111 1010 → 15 10 → 0xFA
十进制 → 二进制:除2取余法(倒序排列余数)
// 验证进制转换
int num = 0b01100001; // 二进制97
System.out.println(num); // 97(十进制)
System.out.println(0141); // 97(八进制141)
System.out.println(0x61); // 97(十六进制61)
// 面试题:快速转换
// 0b1111 = 15 = 0xF
// 0b11110000 = 240 = 0xF0
第二部分:8种基本数据类型
5. 8种基本数据类型详解
Q: Java的8种基本数据类型是什么?各自的占用字节和范围是多少?
A:
| 类型 | 关键字 | 字节 | 范围 | 默认值 |
|---|---|---|---|---|
| 字节型 | byte |
1 | -128 ~ 127 | 0 |
| 短整型 | short |
2 | -32768 ~ 32767 | 0 |
| 整型 | int |
4 | -21亿 ~ 21亿(-2³¹ ~ 2³¹-1) | 0 |
| 长整型 | long |
8 | -922京 ~ 922京(19位) | 0L |
| 单精度浮点 | float |
4 | 精度8位 | 0.0F |
| 双精度浮点 | double |
8 | 精度17位 | 0.0 |
| 字符型 | char |
2 | 0 ~ 65535 | '\u0000' |
| 布尔型 | boolean |
1 | true/false |
false |
public class DataTypeDemo {
public static void main(String[] args) {
// 1. byte:1字节,-128~127
byte b1 = 120;
byte b2 = -128;
// byte b3 = 128; // 编译报错!超出范围
// 2. short:2字节,-32768~32767
short s1 = -32768;
// short s2 = 32768; // 编译报错!
// 3. int:4字节,默认整数类型(最常用)
int i1 = 100;
// int i2 = 2147483648; // 编译报错!超出21亿
// 4. long:8字节,结尾必须加L/l(推荐大写L)
long l1 = 100; // 整型自动转长整型,可不加L
long l2 = 9223372036854775807L; // 超过int范围必须加L
// long l3 = 9223372036854775808L; // 编译报错!超出long范围
// 5. float:4字节,结尾必须加F/f,精度8位(不精确!)
float f1 = 1.4F;
float f2 = 1.12345678988999988F;
System.out.println(f2); // 1.1234568(小数点后舍去)
float f3 = 11111111.12345678988999988F;
System.out.println(f3); // 1.1111111E7(科学计数法)
// 6. double:8字节,默认小数类型,可加D/d
double d1 = 3.14;
double d2 = 3.14D;
double d3 = 1.123456789123456789D;
System.out.println(d3); // 1.1234567891234568(四舍五入)
// 7. char:2字节,0-65535
char c1 = 'a';
char c2 = 97; // 可以用数字赋值
System.out.println(c1 == c2); // true
// 8. boolean:1字节,只有true/false
boolean flag = true;
// boolean flag2 = 1; // 编译报错!不能用0/1表示
}
}
6. 浮点型的精度问题(面试重点)
Q: 为什么金融计算不能用float/double?有什么解决方案?
A: float和 double采用二进制浮点数表示,无法精确表示某些十进制小数(如0.1),会导致精度丢失。
// 精度问题演示
public class FloatPrecision {
public static void main(String[] args) {
// 经典面试题:0.1 + 0.2 != 0.3
System.out.println(0.1 + 0.2); // 0.30000000000000004
// 金融计算错误示例
double money = 0.0;
for (int i = 0; i < 10; i++) {
money += 0.1;
}
System.out.println(money); // 0.9999999999999999,不是1.0!
// 解决方案:使用BigDecimal(后续学习)
// 或者转换为整数计算(分代替元)
int fen = 0;
for (int i = 0; i < 10; i++) {
fen += 10; // 0.1元 = 10分
}
System.out.println(fen / 100.0); // 1.0
}
}
第三部分:关键字与标识符
7. 关键字与标识符的命名规则
Q: 什么是关键字?标识符的命名规则和规范是什么?
A:
关键字:Java中有特殊含义的单词(50+个),如 int、class、if等,全部小写,IDE中通常显示为蓝色。
标识符命名规则(必须遵守):
- 由数字、字母、下划线
_、美元符$组成 - 不能以数字开头
- 不能是关键字
- 区分大小写
命名规范(建议遵守):
| 类型 | 规范 | 示例 |
|---|---|---|
| 类名 | 大驼峰(每个单词首字母大写) | HelloWorld、StudentInfo |
| 变量/方法名 | 小驼峰(首字母小写,后续单词首字母大写) | studentName、getAge() |
| 常量 | 全大写,下划线分隔 | MAX_VALUE、PI |
public class IdentifierDemo { // 类名:大驼峰
public static void main(String[] args) {
// 合法标识符
int age = 20;
String studentName = "张三"; // 小驼峰
double _price = 99.9;
int $count = 100;
// 非法标识符(编译报错)
// int 123abc = 100; // 不能以数字开头
// int abc# = 100; // 包含非法字符#
// int class = 100; // 不能使用关键字
// int char = 'A'; // 不能使用关键字
// 注意:String不是关键字,是类名
double String = 200; // 合法但不推荐!
// 见名知意
int a = 18; // 不推荐,不知道含义
int studentAge = 18; // 推荐,一目了然
}
}
第四部分:方法
8. 方法的定义与调用
Q: 方法的完整定义格式是什么?形参和实参的区别?
A: 方法是封装了特定功能的代码块,可实现代码复用。
定义格式:
修饰符 返回值类型 方法名(参数类型 参数名, ...) {
方法体;
return 返回值; // void方法可省略
}
形参 vs 实参:
| 概念 | 说明 | 位置 |
|---|---|---|
| 形参 | 形式参数,方法定义时的参数 | 方法定义中 |
| 实参 | 实际参数,调用时传入的具体值 | 方法调用中 |
public class MethodDemo {
public static void main(String[] args) {
// 调用无参无返回值方法
printHello();
// 调用有参无返回值方法:50和100是实参
sum(50, 100);
// 调用有参有返回值方法
int result = sumWithReturn(100, 200);
System.out.println("结果:" + result);
// 返回值可继续处理
int finalResult = result + 50;
System.out.println("最终结果:" + finalResult);
}
// 1. 无参无返回值:固定功能,不需要输入和输出
public static void printHello() {
System.out.println("Hello World");
}
// 2. 有参无返回值:需要输入,不需要输出
// a和b是形参
public static void sum(int a, int b) {
int result = a + b;
System.out.println("两数之和:" + result);
}
// 3. 有参有返回值:既需要输入,也需要输出
public static int sumWithReturn(int a, int b) {
return a + b; // 返回结果给调用者
}
}
9. 方法重载(Overload)
Q: 什么是方法重载?重载的条件是什么?与返回值和修饰符有关吗?
A: 方法重载:同一个类中,多个方法名称相同,但形参列表不同。
重载条件(满足其一即可):
- 参数个数不同
- 参数类型不同
- 参数顺序不同
与返回值、修饰符无关!
public class MethodOverload {
public static void main(String[] args) {
// 根据参数自动匹配对应的方法
sum(); // 调用无参版本
sum(10, 20); // 调用int,int版本
sum(10, 20, 30); // 调用三个参数版本
sum(3.14, 10); // 调用double,int版本
}
// 版本1:无参
public static void sum() {
System.out.println("无参版本");
}
// 版本2:两个int参数
public static void sum(int a, int b) {
System.out.println("int+int版本:" + (a + b));
}
// 版本3:三个int参数(个数不同)✓重载
public static void sum(int a, int b, int c) {
System.out.println("三数之和:" + (a + b + c));
}
// 版本4:double和int(类型不同)✓重载
public static void sum(double a, int b) {
System.out.println("double+int版本:" + (a + b));
}
// 以下不是重载(编译报错)
// 与返回值无关!
// public static int sum(int a, int b) { return a + b; }
// 与参数名无关!
// public static void sum(int b, int a) { }
// 与修饰符无关!
// private static void sum(int a, int b) { }
}
第五部分:类型转换
10. 自动类型转换
Q: 什么是自动类型转换?转换的方向是什么?
A: 自动类型转换(隐式转换):小范围类型自动转为大范围类型,无需显式处理。
转换方向(从小到大):
byte → short/char → int → long → float → double
public class AutoConversion {
public static void main(String[] args) {
// byte → int(自动)
byte b = 100;
int i = b;
System.out.println(i); // 100
// short → int(自动)
short s = 200;
int i2 = s;
// int → long(自动)
int num = 100;
long l = num;
// long → float(自动,虽然long8字节,float4字节,但float范围更大)
long bigNum = 100L;
float f = bigNum;
System.out.println(f); // 100.0
// 注意:大范围不能自动转小范围
// long l2 = f; // 编译报错!float不能自动转long
}
}
11. 表达式自动类型提升
Q: 表达式中类型如何提升?byte/short/char运算有什么特殊规则?
A: 表达式最终结果类型由最高类型决定。
特殊规则:byte、short、char参与运算时,直接提升为int!
public class ExpressionPromotion {
public static void main(String[] args) {
// 规则1:表达式结果类型为最高类型
byte b = 100;
int i = 20;
long l = 1000L;
// byte + int → int
// int result1 = b + i; // 正确
// byte result2 = b + i; // 编译报错!结果是int
// char + int + long → long
char c = 'a'; // 97
long result = c + i + l; // 97 + 20 + 1000 = 1117
System.out.println(result); // 1117
// 规则2:byte/short/char运算直接提升为int(面试重点!)
byte b1 = 100;
byte b2 = 127;
// byte b3 = b1 + b2; // 编译报错!byte+byte结果是int
int sum = b1 + b2; // 必须int接收
System.out.println(sum); // 227
// short同理
short s1 = 100;
short s2 = 200;
// short s3 = s1 + s2; // 编译报错!
int s3 = s1 + s2;
// char同理
char c1 = 'A';
char c2 = 'B';
// char c3 = c1 + c2; // 编译报错!
int c3 = c1 + c2; // 65 + 66 = 131
}
}
12. 强制类型转换
Q: 什么是强制类型转换?有什么风险?语法格式是什么?
A: 强制类型转换(显式转换):大范围类型转为小范围类型,需要显式处理。
风险:
- 数据溢出:超出目标类型范围
- 精度丢失:浮点型转整型,小数部分直接丢弃(非四舍五入)
语法:目标类型 变量 = (目标类型)原变量;
public class ForceConversion {
public static void main(String[] args) {
// 1. 基本强制转换
int i = 20;
byte b = (byte) i; // 20在byte范围内,正常转换
System.out.println(b); // 20
// 2. 数据溢出(风险1)
int i2 = 128;
byte b2 = (byte) i2; // 128超出byte范围(-128~127)
System.out.println(b2); // -128(溢出,数据错误!)
// 溢出原理:128的二进制 10000000,byte只有8位,最高位是符号位
// 变成负数,取反+1得到 -128
// 3. 精度丢失(风险2)
double d = 3.99;
int num = (int) d; // 直接丢弃小数部分
System.out.println(num); // 3(不是4!)
// 4. 实际应用场景
int totalSeconds = 125;
int minutes = totalSeconds / 60; // 2
int seconds = totalSeconds % 60; // 5
// 或者强制转换(效果相同)
double avg = 85.6;
int score = (int) avg; // 只保留整数部分85
// IDEA快捷键:Alt+Enter在报错处自动提示强制转换
}
}
第六部分:运算符
13. 算术运算符与连接符
Q: Java中的算术运算符有哪些?+作为连接符的规则是什么?
A:
| 运算符 | 作用 | 示例 |
|---|---|---|
+ |
加法/连接符 | 5 + 3 = 8, "a" + 5 = "a5" |
- |
减法 | 5 - 3 = 2 |
* |
乘法 | 5 * 3 = 15 |
/ |
除法(整数相除得整数) | 10 / 3 = 3, 10.0 / 3 = 3.33 |
% |
取模(求余数) | 10 % 3 = 1 |
+连接符规则:只要有一侧是字符串,就进行字符串拼接。
public class ArithmeticOperator {
public static void main(String[] args) {
// 基本算术运算
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.0 / 3); // 3.333...(浮点数除法)
System.out.println(10 % 3); // 1(余数)
// +作为连接符(重点!)
System.out.println("abc" + 5); // "abc5"
System.out.println(5 + "abc"); // "5abc"
System.out.println(5 + 3 + "abc"); // "8abc"(先算5+3)
System.out.println("abc" + 5 + 3); // "abc53"(先拼接"abc5")
System.out.println("abc" + (5 + 3)); // "abc8"(括号优先)
// 字符与数字相加:字符转ASCII码
System.out.println(5 + 'A'); // 70(5 + 65)
System.out.println("abc" + 5 + 'A'); // "abc5A"(字符串后都变连接)
// 实用技巧:快速打印变量
int x = 20;
System.out.println("x = " + x); // "x = 20"
// IDEA快捷键:x.soutv 自动生成
}
}
14. 自增自减运算符(面试重点)
Q: ++i和 i++的区别是什么?单独使用和混合使用有什么不同?
A:
| 形式 | 名称 | 规则 |
|---|---|---|
++i |
前缀自增 | 先自增,后使用 |
i++ |
后缀自增 | 先使用,后自增 |
单独使用:++i和 i++效果相同,都是i增加1。
混合使用(赋值、运算等):前缀和后缀结果不同!
public class IncrementDemo {
public static void main(String[] args) {
// 1. 单独使用(无区别)
int a = 10;
a++;
System.out.println(a); // 11
int b = 10;
++b;
System.out.println(b); // 11
// 2. 混合使用(有区别!)
// 前缀:先自增,再赋值
int i = 10;
int ret1 = ++i; // i先变成11,再赋值给ret1
System.out.println("i = " + i); // 11
System.out.println("ret1 = " + ret1); // 11
// 后缀:先赋值,再自增
int j = 10;
int ret2 = j++; // j先赋值10给ret2,再变成11
System.out.println("j = " + j); // 11
System.out.println("ret2 = " + ret2); // 10
// 3. 复杂面试题
int m = 10;
int n = 5;
// 拆解:m++(10, m=11) + ++m(12) - --n(4, n=4) - ++n(5, n=5) + 1 + m--(12, m=11)
// = 10 + 12 - 4 - 5 + 1 + 12 = 26
int res = m++ + ++m - --n - ++n + 1 + m--;
System.out.println("res = " + res); // 26
System.out.println("m = " + m); // 11
System.out.println("n = " + n); // 5
// 4. 注意:只能用于变量,不能用于字面量
// System.out.println(10++); // 编译报错!
}
}
15. 赋值运算符(含扩展赋值运算符)
Q: 扩展赋值运算符 +=、-=等有什么特点?与直接写 a = a + b有什么区别?
A: 扩展赋值运算符:自带强制类型转换!
| 运算符 | 等价写法 | 特点 |
|---|---|---|
a += b |
a = (a的类型)(a + b) |
自动强转 |
a -= b |
a = (a的类型)(a - b) |
自动强转 |
a *= b |
a = (a的类型)(a * b) |
自动强转 |
public class AssignmentOperator {
public static void main(String[] args) {
// 基本赋值
int a = 10;
// 扩展赋值
a += 5; // 等价于 a = a + 5; 结果15
a -= 3; // 等价于 a = a - 3; 结果12
a *= 2; // 等价于 a = a * 2; 结果24
a /= 4; // 等价于 a = a / 4; 结果6
a %= 4; // 等价于 a = a % 4; 结果2
// 面试重点:扩展赋值运算符自带强制类型转换
byte b = 100;
byte c = 30;
// b = b + c; // 编译报错!byte+byte=int,不能赋给byte
b += c; // ✓ 不报错!等价于 b = (byte)(b + c)
System.out.println(b); // 130超出byte范围,溢出为-126
// 实际应用:红包计算
double money = 1000.0;
money += 520; // 收红包
money -= 1314; // 发红包
System.out.println("余额:" + money); // 206.0
}
}
16. 关系运算符
Q: 关系运算符有哪些?==和 =的区别?
A:
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
== |
等于 | 5 == 5 |
true |
!= |
不等于 | 5 != 3 |
true |
> |
大于 | 5 > 3 |
true |
< |
小于 | 5 < 3 |
false |
>= |
大于等于 | 5 >= 5 |
true |
<= |
小于等于 | 5 <= 3 |
false |
== vs =:
==:比较运算符,判断两边是否相等=:赋值运算符,将右边值赋给左边
public class RelationalOperator {
public static void main(String[] args) {
// 基本比较
System.out.println(5 > 3); // true
System.out.println(5 < 3); // false
System.out.println(5 >= 5); // true
System.out.println(5 <= 4); // false
// == 与 = 的区别(常见错误!)
int a = 5;
int b = 10;
System.out.println(a == b); // false(比较)
System.out.println(a = b); // 10(赋值,将b的值赋给a)
// 注意:赋值表达式有返回值(被赋的值)
// if (a = 5) { } // 在C/C++中合法,Java中编译报错!
// != 不等于
System.out.println(5 != 3); // true
System.out.println(5 != 5); // false
}
}
17. 逻辑运算符(重点)
Q: 逻辑运算符有哪些?&&和 &的区别?||和 |的区别?
A:
| 运算符 | 名称 | 规则 | 短路特性 |
|---|---|---|---|
& |
逻辑与 | 两边都为 true,结果才为 true |
无(两边都执行) |
| ` | ` | 逻辑或 | 两边都为 false,结果才为 false |
! |
逻辑非 | 取反,!true = false |
- |
^ |
逻辑异或 | 两边不同为 true,相同为 false |
- |
&& |
短路与 | 同 &,但左边为 false右边不执行 |
有(推荐) |
| ` | ` | 短路或 |
public class LogicOperator {
public static void main(String[] args) {
// 1. 基本逻辑运算
System.out.println(true & true); // true
System.out.println(true & false); // false
System.out.println(true | false); // true
System.out.println(!true); // false
System.out.println(true ^ false); // true(不同为true)
System.out.println(true ^ true); // false(相同为false)
// 2. 短路特性(面试重点!)
int i = 10;
int j = 20;
// & 非短路:两边都执行
System.out.println(i > 100 & ++j > 1); // false
System.out.println("j = " + j); // 21(++j执行了)
// && 短路:左边为false,右边不执行
int m = 10;
int n = 20;
System.out.println(m > 100 && ++n > 1); // false
System.out.println("n = " + n); // 20(++n没执行!)
// | 非短路:两边都执行
int a = 10;
int b = 20;
System.out.println(a < 100 | ++b > 1); // true
System.out.println("b = " + b); // 21(++b执行了)
// || 短路:左边为true,右边不执行
int x = 10;
int y = 20;
System.out.println(x < 100 || ++y > 1); // true
System.out.println("y = " + y); // 20(++y没执行!)
// 3. 实际应用:避免空指针异常
String str = null;
// if (str != null & str.length() > 0) { } // 报错,右边执行了
if (str != null && str.length() > 0) { } // 安全,右边不执行
}
}
18. 三元运算符
Q: 三元运算符的语法格式是什么?适用场景是什么?
A: 语法:条件表达式 ? 值1 : 值2
执行流程:
- 条件为
true→ 返回值1 - 条件为
false→ 返回值2
适用场景:简单的二选一逻辑,可替代简单的if-else。
public class TernaryOperator {
public static void main(String[] args) {
// 基本用法
int score = 85;
String result = score >= 60 ? "及格" : "不及格";
System.out.println(result); // "及格"
// 嵌套使用(不推荐,可读性差)
String grade = score >= 90 ? "优秀" : (score >= 60 ? "及格" : "不及格");
// 实际应用:求两个数的最大值
int a = 10;
int b = 20;
int max = a > b ? a : b;
System.out.println("最大值:" + max); // 20
// 实际应用:求绝对值
int num = -5;
int abs = num >= 0 ? num : -num;
System.out.println("绝对值:" + abs); // 5
// 注意:返回值类型要兼容
// String s = score > 60 ? "及格" : 0; // 编译报错!类型不兼容
}
}
第七部分:键盘输入
19. Scanner键盘输入
Q: 使用Scanner接收键盘输入的步骤是什么?常用方法有哪些?
A: 使用步骤:
- 导包:
import java.util.Scanner;(IDEA自动导入) - 创建对象:
Scanner sc = new Scanner(System.in); - 调用方法:
sc.nextXxx()接收不同类型的输入
常用方法:
| 方法 | 接收类型 | 说明 |
|---|---|---|
sc.nextInt() |
int | 接收整数 |
sc.nextDouble() |
double | 接收小数 |
sc.next() |
String | 接收字符串(遇到空格停止) |
sc.nextLine() |
String | 接收一行(包含空格) |
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
// 1. 创建Scanner对象
Scanner sc = new Scanner(System.in);
// 2. 接收整数
System.out.print("请输入年龄:");
int age = sc.nextInt(); // 阻塞等待用户输入
// 3. 接收字符串(next()遇到空格停止)
System.out.print("请输入姓名:");
String name = sc.next();
// 4. 接收小数
System.out.print("请输入成绩:");
double score = sc.nextDouble();
// 5. 输出结果
System.out.println("姓名:" + name + ",年龄:" + age + ",成绩:" + score);
// 注意事项:nextInt()后接nextLine()的问题
System.out.print("请输入编号:");
int id = sc.nextInt();
sc.nextLine(); // 吸收换行符!
System.out.print("请输入地址:");
String address = sc.nextLine(); // 现在能正常接收了
// 6. 关闭资源(好习惯)
sc.close();
}
}