자바 개념 노트 [6-2] : CLASS : 클래스 만들기, 필드, 생성자, 메소드
■ 클래스/객체 개념 심화
▷ 클래스 만드는 과정
1) 소스 파일 생성 : <클래스명>.java
2) 소스 작성: class <클래스명> {...} ← 이 안에 클래스 구성 3가지 요소인, 필드/생성자/메소드등 기술.
3) 컴파일 하면, <클래스명>.class 생성
▷클래스로 객체 찍어내기 (feat new 연산자)
포맷: <클래스명> <객체명> = new <클래스명()> 예: Waffle w1 = new Waffle();
new는 클래스로부터 객체를 생성하는 연산자다. new 연산자 뒤에는 생성자가 오는데, 생성자는 클래스()형태를 가지고 있다. new 연산자로 생성된 객체는 힙영역에 생성되고, 이 객체는 자신의 주소를 리턴하여 클래스 변수에 저장한다.
여기서는 w1가 클래스변수(객체명)로 스택 영역 위치. w1가 참조하는 Waffle 객체는 힙영역 위치.
new 연산자로 리턴된 객체번지가 이 클래스 변수에 저장됨. (클래스변수 = 참조타입변수)
같은 와플 팬에서 찍어냈다고 하더라도, 다 다른 와플이듯이. 같은 클래스에서 찍어냈다고 하더라도 다 다른 객체다.
new 연산자를 사용한 만큼 서로 다른 객체가 메모리에 생성된다.
▷ 클래스 특징
- 1개의 어플리케이션은 1개의 실행클래스(main())와 N개의 라이브러리클래스로 구성
- 클래스는 2가지 용도로 나뉨 : 라이브러리(API)용 & 실행용
- 클래스 1개에서 찍어낸 여러 객체들은 (딸기와플, 사과와플, 레몬와플 등) 각자 서로 다른 메모리 공간에서 활동
- 클래스는 3가지로 구성됨 : 필드, 생성자, 메소드 (Field, Constructor, Method)
※각각의 역할 관련해서는 내용이 길어져 밑에 따로 상세 설명
※여기서 필드/생성자/메소드 코드 작성은 필수요소가 아님.
public class Student {
//Field Constructor Method can be here
}
△ 진짜 이렇게 텅빈 블록으로 작성해도 된다. (컴파일에러X, 예외처리X)
-클래스는 메소드를 가질 수 있음. 그런데, main이라고 이름붙여진 메소드를 가지지 않은 클래스는,
main() 메소드가 있는 다른 클래스 안에서 객체 생성 과정을 거쳐 사용해야 함.
public class Waffle {
public static void main(String args[]) {
MyWaffle w1 = new MyWaffle();
System.out.println(w1.flavor);
System.out.println(w1.pay(3));
}
}
class MyWaffle{
String flavor = "Chocolate";
int price = 1500;
int pay(int numb) {
return numb*this.price;
}
}
// 출력 : Chocolate, 4500
△ MyWaffle은 메인 메소드 없어 독립적으로 보여줄 수 있는게 없다.
- 인스턴스 멤버 개념 알기. (필드와 메소드가 다 인스턴스 멤버)
객체(=인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드.
객체 외부에서 인스턴스 멤버에 접근하기 위해서는 참조 변수를 사용. 예: w1.price
객체 내부에서 인스턴스 멤버에 접근할 땐, this라는 쓴다. 예: this.price;
▷ 클래스 만들 때 주의 할 점:
소스파일은 클래스를 담는 그릇이며, 클래스 자체는 아님을 알자.
소스파일1개당 클래스1개만 만드는 것이 권장 (두 개 이상의 클래스도 만들 수 있긴 하지만..)
소스파일 1개에 여러 클래스 만들 때, 소스파일명과 동일 이름의 클래스 1개는 반드시 있어야함.
소스파일명과 동일한 이름의 클래스만 public 접근 제한자를 붙일 수 있음.
소스파일에 들어있는 클래스만큼 .class (바이트코드) 파일 생성
■ 클래스 구성 멤버: 필드, 생성자, 메소드
#필드:
객체의 고유값, 현재상태 등의 정보, 객체가 가져야할 부품 객체 정보 등. 이러한 속성값들은 미리 설계도에서 초기값을 적어놔도 되고, 안적어놓으면 기본초기값이 들어간다. int라면 0
객체 내부라면 필드이름 또는 this.필드이름으로 바로 접근. 객체 외부라면 클래스변수.필드이름으로 접근
#생성자:
new 연산자에의해 호출되어 객체 초기화 담당. 만약 클래스내에 따로 생성자를 만들어주지 않으면 컴파일러는 기본생성자를 만들어준다. 그래서 생성자 코드 작성은 필수가 아니다.
생성자를 만약 명시적해 쓴다면, 생성자의 매개변수는 필드(속성)값으로만 들어갈 수 있다.
만약 개발자가 직접 생성자를 명시해 써주었다면, 그 생성자대로 객체를 만들어야한다. 만약 와플 생성자로 '맛'을 명시해 넣었다면, 반드시 Waffle w1 = new Waffle("Choco"); 라고 써야하지 Waffle w1 = new Waffle();는 에러
#메소드:
클래스 내부에 있는 함수로 객체의 동작기능을 담당한다. 리턴 타입, 메소드명, 매개변수 선언, 실행 블록 등등으로 메소드를 만들고 호출하면 메소드 중괄호 블록에 있는 모든 코드들이 일괄 실행된다.
메소드를 호출 할 때, 같은 클래스 안에서 부를 땐 그냥 메소드(). 클래스 외부에서 부를 땐 <참조변수명> 메소드()
■ 메소드 심화 설명:
▷ 매개변수의 수가 미정일 때?
예: 평균 계산. 숫자 몇 개가 들어올 지는 그때 그때 다름 )
인풋을 배열로 받음. 타입 [] 또는 타입 쩜쩜쩜으로 받으면 된다.
int mean(int[] values) {~~~~ return meanvalue} 또는 int mean(int ... values) {~~~~ return meanvalue}
▷메소드 리턴 값을 받을 때:
메소드 리턴을 받는 변수가 메소드 리턴 타입과 같거나, 타입 변환이 가능해야 함.
타입 변환은 작은그릇에서 큰 그릇으로 옮겨담는 것만 가능. int 메소드의 int 리턴값을 double a에 담는 것은 OK, 반대 안됨
■ 생성자 오버로딩 , 메소드 오버로딩:
▷생성자 오버로딩
똑같은 이름의 생성자가 여러개 있는 상태. 단, 이 생성자들의 매개변수는 달라야함.
즉, Food()도 되고, Food(int carb)도 되고, Food(int carb, int protien, int fat)등등도 되게 하는 것.
단, 매개변수 갯수와 타입이 동시에 달라야 하지. 단지 매개변수명만 다를 뿐, 똑같은 타입의 똑같은 개수의 중복 생성자는 안됨
※ this()는 자신 객체를 가리키는 말이다. 매개변수명과 필드명이 같을 수 있는데, 헷갈리지 않기 위해, 필드앞에 this()붙임
// Possible Constructor Overloading
Food(int carb, int protein, int fat){
this.carb = carb;
this.protein = protein;
this.fat = fat;
}
Food(int fat){
this.fat = fat;
}
△ 가능한 예
// Wrong Constructor Overloading
Food(int protein){
this.protein = protein;
}
Food(int fat){
this.fat = fat;
}
△틀린 예. 매개 변수명만 다를뿐, 매개변수 갯수와 타입이 모두 동일해서 문제.
Food(35)라고 하면 단백질이 35g인지 지방이 35g인지 혼동되기 때문 (코드 작서 순서는 가이드가 되지 못함)
생성자 오버로딩이 많아질 경우, 생성자간 중복 코드가 발생할 수 있다. 이 경우 필드 초기화 내용은 한 생성자에서만 집중적으로 작성하고, 나머지 새성자는 초기화 내용을 가지고 있는 생성자를 호출하는 식으로 가는 것이 좋다. 다른 생성자를 호출 할 때도 역시 자신 객체를 가리키는 것이므로 this()를 쓴다. this()는 반드시 생성자의 첫줄에서만 허용한다.
// 개선전
Food(int carb, int protein, int fat){
this.carb = carb;
this.protein = protein;
this.fat = fat;
}
Food(int fat){
this.fat = fat;
this.protein = 100; //default value
this.carb = 30; //default value
}
△ 가능하지만, 지저분해지는 생성자 오버로딩
// 개선후
Food(int carb, int protein, int fat){
this.carb = carb;
this.protein = protein;
this.fat = fat;
}
Food(int fat){
this(30,100,fat);
}
△ 훨씬 간결해진 생성자 오버로딩
▷메소드 오버로딩
똑같은 리턴타입, 똑같은 이름의 함수가 여러개 있는 상태. 생성자 오버로딩과 마찬가지로 이 함수들의 매개변수는 달라야함.
public class CalculatorExample {
public static void main(String[] args) {
Calculator myCalcu = new Calculator();
//정사각형의 넓이 구하기
double result1 = myCalcu.areaRectangle(10);
//직사각형의 넓이 구하기
double result2 = myCalcu.areaRectangle(10, 20);
System.out.println("정사각형 넓이=" + result1); //100 출력
System.out.println("직사각형 넓이=" + result2); //200 출력
}
}
class Calculator {
//정사각형의 넓이
double areaRectangle(double width) {
return width * width;
}
//직사각형의 넓이
double areaRectangle(double width, double height) {
return width * height;
}
}