面对对象第七天

---------------------- android培训java培训、期待与您交流! ----------------------

  (一)继承-概述

举例:

class Student
{
    String name;
    int age;
    public void study()
    {
        System.out.println("study");
    }
}
class Work
{
    String name;
    int age;
    public void work()
    {
        System.out.println("work");
    }
}

发现学生和工人有共性的描述姓名和年龄,需要把共性提取出来单独进行描述.只要让学生和工人与单独描述的这个类有关系就可以了.所以接下来应该这样写:

class Person
{
    String name;
    int age;
}
class Student extends Person
{
    public void study()
    {
        System.out.println("study");
    }
}
class Worker extends Person
{
    public void work()
    {
        System.out.println("work");
    }
}

通过extends这个关键字把Person和Student,Worker类建立联系,Person就是父类,也叫超类或者基类,Student和Worker就是子类.

继承的好处:

1.提高了代码的复用性,

2.让类与类之间产生了关系,有了这个关系,才有了多态的特性.

注意:

1.千万不要为了省事而让工人继承学生的属性,因为工人和学生之间没有所属关系(is a),应该是继承之间有所属关系,比如上面例子中的多个类继承这多个类的共有属性.

2.父类中内容要都是子类中有的,不要继承的父类中的内容子类中没有.


  (二)继承-概述2

java支持哪些:

1.java中只支持单继承,不支持多继承,也就是一个孩子只能有一个父亲,不能有多个父亲,另外多继承容易带来安全隐患:当多个父类中定义了相同的方法,也就是返回值类型,方法名,参数列表都相同,但是方法体不同,子类对象不确定要运行哪一个.

class SubDemo extends Demo{}这样是可以的
class SubDemo extends Demo1,Demo2...但这样就错了

举例:假如java支持多继承,那么:

class A
{
    public void show()
    {
        System.out.println("a");
    }
}
class B
{
    public void show()
    {
        System.out.println("b");
    }
}
class C extends A,B
{}
class D
{
    C c=new C();
    c.show();
}

调用show方法的时候,到底会打印a还是b,所以不支持多继承.但是java改良了多继承的方式,叫多实现.

2.java支持多层继承,也就是一个继承体系,类似父亲继承爷爷,儿子继承父亲.

class A{}
class B extends A{}
class C extends B{}

但是如何使用一个继承体系中的功能,办法是先查阅体系中最父类的内容,也就是最共性的方法,知道了最共性的方法也就知道这个体系基本可以怎么用了.但是在具体调用时,要创建最子类的对象.原因是:1.可能父类不能创建对象,2.创建子类对象可以使用更多功能,包括共有和自身特有的.简单一句话就是查看功能看父类,使用功能用子类对象.


  (三)聚集关系

事物之间的关系不仅有继承,还有组合,聚合关系.

聚集关系,包括聚合,组合,就是谁里面有什么,叫has a.但是聚合是多个东西合在一起,相互间没关系,组合关系的相互间有关系.


  (四)子父类中变量的特点

到目前为止,类中包括了三种成员:

1.变量

2.函数

3.构造函数


注意:子类可以直接访问父类中的非私有成员.

下面看看是否能通过创建子类对象,既能访问父类的成员又能访问子类的成员.

class Fu
{
    int num1=4;
}
class Zi extends Fu
{
    int num2=5;
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z=new Zi();
        System.out.println(z.num1+","+z.num2);//都是Zi类的对象在访问,既访问了自己的num2,也访问了Fu里面的非私有成员num1.
    }
}


但是,如果父类和子类具有相同成员变量,那么在创建子类对象之后,调用那个成员变量,会调用谁的,比如:

class Fu
{
    int num=4;
}
class Zi extends Fu
{
    int num=5;
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z=new Zi();
        System.out.println(z.num+","+z.num);
    }
}

这时候的打印结果都是子类的num.同样子类里面的方法调用的变量如果也和父类里面变量同名,那么结果也是子类的,比如:

class Fu
{
    int num=4;
}
class Zi extends Fu
{
    int num=5;
    public void show()
    {
        System.out.println(num);//当调用show方法的时候打印的结果还是子类的num.因为此时省略了this.num
    }
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z=new Zi();
        z.show();
    }
}

