JAVA 기초-16 장 스레드 풀, Lambda 표현식

주요 내용

대기 및 깨우기 케이스
스레드 풀
람다 식

교육 목표

스레드 통신의 개념을
이해할 수 있습니다. 깨어나기를 기다리는 메커니즘을 이해할
수 있습니다. Java에서 스레드 풀의 작동 원리를 설명 할 수 있습니다.
객체 지향에 비해 함수형 프로그래밍의 장점을 이해할 수 있습니다. Lambda
표현식의 표준 형식을 마스터
할 수 있습니다. Lambda 표준 형식을 사용할 수 있습니다. Runnable 및 Comparator 인터페이스를
사용할 수 있습니다. Lambda를 마스터 할 수 있습니다. 표현 생략의 형식과 규칙은
Lambda 생략 형식을 사용하고, Runnable 및 Comparator 인터페이스
를 사용하고, Lambda 표준 형식을 사용하여 사용자 지정 인터페이스 (하나의 추상 방법 사용)를
사용할 수 있으며, Lambda 생략 형식을 사용하여 사용자 지정 인터페이스를 사용할 수 있습니다 ( 그리고 오직 하나의 추상적 인 방법이 있습니다)
Lambda의 두 전제를 명확히 할 수 있습니다.

1 장 웨이크 업 메커니즘 대기

1.1 스레드 간의 통신

개념 : 여러 스레드가 동일한 리소스를 처리하고 있지만 처리 작업 (스레드의 작업)은 다릅니다.

예를 들어, 스레드 A는 번을 생성하는 데 사용되고 스레드 B는 번을 먹는 데 사용됩니다. 번은 동일한 리소스로 이해할 수 있습니다. 스레드 A와 스레드 B가 처리하는 작업은 하나는 생산 용이고 다른 하나는 소 비용입니다. 그런 다음 스레드 A와 스레드 B 사이에서 스레드 통신 문제가 있습니다.

스레드 간 통신을 다루는 이유 :

여러 스레드가 동시에 실행될 때 CPU는 기본적으로 스레드를 무작위로 전환합니다. 작업을 함께 완료하기 위해 여러 스레드가 필요하고 정기적으로 실행되도록하려면 여러 스레드 간의 조정 및 통신이 필요합니다. 데이터 조각의 다중 스레드 협력을 달성하는 데 도움이됩니다.

스레드 간 통신을위한 자원의 효과적인 사용을 보장하는 방법 :

여러 스레드가 동일한 리소스와 다른 작업을 처리하는 경우 스레드 간 동일한 변수의 사용 또는 작동을 해결하기 위해 스레드 통신이 필요합니다. 즉, 여러 스레드가 동일한 데이터에서 작동 할 때 동일한 공유 변수에 대한 경합을 피합니다. 즉, 각 스레드가 리소스를 효과적으로 사용할 수 있도록 특정 수단을 사용해야합니다. 그리고이 방법은 깨우기 메커니즘을 기다리고 있습니다.

1.2 웨이크 업 메커니즘 대기

깨우기 메커니즘을 기다리는 것

이것은 여러 스레드 간의 협력 메커니즘입니다. 쓰레드에 관해서는 종종 락 경쟁과 같은 쓰레드 간의 경쟁을 생각하지만 이것이
전체 이야기 가 아니며 쓰레드 간의 협력 메커니즘도있을 것입니다. 회사의 동료와 마찬가지로 승진을 위해 경쟁 할 수 있지만
특정 작업을 완료 하기 위해 더 자주 협력합니다.

즉, 스레드가 규정 된 작업을 수행 한 후 대기 상태 (wait ())에 들어가고 다른 스레드가 지정된 코드를 실행하기 전에 대기
(notify ()); 여러 스레드가 대기중인 경우 필요한 경우 notifyAll ()을 사용하여 대기중인 모든 스레드를 깨울 수 있습니다.

대기 / 알림은 스레드 간의 협력 메커니즘입니다.

일어나기를 기다리는 중

웨이크 업 대기 (wait for wake-up) 메커니즘은 쓰레드 간의 통신 문제를 해결하기 위해 사용되며, 사용되는 세 가지 방법의 의미는 다음과 같습니다.

1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时
的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象
上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先
入座。
3. notifyAll:则释放所通知对象的 wait set 上的全部线程。
注意:
哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而
此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调
用 wait 方法之后的地方恢复执行。
总结如下:
如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态

