Start

public class Filename {
    // 모든 코드는 클래스 내부에 작성
}
  • Filename.java 파일 생성 시 기본 설정되는 클래스 형태
  • psvm(public static void main) 또는 main + 엔터 키 입력 시 자동으로 main 메서드 완성

public class Filename {
    public static void main(String[] args) {
        System.out.print("Hello");      // print: 줄바꿈 x, println: 줄바꿈 o
        System.out.println(" World");   // [sout + 엔터 키] → System.out.println(); 완성
    }
}

Hello World

  • 모든 실행 코드는 반드시 main 메서드 내부에서 시작
  • public: JVM이 어디서든 접근 가능하도록 설정
  • static: 객체를 생성하지 않고도 실행되도록 설정 (Python의 if __name__ == "__main__": 과 비슷)
  • void: 반환값 없음
  • String[] args: 모든 인자는 문자열로 전달됨

Data types

public class Variables {
    public static void main(String[] args) {
        // ===== 정수형 =====
        int i = 3;                  // 기본 타입은 int (32bit)

        long l = 1000000000000L;    // long (64bit) → 리터럴 뒤에 L 또는 l 필요
        l = 1_000_000_000_000L;     // 언더바(_)는 가독성용으로, 컴파일 시 무시됨

        byte b = 127;               // -128 ~ 127
        short s = 32000;            // -32768 ~ 32767

        // ===== 실수형 =====
        double d = 3.14123456789;   // 기본 타입은 double (64bit)

        float f = 3.14123456789F;   // float (32bit) → 리터럴 뒤에 F 또는 f 필요

        // ===== 문자 / 문자열 =====
        char c1 = 'A';              // 단일 문자 (UTF-16 기반 유니코드)
        char c2 = 65;               // 'A' (Unicode 코드 포인트)

        // ===== 불리언 =====
        boolean isTrue = true;         // 참고: Python과 달리 truthy / falsy 개념 없음
        boolean isFalse = false;

         // ===== 상수 =====
        final double PI = 3.141592;    // final → 재할당 불가 (컴파일 에러)
    }
}

public class TypeCasting {
    public static void main(String[] args) {
        // 작은 범위 → 큰 범위 : 자동 형변환 (int → long → float → double)
        // 큰 범위 → 작은 범위 : 명시적 형변환 필요

        // ===== 정수 → 실수 =====
        int score = 3;
        float score_r2 = score;            // 3.0 (캐스팅 불필요)

        // ===== 실수 → 정수 =====
        float score_f = 3.14F;
        double score_d = 3.1415;
        System.out.println((int) score_f);  // 3
        System.out.println((int) score_d);  // 3

        // ===== 연산 =====
        score_d = 3 + 3.1415;               // 3 → 3.0 (자동 승격)
        score = 3 + (int) 3.14f;            // 3.14f → 3 (캐스팅 필요)

        // ===== 숫자 → 문자열 =====
        String s;
        s = String.valueOf(3);            // 방법1: String 클래스 사용 (권장)
        s = Integer.toString(3);          // 방법2: Integer, Double 클래스 사용
        s = Double.toString(3.1415);

        // ===== 문자열 → 숫자 =====
        int i = Integer.parseInt("3");
        double d = Double.parseDouble("3.1415");
    }
}

enum Resolution {
    HD(1280), FHD(1920), UHD(3840);     // 각 상수 값이 width 값을 갖는 형태

    private final int width;
    Resolution(int width) {             // 생성자로 width 지정
        this.width = width;
    }

    public int getWidth() {             // getter
        return width;
    }
}

public class Type_Enum {
    public static void main(String[] args) {
        Resolution resolution = Resolution.HD;  // 생성된 객체(resolution)에 값(HD) 지정
        resolution = Resolution.valueOf("UHD"); // 값을 FHD으로 변경(문자열로부터 값 가져오는 경우)

        System.out.print("동영상 녹화 품질 : ");
        switch (resolution)  {
            case HD:
                System.out.println("일반화질");
                break;
            case FHD:
                System.out.println("고화질");
                break;
            case UHD:
                System.out.println("초고화질");
                break;
        }

        for (Resolution myRes : Resolution.values()) {  // name: 값 이름, ordinal: 값 입력 순서
            System.out.println(myRes.name() + " : " + myRes.ordinal());
        }

        System.out.println("-------------------");
        for (Resolution myRes : Resolution.values()) {
            System.out.println(myRes.name() + " : " + myRes.getWidth());
        }
    }
}

Operator

public class Operator {
    public static void main(String[] args) {
        // ===== 산술 연산자 =====
        System.out.println(5 + 2);      // 7
        System.out.println(5 - 2);      // 3
        System.out.println(5 * 2);      // 10
        System.out.println(5 / 2);      // 2 (파이썬의 정수 // 정수 와 동일)
        System.out.println(5 % 2);      // 1
        
        // ===== 증감 연산자 (++, --) =====
        int val;
        val = 10;
        System.out.println(val);    // 10
        System.out.println(++val);  // 11 (val+1 먼저 적용 후 문장 실행)
        System.out.println(val);    // 11

        val = 10;
        System.out.println(val);    // 10
        System.out.println(val++);  // 10 (문장 먼저 실행 후 val+1 적용)
        System.out.println(val);    // 11

        // ===== 대입 연산자 =====
        int num = 10;
        num += 2;                   // num = num + 2
        System.out.println(num);    // 12

        num -= 2;                   // num = num - 2
        System.out.println(num);    // 10

        num *= 2;                   // num = num * 2
        System.out.println(num);    // 20

        num /= 2;                   // num = num / 2
        System.out.println(num);    // 10

        num %= 2;                   // num = num % 2;
        System.out.println(num);    // 0

        // ===== 비교 연산자 =====
        System.out.println(5 > 5);  // false
        System.out.println(5 >= 5); // true
        System.out.println(5 < 7);  // true
        System.out.println(5 <= 3); // false
        
        System.out.println(5 == 5); // true
        System.out.println(5 != 5); // false

        // ===== 논리 연산자 =====
        boolean a = true;
        boolean b = false;
        boolean c = 5 > 3;  // true
        System.out.println(a || b || c);    // or 연산 (true가 하나 이상 있으므로 true)
        System.out.println(a && b && c);    // and 연산 (b가 false이므로 false)

        System.out.println(!a);             // not 연산 (!true -> false)
        System.out.println(!b);             // not 연산 (!false -> true)

        // ===== 삼항 연산자 =====
        // 결과 = (조건) ? (true일 때 결과값) : (false일 때 결과값)
        int x = 3;
        int y = 5;
        int max = (x > y) ? x : y;      
        System.out.println(max);                                  // 5

        String result = (x == y) ? "same" : "different";
        System.out.println(x + " and " + y + " are " + result);   // 3 and 5 are different
    }
}

String

public class test {
    public static void main(String[] args) {
        // String은 immutable(불변)이므로 원본은 변경되지 않음
        String s = "I like Java and Python and C.";
        
        // 문자열의 길이
        int len = s.length();               // (29)

        // 대소문자 변환
        String upper = s.toUpperCase();     // 모두 대문자
        String lower = s.toLowerCase();     // 모두 소문자

        // 포함 관계
        boolean isIn = s.contains("C#");    // 해당 문자열 포함 여부 (false)
        int whereIdx = s.indexOf("C#");     // 발견되지 않는다면 -1
        int firstIdx = s.indexOf("Java");   // 처음 발견되는 위치 (7)
        int lastIdx = s.lastIndexOf("and"); // 마지막 발견되는 위치 (23)
        boolean start = s.startsWith("I");  // 이 문자열로 시작하면 true
        boolean end = s.endsWith(".");      // 이 문자열로 끝나면 true

        // 문자열 변환
        String newS = s.replace(" and", ",");           // " and" 를 "," 로 변환
        String subS1 = s.substring(s.indexOf("Java"));  // "Java"가 발견되는 위치부터 끝까지
        String subS2 = s.substring(7, s.indexOf("."));  // 인덱스 7부터 "." 앞까지 (마지막 포함 x)

        // 공백 제거
        s = "      I love Java.     ";
        String trimmedS = s.trim();         // 앞뒤 공백문자 제거(중간 공백은 제거 x)

        // 문자열 결합
        String s1 = "Java";
        String s2 = "Python";
        String mergedS1 = s1 + ", " + s2;             // Java, Python
        String mergedS2 = s1.concat(", ").concat(s2); // Java, Python

        // 문자열 비교
        boolean isAllSame = s1.equals("Java");

        // 문자열 비교 (대소문자 구분 x)
        boolean isCaseSame = s2.equalsIgnoreCase("python");

        // equals() vs ==
        s1 = "1234";               // 문자열 리터럴 "1234"이 String Constant Pool에 저장됨
        s2 = "1234";               // 같은 리터럴은 하나만 생성되기 때문에 s1과 같은 객체를 가리킴
        System.out.println(s1.equals(s2));  // true  (같은 내용)
        System.out.println(s1 == s2);       // true  (같은 객체)

        s1 = new String("1234");   // new String()은 무조건 새 객체 생성
        s2 = new String("1234");   // s1과 다른 객체
        System.out.println(s1.equals(s2));  // true  (같은 내용)
        System.out.println(s1 == s2);       // false (다른 객체)

        // 특수문자, 이스케이프 문자
        System.out.println("I\nLove\nJava.");           // \n : 줄바꿈
        System.out.println("1234\t5678");               // \t : 탭    

        System.out.println("C:\\Program Files\\Java");  // \\ : 역슬래시
        System.out.println("I \"love\" Java.");         // \" : 큰따옴표

        char c = '\'';                                  // \' : 작은따옴표  
        System.out.println(c);                              
    }
}

