프로그래밍/java

JAVA 프로그래밍 55일차 (25/2/4)

wkun 2025. 2. 4. 17:42

contents

 

chapter12

1.스레드란?

1)스레드와 프로세서

2.스레드의 생성

1)Thread 클래스 상속받아서 만들기

2)Runnable 인터페이스 구현

3.스레드의 우선순위

1)스레드의 우선순위 지정

4.스레드의 라이플사이클

1)sleep()

2)yield()

3)join()

5.스레드의 동기화

6.wait()과 notify()

chapter13

1.IO와 스트림(Stream)

1)IO란?

2)스트림(Stream)이란?

2.바이트기반 스트림

1)바이트기반 스트림의 활용

3.바이트기반 보조 스트림

1)바이트기반 보조 스트림의 활용

4.문자기반 스트림

1)문자 기반 스트림의 활용

5.문자기반 보조 스트림

1)문자 기반 보조 스트림의 활용

6.스트림의 예외 처리

<map 방식으로 회원관리프로그램을 만드는 코드>

 

 

-------------------------------------------------

 

 

chapter12

 

1.스레드란?

-스레드는 하나의 프로세스 안에서 두 가지 이상의 일을 하도록 도와줌

 

1)스레드와 프로세서

-프로그램을 실행시키면 메모리가 할당되어 실행되는데 이 실행 중인 프로그램을 프로세스라고 함. 프로세스에서 작업을 수행하는 것이 스레드(Thread)이며 두 가지 이상의 작업을 하려면 두 개 이상의 스레드가 필요함. 그래서 두 가지 이상의 작업을 하는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 함

-프로세스가 실행되는 방식에는 두 가지가 있음

(1)시간분할 방식

-시간분할 방식은 모든 프로세스에게 동일한 시간을 할당하고 골고루 실행함

(2)선점형 방식

-선점형 방식은 각각의 프로세스에게 우선순위를 부여하고 우선순위가 높은 순으로 실행되는 방식임. 우선순위가 높은 프로세스가 잠시 멈추면 그다음 순위의 프로세스가 동작함

 

2.스레드의 생성

-스레드를 만드는 방법은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있음. Thread 클래스를 상속받아서 사용하는 것이 간단하지만 인터페이스를 사용하면 다중상속이 가능하기 때문에 주로 Runable을 사용함

 

1)Thread 클래스 상속받아서 만들기

-Thread 클래스를 상속받고 public void run() 메서드를 오버라이딩함

class Th1 extends Thread {
public void run() {
         // 작업할 내용 ->병렬처리
}
}

-실행할 때는 인스턴스를 생성하고 start() 메서드를 통해서 실행함

Th1 t1 = new Th1(); //인스턴스 생성
t1.start(); //스레드 실행

 

2)Runnable 인터페이스 구현

-Runnable 인터페이스를 구현하고 추상 메서드인 run()을 오버라이딩함

class Th2 implements Runnable {
public void run() {
// 작업할 내용 작성
}
}

 

-실행할 때는 Runnable 인터페이스를 구현한 클래스의 인스턴스를 먼저 생성한 다음, 그 인스턴스를 Thread 클래스의 인수로 전달하여 스레드를 생성함. 그리고 start() 메서드를 통해 실행함

Th2 t2 = new Th2();
Thread t = new Thread(t2);
t.start();

 

<Thread 클래스 상속과 Runnable 인터페이스 구현한 스레드를 각각 실행하는 클래스>

package ch12;


class MyThread extends Thread{
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println("스레드 진행 중" + i);
}
}
}


class MyThread2 implements Runnable{
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println("러너블 진행 중" + i);
}
}
}
public class exam93 {

public static void main(String[] args) {
MyThread th1 = new MyThread(); //Thread 상속한 클래스
Runnable th2 = new MyThread2(); //Runnable 상속한 클래스
Thread t = new Thread(th2);
th1.start(); //병렬처리
t.start();
for(int i =0; i < 10; i++) {
System.out.println("메인 함수 진행 중" + i);
}


}


}

 

실행 결과