대기 및 알림 메소드 호출시주의해야 할 사항

1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对
象调用的wait方法后的线程。
2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继
承了Object类的。
3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这 2 个方
法。

1.3 생산자와 소비자 문제

1.3 생산자와 소비자 문제

깨우기 메커니즘을 기다리는 것은 실제로 고전적인 "생산자 및 소비자"문제입니다.

찐빵의 생산과 찐빵의 소비를 예로 들어 보자.

코드 데모 :

Bun 자원 클래스 :

식품 실 등급 :

Baozipu 스레드 클래스 :

包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子
(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。
接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包
子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取
决于锁的获取情况。
public class BaoZi {
String pier ;
String xianer ;
boolean flag = false ;//包子资源 是否存在 包子资源状态
}
public class ChiHuo extends Thread{
private BaoZi bz;
public ChiHuo(String name,BaoZi bz){
super(name);
this.bz = bz;
    }
@Override
public void run() {
while(true){
synchronized (bz){
if(bz.flag == false){//没包子
try {
bz.wait();
                    } catch (InterruptedException e) {
e.printStackTrace();
                    }
                }
System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
bz.flag = false;
bz.notify();
            }
        }
    }
}

테스트 카테고리 :

public class BaoZiPu extends Thread {
private BaoZi bz;
public BaoZiPu(String name,BaoZi bz){
super(name);
this.bz = bz;
    }
@Override
public void run() {
int count =  0 ;
//造包子
while(true){
//同步
synchronized (bz){
if(bz.flag == true){//包子资源 存在
try {
bz.wait();
                    } catch (InterruptedException e) {
e.printStackTrace();
                    }
                }
// 没有包子 造包子
System.out.println("包子铺开始做包子");
if(count% 2  ==  0 ){
// 冰皮 五仁
bz.pier = "冰皮";
bz.xianer = "五仁";
                }else{
// 薄皮 牛肉大葱
bz.pier = "薄皮";
bz.xianer = "牛肉大葱";
                }
count++;
bz.flag=true;
System.out.println("包子造好了:"+bz.pier+bz.xianer);
System.out.println("吃货来吃吧");
//唤醒等待线程 (吃货)
bz.notify();
            }
        }
    }
}

실행 효과 :

2 장 스레드 풀

2.1 스레드 풀 아이디어 개요

public class Demo {
public static void main(String[] args) {
//等待唤醒案例
BaoZi bz = new BaoZi();
ChiHuo ch = new ChiHuo("吃货",bz);
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
ch.start();
bzp.start();
    }
}

Baozipu가 빵 만들기 시작

빵이 만들어집니다 : 얼음 껍질과 5 개의 알갱이

와서 먹어

식도락가가 얼음 껍질 5 렌빵을 먹고 있습니다

Baozipu가 빵 만들기 시작

빵이 만들어집니다 : 얇은 껍질을 벗긴 쇠고기와 파

와서 먹어

식도락가는 얇은 소고기와 파 빵을 먹고 있습니다

Baozipu가 빵 만들기 시작

빵이 만들어집니다 : 얼음 껍질과 5 개의 알갱이

와서 먹어

식도락가가 얼음 껍질 5 렌빵을 먹고 있습니다

쓰레드를 사용할 때 쓰레드를 생성하는데 이것은 구현하기 매우 쉽지만 문제가 있습니다.

동시 쓰레드가 많고 각 쓰레드가 짧은 시간 동안 작업을 수행하면 종료되므로 쓰레드 생성 빈도가 크게 감소

스레드를 자주 생성하고 파괴하는 데 시간이 걸리기 때문에 시스템의 효율성.

그렇다면 스레드를 재사용 가능하게 만드는 방법이 있습니까? 즉, 작업을 실행 한 후에는 삭제되지 않고 다른 작업을 계속 수행 할 수 있습니까?

Java에서이 효과는 스레드 풀을 통해 얻을 수 있습니다. 오늘은 Java 스레드 풀에 대해 자세히 설명하겠습니다.

2.2 스레드 풀의 개념

스레드 풀 : 실제로 여러 스레드를 보유하는 컨테이너이며 스레드를 반복적으로 사용할 수 있으므로 스레드 개체를 자주 생성 할 필요가 없습니다.

반복적으로 스레드를 생성하고 너무 많은 리소스를 소비 할 필요가 없습니다.

스레드 풀의 많은 작업이 리소스 최적화와 관련되어 있으므로 여기서는 자세히 설명하지 않습니다. 스레드 풀의 작동 원리를 이해하기 위해 그림을 사용합니다.

이유:

스레드 풀을 합리적으로 사용하면 다음과 같은 세 가지 이점이 있습니다.

1. 자원 소비를 줄입니다. 스레드 생성 및 삭제 횟수가 줄어들고 각 작업자 스레드를 재사용하여 여러 작업을 수행 할 수 있습니다.

2. 응답 속도를 향상시킵니다. 작업이 도착하면 스레드가 생성 될 때까지 기다리지 않고 작업을 즉시 실행할 수 있습니다.

3. 스레드의 관리 용이성을 향상시킵니다. 스레드 풀의 작업자 스레드 수는 과도한 내부 소비를 방지하기 위해 시스템의 경제성에 따라 조정할 수 있습니다.

서버를 저장하고 서버를 내려 놓으십시오 (각 스레드는 약 1MB의 메모리가 필요하고 더 많은 스레드가 열릴수록 더 많은 메모리가 소비되고 마지막으로 충돌합니다).

2.3 스레드 풀 사용

Java에서 스레드 풀의 최상위 인터페이스는 java.util.concurrent.Executor이지만 엄격한 의미에서 Executor는 스레드
풀이 아니라 스레드를 실행하는 도구입니다. 실제 스레드 풀 인터페이스는 java.util.concurrent.ExecutorService입니다.

스레드 풀을 구성하는 것은 더 복잡합니다. 특히 스레드 풀의 원리가 명확하지 않은 경우 구성된 스레드 풀이 최적이 아닐 가능성이 매우 높
으므로 java.util.concurrent.Executors 스레드 팩토리 클래스에서 제공됩니다. 일부 정적 팩토리는 일반적으로 사용되는 스레드 풀을 생성하기 위해 생성됩니다. 공식적인
권장 사항은 Executors 엔지니어링 클래스를 사용하여 스레드 풀 개체를 만드는 것입니다.

Executors 클래스에 스레드 풀을 만드는 방법은 다음과 같습니다.

public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象。(创建的是有界线
程池,也就是池中的线程个数可以指定最大数量)

스레드 풀 ExecutorService 개체를 얻은 다음 사용 방법은 다음과 같이 정의 된 스레드 풀 개체를 사용하는 방법입니다.

public Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。

스레드 풀에서 스레드 개체를 사용하는 단계 :

1. 创建线程池对象。
2. 创建Runnable接口子类对象。(task)
3. 提交Runnable接口子类对象。(take task)
4. 关闭线程池(一般不做)。

실행 가능한 구현 클래스 코드 :

스레드 풀 테스트 클래스 :

3 장 람다 식

3.1 함수형 프로그래밍 아이디어 개요

public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep( 2000 );
        } catch (InterruptedException e) {
e.printStackTrace();
        }
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池对象
ExecutorService service = Executors.newFixedThreadPool( 2 );//包含 2 个线程对象
// 创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
// Thread t = new Thread(r);
// t.start(); ‐‐‐> 调用MyRunnable中的run()
// 从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
// 再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
// 将使用完的线程又归还到了线程池中
// 关闭线程池
//service.shutdown();
    }
}

