기본 콘텐츠로 건너뛰기

방송대 방통대 Java프로그래밍 3강 - Java 기본 문법(2)과 클래스와 상속(1) - 요약 노트 시험족보 예상문제 - 올에이클래스

Java프로그래밍 3강 - Java 기본 문법(2)과 클래스와 상속(1)

Java프로그래밍 3강 - Java 기본 문법(2)과 클래스와 상속(1)

같은 자료형의 데이터를 묶어 처리하는 배열과 문자열, Scanner를 이용한 키보드 입력을 학습한다. 이어서 클래스의 데이터 필드와 메소드, 접근 제어자, 객체 생성과 멤버 사용, 생성자와 기본 생성자의 동작을 살펴본다.

1. 배열의 개념과 선언

배열(array)은 같은 자료형의 원소를 정해진 개수만큼 모아 두는 객체이다. 여러 값을 하나의 배열 이름으로 관리하고 각 원소에는 0부터 시작하는 인덱스로 접근한다. 배열의 크기는 배열 객체를 생성할 때 결정되며 생성된 뒤에는 바꿀 수 없다.

배열 변수는 배열 객체 자체가 아니라 배열을 가리키는 참조를 저장한다. 선언만 한 배열 변수에는 아직 실제 원소 공간이 없으며, new로 배열 객체를 만들거나 배열 초기화식을 사용해야 한다.

배열 선언 형식

int[] a;
int b[];

int[][] table;
int matrix[][];

대괄호는 자료형 뒤 또는 변수 이름 뒤에 둘 수 있으나 선언문 안의 대괄호에 배열 크기를 쓰지 않는다. 따라서 int[10] a;와 같은 선언은 허용되지 않는다. Java에서는 int[] a;로 참조변수를 선언한 뒤 a=new int[10];처럼 객체 생성 시 크기를 지정한다.

int[] a, b;는 a와 b가 모두 int 배열 변수이다. 반면 int a[], b;는 a만 배열이고 b는 일반 int 변수이므로, 혼동을 줄이기 위해 대괄호를 자료형 뒤에 두는 표기를 많이 사용한다.

2. 배열 초기화와 생성

선언과 동시에 초기화

배열을 선언하면서 중괄호 안에 원소를 나열하면 컴파일러가 원소 개수에 맞는 배열 객체를 만들고 값을 저장한다.

int[] primes = {2, 3, 5, 7, 11};
int[][] scores = {
    {90, 85, 80},
    {75, 88, 92}
};

중괄호만 사용하는 배열 초기화식은 선언과 동시에 사용할 때 가능하다. 이미 선언된 변수에 나중에 primes={13,17,19};처럼 대입할 수는 없다. 이 경우에는 primes=new int[]{13,17,19};처럼 명시적으로 새 배열을 만들어야 한다.

new를 이용한 배열 생성

int[] a = new int[3];
int[] b;
b = new int[10];

int[][] grid = new int[3][2];

기본 자료형 배열은 생성과 동시에 각 자료형의 기본값으로 초기화된다. int 배열 원소는 0, double은 0.0, boolean은 false, char는 널 문자 값으로 초기화된다. 참조 자료형 배열의 각 원소는 null로 초기화된다.

구분: int[] a;는 배열 참조변수만 선언한다. new int[3]은 길이 3의 실제 배열 객체를 만들고 모든 원소를 0으로 초기화한다.

3. 배열 원소와 length

배열 원소는 배열이름[인덱스]로 접근한다. 인덱스는 0부터 시작하므로 길이가 n인 배열에서 유효한 인덱스는 0부터 n-1까지이다. 범위를 벗어나면 실행 중 ArrayIndexOutOfBoundsException이 발생한다.

모든 배열 객체에는 원소 개수를 나타내는 length 필드가 있다. 메소드가 아니므로 괄호를 붙이지 않고 array.length로 사용한다.

int[] values = {10, 20, 30, 40};

for (int i = 0; i < values.length; i++) {
    System.out.println(values[i]);
}

2차원 배열은 배열의 배열이다. table.length는 행 배열의 개수이고 table[i].length는 i번째 행에 들어 있는 원소 개수이다. 각 행의 길이가 다른 비정방형 배열도 만들 수 있으므로 반복문에서는 각 행의 length를 따로 확인하는 것이 안전하다.

