본문 바로가기
CS/자바

[JAVA] 심화 2

by Hoozy 2023. 3. 10.

이전 게시글 JAVA 심화 1

https://hoozy.tistory.com/entry/JAVA-%EC%8B%AC%ED%99%94-1

 

[JAVA] 심화 1

이전 게시글 JAVA 기초 https://hoozy.tistory.com/entry/JAVA-%EA%B8%B0%EC%B4%88 카테고리 : 자바(심화) 컬렉션 1. List 인터페이스 정렬된 모든 객체 컬렉션을 저장할 수 있는 목록 데이터 전용. ArrayList 동작 배열

hoozy.tistory.com

카테고리 : 자바(심화)

파일

1. java.io.File

  • File 클래스의 createNewFile() 메소드를 이용하여, 새로운 파일 및 디렉토리를 생성할 수 있습니다.
  • 파일의 상위 폴더가 없으면 에러가 뜬다.
  • 파일을 성공적으로 생성하면 true를 리턴하고, 기존에 이미 있는 파일이면 false를 리턴한다.
File file = new File("d:\\example\\file.txt");

        try {
            if (file.createNewFile()) {

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

2. java.io.FileOutputStream

  • 첫 번째 파라미터 : 생성할 파일의 이름 문자열, File 개체, FileDesriptor 객체를 전달한다.
  • 두 번째 파라미터 : 만약 생성하려는 파일이 존재할 경우, 기존 파일을 열어서 그 뒤에 이어서 작성할지(true), 기존 파일을 덮어쓰고 새로 파일을 생성할지(false)를 지정해 줍니다.
  • 마찬가지로 상위 폴더가 없으면 에러가 뜬다.
File file = new File("d:\\example\\file.txt");

        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file, true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

3. java.nio.file.Files

  • Files.createFile() 메소드를 이용해서 파일을 생성할 수 있다.
 Path filePath = Paths.get("d:\\example\\file.txt");

        try {
            Path newFilePath = Files.createFile(filePath);
            System.out.println(newFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
입출력 스트림

입출력

  • Input과 Output을 줄여서 I/O 라고 하고, 자바에서는 이러한 모든 I/O를 입출력 스트림을 통해서 한다.

입출력 스트림

  • 입출력에서 Stream은 Byte 형태로 데이터를 운반하는데 사용되는 연결통로이다.
  • 하나의 스트림으로 입력과 출력을 동시에는 할 수 없다.
  • FIFO 구조로 되어 있다.
  1. InputStream / OutputStream
    • InputStream (입력 스트림) : 스트림을 한 줄 씩 읽는다. System.in을 사용한다.
    • OutputStream (출력 스트림) : 스트림을 한 줄 씩 읽는다. System.out을 사용한다.
import java.io.InputStream; // 임포트 필수
import java.io.OutputStream;        

 /*
* InputStream 로 입력받는 경우 맨 앞 문자 1개만 출력됨 && int 형태로 입력받음
 */        
InputStream in = System.in;
OutputStream out = System.out;

int idata = in.read(); // input 은 read 와 연결되어있기 때문에 in.read 를 사욯한다.

out.write(idata); // output 은 write 와 연결되어있기 때문에 out.write 를 사용한다
out.flush(); // flush 를 써주지 않으면 출력되지 않는다
out.close(); // output 을 끝내는 매서드
  1. InputStreamReader / OutputStreamWriter
    • 여러개의 값을 입출력하기 위한 방법.
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/*
 * InputStreamReader 로 입력받는 경우에는 배열을 어떻게 주느냐에 따라 2개 이상의 값을 받을 수 있음
 */
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in); // InputStreamReader 사용하기 위해 객체 생성

OutputStream out = System.out;
OutputStreamWriter writer = new OutputStreamWriter(out); // OutputStreamWriter 사용하기 위해 객체 생성

char cdata[] = new char[2]; // 이제는 char 를 기본형으로 받을 수 있고, 2개 이상의 값을 배열을 통해 받아올수 있다.
reader.read(cdata);

int IcData = cdata[0]-'0'; // 배열이기 때문에 char 로 받은 값을 int로 변환하여 계산하고 싶은 경우 이처럼 사용해야한다.

writer.write("입력받은 값 : ");
writer.write(cdata);
writer.write("\n");
writer.write("입력받은 첫번째 값 + 10 : ");
writer.write(IcData+10+"\n"); // 입력받은 첫번째 값 +10

System.out.println("#######결과#######");
writer.flush(); // 이 매서드를 통해 출력
writer.close();
  1. BufferedReader / BufferedWriter
    • Buffer : 버퍼는 입력받은 값을 버퍼에 저장해두었다가 버퍼가 가득차거나 개행 문자가 나타나면 버퍼의 내용을 한 번에 전송하게 된다.
import java.io.BufferedReader; // 임포트 필수
import java.io.BufferedWriter;

public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

    String s = br.readLine(); // bufferedwriter 의 기본형은 String

    int i = Integer.parseInt(s) +10;

    br.close(); // bufferedreader 도 입력을 마쳤다면 닫아주자

    bw.write("입력받은 값 : "+ s); // 출력
    bw.newLine(); // 개행 메소드
    bw.write("입력받은 값 +10 : "+i+"\n"); // 이렇게 하니까 제대로 출력됨

    bw.flush(); // 남은 값 출력 && 버퍼 초기화
    bw.close(); // bufferedwriter 닫기
 }
자바 HTTP 요청 GET / POST

1. POST 요청

  • HTTP 통신 - POST
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class Main {
    private static final String URL = "https://www.google.com";
    private static final String POST = "POST";
    private static final String USER_AGENT = "Mozilla/5.0";
    private static final String DATA = "test data";

    public static void main(String[] args) throws IOException {
        URL url = new URL(URL);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        connection.setRequestMethod(POST);
        connection.setRequestProperty("User-Agent", USER_AGENT);
        connection.setDoOutput(true);

        DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
        outputStream.writeBytes(DATA);
        outputStream.flush();
        outputStream.close();

        int responseCode = connection.getResponseCode();

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuffer stringBuffer = new StringBuffer();
        String inputLine;

        while ((inputLine = bufferedReader.readLine()) != null)  {
            stringBuffer.append(inputLine);
        }
        bufferedReader.close();

        String response = stringBuffer.toString();
    }
}
  • JAVA로 HTTP 통신을 위해서는 JAVA API를 활용해야 한다.
  • HttpURLConnection 클래스와 URLConnection 클래스를 활용해 HTTP 통신이 가능하다.

URL 클래스

1. URL 객체 생성하기

  • URL 클래스의 생성자는 다양한 형태로 선언되어 있는데, URL을 문자열 형태로 나타낸 객체의 레퍼런스를 인자로 전달받는 생성자를 이용할 것이다.
import java.net.URL;

URL url = new URL("https://www.google.com");

2. 연결 객체 얻기

  • URL 클래스에 선언된 openConnection() 메소드는 url 객체에 대한 연결을 담당하는 URLConnection 객체를 반환한다.
URLConnection connection = url.openConnection();

HttpURLConnection 클래스

1. HttpURLConnection 객체 생성

import java.net.URL;
import java.net.HttpURLConnection;

URL url = new URL("https://www.google.com");
HttpURLConnection connection = (HttpURLConnection) url. openConnection();

2. 요청 메소드 설정

  • HTTP 요청을 위해서는 요청 메소드를 설정해야한다.
  • setRequestMethod() 메소드는 요청 메소드를 문자열 파라미터로 받아서 유효한 요청 메소드면 method 멤버 변수에 저장하고 아니면 ProtocolException 예외를 발생시킨다.
  • 처리할 수 있는 요청 메소드로는 GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE 가 있다.
connection.setRequestMethod("GET");

3. 요청 헤더 설정

  • setRequestProperty() 메소드로 요청 헤더를 설정할 수 있다.
  • 요청 헤더를 설정함으로써 http 요청을 하는 사용자의 애플리케이션 타입, 운영체제, 소프트웨어 버전 등을 식별할 수 있다.
private static final String USER_AGENT = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0";

connection.setRequestProperty("User-Agent", USER_AGENT);

4. POST 요청 시 데이터 넘겨주기

  • POST 요청을 할 때에는 OutputStream 객체로 데이터를 전송한다.
  • setDoOutput() 메소드를 통해 OutputStream 객체로 전송할 데이터가 있다는 옵션을 설정해야 한다.
  • 변수가 true면 OutputStream으로 데이터를 전송하고, false면 하지 않는다. 기본 설정은 false로 되어 있다.
connection.setDoOutput(true);
  • 전송할 데이터가 문자열일 경우에는 OutputStream 클래스를 확장하는 DataOutputStream 클래스의 writebytes() 메소드를 활용하여 쉽게 데이터를 설정할 수 있다.
  • DataOutputStream 클래스는 생성자에 OutputStream 객체를 전달하여 생성할 수 있다.
  • 위에서 getOutputStream() 메소드를 통해 얻은 객체를 바로 넣어줄 수 있다.
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.writeBytes(DATA);
outputStream.flush();
outputStream.close();

5. 응답 코드 얻기

  • getResponseCode() 메소드를 통해 응답 코드를 얻을 수 있다.
  • 정상적인 응답일 경우 200이 반환된다.
int responseCode = connection.getResponseCode();

6. 응답 데이터 얻기

  • getInputStream() 메소드를 통해 응답 데이터를 읽을 수 있는 InputStream 객체를 얻을 수 있다.
  • 응답을 문자열 타입으로 얻기 위해 BufferedReader 객체를 사용할 수 있다.
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuffer stringBuffer = new StringBuffer();
String inputLine;

while ((inputLine = bufferedReader.readLine()) != null)  {
     stringBuffer.append(inputLine);
}
bufferedReader.close();

String response = stringBuffer.toString();

2. GET 요청

  • GET 요청을 할 경우에는 요청 메소드를 GET으로 변경하고, OutputStream을 사용하지 않게끔 코드를 작성하면 된다.
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class Main {
    private static final String URL = "https://www.google.com";
    private static final String GET = "GET";
    private static final String USER_AGENT = "Mozilla/5.0";
    private static final String DATA = "test data";

    public static void main(String[] args) throws IOException {
        URL url = new URL(URL);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        connection.setRequestMethod(GET);
        connection.setRequestProperty("User-Agent", USER_AGENT);

        int responseCode = connection.getResponseCode();

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuffer stringBuffer = new StringBuffer();
        String inputLine;

        while ((inputLine = bufferedReader.readLine()) != null)  {
            stringBuffer.append(inputLine);
        }
        bufferedReader.close();

        String response = stringBuffer.toString();
    }
}
소켓
  • TCP/IP 기반 네트워크 통신에서 데이터 송수신의 마지막 접점.
  • 소켓 통신 : 소켓을 통해 서버-클라이언트 간 데이터를 주고 받는 양방향 연결 지향성 통신.
    • 보통 지속적으로 연결을 유지하면서 실시간으로 데이터를 주고 받아야 하는 경우에 사용됨.
  • 소켓은 클라이언트 소켓과 서버 소켓으로 구분되며, 소켓간에 통신을 위해서는 네트워크상에서 클라이언트와 서버에 해당되는 컴퓨터를 식별하기 위한 IP 주소와 해당 컴퓨터내에서 현재 통신에 사용되는 응용프로그램을 식별하기 위한 포트번호가 사용된다.

서버 소켓 구현하기

// 1. 서버소켓 생성 
ServerSocket serverSocket = new ServerSocket(8000); // 포트 번호

// 2. 클라이언트 접속 대기
Socket socket = serverSocket.accept();

// 3. 데이터 송수신을 위한 input/output 스트림 생성
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();

// 4. input 스트림을 통한 데이터 수신 (클라이언트 -> 서버)
byte[] inputData = new byte[100];
int length = in.read(inputData);
String inputMessage = new String(inputData, 0, length);

// 5. output 스트림을 통한 데이터 송신 (서버 -> 클라이언트)
String outputMessage = "보낼메시지";
out.write(outputMessage.getBytes());
out.flush();

// 6. 통신 종료
socket.close();
serverSocket.close();

클라이언트 소켓 구현하기

// 1. 클라이언트 소켓 생성을 통한 서버접속
Socket socket = new Socket("127.0.0.1", 8000);  // IP주소, 포트번호

// 2. 데이터 송수신을 위한 input/output 스트림 생성 
InputStream in = socket.getInputStream( );
OutputStream out = socket.getOutputStream( );

// 3. output 스트림을 통한 데이터 송신 (클라이언트 → 서버)
String outputMessage = "보낼메시지";
out.write(outputMessage.getBytes( ));
out.flush( );

// 4. input 스트림을 통한 데이터 수신 (서버 → 클라이언트)
byte[ ] inputData = new byte[100];
int length = in.read(inputData);
String inputMessage = new String(inputData, 0, length);

// 5. 통신 종료
socket.close( );

BufferedReader / BufferedWriter 와 PrintWriter / PrintStream

  • 위에서 byte 단위로 전송하던 데이터를 String 클래스로 변환해야 하기 때문에 InputStreamReader와 OutputStreamWriter를 활용해 변환을 편리하게 할 수 있다.
  • 또한, 데이터 입출력의 효율을 위해 바로 전달하지 않고, 중간에 버퍼를 이용하기 위해서 BufferedReader와 BufferedWriter 클래스를 함께 사용할 수 있다.
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream( )));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream( )));
  • 데이터 출력의 경우에는 출력 포맷을 편리하게 해주는 기능이 있는 PrintWriter 또는 PrintStream 클래스를 사용할 수 있다.