수학에서 함수는 입력과 출력이있는 계산 체계의 집합입니다. 즉, "무언가로 무언가를 수행"합니다. 상대적으로 말하자면 객체 지향

요점은 "객체의 형태로해야 할 일"을 강조하는 반면, 기능적 사고는 객체 지향의 복잡한 구문을 무시하려고합니다.

무엇을해야할까요.

객체 지향적 사고 :

한 가지 작업을 수행하고 문제를 해결할 수있는 개체를 찾고 개체의 메서드를 호출하고 작업을 완료합니다.

함수형 프로그래밍 아이디어 :

결과를 얻을 수있는 한, 누가 그것을 어떻게하는지는 중요하지 않습니다. 누가 그것을하는지는 중요하지 않습니다.

3.2 중복 실행 가능 코드

전통 쓰기

작업을 완료하기 위해 스레드를 시작해야하는 경우 작업 내용은 일반적으로 java.lang.Runnable 인터페이스를 통해 정의되고
java.lang.Thread 클래스는 스레드를 시작하는 데 사용됩니다. 코드 쇼 :

"모든 것이 객체입니다"라는 개념을 바탕으로이 접근 방식은 이해할 수 있습니다. 먼저 Runnable 인터페이스의 익명 내부 클래스 객체를 만들어 작업
내용 을 지정한 다음 시작하도록 스레드에 제공합니다.

