Java基础教程
一句话目标
对于一些比较复杂或者第一眼看上去不太好理解的概念,我信奉的观念就是,用一句话把它解释清楚,而且是用很通俗的语言,当然了,如果你已经能够很好的理解了,还是建议用不是那么正式但又不是很通俗的语言解释。
所以我接下来我会用一句话这个标签来解释这些难懂的概念。
C/C++与Java编译运行过程对比:
数据溢出:
byte是一个字节的数据类型,所以它的表示范围是-128~127
当我们此范围之外的数赋值给byte类型变量时,会发生数据溢出,溢出的方式就是
比127大的数要接着顺时针转,假如是128,比127大1,那就是移动一个数变成-128假如是129,比127大2,那就是移动两个数变成-127,这样以此类推;
比-128小的数要接着逆时针,假如是-129,比-128小1,那就移动一个数变成127,假如是-130,比-128小2,那就是移动两个数变成126,其他数据类型也以此类推。
类与对象
编程语言的几个发展阶段
面向机器语言 ——》面向过程语言——》面向对象语言
(二进制、汇编)——》C语言 ——》Java语言
在面向过程编程中我们是以“方法”为主体的
而面向对象编程汇总我们是以“对象”为主体的
在面向对象语言的学习过程中,一个简单的理念就是,需要完成某种任务的时候,我们首先想到是谁去完成(对象);提到某个数据的时候,想到是谁的数据,这样也更符合我们日常生活中的描述。
类
类?类是干什么的呢?
一句话:类是用来描述和抽象具有相同属性和行为的一类事物的概念,就比如说人类,猫类,狗类,家禽类,这些类中的动物都有相同的属性和行为。
那么如何声明一个类呢?
1 | class People{ //声明了一个人类 |
成员变量的赋值问题
Java规定不能在类中对成员变量进行赋值,但是你可以在类中对成员变量赋初始值,要想对成员变量赋值必须要在方法体内部进行
举个列子:
1 | class People{ |
对象的创建与构造方法
类也可以看做是一种数据类型,也可以用来声明变量,而用类声明的变量被称为对象。
既然类有了,总得有对象来体现这个类的属性和行为吧,那我们就可以创建对象来体现了
创建对象要用关键字new
1 | People zs; //声明对象张三 |
构造方法的名称必须与它坐在的类名称完全一致,而且没有类型,可以有参数,参数一般就是对对象的成员变量进行赋值。
所以这样看起来构造方法在创建对象以及对对象进行初始化起到了至关重要的作用。
构造方法可以有多个,但是其参数一定不同(参数类型、参数个数)。
构造方法不就是把对象的值赋值给实例变量。
1 | class People{ //创建了一个人类 |
对象的内存模型
声明对象和创建对象是有区别的:
1 | People zs; //声明对象张三 |
这个内存模型啊,告诉我们一件事,声明和创建区别很大
类与程序的基本结构
一个应用程序可以有多个源文件,一个源文件可以有多个类,但是一定要有一个主类
参数传值
说一下引用型参数
当参数是数组、对象、接口的时候,称之为引用型参数,传的是引用(地址)而不是实体
举例:
1 | class Battery{ //电池类 |
对象的组合
一句话:将其他类的对象作为自己的成员变量
对象组合的本质就是一个类的数据成员变量存的不是基本的数据类型,而是一个对象的地址
举例:
1 | class Circle{ |
实例成员与类成员
首先类成员中的类变量被所有对象共享,也就是所有的对象的类变量是相同的一处内存空间,并且通过类名访问类变量和通过对象访问类变量都可以改变类变量的值。
加载类的字节码文件的时候,类变量已经分配了内存,而成员变量没有;当该类创建对象时,才会给实例对象分配内存
实例成员就是正常的变量和方法
类成员就是静态的变量和方法(前面加了static修饰的)
类变量也叫做static变量、静态变量、全局变量,我叫它共享变量,分配给这些对象的类变量占有相同的一处内存,改变其中一个对象的这个类变量,其他对象的这个类变量也会跟着变;static方法可以重写,重写就是子类可以重写父类已有的方法。
static关键字
在类中,用static声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
这里要强调一下:
- static修饰的成员变量和方法,从属于类
- 普通变量和方法从属于对象
- 静态方法不能调用非静态成员,编译会报错
static关键字的用途
一句话描述就是:方便在没有创建对象的情况下进行调用(方法/变量)。
显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
static可以用来修饰类的成员方法、类的成员变量,另外也可以编写static代码块来优化程序性能
static方法
static方法也成为静态方法,由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有this的,因为不依附于任何对象,既然都没有对象,就谈不上this了,并且由于此特性,在静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用。
1 | 虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。 |
代码示例
从上面代码里看出:
- 静态方法test2()中调用非静态成员变量address,编译失败。这是因为,在编译期并没有对象生成,address变量根本就不存在。
- 静态方法test2()中调用非静态方法test1(),编译失败。这是因为,编译器无法预知在非静态成员方法test1()中是否访问了非静态成员变量,所以也禁止在静态方法中调用非静态成员方法
- 非静态成员方法test1()访问静态成员方法test2()/变量name是没有限制的
所以,如果想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。最常见的静态方法就是main方法,这就是为什么main方法是静态方法就一目了然了,因为程序在执行main方法的时候没有创建任何对象,只有通过类名来访问。
举例:
1 | class A{ |
实例方法和类方法的区别:
1.
- 有对象才有实例方法的入口地址;将类的字节码文件加载至jvm内存时,不会为实例方法分配内存;
- 但是会给类方法分配入口地址。多个对象的实例方法的入口是相同的,也就是实例方法的入口地址被共享;
- 只有在所有的对象被回收时,入口地址才会被取消;
- 而类方法的入口地址在程序退出的时候才被取消。
2.
- 可以把类变量放在实例方法中(把有static修饰的变量放在没有static修饰的方法中),也就是实例方法可以操作类变量;
- 但是类方法不可以操作实例变量;原因是:再类创建对象之前,实例成员变量还没有分配内存空间。
- static方法中只能有static变量
方法重载
顾名思义是对方法进行重新加载。
那么对哪些方法会重新加载呢?
同一个方法名,但是它们参数的类型不同,它们参数的个数不同。
这种例子很多啊,比如说
1 | f(int a,int b); |
this关键字
this是当前对象的引用,就是说当前用构造函数建的对象是谁,这个this就代表谁,它是一个引用。
this可以出现在:
- 实例方法
- 构造方法
- 但是不可以出现在类方法中
不就是你的方法的参数名跟你的成员变量名一样的时候,你要是执行语句:
1 | 成员变量名=参数名 |
就必须写成:
1 | this.成员变量名=参数名 |
注:
一句话说明包的目的:区分不同文件中相同类
import语句
一句话:相当于C语言中的#include
,就是导入在源程序中要用的各种库或者自己写好的接口。
也就是在同一目录下的Java文件是互通的,要通过import与其他包文件进行通讯。
在java中使用类库就是创建相应的对象(所以说Java是面向对象的语言)。
如果使用import导入了包中的所有类,那么会增加编译时间,但是不会影响程序的性能,因为jvm只加载自己程序要用的(可Java本来就慢啊= =)。
访问权限
类方法总是可以操作类中的类变量,与访问控制符没有关系。
- public
被public标记的变量和方法在任何地方的对象都可以访问(类内部、本包、子类、外部包) - protected
被propected标记的变量和方法仅在本包内可以访问。 - 友好的
同上 - private
只有类内部使用
注:
- 只能用public来修饰类
- 权限由高到低:public——>protected——>友好的——>private
protected和友好型的区别:
当子类和父类不在同一个包中时,父类中的private和友好访问权限的成员变量和方法不会被子类继承;在同一个包中时,子类会将父类的变量和方法除private之外全部继承。
如果子类和父类不在同一个包中,子类不继承父类的友好成员变量和方法
对象数组
如果要一次定义很多对象,建议使用对象数组而不是定义多个对象。
小结
子类与继承
子类与父类
既然我们的代码要描述我们的现实生活那么应该怎么做呢?我们显示生活中父亲生儿子这种事,那可是几乎都存在的啊,不管是人类,还是其他生物,几乎都存在,那就我们也让我们的代码可以继承,让我们原本定义的类,可以让他派生自己的子类,也就是让子类来继承父类的一些属性以及行为。(儿子只能有一个爹,爹却可以生很多儿子)
那么继承的关键字是什么呢?
1 | extends |
最简单的例子啊,人类是个父类,学生类是个子类,那可以这么说:学生类继承了人类
人类
属性:
- 姓名
- 性别
- 年龄
- 身份证号
行为:
- 喝水
- 跑步
- 散步
- 吃饭
1 | class People{ |
那么学生类要继承人类的话,就要把人类所有的属性都原封不动的继承过来,然后如果学生类有需求的话再在学生类中填入自己需要的属性或行为
1 | class Student{ |
上面这些属性和行为是新添加的,都是学生所特有的
子类的继承性
在同一包中:
在同一个包中子类继承父类时,不会继承private标记的成员变量和方法,但是会继承友好的成员变量和方法。(父亲的隐私可不兴看啊)
不在同一包中:
不在同一个包中的子类继承父类时,private和友好的成员变量和方法都不会继承。
protected进一步说明的举例:
我们有一个类D,还有一个类C
- D类中自己声明的protected成员变量和方法,只要D跟C在同一包中,则在C类中创建的D对象可以访问这些protected成员变量和方法;
- D类继承自己父类的那些protected成员变量和方法,需要追溯到这些protected成员变量和方法所在的祖先类跟C类是否在同一包中,若在,则在C类中创建的D对象可以访问这些protected成员变量和方法,反之,则不可以。
子类与对象
创建子类对象时,jvm不仅会为子类的成员变量分配内存还会为父类的成员变量分配内存。
但是我们知道子类继承父类时,并不会全部东西都继承过来,有时候会因为不在同一个包中,protected、友好型和private都不能继承,那么父类这个时候为什么还要为这些不能继承的成员变量分配内存呢?
答案是:子类会用那些从父类继承过来的方法来操作这部分未继承的变量。
成员变量的隐藏和方法重写
在子类继承父类的过程中,因为要继承两部分:变量和方法
- 自己再写一遍父类有的变量就叫做隐藏
- 自己再写一遍父类有的方法就叫重写
继承中成员变量的访问特点:
在父子 类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
- 直接通过子类对象访问成员变量:也就是
对象.成员变量
,规则是:创建对象时,等号左边是谁,就优先使用谁,没有则向上找。 - 间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找。
1 | public class Fu { |
1 | public class Zi extends Fu { |
1 |
|
方法重写
在父子类的继承关系当中,创建子类对象,访问成员方法的规则:
创建的对象是谁,就优先用谁,如果没有则向上找。
注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。
如何理解这个隐藏呢?
就是我子类不是会继承你父类的部分变量吗?
比如父类有一个变量:
public a;
如果我在子类中也声明了public a ;
那么子类从父类继承的a就会被隐藏。
说白了就是你子类要是有的话就用自己的,可以不用父类的了(这个“不用”就可以理解隐藏),要是没有,那就用父类的。)
总结:
- 方法:看等号右边(创建的是谁),就优先调用谁
- 变量:看等号左边是谁,就优先调用谁;看方法属于谁,就优先调用谁
重写与重载
重写就是我子类重新写父类的一些同名方法,返回值和形参都不能改变。即外壳不变,核心重写!
方法的重写规则
- 参数列表与被重写方法的参数列表必须完全相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个类,则不能重写该类的方法。
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
super关键字(super≈父类)
super关键字的用法有三种:
1.在子类的成员方法中,访问父类的成员变量。
2.在子类的成员方法中,访问父类的成员方法。
3.在子类的构造方法中,访问父类的构造方法。
一句话说明作用:
当你在子类的方法中需要访问父类的变量或者方法时,就需要使用super关键字来访问。
很显然super这个关键字也是跟继承、父类和子类有关的概念。
1 | public class Fu { |
1 |
|
注意:super 语句必须是子类构造方法的第一条语句。不能在子类中使用父类构造方法名来调用父类构造方法。 父类的构造方法不被子类继承。调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super(); 作为子类构造方法的第一条语句。静态方法中不能使用 super 关键字。
调用父类的方法语法:
1 | super.方法名(参数列表); |
如果是继承的方法,是没有必要使用 super 来调用,直接即可调用。但如果子类覆盖或重写了父类的方法,则只有使用 super 才能在子类中调用父类中的被重写的方法。
final关键字(不变)
- 修饰类:不可以有子类
- 修饰方法:可以被继承,但继承后不能被重写。(老老实实继承)
- 修饰变量:变为常量
final 修饰类中的属性或者变量:
无论属性是基本类型还是引用类型,final 所起的作用都是变量里面存放的”值”不能变。
这个值,对于基本类型来说,变量里面放的就是实实在在的值,如 1,”abc” 等。
而引用类型变量里面放的是个地址,所以用 final 修饰引用类型变量指的是它里面的地址不能变,并不是说这个地址所指向的对象或数组的内容不可以变,这个一定要注意。
例如:类中有一个属性是 final Person p=new Person(“name”); 那么你不能对 p 进行重新赋值,但是可以改变 p 里面属性的值 p.setName(‘newName’);
final 修饰属性,声明变量时可以不赋值,而且一旦赋值就不能被修改了。对 final 属性可以在三个地方赋值:声明时、初始化块中、构造方法中,总之一定要赋值。
Java 转型问题
Java 转型问题其实并不复杂,只要记住一句话:父类引用指向子类对象。
什么叫父类引用指向子类对象,且听我慢慢道来。
从 2 个名词开始说起:向上转型(upcasting) 、**向下转型(downcasting)**。
举个例子:有2个类,Father 是父类,Son 类继承自 Father。
第 1 个例子:
1 | Father f1 = new Son(); // 这就叫 upcasting (向上转型) |
第 2 个例子:
1 | Father f2 = new Father(); |
你或许会问,第1个例子中:Son s1 = (Son)f1
; 问为什么是正确的呢。
很简单因为 f1 指向一个子类对象,Father f1 = new Son();
子类 s1 引用当然可以指向子类对象了。
而 f2 被传给了一个 Father 对象,Father f2 = new Father();
子类 s2 引用不能指向父类对象。
总结:
1、父类引用指向子类对象,而子类引用不能指向父类对象。
2、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转换吗,如:
1 | Father f1 = new Son(); |
3、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转换,如:
f1 就是一个指向子类对象的父类引用。把f1赋给子类引用 s1 即 Son s1 = (Son)f1;
其中 f1 前面的(Son)必须加上,进行强制转换。
对象的上转型对象
通俗地讲即是将子类对象转为父类对象。
上转型对象的特点:
- 上转型对象不能操作子类新增的变量和方法
- 上转型对象可以访问子类继承或者隐藏的成员变量,也可以调用子类继承的方法或者重写的方法。
- 如果子类重写了父类的某个实例方法后,当上转型对象调用这个方法时,一定调用的是子类重写的,若子类重写了父类的static方法,则调用时只能调用父类的。
- 不可以将父类创建的对象的应用赋值给子类声明的对象(不能说:人是中国人)
举例:
1 | public class Animal { |
注意这里的向上转型:
1 | Animal b=new Bird(); //向上转型 |
此处将调用子类的 eat() 方法。原因:b 实际指向的是 Bird 子类,故调用时会调用子类本身的方法。
需要注意的是:
向上转型时 b 会遗失除与父类对象共有的其他方法。如本例中的 fly 方法不再为 b 所有。
因此输出结果:bird eatting...
对象的下转型对象
与向上转型相反,即是把父类对象转为子类对象。
1 | public class Girl { |
1 | Girl g1=new MMGirl(); //向上转型 |
这里的向下转型是安全的。因为 g1 指向的是子类对象。
而
1 | Girl g2=new Girl(); |
运行出错:
1 | Exception in thread "main" java.lang.ClassCastException: com.wensefu.other1.Girl |
构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
子类构造方法总是先调用父类的构造方法,你没写super(有参数),则默认你写了super(无参数)。
举例:
1 | class SuperClass { |
输出结果为:
1 | ------SubClass 类继承------ |
好像有点感觉了,我们将代码做一点改动,再次感受一下:
在subClass()
构造方法中添一句
1 | super(200); |
然后将父类有参数构造方法改为:
1 | SuperClass(int n) { |
再次运行,得到结果:
1 | ------SubClass 类继承------ |
继承与多态
首先多态是个跟继承有关的概念,
什么叫多态?
多态就是在描述生活
举个例子
多态就是我有一个动物类,然后动物类派生了两个子类,猫类和狗类
我当初在动物类中写了一个方法:发出叫声
很显然猫跟狗的叫声不一样,一个喵喵,一个汪汪,那么我当初在动物类中写的这个发出叫声的方法现在如果这两个子类要用的话,是不是就得在猫类和狗类里面重写了,重写完之后,这整个过程就叫做多态
多态就是 同一个方法的不同实现。
abstract类和abstract方法
在java中我们用abstract关键字来表达抽象。举个例子:
我们说车子都可以跑(run)。但有几个轮子,怎么跑,对于不同的车有不同的结果。自行车需要人踩着跑,汽车发动机推动跑等等,那么我们可以车表达为抽象类。
1 | /** |
我的理解是抽象更像是一种概念,只要是抽象的就不需要具象,抽象类不需要实例化,抽象方法不需要在本类中实现。
抽象方法是一个概念,不用实现!!只要你在一个方法前冠以abstract,那这个方法就变成了一个概念,你不需要去实现一个概念。抽象方法跟接口中的方法是类似的,都不需要实现,所以我们没办法直接调用抽象方法
abstract类中声明的abstract方法要在子类中实现,如果子类未实现必须要将子类也声明为abstract:
abstract类:
1 | 1、用abstract关键字来表达的类,其表达形式为:(public)abstract class 类名{} |
abstract方法:
1 | 1、从上面的例子中我们可以看到抽象方法跟普通方法是有区别的,它没有自己的主体(没有{}包起来的 |
总结:
1 | /* |
接口与实现
接口
官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
个人理解:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解)
接口的特点
就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。
如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。(必须记住:抽象方法只能存在于抽象类或者接口中,但抽象类中却能存在非抽象方法,即有方法体的方法。接口是百分之百的抽象类)
1 | //定义子类,继承父类,实现接口 |
接口定义与实现
接口定义
定义接口用关键词interface
1 | /* |
1 | /* |
接口实现
1 | public class MyInterfaceAbstractImpl implements MyInterfaceAbstract { |
abstract类与接口的比较
在Java语言中,abstract class和interface是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。
- 1.相同点
A. 两者都是抽象类,都不能实例化。
B. interface实现类及abstrct class的子类都必须要实现已经声明的抽象方法。
不同点
A. interface需要实现,要用implements,而abstract class需要继承,要用extends。
B. 一个类可以实现多个interface,但一个类只能继承一个abstract class。
C. interface强调特定功能的实现,而abstract class强调所属关系。
D. 尽管interface实现类及abstrct class的子类都必须要实现相应的抽象方法,但实现的形式不同。interface中的每一个方法都是抽象方法,都只是声明的 (declaration, 没有方法体),实现类必须要实现。而abstract class的子类可以有选择地实现。
这个选择有两点含义:
一是Abastract class中并非所有的方法都是抽象的,只有那些冠有abstract的方法才是抽象的,子类必须实现。那些没有abstract的方法,在Abstrct class中必须定义方法体。
二是abstract class的子类在继承它时,对非抽象方法既可以直接继承,也可以覆盖;而对抽象方法,可以选择实现,也可以通过再次声明其方法为抽象的方式,无需实现,留给其子类来实现,但此类必须也声明为抽象类。既是抽象类,当然也不能实例化。
E. abstract class是interface与Class的中介。
interface是完全抽象的,只能声明方法,而且只能声明pulic的方法,不能声明private及protected的方法,不能定义方法体,也 不能声明实例变量。然而,interface却可以声明常量变量,并且在JDK中不难找出这种例子。但将常量变量放在interface中违背了其作为接 口的作用而存在的宗旨,也混淆了interface与类的不同价值。如果的确需要,可以将其放在相应的abstract class或Class中。
abstract class在interface及Class中起到了承上启下的作用。一方面,abstract class是抽象的,可以声明抽象方法,以规范子类必须实现的功能;另一方面,它又可以定义缺省的方法体,供子类直接使用或覆盖。另外,它还可以定义自己 的实例变量,以供子类通过继承来使用。interface的应用场合
A. 类与类之前需要特定的接口进行协调,而不在乎其如何实现。
B. 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
C. 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
D. 需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。abstract class的应用场合
一句话,在既需要统一的接口,又需要实例变量或缺省的方法的情况下,就可以使用它。最常见的有:
A. 定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
B. 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
C. 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能。