> Java > java지도 시간 > 본문

Java 개선 4부 -----추상 클래스 및 인터페이스

黄舟
풀어 주다: 2017-02-09 13:36:26
원래의
944명이 탐색했습니다.

인터페이스와 내부 클래스는 구현에서 인터페이스를 분리하는 보다 구조화된 방법을 제공합니다.

추상 클래스와 인터페이스는 Java 언어에서 추상 개념을 정의하는 두 가지 메커니즘입니다. Java가 강력한 객체 지향 기능을 갖는 이유는 바로 그것 때문입니다. 추상적 개념에 대한 지원은 매우 유사하고 심지어 상호 교환 가능하지만 차이점이 있습니다.


1. 추상 클래스

객체지향 분야에서는 모든 것이 객체이고 모든 객체가 클래스를 통해 설명된다는 것을 우리 모두 알고 있습니다. 모든 클래스가 객체를 설명하는 데 사용되는 것은 아닙니다. 클래스에 특정 개체를 설명하는 데 필요한 정보가 충분하지 않고 이를 지원하기 위해 다른 특정 클래스가 필요한 경우 이러한 클래스를 추상 클래스라고 부릅니다. 예를 들어, new Animal()은 이것이 Animal 객체를 생성한다는 것을 우리 모두 알고 있지만, 이 Animal이 구체적으로 어떻게 생겼는지는 모릅니다. 특정 동물에 대한 개념이 없기 때문에 필요한 추상 클래스입니다. 개나 고양이와 같은 특정 동물이 있어야만 그것이 어떻게 생겼는지 알 수 있습니다.

객체지향 분야에서는 추상 개념은 문제 영역에 해당하는 특정 개념이 없기 때문에 추상 개념을 표현하는 데 사용되는 추상 클래스를 인스턴스화할 수 없습니다.

동시에 추상 클래스는 데이터 추상화 개념을 구현하고 다형성을 달성하기 위한 메커니즘입니다. 이는 추상 메소드 세트를 정의하며, 이 추상 메소드 세트의 특정 표현식은 파생 클래스에 의해 구현될 수 있습니다. 동시에 추상 클래스는 상속이라는 개념을 제공합니다. 그 출발점은 상속입니다. 그렇지 않으면 존재 의미가 없습니다. 따라서 정의된 추상 클래스는 상속에 사용되어야 하며 동시에 추상 클래스를 노드로 사용하는 상속 관계의 계층적 체인에서 리프 노드는 특정 구현 클래스여야 합니다. (이 이해가 잘못된 것인지는 모르겠습니다!!! 전문가의 안내...)

추상 클래스 사용 시 주의할 점이 몇 가지 있습니다.

1. 추상 클래스는 인스턴스화할 수 없으며 인스턴스화는 참조만 필요한 하위 클래스에 맡겨야 합니다.

2. 추상 메소드는 서브클래스별로 다시 작성되어야 합니다.

3. 추상 클래스에 추상 메소드가 포함되어 있는 한, 해당 메소드는 다른 메소드가 포함되어 있는지 여부에 관계없이 추상 클래스로 정의되어야 합니다.

4. 추상 클래스는 특정 메소드를 포함할 수 있으며, 물론 추상 메소드를 포함할 필요는 없습니다.

5. 하위 클래스의 추상 메소드는 상위 클래스의 추상 메소드와 동일한 이름을 가질 수 없습니다.

6. Abstract는 final과 병렬로 동일한 클래스를 수정할 수 없습니다.

7. Abstract는 private, static, final 또는 Native와 병렬로 동일한 메서드를 수정하는 데 사용할 수 없습니다. ,

예:

추상 동물 클래스 Animal을 정의하고 cry()라는 추상 메서드를 제공합니다. 고양이와 개는 Animal 클래스의 하위 클래스입니다. 따라서 Cat과 Dog는 cry() 메서드를 구현해야 합니다. 다음과 같습니다:

public abstract class Animal {  
    public abstract void cry();  
}  
  
public class Cat extends Animal{  
  
    @Override  
    public void cry() {  
        System.out.println("猫叫:喵喵...");  
    }  
}  
  
public class Dog extends Animal{  
  
    @Override  
    public void cry() {  
        System.out.println("狗叫:汪汪...");  
    }  
  
}  
  
public class Test {  
  
    public static void main(String[] args) {  
        Animal a1 = new Cat();  
        Animal a2 = new Dog();  
          
        a1.cry();  
        a2.cry();  
    }  
}  
  
--------------------------------------------------------------------  
Output:  
猫叫:喵喵...  
狗叫:汪汪...
로그인 후 복사

추상 클래스와 추상 메서드를 만드는 것은 클래스의 추상화를 명시적으로 만들고 사용자와 컴파일러에게 이를 사용하는 방법을 알려줄 수 있기 때문에 매우 유용합니다. 공개 메소드를 상속 계층 구조 위로 쉽게 이동할 수 있습니다. (출처:Think in java)


2. 인터페이스