所以假如要访问父类的成员,要用super关键字来区分.比如在上面的show方法的num前面加上super加以标识.super代表父类对象的引用,这个父类是子类的直接父类,也就是离子类最近的那个父类.

public void show()
    {
        System.out.println(super.num);
    }


有了继承关系以后,子父类的成员变量在内存中的加载方式:当new Zi()的时候,会在对内存中为new Zi()分配属性空间,由于继承关系,外加此时只有一个子类对象,所以会分配两个属性空间,一个是父类的成员变量,一个是子类的成员变量,但都是子类对象的属性。接着要加载类,但是会先加载Fu类,再加载Zi类,加载完以后就会在方法区里面开辟空间,方法区里面有静态区和非静态区,非静态区中就有两个区域,其实是加载几个类就有几个区域,这里加载了父类和子类,就有两片区域,一个存放父类的方法,一个存放子类的方法。


假如改成这样的代码:

class Fu
{
    int num=4;
}
class Zi extends Fu
{
    public void show()
    {
        System.out.println(num);
    }
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z=new Zi();
        z.show();
    }
}

此时System.out.println(num);其实是省略了super.num,但是对象只有子类一个,父类的引用指向了子类对象,这就是之后要讲的多态。



  (五)子父类中函数的特点-覆盖

概念:子类中出现与父类一模一样的方法时,即返回值类型,方法的名称,方法参数类型,参数个数都相同.当子类对象调用该函数,会运行子类函数的内容,如同父类的函数被覆盖一样,也称为重写或者复写。这是函数另一个特性:重写(覆盖),之前学的那个特性是重载.

应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。修改原来的代码,往往是不修改原来的代码.而用重写特性重新定义类来继承,如果要保留父类的函数,在子类中调用的时候在前面加上super关键字即可,所以在设计时往往把原来的代码设计好点,方便扩展.

注意:

1.覆盖时,子类方法权限一定要大于等于父类方法权限,三种权限,从大到小依次是,public,默认权限,private,默认权限就是什么都不写.比如void show(),void前面什么都没写,

2.静态只能覆盖静态.

3.重载:只看同名函数的参数列表.重写:子父类方法要一模一样,包括返回值类型也要相同.

4.当子父类中有相同函数名和参数列表的函数时,而返回值类型不同,此时在编译的时候会提示不能覆盖,所以只要子父类的函数参数列表不同,那么就是两个不同的函数,编译是可以通过的。



  (六)子父类中构造函数的特点-子类实例化过程

在对子类对象进行初始化时,父类的构造函数也会运行,因为子类的构造函数默认第一行有一条隐式的语句super(),这个语句会访问父类中的空参数的构造函数,而且子类中所有构造函数都有这个默认的语句.例如:

class Fu
{
    Fu()
    {
        System.out.println("A");
    }
}
class Zi extends Fu
{
    Zi()
    {
        //实际上在这一行有一个隐式语句super();
        System.out.println("B");
    }
    Zi(int x)
    {
        //这一行也是一样.有super();
        System.out.println("C");
    }
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z1=new Zi();
        Zi z2=new Zi(2);
    }
}

结果当然是

A
B
A
C

但是,当子类函数有明确的访问到父类中具体构造函数的时候,这个默认的super()就不会运行了,比如:

class Fu
{
    Fu(int x)
    {
        System.out.println("A");
    }
    Fu()
    {
        System.out.println("D");
    }
}
class Zi extends Fu
{
    Zi()
    {
        //由于有明确的访问到Fu(int x),所以这一行就没有了默认的super();
        super(4);//注意写法,是super,而不是Fu,super就代表Fu.
        System.out.println("B");
    }
    Zi(int x)
    {
        //由于没有明确访问到父类中的具体构造函数,所以这一行有super();
        System.out.println("C");
    }
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z1=new Zi();
        Zi z2=new Zi(2);
    }
}

结果是

A
B
D
C

小结:

1.super语句一定要定义在子类构造函数的第一行.因为子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的,所以要先初始化父类,比如上面例子中的super(4);

2.子类的所有构造函数,默认都会访问父类中的空参数构造函数,因为子类每一个构造函数内的第一行都有一句隐式super();

