面向对象语言的三大特性之封装、继承、多态

目录

    • 一.面向对象
      • 1.1.怎么去形容面向对象呢?
      • 1.2.类和对象的概念
      • 1.3.static关键字详解
    • 二.封装
        • 2.1.什么是封装?
        • 2.2.private实现封装
    • 三.继承
    • 四.多态
      • 4.1.向上转型
      • 4.2.方法重写
      • 4.3.动态绑定
      • 理解多态

一.面向对象

C语言相信大家应该都不陌生,它是一门面向过程的语言,关注的是过程,通过分析求解问题的过程,再通过函数逐步调用,最后解决问题。

而Java是基于面向对象的,关注的是对象,将一件事拆分为多个对象,通过对象之间的交互解决问题。

1.1.怎么去形容面向对象呢?

  • 简单来说面向对象就是用代码(类)来描述客观世界的事物的一种方式, 一个类主要包含一个事物的属性和行为。面向对象其实就是一种思考问题的方式,是一种思想

所以在用面向对象的思想去解决问题时就可以分为:找对象,建对象,用对象,并维护对象之间的关系。

1.2.类和对象的概念

类就是一类对象的统称。对象就是这一类具体化的一个实例。

举个例子:盖房子的时候我们会有设计图纸,设计图纸就是一个类,而通过这个图纸我们就可以盖出房子,那么在这个例子当中,类就是那个图纸,而房子就是那个对象,所以房子就是一个实体。一个图纸可以实例化无数个对象(当然,正常情况下一个图纸建一个房子就好了,家里有矿的无所谓,你可以认为是在建别墅群)。

总的来说:类相当于一个模板,对象是由模板产生的样本。一个类,可以产生无数的对象。

声明一个类就是创建一个新的数据类型,而类在 Java 中属于引用类型, Java 使用关键字 class 来声明类。在一个类中有属性和方法,实例化通过new 关键字就可以创建一个对象,使用 . 来访问对象中的属性和方法。

1.3.static关键字详解

static关键字的作用

1.修饰属性

静态属性和类相关,和对象无关。就是说一个用static修饰的属性,它只是依赖于当前类,只能通过类名 . 属性访问可以改变属性值。通过当前类实例化后产生的对象无法访问,所以和对象无关。

class TestDemo{
        public int a;
        public static int count;
}
public class Java20210531 {
    public static void main(String[] args) {
        TestDemo s1 = new TestDemo();
        s1.a++;
        TestDemo.count++;
        System.out.println(s1.a);   //1
        System.out.println(TestDemo.count); //1
        System.out.println("============");
        TestDemo s2 = new TestDemo();
        s2.a++;
        TestDemo.count++;
        System.out.println(s2.a);   //1
        System.out.println(TestDemo.count); //2
    }
}

2.修饰方法

如果在任何方法上应用 static 关键字,此方法称为静态方法

1.一样的,静态方法属于类,而不属于类的对象。
2.可以直接调用静态方法,而无需创建类的实例(对象)。
3.静态方法可以访问静态数据成员,并可以更改静态数据成员的值

class TestDemo{
    public int a ;
    public static int count;
    public static void change() {
    count = 20;
    //a = 10; error 不可以访问非静态数据成员
    }
}
public class Java20210531 {
    public static void main(String[] args) {
    TestDemo.change();//无需创建实例对象 就可以调用
    System.out.println(TestDemo.count);	//20
    }
}

注意事项: 静态方法和对象无关, 而是和类相关. 因此这导致了两个情况(重点,选择题易错):

  • 静态方法不能直接使用非静态数据成员或调用非静态方法(非静态数据成员和方法都是和实例相关的).
  • this和super两个关键字不能在静态上下文中使用(this 是当前对象的引用, super是当前对象对父类对象的引用, 也是和当前对象相关).

二.封装

2.1.什么是封装?

软件开发本就是一个复杂的过程,如果代码程度复杂都过高,就不好维护,所以就引入一种管理代码的方式——封装。
在写代码的时候会涉及到两种角色:类的实现者和类的调用者

  • 封装的本质就是让类的调用者不必要了解类的实现者是怎么实现类的,只需要知道如何使用就可以了。这样就降低了类使用者的学习和使用成本, 从而降低了复杂程度
2.2.private实现封装

Java 中对于字段和方法共有四种访问权限 。

权限大小:private < default < protected < public

当我们使用 private 来修饰字段的时候, 就无法直接使用这个字段了.
此时如果需要获取或者修改这个 private 属性, 就需要使用 getter / setter 方法

class Person {
	private String name;//实例成员变量
	private int age;
	public void setName(String name){
	//name = name;//不能这样写
	this.name = name;//this引用,表示调用该方法的对象
	}
	public String getName(){
	return name;
	}
	public void show(){
	System.out.println("name: "+name+" age: "+age);
	}
}
public static void main(String[] args) {
	Person person = new Person();
	person.setName("tao");
	String name = person.getName();
	System.out.println(name);
	person.show();
	// 运行结果
	//tao
	//name: tao age: 0
}

注意事项

  • getName 即为 getter 方法, 表示获取这个成员的值。setName 即为 setter 方法, 表示设置这个成员的值
  • 当set方法的形参名字和类中的成员属性的名字一样的时候,如果不使用this, 相当于自赋值。this 表示当前对象的引用

三.继承

在创建类的过程本身就是为了抽象现实中的一些事物(包含属性和方法),有时候现实中的事物会有一些特定的关系,比如实现三个类,Animal类,cat类,dog类,我们会发现cat类和dog类都同属于Animal类,这种关系称之为继承is a 表示关系

