Post

기강 자바-02





자바의 클래스

  • 자바에서 클래스란,
    • 자바 프로그램의 기본적인 구조를 이루는 요소이다.
    • 동시에 필드와 메서드를 가지는 참조형 타입을 정의하기도 한다.
  • 클래스의 기본 구성은
    • 멤버 변수
    • 메소드
    • 생성자 함수이다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
    private String name;                    // 필드 변수들
    private int age;

    public Person() {                       // 기본 생성자
    }

    public Person(String name, int age) {   // 모든 인자를 포함한 생성자
        this.name = name;
        this.age = age;
    }

    public void sayHello() {                // 메소드
        sout("Hello!!! " + name);
    }
}
  • 가장 기본적인 구조의 클래스이다.
    • 멤버 변수, 생성자 함수들, 멤버 메소드로 이뤄져있는 것을 볼 수 있다.
  • 위에 정의된 생성자 함수란,
    • 해당 클래스 타입의 인스턴스를 만들 때 사용하는 메소드를 말한다.
    • 다른 멤버 메소드와 달리, 메소드 명을 명시하지 않는다.
  • 기본 생성자의 경우 아무 생성자 함수도 정의해주지 않을 경우, 컴파일 시점에 자동으로 생성된다.
    • 기본 생성자란, 아무 인자도 받지 않는 빈 인스턴스를 생성하는 생성자 함수를 말한다.
    • 위 예시의 경우 name과 age를 받는 생성자 함수를 정의해줬기 때문에 따로 기본 생성자를 정의해주지 않으면 사용할 수 없다.





객체 만들기

  • 객체를 만드는 방법은, 참조형 타입의 변수를 정의한 후 인스턴스를 생성하여 초기화하는 것이 가장 기본적인 방법이다.
    • 인스턴스를 만드는 방법은 new 키워드를 사용해 생성자 함수를 호출하는 것이다.
  • 생성자 함수의 동작 방식은 다음과 같다.
    1. new 키워드와 함께 참조형 타입에 정의된 생성자 함수를 호출한다.
    2. 생성자 함수가 호출되면, 객체를 생성한다. (JVM의 힙 메모리에 할당된다.)
    3. 그 다음 생성자 함수는 객체의 초기화 작업을 수행한다. (필드 변수를 초기화한다.)
    4. 생성자 함수가 완료되어 객체를 반환한다.
    5. 반환된 객체는 객체를 참조할 수 있는 변수에 저장된다. (변수에는 객체의 참조값이 할당되며, 해당 변수는 JVM의 스택 영역에 저장된다.)
1
2
3
4
5
6
7
8
class Main {
    public static void main(String[] args) {
        Person person1 = new Person();              // 기본 생성자를 사용
        Person person2 = new Person("babo", 20);    // all args 생성자를 사용

        person2.sayHello();                         // Hello!!! babo
    }
}
  • 인스턴스를 생성하여 참조할 수 있는 타입의 변수에 할당해준다.
    • 변수를 통해 객체에 정의된 메소드와 필드들을 사용할 수 있다.



생성자 함수 정의하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Person {
    private String name;
    private int age;
    private String email;

    public Person() {                           // 기본 생성자
    }

    public Person(String name) {                // name만 받는 생성자
        this.name = name;
    }

    /*
    public Person(String email) {               // 컴파일 에러
        this.email = email;
    }
     */

    public Person(int age) {                    // age만 받는 생성자
        this.age = age;
    }

    public Person(String name, int age, String email) {       // 모든 인자를 다 받는 생성자
        this.name = name;
        this.age = age;
        this.email = email;
    }
}
  • 일반 멤버 메소드와 다르게 메소드 명을 기입하지 않는 것을 알 수 있다.
  • 중간에 주석 처리된 email만 받는 생성자는 주석 처리를 지울 경우 컴파일 에러가 발생한다.
    • 자바에서 같은 클래스 내에 똑같은 메소드 시그니처를 갖는 메소드가 2개 이상 존재할 수 없다.
    • 메소드 시그니처는 메소드 이름과 매개변수의 개수, 타입 및 순서로 결정된다.
    • 생성자 함수도 마찬가지인데,
      • name만 갖는 생성자와 email만 갖는 생성자는 같은 메소드 시그니처를 갖기 때문에 동시에 만들 순 없다.



1
2
3
4
5
6
7
8
9
class Babo {                        // 아무 생성자 함수도 정의하지 않은 클래스
}

class Main {
    public static void main(String[] args) {
        Babo = new Babo();          // 컴파일 시점에 기본 생성자가 생성되어 정의하지 않아도 사용할 수 있다.
    }
}

  • 위에서 설명했듯이 아무 생성자 함수도 정의하지 않은 클래스는 기본적으로 기본 생성자를 갖는다.





