:包、封装、static 关键字与代码块详解)
Java SE 学习笔记 · 面向对象基础篇二前言在上一篇文章中我们学习了 Java 面向对象的基本概念包括类与对象、成员变量、成员方法、this关键字以及构造方法。但是随着项目规模逐渐增大仅仅会创建对象还远远不够。我们还需要解决以下问题如何将不同功能的类分类管理如何限制外部代码对对象内部数据的访问多个对象需要共享相同的数据怎么办静态方法与普通成员方法有什么区别创建对象时静态代码块、实例代码块和构造方法按照什么顺序执行本文结合代码示例系统整理这些知识。一、Java 包1.1 什么是包在java中包是一种组织和管理类接口(这个可以先不用理解后面再说)等类型的机制简单理解为:文件夹例如我们做了一个项目JavaProject ├── Student.java ├── Teacher.java ├── Computer.java └── Main.java可以看到这个项目包含了很多类但是如果类多了起来我们这个项目就会变得难以管理这时候就需要包对这些负责不同功能的类进行分类管理比如src └── com └── example ├── student │ └── Student.java ├── computer │ └── Computer.java └── main └── Main.java那么说了那么多包有什么作用呢包主要有三个作用1:组织和管理不同功能的类。★★★2:避免不同包之间的类名冲突。3:配合访问修饰符实现访问权限控制。1.2 package关键字例如package com.example.student; public class Student { String name; int age; }package声明必须位于 Java 源文件的前部在普通import和类型声明之前。包名通常使用小写字母常见命名方式为反向域名。例如package com.example.demo;对应的文件目录为com/example/demo/1.3 import关键字当我们需要使用其他包中的类时可以通过import导入。例如import java.util.Date; public class Main { public static void main(String[] args) { Date date new Date(); System.out.println(date.getTime()); } }当然这里特殊介绍一种import java.util.*;这样子可以访问这个包中所有的类但是实际项目我们更应该明确导入需要使用的类不仅是为了更加清晰更是为了减少类名冲突二 java的封装2.1 什么是封装封装是 Java 面向对象编程的三大基本特性之一。另外两个分别是继承和多态后面再说。封装的核心思想可以概括为将对象的数据和操作数据的方法组织在一起隐藏内部实现细节只提供必要的对外访问接口。可以用电脑来理解。对于电脑使用者而言只需要知道1:如何开机2:如何关机3:如何使用键盘4:如何通过屏幕查看信息至于 CPU 内部如何工作、主板线路如何设计普通使用者并不需要了解。在 Java 编程中也可以采用类似的设计思想。这就是封装2.2 为什么需要封装假设定义一个学生类class Student { public String name; public int age; }外部可以直接修改成员变量Student s new Student(); s.name 张三; s.age -100;虽然可以但是这个年龄显然不符合实际要求所以我们更希望限制外部代码对成员变量的直接修改2.3 private关键字使用private修饰成员变量class Student { private String name; private int age; }此时在其他类中不能直接访问这些字段Student s new Student(); s.age 18; // 编译错误因为age是私有成员只可以在student这个类内部中直接访问不可以在其他地方访问需要注意private限制的是类级别的访问而不是只能由某一个对象访问。同一个类中的方法可以通过其他同类对象的引用访问其私有字段。2.4 getter 与 setter 方法为了让外部能够合理访问属性我们可以提供公共方法。class Student { private String name; private int age; public String getName() { return this.name; } public void setName(String name) { this.name name; } public int getAge() { return this.age; } public void setAge(int age) { this.age age; } }在其他类中public class Main { public static void main(String[] args) { Student s new Student(); s.setName(张三); s.setAge(18); System.out.println(s.getName()); System.out.println(s.getAge()); } }输出张三 18这里有两个重要概念getter读取对象属性。setter修改对象属性。2.5 setter 的真正价值或许有人问这样是不是脱裤子放屁--多此一举其实并不是通过setter调用更重要的是我们可以在公共方法中控制数据的合法性。例如public void setAge(int age) { if (age 0 age 150) { this.age age; } else { System.out.println(年龄不合法); } }这样可以避免随意修改对象状态。对于某些不允许外部修改的数据甚至可以只提供 getter而不提供 setter。2.6 封装的优点优点说明降低耦合性外部不需要了解类的内部实现降低复杂性只暴露必要的操作接口提高安全性防止对象状态被随意修改提高可维护性内部实现改变时可以尽量保持公共接口稳定需要注意封装的主要目的并不是提高程序运行速度而是改善代码设计和维护性。三 java访问修饰符Java 提供四种常见的成员访问权限1:public2:protected3:默认访问权限不写修饰符4:private下面我们来列表介绍区别3.1 访问权限对比访问范围publicprotected默认权限private同一个类✓✓✓✓同包其他类✓✓✓✗不同包的子类✓有条件✗✗不同包的普通类✓✗✗✗这里我们只了解public默认权限private即可我们可以类比在这里类指自己包指家人public相当于你对外界公开的信息比如你的性别等等默认权限就是指你和你的家人都知道的信息private就是指只有你自己知道的信息3.2 默认访问权限例如class Student { String name; }name没有显式指定访问修饰符。此时它使用默认访问权限也叫包访问权限package-private。同包中的其他类可以访问其他包中的普通类不能直接访问。3.3 跨包访问示例看了那么多理论性的东西下面我们给出一个简单的例子方便大家更好的理解假设项目结构如下com.example ├── model │ └── Computer.java └── app └── Main.javaComputer.javapackage com.example.model; public class Computer { private String cpu; public String screen; String brand; public Computer(String cpu, String screen, String brand) { this.cpu cpu; this.screen screen; this.brand brand; } public String getCpu() { return cpu; } }Main.javapackage com.example.app; import com.example.model.Computer; public class Main { public static void main(String[] args) { Computer computer new Computer(M4, Retina, Apple); System.out.println(computer.screen); // System.out.println(computer.cpu); // System.out.println(computer.brand); System.out.println(computer.getCpu()); } }问题Q1为什么 Main.java 要使用 importQ2为什么 computer.screen 可以访问而 computer.cpu 不能Q3为什么 computer.brand 不能访问Q4为什么 private 修饰的 cpu 可以通过 getCpu() 获取Q5new Computer(M4, Retina, Apple) 有什么作用大家可以尝试做做这个5个问题答案会在文末提出谢谢四static关键字4.1 static 是什么static可以修饰成员变量和成员方法。被static修饰的成员称为静态成员也叫类成员。静态成员属于类而不是某一个具体对象。4.2 普通成员变量和静态成员变量假设定义一个学生类class Student { String name; static String classRoom 1-101; }假如我们要创建多个对象Student s1 new Student(); Student s2 new Student(); Student s3 new Student(); s1.name 张三; s2.name 李四; s3.name 王五;此时每个对象有自己的name但是classRoom是被整个Student类共享4.3 静态变量的访问可以通过类名直接访问System.out.println(Student.classRoom);输出1-101如果修改成1-114514那么将会输出 1-114514.4.4 static 的实际用途一个常见用途是统计对象创建次数。class Student { private static int count 0; public Student() { count; } public static int getCount() { return count; } }调用public class Main { public static void main(String[] args) { Student s1 new Student(); Student s2 new Student(); Student s3 new Student(); System.out.println(Student.getCount()); } }输出3通过静态变量我们可以记录这个类累计创建了多少个对象。需要注意这段示例只统计构造方法执行的次数不等于当前仍然存活的对象数量。五static静态方法5.1 静态方法的定义public static 返回值类型 方法名() { // 方法体 }例如class MyMath { public static int max(int a, int b) { return a b ? a : b; } }使用时int result MyMath.max(10, 20); System.out.println(result);静态方法通常适合实现不需要依赖某个具体对象状态的功能。注意静态方法不可以直接访问非静态成员5.2静态方法能否使用对象class Student { String name; public static void show() { Student student new Student(); student.name 张三; System.out.println(student.name); } }这是合法的。因为静态方法虽然没有自己的this但可以创建对象并通过明确的对象引用访问实例成员。因此应该准确理解为静态方法不能直接访问实例成员但可以通过对象引用访问允许访问的实例成员。同时静态方法一样不可以用this注意5.3 静态成员和实例成员对比比较静态成员实例成员所属类对象推荐访问方式类名.成员对象.成员是否由对象分别拥有否是是否需要当前对象不需要需要静态方法中直接访问可以访问可见的静态成员不能直接访问使用 this静态方法中不允许实例方法中允许六 代码块6.1 什么是代码块我们称{}包围起来的一段代码称为代码块可以分为以下几类1:普通代码块2:实例代码块3:静态代码块4:同步代码块在本篇文章只介绍前3种6.2 普通代码块普通代码块可以定义在方法内部例如public class Main { public static void main(String[] args) { { int x 10; System.out.println(x); } int x 100; System.out.println(x); } }输出10 100可以看见普通代码块可以形成一个局部作用域在上面的例子中第一个x的作用域随着内部代码块结束而结束因此后面可以重新声明x。6.3 实例代码块例如class Student { String name; { name 张三; System.out.println(实例代码块执行); } public Student() { System.out.println(构造方法执行); } }创建对象Student s new Student();输出实例代码块执行 构造方法执行他有一个重要特点就是每创建一个对象实例代码块就会执行一次。6.4 静态代码块静态代码块通过static定义class Student { static String school; static { school 机密大坝; System.out.println(静态代码块执行); } }静态代码块通常用于初始化静态成员。他只会执行一次6.5 静态代码块与实例代码块的区别比较静态代码块实例代码块定义形式static {}{}执行时机类初始化时每次对象初始化时执行次数每次类初始化一次每创建一个对象执行一次是否可以使用 this不可以可以常见用途初始化静态数据初始化实例数据下面介绍一下这三种代码块的执行顺序大家可以把这一段代码运行一下class Student { static { System.out.println(1. 静态代码块); } { System.out.println(2. 实例代码块); } public Student() { System.out.println(3. 构造方法); } } public class Main { public static void main(String[] args) { Student s1 new Student(); System.out.println(); Student s2 new Student(); } }输出的是1. 静态代码块 2. 实例代码块 3. 构造方法 2. 实例代码块 3. 构造方法可以看见静态的代码块只会执行一次第二次创建新的对象时不会再执行通常三种代码块的执行顺序是静态字段显式初始化 / 静态代码块 ↓ 实例字段显式初始化 / 实例代码块 ↓ 构造方法主体最后的总结本章主要学习了 Java 类与对象的进一步知识。其中最重要的内容可以归纳为五点1. package 与 import 用于组织和使用不同包中的类。合理划分包能够提升项目结构的清晰度。2. 封装的重点是隐藏实现细节、控制对外访问。private 与公共方法只是实现封装的常见手段。3. 访问权限决定不同位置的代码能否访问成员。需要理解 public、private、默认访问权限和 protected 的区别先不用理解protected。4. static 成员属于类而不是某个具体对象。静态变量适合保存共享数据静态方法适合实现不依赖实例状态的功能。5. 静态代码块、实例代码块和构造方法具有不同的初始化时机。理解它们的执行顺序有助于阅读代码和排查初始化问题。我们下节再见2026.10.26 11:09