수업 내용 정리

(5주차 31일) 접근 제한자, 생성자, 메소드, 정적 멤버, final

헨헨7 2024. 6. 11. 17:15

Java

접근 제한자

  • 자바에서 제공하는 접근 제한자는 public, protect, default, private가 있다. 
    1. public: 외부에서 자유롭게 접근 가능
    2. protected: 같은 패키지 또는 자식 클래스에서만 접근 가능
    3. default: 같은 패키지에 소속된 클래스에서만 접근 가능
    4. private: 외부에서의 접근을 제한하고 선언된 클래스에서만 접근 가능

  • 예시
package com.beyond.field;

import com.beyond.field.practice.User;

public class FieldApplication {
    public static void main(String[] args) {
        User user = new User();

        /**
         * 접근 제한자 테스트
         *  - public: 어디서든 필드에 직접 접근이 가능하다.
         *  - default: 같은 패키지 내에서만 직접 접근이 가능하다.
         *  - private: 클래스 내에서만 직접 접근이 가능하다.
         */

        user.id = "kim123"; //객체의 값 임의로 변경 가능하다.
        System.out.println(user.id);

        com.beyond.field.User user2 = new com.beyond.field.User();
        user2.name = "김철수";
        System.out.println(user2.name);
        //System.out.println(user.password);
    }
}

 

생성자(Constructor)

> 생성자

  • 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다.
    1. new 연산자로 호출되는 메소드이다.
    2. 모든 클래스는 생성자가 반드시 존재해야 하고 생성자를 하나 이상을 가질 수 있다.
    3. 생성자와 new 연산자에 의해 힙(Heap) 영역에 객체가 생성되고 생성된 객체의 주소가 리턴된다.

> 생성자 선언

  • 생성자 선언: [접근 제한자] 클래스명([매개변수]) { ... }
    1. 생성자 선언은 메소드와 다르게 반환값이 없으며, 생략 가능하다.
    2. 생성자명은 클래스명과 동일하게 지정하여야 한다.
    3. 클래스에 생성자 선언을 생략할 시 컴파일러가 내용이 비어있는 기본 생성자(Default Constructor)를 자동으로 생성한다.
      단, 클래스에서 생성자를 한 개라도 명시적으로 선언했다면 컴파일러는 기본 생성자를 추가하지 않는다.
    4. 생성자도 메소드이기 때문에 오버로딩이 가능하며 오버로딩의 조건은 메소드 오버로딩과 동일하다.
      생성자 오버로딩을 통해서 여러 개의 생성자를 만들고 객체 생성 시 필요한 생성자를 호출해서 객체를 만들 수 있다.
public class Member {
  // 필드 정의
  private String name;
  private int age;
  
  // 생성자 정의
  // 기본 생성자
  public Member() {
  }

  // 매개변수가 있는 생성자 (필드 초기화)
  public Member(String name, int age) {
    this.name = name;
    this.age = age;
  }
  
  // 메소드 정의
  public String information() {
    return "이름은 " + this.name + ", 나이는 " +  this.age + "살 입니다.";
  }
}

 

> this

  • 객체 내부에서 객체는 자신을 this라고 표현한다.
    객체 자기 자신을 참조하고 있는 참조변수이다. (같은 주소값을 가지고 있음)
  • 객체 내부에서 다른 멤버에 접근하기 위해 사용
  • this는 주로 생성자와 메소드의 매개변수 이름이 필드와 동일한 경우, 매개변수와 필드를 구분하기 위해 사용한다.
  • 예시

> 다른 생성자 호출

  • 생성자에서 다른 생성자를 호출할 때 this()를 사용한다.
    1. 단, 생성자의 첫 줄에서만 사용 가능하다.
    2. this()를 통해서 생성자 간에 중복되는 코드를 제거할 수 있다.
    3. 생성자 내에서만 사용 가능하다.
      e.g. 메소드 내부에서는 this()를 사용해서 생성자를 호출할 수 없다.
public class Member {
  private String name;    
  private int age;

  public Member() {
    // 생성자의 매개변수 타입과 순서에 맞게 전달해야 한다.
    this("아무개", 0);
  }