메인 함수 진행 중0
러너블 진행 중0
스레드 진행 중0
러너블 진행 중1
메인 함수 진행 중1
러너블 진행 중2
스레드 진행 중1
스레드 진행 중2
스레드 진행 중3
스레드 진행 중4
러너블 진행 중3
메인 함수 진행 중2
러너블 진행 중4
스레드 진행 중5
러너블 진행 중5
메인 함수 진행 중3
러너블 진행 중6
스레드 진행 중6
러너블 진행 중7
메인 함수 진행 중4
러너블 진행 중8
스레드 진행 중7
러너블 진행 중9
메인 함수 진행 중5
메인 함수 진행 중6
메인 함수 진행 중7
메인 함수 진행 중8
메인 함수 진행 중9
스레드 진행 중8
스레드 진행 중9

 

-각가 스레드를 호출해서 실행함. 실행결과를 보면 스레드를 호출한 순서에 상관없이 메인 함수, 러너블, 스레드가 골고루 실행되고 있음. 참고로 main 부분의 작업을 수행하는 것도 스레드임

 

3.스레드의 우선순위

-사용자는 각각의 스레드에 우선순위를 부여할 수 있음

 

1)스레드의 우선순위 지정

-스레드는 시분할 방식으로 CPU의 시간을 분배하여 실행됨. 다만 사용자가 직접 스레드의 우선순위를 지정해서 특정 스레드에 더 많은 실행시간을 갖게 함. 예를 들면 영화를 다운받으면서 웹으로 당장 내일 제출해야 하는 과제를 할 때, 영화 다운로드보다 현재 작업하는 웹 검색의 우선순위를 높게 설정하는 것이 사용자에게 유리함. 이런 경우에 직접 우선순위를 지정함

void setPriority(int new Priority); 우선순위를 설정함
int getPriority(); 우선순위를 반환함

 

4.스레드의 라이플사이클

-스레드는 현재 상태에 따라 네 가지 상태로 분류할 수 있으며 상태가 변화하는 주기를 라이플 사이클이라고 함

(1)new

-스레드가 키워드 new를 통해서 인스턴스화된 상태임. Runable이 될 수 있는 상태이며 아직 대기열에 올라가지 않은 상태임

(2)Runnable

start() 메서드가 호출되면 new 상태에서 Runnable 상태가 됨. Runnable 상태가 되면 실행할 수 있는 상태로 대기하게 됨. 스케줄러에 의해 선택이 되면 run() 메서드를 바로 수행함

(3)Blocked

-실행중인 스레드가 sleep(), join() 메서드를 호출하게 되면 Blocked 상태가 됨. Blocked 상태가 되면 스케줄러에 의해서 선택받을 수 없음

(4)Dead

-run() 메서드의 실행을 모두 완료하게 되면 스레드는 Dead 상태가 됨. 할당 받은 메모리와 정보가 삭제됨

cf) .close()

 

1)sleep()

-sleep() 메서드는 스레드를 지정된 시간 동안 block 상태로 만듬. 시간은 1000분의 1초까지 지정할 수 있으며 지정된 시간이 지나면 다시 runnable 상태로 돌아가게 됨

 

<sleep()의 block 효과를 이용해서 카운트다운을 하는 코드>

package ch12;


class SleepThread extends Thread{
public void run() {
System.out.println("카운트다운 5초");
for(int i = 5; i >= 0; i--) {//5부터 시작
System.out.println(i);
if(i!=0) { //0초인 경우에는 1초를 기다리지 않음
try {
Thread.sleep(1000); //1초동안 스레드 block
}catch(InterruptedException ie){
System.out.println(ie.toString());
}
}
}
System.out.println("종료");
}
}
public class exam95 {


public static void main(String[] args) {
SleepThread t = new SleepThread();
t.start();


}


}

 

실행 결과

카운트다운 5초
5
4
3
2
1
0
종료

 

-sleep의 파라미터로 1000을 지정하였기 때문에 1초 동안 스레드는 block 상태가 되게 됨. 그리고 1초 후에 다시 runnable 상태가 되고 cpu를 할당받게 되면 스레드는 진행됨

 

2)yield()

-yield() 메서드는 자신의 시간을 양보하는 메서드임. 스레드가 작업을 수행하던 중에 yield()를 만나게 되면 자신에게 할당된 실행시간을 다음 차례의 스레드에게 양보함

 

