Java教程专栏:https://blog.csdn.net/qq_41806966/category_9929686.html
hello,I'm shendi
这节学习面向对象三大特性
目录
面向对象三大特性
面向对象三大特性分别为
- 封装(不要告诉我你是怎么做的,只要做就可以了)
- 继承(使一个类与另一个类建立父子关系)
- 多态(事物的多态性)
先从封装开始
封装
封装是三大特性中最简单的
用的多的地方就是Java Bean了
*Java Bean 就是一个类,只不过这个类比较特别,是专门用表示数据的,这里先了解下
那么,什么是封装呢?
封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
通俗的讲,封装就是将变量私有化(private),并且在类里提供对此变量操作的方法
封装是一种思想,不止局限于变量.
举个粒子
- 我有一台手机,这台手机属于我,别人可以使用一下,但是不能改变这个手机属于我的事实
- 所以这台手机是私有的,别人可以获取,但是不能对此变量进行设置,这就是对手机做了封装 (只提供获取的操作)
- 有一个奇怪的房子,当有人打开房门时会记录次数,并将此人信息记录
- 记录的次数是一个变量,打开房门是一个方法.
为什么需要封装呢?
通过上面的例子举栗子
- 手机是一个变量,如果是public的,也就是说别人都可以随意修改我持有的手机,甚至可以改成null(空),如果别人改成空就代表我没有手机了,所以,这当然不是我想要的,于是把变量私有化,并且提供一个公有的方法来获取这个手机
- 例2更加容易理解,有一个变量为记录打开房门的次数,并且打开房门会使得次数+1
- 打开房门的次数没有人能够知道,除了房子本身.
- 打开房门的次数可以通过打开房门来进行修改,但是这个修改是被限制的(每次+1,而不是随意修改)
实例
读万卷书不如行万里路,敲代码更助于学习理解
我们实现一个电子书籍类
每一个书籍都有书名,作者,评论数,赞,不赞等数据
对于用户来说,书名,作者,是不可修改的,但是可以知道内容
用户可以发表评论,赞此书,以及不赞此书
先自己尝试着敲代码,然后再看下方的示例代码
示例代码
此类中有五个变量,对应的有五个方法,代码如下
/** create with Shendi */
public class Encapsulation {
/** 采用驼峰式命名,之前讲过首单词小写,后续单词的第一个字母大写
* 书名 */
private String bookName = "神奇的书";
// 作者
private String author = "当然是神仙";
// 评论数
private int cNum = 0;
// 赞数
private int goodNum = 0;
// 不赞数
private int noGoodNum = 0;
// 评论
public void comment(String c) {
System.out.println("评论了: " + c);
// 这里评论数要+1, 此方法对评论数做了封装
cNum++;
}
public void good() {
System.out.println("神奇人物赞了" + bookName);
// 赞数++
goodNum++;
}
public void noGood() {
System.out.println("神奇人物觉得此不行: " + bookName);
// no赞数++
noGoodNum++;
}
// 获取书名,书名不可以被用户修改,封装了书名
// 这里的get是获取的意思,对于变量的封装的获取操作,前缀都应该为get(是一种规范)
public String getBookName() {
return bookName;
}
// 获取作者
public String getAuthor() {
return author;
}
}
对一个变量的封装通常分为两种,一种是获取(get),一种是设置(set)
对于获取,方法名应该以 get 开头
对于设置,方法名应该以 set 开头
这是一种规范,因为后面会使用到 IDE(编辑器),里面可以提示一个类有某些方法...
在列举一种非常常见的封装代码
对一个变量提供get和set方法
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
这种就是又提供获取又提供设置
- 获取的方法是返回一个int,因为是获取
- 设置的没有返回值,但是需要传递一个参数,代表你要设置的值
方法里的 this 关键字之前讲过,代表使用当前类里的对象,而不是方法里的(因为num和num重名了,如果不用this就会使用方法里的num,而达不到想要的效果)
那么,你可能会有疑问了,既然又提供get,又提供set,那么为什么要将变量私有化呢?为什么不直接public?
因为封装的作用不仅仅在于此,可以解耦合,如果直接使用变量,当我们需求更改,需要改变那个变量的时候,例如上方的num要改为字符串类型
这个时候使用到此变量的类都会出错,当使用的类很多,我们需要一个一个去修改(耦合在一起了),那得多恐怖
但是我们提供了封装的方法,例如getNum();
当我们将 num 变量改成字符串的时候,出错的只有 getNum() 这个方法,我们可以只用修改此方法来达到更改需求的目的
继承
程序语言为了更加便于理解,于是越来越人性化,越来越贴近生活
继承可以让两个类建立父子关系
需要知道的是,在 Java 中只允许单继承,但是可以多实现
使用 extends 关键字来表示继承
例如
class A extends B {}
这个时候,A就是B的子类(儿子),B就是A的父类(父亲)
父子关系
当你的类继承了另一个类,那么子类会自动继承父类的属性和行为,在之前学过的权限访问修饰符中可以看出,除了private修饰的属性/方法,其余的子类也会自动拥有
子类对象可以自动转换成父类,但是父类转换成子类需要强制转换
例如
class B {
}
class A extends B {
}
class Test {
........main() {
A a = new A();
B b = a;
}
}
但是父类转换子类需要强制转换
....main() {
B b = new B();
// 强制转换的语法是在需要被转换的对象前加一个括号,括号内是要转换的类型
A a = (B) b;
}
子类对象转父类称为向上转型(向上的),父类对象转子类称为向下转型
需要注意的是,父类对象转子类,如果此对象不是子类对象,则会出现转换错误
很好理解,因为子类属于父类,但是父类不属于子类
多实现
我们还没有学习接口,后续将会进行学习
一个新的关键字 implements 用于实现接口
Java中,类只能继承类,但是可以实现接口
接口就是只定义一些行为(方法)的特殊类
实现接口的代码
class A implements B {
}
与继承一样,也可以进行转换
不同的是可以实现多个接口
并且子类需要实现接口里的所有方法
例如接口里有一方法a
那么子类也需要写一个方法a
方法的重写
在子类中,可以写一个与父类的方法一模一样的方法,那么使用这个方法的时候使用的就是子类的方法
这种被称为方法的重写
多态
多态可能难以理解,当学习了设计模式后会对多态以及继承的使用有一个更深的体会
什么是多态?
多态也是一种思想,事物的多态性,比如说,相同的方法,不同的对象使用有不同的结果
例如鱼类,有的鱼吃肉,有的鱼吃草...
实现多态的必要条件是继承
实例
直接实践比较容易理解
就以鱼来举栗子
每个鱼都有吃的方法
A鱼吃肉,B鱼吃草
代码如下
public class Fish {
public void eat() {
System.out.println("父类的鱼吃啥?");
}
}
class FishA extends Fish {
public void eat() {
System.out.println("A鱼吃肉");
}
}
class FishB extends Fish {
public void eat() {
System.out.println("B鱼吃草");
}
}
上面的鱼A和鱼B都继承Fish类,并重写了eat方法
那么,如何体现多态呢?
接下来我们编写main函数和提供一个格外的方法
public class Fish {
public void eat() {
System.out.ptintln("父类的鱼吃啥?");
}
public static void main(String[] args) {
FishA a = new FishA();
FishB b = new FishB();
fishEat(a);
fishEat(b);
}
// 这里是静态的方便使用
// 参数是父类,这样我们可以实现多态
public static void fishEat(Fish f) {
// 如果传递的子类对象这里调用的就是子类的方法
f.eat();
}
}
编译运行
输出结果如下
A鱼吃肉
B鱼吃草
可以看出,一个方法,传递不同对象有不同结果
下一节学习接口