文章目录
- 一、介绍
- (1)封装性
- (2)权限修饰符
- 二、案例
- (1)案例1
- 三、练习
- (1)练习1
- (2)练习2
- (3)练习3
- (4)练习4
面向对象特征之一:封装性
一、介绍
(1)封装性
- 为什么需要封装性?
- 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
- 我要开车,我不需要懂离合、油门、制动等原理和维修也可以驾驶。
随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要 遵循“ 高内聚、低耦合 ”。 高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。
内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。
内聚:把特定的内容封装到不同的类里面,把类里面特定的功能封装到不同的方法当中。
耦合:相互之间要调用的时候,只对外暴露需要用的,不需要使用的细节就不对外暴露了。
理论上:
高内聚
:类的内部数据操作细节自己完成,不允许外部干涉;低耦合
:仅暴露少量的方法给外部使用,尽量方便外部调用。
所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对 象开放,向没必要开放的类或者对象隐藏信息。
通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
(2)权限修饰符
- 如何实现数据封装?
2.1 权限修饰符
Java规定了4种权限修饰,分别是:private
、缺省
、protected
、public
2.2 作用
我们可以使用4种权限修饰来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小。
类:只能使用
public
、缺省
修饰
类的内部成员:可以使用4种权限修饰
进行修饰。
比如,这里的类用的是“缺省
”:
class Animal{ //动物}
这个类用的是“public
”:
public class AnimalTest {public static void main(String[] args) {Animal animal1=new Animal();}
}
但是使用“private
”就不行,比如:
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:私有化(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("动物觅食");}
}
执行结果
⚡注意
不能直接在Animal类中写这种语句:System.out.println("动物");
类里面只能有属性和方法,而上面的语句既不算属性,也不算方法,只是一个执行语句,它只能放在方法里面。
class Animal{ //动物//属性String name; //名字int legs; //腿的个数System.out.println("动物"); //不能这样写//方法public void eat(){System.out.println("动物觅食");}
}
现在想给legs设置一个判断逻辑,只有正偶数的值才能被输出。
在Animal类中直接判断是不对的,如下:
但是属性可以在方法里面调用,所以我们可以在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("输入数据非法");}}
}
输出结果:
但是现在我们还是可以直接来调用属性,现在只不过是多了一种方式判断逻辑而已。
那么如何让它彻底不能通过调用属性来给leg赋值?
可以在定义属性的时候,在前面加一个权限修饰符private
:(表示将这个属性私有化了,就是将它隐藏起来了,仅限在类的内部看到)
private int legs;
此时外界(出了Animal类)就看不到legs属性了。
所以直接调用legs属性就不被允许了。如下:
同时这里的legs也不能用了:
那怎么办呢?
可以在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;}
}
输出结果:
通过这个例子可以看到,为什么需要将一些必要的结构隐藏起来,因为不希望外边的用户可以随意的调用它,就需要将它私有化。如果外边想用,那就提供相应的方法,在方法里面对私有化的属性进行其他的设置。
权限修饰符的使用,就是封装性的体现。
👻总结
在上述案例中,我们给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方法外面也就不能调用它了:
三、练习
(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()); //获取值}
}
⚡输出结果
👻注意
可以将这两个方法合并吗?如下:
现在两个功能合并在一起了,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编译器)
此时我们看到的就是对外暴露的方法。
从后期来看,只要不是一些常量的话,一般都将属性私有化,对外都是暴露方法,通过方法对属性进行修改。
⚡输出结果
👻注意
每创建一个对象,类里面的属性就会各自持有一份。
现在“封装性”考虑的只是控制这些属性、方法在被访问的时候权限的大小而已。不会影响各个对象属性方法之间的关系,都会各自一份属性方法。
(3)练习3
🌋题目描述
【普通员工类】
(1)声明员工类Employee,
- 包含属性:姓名、性别、年龄、电话,属性私有化
- 提供get/set方法
- 提供String getInfo()方法
(2)在测试类的main中创建员工数组,并从键盘输入员工对象信息,最后遍历输出
🤺代码
【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("---------------员工列表完成----------------");}
}
运行输入:
⚡输出结果
(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(); //不可以调用}
}
- 调用属性
- 调用方法
private修饰符只能在本类里面调用,出了类就不行了。
缺省可以在本包内调用,不会局限于类里面。(缺省就是default,但是现在不这么用了,因为容易和switch…case语句里面的default弄混)
③不同包里面
在不同包下面创建文件,现在在test2包下创建OrderTest文件:
接下来想看一下test1包下的Order类里面的属性和方法在包test2内的OrderTest类中能不能调用。
若此时Order类被缺省修饰,就不能在包test2内的OrderTest类中创建对象了,因为缺省修饰的只能在当前包内使用。
class Order{//...
}
可以看到,如果现在创建Order对象是不可以的:
所以,Order类的权限要大一点:
public class Order{//...
}
此时不会报错了,但是导包是必须的,如下:
看一下测试代码:
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
修饰可以:
出了本包之后,不能调用Default和Private。
总结:
代码敲的比较快,如果文章哪里有误,欢迎指正。