int[][] table = {
    {1, 2, 3},
    {10, 11},
    {20, 21, 22, 23}
};

for (int i = 0; i < table.length; i++) {
    for (int j = 0; j < table[i].length; j++) {
        System.out.println(table[i][j]);
    }
}

4. String 클래스와 문자열

String은 문자열을 표현하고 처리하기 위한 참조 자료형 클래스이다. String 변수는 문자열 객체에 대한 참조를 저장하지만, 문자열 리터럴을 직접 대입할 수 있어 기본 자료형처럼 편리하게 사용할 수 있다.

String s1 = "Java";
String s2 = new String("Java");
String s3 = null;

s1과 s2가 나타내는 문자 내용은 같지만 객체 참조의 관점에서는 다를 수 있다. 문자열 내용 비교에는 ==보다 equals()를 사용해야 한다. ==는 참조가 같은 객체를 가리키는지를 비교하고 equals()는 문자열 내용을 비교한다.

문자열 연결 연산자

+ 연산자는 두 문자열을 연결한다. 문자열과 다른 자료형을 +로 연결하면 다른 값이 문자열 형태로 변환되어 이어 붙는다. 연산 순서는 왼쪽에서 오른쪽이므로 괄호 유무에 따라 결과가 달라질 수 있다.

String result = "Java" + " Programming";
System.out.println("sum = " + (10 + 20)); // sum = 30
System.out.println("sum = " + 10 + 20);   // sum = 1020
문자열은 한 번 생성되면 내용 자체가 바뀌지 않는 객체이다. + 연결 결과는 기존 문자열을 수정하는 것이 아니라 새로운 문자열 값을 만든다.

5. 문자열과 화면 출력

System.out.print()는 출력 뒤에 줄을 바꾸지 않고, System.out.println()은 출력한 다음 줄을 바꾼다. println()을 인수 없이 호출하면 빈 줄을 출력한다. System.out.printf()는 형식 문자열에 맞추어 값을 출력한다.

System.out.print("Java ");
System.out.println("Programming");
System.out.println();

int score = 95;
System.out.printf("score = %d%n", score);

println()에서 문자열과 값을 +로 연결하면 상태나 계산 결과를 설명과 함께 출력하기 쉽다. 그러나 덧셈 결과를 먼저 계산해야 한다면 괄호로 우선순위를 분명히 해야 한다.

메소드특징
print()출력 후 줄바꿈 없음
println()출력 후 줄바꿈
printf()서식 지정자를 이용한 형식화 출력

6. Scanner 클래스로 입력받기

Scanner는 키보드나 파일 등에서 기본 자료형과 문자열을 읽을 때 사용하는 클래스이다. 키보드 입력에는 System.in을 연결한 Scanner 객체를 만든다. Scanner는 java.util 패키지에 있으므로 import 문을 사용한다.

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        String name = sc.next();
        int age = sc.nextInt();
        double height = sc.nextDouble();

        System.out.println(name + ", " + age + ", " + height);
        sc.close();
    }
}

토큰 검사와 읽기

검사 메소드읽기 메소드대상
hasNext()next()다음 문자열 토큰
hasNextInt()nextInt()int 값
hasNextDouble()nextDouble()double 값
hasNextLine()nextLine()한 줄 전체

hasNextInt()처럼 hasNext 계열 메소드로 다음 입력의 자료형을 검사한 뒤 읽으면 잘못된 입력을 처리하기 쉽다.

while (sc.hasNextInt()) {
    System.out.println(sc.nextInt());
}
nextInt() 뒤에 nextLine()을 바로 호출하면 숫자 뒤에 남은 줄바꿈 문자를 먼저 읽어 빈 문자열이 나올 수 있다. 필요하면 중간에 nextLine()을 한 번 호출해 남은 줄을 소비한다.

7. 클래스의 구성

클래스는 동종 객체가 가져야 할 데이터와 동작을 하나의 자료형으로 정의한다. 데이터는 멤버 변수 또는 데이터 필드로, 동작은 메소드로 표현한다. 클래스는 다른 클래스를 상속하거나 인터페이스를 구현할 수 있으며, 이번 강의에서는 클래스 정의와 객체 사용의 기초를 중심으로 다룬다.

