클래스 (Class) part.2


1. 접근제어 명시자 (Access Modifier)


- 변수, 메서드, 클래스 어디에나 사용이 가능하다.

- 클래스와 클래스들은 기본적으로 서로가 쉽게 접근 할 수 없다.


클래스는 누구는 접근 할 수 있도록 하고,

누구는 접근하지 못하게 할 수 있다.


클래스의 보안 기능을 살펴보자.


1) 종류

<접근허가>

1. default

- 접근제어명시자가 생략된 형태. 접근할 수 있도록 허가, 같은 폴더 안에 있는 class 안에서만 사용가능)

- 같은 파일 안에서는 public을 안붙여도 된다.

- 한 파일에 하나의 클래스만 있는 것이 원칙이다.

2. public 

- 다른 폴더나 같은 폴더나 어디서나 접근할 수 있는 허가권


<접근불가>

1. private

- 가장 강력한 접근거부

- 같은 클래스에서만 사용 가능

2. protected (class part3에서 다룸)

- defult와 성격이 비슷


<예제 1>

class EmpManager{

    public String name;

    int no;

    double pay;

    

}

public class AccessModifierTest {

    public static void main(String[] args) {

        // TODO 접근제어명시자 테스트

        EmpManager kim = new EmpManager();

        kim.name = "김유신";

        kim.no = 12;

        kim.pay=1000000;

        System.out.println(kim.name + " : " + kim.no + " : " + kim.pay);

        

    }

}


※결과값

김유신 : 12 : 1000000.0


여기서 pay값을 못 보게 하고 싶다면 private를 사용한다.


<예제2>


class EmpManager{

    public String name;

    int no;

    // 클래스 안에서만 작동하기 때문에 오류가 생긴다.

    private double pay;

    

}

public class AccessModifierTest {

    public static void main(String[] args) {

        // TODO 접근제어명시자 테스트

        EmpManager kim = new EmpManager();

        kim.name = "김유신";

        kim.no = 12;

        kim.pay=1000000;

        System.out.println(kim.name + " : " + kim.no + " : " + kim.pay);

        

    }

}


※결과값

Exception in thread "main" java.lang.Error: Unresolved compilation problems:

    The field EmpManager.pay is not visible

    The field EmpManager.pay is not visible

    at AccessModifierTest.main(AccessModifierTest.java:17)


문제는 이렇게 되면 EmpManager라는 클래스가 쓸모없어진다.


그렇다면 어떻게 하면 pay를 못보게 하면서 클래스를 사용할 수 있을까?


이럴 때는 메서드를 만들어주면 된다.


class EmpManager{

    public String name;

    int no;

    private double pay;

    //private 변수를 수정하고 저장 할 수 있는 메서드 (쓰고 저장하는 기능)

    //setter 메서드

    //이런 setter 메서드는 앞에 변수 이름을 set이라고 쓴다.

    // 값을 받아서 private 변수에 넣어준다.

    void setPay(int p) {

        //값에 대한 검증

        pay = p;

    }

    // getter 메서드

    // 값을 가져다가 읽어올 수 있게 해준다. (읽기 기능)

    // 이런 setter 메서드는 앞에 변수 이름을 get이라고 쓴다.

    double getPay() {

        return pay;

    }

    

}

public class AccessModifierTest {

    public static void main(String[] args) {

        // TODO 접근제어명시자 테스트

        EmpManager kim = new EmpManager();

        kim.name = "김유신";

        kim.no = 12;

        //kim.pay=1000000;

        kim.setPay(1000000);

        System.out.println(kim.name + " : " + kim.no + " : " + kim.getPay());

        

    }

}


※ 결과값

김유신 : 12 : 1000000.0


2) setter메서드, getter 메서드


위에 예시에서 보듯이

prive으로 선언될 때는 클래스 내부 기능을 

사용하기 위해서 반드시 메서드가 달려있다.

private 변수하나에 반드시 두 개의 메서드가 따라 다녀야 한다.

이 두 변수 이름을 getter 와 setter 메서드 라고 한다.


