一说到java面向对象编程,我们就想到面向对象的三大特性,封装,继承和多态。封装是 Java 类的编写规范、继承是类与类之间联系的一种形式,而多态为系统组件或模块之间解耦提供了解决方案。
封装
概念:隐藏类的属性和具体实现细节,只提供相关的接口和方法来对隐藏信息进行控制和修改。
封装的思想保证了类内部数据结构的完整性,使用户无法轻易直接操作类的内部数据,这样降低了对内部数据的影响,提高了程序的安全性和可维护性。
封装的目的:
- 只能通过规定的方法访问数据
- 隐藏类的实现细节
- 方便加入控制语句
- 方便实现修改
访问控制符:
在进行封装的时候做好访问权限控制,目前在java中有四种访问控制以及它们的访问范围如下表所示:
default拒绝一切包外访问,protected接受包外的子类访问。
getter和setter方法:
getter | setter |
---|---|
用于获取属性的值 | 用于修改属性的名 |
public class Demo1 {
public String name; // 公有的
protected int age; // 受保护的
char sex; // 默认的
private int num; // 私有的
//获取私有成员num值
public int getNum() {
return num;
}
// 设置私有成员num的值
public void setNum(int num) {
this.num = num;
}
}
继承
概念:继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或类从父类继承方法,使得子类具有父类相同的行为。
继承的特点:
- 继承鼓励类的重用
- 继承可以多层继承
- 一个类只能继承一个父类
- 父类中private修饰的不能被继承
- 构造方法不能被继承
使用继承:
1.编写父类
有公共的属性和方法
package test;
/**
* 父类
*
*/
public class Father {
//父类中的成员属性
public String name;
public int age;
public char sex;
/**
* 父类中的成员方法
*/
public void say(){
System.out.println("我会说话");
}
public void eat(){
System.out.println("我会吃饭");
}
}
2.编写子类,继承父类:
- 子类继承父类时只能继承一个父类
- 继承的关键字 extends
package test;
pulic class Son extends Father {
}
子类访问父类成员
-
访问父类构造方法
super(); 父类无参的构造方法
super(name); 父类有参的构造方法 -
访问父类属性
super.name; -
访问父类方法
super.方法名();
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("猫粮");
Bird bird = new Bird("圆圆");
bird.fly();
}
}
如果我们把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 “封装” 的初衷.两全其美的办法就是 protected 关键字
- 对于类的调用者来说, protected 修饰的字段和方法是不能访问的
- 对于类的子类和同一个包的其他类来说, protected 修饰的字段和方法是可以访问的
多层继承:即子类还可以进一步的再派生出新的子类 。
经我们学习过 final 关键字, 修饰一个变量或者字段的时候, 表示常量 (不能修改) .final 关键字也能修饰类, 此时表示被修饰的类就不能被继承.
super关键字;爱访问父类的成员
- super只能出现在子类的方法和构造方法中;
- super调用构造方法时,只能是第一句;
- super不能访问法父类的private成员;
继承的限制和初始化顺序
子类可以继承父类的所有资源吗?
不能被继承的父类成员:
- private成员
- 子类与父类不在同包,使用默认访问权限的成员
- 构造方法
多重继承关系的初始化顺序是怎样的?
多态
概念:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式
多态的三个必要条件:
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载。
向上转型:
Bird bird = new Bird("圆圆");
Animal bird2 = bird;
// 或者写成下面的方式
Animal bird2 = new Bird("圆圆")
此时 bird2 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为 向上转型.
当子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢
package package2_17;
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal("圆圆");
animal1.eat("谷子");
Animal animal2 = new Bird("扁扁");
animal2.eat("谷子");
}
}
- animal1 和 animal2 虽然都是 Animal 类型的引用, 但是 animal1 指向 Animal 类型的实例, animal2 指向Bird 类型的实例.
- 针对 animal1 和 animal2 分别调用 eat 方法, 发现 animal1.eat() 实际调用了父类的方法, 而animal2.eat() 实际调用了子类的方法
在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为动态绑定。
方法重写
子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override).
注意事项:
- 重写和重载完全不一样. 不要混淆.
- 普通方法可以重写, static 修饰的静态方法不能重写.
- 重写中子类的方法的访问权限不能低于父类的方法访问权限.
重写和重载的区别:
从字面上看,重写就是重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。
在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。
区别 | 重载 | 重写 |
---|---|---|
概念 | 方法名称相同,参数的类型及个数不同 | 方法名称,返回值类型,参数类型及个数完全相同 |
范围 | 一个类 | 继承关系 |
限制 | 无限制要求 | 子类的方法的访问权限不能低于父类的方法访问权限 |
使用多态的好处是什么?
1) 类调用者对类的使用成本进一步降低.
- 封装是让类的调用者不需要知道类的实现细节.
- 多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.
2) 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
3) 可扩展能力更强.