메소드 정의하기

  • 위에서 언급했듯이 클래스는 메소드를 가질 수 있다.
  • 메소드는 다음과 같이 이뤄져 있다.
    • 접근 제한자
    • 리턴 타입
    • 메서드 명
    • 파라미터
    • 메소드 블럭
  • 위의 생성자 함수 부분에서 언급했듯이, 한 클래스 내에 같은 메소드 시그니처를 갖는 메소드가 두 개 이상 존재할 수 없다.



1
2
3
4
5
6
7
8
9
10
11
12
13
class Babo {
    public void sayName(String name) {
        System.out.println("My name is " + name);
    }
}

class Main {
    public static void main(String[] args) {
        Babo babo = new Babo();

        babo.sayName();             // 멤버 메소드의 사용
    }
}
  • 위의 Babo 클래스의 sayName()의 경우 다음과 같이 이뤄져 있다.
    • 접근 제한자 : public
    • 리턴 타입 : void
    • 메서드 명 : sayName
    • 파라미터 : String name
    • 메소드 블럭 : { ~~~ }
  • Babo 타입의 객체를 만들어 멤버 메소드를 사용할 수 있다.





오버로딩

  • 한 클래스 내에서 같은 메서드를 여러 개 구현할 수 있는 것
    • 이를 오버로딩이라고 한다.
    • Overloading


  • 오버로딩의 조건은 아래와 같다.
    • 메서드 이름이 동일하다.
    • 파라미터 구성이 다르다.
    • 리턴 타입은 영향을 주지 않는다.



코드로 짜보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyClass {
    // 생략..


    public void method() {
        // ...
    }

    public void method(Object p1) {
        // ...
    }

    public void method(Object p1, Object p2) {
      // ...
    }
}

  • 위의 method()처럼 하나의 메서드를 파라미터 구성에 따라 여러 개 구성할 수 있다.
    • 같은 리턴 타입, 같은 메서드 명 그러나 다른 파라미터를 가지는 메서드를 구현하는 것을 오버로딩이라 한다.
    • 생성자 함수를 파라미터에 따라 여러 개 구현할 수 있는 것도 오버로딩이라고 볼 수 있다.



과제

  • 오버로딩을 코드로 짜보며 체득하세요.
    • 메서드 이름
    • 파라미터 구성
    • 리턴 타입





접근 제한자

  • 접근 제한자란,
    • 클래스, 변수, 메소드 등에 대한 접근 권한을 지정하는 키워드이다.
  • 접근 제한자는 4가지 종류가 있다.
    • public : 모든 클래스에서 접근 가능
    • protected : 같은 패키지 or 상속 받은 자식 클래스 내에서만 접근 가능
    • default(package-private) : 같은 패키지 내에서만 접근 가능
    • private : 해당 클래스 내에서만 접근 가능



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 여기는 java.babo 패키지

public class Babo {
    public int publicInt;
    protected int protectedInt;
    int defaultInt;
    private int privateInt;
}

