콘텐츠로 이동

클래스와 객체 (2)

1. 메소드, 가비지

1) 메소드

  • 메소드는 C/C++의 함수와 동일
  • 자바의 모든 메소드는 반드시 클래스 안에 있어야 함(캡슐화 원칙)

public int getSumb(int i, int j) {
  int sum;
  sum = i + j;
  return sum;
}
접근 지정자

  • 다른클래스에서메소드를접근할수있는지여부선언
  • public, private, protected, 디폴트(접근 지정자 생략)

리턴 타입

  • 메소드가 리턴하는 값의 데이터 타입

2) 인자 전달__기본 타입의 값이 전달되는 경우

매개 변수가 byte, int, double 등 기본 타입으로 선언되었을 때, 호출자가 건네는 값이 매개 변수에 복사되어 전달. 실인자 값은 변경되지 않음

public class CallByValue {
  public static void main(String args[]) {
    int n = 10;

    increase(n); // 호출

    System.out.println(n);
  }

  static void increase(int m) {
    m = m + 1;
  }
}

실행결과

10

3) 인자 전달__객체가 전달되는 경우

매개 변수가 실인자 객체 공유

public class ReferencePassing {
  public static void main (String args[]) {
    Circle pizza = new Circle(10);

    increase(pizza); // 호출

    System.out.println(pizza.radius);
  }

  static vodi increase(Circle m) {
    m.radius++;
  }
}

실행결과: 11

4) 인자 전달__배열이 전달되는 경우

  • 배열 레퍼런스만 매개 변수에 전달 : 배열 통째로 전달되지 않음
  • 객체가 전달되는 경우와 동일 : 매개 변수가 실인자의 배열 공유
  public class ArrayPassing {
    public static void main(String args[]) {
      int a[] = {1, 2, 3, 4, 5};

      increase(a);

      for (int i = 0; i < a.length; i++)
        System.out.print(a[i] + " ")
    }

    static void increase(int[] array) {
      for( int i = 0; i < array.length; i++)
        array[i]++;
    }
  }
}

실행결과

2 3 4 5 6

[예제 4-8] 인자로 배열이 전달
public class ArrayParameter {
  static void replaceSpace(char a[]) {
    for (int i=0; i<a.length; i++)
      if (a[i] == ' ')
        a[i] = ','; 
    }
  static void printCharArray(char a[]) { 
    for (int i=0; i<a.length; i++)
      System.out.print(a[i]); 
    System.out.println();
  }

  public static void main (String args[]) {
    char c[] = {'T','h','i','s',' ','i','s',' ','a',' ','p','e','n','c','i','l','.'}; 
    printCharArray(c);
    replaceSpace(c);
    printCharArray(c);
  }
}

실행결과

This is a pencil.
This,si,a,pencil.

5) 메소드 오버로딩 (Overloading)

한 클래스 내에서 두 개 이상의 이름이 같은 메소드 작성

  • 메소드 이름이 동일하여야 함
  • 매개변수의개수가서로다르거나,타입이서로달라야함
  • 리턴 타입은 오버로딩과 관련 없음

6) 오버로딩과 메소드 호출

매개변수의 개수와 타입이 서로 다른 3 함수 호출

public static void main(String args[]) {

  MethodSmaple a = new MethodSample();

  int i = a.getSum(1, 2);
  int j = a.getSum(1, 2, 3);
  int k = a.getSum(1.1, 2.2);
}

public class MethodSample {
  public int getSum(int, i, int j) {
    return i + j;
  }

  public int getSum(int, i, int j, int k) {
    return i + j + k;
  }

  public double getSum(double, i, double j) {
    return i + j;
  }
}

오류

class MethodOverloadingFail { // 메소드 오버로딩이 실패한 사례

  public int getSum(int i, int j) {
    return i + j;
  }

  public double getSum(int i, int j) {
    return (double)(i + j);
  }
}

7) 객체 치환 시 주의할 점

객체 치환은 객체 복사가 아니며, 레퍼런스의 복사이다.

public class Samp {
  int id;
  public Samp(int x) { this.id = x; }
  public void set(int x) { this.id = x; }
  public int get() { return this.id; }

  public static void main(String [] args) {
    Smap ob1 - new Samp(3);
    Smap ob2 - new Samp(4);
    Samp s;

    s = ob2;
    ob1 = ob2; // 객체의 치환
    System.out.println("ob1.id=" + ob1.get());
    System.out.println("ob2.id=" + ob1.get());
  }
}

실행결과

ob1.id=4
ob2.id=4

