10-面向对象进阶
第十章 面向对象进阶
1.分类思想
1.1分类思想概述 (理解)
分工协作,专人干专事
2.分包思想
2.1分包思想概述 (理解)
如果将所有的类文件都放在同一个包下,不利于管理和后期维护,所以,对于不同功能的类文件,可以放在不同的包下进行管理
2.2包的概述 (记忆)
-
包
本质上就是文件夹
-
创建包
多级包之间使用 " . " 进行分割
多级包的定义规范:公司的网站地址翻转(去掉www)
比如:黑马程序员的网站址为www.itheima.com
后期我们所定义的包的结构就是:com.itheima.其他的包名 -
包的命名规则
字母都是小写
2.3包的注意事项 (理解)
- package语句必须是程序的第一条可执行的代码
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
2.4类与类之间的访问 (理解)
-
同一个包下的访问
不需要导包,直接使用即可
-
不同包下的访问
1.import 导包后访问
2.通过全类名(包名 + 类名)访问
-
注意:import 、package 、class 三个关键字的摆放位置存在顺序关系
package 必须是程序的第一条可执行的代码
import 需要写在 package 下面
class 需要在 import 下面
3.static关键字
3.1static关键字概述 (理解)
static 关键字是静态的意思,是Java中的一个修饰符,可以修饰成员方法,成员变量
3.2static修饰的特点 (记忆)
-
被类的所有对象共享
是我们判断是否使用静态关键字的条件
-
随着类的加载而加载,优先于对象存在
对象需要类被加载后,才能创建
-
可以通过类名调用
也可以通过对象名调用
3.3static关键字注意事项 (理解)
- 静态方法只能访问静态的成员
- 非静态方法可以访问静态的成员,也可以访问非静态的成员
- 静态方法中是没有this关键字
4. 继承
4.1 继承的实现(掌握)
-
继承的概念
- 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
-
实现继承的格式
- 继承通过extends实现
- 格式:class 子类 extends 父类
- 举例:class Dog extends Animal
-
继承带来的好处
- 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
-
示例代码
public class Fu { public void show() { System.out.println("show方法被调用"); } } public class Zi extends Fu { public void method() { System.out.println("method方法被调用"); } } public class Demo { public static void main(String[] args) { //创建对象,调用方法 Fu f = new Fu(); f.show(); Zi z = new Zi(); z.method(); z.show(); } }
4.2 继承的好处和弊端(理解)
- 继承好处
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
- 继承弊端
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
- 继承的应用场景:
- 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
- is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
- 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
4.3. Java中继承的特点(掌握)
-
Java中继承的特点
- Java中类只支持单继承,不支持多继承
- 错误范例:class A extends B, C
- Java中类支持多层继承
- Java中类只支持单继承,不支持多继承
-
多层继承示例代码:
public class Granddad { public void drink() { System.out.println("爷爷爱喝酒"); } } public class Father extends Granddad { public void smoke() { System.out.println("爸爸爱抽烟"); } } public class Mother { public void dance() { System.out.println("妈妈爱跳舞"); } } public class Son extends Father { // 此时,Son类中就同时拥有drink方法以及smoke方法 }
5. 继承中的成员访问特点
5.1 继承中变量的访问特点(掌握)
在子类方法中访问一个变量,采用的是就近原则。
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲…)
-
示例代码
class Fu { int num = 10; } class Zi { int num = 20; public void show(){ int num = 30; System.out.println(num); } } public class Demo1 { public static void main(String[] args) { Zi z = new Zi(); z.show(); // 输出show方法中的局部变量30 } }
5.2 super(掌握)
- this&super关键字:
- this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
- this和super的使用分别
- 成员变量:
- this.成员变量 - 访问本类成员变量
- super.成员变量 - 访问父类成员变量
- 成员方法:
- this.成员方法 - 访问本类成员方法
- super.成员方法 - 访问父类成员方法
- 成员变量:
- 构造方法:
- this(…) - 访问本类构造方法
- super(…) - 访问父类构造方法
5.3 继承中构造方法的访问特点(理解)
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()
问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法
注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
5.4 继承中成员方法的访问特点(掌握)
通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲…)
5.5 super内存图(理解)
-
对象在堆内存中,会单独存在一块super区域,用来存放父类的数据

5.6 方法重写(掌握)
- 1、方法重写概念
- 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
- 2、方法重写的应用场景
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
- 3、Override注解
- 用来检测当前的方法,是否是重写的方法,起到【校验】的作用
5.7 方法重写的注意事项(掌握)
- 方法重写的注意事项
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能更低(public > 默认 > 私有)
- 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法
- 示例代码
public class Fu {
private void show() {
System.out.println("Fu中show()方法被调用");
}
void method() {
System.out.println("Fu中method()方法被调用");
}
}
public class Zi extends Fu {
/* 编译【出错】,子类不能重写父类私有的方法*/
@Override
private void show() {
System.out.println("Zi中show()方法被调用");
}
/* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
@Override
private void method() {
System.out.println("Zi中method()方法被调用");
}
/* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
@Override
public void method() {
System.out.println("Zi中method()方法被调用");
}
}
5.8 权限修饰符 (理解)

6.抽象类
6.1抽象类的概述(理解)
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
6.2抽象类的特点(记忆)
-
抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义 public abstract class 类名 {} //抽象方法的定义 public abstract void eat(); -
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
抽象类不能实例化
-
抽象类可以有构造方法
-
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
6.3抽象类的案例(应用)
-
案例需求
定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat(猫吃鱼)drink(喝水…)
狗类成员方法:eat(狗吃肉)drink(喝水…)
-
实现步骤
- 猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)
- 父类Animal中,无法将 eat 方法具体实现描述清楚,所以定义为抽象方法
- 抽象方法需要存活在抽象类中,将Animal定义为抽象类
- 让 Cat 和 Dog 分别继承 Animal,重写eat方法
- 测试类中创建 Cat 和 Dog 对象,调用方法测试
-
代码实现
- 动物类
public abstract class Animal { public void drink(){ System.out.println("喝水"); } public Animal(){ } public abstract void eat(); }- 猫类
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }- 狗类
public class Dog extends Animal { @Override public void eat() { System.out.println("狗吃肉"); } }- 测试类
public static void main(String[] args) { Dog d = new Dog(); d.eat(); d.drink(); Cat c = new Cat(); c.drink(); c.eat(); //Animal a = new Animal(); //a.eat(); }
6.4模板设计模式
-
设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 -
模板设计模式
把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法
让使用模板的类(继承抽象类的类)去重写抽象方法实现需求 -
模板设计模式的优势
模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可
-
示例代码
模板类
/* 作文模板类 */ public abstract class CompositionTemplate { public final void write(){ System.out.println("<<我的爸爸>>"); body(); System.out.println("啊~ 这就是我的爸爸"); } public abstract void body(); }实现类A
public class Tom extends CompositionTemplate { @Override public void body() { System.out.println("那是一个秋天, 风儿那么缠绵,记忆中, " + "那天爸爸骑车接我放学回家,我的脚卡在了自行车链当中, 爸爸蹬不动,他就站起来蹬..."); } }实现类B
public class Tony extends CompositionTemplate { @Override public void body() { } /*public void write(){ }*/ }测试类
public class Test { public static void main(String[] args) { Tom t = new Tom(); t.write(); } }
6.5final(应用)
-
fianl关键字的作用
- final代表最终的意思,可以修饰成员方法,成员变量,类
-
final修饰类、方法、变量的效果
-
fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
-
final修饰方法:该方法不能被重写
-
final修饰变量:表明该变量是一个常量,不能再次赋值
-
变量是基本类型,不能改变的是值
-
变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
-
举例
public static void main(String[] args){ final Student s = new Student(23); s = new Student(24); // 错误 s.setAge(24); // 正确 }
-
-
7.代码块
7.1代码块概述 (理解)
在Java中,使用 { } 括起来的代码被称为代码块
7.2代码块分类 (理解)
-
局部代码块
-
位置: 方法中定义
-
作用: 限定变量的生命周期,及早释放,提高内存利用率
-
示例代码
public class Test { /* 局部代码块 位置:方法中定义 作用:限定变量的生命周期,及早释放,提高内存利用率 */ public static void main(String[] args) { { int a = 10; System.out.println(a); } // System.out.println(a); } }
-
-
构造代码块
-
位置: 类中方法外定义
-
特点: 每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
-
作用: 将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
-
示例代码
public class Test { /* 构造代码块: 位置:类中方法外定义 特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性 */ public static void main(String[] args) { Student stu1 = new Student(); Student stu2 = new Student(10); } } class Student { { System.out.println("好好学习"); } public Student(){ System.out.println("空参数构造方法"); } public Student(int a){ System.out.println("带参数构造方法..........."); } }
-
-
静态代码块
-
位置: 类中方法外定义
-
特点: 需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
-
作用: 在类加载的时候做一些数据初始化的操作
-
示例代码
public class Test { /* 静态代码块: 位置:类中方法外定义 特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次 作用:在类加载的时候做一些数据初始化的操作 */ public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(10); } } class Person { static { System.out.println("我是静态代码块, 我执行了"); } public Person(){ System.out.println("我是Person类的空参数构造方法"); } public Person(int a){ System.out.println("我是Person类的带...........参数构造方法"); } }
-
8.接口
8.1接口的概述(理解)
- 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
- Java中接口存在的两个意义
- 用来定义规范
- 用来做功能的拓展
8.2接口的特点(记忆)
-
接口用关键字interface修饰
public interface 接口名 {} -
类实现接口用implements表示
public class 类名 implements 接口名 {} -
接口不能实例化
我们可以创建接口的实现类对象使用
-
接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
8.3接口的成员特点(记忆)
-
成员特点
-
成员变量
只能是常量
默认修饰符:public static final -
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
-
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
-
-
代码演示
- 接口
public interface Inter { public static final int NUM = 10; public abstract void show(); }- 实现类
class InterImpl implements Inter{ public void method(){ // NUM = 20; System.out.println(NUM); } public void show(){ } }- 测试类
public class TestInterface { /* 成员变量: 只能是常量 系统会默认加入三个关键字 public static final 构造方法: 没有 成员方法: 只能是抽象方法, 系统会默认加入两个关键字 public abstract */ public static void main(String[] args) { System.out.println(Inter.NUM); } }
8.4类和接口的关系(记忆)
-
类与类的关系
继承关系,只能单继承,但是可以多层继承
-
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口与接口的关系
继承关系,可以单继承,也可以多继承
2.接口组成更新
2.1接口组成更新概述【理解】
-
常量
public static final
-
抽象方法
public abstract
-
默认方法(Java 8)
-
静态方法(Java 8)
-
私有方法(Java 9)
2.2接口中默认方法【应用】
-
格式
public default 返回值类型 方法名(参数列表)
-
作用
解决接口升级的问题
-
范例
public default void show3() { } -
注意事项
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
2.3接口中静态方法【应用】
-
格式
public static 返回值类型 方法名(参数列表)
-
范例
public static void show() { } -
注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
2.4接口中私有方法【应用】
-
私有方法产生原因
Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
-
定义格式
-
格式1
private 返回值类型 方法名(参数列表)
-
范例1
private void show() { } -
格式2
private static 返回值类型 方法名(参数列表)
-
范例2
private static void method() { }
-
-
注意事项
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
9.多态
9.1多态的概述(记忆)
-
什么是多态
同一个对象,在不同时刻表现出来的不同形态
-
多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
-
代码演示
class Animal { public void eat(){ System.out.println("动物吃饭"); } } class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } } public class Test1Polymorphic { /* 多态的前提: 1. 要有(继承 \ 实现)关系 2. 要有方法重写 3. 要有父类引用, 指向子类对象 */ public static void main(String[] args) { // 当前事物, 是一只猫 Cat c = new Cat(); // 当前事物, 是一只动物 Animal a = new Cat(); a.eat(); } }
9.2多态中的成员访问特点(记忆)
-
成员访问特点
-
成员变量
编译看父类,运行看父类
-
成员方法
编译看父类,运行看子类
-
-
代码演示
class Fu { int num = 10; public void method(){ System.out.println("Fu.. method"); } } class Zi extends Fu { int num = 20; public void method(){ System.out.println("Zi.. method"); } } public class Test2Polymorpic { /* 多态的成员访问特点: 成员变量: 编译看左边 (父类), 运行看左边 (父类) 成员方法: 编译看左边 (父类), 运行看右边 (子类) */ public static void main(String[] args) { Fu f = new Zi(); System.out.println(f.num); f.method(); } }
9.3多态的好处和弊端(记忆)
-
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
-
弊端
不能使用子类的特有成员
9.4多态中的转型(应用)
-
向上转型
父类引用指向子类对象就是向上转型
-
向下转型
格式:子类型 对象名 = (子类型)父类引用;
-
代码演示
class Fu { public void show(){ System.out.println("Fu..show..."); } } class Zi extends Fu { @Override public void show() { System.out.println("Zi..show..."); } public void method(){ System.out.println("我是子类特有的方法, method"); } } public class Test3Polymorpic { public static void main(String[] args) { // 1. 向上转型 : 父类引用指向子类对象 Fu f = new Zi(); f.show(); // 多态的弊端: 不能调用子类特有的成员 // f.method(); // A: 直接创建子类对象 // B: 向下转型 // 2. 向下转型 : 从父类类型, 转换回子类类型 Zi z = (Zi) f; z.method(); } }
9.5多态中转型存在的风险和解决方案 (应用)
-
风险
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
-
解决方案
-
关键字
instanceof
-
使用格式
变量名 instanceof 类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
-
-
代码演示
abstract class Animal { public abstract void eat(); } class Dog extends Animal { public void eat() { System.out.println("狗吃肉"); } public void watchHome(){ System.out.println("看家"); } } class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } } public class Test4Polymorpic { public static void main(String[] args) { useAnimal(new Dog()); useAnimal(new Cat()); } public static void useAnimal(Animal a){ // Animal a = new Dog(); // Animal a = new Cat(); a.eat(); //a.watchHome(); // Dog dog = (Dog) a; // dog.watchHome(); // ClassCastException 类型转换异常 // 判断a变量记录的类型, 是否是Dog if(a instanceof Dog){ Dog dog = (Dog) a; dog.watchHome(); } } }
10.内部类
10.1 内部类的基本使用(理解)
-
内部类概念
- 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
-
内部类定义格式
-
格式&举例:
/* 格式: class 外部类名{ 修饰符 class 内部类名{ } } */ class Outer { public class Inner { } }
-
-
内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
-
示例代码:
/* 内部类访问特点: 内部类可以直接访问外部类的成员,包括私有 外部类要访问内部类的成员,必须创建对象 */ public class Outer { private int num = 10; public class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } }
10.2 成员内部类(理解)
-
成员内部类的定义位置
- 在类中方法,跟成员变量是一个位置
-
外界创建成员内部类格式
- 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 举例:Outer.Inner oi = new Outer().new Inner();
-
私有成员内部类
-
将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
-
示例代码:
class Outer { private int num = 10; private class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } } public class InnerDemo { public static void main(String[] args) { //Outer.Inner oi = new Outer().new Inner(); //oi.show(); Outer o = new Outer(); o.method(); } }
-
-
静态成员内部类
-
静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
-
静态成员内部类中的静态方法:外部类名.内部类名.方法名();
-
示例代码
class Outer { static class Inner { public void show(){ System.out.println("inner..show"); } public static void method(){ System.out.println("inner..method"); } } } public class Test3Innerclass { /* 静态成员内部类演示 */ public static void main(String[] args) { // 外部类名.内部类名 对象名 = new 外部类名.内部类名(); Outer.Inner oi = new Outer.Inner(); oi.show(); Outer.Inner.method(); } }
-
10.3 局部内部类(理解)
-
局部内部类定义位置
- 局部内部类是在方法中定义的类
-
局部内部类方式方式
- 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
-
示例代码
class Outer { private int num = 10; public void method() { int num2 = 20; class Inner { public void show() { System.out.println(num); System.out.println(num2); } } Inner i = new Inner(); i.show(); } } public class OuterDemo { public static void main(String[] args) { Outer o = new Outer(); o.method(); } }
10.4 匿名内部类(应用)
-
匿名内部类的前提
- 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
-
匿名内部类的格式
-
格式:new 类名 ( ) { 重写方法 } new 接口名 ( )
-
举例:
new Inter(){ @Override public void method(){} }
-
-
匿名内部类的本质
- 本质:是一个继承了该类或者实现了该接口的子类匿名对象
-
匿名内部类的细节
-
匿名内部类可以通过多态的形式接受
Inter i = new Inter(){ @Override public void method(){ } }
-
-
匿名内部类直接调用方法
interface Inter{ void method(); } class Test{ public static void main(String[] args){ new Inter(){ @Override public void method(){ System.out.println("我是匿名内部类"); } }.method(); // 直接调用方法 } }
10.4 匿名内部类在开发中的使用(应用)
-
匿名内部类在开发中的使用
- 当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
-
示例代码:
/* 游泳接口 */ interface Swimming { void swim(); } public class TestSwimming { public static void main(String[] args) { goSwimming(new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧"); } }); } /** * 使用接口的方法 */ public static void goSwimming(Swimming swimming){ /* Swimming swim = new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧"); } } */ swimming.swim(); } }
11.Lambda表达式
11.1体验Lambda表达式【理解】
-
代码演示
/* 游泳接口 */ interface Swimming { void swim(); } public class TestSwimming { public static void main(String[] args) { // 通过匿名内部类实现 goSwimming(new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧"); } }); /* 通过Lambda表达式实现 理解: 对于Lambda表达式, 对匿名内部类进行了优化 */ goSwimming(() -> System.out.println("铁汁, 我们去游泳吧")); } /** * 使用接口的方法 */ public static void goSwimming(Swimming swimming) { swimming.swim(); } } -
函数式编程思想概述
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式思想的体现
11.2Lambda表达式的标准格式【理解】
-
格式:
(形式参数) -> {代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:由英文中画线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
-
组成Lambda表达式的三要素:
- 形式参数,箭头,代码块
11.3Lambda表达式练习1【应用】
-
Lambda表达式的使用前提
- 有一个接口
- 接口中有且仅有一个抽象方法
-
练习描述
无参无返回值抽象方法的练习
-
操作步骤
- 定义一个接口(Eatable),里面定义一个抽象方法:void eat();
- 定义一个测试类(EatableDemo),在测试类中提供两个方法
- 一个方法是:useEatable(Eatable e)
- 一个方法是主方法,在主方法中调用useEatable方法
-
示例代码
//接口 public interface Eatable { void eat(); } //实现类 public class EatableImpl implements Eatable { @Override public void eat() { System.out.println("一天一苹果,医生远离我"); } } //测试类 public class EatableDemo { public static void main(String[] args) { //在主方法中调用useEatable方法 Eatable e = new EatableImpl(); useEatable(e); //匿名内部类 useEatable(new Eatable() { @Override public void eat() { System.out.println("一天一苹果,医生远离我"); } }); //Lambda表达式 useEatable(() -> { System.out.println("一天一苹果,医生远离我"); }); } private static void useEatable(Eatable e) { e.eat(); } }
11.4Lambda表达式练习2【应用】
-
练习描述
有参无返回值抽象方法的练习
-
操作步骤
- 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
- 定义一个测试类(FlyableDemo),在测试类中提供两个方法
- 一个方法是:useFlyable(Flyable f)
- 一个方法是主方法,在主方法中调用useFlyable方法
-
示例代码
public interface Flyable { void fly(String s); } public class FlyableDemo { public static void main(String[] args) { //在主方法中调用useFlyable方法 //匿名内部类 useFlyable(new Flyable() { @Override public void fly(String s) { System.out.println(s); System.out.println("飞机自驾游"); } }); System.out.println("--------"); //Lambda useFlyable((String s) -> { System.out.println(s); System.out.println("飞机自驾游"); }); } private static void useFlyable(Flyable f) { f.fly("风和日丽,晴空万里"); } }
11.5Lambda表达式练习3【应用】
-
练习描述
有参有返回值抽象方法的练习
-
操作步骤
- 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
- 定义一个测试类(AddableDemo),在测试类中提供两个方法
- 一个方法是:useAddable(Addable a)
- 一个方法是主方法,在主方法中调用useAddable方法
-
示例代码
public interface Addable { int add(int x,int y); } public class AddableDemo { public static void main(String[] args) { //在主方法中调用useAddable方法 useAddable((int x,int y) -> { return x + y; }); } private static void useAddable(Addable a) { int sum = a.add(10, 20); System.out.println(sum); } }
11.6Lambda表达式的省略模式【应用】
-
省略的规则
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
-
代码演示
public interface Addable { int add(int x, int y); } public interface Flyable { void fly(String s); } public class LambdaDemo { public static void main(String[] args) { // useAddable((int x,int y) -> { // return x + y; // }); //参数的类型可以省略 useAddable((x, y) -> { return x + y; }); // useFlyable((String s) -> { // System.out.println(s); // }); //如果参数有且仅有一个,那么小括号可以省略 // useFlyable(s -> { // System.out.println(s); // }); //如果代码块的语句只有一条,可以省略大括号和分号 useFlyable(s -> System.out.println(s)); //如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉 useAddable((x, y) -> x + y); } private static void useFlyable(Flyable f) { f.fly("风和日丽,晴空万里"); } private static void useAddable(Addable a) { int sum = a.add(10, 20); System.out.println(sum); } }
11.7Lambda表达式的使用前提【理解】
- 使用Lambda必须要有接口
- 并且要求接口中有且仅有一个抽象方法
11.8Lambda表达式和匿名内部类的区别【理解】
- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成

浙公网安备 33010602011771号