抽象类
假如定义一个shape类,这个类提供一个计算周长的方法,但不同Shape子类对周长的计算方法都不一样。既然Shape类不知道如何实现计算周长的方法,干脆就不管了。但如果利用多态来对Shape引用变量指向子类对象方式,Shape变量无法调用计算周长的方法,必须将其强制类型转换为子类类型才可调用,这就降低了进程的灵活性。
Java提供了抽象方法让类包含某个方法又不需要提供方法具体实现。
抽象方法:方法必须使用abstract修饰符来修饰,抽象方法只能存在与抽象类中,但抽象类可以没有抽象方法。
(1) 抽象方法和抽象类规则
(1) 抽象类必须使用abstract修饰符修饰,抽象方法也必须使用abstract修饰符修饰,抽象方法不能有方法体。
(2) 抽象类不能被实例化。
无法使用new关键字来调用抽象类的构造器创建抽象类的对象。
(3) 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)。抽象类的构造器不能用于创建对象,用于被其子类调用。
(4) 有抽象方法的类一定是抽象类。
###(2) 抽象类如何被实例化
以多态的形式,由具体的子类实例化
123456789101112131415 | abstract class Animal{ public abstract void eat();}class Cat extends Animal{ public void eat(){ System.out.println("猫吃鱼"); }}public static void main(String[] args){ //Animal a = new Animal(); //错误,Animal是抽象的,无法实例化 Animal a = new Cat();//父类引用指向子类对象 a.eat();} |
(3) 抽象类的子类
1 . 要么也是抽象类
2 . 要么重写父类中所有抽象方法
(4) 抽象类的成员特点
成员变量:既可以是变量,也可以是常量。
构造方法:用于子类访问父类数据的初始化。
成员方法:既可以是抽象的,也可以是非抽象的
抽象方法: 强制要求子类做的事情。
非抽象方法: 子类继承的事情,提高代码复用性
(5) abstract关键字
不能与static同时存在,被abstract修饰的方法没有方法体,被static修饰的可以用类名.调用,但是类名.调用抽象方法是没有意义的
不能与final同时存在,被abstract修饰的方法强制子类重写,被final修饰的不让子类重写,所以两者互相矛盾
不能与private同时存在,被abstract修饰的是为了让子类继承并强制重写,被private修饰不让子类访问,所以两者互相矛盾的
二、接口
抽象类是从多个类抽象出来的模板,但抽象类允许存在普通方法抽象得不彻底。为了抽象彻底,提炼出一种更加抽象彻底的“抽象类”——接口。接口里不能包含普通方法,接口里的所有方法都是抽象方法。(Java 8对接口进行了改进,允许接口中定义默认方法——可以提供方法实现)
1. 接口的定义
语法: [修饰符] interface接口名 [extends 父接口1,父接口2...]{ 定义零到多个常量 定义零到多个抽象方法 定义零到多个内部类、接口、枚举 定义领导多个默认方法或类方法 }
修饰符:public或缺省
接口名:符合标识符规则
继承:继承多个父接口,但不能继承类
注意: 1)可以包含成员变量(只能是静态常量)——系统会默认增加 public static final三个关键字
Interface Inter{ public static final int num = 10; }
因为num的值不能再修改,因此系统默认final修饰num成员变量
class Demo implements Inter{ public void print(){ //num = 20; System.out.println(num); } }
可以通过类名.成员变量名输出,因此系统默认public 和static修饰num成员变量
System.out.println(Inter.num);
2)方法(抽象实例方法、类方法或默认方法)、内部类(包括内部接口、枚举)
不能包含构造器和初始化块。
接口中的抽象方法系统会默认public和abstract,当实现类重写抽象方法时,需要注意访问权限必须是public
2. 接口的使用
通过implements关键实现接口
语法:[修饰符] class 类名 [extends 父类] implements 接口1 [,接口2 ...]{ 类体部分 }
注意:某个类实现了接口,该类需要把接口中的抽象方法全部重写
3. 特点:
接口不能被实例化——多态方式实例化
4. 类与类、接口与接口、类与接口的关系
类与类:继承关系,只能单继承,可以多层继承
接口与接口:继承关系,可以单继承,也可以多继承
类与接口:实现关系,可以单实现,也可以多实现。
并且还可以在继承一个类的同时实现多个接口
5. 抽象类与接口的区别
(1) 成员区别
抽象类:
成员变量:可以变量,也可以常量
构造方法:有
成员方法:可以抽象,也可以非抽象
接口:
成员变量:只可以常量
构造方法:无
成员方法:只可以抽象
(2) 关系区别
类与类
继承,单继承
类与接口
实现,单实现,多实现
接口与接口
继承,单继承,多继承
三、内部类
在类中定义的类称为内部类
(1) 特点
① 内部类可以直接访问外部类的成员,包括私有
② 外部类要访问内部类的成员,必须创建内部类的对象
外部类类名.内部类类名 对象名 = 外部类对象.内部类对象;
###(2) 成员内部类私有使用(内部类被private修饰)
注意:私有的内部类只能被外部类使用
外部类的静态成员不能访问非静态内部类
非静态内部类中不能定义静态成员
(3) 静态成员内部类(被static修饰的内部类)
注意:静态内部类可以包含静态成员,也可以包含非静态成员。访问静态内部类中非静态成员首先通过静态内部类的构造器创建静态内部类对象: new OuterClass.InnerConstructor()
内->外静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
静态内部类的实例方法不能访问外部类的实例成员,只能访问外部类的静态成员
外->内外部类使用静态内部类的类名作为调用者访问静态内部类的类成员
外部类使用静态内部类的对象作为调用者访问静态内部类的实例成员
(4) 局部内部类(在外部类方法中定义类)——只在方法里有效
`局部内部类只能在其所在的方法中访问`局部内部类访问局部变量(局部变量最好使用final修饰——局部变量不可再修改)
public void method(){ final int num = 10; class Inner{ public void print(){ System.out.println(num); } } Inner i = new Inner(); i.print(); }
(5) 匿名内部类
面试题:
四、初始化块(代码块)
(1) 语法:
[修饰符]{ //初始化块代码 }
修饰符:static或缺省
(2) 作用
给对象进行初始化。对象一建立就运行,且优先于构造函数的运行。
与构造函数的区别:
非静态初始化块给所有对象进行统一初始化,构造函数只给对应对象初始化。因此在代码块中可以将所有构造函数共性的东西定义与初始化。但代码块不能接收参数。
(3) 被static修饰的静态代码块
给类进行初始化。随着类的加载而执行,且只执行一次
与构造代码块的区别:
1)构造代码块用于初始化对象,每创建一个对象就会被执行一次;静态代码块用于初始化类,随着类的加载而执行,不管创建几个对象,都只执行一次。
2)静态代码块优先于构造代码块的执行
3)都定义在类中,一个带static关键字,一个不带static
五、类中所有成员的执行顺序
成员变量与代码块谁在前就先执行谁
代码块先执行,构造器后执行
静态代码块的先执行,非静态代码块的后执行
方法与静态方法谁在前就先执行谁(静态方法只能访问静态的成员)
注意:代码块如果在成员变量前,在代码块中如果执行对成员变量的访问,会报非法前向引用错误。这个是由于Java编译器强制进行的一个检查,目的是避免循环初始化和其他非正常的初始化行为。(针对非静态)
只有在满足一下4点的情况下,成员变量的声明才必须在使用之前:
1.使用出现在类的实例(类)变量初始化,或者类的实例(静态)初始化块中
2.使用不在赋值表达式左边
3.使用通过简单名称
4.类是包含该使用的直接类(接口)