8) 객체 소멸

  • new에 의해 할당 받은 객체와 배열 메모리를 자바 가상 기계로 되돌려 주는 행위 소멸된 객체 공간은 가용 메모리에 포함
  • 자바에서 사용자 임의로 객체 소멸 안됨
  • 자바는 객체 소멸 연산자 없음 (객체 생성 연산자 : new)
  • 객체 소멸은 자바 가상 기계의 고유한 역할
  • 자바 개발자에게는 매우 다행스러운 기능
    • C/C++에서는 할당 받은 객체를 개발자가 프로그램 내에서 삭제해야 함
    • C/C++의 프로그램 작성을 어렵게 만드는 요인
    • 자바에서는 사용하지 않는 객체나 배열을 돌려주는 코딩 책임으로부터 개발자 해방

9) 가비지

  • 가리키는 레퍼런스가 하나도 없는 객체
    • 더이상접근할수없어사용할수없게된메모리
  • 가비지 켈렉션
  • 자바 가상 기계의 가비지 컬렉터가 자동으로 가비지 수집, 반환
[예제 4-9] 가비지의 발생
public class GarbageEx {
  public static void main(Stringp[] args) {
    String a = new String("Good");
    String b = new String("Bad");
    String c = new String("Normal");
    String d, e;
    a = null;
    d = c;
    c = null;
  }
}

객체만 생성된 경우 (6라인까지) : a,b,c 에 문자열 할당되었음
코드 전체 실행후 a를 nil로 선언하여 "Good"은 가비지가 되었음
c는 null 이 되었지만 d가 있기때문에 Normal은 가비지가 되지 않는다.

9) 가비지 컬렉션

  • 자바 가상 기계가 가비지 자동 회수
    • 가용메모리공간이일정이하로부족해질때
    • 가비지를 수거하여 가용 메모리 공간으로 확보
  • 가비지 컬렉터(garbage collector)에 의해 자동 수행
  • 강제 가비지 컬렉션 강제 수행
    • System 또는 Runtime 객체의 gc() 메소드 호출
      System.gc(); // 가비지 컬렉션 작동 요청
      
    • 이코드는자바가상기계에강력한가비지컬렉션요청
    • 그러나자바가상기계가가비지컬렉션시점을전적으로판단

2. 접근 지정자

1) 접근지정자

  • private, protected, public, 디폴트(접근 지정자 생략)
  • 접근 지정자의 목적
    • 클래스나 일부 멤버를 공개하여 다른 클래스에서 접근하도록 허용
    • 객체지향언어의캡슐화정책은멤버를보호하는것
    • 접근지정은캡슐화에묶인보호를일부해제할목적
  • 접근 지정자에 따른 클래스나 멤버의 공개 범위
    패키지 : 상호 관련 있는 클래스 파일(컴파일된 .class)을 저장하여 관리하는 디렉터리
    

2) 클래스의 접근 지정

다른 클래스에서 사용하도록 허용할 지 지정

public 클래스

  • 다른 모든 클래스에게 접근 허용
  • 디폴트 클래스(접근 지정자 생략)
  • package-private라고도 함
  • 같은 패키지의 클래스에만 접근 허용
public class World { // public 클래스 ............
}

class Local { // 디폴트 클래스 ............
}

3) 멤버 접근 지정

  • public 멤버 : 패키지에 관계 없이 모든 클래스에게 접근 허용
  • private 멤버 : 동일 클래스 내에만 접근 허용, 상속 받은 서브 클래스에서 접 근 불가
  • protected 멤버 : 같은 패키지 내의 다른 모든 클래스에게 접근 허용, 상속 받은 서브 클래스는 다른 패키지에 있어도 접근 가능
  • 디폴트(default) 멤버 : 같은 패키지 내의 다른 클래스에게 접근 허용
멤버에 접근하는 클래스 멤버의 접근 지정자
private 디폴트 접근 지정 protected public
같은 패키지의 클래스 X O O O
다른 패키지의 클래스 X X X O
접근 가능영역 클래스 내 동일 패키지 내 동일 패키지와
자식 클래스
모든 클래스
멤버의 접근 지정자 [예제 4-10]
class SampleClass { 
  public int field1; 
  protected int field2; 
  int field3;
  private int field4; 
}

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

  SampleClass s = new SampleClass(); s.field1 = 0;
  s.field2 = 1;
  s.field3 = 2;
  s.field4 = 3; //field4는 SampleClass의 private 멤버이므로 SampleClass 외의 다른 클래스에서 접근할 수 없다.
  } 
}

Failure

Exception in thread "main" java.lang.Error: Unresolved compilation problem: The field s.field4 is not visible at AccessEx.main(AccessEx.java:14)

3. static 멤버, final 키워드

1) static 멤버

static 멤버 선언

class StaticSample { 
  int n;                // non-static 필드
  void g() {...}        // non-static 메소드