PrintWriter out = new PrintWriter(socket.getOutputStream( ));
PrintStream out = new PrintStream(socket.getOutputStream( ));

소켓 통신 사용예제 1 (1:1 채팅 프로그램)

  • 서버
public class MyServer {
    public static void main(String[] args) {
        BufferedReader in = null;
        PrintWriter out = null;

        ServerSocket serverSocket = null;
        Socket socket = null;
        Scanner scanner = new Scanner(System.in);

        try {
            serverSocket = new ServerSocket(8000);

            System.out.println("[Server실행] Client연결대기중...");
            socket = serverSocket.accept();            // 연결대기

            System.out.println("Client 연결됨.");
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream());

            while(true) {
                String inputMessage = in.readLine();    // 수신데이터 한줄씩 읽기    
                if ("quit".equalsIgnoreCase(inputMessage)) break;

                System.out.println("From Client: " + inputMessage);
                System.out.print("전송하기>>> ");

                String outputMessage = scanner.nextLine();
                out.println(outputMessage);
                out.flush();
                if ("quit".equalsIgnoreCase(outputMessage)) break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                scanner.close();        // Scanner 닫기
                socket.close();            // Socket 닫기
                serverSocket.close();        // ServerSocket 닫기
                System.out.println("연결종료");
            } catch (IOException e) {
                System.out.println("소켓통신에러");
            }
        }
    }
}
  • 클라이언트
