JAVA

[JAVA] 객체의 속성(필드),행위(메서드)

개발 공주 2023. 5. 30. 00:21
728x90

객체의 속성 : 필드

필드란?

📌 필드는 객체의 데이터를 저장하는 역할을 한다.

  • 객체의 필드는 크게 고유한 데이터, 상태 데이터, 객체 데이터로 분류할 수 있다.
  • 이처럼 자동차 객체는 4개의 고유한 데이터와 3개의 상태 데이터 그리고 3개의 객체 데이터를 가질 수 있다.
    •  소프트웨어의 부품을 객체라 표현한다.
    • 이 3개의 객체 데이터를 자동차를 만들기 위한 부품 데이터라고 이해해도 좋다.

필드의 초기값과 초기화

📌 우리가 정의하여 선언한 클래스의 필드들은 기본적으로 초기값을 제공하지 않을 경우 객체가 생성될 때 자동으로 기본값으로 초기화된다.

  • 초기값을 제공하는 방법은 ‘필드타입 필드명 = 값;’ 이렇게 직접 초기화 할 수 있다.
    • String model = "Gv80";

필드 타입별 기본값

필드의 사용 방법

📌 ‘필드를 사용한다’ 라는 의미는 필드의 값을 변경하거나 읽는 것을 의미한다.

  • 우리가 클래스에 필드를 정의하여 선언했다고 해서 바로 사용할 수 있는 것은 아니다.
  • 클래스는 설계도일 뿐 실제로 필드의 데이터를 가지고 있는 것은 객체이다.
  • 따라서 객체를 생성한 후에 필드를 사용할 수 있다.

1. 외부 접근

  • Car car = new Car();
    • 이렇게 객체를 생성했다면 우리는 참조변수 car를 이용하여 외부에서 객체 내부의 필드에 접근하여 사용할 수 있다.
    • 이때 객체의 내부 필드에 접근하는 방법은 도트(.) 연산자를 사용하면 된다.
      • car.color = "blue";

2. 내부 접근

  • 도트 연산자를 사용하여 외부에서 객체 내부에 접근할 수 있을 뿐만 아니라 객체 내부 메서드에서도 내부 필드에 접근할 수 있다.
double brakePedal() {
    speed = 0;
    return speed;
}

이처럼 brakePedal() 메서드 내부에서 객체의 필드 speed를 바로 호출해서 사용할 수 있습니다.


5. 객체의 행위 : 메서드

📌 메서드는 객체의 행위를 뜻하며 객체간의 협력을 위해 사용된다.

  • 메서드의 행위를 정의하는 방법은 블록{ } 내부에 실행할 행위를 정의하면된다.

메서드 선언

리턴타입 메서드명(매개변수, ...) {
			 실행할 코드 작성
}

리턴 타입

double brakePedal() {...} // double 타입 반환
char changeGear(char type) {...} // char 타입 반환
boolean onOffLights() {...} // boolean 타입 반환
void horn() {...} // 반환할 값 없음

리턴 타입이란 메서드가 실행된 후 호출을 한 곳으로 값을 반환할 때 해당 값의 타입을 의미.

  • return 리턴타입의 반환값;
  • 주의할 점은 메서드에 리턴타입을 선언하여 반환할 값이 있다면 반드시 return 문으로 해당하는 리턴타입의 반환값을 지정해야 한다.

반환할 값이 없을떄는 리턴타입에 void를 작성해야 한다.

  • 반환값이 없음으로 return문을 반드시 지정할 필요는 없다.
  • 메서드는 실행할 때 return문을 만나면 그대로 종료하게 되는데 void 타입일 때 return; 이렇게 return문을 사용하여 원하는 지점에서 메서드를 종료할 수도 있다.

매개 변수

double gasPedal(double kmh, char type) {
    speed = kmh;
    return speed;
}

매개변수는 메서드를 호출할 때 메서드로 전달하려는 값을 받기 위해 사용되는 변수다.

  • 매개변수는 메서드를 호출할 때 메서드로 전달하려는 값을 받기 위해 사용되는 변수입니다.
  • 위 gasPedal(double kmh, char type) 메서드의 매개변수는 double 타입의 kmh, char 타입의 type 입니다.
    • 해당 매개변수에 값을 전달하기 위해서는 순서와 타입에 맞춰 값을 넣어주면 된다.
    • gasPedal(100, 'D');
  • 전달하려는 값이 없다면 생략 가능하다.
  • 가변길이의 매개변수도 선언할 수 있다.
    • double … speeds 이렇게 … 을 사용하면 아래처럼 매개값을 , 로 구분하여 개수 상관없이 전달 가능하다.
    • carSpeeds(100, 80);
    • carSpeeds(110, 120, 150);
void carSpeeds(double ... speeds) {
    for (double v : speeds) {
        System.out.println("v = " + v);
    }
}

메서드 호출 방법

📌 ‘메서드를 호출한다’ 라는 의미는 메서드의 블록 내부에 작성된 코드를 실행한다는 의미다.

  • 필드와 마찬가지로 클래스의 메서드를 정의하여 선언했다고 해서 바로 사용할 수 있는 것은 아니다.
  • 클래스는 설계도일 뿐 메서드는 객체의 행위를 정의한 것이다.
  • 따라서 객체를 생성한 후에 메서드를 사용할 수 있다.
  • 외부 접근
    • Car car = new Car();
      • 이렇게 객체를 생성했다면 우리는 참조변수 car를 이용하여 외부에서 객체 내부의 메서드에 접근하여 호출할 수 있습니다.
      • 이때 객체의 내부 메서드에 접근하는 방법은 도트(.) 연산자를 사용하면 됩니다.
        • car.brakePedal();
      • 또한 메서드가 매개변수를 가지고 있다면 반드시 호출할 때 매개변수의 순서와 타입에 맞게 매개값을 넣어줘야 합니다.
        • car.gasPedal(100, 'D');
  • 내부 접근
    • 도트 연산자를 사용하여 외부에서 객체 내부에 접근할 수 있을 뿐만 아니라 객체 내부 메서드에서도 내부 메서드에 접근하여 호출할 수 있습니다.
