[자바]/자바의 정석 - 3판

Chapter06. 객체지향

쿠릉쿠릉 쾅쾅 2021. 9. 25. 21:39
728x90

 

 

객체지향 핵심

  • 캡슐화
  • 상속
  • 추상화
  • 다형성

 

 

객체지향 언어 = 프로그래밍 언어 + 객체지향개념(규칙) ← 객체지향개념 규칙 외워야함

 

객체지향 언어 장점

  • 코드 재사용성이 높음
  • 유지 보수 용이

 

 

 

 

클래스와 객체

 

클래스와 객체

 

객체

  • 객체 정의 : 실제로 존재하는 것. 사물 또는 개념. 클래스에 정의된 내용대로 메모리에 생성된 것
  • 객체 용도 : 객체가 가지고 있는 기능과 속성에 따라 다름
  • 객체가 필요한 이유 : 객체(=제품)를 사용하려고 
  • 객체를 사용한다는것은 객체가 가진 속성과 기능을 사용하는 것임
  • 객체의 구성 요소 :  속성(변수) + 기능(메서드)
    • 객체가 가지고 있는 속성과 기능을 객체의 멤버라고 부름
    • 속성(property)의 여러가지 용어 : 멤버변수, 특성, 필드, 상태 
      • 인스턴스의 멤버변수를 사용하려면 '참조변수.멤버변수' 로 사용하면 됨
      • 멤버변수 중에서 static이 붙은 것은 클래스변수(static 변수), static이 붙지 않는 것은 인스턴스 변수임
      • 멤버변수는 인스턴스변수와 static변수를 모두 통칭 하는 말
      • 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙임
    • 기능(function)의 여러가지 용어 : 메서드, 함수, 행위
      • 인스턴스의 메서드를 사용하려면 '참조변수.메서드'를 사용하면 됨
    • 멤버변수와 메서드를 선언하는데 순서는 관계 없지만, 일반적으로 멤버변수를 먼저 선언하고 멤버 변수는 멘버변수끼리 메서드는 메서드끼리 모아놓는것이 일반적임

 

 

객체 설명 (객체가 Tv라고 가정했을 때 이야기)

 

// 1. 클래스 TV 생성
class Tv {
    // 속성 (= 멤버변수)
    String color;  // 색깔
    boolean power;  // 전원상태
    int channel;  // 채널

    // 기능 (= 메서드)
    void power() {power =!power;}  // Tv를 켜거나 끄는 기능을 하는 메서드
    void channelUp() {++channel;}  // Tv의 채널을 높이는 기능을 하는 메서드
    void channelDown() {--channel;}  //Tv의 채널을 낮추는 기능을 하는 메서드
}


//  2. 객체의 생성
클래스명 변수명;  // 클래스의 객체를 참조하기 위한 참조변수 선언
변수명 = new 클래스명();  // 클래스의 객체를 생성 후, 객체의 주소를 참조변수에 저장




// ex.1 객체 생성
// 참조변수 t 는 리모컨 역할임. 
Tv t;  // TV 클래스 타입의 참조변수 t를 선언.
t = new Tv();  // Tv 인스턴스를 생성한 후, 생선된 Tv 인스턴스의 주소를 t에 저장



// ex.2 객체 생성 한줄로 작성
Tv t = new Tv();



// 3. 객체의 사용
t.channel = 7;  // Tv 인스턴스의 멤버변수 channel 의 값을 7로 함
t.channelDown();  // Tv 인스턴스의 메서드 channelDown()을 호출

System.out.println(t.channel)  // 6

 

 

 

예제

class Tv{
    // Tv의 멤버변수
    String color;  // 색상
    boolean power;  // 전원 상태(on/off)
    int channel;  // 채널
    
    // Tv의 메서드
    void power() {power = !power;}  // tv를 켜거나 끔 
    void channelUp() {++channel;}  // tv 채널을 올려줌
    void channelDown() {--channel;}  // tv 채널을 내려줌
}

class Tvtest{
    public static void main(String args[]) {
        Tv t1 = new Tv();
        Tv t2 = new Tv();
        
        System.out.println("[변경 전]");
        System.out.println(t1.channel);  // 0
        System.out.println(t2.channel);  // 0
        System.out.println();
        
        t1.channelUp();
        t2.channel = 7;
        t2.channelDown();
        
        System.out.println("[변경 후 1]");
        System.out.println(t1.channel);  // 1
        System.out.println(t2.channel);  // 6
        System.out.println();
        
        t1 = t2;  // t2가 저장하고 있는 값(주소)을 t1에 저장함. t1의 원래 참조 인스턴스는 사용할 수 없음.
        
        System.out.println("[변경 후 2]");
        System.out.println(t1.channel);  // 6
        System.out.println(t2.channel);  // 6
        System.out.println();
        
        t1.channel = 10;
        
        System.out.println("[변경 후 3]");
        System.out.println(t1.channel);  // 10
        System.out.println(t2.channel);  // 10
        System.out.println();

        t2.channel = 30;
    
        System.out.println("[변경 후 4]");
        System.out.println(t1.channel);  // 30
        System.out.println(t2.channel);  // 30
    
    
    }
}

 

 

객체와 인스턴스

  • 객체 : 모든 인스턴스를 대표하는 일반적인 용어
  • 인스턴스 : 특정 클래스로부터 생성된 객체 (예: Tv 인스턴스 )
  • 인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과 일치해야함

 

 

클래스와 인스턴스 관계

 

 

 

 

소스파일 이름 바꾸는 방법

  • 소스파일 우클릭 → Refactor → Rename → 소스파일 이름 수정 → Finish

 

메인 클래스(메인 메서드)를 수동으로 지정하기

  • Run 카테고리 → Run Configurations → Main class 에 적기

 

 

 

 

객체 배열

  • 객체 배열 == 참조변수 배열
  • 객체 배열 안에는 객체가 저장되는 것이 아니고, 객체의 주소가 저장됨