  static int m;         // static 필드
  static void f() {...} // static 메소드
}

객체 생성과 non-static 멤버의 생성

  • non-static 멤버는 객체가 생성될 때, 객체마다 생긴다.

2) static 멤버의 생성

  • static 멤버는 클래스당 하나만 생성
  • 객체들에 의해 공유됨
class StaticSample{

  int n;
  void g() {...}
  static int m;
  static void f() {...}
}

n, f() 는 객체들에 의해 공유되는 static 멤버

3) static 멤버와 non-static 멤버 특성 정리

non-static 멤버 static 멤버
공간적 특성 멤버는 객체마다 별도 존재
- 인스턴스 멤버라고 부름
멤버는 클라스당 하나 생성
- 멤버는 객체 내부가 아닌 별도의 공간에 생성
- 클래스 멤버라고 부름
시간적 특성 객체 생서 시에 멤버 생성됨
- 객체가 생길 때 멤버도 생성
- 객체 생성 후 멤버 사용 가능
- 객체가 사라지면 멤버도 사라짐
클래스 로딩 시에 멤버 생성
- 객체가 생기기 전에 이미 생성
- 객체가 생기기 전에도 사용 가능
- 객체 가 사라져도 멤버는 사라지지 않음
- 멤버는 프로그램이 종료될 때 사라짐
공유의 특성 공유되지 않음
- 멤버는 객체 내에 각각 공간 유지
동일한 클래스이 모든 객체들에 의해 공유됨

4. static 멤버 사용

클래스 이름으로 접근 가능

StaticSample.m = 3; // 클래스 이름으로 static 필드 접근 
StaticSample.f(); // 클래스 이름으로 static 메소드 호출

객체의 멤버로 접근 가능

StaticSample b1 = new StaticSample();
b1.m = 3; // 객체 이름으로 static 필드 접근 
b1.f(); // 객체 이름으로 static 메소드 호출

non-static 멤버는 클래스 이름으로 접근 안 됨

StaticSample.n = 5; // n은 non-static이므로 컴파일 오류 
StaticSample.g(); // g()는 non-static이므로 컴파일 오류

5) static의 활용

  • 전역 변수와 전역 함수를 만들 때 활용
  • 공유 멤버를 만들고자 할 때
    • static으로 선언한 멤버는 클래스의 객체들 사이에 공유

6) static 메소드의 제약 조건

static 메소드는 오직 static 멤버만 접근 가능

  • 객체가 생성되지 않은 상황에서도 static 메소드는 실행될 수 있기 때문에, non-static 멤버 활용 불가
  • non-static 메소드는 static 멤버 사용 가능
class StaticMethod {
  int n;
  void f1(int x) { n = x; } // 정상
  void f2(int x) { m = x;}  // 정상

  statid int m;

  static void s1(int x) { n = x; } // 컴파일 오류. static 메소드는 non-static 필드 사용 불가

  static void s2(int x) { f1(3); } // 컴파일 오류. static 메소드는 non-static 메소드 사용 불가.

  static void s3(int x) { m = x; } // 정상. static 메소드는 static 필드 사용가능

  static void s4(int x) { s3(3); } // 정상. static 메소드는 static 메소드 호출 가능

}

static 메소드는 this 사용불가

  • static 메소드는 객체 없이도 사용 가능하므로, this 레퍼런스 사용할 수 없음
static void f() { this.n = x; } // 오류. static 메소드에서는 this 사용 불가능
stativ ovid g() { this.m = x; } // 오류. static 메소드에서는 this 사용 불가능 

7) final 클래스와 메소드

final 클래스 - 더 이상 클래스 상속 불가능

final class FinalClass { 
  .....
}
class DerivedClass extends FinalClass { // 컴파일 오류
..... 
}

final 메소드 - 더 이상 오버라이딩 불가능

public class SuperClass {
  protected final int finalMethod() {
   ... 
  }
}

class SubClass extends SuperClass {
  protected int finalMethod() { ... } // 컴파일 오류, 오버라이딩 할 수 없음
}

3) final 필드

final 필드, 상수 선언 : 상수를 선언할 때 사용

class SharedClass {
  public static final double PI = 3.14;
}

상수 필드는 선언 시에 초기 값을 지정하여야 한다.   
상수 필드는 실행 중에 값을 변경할  없다.

```java
public class FinalFieldClass {
  final int ROWS = 10; // 상수 정의, 이때 초기 값(10)을 반드시 설정
  void f() {
    int [] intArray = new int [ROWS]; // 상수 활용
    ROWS = 30; // 컴파일 오류 발생, final 필드 값을 변경할 수 없다.
  }
}