코드 분석

Runnable의 익명 내부 클래스를 사용하기 위해 몇 가지 사항을 분석 할 수 있습니다.

Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
public class Demo01Runnable {
public static void main(String[] args) {
// 匿名内部类
Runnable task = new Runnable() {
@Override
public void run() { // 覆盖重写抽象方法
System.out.println("多线程任务执行!");
}
};
new Thread(task).start(); // 启动线程
}
}
为了指定run的方法体,不得不需要Runnable接口的实现类;
为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
而实际上,似乎只有方法体才是关键所在。

3.3 프로그래밍 아이디어의 전환

방법이 아닌해야 할 일

정말로 익명의 내부 클래스 객체를 만들고 싶습니까? 하지 마라. 이를 위해 객체를 생성하면됩니다. 우리는 정말로하고 싶다

문제는 실행 메서드 본문의 코드를 Thread 클래스에 전달하여 알 수 있다는 것입니다.

코드를 전달하세요. 이것이 우리의 진정한 목적입니다. 객체 생성은 객체 지향 구문의 한계로 인해 채택되어야하는 방법 일뿐입니다.
그래서 더 쉬운 방법이 있습니까? "방법"에서 "해야 할 일"의 본질로 초점을 되 돌리면
목표를 더 잘 달성 할 수 있는 한 프로세스와 형식이 그다지 중요하지 않다는 것을 알게 될 것입니다 .

생활 예

베이징에서 상하이로 여행해야 할 때 고속철도, 자동차, 자전거 또는 도보를 선택할 수 있습니다. 우리의 진짜 목적은 상하이에 도착하는 것이지만 어떻게 거기에 갈 수 있습니까?

상하이의 형식은 중요하지 않기 때문에 우리는 고속철도 비행기보다 더 좋은 방법이 있는지 탐구 해 왔습니다.

그리고 이제 이런 종류의 비행기 (또는 우주선)가 탄생했습니다. Oracle이 2014 년 3 월에 출시 한 Java 8 (JDK 1.8)은 Lambda 표현의
무거운 새 기능 을 추가하여 우리에게 새로운 세계로의 문을 열어주었습니다.

3.4 Lambda의 더 나은 작성 경험

Java 8의 새로운 구문 덕분에 위의 Runnable 인터페이스의 익명 내부 클래스 작성은 더 간단한 Lambda 표현식을 통해 동등 할 수 있습니다.

이 코드는 지금의 실행 효과와 똑같으며 1.8 이상의 컴파일 수준에서 전달할 수 있습니다. 코드의 의미에서 볼 수 있습니다.

스레드가 시작되고 스레드 작업의 내용이보다 간결한 형식으로 지정됩니다.

더 이상 "인터페이스 객체를 만들어야한다"는 제한이없고 "추상적 인 메서드 덮어 쓰기 및 다시 쓰기"의 부담이 없습니다. 간단합니다!

3.5 익명의 내부 클래스 검토

Lambda는 객체 지향을 어떻게 극복합니까? 위의 예에서 핵심 코드는 실제로 다음과 같습니다.

Lambda의 의미를 이해하려면 기존 코드로 시작해야합니다.

구현 클래스 사용

스레드를 시작하려면 Thread 클래스의 개체를 만들고 start 메서드를 호출해야합니다. 스레드 실행 내용을 지정
하려면 Thread 클래스의 생성자 를 호출해야합니다 .

public Thread(Runnable target)

Runnable 인터페이스의 구현 객체를 얻기 위해 인터페이스에 대해 구현 클래스 RunnableImpl을 정의 할 수 있습니다.

그런 다음 Thread 클래스의 구성 매개 변수로 구현 클래스의 개체를 만듭니다.

익명 내부 클래스 사용