double gasPedal(double kmh, char type) {
    changeGear(type);
    speed = kmh;
    return speed;
}
//이처럼 gasPedal(double kmh, char type) 메서드 내부에서 해당 객체의 changeGear(type); 메서드를 호출할 수 있습니다.
  • 반환 값 저장
    • 메서드의 리턴타입을 선언하여 반환할 값이 있다면 변수를 사용하여 받아줄 수 있습니다.
      • 반드시 리턴타입과 변수의 타입이 동일하거나 자동 타입 변환될 수 있어야합니다.
      • double speed = car.gasPedal(100, 'D');
      • double 타입의 변수 speed를 사용하여 double gasPedal(double kmh, char type) 메서드의 double 타입의 반환값을 받아 저장할 수 있습니다.

예제

자동차 클래스

public class Car {

    String company; // 자동차 회사
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격

    double speed;  // 자동차 속도 , km/h
    char gear = 'P'; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태

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

    double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }

    double brakePedal() {
        speed = 0;
        return speed;
    }

    char changeGear(char type) {
        gear = type;
        return gear;
    }

    boolean onOffLights() {
        lights = !lights;
        return lights;
    }

		void horn() {
		    System.out.println("빵빵");
		}

    void carSpeeds(double ... speeds) {
        for (double v : speeds) {
            System.out.println("v = " + v);
        }
    }
}

Main 클래스

public class Main {
    public static void main(String[] args) {
        Car car = new Car(); // 객체 생성

        // 메서드 호출 및 반환값 저장
        double speed = car.gasPedal(100, 'D');
        System.out.println("speed = " + speed);

        boolean lights = car.onOffLights();
        System.out.println("lights = " + lights);

        System.out.println();
        // gasPedal 메서드 내부에 호출된 changeGear(type); 메서드의 결과 확인
        // gear의 초기값은 'P'
        System.out.println("car.gear = " + car.gear); // 'D' 출력

        System.out.println();
        // 가변길이 매개변수 확인
        car.carSpeeds(100, 80);
        System.out.println();
        car.carSpeeds(110, 120, 150);
    }
}

메서드 오버로딩

📌 오버로딩 은 함수가 하나의 기능만을 구현하는것이 아니라 하나의 메서드 이름으로 여러 기능을 구현하도록 하는 Java의 기능입니다.

즉, 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도, 매개변수의 개수 또는 타입, 순서가 다르면 동일한 이름을 사용해서 메서드를 정의할 수 있습니다.

 

  • 오버로딩의 조건
    • 메서드의 이름이 같고, 매개변수의 개수, 타입, 순서가 달라야 합니다.
    • '응답 값만' 다른 것은 오버로딩을 할 수 없습니다.
    • 접근 제어자만 다른 것도 오버로딩을 할 수 없습니다.
    • 결론, 오버로딩은 매개변수의 차이로만 구현할 수 있습니다.
  • 오버로딩의 장점
    1. 메서드 이름 하나로 상황에 따른 동작을 개별로 정의할 수 있습니다.
      1. 예를들면 메세지 출력할때 쓰는 println() 이 있습니다.
      2. println() 의 매개변수로는 int, double, String, boolean 등 다양하게 넣을 수 있습니다.
    2. 메서드의 이름을 절약할 수 있습니다.
      1. 만약 오버로딩이 안된다면 println() 는 printlnInt(), printlnDouble() 처럼 메서드명이 길어지고 낭비 되었을 것 입니다.
    3. 오버로딩된 println() 확인해보기!
public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable
{
			...
			
		public void println() {
        newLine();
    }

    public void println(boolean x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(char x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(int x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(long x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(float x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(double x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(char[] x) {
        if (getClass() == PrintStream.class) {
            writeln(x);
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(String x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(Object x) {
        String s = String.valueOf(x);
        if (getClass() == PrintStream.class) {
            // need to apply String.valueOf again since first invocation
            // might return null
            writeln(String.valueOf(s));
        } else {
            synchronized (this) {
                print(s);
                newLine();
            }
        }
    }


		  ...
}

기본형 & 참조형 매개변수

기본형 매개변수

📌 메서드를 호출할 때 전달할 매개값으로 지정한 값을 메서드의 매개변수에 복사해서 전달합니다.

  • 매개변수의 타입이 기본형일 때는 값 자체가 복사되어 넘어가기 때문에 매개값으로 지정된 변수의 원본 값이 변경되지 않습니다.

참조형 매개변수

📌 메서드를 호출할 때 전달할 매개값으로 지정한 값의 주소를 매개변수에 복사해서 전달합니다.

  • 매개변수를 참조형으로 선언하면 값이 저장된 곳의 원본 주소를 알 수 있기 때문에 값을 읽어 오는 것은 물론 값을 변경하는 것도 가능합니다.
  • 메서드의 매개변수 뿐만 아니라 반환타입도 참조형이 될 수 있습니다.
    • 반환타입이 참조형이라는 것은 반환하는 값의 타입이 “실제값의 주소”라는 의미입니다.