자바칩

[자바의 신] 중간 점검 및 실습: 요약 문제, 실습 문제 본문

Study/Java

[자바의 신] 중간 점검 및 실습: 요약 문제, 실습 문제

아기제이 2024. 5. 11. 21:26
728x90

요약 문제

 

1. 참조 자료형(Reference type)과 기본 자료형(Primitive type)의 차이를 정리해 주세요.

참조 자료형(Reference type) 기본 자료형(Primitive type)
- 기본 자료형 외의 모든 타입은 참조 자료형
- 개발자가 클래스로 직접 생성 가능
- String 클래스만 + 연산 사용 가능하고, 나머지는 모두 불가능
- Pass by Reference: 매개변수로 전달할 때 참조 객체(주소값)를 전달하기 때문에 값 변경 가능
- byte, short, int, long, float, double, char, boolean 타입
- 이 외에는 개발자가 직접 생성 불가능
- 모든 사칙 연산(+, -, *, /, % 등) 가능
- Pass by Value: 매개변수로 전달할때 값만 전달하기 때문에 값 변경 불가능

 

 

2. 기본 자료형 8가지를 나열하고 각 타입의 특징을 정리해 주세요.

기본 자료형 타입 특징
byte 정수형 1바이트(8비트). 기본값: 0. 범위: -2^7 ~ 2^7 - 1 (= -128 ~ 127)
short 정수형 2바이트(16비트). 기본값: 0. 범위: -2^15 ~ 2^15 - 1
int 정수형 4바이트(32비트). 기본값: 0. 범위: -2^31 ~ 2^31 - 1
long 정수형 8바이트(64비트). 기본값: 0L. 범위: -2^63 ~ 2^63 - 1
char 정수형 2바이트(16비트). 기본값: '\u0000'. 범위: 0 ~ 2^16 - 1. ASCII 값을 문자로 표현 가능
float 소수형 4바이트(32비트). 기본값: 0.0f. 부호 비트 1개, 지수 비트 8개, 가수 비트 23개
double 소수형 8바이트(64비트). 기본값: 0.0d. 부호 비트 1개, 지수 비트 11개, 가수 비트 52개
boolean 값은 true 또는 false만 존재. 기본값: false

 

 

3. 형 변환이란 무엇이고 왜 해야 하나요?

타입을 변경하는 것을 형 변환이라고 한다.
byte에서 int로 형 변환을 할 때에는 작은 범위에서 큰 범위로 변환하는 것이기 때문에 자동으로 되지만,
int에서 byte로 형 변환을 할 때에는 큰 범위에서 작은 범위로 변환하는 것이기 때문에 예상하지 못한 값으로 변형이 될 수도 있다. 이것을 개발자가 감안한다는 가정 하에 형 변환한다는 것을 직접 명시해 주어야만 한다.

*사용 예시
byte byteValue = 127;       
int intValue = byteValue + 1;       
byteValue = (byte)intValue;       
System.out.println(byteValue);     

예상 출력 값은 128이라고 생각하지만, 실제 출력 값은 -128이다.
왜냐하면 byte의 범위는 -128 ~ 127이기 때문에, 128이라는 값을 처리할 수가 없다.
128은 비트로 표시하면 다음과 같다.
10000000
byte 범위는 양수는 01111111(127)까지만 사용 가능하기 때문에, 맨 왼쪽 비트는 부호 비트로 취급한다.
즉, 10000000은 -128로 취급해버린다.

 

 

4. if 문의 용도는 무엇이며, if-else와 if-else if는 어떤 점이 다른지 정리해 주세요.

if 문은 조건 별로 분기하기 위해 사용한다.

if-else는 한 가지 조건식으로만 나눌 수 있을 때 사용한다.
if-else는 if 문의 조건식이 참이면 if문이 수행되고, 거짓이면 else문이 수행된다.

if-else if는 조건식을 여러개로 나누어야 할 경우에 사용한다.
if-else if는 if 문의 조건식이 참이면 if문이 수행되는 것 까지는 똑같은데, 거짓이면 그 다음 else if 문으로 넘어가고, 해당 else if 문의 조건식이 거짓이면 다음 else if 문으로 넘어가고, 만약 해당 else if 문의 조건식이 참이면 아래에 있는 else if문 또는 else 문을 건너뛰고 if-else if 문을 탈출한다. 만약 모든 else if 문의 조건식이 거짓이라면, 가장 마지막 else 문을 수행한다.