public class MyClient {
    public static void main(String[] args) {
        BufferedReader in = null;
        PrintWriter out = null;

        Socket socket = null;
        Scanner scanner = new Scanner(System.in);

        try {
            socket = new Socket("127.0.0.1", 8000);

            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream());

            while(true) {
                System.out.print("전송하기>>> ");
                String outputMessage = scanner.nextLine();
                out.println(outputMessage);
                out.flush();
                if ("quit".equalsIgnoreCase(outputMessage)) break;

                String inputMessage = in.readLine();
                System.out.println("From Server: " + inputMessage);
                if ("quit".equalsIgnoreCase(inputMessage)) break;
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                scanner.close();
                if (socket != null) socket.close();
                System.out.println("서버연결종료");
            } catch (IOException e) {
                System.out.println("소켓통신에러");
            }
        }
    }
}
스레드

프로세스

  • 실행중인 프로그램
  • 완전히 독립적인 수행단위
  • 스레드가 2개 이상이면 멀티 스레드이다.

스레드

  • 프로그램 내에서 실행되는 프로그램 제어 흐름(실행단위)을 말한다.
  • 한 프로그램에 여러개의 스레드가 존재할 수 있다.
  • 프로그램 코드를 한 줄씩 실행하는 것이 스레드의 역할이다 (=실행제어)
  • 프로그램을 실행하는 주체이다
  • 보통 가벼운 프로세스(light-process)라고 표현한다