[접근제어자] class 클래스이름
    [extends 부모클래스]
    [implements 인터페이스목록] {

    데이터필드 선언;
    생성자 선언;
    메소드 선언;
}

클래스 내부에는 데이터 필드, 메소드, 생성자 등이 위치한다. 객체마다 따로 가져야 하는 상태는 인스턴스 필드로 정의하고, 객체가 수행할 동작은 메소드로 정의한다.

메소드 정의

[접근제어자] 반환형 메소드이름(매개변수목록)
    [throws 예외목록] {
    실행문;
}

반환값이 없으면 반환형에 void를 쓰고, 반환값이 있으면 선언한 자료형과 맞는 값을 return한다. 매개변수는 메소드 호출 시 외부에서 값을 전달받는 지역변수이다.

8. 클래스와 멤버의 접근 제어

접근 제어자는 클래스나 멤버를 어디에서 사용할 수 있는지 제한한다. Java의 멤버 접근 수준은 public, protected, 접근 제어자를 생략한 package 접근, private로 구분한다.

접근 수준같은 클래스같은 패키지다른 패키지의 하위 클래스그 밖의 클래스
public가능가능가능가능
protected가능가능상속 관계에서 가능불가
package가능가능불가불가
private가능불가불가불가

최상위 클래스에는 public 또는 접근 제어자 생략만 사용할 수 있으며 protected나 private를 붙일 수 없다. public 클래스는 일반적으로 소스 파일 이름과 클래스 이름을 같게 작성한다.

데이터 필드를 private로 감추고 public 메소드로 필요한 동작만 제공하면 객체의 내부 상태를 보호하고 구현 변경의 영향을 줄일 수 있다. 이것은 캡슐화의 기본적인 방법이다.

범위 순서: public이 가장 넓고 private가 가장 좁다. protected는 같은 패키지와 다른 패키지의 하위 클래스에서 사용 가능하며, package 접근은 같은 패키지 안에서만 허용된다.

9. 클래스 정의 예제

원의 반지름과 넓이를 표현하는 Circle 클래스를 생각해 보자. 반지름은 외부에서 직접 바꾸지 못하도록 private로 감추고, 넓이와 반지름을 조회하는 public 메소드를 제공할 수 있다.

package com;

class Circle {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return radius * radius * 3.14;
    }

    public double getRadius() {
        return radius;
    }
}

private 필드 radius는 Circle 클래스 내부에서만 직접 접근할 수 있다. 외부 코드는 getRadius()와 getArea() 같은 public 메소드를 통해 필요한 정보에 접근한다. 이 구조는 객체가 자신의 데이터를 관리하도록 책임을 모은다.

10. 객체의 생성과 참조

클래스 정의가 있다고 해서 객체가 자동으로 만들어지는 것은 아니다. 먼저 참조변수를 선언하고 new 연산자로 객체를 생성해야 한다.

Circle c;              // 참조변수 선언, 객체는 아직 없음
c = new Circle(5.0);   // 객체 생성 후 참조 저장

Circle d = new Circle(10.0);

new는 객체를 위한 메모리 공간을 확보하고 생성자를 호출해 초기화한 뒤 객체에 대한 참조를 반환한다. 참조변수는 그 참조를 저장한다. 여러 참조변수가 같은 객체를 가리킬 수도 있으며, 참조변수 값이 null이면 가리키는 객체가 없다.

상속 관계에서는 부모 클래스형 참조변수가 하위 클래스 객체를 가리킬 수 있다. 예를 들어 Circle이 Shape의 하위 클래스라면 Shape s=new Circle(5.0);과 같은 선언이 가능하다. 이때 참조변수의 타입에 따라 직접 접근 가능한 멤버가 결정된다.

11. 객체 멤버 사용

객체가 가진 데이터 필드나 메소드는 점 연산자(.)로 접근한다. 단, 접근 제어자가 허용하는 범위 안에서만 사용할 수 있다.

Circle c = new Circle(5.0);

double area = c.getArea();
double radius = c.getRadius();

System.out.println(area);
System.out.println(radius);

