JAVA SE重点
- 数据类型部分 :
- 8种基本类型 :byte, char, short, int, long, float, double, boolean。要记住它们的默认值和取值范围,比如char的默认值是
'\u0000',boolean默认是false。 - 引用类型:String、数组、接口、类、枚举等。
- 整形提升 :这是常考的坑点,比如
byte a = 10; byte b = 20; byte c = a + b;会报错,因为a+b会自动提升为int类型,需要强转(byte)(a+b)。 - 布尔类型转换:任何类型和布尔类型都不能相互转换,这点要特别注意。
- 8种基本类型 :byte, char, short, int, long, float, double, boolean。要记住它们的默认值和取值范围,比如char的默认值是
- 常量 :
final修饰的变量是常量,只能赋值一次,不能被修改。- 字面值是直接写出来的常量,比如
5,"hello",12.21。
- 运算符 :
>>>是无符号右移,没有无符号左移。%是取余/取模,注意它的正负号规则(与被除数符号一致)。
- 逻辑控制 :
- switch:重点记不能做switch参数的类型:long, float, double。
- 循环语句:do-while至少执行一次;for-each常用于遍历数组或集合,面试常问for和for-each的区别(for-each更简洁但不能修改元素,不能获取索引,不能处理非数组/集合对象)。
- 命名规则 :
- 小驼峰命名法:首字母小写,后面单词首字母大写,如
maxNum。 - 标识符可以包含数字、字符、下划线、$。
- 小驼峰命名法:首字母小写,后面单词首字母大写,如
JDK 常用命令
javac
-
作用 :Java 编译器,用于将
.java源文件编译成.class字节码文件。 -
示例:
javac HelloWorld.java会生成
HelloWorld.class文件。
java
-
作用 :Java 虚拟机启动器,用于运行已编译的
.class文件(不需要加.class后缀)。 -
示例:
java HelloWorld会执行
HelloWorld类中的main方法。
javap -c
-
作用 :反汇编器,用于查看
.class文件的字节码指令(-c表示只显示反汇编的代码)。 -
示例:
javap -c HelloWorld.class可以查看方法内部的字节码操作,常用于底层调试或学习 JVM 指令。
✅ 记忆口诀:
"编
javac,跑java,反javap -c看字节码。"
✅ 面试常见问题:
javac和java的区别?javac是编译器,java是运行时启动器。
javap -c有什么用?- 查看类的字节码指令,用于分析 JVM 执行过程。
一、方法的定义与作用
- 核心作用 :模块化组织代码,实现复用(避免重复写相同逻辑)。
- 组成要素 :方法名、参数列表、返回值(返回值可以是
void,表示无返回值)。
二、方法的调用方式
-
调用位置:
- 可以在主函数(
main)中调用其他自定义方法。 - 也可以在其他自定义方法的内部调用(方法嵌套调用)。
- 可以在主函数(
-
调用规则:
- 方法名和参数列表必须严格匹配(个数、类型都要对应)。
- 返回值类型可选:如果方法声明有返回值(如
int),调用时需接收;如果是void,则不能接收返回值。
面试常考题(重点!):
给定两个数字,写一个方法交换它们的值。
-
❗ 陷阱:Java是值传递!如果直接传基本类型变量(如
int a, int b),方法内交换不会影响外部变量。 -
✅ 正确做法:传对象(如数组、自定义类)或使用数组包装。
-
示例代码:
void swap(int[] arr) { int temp = arr[0]; arr[0] = arr[1]; arr[1] = temp; } // 调用:swap(new int[]{a, b});
三、方法的重载(Overloading)------面试高频考点!
-
定义 :在同一个类中,方法名相同,参数列表不同 (参数个数不同 / 参数类型不同 / 参数顺序不同),返回值类型无关。
-
示例:
void sum(int); // 重载1:1个int参数 int sum(int, int); // 重载2:2个int参数 int sum(double, double); // 重载3:2个double参数👉 这三个方法在编译时都能区分,不会冲突。
-
构造方法能否重载?
- ✅ 可以!因为构造方法名固定为类名,只要参数列表不同,就可以重载。
- 举例:
Student()、Student(String name)、Student(String name, int age)都是合法重载。
-
易错点:
- 仅返回值不同 ≠ 重载(编译报错)。
- 仅参数名不同 ≠ 重载(参数名不影响签名)。
四、方法的递归(Recursion)------思维+代码双重考验
-
基本思想:将大问题拆解为规模更小的同类子问题,子问题再继续拆解,直到能直接解决。
如:求阶乘
n! = n * (n-1)!,直到1! = 1。 -
核心注意事项:
- 必须有终止条件(递归出口)→ 否则无限递归,导致栈溢出(StackOverflowError)。
- 每次递归必须向终止条件靠近 → 否则死循环。
- 自己调用自己 → 递归的本质是自调用。
-
经典例题:
- 斐波那契数列:
f(n) = f(n-1) + f(n-2),终止条件f(1)=1, f(2)=1。 - 汉诺塔问题、二叉树遍历等。
- 斐波那契数列:
🔥 总结:面试/笔试必背点
| 知识点 | 关键点 |
|---|---|
| 方法定义 | 模块化、可复用;组成:方法名、参数列表、返回值 |
| 方法调用 | 参数匹配、返回值接收;交换实参值需传对象或数组(值传递陷阱) |
| 方法重载 | 同一类中,方法名相同 + 参数列表不同;返回值无关;构造方法可重载 |
| 递归 | 拆解问题 + 终止条件 + 自身调用;防止栈溢出 |
一、数组的定义
-
本质 :数组是存放一组相同数据类型 的集合,且在内存中是连续的。
-
默认值:
- 简单类型(如
int、byte):未赋值时默认值为0(或对应类型的默认值,如boolean默认false)。 - 引用类型(如
String、自定义类):未赋值时默认值为null。
- 简单类型(如
-
示例:
int[] arr1 = new int[5]; // 5个int的默认值都是0 String[] arr2 = new String[3]; // 3个String的默认值都是null
二、数组作为方法的参数
-
引用类型传递 :数组是引用类型,传参时传递的是引用(地址),而非副本。因此,方法内修改数组元素,会影响原数组。
void modify(int[] arr) { arr[0] = 100; // 修改原数组的第0个元素 } public static void main(String[] args) { int[] arr = {1,2,3}; modify(arr); System.out.println(arr[0]); // 输出100 } -
可变参数编程 :
int sum(int... a)表示可以传入任意个数的int参数(本质是数组)。调用时既可以传数组,也可以传多个int值。int sum(int... a) { int res = 0; for (int num : a) res += num; return res; } // 调用:sum(1,2,3) 或 sum(new int[]{1,2,3})
三、JVM内存划分(数组的内存位置)
JVM运行时内存分为5个区域,数组相关的内存分配如下:
- 程序计数器:记录线程执行的字节码行号,与数组无关。
- Java虚拟机栈 :存储局部变量、方法调用栈帧。如果数组是局部变量 (如在方法中定义的数组),其引用存在栈中,但数组本身在堆中。
- 本地方法栈:与本地方法(Native Method)有关,与数组无关。
- 堆:存储对象(包括数组)的实例,数组的实际数据在堆中。
- 方法区 :存储类的元数据、常量池等。如果数组元素是引用类型(如
String),字符串常量可能在方法区的常量池中。
四、数组的拷贝方式(重点:浅拷贝)
数组拷贝需要注意:如果数组元素是引用类型,拷贝的是引用(浅拷贝),即新数组和原数组的元素指向同一个对象。
常见拷贝方式:
-
for循环:手动遍历复制每个元素。 -
clone():Object类的方法,数组实现了Cloneable接口,可调用arr.clone()。 -
System.arraycopy():底层native方法,效率高。int[] src = {1,2,3}; int[] dest = new int[3]; System.arraycopy(src, 0, dest, 0, 3); // 从src的0位置复制3个元素到dest的0位置 -
Arrays.copyOf():基于System.arraycopy封装,支持扩容/缩容。int[] newArr = Arrays.copyOf(src, 5); // 复制src并扩容到长度5,新元素默认值0
五、操作数组的常用工具类:java.util.Arrays
-
Arrays.toString(arr):将数组转为字符串,方便打印(默认print(arr)会输出地址,用这个方法可读性更好)。int[] arr = {1,2,3}; System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 3] -
Arrays.sort(arr):对数组排序(升序)。如果是自定义类,需要实现Comparable接口或传入Comparator。int[] arr = {3,1,2}; Arrays.sort(arr); System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 3]
六、二维数组
-
内存结构 :二维数组本质上是"一维数组的每个元素是一个一维数组"。
int[][] arr = new int[3][]; // 不规则二维数组:3行,每行长度可不同 arr[0] = new int[2]; // 第0行长度2 arr[1] = new int[3]; // 第1行长度3 arr[2] = new int[1]; // 第2行长度1 -
不规则二维数组:允许每行的列数不同(即"锯齿状"数组)。
七、匿名数组
-
定义:没有名字的数组,通常用于一次性传递数组参数(不需要先声明数组变量)。
-
示例:
// 调用方法时直接传匿名数组 int sum = sum(new int[]{1,2,3,4});
关键易错点总结
- 数组默认值 :基本类型是
0/false等,引用类型是null。 - 数组拷贝的浅拷贝:如果元素是引用类型,拷贝后新数组和原数组的元素共享对象。
- 二维数组的内存:先分配"行"的数组,再分配每行的数组。
- 可变参数 :
int... a本质是数组,只能有一个可变参数,且必须在参数列表最后。
一、类与对象的基本概念
- 类 :是模板(蓝图),定义了对象的属性(字段)和行为(方法)。
- 对象 :是类的实体 (实例),通过
new 类名()创建,一个类可实例化多个对象。
二、类的成员(字段、方法、代码块)
- 字段(属性) :对象的状态(如
Person类的name、age)。 - 方法(行为) :对象的动作(如
Person类的eat()、run())。 - 代码块 :
- 静态代码块 :用
static{}修饰,优先执行(与静态属性声明顺序有关),仅执行一次,用于初始化静态资源。 - 实例代码块(构造代码块) :用
{}修饰,在构造方法前执行,用于初始化实例属性(可看作构造方法的"公共逻辑")。
- 静态代码块 :用
三、内部类(面试高频考点)
内部类是定义在类内部的类,分为4类,需重点掌握实例内部类 和静态内部类:
- 实例内部类(非静态内部类)
-
定义:在类内部,无
static修饰。 -
特点:
- 包含外部类的
this引用,有额外开销(内存占用略高)。 - 不能定义静态成员变量 (除非是
static final常量,因为常量在编译期确定)。
- 包含外部类的
-
面试问题:
-
Q:实例内部类是否有额外开销?
A:有,因为持有外部类的引用。
-
Q:实例内部类能否定义静态成员变量?
A:不能,除非是
static final修饰的常量。
-
- 静态内部类
-
定义:在类内部,被
static修饰。 -
特点:
- 不依赖外部类实例,可直接通过
外部类名.内部类名访问。 - 不能访问外部类的非静态成员 (因为静态内部类没有外部类的
this引用)。
- 不依赖外部类实例,可直接通过
-
面试问题:
-
Q:静态内部类能否访问外部类的非静态成员?
A:不能。若需访问,需传入外部类对象。
-
- 匿名内部类
-
定义:没有名字的内部类,是外部类的子类(或实现类),只能用一次。
-
本质:语法糖,简化代码(如线程、接口回调场景)。
// 匿名内部类实现Runnable接口 new Thread(new Runnable() { @Override public void run() { System.out.println("匿名内部类"); } }).start();
- 本地内部类(了解)
- 定义:在方法内部定义的类,作用域仅限方法内。
四、访问类的属性/方法
- 静态成员 (用
static修饰):- 属于类 ,可通过
类名.属性/方法访问(无需创建对象)。
- 属于类 ,可通过
- 非静态成员 (实例成员):
- 属于对象 ,需通过
对象引用.属性/方法访问(必须先new对象)。
- 属于对象 ,需通过
五、封装(面向对象三大特性之一)
- 定义 :用
private修饰属性/方法,隐藏内部实现,对外提供public的get/set方法访问。 - 优点 :
- 降低使用者学习成本(只需关注"如何使用",无需关注"如何实现")。
- 保护数据安全(防止非法修改)。
- 面试常考:封装的意义、如何实现封装。
六、构造方法(对象的"出生证明")
- 定义 :方法名与类名相同,无返回值 (连
void都没有)。 - 作用 :实例化对象时,为对象分配内存 并初始化属性。
- 调用时机 :
new 类名()时自动调用(如Person p = new Person();调用无参构造)。 - 多个构造方法(重载) :
- 若类未显式定义构造方法 ,编译器会默认生成一个无参构造。
- 若显式定义了构造方法 ,编译器不再生成默认无参构造 (需手动写无参构造,否则
new Person()会报错)。
- 面试常考:构造方法的重载、默认构造的生成规则。
七、关键字 this
- 含义 :代表当前对象的引用 (哪个对象调用方法,
this就指向哪个对象)。 - 常见场景 :
- 区分成员变量和局部变量(如
this.name = name;)。 - 构造方法间调用(需放在第一行,如
this(10);调用有参构造)。
- 区分成员变量和局部变量(如
八、toString()方法(Object类的方法)
-
默认行为 :
Object类的toString()返回格式为类名@哈希值(如Person@123456)。 -
重写意义:打印对象时,输出更有意义的信息(如对象的属性值)。
@Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } -
使用 :直接打印对象引用时(如
System.out.println(p);),会自动调用toString()。
九、匿名对象
- 定义 :没有名字的对象(如
new Person().eat();)。 - 特点 :
- 只能使用一次(因为没有引用指向它,GC 会回收)。
- 适合临时调用方法,简化代码。
易错点总结
- 内部类的静态变量 :实例内部类不能有普通静态变量,只能是
static final常量。 - 构造方法的重载:若显式定义了有参构造,默认无参构造会消失,需手动添加。
- 静态成员访问:静态方法不能直接访问非静态成员(因为静态方法属于类,此时可能还没有对象)。
- 匿名内部类的使用场景:仅在需要一次性使用类时使用(如线程、接口回调)。
一、继承(extends)
-
定义 :子类(派生类)通过
extends关键字继承父类(基类)的属性和方法(除构造方法外,所有构造方法会被显式调用)。 -
关键字 super:
- 代表父类对象的引用。
- 用法:
super():调用父类的构造方法(必须放在子类构造方法的第一行)。super.data:调用父类的数据成员。super.fun():调用父类的方法。
- 面试常考:
thisvssuperthis:代表当前对象的引用(可区分局部变量和成员变量)。super:代表父类对象的引用(访问父类的成员)。- 区别:
this()调用本类构造,super()调用父类构造;this可单独用,super不能。
-
继承的优点:
- 代码共享(子类复用父类代码,减少重复)。
- 提高重用性(父类方法/属性可被多个子类继承)。
- 提高可扩展性(子类可扩展父类功能)。
-
面试问题:子类继承了父类的什么?
答:除构造方法(显式调用)外,其他所有成员(字段、方法、静态成员等)都会被继承。
二、多态
-
定义 :父类引用指向子类对象,且子类和父类有同名、同参数列表、同返回值的覆盖方法(重写)。运行时,调用的是子类的方法(运行时多态)。
-
实现条件:
- 向上转型 :父类引用指向子类对象(如
Animal a = new Cat();)。 - 方法重写:子类重写父类的方法(方法名、参数、返回值相同)。
- 代码层次:父类引用调用同名方法时,子类重写的方法会被执行(运行时绑定)。
- 向上转型 :父类引用指向子类对象(如
-
多态的好处:
- 降低类使用者的成本(只需知道对象有某个方法,无需关心具体类型)。
- 封装的延伸(使用者无需了解实现细节,只需用方法)。
-
面试常考:多态的理解?
答:父类引用调用子类重写的方法,运行时执行子类逻辑。体现"一个接口,多种实现"。
-
向下转型:
- 前提:先通过
instanceof判断(如if (a instanceof Cat)),否则会报ClassCastException。 - 用途:将父类引用转回子类引用,访问子类特有方法(如
Cat c = (Cat) a;)。
- 前提:先通过
三、抽象类(abstract)
-
定义 :包含抽象方法 (没有具体实现的方法,用
abstract修饰)的类。 -
关键字 abstract:
- 修饰类:表示该类不能被实例化(只能被继承)。
- 修饰方法:表示该方法没有方法体(必须由子类重写)。
-
特点:
- 不能被实例化(不能
new抽象类)。 - 属性可以是
public static final(常量),也可以是普通属性。 - 可以有非抽象方法(普通方法,提供默认实现)。
- 可以被继承(子类必须重写所有抽象方法,除非子类也是抽象类)。
- 不能被实例化(不能
-
最大意义:为了被继承,强制子类实现抽象方法(规范子类行为)。
-
面试问题:抽象类和接口的区别?
答:
-
抽象类:有构造方法、可有非抽象方法、属性无限制、单继承。
-
接口:无构造方法、方法默认
public abstract(JDK8前)、属性必须是public static final、多实现。(JDK8后接口可含
default/static方法,但核心区别仍存)
-
四、接口(interface)
- 定义 :用
interface修饰,是一种完全抽象的类型(JDK8前),定义行为规范。 - 特点 :
- 方法:JDK8前,方法默认
public abstract;JDK8后,可含default(默认方法,有实现)和static方法。 - 属性:必须是
public static final(常量,必须初始化)。 - 不能被实例化(不能
new接口)。 - 类通过
implements实现接口(可多实现,如class A implements B, C)。
- 方法:JDK8前,方法默认
- 意义 :弥补Java"单继承"的缺陷,实现多继承的效果(类可实现多个接口)。
- 常用接口 :
Comparable:用于自定义对象的自然排序(实现compareTo方法)。Comparator:用于定制排序(实现compare方法,更灵活)。Cloneable:标记接口,实现后可通过clone()复制对象(需重写clone()并抛CloneNotSupportedException)。
五、关键总结(面试/笔试必背)
| 特性 | 继承(extends) | 抽象类(abstract) | 接口(interface) |
|---|---|---|---|
| 关键字 | extends | abstract | interface |
| 实例化 | 子类可实例化 | 不能实例化 | 不能实例化 |
| 方法 | 可继承父类方法 | 可含抽象/非抽象方法 | JDK8前:全抽象;JDK8后:可含default/static |
| 属性 | 继承父类属性 | 无限制(普通/静态/常量) | 必须是public static final |
| 继承/实现 | 单继承(类→类) | 单继承(类→抽象类) | 多实现(类→接口) |
| 构造方法 | 子类调用父类构造(super) | 有构造方法(供子类调用) | 无构造方法 |
六、易错点提醒
- 继承时构造方法的调用 :子类构造方法第一行必须调用
super()(或隐式调用父类无参构造),否则编译错误。 - 多态的前提:必须有"继承/实现"+"方法重写"+"向上转型"。
- 抽象类的继承:子类必须重写所有抽象方法,除非子类也是抽象类。
- 接口的实现:类实现接口时,必须实现所有抽象方法(JDK8前),或实现非默认/非静态方法。
- 向下转型 :必须先
instanceof判断,否则抛ClassCastException。
一、String(字符串)
- 本质与常量池
-
类型 :Java中
String是引用类型。 -
常量池(JDK1.7+) :字符串常量(如
"hello")存放在堆中的常量池 ,相同字符串常量在常量池中只存一份(节省内存)。 -
示例:
String str = "hello"; // "hello"放入常量池 String str2 = "hello"; // 直接从常量池取,str == str2 为true -
方法:
intern():将字符串放入常量池(若已存在则直接引用,不存在则创建后引用)。String s1 = new String("hello"); String s2 = s1.intern(); // s2指向常量池的"hello",s2 == str 为true
2. 拼接性能对比(面试常考)
+拼接 :会产生大量临时对象 (每次+都新建StringBuilder再toString),性能差。append拼接 :StringBuilder/StringBuffer的append方法是在原对象上修改,无临时对象,性能高。- 线程安全 :
StringBuffer:方法加了synchronized,线程安全(但性能略低)。StringBuilder:非线程安全,单线程下性能更高。
- 选择策略 :
- 单线程+大量拼接 → 用
StringBuilder。 - 多线程+大量拼接 → 用
StringBuffer。 - 少量拼接 → 可用
+(编译器会优化为StringBuilder)。
- 单线程+大量拼接 → 用
- 面试常考:
StringvsStringBuffervsStringBuilder
| 维度 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 安全(不可变) | 安全(synchronized) | 不安全 |
| 性能 | 拼接差(临时对象) | 中等(锁开销) | 最高(无锁) |
| 适用场景 | 少量拼接、常量 | 多线程大量拼接 | 单线程大量拼接 |
二、异常(Exception)
- 概念
- 作用 :程序异常时,按预设逻辑处理,让程序尽可能恢复正常并继续执行,同时保证代码清晰。
- 体系 :
Throwable(顶层类)→Error+ExceptionError:错误(如栈溢出、内存溢出),由程序员无法解决(只能重启或修改逻辑)。Exception:异常 ,分为:- 运行时异常(RuntimeException) :非受查异常(编译时不检查,如
NullPointerException、ArrayIndexOutOfBoundsException)。 - 编译时异常(受查异常) :编译时必须处理(如
IOException、SQLException),否则编译报错。
- 运行时异常(RuntimeException) :非受查异常(编译时不检查,如
- 异常处理关键字
-
try-catch-finally:try:包裹可能异常的代码。catch:捕获并处理异常(可多个catch,子类在前,父类在后)。finally:无论是否异常,都会执行(一般用于释放资源,如关闭文件、数据库连接)。
throws:声明异常(告诉调用者"我这个方法可能抛异常,你自己处理")。
throw:手动抛出异常(如throw new RuntimeException("出错了"))。
- 面试问题
-
Q:
finally块的作用?A:一般用于对资源的释放 (如关闭流、数据库连接),确保即使发生异常,资源也能被正确释放。
finally无论try是否异常都会执行(除非JVM退出或System.exit())。 -
Q:如何处理异常?
A:两种方式:
try-catch-finally:捕获并处理。throws:声明抛出,交给调用者处理。
-
Q:自定义异常?
A:继承
Exception(受查异常)或RuntimeException(非受查异常),提供构造方法。class MyException extends Exception { public MyException(String msg) { super(msg); } }
- 关键注意点
Error是错误,程序无法处理,只能修改逻辑。- 运行时异常(如
NullPointerException)是程序逻辑错误,应尽量在编码阶段避免(如判空)。 - 编译时异常必须处理(
try-catch或throws),否则编译失败。 finally中如果有return,会覆盖try或catch中的return(不建议在finally写return)。
总结
- String :重点是常量池、
intern()、拼接性能对比、三者的区别。 - 异常 :重点是异常体系、处理关键字(
try-catch-finally、throws、throw)、自定义异常、面试常见问题。