封装

如何理解面向对象这篇文章中,提到所谓的封装就是“功能都给你做好了,你不必去理解它是怎么写出来的,直接使用即可。”。但你得清楚一点,那就是这句话是相对于使用者来说的,而作为开发者,封装就得我们自己来干。

那么作为开发者,我们应该如何去封装呢?其实你应该反过来问,他们应该如何去使用,这样一想会简单很多,作为使用者,自然是希望越简单越好,也就是说,一些复杂的东西,我们不应该让使用者去操作,那也就是说我们应该把复杂的,以及不必要的参数给它封死,不让使用者去操作。

为什么不让使用者去操作?

因为往往使用者是不太专业的,如果暴露太多的接口给他们,就很有可能出现一些稀奇古怪的问题,好比一个不会做水煮鱼的,如果让他去做那肯定是不好的,那怎么办,给他买一包水煮鱼的调料,让他直接放进锅里就好,这样就减少了不必要的麻烦。我们封装程序也是这样,把复杂的代码封死,不让操作者去操作,以免出错。

比如下面这个例子:

class Average{
private int[] fractions = new int[3]; //分数
private int average = 0; //平均分
public void setFraction(int[] fraction){
fractions = fraction;
}
public double getAverage(){
for(int cell:fractions){
average += cell;
}
return (double) average / fractions.length;
}
} class app{
public static void main(String[] args){
int[] a = {50,40,50}; Average average = new Average();
average.setFraction(a); //设置分数
double n = average.getAverage(); //获取平均分
System.out.println(average.average); //报错
System.out.println(n); //46.0
}
}
提示:Java通过private设置私有变量,通过public将变量设置成公开的。

这里我们之所以将分数和平均分设置成私有变量是为了防止使用者误操作,而且也不必让使用者知道有这么一个变量,只需要让使用者知道怎么去设置分数,和获取平均分就好了。

当然这只是一个很基础的封装,如果想封装出一个好的程序,还得多费一些心思。

继承

拿猫和狗来说,它们都是动物,而且它们有一些共同点,比如:名字,年龄,声音,吃等。把这段话写成代码就是下面这个样子。

class Animal{
private String name;
private int age; public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
} class Cat extends Animal{
public void voice(){
System.out.println(super.getName() + " 喵");
}
public void eat(){
System.out.println(super.getName() + " fish");
}
} class Dog extends Animal{
public void voice(){
System.out.println(super.getName() + " 汪");
}
public void eat(){
System.out.println(super.getName() + " Bone");
}
} class app{
public static void main(String[] args){
Cat cat = new Cat();
cat.setName("猫大王"); //Cat本身没有setName方法,但是它的基类有,所以java解析器会到Cat的基类那里拿
cat.voice(); Dog dog = new Dog();
dog.setName("大黑");
dog.setAge(13);
dog.voice();
System.out.println(dog.getName() + dog.getAge());
}
} ------Output------
猫大王 喵
大黑 汪
大黑13
提示:Java通过extends关键字来实现继承,父类中通过private定义的变量和方法不会被继承,也就是你不能在子类中直接操作父类通过private定义的变量以及方法。

在上面代码中,我们可以看到,CatDog并没有定义setNamesetAgegetNamegetAge方法,但是我们依然可以在CatDog类中使用,这是因为我们通过extends关键字继承了Animal类,因此在Animal中定义的变量和方法,我们可以在子类中直接使用,除private定义的变量和方法。

反过来说,姓名和年龄是猫和狗的基本信息也是它们的共同特性。

不过得注意一下,一个类只能有一个直接父类,也就是在一个类中只能使用一次extends,虽然说它只能有一个直接父类,但是如果它的父类去继承了其他的类,那么在父类中继承来的变量和方法,也是能被子类所使用的。

重写父类方法或变量

一般重写父类方法,是因为你把猫当成是一个基类,而将狗继承自猫类。看似这很好笑,但如果你去翻翻你的代码,这种情况多如牛毛。当然,如果你不需要继承,那就另说了。那么如果碰到这种情况,我们怎么重写基类呢?很简单,在子类中定义一个和父类中一样的方法,如下面这样:

class Animal{
private String name;
private int age; public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
} class Dog extends Animal{
public String getName(){
return super.getName() + "2";
}
public void voice(){
System.out.println(super.getName() + " 汪");
}
public void eat(){
System.out.println(super.getName() + " Bone");
}
} class app{
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("大黑");
System.out.println(dog.getName()); //执行的是Dog中的getName方法
}
}
提示:通过super可以在子类中直接调用父类的方法以及变量,通过this调用当前类。

我觉得把这叫做重写不太好,因为如果从本质来讲,它不算重写,只是Java寻找变量以及方法的规则罢了。Java会先看一下,自己身上有没有某个变量或方法,如果没有,它会接着到父类中找,如果父类中还是没有,那么它又会到更上一级中找,如果一直找上去都没有,那么才报错。