private 필드에는 외부 클래스에서 c.radius처럼 직접 접근할 수 없다. public 메소드로 객체에게 작업을 요청하는 방식이 적절하다. 객체지향 프로그램에서는 “데이터를 꺼내 외부가 처리”하기보다 “객체가 가진 메소드를 호출해 동작을 요청”하는 구조가 중요하다.

참조변수가 null인 상태에서 c.getArea()처럼 멤버를 사용하면 NullPointerException이 발생한다. 객체가 실제로 생성되어 참조가 저장되었는지 확인해야 한다.

12. 생성자의 역할

생성자(constructor)는 객체가 생성될 때 자동으로 호출되어 필드를 초기화하고 필요한 준비 작업을 수행한다. 생성자 이름은 클래스 이름과 같고 반환형을 쓰지 않는다. void도 쓰지 않는다는 점이 일반 메소드와 다르다.

class Circle {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }
}

this.radius는 현재 객체의 필드이고 오른쪽 radius는 매개변수이다. 이름이 같을 때 this를 사용하여 구분한다. 생성자는 매개변수 목록을 다르게 하여 여러 개 정의할 수 있으며 이것을 생성자 오버로딩이라고 한다.

class Circle {
    private double radius;

    public Circle() {
        radius = 1.0;
    }

    public Circle(double radius) {
        this.radius = radius;
    }
}
생성자 특징: 클래스 이름과 동일, 반환형 없음, new로 객체를 만들 때 호출, 필드 초기화 담당, 매개변수 목록이 다르면 오버로딩 가능

13. 기본 생성자와 부모 생성자 호출

클래스에 생성자를 하나도 선언하지 않으면 컴파일러가 매개변수 없는 기본 생성자를 자동으로 제공한다. 그러나 매개변수가 있는 생성자라도 하나를 직접 선언하면 컴파일러는 기본 생성자를 자동으로 추가하지 않는다.

class A {
    // 생성자를 선언하지 않았으므로 A()가 자동 제공됨
}

class B {
    B(int value) {
    }

    // B()는 자동 제공되지 않음
}

생성자의 첫 문장에 다른 생성자 호출을 명시하지 않으면 부모 클래스의 매개변수 없는 생성자를 호출하는 super()가 자동으로 삽입된다. 부모에게 매개변수 없는 생성자가 없다면 하위 클래스 생성자에서 적절한 super(인수)를 첫 문장으로 직접 호출해야 한다.

this(인수)는 같은 클래스의 다른 생성자를, super(인수)는 부모 클래스 생성자를 호출한다. 둘 다 생성자의 첫 문장이어야 하므로 한 생성자에서 동시에 직접 사용할 수 없다.

“생성자가 없으면 기본 생성자가 생긴다”가 아니라 “개발자가 생성자를 하나도 선언하지 않았을 때만 컴파일러가 기본 생성자를 제공한다”가 정확한 표현이다.

핵심 개념 정리

배열은 같은 자료형의 원소를 고정된 개수만큼 저장하는 객체이다. 배열 참조변수 선언과 실제 배열 생성은 구분해야 하며, new로 만든 기본 자료형 배열은 기본값으로 자동 초기화된다. 원소 인덱스는 0부터 시작하고 배열 크기는 length 필드로 확인한다.

String은 참조 자료형이지만 리터럴 대입과 + 연결을 지원한다. 문자열 내용 비교에는 equals()를 사용한다. Scanner는 System.in과 연결하여 키보드 입력을 읽고, hasNextInt() 같은 검사 메소드와 nextInt() 같은 읽기 메소드를 제공한다.

클래스는 데이터 필드와 메소드로 객체의 상태와 동작을 정의한다. 접근 제어자는 멤버의 사용 범위를 제한하며, 객체는 new와 생성자로 만들어진다. 생성자는 클래스와 이름이 같고 반환형이 없으며, 개발자가 생성자를 하나도 선언하지 않은 경우에만 기본 생성자가 자동 제공된다.

최종 정리: Java에서 배열과 객체는 모두 참조를 통해 사용한다. 선언, 생성, 초기화, 접근 범위를 구분하고 객체의 데이터는 감추며 메소드로 동작을 제공하는 것이 이후 상속과 객체지향 프로그래밍을 이해하는 기초가 된다.

