JAVA

수현·2023년 9월 1일
0

Ucamp

목록 보기
3/19

📒 CS & JAVA

📕 전자와 2진수의 이해 및 메모리의 이해

1. 전자와 2진수의 관계

  • 1) 전자
    • 음성자
    • 전류가 흐름
  • 2) 반도체
    • 조건에 따라서 전류가 흐르는 물질
      • 부도체 : 전류가 흐르지 않는 물질
      • 도체 : 전류가 흐르는 물질
    • 온도의 의존성
      • 온도⬆️ : 전류⬇️
      • 온도⬇️ : 전류⬆️
  • 3) 2진수
    • 표현할 수 있는 단위가 0, 1
    • 2가지 경우의 수 bit 표현
  • 4) 캐패시터
    • 축전기
    • 전류를 일시적으로 저장 (배터리)
    • refresh를 통해 전류를 공급하여 데이터 유지
    • 🗒️ 예시 : DRAM
      • 축전지(캐패시터)로 만들어 비트 저장 가능

2. 메모리 하나의 번지에 저장할 수 있는 데이터양은 8bit 일까

  • 1) 통신의 시작
    • 0,1 데이터로 통신
    • 2진수로 된 모스부호 전달 ➡️ 인간이 이해할 수 있도록 부호 변경 (부호화, 문자 인코딩)
  • 2) 유럽권에서 발전
    • 영어 1문자 표현하기 위한 비트수
      • 대소문자 50~60개 + 특수기호 + 숫자 + 오류 검출
      • 1Byte = 8bit
    • 아스키코드 이용하여 비교 매핑
  • 3) 컴퓨터 메모리
    • 1문자를 읽어 1Byte 단위로 메모리에 저장
  • 4) 아스키코드의 한계
    • 유럽권에서 시작했지만, 전세계로 통신이 필요
      • 1Byte로 다른 언어 표현 불가
      • 🗒️ 예시 : 한국어 최소 2Byte 필요
    • 유니코드 표준 코드
      • Euc-KR (2Byte) + CP949
        ➡️ UTF-8 (3Byte) 사용
      • 이모지 UTF9mb4 (4Byte)

📕 컴퓨터 구조와 I/O

1. 컴퓨터 구조

  • 1) CPU, RAM

    • CPU가 RAM 메모리에 요청
    • CPU가 연산 후 결과 반환하여 저장
  • 2) 메모리

    • 메모리 캐패시터 ➡️ 전류 저장 ➡️ 컴퓨터 종료시 삭제
    • 휘발성
    • 전기 필요O
  • 3) 하드디스크

    • 특징
      • 데이터 영구적 저장 장치
      • 전기 필요X
    • 장점
      • 컴퓨터 종료해더 데이터 영구 저장 가능
      • 8GB, 1TB (1000GB)
    • 단점
      • 데이터 저장/찾는데 오래 걸림
  • 4) 메모리 VS 하드디스크

    • 메모리
      • 요청시 바로 전달 가능
    • 하드디스크
      • 요청시 RAM에 데이터가 없어서 페이지폴트 발생
      • CPU가 하드디스크에 요청하여 전달 받고, RAM에서 FIFO, LRU 등 알고리즘 이용하여 교체
  • 5) CPU 레지스터

    • 비용⬆️, 저장공간⬇️
    • CPU 요청시 RAM에서 가져와 레지스터에 저장 후 응답
    • 캐싱 : 레지스터에 저장된 데이터는 RAM에 요청 필요X
  • 6) 32bit 컴퓨터

    • CPU가 한번에 처리할 수 있는 데이터 양 (32bit)

2. I/O 이해와 응용 학습

  • 1) Input/Output 개념
    • Input(저장)/Output(읽기)
    • CPU가 하드디스크에 읽거나 쓰려고 하는 경우
    • I/O를 줄여야 CPU가 처리하는 속도 빨라짐
  • 2) I/O가 많이 발생할 경우
    • 메모리 늘리기
    • 필요한 데이터가 최대한 많이 메모리 존재하도록 (캐싱⬆️)
  • 3) 통신을 통한 파일 다운로드
    • 🗒️ 예시 : CPU가 8GB 다운로드하는 경우
      • 4GB RAM에서 보통 OS가 2GB 저장
      • 2GB x 4번 하드디스크에 저장하는 I/O 발생
    • 분산처리하여 속도⬆️ (하둡, MongoDB의 샤딩)

📕 네트워크 통신

1. 신뢰성 있는 통신

  • 1) 보안 잘못된 통신의 경우
    • 응답을 받지 못한 경우
    • 응답 받았는데 제3자가 보냈다고 착각하는 경우
    • 응답 받았는데 제3자가 임의로 수정한 응답을 받은 경우
  • 2) 신뢰성 있는 통신
    • TCP
    • Hash
    • RSA (보안)

2. TCP 통신

  • 1) Protocol

    • 둘 이상의 네트워크간 통신 규약(약속)

    📖 참고 📖 인터페이스 vs 프로토콜

  • Interface : 시스템과 전송 매체의 연결 지점에 대한 규격

  • Protocol : 시스템이 데이터 교환할 때 임의의 통신 규칙

  • 2) UDP (User Datagram Protocol)

    • 빠른 전송속도
    • 응답에 대한 신뢰성 보장X
  • 3) TCP (Transmission Control Protocol)

    • 3-way handshaking (연결) : 👤ACK ➡️ 💻ACK + SYN ➡️ 👤SYN
    • 4-way handshaking (해제) : 👤FIN ➡️ 💻FIN ➡️ 💻ACK ➡️ 👤ACK
    • 응답에 대한 신뢰성 보장O

3. 신뢰성과 해시(hash)의 관계

  • 1) 해시
    • 단방향 암호화 기법 (복호화)
    • key + salt를 이용해 고정된 길이의 hashg 생성 (해싱)
  • 2) 전자서명
    • 서명자의 신원과 서명자가 해당 전자문서에 서명하였다는 사실을 나타내기위한 정보
    • 서명을 통해서 서명한 사람의 신원을 확인하는 용도
  • 3) 권장 해시
    • MD5, SHA-1 : 크랙 당함
    • SHA-256, SHA-512 : 현재 권장
  • 4) 블록체인
    • 해시로 생성하고, 해킹이 어려움
    • 블록(분산 데이터 저장 환경)에 데이터를 저장함으로써 누구도 임의로 수정할 수 없고 누구나 변경의 결과를 열람할 수 있게끔 만드는 기술

4. 암호화 RSA

  • 1) 대칭키
    • 송수신자가 동일한 키 소유
    • 암복호화에 동일한 키 사용
    • 키 전달의 문제 발생
  • 2) 공개키
    • 송수신자가 다른 키 소유 (공개키/비밀키)
      • 공개키 : 키 암호화, 공개O
      • 개인키 : 키 복호화, 공개X
  • 3) RSA
    • 내용 변경 안됨
    • 내용 보호 가능
    • 인증 (누구로부터 왔는지)

5. HTML

  • 1) 개념
    • 문서 공유의 어려움
    • 문서 작성시 HTML을 사용하여 서로 문서 공유
    • HTML과 브라우저 탄생