3)join()

-join() 메서드는 특정한 스레드가 작업을 먼저 수행할 때 사용함. 실행순서를 지켜야 하는 스레드들을 제어함

join()
join(long millis)
join(long millis, int nanos)

 

-시간을 지정하면 그 시간 동안 특정 스레드가 작업을 수행하고 나머지 스레드들은 block 상태가 되어 시간을 지정하지 않으면 특정 스레드가 작업을 완료할 때까지 나머지 스레드들이 모두 기다림

 

5.스레드의 동기화

-스레드의 동기화란 멀티 스레드로 작업을 할 때, 스레드 간의 작업이 간섭되지 않도록 하는 것임

-단일 스레드로 작업할 때는 프로세스의 자원을 사용하는데 아무런 문제가 없음. 하지만 멀티 스레드 프로세스의 경우 여러 스레드가 같은 자원을 공유하게 되기 때문에 예기치 못한 결과를 불러올 수도 있음. 예를 들어 A와 B가 같은 메서드를 사용할 경우 A가 메서드의 사용을 완료하고 그 결과를 반영하기 전에 B가 메서드를 사용한다면 B의 메서드 사용이 A의 결과에 영향을 미치게 됨. 이것은 마치 화장실 한 칸을 두고 여러 사람이 이용할 때 앞사람이 나오기 전에 뒤에 사람이 들어가는 것과 같은 오류임

-실제 이러한 상황이 현실에서 벌어진다면 큰 혼란을 가져올 수 있음. 물건의 재고와 맞지 않게 주문되거나 경쟁률이 치열한 티켓팅을 하는 상황을 예로 들 수 있음. 이러한 의도하지 않은 결과를 막기 위해서 자바에서는 스레드의 동기화(syschronized)라는 키워드를 사용함

-스레드의 동기화란 공통의 메서드에 대해 한 스레드가 먼저 작업을 하고 있을 때 다른 스레드의 접근을 막는 것임

public synchronized void 메서드명(){
//...수행할 작업
}
public void 메서드명(){
       synchronized (스레드 객체){
       }
}

 

-스레드의 동기화를 사용하기 위해서는 메서드의 선언부에  synchronized 키워드만 붙여주면 됨. 다른 스레드의 접근을 막는 방법은 synchronized 처리가 된 메서드가 자신에게 접근한 스레드에게 자신을 사용할 수 있는 권한인 록(lock)을 부여하게 됨. 그리고 스레드가 작업을 완료하면 록(lock)을 다시 가져옴. 그러므로 이 록(lock)을 가지지 못한 스레드는 메서드를 사용할 수가 없음

 

6.wait()과 notify()

-스레드를 대기시키는 wait() 메서드와 대기 중인 스레드를 깨우는 notify() 메서드를 통해 스레드를 제어할 수 있음

-스레드 동기화를 통해서 객체의 록(lock)을 한 스레드의 작업이 완료될 때까지 제공함으로써 공유하는 데이터를 보호함. 하지만 록(lock)을 가지고 있는 스레드의 작업이 특정한 조건 때문에 완료되지 못하는 상태라면 계속해서 록(lock)을 반납하지 못할 것임. 이는 다른 스레드들이 록(lock)을 받지 못해 작업하지 못하게 하므로 매우 비효율적인 코드가 됨. 동기화에서 발생할 수 있는 이러한 세부적인 문제들을 제어하는 것이 wait()과 notify() 메서드임. wait()는 스레드가 일단 록(lock)을 반납하고 기다리게 하는 메서드임. 반대로 notify() 메서드는 작업을 중단했던 스레드가 다시 록(lock)을 얻을 수 있도록 깨우는 메서드임

void wait()
void wait(long timeout)
void wait(long timeout, int nanos)
void notify()
void notifyAll()

 

-매개변수가 있는 wait() 메서드는 지정된 시간만큼 기다림. 만약 매개변수가 없는 wait() 메서드의 경우에는 notify()가 호출되어야 깨어남. notifyAll() 메서드는 대기중인 스레드를 모두 깨움

 

<mom 스레드는 통장 객체에 돈을 입금하고, son 스레드는 통장 객체에 돈을 출금하는 코드>

