欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA学习笔记_面向对象

程序员文章站 2024-03-12 22:55:26
...

JAVA学习笔记_面向对象

1. 面向对象

1.1 面向对象的思想

  • 概述

    Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

  • 面向过程:强调步骤。

  • 面向对象:强调对象,这里的对象就是洗衣机

  • 三大特征:

    • 继承
    • 封装
    • 多态

**实例:**输出数组

package cn.itcast.day06.demo01;

import java.util.Arrays; // 导入Arrays类

public class Demo01PrintArray {

    public static void main(String[] args) {
        int[] array = { 10, 20, 30, 40, 50, 60 };

        // 要求打印格式为:[10, 20, 30, 40, 50]
        // 使用面向过程,每一个步骤细节都要亲力亲为。
        System.out.print("[");
        for (int i = 0; i < array.length; i++) {
            if (i == array.length - 1) { // 如果是最后一个元素
                System.out.println(array[i] + "]");
            } else { // 如果不是最后一个元素
                System.out.print(array[i] + ", ");
            }
        }
        System.out. println("==============");
        // 使用面向对象
        // 找一个JDK给我们提供好的Arrays类,
        // 其中有一个toString方法,直接就能把数组变成想要的格式的字符串
        System.out.println(Arrays.toString(array));
    }
}
  • 特点: 特点面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装继承多态

1.2 类和对象

  • 什么是类

    • 类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
    • 现实中,描述一类事物:
    • 属性:就是该事物的状态信息。
    • 行为:就是该事物能够做什么。
  • 什么是对象:

    ​ 一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。

  • 类与对象的关系

    • 类是对一类事物的描述,是抽象的
    • 对象是一类事物的实例,是具体的
    • 类是对象的模板,对象是类的实体。

1.3 类的定义

  • 属性:事物的状态信息。
  • 行为:事物能够做什么。
public class ClassName {
    //成员变量
    //成员方法
}
  • 定义类:就是定义类的成员,包括成员变量和成员方法。
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
  • 成员方法:把static去掉

定义一个Student类:

public class Student {

    // 成员变量
    String name; // 姓名
    int age;     // 姓名

    // 成员方法
    public void eat() {
        System.out.println("吃饭饭!");
    }

    public void sleep() {
        System.out.println("睡觉觉!");
    }

    public void study() {
        System.out.println("学习!");
    }
}

成员变量(属性):
String name; // 姓名
int age; // 年龄
成员方法(行为):
public void eat() {} // 吃饭
public void sleep() {} // 睡觉
public void study() {} // 学习

注意事项:

  1. 成员变量是直接定义在类当中的,在方法外边。
  2. 成员方法不要写static关键字。

1.4 类的对象的创建和使用

  • 创建的一个Student的对象

public class Student {
    public static void main(String[] args) {
		// 1. 导包。
		import XXX.XXX.Student;
        // 2. 创建,格式:
        // 类名称 对象名 = new 类名称();

        Student stu = new Student();  // 根据Student类,创建了一个名为stu的对象

        // 3. 使用其中的成员变量,格式:
        // 对象名.成员变量名
        System.out.println(stu.name); // null
        System.out.println(stu.age);  // 0
        System.out.println("=============");

        // 改变对象当中的成员变量数值内容
        // 将右侧的字符串,赋值交给stu对象当中的name成员变量
        stu.name = "小明";
        stu.age = 18;
        System.out.println(stu.name); // 小明
        System.out.println(stu.age); // 18
        System.out.println("=============");

        // 4. 使用对象的成员方法,格式:
        // 对象名.成员方法名()
        stu.eat();
        stu.sleep();
        stu.study();
    }

}
  1. 导包:也就是指出需要使用的类,在什么位置。
    import 包名称.类名称;
    对于和当前类属于同一个包的情况,可以省略导包语句不写。
  2. 创建,格式:
    类名称 对象名 = new 类名称();
    Student stu = new Student();
  3. 使用,分为两种情况:
    使用成员变量:对象名.成员变量名
    使用成员方法:对象名.成员方法名(参数)

注意事项:
如果成员变量没有进行赋值,那么将会有一个默认值,规则和数组一样。

