本篇文章将对抽象类和接口相关知识进行讲解
先来看下面的代码:
class Shape {
public void draw() {
System.out.println("画");
}
}
class Cycle extends Shape {
public void draw() {
System.out.println("圆形");
}
}
public class Test2 {
public static void main(String[] args) {
Shape shape = new Cycle();
shape.draw();
}
}
在上述代码中,Shape类中的draw方法,因为有动态绑定而始终没有被实现,且Shape类不能描绘某个具体的对象,我们可以将这样不能描绘某个具体对象的类设为抽象类,具体操作是:使用abstract关键字修饰这个类,此时这个类被称为抽象类,可以将抽象类中被重写的方法也用abstract修饰,此时这个方法就叫抽象方法,抽象方法不能有具体实现
abstract class Shape {
public abstract void draw();
}
class Cycle extends Shape {
public void draw() {
System.out.println("圆形");
}
}
1. 抽象方法所在类必须是抽象类,抽象类不一定包含抽象方法
2. 抽象类中可以有和非抽象成员变量和成员方法(和普通类一样)
3. 抽象类不能被实例化
abstract class Shape {
public abstract void draw();
}
class Cycle extends Shape {
public void draw() {
System.out.println("圆形");
}
}
public class Test2 {
public static void main(String[] args) {
Shape shape = new Shape();
//编译报错:抽象类不能被实例化
}
}
4. 抽象类必须被继承,普通类继承抽象类后,必须重写抽象类中的抽象方法,(如果抽象类中没有抽象方法则可以不用重写)如果继承抽象类的也是抽象类则不用重写
5. 抽象方法不能被private、final和static修饰
6. 在多层继承关系下,子类必须继承所有父类的抽象方法
abstract class A {
public abstract void print();
}
abstract class B extends A {
public abstract void test();
}
class C extends B {
//必须重写print和test方法,否则编译报错
public void print() {
}
public void test() {
}
}
7. 抽象类也有构造方法,只不过是被子类调用并帮助其初始化(因为抽象类不能实例化对象)
abstract class A {
public abstract void print();
public A() {
}
}
class B extends A {
public B() {
super();
}
public void print() {
}
}
创建一个接口需要用到interface关键字
interface 接口名 {
}
类和接口之间的关系是实现,即类实现接口,具体操作是使用implements关键字实现
类名 implements 接口名 {
}
下面举一个实例:
interface IUsb {
void openDevice(); //默认为抽象方法
void closeDevice();
}
class Mouse implements IUsb {
public void openDevice() {
System.out.println("打开鼠标");
}
public void closeDevice() {
System.out.println("关闭鼠标");
}
public void clink() {
System.out.println("点击鼠标");
}
}
class Keyboard implements IUsb {
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
public void inPut() {
System.out.println("键盘输入");
}
}
class Computer {
public void powerOn() {
System.out.println("开机");
}
public void powerOff() {
System.out.println("关机");
}
public void useDevice(IUsb iUsb) {
iUsb.openDevice(); //这里实现了多态
if (iUsb instanceof Mouse) { //判断iUsb是否引用Mouse对象
Mouse mouse = new Mouse(); //向下转型
mouse.clink();
} else if (iUsb instanceof Keyboard) {
Keyboard keyboard = new Keyboard(); //向下转型
keyboard.inPut();
}
iUsb.closeDevice();
}
}
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
computer.useDevice(new Mouse()); //向上转型
computer.useDevice(new Keyboard()); //向上转型
computer.powerOff();
}
}
1. 接口中定义的成员变量默认被public static final修饰,定义的成员方法默认被public abstract修饰
2. 接口中的方法如果没有具体实现则默认为抽象方法
3. 接口中的方法一般不能具体实现(没有方法块)如果要具体实现,应由default或static修饰
interface IUsb {
default void method() {
System.out.println("实现method方法");
}
static void method1() {
System.out.println("实现method1方法");
}
}
4. 接口不能被实例化
interface IShape {
}
public class Test1 {
public static void main(String[] args) {
IShape iShape = new IShape();
//编译报错:接口不能被实例化对象
}
}
5. 一个类可以继承一个普通类/抽象类,同时还可以实现接口
interface A {
}
class B {
}
class C extends B implements A { //必须先继承在实现
}
6. 一个类实现一个接口后,必须重写其内部的方法
在Java中不支持多继承,但可以实现多接口,即一个类可以实现多个接口
class 类名 implements 接口名,接口名, ... {
}
实际上多接口就是为了解决Java中不能多继承的问题,下面举一个例子:
interface IRun {
void run();
}
interface ISwim {
void swim();
}
class Dog implements IRun, ISwim {
//实现这两个接口后,要重写接口中的抽象方法
public void run() {
System.out.println("跑");
}
public void swim() {
System.out.println("游");
}
}
将run()和swim()两个方法分别写到两个接口中而不是直接将这两个方法写到一个Animal类中让Dog类来继承的原因是:将来如果有其它类比如一个Bird类要继承Animal类,那就会直接将Animal类中所有的方法全部继承,但是“鸟不能游泳”所以这样写不符合逻辑
Java中类和类之间不支持多继承,但接口和接口之间是支持多继承的
interface IJump {
void jump();
}
interface ISing {
void sing();
}
interface IRap extends IJump, ISing { }
class Student implements IRap{
@Override
public void jump() {}
@Override
public void sing() {}
}
接口间的继承的意义就是将多个接口合并为一个接口
object类是所有类的父类
class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
public static void main(String[] args) {
public static void func(Object obj) {
System.out.println(obj); //这里会调用Object类中toString中的方法,打印obj所指对象的地址
}
public static void main(String[] args) {
func(new Person("Sans")); //向上转型
}
}
调用Object类中toString方法的源码顺序:
public void println(Object x) {
String s = String.valueOf(x); //调用valueof方法
synchronized (this) {
print(s);
newLine();
}
}
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString(); //若obj指向某个对象则调用toString方法
}
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
} //返回对象的地址
equals方法是Object类中的成员方法,在此之前先来回顾一下“==”
如果==左右两侧是基本类型变量,比较的是变量中值是否相同
如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同
equals的源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
下面举一个实例:
class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
func(new Person("Sans"));
Person person1 = new Person("Sans");
Person person2 = new Person("Sans");
System.out.println(person1.equals(person2));
//由于person1和person2指向不同对象,所以它们的地址不同
}
如果要使用equals方法比较对象的内容,则需要在Person类中重写equals方法,
class Person {
public String name;
public Person(String name) {
this.name = name;
}
public boolean equals(Object obj) {
Person per = (Person) obj;
return per.name.equals(this.name);
//name是String类型,String类继承了object类同时也重写了equals方法
//所以这里是调用了String类的方法
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("Sans");
Person person2 = new Person("Sans");
System.out.println(person1.equals(person2));
//Person类继承了object类并重写了equals方法,所以这里
//发生动态绑定,调用子类即Person类的equals方法
}