*if-else
if (boolean 타입의 조건식) {
        // 수행 문장
} else {
        // 수행 문장
}

*if-else if
if (boolean 타입의 조건식1) {
        // 수행 문장
} else if (boolean 타입의 조건식2) {
        // 수행 문장
} else if (boolean 타입의 조건식3) {
        // 수행 문장
} else {
        // 수행 문장
}

 

 

5. switch-case 문의 용도를 정리해 주세요.

switch-case 문도 if 문처럼 조건 별로 분기하기 위해 사용한다.
if 문과의 차이점은 switch-case 문은 해당 조건이 참이면 break 문이 있어야 switch-case 문을 빠져나갈 수 있다.
그리고, if 문은 boolean 타입의 조건식으로 판별하는데, switch-case 문은 비교 대상 변수(정수형, String 타입)로 판별한다.

switch (정수형 또는 String 타입의 비교 대상 변수) {
    case 점검값1:
        // 수행 문장
        break;
    case 점검값2:
        // 수행 문장
        break;
    case 점검값3:
        // 수행 문장
        break;
    default:
        // 수행 문장
        break;
}

 

 

6. for, do-while, while을 어떻게 사용하는지 1부터 10까지 더하는 코드를 예로 들어 정리해 주세요.

 

*for 사용법

        int sum = 0;
        for (int n = 1; n <= 10; n++) {
            sum += n;
        }
        System.out.println(sum); 

 

*do-while 사용법

        int sum = 0, n = 1;
        do {
            sum += n;
            n++;
        } while (n <= 10);
        System.out.println(sum);

 

*while사용법

        int sum = 0, n = 1;
        while (n <= 10) {
            sum += n;
            n++;
        }
        System.out.println(sum);

 

 

7. 학생의 학점이나 등수를 String 배열에 넣고 출력하는 코드를 작성해주세요.

        String[] gradeAndRanks = {"4.5점, 1등", "4.0점, 2등", "3.5점, 3등", "3.0점, 4등"};
        for (String gradeAndRank : gradeAndRanks) {
            System.out.println(gradeAndRank);
        }

 

 

8. 생성자는 무엇을 하는 데 사용하는 것이며, 별도로 만들지 않아도 자동으로 생성되는 생성자에 대해서 정리해 주세요.

생성자는 객체를 생성할 때 사용하고, 별도로 만들지 않아도 자동으로 생성되는 생성자는 매개변수가 없는 기본 생성자이다.
하지만 만약 매개변수가 있는 생성자를 클래스에 구현해 놓았다면, 객체가 없는 생성자로 객체를 생성하려고 할 때에는 자동으로 기본 생성자를 생성해주지 않아서 컴파일 에러가 발생한다.

*사용 예시

public class Example {
    // 생성자를 만들지 않아도 컴파일러가 기본 생성자를 자동으로 생성
}
public static void main(String[] args) {
    Example example = new Example();
}

Example 클래스에 생성자를 구현하지 않았지만, 컴파일러는 자동으로 매개변수가 없는 기본 생성자를 생성해준다. 하지만 다음과 같은 경우는 컴파일 에러가 난다.

public class Example {
    public Example(int n) {}    // 매개변수가 있는 생성자를 구현해놓았으면
}
public static void main(String[] args) {
    Example example = new Example();    // 기본 생성자로 객체를 생성하려고 할 때 컴파일 에러 발생
}

 

 

9. Overloading은 무엇인가요? public void setData(int a)라는 메소드를 원하시는 대로 Overloading 해 주세요.

Overloading은 메소드의 이름은 같은데, 매개변수의 타입 및 개수가 다른 메소드를 1개 이상 선언하는 것이다.

public void setData(int a) {}
public void setData(int a, int b) {}
public void setData(double d) {}
public void setData(String str) {}

 

 

10. 패키지를 선언하는 위치와 이름을 지정할 때의 유의점을 정리해 주세요.

