抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了面向对象的一些基础知识,介绍了类和对象的概念,以及如何使用接口和导入包。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8

1 简介

面向对象编程是一种对现实世界建立计算机模型的一种编程方法。简称OOP(Object Oriented Programming)。

面向对象是一种编程思想,是一种思考问题的思维方式。

在现实世界中,当提到动物这个概念,实际上它是一个抽象的概念,而具体动物是指老虎,狮子,大象等等。

在对应的计算机模型中,把动物这种抽象的概念称之为Class,也就是类。而那些具体的对象称之为实例,并且用不同变量标识不同的实例。

面向过程与面向对象都是编写程序的一种思维方式:

  • 面向过程:遇到一件事时,先做什么,再做什么,然后一步步实现的过程。
  • 面向对象:遇到一件事时,先思考有哪些类和对象,然后思考类和对象里有哪些属性和方法,然后分析类和类之间的关系,最后一群对象合力能把事就好。

2 类

2.1 定义

类是一组具有相同特征的对象的集合,是一个抽象的概念。类中定义了这一类对象所具有的属性和方法。

定义类:

java
1
2
3
4
5
6
7
8
9
10
11
public class 类名 {
属性类型 属性名 = 属性值;
...
public void 方法名() {
方法体;
}
...
public static void main(String[] args) {
主函数方法体;
}
}

对象是一个类具体的某一个实例,是看得见摸得着的东西。世间存在的一切都可以看做是对象。

定义对象:

java
1
类名 对象名 = new 类名();

2.2 成员

2.2.1 属性

属性称为成员变量,一般来讲不用赋值,因为有默认值,显式赋值会导致所有由此类创建对象都是此值。

默认值:

  • Boolean类型成员变量的默认值是false。
  • Int类型成员变量的默认值是0。
  • Double类型成员变量的默认值是0.0。
  • String类型成员变量的默认值是null。
  • Char类型成员变量的默认值是\u0000。
2.2.1.1 局部变量

定义:方法中,语句块中,方法参数中定义的变量。

作用域:方法内部,语句块内部。其他方法,其他代码块不能访问。

生命周期:方法或者语句块调用开始到方法或者语句块调用结束。

共享性:方法内部,语句块内部共享。对于多个线程来讲,变量初始化到自己的方法区中,主内存不存在该变量,每个线程在访问方法时使用各自的局部变量,所以线程之间不共享,也就不存在线程安全问题。

访问修饰符:不能被修饰,因为局部变量的作用域本身就比最小的访问权限还要小,所以即便修饰了也不具备相应的访问权限。

默认值:没有默认值,被声明后必须经过初始化才可以使用。

值传递:局部变量解决了方法内部,语句块内部行之间的变量传递问题。如果没有局部变量,不知道行之间怎么传递变量。

2.2.1.2 实例变量

定义:类中定义的变量。又称为成员属性,是描述对象状态的数据,是类中很重要的组成成分。

作用域:同一个类的实例对象内部,不同的实例对象直接不共享。

生命周期:伴随整个类实例始终,变量在创建类实例时被创建,在实例对象被销毁的时候销毁。

共享性:在同一个类实例内部共享,不同实例对象不共享。对于多线程来讲,变量被初始化到主内存中,每个线程拷贝变量到工作内存中进行操作,如果使用的是同一个实例对象的变量,线程之间共享一个主内存变量,存在线程安全问题。

访问修饰符:可以被修饰。

默认值:有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定。

值传递:成员变量解决了类实例各方法之间的变量传递。如果没有成员变量,方法之间变量传递只能靠参数。

2.2.1.3 全局变量

定义:全局变量在Java中也可以叫静态变量,通过static关键字修饰。全局变量也称为类变量、静态变量。

作用域:整个类,在每个实例对象之间共享。

生命周期:伴随整个类始终,变量在第一次使用该类时被创建。

共享性:在整个类共享,并且在不同实例对象之间共享。对于多线程来讲,变量被初始化到主内存中,每个线程拷贝变量到工作内存中进行操作,线程之间共享一个主内存变量,存在线程安全问题。

访问修饰符:可以被修饰。

