Java学习笔记——类与对象(封装、继承与多态)

时间:2023-02-24 12:05:30

        类与对象是在OO变成里面经常出现的字眼,简单的可以理解为类是一类对象的抽象,对象是某一个类的具体实现。面向对象的编程,其实主要就体现在封装、继承与多态这三个方面。下面将逐个的介绍这三个名词。

一、封装

        面向对象里面类的提出就是为了实现封装,我们将一类同样的对象的一些相同或相似的属性及方法抽象出来,封装在一起就实现了封装。封装的目的就是为了实现一些数据及方法的内部隐藏(通过修饰符来实现),并提供一些通用的方法供外部使用,而具体的内部实现细节无需对外公开,加强了安全性。封装类要注意一下几点:

  1. 类里面一般都要提供一个或多个构造函数,构造函数可以重载。一旦提供了有参数的构造函数,那么最好提供一个无参的构造函数,即使是空函数也好。因为下面讲到的继承发生时,总是先运行父类的构造函数,如果需要运行无参的父类构造函数而找不到的话就会报错。
  2. 在类里面可以通过this关键字来指向自身。
  3. 类里的属性一般都设置成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里面只允许实现单继承,子类将完全继承父类中允许继承的属性和方法(具体依据修饰符的访问权限而定),另外子类也可添加自己的属性和方法,但不能选择性的继承父类。如果父类的某些功能无法满足子类要求,可以使用下面要讲到的覆写或重载(有些著作中认为父类与子类之间不存在重载关系)的方法来实现自己特有的方法。关于继承需要注意一下几点:

  1. 子类的构造方法一定会调用父类的构造方法。如果子类没有显式的调用父类的构造方法,那么将默认调用父类的无参方法(这就是上面说的需要添加无参构造的原因)。当然构造方法如果不实现,则系统会默认提供一个无参构造方法。另外子类的构造方法中可以通过super()方法调用父类的某个构造方法,也可以通过this()方法调用本类中的某个构造方法,这两种方式的调用语句都必须位于构造函数的第一行,所以它们是无法并存的。
  2. 父类构造方法是无法被继承的,只能通过在子类中使用super来调用。
  3. Java中所有的类都直接或间接的继承自Object类。如上面的Car没有通过extends关键字继承,那么系统 会默认给其添加上extends Object,让它继承自Object类。
  4. 子类对象的引用可以通过伪装赋值给父类对象的引用,无需进行类型转换;而父类对象引用需要赋值给子类对象时需要经过强制类型转换,当然前提是这个对象在构造时就是此子类类型,否则运行时肯定会因为类型不匹配而报错。这里对一个对象引用的类型检查使用 instanceof关键字。
  5. 说到继承就不得不说一下抽象类和抽象方法,Java中使用abstract关键字修饰。抽象方法时一个没有类体的方法,它用来描述某种功能或行为,但具体的实现过程需要子类来实现。而抽象类一般都做为某个功能或行为的超类出现,抽象类无法实例化对象,只能被继承。面向对象编程的很多思路都是建立在抽象的基础上,所以这部分需要仔细的理解体会。
  6. 这里再提一下接口,从概念上讲它是不属于继承范畴的,但是Java不支持多继承,所以通过接口也可以解决部分多继承的问题。接口中只能出现抽象类,属性也必须是静态常量。Java中的接口常用来实现某个功能,以实现对类的规范以及对行为的封装。另外接口还有助于对象的行为安全性,正所谓对人说人话,对鬼说鬼话,下面会有个例子来实现此功能。
  7. 在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();
	}
}

        上面这个例子用到了接口向上溯型的概念,其实跟子类通过父类伪装是一样的道理,通过实现的接口来获得执行此功能或行为的对象。

三、多态
        最后再来看一下多态。 多态其实就是通过重载和重写来实现,它其实就是说在运行时来确定状态的,上面也已经提到了重载和重写这两个概念,这里略过。

        综上可以看到,面向对象其实就是这三个概念的组合,而在实际编程实现时将根据不同的命题使用不同的组合方式,这也就是设计模式(后面会写到)的使用。而对于面向对象容易出现的误区就是滥用继承,这部分内容页是设计模式这门学科的主要研究话题。