6. 웹 서버 동작과 OSI 7계층

  • 1) 서버
    • 클라이언트에게 네트워크를 통해 정보나 서비스를 제공하는 컴퓨터 시스템
    • 폴더를 외부에서 검색할 수 있도록 공유 (웹 서버)
    • 웹 서버가 직접 요청하는 경우는 없고, 응답만 해줌 (수동적)
  • 2) Stateless와 Stateful
    • Stateful : 상태 지속, 양방향 전송 가능
    • Stateless : 상태 저장X, 단방향 전송 가능 (A : 요청, B : 응답)
    • 서버 부하를 줄이기 위해 Stateless 사용
  • 3) 웹 인증 어려운 이유
    • Stateless 방식
    • 인증 후 요청시 해당 상태가 저장안되어 확인이 어려움
    • 세션 이용 (인증된 사용자인지 확인)

7. OSI 7계층

  • 1) Port
    • 0 ~ 65536
    • 논리적인 접속 장소
    • 통신을 위한 네트워크 서비스/프로세스 등을 식별하는 단위
  • 2) OSI 7계층
    • 네트워크 프로토콜이 통신하는 구조를 7계층으로 분리하여 각 계층간 상호 작동하는 방식을 정해놓은 것
    • 🗒️ 예시 : EMAIL 전송
      • 응용 계층 : email 프로그램, 작성
      • 표현 계층 : 암호화, 압축
      • 세션 계층 : 세션 동기화
      • 트랜스포트 계층 : TCP/UDP 중 TCP 선택
      • 네트워크 계층 : IP (목적지) 선택
      • 데이터 계층 : MAC 주소 (하드웨어 번호) 확인
      • 물리계층 : 실제 광케이블을 통해 전송
  • 3) TCP 헤더
    • 서킷 스위칭
      • 서킷(Circuit) 이라는 연결을 두 장비간 사이에 만들어서 통신에 사용
      • 통신할 때마다 물리적 선을 추가
      • 실시간 전송에 적합
    • 패킷 스위칭
      • 데이터를 패킷(packet) 단위로 분할하여 전송
      • 라우터 알고리즘을 이용하여 경로 설정
    • 라우터
      • 데이터 임시 보관
      • 가장 빠르게 목적지 경로 찾아주는 역할
  • 4) HTTP 헤더
    • MIME 타입
      • Multipurpose Internet Mail Extensions
      • 웹을 통해 여러 형태의 파일 전달할 때 파일 변환으로 사용

8. 클라우드

  • 1) 개념
    • 일정 기간만 많은 서버가 필요할 경우
    • 광대한 네트워크를 통하여 접근할 수 있는 가상화된 서버와 서버에서 작동하는 프로그램과 데이터베이스를 제공하는 IT 환경
    • 종류
      • Iaas (Infra as a Service)
      • Saas (Software as a Service)
      • Paas (Platform as a Service)

9. 동기/비동기

  • 1) 동기 프로그래밍
    • 프로그래밍 : 동기 (일의 순서가 있는지)
    • 데이터 : 동기화 (데이터가 일치하는지)
  • 2) 비동기 프로그래밍
    • 일의 순서가 없이 처리
    • CPU의 비가동시간이 줄어들어 성능⬇️

📒 JAVA

📕 JDK와 이클립스

1. JDK 설치법

  • 1) JDK (Java Development Kit)
    • JAVA를 사용하기 위한 개발 툴
    • JVM (Java Virtual Machine)에서 동작 가능하도록 변환 (.java ➡️ .class)
    • 과정
  • 2) JDK 설치
    • 11 GA 다운로드
    • homebrew 이용하여 설치
      • homebrew 업데이트 : brew update
      • adoptopenjdk/openjdk 추가 : brew tap adoptopenjdk/openjdk
    • 설치 가능한 JDK 찾기 : brew search jdk
    • adoptopenjdk11 설치 : brew install --cask adoptopenjdk11
    • java 설치 경로 확인 : /usr/libexec/java_home -V

2. JDK 환경변수 설정

  • 1) java version 확인 : java --version
    • java 최신 버전이 기본값 설정
  • 2) java version 변경
    • export JAVA_HOME 이용하여 변경
    • zsh 환경변수 파일 수정 : vi ~/.zshrc
    • 변경사항 반영 : source ~/.zshrc

3. eclipse 설치

  • 1) Eclipse Installer 다운로드

    • Elipse IDE for Java Developer 선택
    • 경로 JRE 11.0.2 로 변경
  • 2) java class 생성

    • public static void main 선택

4. eclipse 원리와 메모리 구조

  • 1) JDK 동작 과정

    • 1️⃣ : 사람 이해 언어(.java) ➡️ javac.exe파일 이용해 컴파일
    • 2️⃣ : JVM이해하도록 컴파일 (.class) ➡️ java.exe파일 이용해 실행
    • 3️⃣ : JVM이 OS에 전달 (.class)
    • 4️⃣ : OS가 하드웨어 명령해 제어
  • 2) JDK 특징

    • 자바 소프트웨어 개발 환경(Java Development Kit)
    • JRE(java runtime environment)와 JVM(java virtual machine)과, Javac 등 컴파일러, 디버거, 자바 애플릿 등 응용프로그램 개발 도구 포함
    • OS에 관계 없이 개발 가능 ➡️ OS에 맞는 JDK만 설치
  • 3) 이클립스 원리

    • JDK 개발 툴
    • 프로그래밍시 컴파일 및 디버깅 등 통합 개발 환경(IDE, Integrated Development Environment) 필요
  • 4) 메모리 구조

    • Static
    • Heap
    • Stack

📕 JAVA 개념

1. 자료형

  • 1) 종류

    • boolean (1bit) : 참/거짓 표현
    • int (4Byte) : 정수 표현
    • double (8Byte) : 실수 표현 (소수점 이하의 자리)
    • char (2Byte) : 문자 표현
  • 2) 클래스 자료형 (Beans)

    • 개발자가 만드는 custom 자료형
    • Beans : 여러가지 데이터 자료형을 가지고 있는 클래스
    • Beans 동적 할당 : heap 공간에 해당 클래스가 가지고 있는 모든 데이터 할당 (static 제외)
      • 참조 변수 : 주소, 컴파일시 크기가 정해져 있지X (실행시 정해짐)
      • 일반 변수 : 값, 컴파일시 크기 정해짐
    class MyVar {
    	staitc int n1 = 10;
      static char c1 = 'A';
    }
    
    class Note {
    	int num = 1;
    }
    
    public class VarEx03 {
    
    	static int num = 100;
    	public static void main(String[] args) {
      	System.out.println(MyVar.n1);
          System.out.println(VarEx03.num);  //생략 해서 num 가능
          
          Note note1; // 레퍼런스 변수 (크기가 정해져 있지 않은 것)
          int age; // 일반 변수 (크기가 정해져 있는 것)
          note1 = new Note(); // heap 공간 
          age = 10; // main stack 공간
      }
    }
    • 연습 문제1) 붕어빵을 표현하기 위한 커스텀 자료형(heap)을 만드시오
      • 클래스명 : 붕어빵
      • 필드 : 붕어빵의 가격=1000, 맛=달콤함, 색깔 = 노란색
    class bread {
    	int price = 1000;
      String taste = "달콤함";
      String color = "노란색";
    }
    
    public class Example {
    		 public static void main(String[] args) {
    		bread n = new bread();
       }
    }
    • 연습 문제2) 메모장 코드를 작성하시오
      • 파일명 : HelloWorld.java
      • 폴더 : C:₩javawork₩ch1₩HelloWorld.java
      • 출력 : HelloWorld
    class Note {
    		String memo = "";
    }
    
    public class HelloWorld {
    		public static void main(String[] args) {
    			Note m = new Note();
    		
    			m.memo = "HelloWorld";
    			System.out.println(m.memo);
    		}
    }

    ➡️ 컴파일 : javac 파일명.확장자
    ➡️ 실행

    • (패키지 있을 경우) 상위 폴더 이동java 패키지명.클래스명
    • (패키지가 없을 경우) java 클래스명