默认值:有默认值,和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。

值传递:全局变量解决了类之间的变量传递。如果没有全局变量,类之间变量只能靠构造实例的时候相互传递。

2.2.2 方法

2.2.2.1 定义

方法就是用来完成解决某件事情或实现某个功能的办法。

语法:

java
1
2
3
4
修饰符 返回值类型 方法名(参数类型 参数名, ... , 参数类型 参数名) {
执行语句;
return 返回值;
}

说明:

  • 修饰符:方法的修饰符比较多,有对访问权限进行限定的,有静态修饰符static,还有最终修饰符final等,这些修饰符在后面的学习过程中会逐步介绍。
  • 返回值类型:用于限定方法返回值的数据类型,返回给调用方,返回类型可以是任意类型。根据是否需要返回值:
    • 如果需要返回值,则需要写一个返回类型,并且return后的返回值需要和返回类型一致。
    • 如果不需要返回值,则写void,方法结束的时候不需要写return和返回值。
  • 方法名:功能块的名字,命名规则和变量命名规则一样。
  • 参数类型:参数类型用于限定调用方法时传入参数的数据类型,可以为任意类型,参数类型和返回类型没有任何关系。
  • 参数名:参数名是一个变量,用于接收调用方法时传入的数据。
  • 参数:参数类型和参数名组成参数,参数可以有多个,中间用逗号隔开。
  • 执行语句:里面可以写任何逻辑语句,表示的是此方法的具体功能。
  • 返回值:返回值是被return语句返回的值,该值会返回给调用者。
2.2.2.2 调用

方法的调用可以通过如下方式调用:

java
1
类型 变量名 = 对象实例.方法名(参数);

根据不同情况调用方法:

调用情况 调用语法
是否有参数 有参数:类型 变量名 = 对象实例.方法名(参数, 参数);
无参数:类型 变量名 = 对象实例.方法名();
是否有返回值 有返回值:类型 变量名 = 对象实例.方法名(参数);
无返回值:对象实例.方法名(参数);
是否在当前类中使用 在当前类中使用:类型 变量名 = 方法名(参数);
在其他类中使用:类型 变量名 = 对象实例.方法名(参数);
被static修饰的方法:类型 变量名 = 类名.方法名(参数);
2.2.2.3 参数传递

参数传递是指,调用方法时,将实参的值传递给形参过程。

形参和实参:

  • 定义方法时,参数列表中的变量,称为形式参数。
  • 调用方法时,传入给方法的数值,称为实际参数。

入参类型:

  • 当调用方法时,如果传入的数值为基本数据类型(包含String类型),形式参数的改变对实际参数不影响。
  • 当调用方法时,如果传入的数值为引用数据类型(String类型除外),形式参数的改变对实际参数有影响。
2.2.2.4 重载

重载(overload)是在同一个类中,方法名相同,参数列表不同。

所谓的参数列表不同,主要是参数的个数或者类型,只要有一处不同就可以构成方法的重载了。

2.2.2.5 重写

重写(override)是在子类中重新定义了父类的方法,必须有相同的方法名、参数列表、返回类型。

重写的方法不能拥有比父类的方法更严格的访问控制权限。

构造方法不能被继承,因此不能被重写,在子类中只能通过super关键字调用父类的构造方法。

2.2.3 构造方法

构造方法是类用于创建对象的一个特殊的方法,当类创建对象时,就会调用类的构造方法。

语法:

java
1
2
3
修饰符 类名(参数列表) {
方法体;
}

调用:

java
1
2
new 类名();
new 类名(参数);

说明:

  • 构造方法没有返回类型,也不能使用void关键字。
  • 构造方法的方法名必须和类名一致。
  • 如果在类中没有定义构造方法,那么编译器会提供默认的构造方法,默认的构造方法没有参数列表,也没有方法体。
  • 如果在类中定义了构造方法,那么编译器就不会提供默认的构造方法。
  • 构造方法必须使用new关键字进行调用。

2.2.4 初始化块

初始化块分为静态初始化块和普通初始化块,优先于构造方法执行,经常执行初始化信息。

