从零开始学Java-面向对象进阶(三)

2026-08-21 00:24:47

在前篇文章中,我们学习了面向对象的多态、包、final关键字、权限修饰符、以及抽象类等,下面我们接着来学习第三部分吧!

目录

接口

内部类

接口

我们来看这张图片:

左边的三种动物是不是都有共同体系吃饭和喝水呀,但是狗类和青蛙类会游泳,而兔子类不会游泳呀,那怎么办,写在父类又不行,那写在狗类和青蛙类里面嘛?好像也可以是吧!但是这样子会出现不必要的重复性了。那这个时候是不是就可以定义一种游泳规则,谁要用的时候谁用呢?其实可以的,当我们需要给多个类定义规则的时候,这个时候就要用到接口了。这种规则我们可以叫他是接口。我们接着往下看:

什么是接口呢?

接口:就是一种规则,更侧重于行为。

下面我们就来学习一下什么是接口吧:

如何定义接口以及使用接口

接口的定义和使用

接口用关键字interface来定义

public interface 接口名{}

接口不能实例化,接口不能创建他的对象

接口和类之间是实现关系,通过implements关键字表示

public class 类名 implements 接口名{}

接口的子类(实现类)

要么重写接口中所有抽象方法

要么是抽象类

接口的注意点

接口和类的实现关系,可以单实现,也可以多实现

public class 类名 implements 接口名1,接口名2{};

实现类还可以在继承一个类的同时实现多个接口

public class 类名 extends 父类 implements 接口名1,接口名2{};

下面我们来做个案例练练手吧:

编写带有接口和抽象类的标准JavaBean类 接口

青蛙 属性:名字,年龄 行为:吃虫子,蛙泳

狗 属性:名字,年龄 行为:吃骨头,狗刨

兔子 属性:名字,年龄 行为:吃胡萝卜

定义一个共同属性的动物父类:

public abstract class Animal {

private String name;

private int age;

// 吃行为

public abstract void eat();

}

我们来定义一个游泳的接口,注意了,这个时候就不是创建class文件了,而是implements接口

public interface Swim{

// 定义游泳规则的接口

public abstract void swim();

}

定义一个青蛙类并继承父类和实现游泳的接口:

public class Frog extends Animal implements Swim{

// 重写青蛙吃的行为

@Override

public void eat() {

System.out.println(getAge() + "岁的" + getName() + "在吃虫子");

}

// 重写青蛙动作行为

@Override

public void swim() {

System.out.println(",他还会游泳!");

}

}

定义一个狗类并继承父类和实现游泳的接口:

public class Dog extends Animal implements Swim{

// 重写狗吃的行为

@Override

public void eat() {

System.out.println(getAge() + "岁的" + getName() + "在吃骨头");

}

// 重写狗动作行为

@Override

public void swim() {

System.out.println(",他还会狗刨!");

}

}

定义一个兔子类并继承父类:

public class Rabbit extends Animal {

@Override

public void eat() {

System.out.println(getAge() + "岁的" + getName() + "在吃胡萝卜");

}

}

然后在测试类中为他们创建对象并调用父类和接口方法:

Frog f = new Frog("小青蛙",1);

f.eat(); // 调用父类吃的方法

f.swim(); // 调用接口游泳的方法

Dog d = new Dog("小泰迪",4);

d.eat(); // 调用父类吃的方法

d.swim(); // 调用接口游泳的方法

Rabbit r = new Rabbit("小白兔",2);

r.eat(); // 调用父类吃的方法

我们来运行看一下吧:

接口里面成员的特点

说到成员呢,还是我们的那三个老常客了:成员变量、构造方法、成员方法。下面我们先来说说成员变量吧:

成员变量

只能是常量

默认修饰符:public static final

public:表示公共的,在什么地方都可以去调用接口方法

static:表示静态的,可以方便的进行调用(接口名.常量名称)

final:表示是常量,会用final修饰

构造方法

没有(接口中没有构造方法,因为在接口不能创建对象,接口当中也不需要给子类去赋值)

成员方法

只能是抽象方法

默认修饰符:public abstract

接口和类之间的关系

类和类的关系

继承关系,只能单继承,不能多继承,但是可以多层继承。

类和接口的关系

实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。(如果出现了重名的接口方法,只需要重写一次就可以了)。

接口和接口的关系

继承关系,可以单继承,也可以多继承。

下面就有个问题了,如果我过几天要在接口增加新的方法,那是不是都要在每个类里面去重写呀,是的,在接口添加新的方法都要在继承的类中去重写。那我不重写行不行呀,不行,代码会报错的。