2. java 실행원리

  • 1) java 모든 코드는 클래스 내부에 존재해야 함

  • 2) java는 실행 전에 static 키워드를 가지고 있는 부분을 static 메모리 공간에 로드함

  • 3) java를 실행하면 main의 내부{}를 실행하고, 종료되면 java 프로그램도 종료됨

    • main 밖에 선언된 변수는 main안에서 참조 불가능
    • n2는 실행된 적 없음 (System.out.println(n2) 실행 못함)

3. 메서드

  • 1) 개념

    • 특정 기능을 정의한 코드의 집합
    • 🗒️ 예시 : 메서드 = Method01 클래스의 행위
    public class Method01 {
    
    		 static void start () {
       	System.out.println("Hi");
          System.out.println("Exit");
       }
       
       public static void main(Stiring[] args) {
       	Method01.start(); // 메서드 실행 
       }
    }
  • 2) 메서드 Stack 메모리

    • .class 로드 ➡️ static 찾기 ➡️ main 메서드 실행 ➡️ main 큐 오픈 ➡️ stack의 main메서드 영역 생성
    • main큐 : main 코드 순차적 실행
    • static의 main메서드 : 내부 메서드
  • 3) 지역변수 / 전역변수

    • 지역변수 : stack
      • 메서드가 실행될 때 메모리에 할당, 메서드가 종료될 때 메모리에서 삭제
      • 가장 생명 주기가 짧음
    • 전역변수 : static, heap
      • heap : new 할 때 메모리에 할당, 더이상 참조하지 않을 때 메모리 삭제
      • static : main메서드 실행되기 전에 메모리 할당, main 메서드 종료되면 메모리 삭제
    public class StackEx {
    	static int num = 20; // 전역변수 (static)
       int value = 50; // 전역변수 (heap)
       
       static void a() { // a 메서드의 스택 영역에 저장
       	int n1 = 1;  // 지역변수 (stack)
          System.out.println(n1);
          
          // 오류 발생 (static 공간 할당은 main 메서드 실행 전에만 가능)
          // static int n2 = 2;
       }
       
       public static void main(String[] args) {
       	a();
          System.out.println(sum);
          
          StackEx s = new StackEx();
          System.out.println(s.value);
       }
    }
  • 4) 메서드의 리턴

    • void : 리턴(돌려주지) 않음
    public class MethodEX {
    	static int add() {
      	int sum = 5 + 6;
          
          return sum;
      }
      
      public static void main(String[] args) {
      	int result = add(); // add 메서드 호출
          System.out.println(result);
      }
    }
  • 5) 메서드 변수 활용과 연산자

    • 대입 연산자
    • 사칙 연산자 : +, -, /, *
    • 비교 연산자 : ==, !=, >, >=, <, <= (리턴의 결과가 TRUE/FALSE)
    • 논리 연산자 : &&(and), ||(or)
    • 조건 연산자 : 삼항 연산자(조건 ? true : false)
    public class OperEx {
    	public static void main(String[] args) {
      	int n1 = 10; // 대입 연산자
      	int n2 = 20; 
          int sum = n1 + n2; // 사칙 연산자
          
          String s1 = "나의 나이는 ";
          int age = 27;
          System.out.println(s1 + age); // 결합 
      }
    }
  • 6) 캐스팅

    • 업캐스팅 : 큰 변수 공간에 작은 변수 저장 (묵시적으로 발생)
    • 다운캐스팅 : 작은 변수 공간에 큰 변수 저장 (명시적 형변환)
      • 단점 : 데이터 유실
    public class CastEx{
    	int n1 = 100;
      double d1 = n1; // 업캐스팅
      
      double d2 = 100.8;
      int n2 = (int)d2 // 다운캐스팅 (명시적 형변환)
    }

4. 패키지 / 라이브러리

  • 1) package

    • 패키지는 파일 시스템의 폴더 형태로 패키지 만들어서 클래스 저장/관리
    • 클래스를 유일하게 구분하는 식별자 역할 (클래스 = 패키지.클래스명)
  • 2) import

    • 다른 패키지명에 있는 클래스를 찾지 못할 경우 사용
    • static import는 기존과 다르게 메소드나 변수를 패키지, 클래스명 없이 접근 가능
  • 3) 접근제어자

    • public : 누구나 접근 가능
    • protected : 같은 패키지에 있거나, 상속받은 경우 사용
    • package-private : (default) 같은 패키지 내에서 접근 가능
    • private : 같은 클래스 내에서만 접근 가능
  • 🗒️ 예시 : package와 import 키워드 사용

    • a패키지

      • Cal.java (다른 패키지, import 필요O)
    • b패키지

      • App.java
      • Hello.java (같은 패키지, import 필요X)
    • Cal.java
      ``java
      package test;

      public class Cal {

      void add() {
      System.out.println("더하기 메서드");
      }

      public void minus() {
      System.out.println("빼기 메서드");
      }

      private void multi() {
      System.out.println("곱하기 메서드");
      }

      public void divide() {
      System.out.println("나누기 메서드");
      multi(); // 같은 클래스라 접근 가능
      }
      }

    • App.java

     pacakge test2;
    
     import test.Cal;
    
     public class App {
     	public static void main(String[] args) {
      	Cal c = new Cal();
          
          // 오류 - default 접근 제어자의 경우 실행X
          // c.add();  
          // 성공 - public 접근 제어자의 경우 실행O
          c.minus(); 
          // 오류 - private 접근 제어자의 경우 같은 클래스에서만 가능
          // c.multi(); 
      }
     }