静态初始化块和普通初始化块都可以有多个,都可以写任何逻辑语句,不能定义方法。

执行顺序:

  • 静态成员变量和静态初始化块的执行顺序由定义位置决定,从上往下执行。
  • 成员变量和初始化块的执行顺序由定义的位置决定,从上往下执行。
  • 最后执行构造方法。

普通初始化块和静态初始化块的区别:

  • 普通初始化块能执行多次,静态初始化块只执行一次。
  • 普通初始化块实在对象创建后执行,静态初始化块是在类加载时执行。
  • 普通初始化块可以访问普通成员和静态成员,静态成员初始化块只能访问静态成员。

在静态成员初始化块中,可以对定义在代码前的静态成员进行赋值和访问,可以对定义在代码后的静态成员进行赋值,但是不能访问。

2.2.5 内部类

定义在一个类体内部的类称为内部类,包含内部类的类称为外部类,其他的类称为外部其他类。

内部类可以创建和外部类同名的成员,默认访问内部类成员,通过Outer.this.成员访问外部类成员。

2.2.5.1 普通内部类

特点:

  • 可以直接访问外部类的所有成员。
  • 可以使用访问修饰符。
  • 可以使用普通成员,不能使用静态成员。

外部类访问内部类:

java
1
Inner inner = new Inner();

外部其他类访问内部类:

java
1
2
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
2.2.5.2 静态内部类

特点:

  • 只能访问外部类的静态成员,不能访问普通成员,需要通过创建对象的方式访问外部类普通成员。
  • 可以使用访问修饰符。
  • 可以使用普通成员,可以使用静态成员。

外部类访问内部类:

java
1
2
Inner inner = new Inner();
Inner.静态成员;

外部其他类访问内部类:

java
1
2
Outer.Inner inner = new Outer.Inner();
Outer.Inner.静态成员;
2.2.5.3 局部内部类

特点:

  • 可以直接访问外部类的所有成员,能访问当前局部块中的final修饰的变量。
  • 不能使用访问修饰符。
  • 可以使用普通成员,不能使用静态成员。

作用范围只是在定义它的方法内,并且只能在定义之后访问,否则报编译错误。

外部方法访问内部类:

java
1
Inner inner = new Inner();
2.2.5.4 匿名内部类

匿名内部类new 类名(){};相当于创建了类名的子类对象。

特点:

  • 可以直接访问外部类的所有成员,能访问当前局部块中的final修饰的变量。
  • 不能使用访问修饰符。
  • 可以使用普通成员,不能使用静态成员。

外部类和外部其他类都不能访问匿名内部类。

匿名内部类经常用在方法的实参中,比较简洁:

java
1
2
3
new 抽象类或接口() {
重写方法
};

2.2.6 内部接口

定义在一个类体内部的接口称为内部接口,也称为嵌套接口。

内部接口不能使用访问修饰符,其访问权限受外部类控制,只有外部类及其成员才能访问内部接口。

内部接口中的方法默认使用public abstract修饰,需要创建内部类实现接口中的方法。

2.3 子类和父类

子类使用extends关键字继承父类,类是单继承的,一个子类只能继承一个父类。

2.3.1 表示

2.3.1.1 this

解决了全局变量和局部变量重名的问题。

代表的是当前类的对象,可以访问属性、方法、构造方法。

语法:

java
1
2
3
this.属性名;
this.方法名();
this(参数);

访问构造方法时,必须放在构造方法中,而且必须放在构造方法的第一句。

在一个构造方法中,不能多次调用其他构造方法。

2.3.1.2 super

表示子类中拿到的父类的引用,不是父类的对象。

语法:

java
1
2
3
super.属性名;// 访问父类属性
super.方法名();// 访问父类方法
super(参数);// 访问父类构造方法

访问构造方法时,必须放在构造方法中,而且必须放在构造方法的第一句。

不能与this同时使用。

2.3.2 加载机制

从父到子,静态先行。

父类的静态初始化块和静态变量赋值代码,子类的静态初始化块和静态变量赋值代码,父类的初始化块和变量赋值代码,父类的构造器,子类的初始化块和变量赋值代码,子类的构造器。