// 객체 배열의 생성
Tv[] tvArr = new Tv[3];  // 길이가 3인 Tv 타입의 참조변수 배열


// 객체 배열의 생성 따로 초기화 따로
Tv[] tvArr = new Tv[3];  // 길이가 3인 Tv 타입의 참조변수 배열
tv Arr[0] = new Tv();
tv Arr[1] = new Tv();
tv Arr[2] = new Tv();


// 객체 배열의 생성과 초기화를 한 줄로 작성
Tv[] tvArr = new Tv[3];  // 길이가 3인 Tv 타입의 참조변수 배열
Tv[] tvArr = { new Tv(), new Tv(), new Tv() };


// 객체의 수가 많을 때 ㅡ for문 사용
Tv[] tvArr = new Tv[100];

for(int i=0; i<tvArr.length; i++) {
    tvArr[i] = new Tv();
}

 

 

예제

class Tv{
    String color;
    boolean power;
    int channel;
    
    void power() {power = ![power;}
    void channelUp() {++channel;}
    void channelDown() {--channel;}
};

class Tvtest{
    public static void main(String args[]) {
    
        Tv[] tvArr = new Tv[3];
        for(int i=0; i<tvArr.length; i++){
            tvArr[i] = new Tv();
            tvArr[i].channel = i+10;
        }
    
        for(int i=0; i<tvArr.length; i++){
            tvArr[i].channelUp();
            System.out.println(tvArr[i].channel);
        }
    
    }
}

 

 

 

 

 

 

클래스

  • 클래스 정의 : 객체를 정의해 놓은것
    • 객체의 설계도
    • 데이터 + 함수  →  구조체 + 함수
    • 사용자 정의타입 → 사용자가 원하는 타입을 직접 만들 수 있음. 클래스가 곧  사용자 정의 타입
  • 클래스의 용도는 : 객체를 생성하는데 사용 
  • 클래스(=설계도)가 필요한 이유 : 객체(=제품)를 생성하기 위해
  • 한개의 소스 파일에는 하나의 클래스만 작성하는것이 바람직함
  • 하나의 소스 파일안에 클래스가 여러개일 경우, 메인 메서드가 있는 클래스 이름을 소스파일 이름으로 해야함 
  • 클래스 생성 방법

 

 

 

데이터 저장개념의 발전과정

 

 

 

  • 변수 : 하나의 데이터를 저장할 수 있는 공간
  • 배열 : 같은 종류의 여러 데이터를 하나로 저장할 수 있는 공간
  • 구조체 : 서로 관련 여러 데이터(종류 상관없음)를 하나로 저장할 수 있는 공간
  • 클래스 : 데이터와 함수의 결합 (구조체 + 함수)

 

 

 

클래스 내 선언 위치에 따른 변수의 종류

  • 변수의 선언위치가 변수의 종류가 범위(scope)를 결정함
  • 클래스 내 2가지 영역 존재
    • 클래스 영역  ㅡ iv 변수(인스턴스변수), cv 변수(클래스변수)
    • 메서드 영역  ㅡ lv 변수(지역변수)
  • 클래스 영역에는 선언문만 쓸 수 있음
    • 변수 선언 또는 메서드 선언만 가능
  • 클래스 영역, 메서드 영역은 순서를 따지지 않음

 

 

 

 

인스턴스 변수 iv

  • 각 인스턴스는 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있음
  • 인스턴스 생성 후,  ' 참조변수.인스턴스변수명 ' 으로 접근
  • 인스턴스를 생성 할 때, 생성되고, 참조변수가 없을 때 gc(가비지컬렉터)에 의해 자동 제거됨 
  • 인스턴스가 생성될 때마다 생성되므로 인스턴스마다 각기 다른 값을 가질 수 있음
  • 객체 = iv를 묶어놓은 것
  • 인스턴스마다 고유한 상태를 유지해야하는 속성의 경우, iv로 선언할 것

 

 

 

클래스 변수 cv

  • iv 변수 앞에 static을 붙이면 cv가 됨
  • cv는 모든 iv가 공통된 저장공간(변수)을 공유함.
  • 같은 클래스의 모든 인스턴스들이 공유하는 변수
  • 인스턴스 생성없이  ' 클래스이름.클래스변수명 ' 으로 접근
    • ex) Variables 클래스의 클래스 변수 cv를 사용하려면 ' Variables.cv ' 이렇게 쓸 것
  • 클래스가 로딩될 때 생성되고 프로그램이 종료될 때 소멸
  • 클래스 변수는 iv 변수와 달리 인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있음 
  • 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로 항상 공통된 값을 가짐
  • 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우 cv로 선언할 것
  • public 을 앞에 붙이면 전역변수 성격을 가짐

 

 

인스턴스변수와 클래스변수의 관계

 

  • 카드 내에서 숫자와 모양은 인스턴스 변수에 해당. 카드 크기나 폭은 클래스 변수에 해당됨
  • iv는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있음
  • cv는 모든 인스턴스가 하나의 저장공간을 공유하므로 항상 공통된 값을 가짐 

 

 

// 클래스 변수와 인스턴스 변수

class Card{
    // 인스턴스 변수 선언
    String kind;
    int number; 

    // 클래스 변수 선언
    static int width = 100;
    static int height = 250;
}

// 객체 생성
Card c = new Card();

// 인스턴스 변수 사용
c.kind = "HEART";
c.number = 5;

// 클래스 변수 사용
Card.width = 200;
Card.hegith = 300;

 

 

예제

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

        Card c1 = new Card();
        c1.kind = "Heart";
        c1.number = 7;
    
        Card c2 = new Card();
        c2.kind = "Spade";
        c2.number = 4;
        
        System.out.printf("c1 : %s %d (%d %d)%n", c1.kind, c1.number, Card.width, Card.height);
        // c1 : Heart 7 (100 250)

        System.out.printf("c2 : %s %d (%d %d)%n", c2.kind, c2.number, Card.width, Card.height);
        // c2 : Spade 4 (100 250)
        
        // cv는 모든 인스턴스가 하나의 저장공간을 공유하므로 cv 수정시 모든 인스턴스의  cv가 수정됨 
        c1.width = 50;
        c1.height = 80;
    
        System.out.printf("c1 : %s %d (%d %d)%n", c1.kind, c1.number, Card.width, Card.height);
        // c1 : Heart 7 (50 80)
        
        System.out.printf("c2 : %s %d (%d %d)%n", c2.kind, c2.number, Card.width, Card.height);
        // c2 : Spade 4 (50 80)
    
    
    }
}



