复用代码是Java众多引人注目的功能之一,但想要成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它还必须能够做更多的事情。
Java中所有事物都是围绕着类来展开的。通过创建新类来复用代码,不必重新开头编写。此方法的窍门在于使用类而不破坏现有程序代码。本章中有两种代码重用机制来达到这一目的:
组合语法只需要将对象引用置于新类中即可。例如,假如现在需要一个新的类型,其中需要多个String对象,几个基本数据类型,以及另外一个类的对象。可以如下定义新的类型:
class Apple {
private String s;
Apple() {
System.out.println("Apple()");
s = "Constructed";
}
public String toString() {
return s;
}
}
public class Desk {
private String s1, s2, s3;
private Apple apple1;
private int appleCount;
private float deskHeight;
}
上面的例子中,新创建的类Desk运用了组合的语法,重用了部分代码,将3个String类型数据,2个基本类型数据和一个类的引用组合在一起了。
继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时,总是在继承,因此除非已明确指出从其他类继承,否则就是在隐式的从Java的标准根类Object进行继承。
和组合语法不同,继承需要通过关键字extends来声明”新类的旧类相似”。当这么做时,会自动得到基类中所有的域和方法。例如:
class Pet {
private String s = "The pet is ";
public void append(String a) {
s += a;
}
public void toString() {
return s;
}
}
public class Dog extends Pet {
public void bark() {
System.out.println("woo...");
}
public static void main(String[] args) {
Dog d = new Dog();
d.append("dog");
System.out.println(d.toString());
d.bark();
}
}
上面的例子中,Dog类继承了Pet类,获得了Pet的域及方法。
继承并不只是复制基类的接口。当创建了一个导出类的对象时,该对象中包含了一个基类的子对象。这个自对象与用基类直接创建出来的对象是一样的。二者的区别在于,后者来自于外部,而基类的子对象被包含在了导出类对象内部。
在导出类中对基类的子对象也很关键,需要在导出类的构造方法中调用基类的构造器来执行初始化,Java会自动在导出类的构造器中插入对基类构造器的调用。例如:
class Animal {
Animal() {
System.out.println("Animalss...");
}
Animal(int a) {
System.out.println(a);
}
}
class Pet extends Animal {
Pet() {
System.out.println("Pet...");
}
Pet(int a) {
System.out.println(a);
}
}
public class JavaTest extends Pet{
JavaTest(int a) {
System.out.println("JavaTest...");
}
public static void main(String[] args) {
JavaTest jt = new JavaTest(1);
}
}
输出结果为:
Animalss...
Pet...
JavaTest...
从上面的结果可以看出,Java编译器会帮我们自动调用基类的默认构造方法,如果想通过不同的构造器对内部基类进行初始化,可以通过super()方法来调用。构建过程是从基类”向外”扩散的,最开始调用基类的构造方法,再逐渐调用导出类的构造方法。
代理在Java中没有直接的支持,是在继承和组合之间的中庸之道.代理这个东西还需要详细的了解,在后面会单独去学习,整理出一篇博客。
Java中基类拥有某个已经被多次重载的方法名,在导出类中重新定义该方法名并不会屏蔽在基类中的任何版本。例如:
class BClass {
public void func() {
System.out.println("1...");
}
public void func(int i) {
System.out.println("2...");
}
public void func(String s) {
System.out.println("3...");
}
}
class SClass extends BClass {
public void func(float a) {
System.out.println("6...");
}
}
public class JavaTest{
public static void main(String[] args) {
SClass s = new SClass();
s.func(1f);
s.func();
s.func("");
}
}
运行结果为:
6...
1...
3...
在继承类中又重载了该方法名一次,但是在之前子类中重载的方法都还在。
在Java的继承中,基类和导出类的关系可以概括为“导出类是基类的一种类型“。例如有一个名为Instrument的代表乐器的基类和一个名为Wind的导出类。由于继承可以确保基类中所有的方法在导出类中同样有效,所以能够向基类发送的所有信息也可以向导出类发送。看下面的例子:
class Instrument {
public void play() {}
static void tune(Insatument i) {
// ...
i.play();
}
}
public class Wind extends Instrument {
public static void main(String[] args) {
Wind flute = new Wind();
Instrument.tune(flute);
}
}
上面的例子中,tune()方法的参数能接受Instrument的引用,但是调用的时候传入的是Wind的一个引用。在tune方法中,程序代码可以对Instrument和它所有的导出类起作用,这种将导出类引用转换成基类引用的动作,我们称之为向上转型。向上转型的过程中,类接口中唯一可能发生的事情是丢失方法。由于是从一个较专用类型转换成一个通用类型,所以总是安全的。
在面向对象编程中,有一句话叫做“组合优于继承”,尽管继承被许多人多次强调,但不意味着尽可能的去使用继承。一个最清晰的判断方法就是要问一问自己是否需要从新类向基类进行向上转型。如果必须向上转型,则继承是必要的。后面会专门去写一篇博客来认真的思考,组合优于继承这句话。
关于final关键字的说明,单独将其拿出来和static关键字做了一次理解,点击进入。
原文地址:http://blog.csdn.net/jing_unique_da/article/details/46271509