멀티 스레드 프로그램

  • 하나의 프로세스는 한 번에 스레드를 1개 밖에 실행시키지 못한다.
  • 대신 일정한 시간 간격으로 수행해야하는 스레드를 전환한다.
  • 스레드를 전환할 때는 운영체제의 스케줄러의 기준에 따라 순서가 정해지게 된다.
  • 여러 스레드를 번갈아 처리하기 때문에 엄밀히 말하면 한 번에 한가지를 처리하지만 동시에 작업하는 듯한 효과를 준다.
  • 이와 같은 방식을 시분할 방식 이라고 한다.
  • 장점(사용하는 이유)
    • 프로세스는 독립적이므로, 작업 공간이 독립적이어서 프로세스끼리 자원 및 데이터를 공유하기 어렵다. 이때문에 프로세스간 데이터 전송이 필요한 경우 시간, 자원 소요가 많다.
    • 프로그램 내의 스레드는 독립적이지 않고, 작업공간을 같이 사용하기 때문에 자원 및 데이터를 공유할 수 있다. 이때문에 스레드간 데이터 전송이 필요한 경우 시간, 자원 소요가 적다.
    • 프로세스처럼 작업을 동시에 처리할 수 있는 공통적인 특징이 있으면서, 프로세스보다 오버헤드가 적다는 것이 장점.