① setter 메서드

private 변수를 수정하고 저장 할 수 있는 메서드 (쓰고 저장하는 기능)

이런 setter 메서드는 앞에 변수 이름을 set이라고 쓴다.

값을 받아서 private 변수에 넣어준다.


② getter 메서드

값을 가져다가 읽어올 수 있게 해준다. (읽기 기능)

이런 setter 메서드는 앞에 변수 이름을 get이라고 쓴다.



지금까지 배운 내용을 바탕으로 메인 메서드를 다시 살펴보자.


public static void main(String[] args)

public 자바 가상머신이 어디에든 실행 할 수 있도록 한다.

void 리턴값이 없다. (가상머신이 main찾는데 가상머신에게 리턴하지 않는다.)

main 메서드도 시스템이 찾는 콜백메서드이다.

static (앞으로 배워야 할 것)

(String[] args)  명령행 인자를 위한 매개변수이다.


2. 메서드

- 메서드 때문에 클래스의 특징이 결정된다.

- 변수 값을 임시저장 할 뿐이다. 정말 중요한건 메서드가 한다.


1) 인자 전달 방식

메서드를 호출했을 때 인자를 전달하는 방법이 크게 두가지가 있다.


1. 직접 값을 줄 것인가?

2. 주소를 통해 전달해 줄 것인가?


메서드의 인자전달방식

① 값에 의한 전달 (call by value)

 - 지금까지 했던 모든 전달방식은 값에 의한 전달방식이었다.

    a. 소량의 데이터

    b. 잠깐 사용할 데이터

② 주소(참조)에 의한 전달(call by reference)

    a. 대량의 데이터

    b. 오랫동안 사용할 데이터


<인자 전달 방식>


public class CallByTest1 {

    void display(int a, int b, int c, int d, int e) {

        System.out.println(a + ", " + b + ", " + c);

        

    }

    

    public static void main(String[] args) {

        // TODO 값에 의한 인자 전달 방식

        // 일반변수는 stack을 사용하기 때문에 주소값을 저장할 수 없다.

        // hip에 저장을 해야 안전하게 쓸 수 있다.

        // 어떻하면 이 데이터를 안전하게 보관할 수 있을까? 답은 배열이다.

        int a=4, b=7, c=15, d=80, e=9;

        

        CallByTest1 call = new CallByTest1();

        call.display(a, b, c, d, e);

    }

}


※ 결과값

4, 7, 15


<주소(참조)에 의한 전달>

1.값이 많아 져도 괜찮다. 

2.안전한 저장이 가능하다.


인자와 매개변수는 타입이 똑같아야한다.


주소(참조)에 의한 전달 방법은 두 가지가 있다.


첫 번째, 배열로 묶어서 전달한다.

두 번째, 인스턴스 변수를 선언하여 주소에 저장한다.


첫번째, 배열로 묶어서 전달하는 방법


public class CallByTest1 {

    void display(int [] arr) {

        System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2]);

        

    }

    

    public static void main(String[] args) {

        // TODO 값에 의한 인자 전달 방식

        // 배열로 주소에 의함 참조 방식을 쓴다. 값이 많아져도 된찮다.

        // 값이 많아 져도 괜찮다. 안전한 저장이 가능하다.

        //지역변수를 인스턴스 변수로 선언

        int [] arr = {4, 7, 15, 80, 9};

        

        CallByTest1 call = new CallByTest1();

        call.display(arr);

    }

}


※ 결과값

4, 7, 15


두번째, 인스턴스 변수를 선언하여 주소에 저장하는 방법


public class CallByTest1 {

    //인스턴스변수를 선언하여 주소에 저장한다.

    int a =4, b=7, c=15, d=80, e=9;

    

    void display() {

        System.out.println(a + ", " + b + ", " + c);

        

    }

    

    public static void main(String[] args) {

        // TODO 참조에 의한 전달 방식 2

        int a=4, b=7, c=15, d=80, e=9;

        

        CallByTest1 call = new CallByTest1();

        call.display();

    }

}    


※ 결과값

4, 7, 15