이 RunnableImpl 클래스는 Runnable 인터페이스를 구현하기 위해서만 존재하며 한 번만 사용되므로 익명 내부 클래스
구문을 사용하면 클래스, 즉 익명 내부 클래스의 별도 정의를 저장할 수 있습니다.

public class Demo02LambdaRunnable {
public static void main(String[] args) {
new Thread(() ‐> System.out.println("多线程任务执行!")).start(); // 启动线程
}
}
() ‐> System.out.println("多线程任务执行!")
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}
public class Demo03ThreadInitParam {
public static void main(String[] args) {
Runnable task = new RunnableImpl();
new Thread(task).start();
}
}

익명 내부 클래스의 장단점

한편으로 익명 내부 클래스는 구현 클래스의 정의를 저장하는 데 도움이 될 수 있습니다. 다른 한편으로 익명 내부 클래스의 구문은 너무 복잡합니다!

의미 분석

코드의 의미를주의 깊게 분석하면 Runnable 인터페이스에는 실행 메서드에 대한 정의가 하나만 있습니다.

public abstract void run();

즉, 일을 할 계획 (실제로는 함수)이 공식화됩니다.

无参数:不需要任何条件即可执行该方案。
无返回值:该方案不产生任何结果。
代码块(方法体):该方案的具体执行步骤。

동일한 의미가 Lambda 구문에 반영되어 더 간단합니다.

前面的一对小括号即run方法的参数(无),代表不需要任何条件;
中间的一个箭头代表将前面的参数传递给后面的代码;
后面的输出语句即业务逻辑代码。

3.6 Lambda 표준 형식

Lambda는 객체 지향 규칙 및 규정을 제거하며 형식은 다음 세 부분으로 구성됩니다.

一些参数
一个箭头
一段代码

람다 식의 표준 형식은 다음과 같습니다.

형식 설명 :

괄호 안의 구문은 기존 메소드 매개 변수 목록과 일치합니다. 매개 변수가 없으면 공백으로 두십시오. 여러 매개 변수는 쉼표로 구분됩니다.

->는 포인팅 액션을 의미하는 새로 도입 된 문법 형식입니다.

중괄호 안의 문법은 기본적으로 전통적인 방법과 동일합니다.

3.7 연습 : Lambda 표준 형식 사용 (매개 변수 및 반환 없음)

public class Demo04ThreadNameless {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}).start();
}
}
() ‐> System.out.println("多线程任务执行!")

(매개 변수 유형 매개 변수 이름)-> {code statement}

3.7 연습 : Lambda 표준 형식 사용 (매개 변수 및 반환 없음)

이야기

cook 인터페이스가 주어지면 유일한 추상 메소드 makeFood를 포함하고 매개 변수도없고 반환 값도 없습니다. 다음과 같이 :

다음 코드에서 Lambda의 표준 형식을 사용하여 invokeCook 메서드를 호출하고 "Eating!"이라는 단어를 인쇄하십시오.

대답

备注:小括号代表Cook接口makeFood抽象方法的参数为空,大括号代表makeFood的方法体。

3.8 Lambda 파라미터 및 반환 값

다음 예제는 추상 메소드가 다음과 같이 정의 된 java.util.Comparator 인터페이스의 사용 시나리오 코드를 보여줍니다.

public abstract int compare(T o1, T o2);

개체 배열을 정렬해야하는 경우 Arrays.sort 메서드에는 정렬 규칙을 지정하기 위해 Comparator 인터페이스의 인스턴스가 필요합니다.
두 개의 멤버 변수 인 String name과 int age 있는 Person 클래스 가 있다고 가정합니다 .

public interface Cook {
void makeFood();
}
public class Demo05InvokeCook {
public static void main(String[] args) {
// TODO 请在此使用Lambda【标准格式】调用invokeCook方法
    }
private static void invokeCook(Cook cook) {
cook.makeFood();
    }
}
public static void main(String[] args) {
invokeCook(() ‐> {
System.out.println("吃饭啦!");
    });
}

수요:

使用数组存储多个Person对象
对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序

전통 쓰기

기존 코드를 사용하여 Person [] 배열을 정렬하는 경우 작성 방법은 다음과 같습니다.

이 접근 방식은 객체 지향적 사고에서 "물론"인 것처럼 보입니다. (익명 내부 클래스를 사용하는) Comparator 인터페이스의 인스턴스는
"가장 오래된 것부터"라는 정렬 규칙을 나타냅니다 .