class Card{
    String kind;
    int number;
    
    static int width = 100;
    static int height = 250;
}

 

 

 

 

지역변수 lv 

  • 조건문, 반복문의 블럭{ } 내에 선언된 지역변수는 블럭을 벗어나면 소멸 
  • 메서드 내에서만 사용 가능
  • 메소드 종료시 소멸

 


 

메서드

  • 메서드란, 문장들을 묶어놓은 것
  • 메서드 = 선언부 +구현부
  • 메서드의 반환값 개수는 무조건 0~1개
  • 메서드의 반환값 개수를 여러개로 하고 싶으면 배열을 이용하거나 객체로 묶어서 줘야함
  • 메서드는 입력값 또는 출력값이 없을 수도 있으며, 심지어는 입력값과 출력값 모두 없을 수도 있음
  • 매개변수도 메서드 내에 선언된 것으로 간주되어 lv임
  • 인자의 타입은 매개변수의 타입과 일치하거나 자동형변환이 가능해야함
  • 같은 클래스 내의 메서드끼리는 참조변수를 사용하지 않고도 서로 호출이 가능하지만 static 메서드는 같은 클래스 내의  인스턴스 메서드를 호출할 수 없음
  • 메서드 사용 이유
    • 높은 재사용성
    • 중복된 코드 제거
    • 프로그램의 구조화

 

메서드
반환값이 없는 메서드 일 때

 

 

 

메서드 예시)

메서드 예시

 

 

// ex.1
public static void main(String args[]){
    MyMath mm = new MyMath();
    long result1 = mm.add(5L, 3L);  // 8
    long result2 = mm.subtact(5L, 3L);  // 2
    long result3 = mm.multiply(5L, 3L) // 15
    double result4 = mm.divide(5, 3);  // 1.66666
    
    System.out.printf("%d %d, %d, %f", result1, result2, result3, result4);
}

class MyMath{
    long add(long a, long b){
        return a+b;
    }
    long subtact(long a, long b) {return a-b};
    long multiply(long a, long b) {return a*b};
    double divide(double a, double b) {return a/b};
}

 

 

 

 

 

반환타입

  • 반환값이 없는 경우 반환타입으로 'void'를 적어야함

 

 

return문

  • 메서드의 반환타입이 'void'가 아닌 경우, 구현부{ } 안에 'return 반환값;' 이 반드시 포함되어야함

 

 

 


 

 

JVM의 메모리 구조

JVM의 메모리 구조

 

메서드 영역 (method area)

  • 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장함
  • 이 때, 그 클래스의 클래스 변수도 이 영역에 함께 생성됨

 

호출스택 (call stack 또는 execution stack)

  • 메서드의 작업에 필요한 메모리 공간 제공
  • 메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용됨
  • 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워짐
  • 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드임
  • 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드임
  • 반환타입(return type)이 있는 메서드는 종료되면서 결과값을 자신을 호출한 메서드(caller)에게 반환함

 

힙 (heap)

  • 인스턴스가 생성되는 공간
  • 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성됨
  • 인스턴스 변수들이 생성되는 공간

 

 

 

예제

public static void main(String[] args) {
    System.out.println("main(String[] args)이 시작되었음");
    firstMethod();
    System.out.println("main(String[] args)이 끝났음");
}

static void firstMethod() {
    System.out.println("firstMethod()가 시작되었음");
    secondMethod();
    System.out.println("firstMethod()가 끝났음");
}

static void secondMethod() {
    System.out.println("secondMethod()가 시작되었음");
    System.out.println("secondMethod()가 끝났음");
}


/* 출력값
main(String[] args)이 시작되었음
firstMethod()가 시작되었음
secondMethod()가 시작되었음
secondMethod()가 끝났음
firstMethod()가 끝났음
main(String[] args)이 끝났음
*/

 

 

 


 

 

 

기본형 매개변수와 참조형 매개변수

  • 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨줌
  • 매개변수 타입이 기본형일 때는 기본형이 복사됨
  • 매개변수 타입이 참조형일 때는 인스턴스 주소가 복사됨
  • 메서드의 매개변수를 기본형으로 선언하면 단순히 저장된 값만 얻지만, 참조형으로 선언하면 값이 저장된 곳의 주조를 알 수 있기 때문에 값을 읽어 오는것은 물론 값을 변경하는 것도 가능함

 

 

기본형 매개변수

  • 변수의 값을 읽기만 할 수 있음 (read only)

 

 

참조형 매개변수

  • 변수의 값을 읽고 변경할 수 있음 (read & write)
  • 매개변수가 참조형일 경우 값이 아니라 값이 저장된 주소를 메서드에게 넘겨주기 때문에 값을 변경할 수도 있음

 

 

 

 

/* ex.1 기본형 매개변수 */
class Data {int x;}
class 파일명{
	public static void main (String[] agrs){
        Data d = new Data();
        d.x = 10;
        System.out.println(d.x);   // 10
        changed(d.x);  // 1000
        System.out.println("changed(d.x) 함수 후");  // changed(d.x) 함수 후
        System.out.println(d.x);  // 10
        // d.x 값은 changed(d.x)에 의해서 값이 바뀌지 않음 
    }

	static void changed(int x){
    	x = 1000;
        System.out.println(x);
    }
}




/* ex.2 참조형 매개변수 */
class Data {int x;}
class 파일명{
    public static void main (String[] args){
        Data d = new Data();
        d.x = 10;
        System.out.println(d.x);  // 10
        changed(d);  // 1000
        System.out.println("changed(d) 실행 후");  // changed(d) 실행 후
        System.out.println(d.x);  // 1000
    }
    
