1.概述

学习了前面的面向对象的基础概念后,我们了解要以面向对象的思维来思考一个事物。面向对象有三个基本属性:封装、继承、多态。今天我们就重点讲解其中一个封装的概念。

抛开”封装“这个名词,我们一步一步来思考下面的问题:

2. 问题

我们定义一个学生类,它有姓名、年龄属性,

class Student{
String name;
int age;
}

为了看到这两个属性的值。我们再定义一个方法,把这两个属性显示出来。

public void show(){
System.out.println("姓名"+name);
System.out.println("年龄"+age);
}

我们再定义一个测试类StudentDemo,创建一个学生对象来调用上面的show方法,发现我们也可以对这个学生对象的属性进行赋值和修改。

public class StudentDemo {
public static void main(String[] args) {
// 创建学生对象
Student s = new Student();
s.show();
System.out.println("---------");
// 给成员变量赋值
s.name = "小雨";
s.age = 27;
s.show();
System.out.println("---------");
// 给年龄赋值
s.age = -27; // 这个数据时不合理的
s.show();
System.out.println("--------");
}
}

我们发现,通过对象给成员变量直接赋值,居然可赋一些非法的值,比如给年龄设置一个负数,显然这是不合理的。

我们想应该是这样子比较好,在赋值之前,先定义一个校验函数,对数据进行判断。

那么这个校验函数到底在哪里写做比较合适呢?

StudentDemo类是一个测试类,测试类一般只创建对象,调用方法。它只负责测试和调用,属性的判断应该放进原始的Student类中比较好,所以这个判断应该定义在Student类中。而我们在成员变量的位置可不可以进行数据判断呢?

显然是不可以的,因为做数据校验,必须要依靠一些逻辑语句。逻辑语句是应该定义在方法中的,所以,我们最终决定在Student类中写一个方法进行校验。

// 写一个方法对数据进行校验
/*
返回这类型:void
参数列表:int a
*/
public void setAge(int a){
if (a<0 || age>120){ // 一个人的年龄应该在0~120之间
System.out.println("你给的年龄有问题");
}else{
age = a;
}
}

按照上面我们的分析,我们给出这个方法进行校验输入的值。但是呢?此时,系统仍然可以通过对象来修改属性。还是可以直接赋值。这样我们定义的校验方法就起不到作用,我们应该要求你必须使用我的校验方法,而不能直接调用成员变量进行赋值。

怎样才能强制要求不能直接使用成员变量呢?

针对这种情况,Java就提供了一个用来修饰成员变量和成员方法的关键字private,表示私有的意思。

表示被private修饰的成员只能在本类中访问。

其实我们将到现在,讲的就是一个封装的思想,它表示隐藏对象的属性和实现细节,仅对外提供公共访问的方式。

最终版的代码如下:

class Student{
String name;
private int age;
// 写一个方法对数据进行校验
/*
返回这类型:void
参数列表:int a
*/
public void setAge(int a){
if (a<0 || age>120){
System.out.println("你给的年龄有问题");
}else{
age = a;
}
}
// show()方法,显示所有成员变量的值
public void show(){
System.out.println("姓名"+name);
System.out.println("年龄"+age);
}
}
public class App {
public static void main(String[] args) {
// 创建学生对象
Student s = new Student();
s.show();
System.out.println("---------");
// 给成员变量赋值
s.name = "swift";
// s.age = 27;
s.show();
System.out.println("---------");
// 给年龄赋值
// s.age = -27; // 这个数据时不合理的
s.setAge(-27);
s.show();
System.out.println("--------");
}
}

小结:

封装将不需要对外提供的内容都隐藏起来,把属性隐藏,提供公共方法对其访问。好处就是:隐藏实现细节,提供公共的访问方式;提高了代码的复用性;提高了安全性。