java面对对象(六)--abstract , interface , 内部类

抽象类: abstract

abstract类由来

        父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。

        一个abstract类只关心它的子类是否具有某种功能,并不关心其自身功能的具体行为,功能的具体行为由子类负责实现。抽象类不能被直接实例化,即不能使用关键字new来创建该抽象类的对象。

        抽象:不具体,看不明白。抽象类表象体现。在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。

        抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。

abstract类定义

        Java语法规定,包含抽象方法的类就是抽象类。

        声明方法的存在而不去实现它。abstract修饰的类称为做抽象类;abstract修饰的方法叫做抽象方法,抽象方法只有声明部分,而没有具体的方法体。

abstract方法

        使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

        定义格式:修饰符 abstract 返回值类型 方法名 (参数列表)

        代码举例:public abstract void run()

abstract类的特点:

        1:抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。

        2:抽象方法只定义方法声明,并不定义方法实现。

        3:抽象类不可以被创建对象(实例化)。

        4:只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。

abstract类的细节:

        1:抽象类中是否有构造方法?有,用于给子类对象进行初始化。

        2:抽象类中是否可以定义非抽象方法?

            可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象方法。而且比一般类少了一个创建对象的部分。

        3:抽象关键字abstract和哪些不可以共存?final , private , static定义抽象方法的目的是重写此方法,但如果定义成静态方法就不能被重写

        4:抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。

abstract类的使用规则

        抽象类中可以没有abstract方法(为了强迫使用者必须通过继承来使用这个类);但是一旦类中包含了abstract方法,则这个“类”一定是abstract类,即有抽象方法的类一定是抽象类。

        抽象类的子类必须实现抽象类中的所有抽象方法,否则子类也必须是抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。

public class Cat extends Animal {

        public void run (){

                System.out.println("小猫在墙头走~~~")

        }

}

输出结果:

小猫在墙头走~~~

public class CatTest {

        public static void main(String[] args) {

                // 创建子类对象

                Cat c = new Cat();

                // 调用run方法

                c.run();

        }

}

        此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。

abstract类与普通类

        抽象类前面由abstract修饰,而普通类没有。

        抽象类可以像普通类一样拥有构造方法、静态或非静态成员变量、静态或非静态代码块和静态非抽象方法或非静态非抽象方法;但抽象类不能创建对象。

1、abstract final class Mammal{ }  能编译通过吗, why?

     原因:如果抽象类前面可以添加final就意味着该类无法被继承,也就意味着该抽象类中的抽象方法永远无法得到实现,也就意味着抽象类中的抽象方法是无用的。

2、Mammal抽象类中move抽象方法的访问权限可以为private吗,即“private abstract void move();, why?

     原因:被private修饰的方法其作用范围为本类,如果抽象类中的抽象方法被private修饰就意味着该方法永远无法被实现。

3、Mammal抽象类中move抽象方法可以由static修饰吗,,即“public static abstract void move();” why?

     原因:抽象类中的抽象方法如果可以被static修饰就意味着可以使用抽象类的类名来使用该方法,但是该抽象方法没有方法体,不具有使用的价值,所以Java中规定抽象类中不能包含被static修饰的静态抽象方法。

4、抽象类中的抽象方法是多态的一种表现形式。

---------------------------------------------------------------------

abstract类模板方法设计模式:

        解决的问题:当功能内部一部分实现时确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。

abstract class GetTime{

     public final void getTime(){

         //此功能如果不需要复写,可加final限定

         long start = System.currentTimeMillis();

        code(); //不确定的功能部分,提取出来,通过抽象方法实现

         long end = System.currentTimeMillis();

         System.out.println("毫秒是:"+(end-start));

     }

     public abstract void code();

        //抽象不确定的功能,让子类复写实现

}

class SubDemo extends GetTime{

     public void code(){ //子类复写功能方法

         for(int y=0; y<1000; y++){

              System.out.println("y");

         }

     }

}

abstract类注意事项

        关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

        理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

        理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

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

        理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

        理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

接口:interface