    static void changed(Data d){
        d.x = 1000;
        System.out.println(d.x);
    }
    
}





/* ex.3 참조형 매개변수 */
class 파일명{
    public static void main(String[] args){
        int[] x = {10};
        System.out.println(x[0]);  // 10
        changed(x);  // 1000
        System.out.println("changed(x) 실행 후");  // changed(x) 실행 후
        System.out.println(x[0]);  // 1000
    
    }
    
    static void changed(int[] x){
        x[0] = 1000;
        System.out.println(x[0]);
    }
    
}

 

 

 

 

예제

/* ex.1 */
public static void main (String[] args){
    int[] arr = {3,2,1,6,5,4};
    printArr(arr);  // 배열의 모든 요소 출력
    sortArr(arr);   // 배열 정렬
    printArr(arr);  // 정렬후 결과 출력
    System.out.println(sumArr(arr));
}

static void printArr(int[] arr){
    for(int i : arr) System.out.print(i);
    System.out.println();
}

static int sumArr(int[] arr){
    int sum = 0;
    for(int i=0; i<arr.length; i++) sum += arr[i];
    return sum;
}

static void sortArr(int[] arr){
    for(int i=0; i<arr.length-1; i++){
        for(int j=0; j<arr.length-1-i; j++){
            if(arr[j] > arr[j+1]){
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            } // end of if
        } // end for j
    } // end for i 
}







/* ex.2 */
// 반환값이 있는 메서드를 반환값이 없는 메서드로 바꾸는 방법
// 참조형 매개변수를 활용하여 반환값이 없어도 메서드의 실행결과를 얻어 올 수 있음
// 메서드는 하나의 값만 반환할 수 있지만 이것을 응용하면 여러 개의 값을 반환받는 것과 같은 효과임

class prac{
    public static void main(String[] args){
        prac p = new prac();
        
        int result = p.add(3, 5);
        System.out.println(result);
    
        int[] result2 ={0};
        p.add(3, 5, result2);
        System.out.println(result2[0]);
    
    }
    
    int add(int a, int b) {return a+b;}
    
    void add(int a, int b, int[] result) {result[0] = a + b;}


}

 

 

 

 


 

참조형 반환타입

  • 매개변수뿐만 아니라 반환타입도 참조형이 될 수 있음
  • 반환타입이 '참조형'이라는 것은 메서드가 '객체의주소'를 반환한다는 것을 의미

 

/* ex.1 */
class Date {int x;}
class prac{
    public static void main(String[] args){
        Data d = new Data();
        d.x = 10;
        
        Data d2 = copy(d);
        System.out.println(d.x);  // 10
        System.out.println(d2.x);  // 10
    }
    
    static Data copy(Data d){
        Data tmp = new Data();
        tmp.x = d.x;
        return tmp;
    }
}

 

 

 


 

 

재귀호출 (recursive call)

  • 메서드의 내부에서 메서드 자신을 다시 호출하는 것을 '재귀호출' 이라고 함
  • 재귀호출을 하는 메서드를 '재귀 메서드'라고 함
  • 재귀호출은 반복문으로 바꿀 수 있으며 반복문보다 성능이 나쁨
  • 재귀호출을 사용하는 이유
    • 논리적 간결함
    • 효율이 좋고 알아보기 힘들게 작성하는것보다 다소 비효율적이더라도 알아보기 쉽게 작성하는 것이 논리적 오류가 발생할 확률이 줄어들고 나중에 수정하기도 좋음
    • 이해하기 쉽고 간결한 코드로 작성할 수 있음
  • 재귀호출은 반복문과 유사함
  • 재귀호출은 반복문보다 수행시간이 더 오래 걸림
  • 재귀호출에 드는 비용보다 재귀호출의 간결함이 주는 이득이 큰 경우에만 사용할 것 
  • 재귀호출을 쓸 때 조건문이 필수임
  • 재귀호출의 예
    • 팩토리얼, 제곱, 트리운행, 폴더목록표시 등 

 

void method() {
    method();  // 재귀호출. 메서드 자신을 호출함
}

 

 

 

 

예제

/* ex.1 팩토리얼 */
public static void main(String[] args){
    int result = factorial(4); 
    System.out.println(result);  // 24
}

static int factorial(int n) {
    // n이 0보다 커야하며, 12보단 작아야함. 13보다 크면 int형 최대값을 초과함
    if (n<=0 || n>12) return -1;  // 매개변수 n의 유효성 검사
    
    if(n==1) return 1;
    else return n * factorial(n-1);
}










/* ex.2 팩토리얼 */
static long factorial(int n){
    if(n<0 || n>20) return -1;  // 매개변수 유효성 검사
    
    if (n==1) return 1;
    else return n * factorial(n-1);
}

public static void main(String[] args){
    long result = 0;
    int n = 21;
    
    for(int i =1; i<=n; i++){
        result = factorial(i);
        
        if(result == -1){
            System.out.printf("유효하지 않는 수입니다. (1<=n<=20) : %d%n", n);
            break;
        }
        System.out.printf("%2d != %20d%n", i, result);
    }


}

/* 출력값
 1 !=                    1
 2 !=                    2
 3 !=                    6
 4 !=                   24
 5 !=                  120
 6 !=                  720
 7 !=                 5040
 8 !=                40320
 9 !=               362880
10 !=              3628800
11 !=             39916800
12 !=            479001600
13 !=           6227020800
14 !=          87178291200
15 !=        1307674368000
16 !=       20922789888000
17 !=      355687428096000
18 !=     6402373705728000
19 !=   121645100408832000
20 !=  2432902008176640000
유효하지 않는 수입니다. (1<=n<=20) : 21
*/








/* ex.3 제곱 */
public static void main(String[] args){
    int x = 2;
    int n = 5;
    long result = 0;
    
    for(int i=1; i<=n; i++) result += power(x, i);

    System.out.println(result);  // 62

}
static long power(int x, int n){
    if(n==1) return x;
    return x * power(x, n-1);
}

 

 

 


 

 

