面向对象(基础)特征一:封装性(encapsulation)

2023-10-23 11:31

本文主要是介绍面向对象(基础)特征一:封装性(encapsulation),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、介绍
    • (1)封装性
    • (2)权限修饰符
  • 二、案例
    • (1)案例1
  • 三、练习
    • (1)练习1
    • (2)练习2
    • (3)练习3
    • (4)练习4

面向对象特征之一:封装性

一、介绍

(1)封装性

  1. 为什么需要封装性?
  • 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
  • 我要开车,我不需要懂离合、油门、制动等原理和维修也可以驾驶。

随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要 遵循“ 高内聚、低耦合 ”。 高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。

内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

内聚:把特定的内容封装到不同的类里面,把类里面特定的功能封装到不同的方法当中。

耦合:相互之间要调用的时候,只对外暴露需要用的,不需要使用的细节就不对外暴露了。

理论上:

  • 高内聚类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合仅暴露少量的方法给外部使用,尽量方便外部调用。

所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对 象开放,向没必要开放的类或者对象隐藏信息。

通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

(2)权限修饰符

  1. 如何实现数据封装?
    2.1 权限修饰符
    Java规定了4种权限修饰,分别是:private缺省protectedpublic
    image.png
    image.png
    2.2 作用
    我们可以使用4种权限修饰来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小

:只能使用public缺省修饰
类的内部成员:可以使用4种权限修饰进行修饰。

比如,这里的类用的是“缺省”:

class Animal{   //动物}

这个类用的是“public”:

public class AnimalTest {public static void main(String[] args) {Animal animal1=new Animal();}
}

但是使用“private”就不行,比如:
image.png

private修饰的是类的内部文件,此时AA就是一个类,它对自己可见,而且外边用不了,没有什么作用。

protected也一样,修饰的属性让子类的类能够看到,主要指的是成员,类自己加没有什么意义。

目前写的类是并列关系,都叫外部类,比如下面的AnimalTest类和Animal类:

public class AnimalTest {public static void main(String[] args) {Animal animal1=new Animal();}
}
class Animal{   //动物}

若在Animal类里面再写一个类,这个类就是内部类,内部类可以被四种权限修饰。(后边再说,不着急)

2.3 开发中4种权限使用频率的情况:

比较高:public、private

比较低:缺省、protected