📖 참고 📖 public static void main(String[] args)

  • JVM이 main메서드를 찾으려면 public이 필요 (공개)
  • JVM이 main메서드를 찾으려면 static이 필요 (메모리에 저장)
  • main 메서드만 return 타입 허용X
  • 메서드의 이름 main
  • 4) 라이브러리

    • 다른 프로그램에서 라이르버리 안에 포함된 기능 활용

    • 생성 java 프로젝트 : Export ➡️ jar ➡️ JAR file

      • Runnable jar file : main 메소드 가지고 있는 실행 파일
      • JAR file : 그 외 파일
    • 사용 java 프로젝트 : Build path 설정 ➡️ Configure Build path ➡️ library ➡️ Classpath의 Add External jARs

         package test2;
      
       import test.Cal;
       import lib.lib2;
      
       public class App {
      		 	public static void main(String[] args) {
      		Cal c = new Cal();
        	  c.minus(); 
      
        	  lib2 l = new lib2(); // 라이브러리 
        	  l.lib3();
      			 }	
       }

5. 클래스

  • 1) 클래스

    • java는 파일명 = 클래스명
    • 여러가지 특징(상태)를 가지고 있음
      • 다양한 자료형 가질 수 있음
  • 2) 생성자

    • 클래스 만들어서 상태를 정의할 때는 값을 초기화하지 않음
      • new를 이용해 다른 상태 정의 (다양성)
      • 생성자 = 메서드
    • Cat c1 = new Cat();
      • Cat : 커스텀 타입 (사용자가 만든 타입)
      • c1 : heap 공간을 가리키는 주소
      • new : 메모리에 할당(heap) ➡️ String name, String color
      • Cat() : 생성자 호출 ➡️ 상태 초기화 목적
    • default 생성자
      • 개발자가 작성하지 않아도 됨 (생략 가능한 코드)
      • 직접 생성자를 따로 구현하게 되면, 디폴트 생성자 생략 불가능
      • .java ➡️ .class 컴파일할 때 자바가 자동으로 코드 생성
    • Cat.java
      pulbic class Cat {
      	String name;
          String color;
          
          public Cat() { // default 생성자(메서드)
          
          }
          
          public Cat(String n, String c) { // 생성자(메서드)
          	System.out.println("고양이 탄생");
          }
      }
    • CatApp.java
      public class CatApp {
      	public class void main(String[] args) {
          	Cat c1 = new Cat();
          }
      }
  • 3) this

    • 자기 자신의 힙 공간을 가리킴

    • this.멤버변수 : 매개변수와 객체 자신이 가지고 있는 변수의 이름이 같은 경우 구분하기 위해 사용

    • this(멤버변수) : 생성자 내에서 다른 생성자 호출

    • Food.java

    		public class Food {
    			String name;
    			int price;
    	
    			public Food(String name, int price) {
    				this.name = name;
    				this.price = price;
    			}
    	
    			void myPrint() {
    				System.out.println(name + " 가격은 " + price + "원 입니다.");
    			}
    		}
    • FoodApp.java
    		public class FoodApp {
    			public static void main(String[] args) {
    				Food f1 = new Food("치킨", 2000);
    				Food f2 = new Food("피자", 5000);
    		
    				f1.myPrint();
    				f2.myPrint();
    			}
    		}
  • 4) 클래스, 오브젝트, 인스턴스

    • 클래스 : 유사한 특징을 지닌 객체를 묶어놓은 집합 (설계도)

    • 오브젝트 : 객체를 사용할 수 있도록 실체화 (구현할 대상)

      • OOP : 오브젝트 지향 프로그래밍
    • 인스턴스 : new를 통해 heap 공간에 저장된 객체 (구현된 구체적인 실체)

    • 상태(필드)는 행위(메서드)에 의해서 변함

      • 상태 (필드)
      • 행위 (메서드)
    class Player{
    	String name;
      // int thirsty;
      private int thirsty; // 외부 클래스에서 접근 불가능
      
      public Player(String name, int thirsty) {
      	this.name = name;
          this.thirsty = thirsty;
      }
      
      // 행위 = 메서드
      void water() {
      	this.thirsty = this.thirsty - 50;
      }
      
      int thirstyState() {
      	return this.thirsty;
      }
    }
    public class OOPEx {
    	public static void main(String[] args) {
      	Player p1 = new Player("홍길동", 100);
          
          // 1. 잘못된 시나리오 (객체지향에 맞지 않음)
          p1.thirsty = 50;
          System.out.println(p1.thirsty);
          
          // 2. 잘못된 시나리오 (상태가 행위를 변경함)
          p1.water();
          System.out.println(p1.thirsty);
          
          // 3. 올바른 시나리오
          p1.water();
          System.out.println(p1.thirstyState());
      }
    }

📖 참고 📖 final 변수

  • 1번만 할당할 수 있는 변수 (수정 불가)
  • 객체를 참조하고 있다면, 그 객체의 상태가 바뀌어도 매번 동일한 내용을 참조함

6. 상속

  • 1) 상속
    • 추상화
    • 상태, 행위를 물려 받음
      • 타입 일치가 가능
      • 상태와 행위를 물려받을 수 있지만, 타입이 일치되어야 함
    • 부모 클래스와 겹치지 않는 상태(필드)만 상속됨
      • 부모 클래스와 변수 이름이 동일할 경우, 자신의 클래스 변수 사용

📖 참고 📖 import

  • 다른 패키지 안의 클래스를 사용하기 위해서는 클래스 이름 앞에 패키지 붙여야 함
    • import를 통해 패키지를 매번 입력하는 번거로움 해소
  • 상태, 행위를 가져와서 사용
    • 타입 일치가 되지X
class Engine {
	int power = 2000;
}

class Car { // 자동차는 엔진이 아니기 때문에 상속 불가!
	Engine e; // composition (결합)
    
    public Car(Engine e) {
    	this.e = e;
    }
}

class Hamburger {
	String name = "hamberger";
    String f1 = "양상추";
    String f2 = "패티";
}

class CheeseHamburger extends Hamburger { // 상속 (Composition 필요x)
	String name = "Cheesehamburger"; 
}

class ChickenHamburger { // 결합도 가능
	Hamberger h;
    
    public ChickenHamburger(Hamberger h) {
    	this.h = h;
    }
}

public class OOPEx {
	public static void main(String[] args) {
    	Engine e1 = new Engine();
        Car c1 = new Car(e1);
        System.out.println(c1.e.power);
        
        CheeseHamburger h1 = new CheeseHamburger(); // 상속 ➡️ (햄버거, 치즈)
        // Hamberger h1 = new CheeseHamburger(); // 동일 ➡️ (햄버거, 치즈)
        // CheeseHamberger h1 = new Hamberger(); // X ➡️ (햄버거)
        System.out.println(h1.name);
        System.out.println(h1.f1);
        
        Hamberger h1 = new Hamberger(); // 컴포지션 
        ChickenHamburger ch1 = new ChickenHamburger(h1);
        System.out.println(ch1.h.name);
    }
}
  • 2) 다형성

    • 하나의 객체가 여러가지 타입을 가질 수 있음
    • 부모 클래스의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있음
      • Hamberger h1 = new Hamberger(); // 허용 ➡️ (햄버거)
      • CheeseHamburger h1 = new CheeseHamburger(); // 허용 ➡️ (햄버거, 치즈)
      • Hamberger h1 = new CheeseHamburger(); // 허용 ➡️ (햄버거, 치즈)
      • CheeseHamberger h1 = new Hamberger(); // 오류
  • 3) 오버로딩

    • 함수 이름이 동일하지만, 다른 함수로 인식하게 함
    • 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메소드를 정의 가능
    • 유닛이 많을 경우 동일한 메서드를 많이 생성해야 해서 불편
    • 조건
      • 메소드의 이름이 같고, 매개변수의 개수나 타입이 달라야 함
      • '리턴 값'만 다른 것은 오버로딩을 할 수 없음
  • 4) 오버라이딩

    • 부모 클래스로부터 상속받은 메소드를 자식 클래스에서 재정의하는 것
    • 오버라이드
      • 동적바인딩
      • 부모와 자식이 동일한 이름의 메서드가 있을 경우 부모의 메서드를 무효화함
    • 조건
      • 자식 클래스에서는 오버라이딩하고자 하는 메소드의 이름, 매개변수, 리턴 값이 모두 같아야 함

