JAVA 抽象类、普通类、接口

【概念】

1.普通类

    类可以看成是创建Java对象的模板

    一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。
   一个类可以有多个方法。
2.抽象类

      在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

      抽象类除了  不能实例化对象 之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。

      由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。

      父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。

      在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。



举例:

 1.抽象类(注意到该Employee类没有什么不同,尽管该类是抽象类,但是它仍然有3个成员变量,7个成员方法和1个构造方法。)


但是在main方法中 当我们要  new 这个Emplyee类的时候,就会报错


错误代码如下:

Employee.java:46: Employee is abstract; cannot be instantiated
      Employee e = new Employee("George W.", "Houston, TX", 43);
                   ^
1 error
所以也就是抽象类  不能  实例化对象。


2.抽象类的继承

然后我们选择指定一个Salary类来   继承  Employee  

通过super()来调用Employee构造方法初始化的参数值

并写入Salary类自己独有的方法:

此时再使用main方法来实例化Salary,该对象将从 Employee 类继承7个成员方法,且通过该方法可以设置或获取三个成员变量。

Employee e = new Salary(...),那么父类Employee就可以访问子类Salary的所有成员。

(extends只是继承,相当于重写了父类的某些方法,如果要得到访问的权限,还是要通过  父类  引用对象  = new  子类()的方式)

最后的输出结果如下:

Constructing an Employee
Constructing an Employee
Call mailCheck using  Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0

Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.



抽象方法

     如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。

     Abstract关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。

抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

      public abstract double computePay();


声明抽象方法会造成以下两个结果:

  • 如果一个类包含抽象方法,那么该类必须是抽象类。
  • 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。 最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象


如果Salary类继承了Employee类,那么它必须实现computePay()方法:


抽象类总结规定

  • 1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

  • 2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

  • 3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

  • 4. 构造方法,类方法(用static修饰的方法)不能声明为抽象方法。

  • 5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。



3.接口

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

接口与类相似点:

  • 一个接口可以有多个方法。
  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
  • 接口的字节码文件保存在 .class 结尾的文件中。
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

接口特性

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  • 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  • 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  • 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。


接口很多人都会写,也明白它用在什么地方,会产生哪些效果。特别是很多人一提到接口,就脱口而出规范、设计模式,那么对于接口又了解多少呢?本章试着从java中接口的定义和使用出发,谈谈java中接口具有哪些特性。

一、接口