2.3.3 创建顺序

先调用子类的构造方法。

子类中调用父类的构造方法。如果没有显式说明调用父类的哪个构造,则默认是调用父类的无参构造。如果显式说明了,则调用指定的父类的构造。

依次向上递推,一直到最上级。

2.4 三大特性

2.4.1 封装

封装是指一个类隐藏了对象的属性和实现细节,对自己的数据和方法进行访问权限控制,只允许某些类和对象进行访问和使用,其他的类不能进行访问和使用。

封装是面向对象的特征之一,是对象和类概念的主要特性。简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。

在一个类的内部,某些代码或某些数据是私有的,不能被外界访问。通过这种方式,类对内部数据提供了不同级别的保护,以防止类中的私有数据被外部程序窃取和改动。

好处:

  • 良好的封装能够减少耦合。耦合,指的是模块间的关联程度,封装可以使模块变得更加独立,降低了耦合度。
  • 封装后的类,在类内部可以自由修改代码,不会影响外部的代码。
  • 对类的成员进行精确的访问控制。

访问控制:

  • public:公共访问权限。属性可以被所有类和对象访问,没有进行封装。
  • private:私有访问权限。只能在这个类中被访问,对属性和方法进行彻底的封装。
  • protected:继承访问权限。可以被子类访问,封装的不彻底。

一般来说,如果类中的某个属性不是公有的,而外部程序又需要访问,那么可以提供一个公有的方法,外部程序可以通过调用方法实现对该属性的间接访问。

2.4.2 继承

继承是子类可以获得父类的属性和方法,并且可以在不影响父类代码的前提下,在子类里面对继承的方法进行改写,扩展添加父类里面没有的方法。

继承是单继承,一个父类只能有一个子类,一个子类也只能有一个父类。

好处:

  • 子类可以直接使用父类的属性和方法,不需要重复编写代码,提高了代码的可重用性。

特点:

  • Object类是所有类的基类,所有的类都直接或间接继承了Object类,所以具备Object类的方法。
  • 子类不可以继承父类的构造方法,并且子类必须调用父类的构造方法。
  • 子类的构造方法中默认调用父类的无参构造,如果父类没有无参构造,报编译错误。
  • 子类用super调用父类的有参构造方法。

2.4.3 多态

多态是指一个方法或一个对象具备多种表现形式。

多态主要变现在两个方面:

  • 引用多态,即对象的向上转型,父类的引用指向子类的对象。
  • 方法多态,即子类重写了父类的方法,子类在调用该方法的时候调用的是子类重写的方法,父类在调用该方法的时候调用的是父类的方法。

如果在子类中扩展了一个父类没有的方法,就不能通过父类的引用创建的子类对象来调用该方法。

多态是运行时行为,不是编译时行为。

继承是多态实现的基础。

方法重写体现了多态,而方法重载并没有体现多态,因为重载没有继承。

2.5 类型转换

2.5.1 向上类型转换

向上类型转换(自动类型转换):

java
1
Animal animal = new Dog();

小类型转换成大类型。

子类创建的实例指向父类的引用。

2.5.2 向下类型转换

向下类型转换(强制类型转换):

java
1
Dog dog = (Dog) new Animal();

大类型转换成小类型,有可能导致溢出。

子类的引用指向父类的引用。

如果将子类的引用指向父类创建的实例,在编译时不会报错,但是在运行时会报错:

java
1
Dog dog = (Dog) new Animal();

使用instanceof运算符来解决引用对象的类型,避免类型转换的安全性问题。

作用是测试左边的对象是否是右边的类的实例,返回Boolean类型的数据。

在写程序的时候,如果要进行类型转换,最好使用instanceof运算符来判断它左边的对象是否是它右边的类的实例,再进行强制转换。

2.6 修饰符

2.6.1 访问修饰符

通过访问控制修饰符来控制类及类的方法和变量的访问权限,从而向使用者暴露接口,隐藏实现细节。

四种访问控制级别:

  • public:对所有类可见。
  • protected:对当前包可见,以及对子类可见,即使子类在其他包也是可见的。
  • default:对当前包可见。
  • private:对当前类可见。