여기서 Stack 구조에 대해 잠깐 알아보자.


<Stack 구조>

LiFO 구조이다.

lest in first out 구조

마지막에 나온 것이 먼저 나온다.

Stack 구조를 이용해 여러가지의 데이터타입을 인스턴스 변수로 빼 줄 수 있다.








public class CallByTest1 {

    int i = 4;

    double d = 3.14;

    char c = '가';

    boolean b = true;

    String s = "문자열";

    

    

    void display() {

        System.out.println(i + ", " + d + ", " + c);

        

    }

    

    public static void main(String[] args) {

        // TODO 참조에 의한 인자 전달 방식

        

        CallByTest1 call = new CallByTest1();

        call.display();

    }

}


※ 결과값

4, 3.14, 가


아래와 같이 클래스를 따로 뺀다면 코드가 깔끔해진다.


class Data{

    int i = 4;

    double d = 3.14;

    char c = '가';

    boolean b = true;

    String s = "문자열";

}

public class CallByTest1 {

    void display(Data da) {

        System.out.println(da.i + ", " + da.d + ", " + da.c);

        

    }

    

    public static void main(String[] args) {

        // TODO 참조에 의한 인자 전달 방식

        

        CallByTest1 call = new CallByTest1();

        Data data = new Data();

        call.display(data);

    }

}


※ 결과값

4, 3.14, 가



★ 알고리즘과 디자인패턴 ★ 

어느정도 익숙해지면 알고리즘 공부를 해야한다.


디자인 패턴

- 똑같은 재료를 어떻게 쓸것인가?

- 같은 재료를 가지고 다양한 기능을 수행

- 개발자들의 시행 착오, 다양하게 쓰던 방법을 모아두었다.

- 짧은 시간에 비법을 알 수 있는 비급서


DTO패턴 (Data Object)

- DO(Data Object) VO(value Objec) DTO (Data To Object) 라고 함

Data Object  따로 따로 흩어진 다양한 변수로 묶어서 

쓰기 편하도록 하나의 클래스로 만들어주는 방법

위 예제와 같은 예문을 DTO 패턴이라고 한다.



반드시 주소(참조)에 의한 전달을 써야하는 경우는 어떻게 될까?

내가 원하는 위치에서 값이 바뀔 수 있는 방법.


배열로 전달한다.


public class CallByTest2 {

    void swap(int[] n) {        

        int temp = n[0];

        n[0] = n[1];

        n[1] = temp;

        

    }

    public static void main(String[] args) {

        // TODO 반드시 참조에 의한 전달을 사용해야하는 경우

        // 내가 원하는 위치에서 값이 바뀔 수 있도록 한다.

        // num1과 num2의 주소값을 전달한다.

        int[]num = {5,10};

        CallByTest2 call = new CallByTest2();

        call.swap(num);

        

        System.out.println("교환 후 ...");

        System.out.println(num[0] + ", " + num[1]);

    }

}


※결과값

교환 후 ...

10, 5


public class CallByTest2 {

    

    int num1=5, num2=10;

    

    void swap() {        

        int temp = num1;

        num1 = num2;

        num2 = temp;

        

    }

    public static void main(String[] args) {

        // TODO 반드시 참조에 의한 전달을 사용해야하는 경우

        // 내가 원하는 위치에서 값이 바뀔 수 있도록 한다.

        // num1과 num2의 주소값을 전달한다.

        CallByTest2 call = new CallByTest2();

        call.swap();

        

        System.out.println("교환 후 ...");

        System.out.println(call.num1 + ", " + call.num2);

    }

}


※ 결과값

교환 후 ...

10, 5


같은 클래스지만 main 클래스에서 메소드를 쓰려면 생성자를 써야하는데

이는 static 때문이다. static은 나중에 별도로 설명한다.


3. 데이터 저장방법


1)변수의 단점

- 값을 1개만 저장해야한다.


2) 배열의 단점

- 같은 데이터 타입만 사용 가능

- 크기를 지정해야해서 남는 메모리나 더 큰 메모리가 될 수도 있다.

