面向对象的程序有三大特性 -- 封装, 继承 和 多态.而Java作为一个面向对象的程序设计语言,自然有如上三中特性.今天,就跟着小编的节奏,来探索Java中的"三剑客"究竟是怎样的吧!
目录
一.封装
封装,顾名思义,就是把某些东西封闭包装起来.那么,在Java中,封装就是隐藏对象的属性和实现细节,仅对外公开接口与对象进行交互.
Java主要通过访问限定符来实现封装.访问限定符我们前面介绍过,这里再复习一下.
Java通过这些访问限定符,就能实现封装.
二.继承
1.继承的概念
在讨论继承之前,我们先思考一下为什么要继承?现实世界错综复杂,某实体对象和某实体对象之间有可能存在一些共同之处.在用代码描述他们的时候,就会写出重复冗余的代码.那么,为了避免这种情况出现,我们就可以将他们的共同之处提取出来,只写一份代码,这样就有效地避免了代码的重复.
继承(inheritance):通过共性的抽取,从而实现代码的复用.
例如:猫和狗都是动物,都有共同的动物的属性.
从上面代码我们可以看到,狗和猫都有共同的属性name,age,weight,还有共同的方法eat和sleep.
那么我们就可以将这部分共性抽取出来写成一个动物类,再由狗类和猫类去继承.
我们使用extends关键字来实现继承,基本语法形式为:
子类 extends 父类{
}
注意:
(1) 子类通过继承的方式 将父类的成员变量/ 成员方法 继承到子类中来了.
(2) 子类在继承父类以后,必须添加自己特有的成员,否则就没有必要继承了.
2.父类成员的访问
在继承体系中,子类将父类的成员变量和成员方法都继承下来了,那么在子类中能否直接访问从父类继承下来的成员呢?
(1) 子类访问父类的成员变量
子类在访问某个变量时:
- 如果子类有, 则访问子类的.
- 如果子类没有,父类有, 则访问父类的.
- 如果子类父类都有, 则优先访问子类的. (这里注意,虽然优先访问子类中的,但是如果我就是要访问父类中的可不可以呢? -- 答案是可以的, 这里需要借助到super关键字.)
(2) 子类访问父类的成员方法
子类在调用某方法时:
- 如果子类有, 则访问子类的.
- 如果子类没有,父类有, 则访问父类的.
- 如果子类父类都有, 则优先访问子类的. (这里注意,如果非要访问父类的方法,还需要借助super关键字.)
(3) super关键字
在某些情况下,子类和父类中可能会存在相同名称的成员.这样的情况下,为了能够访问到父类的成员,Java提供了super关键字.该关键字的主要作用是:在子类方法中访问父类的成员.
如上图,使用super关键字就可以访问到父类的成员.
那么,在继承体系中,super和this的关系又是什么呢?下面我为大家画一张图解释.
可以理解为this表示整个子类对象的引用,而super表示子类对象中从父类继承下来的那部分的引用.
注意:在静态方法中,不能使用super.因为没有对象的引用.
3.子类的构造方法
在继承体系中,先有父再有子. 所以,在实例化子类对象的时候,要先帮助从父类继承下来的成员进行构造(调用父类的构造方法),再调用子类自己的构造方法,将子类自己新增的成员构造完整.
但是:我们通过前面的代码发现,即使我们什么都不写,编译器也不会报错.这又是为什么呢? -- 如果用户什么都不写,那么编译器会默认给父类一个无参的构造方法,同时也默认给子类一个无参的构造方法(里面默认包含一个super()语句调用父类构造方法). 如下图所示:
这里有几点注意:
- 子类构造方法调用父类构造方法时,必须是第一条语句(super()语句必须是第一条语句).
- 当用户写了任何一种构造方法时,编译器都不会再提供无参的构造方法.
- 当继承体系中包含代码块时,执行顺序为:
1) 父类的静态代码块 -- 子类的静态代码块
2) 父类的实例代码块 -- 父类的构造方法
3) 子类的实例代码块 -- 子类的构造方法
输出结果如上图所示,怎么样,是不是和我们预先说的顺序完全一致呢~
那么,如果创建了两个子类对象,又会发生什么呢?
从上图我们可以看出,静态代码块只执行一次(因为静态代码块属于类的属性),而实例代码块和构造方法则是new几次对象就执行几次.
4.继承方式
(1) 单继承
形式:
class A{
// ...
}
class B extends A{
// ...
}
(2) 多层继承
形式:
class A{
// ...
}
class B extends A{
// ...
}
class C extends B{
// ...
}
(3) 不同类继承同一个类
形式:
class A{
// ...
}
class B extends A{
// ...
}
class C extends A{
// ...
}
5.final关键字
三.多态
1.多态
通俗来讲,就是"多种形态"(去完成某个行为,当不同的对象去完成时会产生出不同的状态).
实现多态的条件:
1.必须在继承体系下
2.子类必须要对父类中的方法进行重写
3.通过父类引用调用重写的方法
2.重写
重写(override), 也称为"覆写", 重写是子类对父类的方法进行重新编写.
重写的方法 返回值和形参都不能改变, 即:外壳不变,核心重写!
重写的好处在于子类可以根据需要来定义自己特定的行为.
方法重写的规则:
- 子类在重写父类方法时,一般必须与父类的方法原型一致: 返回值类型 方法名 (形参列表) 要完全一致.(注: 重写的方法返回值类型可以不同, 但是必须和父类返回值类型构成父子关系).
- 静态方法, 被private修饰的方法, 被final修饰的方法 都不能被重写.
- 子类的访问权限 必须 大于等于 父类的访问权限.
方法重载和方法重写的区别:
3.向上转型和向下转型
*向上转型
向上转型,实际上就是创建一个子类对象, 将其当成父类对象来使用. (把子类对象给到父类)
语法形式:
父类类型 对象名 = new 子类类型()
例如:
Animal animal = new Cat();
向上转型发生的时机:
(1) 直接赋值:
public static void main(String[] args){
Animal animal = new Dog();
}
(2) 方法传参:
public static void func(Animal animal) {
// ...
}
public static void main(String[] args) {
func(new Dog());
}
(3) 返回值返回:
public static Animal func2(){
return new Dog();
}
总结一句话:向上转型就是把子类对象给到父类.
*向下转型
向下转型,把父类对象给到子类.(因为向下转型不安全,所以我们不常用).
这里我们以Dog类,Cat类和Animal类举例:
打印结果:
我们这里再继续将animal向下转型.
但是我们如果将animal向下转型成狗类,就不会报错.
4.动态绑定
动态绑定:在编译时没有确定调用哪个方法,在运行时才确定 .
例如:在编译时编译器认为是调用父类的方法,但是运行时却调用了子类重写父类的方法.这就叫做动态绑定.
与之相对应的是静态绑定:就是编译时已经确定了调用哪个方法.(编译时认为调用哪个方法, 运行时就调用哪个方法).
发生动态绑定的条件(两点):
1. 父类引用子类对象(发生向上转型)
2. 通过父类引用调用重写的方法
5.多态的代码案例
public class Shape {
public void draw(){
System.out.println("画图形");
}
}
class Rect extends Shape{
@Override
public void draw() {
System.out.println("画一个矩形");
}
}
class Cycle extends Shape{
@Override
public void draw() {
System.out.println("画一个圆圈");
}
}
class FLower extends Shape{
@Override
public void draw() {
System.out.println("画一朵花儿");
}
}
我们在这里调用一下其中一个方法:
运行结果:
好了,关于Java"三剑客" -- "封装 继承 多态"的内容到这里就差不多了.感觉如何?
那么,以上就是本篇博客的全部内容啦,如果喜欢小编的文章,可以点赞,评论,收藏~