一、抽象类和抽象方法
抽象类是介于普通类与接口之间的一种通用接口。
抽象类的目的是通过这个通用接口操作一系列的类,创建抽象类的实体对象几乎没有任何意义。抽象类可以阻止使用者实例化该类。
抽象方法:仅有声明而没有方法体。
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的,否则,编译器就会报错。
正确的方式:
public abstract class Instrument {
private int i;
public String what(){
return "Hello niceeee";
}
//抽象方法必须写在抽象类中
public abstract void play(String str);
}
当抽象类是不完整的(也就是存在抽象方法),那么试图产生该类的对象时,编译器会报错。
如果从一个抽象类继承,并创建该导出类的对象,那么必须为基类中的所有抽象方法提供方法定义。
public abstract class Instrument {
private int i;
public String what(){
return "Hello niceeee";
}
public abstract void play(String str);
}
class Wind extends Instrument{
//导出类必须实现基类中的抽象方法
public void play(String str){
System.out.println("str = [" + str + "]");
}
}
当然,导出类也可以不实现基类中的方法,但是此时导出类也是抽象类,编译器会强制使用abstract来限定该类。
public abstract class Instrument {
private int i;
public String what(){
return "Hello niceeee";
}
public abstract void play(String str);
}
//导出类可以不实现基类的抽象方法,当时该导出类被限定为抽象类
abstract class Percussion extends Instrument{
private String s="good";
}
也可以创建一个没有任何抽象方法的抽象类。但是不能直接实例化。
二、接口
interface关键字产生一个完全抽象的类,根本没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。只提供形式,而未提供任何具体实现。
接口可以包含域,这些域隐式地是static和final的。并且是public的
//添加public关键字,仅限于该接口与文件同名,被添加则是包访问权限
public interface Instrut {
//域是static & final ,必须要初始化
int value=5;
//没有任何方法实体
public void play(String str);
public void adjust();
}
接口中的方法应该显式的声明为public,即使没有声明为public,它们自动都是public。这样的方法在被继承(implements)的过程中,必须被指定为public的,否则只得到包访问权限,而编译器不允许其访问权限被降低。
public interface Instrut {
//域是static & final ,必须要初始化
int value=5;
//显式声明方法为public
public void play(String str);
//可以不声明为public,但是自动默认是public
void adjust();
}
class Stringed implements Instrut{
public void play(String s){
System.out.println("s = [" + s + "]");
}
//实现接口中的方法时,对于接口中没有声明public的方法,必须显式声明为public。不能降低访问权限
public void adjust(){
}
}
三、完全解耦
1、策略设计模式
能够根据所传递的参数对象的不同而具有不同行为的方法。这类方法包含所要执行的算法中固定不变的部分。而”策略”包含变化的部分,也就是传递进去的参数对象。
public class Apply {
//该方法操作类而不是接口,那么只能使用这个类及其子类
//该部分固定不变,根据所传递参数对象的不同,而执行不同行为
//Processor对象就是一个策略
public static void process(Processor p,Object s){
System.out.println("using Processor: "+p.name());
System.out.println(p.process(s));
}
public static String test="this is a test";
public static void main(String[] args) {
process(new Upcase(),test);
process(new Downcase(),test);
}
}
//是一个类,而不是接口
class Processor{
public String name(){
return getClass().getSimpleName();
}
Object process(Object input){
return input;
}
}
class Upcase extends Processor{
String process(Object input){
return ((String) input).toUpperCase();
}
}
class Downcase extends Processor{
String process(Object input){
return ((String) input).toLowerCase();
}
}
//结果:
using Processor: Upcase
THIS IS A TEST
using Processor: Downcase
this is a test
在上例中,必须要使用Processor类型作为参数,才能使用其方法。从而导致Apply.process()方法和Processor之间的耦合过紧。这对于功能相同(方法相同)但是只是方法名不一样的类来说,会出现重复代码。
但是如果Processor是一个接口,那只要实现(implements)该接口就可以调用其方法:
public abstract class StringProcessor implements ProcessorTest{
public String name() {
return getClass().getSimpleName();
}
public abstract String process(Object input);
public static String str="this is a test also";
public static void main(String[] args) {
ApplyTest.process(new UpcaseTest(),str);
}
}
//接口
interface ProcessorTest{
String name();
Object process(Object input);
}
class ApplyTest{
//该方法操作接口ProcessorTest
public static void process(ProcessorTest pt,Object s){
System.out.println("Using Processor "+pt.name());
System.out.println(pt.process(s));
}
}
class UpcaseTest extends StringProcessor{
public String process(Object input) {
return ((String)input).toUpperCase();
}
}
结果:
Using Processor UpcaseTest
THIS IS A TEST ALSO
在上例中,ProcessorTest接口变得更加通用了,只要具有相同的功能元素,都可以使用该接口,而不一定要使用继承类的形式。
四、java中的多继承
在导出类中,不强制要求必须有继承一个是抽象的(抽象类)或具体的(没有任何抽象方法的普通类)基类。
如果要从一个非接口的类继承,那么只能从一个类去继承,其余的基元素都必须是接口。具体类必须放在前面,后面跟着接口。
//可以继承具体基类,实现接口
public abstract class Hero extends CanFly implements CanFight{
}
//继承一个抽象类,实现一个接口
abstract class Action extends CanSwim implements CanFight{
}
//不强制要求必须继承一个抽象或具体的基类,可以直接实现一个接口
abstract class Adventure implements CanFight{}
//接口
interface CanFight{
void fight();
}
//抽象类
abstract class CanSwim{
abstract void swim();
}
//具体类
class CanFly{
public void fly(){
}
}
使用接口的原因:
1、为了能够向上转型为多个基类型
2、防止客户端程序员创建该类的对象(与抽象基类相同)
应该使用接口还是抽象类?如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。如果知道某事物应该成为一个基类,那么应该使它成为一个接口。
五、通过继承扩展接口
通过继承,可以在接口中添加新的方法声明,还可以在新接口中组合数个接口:
public class DragonZilla implements DangerousMonster{
public void destroy() {
}
public void menace() {
}
}
interface Monster{
void menace();
}
interface DangerousMonster extends Monster{
void destroy();
}