클래스 메서드(static 메서드)와 인스턴스 메서드

  • 메서드 앞에 static이 붙어있으면 클래스 메서드. 그렇지 않으면 인스턴스 메서드
  • 같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능함
  • 단, 클래스멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 함
    • 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만. 클래스 멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수 있기 때문
    • 인스턴스 멤버란, 인스턴스 변수와 인스턴스 메서드를 의미

 

/* ex.1 */
class prac{
    void instaceMethod() {} // 인스턴스 메서드
    static void staticMethod() {}  // static 메서드
    
    void instanceMethod2(){  // 인스턴스 메서드
        instanceMethod();  // 다른 인스턴스 메서드 호출
        staticMethod();  // static 메서드 호출
    }

    static void staticMethod2() { // static 메서드
        // instanceMethod();  // 에러. 인스턴스 메서드를 호출할 수 없음
        
        staticMethod();  // static 메서드는 호출 할 수 있음
    }

}






/* ex.2 */
class prac{
    int iv;  // 인스턴스 변수
    static int cv;  // 클래스 변수
    
    void instanceMethod(){  // 인스턴스 메서드
        System.out.println(iv);  // 인스턴스 변수 사용 가능
        System.out.println(cv);  // 클래스 변수 사용 가능
    }

    static void staticMethod(){  // static 메서드
        // System.out.println(iv);  // 에러. 인스턴스 변수 사용 불가능
        
        System.out.println(cv);  // 클래스 변수 사용 가능
    }
    
}






/* ex.3 */
class prac2{
    int iv = 10;  // 인스턴스 변수
    static int cv = 20;  // 클래스 변수
    
    int iv2 = cv;
    // static int cv2 = iv;  // 에러. 클래스변수는 인스턴스 변수를 사용할 수 없음
    static int cv2 - new prac2().iv;  // 객체를 생성해야 쓸 수 있음
    
    static void staticMethod1(){  // static 메서드
        System.out.println(cv);
        
        // System.out.println(iv);  // 에러. 클래스메서드에서 인스턴스변수 사용 불가
        prac2 p = new prac2();
        System.out.println(p.iv);  // 객체를 생성한 후에야 인스턴스 변수 참조 가능
    
    }

    void instanceMethod1() {  // 인스턴스 메서드
        System.out.println(cv);
        System.out.println(iv);  // 인스턴스메서드에서 인스턴스 변수 사용 가능
    }


    static void staticMethod2() {  // static 메서드
        staticMethod1();
        
        // instanceMethod1();  // 에러. 클래스메서드에서 인스턴스 메서드 호출 불가능
        prac2 p = new prac2();
        p.instanceMethod1();  // 인스턴스를 생성한 후에 인스턴스 메서드를 호출 할 수 있음
    }
    
    // 인스턴스메서드에서 인스턴스메서드와 클래스메서드 모두 인스턴스 생성없이 호출 가능
    void instanceMethod2() {  // 인스턴스 메서드
        staticMethod1();  // 
        instanceMethod1();
    }
    
}

 

 

 

클래스 메서드 (static 메서드)

  • 클래스 메서드도 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)' 로 호출 할 수 있음
  • 클래스는 '데이터(변수)와 데이터에 관련된 메서드의 집합'임
  • 같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있음
  • 인스턴스와 관계없는(인스턴스 변수나 인스턴스 메서드를 사용하지 않는) 메서드를 클래스 메서드로 정의함
  • 클래스 메서드(static 메서드)는 인스턴스 변수를 사용할 수 없음
  • 클래스 메서드(static 메서드)는 인스턴스 메서드보다 호출시간이 짧음, 성능 향상
  • 메서드 중에서 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드에 static을 붙일 것을 권장
    • 메서드의 작업내용 중에서 인스턴스 변수를 필요로 한다면, static을 붙을 수 없음

 

 

인스턴스 메서드

  • 인스턴스 메서드는 반드시 객체를 생성해야만 호출할 수 있음
  • 인스턴스 메서드는 인스턴스 변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 하는 메서드임
  • 인스턴스 변수나 인스턴스 메서드에서 static이 붙은 멤버들을 사용하는것은 언제나 가능
  • 인스턴스 변수가 존재한다는 것은 static변수가 이미 메모리에 존재한다는 것을 의미

 

 

예제

/* ex.1 */
class myMath{
    long a,b;
    
    // 인스턴스 메서드. 인스턴스변수 a, b만을 이용해서 작업하므로 매개변수가 필요없음
    long add() {return a+b;} // a, b는 인스턴스변수
    long subtract() {return a-b;}
    long multiply() {return a*b;}
    double divide() {return a/b;}
    
    // 클래스메서드. 인스턴스변수와 관계없이 매개변수만으로 작업이 가능
    static long add(long a, long b) {return a+b;}  // a, b는 지역변수
    static long subtract(long a, long b) {return a-b;}
    static long multiply(long a, long b) {return a*b;}
    static double divide(double a, double b) {return a/b;}
}

class prac{
    public static void main(String[] args){
        //클래스메서드 호출. 인스턴스 생성 없이 호출 가능
        System.out.println(myMath.add(200L, 100L));  // 300
        System.out.println(myMath.subtract(200L,100L));  // 100
        System.out.println(myMath.multiply(200L, 100L));  // 20000
        System.out.println(myMath.divide(200.0, 100.0));  // 2.0
        
        myMath mm = new myMath();  // 인스턴스 생성
        mm.a = 200L;
        mm.b = 100L;
        // 인스턴스메서드는 객체 생성 후에 호출 가능 
        System.out.println(mm.add());  // 300
        System.out.println(mm.subtract());  // 100
        System.out.println(mm.multiply());  // 20000
        System.out.println(mm.divide());  // 2.0
    }
}

 

 

 


 

 