패키지는 반드시 코드의 맨 윗줄에 package 문을 사용해서 선언해야 한다.
패키지 이름은 반드시 java나 javax가 포함된 이름은 지으면 컴파일이 되지 않는다. 그리고 패키지의 하위 패키지까지 접근할 때에는 import 상위패키지.하위패키지1.하위패키지2 같은 방법으로 .을 찍어가면서 선언한다.
그 외에 반드시 지켜야 하는 규칙은 아니지만, 암묵적으로 개발자들끼리 정한 규칙은 패키지 이름은 모두 소문자여야 한다.

 

 

11. 다른 패키지에 선언된 클래스를 사용하기 위한 import는 어디 위치에 선언해야 하며, import static은 무엇인지 정리해 주세요.

package 문과 class 선언 사이에 import 를 선언해야 한다.
import static은 클래스 내에 static으로 선언한 변수나 메소드에 접근하려고 할 때, 그 변수나 메소드 이름만 코드에 작성하려고 할 때 사용한다.

*사용 예시
package mine;
public class Example {
    public static final double PI = 3.14;
    public static void method() {}
}

import static mine.Example.PI;
import static mine.Example.method;

public static void main(String[] args) {
    System.out.println(PI);
    method();
}

 

 

12. 클래스란 무엇인가요? 다음의 단어들이 포함되는 문장을 작성하고, 주어진 단어의 의미도 같이 정리해 주세요(속성, 상태).

클래스는 상태(state)와 행위(behavior)를 갖는 자바의 단위이다. 상태는 클래스의 속성(변수)을 의미하고, 행위는 클래스의 메소드를 의미한다.

 

 

13. 인터페이스, abstract 클래스, 클래스, Enum 클래스가 있는데 각각의 특징 및 다른 점을 정리해 주세요.

인터페이스 public interface ExamInterface {
    public void method1();    // 메소드 선언 필수
    public void method2();
}
abstract 클래스 public abstract class ExamAbstractClass {
    public abstract void method1();    // 메소드 선언 가능 (반드시 abstract로 선언해야 함)
    public void method2() { System.out.println("method2"); }    // 메소드 구현 가능
}
클래스 // 인터페이스의 메소드를 구현하고자 할 때에는 implements,
// abstract 클래스의 메소드를 구현하고자 할 때에는 extends 사용
public class ExamClass extends ExamAbstractClass implements ExamInterface {
    public void method1() { System.out.println("method1"); }    // 메소드 구현 필수
    public void method2() { System.out.println("method2"); }    // 메소드 구현 필수
}
Enum 클래스 - 상수 열거형 클래스
- 상수만 선언할 수도 있고, 상수를 생성자로 초기화해서 선언할 수도 있음
- enum 클래스의 모든 부모 클래스는 java.lang.Enum. 명시해주지 않아도 자동 상속
- 이 외의 다른 클래스는 상속받을 수 없음
- 다른 메소드 구현 가능
- enum클래스명.상수이름 으로 enum 객체 생성 가능. 생성자로는 객체 생성 불가능

public enum ExamEnum {    // extends로 다른 클래스를 상속받을 수 없음
    JANUARY(1),
    FABRUARY(2),
    MARCH(3),
    APRIL(4);

    private final int month;
   
    // 생성자의 접근 제어자를 private, package-private 만 사용 가능
    // 각 상수를 enum 클래스 내에서 선언할 때에만 사용 가능
    ExamEnum(int month) {   
        this.month = month;
    }
}
  인터페이스 abstract 클래스 클래스
선언 시 사용하는 예약어 interface abstract class class
구현되지 않은 메소드 포함 가능 여부 O (필수) O X
구현된 메소드 포함 가능 여부 X O O (필수)
상속(extends) 가능 여부 X O O
구현(implements) 가능 여부 O X X
static 메소드 선언 가능 여부 X O O
final 메소드 선언 가능 여부 X O O
인스턴스/클래스 변수 사용 가능 여부 O O O

 

 

14. instanceof 라는 연산자의 용도를 정리해 주세요.

해당 객체의 클래스 타입을 확인하려고 할 때 사용한다.
객체 instanceof 클래스
이렇게 사용 가능하다. instanceof 연산자의 리턴 타입은 boolean이다.

