# Java教程 - 7 面向对象
# 7.2 封装
面向对象编程,是一种编程思想,简单的理解就是:先有模板,也就是类,根据模板去创建实例,也就是对象,然后使用对象来完成功能开发。
我们经常说面向过程编程和面向对象编程,面向过程编程关注的是实现功能的步骤,面向对象编程更关注的是谁来实现功能。
面向对象编程有3大特性:
- 封装
- 继承
- 多态
下面依次开始讲起。
在前面我们创建类,在类中定义了属性和方法,通过属性和方法来对现实世界的事物进行抽象的描述。
一个事物有很多的属性和方法,但是并不是所有属性和方法都需要开放出来。例如我们定义了一个手机类,我们可以使用手机打电话、拍照等,但是我们并不关心手机电压,驱动信息,也不关心内存的分配,CPU的调度等,虽然这些都属于手机的属性和行为。
我们可以将用户不关心的属性和方法封装并隐藏起来,只给类内部的方法调用,例如上网会用到4G模块,但是不是由用户来使用4G模块,而是由手机上网的功能开调用4G模块,只开放用户直接使用的信息和功能。
那么回过头来看,什么是封装?
它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息。如果要访问这些信息,需要通过该类所提供的方法来实现对内部信息的操作和访问。
# 1 私有属性和方法
那么怎么将不想暴露的变量和方法隐藏起来呢,就需要用到私有成员变量和私有成员方法。
定义私有成员和私有方法的方式是使用 private 关键字修饰变量和方法。
举个栗子:
class Phone {
public String producer = "华为"; // 手机品牌
private int voltage = 12; // 电压
/**
* 定义一个公共方法
*/
void call() {
System.out.println("打电话");
System.out.println("手机品牌: " + this.producer);
System.out.println("手机电压: " + this.voltage);
}
/**
* 定义一个私有方法
*/
private void getRunVoltage() {
System.out.println("当前电压:" + this.voltage);
}
}
public class ObjectTest {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.producer = "小米";
// phone.voltage = 24; // 私有变量,类外部无法访问
phone.call();
// phone.getRunVoltage(); // 私有方法,类外部无法访问
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
上面定义了私有属性 voltage
和私有方法 getRunVoltage
,使用 private
关键字修饰。私有属性和私有方法只能在类内部的方法中调用,在类的外部不能通过对象来调用。
如果不想暴露的属性和方法可以定义为私有成员。私有属性和私有方法只能在类内部的方法中调用,不能通过对象来调用。
执行结果:
打电话 手机品牌:华为 手机电压:12 打电话 手机品牌:小米 手机电压:12
# 2 getter和setter
我们前面说到,封装是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息。如果要访问这些信息,需要通过该类所提供的方法来实现对内部信息的操作和访问。
在面向对象编程思想中,我们一般会将所有的属性都设置为私有的,然后为每个属性提供两个对应的方法,分别用来获取和设置对应的属性,这两个方法称为 getter和setter 方法。
举个栗子:
Phone.java
class Phone {
private String producer = "华为"; // 手机品牌
private int voltage = 12; // 电压
public Phone(String producer, int voltage) {
this.producer = producer;
this.voltage = voltage;
}
public String getProducer() {
return producer;
}
public void setProducer(String producer) {
this.producer = producer;
}
public int getVoltage() {
return voltage;
}
public void setVoltage(int voltage) {
this.voltage = voltage;
}
// 其他方法....
}
/**
* 测试类
*/
public class MethodTest {
public static void main(String[] args) {
Phone phone = new Phone("华为", 12);
System.out.println(phone.getProducer()); // 通过getter方法获取变量,并不是直接访问变量
System.out.println(phone.getVoltage());
phone.setProducer("小米"); // 通过setter方法设置变量,并不是直接给变量赋值
phone.setVoltage(24);
System.out.println(phone.getProducer());
System.out.println(phone.getVoltage());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
在上面为属性 producer
和 voltage
定义了 get 和 set 方法,get
方法以 get
开头,后跟属性名,遵循方法命名规则。set
方法类似,get 方法有返回值,没有参数,set 方法接收参数,没有返回值。
通过 getter 和 setter 来访问私有属性和为私有属性赋值。这样做的话,严格控制了属性获取和设置的入口,如果通过 对象.属性
来修改,代码很多的时候完全会不知道在哪里修改了属性导致出现了问题。另外我们可以在setter方法中对属性的设置进行限制。
执行结果:
华为 12 小米 24
# 7.3 包
在实际的项目中,我们一般会将每个类单独定义到一个 .java
文件中,项目中会定义很多很多的类,如果这些类都放在一个目录下,会变得难管理,还可能存在同名的类。为了更好的组织规划和对控制类的访问权限,我们可以使用 包 来对类进行管理。
# 1 package包
包名是一个以小写字母组成的标识符,通常采用逆域名的方式来命名,例如逗比笔记的域名是 doubibiji.com
,那么包名就是com.doubibiji.项目名称
。这种命名方式有助于避免包名冲突。
例如,我们有一个学生管理系统的项目,那么包名可以定义为 com.doubibiji.stumanagement
。因为一个项目中,不同的类负责不同的功能,还可能进行分层的设计,所以在这个包名下,还可以再进行子包的划分,将不同功能的类规划到不同的子包中。
在 IDEA 中可以直接右键 New 来创建包。
包是有层级的,每个 .
表示一层,在硬盘上其实就是一层文件夹。
通过不同的包,来划分系统中的功能和设计结构,让系统更加清晰,例如下面的项目,将不同的类划分到不同的子包下:
这样项目的结构更为清晰,更好管理。
在每个类中,第一条代码就是 package
,表示当前的类所在的包名。每个包中不能存在同名的类。
在 JDK 中不同功能的类也是划分到不同的包中,下面是一些常用的 Java API 所在的包:
java.lang
:包含一些 Java 语言的核心类,如 String、Math、Integer、System 和 Thread,提供常用功能;java.net
:包含执行与网络相关的操作的类和接口。java.io
:包含能提供多种输入/输出功能的类。java.util
:包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。java.text
:包含了一些 java 格式化相关的类java.sql
:包含了 java 进行 JDBC 数据库编程的相关类/接口java.awt
:包含了构成抽象窗口工具集(abstractwindowtoolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
# 2 import引入
因为不同的类放在不同的包中,在使用其他包中的类或接口,就需要使用 import 进行引入。
在 Java中,如果没有显式地使用 import
语句,编译器会自动导入 java.lang
包。这个包包含了 Java 语言的核心类,其他包中的类都需要手动导入。
下面是常用用法:
# 导入单个类
举个栗子:
在上面的代码中,在 StudentDao.java 中使用 Student 类,就需要使用 import 进行引入:
package com.doubibiji.stumanagement.dao;
// 引入Student类
import com.doubibiji.stumanagement.pojo.Student;
public class StudentDao {
public static void main(String[] args) {
Student student = new Student();
}
}
2
3
4
5
6
7
8
9
10
# 导入包中所有的类
如果要导入某个包下所有的类,可以使用星号(*
),但不太推荐这种用法。
// 导入java.util包下所有的类
import java.util.*;
public class ImportTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
}
}
2
3
4
5
6
7
8
# 静态导入
静态导入允许导入一个类或接口中的静态成员(静态方法、静态字段和静态内部类)。
举个例子:
假设在一个类中有静态属性和静态方法:
package com.doubibiji.utils;
public class Utils {
// 静态属性
public static final int MAX_VALUE = 99;
/**
* 静态方法
*/
public static int max(int a, int b) {
return Math.max(a, b);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
那么在其他的包中,可以使用 import static
单独引入 Utils 中的静态属性和方法:
// 引入静态变量
import static com.doubibiji.utils.Utils.MAX_VALUE;
// 引入静态方法
import static com.doubibiji.utils.Utils.max;
public class StudentDao {
public static void main(String[] args) {
System.out.println(MAX_VALUE);
System.out.println(max(3, 5));
}
}
2
3
4
5
6
7
8
9
10
11
12
# 7.4 权限修饰符
我们前面已经使用了权限修饰符 public
和 private
。
在 Java 中权限修饰符一种有四种:public
、 protected
、 缺省(就是没有修饰符)
、 private
。
它们可以用来修饰属性、方法、构造方法、内部类(类内部的类),如果修饰普通类的话,只能使用:缺省、public
他们对访问权限的限制如下:
修饰符 | 类内部 | 同一个包 | 不通包的子类中 | 同一个工程(项目) |
---|---|---|---|---|
private | 可访问 | |||
缺省 | 可访问 | 可访问 | ||
protected | 可访问 | 可访问 | 可访问 | |
public | 可访问 | 可访问 | 可访问 | 可访问 |
举个栗子:
在 com.doubibiji.hellojava.pojo
包下有 Student
类:
package com.doubibiji.hellojava.pojo;
public class Student {
private String sid;
String name;
public int age;
}
2
3
4
5
6
7
8
9
那么同样在 com.doubibiji.hellojava.pojo
包下有 StudentTest
类中:
package com.doubibiji.hellojava.pojo;
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
// System.out.println(student.sid); // private无法访问
System.out.println(student.name); // 缺省,同一个包下可以访问
System.out.println(student.age); // public,同一个包下可以访问
}
}
2
3
4
5
6
7
8
9
10
11
如果 StudentTest
类在不同的包下,例如在 com.doubibiji.hellojava.utils
包下:
package com.doubibiji.hellojava.utils;
import com.doubibiji.hellojava.pojo.Student;
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
// System.out.println(student.sid); // private无法访问
// System.out.println(student.name); // 缺省,不通包下无法可以访问
System.out.println(student.age); // public,同一个包下可以访问
}
}
2
3
4
5
6
7
8
9
10
11
12
protected
的访问权限是同一个包下可以访问,子类中也可以访问。子类在讲 继承 的时候再讲。