package ch12;


class Account {
int money = 0;


// 입금, 출금
public int showMoney() {
return money;
}


public synchronized void setMoney() {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
System.out.println(ie.toString());
}
this.money += 2000;


System.out.println("어머니가 용돈을 입금했습니다. 현재 잔액" + this.showMoney());


this.notify();
}


public synchronized void getMoney() {
while (money <= 0) {


try {
System.out.println("통장에 잔고가 없어서 아들 대기중");
this.wait();
} catch (InterruptedException e) {

}

}
this.money -= 2000;
System.out.println("아들이 용돈을 출금했습니다. 현재 잔액" + this.showMoney());
}
}


class Son extends Thread {
private Account account;


Son(Account account) {
this.account = account;
}


public void run() {
for (int i = 0; i < 10; i++) {
account.getMoney();
}
}
}


class Mon extends Thread {
private Account account;


Mon(Account account) {
this.account = account;
}


public void run() {
for (int i = 0; i < 10; i++) {
account.setMoney();
}
}
}


public class exam99 {


public static void main(String[] args) {
Account account = new Account();
Son son = new Son(account);
Mon mon = new Mon(account);
son.start();
mon.start();


}
}

 

실행 결과

어머니가 용돈을 입금했습니다. 현재 잔액2000
어머니가 용돈을 입금했습니다. 현재 잔액4000
어머니가 용돈을 입금했습니다. 현재 잔액6000
어머니가 용돈을 입금했습니다. 현재 잔액8000
아들이 용돈을 출금했습니다. 현재 잔액6000
아들이 용돈을 출금했습니다. 현재 잔액4000
아들이 용돈을 출금했습니다. 현재 잔액2000
아들이 용돈을 출금했습니다. 현재 잔액0
통장에 잔고가 없어서 아들 대기중
어머니가 용돈을 입금했습니다. 현재 잔액2000
어머니가 용돈을 입금했습니다. 현재 잔액4000
어머니가 용돈을 입금했습니다. 현재 잔액6000
어머니가 용돈을 입금했습니다. 현재 잔액8000
어머니가 용돈을 입금했습니다. 현재 잔액10000
어머니가 용돈을 입금했습니다. 현재 잔액12000
아들이 용돈을 출금했습니다. 현재 잔액10000
아들이 용돈을 출금했습니다. 현재 잔액8000
아들이 용돈을 출금했습니다. 현재 잔액6000
아들이 용돈을 출금했습니다. 현재 잔액4000
아들이 용돈을 출금했습니다. 현재 잔액2000
아들이 용돈을 출금했습니다. 현재 잔액0

 

-어머니와 아들은 for문을 통해 각각 10번씩 통장에 돈을 입금 또는 출금하게 됨

 

 

chapter13

 

1.IO와 스트림(Stream)

-데이터를 읽고 기록하는 것을 데이터의 Input, Output이라고 하며 데이터를 주고받는 작업을 도와주는 역할을 스트림이라고 함

              요청

cf) client  i -> server

               <- o

1)IO란?

-IO란 Input과 Output을 뜻함. 프로그램으로 들어오는 모든 값을 Input 값이라고 하고 밖으로  나가는 값을 Output 값이라고 함

 

2)스트림(Stream)이란?

-IO 패키지를 다루다 보면 스트림(Stream)이라는 용어를 자주 볼 수 있음. 스트림은 사전적인 의미로는 개울, 시내를 뜻하지만 자바에서는 데이터의 흐름을 의미함

-스트림, 즉 데이터의 통신은 한 쪽 방향으로만 가능하다는 특징이 있음. 그렇기 때문에 입력 스트림과 출력 스트림을 각각 따로 사용해야 하며 먼저 들어온 데이터가 먼저 나가는 FIFO(First In First Out) 구조를 이루고 있음

-스트림은 다양한 종류가 있는데 크게 분류하면 출력단위가 1byte인 바이트기반 스트림과 출력단위가 문자단위인 문자기반 스트림으로 나누어짐

 

2.바이트기반 스트림

-바이트기반 스트림은 바이트단위로 데이터를 입출력하는 스트림임

 

1)바이트기반 스트림의 활용

