25.总结
25.1 核心要点速览
开发环境搭建
| 步骤 | 说明 |
|---|---|
| 安装JDK | 建议使用JDK 8(Android/Unity生态常见要求) |
| 配置环境变量 | JAVA_HOME、Path、CLASSPATH |
| 安装IDE | IDEA(JetBrains)或Eclipse |
注释
三种注释:单行 //、多行 /* ... */、文档 /** ... */(用于类/方法说明)。
变量类型
整型
| 类型 | 字节 | 位数 | 取值范围 |
|---|---|---|---|
| byte | 1 | 8 | -128 ~ 127 |
| short | 2 | 16 | -32768 ~ 32767 |
| int | 4 | 32 | -21亿多 ~ 21亿多 |
| long | 8 | 64 | -9百万兆 ~ 9百万兆 |
注意:Java没有无符号类型(Java 8新增部分无符号方法,使用较少)。
浮点型
| 类型 | 字节 | 位数 | 说明 |
|---|---|---|---|
| float | 4 | 32 | 需加f后缀,如 1.5f |
| double | 8 | 64 | 默认小数类型 |
其他类型
| 类型 | 字节 | 说明 |
|---|---|---|
| boolean | 4(单个)/ 1(数组) | 默认值false |
| char | 2 | 单字符,单引号'A' |
| String | 不定 | 引用类型,首字母大写 |
常量
关键字 final,声明时必须初始化,不可修改。
static final int MAX_VALUE = 100;
类型转换
| 方式 | 说明 | 示例 |
|---|---|---|
| 隐式转换 | 小范围→大范围,自动转换 | byte → short → int → long → float → double |
| 显式转换 | 括号强转,可能丢失精度 | int i = (int)2.5f; |
| 字符串转数值 | 包装类parse方法 | Integer.parseInt("101", 2); |
运算符
| 分类 | 运算符 | 说明 |
|---|---|---|
| 算术 | + - * / % |
整数相除只保留整数 |
| 自增减 | ++ -- |
i++先用后加,++i先加后用 |
| 比较 | > < >= <= == != |
结果为boolean |
| 逻辑 | && || ! |
&&和||有短路特性 |
| 位运算 | & | ^ ~ << >> >>> |
>>>为无符号右移 |
| 三目 | 条件 ? 值1 : 值2 |
简化if-else |
流程控制
条件分支
| 语句 | 特点 |
|---|---|
| if-else | 支持多分支判断 |
| switch | 支持整型、字符型、字符串、枚举;贯穿时可插入代码逻辑 |
循环
| 语句 | 特点 |
|---|---|
| while | 先判断后执行 |
| do-while | 先执行后判断,至少执行一次 |
| for | 固定次数循环 |
| foreach | for(元素类型 x : 遍历对象),用于遍历数组/集合 |
循环控制:break跳出循环,continue跳过本次。
数组
一维数组
int[] arr = new int[5];
int[] arr2 = {1, 2, 3, 4};
int arr3[] = new int[]{1, 2, 3};
二维数组与交错数组
Java中二维数组和交错数组写法相同,都是 [][]。
int[][] arr2D = new int[3][2];
int[][] jagged = new int[3][];
jagged[0] = new int[]{1, 2, 3};
Arrays类常用方法
| 方法 | 作用 |
|---|---|
| fill(数组, 值) | 填充数组 |
| fill(数组, 起, 止, 值) | 填充指定范围 |
| sort(数组) | 升序排序 |
| copyOf(数组, 长度) | 复制数组 |
| copyOfRange(数组, 起, 止) | 复制指定范围 |
| binarySearch(数组, 元素) | 二分查找(需先排序) |
函数
基本语法
修饰符 返回类型 函数名(参数类型 参数名, ...)
{
return 返回值;
}
与C#的区别
| 特性 | Java | C# |
|---|---|---|
| ref/out | 无 | 有 |
| 可变参数 | 类型... 参数名 |
params 类型[] |
| 默认参数 | 无 | 有 |
| 命名规范 | 驼峰命名法 | 帕斯卡命名法 |
面向对象-封装
类声明
class 类名
{
成员变量;
成员方法;
构造函数;
finalize(); // 类似析构函数
}
Java中没有的C#特性
- 成员属性(get/set)
- 析构函数(有finalize替代)
- 索引器
- 运算符重载
面向对象-继承
| 特性 | Java | C# |
|---|---|---|
| 继承关键字 | extends |
: |
| 父类调用 | super |
base |
| 类型判断 | instanceof |
is |
| 密封关键字 | final |
sealed |
Object类关键方法
getClass():获取类型,类似C#的getType()toString():转字符串equals():比较内容(==比较引用地址)
面向对象-多态
方法重写
Java方法默认可重写(除非声明为final),@Override注解可选但推荐使用。
@Override
public void Eat() {
super.Eat(); // 调用父类方法
}
接口
| 特性 | Java | C# |
|---|---|---|
| 继承接口关键字 | implements |
: |
| 接口字段 | 只能是static final常量 | 不允许 |
| 显式实现接口 | 不存在 | 有 |
包
| 概念 | 说明 |
|---|---|
| package | 类似C#命名空间,解决类名冲突 |
| import | 引入包,类似C#的using |
| import static | 静态导入,直接使用静态成员 |
包名规则:统一小写,用.分割,如 com.公司名.项目名.模块名。
内部类
普通内部类
外部类 outer = new 外部类();
外部类.内部类 inner = outer.new 内部类();
- 内部类可直接访问外部类所有成员(包括私有)
- 同名成员通过
this和外部类.this区分
匿名内部类
new 抽象类/接口() {
@Override
public void 方法() { }
};
字符串-String
声明方式
| 方式 | 说明 |
|---|---|
| 直接赋值 | 相同字符串指向同一引用 |
| new String() | 相同字符串也是不同引用 |
字符串比较
==:比较引用地址equals():比较内容equalsIgnoreCase():忽略大小写比较
常用方法
| 方法 | 作用 |
|---|---|
| length() | 长度 |
| indexOf()/lastIndexOf() | 正向/反向查找 |
| charAt(int i) | 获取指定位置字符 |
| substring(起, 止) | 截取子串 |
| trim() | 去首尾空格 |
| replace(旧, 新) | 替换 |
| split(分隔符) | 分割 |
| toUpperCase()/toLowerCase() | 大小写转换 |
字符串-StringBuilder
用于频繁修改字符串的场景,效率远高于String拼接。
| 方法 | 作用 |
|---|---|
| append(内容) | 追加 |
| insert(位置, 内容) | 插入 |
| delete(起, 止) | 删除 |
| toString() | 转String |
泛型
与C#的区别
| 特性 | Java | C# |
|---|---|---|
| 泛型方法声明 | public <T> void Method(T t) |
public void Method<T>(T t) |
| 泛型约束 | <T extends 类/接口> |
where T : 类/接口 |
| 类型通配符 | ? |
无 |
| 基础类型 | 必须用包装类 | 可直接使用 |
类型通配符
类名<?> obj = null;
obj = new 类名<子类>();
集合类-List
ArrayList与LinkedList
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 数组 | 链表 |
| 访问效率 | 高 | 低 |
| 插入删除效率 | 低 | 高 |
常用方法
| 方法 | 作用 |
|---|---|
| add(元素) | 添加 |
| remove(索引/元素) | 删除 |
| get(索引) | 获取 |
| set(索引, 元素) | 修改 |
| contains(元素) | 判断是否存在 |
| size() | 元素个数 |
| clear() | 清空 |
集合类-Set
HashSet与TreeSet
| 特性 | HashSet | TreeSet |
|---|---|---|
| 底层结构 | 哈希表 | 树 |
| 排序 | 无序 | 自动排序 |
| 效率 | 高 | 较低 |
共同特点:不允许重复元素,不能通过索引访问。
TreeSet独有方法
| 方法 | 作用 |
|---|---|
| first()/last() | 获取首/尾元素 |
| pollFirst()/pollLast() | 取出并移除首/尾 |
| headSet(元素) | 获取元素之前的集合 |
| tailSet(元素) | 获取元素之后的集合 |
集合类-Map
HashMap与TreeMap
| 特性 | HashMap | TreeMap |
|---|---|---|
| 底层结构 | 哈希表 | 树 |
| null键/值 | 允许 | 键不允许null |
| 排序 | 无序 | 自动排序 |
| 效率 | 高 | 较低 |
常用方法
| 方法 | 作用 |
|---|---|
| put(键, 值) | 添加键值对 |
| get(键) | 获取值 |
| remove(键) | 删除 |
| containsKey(键) | 判断键是否存在 |
| containsValue(值) | 判断值是否存在 |
| keySet() | 获取所有键 |
| values() | 获取所有值 |
异常处理
基本语法
try {
// 可能出错的代码
} catch (异常类型 ex) {
// 处理异常
} finally {
// 无论如何都执行
}
方法抛出异常
public void Test() throws 异常类型1, 异常类型2 {
// 方法体
}
自定义异常
继承Exception,通过throw抛出。
Lambda表达式
语法
(参数) -> { 代码块 }
参数 -> 结果表达式
函数式接口
仅包含一个抽象方法的接口,用于装载Lambda表达式。
interface ITest {
int Test();
}
ITest t = () -> { return 1 + 2; };
注意:Lambda内不能修改局部变量,可修改外部类成员变量。
方法引用
| 类型 | 语法 | 示例 |
|---|---|---|
| 静态方法 | 类名::静态方法名 |
类名::Test |
| 成员方法 | 对象名::成员方法名 |
obj::Test |
| 构造函数 | 类名::new |
类名::new |
| 数组构造 | 类型[]::new |
Integer[]::new |
Function接口
java.util.function.Function<T, R>:T为参数类型,R为返回值类型。
Function<Integer, String> f = (a) -> a.toString();
String result = f.apply(100);
常用类库
包装类
| 类 | 说明 |
|---|---|
| Integer/Long/Double | 数值包装类,提供parse、toString等方法 |
| Boolean | 布尔包装类 |
| Character | 字符包装类,提供大小写转换、判断等方法 |
大数据类
| 类 | 说明 |
|---|---|
| BigInteger | 任意大小整数 |
| BigDecimal | 高精度浮点数 |
Math类
常用方法:abs()、max()、min()、sqrt()、pow()、ceil()、floor()、round()。
Random类
Random r = new Random();
r.nextInt(); // 随机整数
r.nextInt(n); // 0~n-1随机整数
r.nextDouble(); // 随机浮点数
25.2 面试题精选
基础题
1. Java变量类型及选择
题目
请列举Java中的基本数据类型,并说明在实际开发中如何选择合适的类型。
深入解析
Java基本数据类型分为四大类:
整型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
浮点型:float(4字节)、double(8字节)
字符型:char(2字节)
布尔型:boolean
选择原则:
- 整数优先用int:范围足够大(-21亿~21亿),性能好
- 大整数用long:超过int范围时使用,如时间戳毫秒数
- 小范围用byte/short:内存敏感场景,如大量数据存储
- 小数优先用double:精度足够,默认小数类型
- 金钱计算用BigDecimal:避免精度丢失
int userId = 10001; // ID用int
long timestamp = System.currentTimeMillis(); // 时间戳用long
byte age = 25; // 年龄用byte节省内存
double price = 99.99; // 价格用double
BigDecimal money = new BigDecimal("10000.00"); // 金融计算用BigDecimal
注意:Java没有无符号类型,Java 8新增部分无符号方法但使用较少。
答题示例
Java基本类型分整型、浮点型、字符型、布尔型。
整型有 byte、short、int、long,浮点型有 float、double。
选择时:整数优先用 int,不够用 long;小数用 double;金钱计算用 BigDecimal 避免精度问题。
参考文章
- 3.变量相关
2. String比较方式
题目
Java中判断两个字符串内容是否相等应该用什么方式?为什么不能用==?
深入解析
Java中==和equals()的区别:
==运算符:
- 基本类型:比较值是否相等
- 引用类型:比较引用地址是否相同
equals()方法:
- 比较字符串内容是否相等
equalsIgnoreCase()忽略大小写比较
字符串创建方式的影响:
String s1 = "hello"; // 字符串常量池
String s2 = "hello"; // 指向同一引用
String s3 = new String("hello"); // 堆中新建对象
s1 == s2; // true,同一引用
s1 == s3; // false,不同引用
s1.equals(s3); // true,内容相同
直接赋值时,相同字符串指向同一引用(字符串常量池);new String()每次都创建新对象。
答题示例
==比较引用地址,equals()比较内容。直接赋值的相同字符串指向同一引用,
new String()每次都新建对象。所以判断字符串内容要用
equals(),忽略大小写用equalsIgnoreCase()。
参考文章
- 15.字符串-String
进阶题
1. ArrayList与LinkedList的区别
题目
请说明ArrayList和LinkedList的区别,以及在什么场景下选择哪个?
深入解析
两者都实现List接口,方法使用一致,但底层结构不同:
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 随机访问 | O(1),高效 | O(n),需遍历 |
| 头部插入删除 | O(n),需移动元素 | O(1),只需改指针 |
| 中间插入删除 | O(n) | O(n),需先遍历定位 |
| 内存占用 | 连续空间 | 每个节点额外存储前后指针 |
选择建议:
- ArrayList:查询多、修改少,如配置列表、数据展示
- LinkedList:频繁在头部或中间插入删除,如消息队列
// 查询场景用ArrayList
List<User> userList = new ArrayList<>();
userList.get(0); // O(1)
// 频繁头部插入用LinkedList
LinkedList<Message> queue = new LinkedList<>();
queue.addFirst(new Message()); // O(1)
答题示例
ArrayList 底层是数组,随机访问快 O(1),但插入删除要移动元素。
LinkedList 底层是链表,头部插入删除快 O(1),但随机访问要遍历。
查询多用 ArrayList,频繁头部插入删除用 LinkedList。
参考文章
- 18.集合类-ArrayList和LinkedList
2. HashMap与TreeMap的区别
题目
HashMap和TreeMap有什么区别?什么情况下使用TreeMap?
深入解析
两者都实现Map接口,以键值对存储数据:
| 特性 | HashMap | TreeMap |
|---|---|---|
| 底层结构 | 哈希表 | 红黑树 |
| 顺序 | 无序 | 按键排序 |
| null键 | 允许 | 不允许 |
| 查找效率 | O(1) | O(log n) |
| 插入效率 | O(1) | O(log n) |
使用场景:
- HashMap:不需要排序,追求性能,绝大多数场景
- TreeMap:需要按键排序,如排行榜、按时间范围查询
// 一般场景用HashMap
Map<Integer, User> userMap = new HashMap<>();
// 需要排序时用TreeMap
TreeMap<String, Integer> scores = new TreeMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
// 遍历时按键字典序排序输出
答题示例
HashMap 基于哈希表,无序,查找 O(1),允许 null 键。
TreeMap 基于红黑树,按键排序,查找 O(log n),不允许 null 键。
一般用 HashMap,需要排序时才用 TreeMap。
参考文章
- 20.集合类-HashMap和TreeMap
3. Java多态与C#的区别
题目
Java中的方法重写与C#有什么区别?
深入解析
Java多态特点:
- 方法默认可被重写(除非声明为
final) @Override注解可选,但推荐使用(编译器检查)- 使用
super调用父类方法
C#多态特点:
- 必须用
virtual标记可重写方法 - 子类必须用
override显式重写 - 使用
base调用父类方法
// Java
class Father {
public void eat() { } // 默认可重写
}
class Son extends Father {
@Override
public void eat() {
super.eat(); // 调用父类
}
}
// C#
class Father {
public virtual void Eat() { } // 必须标记virtual
}
class Son : Father {
public override void Eat() { // 必须用override
base.Eat();
}
}
关键区别:Java方法默认虚方法,C#需显式声明。
答题示例
Java 方法默认可重写,除非声明
final;@Override注解可选但推荐。C# 必须用
virtual标记可重写方法,子类用override显式重写。Java 用
super调用父类,C# 用base。
参考文章
- 12.面向对象-多态
深度题
1. HashSet与TreeSet的底层实现及选择
题目
HashSet和TreeSet的底层实现原理是什么?如何选择?
深入解析
HashSet底层:
- 基于HashMap实现,元素作为key存储
- value是一个固定的Object对象
- 通过hashCode()和equals()保证唯一性
- 查找、插入、删除都是O(1)
TreeSet底层:
- 基于TreeMap实现,元素作为key存储
- 使用红黑树存储,自动排序
- 元素必须实现Comparable接口或传入Comparator
- 查找、插入、删除都是O(log n)
选择依据:
// 去重场景,不关心顺序
Set<String> tags = new HashSet<>(); // O(1)效率高
// 需要排序或范围查询
TreeSet<Integer> scores = new TreeSet<>();
scores.add(90);
scores.add(85);
scores.first(); // 最小值
scores.last(); // 最大值
scores.headSet(90); // 小于90的元素
自定义排序:
class User implements Comparable<User> {
int score;
public int compareTo(User o) {
return this.score - o.score; // 按分数排序
}
}
答题示例
HashSet 基于哈希表,O(1) 效率,无序,通过 hashCode 和 equals 去重。
TreeSet 基于红黑树,O(log n),自动排序,元素需实现 Comparable 或传入 Comparator。
只需去重用 HashSet,需要排序或范围查询用 TreeSet。
参考文章
- 19.集合类-HashSet和TreeSet
2. Lambda表达式与函数式接口
题目
Java中的Lambda表达式是如何工作的?什么是函数式接口?
深入解析
Lambda表达式本质:
- Lambda表达式是匿名内部类的语法糖
- 必须与函数式接口配合使用
- 函数式接口 = 只包含一个抽象方法的接口
语法:
(参数) -> { 代码块 }
参数 -> 结果表达式
函数式接口:
@FunctionalInterface // 可选注解,编译器检查
interface ICalculator {
int calculate(int a, int b);
}
ICalculator add = (a, b) -> a + b;
ICalculator multiply = (a, b) -> a * b;
Java内置函数式接口(java.util.function):
Function<T, R>:接收T返回RConsumer<T>:接收T无返回Supplier<T>:无参数返回TPredicate<T>:接收T返回boolean
Function<Integer, String> f = x -> "值:" + x;
String result = f.apply(100); // "值:100"
注意:Lambda内不能修改局部变量(effectively final),可修改外部类成员变量。
答题示例
Lambda 是匿名内部类的语法糖,必须配合函数式接口使用。
函数式接口是只包含一个抽象方法的接口。
Java 内置了 Function、Consumer、Supplier、Predicate 等常用函数式接口。
注意 Lambda 内不能修改局部变量。
参考文章
- 22.lambda表达式
- 23.方法的引用和Function接口
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com