1.5成员变量和局部变量的区别

  • 在类中的位置不一样

  • 成员变量:类中,方法外

  • 局部变量:方法中或者方法声明上(形式参数)

  • 作用的范围不一样

    • 成员变量:类中
    • 局部变量:方法中
  • **初始化值的不同 **

    • 成员变量:有默认值
    • 局部变量:没有默认值。必须先定义,赋值,最后使用
  • **在内存中的位置不同 **

    • 成员变量:堆内存
    • 局部变量:栈内存
  • **生命周期不同 **

    • 成员变量:随着对象的创建而存在,随着对象的消失而消失
    • 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失

1.6 内部类

内部类就是一个类里面还包含另一个类

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

1.6.1 内部类的格式

  • 成员内部类

    格式:

    修饰符 class 外部类名称 {
        修饰符 class 内部类名称 {
            // ...
        }
        // ...
    }
    

    注意:内用外,随意访问;外用内,需要内部类对象。

  • 局部内部类

    如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

    格式:

    修饰符 class 外部类名称 {
        修饰符 返回值类型 外部类方法名称(参数列表) {
            class 局部内部类名称 {
                // ...
            }
        }
    }
    
  • 类的权限修饰符(能否使用)

    修饰符 外部类 成员内部类 局部内部类
    public Y Y N
    protected Y Y N
    default N Y N
    private N Y N

1.6.2 内部类使用

成员内部类的使用
  • 两种方法

    • 间接方式:

      在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。

    • 直接方法:

      外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
      
      //或 如果内部类使用Static修饰则可使用这种方式去创建内部类的对象
      外部类名称.内部类名称 对象名 = new 外部类名称.内部类名称();
      

    例如:

    public class Car {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void methodCar() {
            System.out.println("外部Car的方法");
            // 调用内部类的方法
            Engine engine = new Engine();
            engine.methodEngine();
        }
    
        // 定义一个内部类
        public static class Engine {
    
            // 内部类的方法
            public void methodEngine() {
                System.out.println("发动机点火");
            }
    
        }
    }
    
局部内部类的使用

局部内部类,只有当前所属的方法才能使用它。

注意:

局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

原因:

  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中。
  3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

1.6.3 * 匿名内部类

匿名内部类是为了省略接口的实现类,直接使用new来重写接口的抽象方法。

格式:

接口名称 对象名 = new 接口名称() {
    // 覆盖重写所有抽象方法
};
public class Demo03AnonymityClass {
    public static void main(String[] args) {
        MyInterface myInterface = new MyInterface() {

            @Override
            public void method() {
                System.out.println("匿名类重写抽象方法");
            }
        };
        myInterface.method();
    }
}

注意事项:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
  2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
  3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】

2. 面向对象特征——封装

2.1封装的概念

面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

2.2 封装的关键字——private

  • private的含义:
  1. private是一个权限修饰符,代表最小权限。

  2. 可以修饰成 员变量和成员方法。

  3. 被private修饰后的成员变量和成员方法,只在本类中才能访问。

  • private的使用格式

    private 数据类型 变量名 ;
    
  • **使用 private 修饰成员变量,代码如下 **

    public class Student {
        private String name;
        private int age;
    }
    

    注: 使用了private本类可以直接访问,但是在类外无法直接进行访问

  • 使用setXxx方法、getXxx访问间接访问private的成员变量

    public class Student {
        private String name;
        private int age;
        public void setName(String n) {
            name = n;
        } 
        public String getName() {
        	return name;
        } 
        public void setAge(int a) {
            age = a;
        } 
        public int getAge() {
        	return age;
        }
    }
    

    注:必须叫setXxx或者是getXxx命名规则。
    对于Getter来说,不能有参数,返回值类型和成员变量对应;
    对于Setter来说,不能有返回值,参数类型和成员变量对应。

  • 对于boolean类型的private成员变量的Getter方法

    public class Person {
    
        private boolean male; //性别
        public void setMale(boolean male) {
            this.male = male;
        }
        public boolean isMale() {
            return male;
        }
    }
    

2.3封装优化——this关键字

  • this 的使用:
public class Person {

    String name; // 我自己的名字

    // 参数name是对方的名字
    // 成员变量name是自己的名字
    public void sayHello(String name) {
        System.out.println(name + ",你好。我是" + this.name);
        System.out.println(this);
    }
}

当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。
如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量名
通过谁调用的方法,谁就是this

2.4 封装优化——构造方法