- 데이터 중간에 삽입/삭제 가 매우 불편하다.

- 배열은 데이터를 한번 저장하고 보관하는 것이 가장 적절하다.


3) 클래스

- 배열의 단점을 해결하기 위한 방법으로도 클래스가 사용된다.

- 서로 다른 데이터를 묶을 수 있다. (배열의 불편함 중에 이 부분만 해결 가능)


★ 연결리스트(Linked List) 알고리즘

- 배열의 크기 지정해야하는 문제나 데이터 삽입/삭제 문제를 해결해준다.

- 알고리즘을 공부하면 배울 수 있다.

- 자바에서 클래스로 제공해준다.



우선 이클립스에서 privite 을 사용할 때 

getter setter와 생성자를 쉽게 만드는 방법을 알아보자.


★ getter setter 쉽게 만들기



★ 생성자 쉽게 만들기


그럼 학점 관리 프로그램을 만들어보자.



public class Sunjuk_v3 {

    public static void main(String[] args) {

        // TODO 클래스를 이용한 성적표

        //이는 인스턴스가 3개 만들어진 것이 아니라 인스턴스의 주소를 저장 할 참조변수가 3개 만들어 진 것이다.

        //new만들어갔다고 인스턴스가 생성된 것이 아니라는 점을 생각하자.

    

        Student students[] = new Student[3];

        //생성자가 호출되어야 인스턴스가 생성되기 때문에 생성자를 생성.

        students[0] = new Student("홍길동", 1, 98, 90);

        students[1] = new Student("임꺽정", 2, 76, 55);

        students[2] = new Student("신돌석", 3, 85, 73);

        

        // 총점 구하기

        for (int i = 0; i < students.length; i++) {

            students[i].setTot(students[i].getKor()

                    + students[i].getEng());

        }

        // 평균구하기

        for (int i = 0; i < students.length; i++) {

            students[i].setAvg(students[i].getTot()/2 );

        }

        // 학점 구하기

        for (int i = 0; i < students.length; i++) {

            if (students[i].getAvg() >= 90)

                students[i].setGrade('A');

            else if (students[i].getAvg() >= 80)

                students[i].setGrade('B');

            else if (students[i].getAvg() >= 70)

                students[i].setGrade('C');

            else if (students[i].getAvg() >= 60)

                students[i].setGrade('D');

            else

                students[i].setGrade('F');

        }

        

        

        

        // 등수 구하기

        System.out.println("***성적결과***");

        System.out.println("학번\t국어\t영어\t총점\t평균\t학점\t순위");

        System.out.println("---------------------------------------------");

        for (int i = 0; i < students.length; i++) {

            System.out.println(students[i].getNo()  + "\t"  +

                               students[i].getKor() + "\t"  +

                               students[i].getEng() + "\t " +

                               students[i].getTot() + "\t"  +

                               students[i].getRank()  + "\t" +

                               students[i].getGrade() + "\t" +

                               students[i].getAvg());

            }

            

            System.out.println("정렬 후.......");

            for (int row = 0; row < students.length-1; row++) {

                for (int col = row+1; col < students.length; col++) {

                    if (students[row].getAvg() < students[col].getAvg()) {

                        Student temp = students[row];

                        students[row] = students[col];

                        students[col] = temp;

                    }

                }

            }

            System.out.println("***성적결과***");

            System.out.println("학번\t국어\t영어\t총점\t평균\t학점\t순위");

            System.out.println("---------------------------------------------");

            for (int i = 0; i < students.length; i++) {

                System.out.println(students[i].getNo()  + "\t"  +

                                   students[i].getKor() + "\t"  +

                                   students[i].getEng() + "\t " +

                                   students[i].getTot() + "\t"  +

                                   students[i].getRank()  + "\t" +

                                   students[i].getGrade() + "\t" +

                                   students[i].getAvg());

        }

    }//main end

}//class end


//따로만든 Student 클래스

public class Student {

    //항상 클래스에 있는 변수는 private로 선언

    private String name;

    private int no;

    private int kor;

    private int eng;