-1byte씩 읽어서 1byte씩 출력함. InputStream과 OutputStream이 모든 바이트기반 스트림들의 조상이 되며 InputStream을 상속받는 스트림은 read() 추상 메서드를 구현하고 OutStream을 상속받는 스트림은 write() 추상 메서드를 구현함

InputStream OutputStream
abstract int read() abstract void write(int b)
int read(byte[] b) void write(byte[] b)
int read(byte[] b, int off, int len) void write(byte[] b, int off, int len)

*오버로딩

 

-바이트 기반 스트림은 클래스의 이름이 InputStream, OutputStream으로 끝나며 데이터를 읽거나 출력할 대상에 따라서 적절한 스트림을 사용함

입력 스트림 출력 스트림 대상
FileInputStream FileOutputStream 파일
PipedInputStream PipedOutputStream 메모리
AudioInputStream AudioOutputStream 오디오
ByteArrayInputStream ByteArrayOutputStream 프로세스

 

-스트림을 사용하기 위해서는 인스턴스를 생성할 때 생성자의 인자로 데이터를 입력받거나 출력 받을 곳을 넘겨준 다음 적절한 메서드를 사용함

FileInputStream fis = null;
fis = new FileInputStream("읽어올 파일의 위치");
fis.read();

 

3.바이트기반 보조 스트림

-바이트 기반 보조 스트림은 실제 바이트 기반 스트팀의 성능을 향상 시키는 역할을 함

 

1)바이트기반 보조 스트림의 활용

-보조 스트림은 실제 입력 스트림을 도와서 성능을 향상시키는 역할을 함. 호스에 물을 부드럽게 하는 연수기를 다는 것처럼 직접 데이터를 이동시키는 것은 아니지만 필요한 성능을 가지고 있음

입력 보조 스트림 출력 보조 스트림 사용
FilterInputStream FilterOutputStream 필터를 이용한 입출력
BufferedInputStream BufferedOutputStream 버퍼를 통해 입출력
DataInputStream DataOutputStream 기본형 단위로 데이터 처리
없음 PrintStream print, printf, println

 

.-버조 스트림을 사용하는 방법은 데이터를 전송할 스트림을 선언하고 그 스트림을 인자로 보조 스트림의 생성자에 넣어주면 됨

FileInputStream fis = null;
fis = new FileInputStream("파일의 위치");
BufferdInputStream bis = new BufferedInputStream(fis);

 

-또한 모든 보조 스트림들은 다른 스트림들과 마찬가지로 InputStream과 OutputStream의 자손으로써 입출력방법이 같으므로 보조 스트림의 참조변수로 입출력함

-바이트 기반 스트림과 보조 스트림을 이용해서 파일을 복사하는 프로그램을 작성하기 위해서는 프로그램 코드를 작성하기 전에 읽어 올 txt 파일을 생성함

 

<prac.txt의 내용을 복사하는 코드>

package ch13;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;


public class exam100 {
//파일복사 프로그램
public static void main(String[] args) {
// 인풋스트림
FileInputStream fis = null; //한 줄로 안쓰는 이유: { }범위 제약때문
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try
{
fis = new FileInputStream("c:\\prac.txt");
bis = new BufferedInputStream(fis);

fos = new FileOutputStream("result.txt");
bos = new BufferedOutputStream(fos);

int data;
while((data = fis.read()) != -1) { //eof 데이터가 없음 -1
System.out.println(data);
bos.write(data);
}
bos.close(); //사용한 스트림은 닫아 주기
bis.close();
}
catch (Exception e) {e.printStackTrace();}

}
}

 

실행 결과

97
13
10
98
13
10
99
13
10
100
13
10
101
13
10
102
13
10
103
13
10
236
149
136
235
133
149
13
10
237
149
152
236
132
184
236
154
148

 

-파일 입출력에 관계된 입출력 스트림을 각각 생성함. 일어올 파일의 경로를 작성해주고 저장할 파일명을 작성함. 미리 생성 되어있으면 덮어쓰고 아직 생성되어있지 않다면 자동으로 생성함