在重写父类时,需要注意一下,重写时,方法的返回值类型必须和父类中定义的一致,比如下面这样就会报错

class Animal{
private String name; public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
class Dog extends Animal{
public int getName(){ //和父类中的getName返回值不同,报错
return 123;
}
} class app{
public static void main(String[] args){
Dog dog = new Dog();
System.out.println(dog.getName());
}
}

另外还需要注意,如果重写时,和父类中的参数不一致,则会发生意想不到的事,比如下面这个

class Animal{
private String name; public void setName(String name){
this.name = name;
}
public String getName(String hello){
return this.name + hello;
}
} class Dog extends Animal{
public String getName(){
return "123";
}
} class app{
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("大黑");
System.out.println(dog.getName("hello"));
}
} ------Output------
大黑hello

可以看到当我们给getName传达了参数时,执行的是Animal中的方法,而非Dog中的getName方法,也就是说如果参数不一致最后执行的可能就不是重写的那个方法。另外也不可将父类公开的方法或变量改成私有(如将public改成private),否则也会报错,我估计是Java有一套覆盖规则,如果没有达到条件就不会进行覆盖。

多态

先来几个例子,再讲理论

class Animal{
public int age = 5; public int getAge(){
return age;
} } class Dog extends Animal{
public int age = 8; public int getAge(){
return age + 2;
}
} class app{
public static void main(String[] args){
Animal dog = new Dog();
System.out.println(dog.age);
}
}
------Output------
5

Animal dog = new Dog();这么一句话,可以发现它们的类型并不一样,但却可以正常运行,之所以可以运行是因为,Dog类是Animal的子类,而父类是包括子类的。我们说动物,那么狗是不是就是动物中的一员呢,这是肯定的,而这里之所以如果运行也正是这个理。

不过需要注意一下,通过这种方式创建的对象,在获取实例变量时,获取到的是父类中的实例变量,如果是方法,则看子类中是否存在和父类中同名的方法,如果存在则使用子类中的方法,但是如果子类中有某个方法,而父类中没有,那么就会报错。如下这段代码就会报错

class Animal{
public int age = 5; public int getAge(){
return age;
} } class Dog extends Animal{
public int age = 8; public int getAge(){
return age + 2;
}
public setAge(int a){
this.age = a;
}
} class app{
public static void main(String[] args){
Animal dog = new Dog();
System.out.println(dog.setAge(5));
}
}

因为父类中没有setAge这个方法,因此会报错。

从上面这几段代码,可以看出使用多态也是有缺陷的,添加新方法都没有效果,只有覆盖才有效果。我们可以把它总结成如下这句话:

如果一个Dog类型的对象,它的引用却是其他类型,那么则不能使用Dog类型中的方法,除非引用类中也有这个方法。

这里所谓的多态,在程序中你可以理解成,一个方法,它可以有不同的效果,那怎么实现不同的效果呢?在java中通过切换类型来实现(不一定正确)。

多态有什么用?

还是再来看几个例子吧

class Animal{
public int age = 5; public int getAge(){
return age;
} } class Dog extends Animal{
public int getAge(){
return age + 2;
}
} class Cat extends Animal{
public int getAge(){
return age + 3;
}
} class app{
public static void main(String[] args){
Animal dog = new Dog();
Animal cat = new Cat(); System.out.println(dog.getAge());
System.out.println(cat.getAge());
}
}
------Output------
7
8

可以看到,它会根据自身执行不同的方法。不过话说回来,这并不能代表什么,毕竟我们按照正常情况来创建,效果也可以一样,不过还真有它的用武之处,比如下面这段代码

class Animal{
public int age = 5; public int getAge(){
return age;
} } class Dog extends Animal{
public int getAge(){
return age + 2;
}
} class Cat extends Animal{
public int getAge(){
return age + 3;
}
} class app{
public static void main(String[] args){
Animal[] animals = new Animal[2];
animals[0] = new Dog();
animals[1] = new Cat(); System.out.println(animals[0].getAge());
System.out.println(animals[1].getAge());
}
}
------Output------
7
8

这段代码和上面一段差不多,不过这段代码中用的是一个数组,这种情况就比较适合使用多态了,不然好像没有其他办法来弄了吧(初学java,不太懂)。在这里面多态不仅仅只是指一个方法有不同的效果,在这里还指类型的多样性。

好了就到这里了。

推荐阅读

Java中函数的重载和重写

