封装就是将类的状态信息(属性)和行为(方法)包装在一个类中,并对类的属性设置访问权限,隐藏类的内部实现细节,只通过特定的方法来访问和修改这些属性。
通过封装,可以提高代码的安全性、可维护性和可扩展性。
Java包的使用
包(package)是一种用于组织和管理类、接口及其他程序元素的机制。
包的作用至少有以下几点:
避免命名冲突:在庞大的项目架构中,同名的类可能大量出现。我们通过将这些类分散在不同的包中,能够有效规避命名上的冲突。
提升组织性和管理效率:包的使用有助于将相关的类、接口及其他程序组件整合在一起,从而增强代码的可读性和可维护性。
精细化访问控制:包机制允许我们对类、接口及其他程序组件的访问权限进行精细控制。将特定的类置于特定的包内,可以限制其访问范围,确保只有授权的类能够访问。
1) 包声明
在 Java 源文件的起始部分,我们可以通过使用关键字 package 来声明源文件内类的归属包。
例如,语句“package com.chapter7.project;”表明源文件中的类是属于“com.chapter7. project”包的。
2) 包导入
当需要在一个类中引用另一个包的类时,可以借助关键字 import 来导入所需的类。
例如,语句“import com.chapter7.project.ClassTest;”实现了对“com.chapter7.project”包中 ClassTest 类的导入。
此外,我们还可以使用通配符导入一个包内的所有类,如“import com.chapter7. project*;”。
3) 自定义包
在文件系统中构建一个与包名相对应的目录结构。例如,如果要创建一个名为“com.company.myproject”的包,就应该在项目的根目录下创建一个 com 目录,接着在 com 目录下创建 company 目录,最后在 company 目录下创建 myproject 目录。
将 Java 源文件放置在相应的包目录内,并在源文件的起始处声明包名。
在其他 Java 源文件中,可以通过导入自定义包内的类来使用它们。只要自定义包位于项目的类路径中,Java 编译器和运行环境便能够定位并使用这些包中的类。
Java访问修饰符的权限
在不影响正常功能执行的前提下,应该尽可能使每个类或类中的成员可访问性最小。在 Java 编程语言中,控制成员(属性、方法、嵌套类)可访问性的关键字有 private(私有的)、default(默认)、protected(受保护的)及 public(公有的),如下表所示。
表:访问修饰符的访问权限
访问修饰符
当前类
同一包内
子孙类(同一包)
子孙类(不同包)
其他包
public
√
√
√
√
√
protected
√
√
√
√/X
X
default
√
√
√
X
X
private
√
X
X
X
X
下面按照可访问性范围递减进行说明:
public:对所有类可见。使用对象:类、接口、变量、方法;
protected:对同一包内的类和所有子类可见。使用对象:变量、方法。注意:不能使用修饰类(外部类);
default(默认,什么也不写):在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法;
private:在同一类内可见。使用对象:变量、方法。注意:不能修饰类(外部类)。
Java封装的实现
私有化(private)成员属性会使当前类无法被其他类友好使用,公有化(public)成员属性就失去了封装的意义。
如何保证类成员既不暴露数据域又能被其他类友好访问呢?我们通常将类设计为包含私有域属性(private)和公有访问方法(getter),如果需要对私有的属性进行初始化,就可以提供公有的方法(setter)。
【实例】创建一个 Person 类,属性分别是 age 和 name,将属性进行封装,并且进行初始化,在控制台输出结果,代码如下:
public class Person {
/* 定义全局属性,将属性进行封装 */
private int age;
private String name;
/* 将属性初始化,并提供 setter 方法 */
public void setAge(int age) {
this.age = age;
if (age > 20) {
System.out.println("大于 20 岁");
} else {
this.age = 30;
}
}
public void setName(String name) {
this.name = name;
}
/* 获取到属性的值,提供 getter 方法 */
public int getAge() {
return age;
}
public String getName() {
return name;
}
/* 测试入口 */
public static void main(String[] args) {
Person person = new Person();
person.setAge(20);
person.setName("小明");
System.out.println("name: " + person.getName());
System.out.println("age: " + person.getAge());
}
}
程序的运行结果为:
name: 小明
age: 30
在上述代码中,我们可以看出对属性进行封装后,外部代码无法直接访问这些属性。通过提供 getter() 和 setter() 方法,外部代码可以获取和设置这些属性的值,同时在 setter() 方法中可以添加验证逻辑,确保这些属性的值符合要求。
被私有化的方法可以访问,但只能在本类中调用,在其他类中无法调用。
Java 封装的好处有以下几点:
提高代码的安全性:通过限制对属性的直接访问,可以防止外部代码意外修改或破坏类的内部状态;
提高代码的可维护性:封装隐藏了类的内部实现细节,使外部代码只需关注类提供的公共接口;
内部的实现方式:如果需要修改类的内部实现方式,那么只需修改类的内部代码,而不会影响外部代码的使用;
提高代码的可扩展性:可以在不影响外部代码的情况下,向类中添加新的属性和方法,或者修改现有属性和方法的实现。