JAVA基础 面向对象(五)抽象类 接口 内部类

11 篇文章 0 订阅
5 篇文章 0 订阅

一、抽象类(abstract)

        随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一·般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

说明:子类的对象都已经很具体了,什么功能都能实现了,完全不用再创建父类的对象了,这样的父类,就成了抽象类。

1.1 abstract关键字

      abstract关键字只能用来修饰类和方法。(不能修饰属性,构造器、final等)

(1)abstract 修饰类    即抽象类

     abstract修饰类以后,这个类就成了抽象类,即不能实例化对象的类了。所有的事情交给他的子类去做,所以抽象类一定有子类,实例化子类的对象去完成相关开发操作

语法:  

    【权限修饰符】 abstract class 类名 {}

说明:虽然抽象类没法实例化了,但是还是需要提供构造器(区分接口,接口没有构造器,就是因为没子类,又没法实例化),因为抽象类的子类会通过super继承这个抽象类的属性和方法。

注意:不能实例化 不代表 不能声明

(2)abstract 修饰方法  抽象方法

       abstract修饰方法以后,这个方法就成了抽象方法,即只有方法声明,没有方法体。抽象方法的方法体是留给后代去实现的,因此,后代必须重新该抽象方法,除非后代继续是抽象类。直到后代重写了父辈们的所有抽象类方法后,后代才可以实例化对象。换句话说,如果子类没有写完父辈的所有抽象方法,那么该子类也是抽象类(必须用abstract修饰)。

语法:【权限修饰符】 abstract 返回值类型 方法名(形参列表);  //注意 没有{ },即没有方法体

说明:既然没有方法体,意味着对象或类没法调用该抽象方法,自然意味着抽象方法一定要在抽象类里面,即有抽象方法的类一定是抽象类。但是抽象类中可以没有抽象方法,抽象类只是不让这个类造对象而已。

          既然必须在子类重写抽象方法,显然,抽象类不允许是私有private,要不其他类根本就看不到。

         同理,抽象方法不允许是static的,因为static方法根本不能重写,类方法各是各的,不会覆盖。(因为static是类方法,每个类都可以有多个子类,子类继承父类方法的时候,先从静态开始,没法覆盖父类方法的,所以不能重写)。

语法:public abstract 返回值类型 方法名();  //注意,抽象方法没有方法体,所以没有大括号{}

(3)抽象类使用场景

    比如求几何图形面积。父类就是几何图形,定义长宽高,体积,面积。但是由于不知道几何图形是啥,所以没法写他的面积公式。就可以把父类几何图形写成抽象类,让子类(具体的矩形,圆,三角形),来重写面积,周长公式。

   或者比如乘坐某交通工具去美国,交通工具的燃油消耗率,行驶时间,都是根据具体交通工具而异,因此抽象交通工具,燃油消耗率,行驶时间等方法,让子类(具体的飞机,轮船),来重写这些方法。

抽象类具有多态性:即可以      抽象类  类名 = new 子类(); 然后  类名.子类方法();

  扩展:匿名子类 (综合匿名对象)

所谓匿名,就是只能调用一次,就是为了省事。

匿名子类,就是一个没有名字的子类。抽象类 + 重写抽象类中的方法,就成了一个匿名子类。

语法:  抽象父类  类名 = new 抽象父类空参构造器(){   //子类什么名字不知道,这里的类名是父类类型,多态性体现

抽象父类中的抽象方法(形参列表){方法体;}  //匿名子类,需要重写完抽象方法后,才可实例化出类名

}

说明:这里的父类类名用了多态,其实实例化的是子类对象,不过子类没有名字。匿名子类可以综合匿名对象一起使用,形成一个大匿名,更省事。

