[Java 자바] 6. 클래스 ③ 필드, 생성자, 메소드
6-6. 필드
필드는 객체의 고유 데이터, 객체가 가져야 할 부품, 객체의 현재 상태 데이터를 저장하는 곳
6-6-1. 필드 선언
- 클래스 중괄호 블록 내에서 변수처럼 선언 (단, 생성자와 메소드 중괄호 블록 내부에는 선언 불가)
데이터타입 필드명; // 선언하지 않을 경우 타입별 초기값으로 저장됨
데이터타입 필드명 = 초기값;
6-6-2. 필드 사용
- 필드를 사용한다 == 필드값을 읽고, 변경하는 작업을 한다
- 클래스 내부에서는 필드의 사용이 가능하지만, 외부에서 사용할 경우 클래스로부터 객체를 생성 후 사용해야 함
- 객체를 생성한 후, 인스턴스명.필드명을 통해 클래스의 필드에 접근이 가능
// Person.java
public class Person {
String name = "Erin";
int age = 20;
String hobby;
}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println(p1.name);
System.out.println(p1.age); // 20
p1.age = 30; // 30으로 필드 숫자 변경.
System.out.println(p1.age); // 30
Person p2 = new Person();
System.out.println(p2.age); // 20
}
}
[ 참고: 필드/변수의 비교 ]
변수 | 생성 시기 | 소멸 시기 | 저장 메모리 | 사용 방법 |
클래스 필드 | 클래스가 메모리에 올라갈 시 | 프로그램 종료시 | 메소드 영역 | 클래스이름.필드이름 |
인스턴스 필드 | 인스턴스 생성시 | 인스턴스 소멸시 | 힙 영역 | 인스턴스이름.필드이름 |
지역 변수 | 블록 내 변수 선언문 실행시 | 블록 벗어날시 | 스택 영역 | 변수이름 |
// Person.java
public class Person {
static String nationality;
String name; // 클래스 필드
int birthYear; // 인스턴스 필드
void calKoreanAge(){
int koreanAge = 2022-birthYear+1; // 지역 변수
System.out.println(koreanAge);
}
}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
System.out.println(Person.nationality);
Person.nationality = "Korea";
System.out.println(Person.nationality);
Person p1 = new Person();
p1.nationality; // error 클래스필드는 인스턴스로 접근 불가
p1.birthYear = 2000;
p1.calKoreanAge();
p1.koreanAge; // error 지역 변수는 블록 내에서만 존재하므로 외부에서 사용 불가
}
}
6-7. 생성자
생성자는 new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화 담당
- new 연산자에 의해 생성자가 성공적으로 실행되어야 힙 영역에 객체가 생성되고 그 주소가 리턴됨
6-7-1. 기본 생성자
- 클래스 내부에 생성자 선언을 생략했다면 컴파일러에서 자동으로 기본 생성자를 추가시킴
// 소스 파일(Person.java)
public class Person {
}
// 바이트코드 파일(Person.class)
public class Person {
public Person() { } // 자동 추가
}
// 소스 파일(PersonTest.java)
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Person(); // 기본생성자
}
}
6-7-2. 생성자 선언
- 기본 생성자 대신 생성자를 명시적으로 선언하고 싶을 경우, 아래와 같이 매개변수를 선언할 수 있다.
- 단, 생성자를 명시적으로 선언할 경우 기본 생성자는 만들어지지 않으며, 필요한 경우 직접 작성해야 한다.
// Person.java
public class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person(); // 호출불가
Person p2 = new Person("Erin", 20); // 매개변수를 넣어야 객체 생성 가능
}
}
6-7-3. 필드 초기화
- 객체 생성 시점에서 외부에서 제공되는 다양한 값으로 필드 초기화 가능
- "this.필드"는 this는 객체 자신의 참조를 의미하여, 생성자 밖의 필드 자체를 초기화할 수 있다
// Person.java
public class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person(); // 호출불가
Person p2 = new Person("Erin", 20); // 매개변수를 넣어야 객체 생성 가능
}
}
6-7-4. 생성자 오버로딩(Overloading)
매개 변수를 달리하는 생성자를 여러개 선언하는 것
public class Person {
Person() {}
Person(String name) {}
Person(int age) {}
Person(String name, int age) {}
}
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person("Erin");
Person p3 = new Person(20);
Person p4 = new Person("H", 30);
}
}
// 단, 매개 변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 오버로딩이 아님
Person(String name, String hobby) {}
Person(String hobby, String name) {} // 오버로딩 아님
6-7-5. 다른 생성자 호출 (this())
- 생성자 오버로딩이 많아져 생성자 간 중복 코드가 발생할 경우,
필드 초기화를 한 생성자에만 집중적으로 작성하고 나머지 생성자는 초기화 내용을 가지고 있는 생성자를 호출하여 사용 가능
public class Person {
static String nationality;
String name;
int age;
String job;
Person() {}
Person(String name) {
this(name, 20); // Person(String name, int age) 생성자 호출.
}
Person(String name, int age){
this(name, age, "developer"); // Person(String name, int age, String job) 생성자 호출.
}
Person(String name, int age, String job){
this.name = name;
this.age = age;
this.job = job;
}
}
6-8. 메소드
어떠한 특정 작업을 수행하기 위한 명령문의 집합으로 객체가 할 수 있는 행동을 정의
- 메소드를 활용하면 반복되는 코드의 양을 줄일 수 있고 코드의 유지보수가 쉬워짐
- 메소드의 이름은 소문자로 시작하는 것이 관례(camelCase)
6-8-1. 메소드 선언
[접근제한자] [활용제한자] 반환타입 메소드이름([매개변수s]) {
행위 기술...
}
(1) 반환타입
메소드가 실행 후 리턴하는 값의 데이터 타입으로 리턴 값이 없을 경우는 void를 사용
void powerOn() { ... }
double divide(int x, int y) { ... }
// 사용시
powerOn();
double result = divide(10, 20);
// 반환값이 있을 경우, 반환타입과 동일한 타입의 변수에 리턴값을 저장해야 함.
// 만약 int인 변수에 저장했다면 컴파일 에러 발생
(2) 매개 변수 선언
메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용
// Calculator.java
public class Calculator {
void powerOn() {
System.out.println("전원을 켭니다.");
}
int plus(int x, int y) { // 이 때, 매개변수는 x, y로 plus 메소드 호출시 x, y 변수 없이 호출 불가
int result = x + y;
return result;
}
}
// CalculatorTest.java
public class CalculatorTest {
public static void main(String[] args) {
Calculator myCal = new Calculator();
// 객체 메소드 사용 위해 객체 생성
myCal.powerOn();
int result = myCal.plus(2, 3);
}
}
(3) 매개 변수의 수를 모를 경우
메소드 선언시 매개 변수의 개수를 알 수 없을 경우, 배열 타입으로 선언
// Calculator.java
public class Calculator {
int sum1(int[] values) {
int sum = 0;
for(int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
int sum2(int ... values) {
int sum = 0;
for(int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
}
// CalculatorTest.java
public class CalculatorTest {
public static void main(String[] args) {
Calculator c1 = new Calculator();
// sum1은 배열 값을 매개변수로 받고 있어, 사용 전에 배열을 만들어줘야 함
int[] values1 = {1, 2, 3};
int result1 = c1.sum1(values1);
int result2 = c1.sum1(new int[] {1, 2, 3});
// sum2는 값의 리스트를 넘겨주면 자동으로 배열을 생성해 줌(더 편리)
int result3 = c1.sum2(1, 2, 3);
}
}
6-8-2. 리턴(return)문
1) 리턴값이 있는 메소드
메소드 선언에 리턴 타입이 있으면 반드시 리턴문을 작성해야 함
- 리턴문이 실행되면 메소드는 즉시 종료되기 때문에 리턴문 뒤의 코드는 오류 발생(Unreachable code)
- 하지만 다음의 경우 컴파일 에러가 발생하지 않음
⓶는 return false; 다음에 있지만, if문의 조건식이 false일 경우 정상 실행되기 때문에 Unreachable 에러 발생하지 않음
boolean isLeftGas() {
if(gas == 0) {
System.out.println("gas가 없습니다."); // ------- ⓵
return false;
}
System.out.println("Gas가 있습니다."); // ------- ⓶
return true;
}
2) 리턴값이 없는 메소드(void)
리턴값이 없는 메소드에서 return문을 사용하면 메소드 실행을 강제 종료시킴
6-8-3. 메소드 호출
클래스 내부의 다른 메소드에서 호출할 경우엔 단순 메소드 이름으로 호출이 가능하지만,
클래스 외부에서 호출할 경우에는 클래스로부터 객체를 생성한 뒤 참조 변수를 이용해 메소드를 호출해야 한다.
1) 객체 내부에서 호출
public class Calculator {
int add(int x, int y) {
return x + y;
}
int avg(int x, int y) {
return add(x, y) / 2; // 객체 내부에서 메소드이름으로 바로 호출 가능
}
}
2) 객체 외부에서 호출
public class CalculatorTest {
public static void main(String[] args) {
Calculator c1 = new Calculator(); // 클래스 외부에서 호출시 객체를 먼저 생성해줘야 함
System.out.println(c1.add(2, 3)); // 참조변수를 통해서 클래스 메소드 접근 가능
System.out.println(c1.avg(5, 7));
}
}
6-8-4. 메소드 오버로딩
클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것
- 매개 변수의 타입, 개수, 순서 중 하나가 달라야 함
- 대표적인 메소드 오버로딩으로 println() 메소드가 있음
println(boolean x)
println(char x)
println(char[] x)
println(int x)
...
// println() 메소드는 전달 받는 매개변수 타입에 따라 다양한 원형 중 적절한 원형 호출