黑马程序员——面向对象3:面向对象的特点之——封装(Encapsulation)
2014-09-28 21:45
309 查看
------- android培训、java培训、期待与您交流! ---------
1.封装的定义
封装:指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
对上述定义的理解:实际开发过程中,当我们使用某个对象的某个功能时,我们并不关心,这个对象在内部是如何实现这个功能的,换句话说,我们只需要指挥对象(或者面向对象)去完成我们的既定需求即可。这可以理解为“黑箱原理”。
2.类比举例
我们还是可以举前面“买电脑”的例子。当我们委托“懂电脑”的朋友进行挑选和砍价时,他是具体怎么做的我们不需要知道,换句话说,他进行这两个“操作”的过程,对于我是“隐藏”的。
再举一个“开车”的例子。在我们启动汽车以前,有没有必要去了解汽车内部运转的原理呢?如果真要弄懂,恐怕这辆车报废以前是开不上了。实际上,我们不需要了解汽车的运转原理,只需要学会如何开车就可以了。
也可以以“软件开发”为例。公司招聘软件开发工程师,进行软件开发,老板不需要知道这些工程师是如何写代码,也不需要知道,这些代码是如何运作的,他只需要“面向”这些工程师,并指挥他们,完成既定需求即可。
3.封装的优点
(1) 将变化隔离。即使某个类内部方法的实现原理发生改变(Java版本的升级),也并不影响我们调用这些类对应的对象,因为,这些方法的实现原理对于我们是不可见的,那么实现原理的改变同样是不可见的,我只管去调用就可以。比如,电脑发生故障拿到售后去维修,售后人员更换了某个电脑配件,这个更换动作,对于我们消费者而言是无所谓的,只要电脑能正常工作,满足我们的需求即可。
(2) 便于使用。不必了解对象内部方法的实现原理,就可以使用他们。
(3) 提高了重用性。每一次,需要使用某个对象的方法时,没有必要每次都重新定义(相比于面向过程)。只需要创建该对象,并调用方法即可。
(4) 提高安全性。由于,用户并不了解,对象内部方法的实现原理,也就不可能手动去进行修改,从而避免了人为修改而造成的代码的运行的不稳定或错误。
4.封装的原则
(1) 将不需要对外提供的内容都隐藏起来。
(2) 将成员变量(或称为属性)都隐藏起来,只对外提供对公共方法的访问方法。
注意:我们既不能将对象的所有成员全部暴露,对外也不能一点也不提供访问的方式。就好比台式机主机。如果我们把机箱去掉,只是把各个配件组合在一起,并通过“短路”的方法启动电脑,也是可以使用的,但是麻烦且不安全;相反,如果我们使用完全封闭的机箱壳,用户也就无法使用电脑了。
参考之前学习的进制转换小程序(省略了方法的具体内容):
代码1:
小知识点1:
其实,每个方法本身就是最小单位的封装体。对于调用这个方法的用户来说,同样不需要知道方法的内部实现原理,只需要告诉用户,方法的返回值类型,方法名称,以及所需要传入的名义参数类型即可。以此类推,类就是更高一级的封装体。这个层次关系体现如下:
方法--类--包--框架
关于包将在后面的博客中介绍;而框架是一种高集成度的开发工具,可以帮助工程师完成一些组件的开发,开发人员不再需要自己搭建,直接拿来用即可。
5.如何实现封装
上面说了这么多,我们来看一下具体如何实现封装。
代码2:
这虽然在程序中是允许的,但在现实生活中是不可能的。经过思考,我们发现产生这种问题的原因是因为我们可以直接访问到Person对象的成员变量(属性)age。因此,为了避免类似问题的发生,解决方法是将类的成员变量私有化,在类型修饰符前加上private访问权限修饰符,代码如下:
知识点1:
private,表示私有,是访问权限修饰符的一种(访问权限最小),用于修饰类中的成员(包括成员变量和成员方法)。注意:被private修饰的成员,只能在本类内部访问到。如果从某个类的外部去访问类内的私有成员,编译器就会报错。
既然被private修饰的成员不能被直接访问,并且如果这个成员有被访问的需求,那就应该对外提供某种方法,以实现对该成员的调用,例如在Person类的内部添加如下方法:
知识点2:
对于一个成员变量,只能对其进行两个动作——赋值和获取。那么对于这两种方法,有一个命名规范,对于赋值方法命名为setXxx,对于获取方法命名为getXxx,其中Xxx表示属性名。对于方法名,第一个单词的首字母要小写,从第二个单词开始首字母大写。注意,只要这个属性需要在类外部调用,就一定要使其私有化,并提供对应的赋值和获取方法。需要记住的是,set方法返回值类型一定是void,并且需要传入参数;get方法的返回值类型与对应的成员变量一致,并且方法的参数列表为空。
在这里,要注意区别一个概念。通过private对成员进行私有,仅仅是封装的一种表现形式,千万不能说封装就是私有。实际上,只要类的成员不能被你访问到,那么相对你而言这个成员就是被封装的。
虽然,我们将类的成员变量进行了私有化,但是,我们还是可以通过set方法将负数赋给age属性。那么属性私有化,并对外提供访问方法到底有什么用呢?实际上,我们可以通过在set方法中添加逻辑判断语句来控制传入的参数,提高代码的健壮性。将前文代码进行修改以后最终体现为:
代码3:
下面我们来描述一下,这一段代码在内存中的运行过程。首先还是要从主函数开始运行,在栈内存中为主函数分配一个空间,在其中创建一个Person类类型变量p。再在堆内存中为Person对象开辟空间,同时为其内部的私有成员变量age赋予默认初始化值0,并为这块空间其分配一个内存地址值。将这个地址值赋给栈内存的变量p,p就指向了堆内存中的Person对象。接着调用变量p所指向的Person对象的setAge方法,并将整型数20作为参数传入。此时,又在栈内存为setAge方法开辟一块空间,其内部有个整型变量newAge,并被赋值为20。其中,setAge方法内部的赋值动作,是将newAge的值赋给调用这个方法的对象的成员变量(属性)——age,而不是其他变量。然后调用变量p所指向的Person对象的speak方法,那么就又在栈内存中为speak方法开辟一块空间,并将堆内存中Person对象的age通过输出语句进行输出。这里,speak方法的age变量,同样是调用speak方法的对象所属的属性。那么为什么调用这两个方法时涉及的age变量都是堆内存中Person对象的呢?可以这样理解:既然这两个方法都是由Person对象调用,那么访问到的属性当然就是属于该对象的。
1.封装的定义
封装:指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
对上述定义的理解:实际开发过程中,当我们使用某个对象的某个功能时,我们并不关心,这个对象在内部是如何实现这个功能的,换句话说,我们只需要指挥对象(或者面向对象)去完成我们的既定需求即可。这可以理解为“黑箱原理”。
2.类比举例
我们还是可以举前面“买电脑”的例子。当我们委托“懂电脑”的朋友进行挑选和砍价时,他是具体怎么做的我们不需要知道,换句话说,他进行这两个“操作”的过程,对于我是“隐藏”的。
再举一个“开车”的例子。在我们启动汽车以前,有没有必要去了解汽车内部运转的原理呢?如果真要弄懂,恐怕这辆车报废以前是开不上了。实际上,我们不需要了解汽车的运转原理,只需要学会如何开车就可以了。
也可以以“软件开发”为例。公司招聘软件开发工程师,进行软件开发,老板不需要知道这些工程师是如何写代码,也不需要知道,这些代码是如何运作的,他只需要“面向”这些工程师,并指挥他们,完成既定需求即可。
3.封装的优点
(1) 将变化隔离。即使某个类内部方法的实现原理发生改变(Java版本的升级),也并不影响我们调用这些类对应的对象,因为,这些方法的实现原理对于我们是不可见的,那么实现原理的改变同样是不可见的,我只管去调用就可以。比如,电脑发生故障拿到售后去维修,售后人员更换了某个电脑配件,这个更换动作,对于我们消费者而言是无所谓的,只要电脑能正常工作,满足我们的需求即可。
(2) 便于使用。不必了解对象内部方法的实现原理,就可以使用他们。
(3) 提高了重用性。每一次,需要使用某个对象的方法时,没有必要每次都重新定义(相比于面向过程)。只需要创建该对象,并调用方法即可。
(4) 提高安全性。由于,用户并不了解,对象内部方法的实现原理,也就不可能手动去进行修改,从而避免了人为修改而造成的代码的运行的不稳定或错误。
4.封装的原则
(1) 将不需要对外提供的内容都隐藏起来。
(2) 将成员变量(或称为属性)都隐藏起来,只对外提供对公共方法的访问方法。
注意:我们既不能将对象的所有成员全部暴露,对外也不能一点也不提供访问的方式。就好比台式机主机。如果我们把机箱去掉,只是把各个配件组合在一起,并通过“短路”的方法启动电脑,也是可以使用的,但是麻烦且不安全;相反,如果我们使用完全封闭的机箱壳,用户也就无法使用电脑了。
参考之前学习的进制转换小程序(省略了方法的具体内容):
代码1:
class Demo { public void toBinary(int num){} public void toOctal(intnum){} public void toHex(intnum){} private void trans(intnum, int index, int offset){} }上例中,方法trans其实是没有必要对外暴露的,因为,用户真正需要用到的方法只是获取进制转换后的数字,而其中的“算法”部分没有必要了解,并且出于安全考虑,也应该“隐藏”起来,避免人为修改造成的程序错误。这里修饰符private的意思是“私有”,也就是说这个方法只有这类的内部可以访问到(关于权限修饰符的详细讲解,参考后面的博客)。
小知识点1:
其实,每个方法本身就是最小单位的封装体。对于调用这个方法的用户来说,同样不需要知道方法的内部实现原理,只需要告诉用户,方法的返回值类型,方法名称,以及所需要传入的名义参数类型即可。以此类推,类就是更高一级的封装体。这个层次关系体现如下:
方法--类--包--框架
关于包将在后面的博客中介绍;而框架是一种高集成度的开发工具,可以帮助工程师完成一些组件的开发,开发人员不再需要自己搭建,直接拿来用即可。
5.如何实现封装
上面说了这么多,我们来看一下具体如何实现封装。
代码2:
//定义Person类 class Person { int age; void speak() { System.out.println(“age= ”+age); } } class PersonDemo { public static void main(String[] args) { //创建一个Person对象 Person p = new Perons(); //对Person的成员变量(属性)age赋值 p.age= 20; p.speak(); } }注意:上述代码中,对Person对象的age属性进行赋值时,我们可以对其赋值负数,例如:
p.age = -20;
这虽然在程序中是允许的,但在现实生活中是不可能的。经过思考,我们发现产生这种问题的原因是因为我们可以直接访问到Person对象的成员变量(属性)age。因此,为了避免类似问题的发生,解决方法是将类的成员变量私有化,在类型修饰符前加上private访问权限修饰符,代码如下:
private int age;
知识点1:
private,表示私有,是访问权限修饰符的一种(访问权限最小),用于修饰类中的成员(包括成员变量和成员方法)。注意:被private修饰的成员,只能在本类内部访问到。如果从某个类的外部去访问类内的私有成员,编译器就会报错。
既然被private修饰的成员不能被直接访问,并且如果这个成员有被访问的需求,那就应该对外提供某种方法,以实现对该成员的调用,例如在Person类的内部添加如下方法:
public void setAge(int newAge) { age = newAge; } public int getAge() { return age; }上述两个方法,分别实现对age这个属性的赋值和获取动作。
知识点2:
对于一个成员变量,只能对其进行两个动作——赋值和获取。那么对于这两种方法,有一个命名规范,对于赋值方法命名为setXxx,对于获取方法命名为getXxx,其中Xxx表示属性名。对于方法名,第一个单词的首字母要小写,从第二个单词开始首字母大写。注意,只要这个属性需要在类外部调用,就一定要使其私有化,并提供对应的赋值和获取方法。需要记住的是,set方法返回值类型一定是void,并且需要传入参数;get方法的返回值类型与对应的成员变量一致,并且方法的参数列表为空。
在这里,要注意区别一个概念。通过private对成员进行私有,仅仅是封装的一种表现形式,千万不能说封装就是私有。实际上,只要类的成员不能被你访问到,那么相对你而言这个成员就是被封装的。
虽然,我们将类的成员变量进行了私有化,但是,我们还是可以通过set方法将负数赋给age属性。那么属性私有化,并对外提供访问方法到底有什么用呢?实际上,我们可以通过在set方法中添加逻辑判断语句来控制传入的参数,提高代码的健壮性。将前文代码进行修改以后最终体现为:
代码3:
class Person { private int age; public void setAge(int newAge) { if(newAge > 0 && newAge < 130) { age= newAge; } else System.out.println(“所传参数非法!”); } public void speak() { System.out.println(“age= ”+age); } } class PersonDemo { public static void main(String[] args) { //创建一个Person对象 Person p = new Perons(); //对Person的成员变量(属性)age赋值 p.setAge(20); p.speak(); } }
下面我们来描述一下,这一段代码在内存中的运行过程。首先还是要从主函数开始运行,在栈内存中为主函数分配一个空间,在其中创建一个Person类类型变量p。再在堆内存中为Person对象开辟空间,同时为其内部的私有成员变量age赋予默认初始化值0,并为这块空间其分配一个内存地址值。将这个地址值赋给栈内存的变量p,p就指向了堆内存中的Person对象。接着调用变量p所指向的Person对象的setAge方法,并将整型数20作为参数传入。此时,又在栈内存为setAge方法开辟一块空间,其内部有个整型变量newAge,并被赋值为20。其中,setAge方法内部的赋值动作,是将newAge的值赋给调用这个方法的对象的成员变量(属性)——age,而不是其他变量。然后调用变量p所指向的Person对象的speak方法,那么就又在栈内存中为speak方法开辟一块空间,并将堆内存中Person对象的age通过输出语句进行输出。这里,speak方法的age变量,同样是调用speak方法的对象所属的属性。那么为什么调用这两个方法时涉及的age变量都是堆内存中Person对象的呢?可以这样理解:既然这两个方法都是由Person对象调用,那么访问到的属性当然就是属于该对象的。
相关文章推荐
- 黑马程序员-day05-面向对象(封装 Encapsulation)
- 黑马程序员 Java自学总结六 面向对象三个特征之封装
- 黑马程序员——面向对象的特点
- 黑马程序员-java基础之面向对象,封装
- 黑马程序员 JAVA初级-面向对象 匿名对象、封装、构造函数、构造代码块、this
- 黑马程序员java学习笔记——面向对象的特征封装、继承和多态
- 黑马程序员——OC篇(二)面向对象的三大特性(封装、继承、多态)
- 黑马程序员------面向对象(No.3)(static、静态代码块、封装、说明文档制作、main函数)
- 黑马程序员--面向对象(封装)
- 黑马程序员----Java基础之面向对象(封装 继承 多态)
- 黑马程序员_面向对象(一)_封装
- 黑马程序员————面向对象之封装
- 黑马程序员学习日记——面向对象-封装
- 面向对象三大特性之封装(encapsulation)
- 黑马程序员学习日记 (四)面向对象三大特征: 封装 继承 多态
- 黑马程序员_java面向对象(对第五课面向对象..封装..构造..this应用总结)
- 黑马程序员-------面向对象的封装、继承、多态一些学习笔记
- 黑马程序员——OC笔记之面向对象三大特征(封装、继承、多态)
- 黑马程序员_面向对象(概述、封装)
- 黑马程序员学习笔记四——Java 面向对象 特点之 继承