7. 추상클래스와 인터페이스

  • 1) 추상클래스

    • 추상메서드를 1개 이상 가지고 있는 클래스 (다형성)
      • 일반메서드도 가질 수 있음
    • 반드시 사용되어야 하는 메소드를 추상 클래스에 추상 메소드로 선언하여, 상속받는 클래스는 반드시 재정의
    • new 사용할 수 없음
  • 2) 추상메서드

    • 자식클래스에서 반드시 오버라이딩 해야만 사용할 수 있는 메소드
      • 목적 : 자식 클래스가 반드시 추상 메소드를 구현하도록
    • 선언부만 존재 (구현부-몸체 {} 없음)
    abstract class Animal { // 추상클래스
    	abstract void speak(); // 추상메서드 (몸체{} 없음)
      
      void Hello() {
      	System.out.println("Hi");
      }
    }
    
    class Dog extends Animal {
    	void speak() { // 오버라이드 (Animal의 speak() 무효화됨)
      	System.out.println("멍멍");
      }
    }
    
    class Cat extends Animal {
    	void speak() {
      	System.out.println("야옹");
      }
    }
    
    public class OOPEx {
    	public static void main(String[] args) {
      	Animal a1 = new Dog();
          Animal a2 = new Cat();
          
          a1.speak(); // 동적바인딩 
          
          Animal aa = new Animal(); // 오류 (추상클래스)
      }
    }
  • 3) 인터페이스

    • 클래스들이 필수로 구현해야 하는 추상 자료형
      • 행위에 대한 제약을 줌
    • 추상 메서드와 상수로 구성
      • (일반) 클래스의 경우 인터페이스 안의 메서드 반드시 모두 구현
      • (추상) 클래스의 경우 메서드 구현하지 않으면, 자식 클래스로 구현을 위임 (자식 클래스는 반드시 메서드 구현)
    
    Interface MoveAble {
    
    	// public abstact 생략되어 있음
    	void up();
      void down();
      void left();
      void right();
    }
    
    abstract class 사나운동물 implements MoveAble{ // 추상 클래스
    	abstract void 공격(); // 미완성된 메서드
      
      @Override
      public void up() {
      	System.out.println("위");
      }
    }
    
    abstract class 온순한동물 implements MoveAble{
    	abstract void 채집();
      
      @Override
      public void up() {
      	System.out.println("위");
      }
    }
    
    class 호랑이 extends 사나운동물{
    
    	@Override
      void 공격() {
      	System.out.println("이빨로 공격");
      }
    }
    
    class 원숭이 extends 온순한동물{
    
      @Override // 오버라이드 어노테이션
    	void 채집() {
      	System.out.println("바나나 채집");
      }
    }
    
    public class OOPEx{
    
    	staitc void 조이스틱(온순한 동물 a1) {
      	a1.채집();
      	a1.up();
          a1.down();
          a1.left();
          a1.right();
      }
      
      static void 조이스틱(사나운 동물 a1) { // 오버로딩
      	a1.공격();
          a1.up();
          a1.down();
          a1.left();
          a1.right();
      }
      
    	public static void main(String[] args) {
      	원숭이 a1 = new 원숭이();
          조이스틱(a1);
          
          호랑이 a2 = new 호랑이();
          조이스틱(a2); 
      }
    }

📖 참고 📖 어노테이션

  • JVM이 실행시에 분석해서 해당 메소드 존재하는지 등 확인

8. SRP와 DIP

  • 1) SRP (Single Responsobillity Principle)
    • 단일 책임 원칙
    • 객체는 단 하나의 책임(기능)만 가져야 함
  • 2) DIP (Dependency Inversion Principle)
    • 의존성 역전 원칙
    • 객체에서 어떤 class를 참조해야 할 경우, 직접 참조하지 않고 상위 요소(추상 클래스/인터페이스) 참조해서 사용

📖 참고 📖 CI (Continuous Integretion)

  • 지속적 통합
  • 특징
    • 소프트웨어 개발의 위험성을 줄임
    • 수정이 편함

🗒️ 예시 : 식당 프로세스

  • 추상클래스
    • 홀직원
      • 청소()
    • 종업원
      • 홍길동, 임꺽정
      • 서빙()
      • 주문() : 키오스크(기계)
    • 캐셔
      • 김유신, 이몽룡
      • 계산() : 현금계산/카드계산
      • 정산() : 수기정산/계산기 정산
    • 요리사
      • 장보고, 이순신
      • 요리 ()
      • 종업원은 요리사에게 의존관계 있음
  • 인터페이스
    • 종업원
      • talk() : 종업원/캐셔만 가능, 요리사는 불가능
interface CanAble {
	void talk(); // public abstract 생략
}

abstract class 홀직원 implements CanAble{
	abstract void 청소();
	
	public void talk() {
		System.out.println("손님과 대화");
	}
}

abstract class 종업원 extends 홀직원 {
	void 서빙() {
		System.out.println("서빙");
	}
	
	void 주문() { // 키오스크로 변경된 경우 '주문()' 메소드 삭
		System.out.println("주문");
	}
}

abstract class 캐셔 extends 홀직원 {
	void 정산() {
		System.out.println("정산"); // 수기 -> 계산기 
	}
	
	void 계산() {
		System.out.println("계산"); // 현금 -> 카드  
	}
}

abstract class 요리사 {
	abstract void 요리();
}

class 홍길동 extends 종업원 {
	
	요리사 j; // 요리사 의존 관계 (의존성 역전 원칙)
	
	@Override
	void 청소() {
		System.out.println("화장실 청소!");
	}
}

class 임꺽정 extends 종업원 {

	@Override
	void 청소() {
		System.out.println("주방 청소!");
	}
}

class 김유신 extends 캐셔 {
	
	요리사 j;

	@Override
	void 청소() {
		System.out.println("홀 청소!");
	}
}

class 이몽룡 extends 캐셔 {
	@Override
	void 청소() {
		System.out.println("테이블 청소!");
	}
}

class 장보고 extends 요리사 {

	@Override
	void 요리() {
		System.out.println("양식");
	}
}

class 이순신 extends 요리사 {

	@Override
	void 요리() {
		System.out.println("한식");
	} 
}

public class OOPEx {
	public static void main(String[] args) {
		
	}
}