*사용 예시

public class Parent {
    public void method() { System.out.println("Parent"); }
}

public class Child extends Parent {
    int value = 1;
    public void method() { System.out.println("Child"); }
}

Parent[] parent = new Parent[3];
parent[0] = new Child();
parent[1] = new Parent();
parent[2] = new Child();

for (Parent tempParent : parent) {
    if (tempParent instanceof Child) {
        tempParent.method();
        System.out.println(tempParent.value);
    } else if (tempParent instanceof Parent) {
        tempParent.method();
    }
}

이렇게 부모 클래스 타입의 배열에 부모 클래스 타입도 넣고 자식 클래스 타입도 넣은 뒤에 instanceof로 타입을 확인하고, 해당하는 타입에 맞는 변수와 메소드를 수행 가능하게 해준다. 만약 이런 것이 없다면, 배열로 넘어오는 객체의 타입에 맞는 메소드를 일일히 다 만들어주어야 하는 불편함이 발생한다.

 

 

15. 어떤 클래스를 상속받아 확장을 하면, 부모 클래스의 어떤 것들을 사용할 수 있는지 정리해 주세요.

부모 클래스를 자식 클래스가 상속받아서 확장하면, 부모 클래스의 선언된 변수와 메소드(private 제외)를 모두 자식 클래스의 변수와 메소드인 것처럼 사용 가능하다. 

 

 

16. 변수를 final로 선언하는 것이 어떤 의미가 있는지 정리해 주세요.

변수를 final로 선언하면 해당 변수의 값을 더 이상 변경할 수 없다. 만약 어떤 변수의 값을 변경하게 된다면 프로그램에 영향을 미칠 수도 있는 상황이 생길 때 사용한다. 그러므로 절대 변경될 일이 없을 때 사용하면 된다. 만약 이 값이 한번 정해지고 난 이후에 또 변경하려고 할 때에는 컴파일 에러가 발생한다. final로 선언한 변수는 고정값이어서, "상수"라고도 부른다.

*사용 예시
public static final double PI = 3.14;  

 

 

17. 클래스를 final로 선언하는 것이 어떤 의미가 있는지 정리해 주세요.

클래스를 final로 선언하면 해당 클래스를 더 이상 상속할 수 없다. 즉, 다른 클래스가 해당 클래스를 extends로 확장할 수 없다. 만약 해당 클래스를 다른 클래스가 상속해서 메소드를 overriding하여 내용을 변경하면 프로그램에 영향을 미칠 수도 있는 상황이 생길 때 사용한다. 클래스를 final로 선언한 대표적인 예시로는 String 클래스가 있다.

*사용 예시
public final class Example {}

 

 

18. 변수를 static으로 선언하는 것이 어떤 의미가 있는지 정리해 주세요.

변수를 static으로 선언하는 것을 클래스 변수라고 한다. static 변수가 선언된 클래스가 있을 때, 클래스 타입이 같은 객체들은 모두 static 변수를 공유한다. 만약 처음에 객체1을 생성할 때 static 변수를 초기화해주고, 객체2를 생성할 때 static 변수를 초기화했을 때, 이후에 객체1에서 static 변수의 값을 얻어올 때는 객체2에서 초기화한 static 변수의 값을 얻어온다. 또한, static으로 선언을 하면 클래스이름.static변수 로 사용 가능하다.

*사용 예시

public class Person {
    static String name;
    public Person(String name) {
        this.name = name;
    }
}

Person person1 = new Person("박성훈");
Person person2 = new Person("박종성");
System.out.println(person1.name);            // 박종성
System.out.println(person2.name);            // 박종성
System.out.println(Person.name);              // 박종성

이처럼 static 변수는 모든 객체가 공유를 하므로 가장 마지막에 참조된 값이 출력이 된다.
그러므로, 마지막 출력문처럼 클래스이름.static변수명 으로 사용하면 된다.

 

 

19. 메소드를 static으로 선언하는 것이 어떤 의미가 있는지 정리해 주세요.