    private int tot;

    private int avg;

    private int rank;

    private char grade;

    

    

    //생성자를 만들어준다.

    public Student(String name, int no, int kor, int eng) {

        this.name = name;

        this.no = no;

        this.kor = kor;

        this.eng = eng;

    }

    

    //getter와 setter를 만들어 준다.

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getNo() {

        return no;

    }

    public void setNo(int no) {

        this.no = no;

    }

    public int getKor() {

        return kor;

    }

    public void setKor(int kor) {

        this.kor = kor;

    }

    public int getEng() {

        return eng;

    }

    public void setEng(int eng) {

        this.eng = eng;

    }

    public int getTot() {

        return tot;

    }

    public void setTot(int tot) {

        this.tot = tot;

    }

    public int getAvg() {

        return avg;

    }

    public void setAvg(int avg) {

        this.avg = avg;

    }

    public int getRank() {

        return rank;

    }

    public void setRank(int rank) {

        this.rank = rank;

    }

    public char getGrade() {

        return grade;

    }

    public void setGrade(char grade) {

        this.grade = grade;

    }

    

    

}


//Student[] students = new Student[5];

//이렇게 배열로 만들면 크기를 지정해줘야해서 메모리 공간이 낭비될 수 있다.

//class를 배열처럼 다루자.

//이렇게 하는 것이 DTO 패턴이다.



※ 결과값

***성적결과***

학번    국어    영어    총점    평균    학점    순위

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

1    98    90     188    0    A    94

2    76    55     131    0    D    65

3    85    73     158    0    C    79

정렬 후.......

***성적결과***

학번    국어    영어    총점    평균    학점    순위

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

1    98    90     188    0    A    94

3    85    73     158    0    C    79

2    76    55     131    0    D    65



4. 재귀호출 (Recursive Call)


void func(){

    func();

}


-> func();


스스로를 호출하는 메소드 반복문과 같은 효과이다.

그러나 반복문 보다는 효과가 매우 떨어진다.


메소드이기 때문에 스택에 계속 임시저장을 하기 때문에 메모리를 엄청 사용한다.

그래서 재귀호출은 흐름상 반복을 돌려야 할 때를 제외하고는 쓰지 않는다.

재귀호출은 알고리즘을 배울 때 정리해두면 좋다.


5. static


1) 인스턴스와는 상관없이 따로 만들어지는 별도의 메모리

- 별도로 hip에 만들어지는 메모리

- 인스턴스는 클래스와 상관이 없다.

- 클래스의 소속이 아니기 때문에 인스턴스를 안만들어도 되지만 

  어느 클래스에 있는 변수인지 소속을 알려줘야한다.

- 변수 뿐만일 아니라 메서드도 마찬가지이다.


2) 여러 인스턴스들이 공유할 수 있는 메모리

- 인스턴스가 공통적으로 사용하는 메모리 (프로그램 실행하면 자동으로 만들어짐)


3) this 가 없다.

 - this란 인스턴스의 주소값.

 -  this를 인스턴스를 구별하기 위해 만든 것이다.

 - static은 인스턴스가 없기 때문에 인스턴스의 주소값이 없다.


4) 클래스 변수

 - static 변수는 반드시 클래스 안에서만 선언할 수 있다.

 - 메서드 안에서는 선언 할 수 없다.

 - 커뮤니케이션 오해가 없기 위해 클래스 변수라는 표현보다 static 변수라고 부르자.


5) static block

 - static 변수만을 모아서 초기화

 - 깔끔하게 관리할 수 있다.


예시)

static int i;

static double d;

