规则:
- 如果一个类被声明为
public
,那么文件名必须与该类的名称完全一致(包括大小写)。- 一个
.java
文件中只能有一个public
类,但可以有多个非public
类。- 如果没有
public
类,文件名可以任意(但通常与主类名一致)。Java的设计逻辑:
- Java强制要求
public
类名与文件名一致,是为了让编译器能快速定位类。Java的每个类在编译后都会生成独立的.class
文件(如Car.class
、Engine.class
)。public
类可以被其他包中的代码访问,因此需要明确的文件名与类名对应关系,确保JVM在加载类时能找到正确的文件。建议:
- 始终让文件名与
public
类名一致:避免编译错误。- 一个文件只放一个类:提高代码可读性(即使允许放多个类)。
- 注意大小写:Java区分大小写,
Car.java
和car.java
是不同的文件!
C语言:
- C语言中
char
类型通常为8位(1字节),采用ASCII或本地扩展编码,取值范围通常是 0 − 255 0-255 0−255(无符号char
)或 − 128 − 127 -128-127 −128−127(有符号char
),符号默认由编译器决定,可显式声明有无符号。
- 仅能表示256种可能的值,无法直接处理Unicode字符。
- 多字节字符(如中文)需依赖外部库(如
wchar_t
或UTF-8编码的函数)。Java:
而Java中的
char
类型固定为16位(2字节),采用Unicode编码(UTF-16),始终是无符号的,取值范围是\u0000
(0)~\uffff
(65535)
\u
用于表示 Unicode 字符的转义序列,格式为\uXXXX
,其中XXXX
是 4 位 16 进制数,对应 Unicode 码点的值(C 语言默认不支持)。\u
作为字符转义序列,但在 C11 及之后的标准 中,引入了\u
作为 Unicode 字符转义码点是什么?
码点(Code Point) 是 Unicode 字符集中的 唯一数字标识符,用来表示一个字符。Unicode 码点的范围是 从
U+0000
到U+10FFFF
,其中:
U+
表示 Unicode 码点的前缀。0000
~10FFFF
是 十六进制 数,表示 Unicode 字符的索引。UTF-16编码是一种变长编码,大部分常用字符(BMP平面,即
U+0000
到U+FFFF
)用1个char
表示。超出BMP的字符(如部分Emoji,U+10000
到U+10FFFF
)需要用2个char
(代理对)表示。
什么是BMP?
BMP,全称为 Basic Multilingual Plane,中文意为基本多文种平面。Unicode 共有
17
个平面,BMP 是 Unicode 的0
号平面,即[U+0000, U+FFFF]
区间。BMP 包含了几乎所有的现代语言的文字和大量的符号。总结:
- C的
char
:
面向底层,直接操作字节,符号性灵活,适合处理原始数据或ASCII文本,但需手动处理编码和多字节字符。- Java的
char
:
为国际化设计,强制使用UTF-16编码,无符号且类型安全,适合处理多语言文本,但灵活性较低。
单行注释:
//
多行注释:
/* ... */
文档注释:
/** ... */
在Java中,文档注释(Javadoc) 是一种特殊的注释格式,用于生成标准的API文档(HTML格式),方便开发者理解类、方法、字段的功能和使用方式。
语法格式:
/** * 描述内容 * @标签名 标签内容 */
- 以
/**
开头,*/
结尾。- 每行以
*
开头(非强制,但建议对齐以增强可读性)。核心Javadoc标签:
标签 用途 示例 @param
描述方法的参数(仅用于方法) @param num1 第一个整数
@return
描述方法的返回值(非 void
方法必须使用)@return 两个整数的和
@throws
/@exception
描述方法可能抛出的异常 @throws IllegalArgumentException 参数为负数时抛出
@deprecated
标记方法或类已过时,建议使用替代方案 @deprecated 使用 {@link #newMethod()} 代替
@see
添加相关类、方法或资源的参考链接 @see java.util.ArrayList
@since
指定引入该功能的版本 @since 1.8
@author
标注作者(通常用于类或接口) @author John Doe
@version
指定版本号(通常用于类或接口) @version 1.0.0
{@link}
内联链接到其他类或方法 使用 {@link #calculateSum(int, int)} 计算和
{@code}
将内容格式化为代码样式(不解析HTML) {@code int x = 5;}
用法示例:
类注释:
/** * 表示一个二维坐标系中的点。 * * @author Jane Smith * @version 1.2 * @since 1.0 */ public class Point { private int x; private int y; // ... }
方法注释:
/** * 计算两个整数的和。 * * @param num1 第一个加数(必须为非负数) * @param num2 第二个加数 * @return 两个参数的和 * @throws IllegalArgumentException 如果num1为负数 * @see 算法参考文档 */ public int add(int num1, int num2) { if (num1 < 0) { throw new IllegalArgumentException("num1不能为负数"); } return num1 + num2; }
生成Javadoc文档:
命令行生成:
javadoc -d docs -encoding UTF-8 -charset UTF-8 MyClass.java
-d docs
:指定输出目录为docs
。-encoding UTF-8
:指定源文件编码。-charset UTF-8
:指定生成文档的字符集。IDE生成:
Tools
→Generate JavaDoc
→ 配置输出目录和选项 → 点击生成。核心价值:
生成标准化API文档,提升代码可维护性和团队协作效率。
在Java中,
+
运算符是一个多功能操作符,主要用于算术加法和字符串连接
算术加法:
适用场景:操作数均为数值类型(
byte
,short
,int
,long
,float
,double
,char
)。规则:
- 若操作数类型不同,会进行隐式类型提升(向更高精度的类型转换)。
- 运算结果类型与提升后的类型一致。
示例:
int a = 5 + 3; // 8(int + int → int) double b = 5 + 3.0; // 8.0(int + double → double) char c = 'A' + 1; // 'B'(char提升为int,计算后转回char)
字符串拼接:
适用场景:任一操作数为字符串(
String
)类型。规则:
- 非字符串操作数会自动转换为字符串(调用
toString()
方法)。- 运算结果为新的字符串对象。
示例:
String s1 = "Hello" + " World"; // "Hello World" String s2 = "Age: " + 25; // "Age: 25"(int转String) String s3 = 10 + 20 + "30"; // "3030"(先计算10+20=30,再连接"30") String s4 = "Sum: " + (10 + 20); // "Sum: 30"(括号改变优先级)
在 Java 中,字符串是不可变的,每次使用
+
连接字符串时,实际上是创建了一个新的String
对象。为了提高性能,Java 提供了StringBuilder
和StringBuffer
来优化字符串的连接操作。StringBuilder sb = new StringBuilder(); sb.append("Hello").append(" ").append("World"); String result = sb.toString(); // "Hello World"
控制台输出:
System.out
:标准输出流,默认输出到控制台。
System.out.print()
:输出不换行。System.out.println()
:输出并换行。System.out.printf()
:格式化输出(类似C语言)。System.out.print("Hello"); // Hello System.out.println(" World"); // World(换行) System.out.printf("PI: %.2f", Math.PI); // PI: 3.14
控制台输入:
Scanner
类:最常用的输入工具,可解析基本类型和字符串。import java.util.Scanner; // 导包 public class ConsoleInput { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入姓名: "); String name = scanner.nextLine(); // 读取整行输入 System.out.print("请输入年龄: "); int age = scanner.nextInt(); // 读取整数 System.out.printf("姓名: %s, 年龄: %d", name, age); } }
- 常用方法:
nextInt()
、nextDouble()
:读取数值。nextLine()
:读取整行(包括空格)。next()
:读取单词(以空格分隔)。
定义:
方法是类或对象中用于执行特定任务的代码块。它接受输入参数(可选),执行操作,并可能返回结果。方法封装了功能,提高了代码的可重用性和模块化。
语法:
访问修饰符 返回类型 方法名(参数列表) { // 方法体 }
- 访问修饰符:如
public
、private
、protected
,控制方法的可见性。- 返回类型:方法返回值的数据类型,无返回值时用
void
。- 参数列表:传递给方法的参数,可为空。
- 方法体:实现具体逻辑的代码块。
示例:
public int add(int a, int b) { return a + b; }
定义:
在同一个类中定义多个同名方法,但它们的参数列表不同(参数类型、个数或顺序不同)。返回类型可以不同,但仅返回类型不同不足以构成重载。
- 方法名必须相同。
- 参数列表必须不同(类型、数量或顺序)。
- 返回类型、访问修饰符和异常声明可以不同。
作用:
提高代码可读性,允许用同一方法名处理不同类型或数量的参数。
示例:
public class Calculator { // 重载1:两个int参数 public int add(int a, int b) { return a + b; } // 重载2:三个int参数 public int add(int a, int b, int c) { return a + b + c; } // 重载3:double类型参数 public double add(double a, double b) { return a + b; } }
calc.add(2, 3); // 调用重载1 calc.add(1, 2, 3); // 调用重载2 calc.add(1.5, 2.5); // 调用重载3
定义:
子类重新定义父类中已有的方法,以提供特定实现。重写是实现运行时多态的关键。
- 方法名、参数列表和返回类型必须与父类方法一致(Java 5+ 允许返回子类类型)。
- 访问修饰符不能比父类更严格(如父类是
protected
,子类可为public
,但不能为private
)。- 子类方法抛出的异常不能比父类更宽泛。
- 不能重写
static
、final
或private
方法。作用:
允许子类自定义行为,实现多态性。
示例:
class Animal { public void sound() { System.out.println("动物发出声音"); } } class Dog extends Animal { @Override public void sound() { System.out.println("汪汪"); } } class Cat extends Animal { @Override public void sound() { System.out.println("喵喵"); } }
Animal myDog = new Dog(); myDog.sound(); // 输出 "汪汪"(多态)
注:
@Override
是 Java 中的一个注解(Annotation),用于明确标识某个方法是重写(Override)了父类或接口中的方法。它的核心作用是让编译器帮助你检查方法重写的正确性,避免因拼写错误、参数不匹配等问题导致重写失败。作用:
编译时检查
如果使用了@Override
注解,编译器会强制检查该方法是否:
- 确实重写了父类或接口中的方法。
- 方法名、参数列表、返回类型与父类完全一致。
- 访问权限符不低于父类方法(例如,父类方法是
protected
,子类不能是private
)。如果不符合重写规则,编译器会直接报错,而不是默默接受错误代码。
提高代码可读性
明确告诉其他开发者这是一个重写方法,增强代码的可维护性。
之前学过的C语言是典型的面向过程编程语言,而现在正在学的Java是面向对象编程语言的代表。
什么是面向过程编程?
定义:
面向过程编程是一种以**过程(函数)**为核心的编程范式,通过将程序分解为一系列线性步骤(函数调用),逐步操作数据以完成任务。其核心思想是“怎么做”(How to do)。
核心特征:
- 函数为中心:程序由函数构成,每个函数实现特定功能。
- 数据与行为分离:数据(变量)独立于函数,函数通过参数接收数据并处理。
- 线性流程:代码按预定义顺序执行,强调算法和逻辑步骤。
- 典型语言:C、Pascal、Fortran。
优缺点:
优点 缺点 简单直观,适合小型程序 代码复用性差 执行效率高 难以管理复杂系统 适合算法密集型任务 数据与行为分离易导致耦合度高 什么是面向对象编程?
定义:
面向对象编程是一种以对象为核心的编程范式,将现实世界的实体抽象为包含数据(属性)和行为(方法)的对象,并通过封装、继承、多态、抽象四大特性构建程序。其核心思想是“谁来做”(Who does what)。
核心特征:
- 对象为中心:程序由对象组成,对象是类的实例。
- 封装性:将数据和方法绑定,隐藏内部实现细节。
- 继承性:子类复用父类的属性和方法。
- 多态性:同一接口在不同对象中有不同实现。
- 典型语言:Java、C++、Python。
优缺点:
优点 缺点 代码模块化,易维护扩展 学习曲线陡峭 支持代码复用(继承/组合) 性能略低于面向过程 适合复杂系统开发 过度设计可能导致冗余 核心对比:
面向过程 面向对象 核心单位 函数 对象 设计目标 步骤分解 职责划分 数据与行为 分离 绑定(封装) 扩展性 低(需修改函数) 高(继承、多态) 适用场景 小型工具、算法、嵌入式开发 大型系统、GUI、企业级应用 叽里咕噜的说什么呢,想象你要做一道菜(比如番茄炒蛋),面向过程就是:你亲自动手,严格按照步骤一步一步来:
1. 洗番茄 → 2. 切番茄 → 3. 打鸡蛋 → 4. 开火 → 5. 炒菜 → 6. 出锅
关注的是步骤(先干什么,后干什么),所有事情都要自己干,没有分工,假如你还要做另一道菜(比如青椒肉丝),你需要重写一遍所有步骤。
想象你开了一家餐厅,面向对象就是:你把任务分给不同的人(对象),比如:
- 厨师对象:负责做菜 - 服务员对象:负责端菜 - 收银员对象:负责收钱
关注的是谁来做(每个对象只负责自己的任务),不同对象之间互相配合(比如服务员告诉厨师做菜),如果要新增一道菜,只需要让厨师学新菜谱,其他岗位不用变。
- 面向过程 ➜ “自己做所有事”(关注步骤)。
- 面向对象 ➜ “让别人帮你做事”(关注分工)。
为什么要有面向对象?
想象你开的是大型连锁餐厅:
- 如果每道菜都自己从头做到尾(面向过程),会累死,而且难以管理。
- 如果用面向对象的方式,每个岗位各司其职,系统更灵活,容易扩展(比如新增一个配送员送外卖)。
类(Class):
定义:
- 类 是一个模板或蓝图,用于描述一类对象的属性(成员变量)和行为(成员方法)。
- 类定义了对象的类型,但本身不占用内存空间,只有通过
new
关键字创建对象时才会分配内存。语法:
public class 类名 { // 成员变量(属性) 数据类型 变量名; // 构造方法(初始化对象) public 类名(参数列表) { // 初始化代码 } // 普通方法(行为) 返回类型 方法名(参数列表) { // 方法体 } }
示例:
public class Car { // 成员变量(属性) String color; String brand; // 构造方法 public Car(String color, String brand) { this.color = color; this.brand = brand; } // 方法(行为) public void start() { System.out.println(brand + "汽车启动了!"); } }
对象(Object):
定义:
- 对象 是类的一个具体实例,通过
new
关键字创建。- 每个对象在内存中独立存在,拥有自己的成员变量值(数据)和方法实现(行为)。
如何创建对象?
使用
new
关键字:
- 分配内存:在堆内存中为对象分配空间。
- 调用构造方法:初始化对象的成员变量。
- 返回引用:将对象的内存地址赋给变量(引用)。
语法:
类名 对象名 = new 类名(参数列表);
示例:
public class Main { public static void main(String[] args) { // 创建 Car 类的对象 Car myCar = new Car("红色", "Toyota"); Car yourCar = new Car("蓝色", "BMW"); // 调用对象的方法 myCar.start(); // 输出:Toyota汽车启动了! yourCar.start(); // 输出:BMW汽车启动了! } }
成员变量:
定义:
- 定义在类内部,但在方法、构造方法或代码块之外。
- 包括两种类型:
- 实例变量(非静态成员变量)
- 类变量(静态成员变量,用
static
修饰)特点:
- 作用域:
- 整个类内部均可访问。
- 实例变量通过对象访问(
obj.variable
),静态变量通过类名访问(Class.variable
)。- 生命周期:
- 实例变量:随对象创建而存在,对象被垃圾回收时销毁。
- 静态变量:随类加载而存在,程序结束时销毁。
- 默认值:
- 成员变量有默认初始值(如
int
默认为0
,对象类型默认为null
)。- 存储位置:
- 实例变量:存储在堆内存(对象内部)。
- 静态变量:存储在方法区(类元数据区)。
示例:
public class Car { // 实例变量(成员变量) private String brand; // 默认值为 null private int speed; // 默认值为 0 // 静态变量(类变量) public static int wheels = 4; // 显式初始化 public void accelerate() { speed += 10; // 可以直接访问成员变量 } }
局部变量:
定义:
定义在方法、构造方法、代码块内部或形参列表中。
特点:
- 作用域:
- 仅在定义它的方法、代码块内部有效。
- 超出作用域后无法访问。
- 生命周期:
- 随方法/代码块的执行而创建,执行结束后销毁。
- 默认值:
- 没有默认值,必须显式初始化后才能使用。
- 存储位置:
- 存储在栈内存(方法调用栈帧中)。
示例:
public class Calculator { public int add(int a, int b) { // 形参 a、b 是局部变量 int result = a + b; // 局部变量 result return result; // result 的作用域仅在 add 方法内 } public void printSum() { int x = 5; // 局部变量 x int y = 10; System.out.println(x + y); // 正确 System.out.println(result); // 错误!result 在此不可见 } }
核心对比:
特性 成员变量 局部变量 定义位置 类内部,方法外 方法、代码块内部或形参列表 作用域 整个类内部 仅方法/代码块内部 生命周期 对象或类存在期间 方法/代码块执行期间 默认值 有默认值(如 0
/null
)必须显式初始化 存储位置 堆(实例变量)或方法区(静态) 栈 访问权限 可添加访问修饰符(如 public
)不能使用访问修饰符 内存分配 自动分配 需要手动初始化 局部变量和成员变量同名时怎么办?
局部变量会覆盖成员变量(就近原则),通过
this
关键字访问成员变量。public class Test { private int value = 10; public void setValue(int value) { this.value = value; // this.value 是成员变量,value 是局部变量 } }
在 Java 中,构造方法(Constructor)是类中用于初始化对象的特殊方法,在对象创建时自动调用。
核心特性:
命名与类名相同
构造方法必须与类名完全一致(包括大小写)。无返回类型
构造方法没有返回值(连void
也不写),例如:public class Student { // 构造方法(无返回类型) public Student() { ... } }
自动触发
通过new
关键字创建对象时,构造方法自动执行。可重载
一个类可以有多个构造方法(参数不同),提供多种初始化方式。分类:
默认构造方法(无参构造方法)
如果类中未显式定义任何构造方法,Java 编译器会自动生成一个默认的无参构造方法。
示例:
public class Student { // 编译器自动生成默认构造方法:public Student() {} }
自定义构造方法(带参数):
开发者显式定义,用于灵活初始化对象属性。
示例:
public class Student { private String name; private int age; // 自定义构造方法 public Student(String name, int age) { this.name = name; this.age = age; } }
基本用法:
public class Student { private String name; private int age; // 无参构造方法(显式定义) public Student() { } // 有参构造方法 public Student(String name, int age) { this.name = name; this.age = age; } public static void main(String[] args) { Student stu1 = new Student(); // 调用无参构造方法 Student stu2 = new Student("张三", 18); // 调用有参构造方法 } }
构造方法的重载:
public class Car { private String brand; private String color; // 构造方法1:仅初始化品牌 public Car(String brand) { this.brand = brand; this.color = "黑色"; } // 构造方法2:初始化品牌和颜色 public Car(String brand, String color) { this.brand = brand; this.color = color; } }
构造方法(构造器) 是对象创建的入口,用于确保对象初始状态合法。通过重载构造方法,可以提供多种初始化方式。
定义:JavaBean 是一种符合特定编码规范的 Java 类,主要用于封装数据和提供标准化操作。
核心规范:
公共的无参构造方法
类必须提供无参数构造方法,以便通过反射机制实例化对象。
示例:
public class User { public User() { } // 必须有无参构造方法 }
属性私有化
所有属性(字段)必须声明为
private
,禁止直接暴露给外部。private String name; private int age;
通过公共方法访问属性
提供
getXxx()
和setXxx()
方法操作属性(称为 Getter 和 Setter)。布尔类型属性可使用
isXxx()
作为 Getter。public String getName() { return name; } public void setName(String name) { this.name = name; }
示例:
public class User { // 属性私有化 private String name; private int age; private boolean active; // 无参构造方法(必须) public User() { } // 带参构造方法(可选) public User(String name, int age) { this.name = name; this.age = age; } // Getter 和 Setter(必须) public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 布尔类型可使用 isXxx() public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } }
static
是 Java 中用于修饰类成员(变量、方法、代码块、内部类)的关键字,表示该成员属于类本身而非类的实例。其主要目的是实现与类直接关联的功能或数据共享,避免重复创建实例的消耗。应用场景:
静态变量(类变量)、
定义:用
static
修饰的成员变量,属于类而非实例。特点:
- 所有实例共享同一份静态变量。
- 内存中仅在类加载时分配一次(存储在方法区/元空间)。
- 可通过
类名.变量名
直接访问,无需实例化对象。示例:
public class Counter { static int count = 0; // 静态变量 public Counter() { count++; // 所有实例共享 count } public static void main(String[] args) { new Counter(); new Counter(); System.out.println(Counter.count); // 输出 2 } }
静态方法
定义:用
static
修饰的方法,属于类而非实例。特点:
- 只能直接访问静态成员(变量/方法),不能访问实例成员。
- 可通过
类名.方法名()
直接调用。- 常用作工具方法(如
Math.sqrt()
)。示例:
public class MathUtils { public static int add(int a, int b) { // 静态方法 return a + b; } public static void main(String[] args) { int result = MathUtils.add(3, 5); // 直接调用 System.out.println(result); // 输出 8 } }
静态代码块
定义:用
static { ... }
定义的代码块,在类加载时执行一次。用途:
- 初始化静态变量。
- 加载静态资源(如配置文件)。
示例:
public class DatabaseConfig { static String url; static String username; static { // 静态代码块 url = "jdbc:mysql://localhost:3306/mydb"; username = "root"; System.out.println("数据库配置已加载"); } }
静态内部类
定义:用
static
修饰的内部类,不依赖外部类实例。特点:
- 可直接创建实例:
new Outer.Inner()
。- 不能访问外部类的非静态成员。
- 常用于工具类或单例模式。
示例:
public class Outer { static class Inner { // 静态内部类 void print() { System.out.println("静态内部类"); } } public static void main(String[] args) { Outer.Inner inner = new Outer.Inner(); // 直接创建 inner.print(); } }
底层原理:
- 内存分配
- 静态变量存储在方法区(Method Area)(Java 8 后为元空间 MetaSpace)。
- 类加载时初始化,生命周期与类相同。
- 类加载机制
- JVM 首次使用类时加载类信息,静态代码块和静态变量在此阶段初始化。
- 类卸载时静态资源才会释放(通常由 JVM 管理)。
注意事项:
场景 规则 访问权限 静态方法只能访问静态成员,非静态方法可访问静态和非静态成员。 多线程安全 静态变量是共享资源,需通过同步机制(如 synchronized
)保证线程安全。继承与重写 静态方法不能被子类重写(但可以隐藏),且不支持 @Override
。设计模式 过度使用 static
会导致代码耦合度高,违反面向对象设计原则。为什么静态方法不能访问非静态成员?
非静态成员依赖对象实例存在,而静态方法在类加载时即可调用,此时对象可能尚未创建。
静态变量与实例变量的区别?
维度 静态变量 实例变量 归属 类 对象实例 内存分配 类加载时分配(方法区) 对象创建时分配(堆内存) 生命周期 与类共存亡 与对象共存亡
定义:
继承 是面向对象编程(OOP)的三大特性之一(封装、继承、多态),允许一个类(子类/派生类)基于另一个类(父类/基类)构建,复用父类的属性和方法,并可以扩展或修改其功能。
核心作用:
- 代码复用:避免重复编写公共代码。
- 层次化设计:建立类之间的层次关系(如动物 → 猫 → 布偶猫)。
- 多态支持:通过父类引用操作子类对象。
语法:使用
extends
关键字实现继承class 父类 { // 父类属性和方法 } class 子类 extends 父类 { // 子类特有的属性和方法 }
继承的成员范围:
- 子类可继承的成员
public
和protected
修饰的属性和方法。- 默认访问权限(无修饰符)的成员(若子类与父类在同一包中)。
- 不可继承
private
成员。- 父类的构造方法(但可通过
super()
调用)。子类可重写父类的方法,以提供特定实现。
class Animal { public void makeSound() { System.out.println("动物发出声音"); } } class Cat extends Animal { @Override public void makeSound() { System.out.println("喵喵"); } }
构造方法与继承
创建子类对象时,父类构造方法优先执行。
class Parent { Parent() { System.out.println("父类构造方法"); } } class Child extends Parent { Child() { super(); // 默认隐含调用父类无参构造 System.out.println("子类构造方法"); } } // 输出: // 父类构造方法 // 子类构造方法
若父类只有带参构造方法,子类必须显式调用
super(参数)
。class Parent { Parent(int value) { ... } } class Child extends Parent { Child() { super(10); // 必须显式调用 } }
单继承与多继承
Java只支持单继承(一个子类只能有一个直接父类),但可以通过接口(
implements
)实现多继承的效果
this
关键字
作用:
- 指向当前对象的引用,用于在类的内部访问当前对象的成员(属性、方法、构造方法)。
- 主要解决变量名冲突(如局部变量与成员变量同名)。
使用场景:
区分成员变量和局部变量
public class Person { private String name; // 成员变量 public void setName(String name) { // 参数(局部变量) this.name = name; // 用 this 区分同名变量 } }
调用当前对象的其他构造方法(必须在构造方法的第一行使用)
public class Person { private String name; private int age; // 无参构造方法调用有参构造方法 public Person() { this("Unknown", 0); // 调用下面的构造方法 } public Person(String name, int age) { this.name = name; this.age = age; } }
返回当前对象本身
public class Counter { private int count; public Counter increment() { count++; return this; // 返回当前对象,支持链式调用 } } // 使用示例 Counter counter = new Counter().increment().increment();
注意事项:
- 不能用于静态方法(
static
方法属于类,不依赖对象)。- 不能单独使用(必须指向具体的成员)。
super
关键字
作用:
- 指向父类对象的引用,用于在子类中访问父类的成员(属性、方法、构造方法)。
- 主要解决继承中的成员覆盖问题。
使用场景:
调用父类的构造方法
必须在子类构造方法的第一行使用。
如果父类没有无参构造方法,必须显式调用
super(...)
。class Animal { private String type; public Animal(String type) { this.type = type; } } class Dog extends Animal { public Dog() { super("犬科"); // 显式调用父类有参构造方法 } }
访问父类的成员变量或方法
当子类覆盖父类方法时,用
super
调用父类原始方法。class Animal { public void eat() { System.out.println("动物在吃东西"); } } class Dog extends Animal { @Override public void eat() { super.eat(); // 先调用父类的 eat() System.out.println("狗在啃骨头"); } }
访问父类被隐藏的成员变量
class Parent { String name = "Parent"; } class Child extends Parent { String name = "Child"; public void printNames() { System.out.println(super.name); // 输出 Parent System.out.println(this.name); // 输出 Child } }
注意事项:
- 不能用于静态上下文(静态方法或静态块)。
- 不能单独使用(必须指向具体的父类成员)。
注意:
- 在构造方法中,
this()
和super()
不能共存,必须放在第一行且只能调用一次。super
只能直接访问直接父类,无法跨级访问祖父类。
示例:
class Vehicle { String type; public Vehicle(String type) { this.type = type; } public void start() { System.out.println(type + "启动"); } } class Car extends Vehicle { String brand; public Car(String brand) { super("汽车"); // 调用父类构造方法 this.brand = brand; } @Override public void start() { super.start(); // 调用父类的 start() System.out.println(brand + "汽车正在行驶"); } } public class Main { public static void main(String[] args) { Car car = new Car("丰田"); car.start(); } } // 输出: // 汽车启动 // 丰田汽车正在行驶
什么是抽象?
抽象(Abstraction)是面向对象编程的核心思想之一,指提取对象的本质特征,忽略具体实现细节。通过抽象,可以定义统一的规范或模板,让具体实现由子类或实现类完成。
抽象类(Abstract Class)
定义:用
abstract
修饰的类,称为抽象类。它可以包含抽象方法(无具体实现)和具体方法(有实现),用于定义部分实现的模板。核心特性:
- 不能被实例化:只能通过子类继承后使用。
- 可以包含抽象方法:用
abstract
修饰的方法,无方法体。- 可以包含具体方法:普通方法的实现。
- 可以定义成员变量:与普通类相同。
- 可以有构造方法:用于子类初始化。
示例:
public abstract class Animal { // 抽象方法(无实现) public abstract void makeSound(); // 具体方法(有实现) public void eat() { System.out.println("动物进食"); } }
使用场景:
- 定义模板方法模式:父类定义算法骨架,子类实现具体步骤。
- 部分方法需复用:抽象类中既有通用实现,又预留扩展点。
- 需要状态管理:抽象类可定义成员变量保存状态。
接口(Interface)
定义:用
interface
定义的类型,用于描述对象的行为规范。接口中的方法默认是抽象的(Java 8 前),支持多继承。核心特性:
- 默认方法(Default Method):用
default
修饰,提供默认实现。- 静态方法(Static Method):用
static
修饰,直接通过接口调用。- 私有方法(Java 9+):用
private
修饰,辅助默认方法。- 成员变量默认是
public static final
:即常量。- 不能定义构造方法:无法实例化。
示例:
public interface Flyable { // 抽象方法(默认 public abstract) void fly(); // 默认方法(Java 8+) default void glide() { System.out.println("滑翔中"); } // 静态方法(Java 8+) static int getMaxSpeed() { return 1000; } }
使用场景:
- 定义行为规范:如
Runnable
接口定义线程任务。- 实现多继承:一个类可实现多个接口。
- 解耦组件:通过接口隔离实现与调用方。
对比
维度 抽象类(Abstract Class) 接口(Interface) 关键字 abstract class
interface
实例化 不能直接实例化 不能直接实例化 成员变量 普通变量(可非 final) 默认 public static final
(常量)构造方法 可以有 不能有 继承关系 单继承(一个子类只能继承一个抽象类) 多继承(一个类可实现多个接口) 设计目的 代码复用,定义部分实现 定义行为规范,解耦设计 如何选择:
- 优先使用接口:避免继承带来的强耦合。
- 抽象类用于代码复用:当多个类有共同逻辑时。
定义:多态(Polymorphism) 是面向对象编程(OOP)的核心特性之一,指同一操作作用于不同对象时,可以有不同的行为。它允许使用统一的接口处理不同类型的对象,从而提高代码的灵活性和可扩展性。
Java 中的多态分为两类:
类型 实现方式 特点 编译时多态 方法重载(Overloading) 根据参数列表在编译时确定调用的方法。 运行时多态 方法重写(Overriding) + 继承 根据对象实际类型在运行时确定调用的方法。 运行时多态(动态绑定)
核心机制:JVM 在运行时根据对象的实际类型(而非引用类型)动态绑定要执行的方法。
条件:
- 继承关系:存在父类与子类。
- 方法重写:子类重写父类的方法。
- 向上转型:父类引用指向子类对象。
示例:
class Animal { public void sound() { System.out.println("动物发出声音"); } } class Dog extends Animal { @Override public void sound() { System.out.println("汪汪"); } } class Cat extends Animal { @Override public void sound() { System.out.println("喵喵"); } } public class Main { public static void main(String[] args) { Animal a1 = new Dog(); // 向上转型 Animal a2 = new Cat(); a1.sound(); // 输出 "汪汪"(动态绑定) a2.sound(); // 输出 "喵喵" } }
应用场景:
- 统一接口处理不同对象。
- 结合工厂模式,通过多态创建不同子类对象。
多态与类型转换
向上转型
子类对象赋值给父类引用(自动完成)。
特点:安全,但只能访问父类声明的方法。
Animal animal = new Dog(); // 向上转型
向下转型
父类引用强制转换为子类类型(需显式转换)。
风险:可能抛出
ClassCastException
。安全做法:使用
instanceof
检查。if (animal instanceof Dog) { Dog dog = (Dog) animal; // 安全向下转型 dog.bark(); }
注意事项:
- 静态方法不支持重写,无多态性(通过类名调用)。
- 私有方法不可被重写,无多态性。
- final 方法禁止重写,无多态性。
- 成员变量无多态性,访问时取决于引用类型。
- 构造方法不可被重写,无多态性。
权限修饰符总览
修饰符 类内部 同包内 子类(不同包) 其他包 public
✅ ✅ ✅ ✅ protected
✅ ✅ ✅ ❌ 默认(不写) ✅ ✅ ❌ ❌ private
✅ ❌ ❌ ❌ 注:
- 默认权限:不使用任何修饰符,称为包级私有(package-private)。
- 子类权限:
protected
允许不同包的子类访问父类成员。- 类本身的修饰符:外部类只能使用
public
或默认权限(不能是protected
或private
)。
public
(公开)
作用范围:任何地方均可访问。
典型场景:
- 开放给外部调用的方法(如工具类方法)。
- 常量(
public static final
)。- 主类(包含
main
方法的类必须用public
)。示例:
public class Calculator { public static final double PI = 3.14159; // 公开常量 public int add(int a, int b) { // 公开方法 return a + b; } }
protected
(受保护)
作用范围:本类、同包类、不同包的子类。
典型场景:
- 父类中希望被子类继承或重写的方法或属性。
- 框架设计中需要被子类扩展的成员。
示例:
public class Animal { protected void breathe() { // 子类可继承并调用 System.out.println("呼吸"); } } class Dog extends Animal { public void showBreathe() { breathe(); // 子类可直接调用父类的 protected 方法 } }
默认权限(包级私有)
作用范围:本类、同包类。
典型场景:
- 内部工具方法或属性,不希望被其他包访问。
- 模块化设计中,包内协作的类。
示例:
class Logger { // 默认权限,只能在同包内使用 void log(String message) { // 默认权限方法 System.out.println("[LOG] " + message); } }
private
(私有)
作用范围:仅本类内部。
典型场景:
- 隐藏类的内部实现细节(如成员变量)。
- 防止外部直接修改数据,通过公共方法(getter/setter)控制访问。
示例:
public class BankAccount { private double balance; // 私有属性,外部无法直接访问 public void deposit(double amount) { // 公开方法控制存取 if (amount > 0) { balance += amount; } } public double getBalance() { // 提供公共读取接口 return balance; } }
注意事项:
- 类的修饰符
- 外部类只能使用
public
或默认权限。- 内部类可以是
private
或protected
。- 方法重写时的权限
- 子类重写父类方法时,权限不能缩小(如父类方法是
public
,子类不能改为protected
)。- 构造方法的权限
- 构造方法可以是任意权限(如
private
用于单例模式)。建议:
- 最小化访问原则:
- 优先使用最严格的权限(如
private
),仅在必要时放宽。- 减少代码耦合,提高安全性。
- 封装数据:
- 成员变量尽量设为
private
,通过公共方法(getter/setter)控制访问。- 避免直接暴露内部状态。
- 模块化设计:
- 使用默认权限隐藏包内实现细节。
- 通过
public
类或方法对外提供接口。
定义:
final
是 Java 中用于限制类、方法或变量的修改或继承的关键字,其核心目标是增强代码的安全性、不可变性和设计约束。作用:
修饰对象 作用 变量 变量值(或引用)不可变(常量)。 方法 方法不能被子类重写。 类 类不能被继承。
修饰变量
基本数据类型变量
变量值一旦初始化,不可修改。
final int MAX_VALUE = 100; // MAX_VALUE = 200; // 编译错误:无法为final变量赋值
引用类型变量
引用不可变(即不能指向其他对象),但对象内部状态可修改。
final List<String> list = new ArrayList<>(); list.add("Java"); // 允许:修改对象内容 // list = new LinkedList<>(); // 编译错误:不能改变引用
成员变量与局部变量
成员变量:必须显式初始化(可在声明时、构造方法或初始化块中赋值)。
局部变量:只需在使用前初始化一次。
class Example { final int a = 10; // 声明时初始化 final int b; Example() { b = 20; // 构造方法中初始化 } }
修饰方法
禁止子类重写
确保父类方法逻辑不被修改。
class Parent { public final void show() { System.out.println("父类方法"); } } class Child extends Parent { // 编译错误:无法重写final方法 // public void show() { ... } }
与
private
的隐式final
若方法为
private
,则隐式具有final
效果(子类无法访问,更无法重写)。class Parent { private void method() {} // 隐式final } class Child extends Parent { // 合法:实际是子类的新方法,非重写 private void method() {} }
修饰类
禁止继承
保证类不可被扩展(如
String
、Integer
等核心类均为final
)。final class ImmutableClass { ... } // 编译错误:无法继承final类 // class SubClass extends ImmutableClass { ... }
设计不可变类
若类为
final
,且所有字段为final
,则对象创建后状态不可变。public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } // 仅提供getter,无setter }
在 Java 中,代码块(Code Block)是一段被大括号
{}
包围的代码,用于控制变量的作用域或定义代码的执行逻辑。实例代码块(构造代码块)
定义:直接写在类中,无修饰符,每次创建对象时执行(在构造方法之前)。
作用:初始化实例变量或执行对象共有的初始化逻辑。
语法:
{ // 初始化实例变量或通用逻辑 }
示例:
public class Car { String brand; int speed; // 实例代码块:每次 new 对象时执行 { speed = 0; // 所有 Car 对象初始速度为 0 System.out.println("实例代码块执行:车辆已启动"); } public Car(String brand) { this.brand = brand; } }
静态代码块
定义:用
static
修饰,属于类级别,在类加载时执行一次。作用:初始化静态变量或加载静态资源(如配置文件)。
语法:
static { // 初始化静态资源 }
示例:
public class DatabaseConfig { static String url; static String username; // 静态代码块:类加载时读取配置 static { url = "jdbc:mysql://localhost:3306/mydb"; username = "admin"; System.out.println("静态代码块执行:数据库配置已加载"); } }
实例代码块 vs 静态代码块
- 实例代码块:每次
new
对象时执行(在构造方法之前)。- 静态代码块:类加载时执行一次(仅一次)。
示例:
public class ExecutionOrder { static { System.out.println("静态代码块执行"); } { System.out.println("实例代码块执行"); } public ExecutionOrder() { System.out.println("构造方法执行"); } public static void main(String[] args) { new ExecutionOrder(); new ExecutionOrder(); } } // 输出: // 静态代码块执行 // 实例代码块执行 // 构造方法执行 // 实例代码块执行 // 构造方法执行
使用场景与建议
- 实例代码块
- 场景:多个构造方法共有的初始化逻辑(如设置默认值)。
- 建议:可将通用逻辑提取到私有方法中,由构造方法调用。
- 静态代码块
- 场景:初始化全局配置(如数据库连接)、加载静态资源(如图片、文件)。
- 建议:避免在静态代码块中执行耗时操作(影响类加载速度)。
总结
- 静态代码块处理类级初始化,实例代码块处理对象级初始化。
- 优先选择构造方法或方法封装逻辑,避免滥用代码块!
在 Java 中,内部类(Inner Class) 是定义在另一个类内部的类,它可以访问外部类的成员,并实现更灵活的代码封装。
成员内部类
定义:直接定义在外部类的成员位置(与属性、方法同级)。
特点:
- 可以访问外部类的所有成员(包括
private
)。- 依赖外部类实例存在(不能有静态成员,除非用
static final
常量)。示例:
public class Outer { private int outerField = 10; // 成员内部类 class Inner { void print() { System.out.println("访问外部类字段:" + outerField); // 直接访问外部类成员 } } }
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); // 必须通过外部类实例创建
静态内部类
定义:用
static
修饰的内部类。特点:
- 不依赖外部类实例(可直接创建)。
- 不能直接访问外部类的非静态成员(只能访问静态成员)。
示例:
public class Outer { private static int staticField = 20; private int instanceField = 30; static class StaticInner { void print() { System.out.println(staticField); // 合法 // System.out.println(instanceField); // 错误!无法访问非静态成员 } } }
Outer.StaticInner inner = new Outer.StaticInner(); // 直接创建
局部内部类
定义:定义在方法或代码块内部的类。
特点:
- 作用域仅限于所在方法或代码块。
- 可以访问外部类的成员,但只能访问所在方法的
final
或 effectively final 局部变量。示例:
public class Outer { private int outerField = 40; public void method() { int localVar = 50; // 必须为 final 或 effectively final class LocalInner { void print() { System.out.println(outerField); // 合法 System.out.println(localVar); // 合法(localVar 不可修改) } } LocalInner inner = new LocalInner(); inner.print(); } }
匿名内部类
定义:没有名字的局部内部类,通常用于简化代码(如事件监听、线程实现)。
特点:
- 直接继承父类或实现接口。
- 只能使用一次(无法复用)。
示例:
public interface ClickListener { void onClick(); } public class Button { public void setOnClickListener(ClickListener listener) { listener.onClick(); } } // 使用匿名内部类 Button button = new Button(); button.setOnClickListener(new ClickListener() { @Override public void onClick() { System.out.println("按钮被点击!"); } });
应用场景:
封装性增强
场景:将仅用于某个外部类的辅助逻辑封装在内部(如链表节点
Node
)。public class LinkedList { private Node head; // 成员内部类:外部无需关心 Node 的实现 private class Node { int data; Node next; } }
回调与事件处理
场景:匿名内部类简化事件监听(如 Android 点击事件)。
// Java Swing 示例 JButton button = new JButton("提交"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("提交表单"); } });
访问外部类私有成员
场景:内部类直接操作外部类私有属性。
public class Outer { private int secret = 100; class Inner { void revealSecret() { System.out.println("秘密是:" + secret); // 直接访问 private 成员 } } }
静态工具类
场景:静态内部类实现与外部类相关的工具逻辑。
public class MathUtils { // 静态内部类:提供特定算法 public static class Calculator { public static int add(int a, int b) { return a + b; } } } // 使用 int sum = MathUtils.Calculator.add(2, 3);
内部类是实现高内聚、低耦合的重要手段,但需根据场景选择合适类型,避免滥用!
定义:异常(Exception) 是程序执行过程中发生的非正常事件,它会中断程序的正常流程。Java 通过异常机制提供了一种结构化的错误处理方式。
分类:
Java 异常类均继承自
Throwable
,分为三大类:
类型 特点 示例 Error JVM 严重错误,程序无法恢复(通常不处理) OutOfMemoryError
检查型异常(Checked Exception) 编译时强制要求处理(继承自 Exception
但非RuntimeException
的子类)IOException
,SQLException
非检查型异常(Unchecked Exception) 运行时异常,通常由程序逻辑错误引起( RuntimeException
及其子类)NullPointerException
,ArrayIndexOutOfBoundsException
异常处理:
使用
try-catch-finally
块捕获并处理异常
核心思想:在代码中直接捕获异常并处理,避免程序终止。
适用场景:当前方法明确知道如何处理异常时(如重试、回滚、日志记录等)。
语法:
try { // 可能抛出异常的代码 } catch (ExceptionType1 e1) { // 处理特定异常 } catch (ExceptionType2 e2) { // 处理其他异常 } finally { // 无论是否异常,最终执行的代码(如释放资源) }
示例:
try { FileInputStream fis = new FileInputStream("file.txt"); } catch (FileNotFoundException e) { System.out.println("文件未找到"); } finally { if (fis != null) fis.close(); // 需处理IOException }
使用
throws
声明抛出异常
核心思想:将异常抛给调用者处理,当前方法不直接处理。
适用场景:当前方法无法处理异常(如工具类方法、底层逻辑),需由上层调用者决定如何应对。
示例
public void readFile() throws FileNotFoundException, IOException { FileReader file = new FileReader("test.txt"); // 其他可能抛出 IOException 的操作 }
throw
抛出异常
手动抛出异常对象,可以是自定义异常。
示例:
if (age < 0) { throw new IllegalArgumentException("年龄不能为负数"); }
自定义异常
步骤:
- 继承
Exception
(检查型异常)或RuntimeException
(非检查型异常)。- 提供构造方法(通常调用父类构造)。
示例:
public class InsufficientFundsException extends RuntimeException { public InsufficientFundsException(String message) { super(message); } } // 使用 if (balance < amount) { throw new InsufficientFundsException("余额不足"); }
Error 和 Exception 的区别?
- Error:JVM 严重问题(如内存溢出),程序无法恢复。
- Exception:可捕获处理的异常(检查型需处理,非检查型通常不强制)。
在Java中,
String
类可以不用new
关键字创建对象,这是由**字符串常量池(String Pool)**机制和Java对字符串字面量的特殊处理共同实现的。字符串常量池(String Pool)
什么是字符串常量池?
字符串常量池是Java堆内存中的一块特殊存储区域,用于缓存字符串字面量。当通过字面量(如
"abc"
)创建字符串时,JVM会先在字符串常量池中检查是否存在相同内容的字符串:
- 如果存在:直接返回池中已有字符串的引用,不创建新对象。
- 如果不存在:在池中创建该字符串对象,并返回引用。
目的:减少重复字符串的内存占用,提升性能。
示例:
String s1 = "abc"; // 第一次创建,池中没有,故在池中新建对象 String s2 = "abc"; // 直接复用池中的"abc",s1和s2指向同一对象 System.out.println(s1 == s2); // 输出 true(引用相同)
new String()
与字面量的区别
使用
new
关键字:每次都会在堆中强制创建新对象,即使内容相同。
String s3 = new String("abc"); // 在堆中新建对象,不检查常量池 String s4 = new String("abc"); // 再新建一个对象 System.out.println(s3 == s4); // 输出 false(引用不同)
使用字面量:
直接利用常量池,避免重复构建。
String s5 = "abc"; // 从常量池获取 String s6 = "abc"; // 同上 System.out.println(s5 == s6); // 输出 true(引用相同)
字符串的不可变性
String
类被设计为不可变(所有修改操作都返回新对象),这使得常量池可以安全地缓存字符串:
- 多个引用共享同一字符串时,无需担心内容被意外修改。
- 缓存字符串的哈希值,提升性能(如
HashMap
的键)。编译时优化
字面量的处理:
在编译阶段,字符串字面量会被直接写入类的常量池。当类加载时,JVM会将这些字面量预先加载到字符串常量池中。
String s7 = "hello" + " world"; // 编译期优化为"hello world" // 等价于 String s7 = "hello world";
intern
方法手动将字符串添加到常量池:调用
intern()
方法时,如果池中已有相同内容的字符串,返回其引用;否则将当前字符串添加到池中。String s8 = new String("xyz").intern(); String s9 = "xyz"; System.out.println(s8 == s9); // 输出 true
总结
Java通过字符串常量池和字面量处理机制,使得直接赋值(如
"abc"
)能够复用已有对象,无需显式使用new
。这种处理能够节省内存、提升性能,同时依赖字符串的不可变性保证安全性。
增强 for 循环(也称为 for-each 循环)是 Java 5 引入的语法糖,旨在简化集合和数组的遍历操作。它隐藏了迭代器或索引的细节,使代码更简洁易读。
基本语法:
for (元素类型 变量名 : 集合或数组) { // 操作变量 }
示例:
List<String> list = Arrays.asList("A", "B", "C"); for (String s : list) { System.out.println(s); // 输出 A, B, C } int[] array = {1, 2, 3}; for (int num : array) { System.out.println(num); // 输出 1, 2, 3 }
原理:
遍历数组:编译器会将其转换为传统索引循环
// 源码:for (int num : array) { ... } for (int i = 0; i < array.length; i++) { int num = array[i]; // 操作 num }
遍历集合:编译器会调用集合的
iterator()
方法,使用迭代器遍历// 源码:for (String s : list) { ... } Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String s = iterator.next(); // 操作 s }
注意事项:
无法直接访问索引
若需索引,需改用传统 for 循环或额外计数变量。
int index = 0; for (String s : list) { System.out.println(index + ": " + s); index++; }
不能修改集合结构
遍历时直接调用集合的
add()
或remove()
会抛出ConcurrentModificationException
。