(尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/84592274冷血之心的博客)
面向对象的语言有三大特性,即封装继承与多态。三大特性是面向对象编程的核心,对于初学者务必加强对三大特性的理解与领会。在这篇博客中,我们通过具体的案例来依次阐述封装继承与多态的概念与使用。
(一)封装
封装的定义
何为封装?
把事物抽象成一个类,将事物拥有的属性和动作隐藏起来,只保留特定的方法与外界联系。当内部的逻辑发生变化时,外部调用不用因此而修改,它们只调用开放的接口,而不用去关心内部的实现。
封装的好处
- 实现了专业的分工,将处理逻辑封装成一个方法,做到见名知其义
- 良好的封装能够减少耦合
- 隐藏信息,实现细节
接下来我们举两个封装的具体案例来进行阐述。
案例一:
package com.pak1;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.name = "小明";
student.age = 16;
student.printStudentAge();
Student student2 = new Student();
student2.name = "小白";
student2.age = 120;
student2.printStudentAge();
}
}
class Student {
String name;
int age;
public void printStudentAge() {
System.out.println(name + "同学的年龄:" + age);
}
}
输出如下:
这个时候我们可以看到输出的小白同学的年龄120明显不科学,所以我们需要做一些内部逻辑的处理。所以需要进行代码封装,将内部逻辑进行一个隐藏。
封装之后的代码如下:
package com.pak1;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("小明");
student.setAge(16);
student.printStudentAge();
Student student2 = new Student();
student.setName("小白");
student.setAge(120);
student2.printStudentAge();
}
}
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) {
if (age < 0 || age > 60)
throw new RuntimeException("年龄设置不合法");
this.age = age;
}
public void printStudentAge() {
System.out.println(name + "同学的年龄:" + age);
}
}
输出结果如下:
我们将Student这个类的name和age私有化,提供了公共的get/set方法才能进行访问,在get/set方法中我们可以对内部逻辑进行封装处理,外部的调用方不必关心我们的处理逻辑。
我们再来看另一种封装,在一个方法中,太多的逻辑将会导致该方法违反了单一性,导致该方法的可读性变差,这个时候我们需要将可以拆成一块的方法进行拆分,建立相应的service或者utils来封装该方法。举例如下:
案例二:
package com.pak1;
public class TestFZ {
public static void main(String[] args) {
int score = 78;
if (score > 100 || score < 0) {
System.out.print("对不起,你的分数输入有错误");
} else if (score > 89) {
System.out.print("奖励你一台手机");
} else if (score > 79) {
System.out.print("奖励你一副耳机");
} else if (score > 59) {
System.out.print("奖励你一朵小红花");
} else if (score > 59) {
System.out.print("奖励你一朵小红花");
}else {
System.out.print("奖励你一个耳光");
}
}
}
好了,这部分的处理逻辑很复杂,看起来可读性也很差,这个时候我们需要封装。封装之后的代码如下:
package com.pak1;
public class TestFZ {
public static void main(String[] args) {
int score = 78;
TestFZ testFZ = new TestFZ();
testFZ.printReward(score);
}
public void printReward(int score) {
if (score > 100 || score < 0) {
System.out.print("对不起,你的分数输入有错误");
} else if (score > 89) {
System.out.print("奖励你一台手机");
} else if (score > 79) {
System.out.print("奖励你一副耳机");
} else if (score > 59) {
System.out.print("奖励你一朵小红花");
} else if (score > 59) {
System.out.print("奖励你一朵小红花");
} else {
System.out.print("奖励你一个耳光");
}
}
}
这样我们便把处理各个分数段奖励的逻辑进行了封装,增强了可读性,并且外部接口不需要了解内部如何实现。
(二)继承
继承的概念
继承是面向对象的最显著的一个特征。继承是从已有的类(父类或者超类)中派生出新的类(子类),新的类能吸收已有类的数据属性和行为,并能扩展新的能力(方法的覆盖/重写)。JAVA不支持多继承,一个类只能有一个父类。父类是子类的一般化,子类是父类的特殊化(具体化)
子类的特点:
- 子类拥有父类非private的属性和方法
- 子类可以添加自己的方法和属性,即对父类进行扩展
- 子类可以重新定义父类的方法,即方法的覆盖/重写
构造函数:
- 构造函数不能被继承,子类可以通过super()显示调用父类的构造函数
- 创建子类时,编译器会自动调用父类的无参构造函数
- 如果父类没有定义无参构造函数,子类必须在构造函数的第一行代码使用super()显示调用
由于后边要阐述多态,这里我们先来介绍一个重要的概念,即方法的覆盖/重写。
覆盖/重写的概念:
当子类需要修改父类的一些方法进行扩展,增大功能,程序设计者常常把这样的一种操作方法称为重写,也叫称为覆盖。
可以这么理解:重写就是指子类中的方法与父类中继承的方法有完全相同的返回值类型、方法名、参数个数以及参数类型。这样,就可以实现对父类方法的覆盖。如果子类将父类中的方法重写了,而我们想调用父类中的同名方法怎么办?此时,通过使用super关键就可以实现这个功能,super关键字可以从子类访问父类中的内容,如果要访问被重写过的方法,使用“super.方法名(参数列表)”的形式调用。
下边我们给出一个案例,子类扩展了父类的行为,并且重写了父类中的方法。代码如下:
package com.pak1;
public class Animal {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
return "{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Dog extends Animal {
// 对父类中方法的一种重写
public String getName() {
return super.getName() + "Dog";
}
// 狗叫和狗吃饭都是在扩展父类中的行为
public void voice() {
System.out.println(super.getName() + " 汪");
}
public void eat() {
System.out.println(super.getName() + "吃东西");
}
}
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("大狗");
dog.setAge(3);
System.out.println(dog.toString());
dog.eat();
dog.voice();
System.out.println(dog.getName()); //执行的是Dog中的getName方法
}
}
(三)多态
多态的概念
多态的本质是:一个程序中同名的不同方法。在面向对象的程序设计中,多态主要有以下三种方式来实现。
- 通过子类对父类方法的覆盖来实现多态
- 通过一个类中方法的重载来实现多态
- 通过将子类的对象作为父类的对象实现多态。
覆盖的概念我们在前面以及介绍了,接下来我们简单阐述下何为重载。
- 重载是指一个类里面(包括父类的方法)存在方法名相同,但是参数不一样的方法,参数不一样可以是不同的参数个数、类型或顺序
- 如果仅仅是修饰符、返回值、throw的异常 不同,那么这是2个相同的方法
我们重点阐述第三种实现方法,即通过将子类的对象作为父类的对象实现多态
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。(这句话是我理解第三种方法的关键,请仔细阅读理解)
对象的引用型变量是具有多态性的,因为一个引用型变量可以指向不同形式的对象,即:子类对象作为父类对象来使用。在这里涉及到了向上转型和向下转型
向上转型:
(时间有限,明天分析)
向下转型:
(时间有限,明天分析)
举一个例子,在一个单位中,有职工employee,职工中又有少数是管理者manager,管理者中又有一部分是领导。若小明是管理者manager类的对象,他也可以被看做是employee的对象,即他也可以被看做是一个职工,他同时具备着职工的所有属性。
public class testDuoTai {
public static void main(String[] args) {
Employee emp1=new Employee("小明",23, 1000); //emp1是Employee的对象
System.out.println(emp1.getInfo());
Employee emp2=new Manager("小明",23, 1000,5000); //注意此处emp2是Manager类的对象
System.out.println(emp2.getInfo());
}
}
//定义一个父类
class Employee
{
String name;
int age;
float salary;
Employee(){};
Employee(String name,int age,float sal)
{
this.name=name;
this.age=age;
this.salary=sal;
}
String getInfo()
{
return "职工姓名:"+name+"年龄:"+age+"工资:"+salary;
}
}
//定义一个子类
class Manager extends Employee
{
float allowance;
Manager(String name,int age,float sal,float aa)
{
this.name=name;
this.age=age;
this.salary=sal;
allowance=aa;
}
}
输出结果如下:
接下来我们再来看一个经典的多态讲解案例,代码如下:
package com.pak2;
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A {
public String show(B obj) {
return ("B and B");
}
public String show(A obj) {
return ("B and A");
}
}
class C extends B {}
class D extends B {}
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("--------------");
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("--------------");
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
这段代码的输出是啥呢?大家可以先分析分析,然后再看正确答案。
好了,正确答案如下:
晚上下班回家时间有限,具体分析我们明天见~
(待续........)
如果对你有帮助,记得点赞哦~欢迎大家关注我的博客,可以进群366533258一起交流学习哦~
本群给大家提供一个学习交流的平台,内设菜鸟Java管理员一枚、精通算法的金牌讲师一枚、Android管理员一枚、蓝牙BlueTooth管理员一枚、Web前端管理一枚以及C#管理一枚。欢迎大家进来交流技术。