2.6.1.1 public

可修饰类、接口、属性、方法、构造方法。

被public修饰的目标能够被所有的类访问,如果是其他包中的类,则需要导入所在的包。

父类中声明为public的方法在子类中也必须为public。

如果一个类使用public修饰,那该类的类名必须与他所在的源文件名相同。一个源文件中有且只有一个public类。

2.6.1.2 protected

可修饰属性、方法、构造方法,不能修饰类和接口。

被protected修饰的目标能够被同一个包里的类访问,如果是其他包中的类,只能被其他包中的子类访问。

父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。

2.6.1.3 default

可修饰类、接口、属性、方法、构造方法。

被default修饰的目标只能被同一个包里的类访问。

父类中声明为default的方法,不能在子类中声明为private。

如果没有使用任何修饰符,默认就是default访问控制级别。

2.6.1.4 private

可修饰内部类、属性、方法、构造方法,不能修饰普通类和接口。

被声明为private的目标只能被当前类访问。

父类中声明为private的方法,不能被子类重写。

2.6.2 静态修饰符

静态修饰符static可修饰内部类、属性、方法、初始化块,不能修饰普通类、接口、构造方法。

说明:

  • 修饰类,其成员被称为静态成员,比如静态属性、静态方法、静态初始化块、静态内部类。
  • 修饰成员,可以直接通过类名调用,比较简单。
  • 修饰方法和初始化块,只能访问静态成员,不能访问非静态成员。

静态成员随着类的加载而加载,静态成员优先于普通成员而加载。只加载一次,普通成员随着对象创建而加载,可以加载多次。

所有对象都共享静态成员。

被static修饰的方法和初始化块中不能使用this或super。

2.6.3 最终修饰符

最终修饰符final可修饰类、属性、方法。

说明:

  • 修饰类,不能被继承,也称为太监类、最终类,比如String类就是最终类。
  • 修饰方法,不能被重写,但可以被继承。
  • 修饰局部变量和全局变量,都称为常量,一旦赋值不能更改,保存在常量池。修饰的全局变量声明时必须赋值,要么在声明时,要么在所有构造方法,要么在初始化块。

常量不能更改:

  • 如果常量类型为基本类型,那么常量的值不能更改。
  • 如果常量的类型为引用类型,那么引用的对象不能更改,但对象的属性可以更改。

final和static的相同点:

  • 修饰的方法都只能在本类中被重载,不能被子类重写。

final和static的不同点:

  • 含义不同:static表示该成员要随类加载而加载。final表示该成员不可被修改。
  • 修饰范围不同:static可修饰内部类、属性、方法、初始化块,不可修饰普通类、构造方法。final可修饰类、属性、方法,不可修饰初始化块、构造方法。

2.6.4 抽象修饰符

抽象修饰符abstract可以修饰类和方法,但不能用来修饰属性,也不能修饰构造方法。不能和private连用,也不能同final和static连接使用。

说明:

  • 抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类。如果子类没有实现父类中所有的抽象方法,那么子类也必须被定义为抽象类。
  • 抽象类中可以有构造方法,创建子类的实例时可能会调用这些构造方法。

与其他修饰符:

  • 抽象方法不能被static修饰符修饰。因为static修饰的方法随类的加载而加载,而此时抽象类没有方法体。
  • 抽象类及抽象方法不能被final修饰符修饰。因为final修饰的类不允许拥有子类,而抽象类允许创建其子类实现抽象方法。

2.7 Lambda表达式

2.7.1 简介

Lambda表达式也可称为闭包,允许把函数作为一个方法的参数传递进方法中。

JDK1.8之后提供了Lambda表达式,可以使代码变的更加简洁紧凑。

2.7.2 规范

语法:

java
1
参数列表 -> 方法体

说明:

  • 如果参数列表有多个值,可以使用小括号包含,如果只有一条,则可以省略小括号。
  • 如果方法体有多条语句,可以使用大括号包含,如果只有一条,则可以省略大括号,如果是return语句,则可以省略return关键字。

2.7.3 使用

