从零开始学Java(十二)Java封装的使用

封装是面向对象的三大特征之一,什么是封装?封装有什么好处?怎么封装,代码怎么写?这是这篇文章学习的内容。

什么是封装?

封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节,但可以通过该对象对外提供的接口来访问该对象。

在现实世界当中我们可以看到很多事物都是封装好的,比如“鼠标”,外部有一个壳,将内部的原件封装起来,至于鼠标内部的细节是什么,我们不需要关心,只需要知道鼠标对外提供了左键、右键、滚动滑轮这三个简单的操作。对于用户来说只要知道左键、右键、滚动滑轮都能完成什么功能就行了。为什么鼠标内部的原件要在外部包装一个“壳”呢,起码内部的原件是安全的,不是吗。

再如“数码相机”,外部也有一个壳,将内部复杂的结构包装起来,对外提供简单的按键,这样每个人都可以很快的学会照相了,因为它的按键很简单,另外照相机内部精密的原件也受到了壳儿的保护,不容易坏掉。

根据以上的描述,可以得出封装有什么好处呢?

封装之后就形成了独立实体,独立实体可以在不同的环境中重复使用,显然封装可以降低程序的耦合度,提高程序的扩展性,以及重用性或复用性,例如“鼠标”可以在A电脑上使用,也可以在B电脑上使用。另外封装可以隐藏内部实现细节,站在对象外部是看不到内部复杂结构的,对外只提供了简单的安全的操作入口,所以封装之后,实体更安全了。

封装的代码实现两步:

  • 第一步:属性私有化

  • 第二步:1个属性对外提供两个set和get方法。外部程序只能通过set方法修改,只能通过get方法读取,可以在set方法中设立关卡来保证数据的安全性。

在强调一下: set和get方法都是实例方法,不能带static。

不带static的方法称为实例方法,实例方法的调用必须先new对象。

set和get方法写的时候有严格的规范要求:(大家要按照规矩来)

set方法长这个样子:

public void set+属性名首字母大写(1个参数){xxx = 1个参数;}get方法长这个样子:public 返回值类型 get+属性名首字母大写(无参){return xxx;}

我们来看一段代码,在不进行封装的前提下,存在什么问题:

public class MobilePhone {//电压:手机正常电压在3~5Vdouble voltage;}
public class MobilePhoneTest {public static void main(String[] args) {MobilePhone phone = new MobilePhone();phone.voltage = 3.7;System.out.println("手机电压 = " + phone.voltage);phone.voltage = 100;System.out.println("手机电压 = " + phone.voltage);}}

运行结果如下图所示:

图1:未进行封装的程序测试

以上程序MobilePhone类未进行封装,其中的电压属性voltage对外暴露,在外部程序当中可以对MobilePhone对象的电压voltage属性进行随意访问,导致了它的不安全,例如手机的正常电压是3~5V,但是以上程序已经将手机电压设置为100V,这个时候显然是要出问题的,但这个程序编译以及运行仍然是正常的,没有出现任何问题,这是不对的。

为了保证内部数据的安全,这个时候就需要进行封装了,封装的第一步就是将应该隐藏的数据隐藏起来,起码在外部是无法随意访问这些数据的,怎么隐藏呢?我们可以使用java语言中的private修饰符,private修饰的数据表示私有的,私有的数据只能在本类当中访问。请看程序:

public class MobilePhone {//电压:手机正常电压在3~5Vprivate double voltage;}
public class MobilePhoneTest {public static void main(String[] args) {MobilePhone phone = new MobilePhone();phone.voltage = 3.7;System.out.println("手机电压 = " + phone.voltage);phone.voltage = 100;System.out.println("手机电压 = " + phone.voltage);}}

以上程序编译报错了,请看下图:

图2:private修饰的数据无法在外部程序中直接访问

通过以上的测试,手机对象的电压属性确实受到了保护,在外部程序中无法访问了。但从当前情况来看,voltage属性有点儿太安全了,一个对象的属性无法被外部程序访问,自然这个数据就没有存在的价值了。所以这个时候就需要进入封装的第二步了:对外提供公开的访问入口,让外部程序统一通过这个入口去访问数据,我们可以在这个入口处设立关卡,进行安全控制,这样对象内部的数据就安全了。

对于“一个”属性来说,我们对外应该提供几个访问入口呢?通常情况下我们访问对象的某个属性,不外乎读取(get)和修改(set),所以对外提供的访问入口应该有两个,这两个方法通常被称为set方法和get方法(请注意:set和get方法访问的都是某个具体对象的属性,不同的对象调用get方法获取的属性值不同,所以set和get方法必须有对象的存在才能调用,这样的方法定义的时候不能使用static关键字修饰,被称为实例方法。实例方法必须使用“引用”的方式调用。还记得之前我们接触的方法都是被static修饰的,这些方法直接采用“类名”的方式调用,而不需要创建对象,在这里显然是不行的)。请看以下代码:

public class MobilePhone {

//电压:手机正常电压在3~5Vprivate double voltage;public MobilePhone(){

}public void setVoltage(double _voltage){if(_voltage < 3 || _voltage > 5){//当电压低于3V或者高于5V时抛出异常,程序则终止throw new RuntimeException("电压非法,请爱护手机!");}//程序如果能执行到此处说明以上并没有发生异常,电压值合法voltage = _voltage;}public double getVoltage(){return voltage;}}
public class MobilePhoneTest {public static void main(String[] args) {MobilePhone phone = new MobilePhone();phone.setVoltage(3.7);System.out.println("手机电压 :" + phone.getVoltage());phone.setVoltage(100);System.out.println("手机电压 :" + phone.getVoltage());}}

运行结果如下图所示:

图3:对封装之后的测试

通过以上程序,可以看出MobilePhone的voltage属性不能在外部程序中随意访问了,只能调用MobilePhonesetVoltage()方法来修改电压,调用getVoltage()方法来读取电压,在setVoltage()方法中编写了安全控制代码,当电压低于3V,或者高于5V的时候,程序抛出了异常,不允许修改电压值,程序结束了。只有合法的时候,才允许程序修改电压值。(异常机制在后续的内容中会学到,不要着急。)

总之,在java语言中封装的步骤应该是这样的:

需要被保护的属性使用private进行修饰,给这个私有的属性对外提供公开的set和get方法,其中set方法用来修改属性的值,get方法用来读取属性的值。并且set和get方法在命名上也是有规范的,规范中要求set方法名是set + 属性名(属性名首字母大写),get方法名是get + 属性名(属性名首字母大写)。

其中set方法有一个参数,用来给属性赋值,set方法没有返回值,一般在set方法内部编写安全控制程序,因为毕竟set方法是修改内部数据的,而get方法不需要参数,返回值类型是该属性所属类型(先记住,以后讲:另外set方法和get方法都不带static关键字,不带static关键字的方法称为实例方法,这些方法调用的时候需要先创建对象,然后通过“引用”去调用这些方法,实例方法不能直接采用“类名”的方式调用。)

例如以下代码:

public class Product {private int no;private String name;private double price;public Product(){

}public Product(int _no , String _name , double _price){no = _no;name = _name;price = _price;}public int getNo() {return no;}public void setNo(int _no) {no = _no;}public String getName() {return name;}public void setName(String _name) {name = _name;}public double getPrice() {return price;}public void setPrice(double _price) {price = _price;}}
public class ProductTest {public static void main(String[] args) {Product p1 = new Product(10000 , "小米5S" , 2000.0);System.out.println("商品编号:" + p1.getNo());System.out.println("商品名称:" + p1.getName());System.out.println("商品单价:" + p1.getPrice());p1.setNo(70000);p1.setName("小米6");p1.setPrice(2100.0);System.out.println("商品编号:" + p1.getNo());System.out.println("商品名称:" + p1.getName());System.out.println("商品单价:" + p1.getPrice());}}

运行结果如下图所示:

图4:set和get方法测试

有的小伙伴可能会有这样的疑问:构造方法中已经给属性赋值了,为什么还要提供set方法呢?

因为,这是两个完全不同的时刻,构造方法中给属性赋值是在创建对象的时候完成的,当对象创建完毕之后,属性可能还是会被修改的,后期要想修改属性的值,这个时候就必须调用set方法了。

(0)

相关推荐

  • 面向对象(面向对象真的难吗,你只不过没有理清思路而已)

    面向对象真的难吗?其实我看不然,只不过我们学习的时候比较杂论,并且也没有真正领悟到他内部的强大.那么开始进入正题,众所周知面向对象三大概念:封装,继承,多态.封装中又可以实现构造方法,方法的重载.继承 ...

  • 《 高淇Java300集视频》:【3】面向对象基础

    面向过程(Procedure Oriented)和面向对象(Object Oriented,OO)都是对软件分析.设计和开发的一种思想,它指导着人们以不同的方式去分析.设计和开发软件. 面向对象编程, ...

  • Java基本概念:内部类

    一.简介 描述: 很多时候我们创建类的对象的时候并不需要使用很多次,每次只使用一次,这个时候我们就可以使用内部类了. 内部类不是在一个java源文件中编写两个平行的类,而是在一个类的内部再定义另外一个 ...

  • 怎么一本正经地秀技

    前言 修饰符怎么使用也是Java基础中比较重要的知识点,彻底理解了之后,后面学习更高深的东西才能得心应手.今天,以修饰符中比较常见的final为切入点,来谈谈final的使用的奇淫技巧以及一些相关的知 ...

  • Java基础之:OOP——代码块

    代码块又称初始化块,是类中的成员(即类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{}包围起来,也是通过调用执行. 但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象 ...

  • Java基础之:OOP——类变量与类方法

    类变量与类方法,我们习惯也将其称为静态变量与静态方法. 类变量/静态变量 通过一个实际案例来了解,为什么需要使用静态变量. 引入案例 声明一个学生类,每创建一个学生对象,统计学生的人数. public ...

  • Java基本概念:接口

    一.简介 描述: 普通类只有具体实现,抽象类具体实现和规范都有,接口只有规范! 接口就是比抽象类还抽象的抽象类,可以更加规范的对子类进行约束,全面专业地实现了规范和具体实现的分离. 抽象类还提供某些具 ...

  • Java中this关键字的使用

    一,表示类中属性 1,没有使用this的情况   class Person{ // 定义Person类 private String name ; // 姓名 private int age ; // ...

  • this关键字的使用

    this关键字 this关键字总是指向调用该方法的对象.this可以用来修饰属性.方法.构造器. static修饰的方法中不能使用this引用.如果在static修饰的方法中使用this关键字,则这个 ...

  • 从零开始学八字(二十):地支六合

    今天,我们聊聊地支六合. 天干有十位,阴阳相合,所以是五合.地支十二位,阴阳相合,所以是六合.即子丑合.寅亥合.卯戌合.辰酉合.巳申合.午未合. 关于地支为何六合之原因,<三命通会>解释的 ...

  • 从零开始学伤寒(二十)

    言行如一,从对自己诚实开始. 这是源寶每天原创更文第一年第287篇. 9.太陽病,欲解時,從巳至未上. 古人认为天人合一,中医自然也包括在内,而这无疑是正确的观点.人是自然的一部分,人受自然影响很大, ...

  • 从零开始学伤寒(二十二)

    言行如一,从对自己诚实开始. 这是源寶每天原创更文第一年第289篇. 12.太陽中風,陽浮而陰弱.陽浮者,熱自發:陰弱者,汗自出.嗇嗇惡寒,淅淅惡風,翕翕發熱,鼻鳴幹嘔者,桂枝湯主之. 终于迎来了第一 ...

  • 0291从零开始学伤寒(二十四)

    言行如一,从对自己诚实开始.   这是源寶每天原创更文第一年第291篇. 中药有很多种剂型,比如常见的汤.丸.散.膏等,无论什么剂型,都是一个含有解码的过程,怎么说呢? 我们知道,任何事物都是由能量和 ...

  • 相学不求人,先学面相十二宫!

    玄学天下 弘扬中国传统文化,传播中华道教文化.仙道贵生,无量渡人! Official Account 一.面相十二宫:福德宫 福德宫位于眉上方偏外部位,即在眉角之上的部位,也就是天苍.地库.福堂之处, ...

  • 西游疑问:孙悟空为何要学“七十二般变化”?

    文/刘宴斌 西游疑问:孙悟空因为最先跃入水帘洞而被拜为美猴王,洞中生活设施一应俱全,难道N年前此处有谁住过,是神仙.妖怪还是另一个曾经称霸花果山的大王? 西游疑问:孙悟空为何决议要去学艺?自从悟空被众 ...

  • 从零开始学八字(二):阴阳

    任何一种术数的起源,都离不开阴阳.关于阴阳的古老解释,请点击(三命通会学习笔记(四):原造化之始开篇之阴阳概念),在这里就不过多解释,那些晦涩难懂的理论,我们都要抛开.今天就轻理论,重象法,给大家讲讲 ...

  • 从零开始学八字(二一):地支三合

    今天,我们聊聊地支三合. 之所以叫三合,并不是因为只有三组地支合,是因为每个合都有三个地支,分别是申子辰合水局.寅午戌合火局.巳酉丑合金局.亥卯未合木局.关于三合的来源,<三命通会>中有两 ...

  • 相学不求人,先学面相十二宫!(赶紧收藏!)

    一.面相十二宫:福德宫 福德宫位于眉上方偏外部位,即在眉角之上的部位,也就是天苍.地库.福堂之处,关系财运和福气的吉凶.福堂又分为内福堂,外福堂,有的将福德宫包含前额的天仓和下巴的地库在内,以察看福气 ...

  • 跟我学中医 | 十二经脉概说(一)——名称、走向交接规律

    十二经脉是经络系统的核心组成部分.经络系统的十二经别以及络脉等都是从十二经脉中分出,彼此联系,相互配合而协同发挥作用. 一.十二经脉的名称(一)命名原则 内为阴,外为阳:阴阳理论贯穿整个中医理论,经络 ...