📕 자료구조

1. 배열

  • 1) 개념
    • 연관된 데이터를 저장하기 위한 자료 구조
    • 연속된 자료형(int, char)등 공간이 필요
  • 2) 특징
    • 데이터 읽기가 빠름
    • 시작 번지의 주소만 저장
    • 0번지부터 시작함
  • 3) 선언
    • 1차원 배열 : int[] nums = new int[3];
    • 2차원 배열 : int[][] nums = new {{1,2,3}, {4,5,6}};
interface RemoconAble() {
	public void 초록버튼();
    public void 빨간버튼(); 
}

class Samsung implements RemoconAble{
	void 초록버튼() {
    	System.out.println("전원 켜짐");
    }
    void 빨간버튼() {
    	System.out.println("전원 꺼짐");
    }
}

public class Example{
	// 삼성 리모콘 2개 만들기 (new)
    Samsung[] s = new Samsung[2];
    s[0] = new Samsung();
    s[1] = new Samsung();
}

2. 반복문

  • 1) 개념

    • 동일한 코드를 자동적으로 반복시켜 주는 것
  • 2) for문

    • 시작값; 조건값; 증감값;
    • for (int i = 1; i < 101; i=i+1)
      • int i = 1 : 초기화 (for문 실행시 1번만 실행됨)
      • i < 101 : 조건문 (처음부터 끝까지 계속 실행)
      • i=i+1 : 증감식 (for문 한번 실행된 이후 부터 계속 실행)
  • 3) while문

    • 종료가 없음
    • while(실행조건)

    3. 조건문

  • 1) 개념

    • 각 조건에 해당하여 코드를 실행시킴
  • 2) if문

    • 조건이 일치할 경우에만 코드 실행
    • if(조건)

📕 제네릭 및 스레드

1. Object 클래스

  • 1) 개념
    • 모든 클래스의 부모
    • 타입을 다운캐스팅 필요
class Dog {
	String name = "강아지";
}

public class Object{
	public static void main(String[] args) {
    	Object o1 = new Dog();
        
        Dog d1 = (Dog)o1; // 타입 다운캐스팅
    }
}

2. 제네릭 클래스

  • 1) 개념
    • 데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법
    • 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것
  • 2) 특징
    • 다운캐스팅 필요X
class 호랑이 {
	String name = "호랑이";
}

// 기존
class 동물 {
	Object data;
}

// 제네릭 변경 
class 동물<T> {
	T data;
}

public class Generic {
	public static void main(String[] args) {
    	// 기존
    	동물 s1 = new 동물();
        s1.data = new 호랑이();
       
        System.out.println(s1.data.name); // 오류 (s1이 동물을 바라보고 있어서)
        
        호랑이 h1 = (호랑이)s1.data;
        System.out.println(h1.name);
        
        // 제네릭 변경
        동물<호랑이> s1 = new 동물<>();
        s1.data = new 호랑이(); // T data가 null 이어서 이 형식 필요
        System.out.println(s1.data.name); // 성공
    }
}
  • 3) 와일드카드
    • <\? extends object>
    • 오브젝트를 상속하고 있는 모든 클래스 중에 하나여도 됨
    • private 변수에 대한 메서드 생성 : Source ➡️ Generate Getters and Setters
abstract class animal { // 동물 하나로 묶기
	abstract String getName();
}

class 호랑이 {
	private String name = "호랑이"; // generate get and set

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

class 동물<T> {
	private T data; 

	public T getData() {
		return data;

	public void setData(T data) {
		this.data = data;
	}
}

public class Generic {

	static 동물<? extends animal> 행동 (int time) { // extends Object 생략 가능
		if (time == 9) {
			호랑이 h1 = new 호랑이();	
			동물<호랑이> a1 = new 동물<>();
			a1.setData(h1);
			return a1;
		}
	}

	public static void main(String[] args) {
		동물<?> a1 = 행동(9);
        
        동물<? extends animal> a1 = 행동(9);
        System.out.println(a1.getName().setName());
	}
}

3. 컬렉션

  • 1) 개념

    • 컬렉션은 배열과 유사 (연속된 공간 없어도 할당 가능)
    • 동적으로 공간 사용
  • 2) 특징

    • 기본 자료형(값) : Int, double, char, boolean
    • 레퍼런스 자료형(주소) : String, 커스텀자료형(클래스)
    • 래핑 클래스 : Integer, Double, Character, Boolean
    ArrayList<Integer> c1 = new ArrayList<>();
    
    c1.add(1);

4. 스레드

  • 1) 개념
    • 프로세스안에서 실질적으로 작업을 실행하는 단위
    • 자바에서는 JVM(Java Virtual Machine)에 의해 관리
    • context switching으로 부하가 발생
  • 2) 동기/비동기
    • 동기 : 직렬적으로 태스크(task)를 수행하고, 태스크는 순차적으로 실행되며 어떤 작업이 수행 중이면 다음 작업은 대기 (일의 순서O)
    • 비동기 : 병렬적으로 태스크를 수행하고, 태스크가 종료되지 않은 상태라 하더라도 대기하지 않고 다음 태스크를 실행 (일의 순서X)
class SubTread implements Runnable {

	// 자바의 서브스레드
    @Override
    public void run() {
    
    	for (int i = 1; i < 10; i++) {
        	try {
            	Thread.sleep(1000); 
                System.out.println("서브 스레드 : " + i);
            } catch (InterruptedException e) {
            	e.printStackTrace();
            }
        }
        
    }
}

public class Thread {
	// 자바의 메인 스레드
	public static void main(String[] args) {
    
    	SubThread st = new SubThread();
        Thread t1 = new Thread(st); // 타겟 선정
        t1.start(); // run 메서드 실행
        
    	for (int i = 1; i < 10; i++) {
        	try {
            	Thread.sleep(1000); //ms 단위
                System.out.println("메인 스레드 : " + i);
            } catch (InterruptedException e) {
            	e.printStackTrace();
            }
        }
    }
}

5. 예외처리

  • 1) 개념
    • 컴파일시/런타임시 예외 처리
      • 컴파일 예외 : JAVA가 확인
      • 런타임 예외 : 개발자가 확인
    • InterruptedException : Thread.sleep();하는 동안 interrupt 예외 발생 가능
    • e.getMessage() : 오류 로그 (파일로 남겨두기)
    • e.printStackTrace() : 오류를 추적해주는 로그
  • 2) try/catch문
    • catch : try(시도)하다가 예외가 발생하면 어떻게 처리할지 정의하는 영역
class{
	void shoot() {
    	System.out.println("총 발사");
    }
}

public class Exception{
	public static void main(String[] args) {
    	// 컴파일 에러
    	Thread.sleep(1000); // try/catch문 필요
    
    	// 컴파일 예외
    	try {
    		Thread.sleep(1000);
    	} catch (InterruptedException e) {
        	e.printStackTrace(); // 오류를 추적해주는 로그
        }
        
        // 런타임 에외
        int[] nums = {1, 2, 3};
        
        try {
        	System.out.println(nums[3]); // 강제 종료
        } catch(ArrayIndexOutOfBoundsException e) {
        	System.out.println(e.getMessage()); // Index 3 out of bounds for length 3 (로그 파일로 남기기)
        }
        System.out.println("메인 스레드 종료");
        
        // 런타임 예외S = null;
        try {
        	s.shoot();
        } catch(RuntimeException e) {
        	System.out.println("에러메시지");
            s = new();
            s.shoot();
        }
        
    }
}