3.当父类中没有空参数的构造函数时,子类必须手动通过super()语句指定要访问父类中的构造函数.父类中有带参数和空参数的构造函数,子类可以调用带参数的也可以调用空参数的,在调用空参数的时候子类的那个super();可以不写.

4.子类构造函数第一行也可以手动指定this()来访问本类中的构造函数,但是如果子类的某个构造函数在第一行指定了this(),那么子类的这个构造函数就没有默认的super()了,因为子类构造函数第一行始终有一个空参数的构造函数,如果写this,就没有super,没写this,那就是super().比如:

class Fu
{
    Fu(int x)
    {
        System.out.println("A");
    }
    Fu(String x)
    {
        System.out.println("E");
    }
    Fu()
    {
        System.out.println("D");
    }
}
class Zi extends Fu
{
    Zi()
    {
        super(4);
        super("A");
        System.out.println("B");
    }
}
class ExtendsDemo
{
    public static void main(String[] args)
    {
        Zi z1=new Zi();
    }
}

会报错,提示对super的调用必须是构造器中的第一个语句,也就是说子类每个构造函数里面最多一个super(),而且还是在第一行.同样this()也最多一个,写在第一行,原因就是开始讲的子类会继承父类数据就要先明确这些数据怎么初始化.比如:

class Zi extends Fu
{
    Zi()
    {
        this(2);
        this("x");
        System.out.println("B");
    }
    Zi(int x)
    {}
    Zi(String x)
    {}
}

还是会报错,道理和super一样.

5.父类中的构造函数第一行也有super();因为java中有一个类叫Object,这是所有类的父类.

关于方法调用的小结

首先考虑权限:被调用的方法不能是私有的,另外如果是子类调用父类的方法,子类方法的权限必须大于等于父类。

再考虑用法:在同一个类中,属于同一个类里面的方法可以互相调用。

一个是一般方法的调用,就是在一个方法体里面写上被调用方法的方法名(),括号里面的参数列表要符合被调用的那个方法。

一个是构造方法的调用,就是一个构造方法的方法体里面写上this(),括号里面的参数列表要符合被调用的构造方法。

在不同类中,一个类里面的方法要调用另一个类的方法可以继承或者实现,或者是建立对象,下面只说继承的情况。

子类调用父类的一般方法,在子类的方法体里面写super.被调用的方法名(),和同一类调用不同就是函数名前面有super .

子类调用父类的构造方法,在子类的构造方法体里面写super (),括号里面的参数列表要符合被调用的构造方法。

括号里面的参数列表,写的时候要么直接写要传入的数据,但类型和数量要符合被调用方法的参数列表,比如this(4)或者show(4),要么写参数,但这个参数要和调用者参数列表里面的参数相同,比如

Student(String n)
    {
        this(n);//或者show (n);
    }

另外需要注意:在使用System.out.println()输出的时候,如果要输出方法,那么不能输出返回值类型为void的方法,必须要有明确的返回值才行。


  (七)final关键字

final的特点:

1.既可以修饰类,函数,变量.

2.被final修饰的类不可以被继承.

例如:

final class A{}

class B extends A{}

会报错,提示无法从最终A进行继承.

3.被fian修饰的方法不可以被重写.final加在类前面,就是类不能被继承,加在方法前面,就是方法不能被重写.

例如:假如有一个类,里面有些方法是调用底层的,不要被重写,有些方法是可以被重写的:

class A
{
    final void show1(){}
    void show2(){}
}

那么show2()能被重写,而show1()不能.

4.被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量,又可以修饰局部变量.当在描述事物时,一些数据的出现是固定的,为了增强阅读性,都给这些值起个名字方便阅读,如果值不需要改变,此时就加上final.加上final的变量就变成了常量,此时的常量名应该所有字母大写,如果有多个单词,单词间用下划线链接,例如:

class Test
{
    public static void main(String[] args)
    {
        final int PAI=3.14;
        PAI=1;
    }    

}

会报错,x只能赋值一次,x=1是再次赋值,和赋值多少没关系,哪怕是赋原值. 给3.14取名叫PAI是为了方便阅读性,这样PAI就是常量,PAI就是3.14.一般这样的常量还加上public static来修饰,全局的共用常量.