exp:某方法(new 抽象类(){  //main中的某方法,直接调用一个匿名子类的匿名对象

抽象方法1()  //匿名子类的匿名对象需要完成抽象类的抽象方法

抽象方法2(){

})

二、接口(Interface)

       一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是, Java不支持多重继承。有了接口,就可以得到多重继承的效果。另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a(篮球运动员is a 运动员)的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接,但他们并不是is-a关系,而是has-a 关系,比如跨栏运动员和大学生都具备学习的技能,但显然学习技能不是他们的父类。飞机、风筝、热气球都可以飞等等。这种has-a关系,就可以用接口。

 

·    接口就是规范,定义的是一组规则,体现了现实世界中“如果你是. 则必须能.”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

2.1 接口的使用    interface关键字

在java中,接口和类是并列关系,(可以想象成接口是一种特别的类)

接口中,只能定义全局常量和抽象方法(JDK7以前 稳定版),静态方法、默认方法(JDK8以后)。

一个类可以实现多个接口。

接口可以继承接口,并且可以多重继承。

接口也能体现多态性。即  接口 类名 = new 实现类();

全局常量:public static final 声明的常量

抽象方法:public abstract 声明的方法

静态方法(Java8),工具方法: 接口中定义的静态方法,只能通过接口来调用。实现类继承不到(唯一继承不到的),没法调用,很像工具类。

默认方法(Java8):通过实现类的对象,可以调用接口中的默认方法,并且实现类可重写接口中的默认方法。如果实现类的父类和继承的接口中有同名同参数的方法,而且实现类中没有重写这个方法,在main中调用实现类这个同名方法时,默认用父类方法,(类优先原则)。如果实现类 实现了 多个接口,且实现类没有父类,多个接口之间都有同名同参数的默认方法,此时在main中调用实现类的同名方式时,报错(接口冲突),解决办法,在实现类中重写此方法。

如果想在实现类中调同名接口的默认方法的语法:接口名.super.接口方法名; (如果方法不同名,就直接用实现类对象调就是了)

说明:接口和父类是两回事,类是先继承父类,再implements接口。类的对象可以继承父类和接口的所有(除了接口的静态方法)。接口的默认方法,就是类的普通方法。

语法:  interface 接口名 【extends 接口1,接口2...】{  //接口可以继承,并且是多重继承

【public static final】 数据类型 变量名 【=默认值】;//由于接口中只能是全局常量,因此public static final 可以省略

【public abstract】返回值类型 方法名();//抽象方法没有方法体,所以没有大括号

public static 返回值类型 方法名(); //接口的静态方法,工具,只能接口调用。实现类没法继承

【public】 default 返回值类型 方法名();//接口的默认方法,实现类可以重写,也可以调用

}

 说明:接口中不能定义构造器,因为接口是has-a关系,没有子类,同时意味着接口不可以实例化(又没子类,又不能实例化,那自然不用构造器了)。为了实现接口的价值,java让类(非子类)去实现接口的价值(implements),这个类就是实现类。如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化。如果实现类没有覆盖完接口中的所有方法,则此实现类仍为抽象类。

注意:不能实例化,不代表不能声明

语法:class 类名 【extends 父类 】implements 接口1【,接口2...】{ }  //类可以实现多个接口

exp:

class Aircraft{

public void taxi(){

System.out.println("航空器滑行");

}

interface Flyable{

int MAX_SPEED = 7900;  //省略了【public static final】 

public void taxi(){

System.out.println("飞起来了");  

}

void abstract void fly(); //抽象方法fly(),没有大括号,等实现类来重写。省略了【public abstract】

}

class Plane extends Aircraft implements Flyable{  //实现类Plane 继承 Aircraft  实现接口 Flyable

public void taxi(){

System.out.println("飞机滑行");

}

public void fly(){    //重写接口中的fly()方法,此时可以构造对象

System.out.println("蝴蝶飞飞");}

taxi(); //调用自己重写的“飞机滑行”

super.taxi();//调用父类的“航空器滑行”

Flyable.super.taxi();//调用接口的“飞起来了”    如果方法名不重名,就直接用实例化对象调了

}

扩展:接口也可以像抽象类一样玩匿名

三、内部类

     Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B是外部类 (内部类是相对的)

     内部类分为成员内部类(静态、非静态 )和 局部内部类 (方法内、代码块内、构造器内)

3.1成员内部类

1、作为外部类的成员(类似成员变量),内部类方法中可以调用外部类的属性和方法,注意如果内部类是静态的,就没法调用外部类非静态的属性和方法(生命周期不同)。同时,内部类还可以用权限修饰符修饰

2、内部类也是一个类,可以定义类的属性,方法,甚至再嵌套内部类,

3、允许内部类调用外部类的属性和方法,如果不重名,直接调。如果重名,需要加入   外部类.this.属性/方法

4、内部类对象的实例

语法:外部类.内部类名 变量名 = new 外部类.内部类构造器();  //静态内部类实例化,直接用外部类来实例化

          外部类 外部类对象; //先实例化一个外部类对象

          外部类.内部类名 变量名 = 外部类对象.new 内部类构造器(); //非静态内部类实例化,需要通过外部类对象来实例化

             变量名.内部类方法();  //调用内部类方法

exp: class Animal{    //外部类动物

      String name; 

     public void eat(){

         System.out.println("吃饭");}

 

      static class Dog{    //静态内部类 狗

      String name;    //狗名

      public void spark(){  //狗会叫

     eat();    //报错,静态内部类没法调用外部类的非静态方法,生命周期不同,先有静态,实例化后才有对象

      System.out.println("旺旺旺");  }

      }

     class Bird {    //非静态内部类 鸟

      String name;   // 鸟名字

      public Bird();   //鸟构造器

      public void sing(){   //鸟会唱歌

            System.out.println("我是小鸟");

           eat(); } //非静态内部类调用外部类 eat方法,允许。省略了person.this.eat()。直接this.eat(),就是调用Bird的eat了

      }

main(){

//直接用外部类就可以实例化静态内部类Dog,如果是实例化非静态内部类,还要外部类的对象来实例化

Animal.Dog dog = new Animal.Dog(); 

dog.spark();

//实例化非静态内部类Bird,需要先实例化外部类对象才行

Animal animal = new Animal();  //先实例化外部类对象 animal

Animal.Bird bird = animal.new Bird();  // 通过animal对象 来实例化 内部类bird对象

bird.sing(); //调用唱歌方法

}

3.2 局部内部类   就是在方法中写内部类

注意:如果在方法中定义有方法的局部变量,如果内部类的方法要使用这个变量,则必须声明成final

exp: public Comparable getComparable(){     //定义一个方法,用于获取一个实现了comparable接口的对象

     int sum = 10;  //jdk8以上 如果sum被内部类使用,自动变成 final int sum

     class MyComparable implements Comparable{  //在实现类MyComparable 重写接口Comparable的方法

       System.out.println(sum);//  打印外部方法的变量 sum

       public int compareTo(Object o){  

      return 0;}

   }   

   return new MyComparable();  //重写了方法后,MyComparable()可以实例化,返回给外部

}

扩展:节约代码写法(匿名写法)

public Comparable getComparable(){  //匿名,省略了MyComparable()

return new Comparable(){

         public int compareTo(Object o){  

         return 0;}

     }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值