class BaboChild1 extends Babo {
    public void hello() {
        sout(super.publicInt);          // O    모든 클래스 접근 가능
        sout(super.protectedInt);       // O    자식 클래스 or 같은 패키지라 접근 가능
        sout(super.defaultInt);         // O    같은 패키지라 접근 가능

        sout(super.privateInt);         // X    같은 클래스가 아니라 접근 불가능
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 여기는 java.boba 패키지

class Boba {
    private Babo babo;

    // constructor

    public void hello() {
        sout(babo.publicInt);          // O    모든 클래스 접근 가능

        sout(babo.protectedInt);       // X    자식 클래스 or 같은 패키지 아니라 접근 불가능
        sout(babo.defaultInt);         // X    같은 패키지 아니라 접근 불가능
        sout(babo.privateInt);         // X    같은 클래스가 아니라 접근 불가능
    }
}

class BaboChild2 extends Babo {
    public void hello() {
        sout(super.publicInt);          // O    모든 클래스 접근 가능
        sout(super.protectedInt);       // O    자식 클래스 or 같은 패키지라 접근 가능

        sout(super.defaultInt);         // X    다른 패키지라 접근 불가능
        sout(super.privateInt);         // X    같은 클래스가 아니라 접근 불가능
    }
}
  • 위의 코드 블럭과 아래의 코드 블럭은 서로 다른 패키지이다.
    • 접근 제한자에 따라서 접근할 수 있는 것과 없는 것을 볼 수 있다.
    • 얘시에는 멤버 변수만을 나타냈지만, 메소드의 접근 제한자 역시 똑같이 동작한다.





this 키워드

  • 생성자 함수를 만들 때 ‘this.name = name’에서 처럼 this라는 키워드를 봤을 것이다.
  • this란 자기 자신의 객체를 가리키는 키워드이다.



1
2
3
4
5
6
7
8
9
10
11
12
class Person {
    private String name;
    private int age;

    public Person(String name) {            // this가 붙지 않은 name은 매개변수로 받아온 애를 가르킨다.
        this.name = name;                   // this가 붙은 name은 자기 자신 인스턴스의 name 즉, 해당 객체의 멤버 필드인 name을 가르킨다.
    }

    public void sayName(String name) {
        System.out.printf("%s is my name, not %s", this.name, name);    // 해당 객체의 멤버 변수인 name이 맘마,
    }                                                                   // 매개변수로 들어온 name이 밤바일 때
}                                                                       // sayName()을 호출하면 콘솔에 어떤 문장이 찍힐까?
  • 예시의 정답은
    • 맘마 is my name, not 밤바



this()

  • this 외에 this() 메소드도 존재한다.
  • 해당 메소드는 자기 자신의 생성자 함수를 가르킨다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person {
    private String name;
    private int age;
    private Gender gender;

    // 1
    public Person(String name) {
        this.name = name;
    }

    // 2
    public Person(String name, int age) {
        this(name);                                         // 이 경우 1번 생성자 함수를 가르킴
        this.age = age;
    }

    // 3
    public Person(String name, int age, Gender gender) {
        this(name, age);                                    // 이 경우 2번 생성자 함수를 가르킴
        this.gender = gender;
    }
}
  • this()는 같은 타입의 다른 생성자 함수를 나타내는 키워드이다.
  • 사용하는 메소드의 인자의 개수, 타입 및 순서를 통해 생성자 함수를 추정하고 호출한다.





내부 클래스

  • 내부 클래스란,
    • 클래스 내부에 정의된 클래스를 말한다.
    • 내부 클래스는 외부의 클래스에 쉽게 접근할 수 있다.
  • 내부 클래스는 크게 4가지가 있다.
    • 인스턴스 내부 클래스
    • 지역 내부 클래스
    • 정적 내부 클래스
    • 익명 내부 클래스
  • 이번엔 인스턴스 내부 클래스만 알아본다.



인스턴스 내부 클래스

1
2
3
4
5
6
7
8
9
10
11
12
public class OuterClass {
    private int outVal = 100;

    public class InnerClass {
        private int inVal = 200;

        public void printVals() {
            sout("outVal= " + outVal);
            sout("inVal= " + inVal);
        }
    }
}
  • 이렇게 인스턴스 내부 클래스를 정의할 수 있다.
    • 외부 클래스 OuterClass와
    • 인스턴스 내부 클래스 InnerClass를 볼 수 있다.
      • InnerClass는 외부 클래스의 멤버 필드가 private이어도 자유롭게 접근할 수 있는 것을 볼 수 있다.
      • 단, this를 사용해 외부 클래스의 멤버 변수에 접근할 수는 없다. (둘은 서로 다른 인스턴스이기 때문에~)



1
2
3
4
5
6
class Main {
    public static void main(String[] args) {
        OuterClass o = new OuterClass();        // 외부 클래스 생성
        InnerClass i = o.new InnerClass();      // 외부 클래스의 인스턴스가 생성되어야 내부 클래스의 인스턴스 생성 가능.
    }
}
  • 인스턴스 내부 클래스는 외부 클래스의 인스턴스가 꼭 생성되어 있어야 한다.
    • 메모리 구조상 인스턴스 내부 클래스의 객체는 외부 클래스의 인스턴스를 참조하는 포인터를 갖고 있는데,
    • 이 포인터를 통해 외부 클래스의 변수 등에 접근이 가능한 것이다.
  • 인스턴스 내부 클래스의 객체는 외부 클래스의 객체가 소멸될 때 같이 소멸된다.
    • 외부 클래스의 인스턴스가 생성되었다고, 내부 클래스의 인스턴스가 꼭 같이 생성되는 건 아니지만
    • 외부 클래스의 인스턴스가 소멸된다면, 내부 클래스의 인스턴스 역시 소멸된다.
    • 메모리 사용 측면에서 효율적이다~



과제

  • 인스턴스 내부 클래스를 정의하고, 사용해 보세요.
    • 내부 클래스에서 외부 클래스의 변수와 메소드에 접근하여 사용해 보세요.





과제

  • 자바의 연산자들에 대해 알아보세요.
    • 산술 연산자
    • 관계 연산자
    • 논리 연산자
    • 비트 연산자
    • 할당 연산자
    • 삼항 연산자
  • 자바의 연산자들의 우선 순위에 대해 알아보세요.





This post is licensed under CC BY 4.0 by the author.