Condition

public class Condition {
    public static void main(String[] args) {
        // ===== if, else if, else =====
        int a = 10;
        int b = 20;
        int c = 30;

        if (a < b)
            System.out.println("b is greater than a");    // 하나의 문장은 { } 생략 가능

        if (a > b) {
            System.out.println("a is greater than b");
        } else if (a > c) {
            System.out.println("a is greater than c");
        } else if (a == b) {
            System.out.println("a equals b");
        } else {
            System.out.println("a is the smallest");
        }

        // ===== Switch Case =====
        int rank = 2;
        switch (rank) {
            case 1:                                 // 1등은 전액
                System.out.println("전액 장학금");
                break;
            case 2:                                 // 2, 3등은 반액(break 생략으로 통합)
            case 3:
                System.out.println("반액 장학금");
                break;
            default:                                // 그 외 등수는 장학금 대상 아님
                System.out.println("장학금 없음");
        }

        // ===== For =====
        int sum = 0;
        for (int i = 1; i <= 10; i++) {     // 초기식; 조건식; 증감식
            sum += i;
        }
        System.out.println("1부터 10까지의 총합은 " + sum + "입니다.");

        // ===== While =====
        sum = 0;
        int idx = 1;                        // 초기식
        while (idx <= 10) {                 // 조건식
            sum += idx;                       
            idx++;                          // 증감식
        }
        System.out.println("1부터 10까지의 총합은 " + sum + "입니다.");
        
        // ===== Do While =====
        idx = 100;
        do {
            System.out.println("do-while"); // 조건이 처음부터 false여도 1번은 실행됨
        } while (idx <= 10);

        // ===== Continue =====
        for (int i = 1; i <= 5; i++) {
            if (i == 3) {
                continue;                   // 이번 반복만 스킵
            }
            System.out.print(i + " ");      // 1 2 4 5
        }

        // ===== Break =====
        for (int i = 1; i <= 5; i++) {
            if (i == 3) {
                break;                      // 반복문 즉시 종료
            }
            System.out.print(i + " ");      // 1 2
        }
    }
}

Array

public class Arr {
    public static void main(String[] args) {
        // ===== 배열 선언 =====
        // 방법 1
        String[] fruits1 = new String[4];
        
        // 방법 2
        String fruits2[] = new String[4];

        // 방법 3
        String[] fruits3 = new String[] { "Apple", "Banana", "Cherry", "Durian" };

        // 방법 4
        String[] fruits4 = { "Apple", "Banana", "Cherry", "Durian" };

        // ===== 배열 루프 =====
        for (int i = 0; i < fruits4.length; i++) {  // 배열 길이 이용
            System.out.println(fruits4[i]);
        }

        for (String f : fruits4) {                  // for each 반복문
            System.out.println(f);
        }

        // ===== 2차원 배열 =====
        String[][] arr2d = {
            {"A0", "A1", "A2", "A3", "A4"},
            {"B0", "B1", "B2", "B3", "B4"},
            {"C0", "C1", "C2", "C3", "C4"}
        };
    }
}

Method

public class Method {
    // ===== 메소드 정의(main 영역 밖) =====
    public static void callMethod() {          // 1. Parameter x, Return x
        System.out.println("기본 메소드");
    }

    public static void power(int number) {     // 2. Parameter
        int result = number * number;
        System.out.println(number + " ** 2 = " + result);
    }

    public static String getNumber() {         // 3. Return (반환값 타입 정의 필요)
        String phoneNumber = "02-1234-5678";
        return phoneNumber;
    }

    public static int getPower(int number) {   // 4. Parameter + Return
        int result = number * number;
        return result;
    }

    public static String printStr(String s) {  // 5. Overloading (이름이 같은 메소드 여러 개)
        return s;
    }

    public static String printStr(int number) {
        return String.valueOf(number);
    }

    public static String printStr(String s, int n) {
        return s + String.valueOf(n);
    }

    public static String printStr(int n, String s) {
        return s + String.valueOf(n);
    }

    // ===== 메소드 호출(main 영역 안) =====
    public static void main(String[] args) {
        callMethod();                          // 1
        power(3);                              // 2 (3을 Argument로 전달)
        String contact = getNumber();          // 3 (반환값을 받을 변수 선언 필요)
        int retVal = getPower(2);              // 4
        System.out.println(printStr("123"));   // 5 (문자열 타입 매개변수 1개)
        System.out.println(printStr(123));       // (매개변수 타입 다름)
        System.out.println(printStr("ben", 6));  // (매개변수 개수 다름) 
        System.out.println(printStr(7, "will")); // (매개변수 순서 다름) 
    } 
}

Class

custom package 1

package dashcam;

public class BlackBox {
    // ===== 인스턴스 변수(서로 다른 객체에서 서로 다른 값) 정의 =====
    String modelName;
    String resolution;
    int price;
    String color;
    int serialNumber;

    // ===== 클래스 변수(모든 객체에 똑같이 적용) 정의 =====
    static int serialCounter = 0;
    static boolean canAutoReport = false;
    
    // ===== 생성자(객체 생성 시 자동 호출) =====
    BlackBox() {
        System.out.println("기본 생성자 호출");
        this.serialNumber = ++serialCounter;    // 생성자에서 인스턴스 변수 업데이트
    }

    BlackBox(String modelName, String resolution, int price, String color) {
        this();                                 // this와 괄호로 다른 생성자 호출 가능
        System.out.println("사용자 정의 생성자 호출");
        this.modelName = modelName;
        this.resolution = resolution;
        this.price = price;
        this.color = color;
    }

    // ===== 메소드(객체가 사용할 기능) 정의 =====
    void autoReport() {
        if (canAutoReport) {
            System.out.println("충돌 감지 자동 신고 실행");
        }
        else {
            System.out.println("자동 신고 기능 미지원");
        }
    }
    
    void appendModelName(String modelName) {
        this.modelName += modelName;    // this로 클래스의 인스턴스 변수임을 명시
    }

    int getVideoFileCount(int type) {
        if (type == 1) {
            return 9;
        }
        else if (type == 2) {
            return 1;
        }
        return 10;
    }
    
    // ===== 메소드 오버로딩 =====
    void record(boolean showDateTime, boolean showSpeed, int min) {
        System.out.println("영상 녹화 시작");
        if (showDateTime) {
            System.out.println("녹화 영상에 날짜정보 표시");
        }
        if (showSpeed) {
            System.out.println("녹화 영상에 속도정보 표시");
        }
        System.out.println("영상은 " + min + "분 단위로 기록");
    }

    void record() {
        record(true, true, 5);  // 파라미터 입력 안 하면 원하는 기본값으로 호출
    }

    // ===== 클래스 메소드 =====
    static void callServiceCenter() {
        System.out.println("1588-1234로 연결");
        canAutoReport = true;   // static 메소드에서 클래스 변수 접근 가능
    }
}
package dashcam;

public class BlackBoxRefurbish {
    public String modelName;    // public : 모든 클래스에서 접근 가능
    String resolution;          // default : 같은 패키지 내에서만 접근 가능(접근 제어자 명시x)
    private int price;          // private : 해당 클래스 내에서만 접근 가능
    protected String color;     // protected : 1. 같은 패키지 내에서 접근 가능
                                            // 2. 다른 패키지인 경우 자식 클래스에서 접근 가능
    
    // ===== getter & setter =====
    public String getModelName() {
        return modelName;
    }
    public void setModelName(String modelName) {
        this.modelName = modelName;
    }