-각각 버퍼를 사용할 수 있도록 현재 입출력 스트림의 인자를 받아서 보조 스트림의 인스턴스를 생성함. 입력 스트림에서 읽어온 값이 -1이라는 것은 더 이상 읽어올 값이 없다는 것이기 때문에 while문에서 입력 값이 -1이 아니면 계속해서 출력 할 수 있도록 코드를 작성함

-fis.read()가 -1을 반환하는 것은 파일의 끝에 도달했음을 의미함. 이 경우, while 루프가 종료되므로 -1이 출력되지 않음. 즉, -1은 EOF(End Of File)를 나타내는 값이지, 실제로 파일의 내용으로 출력되는 값이 아님

-중요한 것은 스트림도 외부와의 연결이기 때문에 반드시 close()를 해야 한다는 것임. 프로그램이 종료되면서 자동으로 종료되긴 하지만 24시간 돌리는 프로그램일 경우에는 심각한 낭비를 초래할 수 있음. 

-연결한 순서와는 반대로 close()함

 

4.문자기반 스트림

-문자 기반 스트림은 16비트의 문자나 문자열을 읽고 쓰는 스트림임

 

1)문자 기반 스트림의 활용

-자바에서 문자를 담는 char형 변수는 2byte의 크기가 있음. 그렇기 때문에 바이트기반 스트림으로는 문자의 입출력을 처리하는 데 불편함이 있음. 그래서 자바에서는 따로 문자 데이터의 입출력을 다루는 스트림을 제공함. Reader와 Witer 스트림이 모든 문자기반 스크림의 조상이 되며 마찬가지로 이를 상속받는 스트림들은 필요한 추상 메서드를 구현함

Reader Writer
int read() void write(char[] cbuf)
int read(char[] cbuf)  void write(char[] cbuf)
abstract int read(char[] cbuf, int off, int len) abstract void write(char[] cbuf, int off, int len)
void write(String str)
void write(String str, int off, int len)

 

-문자기반 스트림은 이름이 InputStream에서 Reader로, OutputStream에서 Writer로 바뀐 것만 빼면 바이트기반 스트림과 사용방법이 비슷함. 적절한 필요에 따라서 스트림을 선택하고 사용함

입력 스트림 출력 스트림 대상
FileInputReader FileWriter 파일
PipedInputReader PipedWriter 메모리
CharArrayReader CharArrayWriter 프로세스

 

-예를 들어 텍스트 파일을 읽어오고 싶을 때는 FileReader 스트림의 객체를 생성하며 읽어올 파일의 위치를 생성자의 인자로 넘기면 됨

FileReader fr = null;
fr = new FileReader("파일 위치");

 

5.문자기반 보조 스트림

-문자 기반 보조 스트림은 문자기반 스트림의 성능을 향상시키는 역할을 함

 

1)문자 기반 보조 스트림의 활용

-문자 기반 스트림에도 성능을 향상시키기 위한 보조 스트림이 있음

입력 보조 스트림 출력 보조 스트림 사용
FilterReader FilterWriter 필터를 이용한 문자 입출력
BufferedReader BufferedWriter 버퍼를 통해 문자 입출력

 

-보조 스트림을 사용하려면 문자기반 스트림을 생성한 후 그 인자를 보조 스트림으로 넘겨 주면 됨

FileReader fr = null;
fr = new FileReader("파일 위치");
BufferdReader br = new BufferedReader(fr);

 

6.스트림의 예외 처리

-스트림을 사용할 때는 예외 처리를 하는 것이 좋음

-입출력의 모든 메서드는 IOException이 발생할 가능성이 있음. 그렇기 때문에 반드시 예외처리해야 함. 지금까지의 코드에서는 예외를 던져서 처리했지만 실제로는 try-catch문으로 처리하는 것이 좋음. 덧붙여서 사용한 스트림을 오류 여부와 관계없이 반드시 닫아주기 위해 finally에서 close() 메서드를 사용하는 것이 좋음 

 

 

<map 방식으로 회원관리프로그램을 만드는 코드>

package project1;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;


