4. 자바의 배열을 복습할 수 있는 과제
목표: 배열을 선언하고 초기화하는 방법을 이해하고, 배열을 통해 데이터를 관리하는 방법을 학습한다.
과제:
- 10개의 정수 값을 저장할 수 있는 배열을 생성하고, 1부터 10까지의 값을 배열에 저장한 후, 이를 출력하세요.
package javabasic;
import java.util.Arrays;
public class Example10 {
public static void main(String[] args) {
/**
* 10개의 정수 값을 저장할 수 있는 배열을 생성하고,
* 1부터 10까지의 합을 배열에 저장한 후, 이를 출력하세요
*/
//크기 10인 intArr 배열 생성
int[] intArr = new int[10];
//i가 0부터 배열 길이보다 작을 때까지 배열에 저장하고 출력
for (int i = 0; i < intArr.length; i++) {
intArr[i] = i + 1;
}
System.out.println(Arrays.toString(intArr));
}
}
- 사용자로부터 5명의 학생의 점수를 입력받아 배열에 저장한 다음, 평균 점수를 계산하여 출력하세요.
package javabasic;
import java.util.Scanner;
public class Example11 {
public static void main(String[] args) {
/**
* 사용자로부터 5명의 학생의 점수를 입력받아
* 배열에 저장한 다음,
* 평균 점수를 계산하여 출력하세요.
*/
//Scanner 객체 생성
Scanner scanner = new Scanner(System.in);
//크기 5인 studentArr double 배열 생성
double[] studentArr = new double[5];
//학생의 점수를 for문으로 입력 받기
for (int i = 0; i < studentArr.length; i++) {
System.out.print((i+1) + "번째 학생의 점수를 입력해 주세요: ");
studentArr[i] = scanner.nextDouble();
}
//합은 sum, 평균은 average로 선언 후 0으로 초기화
//합에 누적하여 더하기
double sum = 0;
double average = 0;
for (int i = 0; i < studentArr.length; i++) {
sum += studentArr[i];
}
//평균은 합에서 studentArr.length 길이만큼 나누기
for (int i = 0; i < studentArr.length; i++) {
average = sum / studentArr.length;
}
System.out.println("평균 점수는 " + average + "입니다.");
}
}
- 주어진 정수 배열에서 최댓값과 최솟값을 찾아 출력하는 프로그램을 작성하세요.
package javabasic;
public class Example12 {
public static void main(String[] args) {
/**
* 주어진 정수 배열에서
* 최댓값과 최솟값을 찾아 출력하는 프로그램을 작성하세요
*/
//정수 배열 {1, 5, 6, 7, 8, 9} 선언 및 초기화
int[] intArr = {1, 5, 6, 7, 8, 9};
//max는 배열 첫번째 값으로 초기화
//min은 배열 첫번째 값으로 초기화
int max = intArr[0];
int min = intArr[0];
//순회하며 max와 min 값 갱신
for (int i = 1; i < intArr.length; i++) {
if (intArr[i] > max) {
max = intArr[i];
} else if (intArr[i] < min) {
min = intArr[i];
}
}
System.out.println(max);
System.out.println(min);
}
}
5. 자바의 클래스와 인스턴스, 메서드와 생성자를 복습할 수 있는 과제
목표: 객체 지향 프로그래밍의 기본인 클래스와 객체의 개념을 이해하고, 클래스를 정의하고 사용하는 방법을 실습한다.
과제:
- Person 클래스를 생성하고, 이름(name)과 나이(age)를 필드로 가지게 하세요. 해당 클래스에는 이름과 나이를 설정할 수 있는 생성자와, 이를 출력할 수 있는 메서드를 포함하세요.
- Book 클래스를 만들고, 제목(title), 저자(author), 가격(price)을 필드로 정의하세요. 모든 필드를 초기화하는 생성자와, 필드 값을 출력하는 메서드를 포함하세요.
- Person 객체를 두 개 생성하고, 각 객체에 대한 정보를 출력하세요. 이어서 Book 객체를 생성하고, 그 정보를 출력하세요.
package javabasic.ex5;
public class Person {
//이름
public String name;
//나이
public int age;
//이름과 나이를 파라미터로 받는 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//이를 출력할 수 있는 메서드
public void printPerson() {
System.out.println("이름 : " + name + ", 나이 : " + age);
}
}
package javabasic.ex5;
public class Book {
//제목
public String title;
//저자
public String author;
//가격
public int price;
//제목, 저자, 가격을 파라미터로 받는 생성자
public Book(String title, String author, int price) {
this.title = title;
this.author = author;
this.price = price;
}
//이를 출력할 수 있는 메서드
public void printBook() {
System.out.println("제목 : " + title + ", 저자 : " + author + ", 가격 : " + price);
}
}
package javabasic.ex5;
public class Main {
public static void main(String[] args) {
//Person 타입의 p1, p2 객체 생성
Person p1 = new Person("lee", 20);
Person p2 = new Person("kim", 30);
//.으로 접근하여 출력 메서드 실행
p1.printPerson();
p2.printPerson();
//Book 타입의 b1, b2 객체 생성
Book b1 = new Book("a", "qq", 10000);
Book b2 = new Book("b", "ww", 20000);
//.으로 접근하여 출력 메서드 실행
b1.printBook();
b2.printBook();
}
}
6. 문자열 처리 유틸리티 클래스를 통한 메서드 및 메서드 오버로딩 복습을 위한 과제
목표: 문자열 처리 관련 메서드를 구현하고, 메서드 오버로딩을 통해 같은 기능을 다양한 매개변수로 제공할 수 있는 능력을 실습한다.
과제 설명:
- 기본 문자열 처리 메서드 작성
- StringUtil 클래스를 생성하세요. 이 클래스는 문자열 처리와 관련된 다양한 기능을 제공하는 유틸리티 메서드를 포함할 것입니다.
- 다음 기능을 수행하는 메서드를 StringUtil 클래스 내에 작성하세요:
- reverse: 하나의 **String**을 매개변수로 받아 그 문자열을 뒤집은 결과를 반환합니다.
- concat: 두 개의 **String**을 매개변수로 받아 이를 연결한 결과를 반환합니다.
- contains: 하나의 **String**과 하나의 문자를 매개변수로 받아, 주어진 문자열이 해당 문자를 포함하는지 여부를 반환합니다.
- 메서드 오버로딩 실습
- concat 메서드를 오버로딩하여, 세 개의 **String**을 매개변수로 받아 이를 모두 연결한 결과를 반환하는 버전을 추가하세요.
- contains 메서드를 오버로딩하여, 두 개의 **String**을 매개변수로 받아 두 번째 문자열이 첫 번째 문자열을 포함하는지 여부를 반환하는 버전을 추가하세요.
package javabasic.ex6;
public class StringUtil {
//하나의 String을 매개변수로 받아 그 문자열을 뒤집을 결과를 반환하는 메서드
public static String reverse(String str) {
String reverseString = "";
for (int i = str.length() - 1; i >= 0; i--) {
reverseString += str.charAt(i);
}
return reverseString;
}
//두 개의 String을 매개변수로 받아 이를 연결한 결과를 반환하는 메서드
public static String concat(String str1, String str2) {
return str1 + str2;
}
//하나의 String과 하나의 문자를 매개변수로 받아, 주어진 문자열이 해당 문자를 포함하는지 여부를 반환
public static boolean contains(String str, char c) {
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == c) {
return true;
}
}
return false;
//return str.indexOf(c) != -1; //for문을 한 줄로 축약 가능
}
/*
메서드 오버로딩 실습
*/
//concat 메서드를 오버로딩 하여, 세 개의 String을 매개변수로 받아 이를 모두 연결한 결과를 반환하는 메서드
public static String concat(String str1, String str2, String str3) {
return str1 + str2 + str3;
}
//contains 메서드를 오버로딩하여, 두 개의 String을 매개변수로 받아 두 번째 문자열이 첫 번째 문자열을 포함하는지 여부를 반환하는 메서드
public static boolean contains(String str1, String str2) {
for (int i = 0; i <= str2.length() - str1.length(); i++) {
boolean currentStringIsEqual = true;
for (int j = i; j < i + str1.length(); j++) {
if(str2.charAt(j) != str1.charAt(j - i)) {
currentStringIsEqual = false;
}
}
if(currentStringIsEqual) {
return true;
}
}
return false;
//return str2.contains(str1); //for문을 한 줄로 축약 가능
}
}
package javabasic.ex6;
public class StringUtilMain {
public static void main(String[] args) {
//reverse 메서드 실행
System.out.println(StringUtil.reverse("hello"));
//concat 메서드 실행
System.out.println(StringUtil.concat("hello", "java"));
//contains 메서드 실행
System.out.println(StringUtil.contains("hello", 'e'));
//오버로딩된 concat 메서드 실행
System.out.println(StringUtil.concat("hello", "java", "hello"));
//오버로딩된 contains 메서드 실행
System.out.println(StringUtil.contains("hel", "hello"));
}
}
내부 클래스(Inner Class)
이번 챕터에서는 클래스 안의 클래스를 의미하는 내부 클래스에 대해 알아보겠습니다.
다음의 학습 목표를 통해 이번 챕터의 학습 내용을 먼저 확인해 봅시다.
학습 목표
- 내부 클래스의 기본 개념과 장점에 대해 이해할 수 있다.
- 선언 위치에 따른 내부 클래스의 종류와 특징에 대해 이해하고 설명할 수 있다.
- 내부 클래스의 종류에 따른 유효 범위와 접근성을 이해할 수 있다.
내부 클래스
내부 클래스(Inner Class)는 클래스 내에 선언된 클래스로, 외부 클래스와 내부 클래스가 서로 연관되어 있을 때 사용합니다.
내부 클래스를 사용하면 외부 클래스의 멤버들에 쉽게 접근할 수 있고, 코드의 복잡성을 줄일 수 있습니다.
또한 외부적으로 불필요한 데이터를 감출 수 있어 뒤에서 학습하게 될 객체지향의 중요한 핵심 원칙인 캡슐화(encapsulation)를 달성하는 데 유용합니다. 캡슐화에 대한 좀 더 자세한 내용은 추후 학습하도록 하겠습니다.
class Outer { // 외부 클래스
class Inner {
// 인스턴스 내부 클래스
}
static class StaticInner {
// 정적 내부 클래스
}
void run() {
class LocalInner {
// 지역 내부 클래스
}
}
}
위의 코드 예제는 외부 클래스와 그 안에 포함될 수 있는 세 가지의 내부 클래스의 종류를 보여주고 있습니다.
사실 클래스의 선언과 객체의 생성을 동시에 수행하는 일회용 내부 클래스인 익명 내부 클래스도 있지만, 우선순위가 낮기 때문에 편의상 이번 콘텐츠에서는 생략하도록 하겠습니다.
위의 코드 예제로 다시 돌아가서, 세 가지의 내부 클래스의 종류는 각각 인스턴스 내부 클래스, 정적 내부 클래스, 그리고 지역 내부 클래스로 구분할 수 있습니다. 기본적으로 내부 클래스는 외부 클래스 내에 선언된다는 점을 제외하면 일반 클래스와 차이점이 없습니다. 단지 외부 클래스와 내부 클래스가 서로 연관되어 있을 때 사용의 편의성을 고려하여 만들어진 문법 요소입니다.
앞서 언급한 세 가지 내부 클래스는 변수가 선언 위치에 따라 인스턴스 변수, 클래스 변수, 그리고 지역 변수로 구분되는 것과 유사하게 그 위치를 중심으로 구분될 수 있고, 그 유효범위(scope)와 특성이 변수의 그것과 매우 유사하다고 할 수 있습니다.
이제 아래의 표를 통해 선언 위치에 따른 이너 클래스의 구분에 대해서 잠시 살펴보도록 합시다.
멤버 내부 클래스
앞서 학습한 변수와 유사하게, 우리는 인스턴스 내부 클래스와 정적 내부클래스를 하나로 묶어 멤버 내부 클래스라 통칭합니다.
인스턴스 내부 클래스
인스턴스 내부 클래스는 객체 내부에 멤버의 형태로 존재하며, 외부 클래스의 모든 접근 지정자의 멤버에 접근할 수 있습니다. 아래의 코드 예제를 통해 한번 확인해 봅시다.
class Outer { //외부 클래스
private int num = 1; //외부 클래스 인스턴스 변수
private static int sNum = 2; // 외부 클래스 정적 변수
private InClass inClass; // 내부 클래스 자료형 변수 선언
public Outer() {
inClass = new InClass(); //외부 클래스 생성자
}
class InClass { //인스턴스 내부 클래스
int inNum = 10; //내부 클래스의 인스턴스 변수
void Test() {
System.out.println("Outer num = " + num + "(외부 클래스의 인스턴스 변수)");
System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
}
}
public void testClass() {
inClass.Test();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
System.out.println("외부 클래스 사용하여 내부 클래스 기능 호출");
outer.testClass(); // 내부 클래스 기능 호출
}
}
// 출력값
외부 클래스 사용하여 내부 클래스 기능 호출
Outer num = 1(외부 클래스의 인스턴스 변수)
Outer sNum = 2(외부 클래스의 정적 변수)
위의 코드 예제를 하나씩 입력해 보면서 흐름을 이해해봅시다.
구체적인 문법들을 모두 이해하지 않아도 좋고, 전체적인 흐름을 이해하면 충분합니다. 위의 코드 예제를 정리해 보면, 인스턴스 내부 클래스는 외부 클래스의 내부에 위치해 있으며 뒤에서 학습하게 될 private 접근 제어자(해당 클래스 안에서만 접근 가능한 멤버에 사용)를 사용하고 있음에도 내부에서 외부 클래스의 인스턴스 변수와 정적 변수에 각각 접근하여 해당 값을 사용하고 있습니다.
또 한 가지 유의할 사항은 인스턴스 내부 클래스는 반드시 외부 클래스를 생성한 이후에 사용해야 한다는 점입니다. 따라서 클래스의 생성과 상관없이 사용할 수 있는 정적 변수와 정적 메서드는 인스턴스 내부 클래스에서 선언할 수 없습니다.
정적 내부 클래스
직전의 내용을 통해 우리는 내부 클래스가 기본적으로 외부 클래스의 존재에 의존하고 있다는 사실을 알 수 있었습니다. 만약 내부 클래스가 외부 클래스의 존재와 무관하게 정적 변수를 사용할 수 있게 하려면 어떻게 해야 할까요?
이 경우에 사용할 수 있는 것이 바로 정적 내부 클래스입니다. 정적 내부 클래스는 인스턴스 내부 클래스와 동일하게 클래스의 멤버 변수 위치에 정의하지만, static 키워드를 사용한다는 점에서 차이가 있다고 할 수 있습니다. 뭔가 좀 익숙하게 들리지 않나요?
class Outer { // 외부 클래스
private int num = 3; // 외부 클래스의 인스턴스 변수
private static int sNum = 4;
void getPrint() {
System.out.println("인스턴스 메서드");
}
static void getPrintStatic() {
System.out.println("스태틱 메서드");
}
static class StaticInClass { // 정적 내부 클래스
void test() {
System.out.println("Outer sNum = " +sNum + "(외부 클래스의 정적 변수)");
getPrintStatic();
// num 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
}
}
}
public class Main {
public static void main(String[] args) {
Outer.StaticInClass a = new Outer.StaticInClass(); //정적 이너 클래스의 객체 생성
a.test();
}
}
//출력값
Outer sNum = 4(외부 클래스의 정적 변수)
스태틱 메서드
지역 내부 클래스
지역 내부 클래스는 클래스의 멤버가 아닌 메서드 내에서 정의되는 클래스입니다.
지역 내부 클래스도 지역 변수와 유사하게 메서드 내부에서만 사용가능하기 때문에 일반적으로 메서드 안에서 선언 후에 바로 객체를 생성해서 사용합니다.
아래 코드를 통해 좀 더 이해해 보도록 합시다. 마찬가지로 직접 입력하면서 코드의 흐름을 파악할 수 있도록 시도해 보세요.
class Outer { //외부 클래스
int num = 5;
void test() {
int num2 = 6;
class LocalInClass { //지역 내부 클래스
void getPrint() {
System.out.println(num);
System.out.println(num2);
}
}
LocalInClass localInClass = new LocalInClass();
localInClass.getPrint();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
//출력값
5
6
위의 코드 예제를 보면 지역 내부 클래스 LocalInClass가 메서드 안에서 선언되고 생성된 후에 정의된 메서드를 호출하여 외부 클래스의 변수들을 출력하고 있는 것을 확인할 수 있습니다.
앞서 언급한 것처럼, 내부 클래스는 기본적으로 개발자의 편의를 위해 서로 연관 있는 클래스들을 연결시켜 준 것에 지나지 않습니다. 지금 단계에서는 특별한 문법요소에 집중하기보다 '오 이런 것도 있구나'라는 수준에서 참고하고 넘어가고, 나중에 필요한 경우에 다시 돌아와서 내용을 확인하는 것을 권장드립니다.
종합 퀴즈
1
다음 중 클래스와 객체에 대한 설명으로 옳지 않은 것을 고르세요.
- [ ] A.객체는 클래스에 정의된 대로 생성된다.
- [ ] B.객체의 범주에는 눈에 보이지 않는 공식, 사상, 철학, 개념 등 무형의 대상은 포함되지 않는다.
- [ ] C.클래스와 객체의 관계를 설계도와 실제 제품으로 비유할 수 있다.
- [ ] D.클래스를 통해 생성된 객체를 해당 클래스의 인스턴스라 부를 수 있다.
정답 : B
2
다음 중 클래스에 대한 설명으로 옳은 것을 모두 고르세요.
- [ ] A.클래스명은 class 키워드를 사용하여 정의할 수 있고, 주로 대문자로 시작하는 것이 관례이다.
- [ ] B.클래스는 필드와 메서드 두 가지 구성요소로만 이루어져 있다.
- [ ] C.클래스는 객체를 생성하기 위한 일종의 틀이지만, 클래스를 사용하지 않고도 객체를 생성할 수 있다.
- [ ] D.일반적으로 하나의 소스 파일에 하나의 클래스를 정의하는 것이 권장된다.
정답 : A, B, D
3
다음 중 객체에 대한 설명으로 틀린 것을 모두 고르세요.
- [ ] A.객체는 new 키워드를 통해 생성되며, 객체의 멤버에는 포인트 연산자(.)를 통해 접근할 수 있다.
- [ ] B.객체는 자바 메모리 구조 중 클래스 영역에 저장된다.
- [ ] C.객체와 인스턴스는 기본적으로 같지만, 인스턴스는 특정 클래스와의 관계를 강조한다는 점을 강조하는 표현이다.
- [ ] D.동일한 클래스로 만든 모든 객체는 각기 다른 메서드 값을 공유한다.
정답: B, D
4
다음 중 객체에 대해 틀린 것을 고르세요.
- [ ] A.참조변수의 타입은 기본적으로 인스턴스의 타입과 일치해야 한다.
- [ ] B.특정 인스턴스를 참조변수에 할당하면 해당 참조변수에는 할당한 인스턴스의 주소값이 저장된다.
- [ ] C.객체의 속성과 기능은 각각 필드와 메서드로 표현된다.
- [ ] D.같은 클래스로부터 생성된 객체는 반드시 같은 속성값을 가져야 한다.
정답: D
5
다음의 코드 예제는 Cat 클래스를 정의하고, 생성하여 사용하는 과정을 보여줍니다. 다음 보기 중 틀린 것을 고르세요.
public class CatTest {
public static void main(String[] args) {
Cat lucky = new Cat();
lucky.name = "러키";
lucky.color = "고등어";
lucky.age = 4;
Cat haku = new Cat();
haku.name = "하쿠";
haku.color = "그레이";
haku.age = 4;
haku.eat();
lucky.eat();
lucky.cry();
}
}
class Cat {
String name;
String color;
int age;
void sleep() {
System.out.println("쿨쿨💤")
}
void eat() {
System.out.println("냠냠🍚")
}
void cry() {
System.out.println("야옹야옹😺")
}
}
- [ ] A.Cat 클래스를 기반으로 두 마리의 고양이 인스턴스를 생성했다.
- [ ] B.Cat 클래스는 각각 3개의 속성과 기능이 정의되어 있다.
- [ ] C.haku.eat() 과 lucky.eat()는 각각 다른 결과를 출력한다.
- [ ] D.러키는 고등어색 고양이이고, 나이는 4살이다.
정답: C
6
다음 중 필드에 대한 설명으로 옳은 것을 모두 고르세요.
- [ ] A.자바 프로그래밍에서 필드는 객체의 기능을 정의할 때 사용된다.
- [ ] B.필드는 클래스 변수와 인스턴스 변수를 가리키며, static 키워드의 유무로 구분할 수 있다.
- [ ] C.클래스 변수는 각 인스턴스가 가지는 개별적인 속성을 정의할 때 사용된다.
- [ ] D.클래스 변수는 인스턴스 생성 없이 클래스명.멤버명 으로 사용이 가능하다.
정답: B, D
7
자바의 세 가지 변수 유형에 대한 다음의 설명 중 옳은 것을 모두 고르세요.
- [ ] A.자바의 세 가지 변수 유형은 클래스 변수, 인스턴스 변수, 그리고 지역 변수이다.
- [ ] B.인스턴스 변수는 클래스가 메모리에 올라갈 때 생성된다.
- [ ] C.일반적으로 특정 클래스로부터 생성된 모든 객체가 공유하는 속성을 클래스 변수로, 각 인스턴스의 개별적인 속성을 인스턴스 변수로 선언한다.
- [ ] D.지역 변수는 스택 메모리 영역에 저장되며, 반드시 직접 초기화를 시켜주어야 한다.
정답: A, C, D
8
다음 코드 예제의 출력값으로 알맞은 것을 고르세요.
public class CozStudentsTest {
public static void main(String[] args) {
CozStudents cozStudents = new CozStudents();
cozStudents.averageAge = 29;
System.out.println("이번 수강생들의 평균 나이는 " + cozStudents.averageAge + "살입니다.");
nextGeneration(cozStudents.averageAge);
System.out.println("최종적인 다음 수강생들의 평균 나이는 " + cozStudents.averageAge + "살입니다.");
}
static void nextGeneration(int averageAge) {
averageAge = 30;
System.out.println("다음 수강생들의 평균 나이는 " + averageAge + " 살입니다.");
}
}
class CozStudents {
int averageAge;
}
- [ ] A. 이번 기수 수강생들의 평균 나이는 29살 입니다. 다음 기수 수강생들의 평균 나이는 30살 입니다. 최종적인 다음 기수 수강생들의 평균 나이는 29살 입니다.
- [ ] B. 이번 기수 수강생들의 평균 나이는 29살 입니다. 다음 기수 수강생들의 평균 나이는 30살 입니다. 최종적인 다음 기수 수강생들의 평균 나이는 30살 입니다.
- [ ] C. 이번 기수 수강생들의 평균 나이는 29살 입니다. 다음 기수 수강생들의 평균 나이는 29살 입니다. 최종적인 다음 기수 수강생들의 평균 나이는 29살 입니다.
정답: A
9
자바 메서드에 대한 설명으로 틀린 것을 고르세요.
- [ ] A.메서드란 자바 클래스에서 “특정 작업을 수행하는 일련의 명령문들의 집합"을 의미하며, 객체의 기능을 정의할 때 주로 사용된다.
- [ ] B.메서드는 메서드 시그니처와 메서드 바디로 구분할 수 있다.
- [ ] C.메서드의 반환타입이 void인 경우에도 명시적 return 문은 필요하다.
- [ ] D.클래스 내부의 메서드끼리는 별도의 객체 생성 없이 상호 호출이 가능하다.
정답: C
10
다음 중 메서드 오버로딩에 대한 설명으로 틀린 것을 고르세요.
- [ ] A.메서드 오버로딩이란 하나의 클래스 안에 동일한 이름의 메서드를 여러 개 정의하는 것을 의미한다.
- [ ] B.메서드 오버로딩이 성립하기 위해서는 반환 타입도 일치시켜 주어야 한다.
- [ ] C.메서드 오버로딩의 대표적인 예시로 PrintStream 클래스의 println 메서드가 있다.
- [ ] D.메서드 오버로딩이 성립되려면 메서드의 이름이 같아야 하고, 매개변수의 개수 또는 타입이 달라야한다.
정답: B
11
다음 중 생성자에 대한 설명으로 틀린 것을 고르세요.
- [ ] A.생성자는 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드이다.
- [ ] B.생성자의 이름은 반드시 클래스 이름과 같아야하며, 리턴 타입을 가지지 않는다.
- [ ] C.생성자는 특수한 목적을 가진 메서드이기 때문에 메서드 오버로딩이 불가능하다.
- [ ] D.모든 클래스에는 반드시 하나 이상의 생성자가 존재해야 한다.
정답: C
12
다음 중 this 키워드와 this() 메서드에 대한 설명으로 옳지 않은 것을 모두 고르세요.
- [ ] A.this 키워드는 객체 자신을 의미하는 참조변수이며, 이를 통해 객체 자신의 변수에 접근 가능하다.
- [ ] B.클래스 메서드 안에서도 this 를 사용할 수 있다.
- [ ] C.this() 메서드는 생성자 외부에서도 사용될 수 있다.
- [ ] D.생성자 내에서 this(...)을(를) 호출하는 것은 같은 클래스 내의 또 다른 생성자를 호출하는 것과 같다.
정답: B, C
13
다음의 코드를 보고 틀린 것을 모두 고르세요.
public class PersonTest {
public static void main(String[] args) {
Person kimcoding = new Person("김코딩", "프로그래머", 30);
Person nahacker = new Person("나해커", "해커", 32, true);
int age = kimcoding.getAge();
System.out.println("김코딩의 나이는 " + age + "살 입니다.");
}
}
class Person {
private String name;
private String occupation;
private int age;
private boolean isSingle;
Person(){
System.out.println("첫 번째 생성자");
};
Person(String name, String occupation, int age) {
this(name, occupation, age, false);
System.out.println("두 번째 생성자");
}
Person(String name, String occupation, int age, boolean isSingle) {
System.out.println("세 번째 생성자");
this.name = name;
this.occupation = occupation;
this.age = age;
this.isSingle = isSingle;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- [ ] A.PersonTest 의 main 메서드를 실행하면 가장 먼저 “두 번째 생성자"라는 문구가 출력된다.
- [ ] B.Person 클래스는 총 3 개의 생성자를 가지고 있다.
- [ ] C.PersonTest 의 main 메서드를 실행하면 “세 번째 생성자"라는 문구가 두 번 출력된다.
- [ ] D.김코딩은 싱글이다.
정답: A
[실습] 개요
🕹️ 텍스트 RPG 게임 프로그램 만들기
거대한 세계관과 화려한 그래픽을 가지고 있는 지금의 게임은 과거 TRPG 시절로 올라가게 됩니다. 게임을 수행하는 사용자들은 텍스트 기반으로 캐릭터의 이름, 공격력, 방어력, 체력 등을 직접 입력하며 상대방과 겨루거나 혹은 **텍스트 기반**의 모험 시나리오를 진행하게 됩니다. **본 예제**는 과거 TRPG 일부를 재현해 보도록 하겠습니다.
🖥 [텍스트 스타크래프트 프로그램 ] 출력 예시
[안내] TRPG 스타크래프트 시작합니다.
[안내] 자신의 유닛 정보를 입력해 주세요.
[시스템] 유닛 [이름] 을 입력해 주세요 :레이스
[시스템] 유닛 [공격력] 을 입력해 주세요 : (Ex 50)50
[시스템] 유닛 [방어력] 을 입력해 주세요 : (Ex 1)1
[시스템] 유닛 [체력] 을 입력해 주세요 : (Ex 100)100
[안내] 생성된 유닛 정보는 다음과 같습니다.
[안내] 레이스 유닛이 게임에 참여하였습니다.
[공격력] : 50
[방어력] : 1
[체력] : 100
========================================
[안내] 상대 유닛 정보를 입력해 주세요.
[시스템] 유닛 [이름] 을 입력해 주세요 :히드라
[시스템] 유닛 [공격력] 을 입력해 주세요 : (Ex 50)50
[시스템] 유닛 [방어력] 을 입력해 주세요 : (Ex 1)1
[시스템] 유닛 [체력] 을 입력해 주세요 : (Ex 100)100
[안내] 생성된 유닛 정보는 다음과 같습니다.
[안내] 히드라 유닛이 게임에 참여하였습니다.
[공격력] : 50
[방어력] : 1
[체력] : 100
========================================
----------------------------------------
[안내] [레이스]유닛이 [공격] 하였습니다.
[안내] 상대 유닛의 남은 [체력]은 50 입니다.
----------------------------------------
[안내] [레이스]유닛이 [공격] 하였습니다.
[안내] 상대 유닛의 남은 [체력]은 0 입니다.
----------------------------------------
[안내] 더 이상 공격할 수 없습니다.
[안내] 상대 유닛이 제거되었습니다.
👾 [블리자드]에서 보내준 프로그래밍 요청서
✏️ [TRPG 프로그램 기능] 본 프로그램은 아래와 같이 네 가지 기능을 추가해 주세요.
- 나의 유닛과 상대 유닛의 정보를 직접 입력할 수 있도록 해주세요.
- 입력된 정보는 출력하여 확인할 수 있도록 해주세요.
- 공격 기능은 꼭 넣어주세요.
- 체력이 0 이하로 떨어지면 게임이 종료되도록 해주세요.
🛠 [김러키]에게서 내려온 프로그래밍 참고사항
⚙ 위 내용은 어떻게 코딩할 수 있을까요?
▼ [ 1단계 ] 프로그램에 필요한 기능들 생각해 보기
프로그램을 제작하기 전 어떠한 기능들이 있는지 생각해 봅니다. 기능 구현 난이도를 생각하며 정렬해 봅시다.
유닛 생산 → 유닛 정보 확인 → 유닛 공격
#입력 #출력 #메서드 #변수 타입 전환 #공격 정책(연산)
▼ [ 2단계 ] 프로그램에 필요한 기능 혹은 적용하고 싶은 개념들을 사전 학습하기
해당 프로그램에서는 this라는 개념을 공부하고 적용해 보도록 하겠습니다.
🤔 this?
📚 클래스의 외부에서 멤버(필드, 메서드, 내부 클래스)를 호출하기 위해서는 객체를 먼저 생성한 후 참조 변수명.멤버명의 형태로 호출하지만, 클래스 내부에서는 객체의 생성 없이 필드와 메서드를 바로 사용할 수 있다고 했습니다.
하지만 모든 사용할 수 있는 상태의 멤버는 항상 객체 내에만 존재합니다. 그렇다면 어떻게 클래스 내부에서는 객체를 생성하지 않고 바로 필드와 메서드를 사용할 수 있을까요?
💡 내부 객체 참조 변수명인 this 키워드
우리는 클래스 내부에서도 객체 안의 멤버를 사용해 왔습니다. 즉, ‘참조 변수명.멤버명’의 형태를 사용해 온 셈입니다. 다만 객체를 직접 만들지 않은 것뿐입니다.
모든 메서드에는 자신이 포함된 클래스의 객체를 가리키는 this라는 참조 변수가 있습니다.
모든 멤버는 객체 속에 존재하는 것이므로 우리가 int m = 3이라는 필드를 클래스 내부에서 출력하고자 할 때도 System.out.println(this.m)과 같이 작성해야 합니다. 다만 this. 를 생략하면 컴파일러가 자동으로 this. 를 추가해 주기 때문에 지금까지 클래스 내부에서 필드와 메서드를 그대로 사용할 수 있던 것입니다. 지역 변수는 멤버가 아니므로 this. 가 자동으로 붙지 않습니다.
📚 예제 코드를 통해 공부해 보기
class A{
// 변수(m,n)를 선언합니다.
// 현재 아래 변수에는 아무 값이 들어있지 않습니다.
int m,n;
// init() 메서드 선언
// int 타입의 a, b를 전달받습니다.
void init(int a, int b) {
// 넘겨받은 a,b 값을 위에 정의된 필드 m과 n에 대입합니다.
this.m = a;
this.n = b;
}
// work() 메서드 선언
// 본 클래스에 있는 메서드를 호출 합니다.
void work() {
// 내부 클래스의 init() 메서드 호출
this.init(2, 3);
}
}
public class This_ex {
public static void main(String[] args) {
// A 클래스 객체 생성
A a = new A();
System.out.println(a.m); // 0
System.out.println(a.n); // 0
// a 객체의 work() 메서드 출력
a.work();
System.out.println(a.m); // 2
System.out.println(a.n); // 3
}
}
또한 work() 메서드에서는 init() 메서드를 호출했습니다. 이렇게 클래스 내부에서 멤버인 필드와 메서드를 호출할 때 실제로는 this.m, this.n 그리고 this.init()와 같이 표현돼야 하며, this. 를 생략했을 때 자동으로 추가되는 것입니다.
이상의 내용을 다시 한번 정리하면, 모든 멤버는 활용할 때 소속과 함께 표기해야 하며, 클래스 내부에서 멤버를 활용할 때 소속을 표기하지 않으면 컴파일러가 자동으로 this. 를 붙여 줍니다.
this. 를 생략해도 항상 컴파일러가 추가해 주므로 굳이 신경 쓸 필요가 없어 보이지만, 그렇지 않습니다. this. 를 명시적으로 붙여줘야 할 때가 있기 때문입니다.
필드명은 m, n이며 init(int m, int n) 메서드에도 지역 변수 m과 n이 있습니다. init() 메서드에서는 입력받은 지역 변수 m과 n의 값을 필드 m과 n에 각각 대입하고자 합니다. 먼저 필드와 지역 변수의 사용 범위를 알아보겠습니다.
필드 m, n은 클래스 내부에 선언돼 있으며, 클래스 전체에서 사용할 수 있습니다. 반면 init() 메서드에서 선언된 지역 변수 m, n은 init() 메서드 내부에서만 사용할 수 있습니다.
따라서 init() 메서드 내부에서는 필드 m, n과 지역 변수 m, n을 모두 사용할 수 있게 됩니다. 그렇다면 init() 메서드 내부에서 필드 m, n을 사용하면 이는 지역변수일까요, 필드일까요?
지역 변수와 필드 모두를 사용할 수 있는 영역에서는 사용 범위가 좁은 변수, 즉 지역 변수로 인식합니다. 따라서 init() 메서드 안에서 m = m, n = n과 같이 작성하면 컴파일러는 이들 모두를 지역 변수로 인식하므로 this. 는 당연히 추가되지 않을 것입니다.
class A{
// 필드 생성
int m,n;
// 메서드 생성
void init(int m, int n) {
// 매개변수는 지역변수에 할당되어 집니다.
// 즉 init() 에서만 사용 가능
m = m;
n = n;
}
}
public class This_ex {
public static void main(String[] args) {
// 필드명과 지역 변수명이 같고, this 키워드를 사용하지 않음.
// 필드는 값을 초기화 하지 않을 때 JVM이 강제 값 초기화(0).
// 객체 생성
A a = new A();
// 객체의 m, n을 출력(현재 초기화만 진행)
System.out.println(a.m); // 0
System.out.println(a.n); // 0
// init()메서드에 2와 3을 각각 할당
// 해당 값은 메서드에서만 활용가능
a.init(2, 3);
// 우리가 호출한 값은 클래스 내부에 있는 필드값.
System.out.println(a.m); // 0
System.out.println(a.n); // 0
}
}
지역 변수에 지역 변수값을 다시 대입하는 형태이므로 필드값은 전혀 변화가 없습니다. 따라서 다음과 같이 객체를 생성한 후 메서드를 호출하고 필드값을 확인하면 모두 값이 0으로 나옵니다.
따라서 의도한 바와 같이 넘겨받은 지역 변수 m, n의 값을 필드 m, n에 대입하기 위해서는 다음과 같이 this.m = m, this.n = n과 같이 필드에 this. 를 붙여 표기해야만 합니다.
class B{
int m;
int n;
void init(int m, int n) {
// 필드에 존재하는 m,n 값에 대입
this.m = m;
this.n = n;
}
}
public class Class_cons {
public static void main(String[] args) {
// 필드명과 지역 변수명이 같고, this 키워드를 사용함.
B b = new B();
b.init(2, 3);
System.out.println(b.m); // 2
System.out.println(b.n); // 3
}
}
이러한 문제점은 지역 변수와 필드명이 동일하기 때문에 발생합니다. 애초에 이름이 서로 달랐다면 this.m = m과 같이 필드와 지역 변수를 명시적으로 구분할 필요가 없겠지만, 자바에서 제공하는 대부분의 API에는 메서드의 지역 변수명이 필드명과 동일하게 구성돼 있습니다.
따라서 this.m = m과 같은 표현은 앞으로도 계속 보게 될 형식입니다.
▼ [3단계 ] 프로그램의 순서를 생각하고 그려보기
유닛 생산 → 유닛 정보 확인 → 유닛 공격