  1. 封装性的体现

场景1:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改

场景2:将类中不需要对外暴露的方法,设置为private.

场景3:单例模式中构造器private的了,避免在类的外部创建实例。(放到static关键字后讲)

二、案例

(1)案例1

看下面的代码:

package yuyi.exer01.exe01;/*** ClassName: AnimalTest* Package: yuyi.exer01.exe01* Description:** @Author 雨翼轻尘* @Create 2023/10/22 0022 13:29*/
public class AnimalTest {public static void main(String[] args) {Animal animal1=new Animal();animal1.name="金蟾";animal1.legs=4;System.out.println("name="+animal1.name+",legs="+animal1.legs);animal1.eat();}
}
class Animal{   //动物//属性String name;    //名字int legs;   //腿的个数//方法public void eat(){System.out.println("动物觅食");}
}

执行结果
image.png

⚡注意

不能直接在Animal类中写这种语句:System.out.println("动物");

类里面只能有属性和方法,而上面的语句既不算属性,也不算方法,只是一个执行语句,它只能放在方法里面。

class Animal{   //动物//属性String name;    //名字int legs;   //腿的个数System.out.println("动物");	//不能这样写//方法public void eat(){System.out.println("动物觅食");}
}

现在想给legs设置一个判断逻辑,只有正偶数的值才能被输出。

在Animal类中直接判断是不对的,如下:
image.png

但是属性可以在方法里面调用,所以我们可以在Animal类中写一个方法,在方法里面对legs进行逻辑判断。

//设置legs属性值
public void setLegs(int l){if(l>=0&&l%2==0){   //腿的个数为正偶数legs=l;}else{System.out.println("输入数据非法");}
}

在给属性赋值的时候,不需要再直接调用属性了,可以通过刚才的setLegs方法来调用,如下:

 //animal1.legs=4;
animal1.setLegs(4); //若输入的值是正偶数,就能正确输出结果

完整代码:

public class AnimalTest {public static void main(String[] args) {Animal animal1=new Animal();animal1.name="金蟾";//animal1.legs=4;animal1.setLegs(4); //若输入的值是正偶数,就能正确输出结果System.out.println("name="+animal1.name+",legs="+animal1.legs);animal1.eat();}
}
class Animal{   //动物//属性String name;    //名字int legs;   //腿的个数/*不能直接在这里判断if(legs>=0){}*///方法public void eat(){System.out.println("动物觅食");}public void setLegs(int l){if(l>=0&&l%2==0){   //腿的个数为正偶数legs=l;}else{System.out.println("输入数据非法");}}
}

输出结果:
image.png


但是现在我们还是可以直接来调用属性,现在只不过是多了一种方式判断逻辑而已。

那么如何让它彻底不能通过调用属性来给leg赋值?

可以在定义属性的时候,在前面加一个权限修饰符private:(表示将这个属性私有化了,就是将它隐藏起来了,仅限在类的内部看到)

 private int legs;

此时外界(出了Animal类)就看不到legs属性了。

所以直接调用legs属性就不被允许了。如下:
image.png

同时这里的legs也不能用了:
image.png

那怎么办呢?

可以在Animal类中再写一个方法:

//获取legs属性值
public int getLegs(){return legs;
}

这个方法的权限是public,使用范围还是很大的。

现在就可以调用了:

System.out.println("name="+animal1.name+",legs="+animal1.getLegs());

此时编译器看到的就是这个方法的返回值类型(int类型的变量),所以就可以使用了。


整体代码:

package yuyi.exer01.exe01;/*** ClassName: AnimalTest* Package: yuyi.exer01.exe01* Description:** @Author 雨翼轻尘* @Create 2023/10/22 0022 13:29*/
public class AnimalTest {public static void main(String[] args) {Animal animal1=new Animal();animal1.name="金蟾";//因为legs声明为private,是私有的,出了Animal类之外就不能调用了//animal1.legs=4;//只能通过setLegs(),间接对legs属性进行赋值animal1.setLegs(4); //若输入的值是正偶数,就能正确输出结果//System.out.println("name="+animal1.name+",legs="+animal1.legs);System.out.println("name="+animal1.name+",legs="+animal1.getLegs());animal1.eat();}
}
class Animal{   //动物//属性String name;    //名字private int legs;   //腿的个数/*不能直接在这里判断if(legs>=0){}*///方法public void eat(){System.out.println("动物觅食");}//设置legs属性值public void setLegs(int l){if(l>=0&&l%2==0){   //腿的个数为正偶数legs=l;}else{System.out.println("输入数据非法");}}//获取legs属性值public int getLegs(){return legs;}
}

输出结果:
image.png

通过这个例子可以看到,为什么需要将一些必要的结构隐藏起来,因为不希望外边的用户可以随意的调用它,就需要将它私有化。如果外边想用,那就提供相应的方法,在方法里面对私有化的属性进行其他的设置。

权限修饰符的使用,就是封装性的体现。


👻总结

在上述案例中,我们给Animal的对象的legs属性赋值。在实际的常识中,我们知道legs不能赋值为负数的。但是如果
直接调用属性legs,是不能加入判断逻辑的。

那怎么办呢?

将legs属性私有化(private),禁止在Animal类的外部直接调用此属性。

提供给legs属性赋值的setLegs()方法,在此方法中加入legs赋值的判断逻辑if(legs >= 0 && legs % 2 ==0)
将此方法暴露出去,使得在Animal类的外部调用此方法,对legs属性赋值。

提供给legs属性获取的getLegs()方法,此方法对外暴露。使得在Animal类的外部还可以调用此属性的值。

若是在类的外部不想让用户获取legs的值,可以不写getLegs()方法,或者将它的权限修饰符设置为private,如下:

private int getLegs(){return legs;
}

此时getLegs方法对外也不暴露了,在Animal方法外面也就不能调用它了:
image.png

三、练习

(1)练习1

🌋题目描述

创建程序,在其中定义两个类:Person和PersonTest类。定义如下:

用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。

在PersonTest类中实例化Person类的对象b,调用setAge()和getAge()方法,体会Java的封装性。

🤺代码

package yuyi.exer01.exe01;/*** ClassName: Person* Package: yuyi.exer01.exe01* Description:*创建程序,在其中定义两个类:Person和PersonTest类。定义如下:* 用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。* 在PersonTest类中实例化Person类的对象b,调用setAge()和getAge()方法,体会Java的封装性。** @Author 雨翼轻尘* @Create 2023/10/22 0022 15:02*/
public class Person {//属性private int age;    //不对外暴露//设置age属性public void setAge(int agenumber){if(agenumber>=0&&agenumber<=130){age=agenumber;}else{System.out.println("输入的年龄有误");}}//获取age属性public int getAge(){return age;}
}
class PersonTest{public static void main(String[] args) {//创建Person实例1Person b=new Person();//b.age=21;   //编译不通过b.setAge(22);   //赋值System.out.println(b.getAge()); //获取值}
}

⚡输出结果
image.png

👻注意

可以将这两个方法合并吗?如下:
image.png

现在两个功能合并在一起了,Age方法的耦合度更高,不建议这样来写!

比如之前已经有了一个Person对象,现在我只想看看是多少岁,如果是左边的两种方法,我就可以通过getAge看一下是多少岁。

但如果是右边的Age方法,此时我需要传进一个参数,然后又返回相同的数值,这就违背初衷了。

每一个方法就只单纯的完成一个功能,在实际需要的时候再去组合这些方法完成特定的操作即可。不要刻意合并!!!

(2)练习2

🌋题目描述

【自定义图书类】

设定属性包括:

书名bookName,

作者author,

价格price;

方法包括:

相应属性的get/set方法,

图书信息介绍等。

🤺代码

【book.java】

package yuyi.exer01.exe01;/*** ClassName: book* Package: yuyi.exer01.exe01* Description:* 【自定义图书类】* 设定属性包括:* 书名bookName,* 作者author,* 价格price;* <p>* 方法包括:* 相应属性的get/set方法,* 图书信息介绍等。** @Author 雨翼轻尘* @Create 2023/10/22 0022 15:39*/
public class book {//属性private String bookName; //书名private String author;   //作者private double price;   //价格//方法//1.1设置书名public void setbookName(String b) {//...bookName = b;}//1.2获取书名public String getbookName() {return bookName;}//2.1设置作者public void setauthor(String a) {//...author = a;}//2.2获取作者public String getauthor() {return author;}//3.1设置价格public void setprice(double s) {//...price = s;}//3.2获取价格public double getprice() {return price;}//4.获取图书信息public String showInfo(){return "bookName:"+bookName+",author:"+author+",price:"+price;}
}

【bookTest.java】

package yuyi.exer01.exe01;/*** ClassName: bookTest* Package: yuyi.exer01.exe01* Description:** @Author 雨翼轻尘* @Create 2023/10/22 0022 15:54*/
public class bookTest {public static void main(String[] args) {book book1=new book();book1.setbookName("西游记");book1.setauthor("吴承恩");book1.setprice(30.46);System.out.println(book1.showInfo());}
}

可以使用Ctrl+Alt+L来调整代码格式(idea编译器)

此时我们看到的就是对外暴露的方法。

从后期来看,只要不是一些常量的话,一般都将属性私有化,对外都是暴露方法,通过方法对属性进行修改。

⚡输出结果
image.png

👻注意

每创建一个对象,类里面的属性就会各自持有一份。

现在“封装性”考虑的只是控制这些属性、方法在被访问的时候权限的大小而已。不会影响各个对象属性方法之间的关系,都会各自一份属性方法。

(3)练习3

🌋题目描述

【普通员工类】

(1)声明员工类Employee,

  • 包含属性:姓名、性别、年龄、电话,属性私有化
  • 提供get/set方法
  • 提供String getInfo()方法
    (2)在测试类的main中创建员工数组,并从键盘输入员工对象信息,最后遍历输出
    image.png

image.png

🤺代码

【Employee.java】

package yuyi.exer01.exe01;/*** ClassName: Employee* Package: yuyi.exer01.exe01* Description:*【普通员工类】* (1)声明员工类Employee,* - 包含属性:姓名、性别、年龄、电话,属性私有化* - 提供get/set方法* - 提供String getInfo()方法* (2)在测试类的main中创建员工数组,并从键盘输入员工对象信息,最后遍历输出** @Author 雨翼轻尘* @Create 2023/10/22 0022 16:06*/
public class Employee {//属性private String name;private char sex;private int age;private String number;//方法//1.1设置姓名public void setname(String na){//...name=na;}//1.2获得姓名public String getname(){return name;}//2.1设置性别public void setsex(char se){//...sex=se;}//2.2获得性别public char getsex(){return sex;}//3.1设置年龄public void setage(int ag){//...age=ag;}//3.2获得年龄public int getage(){return age;}//4.1设置电话public void setnumber(String nu){//...number=nu;}//4.2获得电话public String getnumber(){return number;}//String getInfo()public String getInfo(){//return getname()+"\t"+getsex()+"\t"+getage()+"\t"+getnumber();return name+"\t"+sex+"\t"+age+"\t"+number;}
}

【EmployeeTest.java】

package yuyi.exer01.exe01;import java.util.Scanner;/*** ClassName: EmployeeTest* Package: yuyi.exer01.exe01* Description:** @Author 雨翼轻尘* @Create 2023/10/22 0022 18:03*/
public class EmployeeTest {public static void main(String[] args) {//Employee ie=new Employee();//创建Employee[] 假设只有两个员工Scanner scan=new Scanner(System.in);Employee[] ie=new Employee[2];for (int i = 0; i < ie.length; i++) {ie[i]=new Employee();System.out.println("--------添加第"+(i+1)+"个员工--------");System.out.print("姓名:");String name=scan.next();System.out.print("性别:");char sex=scan.next().charAt(0);System.out.print("年龄:");int age=scan.nextInt();System.out.print("电话:");String number=scan.next();//给指定的employee对象的各属性赋值ie[i].setname(name);ie[i].setsex(sex);ie[i].setage(age);ie[i].setnumber(number);}//遍历员工列表System.out.println("---------------员工列表----------------");System.out.println("编号\t姓名\t性别\t年龄\t电话");for (int i = 0; i < ie.length; i++) {System.out.println((i+1)+"\t"+ie[i].getInfo());}System.out.println("---------------员工列表完成----------------");}
}

运行输入:
image.png

⚡输出结果
image.png

(4)练习4

🌋题目描述

测试权限修饰,先忽略protected。

🤸测试

在包test1里面创建一个类Order

ackage yuyi.exer01.exe01.test1;public class Order {//声明不同权限的属性private int orderPrivate;int orderDefault;   //缺省权限public int orderPublic;//声明不同权限的方法private void methodPrivate(){}void methodDefault(){}public void methodPublic(){}}

①在当前类里面

在类的某一个方法内,上面的都可以调用。方法里面可以调属性,调方法。(如下面的test方法)

package yuyi.exer01.exe01.test1;public class Order {//声明不同权限的属性private int orderPrivate;int orderDefault;   //缺省权限public int orderPublic;//声明不同权限的方法private void methodPrivate(){}void methodDefault(){}public void methodPublic(){}public void test(){//调用属性orderPrivate=1;orderDefault=2;orderPublic=3;//调用方法methodPrivate();methodDefault();methodPublic();}}

权限再小,在当前类中也能用。

当前包里面,当前类外面

在当前test1包里面新建一个类OrderTest,调用Order类里面的结构。

public class OrderTest {public static void main(String[] args) {//Order类的权限是public,在各个包里面都能看得见//因为是同一个包下,所以不用importOrder order=new Order();}
}

即使现在的Order类是缺省修饰的,也可以被调用。因为缺省权限是本包内有效。如下:

class Order{//...
}

接下来测试调用属性和方法:

package yuyi.exer01.exe01.test1;/*** ClassName: OrderTest* Package: yuyi.exer01.exe01.test1* Description:** @Author 雨翼轻尘* @Create 2023/10/22 0022 19:45*/
public class OrderTest {public static void main(String[] args) {//Order类的权限是public,在各个包里面都能看得见Order order=new Order();//调用属性order.orderPublic=1;order.orderDefault=2;//order.orderPrivate=3; //不可以调用//调用方法order.methodDefault();order.methodPublic();//order.methodPrivate();    //不可以调用}
}
  • 调用属性

image.png

  • 调用方法

image.png

private修饰符只能在本类里面调用,出了类就不行了。

缺省可以在本包内调用,不会局限于类里面。(缺省就是default,但是现在不这么用了,因为容易和switch…case语句里面的default弄混)

不同包里面

在不同包下面创建文件,现在在test2包下创建OrderTest文件:
image.png

接下来想看一下test1包下的Order类里面的属性和方法在包test2内的OrderTest类中能不能调用。

若此时Order类被缺省修饰,就不能在包test2内的OrderTest类中创建对象了,因为缺省修饰的只能在当前包内使用。

class Order{//...
}

可以看到,如果现在创建Order对象是不可以的:
image.png

所以,Order类的权限要大一点:

public class Order{//...
}

此时不会报错了,但是导包是必须的,如下:
image.png


看一下测试代码:

package yuyi.exer01.exe01.test2;import yuyi.exer01.exe01.test1.Order;/*** ClassName: OrderTest* Package: yuyi.exer01.exe01.test2* Description:** @Author 雨翼轻尘* @Create 2023/10/23 0023 0:04*/
public class OrderTest {public static void main(String[] args) {Order order=new Order();//调用属性order.orderPublic=1;//order.orderPrivate=2;	//不可以//order.orderDefault=3;	//不可以//调用方法order.methodPublic();//order.methodPrivate();	//不可以//order.methodDefault();	//不可以}
}

只有Public修饰可以:
image.png

出了本包之后,不能调用Default和Private。

总结:
在这里插入图片描述
代码敲的比较快,如果文章哪里有误,欢迎指正。

这篇关于面向对象(基础)特征一:封装性(encapsulation)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/267567

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

c++基础版

c++基础版 Windows环境搭建第一个C++程序c++程序运行原理注释常亮字面常亮符号常亮 变量数据类型整型实型常量类型确定char类型字符串布尔类型 控制台输入随机数产生枚举定义数组数组便利 指针基础野指针空指针指针运算动态内存分配 结构体结构体默认值结构体数组结构体指针结构体指针数组函数无返回值函数和void类型地址传递函数传递数组 引用函数引用传参返回指针的正确写法函数返回数组

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

OmniGlue论文详解(特征匹配)

OmniGlue论文详解(特征匹配) 摘要1. 引言2. 相关工作2.1. 广义局部特征匹配2.2. 稀疏可学习匹配2.3. 半稠密可学习匹配2.4. 与其他图像表示匹配 3. OmniGlue3.1. 模型概述3.2. OmniGlue 细节3.2.1. 特征提取3.2.2. 利用DINOv2构建图形。3.2.3. 信息传播与新的指导3.2.4. 匹配层和损失函数3.2.5. 与Super

【MRI基础】TR 和 TE 时间概念

重复时间 (TR) 磁共振成像 (MRI) 中的 TR(重复时间,repetition time)是施加于同一切片的连续脉冲序列之间的时间间隔。具体而言,TR 是施加一个 RF(射频)脉冲与施加下一个 RF 脉冲之间的持续时间。TR 以毫秒 (ms) 为单位,主要控制后续脉冲之前的纵向弛豫程度(T1 弛豫),使其成为显著影响 MRI 中的图像对比度和信号特性的重要参数。 回声时间 (TE)