在前篇文章中,我们学习了面向对象的多态、包、final关键字、权限修饰符、以及抽象类等,下面我们接着来学习第三部分吧!
目录
- 接口
- 内部类
接口
我们来看这张图片:
左边的三种动物是不是都有共同体系吃饭和喝水呀,但是狗类和青蛙类会游泳,而兔子类不会游泳呀,那怎么办,写在父类又不行,那写在狗类和青蛙类里面嘛?好像也可以是吧!但是这样子会出现不必要的重复性了。那这个时候是不是就可以定义一种游泳规则,谁要用的时候谁用呢?其实可以的,当我们需要给多个类定义规则的时候,这个时候就要用到接口了。这种规则我们可以叫他是接口。我们接着往下看:
- 什么是接口呢?
- 接口:就是一种规则,更侧重于行为。
下面我们就来学习一下什么是接口吧:
如何定义接口以及使用接口
-
接口的定义和使用
- 接口用关键字interface来定义
public interface 接口名{}
- 接口不能实例化,接口不能创建他的对象
- 接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
- 接口的子类(实现类)
- 要么重写接口中所有抽象方法
- 要么是抽象类
- 接口用关键字interface来定义
-
接口的注意点
-
接口和类的实现关系,可以单实现,也可以多实现
- 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(){}
- 格式1:
- 静态的私有方法
- 格式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类的继承对象,继承的多态。
下面我们来总结一下吧:
-
什么是匿名内部类:
- 匿名内部类本质上就是隐藏了名字的内部类。可以写在成员位置,也可以写在局部位置。
-
匿名内部类的格式:
-
格式的细节:
- 包含了继承或者实现、方法重写、创建对象。整体就是一个类的子类对象或者接口的实现类对象。
-
使用场景:
- 当方法的参数是接口或者类时,(以接口为例:可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码)
好啦,这篇文章就学到这里,有什么不懂的可以在评论区评论一起探讨哟,我们下期不见不散!!!
==最后非常感谢您的阅读,也希望能得到您的反馈 ==