Thread是一个具体类,有属性和行为;Runnable是一个函数式接口,可以定义执行任务。Thread和Runnable能扯上联系关键是run方法。Thread子类覆盖run和实现Runnable的run,这两种方法都可以为线程定义一个任务。从使用上看,Thread子类覆盖run方法将任务的定义和执行结合在一块,实现Runnable的run方法则将任务的定义和执行分开。看过源码就可以知道,这两种方式最终执行的逻辑是Thread中的run方法,只不过run方法中有一个if判断,当target不为null时,则执行target中的run方法,这个target就是一个Runnable的实现类对象。在实际使用中,一般都通过Runnable定义一个任务对象,再通过构造器传给Thread。这样会使得任务的定义从Thread的继承体系中脱离出来,具有更好的灵活性。这也是组合优先于继承的体现。
通过继承可以快速的实现和扩展一个类,但是继承破坏了封装性,父类内部细节对于子类来说是可见的,在父类中的修改也会改变子类的实际行为,并且使用继承的方式在编译期就确定了子类的具体行为,不利于应用的灵活性。相较于继承,组合只需要知道能做什么,不需要了解具体细节,属于黑盒复用。通过组合的方式将已有对象组合到新对象中,调用已有对象中的方法就能实现具体的行为。由于组合关系各个对象内部细节是隐藏的,所以只能通过调用接口编程,这样我们可以在运行期间让另一个实现该接口的对象代替原对象,从而在运行期间控制对象的行为。而且使用组合可以保证类的单一职责,摆脱复杂的继承层次。