接口定义

        接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。

        接口的定义,使用 interface 关键字,会被编译成.class文件,但它并不是类,而是另外一种引用数据类型。

        引用数据类型:数组,类,接口。

        接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

        面向接口编程,传递依赖关系有三种方式:接口传递、构造方法传递、setter传递。在实际的开发中我们主要通过以下几点来较好的遵守依赖倒转原则:

        底层模块尽量都要有抽象类或接口,或两者都有

        变量的声明类型尽量是抽象类或接口

        使用继承时要遵循里式替换原则

        遵循依赖倒转原则可以降低类之间的耦合性,提高系统稳定性,降低修改造成的风险。

接口格式

public interface 接口名称 {

// 抽象方法

// 默认方法

// 静态方法

// 私有方法

}

接口含有全局常量。

注意:接口中的成员都有固定的修饰符。    

成员变量:public static final
interface Inter{
     public static final int x = 3;
}

接口含有抽象方法

        抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。接口中有抽象方法,说明接口不可以实例化接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。

代码如下:

public interface InterFaceName {
    public abstract void method();
}

接口含有默认方法和静态方法

        默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。

        静态方法:使用 static 修饰,供接口直接调用。

        代码如下:

public interface InterFaceName {
    public default void method() {
        // 执行语句
    }

    public static void method2() {
        // 执行语句
    }
}

接口含有私有方法和私有静态方法

私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。

代码如下:

public interface InterFaceName {
    private void method() {
        // 执行语句
    }
}

接口基本的实现

接口实现的概述

        类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。

非抽象子类实现接口:

        1. 必须重写接口中所有抽象方法。

        2. 继承了接口的默认方法,即可以直接调用,也可以重写。

        实现格式:

class 类名 implements 接口名 {
    // 重写接口中抽象方法【必须】
    // 重写接口中默认方法【可选】
}

接口中抽象方法的使用

        必须全部实现,代码如下:

定义接口:

public interface LiveAble {

        // 定义抽象方法

public abstract void eat();

public abstract void sleep();

}

输出结果:

吃东西

晚上睡

定义实现类:

public class Animal implements LiveAble {

@Override

public void eat() {

System.out.println("吃东西");

}

@Override

public void sleep() {

System.out.println("晚上睡");

}

}

定义测试类:

public class InterfaceDemo {

public static void main(String[] args) {

// 创建子类对象

Animal a = new Animal();

// 调用实现后的方法

a.eat();

a.sleep();

}

}

接口中默认方法的使用

        可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。

  1. 继承默认方法,代码如下:

定义接口

public interface LiveAble {

   public default void fly(){

       System.out.println("天上飞");

   }

}

定义实现类:

public class Animal implements LiveAble {

     // 继承,什么都不用写,直接调用

}

定义测试类:

public class InterfaceDemo {

   public static void main(String[] args) {

     // 创建子类对象

      Animal a = new Animal();

     // 调用默认方法

     a.fly();

   }

}

输出结果:

天上飞

  1. 重写默认方法,代码如下:

定义接口:

public interface LiveAble {

    public default void fly(){

        System.out.println("天上飞");

    }

}

定义实现类:

public class Animal implements LiveAble {

    @Override

    public void fly() {

        System.out.println("自由自在的飞");

    }

}

定义测试类:

public class InterfaceDemo {

    public static void main(String[] args) {

        // 创建子类对象

        Animal a = new Animal();

        // 调用重写方法

        a.fly();

    }

}

输出结果:

自由自在的飞

接口中静态方法的使用

        静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用,代码如下:

定义接口:

public interface LiveAble {

    public static void run(){

        System.out.println("跑起来~~~");

    }

}

定义实现类

public class Animal implements LiveAble {

    // 无法重写静态方法

}

定义测试类:

public class InterfaceDemo {

    public static void main(String[] args) {

        // Animal.run(); // 【错误】无法继承方法,也无法调用

        LiveAble.run(); //

    }

}

输出结果:

跑起来~~~

接口中私有方法的使用

        私有方法:只有默认方法可以调用。

        私有静态方法:默认方法和静态方法可以调用。

        如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。同学们在已学技术的基础上,可以自行测试。

定义接口:

public interface LiveAble {
   default void func(){
      func1();
      func2();
   }

   private void func1(){
      System.out.println("跑起来~~~");
   }

   private void func2(){
      System.out.println("跑起来~~~");
   }
}

接口的多实现

        在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