    public String getResolution() {
        if (resolution == null || resolution.isEmpty()) {   // 값을 미입력하는 실수 대비
            return "판매자에게 문의하세요.";
        }
        return resolution;
    }
    public void setResolution(String resolution) {
        this.resolution = resolution;
    }

    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {                       // 값을 적게 입력하는 실수 대비
        if (price < 100000) {
            this.price = 100000;
        }
        else {
            this.price = price;
        }
    }

    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
}

custom package 2

package camera;

public class Camera {               // 부모 클래스(기능 물려주기)
    public String name;

    public Camera() {               // 생성자 1(같은 클래스의 생성자 2 호출)
        this("카메라");
    }

    protected Camera(String name) { // 생성자 2(실제 초기화 담당)
        this.name = name;
    }

    public void takePicture() {     // 사진 촬영(모든 종류 카메라의 기본 기능)
        System.out.println(this.name + " : 사진을 촬영합니다.");
    }

    public void recordVideo() {     // 동영상 녹화(모든 종류 카메라의 기본 기능)
        System.out.println(this.name + " : 동영상을 녹화합니다.");
    }

    public void showMainFeature() { // 자식 클래스에서 오버라이드 할 메소드
        System.out.println(this.name + "의 주요 기능 : 사진 촬영, 동영상 녹화");
    }
}
package camera;

// FactoryCam IS A Camera (o) | Camera IS A FactoryCam (x)
public class FactoryCam extends Camera {    // 자식 클래스(기능 물려받기)
    public FactoryCam() {
        super("공장 카메라");                  // super() 으로 부모 클래스의 생성자에 접근
    }

    public void recordVideo() {             // 동영상 녹화
        super.recordVideo();                // super.  으로 부모 클래스의 메소드에 접근
        detectFire();
    }

    public void detectFire() {              // 화재 감지
        System.out.println("화재를 감지합니다.");
    }

    public void showMainFeature() {         // 부모 클래스의 메소드 오버라이딩(재정의)
        System.out.println(this.name + "의 주요 기능 : 화재 감지");
    }
}
package camera;

// SpeedCam IS A Camera (o) | Camera IS A SpeedCam (x)
public class SpeedCam extends Camera {      // 자식 클래스(기능 물려받기)
    public SpeedCam() {
        super("과속단속 카메라");               // super() 으로 부모 클래스의 생성자에 접근
    }

    public void takePicture() {             // 사진 촬영
        super.takePicture();                // super.  으로 부모 클래스의 메소드에 접근
        checkSpeed();
        recognizeLicensePlate();
    }

    public void checkSpeed() {              // 속도 측정
        System.out.println("속도를 측정합니다.");
    }

    public void recognizeLicensePlate() {   // 번호 인식
        System.out.println("차량 번호를 인식합니다.");
    }

    @Override                               // annotation(오버라이드 불가능한 경우 에러 발생)
    public void showMainFeature() {         // 부모 클래스의 메소드 오버라이딩(재정의)
        System.out.println(this.name + "의 주요 기능 : 속도 측정, 번호 인식");
    }
}

package camera;

public final class ActionCam extends Camera {   // final로 클래스 상속 금지
    public final String lens;                   // final로 값 수정 금지(초기화는 바로 or 생성자)
    public ActionCam() {
        super("액션 카메라");
        lens = "광각렌즈";
    }

    public final void makeVideo() {             // final로 메소드 오버라이딩 금지
        System.out.println(this.name + " : " + this.lens + "로 촬영한 영상을 통해 비디오를 제작합니다.");
    }
}
package camera;

public class SlowActionCam {                    // ActionCam 상속 불가
    public String name;
    public SlowActionCam() {
        this.name = "슬로우 액션 카메라";
    }

    public void makeVideo() {
        System.out.println("자체 개발한 비디오 제작 기능");
    }
}

main class

package learnClass;                 // 해당 패키지(연관된 클래스들의 묶음) 명시             

import dashcam.BlackBox;            // 다른 패키지의 특정 클래스 사용
import dashcam.BlackBoxRefurbish;
import camera.*;                    // 다른 패키지의 모든 클래스 사용

public class OOP {
    public static void main(String[] args) {
        // ============== BlackBox 클래스의 인스턴스(객체) ==============
        BlackBox bbox1 = new BlackBox();    // 기본 생성자 사용
        bbox1.modelName = "g1";             // 인스턴스 변수 업데이트
        bbox1.resolution = "FHD";
        bbox1.price = 200000;
        bbox1.color = "black";

        BlackBox bbox2 = new BlackBox("g2", "UHD", 300000, "white");    // 사용자 정의 생성자 사용

        BlackBox.canAutoReport = true;                                  // 클래스 변수 업데이트
        System.out.println("전 제품 자동 신고: " + BlackBox.canAutoReport); // 클래스 변수 접근

        bbox1.autoReport();                 // 메소드 호출
        bbox1.appendModelName("(latest)");
        int fileCount = bbox1.getVideoFileCount(1);
        fileCount = bbox1.getVideoFileCount(2);
        
        bbox1.record(false, false, 10);     // 메소드 오버로딩 예시
        bbox1.record();

        BlackBox.callServiceCenter();       // 클래스 메소드 접근

        // ============== BlackBoxRefurbish 클래스의 인스턴스(객체) ==============
        BlackBoxRefurbish bbox3 = new BlackBoxRefurbish();
        bbox3.modelName = "g3";
     // bbox3.price = -5000;        // price는 private이기 때문에 직접 접근 불가
        bbox3.setPrice(-5000);      // setter를 통해서만 price에 접근 가능
        bbox3.color = "red";

        System.out.println("해상도 : " + bbox3.getResolution());    // getter로 null값 출력 방지


        // ============== Camera 패키지 클래스들의 인스턴스(객체) ==============
        Camera camera = new Camera();           // 부모 클래스
        Camera factoryCam = new FactoryCam();   // 자식 클래스(다형성, 부모 클래스로 서로 다른 객체 생성)
        SpeedCam speedCam = new SpeedCam();     // 자식 클래스
        
        if (factoryCam instanceof FactoryCam) {     // 부모 클래스로 정의한 객체로 자식 클래스 메소드 접근 방법
            ((FactoryCam)factoryCam).detectFire();  // FactoryCam 클래스 타입으로 변환 후 호출
        }

        // --------------- 참조 ---------------
        int a = 10;                 // 기본 자료형(int, float, double, long, boolean 등)
        int b = 20;
        b = a;                      // 값 복사(복사본을 변경해도 원본에 영향 x)
        System.out.println(a);          // 10
        System.out.println(b);          // 20
        b = 30;
        System.out.println(a);          // 10
        System.out.println(b);          // 30

        Camera c1 = new Camera();   // 참조 자료형(String, 사용자 정의 클래스)
        Camera c2 = new Camera();
        c1.name = "카메라1";
        c2.name = "카메라2";
        System.out.println(c1.name);    // 카메라1
        System.out.println(c2.name);    // 카메라2
        c2 = c1;                    // 참조 복사(주소값을 복사하므로 2개의 변수가 동일한 객체 가리킴)
        System.out.println(c1.name);    // 카메라1
        System.out.println(c2.name);    // 카메라1
        c2.name = "고장난 카메라";
        System.out.println(c1.name);    // 고장난 카메라
        System.out.println(c2.name);    // 고장난 카메라

        c2 = null;                  // 참조 관계 끊는 방법

        // --------------- final ---------------
        ActionCam actionCam = new ActionCam();
        actionCam.makeVideo();

        SlowActionCam slowActionCam = new SlowActionCam();
        slowActionCam.makeVideo();  // 메소드 오버라이딩 아님
    }
}

Abstract Class

custom package 1

package learnAbstractClass.camera;

public abstract class Camera {                  // 추상 클래스(미완성된 클래스)
    public void takePicture() {
        System.out.println("사진을 촬영합니다.");
    }

    public void recordVideo() {
        System.out.println("동영상을 녹화합니다.");
    }

    public abstract void showMainFeature();     // 추상 메소드(선언만 하고 나중에 구현 필요)
}
package learnAbstractClass.camera;

import learnAbstractClass.detector.Detectable;
import learnAbstractClass.reporter.Reportable;

// Camera 클래스를 상속하고 Detectable과 Reportable 인터페이스의 기능을 사용하는 클래스
public class FactoryCam extends Camera implements Detectable, Reportable {
    private Detectable detector;                    // 외부에서 전달받은 인터페이스 변수
    private Reportable reporter;

    public void setDetector(Detectable detector) {  // setter로 원하는 기능 받아오기
        this.detector = detector;
    }

    public void setReporter(Reportable reporter) {
        this.reporter = reporter;
    }

    @Override
    public void showMainFeature() {                 // 추상 메소드를 오버라이드로 구현
        System.out.println("화재 감지");
    }