5.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量.

小结:到目前为止,能在类前面修饰的有什么都不写,public,final.

 

  (八)抽象类

抽象类概念:

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。


当多个类中出现了相同功能,但功能主体不同,这时可以进行向上抽取,只抽取功能定义,不抽取功能主体.那么这种只有功能声明,没有功能主体的方法称为抽象方法,比如:

abstract class Student//抽象方法必须存在于抽象类中.意思是告诉别人,别创建对象,因为创建抽象类的对象没意义,没有方法体.
{
    abstract void study();//没有带中括号,只抽取的方法定义study(),但是没有方法体,不知道这个方法到底是干什么的,所以要在前面加上abstract关键字标识这是抽象的方法.
}
class BaseStudent extends Student
{
    void study()
    {
        System.out.println("base study");
    }
}
class AdvStudent extends Student
{
    void study()
    {
        System.out.println("adv study");
    }
}

class Demo
{
    public static void main(String[] args)
    {
        new AdvStudent().study();
    }
}

抽象类的特点:

1.抽象方法只有方法声明,没有方法体,抽象方法一定定义在抽象类中.

2.抽象方法和抽象类必须被abstract修饰.

3.抽象类不可以实例化,因为抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例,另外调用抽象方法也没意义.

4.抽象类通过其子类实例化,也就是抽象类中的抽象方法要被使用,必须要由子类重写其所有的抽象方法后,建立子类对象调用.如果子类只重写了部分抽象方法,那么这个子类还是一个抽象类.

5.抽象类中可以有抽象方法,也可以有非抽象方法.

6.抽象类中可以不定义抽象方法,作用仅仅是不让该类建立对象.

7.抽象类里面是可以有构造函数的,抽象的这个类是父类,里面的东西是由子类实例化调用的.

8.抽象关键字abstract不可以和哪些关键字共存?  不能和static final private共存.

原因:

1.被final修饰的类不能有子类,而被abstract修饰的类一定是一个父类。抽象类中的抽象方法要被使用,必须是由子类复写所有的抽象方法后,建立子类对象调用。

2.假如抽象类中的抽象方法被私有了,就不为子类所知,子类就无法复写,而抽象方法出现的就是需要被复写。
3.被static修饰的方法,可以直接类名调用,可是抽象方法运行没意义.

  (九)抽象类2

  (十)抽象类练习

/*
假如我们在开发一个系统时需要对员工进行建模,员工包含3个属性:姓名,工号,工资.经理也是员工,除了含有员工的属性外,还有一个奖金属性.请使用继承的思想设计出员工类和经理类.要求类中提供必要的方法进行属性访问.
*/
abstract class Employee
{
    private String name;
    private String id;
    private double pay;
    Employee(String name,String id,double pay)
    {
        this.name=name;
        this.id=id;
        this.pay=pay;
    }
    public abstract void work()//注意abstract写在public后面
}
class Manager extends Employee
{
    private int bonus;
    Manager(String name,String id,double pay,int bouns)
    {
        super(name,id,pay);
        this.bonus=bonus;
    }
    public void work()
    {
        System.out.println("manager work");
    }
}
class Pro extends Employee//假如还有一个专业员工类,这个类包含员工的三种属性.只是工种不同.
{
    Pro(String name,String id,double pay)
    {
        super(name,id,pay);
    }
    public void work()
    {
        System.out.println("professional work");
    }
}


  (十一)模板方法模式

/*
需求:获取一段程序运行的时间.
原理:获取程序开始和结束的时间,并相减.
思路:如何获取时间.在java api文档里面的System类中有一个currentTimeMillis方法,此方法返回以毫秒为单位的当前时间.
具体返回当前时间与协调世界时 1970年1月1日午夜之间的时间差(以毫秒为单位),可见这个数值非常大.
所以可以用System.currentTimeMillis();
*/
class GetTime
{
    public void getTime()
    {
        long start=System.currentTimeMillis();
        //在start和end之间写一段代码,看看运行需要多长时间.
        for(int x=0;x<1000;x++)
        {
            System.out.print(x);
        }
        long end=System.currentTimeMillis();
        System.out.println("毫秒:"+(end-start));
    }
}
class TemplateDemo
{
    public static void main(String[] args)
    {
        GetTime gt=new GetTime();
        gt.getTime();
    }
}