static{

    i = 10;

    d = 3.14;


public static void main(String[] args)

public 자바 가상머신이 어디에든 실행 할 수 있도록 한다.

static main이 쉽게 호출 할 수 있도록 인스턴스를 생성하지 않고 어디서나 사용할 수 있는 static사용

void 리턴값이 없다. (가상머신이 main찾는데 가상머신에게 리턴하지 않는다.)

main 메서드도 시스템이 찾는 콜백메서드이다.

(String[] args)  명령행 인자를 위한 매개변수이다.



 static 사용 사례를 확인해보자.


class StaticDemo1{

    int a;

    int b;

    static int c;

}

//하지만 static의 소속은 해당클래스에 있기 때문에 접근제한자 private의 영향을 받는다.

public class StaticTest1 {

    

    //변수 static을 사용하면 어디서나 쓸 수 있다.

    static int d;

    

    //변수 뿐 아니라 메서드도 static이 붙으면 클래스와는 별도로 사용할 수 있다.

    static void method1() {

        System.out.println("난 메서드야...");

    }

    

    //main에 static을 만든 이유는 가상머신이 일일히 인스턴스를 생성해

    // main을 호출하여 프로그램을 실행하기 어렵기 때문이다.    

    public static void main(String[] args) {

        // TODO static이란 무엇인가?

        

        StaticDemo1.c = 100;

        //static은 인스턴스를 넣어도 쓸 수있지만 없어도 할 수 있다.

        StaticDemo1 demo1 = new StaticDemo1();

        StaticDemo1 demo2 = new StaticDemo1();

        System.out.println(demo1.c);

        System.out.println(demo2.c);

        

        demo2.c = 200;

        System.out.println(demo2.c);

        

        //static이 붙은건 인스턴스를 만들지 않는다.

        //StaticTest1.d 이지만 같은 클래스에 있기 때문에 생략해주었다.

        d = 300;

    }

}


※ 결과값

100

100



참고로, 인스턴스와 객체는 거의 비슷한 개념으로 보아도 좋다.


//스테틱과 인스턴스, 누가 더 빠를까?

public class StaticTest2 {

    StaticTest2(){

        System.out.println("생성자 호출됨 : 인스턴스 생성");

    }

    static double d1, d2;

    static {

        System.out.println("static 생성");

        //Math.은 수학계산을 도와주는 메서드이다.

        d1 = Math.sqrt(4.0);

        d2 = Math.sqrt(8.0);

    }

    

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        new StaticTest2();

        System.out.println(d1 + "," + d2);

    }

}


※ 결과값

static 생성

생성자 호출됨 : 인스턴스 생성

2.0,2.8284271247461903


static이 인스턴스생성보다 더 빠르다.

이를 통해 static은 인스턴스와 상관없이 움직인다는 것을 알 수 있다.


6. 중첩클래스

- 클래스도 반복문처럼 중첩을 할 수 있다.

- 전용으로 써야할 클래스가 있는 경우 중첩클래스를 사용하면 편하다.

- 클래스가 내부에 있으면(중첩하면) private에 접근할 수 있다.




1) 일반클래스

- 바깥에 있는 클래스의 인스턴스가 먼저 만들어 져야하고

  내부 클래스의 인스턴스가 또 생성이 되어야한다.

  바깥 클래스의 메모리 영역이 결정되어야 

  내부 클래스의 메로리를 쓸 수 있기 때문이다.


 - 안에있는 클래스는 바깥에 있는 클래스를 모두 쓸 수 있다. (변수, 메소드)


 - 바깥에 있는 클래스는 안에 있는 클래스를 접근 할 수 없다.





2) static 클래스

- static 클래스는 내부클래스가 되었을 때

  클래스 안에 있지만 바깥에 클래스와는 상관 없다.

  그렇기 때문에 인스턴스를 따로 생성해도 상관없다.


- static 클래스의 경우 일반 클래스와 다르게 

  별개의 메모리를 가지고 있기 때문에

  사용하기 위해서는 인스턴스를 매번 생성해야한다.

  그래서 중첩클래스는 대부분 일반클래스를 사용한다.




<static클래스의 경우>


public class OuterClass {

    private int outer;

    

    

    //클래스가 내부에 있으면(중첩하면) private에 접근할 수 있다.

    static class InnerClass{

        private int inner;

        

        void innerMethod() {

            System.out.println(inner);

            

            OuterClass obj2 = new OuterClass();

            obj2.outer = 200;

            System.out.println("outer=" + obj2.outer);

        }

    }

    