메소드를 static으로 선언하면 변수를 static으로 선언한 것 처럼, 클래스 타입이 같은 객체들은 모두 static 메소드를 공유한다. 클래스이름.static메소드명()으로 사용 가능하다.

 

 

20. try-catch-finally 블록은 왜 사용하고 각각의 블록이 어떤 용도로 사용되는지 정리해 주세요.

try-catch-finally 블록은 예외가 발생했을 때를 대비하여, 예외 처리를 해주기 위해서 사용한다.

*사용 예시

int[] num = new int[5];
try {
    System.out.println(num[5]);    // 여기서 예외가 발생하고, 바로 catch 문으로 넘어감
    System.out.println("이것은 출력이 되지 않습니다.");    
} catch (NullPointerException e) {    
    // 발생한 예외가 NullPointerException에 해당할 경우, 해당 catch 문 수행
} catch (ArrayIndexOutOfBoundsException e) {    
    // 발생한 예외가 ArrayIndexOutOfBoundsException에 해당할 경우, 해당 catch 문 수행
    System.out.println(num.length);
} catch (Exception e) {        
    // 해당하는 예외가 앞의 catch 문에 모두 해당이 안되었다면, 해당 catch 문 수행
} finally {    
    System.out.println("이것은 무조건 출력이 됩니다.");
}
System.out.println("이것은 무조건 출력이 됩니다.");

사이트가 5인 배열에 5번째 인덱스를 접근하면 배열 인덱스 범위 초과 에러(ArrayIndexOutOfBoundsException)가 발생하여 2번째 catch 문이 수행된다. 그리고 아래의 catch 문을 수행하지 않고 바로 finally 문으로 간다.
참고로 catch 문 선언 순서는 매우 중요하다. 항상 Exception 클래스의 예외를 맨 마지막 catch 문에 선언해야 한다. 만약 Exception 클래스를 catch 문 중에 가장 먼저 선언하면, 모든 예외의 부모 클래스인 Exception 클래스는 예외가 발생하면 어떤 예외든 받을 수 있기 때문에, 그 밑에 있는 catch 문에 있는 자식 클래스 예외는 절대 수행될 일이 없다. 그러므로 컴파일러는 왜 쓸데없는 것을 선언했냐면서 에러를 발생시킨다.

 


실습 문제

 

*Employee.java

package c.middle;

public class Employee {
    private String name;    
    private int type;    
    private long salary;    

    public Employee(String name, int type, long salary) {
        this.name = name;
        this.type = type;
        this.salary = salary;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setType(int type) {
        this.type = type;
    }

    public void setSalary(long salary) {
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public int getType() {
        return type;
    }

    public long getSalary() {
        return salary;
    }
}

 

*CalculateSalary.java

package c.middle;

public class CalculateSalary {
    public static void main(String[] args) {
        CalculateSalary calculateSalary = new CalculateSalary();
        calculateSalary.calculateSalaries();
    }

    public long getSalaryIncrease(Employee employee) {
        int type = employee.getType();
        long salary = employee.getSalary();
        long salaryIncrease = salary;
       
        switch (type) {
            case 1:     // owner
                salaryIncrease += (long)(salary * -0.95);
                break;
            case 2:     // manager
                salaryIncrease += (long)(salary * 0.1);
                break;
            case 3:     // designer
                salaryIncrease += (long)(salary * 0.2);
                break;
            case 4:     // architect
                salaryIncrease += (long)(salary * 0.3);
                break;
            case 5:     // developer
                salaryIncrease += salary * 1;
                break;
        }

        return salaryIncrease;
    }

    public void calculateSalaries() {
        Employee[] employees = new Employee[5];

        employees[0] = new Employee("LeeDaeRi", 1, 1000000000);
        employees[1] = new Employee("KimManager", 2, 100000000);
        employees[2] = new Employee("WhangDesign", 3, 70000000);
        employees[3] = new Employee("ParkArchi", 4, 80000000);
        employees[4] = new Employee("LeeDevelop", 5, 60000000);

        for (Employee employee : employees) {
            long salaryIncrease = getSalaryIncrease(employee);
            employee.setSalary(salaryIncrease);
            System.out.println(employee.getName() + " = " + employee.getSalary());
        }
    }
}