继承:使用extends关键字 ,A extends B
A:表示子类,派生类
B:表示父类,超类,基类

  • 继承的作用主要是为了实现代码复用
  • 子类继承父类除构造方法外的所以属性和方法
  • 因为构造方法无法被继承,所以在子类中可以通过super关键字调用父类的构造器,要先帮助父类进行构造

注意事项

另外final关键字的功能是限制类被继承,如下:

  • final关键字修饰一个变量或者字段的时候, 表示常量 (不能修改).
  • final关键字修饰类, 此时表示被修饰的类就不能被继承

面试问题:this和super的区别?

1.this表示当前对象的引用 ————————super表示父类对象的引用
2.this():调用当前对象的构造方法 ———— super():显示调用父类的构造方法(必须都放在第一行)
3.this.data:调用当前对象的属性 —————super.data:调用父类对象的成员属性
4.this.fun():调用当前对象的方法 ————— super.fun() :调用父类对象的方法

四.多态

在了解多态之前,我们需要先了解这几个概念:向上转型、方法重写、动态绑定。

4.1.向上转型

例如:

Animal cat= new Cat("小花");

在上面的代码中,我知道Cat是子类,Animal是父类,而子类继承了父类,cat就是一个被实例化出来的对象,也可以称之为是父类(Animal)的引用。
所以这行代码的意思:

此时 cat 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例(产生的对象). 这种写法称为 向上转型

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);
    }
}
// Bird.java
 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 void fly1() {
        System.out.println("飞");
    }
}
// Test.java
class Test {
    public static void main(String[] args) {
        Animal animal1 = new Animal("圆圆");
        animal1.eat("谷子");
        Animal animal2 = new Bird("扁扁");
        animal2.eat("谷子");
    }
}

向上转型在Java中非常的常见,但这里需要注意的通过实例化产生的对象只能访问父类的方法和属性(子类的方法可能大于父类的方法数量)

4.2.方法重写

子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override)

注意事项

  • 普通方法可以重写,static修饰的静态方法不能被重写,private权限的修饰方法不能被重写(private权限在当前类生效)
  • 子类如果重写父类的方法,那么子类访问修饰权限一定要大于等于父类的访问修饰的权限
  • 注意区分重写和重载的区别

重载和重写的区别?(面试问题)

重载要求方法名相同,参数类型和个数不同,返回值不做要求
重写要求方法名相同,参数类型和个数必须相同,放回值也要相同(注:构成协变类型可以不相同),final修饰的方法不能被重写

  • 协变返回类型一句话:子类覆盖父类方法,返回类型可以是,父类返回类型的子类。

4.3.动态绑定

当子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢?
其实这样就会打印出来子类的方法,这个时候就会很奇怪,不应该是调用父类,打印父类的方法嘛,在这个过程当中就发生动态绑定,又叫运行时绑定。

概念1(太官方):在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的, 因此称为 动态绑定.

概念2(太口水话):在Java中,如果满足父类和子类中都存在一个相同的方法,通过父类的引用在调用这个方法的时候,程序编译的是父类方法,在运行时绑定到了子类的方法,最后打印出来子类方法,这个过程称为 动态绑定

注意:另外在构造方法中也可以发生动态绑定

动态绑定发生的前提

  • 一定要发生向上转型
  • 父类和子类有同名的重写/覆盖/覆写方法

理解多态

有了对上面这些知识的了解后,我们就可以理解什么是多态,总的来说,多态是一种思想,是对同一个行为表现多种不同的表现形式…(这是啥???完全没懂,先上代码)

class Animal {
    public void shout() {
        //啥也不干
    }
}
class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵");
    }
}
class  Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪");
    }
}
/我是分割线//
public class Java20210531 {
    public static void main(String[] args) {
        Animal animal1 = new Cat();
        Animal animal2 = new Dog();
		animalShout(animal1);
        animalShout(animal2);
        //喵喵喵
        //汪汪汪
    }
    public static void animalShout(Animal animal) {
        animal.shout();
    }
}

这个代码够简单了(捂脸),很容易理解,分割线上面是类的实现者编写,下面是类的调用者编写,当类的调用者在编写 animalShout这个方法的时候, 参数类型为 Animal (父类), 此时在该方法内部并不知道, 也不关注当前的 animal 引用指向的是哪个类型(哪个子类)的实例. 此时 animal这个引用调用 shout()方法可能会有多种不同的表现(和 animal对应的实例相关), 这种行为就称为 多态.

简单来说,多态就是在多个子类同时继承一个父类的情况下,并且每个子类都重写了父类的某个方法,再通过父类引用不同的子类对象,调用同一个方法时,就会根据你引用的子类对象动态绑定到子类的方法中。(表述如果有误,请指正)

总结

  • 多态发生的前提,要先发生向上转型,方法的重写,动态绑定
  • C++ 中的 “动态多态” 和 Java 的多态类似. 但是 C++ 还有一种 “静态多态”(模板), 就和继承体系没有关系了
  • Python 中的多态体现的是 “鸭子类型”, 也和继承体系没有关系
  • Go 语言中没有 “继承” 这样的概念, 同样也能表示多态

无论是哪种编程语言, 多态的核心都是让调用者不必关注对象的具体类型. 这是降低用户使用成本的一种重要方式

你可能感兴趣的:(JavaSE,多态,java,面向对象编程)