코드 분석

위의 코드가 실제로 무엇을하는지 알아 봅시다.

为了排序,Arrays.sort方法需要排序规则,即Comparator接口的实例,抽象方法compare是关键;
为了指定compare的方法体,不得不需要Comparator接口的实现类;
为了省去定义一个ComparatorImpl实现类的麻烦,不得不使用匿名内部类;
必须覆盖重写抽象compare方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
实际上,只有参数和方法体才是关键。

Lambda 쓰기

public class Person {
private String name;
private int age;
// 省略构造器、toString方法与Getter Setter
}
import java.util.Arrays;
import java.util.Comparator;
public class Demo06Comparator {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("古力娜扎",  19 ),
new Person("迪丽热巴",  18 ),
new Person("马尔扎哈",  20 ) };
// 匿名内部类
Comparator<Person> comp = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() ‐ o2.getAge();
            }
        };
Arrays.sort(array, comp); // 第二个参数为排序规则,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
        }
    }
}

3.9 연습 : Lambda 표준 형식 사용 (참여 및 반환 포함)

이야기

계산기 인터페이스가 주어지면 합계를 얻기 위해 두 개의 정수를 더하는 추상 메서드 calc를 포함합니다.

다음 코드에서 Lambda의 표준 형식을 사용하여 invokeCalc 메서드를 호출하여 120 및 130의 더하기 계산을 완료하십시오.

대답

import java.util.Arrays;
public class Demo07ComparatorLambda {
public static void main(String[] args) {
Person[] array = {
new Person("古力娜扎",  19 ),
new Person("迪丽热巴",  18 ),
new Person("马尔扎哈",  20 ) };
Arrays.sort(array, (Person a, Person b) ‐> {
return a.getAge() ‐ b.getAge();
        });
for (Person person : array) {
System.out.println(person);
        }
    }
}
public interface Calculator {
int calc(int a, int b);
}
public class Demo08InvokeCalc {
public static void main(String[] args) {
// TODO 请在此使用Lambda【标准格式】调用invokeCalc方法来计算120+130的结果ß
    }
private static void invokeCalc(int a, int b, Calculator calculator) {
int result = calculator.calc(a, b);
System.out.println("结果是:" + result);
    }
}
public static void main(String[] args) {
invokeCalc( 120 ,  130 , (int a, int b) ‐> {
return a + b;
    });
}
备注:小括号代表Calculator接口calc抽象方法的参数,大括号代表calc的方法体。

3.10 Lambda 생략 형식

파생되거나 생략 될 수 있습니다.

Lambda는 "방법"보다는 "무엇"을 강조하므로 컨텍스트에서 파생 될 수있는 모든 정보를 생략 할 수 있습니다. 예를 들어 위의 예
에서는 Lambda를 생략 할 수도 있습니다.

생략 규칙

Lambda 표준 형식에 따라 생략 사용 규칙은 다음과 같습니다.

1. 小括号内参数的类型可以省略;
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
备注:掌握这些省略规则后,请对应地回顾本章开头的多线程案例。

3.11 연습 : Lambda를 사용하여 형식 생략

이야기

여전히 makeFood의 유일한 추상 메서드가 포함 된 이전 cook 인터페이스를 사용하고 있습니다. 다음 코드에서 Lambda의 생략 된 형식을 사용하여
invokeCook 메서드 를 호출하고 "Eating!"이라는 단어를 출력하십시오.

대답

3.12 Lambda 사용을위한 사전 조건

public static void main(String[] args) {
invokeCalc( 120 ,  130 , (a, b) ‐> a + b);
}
public class Demo09InvokeCook {
public static void main(String[] args) {
// TODO 请在此使用Lambda【省略格式】调用invokeCook方法
    }
private static void invokeCook(Cook cook) {
cook.makeFood();
    }
}
public static void main(String[] args) {
invokeCook(() ‐> System.out.println("吃饭啦!"));
}

Lambda의 구문은 객체 지향 복잡성의 제약없이 매우 간결합니다. 그러나 다음을 사용할 때 특별한주의가 필요한 몇 가지 문제가 있습니다.

1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一
时,才可以使用Lambda。
2. 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

추천

출처blog.csdn.net/weixin_43419256/article/details/108230718