    @Override
    public void detect() {                          // 인터페이스의 메소드 구현(실제 동작은 해당 객체에 위임)
        detector.detect();
    }

    @Override
    public void report() {
        reporter.report();
    }
}

custom package 2

package learnAbstractClass.reporter;

public interface Reportable {
    void report();                                  // 인터페이스에서 메소드 선언
}
package learnAbstractClass.reporter;

public class NormalReporter implements Reportable { // 인터페이스를 구현하여 기능을 정의하는 클래스
    @Override
    public void report() {
        System.out.println("일반 화재 신고를 진행합니다.");
    }
}
package learnAbstractClass.reporter;

public class VideoReporter implements Reportable {
    @Override
    public void report() {
        System.out.println("직전 30초 영상과 함께 신고를 진행합니다.");
    }
}

custom package 3

package learnAbstractClass.detector;

public interface Detectable {
    void detect();                                          // 인터페이스에서 메소드 선언
}
package learnAbstractClass.detector;

public class FireDetector implements Detectable {           // 인터페이스를 구현하여 기능을 정의하는 클래스
    @Override
    public void detect() {
        System.out.println("일반 성능으로 화재를 감지합니다.");
    }
}
package learnAbstractClass.detector;

public class AdvancedFireDetector implements Detectable{
    @Override
    public void detect() {
        System.out.println("향상된 성능으로 화재를 감지합니다.");
    }
}

main class

package learnAbstractClass; 

import learnAbstractClass.camera.*;
import learnAbstractClass.reporter.*;
import learnAbstractClass.detector.*;

public class _01_AbstractClass {
    public static void main(String[] args) {
        // ============== 추상 클래스와 자식 클래스 ==============
     // Camera camera = new Camera();               // 추상 클래스 객체는 생성 불가

        FactoryCam factoryCam = new FactoryCam();   // 추상 클래스의 자식 클래스 객체는 생성 가능
        factoryCam.showMainFeature();

        // ============== 인터페이스 ==============
        // ---------- 신고 기능 ----------
        Reportable normalReporter = new NormalReporter();
        normalReporter.report();

        Reportable videoReporter = new VideoReporter();
        videoReporter.report();

        // ---------- 감지 기능 ----------
        Detectable fireDetector = new FireDetector();
        fireDetector.detect();

        Detectable advancedFireDetector = new AdvancedFireDetector();
        advancedFireDetector.detect();

        // ---------- 객체에 원하는 기능 장착 및 실행 ----------
        factoryCam.setDetector(advancedFireDetector);   // setter를 통해서 외부에서 기능 주입
        factoryCam.setReporter(videoReporter);

        factoryCam.detect();                            // 주입된 객체의 메소드 호출
        factoryCam.report();
    }
}

Generics

custom package 1

package generics.coffee;

public class CoffeeByNumber {
    public int waitingNumber;
    public CoffeeByNumber(int waitingNumber) {
        this.waitingNumber = waitingNumber;
    }

    public void ready() {
        System.out.println("커피 준비 완료 : " + waitingNumber);
    }
}
package generics.coffee;

public class CoffeeByNickname {
    public String nickname;
    public CoffeeByNickname(String nickname) {
        this.nickname = nickname;
    }

    public void ready() {
        System.out.println("커피 준비 완료 : " + nickname);
    }
}

package generics.coffee;

public class CoffeeByName {
    public Object name;     // Integer, Double, String, 사용자 정의 클래스 모두 가능
    public CoffeeByName(Object name) {
        this.name = name;
    }

    public void ready() {
        System.out.println("커피 준비 완료 : " + name);
    }
}

package generics.coffee;

public class Coffee <T>{    // 제네릭 클래스    
    public T name;          // 타입 미상
    public Coffee(T name) {
        this.name = name;
    }

    public void ready() {
        System.out.println("커피 준비 완료 : " + name);
    }
}

package generics.coffee;

import generics.user.User;

public class CoffeeByUser <T extends User>{     // 반드시 User 클래스를 상속하는 타입만 사용
    public T user;
    public CoffeeByUser(T user) {
        this.user = user;
    }

    public void ready() {
        System.out.println("커피 준비 완료 : " + user.name);
        user.addPoint();
    }
}

custom package 2

package generics.user;

public class User {
    public String name;
    public User(String name) {
        this.name = name;
    }

    public void addPoint() {
        System.out.println(this.name + "님, 포인트 적립되었습니다.");
    }
}
package generics.user;

public class VIPUser extends User {     // User 클래스 상속
    public VIPUser(String name) {
        super("특별한 " + name);
    }
}

main class

package generics;

import generics.coffee.*;
import generics.user.*;

public class Generics {
    public static void main(String[] args) {
        // ============== 제네릭스 ==============
        // 기본 자료형은 Wrapper 클래스 타입으로 변환 필요
        Integer[] iArray = {1, 2, 3, 4, 5};             // int → Integer
        Double[] dArray = {1.0, 2.0, 3.0, 4.0, 5.0};    // double → Double
        Character[] cArray = {'a', 'b', 'c', 'd', 'e'}; // char → Character
        String[] sArray = {"A", "B", "C", "D", "E"};    // 참조형은 그대로 사용

        printAnyArray(iArray);
        printAnyArray(dArray);
        printAnyArray(cArray);
        printAnyArray(sArray);

        // ============== 제네릭 클래스 ==============
        // -------- 방법1(타입별 클래스 모두 생성) --------
        CoffeeByNumber c1 = new CoffeeByNumber(33);           // 정수형만 가능
        c1.ready();

        CoffeeByNickname c2 = new CoffeeByNickname("유재석");   // 문자열만 가능
        c2.ready();

        // -------- 방법2(Object로 모든 타입에 적용) --------
        CoffeeByName c3 = new CoffeeByName(34);               
        c3.ready();
        int c3Name = (int) c3.name;                           // Object 타입이라서 형변환 필요

        CoffeeByName c4 = new CoffeeByName("박명수");
        c4.ready();
     // String c4Name = (String) c3.name;                     // 정수->문자열 변환 에러

        // -------- 방법3(제네릭 클래스로 모든 타입에 적용) --------
        Coffee<Integer> c5 = new Coffee<>(35);                // 자료형 정의
        c5.ready();
        int c5Name = c5.name;

        Coffee<String> c6 = new Coffee<>("조세호");
        c6.ready();
        String c6Name = c6.name;

        // -------- 방법4(제한된 클래스만 받도록 설정) --------
        CoffeeByUser<User> c7 = new CoffeeByUser<>(new User("강호동"));
        c7.ready();

        CoffeeByUser<User> c8 = new CoffeeByUser<>(new VIPUser("서장훈"));
        c8.ready();

        // -------- 방법5(여러 값 동시에 전달) --------
        orderCoffee("김영철");
        orderCoffee(36);

        orderCoffee("김희철", "아메리카노");
        orderCoffee(37, "라떼");

        // ============== Wrapper Class ==============
        Integer x = 10;             // Integer 객체
        String y = x.toString();    // toString() 메서드 호출 가능

        Double a = 1.0;             // Double 객체
        int b = a.intValue();       // intValue() 메서드 호출 가능
    }

    //------------------------------------------------
    private static <T> void printAnyArray(T[] array) {          // T : Type, V : Value...
        for (T t : array) {
            System.out.print(t + " ");
        }
        System.out.println();
    }
    
    //------------------------------------------
    public static <T> void orderCoffee(T name) {                // 값 1개 전달
        System.out.println("커피 준비 완료 : " + name);
    }

    public static <T, V> void orderCoffee(T name, V coffee) {   // 값 2개 전달
        System.out.println(coffee + " 준비 완료 : " + name);
    }
}

(Collection Framework)

ArrayList

import java.util.ArrayList;
import java.util.Collections;