인터페이스는 추상 클래스보다 더 추상적인 "클래스"입니다. 여기서는 "클래스"를 더 잘 표현할 수 있는 단어를 찾을 수 없기 때문에 따옴표로 묶었습니다. 하지만 인터페이스 자체는 클래스가 아니라는 점을 분명히 해야 합니다. 이는 인터페이스를 인스턴스화할 수 없다는 사실에서 알 수 있습니다. 예를 들어 new Runnable();은 확실히 잘못된 구현 클래스입니다.

인터페이스는 클래스 간 프로토콜을 설정하는 데 사용되며, 구체적인 구현 없이 형식만 제공합니다. 동시에 인터페이스를 구현하는 구현 클래스는 인터페이스의 모든 메소드를 구현해야 합니다. Implements 키워드를 사용하면 해당 클래스가 특정 인터페이스 또는 특정 인터페이스 그룹을 따르고 있음을 나타냅니다. 겉모습이지만 이제 어떻게 작동하는지 명시해야 합니다."

接口是抽象类的延伸,java了保证数据安全是不能多重继承的,也就是说继承只能存在一个父类,但是接口不同,一个类可以同时实现多个接口,不管这些接口之间有没有关系,所以接口弥补了抽象类不能多重继承的缺陷,但是推荐继承和接口共同使用,因为这样既可以保证数据安全性又可以实现多重继承。

在使用接口过程中需要注意如下几个问题:

1、个Interface的方所有法访问权限自动被声明为public。确切的说只能为public,当然你可以显示的声明为protected、private,但是编译会出错!

2、接口中可以定义“成员变量”,或者说是不可变的常量,因为接口中的“成员变量”会自动变为为public static final。可以通过类命名直接访问:ImplementClass.name。

3、接口中不存在实现的方法。

4、实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。

5、不能使用new操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。可以使用 instanceof 检查一个对象是否实现了某个特定的接口。例如:if(anObject instanceof Comparable){}。

6、在实现多接口的时候一定要避免方法名的重复。


三、抽象类与接口的区别

尽管抽象类和接口之间存在较大的相同点,甚至有时候还可以互换,但这样并不能弥补他们之间的差异之处。下面将从语法层次和设计层次两个方面对抽象类和接口进行阐述。

3.1语法层次

在语法层次,java语言对于抽象类和接口分别给出了不同的定义。下面已Demo类来说明他们之间的不同之处。

使用抽象类来实现:

public abstract class Demo {  
    abstract void method1();  
      
      
    void method2(){  
        //实现  
    }  
}
로그인 후 복사

使用接口来实现

interface Demo {  
    void method1();  
    void method2();  
}
로그인 후 복사



抽象类方式中,抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法,但是接口方式中,它仅能够有静态、不能修改的成员数据(但是我们一般是不会在接口中使用成员数据),同时它所有的方法都必须是抽象的。在某种程度上来说,接口是抽象类的特殊化。


对子类而言,它只能继承一个抽象类(这是java为了数据安全而考虑的),但是却可以实现多个接口。

3.2设计层次

上面只是从语法层次和编程角度来区分它们之间的关系,这些都是低层次的,要真正使用好抽象类和接口,我们就必须要从较高层次来区分了。只有从设计理念的角度才能看出它们的本质所在。一般来说他们存在如下三个不同点:

1、 抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

2、 跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。

3、 设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。

(上面纯属个人见解,如有出入、错误之处,望各位指点!!!!)

为了更好的阐述他们之间的区别,下面将使用一个例子来说明。该例子引自:http://blog.csdn.net/ttgjz/article/details/2960451

我们有一个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的功能,那么上述的定义方式就要反过来了。

批注:

   ISP(Interface Segregation Principle):面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。

   一个类对另外一个类的依赖性应当是建立在最小的接口上的。

인터페이스는 역할을 나타내며, 하나의 인터페이스에 다른 역할을 할당해서는 안 됩니다. 관련되지 않은 인터페이스는 함께 병합되어 크고 비대한 인터페이스를 형성하며 이는 역할과 인터페이스를 오염시킵니다.

4. 요약

1. 추상 클래스는 Java 언어에서 상속 관계를 나타냅니다. 하위 클래스는 상위 클래스를 하나만 가질 수 있지만 인터페이스는 여러 개 있을 수 있습니다.

2. 추상 클래스는 자체 멤버 변수와 비추상 클래스 메서드를 가질 수 있지만 인터페이스에는 정적 및 불변 멤버 데이터만 존재할 수 있습니다(그러나 멤버 데이터는 일반적으로 인터페이스에 정의되지 않습니다). 모든 방법은 추상적입니다.

3. 추상 클래스와 인터페이스에 반영된 디자인 개념은 다릅니다. 추상 클래스는 "is-a" 관계를 나타내고, 인터페이스는 "like-a" 관계를 나타냅니다.

추상 클래스와 인터페이스는 Java 언어의 두 가지 다른 추상 개념이지만 둘 사이에는 큰 유사점이 있습니다. 그러나 그들의 선택은 종종 문제 영역에 대한 이해를 반영합니다. 문제 영역의 성격을 잘 이해해야만 올바르고 합리적인 설계를 할 수 있습니다.

기초를 다지고 실력을 키워 어려움을 두려워하지 않고 정상에 오르세요! ! ! ! ! !


위 내용은 Java Improvement Part 4----추상 클래스 및 인터페이스 관련 내용을 참고하시기 바랍니다. .php.cn)!


원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