其实是有办法的,我们来看一下:

下面我们就来说一下在接口中添加方法的解决方式吧:

接口中新增默认方法

允许在接口中定义默认方法,需要使用关键字default修饰。

作用:解决接口升级的问题

接口中默认方法的定义格式:

格式:public default 返回值类型 方法名(参数列表){}

格式:public default void show(){}

接口中默认方法的注意事项:

默认方法不是抽象方法,所以不强制被重写,但是如果被重写,重写的时候就需要去掉default关键字

public可以省略,default不能省略

如果实现多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写。

下面我们来实操一下吧:

在接口里面定义两个方法:

定义一个类来继承接口:

我明明在接口定义两个方法呀,为什么我只重写了一个method方法就不会被报错呢?其实是不是因为method方法加了强制abstract是被强制重写的方法呀,show方法是加了default的默认方法,所以说什么时候使用在重写就可以啦。但是我们需要重写的时候需要注意去掉default关键字。我们来看重写方法:

@Override

public void show() {

System.out.println("重写show方法");

}

好啦,新增默认方法就学到这里,下面我们来学一下新增静态方法吧

接口中新增静态方法

允许在接口中定义静态方法,需要使用关键字static修饰。

接口中静态方法的定义格式:

格式:public static 返回值类型 方法名(参数列表){}

格式:public static void show(){}

接口中默认方法的注意事项:

静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。

public可以省略,static不能省略

怎么样,是不是和默认方法是一样的呢!下面我们来学习一下私有方法吧:

接口中新增私有方法

接口中私有方法的定义格式:

默认的私有方法

格式1:private 返回值类型 方法名(参数列表){}

格式1:private void show(){}

静态的私有方法

格式2:private static 返回值类型 方法名(参数列表){}

格式2:private static void show(){}

我们来看段代码吧:

public default void show1(){

System.out.println("show3方法开始执行!");

show3();

}

public static void show2(){

System.out.println("show4方法开始执行!");

show4();

}

// 默认的私有方法,给默认方法服务的

private void show3(){

System.out.println("忽略一百行代码");

}

//静态的私有方法,给静态方法服务的

private static void show4(){

System.out.println("忽略一百行代码");

}

下面我们来给接口做个总结吧:

JDK7以前:接口中只能定义抽象方法。

JDK8:接口中可以定义有方法体的方法。(静态、默认)

JDK9:接口中可以定义私有方法

私有方法分两种:普通的私有方法和静态的私有方法

接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以啦。

当一个方法参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口的多态。

适配器

什么是适配器呢?我们往下看:

假设我们接口有10个方法,而我又只要使用第五个方法怎么办呢?

public interface Inter {

// 定义10个方法

public abstract void method1();

public abstract void method2();

public abstract void method3();

public abstract void method4();

public abstract void method5();

public abstract void method6();

public abstract void method7();

public abstract void method8();

public abstract void method9();

public abstract void method10();

}

像我们之前是不是要重写接口里面的10个方法呀,那样不是很麻烦嘛?

这个时候我们就可以创建他们的第三者:适配器

看这个时候就不会出现全部方法了,需要用到哪个方法在重写即可。下面我们来总结一下吧:

当一个接口中抽象方法过多,但是我只要使用其他一部分的时候,就可以去设计一个适配器。

书写步骤:

编写中间类以XXXAdapter起名,XXX(接口名),实现对应的接口。

对接口中的抽象方法进行空实现。

让真正的实现类继承中间类,并重写需要调用的方法。

为了避免其他类创建适配器的对象,中间的适配器类用abstract进行修饰

好啦,接口我们就学习到这里,下面我们来学习一下内部类吧:

内部类

内部类介绍

内部类是类的五大成员:属性、方法、构造方法、代码块、内部类

什么是内部类:在一个类的里面,在定义一个类,这就叫内部类。

我们来看一段案例了解一下吧:

需求:写一个javabean类描述汽车

属性:汽车的品牌,车龄,颜色,发动机的品牌,使用年限

public class Cat { // 外部类

String carName; // 汽车颜色

int carAge; // 汽车年龄

String carColor; // 汽车颜色

class Engine{ // 内部类

String engineName; // 发动机品牌

int engineAge; // 使用年限

}

}

在定义内部类的时候我们要遵守这些规则:

内部类表示的事物是外部类的一部分。

内部类单独出现没有任何意义。

内部类访问的特点:

内部类可以直接访问外部类的成员,包括私有。

外部类要访问内部类的成员,必须要创建对象,否则访问不了。