public class ArrayList {
    public static void main(String[] args) {
        // ===== 어레이 리스트 생성 =====
        ArrayList<String> list = new ArrayList<>();

        // ===== 데이터 추가 =====
        list.add("유재석"); // 인덱스 0
        list.add("조세호"); // 인덱스 1
        list.add("김종국"); // 인덱스 2
        list.add("박명수"); // 인덱스 3
        list.add("강호동"); // 인덱스 4

        // ===== 데이터 조회 =====
        String firstIn = list.get(0);     // 인덱스 0 값 조회 

        // ===== 데이터 삭제 =====
        System.out.println("신청 학생 수 : " + list.size());    // 5
        list.remove("박명수");              // 인덱스 3 삭제
        System.out.println(list.get(3));  // 인덱스 4의 값이 3 자리로 이동
        list.remove(list.size() - 1);     // 마지막 인덱스 삭제
        System.out.println("남은 학생 수 : " + list.size());    // 3

        // ===== 데이터 순회 =====
        for (String s : list) {
            System.out.print(s + " ");                       // "유재석 조세호 김종국 "
        }

        // ===== 데이터 정렬 =====
        Collections.sort(list);           // 사전 순 정렬  
        for (String s : list) {
            System.out.print(s + " ");                       // "김종국 유재석 조세호 "  
        }

        // ===== 데이터 변경 =====
        list.set(0, "이수근");              // 인덱스 0 값 변경

        // ===== 데이터 확인 =====
        System.out.println(list.indexOf("김종국")); // 방법1. 해당 데이터의 인덱스값 확인
        if (list.contains("김종국")) {              // 방법2. 리스트 내에 포함 여부를 bool로 반환
            System.out.println("수강 신청 성공");
        } else {
            System.out.println("수강 신청 실패");
        }

        // ===== 전체 삭제 =====
        list.clear();
        
        if (list.isEmpty()) {
            System.out.println("리스트가 비었습니다.");
        }
    }
}

LinkedList

import java.util.Collections;
import java.util.LinkedList;

public class LinkedList {
    public static void main(String[] args) {
        // ===== 링크드 리스트 생성 =====
        LinkedList<String> list = new LinkedList<>();

        // ===== 데이터 추가 (마지막 위치) =====
        list.add("유재석");
        list.add("조세호");
        list.add("김종국");
        list.add("박명수");
        list.add("강호동");

        // ===== 데이터 추가 (위치 지정) =====
        list.add(1, "김영철");                  // 원하는 위치에 데이터 추가
        list.addFirst("서장훈");                // 맨 앞에 데이터 추가
        list.addLast("김희철");                 // 맨 뒤에 데이터 추가

        // ===== 데이터 조회 =====
        System.out.println(list.get(2));      // 해당 인덱스의 데이터
        System.out.println(list.getFirst());  // 첫번째 데이터
        System.out.println(list.getLast());   // 마지막 데이터

        // ===== 데이터 삭제 =====
        list.remove(list.size() - 1);         // 해당 인덱스의 데이터
        list.removeFirst();                   // 첫번째 데이터
        list.removeLast();                    // 마지막 데이터

        // ===== 데이터 정렬 =====
        Collections.sort(list);
        for (String s : list) {
            System.out.println(s);
        }

        // ===== 데이터 변경 =====
        list.set(0, "이수근");

        // ===== 데이터 확인 =====
        System.out.println(list.indexOf("김종국"));
        boolean isIn = list.contains("김종국");

        // ===== 전체 삭제 =====
        list.clear();
        boolean isZero = list.isEmpty()
    }
}

HashSet

import java.util.HashSet;           // 중복 허용 x, 순서 보장 x
import java.util.LinkedHashSet;     // 중복 허용 x, 순서 보장 o

public class HashSet {
    public static void main(String[] args) {
        // ===== 세트 생성 =====
        HashSet<String> set = new HashSet<>();

        // ===== 데이터 추가 =====
        set.add("삼겹살");
        set.add("쌈장");
        set.add("소금");
        set.add("후추");
        set.add("삼겹살");
        set.add("깻잎");
        set.add("상추");
        set.add("삼겹살");                                    // 8번 추가

        System.out.println("총 상품 가지 수 : " + set.size());  // 6

        // ===== 데이터 순회 ===== 
        for (String s : set) {
            System.out.println(s);
        }

        // ===== 데이터 확인 =====
        if (!set.contains("삼겹살")) {
            System.out.println("정육 코너 방문");
        }

        // ===== 삭제 =====
        set.remove("삼겹살");

        // ===== 전체 삭제 =====
        set.clear();
        if (set.isEmpty()) {
            System.out.println("집으로 출발");
        }

        // ===== 링크드 세트 생성 =====
        HashSet<Integer> intSet = new LinkedHashSet<>();
        intSet.add(1);
        intSet.add(13);
        intSet.add(5);

        for (int i : intSet) {          
            System.out.println(i);      // 1 13 5
        }
    }
}

HashMap

import java.util.HashMap;           // 중복 허용 x, 순서 보장 x
import java.util.LinkedHashMap;     // 중복 허용 x, 순서 보장 x

public class HashMap {
    public static void main(String[] args) {
        // ===== 맵 생성 (Key, Value) =====
        HashMap<String, Integer> map = new HashMap<>();

        // ===== 데이터 추가 =====
        map.put("유재석", 10);
        map.put("박명수", 5);
        map.put("김종국", 3);
        map.put("김종국", 30);
        map.put("김종국", 50);              // 김종국 -> 마지막 값 50으로 저장됨

        System.out.println("총 고객 수 : " + map.size());           // 3

        // ===== 데이터 조회 =====
        System.out.println("유재석님의 포인트 : " + map.get("유재석")); // 10 (값)

        // ===== 데이터 확인 =====
        if (map.containsKey("서장훈")) {    // 키가 있는지 확인
            int point = map.get("서장훈");
            map.put("서장훈", ++point);
            System.out.println("서장훈님의 누적 포인트 : " + map.get("서장훈"));
        } else {
            map.put("서장훈", 1);
            System.out.println("서장훈님 신규 등록 (포인트 1)");
        }

        // ----- Key 순회 -----
        for (String key : map.keySet()) {
            System.out.println(key);
        }

        // ----- Value 순회 -----
        for (int value : map.values()) {
            System.out.println(value);
        }

        // ----- Key & Value 순회 -----
        for (String key : map.keySet()) {
            System.out.println("고객 이름 : " + key + "\t포인트 : " + map.get(key));
        }

        // ===== 데이터 삭제 =====
        map.remove("유재석");

        // ===== 전체 삭제 =====
        map.clear();
        if (map.isEmpty()) {
            System.out.println("남은 고객 수 : " + map.size());
        }

        // ===== 링크드 맵 생성 =====
        HashMap<String, Integer> map = new LinkedHashMap<>();
    }
}

Iterator

import java.util.*;

public class Iterator {
    public static void main(String[] args) {
        // ===== 어레이 리스트에서 이터레이터 사용 =====
        List<String> list = new ArrayList<>();  // List 인터페이스로 객체 생성
        list.add("유재석");
        list.add("(알 수 없음)");
        list.add("김종국");
        list.add("(알 수 없음)");
        list.add("강호동");
        list.add("(알 수 없음)");

        // ----- 1. for문으로 순회 -----
        for (String s : list) {
            System.out.println(s);
        }

        // ----- 2. 이터레이터로 순회 -----
        Iterator<String> it = list.iterator();  // 이터레이터 타입으로 받기(Ctrl+Alt+V)
        System.out.println(it.next());          // 커서 위치: 0
        System.out.println(it.next());          // 커서 위치: 1

        it = list.iterator();                   // 커서 위치: 0 (처음으로 복원)
        while (it.hasNext()) {                  // 다음에 가져올 데이터가 있으면 true 반환
            System.out.println(it.next());
        }

        it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            if (s.contains("(알 수 없음)")) {     // 해당 문자열이 포함되어 있으면 true 반환
                it.remove();                    // 데이터 삭제
            }
        }

        // ===== 세트에서 이터레이터 사용 =====
        HashSet<String> set = new HashSet<>();
        set.add("유재석");
        set.add("박명수");

        Iterator<String> itSet = set.iterator();
        while (itSet.hasNext()) {
            System.out.println(itSet.next());
        }

        // ===== 맵에서 이터레이터 사용 =====
        HashMap<String, Integer> map = new HashMap<>();
        map.put("유재석", 10);
        map.put("박명수", 5);
                                    
     // map.iterator()              // 키나 값 중 하나를 선택해야 하므로 바로 사용 불가

        // ----- Key 순회 -----
        Iterator<String> itMapKey = map.keySet().iterator();
        while (itMapKey.hasNext()) {
            System.out.println(itMapKey.next());
        }

        // ----- Value 순회 -----
        Iterator<Integer> itMapValue = map.values().iterator();
        while (itMapValue.hasNext()) {
            System.out.println(itMapValue.next());
        }
        
        // ----- Key & Value 순회 -----
        Iterator<Map.Entry<String, Integer>> itMap = map.entrySet().iterator();
        while (itMap.hasNext()) {
            System.out.println(itMap.next());
        }
    }
}

Anonymous Class

custom package

package learnAnonymousClass.converter;

@FunctionalInterface                                // 함수형 인터페이스 사용을 알리는 에노테이션
public interface Convertible {
    void convert(int USD);
}
package learnAnonymousClass.converter;