  public Member(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
  • 예시
    /**
     * 매개변수가 있는 생성자
     *  - 객체 생성과 동시에 전달된 값으로 필드를 초기화하는 목적으로 사용된다.
     */
    public User(String id, String password, String name) { // 생성자 오버로딩
        this.id = id;
        this.password = password;
        this.name = name;
    }
    public User(String id, String password, String name, int age, char gender) { // 생성자 오버로딩
        // 생성자에서 다른 생성자를 호출할 수 있다.
        // 클래스 내에서 문자열 매개변수 3개를 가지는 위 생성자를 호출하면서 전달받은 값으로 초기화한다.
        this(id, password, name); // 가장 첫 줄에 사용하여야 한다.
        this.age = age;
        this.gender = gender;
    }

 

메소드(Method)

> 메소드

  • 객체의 기능, 동작을 구현하는 것에 해당하는 코드 블록({})이다.
    1. 수학의 함수와 비슷하며, 호출을 통해 사용한다.
    2. 호출 시 중괄호 블록 내에 있는 코드들이 순차적으로 실행된다.
    3. 메소드 외부로부터 필요값을 전달받을 수 있고, 실행 후 결과값을 반환할 수도 있다.

> 메소드 선언

  • 메소드 선언: [접근 제한자] [예약어] 반환형 메소드명([매개변수]) { ... }
    1. 선언부: (리턴 타입, 메소드, 매개변수)와 실행 블록: ({})으로 구성
    2. 접근 제한자: public, private, default, protected 설정 가능
    3. 매개변수는 매소드가 실행될 때 필요한 데이터를 외부로부터 받기 위해 사용
    4. 매소드를 호출한 곳으로 돌아가면서 결과값을 반환하기 위해 return 문을 사용
public class Member {
  // 필드 정의
  private String name;
  private int age;
  
  // 생성자 정의
  ...
  
  // 메소드 정의
  public String information() {
    return "이름은 " + this.name + ", 나이는 " +  this.age + "살 입니다.";
  }
}
  • 예시

> 메소드 호출

  • 같은 클래스 내부의 다른 메소드에서 호출할 경우, 메소드 이름으로 호출 가능하다.
  • 같은 클래스가 아닌 외부에서 메소드를 호출하려면, 클래스로부터 객체를 생성한 후에 메소드 호출이 가능하다.
    1. 접근 제한자가 public인 경우, 도트(.) 연산자를 사용하여 메소드에 접근할 수 있다.
String info = null;
Member member = new Member();

// 도트(.) 연산자를 사용해서 메소드에 접근
info = member.information();
  • 메소드를 호출하고 리턴값을 받고 싶다면 변수를 선언하고 대입하면 된다.
  • 예시

> 메소드 오버로딩

  • 클래스 내에 같은 이름의 메소드를 여러 개 선언 가능하다.
    1. 매개값을 다양하게 받아서 필요한 처리를 할 수 있다.
    2. 조건: 매개변수의 타입, 개수, 순서 중 하나가 달라야 한다.
  • 참고: 매개변수 이름만 바꾸는 것은 메소드 오버로딩이 아니다.
    또한, 리턴 타입만 다르고 매개변수가 동일한 것도 메소드 오버로딩이 아니다.
  • 예시
// 오버로딩의 대표적인 예시
System.out.println(1);
System.out.println(false);
System.out.println('A');
System.out.println("Hello World!");
package com.beyond.method.practice;

public class Overloading {
    /**
     * 메소드 오버로딩
     *  - 매개변수의 타입, 개수, 순서 중 하나가 달라야 오버로딩이 성립한다.
     */
    public void test() {
    }
    public void test(int a) {
    }
    public void test(int a, String s) {
    }
    public void test(String s, int a) {
    }
    public void test(int a, int b) {
    }

    /**
     * 1. 매개변수의 이름이 다르다고 오버로딩이 적용되지는 않는다.
     *  - 매개변수의 이름과 상관없이 자료형의 개수와 순서가 같다면 에러가 발생한다. (이하 동일)
     * 2. 접근 제한자가 다르다고 오버로딩이 적용되지 않는다.
     * 3. 반환형이 다르다고 오버로딩이 적용되지 않는다.
     */
//    public void test(int c, int d) {
//    }
//    private void test(int a, int b) {
//    }
//    public int test(int e, int f) {
//    }
}

 

> Getter, Setter 메소드

  • 객체 지향 프로그래밍: 객체의 데이터는 객체 외부에서 직접적으로 접근하는 것이 불가능
    1. Getter: 메소드를 통해서 필드값을 가공한 후 외부로 전달하는 역할을 하는 메소드
    2. Setter: 메소드를 통해서 검증된 유효한 값만 데이터로 저장하는 역할을 하는 메소드
public class Member {
  // 필드 선언
  private String name;
  private int age;

  // Getter & Setter
  public void setName(String name) {
    // 매개변수의 이름과 필드의 이름이 동일할 경우 this를 사용해서 필드에 접근할 수 있다.
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  public void setAge(int age) {
    // 유효한 값만 데이터로 저장되도록 Setter 작성
    this.age = (age >= 1) ? age : 1 ;
  }

  public int getAge() {
    return this.age;
  }
}
  • 참고: 필드 타입이 boolean인 경우, Getter은 is로 시작하는 것이 관례(get으로 시작하지 않음)

정적(Static) 멤버

> 정적 멤버

  • 클래스에 고정된 멤버로, 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 의미한다.
    1. 정적 멤버는 인스턴스에 소속된 멤버가 아니라, 클래스에 소속된 멤버이기 때문에 클래스 멤버라고도 한다.

> 정적 멤버 선언

  • 필드와 메소드 선언 시 static 키워드를 붙여 선언한다.
    1. 필드를 선언할 때, 객체들이 공유할 목적의 데이터라면 정적 필드로 선언한다.
    2. 메소드를 선언할 때, 메소드 내부에서 정적 멤버를 사용하거나 필드를 사용하지 않는다면 정적 메소드로 선언한다.
// java.lang에서 제공하는 Math 클래스
public final class Math {
  public static int max(int a, int b) {
    return (a >= b) ? a : b;
  }

  public static int min(int a, int b) {
    return (a <= b) ? a : b;
  }
  ...
}

 

> 정적 멤버 사용

  • 객체를 생성하지 않고 클래스 이름과 도트(.) 연산자로 접근한다.
    1. 정적 메소드에서 객체의 필드나 메소드에 접근할 수 없다.
    2. this 키워드를 사용할 수 없다.
  • 정적 필드와 정적 메소드는 객체 참조 변수로도 접근이 가능하지만 정적 요소는 클래스 이름으로 접근하는 것이 좋다.
  • 예시(정적 필드)
package com.beyond.statics.practice;

public class StaticField {
    /**
     * static 필드(정적 필드)
     *  - 프로그램 실행과 동시에 메모리에 생성되고, 객체들이 공유하면서 사용할 목적으로 선언
     *  - 프로그램이 실행될 때 해당 클래스가 로드되면서 메모리에 생성되고,
     *    프로그램이 종료될 때 소멸된다.
     */
    public static int number = 2;

    public static String message = "StaticField에 선언된 정적 필드입니다.";

    // static 필드에 대한 Getter, Setter 메소드 또한 static 키워드가 붙어야 한다.
    public static String getMessage() {
        return message;
    }

    public static void setMessage(String message) {
        StaticField.message = message;
    }

}
  • 예시(정적 메소드)
package com.beyond.statics.practice;

public class StaticMethod {
    private static int num1 = 10;
    private static int num2 = 20;
    private int num3 = 30;
    /**
     * Static 메소드(정적 메소드)
     *  1. 매개변수와 반환값이 없는 정적 메소드
     *  2. 매개변수가 없고 반환값이 있는 정적 메소드
     *  3. 매개변수가 있고 반환값이 없는 정적 메소드
     *  4. 매개변수가 있고 반환값도 있는 정적 메소드
     */

    //1.
    public static void method1() {
        System.out.println(num1 + num2 ++);
        // 정적 메소드에서 필드에 접근할 수 없다.
        // 정적 메소드는 객체를 생성하지 않고 사용하기 때문에 필드에 접근 할 수 없다.
        // System.out.println(num3);
    }

    //2.
    public static int method2() {
        // 지역변수가 존재하는 경우, 지역변수 우선
        int num1 = 1;
        int num2 = 2;

        // 직접 경로 지정하여 static 필드로 리턴하도록 함
        return StaticMethod.num1 + StaticMethod.num2;
    }

    //3.
    public static void method3(int num3) {
        System.out.println(StaticMethod.method2() + num3);
    }

    //4.
    public static int method4(int... numbers) {
        int sum = 0;
        for (int i = 0; i < numbers.length; i++) {
            sum += numbers[i];
        }
        return sum;
    }
}

 

final 필드와 상수

> final 필드

  • final 필드는 초기값이 저장되면 이후 값을 변경할 수 없다.
    1. 필드를 선언할 때 명시적으로 지정하는 방법
    2. 생성자를 통해서 지정하는 방법
public class Member {
  public final String gender;

  // 생성자를 통해 final 필드 초기화
  public Member(String gender) {
    this.gender = gender;
  }
}
  • final 필드는 상수라고 하지 않는다.
    한 번 초기화되면 수정할 수 없는 필드지만, 객체마다 다른 값으로 초기화할 수 있다.

> 상수(static final)

  • 객체마다 저장되지 않고, 클래스에만 포함된다.
  • 한 번 초기값이 저장되면 변경할 수 없다.
  • static final 필드는 상수라고 한다.
    1. 상수의 이름은 대문자로 작성하는 것이 관례이다.
    2. 서로 다른 단어가 혼합된 경우, 언더바(_)로 구분한다.
// java.lang에서 제공하는 Math 클래스
public final class Math {
  public static final double PI = 3.14159265358979323846;
  private static final double DEGREES_TO_RADIANS = 0.017453292519943295;
  ...
}