필드 = 객체의 속성
필드는 객체의 데이터를 저장하는 역할을 한다.
- 객체의 필드는 크게 고유한 데이터, 상태 데이터, 객체 데이터로 분류할 수 있다.
- 이처럼 자동차 객체는 4개의 고유한 데이터와 3개의 상태 데이터 그리고 3개의 객체 데이터를 가질 수 있다.
- 우리가 처음 소프트웨어의 부품을 객체라 표현한다.
- 이 3개의 객체 데이터를 자동차를 만들기 위한 부품 데이터라고 이해해도 좋다.
public class Car {
String company; // 자동차 회사
String model; // 자동차 모델
String color; // 자동차 색상
double price; // 자동차 가격
double speed; // 자동차 속도 , km/h
char gear; // 기어의 상태, P,R,N,D
boolean lights; // 자동차 조명의 상태
Tire tire;
Door door;
Handle handle;
public Car() {} // 기본 생성자
double gasPedal(double kmh) {
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("빵빵");
}
}
우리가 정의하여 선언한 클래스의 필드들은 기본적으로 초기값을 제공하지 않을 경우 객체가 생성될 때 자동으로 기본값으로 초기화된다.
- 초기값을 제공하는 방법은 ‘필드 타입 필드명 = 값;’ 이렇게 직접 초기화할 수 있다.
- String model = "Gv80";
필드 사용방법
필드를 사용한다’라는 의미는 필드의 값을 변경하거나 읽는 것을 의미합니다.
- 우리가 클래스에 필드를 정의하여 선언했다고 해서 바로 사용할 수 있는 것은 아닙니다.
- 클래스는 설계도일 뿐 실제로 필드의 데이터를 가지고 있는 것은 객체입니다.
- 따라서 객체를 생성한 후에 필드를 사용할 수 있습니다.
- 간단히, 클래스는 설계도이다. 붕어빵 틀 설계도에 속(데이터)를 넣을 수 없다. 붕어빵 틀(객체)에 속(데이터)를 넣어야한다.
- 외부 접근
- Car car = new Car();
- 이렇게 객체를 생성했다면 우리는 참조 변수 car를 이용하여 외부에서 객체 내부의 필드에 접근하여 사용할 수 있습니다.
- 이때 객체의 내부 필드에 접근하는 방법은 도트(.) 연산자를 사용하면 됩니다.
- car.color = "blue";
- Car car = new Car();
- 내부 접근
- 도트 연산자를 사용하여 외부에서 객체 내부에 접근할 수 있을 뿐만 아니라 객체 내부 메서드에서도 내부 필드에 접근할 수 있습니다.
double brakePedal() {
speed = 0;
return speed;
}
- 이처럼 brakePedal() 메서드 내부에서 객체의 필드 speed를 바로 호출해서 사용할 수 있습니다.
public class Car {
String company; // 자동차 회사
String model = "Gv80"; // 자동차 모델
String color; // 자동차 색상
double price; // 자동차 가격
double speed; // 자동차 속도 , km/h
char gear; // 기어의 상태, P,R,N,D
boolean lights = true; // 자동차 조명의 상태
Tire tire = new Tire();
Door door;
Handle handle;
public Car() {} // 기본 생성자
double gasPedal(double kmh) {
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("빵빵");
}
}
//- model 필드 값에 “Gv80” 초기값을 주겠습니다.
//- lights 필드 값에 true 초기값을 주겠습니다.
//- tire 필드 값에 `new Tire()` 초기값을 주겠습니다.
main 메서드를 사용하여 테스트한 경우
public class Main {
public static void main(String[] args) {
Car car = new Car(); // 객체 생성
// 초기값과 기본값 확인하기
System.out.println("car.model = " + car.model); // 초기값 "Gv80"이 출력됩니다.
System.out.println("car.color = " + car.color); // 기본값 null이 출력됩니다.
System.out.println();
System.out.println("car.speed = " + car.speed); // 기본값 0.0이 출력됩니다.
System.out.println("car.gear = " + car.gear); // 기본값 \u0000(공백)이 출력됩니다.
System.out.println("car.lights = " + car.lights); // 초기값 true가 출력됩니다.
System.out.println();
System.out.println("car.tire = " + car.tire); // 초기값 인스턴스의 주소가 출력됩니다.
System.out.println("car.door = " + car.door); // 기본값 null이 출력됩니다.
System.out.println();
// 필드 사용
car.color = "blue"; // 필드 color에 "blue" 데이터를 저장합니다.
car.speed = 100; // 필드 speed에 100 데이터를 저장합니다.
car.lights = false; // 필드 lights에 false 데이터를 저장합니다.
System.out.println("car.color = " + car.color); // 저장된 "blue" 데이터가 출력됩니다.
System.out.println("car.speed = " + car.speed); // 저장된 100.0 데이터가 출력됩니다.
System.out.println("car.lights = " + car.lights); // 저장된 false 데이터가 출력됩니다.
}
}
메서드 = 객체의 행위
- 특정 작업을 수행하는 코드 블록, 객체 지향 프로그래밍에서 메서드는 주로 클래스 내부에 정의되며, 해당 클래스의 객체에서 호출하여 사용할 수 있다. 메서드는 작업을 캡슐화하여 코드의 재사용성을 높이고, 유지보수와 확장성을 용이하게 한다.
<접근 제어자> <반환 타입> <메서드 이름>(<매개변수>) {
// 메서드 본문 (실제 작업을 수행)
return <값>;
}
반환(리턴) 타입
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');
- 전달하려는 값이 없다면 생략 가능하다.
가변길이 매개변수
void carSpeeds(double ... speeds) {
for (double v : speeds) {
System.out.println("v = " + v);
}
}
- double … speeds 이렇게 … 을 사용하면 아래처럼 매개값을 , 로 구분하여 개수 상관없이 전달 가능하다.
- carSpeeds(100, 80);
- carSpeeds(110, 120, 150);
메서드 호출 방법
‘메서드를 호출한다’라는 의미는 메서드의 블록 내부에 작성된 코드를 실행한다는 의미입니다.
- 필드와 마찬가지로 클래스의 메서드를 정의하여 선언했다고 해서 바로 사용할 수 있는 것은 아닙니다.
- 클래스는 설계도일 뿐 메서드는 객체의 행위를 정의한 것입니다.
- 따라서 객체를 생성한 후에 메서드를 사용할 수 있습니다.
- 외부 접근
- Car car = new Car();
- 이렇게 객체를 생성했다면 우리는 참조 변수 car를 이용하여 외부에서 객체 내부의 메서드에 접근하여 호출할 수 있습니다.
- 이때 객체의 내부 메서드에 접근하는 방법은 도트(.) 연산자를 사용하면 됩니다.
- car.brakePedal();
- 또한 메서드가 매개변수를 가지고 있다면 반드시 호출할 때 매개변수의 순서와 타입에 맞게 매개값을 넣어줘야 합니다.
- car.gasPedal(100, 'D');
- Car car = new Car();
- 내부 접근
- 도트 연산자를 사용하여 외부에서 객체 내부에 접근할 수 있을 뿐만 아니라 객체 내부 메서드에서도 내부 메서드에 접근하여 호출할 수 있습니다.
- 아래처럼 gasPedal(double kmh, char type) 메서드 내부에서 해당 객체의 changeGear(type); 메서드를 호출할 수 있습니다.
- 도트 연산자를 사용하여 외부에서 객체 내부에 접근할 수 있을 뿐만 아니라 객체 내부 메서드에서도 내부 메서드에 접근하여 호출할 수 있습니다.
double gasPedal(double kmh, char type) {
changeGear(type);
speed = kmh;
return speed;
}
- 반환 값 저장
- 메서드의 리턴 타입을 선언하여 반환할 값이 있다면 변수를 사용하여 받아줄 수 있습니다.
- 반드시 리턴 타입과 변수의 타입이 동일하거나 자동 타입 변환될 수 있어야 합니다.
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);
}
}
}
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);
}
}
오버로딩
- 같은 이름의 메서드나 생성자를 매개변수의 개수나 타입이 달라지도록 여러 번 정의하는 기법입니다. 오버로딩을 사용하면 같은 이름으로 다양한 작업을 처리할 수 있습니다.
오버로딩의 특징
- 같은 이름의 메서드나 생성자지만 매개변수의 개수나 타입이 달라야 합니다.
- 반환 타입은 오버로딩을 구별하는 기준이 되지 않습니다.
- 컴파일 시점에 메서드 호출을 구분하기 때문에, 실행 시간에 결정되는 것이 아니라 호출 시점에서 적절한 메서드가 선택됩니다.
오버로딩의 조건
- 메서드의 이름이 같고, 매개변수의 개수, 타입, 순서가 달라야 합니다.
- '응답 값만' 다른 것은 오버로딩을 할 수 없습니다.
- 접근 제어자만 다른 것도 오버로딩을 할 수 없습니다.
- 결론, 오버로딩은 매개변수의 차이로만 구현할 수 있습니다.
오버로딩의 장점
- 코드의 가독성 향상: 비슷한 작업을 수행하는 메서드를 같은 이름으로 처리할 수 있어 코드가 깔끔하고 이해하기 쉬워집니다.
- 유지보수 용이성: 같은 이름의 메서드를 사용하여 여러 매개변수에 대한 처리 로직을 관리할 수 있어 유지보수가 용이합니다.
오버로딩 규칙
- 매개변수의 타입이 달라야 오버로딩으로 인식됩니다.
- 매개변수의 개수가 달라야 오버로딩으로 인식됩니다.
- 매개변수의 순서가 달라야 오버로딩으로 인식됩니다.
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();
}
}
}
...
}
기본형 & 참조형 매개변수
기본형 매개변수
- 매개변수의 타입이 기본형일 때는 값 자체가 복사되어 넘어가기 때문에 매개값으로 지정된 변수의 원본 값이 변경되지 않습니다.
- 메서드를 호출할 때 전달할 매개값으로 지정한 값을 메서드의 매개변수에 복사해서 전달합니다.
참조형 매개변수
- 매개변수를 참조형으로 선언하면 값이 저장된 곳의 원본 주소를 알 수 있기 때문에 값을 읽어 오는 것은 물론 값을 변경하는 것도 가능합니다.
- 메서드의 매개변수뿐만 아니라 반환 타입도 참조형이 될 수 있습니다.
- 반환 타입이 참조형이라는 것은 반환하는 값의 타입이 “실제 값의 주소”라는 의미입니다.
public class Car {
String company; // 자동차 회사
String model; // 자동차 모델
String color; // 자동차 색상
double price; // 자동차 가격
double speed; // 자동차 속도 , km/h
char gear; // 기어의 상태, P,R,N,D
boolean lights; // 자동차 조명의 상태
Tire tire;
Door door = new Door();
Handle handle = new Handle();
public Car() {} // 기본 생성자
double gasPedal(double kmh, char type) {
changeGear(type);
speed = kmh;
return speed;
}
double brakePedal(char type) {
speed = 0;
type = 'P'; // 정지 후 매개변수 type을 어떤 타입으로 전달 받았는지 상관없이 'P'로 고정시키기
changeGear(type);
return speed;
}
char changeGear(char type) {
gear = type;
return gear;
}
boolean onOffLights() {
lights = !lights;
return lights;
}
void horn() {
System.out.println("빵빵");
}
Tire setTire(Tire tireCompany) {
tireCompany.company = "KIA"; // 금호 타이어를 전달 받았지만 강제로 KIA 타이어로 교체
tire = tireCompany;
return tire;
}
}
public class Tire {
String company; // 타이어 회사
public Tire() {}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // 객체 생성
// 기본형 매개변수
char type = 'D';
car.brakePedal(type);
// 메서드 실행 완료 후 전달할 매개값으로 지정된 type 값 확인
System.out.println("type = " + type); // 기존에 선언한 값 'D' 출력, 원본 값 변경되지 않음
// 메서드 실행 완료 후 반환된 car 인스턴스의 gear 타입 확인
System.out.println("gear = " + car.gear); // 객체 내부에서 type을 변경하여 수정했기 때문에 'P' 출력
System.out.println();
// 참조형 매개변수
Tire tire = new Tire();
tire.company = "금호"; // 금호 타이어 객체 생성
// 차 객체의 타이어를 등록하는 메서드 호출한 후 반환값으로 차 객체의 타이어 객체 반환
Tire carInstanceTire = car.setTire(tire);
// 메서드 실행 완료 후 전달할 매개값으로 지정된 참조형 변수 tire의 company 값 확인
System.out.println("tire.company = " + tire.company); // "KIA" 출력
// 전달할 매개값으로 지정된 tire 인스턴스의 주소값이 전달되었기 때문에 호출된 메서드에 의해 값이 변경됨.
// 메서드 실행 완료 후 반환된 car 인스턴스의 tire 객체 값이 반환되어 저장된 참조형 변수 carInstanceTire의 company 값 확인
System.out.println("carInstanceTire.company = " + carInstanceTire.company); // "KIA" 출력
}
}
'Back-End (Web) > JAVA' 카테고리의 다른 글
[JAVA] 생성자 (1) | 2024.11.12 |
---|---|
[JAVA] 인스턴스 멤버와 클래스 멤버 (0) | 2024.11.12 |
[JAVA] 클래스 설계와 객체 생성 (0) | 2024.11.12 |
[JAVA] 객체지향 프로그래밍 (1) | 2024.11.12 |
[JAVA] 참조형 자료구조 정리(LIST / STACK / QUEUE / SET / MAP) (0) | 2024.11.11 |