首先,了解接口interface就必须和类class区分开,为什么这么说呢?因为接口和类是两个概念,但接口又和类具有一定的关系。有人不禁会问,怎么说呢?类class是从java.lang.Object类派生而来,但接口interface并不是从某一个特定接口派生而来,两个interface可能没有任何交集,但两个class一定存在交集。接口不是Object的子类,但接口却隐士申明了Object中的所有可继承方法,类似于Object子类继承了它的所有可继承方法,这里申明的方法都是abstract形式,也即只有申明,没有方法体block。
其次,接口的申明形式如下:

  [annotation] [modifier] interface identitor [extends interfacelist] {
  [public | static |  ]  [FieldConstant];  
  [public | abstract |  ]  [AbstractMethod];
  [public | static |  ]  [MemberType];
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中,”[ ]”表示可有可无,而[ a | b ]表示从a或b中选择一个。annotation表示接口的注解;modifier表示接口的修饰符,正如前面所说top interface只能是public, nested interface可是static,在class中还可以是protected或private;identitor表示该接口的名称,一般取名时前缀为”I” + interfaceName 或者 interfaceName + “able”;interfacelist表示继承父接口列表,如Comparable, Serializable,Iterator等;FieldConstant表示接口中的字段;AbstractMethod表示接口中的abstract方法;MemeberType表示接口中的成员,如class,interface。

二、interface种类

java将接口分为两种:normal interface,annotation type。normal这个单词不用解释,既然有两种,那么他们肯定存在差异,也具有一定的共性。下面分别谈谈这两种interface。

三、normal interface

通用接口又可以分为外层接口top interface、内嵌接口nested interface,也即是作为一个单独的接口,还是被嵌入于类中、接口中的接口。它们的接口体body定义相同,唯一的区别在于修饰符的差异。nested interface的修饰符可以是static、public、protected或private(这两者只能是作为class中的内嵌接口才能修饰,而作为top interface中的内嵌接口进行修饰,会编译报错)。
1、FieldConstant常量字段
       在接口中定义的字段都被修饰符public static final默认进行了修饰,虽然可以显示的添加public、static或final,但是建议不要添加,因为这是多余的行为,可以添加只是说明字段的修饰符。此处,还需注意一个问题,那就是常量的初始化问题,因为class中的常量可以在定义的时候进行初始化,也可以在构造函数中进程初始化,那么问题是接口中的常量何时进行初始化?因为不能显示的调用接口的初始化,所以常见在定义的时候就必须初始化,否则编译器会抛出初始化Error。其次,接口中的FieldConstant能否被子类继承?可以,如果class实现了interface,那么该接口的实例引用可以引用该接口中的常量字段,如果subinterface继承了该接口,那么subinterface继承了该接口的所有常量字段。

public class Test implements ITest {
    public static void main(String... args) {
        ITest test = new Test();
        //通过实例引用接口ITest的常量字段
        System.out.println(test.strTest); //hello world
        //通过接口引用常量字段
        System.out.println(ITest.strTest); //hello world
        //通过子接口引用常量字段
        System.out.println(ISubTest1.strTest); // hello world
    }
}
public interface ITest {
    //字段常量在定义的时候必须进行初始化
    String strTest = "hello world";
} 
public interface ISubTest1 extends ITest {}

提问:接口中的常量字段能否hide父接口中相同名称的字段?肯定可以!
2、AbstractMethod抽象方法
接口中定义的方法没有方法体,并且修饰符为public abstract默认进行修饰,也就是可以给接口中的方法添加这两种修饰符,但是建议不添加,完全是画蛇添足,理由和常量字段一样。既然是方法,那么就必然涉及到方法的重写、重载以及实现问题。

public interface ITest {
    public abstract testMethod();
    //可以不用添加public abstract修饰符,编译器默认添加
    test();
}
  • 重写override
    在class中重写,子类通过重写父类的方法来改变通过该方法名进行引用时,所具有的行为。那么,interface的方法重写是什么样的呢?除了方法体,方法重写的签名和返回值都和class中方法的重写要求相同。
public interface ITest {
    //定义一个待重写的方法
    Object test() throws IOException, ClassCastException;

    String test2();
}

public interface ISubTest extends ITest {
    //第一种,返回值类型是重写方法的子类型
    String test() throws IOException, ClassCastException;
    //第二种,异常子句重写
    Object test() throws IOException;
    String test() throws CLassCastException;
    String test() throws EOFException; 
    //第三种,正常重写
    Object test();
    String test(Object obj) throws IOException;
}

public class Test implements ISubTest {
    //实现子接口的方法,如果子接口没有重写父接口的方法,则必须实现父接口的方法。
    //也即,实现子接口申明的、继承的方法。

    //第一种,返回值类型是重写方法的子类型
    String test() throws IOException, ClassCastException {
        return null;
    }
    //第二种,异常子句重写
    Object test() throws IOException {
        return null;
    }
    String test() throws CLassCastException {
        return null;
    }
    String test() throws EOFException {
        return null;
    }
    //第三种,正常重写
    Object test() {
        return null;
    }
    String test(Object obj) throws IOException {
        return null;
    }

    //实现子接口继承的方法
    String test2(){
        return null;
    }
}
  • 重载overload
    重载相对于重写来说没那么复杂,它所要求的是方法的签名不同,所谓方法签名是指方法的返回值和方法的参数组合,如String test(Object obj)的签名signatrue为(Object)String。但是,这里并不能根据返回值的不同来判定重载,而在JVM的底层中是根据signature来进行实现,只是编译器进行了过滤处理。
public interface ITest {
    //原始方法
    String test();

    //第一种,参数个数不同
    String test(String str);

    //第二种,参数的类型不同
    String test(int a);

    //第三种,参数的类型和个数不同
    String test(String str1, String str2);
}
  • 方法的实现
    接口中方法的实现都必须反映到实现类中,同时允许class通过继承类实现接口interface。此外,class实现implements接口,那么该class必须实现接口中的所有abstract方法(后面会说JDK8的新特性)。
    public class Test {
        //父类中定义了test1, 记得实现时修饰符的全限必须是public
        public void test1() {
        }
    }
    class A extends Test implements ITest1, ITest2 {
        //实现ITest2接口方法,记得实现时修饰符的全限必须是public
        public String test2() {
            return null;
        }
    }

    interface ITest1 {
        //方法被A的父类Test实现
        void test1();
    }

    interface ITest2 {
        //方法被A实现
        String test2();
    }

提问:接口中字段和方法是否可以具有相同名称?肯定可以!
3、MemberType成员类型
class中可以申明内部类innerClass、内部接口innerInterface和内部枚举innerEnum,接口也同样可以,此时对于innerClass、innerInterface成员类型的默认修饰符为public static,那么在定义时可以显示的指定这两个修饰符,但不建议。但innerEnum是内部枚举类型,此时它的默认修饰符为public static final,也就是说在接口中定义的枚举都是常量枚举。

public interface ITest {
    //修饰符默认为public static 
    class NestedClass {
        private int a;
        NestedClass (int a) {
            this.a = a;
        }
    }

    //默认修饰符为 public static
    interface INestedInterface {

    }

    //默认修饰符为public static final
    enum NestedEnum {
        RED, YELLOW,GREEN;
    }
}

提问:接口中的字段是否可以和成员类型具有相同的名字?可以;接口中的方法能否具有和成员类型相同的名字呢?可以。
4、内嵌接口NestedInterface
接口可以作为top interface的内嵌成员,也可以作为class中的内嵌成员。无论作为class还是interface的内嵌成员,其中接口体的定义内如如前所述,但是作为class的成员时,接口申明的修饰符还可以是protected 、private或者static。

四、接口中的defaut方法

考虑这样一种场景,某个已发布的jar中的某个接口需要集成一个新的功能函数,那么此时需要往接口中添加一个abstract方法,而所有implements这个接口的class都必须实现这个abstract方法,如果采用一个接口适配器来实现程序,也就需要在适配器类中进行实现即可,其他情况是否就很麻烦?JDK8中在接口中引入了default方法,它是一个接口实例方法,可以用来继承。那么default方法和接口的abstract方法有什么相同点和特性?具体体现在重写、重载和实现。

public interface ITest {
   //abstract抽象方法
   void test();

   //default默认方法
   default void testDefault() {
    System.out.println("hello world");
   }

   //@complile-error默认方法不能作为abstract方法的具体实现
   /*default void test() {
   }*/
}
  • 重写
    方法重写的方式就四种,default方法和普通的abstract方法没有区别,都必须满足下列四种情况之一:1、具有和父接口方法相同的返回值、名称和抛出异常;2、具有和父接口方法相同名称、抛出异常,但返回值类型是子类型;3、具有和父接口方法相同的名称、返回值,但抛出异常兼容(子异常类型);4、具有和父接口方法相同的名称,但返回值和抛出异常兼容。
    但default方法不能重写abstract方法,它只能重写父类的default方法,同时default方法不能具有和abstract方法相同的名称;
public interface ITest {
    //抽象方法
    Object test();
    //默认方法
    default Object testDefault() {
     System.out.println("hello world");
    }
}
public interface ISubTest extends ITest {
    //@compile-error编译错误
    //默认方法不能作为abstract方法的重写方法
    /*default Object test() {
         System.out.println("test");
    }*/

    //默认方法可以重写默认方法
    default String testDefault() {
        System.out.prinln("hello world");
    }
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值