考虑到上面的那段位于start和end之间的代码是我们需要需要测试时间的代码,不断在变化,所以把这段代码封装:

class GetTime
{
    public void getTime()
    {
        long start=System.currentTimeMillis();
        runCode();
        long end=System.currentTimeMillis();
        System.out.println("毫秒:"+(end-start));
    }
    public void runCode()
    {
        for(int x=0;x<1000;x++)
        {
            System.out.print(x);
        }
    }
}

当我们把要测试的这段代码封装以后,如果以后要更改,就可以在子类中重写这段封装好的代码了.而getTime方法继承了,不用重写,重写的只是我们需要测试的那段代码.注意建立对象是建立子类对象.

class GetTime
{
    public void getTime()
    {
        long start=System.currentTimeMillis();
        runCode();
        long end=System.currentTimeMillis();
        System.out.println("毫秒:"+(end-start));
    }
    public void runCode()
    {
        for(int x=0;x<1000;x++)
        {
            System.out.print(x);
        }
    }
}
class SubTime extends GetTime//继承了父类的getTime方法,重写了父类的runCode方法.
{
    public void runCode()
    {
        for(int x=0;x<4000;x++)
        {
            System.out.print(x);
        }
    }
}
class TemplateDemo
{
    public static void main(String[] args)
    {
        SubTime gt=new SubTime();//建立子类对象
        gt.getTime();//调用父类的getTime方法.
    }
}

考虑到GetTime类中的runCode方法里面到底要运行什么程序我并不知道,这段要测试的代码是在SubTime类中定义的,所以GetTime类中的runCode方法应该是抽象的,那么GetTime类也是抽象的,另外为了防止GetTime类中的getTime方法被重写,需要加上final.

abstract class GetTime
{
    public final void getTime()
    {
        long start=System.currentTimeMillis();
        runCode();
        long end=System.currentTimeMillis();
        System.out.println("毫秒:"+(end-start));
    }
    public abstract void runCode();
}
class SubTime extends GetTime
{
    public void runCode()
    {
        for(int x=0;x<4000;x++)
        {
            System.out.print(x);
        }
    }
}
class TemplateDemo
{
    public static void main(String[] args)
    {
        SubTime gt=new SubTime();
        gt.getTime();
    }
}

上面就是最终优化完的测试程序运行时间的代码,当代码优化后,就可以解决这类问题.这种方法就是模板方法设计模式.

模板方法设计模式概念:

在定义功能时,功能的一部分是确定的,比如上面的getTime方法,一部分是不确定的,比如上面的runCode,而确定的部分在使用不确定部分.这时就将不确定部分暴露出去,比如上面封装了runCode方法,由其子类去重写完成,被封装的方法通常是抽象的,其类也是抽象的,但有时被封装的这部分有默认的初始化值,那就不是抽象的,确定的那部分通常要加final修饰,防止被重写,不加也行.

 

  (十二)接口

概念:初期理解可以认为是一个特殊的抽象类,当抽象类里面的方法全是抽象的时候,那么该类可以通过接口的方式来表示.

class用于定义类

interface用于定义接口.

格式特点:

1.接口定义中常见定义:常量,抽象方法.

2.接口中的成员都有固定修饰符.

常量:public static final,这样这个常量就成全局常量了.

方法;public abstract

3.接口中的成员都是public.

4.只要写interface,系统会自动加上那些关键字,所以那些关键字可以不写,但是阅读性差了,最好还是全写.

5.类与类之间是继承关系,用extends,类与接口之间是实现关系,用implements.

小结:

1.接口不可以实例化,因为有抽象方法.

2.要使用接口里面的方法,需要用子类实现接口,子类对接口中的抽象方法全重写后,子类才能实例化,否则子类还是一个抽象类.

举例:

interface InterFace1
{
    public static final int NUM=3;
    public abstract void show();
}
class InterFace2 implements InterFace1
{
    public void show(){} //由于接口里面的成员全是public,已经是最高权限,所以子类的方法也只能是public
}
class InterFaceDemo
{
    public static void main(String[] args)
    {
        InterFace2 i=new InterFace2();
        System.out.println(i.NUM);
        System.out.println(InterFace2.NUM);//子类实现了接口中的那个静态常量,所以可以直接用类名调用,
        System.out.println(InterFace1.NUM);//同理,类名调用静态.

        NUM=4;//这个是不行的,因为是常量,不能再次赋值.
    }
}


  (十三)接口2

接口的特性:

1.接口可以被类多实现.由于不能多继承,但可以多实现.,也就是一个子类可以实现多个接口.多个接口之间用逗号连接,例如:

interface InterFace1
{
    public static final int NUM=3;
    public abstract void show();
}
interface InterFace2
{
    public abstract void show();
}
class InterFace implements InterFace1,InterFace2
{
    public void show(){}
}

多继承不支持的原因是如果多个父类有相同的方法,那么继承之后不知道执行哪一个,而多实现支持,因为父类中的方法没有方法体,即是相同也无妨,因为具体的方法体都在子类中定义.

2.一个类可以同时继承一个类,并实现多个接口,例如:

interface InterFace1
{
    public static final int NUM=3;
    public abstract void show();
}
interface InterFace2
{
    public abstract void show();
}
class Demo
{
    public void function(){}
}
class InterFace extends Demo implements InterFace1,InterFace2
{
    public void show(){}
}

这样就实现了功能的扩展,一个类既继承了另一个类的功能,又实现了其他多个接口的功能,所以说接口是程序功能的扩展.

3.接口与接口之间是继承的关系,举例:

interface A
{
    public abstract void methodA();
}
interface B extends A
{
    public abstract void methodB();
}
interface C extends B
{
    public abstract void methodC();
}
class D implements C
{
    public void methodA(){}
    public void methodB(){}
    public void methodC(){}
}

由于C继承了B,B继承了A,所以C里面包含了三个方法,D实现C以后,当然要把这三个方法都重写掉才行.当然,由于接口都没有方法体,所以支持多继承,比如上面的C可以直接继承B和A,写成:

interface A
{
    public abstract void methodA();
}
interface B
{
    public abstract void methodB();
}
interface C extends B,A
{
    public abstract void methodC();
}

注意:

java中多继承可以实现,但是是接口与接口之间,类与类之间不能,多继承和多实现的时候,注意多个接口中的方法的返回值类型要一致,因为:比如A接口定义一个返回类型int的方法,B接口定义一个返回类型boolean的方法,但是两个方法名都相同,然后C类多实现了A和B,那么在实例化C的时候,jvm就不知道返回值类型到底是什么.


  (十四)接口的特点

1.接口是对外暴露的规则。
 
2.接口是程序的功能扩展。
 
3.接口可以用来多实现。
 
4.类与接口之间是实现关系,而且类可以
 
5.继承一个类的同时实现多个接口。
 
6.接口与接口之间可以有继承关系。

7.接口的出现降低了耦合性,也就是降低了开发者之间的紧密关系,不会出现A没完成开发,B就不能开发.


  (十五)接口举例体现

abstract class Student//先描述下每个学生的共性,都要学习睡觉.但是学习内容不同,所以学习是抽象的
{
    abstract void study();
    void sleep()
    {
        System.out.println("Sleep");
    }
}
interface Smoke//由于不是每个同学都抽烟,所以提取出来作为一个接口方便需要抽烟的人调用.抽烟的牌子不同,抽烟也是抽象的.
{
    public abstract void smoke();
}
class A extends Student implements Smoke//A同学除了要学习睡觉还要抽烟,所以就实现抽烟接口,扩展了功能.
{
    void study(){}
    public void smoke(){}
}
class B extends Student//B同学不需要抽烟,就不需要去实现抽烟接口.
{
    void study(){}
}

如果还有人喝酒,再定义一个喝酒接口,在又抽烟又喝酒的同学后面多实现即可.这就是功能的扩展.所以基本功能,也就是大家都有的功能定义在类中,特有功能定义在接口中.

---------------------- android培训java培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net/heima

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值