public class KRWConverter implements Convertible {  // Convertible 인터페이스를 클래스로 구현
    @Override
    public void convert(int USD) {
        System.out.println(USD + " 달러 = " + (USD * 1500) + " 원");
    }
}

package learnAnonymousClass.converter;

@FunctionalInterface
public interface ConvertibleWithNoParams {      // 전달값이 없는 함수형 인터페이스
    void convert();
}
package learnAnonymousClass.converter;

@FunctionalInterface
public interface ConvertibleWithTwoParams {     // 전달값이 2개인 함수형 인터페이스
    void convert(int USD, int KRW);
}
package learnAnonymousClass.converter;

@FunctionalInterface
public interface ConvertibleWithReturn {        // 반환값이 있는 함수형 인터페이스
    int convert(int USD, int KRW);
}

main class

package learnAnonymousClass;

import learnAnonymousClass.converter.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class AnonymousClass {
    public static void main(String[] args) {
        // ======== 기본 클래스 ========
        Coffee c1 = new Coffee();
        c1.order("아메리카노");
        
        // ======== 익명 클래스로 클래스 상속 없이 메소드 재정의 =========
        Coffee specialCoffee = new Coffee() {   // 해당 객체만 사용하는 일회성 구현
            @Override       
            public void order(String coffee) {
                super.order(coffee);    
                System.out.println("(귓속말) 딸기 케이크는 서비스예요.");
            }

            @Override
            public void returnTray() {
                System.out.println("(귓속말) 자리에 두시면 제가 치울게요.");
            }
        };
        specialCoffee.order("바닐라 라떼");
        specialCoffee.returnTray();

        // ======== 익명 클래스로 추상 클래스 상속 없이 정의 ========
        HomeMadeBurger momMadeBurger = getMomMadeBurger();
        momMadeBurger.cook();

        HomeMadeBurger broMadeBurger = getBroMadeBurger();
        broMadeBurger.cook();

        // ======== 람다(접근 제어자, 이름, 파라미터 자료형, 반환타입 x) ========
        // (반드시 함수형 인터페이스와 사용되어야 하기 때문에 예시만 나열)
        () -> {                         // 전달값 x, 반환값 x
            String s = "test";
            System.out.println(s);
        }

        s -> System.out.println(s)      // 전달값 o(1개는 괄호 생략), 반환값 x

        (x, y) -> x + y                 // 전달값 o(두 개 이상), 반환값 o

        // ======== 함수형 인터페이스(1개의 추상 메소드를 갖는 인터페이스) ========
        // ----- KRWConverter 객체로 클래스에서 구현한 convert() 실행 -----
        KRWConverter converter = new KRWConverter();
        converter.convert(2);       // 방법 1
        convertUSD(converter, 2);   // 방법 2

        // ----- Convertible 인터페이스 구현 객체를 람다로 생성 -----
        // 직접 전달
        convertUSD((USD) -> System.out.println(USD + " 달러 = " + (USD * 1500) + " 원"), 1);
        // 변수에 담아서 전달
        Convertible convertible = (USD) -> System.out.println(USD + " 달러 = " + (USD * 1500) + " 원"); 
        convertUSD(convertible, 2);
        
        // ----- 전달값이 하나도 없는 경우 -----
        ConvertibleWithNoParams c1 = () -> System.out.println("1 달러 = 1500원");
        c1.convert();

        // ----- 두 줄 이상의 코드가 있는 경우 -----
        c1 = () -> {
            int USD = 5;
            int KRW = 1500;
            System.out.println(USD + " 달러 = " + (USD * KRW) + " 원");
        };
        c1.convert();

        // ----- 전달값이 2개인 경우 -----
        ConvertibleWithTwoParams c2 = (d, w) -> System.out.println(d + " 달러 = " + (d * w) + " 원");
        c2.convert(10, 1400);

        // ----- 반환값이 있는 경우 -----
        ConvertibleWithReturn c3 = (d, w) -> d * w; // {return d * w}; 를 생략
        int result = c3.convert(20, 1400);
        System.out.println("20 달러 = " + result + " 원");

        // ======== 스트림 생성(일회성 동작, 원본 손상 x)========
        // ----- Arrays.stream -----
        int[] scores = {100, 95, 90, 85, 80};
        IntStream scoreStream = Arrays.stream(scores);

        String[] langs = {"python", "java", "javascript", "c", "c++", "c#"};
        Stream<String> langStream = Arrays.stream(langs);

        // ----- Collection.stream() -----
        // 생성 방법 1 (리스트 생성 후 값 하나씩 추가)
        List<String> langList = new ArrayList<>();
        langList.add("python");
        langList.add("java");
        
        // 생성 방법 2 (리스트 생성과 동시에 값 정의)
        langList = Arrays.asList("python", "java", "javascript", "c", "c++", "c#");

        Stream<String> langListStream = langList.stream();

        // ----- Stream.of() -----
        Stream<String> langListOfStream = Stream.of("python", "java", "javascript", "c", "c++", "c#");

        // ======== 스트림 사용 예제 ========
        // 중간 연산 (Intermediate Operation) : filter, map, sorted, distinct, skip , ...
        // 최종 연산 (Terminal Operation) : count, min, max, sum, forEach, anyMatch, allMatch, ...

        // 90점 이상인 점수만 출력
        Arrays.stream(scores).filter(x -> x >= 90).forEach(x -> System.out.println(x));
     // Arrays.stream(scores).filter(x -> x >= 90).forEach(System.out::println); 위와 동일

        // 90점 이상인 사람의 수
        int count = (int) Arrays.stream(scores).filter(x -> x >= 90).count();   // long으로 반환
        System.out.println(count);

        // 90점 이상인 점수들의 합
        int sum = Arrays.stream(scores).filter(x -> x >= 90).sum();
        System.out.println(sum);

        // 90점 이상인 점수들을 정렬 후 출력
        Arrays.stream(scores).filter(x -> x >= 90).sorted().forEach(System.out::println);

        // c 로 시작하는 프로그래밍 언어만 출력
        Arrays.stream(langs).filter(x -> x.startsWith("c")).forEach(System.out::println);

        // java 라는 글자를 포함하는 언어만 출력
        Arrays.stream(langs).filter(x -> x.contains("java")).forEach(System.out::println);

        // 4글자 이하의 언어를 정렬해서 출력
        langList.stream().filter(x -> x.length() <= 4).sorted().forEach(System.out::println);

        // 4글자 이하의 언어 중에서 c 라는 글자를 포함하는 언어만 출력
        langList.stream()
                .filter(x -> x.length() <= 4)
                .filter(x -> x.contains("c"))
                .forEach(System.out::println);

        // 4글자 이하의 언어 중에서 c 라는 글자를 포함하는 언어가 하나라도 있는지 여부
        boolean anyMatch = langList.stream()
                .filter(x -> x.length() <= 4)
                .anyMatch(x -> x.contains("c"));
        System.out.println(anyMatch);

        // 3글자 이하의 언어들은 모두 c 라는 글자를 포함하는지 여부
        boolean allMatch = langList.stream()
                .filter(x -> x.length() <= 3)
                .allMatch(x -> x.contains("c"));
        System.out.println(allMatch);

        // 4글자 이하의 언어 중에서 c 라는 글자를 포함하는 언어 뒤에 (어려워요) 라는 글자를 함께 출력
        langList.stream()
                .filter(x -> x.length() <= 4)
                .filter(x -> x.contains("c"))
                .map(x -> x + " (어려워요)")        // map()으로 데이터 가공
                .forEach(System.out::println);

        // c 라는 글자를 포함하는 언어를 대문자로 출력
        langList.stream()
                .filter(x -> x.contains("c"))
                .map(String::toUpperCase)
                .forEach(System.out::println);

        // c 라는 글자를 포함하는 언어를 대문자로 변경하여 새 리스트로 저장, 출력
        List<String> langListStartsWithC = langList.stream()
                .filter(x -> x.contains("c"))
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        langListStartsWithC.stream().forEach(System.out::println);
    }

    //-----------------------------------------------
    public static HomeMadeBurger getMomMadeBurger() {
        return new HomeMadeBurger() {
            @Override
            public void cook() {
                System.out.println("집에서 만드는 엄마표 수제 버거");
                System.out.println("재료: 빵, 소고기패티, 해시브라운, 양상추, 마요네즈, 피클");
            }
        };
    }

    private static HomeMadeBurger getBroMadeBurger() {
        return new HomeMadeBurger() {
            @Override
            public void cook() {
                System.out.println("집에서 만드는 동생표 군대리아");
                System.out.println("재료: 빵, 치킨패티, 양배추 샐러드, 딸기잼, 치즈, 삶은 계란");
            }
        };
    }
    
    //-------------------------------------------------------------
    public static void convertUSD(Convertible converter, int USD) {
        converter.convert(USD);         // 추상 메소드
    }
}

