014. 클래스와 인스턴스 실습, 생성자(Constructor)
2023.02.13 (월)
클래스와 인스턴스
- 실습
- Test072
- 클래스와 인스턴스
/*=============================================
■■■ 클래스와 인스턴스 ■■■
==============================================*/
// 사용자로부터 임의의 정수를 입력받아
// 1 부터 입력받은 수 까지의 합을 연산하여
// 결과값을 출력하는 프로그램을 구현한다.
// 단, 지금까지처럼 main()메소드에 모든 기능을 적용하는 것이 아니라
// 클래스와 인스턴스의 개념을 활용하여 처리할 수 있도록 한다.
// (→Hap 클래스 설계)
// 또한, 데이터 입력 처리 과정에서 BufferedReader 의 readLine() 을 사용하며,
// 입력 데이터가 1보다 작거나 1000보다 큰 경우
// 다시 입력받을 수 있는 처리를 포함하여 프로그램을 구현할 수 있도록 한다.
// 실행 예)
// 임의의 정수 입력(1 ~ 1000) : 1050
// 임의의 정수 입력(1 ~ 1000) : -45
// 임의의 정수 입력(1 ~ 1000) : 100
// >> 1 ~ 100 까지의 합 : 5050
// 계속하려면 아무 키나 누르세요...
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
class Hap
{
int n, a, s; // 임의의 정수, 1부터 증가해나갈 값, 합계를 담을 변수
void input() throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
do
{
System.out.print("임의의 정수 입력(1~1000) : ");
n = Integer.parseInt(br.readLine());
}
while (n<1 || n>1000);
}
int cal()
{
s = 0;
for (a=1; a<=n; a++)
s += a;
return s;
}
void print(int sum)
{
System.out.printf(">> 1 ~ %d 까지의 합 : %d\n", n, sum);
}
}// Hap class end
public class Test072
{
public static void main(String[] args) throws IOException
{
Hap h = new Hap();
h.input();
int ans = h.cal();
h.print(ans);
}//main end
}//class end
// 실행 결과
/*
임의의 정수 입력(1~1000) : 1050
임의의 정수 입력(1~1000) : -50
임의의 정수 입력(1~1000) : 54
>> 1 ~ 54 까지의 합 : 1485
계속하려면 아무 키나 누르십시오 . . .
*/
- Test073
- 클래스와 인스턴스
/*=============================================
■■■ 클래스와 인스턴스 ■■■
==============================================*/
// 사용자로부터 임의의 두 정수와 연산자를 입력받아
// 해당 연산을 수행하는 프로그램을 구현한다.
// 단, 클래스와 인스턴스의 개념을 활용하여 작성할 수 있도록 한다.
// (→ Calculate 클래스 설계)
// 실행 예)
// 임의의 두 정수 입력 (공백 구분) : 10 5
// 임의의 연산자 [+ - * /] : +
// 10 + 5 = 15
// 계속하려면 아무 키나 누르세요...
import java.util.Scanner;
import java.io.IOException;
class Calculate
{
// 주요 변수 선언
int su1, su2; // 사용자로부터 입력받은 두 정수를 담아낼 변수
char op; // 사용자로부터 입력받은 연산자를 담아낼 변수
// 메소드 정의 (기능 : 입력)
void input() throws IOException
{
Scanner sc = new Scanner(System.in);
System.out.print("임의의 두 정수 입력(공백 구분) : ");
su1 = sc.nextInt();
su2 = sc.nextInt();
System.out.print("임의의 연산자 [+ - * /] : ");
op = (char)System.in.read();
}
// 메소드 정의 (기능 : 연산)
// → 나눗셈 연산을 실수 기반으로 처리 → 반환 자료형 → double
//int cal()
//{
//}
// → 나눗셈 연산을 정수 기반으로 처리 → 반환 자료형 → int
double cal()
{
double result = -1;
switch (op)
{
case '+': result = su1 + su2; break;
case '-': result = su1 - su2; break;
case '*': result = su1 * su2; break;
case '/': result = (double)su1 / su2; break;
//default : result = -1 // result 값이 초기화 되어있기 때문에 생략 가능
}
return result;
}
// 메소드 정의 (기능 : 출력)
void print(double s)
{
System.out.printf(">> %d %c %d = %.2f\n", su1, op, su2, s);
}
}// Calculate class end
public class Test073
{
public static void main(String[] args) throws IOException
{
Calculate cal = new Calculate();
cal.input(); // 생성한 인스턴스를 통한 입력 메소드 호출
double r = cal.cal(); // 생성한 인스턴스를 통한 연산 메소드 호출
cal.print(r); // 생성한 인스턴스를 통한 출력 메소드 호출
}
}
// 실행결과
/*
임의의 두 정수 입력 (공백 구분) : 50 3
임의의 연산자 [+ - * /] : +
50 + 3 = 53.0
계속하려면 아무 키나 누르십시오 . . .
*/
/*
임의의 두 정수 입력 (공백 구분) : 30 8
임의의 연산자 [+ - * /] : -
30 - 8 = 22.0
계속하려면 아무 키나 누르십시오 . . .
*/
/*
임의의 두 정수 입력 (공백 구분) : 31 132
임의의 연산자 [+ - * /] : *
31 * 132 = 4092.0
계속하려면 아무 키나 누르십시오 . . .
*/
/*
임의의 두 정수 입력 (공백 구분) : 654 54
임의의 연산자 [+ - * /] : /
654 / 54 = 12.1
계속하려면 아무 키나 누르십시오 . . .
*/
클래스와 인스턴스
- 생성자 (Constructor)
- 생성자를 사용하는 이유
※ 서로 다른 인스턴스의 생성은, 인스턴스 변수의 초기화라는 문제를 고민하게 한다.
지금까지 작성해 왔던 방식으로 인스턴스 생성 후 변수나 메소드에 접근하는 방식을 이용한다면,

