1. 싱글톤


class Soojebi {
    static private Soojebi instance = null; 
    // main 에서 s1 선언 후 정적 변수는 더 이상 null 이 아니게 됨 
    private int count = 0;

    static public Soojebi get() {
        if (instance == null) { // 정적 변수인 instance 가 null이면 
            instance = new Soojebi();// Soojebi 생성자 
        }
        return instance; // 그게 아니면 기본 instance 객체 리턴 
    }

    public void count() {
        count++; // 증가 
    }

    public int getCount() {
        return count;
    }
}

class Soojebi2 {
    public static void main(String[] args) {
        Soojebi s1 = Soojebi.get(); // 여기서 Soojebi 인스턴스 생성 
        s1.count(); //count= 1

        Soojebi s2 = Soojebi.get(); // 인스턴스가 null 이 아니므로 s1 에서 생성된 인스턴스 반환 
        s2.count(); // 인스턴스의 변수 중 저장된 count 에서  ++ 
        //count= 2

        Soojebi s3 = Soojebi.get();// 인스턴스가 null 이 아니므로 s1 에서 생성된 인스턴스 반환 
        s3.count(); // 즉 s1 , s2 ,s3 은 동시에 객체 Soojebi(s1) 를 가리킴
        // count = 3

        System.out.println(s1.getCount());  //3 출력 
    }
}

2. 상속

class Soojebi {
    public static void main(String[] args) {
        Parent c = new Child(); 
        //업캐스팅 시 오버라이딩 메서드일 경우 
        //자식의 메서드가 호출됨 
        c.paint(); // 현재까지 BDCD
        c.draw(); // Child의 draw 호출 -> "D" 출력 
        // BDCDD 출력 
    }
}

class Parent {
    public void paint() {
        System.out.print("A");
        draw();
    }

    public void draw() {
        System.out.print("B"); // "B" 출력 
        draw(); //두번째 호출 Child 의 draw()->  "D" 출력
    }
}

class Child extends Parent {
    public void paint() { // 호추출
        super.draw(); // 부모의 draw() 함수 호출 
        System.out.print("C"); // 현재까지 "BDC" 
        this.draw(); // 자기 자신의 draw() 호출 -> "D" 출력 
    } // 현재까지 BDCD

    public void draw() {
        System.out.print("D");
    }
}

3. 재귀+배열

class Soojebi {
    public static String fn(String str, int index, boolean[] seen) {
        if (index < 0) return "";
        char c = str.charAt(index);
        String result = fn(str, index - 1, seen);

        if (!seen[c]) {
            seen[c] = true;
            return c + result;
        }
            return result;
    }

    public static void main(String[] args) {
        String str = "abacabcd";
        int length = str.length();
        boolean[] seen = new boolean[256];
        System.out.print(fn(str, length - 1, seen));
    }
}

4. 2차원배열 (역삼각형 채우기)

public class Soojebi {

    public static void main(String[] args) {
        // 1) 3x3 정수형 2차원 배열 생성
        int[][] arr = new int[3][3];

        // 2) 배열을 0으로 초기화
        init(arr);

        //배열 역삼각형 모양으로 숫자 채우기
        hourGlass(arr);
        // 출력 함수 
        arrayPrint(arr);
    }

    public static void init(int arr[][]) {
        // arr.length -> 행의 수 (여기서는 3)
        for (int i = 0; i < arr.length; i++) {
            // arr[0].length -> 열의 수 (여기서는 3)
            for (int j = 0; j < arr[0].length; j++) {
                arr[i][j] = 0; // 각 칸을 0으로 설정
            }
        }
    }