멀티 프로세싱 / 멀티 태스킹 / 멀티 스레드 차이점

용어 멀티프로세싱 멀티 태스킹 멀티 스레딩
관점 시스템 관점 프로그램 외부에서의 관점 프로그램 내부에서의 관점
의미 CPU 여러개에서 동시에 여러개의 프로세스 수행 CPU 1개에서 동시에 여러 프로그램을 실행 processor 1개가 동시에 여러 스레드를 실행
예시 다수의 송금거래를 동시에 처리하는 은행전산 시스템 PC카톡 켜놓고 youtube 음악 들으면서 온라인 뱅킹 업무 프로그램 안에서 실행되는 코드 흐름이 여러개
멀티스레드 적용하기 위한 조건
  • 병행성 : 다수의 스레드 생성 방법 존재
  • 동기화 : 작업이 방해 받지 않고, 각 스레드의 동기화 방법 존재
  • 통신 : 서로 다른 스레드가 정보를 교환할 수 있는 방법 존재
스레드 생성 방법
  • 스레드는 우선 순위를 정하지 않으면 순서를 알 수 없다.

1. Thread 클래스 상속받아서 생성

  • java.lang.Thread 클래스를 상속 받아서 run() 메소드 오버라이딩
  • 실행 스레드로 자신의 콜 스택을 갖춘 독립적인 프로세스
  • start() 메소드를 통해 스레드가 시작한다.

2. Runnable 클래스 상속받아서 생성

  • java.lang.Runnable 인터페이스로 부터 run() 메소드를 구현하여 생성한다.
    • Runnable 인터페이스는 run() 메소드 1개만 가지는 함수형 인터페이스이다.
  • Runnable 인터페이스를 구현한다고 해서 바로 스레드가 되지 않는다.
  • Thread 클래스를 통해 스레드가 될 수 있다.
    • 객체 참조변수를 인자값으로 하는 Thread 생성

스레드 생성하기

  1. Thread() : 일반적인 스레드 객체 생성. Thread-n 이런 이름을 가진 스레드가 만들어짐.
  2. Thread(Runnable target) : run 메소드를 가지는 객체를 인자값으로 할당하기
  3. Thread(Runnable target, String name) : run 메소드를 가지는 객체와 스레드 이름을 인자값으로 할당하기
  4. Thread(String name) : 스레드 생성하면서 스레드 이름 지어주기
  • Thread 클래스 상속받아서 스레드 생성하기
public class Test01 extends Thread{
    @Override
    public void run() {
        /* 스레드 실행코드 */
    }
}
  • Runnable 인터페이스 구현하여 스레드 생성하기
public class Test01 implements Runnable {
    @Override
    public void run() {
        /* 스레드 실행코드 */
    }
}
Synchronized
  • 여러 개의 쓰레드가 한 개의 자원을 사용하고자 할 때, 현재 데이터를 사용하고 있는 쓰레드를 제외하고 나머지 쓰레드들은 데이터에 접근할 수 없게 막는 개념
  • 데이터의 thread-safe를 하기 위해 자바에서 Synchronized 키워드를 제공해 멀티 쓰레드 환경에서 쓰레드간 동기화를 시켜 데이터의 thread-safe를 보장합니다.
  • Synchronized는 변수화 메소드에 사용해서 동기화 할 수 있으며, Synchronized 키워드를 남발하게 되면 오히려 프로그램의 성능저하를 일으킬 수 있음

JAVA 심화 2 끝.

다음 게시글 자바 프레임워크

https://hoozy.tistory.com/entry/JAVA-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC

 

[JAVA] 프레임워크

이전 게시글 JAVA 심화 2 https://hoozy.tistory.com/entry/JAVA-%EC%8B%AC%ED%99%94-2 카테고리 : 자바(프레임워크) 웹 애플리케이션을 만들고 유지하는데 사용되는 강력한 오픈소스 JAVA 프레임워크는 2가지가 있

hoozy.tistory.com

참고 자료

https://velog.io/@digh0515/Java-java-Http-%ED%86%B5%EC%8B%A0
https://hianna.tistory.com/588
https://hbase.tistory.com/171
https://terianp.tistory.com/19
https://kadosholy.tistory.com/125
https://makecodework.tistory.com/entry/Java-%EC%8A%A4%EB%A0%88%EB%93%9CThread-%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0

'CS > 자바' 카테고리의 다른 글

[JAVA] 프레임워크  (0) 2023.03.10
[JAVA] 심화 1  (0) 2023.03.08
[JAVA] 기초  (0) 2023.03.07

댓글