使用Lambda表达式之前:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Demo {
public static void main(String[] args) {
// 使用匿名内部类实现接口
WorkInterface work = new WorkInterface() {
@Override
public void doSomething() {
System.out.println("使用匿名内部类执行方法");
}
};
// 执行方法
work.doSomething();
}

// 定义内部接口
interface WorkInterface {
void doSomething();
}
}

使用Lambda表达式之后:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo {
public static void main(String[] args) {
// 使用Lambda表达式实现接口
WorkInterface work = () -> System.out.println("使用Lambda表达式执行方法");
// 执行方法
work.doSomething();
}

// 定义内部接口
interface WorkInterface {
void doSomething();
}
}

2.7.4 作用域

Lambda表达式只能引用标记了final的外层局部变量,不能在Lambda内部修改定义在域外的局部变量,否则会编译错误。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo {
public static void main(String[] args) {
// 局部变量
final int num = 100;
// 使用Lambda表达式实现接口
WorkInterface work = i -> System.out.println(i);
// 执行方法,编译报错
work.doSomething(num++);
}

// 定义内部接口
interface WorkInterface {
void doSomething(int i);
}
}

在Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo {
public static void main(String[] args) {
// 局部变量
final int num = 100;
// 使用Lambda表达式实现接口,编译报错
WorkInterface work = num -> System.out.println(num);
// 执行方法
work.doSomething(num);
}

// 定义内部接口
interface WorkInterface {
void doSomething(int i);
}
}

2.7.5 方法引用

方法引用是JDK1.8中提出的用来简化Lambda表达式的一种手段,通过类名和方法名定位静态方法或者实例方法。

2.7.5.1 静态方法引用

如果是静态方法引用,使用类名::静态方法名的形式,支持传入参数作为方法入参。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Demo {
public static void main(String[] args) {
// 使用静态方法引用
WorkInterface work = Demo::sum;
// 执行方法
work.doSomething(1, 2);
}

// 定义内部接口
interface WorkInterface {
void doSomething(int a, int b);
}

// 定义静态方法
public static void sum(int a, int b) {
System.out.println(a + b);
}
}
2.7.5.2 实例方法引用

如果是实例方法引用,使用对象实例::方法名语法执行,支持传入参数作为方法入参。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo {
public static void main(String[] args) {
// 创建对象
Demo demo = new Demo();
// 使用实例方法引用
WorkInterface work = demo::sum;
// 执行方法
work.doSomething(1, 2);
}

// 定义内部接口
interface WorkInterface {
void doSomething(int a, int b);
}

// 定义实例方法
public void sum(int a, int b) {
System.out.println(a + b);
}
}
2.7.5.3 构造方法引用

如果是构造方法引用,使用类名::new语法执行,支持传入参数作为方法入参。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Demo {
public static void main(String[] args) {
// 使用构造方法引用
WorkInterface work = Demo::new;
// 执行方法
work.doSomething().sum(1, 2);;
}

// 定义内部接口
interface WorkInterface {
Demo doSomething();
}

// 定义实例方法
public void sum(int a, int b) {
System.out.println(a + b);
}
}

3 接口

3.1 说明

接口可以理解成一种特殊的抽象类,定义了一组规范,体现了程序设计的多态和高内聚低偶合的设计思想。

定义接口:

java
1
2
3
public interface 接口名 {
void 方法名();
}

接口不能实例化。

3.2 好处

允许多继承、多实现,解决了前面单继承缺陷。

灵活,解耦性高。

3.3 成员

不能定义普通变量,只能定义常量,并且只能使用public static final修饰符,可以省略修饰符。

不能定义普通方法,可以定义抽象方法,使用public abstract修饰符,可以省略修饰符。

从JDK1.8开始支持定义默认方法和静态方法:

  • 默认方法:使用default修饰符,非抽象方法,可以拥有方法体。
  • 静态方法:使用static修饰符,非抽象方法,可以拥有方法体。

不能定义构造方法,因为接口不支持实例化。

支持内部类和内部接口。

3.4 关系

3.4.1 类对接口的实现

类对接口的实现使用implement关键字完成,一个类可以实现多个接口。