    public static void main(String[] args) {

        // TODO 중첩클래스 (내부클래스가 static일 경우)

        InnerClass obj1 = new InnerClass();

        obj1.innerMethod();

    }

}


※ 결과값

0

outer=200



<일반클래스의 경우>


public class OuterClass {

    

    //안에있는 클래스는 바깥에 있는 클래스를 모두 쓸 수 있다. (변수, 메소드)

    //바깥에 있는 클래스는 안에 있는 클래스를 접근 할 수 없다.

    private int outer;

    

    

    //클래스가 내부에 있으면(중첩하면) private에 접근할 수 있다.

    class InnerClass{

        private int inner;

        

        void innerMethod() {

            System.out.println(inner);

            

            OuterClass obj2 = new OuterClass();

            obj2.outer = 200;

            System.out.println("outer=" + obj2.outer);

        }

    }

    

    public static void main(String[] args) {

        // TODO 중첩클래스 (내부클래스가 일반클래스일 경우)

        //일반클래스는 바깥에 있는 클래스의 인스턴스가 먼저만들어져야 내부 클래스를 쓸 수 있다.

        //OuterClass out = new OuterClass();

        //InnerClass obj1 = out.new InnerClass();

        

        //같은 클래스에 있기 때문에 .OuterClass을 생략해도 되지만 바깥에서 불러올 때는

        //바깥 클래스의 인스턴스를 먼저 생성해야하기 때문에 OuterClass.을 반드시 써야한다.

        OuterClass.InnerClass obj1 = new OuterClass().new InnerClass();

        obj1.inner = 100;

        obj1.innerMethod();

    }

}


※ 결과값

0

outer=200


지금까지 배운 내용을 종합하여 디자인패턴을 하나 배워보자.


**Singleton

- 인스턴스를 단 하나만 쓸 수 있는 디자인패턴

- 하나만 있어도 충분한 인스턴스를 계속 만들어 메모리를 낭비하는 경우를 사전에 방지한다.

- private 은 class 앞에 못 쓴다.

- 기본 생성자에 private을 붙힌다.

- new가 있다고 해서 다 인스턴스가 만들어진다고 생각하지 말아라.

- 메서드만가지고 인스턴스가 만들어지는 경우는 모두 싱글턴이다.


싱글턴 패턴을 만들기 위해 필요한 것

- 재료 : 생성자, static, private

- 방법

    ①외부에서 절대를 인스턴스를 생성할 수 없게 한다.

    ②내부에서 단 하나의 인스턴스를 생성한다.

    ③그렇게 만들어진 인스턴스를 외부에서 사용 할 수 있게 한다.


class SingletonDemo{

    

    private int i;

    // 인스턴스를 하나 만들어준다.

    // 보호할 수 있도록 private 처리를 한다.

    // 단 하나만 만들어 질 수 있도록 static을 놓는다.

    private static SingletonDemo instance = new SingletonDemo();

    

    // 기본생성자에 private을 붙여서 인스턴스를 하나만 생성하도록 한다.

    private SingletonDemo() {

        

    }

    

    //외부에서 쓸 수 있도록 메서드를 사용한다.

    //리턴값이 SingletonDemo이기 때문에 void 자리에 클레스 타입을 쓴다.

    //인스턴스 없어도 호출할 수 있도록 static을 쓴다.

    public static SingletonDemo getInstance() {

        return instance;

    }

}

public class SingletonTest {

    public static void main(String[] args) {

        // TODO Singeton Patten

        

        /*

        SingletonDemo obj1 = new SingletonDemo();

        SingletonDemo obj1 = new SingletonDemo();

        */

        

        SingletonDemo obj1 = SingletonDemo.getInstance();

        SingletonDemo obj2 = SingletonDemo.getInstance();

        SingletonDemo obj3 = SingletonDemo.getInstance();

        

        System.out.println("주소 비교 : " + (obj1==obj2));

        System.out.println("주소 비교 : " + (obj2==obj3));

    }

}


※결과값

주소 비교 : true

주소 비교 : true


+ Recent posts