//----------------------------------------------------------
class Coffee {
    public void order(String coffee) {
        System.out.println("주문하신 " + coffee + " 나왔습니다.");
    }

    public void returnTray() {
        System.out.println("컵 반납이 완료되었습니다.");
    }
}

//----------------------------------------------------------
abstract class HomeMadeBurger {         // 추상 클래스
    public abstract void cook();
}

Exception Handling

import java.io.BufferedWriter;

import java.io.FileWriter;
import java.io.IOException;

public class ExceptionHandling {
    public static void main(String[] args) {
        // 오류: 컴파일 오류, 런타임 오류(에러 error, 예외 exception)
        
        // ========= Try Catch(예외 처리) =========
        try {
            // ArithmeticException
            System.out.println(3 / 0);      

            // ArrayIndexOutOfBoundsException
            int[] arr = new int[3];         
            arr[5] = 100;

            // ClassCastException
            Object obj = "test";
            System.out.println((int) obj);

            // NullPointerException
            String s = null;
            System.out.println(s.toLowerCase());
        } catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {  // 특정 에러 처리
            System.out.println("숫자를 확인하세요.");                              
        } catch (ClassCastException e) {                                    // 특정 에러 처리
            System.out.println("잘못된 형 변환입니다.");  
        } catch (Exception e) {                                             // 나머지 에러 일괄처리
            System.out.println("에러: " + e.getMessage());   // 예외발생 이유 문자열 출력
            e.printStackTrace();                            // 예외발생 이유 상세 정보 출력
        }
        System.out.println("프로그램 정상 종료");

        // ========= Throw(예외 발생시키기) =========
        int age = 17;
        try {
            if (age < 19) {
                throw new Exception("만 19세 미만에게는 판매하지 않습니다.");
            } else {
                System.out.println("주문하신 상품 여기 있습니다.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // ========= Finally =========
        try {
            System.out.println("택시의 문을 연다.");
            throw new Exception("휴무 택시");
            // System.out.println("택시에 탑승한다.");
        } catch (Exception e) {
            System.out.println("문제 발생 : " + e.getMessage());
        } finally {
            System.out.println("택시의 문을 닫는다.");
        }

        try {
            System.out.println(3 / 0);
        } finally {
            System.out.println("프로그램 정상 종료");
        }

        // ========= Try with resources =========
        // bw.close() 직접 호출
        MyFileWriter writer1 = null;
        try {
            writer1 = new MyFileWriter();
            writer1.write("아이스크림이 먹고 싶어요");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                writer1.close();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        // bw.close() 자동으로 호출
        try (MyFileWriter writer2 = new MyFileWriter()) {   // try 구문 괄호에서 객체 정의
            writer2.write("빵이 먹고 싶어요");                  
        } catch (Exception e) {
            e.printStackTrace();
        }

        // ========= 사용자 정의 예외 =========
        int age = 17; // 만 17세
        try {
            if (age < 19) {
                throw new AgeLessThan19Exception("만 19세 미만에게는 판매하지 않습니다.");
            } else {
                System.out.println("주문하신 상품 여기 있습니다.");
            }
        } catch (AgeLessThan19Exception e) {
            System.out.println("조금 더 성장한 뒤에 오세요.");
        } catch (Exception e) {
            System.out.println("모든 예외를 처리합니다.");
        }

        // ========= Throws(예외 처리 미루기) =========
        try {                           // 메인 메소드에서 writeFile() 호출 후 예외 해결
            writeFile();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("메인 메소드에서 해결할게요.");
        }
    }

    // 예외를 호출한 곳으로 넘기는 메소드(throws IOException 없으면 내부에서 Try Catch문으로 자체 해결)
    public static void writeFile() throws IOException {
        FileWriter writer = new FileWriter("test.txt");
        throw new IOException("파일 쓰기에 실패했어요!!");
    }
}

//-------------------------------------------
class MyFileWriter implements AutoCloseable {       // java.lang 패키지의 AutoCloseable 인터페이스
    @Override
    public void close() throws Exception {          // 인터페이스 구현
        System.out.println("파일을 정상적으로 닫습니다.");
    }

    public void write(String line) {
        System.out.println("파일에 내용을 입력합니다.");
        System.out.println("입력 내용 : " + line);
    }
}

//----------------------------------------------
class AgeLessThan19Exception extends Exception {    // 사용자 정의 예외(Exception 클래스 상속)
    public AgeLessThan19Exception(String message) {
        super(message);
    }
}

Thread

custom package

package LearnThread.clean;

public class CleanThread extends Thread {           // 쓰레드 클래스 상속(다중 상속 불가)
    public void run() {
        System.out.println("-- 직원 청소 시작 (Thread) --");
        for (int i = 2; i <= 10; i += 2) {
            System.out.println("(직원) " + i + "번방 청소 중 (Thread)");
        }
        System.out.println("-- 직원 청소 끝 (Thread) --");
    }
}
package LearnThread.clean;

public class CleanRunnable implements Runnable {    // 러너블 인터페이스 구현(다른 부모 클래스 상속 가능)
    @Override
    public void run() {
        System.out.println("-- 직원 청소 시작 (Runnable) --");
        for (int i = 2; i <= 10; i += 2) {
            System.out.println("(직원) " + i + "번방 청소 중 (Runnable)");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("-- 직원 청소 끝 (Runnable) --");
    }
}
package chapLearnThread.clean;

public class Room {
    public int number = 1;
    synchronized public void clean(String name) {   // 동기화(한 쓰레드가 점유하는 동안 다른 쓰레드는 대기)
        System.out.println(name + " : " + number + "번방 청소 중"); // 직원1 or 직원2 + 방 번호
        number++;
    }
}

main class

package LearnThread;

import LearnThread.clean.*;

public class Thread {
    public static void main(String[] args) {
        // =========== Thread ===========
        // 1. 하나의 프로세스가 1개의 쓰레드 사용
        cleanBySelf();                                  // 1 2 3 4 5 6 7 8 9 10 청소(직원)

        // 2. 하나의 프로세스가 동시에 2개의 쓰레드 사용
        CleanThread cleanThread = new CleanThread();
     // cleanThread.run();      (메소드를 직접 입력하면 1개의 쓰레드에서 순차 처리됨)
        cleanThread.start();                            // 1 3 5 7 9 청소(직원)
        cleanByBoss();                                  // 2 4 6 8 10 청소(사장)

        // =========== Runnable ===========
        CleanRunnable cleanRunnable = new CleanRunnable();
        Thread thread = new Thread(cleanRunnable);
        thread.start();
        cleanByBoss();

        // =========== Join ===========
        thread.start();
        try {
            thread.join(2500);      // 쓰레드 동작이 끝날 때까지 정해진 시간동안 대기 후 넘어감
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        cleanByBoss();

        // =========== Multi Thread ===========
        Runnable cleaner1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("-- 직원1 청소 시작 --");
                for (int i = 1; i <= 10; i += 2) {
                    System.out.println("(직원1) " + i + "번방 청소 중");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.println("-- 직원1 청소 끝 --");
            }
        };

        Runnable cleaner2 = () -> {
            System.out.println("-- 직원2 청소 시작 --");
            for (int i = 2; i <= 10; i += 2) {
                System.out.println("(직원2) " + i + "번방 청소 중");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("-- 직원2 청소 끝 --");
        };

        Thread cleanerThread1 = new Thread(cleaner1);
        Thread cleanerThread2 = new Thread(cleaner2);

        cleanerThread1.start();
        cleanerThread2.start();

        // =========== Synchronization ===========
        Room room = new Room();

        Runnable cleaner1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("-- 직원1 청소 시작 --");
                for (int i = 1; i <= 5; i++) {
                    room.clean("직원1");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    if (i == 2) {       // 예외 발생 시에는 해당 쓰레드만 종료되고 다른 쓰레드에는 영향 x
                        throw new RuntimeException("못해먹겠다!!");
                    }
                }
                System.out.println("-- 직원1 청소 끝 --");
            }
        };

        Runnable cleaner2 = () -> {
            System.out.println("-- 직원2 청소 시작 --");
            for (int i = 1; i <= 5; i++) {
                room.clean("직원2");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("-- 직원2 청소 끝 --");
        };

        Thread cleanerThread1 = new Thread(cleaner1);
        Thread cleanerThread2 = new Thread(cleaner2);

        cleanerThread1.start();     // 두 직원이 1~10번 순으로 돌아가며 각각 5개 방을 청소
        cleanerThread2.start();
    }
    
    //--------------------------------
    public static void cleanBySelf() {
        System.out.println("-- 혼자 청소 시작 --");
        for (int i = 1; i <= 10; i++) {
            System.out.println("(혼자) " + i + "번방 청소 중");
        }
        System.out.println("-- 혼자 청소 끝 --");
    }

    public static void cleanByBoss() {
        System.out.println("-- 사장 청소 시작 --");
        for (int i = 1; i <= 10; i += 2) {
            System.out.println("(사장) " + i + "번방 청소 중");

            try {                               // join()으로 대기할 경우 사용
                Thread.sleep(1000);             // 지정한 시간 동안 멈춤
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("-- 사장 청소 끝 --");
    }
}

I/O, File

import java.util.Scanner;

import java.io.File;
import java.io.IOException;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class IOAndFile {
    public static void main(String[] args) {
        // ========== Input ==========
        Scanner sc = new Scanner(System.in);

        System.out.println("이름을 입력하세요");
        String name = sc.next();                    // 입력받은 값을 띄어쓰기로 한 단어씩 받음
        System.out.println("혈액형을 입력하세요");
        String bloodType = sc.nextLine();           // 입력받은 값을 하나의 문장으로 모두 받음
        System.out.println("키를 입력하세요");
        int height = sc.nextInt();                  // 입력된 값(문자열)을 정수형으로 변경
        System.out.println("몸무게를 입력하세요");
        double weight = sc.nextDouble();

        // ========== Output ==========
        // 방법 1. System.out.format();
        // 방법 2. System.out.printf();  (souf 입력하면 자동으로 완성)

        // 정수
        System.out.printf("%d%n", 1);               // 1
        System.out.printf("%d %d %d%n", 1, 2, 3);   // 1 2 3
        System.out.printf("%d%n", 1234);            // 1234
        System.out.printf("%6d%n", 1234);           // __1234 (공간 6자리 확보 후 출력)
        System.out.printf("%06d%n", 1234);          // 001234 (공간 6자리 확보 후 공백을 0으로 채우기)
        System.out.printf("%6d%n", -1234);          // _-1234
        System.out.printf("%+6d%n", 1234);          // _+1234 (항상 +- 기호 표시)
        System.out.printf("%-6d%n", 1234);          // 1234__ (공간 6자리 확보 후 왼쪽 정렬 출력)
        System.out.printf("%,15d%n", 1000000000);   // __1,000,000,000 (세자리마다 콤마로 구분)

        // 실수
        System.out.printf("%f%n", Math.PI);         // 3.141593 (일반 출력)
        System.out.printf("%.2f%n", Math.PI);       // 3.14 (소수점 둘째자리까지 반올림)
        System.out.printf("%6.2f%n", Math.PI);      // __3.14 (공간 6자리 확보 후 소수점 둘째자리)
        System.out.printf("%06.2f%n", Math.PI);     // 003.14 (공간 6자리 확보 후 공백을 0으로 채우기)
        System.out.printf("%-6.2f%n", Math.PI);     // 3.14__ (왼쪽 정렬)
        System.out.printf("%+6.2f%n", Math.PI);     // _+3.14 (항상 +- 기호 표시)

        // 문자열
        System.out.printf("%s%n", "Java");          // Java (일반 출력)
        System.out.printf("%6s%n", "Java");         // __Java (공간 6자리 확보 후 우측 정렬)
        System.out.printf("%-6s%n", "Java");        // Java__ (공간 6자리 확보 후 좌측 정렬)
        System.out.printf("%6.2s%n", "Java");       // ____Ja (공간 6자리 확보 후 우측 정렬해서 2글자 출력)
        System.out.printf("%-6.2s%n", "Java");      // Ja____ (공간 6자리 확보 후 왼쪽 정렬해서 2글자 출력)

        // 응용(공백으로 가독성 있게 정렬)
        System.out.println("이름      영어   수학   평균");
        System.out.printf("%-6s %4d %4d %6.1f%n", "강백호", 90, 80, 85.0);
        System.out.printf("%-6s %4d %4d %6.1f%n", "서태웅", 100, 100, 100.0);
        System.out.printf("%-6s %4d %4d %6.1f%n", "채치수", 95, 100, 97.5);

        // ========== File ==========
        String fileName = "test.txt";       // 파일명 정의
        File file = new File(fileName);     // 파일 객체 생성
        try {
            file.createNewFile();           // (파일이 없는 경우에만) 생성
            if (file.exists()) {            // 파일 존재 여부 확인
                System.out.println("파일 이름 : " + file.getName());
                System.out.println("파일 절대 경로 : " + file.getAbsolutePath());
                System.out.println("파일 크기(Byte) : " + file.length());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // ========== Folder ==========
        // 단일 폴더 생성
        String folderName = "A";
        File folder = new File(folderName); 
        folder.mkdir();                     // 단일 폴더 생성
        if (folder.exists()) {
            System.out.println("폴더가 존재합니다: " + folder.getAbsolutePath());
        }

        // 하위 구조의 폴더 여러개 생성
        // 윈도우 : C:\\Users\\Nadocoding\\Desktop
        // 맥    : /Users/Nadocoding/Desktop
        // 모든OS : "A" + File.separator + "B" + File.separator + "C"
        folder = new File(folderName);
        folderName = "A/B/C";
        folder.mkdirs();                    // 부모 폴더가 존재하지 않는 경우 사용
        if (folder.exists()) {
            System.out.println("폴더가 존재합니다: " + folder.getAbsolutePath());
        }

        // ========== File & Folder ==========
        // 현재 위치(상대 경로): "." 
        // 현재 위치(절대 경로): "C:\\Users\\Nadocoding\\Desktop\\JavaWorkspace\\src\\IOAndFile";
        String folder = "src/IOAndFile";                    // 상대 경로
        File filesAndFolders = new File(folder);
        System.out.println("현재 폴더 경로: " + filesAndFolders.getAbsolutePath());
        for (File file : filesAndFolders.listFiles()) {     // 모든 폴더와 파일 정보
            if (file.isFile()) {
                System.out.println("(파일) " + file.getName());
            } else if (file.isDirectory()) {
                System.out.println("(폴더) " + file.getName());
            }
        }

        // ========== Read & Write File ==========
        // BufferedWriter로 쓰기 (FileWriter에 append=true 설정 시 덮어쓰지 않고 그대로 이어씀)
        try(BufferedWriter bw = new BufferedWriter(new FileWriter("goodjob.txt", true))) {
            bw.write("1. 이제 거의 끝이 보여요.");  // 작성
            bw.newLine();                      // 줄바꿈
            bw.write("2. 여기까지 오신 여러분들 정말 대단해요!");
            bw.newLine();
            bw.write("3. 조금만 더 힘내요!");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // BufferedReader로 읽기
        try (BufferedReader br = new BufferedReader(new FileReader("goodjob.txt"))) {
            String line;
            while((line = br.readLine()) != null) {     // 한 줄씩 읽어와서 출력
                System.out.println(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // ========== Delete File & Folder ==========
        // 파일 삭제
        File file = new File("goodjob.txt");
        if (file.exists()) {
            if (file.delete()) {
                System.out.println("파일 삭제 성공: " + file.getName());
            } else {
                System.out.println("파일 삭제 실패: " + file.getName());
            }
        }

        // 단일 폴더 삭제(하위 폴더가 없을 때만 가능)
        File folder = new File("A");    // "A/B/C" 의 경우 가장 하위 폴더 C만 삭제
        if (folder.exists()) {
            if (folder.delete()) {
                System.out.println("폴더 삭제 성공: " + folder.getAbsolutePath());
            } else {
                System.out.println("폴더 삭제 실패: " + folder.getAbsolutePath());
            }
        }

        // 해당 폴더의 모든 하위 폴더까지 함께 삭제
        if (deleteFolder(folder)) {
            System.out.println("*폴더 삭제 성공: " + folder.getAbsolutePath());
        } else {
            System.out.println("*폴더 삭제 실패: " + folder.getAbsolutePath());
        }
    }

    //-----------------------------------------------
    public static boolean deleteFolder(File folder) {
        if (folder.isDirectory()) {                 // 폴더만 확인
            for (File file : folder.listFiles()) {  // 해당 폴더 내 모든 하위 목록들 하나씩 확인
                deleteFolder(file);                 // 재귀 호출
            }
        }
        System.out.println("삭제 대상: " + folder.getAbsolutePath());
        return folder.delete();
    }
}



References

『나도코딩의 자바 기본편 - 풀코스 (20시간)』, https://www.inflearn.com/course/%EB%82%98%EB%8F%84%EC%BD%94%EB%94%A9-%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8/dashboard?cid=329986

Leave a comment