语法:

java
1
class A implements B, C { ... }

普通类必须实现里面所有的抽象方法,抽象类不用实现里面所有抽象方法。

实现接口的抽象方法只能使用public修饰符。

3.4.2 接口对接口的继承

接口对接口的继承使用extends关键字,一个接口可以继承多个接口。

语法:

java
1
interface A extends B, C { ... }

3.5 函数式接口

3.5.1 定义

从JDK1.8开始新增函数式接口,在接口里面只能有一个抽象方法,也称为SAM(Single Abstract Method)接口。

示例:

java
1
2
3
interface DemoInterface {
void demo();
}

可以使用@FunctionalInterface注解对接口进行编译级错误检查,当接口不符合函数式接口定义的时候,编译器会报错。

示例:

java
1
2
3
4
@FunctionalInterface
interface DemoInterface {
void demo();
}

3.5.2 使用

函数式接口可以包含默认方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
interface DemoInterface {
void demo();

default void defaultRead() {
System.out.println("defaultRead() ...");
}

default void defaultWrite() {
System.out.println("defaultWrite() ...");
}
}

函数式接口可以包含静态方法

java
1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
interface DemoInterface {
void demo();

static void staticRead() {
System.out.println("staticRead() ...");
}

static void staticWrite() {
System.out.println("staticWrite() ...");
}
}

函数式接口可以包含Object类的public方法:

java
1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
interface DemoInterface {
void demo();

@Override
public String toString();

@Override
public boolean equals(Object obj);
}

这些方法对于函数式接口来说,不会被当成抽象方法。虽然是抽象方法,但接口的实现类是Object的子类,会自动实现这些方法。

包含Object类的public方法,其他访问修饰符修饰的方法是不允许在函数式接口里定义的。

3.5.3 内置接口

内置的函数式接口放在java.util.function包下,默认在JDK安装路径下的src.zip文件中。

3.5.3.1 Supplier接口

没有参数,有返回值,作用是为了产生对象。

示例:

java
1
2
3
4
@FunctionalInterface
public interface Supplier<T> {
T get();
}
3.5.3.2 Consumer接口

有参数,没有返回值,作用是为了进行某种操作,比如打印操作。

示例:

java
1
2
3
4
5
6
7
8
9
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);

default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
3.5.3.3 Function接口

有参数,有返回值,作用是对参数进行处理并返回。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

static <T> Function<T, T> identity() {
return t -> t;
}
}
3.5.3.4 Predicate接口

有参数,有返回值,但返回值是Boolean类型的值,作用是对传入的参数进行某种判断。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);

default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

default Predicate<T> negate() {
return (t) -> !test(t);
}

default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}

static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

4 包

包可以对程序员编写的代码文件进行目录层次的管理,解决了同一个项目中类名重复的问题。

在不同的包下的两个名字相同的两个类,会被编译器看做是不同的两个类。

4.1 命名规则

小圆点、数字、下划线、美元符,不能数字开头,不能是关键字。

一般由小写字母和小圆点组成,采用域名倒置的写法。

4.2 声明规则

必须在代码的第一行对包进行声明。

使用package关键字进行声明,语法是:

java
1
package 包名;

如果没有声明包,则表示无包名。

一个文件中最多只能有一个包的声明。

4.3 导入规则

建议在包的声明下面导入包,实际上是导入包中的某个用到的类。

使用import关键字对包进行导入,语法是:

java
1
import 包名.类名;

如果没有导入在当前类用到的类,那么在使用的时候需要写明用到的类所在的包名和类名,导入之后便可以只写类名。

一个文件中可以有多个导入。

4.4 核心包

比较重要的核心包:

  • java.lang:包含一些核心类,如String、Math、Integer、System和Thread,提供常用功能。
  • java.net:包含执行与网络相关的操作的类和接口。
  • java.io:包含能提供多种输入/输出功能的类。
  • java.util:包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
  • java.text:包含了一些格式化相关的类。
  • java.sql:包含了进行JDBC数据库编程相关的类。
  • java.awt:包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
  • java.applet:包含applet运行所需的一些类。

评论