오버로딩 (overloading)

  • 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것
  • 한 클래스 내에 이미 사용하는 이름과 같은 이름을 가진 메서드가 있더라도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있음
  • 같은 이름의 메서드를 정의한다고 해서 무조건 오버로딩이 아님
  • 오버로딩 조건
    • 메서드 이름이 같아야함
    • 매개변수의 개수 또는 타입이 달라야함
    • 매개변수는 같고 리턴타입이 다른 경우에는 오버로딩이 성립되지 않음
  • 오버로딩된 매서드들은 매개변수에 의해서만 구별할 수 있음
  • 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못함 
  • 오버로딩 장점
    • 오버로딩을 통해 여러 메서드를 하나의 이름으로 정의할 수 있음
    • 메서드 이름 절약

 

 

 

 

오버로딩 예

 

 

 

 

 

예제

/* ex.1 */
class prac{
    public static void main(String[] args){
    	myMath mm = new myMath();
        System.out.println("쿠쿠 " + mm.add(3,3));  // int add(int a, int b) 쿠쿠 6
        System.out.println("카카 " + mm.add(3L,3));  // long add(long a, int b) 카카 6
        System.out.println("우우 " + mm.add(3,3L));  // long add(int a, long b) 우우 6
        System.out.println("아아 " + mm.add(3L,3L));  // long add(long a, long b) 아아 6
        // 출력 순서가 바뀐 이유는 println 메서드가 결과를 출력 하려면 
        // add 메서드의 결과가 먼저 계산되어야 하기 때문
        


        int[] a ={100, 200, 300};
        System.out.println("유유 "+ mm.add(a));  // int add(int[] a) 유유 600
    }
}

class myMath{
    int add(int a, int b){
        System.out.print("int add(int a, int b) ");
        return a+b;
    }

    long add(long a, int b){
        System.out.print("long add(long a, int b) ");
        return a+b;
    }
    
    long add(int a, long b){
        System.out.print("long add(int a, long b) ");
        return a+b;
    }
    
    long add(long a, long b){
        System.out.print("long add(long a, long b) ");
        return a+b;
    }
    
    int add(int[] a){  // 배열의 모든 요소의 합
        System.out.print("int add(int[] a) ");
        int result =0;
        
        for(int i=0; i<a.length; i++) result += a[i];
        
        return result;
    }
    
}

 

 

 


 

 

가변인자(varargs)와 오버로딩

  • 매개변수가 동적으로 지정될 수 있도록 하는 기능을 가변인자(variable arguments)라고 함
  • 가변인자 외에도 매개변수가 더 있다면, 가변인자를 매개변수 중에서 제일 마지막에 선언해야 함
  • 가변인자는 내부적으로 배열을 이용하는 것임
  • 가변인자가 선언된 메서드를 호출할 때마다 배열이 새로 생성됨
    • 비효율적이므로 꼭 필요한 경우에만 가변인자를 사용할 것
  • 가변인자를 사용한 메서드는 오버로딩을 하지 않는것이 좋음

 

 

가변인자와 매개변수의 타입을 배열로 하는것의 차이점은?

  • 매개변수의 타입을 배열로 하면 반드시 인자를 지정해 줘야함
  • 매개변수 인자를 생략할 수 없기에 null 이나 길이가 0인 배열을 인자로 지정해줘야하는 불편함이 있음
/* 매개변수 */
String concatenate(String[] str){
    return "String";
}

String result = concatenate(new String[0]);  // 인자로 길이가 0인 배열을 지정
String result = concatenate(nill);  // 인자로 null을 지정
// String result = concatenate();  // 에러. 인자가 필요함

 

 

 

 

가변인자 예제

/* ex.1 */
class prac {
    public static void main(String[] args){
        String[] strArr = {"100", "200", "300"};
        
        System.out.println(concatenate(" ", "100", "200", "300"));          // 100 200 300 
        System.out.println(concatenate("-", strArr));                       // 100-200-300-
        System.out.println(concatenate(",", new String[]{"1", "2", "3"}));  // 1,2,3,
        System.out.println("[" + concatenate(",", new String[0])+ "]");     // []
        System.out.println("[" + concatenate(",")+ "]");                    // []
        
    
    }

    static String concatenate(String delim, String... args){  // 가변인자 사용
        String result = "";
        
        for(String str : args){
            result += str + delim;
        }
        
        return result;
    }

}

 

 

 


 

생성자

  • 생성자란 인스턴스가 생성될 때 호출되는 '인스턴스 변수 초기화 메서드'
  • 인스턴스 변수의 초기화 작업에 주로 사용
  • 모든 클래스에는 반드시 하나 이상의 생성자가 있어야 함
  • 인스턴스 생성 시에 실행되어야 할 작업을 위해서 사용되기도 함
  • 생성자는 클래스 내에 선언되며 메서드 구조와 유사함
  • 생성자의 이름은 클래스의 이름과 같아야 함
  • 생성자는 리턴값이 없으나 void는 생략함
  • 생성자도 오버로딩이 가능하므로 하나의 클래스에 여러개의 생성자가 존재할 수 있음
  • 컴파일 할 때, 소스파일(*.java)의 클래스에 생성자가 하나도 정의되지 않는 경우 컴파일러는 자동적으로 기본 생성자를 추가하여 컴파일함
    • 컴파일러가 자동적으로 기본 생성자를 추가해주는 경우는 클래스 내에 생성자가 하나도 없을때 뿐임
  • 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는것이 아님. '클래스()' 가 생성자임

 

 

 

 

 

기본 생성자

  • 매개변수가 없는 생성자
  • 클래스에 생성자가 하나도 없으면 컴파일러가 기본 생성자를 추가함
  • 생성자가 하나라도 있으면 컴파일러는 기본 생성자를 추가하지 않음
  • 클래스에 생성자를 정의할 때는 반드시 내용이 없는 기본 생성자도 함께 정의해주는 것이 좋음
class card{
    // 기본 생성자
    card(){}  //  클래스명() {}
}

 

 

 

예제

/* ex.1 */
class Data1{
    int value;
}

class Data2{
    int value;
    
