Programming/Java

[Java 자바] 6. 클래스 ③ 필드, 생성자, 메소드

erinh 2022. 7. 31. 13:51
반응형

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() 메소드는 전달 받는 매개변수 타입에 따라 다양한 원형 중 적절한 원형 호출

 

반응형