public class start {


public static ArrayList<member> memberlist = new ArrayList<member>(); //주소 저장
public static HashMap<String, member> maplist = new HashMap<String, member>();


public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int choice = 0;
// 회원가입
// 회원조회


while (true) {
System.out.println("회원관리 프로그램입니다."); // 다시 시작
System.out.println("0. 종료");
System.out.println("1. 회원가입");
System.out.println("2. 전체회원조회");
System.out.println("3. 특정회원조회");
choice = sc.nextInt(); // 밖에 있으면 무한반복, 다시 입력하기 위해 여기 위치
if (choice == 0) {
System.out.println("프로그램을 종료합니다.");
break;


} else if (choice == 1) {
join2(); //함수변경 갈아끼움 join(): ArrayList , join2(): Map 방식


} else if (choice == 2) {
search();


}else if (choice == 3) {
membersearch2(); //함수변경 membersearch(), membersearch2()
}
else {
System.out.println("잘못입력하셨습니다.");
}
}


}


public static void join2() {
System.out.println("회원가입을 실행합니다.");
Scanner text = new Scanner(System.in);
Scanner number = new Scanner(System.in);
member mb = new member();


System.out.println("아이디를 입력해주세요!");
String id = text.nextLine();
mb.setId(id);


System.out.println("패스워드를 입력해주세요!");
String password = text.nextLine();
mb.setPassword(password);


System.out.println("이름을 입력해주세요!");
String name = text.nextLine();
mb.setName(name);


System.out.println("나이를 입력해주세요!");
int age = number.nextInt();
mb.setAge(age);

maplist.put(id, mb);



}
public static void join() {
System.out.println("회원가입을 실행합니다."); // 함수 연결 하기
Scanner text = new Scanner(System.in); // 문자, 숫자 섞어쓸 수 없음, 한번 쓰면 오염됨
Scanner number = new Scanner(System.in);
member mb = new member(); //1.먼저 입력


System.out.println("아이디를 입력해주세요!");
String id = text.nextLine();
mb.setId(id);


System.out.println("패스워드를 입력해주세요!");
String password = text.nextLine();
mb.setPassword(password);


System.out.println("이름을 입력해주세요!");
String name = text.nextLine();
mb.setName(name);


System.out.println("나이를 입력해주세요!");
int age = number.nextInt();
mb.setAge(age);


// member mb = new member(name, id, password, age); //2.나중에 입력 - 멤버 매개변수
memberlist.add(mb);
}

public static void search() {
System.out.println("전체회원조회입니다.");
for (member mb : memberlist) {
System.out.println("-------멤버--------");
System.out.println("아이디:" + mb.getId());
System.out.println("패스워드:" + mb.getPassword());
System.out.println("이름:" + mb.getName());
System.out.println("나이:" + mb.getAge());
System.out.println("-----------------");


}
}
public static void membersearch() {
System.out.println("찾으실 회원의 아이디를 입력하세요");
Scanner sc = new Scanner(System.in);
String id = sc.nextLine();
for(int i = 0; i < memberlist.size(); i++) {
member mb = memberlist.get(i);
String getId = mb.getId();
if(id.equals(getId)) { //검색엔진 if
System.out.println("-------멤버--------");
System.out.println("아이디:" + mb.getId());
System.out.println("패스워드:" + mb.getPassword());
System.out.println("이름:" + mb.getName());
System.out.println("나이:" + mb.getAge());
System.out.println("-----------------");
break;
}
}
}
public static void membersearch2() {
System.out.println("찾으실 회원의 아이디를 입력하세요");
Scanner sc = new Scanner(System.in);
String id = sc.nextLine();
if(maplist.containsKey(id)) {
member mb = maplist.get(id);
System.out.println("-------멤버--------");
System.out.println("아이디:" + mb.getId());
System.out.println("패스워드:" + mb.getPassword());
System.out.println("이름:" + mb.getName());
System.out.println("나이:" + mb.getAge());
System.out.println("-----------------");
}else {
System.out.println("해당멤버가 없습니다.");
}
}
}
package project1;
//project의 main은 하나만 가짐
public class member { //member 변수x4, 생성자x2, getter/setterx4
private String name;
private String id;
private String password;
private int age; //데이터 저장변수에 접근하지 못하도록 하기위해 private

//생성자: 필수

public member(String name, String id, String password, int age) {
super();
this.name = name;
this.id = id;
this.password = password;
this.age = age;
}
public member() {} //기본생성자

//getter, setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}



}