예상문제 20선

1. Java 배열에 대한 설명으로 옳지 않은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④
배열의 길이는 객체 생성 시 결정되며 이후 변경할 수 없다. 다른 길이가 필요하면 새 배열을 만들어야 한다.

2. Java에서 권장되는 배열 참조변수 선언 형식은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①
Java에서는 int[] a;처럼 배열 자료형을 선언한다. 크기는 선언이 아니라 new로 객체를 만들 때 지정한다.

3. 배열 선언과 동시에 초기화하는 올바른 문장은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②
중괄호만 사용하는 초기화식은 선언과 동시에 int[] a={...}; 형식으로 사용한다.

4. new int[5]로 생성한 배열 원소의 초기값은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③
int 배열의 원소는 배열 생성 시 int의 기본값인 0으로 자동 초기화된다.

5. 길이가 n인 배열의 마지막 유효 인덱스는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②
배열 인덱스는 0부터 시작하므로 길이 n의 마지막 인덱스는 n-1이다.

6. 2차원 배열 table에서 첫 번째 행의 원소 개수를 구하는 표현은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④
table[0]은 첫 번째 행 배열이며 그 행의 원소 개수는 table[0].length로 구한다.

7. 문자열의 내용을 비교할 때 사용하는 메소드는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①
String의 내용 비교에는 equals()를 사용한다. ==는 두 참조가 같은 객체를 가리키는지 비교한다.

8. 다음 출력문의 결과는? System.out.println("sum=" + 10 + 20);

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③
문자열 연결은 왼쪽부터 진행되어 "sum="+10이 먼저 문자열이 되고 이어서 20이 연결되므로 sum=1020이 출력된다.

9. 출력 후 자동으로 줄을 바꾸는 메소드는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③
println()은 값을 출력한 뒤 줄바꿈한다. 인수 없이 호출하면 빈 줄을 만든다.

10. 키보드 입력을 위한 Scanner 객체 생성으로 옳은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②
System.in을 생성자 인수로 전달해 new Scanner(System.in)으로 키보드 입력용 객체를 만든다.

11. 다음 입력이 int인지 검사하는 Scanner 메소드는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④
hasNextInt()는 다음 토큰을 int로 읽을 수 있는지 boolean으로 알려 준다. 실제 읽기는 nextInt()가 한다.

12. 클래스에서 객체의 상태를 저장하는 멤버는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①
데이터 필드 또는 멤버 변수는 각 객체가 가지는 상태 값을 저장한다.

13. 반환값이 없는 메소드의 반환형은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②
Java에서 값을 반환하지 않는 메소드는 반환형 위치에 void를 사용한다.

14. 멤버 접근 범위가 가장 좁은 접근 제어자는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①
private 멤버는 선언된 클래스 내부에서만 직접 접근할 수 있어 범위가 가장 좁다.

15. 접근 제어자를 생략한 멤버를 사용할 수 있는 기본 범위는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③
접근 제어자를 생략하면 package 접근이 되어 같은 패키지에 속한 클래스에서 사용할 수 있다.

16. 참조변수만 선언하고 아직 객체를 생성하지 않은 문장은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④
Circle c;는 참조변수만 선언한다. new가 실행되어야 실제 Circle 객체가 생성된다.

17. new Circle(5.0)의 수행 내용으로 가장 적절한 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①
new는 객체 메모리를 확보하고 알맞은 생성자를 호출해 초기화한 뒤 객체 참조를 결과로 돌려준다.

18. 생성자에 대한 설명으로 옳지 않은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④
생성자는 반환형을 전혀 쓰지 않는다. void를 쓰면 생성자가 아니라 일반 메소드가 된다.

19. 컴파일러가 기본 생성자를 자동 제공하는 조건은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②
개발자가 생성자를 하나도 선언하지 않은 경우에만 매개변수 없는 기본 생성자가 자동으로 만들어진다.

20. 하위 클래스 생성자에서 부모 클래스 생성자를 명시적으로 호출하는 키워드는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③
super(인수)는 부모 클래스 생성자를 호출하며 하위 클래스 생성자의 첫 문장에 위치해야 한다.

댓글