构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。

  • 格式:

    public 类名称(参数类型 参数名称) {
        方法体
    }
    
    package cn.fate.java_learn.basic.Class;
    
    import javax.xml.namespace.QName;
    
    public class Student {
    
        private String name;
        private int age;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public Student() {
            System.out.println("无参数构造方法");
        }
    
        public Student(String name, int age) {
            System.out.println("带参数的构造方法");
            this.name = name;
            this.age = age;
        }
    
        public void show() {
            System.out.println("我叫:" + name + ",年龄:" + age);
        }
    
    }
    

    注:

    1. 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样。
    2. 构造方法不要写返回值类型、void都不写。
    3. 构造方法不能return一个具体的返回值。
    4. 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。public Student() {}
    5. 一旦编写了至少一个构造方法,那么编译器将不再赠送。
    6. 构造方法也是可以进行重载的。 重载:方法名称相同,参数列表不同。

2.5 标准代码——JavaBean

一个标准的类通常要拥有下面四个组成部分:

  1. 所有的成员变量都要使用private关键字修饰
  2. 为每一个成员变量编写一对儿Getter/Setter方法
  3. 编写一个无参数的构造方法
  4. 编写一个全参数的构造方法

这样标准的类也叫做Java Bean

  • 编写一个标准的类
package cn.fate.java_learn.basic.Class;

public class StandardStudent {
    private String name;
    private int age;
    private boolean Male;

    public StandardStudent() {
    }

    public StandardStudent(String name, int age, boolean male) {
        this.name = name;
        this.age = age;
        Male = male;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isMale() {
        return Male;
    }

    public void setMale(boolean male) {
        Male = male;
    }
}

注 : IDEA 可以使用快捷键Alt+Insert快速生成Getter、Setter 及构造方法

2.6 匿名对象

创建对象的标准格式:
类名称 对象名 = new 类名称();

匿名对象就是只有右边的对象,没有左边的名字和赋值运算符。
new 类名称();
注意事项:匿名对象只能使用唯一的一次,下次再用不得不再创建一个新对象。
使用建议:如果确定有一个对象只需要使用唯一的一次,就可以用匿名对象。

import java.util.Scanner;

public class DemoAnonymous {
    public static void main(String[] args) {

        methodParam(new Scanner(System.in));
        Scanner sc = methodReturn();
        int num = sc.nextInt();  
        System.out.println("Input num is" + num);
        methodParam(new Scannner); // 匿名对象充当参数
    }
    public static void methodParam(Scanner sc){
        int num = sc.nextInt(); 
        System.out.printf("Input num is % d\n", num);
    }

    public static Scanner methodReturn(){
        return new Scanner(System.in); // 返回匿名对象
    }

}

3. 面向对象特征——继承

3.1 继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

3.2 类的继承格式

在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

class 父类 {
}
 
class 子类 extends 父类 {
}

3.3 继承中的成员变量的关系

父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:

直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找。

间接通过成员方法访问成员变量:
该方法属于谁,就优先用谁,没有则向上找。

当出现三种情况时的调用关系:

  • 局部变量: 直接写成员变量名
  • 本类的成员变量: this.成员变量名
  • 父类的成员变量: super.成员变量名

注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的

// 父类
public class Fu {
    
    int num = 10;
    
}

// 子类
public class Zi extends Fu {

    int num = 20;

    public void method() {
        int num = 30;
        System.out.println(num);       // 30,局部变量
        System.out.println(this.num);  // 20,本类的成员变量
        System.out.println(super.num); // 10,父类的成员变量
    }
}


3.4 重写(Override)

重写(Override)

概念:在继承关系当中,方法的名称一样,参数列表也一样。

重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。
重载(Overload):方法的名称一样,参数列表【不一样】。

方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。

方法覆盖重写的注意事项:

  1. 必须保证父子类之间方法的名称相同,参数列表也相同。
    @Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
    这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。

  2. 子类方法的返回值必须**【小于等于】**父类方法的返回值范围。
    小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String**就是Object的子类。

  3. 子类方法的权限必须**【大于等于】**父类方法的权限修饰符。
    小扩展提示:public > protected > (default) > private
    备注:(default)不是关键字default,而是什么都不写,留空。

3.5 构造方法的访问特点

继承关系中,父子类构造方法的访问特点:

  1. 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
  2. 子类构造可以通过super关键字来调用父类重载构造。
  3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造

总结:
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。

3.6 super关键字的三种用法:

  1. 在子类的成员方法中,访问父类的成员变量。
  2. 在子类的成员方法中,访问父类的成员方法。
  3. 在子类的构造方法中,访问父类的构造方法。

3.7 this关键字的三种用法:

  1. 在本类的成员方法中,访问本类的成员变量。
  2. 在本类的成员方法中,访问本类的另一个成员方法。
  3. 在本类的构造方法中,访问本类的另一个构造方法。

在第三种用法当中要注意:

  • this(…)调用也必须是构造方法的第一个语句,唯一一个。
  • superthis两种构造调用,不能同时使用。

总结:

super关键字用来访问父类内容,而this关键字用来访问本类内容

3.8 Java 继承的关系

JAVA学习笔记_面向对象

3.9 抽象类

抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。

如何使用抽象类和抽象方法:

  1. 不能直接创建new抽象类对象。
  2. 必须用一个子类来继承抽象父类。
  3. 子类必须覆盖重写抽象父类当中所有的抽象方法。
    覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
  4. 创建子类对象进行使用。
// 父类
public abstract class Animal {
    // 定义一个抽象方法
    public abstract void eat();

    // 定义一个普通的方法
    public void method() {
        System.out.printf("普通方法");
    }
}


//子类
public class Cat extends Animal {
    public void eat(){
        System.out.println("Cat eats fish");
    }
}

// Main
public class DemoAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
    }
}


注意事项:

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

    • 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    • 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    • 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,以满足特殊类的需求。
  4. 抽象类的子类(除子类也为抽象类),必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。

    • 理解:假设不重写所有抽象方法,则类中可能包含抽象方法没有意义,无法被使用。

练习:红包随机分割。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;

public class RandomSplit {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList();
        list = divived(100, 20);
        System.out.println(list);
    }


    public static ArrayList<Integer> divived(double money, int n) {
	 
        /*
         * 红包拆分方法
         * @param money  被拆分的总金额 (单位元)
         * @param n      被拆分的红包个数
         * @return 拆分后的每个红包金额数组
         */
        
        // 创建一个长度的红包数组
        ArrayList<Integer> redList = new ArrayList<>();

        int moneyFen = (int) (money * 100);
        // 判断红包的总金额
        if (money > 200) {
            System.out.println("单个红包不能超过200元");
            return redList; // 返回空的红包集合
        }
        if (moneyFen < n || moneyFen < 1) {
            System.out.println("被拆分的总金额不能小于0.01元");
            return redList; // 返回空的红包集合
        }
        // 创建一个n长的数组
        Integer[] array = new Integer[n];

        //1. 给每个包分0.01元,确保每个包有钱
        Arrays.fill(array, 1);
        moneyFen -= n;   //总金额减去已分配的0.01 * n 元

        //2. 进行随机分配
        Random rand = new Random();

        while (moneyFen > 1) {
            int moneyCut = rand.nextInt(moneyFen);  // 随机选择分配的包
            int i = rand.nextInt(n);  // 随机选择分配的包
            array[i] += moneyCut; // 分配给每一个包并且转换为元
            moneyFen -= (int) moneyCut;
        }
        //把最后的包放到最后一个包里面
        array[n - 1] += moneyFen ;
        // 把分好的包放到redList里面
        Collections.addAll(redList, array);

        return redList;
    }
}

4. 面向对象特征——多态

4.1 多态的概念

多态是同一个行为具有多个不同表现形式或形态的能力。

多态存在的三个必要条件:

  • 继承
  • 重写
  • 父类引用指向子类对象

4.2 多态的个格式

格式:

父类名称 对象名 = new 子类名称();
// 或者:
接口名称 对象名 = new 实现类名称();

4.3 多态中的成员变量和成员方法

  • 访问成员变量的两种方式:

    1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
    2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
  • 成员方法的访问规则是:
    new的是谁,就优先用谁,没有则向上找。

成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
口诀: 编译看左边,运行看右边。

4.4 对象的转型

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型 。

    向上转型是安全的,等于从小范围转向大的范围。

    格式:

    父类类型 变量名 = new 子类类型();
    // 比如
    Animal a = new Cat();
    
  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。

    注: 向下转型的对象,之前的必须是通过向上转型过来的,才能向下转回原来的对象。

    格式:

    子类类型 变量名 = (子类类型) 父类变量名;
    // 比如:
    Cat c =(Cat) a;
    

    向下转型的异常:

    ​ 如果错误的转换为其他对象,编译时会出现 ClassCastException的报错。为了确定向下转型的正确性,我们可以使用关键字 instanceof ,进行校验。

    校验格式:

    变量名 instanceof 数据类型
    如果变量属于该数据类型,返回true。
    如果变量不属于该数据类型,返回false

    实例:

    public class Test {
        public static void main(String[] args) {
            // 向上转型
            Animal a = new Cat();
            a.eat(); // 调用的是 Cat 的 eat
            // 向下转型
            if (a instanceof Cat) {
                Cat c = (Cat) a;
                c.catchMouse(); // 调用的是 Cat 的 catchMouse
            } else if (a instanceof Dog) {
                Dog d = (Dog) a;
                d.watchHouse(); // 调用的是 Dog 的 watchHouse
            }
        }
    }
    

