함수
package 함수;
public class JavaTest{
static void myFunction(){ // static 으로 선언된 함수는 static으로 선언된 함수에서 선언 가능
System.out.println("return 값을 가져갈 수 없음");
}
static void myAdd(int num){ // 매개변수 int num
System.out.println("매개변수 하나가 있습니다." + num);
}
static void myAdd(String a, String b){
System.out.println(a + b);
}
// 오버로딩: 함수 이름이 같지만 매개변수의 개수나 자료형이 다른 경우 호출 가능
int myAdd(int a, int b){
return a+b;
}
public static void main(String[] args) {
JavaTest.myFunction();
// 함수에 인자값 5를 전달
JavaTest.myAdd(5);
JavaTest.myAdd("지", "연");
// 인스턴스 생성
JavaTest jt = new JavaTest();
int result = jt.myAdd(10, 13);
System.out.println("int myAdd: " + result);
}
}
✅ 오버로딩: 함수 이름이 같지만 그 안의 매개변수의 개수나 자료형이 다를 경우
✅ static: 정적 메소드는 클래스가 메모리에 올라갈 때 정적 메소드가 자동적으로 생성된다. 따라서 호출 할 때 인스턴스를 생성해주지 않아도 된다.
✅ 그러나 int myAdd() 함수는 static 메소드가 붙지 않았기 때문에 인스턴스를 생성해서 호출해줘야 한다.
예외 처리
import java.util.Scanner;
public class JavaTest{
public static void main(String[] args) {
try{
// 사용자로부터 입력 받기
System.out.println("값 입력");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] members = {1, 2, 3, 4, 5};
System.out.println("배열의 값: " + members[n]);
// 무조건 오류 출력
// throw new Exception("사용자오류");
}
catch (ArrayIndexOutOfBoundsException e){ // 배열의 요소를 초과하였을 때
System.out.println(e.getMessage());
}
catch (Exception e){ // 예외가 발생하였을 때
System.out.println(e.getMessage());
}
finally{ // 오류가 발생하더라도, 발생하지 않더라도 문구 출력
System.out.println("오류가 발생하였습니다.");
}
}
}
접근 제어자
접근 권한 범위: public > protected > default > private
private: private로 선언된 함수, 멤버변수, 생성자는 선언된 클래스 내부에서만 접근 가능
default: 접근 제어자를 사용하지 않을 경우 클래스, 함수, 멤버변수, 생성자는 동일한 패키지에서만 접근 가능
protected: 함수, 멤버변수, 생성자는 동일 패키지 혹은 다른 패키지의 하위 클래스에서만 접근 가능
public: public으로 선언된 함수, 멤버변수, 생성자는 모든 다른 클래스에서 접근 가능
- final: 클래스 사용 시 다른 클래스에서 상속 불가능, 매소드, 멤버 변수에 사용 시 오버라이딩 수정 불가
- static: 매소드, 멤버변수에 사용, 클래스에 속하게 되며 객체의 생성 없이 바로 접근 가능
- abstract: 클래스 사용시 객체 생성 불가능, 오직 다른 클래스가 상속 받아 사용해야 함
- 메소드 상속시 오직 abstract 클래스에서만 abstract 메소드 정의 가능, 메소드는 바디(중괄호) 부분이 없음
- 메소드 예: abstract void run(): 바디 부분은 상속받은 곳에서 기능에 맞게 작성
package 접근제어자;
public class JavaTest{
public static void main(String[] args) {
// 접근 권한 범위: public > protected > default > private
JavaTests javaTest = new JavaTests();
System.out.println("값 열기 " + javaTest.var);
System.out.println("문자열 열기 " + javaTest.str);
// 값과 문자열 초기화
int var = 0;
String str = " ";
JavaTests javaTest2 = new JavaTests(5, "100");
System.out.println("값 열기 " + javaTest2.var);
System.out.println("문자열 열기 " + javaTest2.str);
// javaTest2.a = 5 final로 선언한 변수는 값을 변경하는 것이 불가능
System.out.println(javaTest2.a);
// System.out.println(javaTest2.abc); private로 선언한 변수는 외부에서 가져와 사용하는 것이 불가능
javaTest2.setAbc(100);;
System.out.println("private 요소 얻기: " + javaTest2.getAbc());
}
}
package 접근제어자;
public class JavaTests {
private int abc; // 외부에서 접근 불가능
final int a = 5; // 값 변경 불가능
public int var;
public String str;
public JavaTests(){
var = 01;
str = "null1";
}
public JavaTests(int a, String s){
var = 02;
str = "null2";
}
public int getAbc() { // 값을 가져오고
return abc;
}
public void setAbc(int abc) { // 값을 할당
this.abc = abc;
}
}
캡슐화
'데이터의 은닉'을 목적으로 함
- 민감한 데이터를 private로 감추어서 public 의 setter 또는 getter 메소드로만
private 으로 감춘 데이터에 접근하거나 변경할 수 있음
- 따라서 이러한 private 변수에 read-only(읽기 전용-getter만 제공) 또는 write-only(변경만 가능-setter만 제공)한 선택적 접근을 제어할 수 있음
- public 보다 보안성이 증가하여 멤버변수와 함수를 더 좋게 제어할 수 있음
데이터의 은닉 이외에도 아래와 같은 목적이 있음
- 유연성: 같은 타입을 이루는 자료형에서 Map, Stack, Queue 와 같은 Collection 변경을 클래스 내부의 일부분만 변경하여 자료형을 변경할 수 있음
- 초기화 작업을 클래스에 모아서 처리함으로써 초기화 중에 데이터의 가공이 일어날 경우 일부분만 변경하면 됨
상속
부모 클래스가 자식 클래스를 상속
- 상속받을 때는 extends 키워드 사용
package 상속;
// 부모, 상위 클래스
public class Vehicle {
protected String model = "홍길동";
public void sound(){
System.out.println("정적이 울렸습니다.");
}
}
package 상속;
// 자식, 하위 클래스
public class Car extends Vehicle{
public String handle =" ";
public void run()
{
System.out.println("경적이 울립니다.");
}
public void sound()
{
System.out.println("경적이 울렸습니다.");
}
}
package 상속;
public class JavaTest {
public static void main(String[] args) {
Car car = new Car(); // 자식 클래스의 인스턴스
System.out.println("부모 클래스의 요소: " + car.model);
car.sound();
System.out.println("자식 클래스의 요소: " + car.handle);
}
}
- 같은 패키지 내 클래스에서 사용 가능함
- 상속은 코드를 재사용하는데 유용
- 이미 존재하고 검증된 클래스의 멤버변수와 메소드를 재사용 하여 새로운 클래스를 만들 때 사용
다형성
- 여러 가지 자료형을 가질 수 있는 것을 의미
- 앞의 상속에서 멤버변수, 메소드를 다른 클래스로부터 물려 받음
- 다형성은 이러한 상속받은 메소드들을 동일하게 호출하여 다른 작업을 수행하는 것을 가능하게 함
package 다형성;
// 부모, 상위 클래스
public class Animal {
public void animalBark(){
System.out.println("동물 소리");
}
}
package 다형성;
// 자식, 하위 클래스
public class Bird extends Animal{
public void animalBark(){
System.out.println("짹짹");
}
}
package 다형성;
// 자식, 하위 클래스
public class Dog extends Animal{
public void animalBark() {
System.out.println("멍멍");
}
}
package 다형성;
// 자식, 하위 클래스
public class Owl extends Animal{
public void animalBark(){
System.out.println("부엉부엉");
}
}
package 다형성;
public class JavaTest {
public static void main(String[] args) {
/*
Animal animal = new Animal();
Animal bird = new Bird();
Animal dog = new Dog();
Animal owl = new Owl();
animal.animalBark();
bird.animalBark();
dog.animalBark();
owl.animalBark();
*/
// 쉬운 교체로 기존 코드의 재사용
// 동일한 데이터 타입으로 다양한 작업 수행
Animal animal = new Owl();
animal.animalBark();
// 코드의 확장
if (animal instanceof Owl) {
System.out.println("부엉이 입니다.");
}
}
}
오버라이딩
- 부모 클래스의 메소드를 자식 클래스에서 재정의 하여 사용하는 것
오버로딩
- 같은 이름의 매소드를 여러개 가지면서 매개변수의 개수, 유형이 다른 것
- 실무에서는 기능이 추가되거나 변경될 때 사용
추상화
- 메소드의 세부 사항을 숨기고, 메소드의 필수적인 정보만 보여주는 과정
- 이러한 추상화를 추상 클래스 또는 인터페이스라 함
- Abstract class: 객체 생성이 불가능하며 상속 받아서 해당 기능을 사용해야 함
- Abstract method: Abstract class 에서만 Abstract method 생성이 가능하며, 중괄호가 없는 것이 특징 ({,} 없음)
- 추상 클래스 Car
package 다형성;
abstract class Car {
public abstract void start();
// abstract 클래스는 무조건 abstract 메소드를 재정의 해줘야 함
public void accelerate(){
System.out.println("가속합니다.");
}
}
- Car의 자식 클래스
package 다형성;
public class X500Car extends Car{
// 오버라이딩: 부모 클래스의 메소드를 자식 클래스에서 재정의 하여 사용하느 것
@Override
public void start() {
System.out.println("X500 모델의 시작");
}
}
- main 클래스
package 다형성;
public class JavaTest {
public static void main(String[] args) {
X500Car x500Car = new X500Car();
x500Car.accelerate();
x500Car.start();
}
}
인터페이스
- 인터페이스는 완전한 추상 클래스임
- 인터페이스는 멤버 변수를 가지고 있지 않음
- 메소드의 바디{} 부분이 비어 있으며, 관련된 메소드들을 그룹화하는데 사용함
- 인터페이스는 new를 통한 객체 생성 불가
- 인터페이스는 생성자를 포함할 수 없음
- 인터페이스를 구현한 클래스는 모든 메소드를 오버라이드로 재정의를 해야 함
package 인터페이스;
public interface Menu {
public void coffee();
public void water();
public void drink();
}
package 인터페이스;
public interface State {
public void hot();
public void ice();
}
- 인터페이스의 핵심은 implements 라는 키워드를 사용
- interface 키워드를 통해 인터페이스 생성
- 인터페이스에는 메소드의 이름, 타입만 적어줌
package 인터페이스;
public class Client implements Menu, State {
@Override
public void coffee() {
System.out.println("아메리카노 나왔습니다.");
}
@Override
public void water() {
System.out.println("물 나왔습니다.");
}
@Override
public void drink() {
System.out.println("음료 나왔습니다.");
}
@Override
public void hot() {
System.out.print("따뜻한 ");
}
@Override
public void ice() {
System.out.print("차가운 ");
}
}
- Menu, State 인터페이스를 implements
package 인터페이스;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
Client cnt = new Client();
System.out.println("어서오세요 손님, 어떤 걸로 드릴까요? (아메리카노, 물, 음료)");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String order = br.readLine();
System.out.println("음료는 hot/ice 중, 어떤 상태로 드릴까요? ");
BufferedReader brState = new BufferedReader(new InputStreamReader(System.in));
String state = brState.readLine();
switch (order){
case "아메리카노":
if(state.equals("hot")){
cnt.hot();
cnt.coffee();
}
else if(state.equals("ice")){
cnt.ice();
cnt.coffee();
}
break;
case "물":
if(state.equals("hot")){
cnt.hot();
cnt.water();
}
else if(state.equals("ice")){
cnt.ice();
cnt.water();
}
break;
case "음료":
if(state.equals("hot")){
cnt.hot();
cnt.drink();
}
else if(state.equals("ice")){
cnt.ice();
cnt.drink();
}
break;
}
}
}
출력 결과
장점
- 다중 인터페이스를 구현함으로써 대체 가능
ex) implements Menu, State
내부 클래스
- 클래스 안에 클래스 선언이 가능함
- 이러한 내부 클래스를 내포하는 클래스와 내부 클래스는 더욱 밀접한 관계를 가지게 되며, 코드를 읽기 쉬워질 뿐만 아니라 서로 연관되어 있기 때문에 관리와 파악이 쉬움
package 내부클래스;
public class StudyJava {
public int a = 5;
// 내부 클래스
// 외부 클래스의 멤버 변수를 사용하고 싶기 때문에 static 메서드 붙이지 않는다.
class InnerTest{
int b = 7;
public int add() {
return a+b;
}
}
}
package 내부클래스;
public class JavaTest {
public static void main(String[] args) {
StudyJava studyJava = new StudyJava();
// StudyJava 객체 안에 있는 내부 클래스인 InnerTest 선언
StudyJava.InnerTest innerTest = studyJava.new InnerTest();
System.out.println("더한 값은? " + innerTest.add());
}
}
열거형
- 서로 관련 있는 상수들을 모아둔 클래스와 비슷한 형식
- 제약 사항들
- 다른 클래스를 상속받을 수 없음
- 객체의 생성 불가
- 값의 재할당 불가
- 보통 메소드는 생성하지 않음
package 열거형;
import javax.swing.*;
public class JavaTest {
// 열거형 선언
public enum Season{
// 멤버 변수에 public static final 키워드가 붙은 것과 동일
// 사용할 상수는 대문자로 적는 것이 원칙
// 예) 주로 변하지 않는 것들: 모델 타입, 요일, 계절 등..
Spring, Summer, Autumn, Winter;
}
public static void main(String[] args) {
// 열거형에 선언된 상수들 중 하나로 일단 넣어줌
Season season = Season.Spring;
switch (season){
case Spring:
System.out.println("봄이다!");
break;
case Summer:
System.out.println("여름이다!");
break;
case Autumn:
System.out.println("가을이다!");
break;
case Winter:
System.out.println("겨울이다!");
break;
}
System.out.print("열거형에서 해당 계절이 적힌 순서는 ");
System.out.println(season.ordinal() + " 입니다.");
// season = 0;
}
}
ArrayList
- 배열 대신 자주 사용되며, 배열과 달리 크기를 조절할 수 있음
- 자바의 List 인터페이스를 상속받고 있음
- 배열의 크기는 한 번 정해지면 크기를 변경할 수 없고, 속도가 약간 더 뛰어남
- 그러나 ArrayList와 배열의 사용 방법은 전혀 다름
package ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class JavaTest {
public static void main(String[] args) {
// String data
ArrayList<String> arrayList = new ArrayList<String>();
// int data
// ArrayList<Integer> arrayList1 = new ArrayList<Integer>();
arrayList.add("봄"); // 0
arrayList.add("여름"); // 1
arrayList.add("가을"); // 2
arrayList.add("겨울"); // 3
System.out.println("계절 뽑기: " + arrayList.get(0));
System.out.println("ArrayList 크기: " + arrayList.size());
// ArrayList에 저장된 값 변경 .set(인덱스 값, 변경하고자 하는 값)
arrayList.set(0, "벚꽃");
// 변경한 값 출력
System.out.println("봄 -> " + arrayList.get(0));
// arrayList의 상위 클래스 : List
List list;
list = arrayList;
arrayList.set(0, "봄");
System.out.println(list.get(0)); // List 인터페이스에 포함된 모든 list들은 이렇게 사용 가능
System.out.println(arrayList.get(0));
// ArrayList 정렬할 때엔 Collections.sort()
// 지금 ArrayList에는 한글 문자가 들어가 있으므로 ㄱ~ㅎ 순서대로 정렬
// 영어는 A~ 부터 순서대로 정렬
// 숫자는 마이너스 포함해서 가장 작은 값부터 ~ 순서대로 정렬
Collections.sort(arrayList);
// arrayList.size(): arrayList 크기만큼 반복하기
for(int i =0; i<arrayList.size(); i++){
System.out.println("List 정렬 (ㄱ~ ): " + arrayList.get(i));
}
}
}
HashMap
- HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션
- Map 인터페이스의 기능들을 모두 구현할 수 있음
- HashMap을 사용하면 시간 복잡도가 O(1) 로 효율적으로 사용 가능
- 키와 값의 1:1 저장 ex) "101" 혹은 "apple" 이라는 키로 "사과" 값을 저장하거나 얻을 수 있음
- 키와 값의 자료형은 서로 달라도 괜찮음
package HashMap;
import java.util.HashMap;
public class JavaTest {
public static void main(String[] args){
HashMap<String, Object> hashMap = new HashMap<String, Object>();
// HashMap<key, value>
hashMap.put("fruit", "복숭아");
hashMap.put("userVALUE", 23);
hashMap.put("userID", "아이디");
hashMap.put("userNAME", "이름");
// hashMap.put(key) 하면 해당 key에 해당하는 value 출력 됨
System.out.println("hashMap.get(key value)을 통한 값 얻기: " + hashMap.get("fruit"));
// get 함수의 data 자료형이 object이기 때문에 오류가 발생
// int var = hashMap.get("userNAME");
// String 타입의 key: "userVALUE" 의 value: 300 출력 hashMap<String, Object>
// 여기서 Object는 int 타입
int var = Integer.parseInt(hashMap.get("userVALUE").toString());
// String 타입인 key: "101"의 value: "복숭아" 출력 hashMap<String, Object>
// 여기서 Object는 String 타입
String str = hashMap.get("fruit").toString();
// hashMap의 size 찾기
System.out.println("hashMap의 size: " + hashMap.size());
// for( key 값을 가져올 변수 i : key에 해당 되는 값을 가져옴)
for (String i : hashMap.keySet()) {
System.out.println("저장된 key: " + i);
System.out.println(hashMap.get(i));
}
}
}
package HashMap;
import java.util.HashMap;
public class JavaTest {
public static void main(String[] args){
HashMap<Integer, String> hashMap = new HashMap<Integer, String>();
hashMap.put(01, "key(01)- value");
hashMap.put(02, "key(02)- value");
hashMap.put(03, "key(03)- value");
hashMap.put(04, "key(04)- value");
hashMap.put(05, "key(05)- value");
// <Integer, String>
// int value = Integer.parseInt(hashMap.get(01).toString());
// System.out.println(value);
// key: 01
// hashMap.get() 을 통해 key에 저장된 값 얻어오기
String str = hashMap.get(01).toString();
System.out.println(str);
System.out.println("hashMap 크기: " + hashMap.size());
for(Integer i : hashMap.keySet()){
System.out.println("key: " + i);
// key에 저장된 값 얻어오기
System.out.println(hashMap.get(i));
}
}
}
Stack
- LIFO(Last In First Out)
- 맨 나중에 넣은 값이 가장 먼저 나오게 됨
ex) 1,2,3,4,5 순서대로 넣고 -> 5,4,3,2,1 순으로 나옴
- push (값 넣기) pop (값 뽑기)
package Stack;
import java.util.Stack;
public class JavaTest {
public static void main(String[] args){
Stack<Integer> stack = new Stack<Integer>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
System.out.println("저장된 값을 하나씩 출력(Stack): " + stack.pop());
}
}
Queue
- FIFO(First In First Out)
- 가장 먼저 넣은 값이 가장 먼저 나옴
- java에서는 Queue를 인터페이스로 지원함
- add (값 넣기) poll (값 뽑기)
package Queue;
import java.util.LinkedList;
import java.util.Queue;
public class JavaTest {
public static void main(String[] args){
Queue<Integer> queue = new LinkedList<Integer>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
queue.add(5);
System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
System.out.println("저장된 값을 하나씩 출력(Queue): " + queue.poll());
}
}
Stream
- 스트림은 단일 방향의 흐름을 의미
- 프로그램이 데이터를 입력 받을 때 사용하는 입력 스트림
- 프로그램이 데이터를 내보낼 때 사용하는 출력 스트림
package Stream;
import java.io.*;
public class JavaTest {
public static void main(String[] args) throws FileNotFoundException {
try {
// 복사 할 파일의 경로와
// 복사 될 파일의 경로를 적음
String testFile = "C:\\Users\\study\\test.txt";
String copyFile = "C:\\Users\\study\\copy.txt";
// 복사할 파일의 내용을 읽을 변수
FileInputStream input = new FileInputStream(testFile);
// 복사 될 파일에 내용을 쓰기 위한 변수
FileOutputStream output = new FileOutputStream(copyFile);
// 문자가 얼마나 읽어졌나에 대한 숫자를 확인하기 위한 변수
int readByteNum;
// 읽어드릴 문자열을 저장하기 위한 byte 배열
byte[] bytes = new byte[100];
// 파일 복사가 이루어지는 동작
while ((readByteNum = input.read(bytes)) != -1){ // testFile에 적힌 내용응 bytes 배열에 저장
// 0부터 시작해서 읽은 byte 단위까지 읽을 것임
output.write(bytes, 0, readByteNum);
}
// 사용한 자원을 닫음
output.flush();
output.close();
input.close();
System.out.println("Copy finished! - - - ");
} catch (IOException e) {
System.out.println("error: " + e.getMessage());
}
}
}
- 복사가 완료된 모습을 확인
스레드
- 비디오 재생 프로그램은 화면 출력과 동시에 오디오 출력도 가능
- 이처럼 한 개의 프로그램에서 여러 작업을 가능하게 해주는 것이 '스레드'
package thread;
public class Sound implements Runnable{
@Override
public void run() {
// thread: start() 호출 시에 동작하는 함수
for (int i = 0; i < 5; i++) {
try {
System.out.println("== == == sound == == == ");
Thread.sleep(300); // 0.3초마다 출력
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
package thread;
public class Main {
public static void main(String[] args) {
Sound sound = new Sound();
Thread thread = new Thread(sound);
thread.start(); // Sound 클래스의 thread 가 동작
for (int i = 0; i < 5; i++) {
try {
System.out.println("-- -- -- Main -- -- --");
Thread.sleep(500); // 0.5초마다 출력
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
+ 비동기: 스레드에서 각자의 작업을 서로 병행하면서 실행
- 다수의 작업에 대해 병렬작업을 진행하는 것을 의미
+ 동기: 하나하나 작업을 처리해가는 방식
- 멀티스레드 환경에서 같은 자원(변수)을 사용하면 그 자원이 다른 스레드가 작업을 끝날 때까지 기다리는 교착상태가 발생할 수 있음
싱글톤 패턴
- 시스템 설계에서 다루는 내용
- 싱글톤 패턴은 클래스의 인스턴스가 하나만 생성되도록 하는 개발 방법임
적용되는 사례)
- 응용프로그램에서 단일 창만 띄우거나 클래스의 객체를 생성할 때 사용
- Spring은 사용자의 요청이 매우 많을 수 있기 때문에 그 때마다 많은 객체를 생성할 수 없음 -> 하나의 객체를 생성하여 스레드로 공유하는 싱글톤 패턴과 비슷한 방식을 사용
package SingleTon;
// 멀티 환경에 취약
// 가장 기본적인 싱글톤 형식으로 개발 상황에 맞게 변경이 필요
public class SingleTon {
// 자기 자신의 객체를 생성
private static SingleTon singleTonObject;
// 다른 객체의 생성을 막음
private SingleTon() {
}
public static SingleTon getSingleTonObject() {
if (singleTonObject == null) {
singleTonObject = new SingleTon();
}
return singleTonObject;
}
}
'Study' 카테고리의 다른 글
[STUDY] 스프링 입문 기록(1) - 코드로 배우는 스프링부트, 웹 MVC, DB 접근 기술 (0) | 2023.02.23 |
---|---|
[디자인 패턴] MVC, MVP, MVVM (0) | 2022.07.26 |
[STUDY] TCP, UDP와 HTTP 프로토콜 (0) | 2022.06.20 |
[JAVA] 동기, 비동기와 멀티스레딩 (0) | 2022.06.17 |
[JAVA] Process(프로세스)와 Thread(스레드) (0) | 2022.06.01 |