概述
面向对象编程有三大特点:封装、继承和多态,个人觉得这三大特性具有承上启下的作用,必须要放到一篇博文里面来讲清楚,屏幕前的各位要记得拿好你们的小本本记清楚哦。
封装
封装是面向对象编程的核心思想。将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。例如,用户使用计算机时,只需要使用手指敲击键盘就可以实现一些功能,无需知道计算机内存是如何工作的,即使可能知道计算机的工作原理,但在使用计算机时也并不完全依赖于计算机工作原理这些细节。
采用封装的思想保证了类内部数据结构的完整性,应用该类的用户不能轻易地直接操作此数据结构只能执行类允许公开的数据。这样就避免了外部操作对内部数据的影响,提高了程序的可维护性。
我们先来看看没有封装过的代码是怎样的:
public class Test {
public static void main(String[] args) {
//创建一个学生对象
Student student = new Student();
//可以直接获取学生对象的各个属性值
String num = student.stu_num;
String name = student.stu_name;
int age = student.stu_age;
}
}
class Student{
String stu_num;//学号
String stu_name;//姓名
int stu_age;//年龄
}
从上面的代码可以看到,如果类Student没有进行封装操作的话,外部代码是可以直接访问其内部的数据,或者说可以直接修改其中的数据。因此我们可以将封装理解为一个保护屏障,进行了封装操作的类可以保证该类当中的数据和代码不会被恶意的修改和使用。同时,如果需要访问该类的代码和数据,必须通过严格的接口控制。
封装的具体方法步骤:
1.属性私有化
2.对外提供访问接口(即提供 setXXX() 方法和 getXXX() 方法)
将以上代码进行封装:
public class Test {
public static void main(String[] args) {
//创建一个学生对象
Student student = new Student();
//不可以直接获取学生对象的各个属性值,必须通过对外提供的方法才可以进行访问
String num = student.getStu_num();
String name = student.getStu_name();
int age = student.getStu_age();
}
}
class Student{
private String stu_num;//学号
private String stu_name;//姓名
private int stu_age;//年龄
//提供每个属性的setXXX()方法和getXXX()方法
public String getStu_num() {
return stu_num;
}
public void setStu_num(String stu_num) {
this.stu_num = stu_num;
}
public String getStu_name() {
return stu_name;
}
public void setStu_name(String stu_name) {
this.stu_name = stu_name;
}
public int getStu_age() {
return stu_age;
}
public void setStu_age(int stu_age) {
this.stu_age = stu_age;
}
}
继承
继承是面向对象开发思想中的一个非常重要的概念,体现了两个类之间的关系,继承使得整个程序的架构具有一定的弹性,在程序中复用一些已经定义完善的类不仅可以减少软件开发周期,也可以提高软件的可维护性和可扩展性。
举个简单的例子来说明吧,假如我们现在需要构造一个鸽子类,如果不考虑继承的话,我们就可以单独的构造这个鸽子类,在鸽子类当中定义各种需要的属性和方法。试想一下,如果还需要构造麻雀、燕子类的话,就需要将上面的步骤再进行一遍,这样代码明显就比较臃肿。由于鸽子类属于鸟类,具有与鸟类相同的属性和方法,我们便可以在创建鸽子类的时候将鸟类拿来复用,相同的属性和方法就不需要再定义一遍,这样就节省了定义鸟和鸽子共同具有的属性和行为的时间,这就是继承的基本思想。
在Java中使用extends关键字来标识两个类的继承关系。我们来分析以下代码来熟悉其中的知识点:
class Test{
//无参构造方法
public Test() {
}
//成员方法
protected void doSomething() {
//SomeSentence
}
}
public class Test2 extends Test{
//构造方法
public Test2() {
super();//调用父类的无参构造方法
super.doSomething();//调用父类成员方法
}
public void doSomething() {
//SomeNewSentence
}
}
说明:Test2类继承Test类,也可以说Test类是Test2的父类(超类),Test2类为Test类的子类(派生类)。在子类中可以连同初始化父类构造方法来完成子类初始化操作,既可以在子类的构造方法中使用 super() 语句调用父类的构造方法,也可以在子类中使用super关键字调用父类的成员方法等。需要注意的是,子类没有权限去调用父类中被修饰为private的方法,只可以调用父类中修饰为public或protected的成员方法。
Tips:有关super关键字的讲解将会更新在后期博文。
继承并不只是扩展父类的功能,还可以重写父类的成员方法,重写(覆盖)就是在子类中将父类的成员方法的名称保留,重写成员方法的实现内容。
何时考虑使用方法覆盖呢???
子类继承父类之后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行“方法的覆盖”。
方法覆盖的条件???
条件一:两个类必须要有继承关系;
条件二:重写之后的方法和之前的方法具有相同的方法名和相同的形参列表,返回值类型可能不一样;
条件三:访问权限不能比之前的更低,可以更高(也即访问权限可以修改);
条件四:重写之后的方法不能比之前的方法抛出更多的异常。
注意事项:(重点)
- 方法覆盖只针对于方法,和属性无关;
- 私有方法无法被覆盖;
- 构造方法不能被继承,所以构造方法也不能被覆盖;
- 方法覆盖只针对于实例方法,静态方法覆盖没意义。
继承重点总结:
- Java中的继承只支持单继承,不支持多继承;
- 虽然Java不支持多继承,但是可以产生多继承的效果。例如:class C extends B,class B extends A,也就是说,C直接继承B,其实C还间接继承A;
- Java中规定,子类继承父类,除构造方法不能继承之外,剩下的都可以继承,但是私有的属性无法在子类中直接访问;
- Java中的类没有显示的继承任何类,则默认继承Object类,Object类是Java语言提供的根类,也就是说,一个对象与生俱来就有Object类当中的所有特征;
- 继承也存在一些缺点,继承的类与类之间的耦合度非常高,不利于代码的扩展。
多态
利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。在正式的学习多态机制之前,我们需要普及两个概念,即向上转型和向下转型。Java中允许向上转型,也允许向下转型。说的简单一点,向上转型就是将子类对象转为父类对象,向下转型就是将父类对象转为子类对象,无论是向上转型还是向下转型,两种类型之间必须要有继承关系。下面通过两个例子来讲解一下向上转型和向下转型的概念。
Tips:多态指的是多种形态,编译的时候一种形态,运行的时候又是另外一种形态。
实例1:向上转型
//定义四边形类
class Quadrangle{
//定义四边形类当中的静态方法
public static void draw(Quadrangle q) {
//SomeSentence
}
}
//定义平行四边形类并继承四边形类
public class Parallelogram extends Quadrangle{
public static void main(String[] args) {
//实例化平行四边形类对象引用
Parallelogram p = new Parallelogram();
Quadrangle q = p;
//调用父类方法
Parallelogram.draw(q);
}
}
由以上代码可以知道,平行四边形类Parallelogram继承了四边形类Quadrangle,这种情况下的类型转换就是“向上转型”,由于向上转型是从一个较具体的类到较抽象的类的转换,所以说它总是安全的。
实例2:向下转型
//定义四边形类
class Quadrangle{
//定义四边形类当中的静态方法
public static void draw(Quadrangle q) {
//SomeSentence
}
}
//定义平行四边形类并继承四边形类
public class Parallelogram extends Quadrangle{
public static void main(String[] args) {
Parallelogram.draw(new Parallelogram());
//将平行四边形类对象看作是四边形对象,称为向上转型操作,是正确的写法
Quadrangle q = new Parallelogram();
//将父类对象赋予子类对象,这种写法是错误的
//Parallelogram p = q;
//将父类对象赋予子类对象,并强制转换为子类型,是正确的写法
Parallelogram p = (Parallelogram) q;
}
}
由以上代码可以知道,直接将父类对象赋予子类,会发生编译错误,因为父类对象不一定是子类的实例。当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生ClassCastException异常,所以在执行向下转型之前需要判断父类对象是否为子类对象的实例,那么这个判断的操作就是由instanceof操作符来完成。可以使用instanceof操作符判断是否一个类实现了某个接口,也可以判断一个实例对象是否属于一个类。
instanceof关键字说明:
1.instanceof可以在运行阶段动态判断引用指向的对象的类型;
2.语法格式为:引用 instanceof 类型;
3.运算结果只能为true/false。
总而言之,多态在开发中的作用就是降低程序的耦合度,提高程序的扩展力,那么在学完前面的内容之后,我们需要解决以下两个问题。
1.为什么说静态方法的覆盖没有意义呢???
首先,方法覆盖和多态不能分开,多态和对象有直接的关系,而静态方法的执行是不需要对象的。所以,一般情况下,我们会说静态方法“不存在”方法覆盖,即不探讨静态方法的覆盖问题。
2.方法覆盖的返回值类型问题???
对于返回值时基本数据类型来说,必须要一致;对于返回值类型是引用数据类型来说,重写之后的返回值类型可以变得更小。这里的更小指的是:重写之后的方法的返回值类型可以为原方法返回值类型的子类。
以上内容均为个人学习的一点心得,其中的知识点若有错误请留言提醒,若有侵权内容提醒马上删除。