黑马程序员--抽象类和接口
------- android培训、java培训、期待与您交流! ----------
?
抽象类
包含了抽象方法的一个类叫作“抽象类”。如果一个类里包含了一个或多个抽象方法,类就必须指定成
abstract(抽象)。“抽象方法”,属于一种不完整的方法,只含有一个声明,没有方法主体。
下面是抽象方法声明时采用的语法:
abstract void f();
如果从一个抽象类继承,而且想生成新类型的一个对象,就必须为基础类中的所有抽象方法提供方法定义。
如果不这样做(完全可以选择不做),则衍生类也会是抽象的,而且编译器会强迫我们用abstract 关键字标志那个类的“抽象”本质。
即使不包括任何abstract 方法,亦可将一个类声明成“抽象类”。如果一个类没必要拥有任何抽象方法,而且我们想禁止那个类的所有实例,这种能力就会显得非常有用。
在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。
(1)多个类有相同的方法声明,但是方法体不一样。这个时候,我们考虑把方法声明进行抽取。
? 让子类继承后,自己去实现方法体。没有方法体的方法,我们需要用抽象标志下。
? 抽象的关键字是:abstract。
(2)抽象类:
该方法称为抽象方法,包含抽象方法的类就是抽象类。
(3)抽象类的特点:
A:抽象类和抽象方法都要用abstract进行修饰
B:抽象类不能被实例化
C:抽象类中不一定有抽象方法,但是,有抽象方法的类一定是抽象类。
(4)抽象类中数据的特点
A:成员变量
抽象类中可以有变量,也可以有常量。
B:成员方法
抽象类中可以有抽象方法,也可以有非抽象方法。
C:构造方法
抽象类是一个类,所以,它有构造方法。
虽然本身不能实例化。但是可以给子类实例化使用。
(5)抽象类中的问题
A:抽象类中是否有构造方法?能不能被实例化?如果不能,为什么有构造方法?
抽象类有构造方法。
????????
1. 抽象方法 abstract void f(); 抽象方法不能包含有任何方法的BODY 。
2. 如果一个类包含1个或者多个抽象类, 则该类必须限定为抽象的。
需要在前面指定 abastract 关键字。
(1)抽象类不能被实例化
(2)包含抽象方法的类,必须标识 abstract
3. 如果从一个抽象类继承, 必须对所有抽象方法进行覆盖 , 否则导出类也是抽象的
4. 也可以考虑创建没有任何抽象方法的抽象类 。
abstract class Instrument {
// 抽象类中可以有非抽象方法。
private int i; // Storage allocated for each
public abstract void play(Note n);
public String what() { return "Instrument"; }
public abstract void adjust();
}
抽象类不能被实例化。
抽象类中的构造方法供子类实例化调用。
B:抽象关键字abstract不可以和哪些关键字共存?
**private:
私有内容子类继承不到,所以,不能重写。
但是abstract修饰的方法,要求被重写。两者冲突。
????????
抽象类的特点:
1.抽象方法一定定义在抽象类中
2.抽象方法和抽象类都必须被abstract关键字修饰
3.抽象类不可以用new创建对象,因为调用抽象方法没有意义
4.抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类
抽象类和一般类没有太大的不同
只是要注意该怎么描述事物就怎么描述,只不过该事物中出现了一些看不懂的东西
这些不确定的部分也是该事物的功能,需要明确出来,但是无法定义主体
抽象类比一般类多了个抽象函数
抽象类不可以实例化
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象
*/
abstract class Student
{
abstract void study();//抽象方法必须存储在抽象类中
void sleep()//抽象类也可以有一般方法,也可以有抽象方法
{
System.out.println("躺着");
}
}
class ChongCiStudent extends Student
{
void study()
{
System.out.println("chongci");//子类要复写父类所有的抽象方法
}
}
class BaseStudent extends Student
{
void study()
{
System.out.println("base study");
}
}
class AdvStudent extends Student
{
void study()
{
System.out.println("adv? study");
}
}
class? AbstractDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
接口interface
(1)当一个类中的方法都是抽象的时候,java提供了另一种表示方式,叫接口。
? 用interface关键字表示。类与接口关系用implements表示。
(2)接口的成员特点
A:成员变量
是常量,默认修饰 public static final
B:成员方法
都是抽象的,默认修饰 public abstract
(3)关系
A:类与类的关系
是继承关系。类与类只能单继承,可以多重继承。
B:类和接口的关系
是实现关系。类可以多实现接口。
类在继承一个类的同时,可以实现多个接口。
C:接口和接口的关系
是继承关系。接口可以多继承接口。
(4)接口的特点
A:是对外暴露的规则
B:是功能的扩展
C:接口的出现降低耦合性。
耦合(类与类之间的关系)
内聚(类完成功能的能力)
编程规范:低耦合,高内聚。
D:接口可以多实现。如:CPU和主板、笔记本的USB插口、插座
(5)接口和抽象类的区别
A:抽象类只能被单继承
? 接口可以多实现,接口的出现避免了多继承的局限性。
B:抽象类中的数据特点:
成员变量:可以是变量,也可以是常量
成员方法:可以是抽象方法,也可以是非抽象方法
构造方法:有构造方法
? 接口中的数据特点:
成员变量:是常量。默认修饰 public static final
成员方法:都是抽象方法。都有默认修饰 public abstract
构造方法:没有构造方法
C:抽象类中定义的是继承体系中的共性功能。
? 接口中定义的是继承体系中的扩展功能。
D:抽象类被继承是"is a"关系:xx是yy的一种
? 接口被实现是"like a"关系:xx像yy的一种
04 子父类中变量的特点
内存中的方法区(静态区 非晶态区)
多态:父类super指向子类对象
05
子父类中函数的特点
覆盖(重写):子父类中同名函数
当子类出现和父类一样的函数时
当子类对象调用该函数,会运行子类函数的内容
如同父类的函数被覆盖一样
重写(覆盖)
当子类集成了父类,沿袭了父类的功能到子类中
但是子类虽有该功能,但是功能的内容却和父类不一致
这时,没必要定义新功能(speak2的出现没有意义),而是使用覆盖特殊,保留父类的功能定义并重写功能内容
class Tel
{
void show()
{
System.out.println("number");
}
}
class NewTel extends Tel
{
supper.show();//System.out.println("number");
System.out.println("name");
System.out.println("pic");
}
覆盖:
1,子类覆盖父类,必须保证子类权限大与父类权限才可以覆盖
2.静态只能覆盖静态
权限:public >默认> private
重载:只看同名函数的参数列表
重写:字父类方法要一模一样
06子类实例化过程
在对子类对象进行初始化时,父类的构造函数也会运行
那是因为子类的构造函数默认第一行有一条隐式的语句supper();
supper();会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是supper();
class Zi extends Fu
{
Zi()
{
//supper();隐式说明
//super(4); 手动访问
System.out.println("zi run");
}
??????? Zi(int x)
??????? {
???????????? //super();
??????????? System.out.println("zi..."+x);
??????? }
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的
所以子类对象初始化时,要先访问一下父类中的构造函数
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来完成
class Person
{
String name;
Person(String name)
{
this.name = name;
}
void show(){}
}
class Student extends Person
{
Student (String name)
{
super(name);
}
void method()
{
super.show();
}
}
super语句一定定义在子类构造函数的第一行
Zi(int x)
{
//this();有this就不能用supper访问父类了
//super(2);
System.out.println("zi...."+x);
}
}
子类的实例化过程
结论:子类的所有构造函数,默认都会访问父类中空参数的构造函数,因为每一个构造函数内第一行都有一句隐式的super语句
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问的构造函数
子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少会有一个构造函数访问父类中的构造函数
this和super不能在同一行
final关键字
final:最终,作为一个修饰符
1,可以修饰类:函数,变量
2.被final修饰的类不可以被继承.为了避免被继承,被子类复写功能
3.被final修饰的方法不可以被复写
4.被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量又可以修饰局部变量
??? 当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读,都给这些值起个名字方便阅读
??? 而这个值不需要改变,所以给这些值加final修饰(类似于宏定义)
??? 作为常量:常量的书写规范所有字母都大写,如果有多个单词组成
??? 单词见通过_连接(JAVA_HOME)。
5.内部类定义在类中局部位置上时,只能访问该局部被final修饰的局部变量(类的修饰符有public,final)或默认
全局常量:
public static final double PI = 3.14; 类名直接访问
?