    Data2(int x){  // 매개변수가 있는 생성자
        value = x;
    }
}

class prac{
    public static void main(String[] args){
        Data1 d1 = new Data1();  // 기본 생성자가 추가됨
        
        // Data2에는 이미 Data2(int x)가 정의되어있어서 기본 생성자가 추가되지 않음
        // Data2 d2 = new Data2();  // 에러. 매개변수를 입력하지 않음  
        Data2 d2 = new Data2(10);
    }
}

 

 

 

매개변수가 있는 생성자

  • 생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있음
  • 인스턴스마다 각기 다른 값으로 초기화되어야하는 경우가 많기 때문에 매개변수를 사용한 초기화는 매우 유용함
  • 인스턴스를 생성한 다음에 인스턴스 변수의 값을 변경하는 것보다 매개변수를 갖는 생성자를 사용한것이 더 간결하고 직관적임

 

 

 

 

 

 

예제

class Car{
    String color;
    String gearType;
    int door;
    
    Car(){};
    
    Car(String c, String g, int d){
        color = c;
        gearType = g;
        door = d;
    }
}

class prac{
    public static void main(String[] args){
        Car c1 = new Car();
        c1.color = "white";
        c1.gearType = "auto";
        c1.door = 4;
    
        Car c2 = new Car("Black", "auto", 2);
    
        System.out.printf("c1 %s %s %d%n", c1.color, c1.gearType, c1.door);  // c1 white auto 4
        System.out.printf("c2 %s %s %d%n", c2.color, c2.gearType, c2.door);  // c2 Black auto 2
    }
}

 

 

 

 

생성자에서 다른 생성자 호출하기 - this, this()

  • 같은 클래스의 멤버들 간에 서로 호출할 수 있는 것처럼 생성자 간에도 서로 호출이 가능
  • 다른 생성자 호출시, 생성자의 이름으로 클래스 이름 대신 this를 사용해야함
  • 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에만 호출이 가능함
  • 생성자의 매개변수로 선언된 변수의 이름이 인스턴스변수와 같을 경우 인스턴스 변수 앞에 this 를 붙여줌
  • 'this.인스턴스변수명'  은 인스턴스 변수임. 생성자의 매개변수인 지역변수와 구별이 가능
  • this는 참조변수로 인스턴스 자신을 가리킴. 인스턴스 주소가 저장되어 있음
  • this를 사용할 수 있는건 인스턴스멤버뿐
  • static메서드에서는 인스턴스 멤버들을 사용할 수 없는것처럼, this 역시 사용할 수 없음
    • static 메서드는 인스턴스를 생성하지 않고도 호출될 수 있으므로 static 메서드가 호출된 시점에 인스턴스가 존재하지 않을수도 있기 때문
  • this는 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있음
  • 모든 인스턴스 메서드에 지역변수로 숨겨진채로 존재함
  • this(), this(매개변수)는  생성자. 같은 클래스의 다른 생성자를 호출할 때 사용함
  • this는 참조변수, this()는 생성자

 

 

예제

/* ex.1 */
class Car{
    String color;
    String gearType;
    int door;
    
    Car(){
        this("white", "auto", 4);
    }
    
    Car(String color){
        this(color, "auto", 4);
    }
    
    Car(String color, String gearType, int door){
        this.color = color;  // this.color는 인스턴스 변수. color는 생성자의 매개변수로 정의된 지역변수
        this.gearType = gearType;
        this.door = door;
    }
    
}


class prac{
    puㅠlic static void main(String[] args){
        Car c1 = new Car();
        Car c2 = new Car("blue");
         
        System.out.printf("c1 %s %s %d%n", c1.color, c1.gearType, c1.door);  // c1 white auto 4
        System.out.printf("c2 %s %s %d%n", c2.color, c2.gearType, c2.door);  // c2 blue auto 4
    
    }
    

}

 

 

 

 

 

 

 

생성자를 이용한 인스턴스의 복사

  • 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용함 
  • 생성자에서 참조변수를 매개변수로 받아서 인스턴스변수들의 값을 복사
  • 인스턴스를 생성할 때 다음 2가지 사항을 결정 해야함
    • 클래스 ㅡ 어떤 클래스의 인스턴스를 생성할 것인가
    • 생성자 ㅡ 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가

 

 

 

 

 

/* ex.1 */
class Car{
    String color;
    String gearType;
    int door;
    
    Car(){
        this("white", "auto", 4);    
    }

    Car(Car c){  // 인스턴스의 복사를 위한 생산자
        this(c.color, c.gearType, c.door);
    }

    Car(String color, String gearType, int door){
        this.color = color;
        this.gearType = gearType;
        this.door = door;
    }
}

class prac{
    public static void main(String[] args){
        Car c1 = new Car();
        Car c2 = new Car(c1);  // c1의 본사본 c2 생성
        
        System.out.printf("c1 %s %s %d%n", c1.color, c1.gearType, c1.door);  // c1 white auto 4
        System.out.printf("c2 %s %s %d%n", c2.color, c2.gearType, c2.door);  // c2 white auto 4
        
        System.out.println("변경 후");  // 변경 후
        c1.door = 100;
        
        System.out.printf("c1 %s %s %d%n", c1.color, c1.gearType, c1.door);  // c1 white auto 100
        System.out.printf("c2 %s %s %d%n", c2.color, c2.gearType, c2.door);  // c2 white auto 4
     
    }
}

 

 

 

 


 

 

변수의 초기화

  • 변수 초기화란 변수를 선언하고 처음으로 값을 저장하는 것
  • 선언과 동시에 적절한 값으로 초기화하는것이 바람직
  • 멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞게 기본값으로 초기화 됨
  • 지역변수는 사용하기 전에 반드시 초기화를 해야함
  • 배열의 초기화는 선택적임

 

/* ex.1 */
class prac{
    int x;         // 인스턴스 변수
    int y = x;     // 인스턴스 변수
    
    void method(){
        int i;           // 지역변수
        // int j = i;    // 에러. 지역변수를 초기화하지 않고 사용
    }

}

 

 

 

 

