首页 > 代码库 > 第十讲:面向对象编程(五)

第十讲:面向对象编程(五)

正确的道路是这样:吸取你的前辈所做的一切,然后再往前走。 


本讲内容:抽象类、初始化块


一、抽象类

面向对象中,所有的对象都是某一个类的实例,但是并不是每个类都可以实例化成一个对象。如果一个类中没有足够的信息来描绘一个具体的对象,那么这个类就不能被实例化,我们称之为抽象类。抽象类用来描述一系列看起来不同,但究其本质是相同的对象。譬如把苹果、橘子、梨等抽象出来一个概念叫水果。这时候我们把水果抽象成一个Fruit类时,就最好不要让它直接初始化,创建出一个Fruit()实例对象的结果似乎难以想象。

1、用 abstract 修饰的类定义,称为抽象类,抽象类不能被实例化(不能创建其对象)。
2、用 abstract 修饰的方法,称为抽象方法,抽象方法不能有方法体(即不能写大括号
3、抽象类被继承之外,没有用途,没有目的。


抽象类的作用:

抽象类的作用就是我告诉你一个类名,他的作用,然后程序员在具体实现什么功能时再用继承的方法重构这个类,完善功能。

abstract class Fruit {
	abstract void myName();
}
class Grape extends Fruit {
	void myName() {
		System.out.println("我是葡萄!");
	}
}
class Apple extends Fruit {
	void myName() {
		System.out.println("我是苹果!");
	}
}
public class text {
	public static void main(String[] args) {
		Fruit a=new Grape();
		Fruit b=new Apple();
		a.myName();
		b.myName();
	}
}
打印结果:
我是葡萄!
我是苹果!

注意:
1、一个编译单元里是可以写多个顶级类的,只要public修饰的顶级类只有一个就行了。
2、抽象类实际上是定义了一个标准和规范,等着他的子类们去实现,譬如水果这个抽象类里定义了一个名字的抽象方法,它就定义了一个规则,那就是谁要是水果类的子类,谁就要去实现这个抽象方法。
葡萄和苹果的类继承了水果这个抽象类,实现了名字的方法。
一个对象除了被看成自身的类的实例,也可以被看成它的超(父)类的实例。我们把一个对象看做超类对象的做法叫做向上转型。譬如Fruit a=new Grape();
虽然都是水果类型,但是方法在运行时是按照它本身的实际类型来执行操作的。因此 a.myName()执行的是葡萄的名字,b.myName()执行的是苹果的名字,我们称之为运行时多态。


把一个类看做一个超类有什么样的损失或者不便,我们看一个例子

abstract class Fruit {
	abstract void myName();
}
class Apple extends Fruit {
	void myName() {
		System.out.println("我是苹果!");
	}
	void color(){
		System.out.println("红苹果");
	}
}
public class text {
	public static void main(String[] args) {
		Fruit b=new Apple();
		b.myName();
		((Apple)b).color();
	}
}
把b强制转换为Apple对象,只有转换为Apple对象后,才能使用color方法,否则即使你知道他是一个Apple对象也不能调用color方法。这就是子类对象付给超类引用所带来的不便或者说是损失。


二、初始化块

在类中有两个位置可以放置执行操作的代码,这两个位置是方法和构造函数。初始化块是第三个可以放置执行操作的位置。当首次加载类(静态初始化块)或者创建一个实例(new 实例初始化块)时,就会运行初始化块。

class Superclass{
	Superclass() {
		 System.out.println("父类SuperClass的构造函数");
	}
}
public class Lei08_10 extends Superclass{
	Lei08_10(int x) {
		System.out.println("带参数的构造函数");
	}
	Lei08_10() {
		System.out.println("不带参数的构造函数");
	}
	static{
		 System.out.println("第一个静态初始化块");
	}
	{
		System.out.println("第一个实例初始化块");
	}
	{
		System.out.println("第二个实例初始化块");
	}
	static{
		System.out.println("第二个静态初始化块");
	}
	public static void main(String[] args) {
		new Lei08_10(1);
		new Lei08_10();
	}
}
运行结果:

第一个静态初始化块
第二个静态初始化块
父类SuperClass的构造函数
第一个实例初始化块
第二个实例初始化块
带参数的构造函数
父类SuperClass的构造函数
第一个实例初始化块
第二个实例初始化块
不带参数的构造函数

从上面的例子中我们知道:
1、初始化块没有名称,没有参数,也没有返回值,只有一个大括号。用 static 修饰的初始化块就要静态初始化块,相对应的,没有static修饰的初始化块就叫实例初始化块。
2、静态初始化块在首次加载类时会运行一次,优先于对象的存在。
3、实例初始化块在每次创建对象时会运行一次。
4、实例初始化块在构造函数的super()调用之后运行。
5、初始化块之间的运行顺序取决于他们在类文件中出现的顺序,出现在前面的先执行。
6、初始化块从书写惯例上应该写在靠近类文件的顶部,构造函数附近的某个位置。

本讲就到这里,Take some time and enjoy it

第十讲:面向对象编程(五)