面向对象(下)
一、抽象类
(一)抽象类概述
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。
(二)抽象类特点
1.抽象类和抽象方法必须用abstract关键字修饰
抽象类格式:abstract class 类名 {}
抽象方法格式:public abstract void eat();
2.抽象方法没有方法实现体,只给出一个方法声明
抽象类,就是被abstract所修饰的类,父类将所有子类的共性功能向上抽取后,他并不知道,每个子类对这个共性功能的具体实现,所以没有必要在父类中给出共性功能的具体实现,而是给出声明即可,所谓给出功能的声明,就是将此功能抽象出来,然后强制子类必须重写该抽象的功能。
3.抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
4.抽象类中可以有构造方法,但抽象类不能进行实例化,那么要构造方法有什么作用呢?
用于子类访问父类数据时的初始化
5.抽象类不能直接实例化,那么抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
6.抽象类的子类
要么是抽象类
要么重写抽象类中的所有抽象方法
案例演示1
public class MyTest {
public static void main(String[] args) {
//new Animal();报错:抽象类不能直接创建对象
//我们可以采用多态间接的去实例化抽象类
Animal an=new Cat();
an.eat();
an.sleep();
an.show();
}
}
//一旦一个类中,有了抽象方法,此类必须为抽象类
//一个抽象类中也可以没有抽象方法
//抽象类中既可以有抽象方法,也可以有非抽象方法,抽象方法,强制子类重写,非抽象方法,可以让子类继承下去用
abstract class Animal {
public Animal() {
System.out.println("父类的构造方法执行了");
}
//abstract关键字:抽象的,可以修饰类,修饰方法
public abstract void eat(); //抽象方法,此方法没有方法实现体
public abstract void sleep();//抽象方法,此方法没有方法实现体
//抽象类中既可以有抽象方法,也可以有非抽象方法
public void show(){
System.out.println("这是父类的一个非抽象方法");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫爱吃鱼");
}
@Override
public void sleep() {
System.out.println("猫白天睡觉");
}
}
案例演示2
抽象类的子类,要么重写父类中所有的抽象方法,要么自己也是一个抽象类
public class MyTest {
public static void main(String[] args) {
}
}
abstract class A {
public abstract void a();
public abstract void aa();
}
abstract class B extends A{
//抽象类的子类,如果选择不重写抽象类的抽象方法,那么它必须也是一个抽象类
public abstract void b();
}
class C extends B{
//C类必须重写B类和A类的所有抽象方法
@Override
public void a() {
}
@Override
public void aa() {
}
@Override
public void b() {
}
}
问1:一个类如果没有抽象方法,可不可以定义为抽象类 ? 如果可以,有什么意义?
答:可以,定义为抽象类,外界就不能直接创建该类的对象。
问2:abstract不能和哪些关键字共存?
答:
- private:如果用private修饰抽象方法,该方法不能被重写,矛盾。
- final:如果用final修饰抽象类,该类不能被继承,矛盾;如果用final修饰抽象方法,该方法不能被重写,矛盾。
- static:如果用static修饰抽象方法,方法变为静态方法,静态方法不存在重写。
(三)抽象类的成员特点
1.成员变量:既可以是变量,也可以是常量。
2.构造方法:有。
用于子类访问父类数据的初始化。
3.成员方法:既可以是抽象的,也可以是非抽象的。
- 抽象方法: 强制要求子类重写。
- 非抽象方法: 子类继承,提高代码复用性。
案例演示
public class MyTest {
public static void main(String[] args) {
}
}
abstract class A{
//抽象类中的成员变量 即可定义变量也可以定义常量
int num=100;
public static final int num2=1000;
//抽象类中有构造方法,用来让子类创建对象时初始化父类数据
public A(int num) {
this.num = num;
}
}
二、接口
(一)接口概述
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
接口被实现体现的是:”like a”的关系。 接口中定义的是该继承体系的扩展功能。
(二)接口特点
1.接口用关键字interface表示
格式:interface 接口名 {}
2.类实现接口用implements表示
格式:class 类名 implements 接口名 {}
3.接口不能实例化
那么,接口如何实例化呢?
按照多态的方式来实例化。
4.接口的子类
a:可以是抽象类。但是意义不大。
b:可以是具体类。要重写接口中的所有抽象方法。(推荐方案)
总而言之,除非实现接口的类是抽象类,否则该类要实现接口中的所有方法。
案例演示
public class MyTest {
public static void main(String[] args) {
Cat cat = new Cat();
Animal an = cat;
an.eat();
an=new Dog();
an.eat();
// CalcInterface 是猫类的一个父接口,猫类也是父接口的一个子类
CalcInterface c = cat;
c.calc();
//多态
c= new Dog();
c.calc();
//接口不能直接实例化
}
}
abstract class Animal {
public abstract void eat();
}
interface CalcInterface {
public abstract void calc();
}
class Cat extends Animal implements CalcInterface{
//继承Animal类的同时实现CalcInterface接口
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void calc() {
System.out.println("猫经过不断地努力学习,会做算术了");
}
}
class Dog extends Animal implements CalcInterface{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void calc() {
System.out.println("狗经过不断地努力学习,会做算术了");
}
}
(三)接口的成员特点
1.成员变量:只能是常量,并且是静态的。
默认(缺省)修饰符:public static final
建议:自己手动给出。
2.构造方法:接口没有构造方法。
3.成员方法:没有非抽象方法,只能是抽象方法。
默认(缺省)修饰符:public abstract
建议:自己手动给出。
案例演示
public class MyTest {
public static void main(String[] args) {
//接口中的成员变量全是静态的公共常量
}
}
interface A{
//接口中成员变量前面有默认的修饰符:public static final
int a=100;
public static final int b=1000;
//接口中成员方法前面存在默认修饰符:public abstract
void test();
public abstract void show();
}
(四)接口的默认方法
在JDK1.8之后,接口中提供了用default修饰的默认方法,可以给出方法的具体实现,子类可以继承下去用
默认方法不是抽象方法,可以被继承,不强制重写,也可以重写
案例演示
public class MyTest{
public static void main(String[] args) {
C c= new C();
c.test1();
c.test22();
c.show1();
c.show11();
A a=c;
a.show1();
a.test1();
a.test2();
B b=c;
b.show11();
b.test11();
b.test22();
}
}
interface A{
void show1();
//JDK1.8之后接口中可以定义默认方法,可以有方法的具体实现
default void test1(){
//前面缺省一个public,默认方法不是抽象方法
System.out.println("test1");
}
default void test2() {
System.out.println("test2");
}
}
interface B {
void show11();
default void test11() {
System.out.println("test11");
}
default void test22() {
System.out.println("test22");
}
}
class C implements A,B{
@Override
public void show1() {
System.out.println("show1");
}
@Override
public void show11() {
System.out.println("show11");
}
//接口的默认方法不强制重写,可以直接继承用,当然也可以重写
@Override
public void test22() {
System.out.println("Test22");
}
}
三、区别与联系
(一)类与类、类与接口、接口与接口的关系
1.类与类:
继承关系。只能单继承,不能多继承,可以多层继承。
2.类与接口:
实现关系。可以单实现,也可以多实现,并且还可以在继承一个类的同时实现多个接口。
3.接口与接口:
继承关系。可以单继承,也可以多继承。
案例演示
public class MyTest {
public static void main(String[] args) {
}
}
class Fu{
public void test(){
};
}
interface A{
void a();
}
interface B{
void b();
}
class Zi extends Fu implements A,B{
//可以继承一个类的同时实现多个接口
@Override
public void a() {
}
@Override
public void b() {
}
}
interface C{
void c();
}
interface D{
void d();
void dd();
}
interface E extends C,D{
//接口和接口是继