5. Java中的接口

接口就是多个类的公共规范。

接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

如果是Java 7,那么接口中可以包含的内容有:

  1. 常量
  2. 抽象方法

如果是Java 8,还可以额外包含有:
3. 默认方法
4. 静态方法

如果是Java 9,还可以额外包含有:

  1. 私有方法

4.1 定义接口的格式

public interface 接口名称 [extends 其他的接口名] {
       // 声明变量
       // 抽象方法
}

接口有以下特性:

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
  • 接口中的方法都是公有的。

4.2 接口的使用步骤

  1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
    格式:
public class 实现类名称 implements 接口名称 {
    // ...
}
  1. 接口的实现类必须覆盖重写(override)接口中所有的抽象方法。

  2. 创建实现类的对象,进行使用。

4.3 接口中抽象(abstract)方法

在任何版本的Java中,接口都能定义抽象方法。
格式:

public abstract 返回值类型 方法名称(参数列表)//方法体;

注意事项:

  1. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
  2. public abstract关键字修饰符可以选择性地省略。
  3. 方法的三要素,可以随意定义。
  4. 实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
public interface MyInterfaceAbstract {

    // 这是一个抽象方法
    public abstract void methodAbs1();

    // 这也是抽象方法
    abstract void methodAbs2();

    // 这也是抽象方法
    public void methodAbs3();

    // 这也是抽象方法
    void methodAbs4();
}

4.4 接口中的默认(default)方法

从Java 7开始,接口当中允许定义Default方法。

接口中使用的默认方法,可以解决接口升级的问题。默认方法,实现类可以不重写。

格式:

public default 返回值类型 方法名称(参数列表) {
    方法体
}

注:

  1. 关键字public 修饰符可以选择性地省略。
  2. 接口的默认方法,可以通过接口实现类对象,直接调用。
  3. 接口的默认方法,也可以被接口实现类进行覆盖重写。
    public interface MyInterfaceDefault {
        // 抽象方法
        public abstract void methodAbstract();

        // 默认方法
        public default void methodDefault() {
            System.out.println("默认方法");
        }
}

4.5 接口中的私有(private)方法

从Java 9开始,接口当中允许定义私有方法。

  1. 普通私有方法,解决多个默认方法之间重复代码问题

格式:

private 返回值类型 方法名称(参数列表) {
 方法体
}
  1. 静态私有方法,解决多个静态方法之间重复代码问题
    格式:
private static 返回值类型 方法名称(参数列表) {
 方法体
}
public interface MyInterfacePrivate {
    public default void methodDefault1() {
        System.out.println("默认方法1");
        methodDefaultCommon();
    }

    public default void methodDefault2() {
        System.out.println("默认方法2");
        methodDefaultCommon();
    }

    public static void methodStatic1() {
        System.out.println("静态方法1");
        methodStaticCommon();
    }

   public static void methodStatic2() {
        System.out.println("静态方法2");
        methodStaticCommon();
    }

    private static void methodStaticCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }

    private void methodDefaultCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}

4.6 接口中的静态(static)方法

从Java 8开始,接口当中允许定义静态方法。
格式:

public static 返回值类型 方法名称(参数列表) {
    方法体
}

注 :

  1. 关键字public 修饰符可以选择性地省略。
  2. 静态方法的调用通过接口名称直接调用,而不是使用实现类的对象进行调用,因为静态和对象没关系。
public interface MyInterfaceStatic {
    public static void methodStatic() {
        System.out.println("这是接口的静态方法!");
    }
}

4.7 接口中的常量的定义和使用

接口当中也可以定义**“成员变量”**,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:

public static final 数据类型 常量名称 = 数据值;

// public static final 都可以省略

数据类型 常量名称 = 数据值;

备注:
一旦使用final关键字进行修饰,说明不可改变。