6. String Constant Pool

  • 1) 개념
    • 문자열 상수 공간
    • string : 기본형이 아닌 참조형 변수
    • string 객체 생성 방법
      • String literal "" 사용 (내용이 같으면 동일한 주소)
      • new 연산자 사용 (내용이 같아도 다른 객체)
  • 2) 특징
    • 같은 문자열이면 같은 공간 공유 (메모리 효율⬆️)
    • 문자열을 자주 변경하면 새로운 공간이 너무 많이 할당됨
public class String{
	public static void main(String[] args) {
    	String s1 = new String("바다");
        String s2 = new String("바다");
        System.out.println(s1 == s2); // false (주소 일치X)
        
        String s3 = "바다";
        String s4 = "바다";
        System.out.println(s3 == s4); // true (주소 일치)
    }
}

📖 참고 📖 string.equals("문자열");

  • 문자열 비교시 값 자체와 주소 모두 비교
  • string literal과 new 연산자 상관 없이 비교 가능
    • String s1 = new String("바다");
    • System.out.println(s1.equals("바다"); // true
  • 🗒️ 예시 :

📖 참고 📖

  • ✏️

⬆️⬇️➡️

📕 버퍼와 소켓통신

1. 버퍼(Stream)

  • 1) 버퍼

    • 임시 저장 공간
  • 2) Stream

    • Byte 형태로 데이터를 운반하는데 사용되는 연결 통로
      • 데이터를 통신하기 위해 인코딩한 후 전달 ➡️ 디코딩을 통해 화면에 출력
      • InputStream(입력), OutputStream(출력)
    • 단방향 통신만 가능해서, 하나의 스트림으로 입력과 출력을 동시에 처리X
    • 큐의 FIFO 구조
  • 3) 과정

    • 1️⃣ : 키보드 A를 인코딩해서 010000000으로 컴퓨터 에전송
    • 2️⃣ : ByteStream으로 흘러들어감 (Input)
    • 3️⃣ : read() 메서드로 01000000 -> 65로 디코딩
    • 4️⃣ : int 65(숫자)를 char A (문자)로 부호화 시킴
    import java.io.InputStream;
    import java.io.IOException;
    
    public class Stream{
    	public static void main(String[] args) {
      	InputStream in = System.in; // System.in : 키보드에 연결된 스트림
          
          try {
          	int data = in.read(); // 디코딩
              System.out.println((char)data); // 부호화
          } catch (Exception e) {
          	e.printStackTrace();
          }
      }
    }
  • 4) BufferedReader

    • 숫자 ➡️ 부호화
    • 문자를 가변적으로 받음 (배열의 공간 낭비가 없음)
  • 5) BufferedReader 특징

    • 양끝단의 버퍼의 크기를 맞춤
    • 버퍼가 가득차면 자동 전송
    import java.io.InputStream;
    import java.io.InputStreamReader
    
    public class Stream{
    	public static void main(String[] args) {
      	InputStream in = System.in; 
          InputStreamReader ir = InputStreamReader(in); // 65 -> A로 부호화
          
          try {
          	char[] data = new char[3]; // 여러개 입력 가능 
          	ir.read(data); // 캐스팅 없이 디코딩 가능 
              System.out.println(data);
          } catch (Exception e) {
          	e.printStackTrace();
          }
          
          
          
          BufferedReader br = new BufferedReader(ir);
          
          try {
          	String data = br.readLine(); // 키보드로 입력
              System.out.println(data);  // 화면에 출력
              
          } catch (Exception e) {
          	System.out.println(e.getMessage());
          }
          
      }
    }

