【从零开始java学习|第十七篇】面向对象进阶
目录
一、封装
二、包(Package):类的 “组织与管理工具”
1. 包的核心作用
2. 包的定义与命名规范
(1)定义语法
(2)命名规范(强制遵循,面试常考)
3. 包的导入(import):使用其他包的类
(1)三种导入方式
(2)示例:导入与使用
(3)注意:默认导入的包
4. 包的访问权限(与 default 修饰符联动)
三、final 关键字:“不可修改” 的约束工具
1. final 修饰类:类不能被继承
2. final 修饰方法:方法不能被重写
3. final 修饰变量:变量不能被重新赋值
(1)final 修饰局部变量
(2)final 修饰成员变量
(3)final 修饰引用变量(面试高频)
(4)static final:静态常量(面试重点)
4. final 的核心面试考点
四、权限修饰符:控制 “访问范围” 的工具
1. 4 种权限修饰符的访问范围(面试必记)
关键说明:
2. 权限修饰符的典型应用场景
(1)private:封装成员变量
(2)protected:子类继承访问
(3)public:暴露公共接口
(4)default:同包内复用
3. 权限修饰符的面试高频问题
五、代码块:“初始化逻辑” 的载体
1. 静态代码块(Static Block)
(1)定义与执行时机
(2)核心作用
(3)示例:
2. 实例代码块(Instance Block)
(1)定义与执行时机
(2)核心作用
3. 局部代码块(Local Block)
(1)定义与执行时机
4. 代码块的执行顺序
六、总结与面试核心串联
一、封装
封装 = 把数据藏起来,只留安全门。
将类的属性私有化(private
),外界只能通过公开方法(get/set
或其他业务方法)间接访问,从而在方法里加校验、隐藏实现细节、降低耦合。
生活例子:ATM 卡
-
你的余额是 private —— 外部不能直接改。
-
存钱/取钱走 public 方法
deposit()
/withdraw()
:-
存钱:负金额拒绝。
-
取钱:余额不足拒绝;大额还要短信验证。
-
-
结果:
-
钱不会被“隔空”乱改;
-
所有业务规则集中在一个类里,外部只关心“能存能取”,不用管内部怎么算利息、怎么记日志。
-
二、包(Package):类的 “组织与管理工具”
在 Java 中,包是用于组织类和接口的目录结构,核心作用是解决 “类名冲突” 和 “代码分类管理” 问题,是大型项目中代码结构化的基础。
1. 包的核心作用
- 避免类名冲突:不同包中可存在同名类(如
com.example.User
与com.test.User
),通过 “包名 + 类名” 的全限定名区分; - 分类管理代码:按功能 / 模块划分包(如
controller
存接口类、service
存业务逻辑类、dao
存数据访问类),便于维护; - 控制访问权限:配合
default
权限修饰符,实现 “同包可见,跨包不可见” 的访问控制。
2. 包的定义与命名规范
(1)定义语法
在.java
文件的第一行声明包,格式:
package 包名; // 必须在文件最顶部,且只能有一个package语句// 示例:声明类属于com.example.demo包
package com.example.demo;public class User { ... }
(2)命名规范(强制遵循,面试常考)
- 采用 “域名反转” 格式(全小写,避免与类名冲突),如公司项目常用
com.公司名.项目名.模块名
; - 示例:
- 阿里巴巴电商项目:
com.alibaba.ecommerce.order
(订单模块); - 个人学习项目:
com.study.demo.util
(工具类模块)。
- 阿里巴巴电商项目:
3. 包的导入(import):使用其他包的类
当需要使用非当前包的类时,需通过import
语句导入,避免每次写全限定名(如com.example.demo.User
)。
(1)三种导入方式
导入方式 | 语法示例 | 说明 |
---|---|---|
导入单个类 | import com.example.demo.User; | 仅导入指定包下的单个类,推荐优先使用 |
导入整个包的类 | import com.example.demo.*; | 导入指定包下的所有类(不包含子包) |
导入静态成员 | import static java.lang.Math.PI; | 导入类的静态变量 / 方法(无需类名调用) |
(2)示例:导入与使用
// 导入java.util包下的ArrayList类
import java.util.ArrayList;
// 导入java.lang.Math的静态成员PI
import static java.lang.Math.PI;public class PackageDemo {public static void main(String[] args) {// 使用导入的ArrayList(无需写全限定名)ArrayList<String> list = new ArrayList<>();// 使用导入的静态常量PI(无需写Math.PI)System.out.println("圆的周长:" + 2 * PI * 5); }
}
(3)注意:默认导入的包
Java 会自动导入java.lang
包(如String
、Math
、System
),无需手动写import
,例如:
// 无需import java.lang.String,直接使用
String str = "Hello";
// 无需import java.lang.System,直接使用
System.out.println(str);
4. 包的访问权限(与 default 修饰符联动)
包的访问控制依赖权限修饰符(后续详解),其中default
(默认权限,无修饰符)的规则是:
- 被
default
修饰的类、成员变量、方法,仅同包内的类可访问,跨包不可访问。
示例:
// 包1:com.example.a
package com.example.a;// default类(无修饰符):仅com.example.a包内可访问
class DefaultClass {// default方法:仅同包可访问void defaultMethod() {System.out.println("同包可见");}
}// 包2:com.example.b(跨包)
package com.example.b;
import com.example.a.DefaultClass; // 编译报错:DefaultClass是default类,跨包不可见
三、final 关键字:“不可修改” 的约束工具
final
是 Java 中用于 “限制修改” 的关键字,可修饰类、方法、变量,核心作用是 “锁定” 目标,避免被篡改,是封装和安全性的重要支撑。
1. final 修饰类:类不能被继承
- 语法:
final class 类名 { ... }
; - 作用:禁止该类有子类(即 “最终类”),确保类的逻辑不被修改;
- 典型场景:Java 内置的
String
、Integer
等类都是final
类,避免被继承后破坏不可变性; - 注意:final 类中的方法默认是 final 方法(无需显式声明),但成员变量可非 final。
示例:
// final类:不能被继承
final class FinalClass {public void show() {System.out.println("final类的方法");}
}// class SubClass extends FinalClass { ... } // 编译报错:无法继承final类
2. final 修饰方法:方法不能被重写
- 语法:
public final void 方法名() { ... }
; - 作用:禁止子类重写该方法,确保父类方法的逻辑不被篡改;
- 典型场景:父类中核心逻辑的方法(如计算规则、安全校验),不允许子类修改。
示例:
class Parent {// final方法:子类不能重写public final void finalMethod() {System.out.println("父类的final方法");}
}class Child extends Parent {// @Override // 编译报错:无法重写final方法// public void finalMethod() { ... }
}
3. final 修饰变量:变量不能被重新赋值
final 修饰变量时,变量成为 “常量”,只能赋值一次,赋值后不可修改。根据变量类型(局部变量、成员变量),初始化时机不同。
(1)final 修饰局部变量
- 必须在使用前初始化(可显式赋值或通过表达式赋值),赋值后不可修改;
- 示例:
public void testLocalFinal() {// 显式初始化final int num1 = 10;// num1 = 20; // 编译报错:final变量不能重新赋值// 先声明,后初始化(使用前必须赋值)final int num2;num2 = 20; // 合法// num2 = 30; // 编译报错 }
(2)final 修饰成员变量
- 必须在对象创建完成前初始化,有三种方式:
- 显式初始化(声明时赋值);
- 构造器初始化(每个构造器都必须赋值);
- 静态代码块初始化(仅静态 final 变量可用);
- 示例:
class FinalVarDemo {// 1. 显式初始化final String var1 = "显式初始化";// 2. 构造器初始化(声明时不赋值)final String var2;public FinalVarDemo() {var2 = "构造器初始化"; // 必须赋值,否则编译报错}// 3. 静态final变量:静态代码块初始化static final String STATIC_VAR;static {STATIC_VAR = "静态代码块初始化";} }
(3)final 修饰引用变量(面试高频)
- final 修饰引用变量时,引用地址不可修改,但引用指向的对象内容可修改;
- 示例:
public class FinalRefDemo {public static void main(String[] args) {// final引用变量:地址不可变final ArrayList<String> list = new ArrayList<>();// 合法:对象内容可修改list.add("a");list.add("b");// 非法:引用地址不可修改// list = new ArrayList<>(); // 编译报错} }
(4)static final:静态常量(面试重点)
- 同时用
static
和final
修饰的变量,称为 “静态常量”,具备两个特性:static
:属于类,仅初始化一次,内存中唯一;final
:值不可修改;
- 命名规范:全大写,单词间用下划线分隔(如
PI
、MAX_VALUE
); - 典型场景:Java 内置的
Math.PI
、Integer.MAX_VALUE
。
4. final 的核心面试考点
- final 类能否有子类? 不能,final 类禁止继承;
- final 方法能否被重载? 可以!重载是同一类中同名不同参数,与 final 无关(final 仅禁止重写);
- final 引用变量能否修改对象内容? 可以,final 仅锁定引用地址,不锁定对象内容;
- static final 变量和 final 变量的区别? 静态常量属于类(全局唯一),final 变量属于对象(每个对象一份,值不可变)。
四、权限修饰符:控制 “访问范围” 的工具
Java 提供 4 种权限修饰符,用于控制类、成员变量、成员方法的访问范围,是封装特性的核心实现(隐藏内部细节,暴露必要接口)。
1. 4 种权限修饰符的访问范围(面试必记)
按访问范围从大到小排序:public > protected > default(默认) > private
,具体范围如下表:
权限修饰符 | 本类可见 | 同包其他类可见 | 不同包子类可见 | 全局所有类可见 | 可修饰的目标 |
---|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ | 类、成员变量、成员方法 |
protected | ✅ | ✅ | ✅ | ❌ | 成员变量、成员方法(不能修饰类) |
default | ✅ | ✅ | ❌ | ❌ | 类、成员变量、成员方法(无修饰符) |
private | ✅ | ❌ | ❌ | ❌ | 成员变量、成员方法(不能修饰类) |
关键说明:
- default:无任何修饰符,仅同包可见;
- protected:跨包子类可见,但需通过 “子类对象” 或 “子类内部” 访问(不能通过 “父类对象” 跨包访问);
- 类的修饰限制:仅
public
和default
可修饰类(外部类),protected
和private
不能修饰外部类(可修饰内部类)。
2. 权限修饰符的典型应用场景
(1)private:封装成员变量
- 用
private
修饰成员变量,禁止外部直接访问,通过public
的getter/setter
方法暴露访问接口,实现数据安全; - 示例(JavaBean 的封装):
public class User {// private成员变量:外部不可直接访问private String name;// public getter:外部获取namepublic String getName() {return name;}// public setter:外部修改name,可加校验public void setName(String name) {if (name != null && !name.isEmpty()) {this.name = name;}} }
(2)protected:子类继承访问
- 用
protected
修饰父类的成员,允许不同包的子类访问,同时禁止非子类的全局访问; - 示例:
// 包1:com.example.parent package com.example.parent; public class Parent {// protected方法:不同包子类可访问protected void protectedMethod() {System.out.println("父类的protected方法");} }// 包2:com.example.child(不同包的子类) package com.example.child; import com.example.parent.Parent; public class Child extends Parent {public void test() {// 合法:子类内部访问父类的protected方法protectedMethod();} }
(3)public:暴露公共接口
- 用
public
修饰类或方法,作为对外的 “公共接口”(如工具类的静态方法、服务类的核心方法); - 示例:
java.util.Arrays
的sort()
方法(public static void sort (int [] a)),全局可调用。
(4)default:同包内复用
- 用 default 修饰类或成员,仅允许同包内的类访问,适合模块内部的代码复用(避免外部依赖);
- 示例:项目内部的工具类(如
com.example.util.InternalUtil
),仅同包的业务类使用。
3. 权限修饰符的面试高频问题
- private 成员能否被子类继承? 不能!private 成员仅父类内部可见,子类无法继承;
- protected 成员在不同包的非子类中能否访问? 不能!protected 仅允许同包或不同包的子类访问;
- 一个.java 文件中能否有多个 public 类? 不能!一个.java 文件只能有一个 public 类,且类名必须与文件名一致;
- 为什么 JavaBean 的成员变量用 private,而 getter/setter 用 public? 用 private 封装数据,避免外部直接修改;用 public 暴露安全的访问接口,可在 setter 中加数据校验(如年龄范围、非空判断)。
五、代码块:“初始化逻辑” 的载体
代码块是 Java 中用于 “集中执行初始化逻辑” 的代码片段,按定义位置和执行时机分为静态代码块、实例代码块、局部代码块,核心作用是简化初始化操作。
1. 静态代码块(Static Block)
(1)定义与执行时机
- 语法:在类中,用
static
修饰的代码块:static {// 初始化逻辑(仅执行一次) }
- 执行时机:类加载时执行(早于对象创建),且仅执行一次(无论创建多少对象,静态代码块只运行一次);
- 执行顺序:多个静态代码块按定义顺序执行。
(2)核心作用
- 初始化静态变量(尤其是复杂初始化,如读取配置文件、加载驱动);
- 执行类级别的预处理操作(如注册监听器、初始化工具类)。
(3)示例:
public class StaticBlockDemo {// 静态变量static String config;// 静态代码块1:初始化configstatic {System.out.println("静态代码块1执行");config = "数据库连接地址:jdbc:mysql://localhost:3306/test";}// 静态代码块2:按定义顺序执行static {System.out.println("静态代码块2执行");}public static void main(String[] args) {// 类加载时已执行静态代码块System.out.println(config);// 输出顺序:静态代码块1执行 → 静态代码块2执行 → 数据库连接地址:...}
}
2. 实例代码块(Instance Block)
(1)定义与执行时机
- 语法:在类中,无
static
修饰的代码块:{// 初始化逻辑(每次创建对象都执行) }
- 执行时机:对象创建时执行(在构造方法之前执行),每次创建对象都会执行一次;
- 执行顺序:多个实例代码块按定义顺序执行,执行完所有实例代码块后,再执行构造方法。
(2)核心作用
- 初始化实例变量(多个构造方法共享的初始化逻辑,避免重复代码);
- 示例:
public class InstanceBlockDemo {int num;// 实例代码块:初始化num{System.out.println("实例代码块执行");num = 10; // 所有构造方法创建的对象,num初始值都是10}// 构造方法1public InstanceBlockDemo() {System.out.println("无参构造执行,num=" + num);}// 构造方法2public InstanceBlockDemo(int num) {this.num = num; // 覆盖实例代码块的初始化值System.out.println("有参构造执行,num=" + num);}public static void main(String[] args) {new InstanceBlockDemo(); // 输出:实例代码块执行 → 无参构造执行,num=10new InstanceBlockDemo(20); // 输出:实例代码块执行 → 有参构造执行,num=20} }
3. 局部代码块(Local Block)
(1)定义与执行时机
- 语法:在方法内或代码块内的代码片段,用
{}
包裹:public void method() {// 局部代码块{int localVar = 10;System.out.println(localVar);}// System.out.println(localVar); // 编译报错:localVar作用域已结束 }
- 执行时机:随方法调用而执行,执行完后局部变量立即释放;
- 核心作用:控制局部变量的作用域(缩小变量生命周期,节省内存)。
4. 代码块的执行顺序
当一个类包含静态代码块、实例代码块、构造方法时,执行顺序为:
静态代码块(类加载时执行,仅一次) → 实例代码块(对象创建时执行,每次创建都执行) → 构造方法(实例代码块之后执行)
示例验证:
public class BlockOrderDemo {// 静态代码块static {System.out.println("1. 静态代码块");}// 实例代码块{System.out.println("2. 实例代码块");}// 构造方法public BlockOrderDemo() {System.out.println("3. 构造方法");}public static void main(String[] args) {System.out.println("创建第一个对象:");new BlockOrderDemo(); // 输出:1. 静态代码块 → 2. 实例代码块 → 3. 构造方法System.out.println("\n创建第二个对象:");new BlockOrderDemo(); // 输出:2. 实例代码块 → 3. 构造方法(静态代码块已执行过)}
}
六、总结与面试核心串联
- 包与权限修饰符的联动:包通过目录结构划分代码,权限修饰符通过
default
/protected
控制包内 / 跨包访问,共同实现代码的模块化和安全性; - final 与封装的联动:final 修饰类 / 方法 / 变量,锁定核心逻辑和数据,配合 private 封装,进一步增强代码的不可篡改性;
- 代码块与初始化的关系:静态代码块初始化类级资源,实例代码块初始化对象级资源,简化多构造方法的重复初始化逻辑;
- 常见问题:
- “一个类的初始化顺序是怎样的?”:静态代码块 → 实例代码块 → 构造方法;
- “final、static final、static 的区别?”:final 值不可变,static 属于类,static final 是类级常量(值不可变 + 全局唯一);
- “protected 权限在不同包的非子类中能否访问?”:不能,protected 仅允许同包或不同包的子类访问。
如果我的内容对你有帮助,请点赞,评论,收藏。接下来我将继续更新相关内容!