备注:
一旦使用final关键字进行修饰,说明不可改变。

注意事项:

  1. 接口当中的常量,可以省略public static final,注意:其实都可以省略。
  2. 接口当中的常量,必须进行赋值;不能不赋值。
  3. 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
  4. 调用常量时使用接口名称去调用。

4.8 接口中方法的总结

  1. 成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;

注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。

注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。

  1. 接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);

注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。

注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。

  1. 从Java 8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表) { 方法体 }

注意:默认方法也可以被覆盖重写

注意:默认方法也可以被覆盖重写

  1. 从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表) { 方法体 }

注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法

注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法

  1. 从Java 9开始,接口里允许定义私有很乏,格式:
    普通私有方法:
private 返回值类型 方法名称(参数列表) { 方法体 }

静态私有方法:

private static 返回值类型 方法名称(参数列表) { 方法体 }

注意:private的方法只有接口自己才能调用,不能被实现类或别人使用。

4.9 类的继承和实现接口的问题

类的继承关系是单继承的,但类可同时实现多个接口。

使用接口时的注意事项:

  1. 接口是没有者构造方法的。
  2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
  3. 实现类所实现的多个接口当中,存在重复的抽象方法,只需要覆盖重写一次即可。
  4. 实现类没有覆盖重写所有接口当中的所有抽象方法,实现类就必须是一个抽象类。
  5. 实现类实现的多个接口当中,存在重复的默认方法,实现类一定要对冲突的默认方法进行覆盖重写。
  6. 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,优先用父类当中的方法。

4.10 接口之间的多继承

在Java中,类的多继承是不合法,但接口允许多继承。

在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。

格式:

public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {
    // 覆盖重写所有抽象方法
}

注意事项:

  1. 多个父接口当中的抽象方法如果重复,由于实现类中必重写抽象方法,说以没影响。
  2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写。

综合案例

  • 问题:

    笔记本电脑(laptop)通常具备使用USB设备的功能。在生产时,笔记本都预留了可以插入USB设备的USB接口,但具体是什么USB设备,笔记本厂商并不关心,只要符合USB规格的设备都可以。定义USB接口,具备最基本的开启功能和关闭功能。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,实现USB接口,否则鼠标和键盘的生产出来也无法使用。

  • 分析:

    • USB接口,包含开启功能、关闭功能
    • 笔记本类,包含运行功能、关机功能、使用USB设备功能
    • 鼠标类,要实现USB接口,并具备点击的方法
    • 键盘类,要实现USB接口,具备敲击的方法
    // Usb接口
    
    public interface Usb {
    
        public abstract void on();
    
        public abstract void off();
    }
    
    
    // Mouse 鼠标类
    public class Mouse implements Usb {
        @Override
        public void on() {
            System.out.println("开启鼠标");
        }
    
        @Override
        public void off() {
            System.out.println("关闭鼠标");
        }
    
        public void click() {
            System.out.println("鼠标点击");
        }
    }
    
    
    
    // 键盘类
    public class Keboard implements Usb {
    
        @Override
        public void on() {
            System.out.println("开启键盘");
        }
    
        @Override
        public void off() {
            System.out.println("关闭键盘");
        }
    
        public void inputs() {
            System.out.println("键盘输出");
        }
    }
    
    // 电脑类
    public class Computer {
        public void powerOn() {
            System.out.println("开启电脑");
        }
    
        public void powerOff() {
            System.out.println("关闭电脑");
        }
    
        public void useDevice(Usb usb) {
            // 开启Usb设备
            usb.on();
    
            // 使用Usb设备
            if (usb instanceof Keboard) {
                // 向下转换为键盘
                Keboard keboard = (Keboard) usb;
                keboard.inputs();
            } else if (usb instanceof Mouse) {
                // 向下转换为鼠标
                Mouse mouse = (Mouse) usb;
                mouse.click();
            }
    
            // 关闭usb设备
            usb.off();
        }
    }
    
    // 测试
    
    public class DemoMain {
        public static void main(String[] args) {
            Computer computer = new Computer();
            // 开启电脑
            computer.powerOn();
    
            // 向上转换出一个use
            Usb usb = new Mouse();
            computer.useDevice(usb);
    
    
            // 直接传入一个usb设备
            Keboard keboard = new Keboard();
            computer.useDevice(keboard);  // 掉用过程: 参数传过去先向上转换, 再向下转换。
            // 关闭电脑
            computer.powerOff();
        }
    }