    /*
     * - v: 채워 넣을 숫자(1부터 시작)
     * - 바깥 루프 i: 각 행(row)을 의미
     * - 안쪽 루프 j: i부터 시작해서 오른쪽 끝까지 채움 -> 윗삼각(대각선 포함)이 채워짐
     *
     * 채워지는 순서(예시, 최종 결과):
     * arr =
     * [ [1, 2, 3],
     *   [0, 4, 5],
     *   [0, 0, 6] ]
     *
     * 이유: 각 행에서 j를 i부터 시작하면
     *   i=0 -> j=0,1,2  (행0의 모든 열)
     *   i=1 -> j=1,2    (행1의 대각선부터 우측)
     *   i=2 -> j=2      (행2의 대각선)
     */
    public static void hourGlass(int arr[][]) {
        int v = 0; // 값을 0으로 초기화. ++v를 사용해서 1부터 채움.
        for (int i = 0; i < arr.length; i++) {           // 각 행(row)
            for (int j = i; j < arr[0].length; j++) {    // 각 행에서 열을 i부터 시작
                // ++v: 먼저 v를 1 증가시키고 증가된 값을 사용.
                arr[i][j] = ++v;
                // arr[0][0] = 1, 다음 => arr[0][1] = 2, ...
            }
        }
    }

    /**
     * - 0이면 공백(" ")을 출력해서 비어 보이게 함
     * - 숫자면 그대로 출력 
     *  이렇게 하면 자릿수 정렬(칸 맞춤)이 더 보기 좋아짐.
     */
    public static void arrayPrint(int arr[][]) {
        for (int i = 0; i < arr.length; i++) {          // 각 행 출력
            for (int j = 0; j < arr[0].length; j++) {   // 각 열 출력
                if (arr[i][j] == 0) {
                    // 0이면 공백 한 칸 출력 (원래 코드 동작 유지)
                    System.out.print(" ");
                } else {
                    // 0이 아니면 숫자 출력
                    System.out.print(arr[i][j]);
                }
            }
            // 한 행 출력이 끝나면 줄바꿈
            System.out.println("");
        }
    }
}
//hourGlass 에서 채워진 대로 
//역삼각형 모양의 숫자판이 출력됨 
// 1 2 3 
//   4 5
//     6 

5. 상속과 생성자

class Parent {
    public Parent() { // 2. Parent 의 생성자가 먼저 호출 됨 
    
        this(3); // 3. Parent 클래스의 정수를 매개변수로 받는 메서드를 호출 하여 3을 전달 
        
        System.out.print("A"); // 5.this(3) 메서드 호출이 끝나서 A 출력 
    }

    public Parent(int x) { // 4. 호출되어 B 를 출력 
        System.out.print("B");
    }

    public void fn1() {
        System.out.print("C");
    }

    public void fn2() {
        System.out.print("D"); // 7. 호출되어 D 출력 
    }
}

class Child extends Parent {
    public Child() { // 11. 호출되어 E 출력 
        System.out.print("E");
    }

    public Child(int x) { // 9. 호출 
        this(); //10. Child 의 매개변수가 없는 생성자 호출  
        System.out.print("F"); // 12. this() 함수가 종료되어 F 출력 
    }

    public void fn1() {
        System.out.print("G"); // 13. G 출력 
    }

    public void fn2() {
        System.out.print("H");
    }
}

public class Soojebi {
    public static void main(String[] args) {
        Parent a = new Parent(); // 1. Parent 의 인스턴스를 a 에 저장 
        // 여기까지 BA 출력 
        a.fn2(); // 6. Parent의 fn2() 함수 호출 
        // 여기까지 BAD 
        new Child(5).fn1(); 
        //8. new Child(5) 생성자 호출 -> 자바의 상속 규칙 때문에, 자식 클래스의 생성자가 호출될 때는 
        // 무조건 부모 클래스의 생성자가 가장 먼저 호출 
        // BA 출력 
        // new Child(5)-> Child 의 정수를 매개변수로 받는 생성자 호출
        // new Child(5) 까지하면 현재까지 출력은 BADBAEF 
        // 마지막으로 Child 의 fn1 가 호출 됨 
        // 최종 출력값은 BADBAEFG 
         
    }
}