▶ 변수의 개수가 많아질수록 매우 비합리적인 행위를 반복해야만 한다.
→ 그렇다면, 사과장수의 속성값(변수)를 원하는 형태로 초기화 하는 메소드를 만들면?

▶ 속성을 한번에 초기화 하는 기능을 사용하여 비교적 구문이 간단해지긴 했으나, 바람직하지 않다!
▶ why?

1) 사실은 이 과정 역시 비합리적으로 생성과 동시에 초기화를 할 수 있음
- 생성자의 필요성

2) 판매가격 등 변경이 되면 안되는 중요한 변수의 경우 값을 final 로 상수화 시켜야할 필요성이 있음
- final이 붙은 변수는 상수가 되었기 때문에 인스턴스 생성 하여 값을 초기화시킬 수 없음.
(문번 10번, 19번 참조)
→ 생성자를 사용하는 법!

※ 생성자의 이름은 항상 예외없이 클래스의 이름과 동일해야 하며
필요할 경우 인수를 받아들이는 것도 가능하고,
같은 이름의 메소드를 정의하는 중복정의(오버로딩)가 가능하지만,
리턴값(반환값)은 가질 수 없다.
// ※ 생성자는 다른 일반 메소드처럼 호출될 수 없고,
// 『new』연산자를 이용하여 객체를 생성하기 위해 호출되며,
// 각 클래스의 인스턴스를 생성한 후에
// 생성된 객체의 멤버를 초기화 시키는 작업을 수행하게 된다.
○ 생성자(Constructor)의 역할
1. 인스턴스 생성 → 메모리 할당
2. 초기화
○ 생성자(Constructor)의 특징
1. 생성자는 메소드이지만, 일반적인 메소드처럼 호출될 수 없으며, 반환 자료형을 가질 수 없다.
(『void』조차 가질 수 없으며, 값을 반환할 수도 없다.)
+
생성자가 반환자료형을 가지지 않는 특성으로 아무 것도 붙이지 않는 문법을 선점해갔기 때문에,
메소드에서 값을 반환하지 않을 때 붙이는 자료형을 void(공허의) 를 붙이게 됨.
2. 생성자는 클래스와 동일한 이름을 가져야 한다.
(대소문자 명확히 구분 → 클래스의 명명법과 같은 규칙 적용)
3. 생성자는 객체를 생성하는 과정에서 『new 생성자();』의 형태로 호출된다.
(인스턴스 생성 시 단 한 번만 호출) → final 변수(상수화된 변수) 초기화 가능
- Test074
- 생성자 (Constructor) 관찰
/*=============================================
■■■ 클래스와 인스턴스 ■■■
- 생성자 (Constructor)
==============================================*/
class NumberTest
{
int num;
// ※ 클래스에... 사용자 정의 생성자를 정의하지 않았다면...
// 컴파일 과정에서 디폴트(default) 생성자가 자동으로 삽입된다.
// 즉, 클래스에... 사용자 정의 생성자가 정의되어 있다면...
// 컴파일 과정에서 디폴트(default) 생성자는 자동으로 삽입되지 않는다.
// 디폴트(default) 생성자
// NumberTest()
//{
// 텅 비어있는 상태
//}
// ※ 사용자 정의 생성자 // 사용자가 직접 정의하고 사용하게 되는 생성자
NumberTest()
{
// 아래와 같이 내용을 채울 수도 있고, 비워서 만들어두어도 사용자 정의 생성자이다.
num = 10;
System.out.println("사용자 정의 생성자 호출~");
}
int getNum()
{
return num;
}
}
public class Test074
{
public static void main(String[] args)
{
// NumberTest 클래스 기반의 인스턴스 생성
NumberTest nt1 = new NumberTest();
//--==>> 사용자 정의 생성자 호출~
//-- 인스턴스가 생성되는 시점에서
// 이와 동시에 선택의 여지 없이 생성자 호출이 이루어진다.
// ----------- NumberTest();
/*
// 사용자 정의 생성자 주석처리 후 확인
int result = nt1.getNum();
// -------------- 생성자를 따로 메소드 정의하지 않았는데 에러가 안남!
// 다른 클래스에서 이미 정의 되어있기 때문에 실행 됨 - default 생성자 때문!
System.out.println("result : " + result);
*/
//nt1.action();
//--==>> 에러 발생(컴파일 에러)
// can not find symbol // NumberTest 클래스에 action이라는 메소드 존재X
//nt1.NumberTest();
//--==>> 에러 발생(컴파일 에러)
// can not find symbol // 생성자는 인스턴스 생성시 단 한 번만 호출 되기 때문!
//int num1 = nt1.getNum();
//System.out.println(num1);
System.out.println(nt1.getNum()); // 상기 두 문장을 하나로 만들 수 있음
//--==>> 10
nt1.num = 200;
System.out.println(nt1.getNum());
//--==>> 200
NumberTest nt2 = new NumberTest();
//--==>> 사용자 정의 생성자 호출~
System.out.println(nt2.num);
//--==>> 10
}
}
// 실행 결과
/*
사용자 정의 생성자 호출~
10
200
사용자 정의 생성자 호출~
10
계속하려면 아무 키나 누르십시오 . . .
*/
- Test075
- 생성자 (Constructor) 관찰
/*=============================================
■■■ 클래스와 인스턴스 ■■■
- 생성자 (Constructor)
==============================================*/
class NumberTest2
{
int num;
// default 생성자
/*
NumberTest2()
{
}
*/
// ※ 사용자 정의 생성자가 정의되어 있으므로
// default 생성자가 자동으로 삽입되지 않음~
// 생성자 → 사용자 정의 생성자
NumberTest2(int n)
{
num = n;
System.out.println("생성자 호출 시 매개변수 전달 : " + n);
}
int getNum()
{
return num;
}
}
public class Test075
{
public static void main(String[] args)
{
// NumberTest2 클래스 기반의 인스턴스 생성
//NumberTest2 nt1 = new NumberTest2();
//--==>> 에러 발생 (컴파일 에러)
//--NumberTest2 클래스에는
// 사용자 정의 생성자가 존재하고 있는 상황이기 때문에
// 『default 생성자』가 자동으로 삽입되지 않으며
// 사용자가 정의한 생성자는 매개변수를 갖는 형태이기 때문에
// 위와 같이 매개변수 없는 생성자를 호출하는 형태의 구문은
// 문제를 발생시키게 된다.
NumberTest2 nt1 = new NumberTest2(10);
//--==>> 생성자 호출 시 매개변수 전달 : 10
System.out.println("메소드 반환 값 : " + nt1.getNum());
//--==>> 메소드 반환 값 : 10
System.out.println("nt1.num : " + nt1.num);
//--==>> nt1.num : 10
NumberTest2 nt2 = new NumberTest2(3654);
//--==>> 생성자 호출 시 매개변수 전달 : 3654
System.out.println("메소드 반환 값 : " + nt2.getNum());
System.out.println("nt2.num : " + nt2.num);
//--==>> 메소드 반환 값 : 3654
// nt2.num : 3654
}
}
- Test076
- 생성자 (Constructor)
- this 키워드
/*=============================================
■■■ 클래스와 인스턴스 ■■■
- 생성자 (Constructor)
==============================================*/
public class Test076
{
int x;
// 생성자 → 사용자 정의 생성자 → 매개변수 없는 생성자
Test076()
{
// ※ 생성자 내부에서 다른 생성자 호출하는 것은 가능하다. 다른 메소드들 처럼...
// 하지만, 생성자 내부에서 가장 먼저 실행되어야 한다. check!
this(100);
x = 10;
// this.x = 10;
System.out.println("인자 없는 생성자");
System.out.println("Test076이 갖고있는 x : " + x);
//Test076(100);
//this(100);
//--==>> 에러발생(컴파일 에러)
// call to this must be first statement in constructor
}
// 생성자 → 사용자 정의 생성자 → 정수형 매개변수를 넘겨받는 생성자
Test076(int x)
{
//x = num;
//x = x; //-- 둘 다 지역변수 x
// 『this』 키워드~!!!
this.x = x;
System.out.println("인자가 하나인 생성자");
System.out.println("Test076이 갖고있는 x : " + this.x);
}
public static void main(String[] args)
{
// Test076 클래스 기반의 인스턴스 생성
Test076 ob1 = new Test076();
//--==>> 인자 없는 생성자
//--==>> 인자가 하나인 생성자
// Test076이 갖고있는 x : 100
// 인자 없는 생성자
// Test076이 갖고있는 x : 10
System.out.println();
// Test076 클래스 기반의 인스턴스 생성
Test076 ob2 = new Test076(100);
//--==>> 인자가 하나인 생성자
// Test076이 갖고있는 x : 100
System.out.println();
System.out.println("main 에서 ob1.x : " +ob1.x);
System.out.println("main 에서 ob2.x : " +ob2.x);
//--==>> main 에서 ob1.x : 10
// main 에서 ob2.x : 100
}
}
// 실행결과
/*
인자가 하나인 생성자
Test076이 갖고있는 x : 100
인자 없는 생성자
Test076이 갖고있는 x : 10
인자가 하나인 생성자
Test076이 갖고있는 x : 100
main 에서 ob1.x : 10
main 에서 ob2.x : 100
계속하려면 아무 키나 누르십시오 . . .
*/
※ this 와 this( )
1) this는 객체 자신을 가리키는 레퍼런스 변수로, 자신의 객체에 접근할 때 사용된다.
- 주로 멤버변수와 매개변수의 이름이 동일할 때, 이를 구분하기 위해 사용
2) this( )는 같은 클래스에서 생성자가 다른 생성자를 호출할 때 사용된다.
- 주로 코드의 중복을 줄일 목적으로 사용됨
- this( )는 생성자 코드에서만 사용할 수 있음
- this( )는 생성자 코드안에서 사용될 때 가장 먼저 실행되어야 하므로 가장 윗줄에 위치한다.
* 클래스명.메소드명(또는 매개변수) 는 다른 문법에서 선점하였기 때문에 사용할 수 없다.
- Test077
- 생성자 (Constructor)
- 오버 로딩
/*=============================================
■■■ 클래스와 인스턴스 ■■■
- 생성자 (Constructor)
==============================================*/
public class Test077
{
int val1;
double val2;
Test077()
{
val1=0;
val2=0;
System.out.println("매개변수 없는 생성자...");
}
Test077(int x)
{
val1=x;
val2=0;
System.out.println("int형 데이터를 매개변수로 받는 생성자...");
}
Test077(double y)
{
val1=0;
val2=y;
System.out.println("double형 데이터를 매개변수로 받는 생성자...");
}
Test077(int x, double y)
{
val1=x;
val2=y;
System.out.println("int형 변수와 double형 변수를 매개변수로 받는 생성자...");
}
public static void main(String[] args)
{
Test077 ob1 = new Test077();
//--==>> 매개변수 없는 생성자...
System.out.println(ob1.val1 + "," + ob1.val2);
//--==>> 0,0.0
Test077 ob2 = new Test077(4);
//--==>> int형 데이터를 매개변수로 받는 생성자...
System.out.println(ob2.val1 + "," + ob2.val2);
//--==>> 4,0.0
Test077 ob3 = new Test077(7.0);
//--==>> double형 데이터를 매개변수로 받는 생성자...
System.out.println(ob3.val1 + "," + ob3.val2);
//--==>> 0,7.0
Test077 ob4 = new Test077(4, 7.0);
//--==>> int형 변수와 double형 변수를 매개변수로 받는 생성자...
System.out.println(ob4.val1 + "," + ob4.val2);
//--==>> 4,7.0
}
}
// 실행 결과
/*
매개변수 없는 생성자...
0,0.0
int형 데이터를 매개변수로 받는 생성자...
4,0.0
double형 데이터를 매개변수로 받는 생성자...
0,7.0
int형 변수와 double형 변수를 매개변수로 받는 생성자...
4,7.0
계속하려면 아무 키나 누르십시오 . . .
*/
※ 오버로딩(또는 중복적용) (간략한 개념만)
- 클래스 내부에서 메소드는 "식별자" 역할 → 동일 클래스 내에 중복되는 이름은 없어야함
그러나 메소드에 넘겨주는 매개변수의 개수나 매개변수의 타입이 다르면 동일한 이름을 사용할 수 있다.
- 허용하는 이유는 동일한 기능을 가진 메소드를 만드는데 매번 다른 이름을 만들려면 너무 힘들기 때문
허용하지 않는다면 아래와 같이 선긋는 메소드를 만들때 번호를 붙여 1번은 점선 2번은 두줄 등.. 생성해야
하고 이 모든 기능의 번호 외워 사용하기가 힘들기 때문.
ex) 선긋기( ) { System.out.println("----------------");}
선긋기2( ) { System.out.println("===========");}
이렇게 하는 대신에
ex) 선긋기() {System.out.println("----------------"); }
선긋기(int n) {
for (int i=0; i<n ; i++ )
System.out.println("----------------"); }
이런 식으로 사용할 수 있게 해주는 것.
- Test078
- 생성자 (Constructor)와 초기화 블럭(Initialized Block)
※ 초기화 블럭 (Initialized Block)
- 선언과 동시에 초기화 하지 않았을 때,
즉, 전역변수를 선언만 해두었을때 (-전역변수는 선언만 할 경우 자바에서 자동으로 초기화 하는 값이 담김)
다시 다른 값으로 대입해서 전역변수의 값을 덮어쓰려고 할 때 사용하는 것.
- 대입연산만 있으면 오류가 나기 때문에 블레이스 안에 넣어둔 것 뿐.
- 초기화 블럭보다 생성자가 우선순위가 높다
초기화 블럭을 생성자 위에 놓든, 아래에 놓든 초기화 블럭이 먼저 실행 된 후 생성자가 실행됨
→ 초기화 블럭이 어디에 위치하든 먼저 생성 된 후 생성자가 생성되면,
초기화 블럭이 대입해둔 값이 생성자의 값에 의해서 덮어씌워지면서 사라지게 됨
→ 초기화 블럭이 일한게 의미가 없음
→ 즉, 생성자가 더 중요함!
/*=====================================================
■■■ 클래스와 인스턴스 ■■■
- 생성자 (Constructor)와 초기화 블럭(Initialized Block)
======================================================*/
public class Test078
{
// 수행할 수 없음
/*
int n; // 선언
int m; // 선언
n = 100; // 대입 연산 // 선언은 문제가 없으나 대입연산은 불가능 함.
m = 200; // 대입 연산 // 초기화 개념 check!
*/
// 수행 가능
/*
int n = 100; // 선언과 동시에 초기화
int m = 200; // 선언과 동시에 초기화
*/
int n;
int m;
// 생성자 (Constructor)
Test078()
{
n = 100;
m = 200;
System.out.println("생성자 실행...");
}
// 초기화 블럭(Initialized Block) // 생성자보다 하위에 있음.
{
n = 10;
m = 20;
System.out.println("초기화 블럭 실행...");
}
// 생성자(Constructor)
Test078(int n, int m)
{
this.n = n;
this.m = m;
System.out.println("매개변수 있는 생성자 실행...");
}
// 멤버 출력 메소드 정의
void write()
{
System.out.println("n:" + n + ", m:" + m);
}
public static void main(String[] args)
{
//Test078 인스턴스 생성
Test078 ob1 = new Test078();
//--==>> 초기화 블럭 실행...
// 생성자 실행...
ob1.write();
//--==>> n:10, m:20
//--==>> n:100, m:200
// Test078 인스턴스 생성
Test078 ob2 = new Test078(1234, 2345);
//--==>> 초기화 블럭 실행...
// 매개변수 있는 생성자 실행...
ob2.write();
//--==>> n:1234, m:2345
}
}
// 실행결과
/*
초기화 블럭 실행...
생성자 실행...
n:100, m:200
초기화 블럭 실행...
매개변수 있는 생성자 실행...
n:1234, m:2345
계속하려면 아무 키나 누르십시오 . . .
*/
// 개인적 감상
클래스와 인스턴스의 개념을 이해하면서,
BufferedReader 나 Scanner 등 인스턴스 생성한다고 써왔던 부분을 실제로 어떤 방법으로 사용하는지 알게되었다.
이를 복습하면서도, 중요한 개념임에도 불구하고 정리가 잘 되어있지 않아서 강의를 들으면서도 긴가민가 하면서 쓰던 부분들을 다시 확인할 수 있었다.
쉬운 듯 쉽지않았던 개념이라, 어떻게하면 조금 더 쉽게 이해할 수 있을까 구체적으로 정리해보면서 강의의 흐름과 인스턴스 사용법을 이해할 수 있었다.