Java中的封装、继承、多态的更多相关文章

  1. java面向对象(封装-继承-多态)

    框架图 理解面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是一种思想 面向过程强调的是功能行为 面向对象将功能封装进对象,强调具备了功能的对象. 面向对象是基于面向过程的. 面向对象的特点 ...

  2. Java三大特性(封装,继承,多态)

    Java中有三大特性,分别是封装继承多态,其理念十分抽象,并且是层层深入式的. 一.封装 概念:封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将抽象得到的数据 ...

  3. Java基础——面向对象(封装——继承——多态 )

    对象 对象: 是类的实例(实现世界中 真 实存在的一切事物 可以称为对象) 类: 类是对象的抽象描述 步骤: 1.定义一个类 (用于 描述人:) ( * 人:有特征和行为) 2.根据类 创建对象 -- ...

  4. java类的封装 继承 多态

    1.猜数字小游戏 package cn.jiemoxiaodi_02; import java.util.Scanner; /** * 猜数字小游戏 * * @author huli * */ pub ...

  5. java四大特性理解(封装继承多态抽象)

    封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口.面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治.封装的对象,这些对象通过一个受保护的接口访问其他对象.封装是一 ...

  6. 浅谈学习C++时用到的【封装继承多态】三个概念

    封装继承多态这三个概念不是C++特有的,而是所有OOP具有的特性. 由于C++语言支持这三个特性,所以学习C++时不可避免的要理解这些概念. 而在大部分C++教材中这些概念是作为铺垫,接下来就花大部分 ...

  7. php面向对象 封装继承多态 接口、重载、抽象类、最终类总结

    1.面向对象 封装继承多态  接口.重载.抽象类.最终类 面向对象 封装继承多态  首先,在解释面向对象之前先解释下什么是面向对象? [面向对象]1.什么是类? 具有相同属性(特征)和方法(行为)的一 ...

  8. Java基础笔记-抽象,继承,多态

    抽象类: abstract修饰 抽象方法必须定义在抽象类中,抽象类不能创建对象. 在抽象方法中可以不定义抽象方法,作用是:让该类不能建立对象. 特点是: 1.定义在抽象类中 2.方法和类都用abstr ...

  9. c#中的里氏转换和Java中强制类型转换在多态中的应用

    在c#中: 注意: 子类并没有继承父类的构造函数,而是会默认调用父类那个无参数的构造函数. 如果一个子类继承了一个父类,那么这个子类除了可以使用自己的成员外,还可以使用从父类那里继承过来的成员.但是父 ...

  10. Python 入门 之 面向对象的三大特性(封装 / 继承 / 多态)

    Python 入门 之 面向对象的三大特性(封装 / 继承 / 多态) 1.面向对象的三大特性: (1)继承 ​ 继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可以 ...

随机推荐

  1. photo中的图文混排

    photoshop中无法象word中自动图文混排,但可以通过手工绘制路径完成图文混排. 如下图,先摆放图像,然后绘制路径,然后在路径中输入或粘贴文字即可.

  2. mark一篇文章--用nodejs搭建一个本地反向代理环境

    调试线上代码的时候,我们经常遇到的一个问题就是 本地一套环境,线上一套环境,本地没有的文件用线上的这种需求.我简单来说下使用nodejs如何做到. 先说下不用nodejs我们怎么做,工具比如fiddl ...

  3. Python+Selenuim测试网站,只能打开Firefox浏览器却不能打开网页的解决方法

    最开始我使用的Selenium版本为2.48,Firefox版本为37,自动化打开网站的时候,可以正常打开. 后来由于Firefox的自检测更新,版本更新为47,导致版本不兼容,自动化打开网站浏览器时 ...

  4. Javascript Jquery 中的数组定义与操作_子木玲_新浪博客

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  5. 自动化运维 Ansible

    自动化运维 Ansible 特性 (1).no agents:不需要在被管控主机上安装任何客户端: (2).no server:无服务器端,使用时直接运行命令即可: (3).modules in an ...

  6. Linux磁盘配额实验

    1.实现磁盘限额的条件 *需要Linux内核支持 *安装quota软件包2.Linux磁盘限额的特点 作用范围:针对指定 文件系统(分区) 限制对象:普通用户帐号.组帐号 限制类型:磁盘容量(默认单位 ...

  7. mysql面试

    第一方面:30种mysql优化sql语句查询的方法 避免全表扫描: 1.where 及 order by 上建立索引.2.避免在 where 子句中使用!=或<>操作符3. select ...

  8. 数据库ER图 PowerDesigner

    一.概念数据模型概述数据模型是现实世界中数据特征的抽象.数据模型应该满足三个方面的要求:1)能够比较真实地模拟现实世界2)容易为人所理解3)便于计算机实现 概念数据模型也称信息模型,它以实体-联系(E ...

  9. NSURLRequest POST方式请求服务器示例

    http://lizhuang.iteye.com/blog/1833297 1.  准备阶段 NSString *urlString = [NSString stringWithFormat:@&q ...

  10. BMP085气压传感器驱动

    BMP085是新一代的小封装气压传感器,主要用于气压温度检测,在四轴飞行器上可以用作定高检测,该传感器属于IIC总线接口,依然沿用标准IIC驱动程序 使用该传感器需要注意的是我们不能直接读出转换好的二 ...