인터페이스는 무엇이고 추상 클래스는 무엇인가요? 추상 클래스에도 인터페이스가 필요한 이유는 무엇인가요?
추상 클래스는 일반 클래스에 상대적입니다. 클래스 측면에서 일반 클래스는 인스턴스화된 객체를 직접 생성할 수 있는 완전한 함수형 클래스를 말하며, 일반 클래스에는 생성자, 일반 메서드, 정적 메서드, 상수, 변수 등이 포함될 수 있습니다. 추상 클래스는 일반 클래스의 구조에 추상 메서드 구성 요소를 추가하거나 클래스를 추상 유형으로 직접 선언하는 것을 의미합니다.
또한 추상 메서드는 일반 메서드에 상대적이며 모든 일반 메서드에 사용됩니다. 메서드 본문을 나타내는 "{}". 메서드 본문이 있는 메서드는 객체에서 직접 사용해야 합니다. 추상 메서드는 메서드 본문이 없는 메서드를 나타내며, 추상 메서드도 abstract 키워드로 수정해야 합니다. 추상 메서드가 있는 클래스는 추상 클래스이며, 추상 클래스는 abstract 키워드를 사용하여 선언해야 합니다.
다음은 간단한 추상 클래스입니다.
package abstractdemo; //抽象类 public abstract class AbstractDemo { //抽象方法,没有方法体 public abstract void test(); public void test1(){ System.out.println("普通方法"); } public static void main(String[] args) { Class18f2cef21ebec83e6009fc1ed61f2bdf c = AbstractDemo.class; System.out.println(c.getName()); } }
추상 클래스 인스턴스:
package abstractdemo; public abstract class AbstractTest { public static void main(String[] args) { AbstractDemo ad = new AbstractDemo() { @Override public void test() { System.out.println("实例抽象类要重写抽象类中所有的抽象方法!"); } }; ad.test(); System.out.println(ad.getClass()); } }
위에서 볼 수 있듯이 추상 클래스는 직접 인스턴스화할 수 없습니다. 왜 직접 인스턴스화할 수 없나요? 클래스가 인스턴스화된다는 것은 객체가 클래스의 속성이나 메서드를 호출할 수 있지만 추상 클래스에는 추상 메서드가 있고 추상 메서드에는 메서드 본문이 없으며 메서드 본문 없이는 호출할 수 없음을 의미합니다. 메소드 호출을 할 수 없는데 어떻게 인스턴스화된 객체를 생성할 수 있나요? 따라서 추상 클래스를 인스턴스화할 때 먼저 추상 클래스의 추상 메서드를 재정의하라는 메시지가 표시됩니다. 추상 클래스에 추상 메서드가 없으면 클래스를 인스턴스화할 수 없으며 인스턴스화 중에 컴파일 오류가 보고됩니다. ;
Abstract 클래스 사용 원칙은 다음과 같습니다.
(1) 추상 메서드는 공개 또는 보호되어야 합니다(비공개인 경우 하위 클래스에서 상속될 수 없고 하위 클래스에서 해당 메서드를 구현할 수 없기 때문입니다). 기본값은 공개입니다.
(2) 추상 클래스는 직접 인스턴스화할 수 없으며 상향 변환을 통해 하위 클래스에서 처리해야 합니다.
(3) 추상 클래스에는 상속 확장을 사용하여 하위 클래스만 상속할 수 있습니다.
(4) 하위 클래스(추상 클래스가 아닌 경우)는 추상 클래스의 모든 추상 메서드를 재정의해야 합니다(하위 클래스가 상위 클래스의 추상 메서드를 구현하지 않는 경우 하위 클래스도 추상 클래스입니다. 추상 클래스)
아래 참조 코드 조각:
package abstractdemo; //抽象类 public abstract class AbstractDemo { //抽象方法,没有方法体 public abstract void test(); public void test1(){ System.out.println("普通方法"); } public static void main(String[] args) { Class18f2cef21ebec83e6009fc1ed61f2bdf c = AbstractDemo.class; System.out.println(c.getName()); } } package abstractdemo; public class Test extends AbstractDemo{ @Override public void test() { System.out.println("继承抽象类,一定要重写抽象类中的抽象方法,如果不重写,那么该类也是抽象类!"); } } package abstractdemo; public class Demo { public static void main(String[] args) { AbstractDemo ad = new Test(); ad.test(); } }
现在就可以清楚的发现:
(1)抽象类继承子类里面有明确的方法覆写要求,而普通类可以有选择性的来决定是否需要覆写;
(2)抽象类实际上就比普通类多了一些抽象方法而已,其他组成部分和普通类完全一样;
(3)普通类对象可以直接实例化,但抽象类的对象必须经过向上转型之后才可以得到。
虽然一个类的子类可以去继承任意的一个普通类,可是从开发的实际要求来讲,普通类尽量不要去继承另外一个普通类,而是去继承抽象类。
抽象类的使用限制:
1.由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。抽象类中是有构造函数的,所以子类继承抽象类,构造方法的调用顺序仍然是先父类构造函数,然后子类构造函数
2.抽象类不可以修饰为final,因为抽象类有子类,而final修饰的类不能被继承;
3.外部抽象类不能被修饰为static,外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类的成员,继承的时候使用“外部类.内部类”的形式表示类名称。
package com.wz.abstractdemo;static abstract class A{//定义一个抽象类public abstract void print(); }class B extends A{public void print(){ System.out.println("**********"); } }public class TestDemo {public static void main(String[] args) { A a = new B();//向上转型 a.print(); } }
执行结果
Exception in thread "main" java.lang.Error: Unresolved compilation problem: Illegal modifier for the class A; only public, abstract & final are permitted at com.wz.abstractdemo.A.<init>(TestDemo.java:3) at com.wz.abstractdemo.B.<init>(TestDemo.java:9) at com.wz.abstractdemo.TestDemo.main(TestDemo.java:18)
再看一个关于内部抽象类:
package com.wz.abstractdemo;abstract class A{//定义一个抽象类static abstract class B{//static定义的内部类属于外部类public abstract void print(); } }class C extends A.B{public void print(){ System.out.println("**********"); } }public class TestDemo {public static void main(String[] args) { A.B ab = new C();//向上转型 ab.print(); } }
执行结果:
**********
4.任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接调用,对于抽象类也一样。但是修饰为abstract的抽象方法,不能修饰为static,没有意义;
5.有时候由于抽象类中只需要一个特定的系统子类操作,所以可以忽略掉外部子类。这样的设计在系统类库中会比较常见,目的是对用户隐藏不需要知道的子类。
范例如下:
package com.wz.abstractdemo;abstract class A{//定义一个抽象类public abstract void print();private static class B extends A{//内部抽象类子类public void print(){//覆写抽象类的方法System.out.println("Hello World !"); } }//这个方法不受实例化对象的控制public static A getInstance(){return new B(); } }public class TestDemo {public static void main(String[] args) {//此时取得抽象类对象的时候完全不需要知道B类这个子类的存在A a = A.getInstance(); a.print(); } }
运行结果:
Hello World !
抽象类的应用——模板设计模式
例如,现在有三类事物:
(1)机器人:充电,工作;
(2)人:吃饭,工作,睡觉;
(3)猪:进食,睡觉。
现要求实现一个程序,可以实现三种不同事物的行为。
先定义一个抽象行为类:
package com.wz.abstractdemo;public abstract class Action{public static final int EAT = 1 ;public static final int SLEEP = 3 ;public static final int WORK = 5 ;public abstract void eat();public abstract void sleep();public abstract void work();public void commond(int flags){ switch(flags){case EAT:this.eat();break;case SLEEP:this.sleep();break;case WORK:this.work();break;case EAT + SLEEP:this.eat();this.sleep();break;case SLEEP + WORK:this.sleep();this.work();break;default:break; } } }
定义一个机器人的类:
package com.wz.abstractdemo;public class Robot extends Action{ @Overridepublic void eat() { System.out.println("机器人充电"); } @Overridepublic void sleep() { } @Overridepublic void work() { System.out.println("机器人工作"); } }
定义一个人的类:
package com.wz.abstractdemo;public class Human extends Action{ @Overridepublic void eat() { System.out.println("人吃饭"); } @Overridepublic void sleep() { System.out.println("人睡觉"); } @Overridepublic void work() { System.out.println("人工作"); } }
定义一个猪的类:
package com.wz.abstractdemo;public class Pig extends Action{ @Overridepublic void eat() { System.out.println("猪进食"); } @Overridepublic void sleep() { System.out.println("猪睡觉"); } @Overridepublic void work() { } }
测试主类:
package com.wz.abstractdemo;public class AbstractDemo {public static void main(String[] args) { fun(new Robot()); fun(new Human()); fun(new Pig()); }public static void fun(Action act){ act.commond(Action.EAT); act.commond(Action.SLEEP); act.commond(Action.WORK); } }
运行结果:
机器人充电 机器人工作 人吃饭 人睡觉 人工作 猪进食 猪睡觉
모든 하위 클래스가 작업을 정상적으로 완료하려면 지정된 방법에 따라 재정의해야 합니다. 이때 추상 클래스의 기능은 클래스 정의 템플릿의 기능입니다.
-------------------------------------------------- --- ---------------------------------- --- ---------------------------------- --- ---------------------------------- --- ---------------------------------- --- ------------------------
인터페이스는 a 추상 클래스보다 더 추상적인 "클래스"입니다. 여기서는 "클래스"를 더 잘 표현할 수 있는 단어를 찾을 수 없기 때문에 따옴표로 묶었습니다. 하지만 인터페이스 자체는 클래스가 아니라는 점을 분명히 해야 합니다. 이는 인터페이스를 인스턴스화할 수 없다는 사실에서 알 수 있습니다. 예를 들어, new Runnable()은 확실히 잘못된 것이므로 구현 클래스만 새로 만들 수 있습니다.
인터페이스는 클래스 간 프로토콜을 설정하는 데 사용됩니다. 인터페이스가 제공하는 것은 특정 구현이 없는 형식일 뿐입니다. 동시에 인터페이스를 구현하는 구현 클래스는 인터페이스의 모든 메소드를 구현해야 합니다. Implements 키워드를 사용하면 해당 클래스가 특정 인터페이스 또는 특정 인터페이스 그룹을 따르고 있음을 나타냅니다. 겉모습이지만 이제는 작동 방식을 명시해야 합니다.”
인터페이스는 데이터 보안을 보장하기 위해 다중 상속을 허용하지 않습니다. 즉, 상속은 상위 클래스를 하나만 가질 수 있지만 인터페이스는 여러 인터페이스를 구현할 수 있습니다. 동시에 인터페이스와 관계없이 인터페이스는 관계가 없으므로 추상 클래스가 다중 상속을 가질 수 없다는 단점을 인터페이스에서 보완하지만 상속과 인터페이스를 함께 사용하는 것이 좋습니다. 데이터 보안뿐만 아니라 다중 상속도 달성합니다.
인터페이스를 사용할 때 다음 사항에 주의해야 합니다.
1. 인터페이스의 모든 메소드 접근 권한은 자동으로 public으로 선언됩니다. 정확하게 말하면 공개만 가능합니다. 물론 보호됨 또는 비공개로 명시적으로 선언할 수 있지만 컴파일 오류가 발생합니다!
2. 인터페이스의 "멤버 변수"는 자동으로 public static final이 되므로 인터페이스에서 "멤버 변수" 또는 불변 상수를 정의할 수 있습니다. 클래스 이름 ImplementClass.name을 통해 직접 액세스할 수 있습니다.
3. 인터페이스에 구현된 메서드가 없습니다.
4. 인터페이스를 구현하는 비추상 클래스는 인터페이스의 모든 메서드를 구현해야 합니다. 추상 클래스는 구현할 필요가 없습니다.
5. new 연산자를 사용하여 인터페이스를 인스턴스화할 수는 없지만 인터페이스를 구현하는 클래스의 개체를 참조해야 하는 인터페이스 변수를 선언할 수 있습니다. 객체가 특정 인터페이스를 구현하는지 여부를 확인하려면 인스턴스를 사용할 수 있습니다. 예: if(Comparable의 객체 인스턴스){}.
6. 다중 인터페이스 구현 시 메소드 이름의 중복을 피해야 합니다.
Java 언어에서 추상 클래스와 인터페이스는 추상 클래스 정의를 지원하는 두 가지 메커니즘입니다. Java에 강력한 객체 지향 기능이 부여되는 것은 바로 이 두 가지 메커니즘의 존재 때문입니다. 추상 클래스 정의에 대한 지원 측면에서 추상 클래스와 인터페이스 사이에는 큰 유사점이 있으며 서로 대체될 수도 있습니다. 따라서 많은 개발자는 추상 클래스를 정의할 때 추상 클래스와 인터페이스를 선택하는 데 좀 더 무관심한 것 같습니다. 실제로 둘 사이에는 큰 차이가 있으며, 그들의 선택은 문제 영역의 성격에 대한 이해와 설계 의도에 대한 이해가 올바르고 합리적인지 여부까지 반영합니다.
|
추상 클래스 |
인터페이스 |
인스턴스화 |
cannot |
cannot |
Class |
상속 관계, 클래스는 상속 관계를 한 번만 사용할 수 있습니다. 다중 상속은 다중 인터페이스를 상속하여 달성할 수 있습니다 |
클래스는 다중 인터페이스를 구현할 수 있습니다 |
데이터 멤버 |
자체를 가질 수 있습니다 |
정적일 수는 없습니다. 즉, 정적 최종이어야 하며 일반적으로 여기에 정의되어 있지 않습니다. |
method |
비공개, 비추상 메서드일 수 있으며 추상 메서드를 구현해야 합니다 | 비공개일 수 없습니다. 기본값은 공개이며 추상 유형입니다. |
변수 |
비공개일 수 있으며, 기본값은 친숙한 유형이며 해당 값은 하위 클래스에서 재정의되거나 재할당될 수 있습니다. |
비공개일 수 없습니다. 기본값은 public static final 유형이며, 초기값을 구현 클래스에서 재정의할 수 없으며 값을 변경할 수 없습니다. |
디자인 컨셉 |
은 "is-a" 관계를 나타냅니다. |
"like-a" 관계를 나타냅니다 |
달성됨 | 상속해야 하고, 확장을 사용해야 합니다 |
구현을 사용해야 합니다 |
추상 클래스와 인터페이스는 모두 Java 언어에서 추상 클래스를 정의하는 데 사용됩니다(이 기사의 추상 클래스는 추상 클래스를 번역한 것이 아니라 추상 본문을 나타내며 추상 클래스는 Java 언어에서 정의하는 데 사용됩니다( 추상 클래스의 메소드)가 정의되어 있는데 추상 클래스란 무엇이며 추상 클래스를 사용하면 어떤 이점을 얻을 수 있습니까?
메서드를 구현하지 않고 존재를 선언하는 클래스를 추상 클래스라고 합니다. 몇 가지 기본 동작을 구현하는 클래스를 만들고 해당 클래스에 대한 메서드를 선언하는 데 사용되지만 구현에서는 사용할 수 없습니다. 이 수업에 이 수업. 추상 클래스의 인스턴스를 만들 수 없습니다. 그러나 유형이 추상 클래스인 변수를 생성하고 이 변수가 구체적인 하위 클래스의 인스턴스를 가리키도록 할 수 있습니다. 추상 생성자나 추상 정적 메서드는 있을 수 없습니다. Abstract 클래스의 하위 클래스는 상위 클래스의 모든 추상 메서드에 대한 구현을 제공합니다. 그렇지 않으면 추상 클래스이기도 합니다. 대신 하위 클래스에서 메서드를 구현하세요. 해당 동작을 인식하는 다른 클래스는 해당 클래스에서 이러한 메서드를 구현할 수 있습니다.
인터페이스는 추상 클래스의 변형입니다. 인터페이스에서 모든 메소드는 추상적입니다. 이러한 인터페이스를 구현하면 다중 상속을 얻을 수 있습니다. 인터페이스의 모든 메소드는 추상적이며 그 중 어느 것도 프로그램 본문을 갖고 있지 않습니다. 인터페이스는 정적 최종 멤버 변수만 정의할 수 있습니다. 인터페이스 구현은 구현 클래스가 인터페이스 정의에서 동작을 상속할 수 없다는 점을 제외하면 서브클래싱과 유사합니다. 클래스가 특정 인터페이스를 구현할 때 해당 인터페이스의 모든 메서드를 정의(즉, 프로그램 본문 제공)합니다. 그런 다음 인터페이스를 구현하는 클래스의 모든 개체에서 인터페이스의 메서드를 호출할 수 있습니다. 추상 클래스가 있으므로 인터페이스 이름을 참조 변수의 유형으로 사용할 수 있습니다. 일반적인 동적 연결이 적용됩니다. 참조는 인터페이스 유형 간에 변환될 수 있으며, instanceof 연산자를 사용하여 객체의 클래스가 인터페이스를 구현하는지 여부를 확인할 수 있습니다.인터페이스는 인터페이스를 상속할 수 있습니다. 추상 클래스는 인터페이스를 구현할 수 있고 추상 클래스는 엔터티 클래스를 상속할 수 있지만 전제는 엔터티 클래스에 명확한 생성자가 있어야 한다는 것입니다.
인터페이스는 "구현 방법"에 관계없이 "어떤 기능을 달성할 수 있는지"에 더 중점을 둡니다.
1. 유사점 A. 둘 다 추상 클래스이며 둘 다 인스턴스화할 수 없습니다. 2. 차이점 인터페이스 적용 사례 추상 클래스의 응용 시나리오 인터페이스 및 추상 클래스의 이점: 好像定义接口是提前做了个多余的工作。下面我给大家总结了4点关于JAVA中接口存在的意义: 1、重要性:在Java语言中, abstract class 和interface 是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力。 2、简单、规范性:如果一个项目比较庞大,那么就需要一个能理清所有业务的架构师来定义一些主要的接口,这些接口不仅告诉开发人员你需要实现那些业务,而且也将命名规范限制住了(防止一些开发人员随便命名导致别的程序员无法看明白)。 3、维护、拓展性:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。 可是在不久将来,你突然发现这个类满足不了你了,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。 如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。 4、安全、严密性:接口是实现软件松耦合的重要手段,它描叙了系统对外的所有服务,而不涉及任何具体的实现细节。这样就比较安全、严密一些(一般软件服务商考虑的比较多)。
B. 인터페이스 구현 클래스와 추상 클래스의 하위 클래스 모두 선언된 추상 메서드를 구현해야 합니다.
A. 인터페이스는 구현을 사용하여 구현해야 하고, 추상 클래스는 확장을 사용하여 상속해야 합니다.
B. 클래스는 여러 인터페이스를 구현할 수 있지만 클래스는 하나의 추상 클래스만 상속할 수 있습니다.
C. 인터페이스는 특정 기능의 구현을 강조하는 반면 추상 클래스는 소유권 관계를 강조합니다.
D. 인터페이스 구현 클래스와 추상 클래스의 하위 클래스 모두 해당 추상 메서드를 구현해야 하지만 구현 형식이 다릅니다. 인터페이스의 모든 메소드는 추상 메소드이며 선언만 됩니다.
(선언, 메서드 본문 없음), 구현 클래스에서 이를 구현해야 합니다. 추상 클래스의 하위 클래스는 선택적으로 구현될 수 있습니다.
이 선택에는 두 가지 의미가 있습니다.
첫째, Abstract 클래스의 모든 메서드가 추상은 아니며 추상이 있는 메서드만 추상이며 하위 클래스에서 이를 구현해야 합니다. 추상이 없는 메서드의 경우 메서드 본문은 Abstract 클래스에 정의되어야 합니다.
둘째, 추상 클래스의 하위 클래스가 이를 상속할 때 비추상 메서드를 직접 상속하거나 재정의할 수 있습니다. 추상 메서드의 경우 이를 구현하도록 선택하거나 해당 메서드를 구현하지 않고 다시 추상으로 선언할 수 있습니다. 구현할 수 있지만 이 클래스도 abstract로 선언해야 합니다. 물론 추상 클래스이기 때문에 인스턴스화할 수는 없습니다.
E. 추상 클래스는 인터페이스와 클래스 사이의 중개자입니다.
인터페이스는 완전히 추상적입니다. 메서드만 선언할 수 있고, 공용 메서드만 선언할 수 있으며, 메서드 본문을 정의할 수 없고, 인스턴스 변수를 선언할 수 없습니다. 그러나 인터페이스는 상수 변수를 선언할 수 있으며 JDK에서 이러한 예를 찾는 것은 어렵지 않습니다. 그러나 인터페이스에 상수 변수를 배치하는 것은 인터페이스로서의 존재 목적에 위배되며 인터페이스와 클래스의 서로 다른 값을 혼동하기도 합니다. 꼭 필요한 경우 해당 추상 클래스나 클래스에 넣을 수 있습니다.
추상 클래스는 인터페이스와 클래스를 연결하는 역할을 합니다. 한편으로 추상 클래스는 추상적이며 하위 클래스가 구현해야 하는 함수를 표준화하기 위해 추상 메서드를 선언할 수 있습니다. 반면에 하위 클래스에서 직접 사용하거나 재정의할 수 있도록 기본 메서드 본문을 정의할 수 있습니다. 또한 상속을 통해 하위 클래스에서 사용할 자체 인스턴스 변수를 정의할 수도 있습니다.
A. 클래스는 구현 방법에 관계없이 사전에 조정하기 위해 특정 인터페이스가 필요합니다.
B. 특정 기능을 구현할 수 있는 식별자로 존재하거나, 인터페이스 메소드가 없는 순수 식별자일 수도 있습니다.
C. 클래스 그룹은 단일 클래스로 처리되어야 하며 호출자는 인터페이스를 통해 이 클래스 그룹에만 연결됩니다.
D. 특정 여러 기능을 구현해야 하며 이러한 기능은 전혀 연결되지 않을 수 있습니다.
한마디로 통합 인터페이스와 인스턴스 변수 또는 기본 메서드가 모두 필요할 때 사용할 수 있습니다. 가장 일반적인 것은 다음과 같습니다.
A. 인터페이스 세트를 정의하지만 각 구현 클래스가 모든 인터페이스를 구현하도록 강제하고 싶지는 않습니다. 추상 클래스를 사용하여 메서드 본문 집합 또는 빈 메서드 본문을 정의한 다음 하위 클래스에서 다루고자 하는 메서드를 선택하도록 할 수 있습니다.
B. 어떤 경우에는 순수한 인터페이스만으로는 클래스 간의 조정을 충족할 수 없습니다. 또한 서로 다른 관계를 구별하기 위해 클래스의 상태를 나타내는 변수가 필요합니다. Abstract의 중개자 역할은 이를 매우 잘 충족시킬 수 있습니다.
C. 상호 조정된 메서드 집합을 표준화합니다. 그 중 일부는 상태에 관계없이 공통적이고 공유할 수 있으며, 다른 메서드에서는 각 하위 클래스가 해당 메서드를 구현해야 합니다. 특정 기능을 구현하기 위해 특정 상태를 소유합니다.
尽管抽象类和接口之间存在较大的相同点,甚至有时候还可以互换,但这样并不能弥补他们之间的差异之处。下面将从语法层次和设计层次两个方面对抽象类和接口进行阐述。
在语法层次,java语言对于抽象类和接口分别给出了不同的定义。下面已Demo类来说明他们之间的不同之处。
使用抽象类来实现:
public abstract class Demo { abstract void method1(); void method2(){ //实现 } }
使用接口来实现
interface Demo { void method1(); void method2(); }
抽象类方式中,抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法,但是接口方式中,它仅能够有静态、不能修改的成员数据(但是我们一般是不会在接口中使用成员数据),同时它所有的方法都必须是抽象的。在某种程度上来说,接口是抽象类的特殊化。
对子类而言,它只能继承一个抽象类(这是java为了数据安全而考虑的),但是却可以实现多个接口。
上面只是从语法层次和编程角度来区分它们之间的关系,这些都是低层次的,要真正使用好抽象类和接口,我们就必须要从较高层次来区分了。只有从设计理念的角度才能看出它们的本质所在。一般来说他们存在如下三个不同点:
1、 抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
2、 跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。
3、 设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
我们有一个Door的抽象概念,它具备两个行为open()和close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:
抽象类:
abstract class Door{ abstract void open(); abstract void close(); }
接口
interface Door{ void open(); void close(); }
至于其他的具体类可以通过使用extends使用抽象类方式定义Door或者Implements使用接口方式定义Door,这里发现两者并没有什么很大的差异。
但是现在如果我们需要门具有报警的功能,那么该如何实现呢?
解决方案一:给Door增加一个报警方法:clarm();
abstract class Door{ abstract void open(); abstract void close(); abstract void alarm(); }
或者
interface Door{ void open(); void close(); void alarm(); }
这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle)(参见:),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方 法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变而改变,反之依然。
解决方案二
既然open()、close()和alarm()属于两个不同的概念,那么我们依据ISP原则将它们分开定义在两个代表两个不同概念的抽象类里面,定义的方式有三种:
1、两个都使用抽象类来定义。
2、两个都使用接口来定义。
3、一个使用抽象类定义,一个是用接口定义。
由于java不支持多继承所以第一种是不可行的。后面两种都是可行的,但是选择何种就反映了你对问题域本质的理解。
如果选择第二种都是接口来定义,那么就反映了两个问题:1、我们可能没有理解清楚问题域,AlarmDoor在概念本质上到底是门还报警器。2、如果我们对问题域的理解没有问题,比如我们在分析时确定了AlarmDoor在本质上概念是一致的,那么我们在设计时就没有正确的反映出我们的设计意图。因为你使用了两个接口来进行定义,他们概念的定义并不能够反映上述含义。
第三种,如果我们对问题域的理解是这样的:AlarmDoor本质上Door,但同时它也拥有报警的行为功能,这个时候我们使用第三种方案恰好可以阐述我们的设计意图。AlarmDoor本质是们,所以对于这个概念我们使用抽象类来定义,同时AlarmDoor具备报警功能,说明它能够完成报警概念中定义的行为功能,所以alarm可以使用接口来进行定义。如下:
abstract class Door{ abstract void open(); abstract void close(); } interface Alarm{ void alarm(); } class AlarmDoor extends Door implements Alarm{ void open(){} void close(){} void alarm(){} }
这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。
批注:
|
1. 추상 클래스는 Java 언어에서 상속 관계를 나타냅니다. 하위 클래스는 상위 클래스를 하나만 가질 수 있지만 인터페이스는 여러 개 가질 수 있습니다.
2. 추상 클래스는 자체 멤버 변수와 비추상 클래스 메서드를 가질 수 있지만 인터페이스에는 정적 및 불변 멤버 데이터만 존재할 수 있습니다(그러나 멤버 데이터는 일반적으로 인터페이스에 정의되지 않음). 방법은 추상적입니다.
3. 추상 클래스와 인터페이스에 반영된 디자인 개념은 다릅니다. 추상 클래스는 "is-a" 관계를 나타내고 인터페이스는 "like-a" 관계를 나타냅니다.
추상 클래스와 인터페이스는 Java 언어의 두 가지 다른 추상 개념입니다. 둘 사이에는 큰 유사점이 있지만 이들의 존재는 다형성을 매우 잘 지원합니다. 그러나 그들의 선택은 종종 문제 영역에 대한 이해를 반영합니다. 문제 영역의 성격을 잘 이해해야만 올바르고 합리적인 설계를 할 수 있습니다.
위 내용은 인터페이스는 무엇입니까? 추상 클래스란 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!