[Java入门]抽象类和接口
[Java入门]抽象类和接口
- 1. 抽象类
- 1.1 抽象类的概念
- 1.2 抽象类语法
- 1.3 抽象类特性
- 1.4 抽象类的作用
- 2. 接口
- 2.1 接口的概念
- 2.2 语法规则
- 2.3 接口使用
- 2.4 接口特性
- 2.5 实现多个接口
- 2.6 接口间的继承
- 2.7 抽象类和接口的区别
- 3. Object类
- 3.1 获取对象信息
- 3.2 对象比较equals方法
1. 抽象类
1.1 抽象类的概念
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 比如:
【说明】
- 从图中可知,矩形类、圆形类、三角形类都属于图形类,所以三者都与Shape类是继承关系
- 由于Shape不是具体的图形,该类虽然存在draw方法,但无法画出具体图形,即draw方法无法具体实现
- 因此Shape类中没有足够的信息来描绘自己的对象,所以Shape类就能被设计为抽象类
【说明】
- 再来看这个图,很明显可以看出Student类、Teacher类、Waiter类都与Person类是继承关系
- 由于Person不是具体的人,所以无法确定职业,即使它存在career方法,那也无法具体的实现该方法
- 因此Person类中没有足够的信息来描绘自己的对象,career方法也无法具体的实现,所以该类就可以被设计为抽象类
从上面两个例子中,两个父类Shape类和Person类 的 draw方法和career方法是无法具体实现的,只有它们的子类才会有具体的实现,像这种没有具体实现的方法,通常设计为抽象方法(abstract method),而包含抽象方法的类称为 抽象类(abstract class)。
1.2 抽象类语法
在Java中,如果一个类被 abstract 修饰,那么这个类被称为抽象类,而在抽象类中,存在被 abstract 修饰的方法,那么这个方法就被称为 抽象方法。
//抽象类: 在普通类前用 abstract 修饰
//抽象类也是类 也可以定义成员变量 和 方法
public abstract class Person {public String name;public int age;public void chat(){System.out.println("在聊天!");}//抽象方法:在普通方法前用 abstract 修饰//抽象方法 可以没有方法体public abstract void career();
}
注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
1.3 抽象类特性
- 抽象类被 abstract 修饰
- 抽象类跟普通类一样,也可以包含普通方法和属性,还有构造方法
- 抽象方法被 abstract 修饰,且必须在抽象类中
- 抽象方法不能被 final 和 static 修饰,因为要被重写
- private 和 abstract 不能同时存在
看报错提示:private abstract void career();//Illegal combination of modifiers: 'abstract' and 'private'public static abstract void career();//Illegal combination of modifiers: 'abstract' and 'static'public final abstract void career();//Illegal combination of modifiers: 'final' and 'abstract'
- 抽象类被设计出来就是为了被继承
- 普通类继承抽象类 必须重写抽象类中所有的抽象方法,否则就将普通类改为抽象类
abstract class Person {public abstract void career();
}class Student extends Person{//@Overridepublic void career() {System.out.println("我的职业是学生!");}
}
- 当普通类B不想重写时,就将普通类B变成抽象类B,那么当普通类C想继承抽象类B时,必须要重写抽象类B 和 抽象类A 的所有抽象方法
abstract class Person {public abstract void career();
}public abstract class Student extends Person{@Overridepublic abstract void career();public abstract void homework();
}class StudentOfPrimary extends Student{@Overridepublic void career() {System.out.println("我是小学生!");}@Overridepublic void homework() {System.out.println("我在写小学生作业!");}
}
- 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
- 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
1.4 抽象类的作用
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验.
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.
充分利用编译器的校验, 在实际开发中是非常有意义的.
2. 接口
2.1 接口的概念
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
2.2 语法规则
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。
public interface Drawable {public static final int a = 0;//接口中定义变量时 public static final 是固定存在的 可以不写public abstract void draw();//接口中定义方法时 public abstract 是固定存在的 可以不写
}
注意:接口中的方法都是抽象方法
提示:
- 创建接口时, 接口的命名一般以大写字母 I 开头.
- 接口的命名一般使用 “形容词” 词性的单词.
- 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
2.3 接口使用
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。 模板:
public class 类名 implements 接口名{
}
比如:
public interface Drawable {public abstract void draw();
}public abstract class Shape {public abstract void getArea();
}public class Circle extends Shape implements Drawable{@Overridepublic void getArea() {System.out.println("圆的面积");}@Overridepublic void draw() {System.out.println("圆");}
}public class Main {public static void main(String[] args) {Circle circle = new Circle();circle.getArea();circle.draw();}
}
2.4 接口特性
- 接口中定义变量时 public static final 是固定存在的 可以不写
- 接口中定义方法时 public abstract 是固定存在的 可以不写
- 接口中的方法都是抽象方法
- 接口中的方法不能在接口中实现,只能在实现该接口的类中实现
- 若接口中的方法被static修饰,那么可以在接口中有具体的实现
public interface Drawable {public static final int a = 0;public abstract void draw();static void draw1(){System.out.println("画画");}
}
//Circle类 实现 Drawable接口
public class Circle implements Drawable{@Overridepublic void draw() {System.out.println("圆");}
}
- 接口是引用类型,但不能通过 new 来实例化
Drawable drawable = new Drawable();
报错:'Drawable' is abstract; cannot be instantiated
- 在重写接口中的方法时,必须加上public
- 接口中不能有构造方法和代码块
2.5 实现多个接口
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。举例说明:
一个动物类
public abstract class Animal {private String name;private int age;public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public abstract void makeSound();
}两个接口
public interface Swimming {public void swim();
}public interface Trainbale {public void train();
}两个子类:
猫
public class Cat extends Animal implements Trainbale{public Cat(String name, int age) {super(name, age);}@Overridepublic void makeSound() {System.out.println(getName() + "喵喵喵!");}@Overridepublic void train() {System.out.println(getName() + "可被训练!");}
}狗(实现了两个接口)
public class Dog extends Animal implements Trainbale,Swimming{public Dog(String name, int age) {super(name, age);}@Overridepublic void makeSound() {System.out.println(getName() + "旺旺旺");}@Overridepublic void train() {System.out.println(getName() + "可被训练!");}@Overridepublic void swim() {System.out.println(getName() + "会游泳!");}
}
从代码可以看出,子类狗继承了Animal类实现了两个接口,这是根据它的特性实现的。这是Java中最常见的的用法:一个类继承一个父类实现多个接口
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性 .
- 猫是一种动物,具有可被训练的特性.
- 狗是一种动物,具有会游泳、可被训练的特性.
这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
2.6 接口间的继承
在Java中,类与类之间可以继承,类与接口之间可以实现,那接口与接口之间呢?是可以继承的,且可以多继承,可以借助接口达到多继承的目的!
public interface Flying {public void fly();
}
public interface Trainbale {public void train();
}
//接口与接口之间也是通过 extends 继承,并非用 implements
//若继承的接口中已写有方法,那么本接口可以不用添加特有方法
public interface Merge extends Flying,Trainbale{
}//Bird类 实现了 Merge接口 那么就要重写train、fly方法
public class Bird implements Merge{@Overridepublic void train() {System.out.println("可被训练!");}@Overridepublic void fly() {System.out.println("会飞!");}
}
接口之间的继承相当于把多个接口合并起来。
2.7 抽象类和接口的区别
抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别(重要!!! 常见面试题).
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
抽象类存在的意义是为了让编译器更好的校验, 像 前面提到的Animal、Shape等 这样的类我们并不会直接使用, 而是使用它的子类.万一不小心创建了 Animal 或 Shape的实例, 编译器会及时提醒我们.
3. Object类
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象(无论是Java自带的还是自己设计创建的)都可以使用Object的引用进行接收。
public class A {
}public class Test {public static void main(String[] args) {A a = new A(); //自己设计的类String str = new String("123");//Java中已存在的类func(a);func(str);}
public static void func(Object o){System.out.println(o);}
}//打印结果
demo5.A@119d7047
123
所以在开发之中,Object类是参数的最高统一类型。
但是Object类也存在一些定义好的方法。如下:
对于 Object 类中已存在的所有方法都需要熟悉掌握。
我们先来熟悉 toString()、 equals() 两个方法
3.1 获取对象信息
如果要打印对象中的内容,可以直接重写Object类中的toString()方法,内容可以根据自己的要求修改。
这是Object类中的toString()方法具体内容,可以看出是默认打印地址
3.2 对象比较equals方法
在Java中,== 进行比较时:
a.如果 == 左右两侧是基本类型变量,比较的是变量中值是否相同
b.如果 == 左右两侧是引用类型变量,比较的是引用变量地址是否相同
c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:
class Person{private String name ;private int age ;public Person(String name, int age) {this.age = age ;this.name = name ;}
}public class Test {public static void main(String[] args) {Person p1 = new Person("gaobo", 20) ;Person p2 = new Person("gaobo", 20) ;int a = 10;int b = 10;System.out.println(a == b); // 输出trueSystem.out.println(p1 == p2); // 输出false 因为是两个对象,是两个不同的地址System.out.println(p1.equals(p2)); // 输出false}
}
未重写的equals() 方法的内容就是 比较两对象地址是否相同。
把Person类的equals方法重写后,然后比较:
class Person{private String name ;private int age ;public Person(String name, int age) {this.age = age ;this.name = name ;}public boolean equals(Object obj) {if (obj == null) {return false ;}if(this == obj) {return true ;}// 若不是Person类对象 返回 falseif (!(obj instanceof Person)) {return false ;}Person person = (Person) obj ; // 向下转型,比较属性值return this.name.equals(person.name) && this.age==person.age ;}
}//结果
System.out.println(a == b); // 输出true
System.out.println(p1 == p2); // 输出false
System.out.println(p1.equals(p2)); // 输出true
结论:比较对象中内容是否相同的时候,一定要重写equals方法。