内部类分类

内部类分为:成员内部类、静态内部类、局部内部类、匿名内部类其中前面三个我们只需要了解就可以啦,而匿名内部类需要重点掌握的。下面我们一起来学一下:

成员内部类

成员内部类书写:

写在成员位置的,属于外部类的成员

成员内部类可以被一些修饰符修饰:private、默认、protected、public、static等...

获取成员内部类对象:

方法1:当成员内部类被private修饰时。在外部类中编写方法,对外提供内部类对象。

方法2:直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

成员内部类获取外部类的成员变量:外部类名.变量名

我们来看下面这段代码吧:

Outer.this.a是什么意思呢?Outer.this.表示的就是外部类的地址值,直接在外部类的地址值中去找变量a的值。

静态内部类

什么是静态内部类呢?我们还是来看这段代码:

public class Cat { // 外部类

String carName; // 汽车颜色

int carAge; // 汽车年龄

String carColor; // 汽车颜色

static class Engine{ // 静态内部类

String engineName; // 发动机品牌

int engineAge; // 使用年限

}

}

被static修饰的类叫做静态内部类。静态内部类我们需要注意一点:

静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。

创建静态内部类对象的格式:外部类名.内部类名. 对象名 = new 外部类名.内部类名();

调用静态方法的格式:外部类名.内部类名.方法名();

调用非静态方法的格式:先创建对象,用对象名调用。

我们来看一下调用的两种格式吧:

局部内部类

局部内部类:

将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。

外界是无法使用的,需要在方法内部创建对象并调用。

该类可以直接访问外部类的成员,也可以访问方法内的局部变量。

下面我们来看段代码:

由于这个不常用,使所以只需要知道就可以啦!下面我们来学最最最最最最重要的匿名内部类吧!

匿名内部类

匿名内部类定义格式:

new 类名或者接口名(){

重写方法;

}

例:

new Inter(){

public void show(){

}

}

匿名内部类整体格式包含了三部分:继承\实现、方法重写、创建对象。

下面我们来实操一下吧:

定义一个接口,接口里面编写游泳方法:

public interface Swim {

public abstract void swim();

}

我们用之前的方法来对接口的方法重写看一下格式:

public class Student implements Swim{ // Swim接口名

@Override

public void swim() { // 重写接口游泳的方法

System.out.println("重写了游泳的方法!");

}

};

用匿名内部类编写接口的重写方法格式:

// 编写匿名内部类的代码

new Swim(){ // Swim接口名

@Override

public void swim() { // 重写接口游泳的方法

System.out.println("重写了游泳的方法!");

}

};

怎么样,是不是匿名方法的反而更简单一点呢?那是什么意思呢?我们来看下面这张图片:

下面我们再来看一个匿名类的:

定义一个类:

public abstract class Outer {

public abstract void eat();

}

用匿名重写类里面的eat方法:

// 编写匿名内部类的代码

new Outer(){ // Outer类名

@Override

public void eat() { // 重写类吃的方法

System.out.println("重写了吃的方法!");

}

};

怎么样,是不是也是和接口是一样的呢,下面我们也来说一下是什么意思吧:

那怎么使用呢?我们来看一下:

首先定义一个方法:

public static void method(Outer a){

a.eat();

}

我们想要调用method方法,之前是不是要先写一个子类继承父类,然后在创建子类对象传递给method方法呀。那好麻烦呀,我们来看一下内部类的使用方式:

// 在测试类中调用下面的method方法

method(

// 直接改写eat方法

new Outer() {

@Override

public void eat() {

System.out.println("狗在吃骨头");

}

}

);

怎么样,是不是用匿名内部类就简单多啦!是的。下面我们再来扩展一些知识吧:

Outer d = new Outer() {

@Override

public void eat() {

System.out.println("狗在吃骨头");

}

};

d.eat();

怎么样,是不是和上面一样的呀!这个整体我们可以理解为Outer类的继承对象,继承的多态。

下面我们来总结一下吧:

什么是匿名内部类:

匿名内部类本质上就是隐藏了名字的内部类。可以写在成员位置,也可以写在局部位置。

匿名内部类的格式:

格式的细节:

包含了继承或者实现、方法重写、创建对象。整体就是一个类的子类对象或者接口的实现类对象。

使用场景:

当方法的参数是接口或者类时,(以接口为例:可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码)

好啦,这篇文章就学到这里,有什么不懂的可以在评论区评论一起探讨哟,我们下期不见不散!!!

==最后非常感谢您的阅读,也希望能得到您的反馈 ==