-
什么是抽象类?
类和类之间具有共同特征,将这些共同特征提取出来,形成的就是抽象类。类本身是不存在的,所以抽象类无法创建对象(无法实例化)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5h8vEwkj-1633269397150)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20211001235508415.png)]
-
抽象类属于什么类型?
抽象类也是属于引用数据类型。
-
抽象类怎么定义?
语法:
{ 修饰符列表 } abstract class 类名{
类体;
-
抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承。
-
final和abstract不能联合使用,这两个关键字是对立的。
-
抽象类的子类可以是抽象类也可以是非抽象类。
-
抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法供子类使用。
-
抽象类关联到抽象方法的概念,抽象方法表示没有实现的方法,没有方法体的方法,例如:
public abstract void doSome();
抽象方法的特点:
特点1:没有方法体,以分号结尾。
特点2:前面修饰符列表中有abstract关键字。
-
抽象类中不一定有抽象方法。抽象方法必须出现在抽象类中。
-
一个非抽象的类继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写,或者也可以叫做“实现“。
**面试题(判断题):**java语言中,凡是没有方法体的方法都是抽象方法。
错误。Object类中有很多方法都没有方法体,都是以“;”结尾的,但它们都不是抽象方法,例如:
public native int hashCode();
前面修饰符列表中没有abstract。有同一个native。表示调用JVM本地程序。
-
接口也是一种“引用数据类型”。编译之后也是一个class字节码文件。
-
接口是完全抽象的。(抽象类是半抽象的)或者也可以说接口是特殊的抽象类。
-
接口怎么定义?
{ 修饰符列表 } interface 接口名 { }
-
接口支持多继承,一个接口可以继承多个接口。
-
接口中只包含两部分内容,一部分是:常量。一部分是:抽象方法。接口中没有其它内容。
-
接口中所有的元素都是public修饰的。(都是公开的。)
-
接口中的抽象方法定义时:public abstract修饰符可以省略。
-
接口中的方法都是抽象方法,所以接口中的方法不能有方法体。
-
接口中的常量的public static final可以省略。
-
类实现接口要实现所有方法。
(1)类和类之间叫做继承,类和接口之间叫做实现。【仍然可以将“实现”看做“继承”】
继承使用extends关键字完成。
实现使用implements关键字完成。
(2)当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(覆盖、重写)
public class Test01 {
public static void main(String[] args) {
//父类型的引用指向子类型的对象
MyMath mm = new MyMathImpl();
//调用接口里面的方法(面向接口编程)
int result1 = mm.sum(10 ,20);
System.out.println(result1);
int result2 = mm.sub(20 ,10);
System.out.println(result2);
}
}
interface MyMath{
double PI = 3.1415926;
int sum(int a, int b);
int sub(int a, int b);
}
class MyMathImpl implements MyMath{
public int sum(int a, int b){
return a+b;
}
public int sub(int a, int b){
return a-b;
}
}
-
一个类可以同时实现多个接口。
这种机制弥补了单继承带来的缺陷。
public class Test02{ public static void main(String[] args) { // 多态该怎么用呢? // 都是父类型引用指向子类型对象 A a = new D(); //a.m2(); // 这时编译报错。A接口中没有m2()方法。 B b = new D(); C c = new D(); // 这个编译没问题,运行也没问题。 // 调用其他接口中的方法,你需要转型(接口转型。) B b2 = (B)a; b2.m2(); // 直接向下转型为D可以吗?可以 D d = (D)a; d.m2(); } } interface A{ void m1(); } interface B{ void m2(); } interface C{ void m3(); } // 实现多个接口,其实就类似于多继承。 class D implements A,B,C{ // 实现A接口的m1() public void m1(){ } // 实现B接口中的m2() public void m2(){ System.out.println("m2 ...."); } // 实现接口C中的m3() public void m3(){ } }接口A和接口B虽然没有继承关系,但是写代码的时候,可以互转。
编译器没意见。但是运行时可能出现:ClassCastExceptionpublic class Test03{ public static void main(String[] args){ M m = new E(); // 经过测试:接口和接口之间在进行强制类型转换的时候,没有继承关系,也可以强转。 // 但是一定要注意,运行时可能会出现ClassCastException异常。 // 编译没问题,运行有问题。 if(m instanceof K){ K k = (K)m; } } } interface K{ } interface M{ } class E implements M{ }之前有一个结论:
无论向上转型还是向下转型,两种类型之间必须要有继承关系,
没有继承关系编译器会报错。(这句话不适用在接口方面。)
最终实际上和之前还是一样,需要加:instanceof运算符进行判断。
向下转型养成好习惯。转型之前先if+instanceof进行判断。-
继承和实现都存在的话,代码应该怎么写?
extends 关键字在前。
implements 关键字在后。
public class Test03{ public static void main(String[] args){ // 创建对象(表面看Animal类没起作用!) Flyable f = new Cat(); //多态。 f.fly(); // 同一个接口 Flyable f2 = new Pig(); // 调用同一个fly()方法,最后的执行效果不同。 f2.fly(); Flyable f3 = new Fish(); f3.fly(); } } // 动物类:父类 class Animal{ } // 可飞翔的接口(是一对翅膀) // 接口通常提取的是行为动作。 interface Flyable{ void fly(); } // 动物类子类:猫类 // Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫变的可以飞翔。 class Cat extends Animal implements Flyable{ public void fly(){ System.out.println("飞猫起飞,翱翔太空的一只猫!!"); } } // 蛇类,如果你不想让它飞,可以不实现Flyable接口 // 没有实现这个接口表示你没有翅膀,没有给你插翅膀,你肯定不能飞。 class Snake extends Animal{ } // 想飞就插翅膀这个接口。 class Pig extends Animal implements Flyable{ public void fly(){ System.out.println("我是一只会飞的猪!!!"); } } class Fish implements Flyable{ //没写extends,也是有的,默认继承Object。 public void fly(){ System.out.println("我是六眼飞鱼!!!"); } }
-
分析:
中午去餐餐馆吃饭,这个过程中有接口吗?
菜单是一个接口。(菜单上有一个抽象的照片:西红柿炒鸡蛋)
谁面向接口调用。(顾客面向菜单点菜,调用接口)
谁负责实现这个接口。(后台的厨师负责把西红柿鸡蛋做好!是接口的实现者。)
这个接口的作用是:这个“菜单”,让“顾客”和“后厨”解“耦合”了。
顾客不用找后厨,后厨不用找顾客。他们之间完全依靠这个抽象的菜单沟通。
package Restaurant;
//接口:菜单,抽象的
public interface FoodMenu {
// 西红柿炒蛋
void shiZiChaoJiDan();
// 鱼香肉丝
void yuXiangRouSi();
}
package Restaurant;
// 顾客
public class Customer{
// 顾客手里有一个菜单
private FoodMenu foodMenu;
// 如果以下这样写,就表示写死了(焊接了。没有可插拔了。)
// 中餐厨师
//ChinaCooker cc;
// 西餐厨师
//AmericCooker ac
// 构造方法
public Customer(){
}
public Customer(FoodMenu foodMenu){
this.foodMenu = foodMenu;
}
// setter and getter
public void setFoodMenu(FoodMenu foodMenu){
this.foodMenu = foodMenu;
}
public FoodMenu getFoodMenu(){
return foodMenu;
}
// 提供一个点菜的方法
public void order(){
// 先拿到菜单才能点菜
// 调用get方法拿菜单。
//FoodMenu fm = this.getFoodMenu();
// 也可以不调用get方法,因为在本类中私有的属性是可以访问
foodMenu.shiZiChaoJiDan();
foodMenu.yuXiangRouSi();
}
}
package Restaurant;
//中餐厨师
// 实现菜单上的菜
// 厨师是接口的实现者。
public class ChinaCooker implements FoodMenu{
// 西红柿炒蛋
public void shiZiChaoJiDan(){
System.out.println("中餐师傅做的西红柿炒鸡蛋,东北口味!");
}
// 鱼香肉丝
public void yuXiangRouSi(){
System.out.println("中餐师傅做的鱼香肉丝,东北口味!");
}
}
package Restaurant;
//西餐厨师
// 实现菜单上的菜
// 厨师是接口的实现者。
public class AmericaCooker implements FoodMenu{
// 西红柿炒蛋
public void shiZiChaoJiDan(){
System.out.println("西餐师傅做的西红柿炒鸡蛋!");
}
// 鱼香肉丝
public void yuXiangRouSi(){
System.out.println("西餐师傅做的鱼香肉丝!");
}
}
package Restaurant;
public class Test{
public static void main(String[] args){
// 创建厨师对象
FoodMenu cooker1 = new ChinaCooker();
// 创建顾客对象
Customer customer = new Customer(cooker1);
// 顾客点菜
customer.order();
}
}
注意:
类和类之间的关系:
is a(继承)、has a(关联)、like a(实现)
is a:
“Cat is a Animal”(猫是一个动物)
凡是满足is a的表示“继承关系“
A extends B
has a:
I has a Pen(我有一支笔)
凡是能够满足has a的表示“关联关系”
关联关系通常以“属性”的形式存在。
A{
B b;
}
like a:
cooker like a FoodMenu(厨师像一个菜单一样)
凡是能够满足like a 关系的表示“实现关系”
实现关系通常是:类实现接口。
A implements B
(三)抽象类和接口的区别
抽象类是半抽象的,接口是完全抽象的。
抽象类中构造方法,接口中没有构造方法。
接口和接口之间支持多继承,类和类之间只能是单继承。
一个类可以同时实现多个接口,一个抽象类只能继承一个类。(单继承)
接口中只允许出现常量和抽象方法。



