第四章、Java面向对象(八)
本人也是刚入门Java语言,可能会有一些地方出现错误,描述的不对。如果发现不对的地方请及时指出,好对其进行修改。这样不仅可以让我学到东西,也可以让其他刚入门的朋友学习更正确的内容。
所有内容仅供参考。不具有完全的准确性!
注:关于Java的所有内容都会参考到尚硅谷在网上公开的学习视频及其提供的PPT
一、接口的使用
interface: 接口
接口和类是并列的两个结构
推荐:https://blog.csdn.net/qq_19782019/article/details/80259836
(一)为什么要有接口
通过继承我们了解到,一个类最多只能继承一个父类,这一点在我们写代码时是有很大限制的。有时多个类中的属性和方法都是我们想要的,但又由于只能继承一个类,我们就需要再把其他没能继承的类中的属性和方法重新再写一遍。
这时接口就出现了,接口说我可以实现多继承(严格来说不叫继承,而是可以实现多个类)。通过接口我们就可以很好的接口上述情况。
例String类:
implements 后边都是实现的接口,接口和接口之间逗号隔开
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
.....
}
(二)什么是接口
-
官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
-
我的解释:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成(抽象方法和常量值定义的集合)。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.7及之前)
-
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
(三)如何使用接口
定义Java类的语法格式:先写extends,后写implements
例:
public class C extends A implements B{....}
语法格式:
public interface A{
...
}
将class替换为interface,public可以省略
- JDK 7 及 以前: 只能够定义全局常量和抽象方法
全局常量:public static final.(可以省略不写,仍是常量)
抽象方法:public abstract - JDK 8: 除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
- 接口中不可以定义构造器:意味着接口不可以实例化
- 类需要通过implements的方式实现接口
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有全部重写接口中的抽象方法,则此实现类仍是一个抽象类 - 一个类可以实现多个接口 —>弥补了Java单继承的局限性
class A extends B implements C,D - 接口和接口之间可以继承,且可以多继承
注意:接口中的属性都是public修饰的,抽象方法也是public
定义一个接口类:
interface Flyable{
//全局常量
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
//抽象方法
public abstract void fly();
//省略了public abstract修饰,仍是一个抽象方法
void stop();
//在 JDK 7 之前 接口中只有抽象方法
// public void eat(){
//
// }
//接口中不可以定义构造器Interfaces cannot have constructors
// public Flyable(){
//
// }
}
实现接口
若不想实现接口中的所有抽象方法,那该实现类就还是一个抽象类,需要abstract修饰
abstract class Birde implements Flyable{
@Override
public void fly() {
// TODO Auto-generated method stub
}
}
//实现多个接口,逗号隔开
//需要实现所有接口中的抽象方法,否则该实现类就还是一个抽象类,需要abstract修饰
//若还需要继承其他类,则继承写在接口之前
class Bullet extends Object implements Flyable,Attactable{
@Override
public void attact() {
// TODO Auto-generated method stub
}
@Override
public void fly() {
// TODO Auto-generated method stub
}
@Override
public void stop() {
// TODO Auto-generated method stub
}
}
接口的多继承性
注意:继承的类也必须都是接口
public interface JDKNewFeature extends USB,NetWork{
...
}
通过接口实例化
我们不能直接去实例化一个接口,因为接口中的方法都是抽象的,是没有方法体的。但是,我们可以使用接口类型的引用指向一个实现了该接口的对象,并且可以调用这个接口中的方法。
public interface JDKNewFeature extends USB,NetWork{
//静态方法
public static void method1(){
System.out.println("JDK8以后,接口中可以写静态方法");
}
//默认方法
public default void method2(){
System.out.println("JDK8以后,接口中可以写默认方法");
}
default void method3(){
System.out.println("JDK8以后,接口中可以写默认方法,接口若没有些权限修饰符,则权限修饰符为public而不是缺省");
}
}
class Person implements JDKNewFeature{
@Override
public void start() {
}
@Override
public void stop() {
}
@Override
public void brose() {
}
}
class Student{
@Test
public void test(){
JDKNewFeature jdkNF = new Person();
}
}
Java SE1.8 :除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
- 接口中定义的静态方法只能通过接口调用(接口名.静态方法)
- 通过实现类的对象可以调用接口类中的默认方法
- 如果实现类重写了接口中的默认方法,那么调用时就是重写后的方法
- 如果子类(实现类)继承的父类和实现的接口中声明了同名同参数的方法,在没有重写的情况下,调用的是父类中的方法
- 如果实现类实现了多个接口,而这多个接口中出现了同名同参数的默认方法,那么在实现类没有重写该默认方法时调用直接报错(编译不通过) —>接口冲突
- 当重写了默认方法以后,若还想调用接口中的方法: 接口名.super.默认方法
(四)接口和抽象类的区别
相同点: 抽象类和接口都不能实例化对象,但是可以定义抽象类和接口类型的引用;
继承抽象类和实现接口都要对其中的抽象方法全部实现;
不同点:
- 接口比抽象类更加抽象,抽象类中可以定义构造方法(构造器),可以有抽象方法和具体方法,而接口中的方法全部都是抽象方法(接口中也可以没有方法,这样的接口称为空接口)
- 抽象类中的方法可以是private,protected,public,接口全部都是public
- 抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量(final)
- 有抽象方法的类必须声明为抽象类,而抽象方法未必要有抽象方法
二、内部类
-
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
-
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
-
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完
整的名称。
Inner class的名字不能与包含它的外部类类名相同; -
分类:
成员内部类(静态和非静态)
局部内部类(方法内、代码块内、构造器内)
匿名内部类 -
成员内部类
- 作为内部类的成员:
① 可以调用外部类的结构
② 可以被static修饰,但此时就不能再使用外层类的非static的成员变量
③ 可以被四种权限修饰符修饰 - 作为一个类:
① 类内可以定义属性、方法、构造器等(也可以继续定义内部类)
② 可以被final修饰,表示此类不能被继承,反之可以被继承
③ 可以被abstract修饰,因此可以被其它的内部类继承
- 关于内部类的注意事项
- 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
- 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
例
public class InnerClassTest {
public static void main(String[] args) {
//创建静态内部类实例
Person.Bird bird = new Person.Bird();
//创建非静态的内部类实例
// Person.Dog dog = new Person.Dog();//编译不通过,非静态的需要通过外部类的对象调用
Person p = new Person();
Person.Dog dog = p.new Dog();
}
}
class Person{
String name;
public void eat(){
System.out.println("人");
}
//非静态成员内部类
class Dog{
String name;
int age;
//内部类的方法
public void show(){
System.out.println("这一个条狗");
eat();//非静态内部类可以直接调用
// this.eat(); //此时是编译报错,this是Dog的对象
Person.this.eat();//可以通过外部类名.this.外部类的方法调用
}
public void eat(){
}
}
//静态成员内部类
static class Bird{
}
//外部类的方法
public void method(){
//局部内部类
class AA{
}
}
//非静态代码块
{
//局部内部类
class BB{
}
}
//构造器
public Person(){
//局部内部类
class CC{
}
}
}
- 局部内部类的特点
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
- 局部内部类可以使用外部类的成员,包括私有的。
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
- 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
- 局部内部类不能使用static修饰,因此也不能包含静态成员