类与对象是在OO变成里面经常出现的字眼,简单的可以理解为类是一类对象的抽象,对象是某一个类的具体实现。面向对象的编程,其实主要就体现在封装、继承与多态这三个方面。下面将逐个的介绍这三个名词。
一、封装
面向对象里面类的提出就是为了实现封装,我们将一类同样的对象的一些相同或相似的属性及方法抽象出来,封装在一起就实现了封装。封装的目的就是为了实现一些数据及方法的内部隐藏(通过修饰符来实现),并提供一些通用的方法供外部使用,而具体的内部实现细节无需对外公开,加强了安全性。封装类要注意一下几点:
- 类里面一般都要提供一个或多个构造函数,构造函数可以重载。一旦提供了有参数的构造函数,那么最好提供一个无参的构造函数,即使是空函数也好。因为下面讲到的继承发生时,总是先运行父类的构造函数,如果需要运行无参的父类构造函数而找不到的话就会报错。
- 在类里面可以通过this关键字来指向自身。
- 类里的属性一般都设置成private隐藏,如果需要外部访问或设置,可以通过get/set方法实现。
比如我封装了一个汽车类Car,供后面使用,示例代码如下:
package com.mct.main; public class Car { protected String name; // 一般不将类的属性设置为public,这里设置为protected,因为这两个属性肯定是需要被子类继承的。 protected String color; public String getName() { // 通过get,set方法让外部访问并设置属性。 return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public Car(){} // 如果提供了有参数的构造函数,则最好再提供一个无参的构造函数,即使是空函数也好。 public Car(String name, String color){ this.name = name; this.color = color; } public void speedUp(){ System.out.println("运行这里加速 "); } public void speedDown(){ System.out.println("运行这里减速"); } }
二、继承
继承是使用已经存在的类为基础实现新的类,继承使用extends关键字。在Java里面只允许实现单继承,子类将完全继承父类中允许继承的属性和方法(具体依据修饰符的访问权限而定),另外子类也可添加自己的属性和方法,但不能选择性的继承父类。如果父类的某些功能无法满足子类要求,可以使用下面要讲到的覆写或重载(有些著作中认为父类与子类之间不存在重载关系)的方法来实现自己特有的方法。关于继承需要注意一下几点:
- 子类的构造方法一定会调用父类的构造方法。如果子类没有显式的调用父类的构造方法,那么将默认调用父类的无参方法(这就是上面说的需要添加无参构造的原因)。当然构造方法如果不实现,则系统会默认提供一个无参构造方法。另外子类的构造方法中可以通过super()方法调用父类的某个构造方法,也可以通过this()方法调用本类中的某个构造方法,这两种方式的调用语句都必须位于构造函数的第一行,所以它们是无法并存的。
- 父类构造方法是无法被继承的,只能通过在子类中使用super来调用。
- Java中所有的类都直接或间接的继承自Object类。如上面的Car没有通过extends关键字继承,那么系统 会默认给其添加上extends Object,让它继承自Object类。
- 子类对象的引用可以通过伪装赋值给父类对象的引用,无需进行类型转换;而父类对象引用需要赋值给子类对象时需要经过强制类型转换,当然前提是这个对象在构造时就是此子类类型,否则运行时肯定会因为类型不匹配而报错。这里对一个对象引用的类型检查使用 instanceof关键字。
- 说到继承就不得不说一下抽象类和抽象方法,Java中使用abstract关键字修饰。抽象方法时一个没有类体的方法,它用来描述某种功能或行为,但具体的实现过程需要子类来实现。而抽象类一般都做为某个功能或行为的超类出现,抽象类无法实例化对象,只能被继承。面向对象编程的很多思路都是建立在抽象的基础上,所以这部分需要仔细的理解体会。
- 这里再提一下接口,从概念上讲它是不属于继承范畴的,但是Java不支持多继承,所以通过接口也可以解决部分多继承的问题。接口中只能出现抽象类,属性也必须是静态常量。Java中的接口常用来实现某个功能,以实现对类的规范以及对行为的封装。另外接口还有助于对象的行为安全性,正所谓对人说人话,对鬼说鬼话,下面会有个例子来实现此功能。
- 在Java多继承的工具箱里,还有一个内部类的概念。
使用接口来实现俗语“见人说人话,见鬼说鬼话"
- 新建说人话接口:PersonToneAble
public interface PersonToneAble { public abstract void personSpeak(); }
- 新建说鬼话接口:GhostToneAble
public interface GhostToneAble { public abstract void ghostSpeak(); }
- 实现一个超人类:SuperMan,他既能说人话,也能说鬼话
public class SuperMan implements PersonToneAble, GhostToneAble { @Override public void ghostSpeak() { // TODO Auto-generated method stub System.out.println("@#$%*^%"); } @Override public void personSpeak() { // TODO Auto-generated method stub System.out.println("Hello"); } }
- 创建一个机构,雇佣一个超人,分别派他去地球和地狱执行任务
public class Agency { public void gotoEarth(PersonToneAble p){ p.personSpeak(); } public void gotoEvil(GhostToneAble p){ p.ghostSpeak(); } public void action(){ SuperMan superMan = new SuperMan(); gotoEarth(superMan); gotoEvil(superMan); } public static void main(String[] args) { // TODO Auto-generated method stub Agency agency = new Agency(); agency.action(); } }
上面这个例子用到了接口向上溯型的概念,其实跟子类通过父类伪装是一样的道理,通过实现的接口来获得执行此功能或行为的对象。
三、多态
最后再来看一下多态。 多态其实就是通过重载和重写来实现,它其实就是说在运行时来确定状态的,上面也已经提到了重载和重写这两个概念,这里略过。
综上可以看到,面向对象其实就是这三个概念的组合,而在实际编程实现时将根据不同的命题使用不同的组合方式,这也就是设计模式(后面会写到)的使用。而对于面向对象容易出现的误区就是滥用继承,这部分内容页是设计模式这门学科的主要研究话题。