目录
面向对象程序三大特性:封装、继承、多态
1. 什么是面向对象
Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。
2. 面向对象与面向过程
面向过程
我们以洗衣服为例子
传统洗衣服
传统的方式:注重的是洗衣服的过程,少了一个环节可能都不行。
面向对象
现代洗衣服方式
我们可以只注重衣服,洗衣机,洗衣粉这几个对象,而不用关心洗衣机怎么去怎样把衣服洗干净
以面向对象方式来进行处理,就不关注洗衣服的过程,具体洗衣机是怎么来洗衣服,如何来甩干的,用户不用去关心,只需要将衣服放进洗衣机,倒入洗衣粉,启动开关即可,通过对象之间的交互来完成的。
面向过程和面向对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景。
3. 封装
引入封装
何为封装呢?简单来说就是制造外壳保护细节
举个例子:计算机
我们平时见到的台式计算机只是一个外壳,我们可以接触到的只有开关键。但计算机内部如何去运行,管理,保存数据,我们对此是不了解的。 因此我们可以认为计算机生产厂商,为了保护计算机内部去套壳,既保护了电脑,也保护了计算机内部运行的信息。
下面我们开始正式的讲解封装
我们先写出正常代码
//类
class Student{
//成员属性
String name;
int age;
//成员方法
public void eat(){
System.out.println(this.name + "在吃饭");
}
public void sleep(){
System.out.println(this.name + "在睡觉");
}
public void show(){
System.out.println("姓名 " + this.name + " 年龄 " + this.age);
}
}
//类的使用者
public class Blog {
public static void main(String[] args) {
//实例化一个对象
Student student = new Student();
student.name = "小罗";
student.age = 10;
//调用方法
student.eat();
student.sleep();
student.show();
}
}
这里的Student类 就好比 是前文所说的计算机,而其中的成员变量和成员方法就好比是计算机内部的器件和运行规则。而我们的 Blog 类 就好比是我们自己(实际使用者)。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互
封装的具体实现
用private修饰,此时已经报错
那我们需要用到成员变量时要怎么办呢?
我们可以自己在类中创造方法来 获取和设定 成员变量(Java中已经给好了相应的方法)
get方法和set方法
我们可以通过get方法和set方法,去获取和设定 成员变量
class Student{
//成员属性
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//成员方法
public void eat(){
System.out.println(this.name + "在吃饭");
}
public void sleep(){
System.out.println(this.name + "在睡觉");
}
public void show(){
System.out.println("姓名 " + this.name + " 年龄 " + this.age);
}
}
//类的使用者
public class Blog {
public static void main(String[] args) {
//实例化一个对象
Student student = new Student();
//调用set get方法
student.setName("小罗");
student.setAge(10);
System.out.println(student.getName());
System.out.println(student.getAge());
student.eat();
student.sleep();
student.show();
}
4. 继承
为什么需要继承
Java 中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是现实世界错综复杂,事物之间可能会存在一些关联。因此需要一些程序设计来解决这个问题。
下面通过代码来发现问题
//定义一个狗类
class Dog{
public String name;
public int age;
public String color;
public void eat() {
System.out.println(this.name + "在吃东西");
}
public void run() {
System.out.println(this.name + "在跑");
}
}
//定义一个鸟类
class Bird{
public String name;
public int age;
public String color;
public void eat() {
System.out.println( this.name + "在吃东西");
}
public void fly() {
System.out.println( this.name + "在飞");
}
}
public class blog {
public static void main(String[] args) {
Dog dog = new Dog(); //创建对象
//初始化
dog.name = "小灰";
dog.age = 10;
dog.color = "灰色";
//调用方法
dog.eat();
dog.run();
Bird bird = new Bird();
bird.name = "小花";
bird.age = 10;
bird.color = "花色";
bird.eat();
bird.fly();
}
}
我们可以看到Dog类和Bird类中有以下相同的成员变量和成员方法
public String name;
public int age;
public String color;
public void eat() {
System.out.println(this.name + "在吃东西");
}
因此 我们为了节省代码,提高代码的复用性,我们引入了继承这个概念。
继承的概念
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性
的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了
由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
继承的语法
修饰符 class 子类名 extends class 父类名 {
// ...
}
关键字是 extends
因为 小狗和小鸟 都是属于动物,他们有自己的年龄、颜色等等属性。也会吃东西等等相同动作,并且作为宠物都会给他们给予名字 。
因此 我们创建一个新的类(Animals) 作为父类,并将 这些相同的属性和动作作为成员变量和成员方法 放入其中。
以下代码为使用继承之后。我们可以明显的看到 代码少了很多。
并且在现实生活中我们只需要关注子类特有的属性和方法即可
class Animals{
public String name;
public int age;
public String color;
public void eat() {
System.out.println(this.name + "在吃东西");
}
}
class Dog extends Animals{
public void run() {
System.out.println(this.name + "在跑");
}
}
class Bird extends Animals{
public void fly() {
System.out.println( this.name + "在飞");
}
}
public class blog {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "小灰";
dog.age = 10;
dog.color = "灰色";
dog.eat();
dog.run();
Bird bird = new Bird();
bird.name = "小花";
bird.age = 10;
bird.color = "花色";
bird.eat();
bird.fly();
}
}
继承关系的执行顺序
在之前的学习中我们知道了
1.静态代码块先执行,并且只执行一次,在类加载阶段执行
2.当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
class Persons{
String name;
int age;
public Persons(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person构造方法被调用");
}
{
System.out.println("Person实例代码块被调用");
}
static {
System.out.println("Person静态代码块被调用");
}
}
class Students extends Persons{
public Students(String name, int age) {
super(name, age);
System.out.println("Students构造方法被调用");
}
{
System.out.println("Students实例代码块被调用");
}
static {
System.out.println("Students静态代码块被调用");
}
}
public class job {
public static void main(String[] args) {
Students students = new Students("小罗",18);
Students students2 = new Students("小米",18);
}
}
通过分析执行结果,得出以下结论:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
继承方式
单继承
多层继承
不同类继承同一个类
最重要的是,不可以同一个类继承不同的类
5. 多态
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
ps:这张图看起来很熟悉,没错,这张图的思路就是基于继承。
多态举例子来说的话,就是动物会吃东西 ,但具体到小狗时就是小狗吃狗粮,小猫吃猫粮。因此多态简单来说,就是同一件事情,发生在不同对象身上,就会产生不同的结果。
多态实现涉及的知识点
1.方法重写
2.向上转型
3.继承(上篇文章已经讲述过)
因此我们先学习 向上转型 和 方法重写 这样到最后,我们才能更好的理解多态和实现多态
向上转型和向下转型
向上转型
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
例: Animals animals = new Cat();
下面我们看具体的代码
class Animals{
String name;
int age;
public void eat(){
System.out.println("在吃东西");
}
}
class Cat extends Animals{
@Override
public void eat() {
System.out.println("在吃猫粮");
}
}
class Dog extends Animals{
@Override
public void eat() {
System.out.println("在吃狗粮");
}
}
public class material {
public static void main(String[] args) {
Animals animals = new Dog(); // 向上转型
//父类引用 子类对象
}
}
把一个子类对象赋值给父类引用,这意味着对象的范围变大了,因此向上转型指的是子类对象。
我们可以举一个例子,Animals animals = new Dog(); ,此语句可以用语言理解为我们把小狗归为动物类或者我们说小狗就是一种动物
向上转型的主语就是子类对象
而向上转型还有三种形式
1. 直接赋值
2. 方法传参
3. 方法返回值
public class material {
//方法返回
public static Animals test(){
return new Dog();
}
//方法传参
public static void func(Animals animals){
}
}
public static void main(String[] args) {
//1.直接赋值
Animals animals = new Dog(); // 向上转型
//父类对象引用 子类对象
//2.方法传参
// public static void func(Animals animals){
// }
func(new Dog());
//3.方法返回
// public static Animals test(){
// return new Dog();
// }
test();
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
根据向上转型的缺陷,我们就引出了向下转型。
向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型。
向下转型的主语是父类对象引用
Animals animals = new Animals(); // 正常创建父类对象
Dog dog = (Dog)animals; // 将父类对象引用 向下转型
Animals animals = new Dog();// 向上转型 大范围 <-- 小范围
Dog dog = (Dog)animals; // 向下转型 小范围 <-- 大范围
提示:千万不能随意使用向下转型,因为强制类型转换可能会使数据丢失
方法重写
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。
简而言之,就是子类能够根据需要实现父类的方法。
方法重写的要求:
1. 方法名和父类相同
2. 方法的参数列表相同(个数,类型,顺序)
3. 返回值相同
class Animals{
String name;
int age;
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animals{
@Override
public void eat() {
System.out.println("小狗在吃狗粮");
}
}
public class test {
public static void main(String[] args) {
Animals animals = new Animals();
animals.eat();
Animals animals1 = new Dog();
animals1.eat();
}
}
像代码 Animals animals1 = new Dog(); animals1.eat(); 最后却调用了子类的eat方法。这种行为我们称为动态绑定
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。
方法重写注意事项:
1. 静态方法(static) 不能被重写
2. 被private修饰的方法 不能被重写
3. 被final修饰的方法 不能被重写
4. 如果方法被重写,那么子类的访问权限一定要 大于 等于 父类的访问权限
多态实现条件
在java中要实现多态,必须要满足如下几个条件,缺一不可:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
class Animals{
String name;
int age;
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animals{
@Override
public void eat() {
System.out.println("小狗在吃狗粮");
}
}
public class test {
public static void main(String[] args) {
Animals animals = new Animals();
animals.eat();
Animals animals1 = new Dog();
animals1.eat();
}
}