title | tags | grammar_cjkRuby |
---|---|---|
Java基础06 |
抽象类,接口,多态,构造方法 |
true |
[TOC]
当我们定义一个父类的时候,有些方法可能需要子类去做具体的功能,而在父类中并不能确定做哪些功能,那么我们就可以将这些方法定义成抽象方法.所谓的抽象方法就是没有方法体的方法.那么我们的类也必须定义 这个类为抽象的类.
- 抽象方法和抽象类需要使用特定的修饰符进行修饰,这个修饰符就是 abstract 抽象类的格式为 public abstract class 类名 { }
- 抽象方法的格式为 public abstract 返回值类型 方法名(参数);
- 抽象类和抽象方法都需要被abstract修饰,抽象方法一定要定义在抽象类中.
- 抽象类也可以没有抽象方法,那么定义抽象类的意义在于不能 使用这个类创建对象.
- 普通类能做的事情,抽象类都可以做(定义成员变量,定义成员方法),唯独不能进行实例化.
- 子类继承抽象类,必须实现抽象方法,如果子类不实现抽象方法,那么子类必须是抽象类
- 子类重写父类的方法时,不能使用abstract关键字
抽象类注意的问题
- 抽象类因为需要子类去实现,所以肯定得是一个父类
- 抽象类可以不定义抽象方法,意义在于,不让该类创建对象,方法可以让子类使用
- 一个方法如果没有方法体,则必须使用abstract进行修饰
abstract不能与那些关键字共存?
- private:私有的方法子类是无法继承的,也不能覆盖,而abstract和private一起使用修饰方法时,abstract既要去实现这个方法,而private修饰子类根本无法得到父类这个方法.互相矛盾.
- final: 因为被final定义的方法不能被重写,而抽象方法需要子类去重写进行实现功能,所以矛盾.
- static:因为static不用实例化可直接调用,即可直接能调用,而abstract需要子类实现后才能调用,所以矛盾.
- 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的类.
- 接口中只允许出现抽象方法,不允许非抽象方法
- 接口的源文件也是java文件,编译后的文件也是**.class**,所以可以把接口看做是一个特殊的类.
- 与定义类的class不同,接口定义时需要使用interface关键字
- 定义的格式
public interface 接口名{
抽象方法1;
抽象方法2;
抽象方法3;
}
- 因为接口是供别的类去实现的,并且要实现他的抽象方法,所以接口中方法的修饰符必须定义成public
- 接口中不能定义普通的成员变量,只能定义成public static final 成员变量
注意如果定义方法的时候,不写public abstract,不会报错,但是系统会默认添加;定义成员变量的时候使用public static final,如果不这样写,不会报错,但是系统会默认添加.
- 一个实现类接口的格式为:
class 类名 implements 接口1,接口2{
重写接口中方法
}
- 一个类可以同时实现多个接口,但是需要将抽象方法进行实现,创建接口的时候修饰符可以省略,系统会自动加上,但是实现的时候,就必须添加public
- 接口不能创建对象
- 接口的变量使用public static final修饰,如果不写,默认添加
- 子类必须重写接口中所有的抽象方法后,才能创建对象,如果子类不能够重写所有的抽象方法,那么子类必须定义成抽象类
因为接口中的方法都是没有实现的,所以就算有两个父类接口的方法名称相同,子类实现接口的时候也不会有什么影响,重写即可,所以接口与接口之间是存在多继承的.
interface MyInterface{
void method1();
}
interface MyInterface2{
void method2();
}
interface MyInterface3{
void method3();
}
interface MyInterface4 extends MyInterface1,MyInterface2,MyInterface3{
void method4();
}
- 扩展原有类的功能
- 设定了规则
- 降低耦合性
-
相同点
- 都位于继承的顶端,用于被其他类实现或继承;
- 都不能直接实例化对象
- 都包含抽象方法,其子类都必须复写这些抽象方法
-
区别
- 抽象类可以定义非抽象方法,避免子类重复实现这些方法,提高代码的复用率,接口只能包含抽象方法
- 多态的表现形式为父类的变量指向子类的对象
- 多态的前提是必须有子类关系或者类实现接口关系,否则无法完成多态
- 父类类型的变量调用方法的时候,实际上会调用子类重写的方法.
- 普通类多态定义的格式:父类 变量名 = new 子类();
class Father{ }
class Child extends Father{ }
//类的多态使用
Father f = new Child();
- 抽象类多态定义的格式:抽象类 变量名 = new 抽象类子类();
abstract class Father{
public abstrct void method();
}
class Child extends Father{
public void method(){
System.out.println("重写父类抽象方法");
}
}
- 接口多态定义的格式:接口 变量名 = new 接口实现类();
interface Facher {
public abstract void method();
}
class Child implements Father {
public void method(){
System.out.println("重写接口抽象方法");
}
}
// 接口的多态使用
Father f = new Child();
注意:同一个父类的方法会被不同的子类重写.在调用方法时,调用的是各个子类重写后的方法.
- 对于成员方法: 编译的时候看=左边,运行的时候看=右边
- 对于成员变量:编译的时候看=左边,运行的时候看=左边
- 如果是多态的话,程序在进行编译的时候只会看变量的类型,而实际运行的时候则是看具体的对象,那么如果想要调用父类中没有,但是子类中有的方法该怎么办?
- 可以使用强制类型转换,也叫做多态的向下转型,将父类类型再转换为子类类型 子类类型 变量 = (子类类型) 变量名称
- 注意:如果强制类型转换的话,必须要保证变量的原始类型就是需要转换的类型,否则会抛出异常子类变量 变量名 = (转换类型) 变量名称
如果直接进行强制类型转换的时候可能出现问题,所以可以使用instanceof进行判断,如:对象 instanceof 类名
if(a instanceof Child){
Child c = (Child)a;
}
如果我们希望在创建对象的时候就能对对象的成员变量进行赋值,就需要创建构造函数,构造函数的目的就是在创建对象的时候给对象的成员变量进行赋值
修饰符 构造方法名(参数列表){
}
- 构造方法名称必须和类型保持一致.
- 构造方法没有具体的返回值.
- 如果我们没有定义构造方法,编译器会在编译的时候添加默认无参构造方法,这就是我们之前从未定义过构造方法,而能一致使用的原因.
- 如果我们定义了新的构造方法,多个构造方法是以重载的形式存在的.
- 构造方法是可以被private修饰的,作用:其他程序无法创建类该类的对象.
- 子类创建对象的时候,会调用父类的构造方法,在子类构造方法中的第一行总有一个隐式的调用**super();**会默认取调用父类的无参构造方法.
public class TTTTTT {
public static void main(String[] args) {
new Zi();
}
}
class Fu{
int a;
Fu(){
System.out.println("父类构造方法"+n
um);
a = 250;
}
}
class Zi extends Fu{
Zi(){
System.out.println("子类构造方法"+num);
}
}
//输出的结果是
//父类构造方法0
//子类构造方法250
- 思考1
为什么子类的构造方法会去调用父类的构造方法呢?
因为构造方法本身就是初始化对象的成员变量的,父类的构造方法也是这个目的,如果不去调用,我们就不知道父类做了什么操作,所以才会隐式的去调用super();子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。这样,才可以使用父类中的内容。
- 思考2
如果子类会默认调用父类的无参构造方法,如果父类中又没有无参的构造方法,程序会出现什么问题?编译器会自动去找到对应的有参的构造方法么?
如果父类中没有空参构造方法,那么子类的的构造方法就会报错,需要我们显示的调用父类的有参数的构造方法