抽象类与接口
抽象类与接口
抽象类的定义与使用
接口的定义与使用
抽象类与接口类的区别
写在前面:
(1)项目开发中,尽量不要直接继承实现好的类,尽量继承抽象类和接口。(因普通类无法强制要求子类覆写其抽象方法,抽象类和接口可以强制要求子类进行覆写操作)
(2)同时可以用抽象类和接口时,应遵循接口优先原则。(原因是抽象类必须要应用于继承关系中,接口应用不局限于继承,应用范围较广,且可避免抽象类单继承缺陷)
抽象类的定义与使用(最大缺陷是应用场景单一(继承),单继承)
定义:抽象类是普通类的超集(即抽象类有的普通类一定有(构成结构))。抽象类只是在普通类的基础上增加了一些抽象方法(抽象类中含抽象方法个数可从0-N个)。所谓抽象方法是指只声明未定义的方法(即没有方法体)。
抽象方法必须满足两个要求:(1)没有方法体。(2)必须用abstract关键字定义,表示抽象类(抽象方法)。
若只满足没有方法体,不一定是抽象方法,还有可能是本地方法,本地方法使用native关键字定义,作用是调用C或其他语言本地方法。
public abstract void fun();//抽象方法
public native void fun();//本地方法
注意:含有抽象方法的类一定是抽象类,抽象类中不一定有抽象方法。
我们通过抽象类的定义可知,抽象类中一定包含抽象方法,但因为抽象方法只定义并未实现,故抽象类不能直接实例化对象,需通过子类(非抽象类)向上转型实例化对象。
//简单使用抽象类
abstract class Person{//抽象类Person
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public abstract void print();//抽象方法
public abstract void fun();
}
//非抽象子类Student继承抽象类Person类,并在子类中具体实现父类中的抽象方法
class Student extends Person{
public Student(String name,int age){
super(name,age);
}
public void print(){//方法重载(父类抽象方法具体实现)
System.out.println("你今天真好看!");
}
public void fun(){
System.out.println(this.getName()+"今年"+this.getAge());
}
}
public class Test{
public static void main(String[] args){
Person per=new Student("张三",18);//抽象类通过子类向上转型实例化
per.print();
per.fun();
}
}
//若抽象子类继承抽象父类,则可以选择性继承父类中的抽象方法
abstract class Person{//父类Person
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public abstract void print();
public abstract void fun();
}
abstract class Student extends Person{//抽象子类Student继承父类
public Student(String name,int age){
super(name,age);
}
public void print(){//只覆写抽象方法print()
System.out.println("你今天真好看!");
}
}
class Worker extends Student{
public Worker(String name,int age){
super(name,age);
}
public void fun(){//非抽象类子类必须强制覆写抽象类未覆写的抽象方法
System.out.println(this.getName()+"今年"+this.getAge());
}
}
public class Test{
public static void main(String[] args){
Person per=new Worker("张三",18);
per.print();
per.fun();
}
}
注意:
(1)抽象类不可直接实例化对象,若出现则编译报错。
(2)若抽象类中定义了抽象类,子类必须全部覆写,缺一不可。
(3)抽象类中抽象方法访问权限不能使用private,因抽象类必须强制要求子类覆写抽象方法,故private与abstract不能共同使用,若同时使用则编译报错。
对于抽象类适用原则的总结:
(1)所有抽象类必须有子类,且abstract与private不可同时使用;abstract与final不可同时使用,因final定义只能继承不能修改。
(2)抽象类的子类必须覆写抽象类的所有抽象方法(子类不是抽象类);若子类为抽象类,则可以选择性覆写。(方法覆写建议使用public权限)
(3)抽象类的对象可以通过对象多态性利用子类为其实例化。
开发中抽象类的另一种存在:
abstract class Person{
private String name="张三";
private int age=18;
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public abstract void print();
public abstract void fun();
public static Person StudentA(){//获取抽象类对象
class Student extends Person{//方法内部类
public void print(){//抽象方法实现
System.out.println(this.getName()+"今年"+this.getAge());
}
public void fun(){
System.out.println("你今天真好看!");
}
}
return new Student();//方法内部类只能在定义此内部类的类中使用,对其类隐藏,故Student类必须在此进行实例化
}
}
public class Test{
public static void main(String[] args){
Person per=Person.StudentA();//调用Person抽象类中StudentA方法,间接调用Student对象,因抽象类不能直接实例化对象。
per.print();
per.fun();
}
}
此类模式属于非正常模式,但对于子类封装具有一定好处。
抽象类的相关规定
1.抽象类是普通方法的超集,故抽象类中也可用有构造方法,也遵循调用子类构造方法时先调用父类构造方法;如果父类中无无参构造,子类构造方法中一定要使用super()语句指出要调用哪个父类构造方法。
//看一段抽象类的特殊代码
abstract class Person{
public Person(){
this.print();
}
public abstract void print();
}
class Student extends Person{
private String name="野原新之助";
public Student(String name){
//super();
//String name="野原新之助";
this.name=name;
}
public void print(){
System.out.println(this.name);
}
}
public class Test{
public static void main(String[] args){
Person per=new Student("野原向日葵");
}
}
程序运行分析图
对以上程序做修改:
abstract class Person{
public Person(){
this.print();
}
public abstract void print();
}
class Student extends Person{
private String name="野原新之助";
public Student(String name){
this.name=name;
}
public void print(){
System.out.println(this.name);
}
}
public class Test{
public static void main(String[] args){
Person per=new Student("野原向日葵");
per.print();//调用print()方法
}
}
2.抽象类中允许不定义任何的抽象方法,但是此时抽象方法依然无法直接创建实例化对象。
3.抽象类中abstract与final不能同时使用,abstract与private不能同时使用。
4.抽象类也分为内部抽象类和外部抽象类,内部类可使用static修饰。
//抽象内外部类的简单实用
abstract class A{//外部抽象类
public abstract void print();
abstract class B{//内部抽象类
public abstract void fun();
}
}
class C extends A{//外部类C继承外部抽象类A
public void print(){//抽象方法实现
System.out.println("你今天真好看!");
}
class D extends B{//内部类D继承内部抽象类
public void fun(){
System.out.println("你明天真好看!");
}
}
}
public class Test{
public static void main(String[] args){
C c=new C();
c.print();
C.D d=new C().new D();
d.fun();
}
}
其实关于外部抽象类与内部抽象类的继承来说,子类继承哪个类实现哪个类的所有抽象方法即可。(也就是说,内部抽象类的抽象方法与外部抽象类的抽象方法无关)
//子类只继承内部抽象类
abstract class A{
public abstract void print();
static abstract class B{//静态内部类可直接实例化对象,与外部类无关,故要直接且只继承内部抽象类时,内部抽象类必须定义为静态内部类
public abstract void fun();
}
}
class C extends A.B{//此时A.B表示B为A的抽象内部类
public void fun(){
System.out.println("你今天真好看!");
}
}
public class Test{
public static void main(String[] args){
C c=new C();
c.fun();
}
}
//子类只继承外部抽象类
abstract class A{
public abstract void print();
abstract class B{
public abstract void fun();
}
}
class C extends A{//子类只继承外部抽象类时可直接继承
public void print(){
System.out.println("你今天真好看!");
}
}
public class Test{
public static void main(String[] args){
C c=new C();
c.print();
}
}
接口的定义与使用(解决抽象类单继承缺陷,可实现多继承,且应用场景广泛)
定义:接口就是抽象方法和全局变量的集合(JDK8之后接口扩展了一些普通方法),在JAVA中接口使用interface关键字定义。
接口命名规则:
(1)定义接口时为了区分建议在接口前添加字目I;
(2)子类若想使用接口,那么就必须使用implements关键字来实现接口(因接口是更纯粹的抽象类),且一个子类可实现多个接口(子类命名一般使用Impl结尾,表示该类实现接口)。
接口实现多继承概念:对于接口的子类(非抽象类)必须覆写(1)接口中全部的抽象方法,(2)子类命名一般使用Impl结尾,表示该类实现接口;再通过子类的向上转型实例化子类来得到接口的实例化对象。
//接口实现多继承的简单实现
interface IA{
void printA();
}
interface IB{
void printB();
}
class AImpl implements IA,IB{
public void printA(){
System.out.println("你今天真好看!");
}
public void printB(){
System.out.println("你明天也好看!");
}
}
public class Test{
public static void main(String[] args){
IA a=new AImpl();
a.printA();
IB b=new AImpl();
b.printB();
}
}
注意:(1)通过子类向上转型实例化对象后只能调用本接口的抽象方法。
(2)父接口之间可相互转化(两个接口有了共同子类后就建立了某种联系,可通过子类相互转换;若无共同子类则不可以相互转换)。
interface IA{
void printA();
}
interface IB{
void printB();
}
class AImpl implements IA,IB{
public void printA(){
System.out.println("你今天真好看!");
}
public void printB(){
System.out.println("你明天也好看!");
}
}
public class Test{
public static void main(String[] args){
IA a=new AImpl();
a.printA();
IB b=(IB) a;//将对象a强转为IB类型
b.printB();
}
}
接口使用限制
1.接口中无论属性还是方法只允许使用public权限(在接口中public static abstract final均可以省略)。
阿里规范:接口中属性与方法不加任何修饰符,public也不加。
//完整格式
interface IA{
public static final String MSG="I am a student!";
public abstract void print();
}
//简化格式
interface IA{
String MSG="I am a student!";
void print();
}
2.当一个接口既需要继承抽象类又需要实现接口时,先使用extends继承抽象类再使用implements实现接口。
//非抽象子类继承抽象类又同时实现接口时简单应用
interface IA{
void printA();
}
abstract class B{
public abstract void printB();
}
class CImpl extends B implements IA{
public void printB(){
System.out.println("你今天真好看!");
}
public void printA(){
System.out.println("你明天也好看!");
}
}
public class Test{
public static void main(String[] args){
B b=new CImpl();
b.printB();
IA a=new CImpl();
a.printA();
}
}
注意:
(1)非抽象子类继承抽象类又同时实现接口时,若先使用implements实现接口再继承抽象类,编译报错。
(2)父接口与父抽象类直接也可相互转换(前提是必须有同一子类)。
interface IA{
void printA();
}
abstract class B{
public abstract void printB();
}
class CImpl extends B implements IA{
public void printB(){
System.out.println("你今天真好看!");
}
public void printA(){
System.out.println("你明天也好看!");
}
}
public class Test{
public static void main(String[] args){
B b=new CImpl();
b.printB();
IA a=(IA) b;//父接口与父抽象类相互转化
a.printA();
}
}
(3)子类若为抽象类继承抽象类同时又实现接口时。
interface IA{
void printA();
}
abstract class B{
public abstract void printB();
}
//抽象子类继承抽象类与实现接口,此时implements代表实现接口IA
abstract class CImpl extends B implements IA{
}
class D extends CImpl implements IA{//非抽象子类继承抽象父类时,implements此时代表该类可实现接口IA,只是声明为了增强代码的可读性,并无实际意义
public void printB(){
System.out.println("你今天真好看!");
}
public void printA(){
System.out.println("你明天也好看!");
}
}
public class Test{
public static void main(String[] args){
B b=new D();
b.printB();
IA a=new D();
a.printA();
}
}
(4)抽象类可以使用implements实现多个接口,反之,接口无法继承抽象类(原因是接口是更纯粹的抽象类,结构构成只有全局常量和抽象方法,若能继承抽象类,则接口中就有其他成员方法,与接口定义矛盾,故接口无法继承抽象类)。
interface IA{
void printA();
}
abstract class B implements IA{//抽象类可实现接口
public abstract void printB();
}
class CImpl extends B{
public void printA(){
System.out.println("你今天真好看!");
}
public void printB(){
System.out.println("你明天也好看!");
}
}
public class Test{
public static void main(String[] args){
IA a=new CImpl();
a.printA();
B b=new CImpl();
b.printB();
}
}
(5)一个接口可以使用extends继承多个父接口。
//一个接口实现多继承简单实用
interface IA{
void printA();
}
interface IB{
void printB();
}
interface CImpl extends IA,IB{//接口的多继承
void printC();
}
class D implements CImpl{
public void printA(){
System.out.println("haha");
}
public void printB(){
System.out.println("xixi");
}
public void printC(){
System.out.println("wawa");
}
}
public class Test{
public static void main(String[] args){
IA a=new D();
a.printA();
IB b=new D();
b.printB();
CImpl c=new D();
c.printC();
}
}