2. 소켓통신 (서버/클라이언트)

  • 1) 소켓통신
    • 포트간 ByteStream을 이용하여 데이터를 주고 받는 것
    • ip주소를 이용하여 서버 소켓과 클라이언트 소켓 연결 (포트 통신)
  • 2) 포트
    • 데이터를 주고 받는 통로
    • 0 ~ 65535(2^16, 2Byte)
      • 0 ~ 1023포트 사용X
  • 3) 소켓 통신 과정
    • 1️⃣ : 서버 소켓 시작
    • 2️⃣ : 서버 소켓 생성 완료
      • ServerSocket serverSocket;
      • Socket socket;
    • 3️⃣ : 클라이언트 접속 대기중
      • socket = serverSocket.accept();
    • 4️⃣ : 클라이언트 연결 완료
      • socket.getInputStream(); // 서버 소켓과 클라이언트 소켓 연결하는 ByteStream

  • ServerFile.java

    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class ServerFile {
    	
       ServerSocket serverSocket; // 클라이언트 연결을 받는 소켓
       Socket socket; // 실제 통신을 하는 소켓
       BufferedReader br;
       
       public ServerFile() {
       	System.out.println("1. 서버 소켓 시작");
           
           try {
           	ServerSocket = new ServerSocket(10000);
               System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 ");
               socket = serverSocket.accept(); // 클라이언트 접속 대기중
               System.out.println("3. 클라이언트 연결 완료");
               
               br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // ByteStream 연결
    
    				String msg = br.readLine();
               System.out.println("4. 클라이언트로부터 받은 메시지 : " + msg);
               
           } catch(Exception e) {
           	System.out.println("서버 소켓 에러 발생 : " + e.getMessage());
           }
       }
       
       public static void main(String[] args) {
       	new ServerFile();
       }
    }
  • ClientFile.java

    import java.net.Socket;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    
    public class ClientFile {
    
    	Socket socket;
      BufferedWriter bw;
      BufferedReader br;
      
      public ClientFile() {
      	System.out.println("1. 클라이언트 소켓 시작");
          try {
          	socket = new Socket("localhost", 10000); // 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)
              
              System.out.println("2. 버퍼(write) 연결 완료");
    				bw = new BufferedWriter(new OutputStreamWriter(socket.getOutpputStream());
              
              // 키보드 연결
              System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료");
              br = new BufferedReader(new InputStreamReader(System.in));
              
              System.out.println("4. 키보드 메시지 입력 대기중");
              String keyboardMsg = br.readLine();
          
              bw.write(keyboardMsg + "\n");  // 메시지 끝을 알려줘야 함 (\n 필요)
              bw.flush(); // 버퍼 비워주기
              
          } catch (Exception e) {
          	System.out.println("클라이언트 소켓 에러 발생함 : " + e.getMessage());
          }
      }
      
      public static void main(String[] args) {
      
      	new ClientFile();
          
      }
    }

📖 참고 📖 소켓 2개인 이유

  • ✏️ 클라이언트 소켓에서 ip주소(10000)으로 서버소켓에 요청시
    • 1️⃣ : 서버소켓 실행
    • 2️⃣ : 클라이언트 소켓 실행
    • 3️⃣ : 연결 시도
    • 4️⃣ : 서버소켓은 연결만 받는 용도로, 별도의 통신용 소켓 생성 (1024 ~ 65535중 사용하지 않는 포트 번호를 랜덤으로 선정)
    • 5️⃣ : ByteStream 연결
  • ✏️ 서버 소켓
    • ServerSocket serverSocket;
    • Socket socket;

3. 소켓 통신 스레드 적용

  • 1) 수정 사항1️⃣

    • 서버 : 계속 메시지를 read하도록 while문으로 수정
    • 클라이언트 : 계속 키보드에서 input과 write 대기하도록 while문으로 수정
  • ServerFile2.java

    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class ServerFile2 {
    	
       ServerSocket serverSocket; // 클라이언트 연결을 받는 소켓
       Socket socket; // 실제 통신을 하는 소켓
       BufferedReader br;
       
       public ServerFile() {
       	System.out.println("1. 서버 소켓 시작");
           
           try {
           	ServerSocket = new ServerSocket(10000);
               
               System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 ");
              	socket = serverSocket.accept(); // 클라이언트 접속 대기중
               System.out.println("3. 클라이언트 연결 완료");
               
               br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // ByteStream 연결
    
    				//------------------수정-----------------
    				while (true) {
    					String msg = br.readLine();
              		System.out.println("4. 클라이언트로부터 받은 메시지 : " + msg);
              
           } catch(Exception e) {
           	System.out.println("서버 소켓 에러 발생 : " + e.getMessage());
           }
       }
       
       public static void main(String[] args) {
       	new ServerFile();
       }
    }
  • ClientFile2.java

    import java.net.Socket;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    
    public class ClientFile2 {
    
    	Socket socket;
      BufferedWriter bw;
      BufferedReader br;
      
      public ClientFile() {
      	System.out.println("1. 클라이언트 소켓 시작");
          try {
          	socket = new Socket("localhost", 10000); // 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)
              
              System.out.println("2. 버퍼(write) 연결 완료");
    				bw = new BufferedWriter(new OutputStreamWriter(socket.getOutpputStream());
              
              // 키보드 연결
              System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료");
              br = new BufferedReader(new InputStreamReader(System.in));
              
              //------------------수정-----------------
              while (true) {
             	 	System.out.println("4. 키보드 메시지 입력 대기중");
             		 String keyboardMsg = br.readLine();
          
             		 bw.write(keyboardMsg + "\n");  // 메시지 끝을 알려줘야 함 (\n 필요)
             		 bw.flush(); // 버퍼 비워주기
               }
          } catch (Exception e) {
          	System.out.println("클라이언트 소켓 에러 발생함 : " + e.getMessage());
          }
      }
      
      public static void main(String[] args) {
      
      	new ClientFile();
          
      }
    }
  • 2) 수정 사항2️⃣

    • 메인 스레드가 바빠서 서브 스레드 생성
    • 단방향 ➡️ 양방향 통신으로 변경 (버퍼 추가 생성)
  • ServerThread.java

    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class ServerThread {
    	
       ServerSocket serverSocket; // 클라이언트 연결을 받는 소켓
       Socket socket; // 실제 통신을 하는 소켓
       BufferedReader br;
       // ------------------수정-----------------
       // 새로운 스레드 필요
       BufferedWriter bw; // 쓰기를 위한 버퍼
       BufferReader keyboard; // 키보드로부터 읽는 버퍼 
       
       public ServerFile() {
       	System.out.println("1. 서버 소켓 시작");
           
           try {
           	ServerSocket = new ServerSocket(10000);
               
               System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 ");
              	socket = serverSocket.accept(); // 클라이언트 접속 대기중
               System.out.println("3. 클라이언트 연결 완료");
               
               br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // ByteStream 연결
               // ------------------수정-----------------
               keyboard = new BufferReader(new InputStreamReader(System.in);
               bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream());
    
    				// 쓰기 스레드 역할 (write 스레드 실행)
    				WriteThread wt = nw WriteThread();
               Thread t1 = new Thread(wt);
               t1.start();
               
    				// main 스레드 역할 (글 읽기)
    				while (true) {
    					String msg = br.readLine();
              		System.out.println("4. 클라이언트로부터 받은 메시지 : " + msg);
              
           } catch(Exception e) {
           	System.out.println("서버 소켓 에러 발생 : " + e.getMessage());
           }
       }
       
       // 쓰기를 위한 스레드
       class WriteThread implements Runnable {
       	public void run() {
           	while (true) {
               	try {
                   	String keyboardMsg = keyboard.readLine();
                       bw.write(keyboardMsg + "\n");
                       bw.flush();
                   } catch (Exception e) {
                   	System.out.println("서버 소켓 쪽에서 키보드 입력받는 중 오류 발생 : " + e.getMessage());
                   }
               }
           }
       }
       
       public static void main(String[] args) {
       	new ServerFile();
       }
    }
  • ClientThread.java

    import java.net.Socket;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    
    public class ClientThread {
    
    	Socket socket;
      BufferedWriter bw;
      BufferedReader keyboard;
      
      public ClientFile() {
      	System.out.println("1. 클라이언트 소켓 시작");
          try {
          	socket = new Socket("localhost", 10000); // 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)
              
              System.out.println("2. 버퍼(write) 연결 완료");
    				bw = new BufferedWriter(new OutputStreamWriter(socket.getOutpputStream());
              
              // 키보드 연결
              System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료");
              keyboard = new BufferedReader(new InputStreamReader(System.in));
              
              //------------------수정-----------------
              br = new BufferedReader(new InputStreamReader(socket.getInputStream());
              
              // 읽기 쓰레드 역할 (read 메서드 실행)
              ReadThread rt = new ReadThread();
              Thread t1 = new Thread(rt);
              t1.start();
              
              // main 스레드 역할 (글 쓰기)
              while (true) {
             	 	System.out.println("4. 키보드 메시지 입력 대기중");
             		 String keyboardMsg = keyboard.readLine();
          
             		 bw.write(keyboardMsg + "\n");  // 메시지 끝을 알려줘야 함 (\n 필요)
             		 bw.flush(); // 버퍼 비워주기
               }
          } catch (Exception e) {
          	System.out.println("클라이언트 소켓 에러 발생함 : " + e.getMessage());
          }
      }
      
      class ReadThread implements Runnable {
      	public void run() {
          	while (true) {
          		try {
              		String msg = br.readLine();
                 		System.out.println("서버로 부터 받은 메시지 : " + msg);
              	} catch (Exception e) { 
              		System.out.println("클라이언트 소켓 쪽에서 서버 소켓 메시지를 입력받는 중 오류 발생 :" + e.getMessage());
              	}
              }
          }
      }
      
      public static void main(String[] args) {
      
      	new ClientFile();
          
      }
    }
profile
Notion으로 이동 (https://24tngus.notion.site/3a6883f0f47041fe8045ef330a147da3?v=973a0b5ec78a4462bac8010e3b4cd5c0&pvs=4)

0개의 댓글