实现格式
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {
   // 重写接口中抽象方法【必须】
   // 重写接口中默认方法【不重名时可选】
}

抽象方法

        接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。代码如下:

定义多个接口:

interface A {

   public abstract void showA();

   public abstract void show();

}

interface B {

  public abstract void showB();

  public abstract void show();

}

定义实现类:

public class C implements A,B{

  @Override

  public void showA() {

    System.out.println("showA");

  }

  @Override

  public void showB() {

    System.out.println("showB");

  }

  @Override

  public void show() {

    System.out.println("show");

  }

}

默认方法

        接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。代码如下:

定义多个接口:

interface A {

  public default void methodA(){}

  public default void method(){}

}

interface B {

  public default void methodB(){}

  public default void method(){}

}

定义实现类:

public class C implements A,B{

  @Override

  public void method() {

    System.out.println("method");

  }

}

静态方法

        接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。

优先级的问题

`    当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。代码如下:

定义接口:

interface A {

public default void methodA(){

   System.out.println("AA");

}

}

定义父类:

class D {

  public void methodA(){

    System.out.println("DDD");

  }

定义子类:

class C extends D implements A {

// 未重写methodA方法

}

定义测试类:

public class Test {

public static void main(String[] args) {

        C c = new C();

        c.methodA();

}

}

输出结果:

接口的多继承

        一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。代码如下:

定义父接口:

interface A {

public default void method(){

System.out.println("AAAAAAAAAAAAAAAAAAA");

}

}

interface B {

public default void method(){

System.out.println("BBBBBBBBBBBBBBBBBBB");

}

}

定义子接口:

interface D extends A,B{

@Override

public default void method() {

System.out.println("DDDDDDDDDDDDDD");

}

}

小贴士:

子接口重写默认方法时,default关键字可以保留。

子类重写默认方法时,default关键字不可以保留。

接口中其他成员特点

  •         接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
  •         接口中,没有构造方法,不能创建对象。
  •         接口中,没有静态代码块。

接口回调

        抽接口回调描述的是一种现象:接口声明的变量指向其实现类实例化的对象,那么该接口变量就可以调用其实现类中的方法。

接口与类的区别

        类与类之间存在着继承关系,类与接口中间存在的是实现关系。

        继承用extends  ;实现用implements ;

        通过extends关键字可以使当前所自定义的接口实现继承,但需要注意以下两点:

                1、接口只能继承父接口,不能继承抽象类和普通类。

                2、接口弥补了Java单一继承的缺点(Java中的类只能继承一个父类),即接口可以继承多个父接口,它们之间用英文逗号隔开。

        接口和类不一样的地方,就是,接口可以被多实现,这就是多继承改良后的结果。java将多继承机制通过多现实来体现。

        一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性。还可以将类进行功能的扩展。

        其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口

接口都用于设计上,设计上的特点:(可以理解主板上提供的接口)

        1:接口是对外提供的规则。

        2:接口是功能的扩展。

        3:接口的出现降低了耦合性。

接口与抽象类:

        抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取,特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。

        接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。

抽象类和接口的共性:

        都是不断向上抽取的结果。

抽象类和接口的区别:

        1:抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。

        2:抽象类中可以定义非抽象方法,子类可以直接继承使用。接口中都有抽象方法,需要子类去实现。

        3:抽象类使用的是is a 关系。接口使用的 like a 关系。

        4:抽象类的成员修饰符可以自定义。接口中的成员修饰符是固定的。全都是public的。

        在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。

内部类:

        内部类是指在一个外部类内定义的类,他对于简化事件处理非常有用。按照是否有类名分为有名内部类和匿名内部类。

        外部类可以调用内部类的静态成员,非静态成员,但内部类只能访问外部类的非静态成员

内部类静态内部类跟非静态内部类:

        非静态内部类需要外部类实例化之后才能实例化。

内部类的特点:

             1、普通外部类、接口和抽象类只能使用public和default修饰,内部类四种访问权限修饰符都可以;

             2、static不能修饰普通外部类、接口和抽象类,但可以修饰内部类;

             3、内部类是一个独立的类:编译之后内部类会被编译成独立的.class文件,文件名为外部类的类名+$+数字;

             4、内部类有static修饰,则内部类只能“直接”访问外部类的静态成员变量;

             5、普通外部类、接口和抽象类都可以成为匿名内部类;

             6、内部类可以访问外部类的所有访问权限的成员变量;

             7,内部类不允许声明static成员。

接口内部类:

  1. 接口的有名内部类:
  1. 接口的匿名内部类:

        如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象。这时,为了方便设计和访问,直接将A类定义在B类中。就可以了。A类就称为内部类。内部类可以直接访问外部类中的成员。而外部类想要访问内部类,必须要建立内部类的对象。

class Outer{
     int num = 4; 
     class  Inner {
         void show(){
              System.out.println("inner show run "+num);             
         }
     }

     public void method(){
         Inner in = new Inner();//创建内部类的对象。
         in.show();//调用内部类的方法。
     }
}

        当内部类定义在外部类中的成员位置上,可以使用一些成员修饰符修饰 private、static。

1:默认修饰符。

        直接访问内部类格式:外部类名.内部类名 变量名 =  外部类对象.内部类对象;

        Outer.Inner in = new Outer.new Inner();//这种形式很少用。

          但是这种应用不多见,因为内部类之所以定义在内部就是为了封装。想要获取内部类对象通常都通过外部类的方法来获取。这样可以对内部类对象进行控制。

2:私有修饰符。

          通常内部类被封装,都会被私有化,因为封装性不让其他程序直接访问。

3:静态修饰符。

          如果内部类被静态修饰,相当于外部类,会出现访问局限性,只能访问外部类中的静态成员。

        注意;如果内部类中定义了静态成员,那么该内部类必须是静态的。

内部类编译后的文件名为:

“外部类名$内部类名.java”;

内部类为什么可以直接访问外部类中的成员呢?

        那是因为内部中都持有一个外部类的引用。这个是引用是 外部类名.this

        内部类可以定义在外部类中的成员位置上,也可以定义在外部类中的局部位置上。

        当内部类被定义在局部位置上,只能访问局部中被final修饰的局部变量。

匿名内部类:

没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形式。匿名内部类其实就是一个匿名子类对象想要定义匿名内部类:需要前提,内部类必须继承一个类或者实现接口。

匿名内部类的格式:

        new 父类名&接口名(){ 定义子类成员或者覆盖父类方法 }.方法。

匿名内部类的使用场景:

        当方法的参数是接口类型引用时,如果接口中的方法不超过3个。可以通过匿名内部类来完成参数的传递。

        其实就是在创建匿名内部类时,该类中的封装的方法不要过多,最好两个或者两个以内。

new Object(){

void show(){

       System.out.println("show run");       

     }

}.show();

Object obj = new Object(){

     void show(){

         System.out.println("show run");

     }

};

obj.show();

        写法是正确,1和2都是在通过匿名内部类建立一个Object类的子类对象。

        区别:第一个可是编译通过,并运行。第二个编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,就被提升为了Object类型,而编译时检查Object类中是否有show方法,所以编译失败。

class InnerClassDemo6 {

     +(static)class Inner{

         void show(){}

     }

     public void method(){

        this.new Inner().show();//可以

     }

     public static void main(String[] args) {

//static不允许this错误,Inner类需要定义成static

         This.new Inner().show();

     }

}

interface Inter{

     void show();

}

class Outer{

//通过匿名内部类补足Outer类中的代码。

     public static Inter method(){

         return new Inter(){

              public void show(){}

         };

     }

}

class InnerClassDemo7 {

     public static void main(String[] args) {

        Outer.method().show();

     //Outer.method():意思是:Outer中有一个名称为method的方法,而且这个方法是静态的。

     //Outer.method().show():当Outer类调用静态的method方法运算结束后的结果又调用了show方法,意味着:method()方法运算完一个是对象,而且这个对象是Inter类型的。

         function (new Inter(){

              public void show(){}

         }); //匿名内部类作为方法的参数进行传递。

     }

     public static void function(Inter in){

         in.show();

     }

}

普通类:

普通父类的有名内部类

普通类的匿名内部类:

总结:有名内部类可以添加新的属性和方法,匿名内部类无法添加新的属性和方法,因为匿名内部类只存在上传型对象;

抽象类:

抽象类的有名内部类

抽象类的匿名内部类

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值