멤버변수 초기화 방법

  • 명시적 초기화
  • 생성자
  • 초기화 블럭
    • 인스턴스 초기화 블럭 : 인스턴스 변수를 초기화하는데 사용
    • 클래스 초기화 블럭 : 클래스 변수를 초기화 하는데 사용

 

 

 

명시적 초기화

  • 명시적 초기화란. 변수를 선언과 동시에 초기화 하는 것
  • 명시적 초기화가 간단하고 명료하지만, 보다 복잡한 초기화 작업이 필요할 때는 '초기화 블럭' 또는 '생성자'를 사용
class Car(){
    int door = 4;  // 기본형 변수의 초기화
    Engin e = new Engine();  // 참조형 변수의 초기화

}

 

 

 

초기화 블럭

  • 초기화 블럭에는 '클래스 초기화 블럭'과 '인스턴스 초기화 블럭' 두 가지 종류가 있음
  • 클래스 초기화 블럭
    • 클래스 변수의 초기화에 사용
    • 인스턴스 초기화 블럭 앞에 단순히 static을 덧붙이기만 하면 됨
    • 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행
    • 클래스가 처음 로딩 될 때 클래스 변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화 블럭이 클래스 변수들을 초기화하는것
  • 인스턴스 초기화 블럭
    • 인스턴스 변수의 초기화에 사용
    • 단순히 클래스 내에 블럭{ }을 만들고 그 안에 코드를 작성하면 됨
    • 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때 마다 (생성자보다 먼저 )수행
    • 인스턴스 변수의 초기화는 주로 생성자를 사용
    • 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행돼야하는 코드를 넣는데 사용함
  • 초기화 블럭 내에서 메서드 내에서와 같이 조건문, 반복문, 예외처리구문 등 자유롭게 사용 가능

 

 

class prac{
    static { /* 클래스 초기화 블럭 */ }
    
    { /* 인스턴스 초기화 블럭  */}
 
}

 

 

 

 

 

예제1

class prac{
    static {  // 클래스 초기화 블록  → 딱 1번만 실행
        System.out.println("static { }"); 
    }

    {  // 인스턴스 초기화 블록   → 인스턴스가 생성될 때마다 (생성자보다 먼저) 실행
        System.out.println("{ }");
    }
    
    public prac(){  // 생성자  → 인스턴스가 생성될 때마다 실행 
        System.out.println("생성자");
    }
    
    public static void main(String[] args){
        prac p = new prac();
        
        System.out.println();
        
        prac p2 = new prac();
    }

}

/* 출력값
static { }
{ }
생성자

{ }
생성자
*/

 

 

예제2

import java.util.Arrays;

class prac{
    static int[] arr = new int[10];  // 명시적 초기화
    
    static{  // 클래스 블럭 초기화
        for(int i=0; i<arr.length; i++){
            arr[i] = (int)(Math.random()*10) +1;  // 1~10 까지 임의의 수 대입 
        }
    }
    
    public static void main(String[] args){
        System.out.println(Arrays.toString(arr));  // [1, 3, 4, 9, 1, 8, 10, 8, 4, 1] 항상 값이 바뀜
    
    }

}

배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화 작업이 불가능.
이런 경우에 추가적으로 클래스 초기화 블럭을 사용할 것

 

 

 

 

 

멤버변수의 초기화 순서

  • 클래스 변수의 초기화 순서 
    • 기본값 → 명시적 초기화 → 클래스 초기화 블럭
    • 클래스 변수는 항상 인스턴스 변수보다 먼저 생성되고 초기화됨
  • 인스턴스 변수 초기화 순서
    • 기본값 → 명시적 초기화 → 인스턴스 초기화블럭 → 생성자

 

 

 

초기화 실행 순서

① cv가 메모리에 생성되고, cv에는 int형의 기본값인 0이 cv에 저장

② 명시적 초기화(int cv = 1)에 의해서 cv에 1이 저장

③ 클래스 초기화 블록(cv = 2)이 수행되어 cv에 2가 저장 

④ InitTest 클래스가 인스턴스에 생성되면서 iv가 메모리(heap)에 존재, iv 역시 int형 변수이므로 기본값 0이 저장

⑤ 명시적 초기화에 의해서 iv에 1이 저장

⑥ 인스턴스 초기화 블럭이 수행되어 iv에 2가 저장

⑦ 마지막으로 생성자가 수행되어 iv에 3이 저장

 

 

 

예제1

class Product{
    static int count = 0;  // 생성된 인스턴스의 수를 저장하기 위한 변수
    int serialNo;  // 인스턴스 고유의 번호
    
    {
        ++count;
        serialNo = count;
    }
    
    public Product(){}  // 기본 생성자, 생략 가능
}

class prac{
    public static void main(String[] args){
        Product p1 = new Product();
        Product p2 = new Product();
        Product p3 = new Product();
        
        System.out.printf("p1 %d%n", p1.serialNo);  // p1 1
        System.out.printf("p2 %d%n", p2.serialNo);  // p2 2
        System.out.printf("p3 %d%n", p3.serialNo);  // p3 3
        System.out.printf("count %d%n", Product.count);  // count 3
    }

}

만약에 count를 인스턴스 변수로 선언했다면, 인스턴스가 생성될때 마다 0으로 초기화 될 것임

 

 

 

 

예제2

class Document{
    static int count = 0;
    String name;  // 문서명

    Document(){
        this("제목없음" + ++count);
    }
    
    Document(String name){
        this.name = name;
        System.out.printf("문서 %s가 생성됨%n", this.name);
    }
}

class prac{
    public static void main(String[] args){
        Document d1 = new Document();  // 문서 제목없음1가 생성됨
        Document d2 = new Document();  // 문서 제목없음2가 생성됨
        Document d3 = new Document("자바.txt");  // 문서 자바.txt가 생성됨
        Document d4 = new Document();  // 문서 제목없음3가 생성됨
    }

}

 

 

 

 

 

 

728x90