이것이 자바다 - Part 05

mj·2023년 1월 11일
0
post-thumbnail

Part 05 참조 타입

데이터 타입 분류

자바의 데이터 타입은 크게 기본타입과 참조타입으로 분류된다.

참조타입이란 객체의 번지를 참조하는 타입으로 배열 열거, 클래스, 인터페이스 타입이 있다.

메모리 사용 영역

java 명령어로 JVM이 구동되면 JVM은 운영체제에서 할당받은 메모리 영역을 다음과 같이 구분해서 사용한다.

메모리 영역
1. 메소드 영역
2. 힙 영역
3. 스택 영역

메소드 영역

메소드 영역은 바이트코드 파일을 읽은 내용이 저장되는 영역으로 클래스별로 상수, 정정 필드, 메소드 코드, 생성자 코드 등이 저장된다.

힙 영역

힙 영역은 객체가 생성되는 영역이다. 객체의 번지는 메소드 영역과 스택 영역의 상수와 변수에서 참조할 수 있다.

스택 영역

스택 영역은 메소드를 호출할 때마다 생성되는 프레임이 저장되는 영역이다.

메소드 호출이 끝나면 프레임은 자동 제거된다.

참조 타입 변수의 ==, != 연산

==, != 연산자는 변수의 값이 같인지 아닌지를 조사한다.

참조 타입 변수의 값은 객체의 번지이므로 참조 타입 변수의 ==, != 연산자는 번지를 비교하는 것이 된다. 번지가 같다면 동일한 객체를 참조하는 것이고, 다르다면 다른 객체를 참조하는 것이다.

package ch05.sec03;

public class ReferenceVariableCompareExample {
    public static void main(String[] args) {
        int[] arr1;
        int[] arr2;
        int[] arr3;

        arr1 = new int[] { 1, 2, 3 };
        arr2 = new int[] { 1 ,2 ,3 };
        arr3 = arr2;

        System.out.println(arr1 == arr2);
        System.out.println(arr2 == arr3);
    }
}

null과 NullPointerException

참조 타입 변수는 아직 번지를 저장하고 있지 않다는 뜻으로 null 값을 가질 수 있다.

변수의 값이 null 인 상태에서 객체의 데이터나 메소드를 사용하려고 하면 NullPointerException 이 발생한다.

int[] intArray = null;
intArray[0] = 10		//NullPointerException

자바는 코드를 이용해서 객체를 직접 제거하는 방법을 제공하지 않기 때문에 경우에 따라서는 더이상 객체를 사용하지 않을 때 참조 타입 변수에 일부러 null을 대입하기도 한다.

이 경우에는 힙 메모리에는 객체가 남아있지만 변수가 객체를 참조하지는 않게된다. 자바는 이러한 객체를 쓰레기로 취급하고, Garbage Collecor 를 실행시켜 자동으로 제거한다.

문자열 타입

자바의 문자열은 String 객체로 생성된다. String 타입 변수에 문자열을 대입하면 힙 영역에 String 객체가 생성되고, 해당 객체의 번지가 변수에 저장된다.

문자열 비교

자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 설계되어 있다.

String name1 = "홍길동";
String name2 = "홍길동";
System.out.println(name1 == name2); 	//true

new 연산자로 직접 String 객체를 생성하고 대입할 수도 있다. 이 경우에 각 변수는 문자열 리터럴이 같다라도 서로 다른 객체의 번지를 가지게 된다.

String name1 = new String("홍길동");
String name2 = new String("홍길동");
System.out.println(name1 == name2); 	//false

두 String 객체에서 객체 번지에 상관없이 내부 문자열만을 비교할 경우에는 String 객체의 equals() 메소드를 사용한다.

문자 추출

문자열에서 특정 위치의 문자를 얻고 싶다면 charAt 메소드를 이용할 수 있다.

package ch05.sec5;

public class CharAtExample {
    public static void main(String[] args) {
        String ssn = "9506241230123";
        char sex = ssn.charAt(6);
        switch (sex) {
            case '1':
            case '3':
                System.out.println("남자입니다.");
                break;
            case '2':
            case '4':
                System.out.println("여자입니다.");
                break;
        }
    }
}

문자열 길이

문자열에서 문자의 개수를 얻고 싶다면 length 메소드를사용한다.

String subject = "자바 프로그래밍";
int length = subject.length();

문자열 대체

문자열에서 특정 문자열을 다른 문자열로 대체하고 싶다면 replace 메소드를 사용한다.

String oldStr = "자바 프로그래밍";
String newStr = oldStr.replace("자바", "Java");

String 객체의 문자열은 변경이 불가한 특성을 갖기 때문에 replace() 메소드가 리턴하는 문자열은 기존의 문자열과 다른 문자열이다.

문자열 잘라내기

문자열에서 특정 위치의 문자열을 잘라내어 가져오고 싶다면 substring() 메소드를 사용한다.

package ch05.sec5;

public class SubStringExample
{
    public static void main(String[] args) {
        String ssn = "880815-1234567";

        String firstNum = ssn.substring(0,6);
        System.out.println(firstNum);

        String secondNum = ssn.substring(7);
        System.out.println(secondNum);
    }
}

문자열 찾기

문자열에서 특정 문자열의 위치를 찾고자 할 때에는 indexOf() 메소드를 사용한다. indexOf 메소드는 주어진 문자열이 시작되는 인텍스를 리턴한다.

만약 주어진 문자열이 포함되어 있지 않으면 -1을 리턴한다.

package ch05.sec5;

public class ReplaceExample
{
    public static void main(String[] args) {
        String oldStr = "자바 문자열은 불변입니다. 자바 문자열은 String입니다.";
        String newStr = oldStr.replace("자바", "Java");

        System.out.println(oldStr);
        System.out.println(newStr);
    }
}

문자열 분리

문자열이 구분자를 사용하여 여러 개의 문자열로 구성되어 있을 경우, 이를 따로 분리해서 얻고 싶다면 split 메소드를 사용한다.

package ch05.sec5;

public class SplitExample
{
    public static void main(String[] args) {
        String board = "1,자바 학습,참조 타입 String을 학습합니다.,홍길동";

        String[] tokens = board.split(",");

        System.out.println("번호: "+tokens[0]);
        System.out.println("제목: "+tokens[1]);
        System.out.println("내용: "+tokens[2]);
        System.out.println("내용: "+tokens[3]);
        System.out.println();

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

배열 타입

배열은 연속된 공간에 값을 나열시키고, 각 값에 인텍스를 부여해 놓은 자료구조이다.

인덱스는 대괄호와 함께 사용하여 각 항목의 값을 읽거나 저장하는데 사용한다.

배열은 다음과 같은 특징이 있다.

배열의 특징

  • 배열은 같은 타입의 값만 관리한다.
  • 배열의 길이는 늘리거나 줄일 수 없다.

배열 변수 선언

배열 변수 선언은 다음과 같이 두 가지 형태가 있지만 관례적으로 첫 번째 방법을 주로 사용한다.

int[] intArray1;	//첫번째
int intArray2[]; 	//두번째

값 목록으로 배열 생성

배열에 저장될 값의 목록이 있다면, 다음과 같이 간단하게 배열을 생성할 수 있다.

String[] season = {"Sprint", "Summer", "Fail", "Winter" };

배열 변수를 미리 선언한 후에는 값 목록을 변수에 대입할 수 없다.

변수 선언 시점과 값 대입 시점이 다를 때는 다음과 같이 값을 대입하면 된다.

변수 = new 타입[] { 값1, 값2, 값2... };

new 연산자로 배열 생성

값의 목록은 없지만 향후 값들을 저장할 목적으로 new 연산자를 사용하여 배열을 미리 생성할 수도 있다.

길이는 배열이 저장할 수 있는 항목 수를 말한다.

타입[] 변수 = new 타입[길이]

new 연산자로 배열을 처음 생성하면 배열 항목은 기본값으로 초기화된다.

데이터 타입초기값
byte[]0
char[]'\u0000'
short[]0
int[]0
long[]0L
float[]0.0F
double[]0.0
boolean[]false
클래스[]null
인터페이스[]null

배열 길이

배열의 길이란 배열에 저장할 수 있는 항목 수를 말한다.

배열의 길이를 얻으려면 length 필드를 사용하면 된다.

배열변수.length

package ch05.sec06;

public class ArrayLengthExample
{
    public static void main(String[] args) {
        int[] scores = {84,90,96};

        int sum = 0;
        for(int i = 0; i<scores.length; i++){
            sum += scores[i];
        }
        System.out.println("총합 : "+sum);

        double avg = (double) sum / scores.length;
        System.out.println("평균 : "+avg);
    }
}

다차원 배열

배열 항목에는 또 다른 배열이 대입될 수 있는데, 이러한 배열을 다차원 배열이라고 부른다.

값 목록으로 다차원 배열 생성

값 목록으로 다차원 배열을 생성하려면 배열 변수 선언 시 타입 뒤에 대괄호[] 를 차원의 수만큼 붙이고, 값 목록도 마찬가지로 차원의 수만큼 중괄호를 중첩시킨다.

int[][] scores = {
	{ 80, 90, 96 },
    {75, 88 }
};

int score1 = scores[0][2];	//96
int score2 = scores[1][1];	//88

scores.length			//2
scores[0].length		//3
scores[1].length		//2

new 연산자로 다차원 배열 생성

new 연산자로 다차원 배열을 생성하려면 배열 변수 선언 시 타입 뒤에 대괄호 [] 를 차원의 수만큼 붙이고, new 뒤에도 차원의 수만큼 대괄호 []를 작성하면 된다.

int[][] scores = new int[2][];
scores[0] = new int[3];
scores[1] = new int[2];

객체를 참조하는 배열

기본타입 배열은 각 항목에 값을 직접 저장하지만, 참조 타입 배열은 각 항목에 객체의 번지를 저장한다.

package ch05.sec08;

public class ArrayReferenceObjectExample {
    public static void main(String[] args) {
        String[] strArray = new String[3];
        strArray[0] = "Java";
        strArray[1] = "Java";
        strArray[2] = new String("Java");

        System.out.println(strArray[0] == strArray[1] );	//true
        System.out.println(strArray[0] == strArray[2] );	//false
        System.out.println(strArray[0].equals(strArray[2]) );	//true
    }
}

배열 복사

배열은 한번 생성하면 길이를 변경할 수 없다. 더 많은 저장 공간이 필요하다면 더 큰 길이의 배열을 새로 만들고 이전 배열로부터 항목들을 복사해야 한다.

가장 기본적인 복사 방법을 for 문을 이용하여 항목을 하나씩 읽고 새로운 배열에 저장하는 것이다.

package ch05.sec09;

public class ArrayCopyByForExample {
    public static void main(String[] args) {
        int[] oldIntArray = {1, 2, 3};
        int[] newIntArray = new int[5];

        for (int i = 0; i < oldIntArray.length; i++) {
            newIntArray[i] = oldIntArray[i];
        }

        for (int i = 0; i < newIntArray.length; i++) {
            System.out.print(newIntArray[i] + ", ");
        }
    }
}

배열 복사를 위한 좀 더 간단한 벙법으로는 System.arraycopy() 메소드가 있다.

System.arraycopy(원본 배열, 복사 시작 인덱스, 새 배열, 새 내열 붙여넣기 시작 인덱스, 복사 항목 수)

package ch05.sec09;

public class ArrayCopyExample {
    public static void main(String[] args) {
        String[] oldStrArray = {"java", "array", "copy"};
        String[] newStrArray = new String[5];

        System.arraycopy(oldStrArray, 0, newStrArray, 0, oldStrArray.length);
        for (int i = 0; i < newStrArray.length; i++) {
            System.out.print(newStrArray[i] + ", ");
        }
    }
}

배열 항목 반복을 위한 향상된 for 문

자바는 배열 및 컬렉션을 좀 더 쉽게 처리할 목적으로 다음과 같은 for 문을 제공한다.

package ch05.sec10;

public class AdvancedForExample {
    public static void main(String[] args) {
        int[] scores = {95, 71, 84, 93, 87};

        int sum = 0;
        for (int score : scores) {
            sum = sum + score;
        }
        System.out.println("점수 총합 = " + sum);

        double avg = (double) sum / scores.length;
        System.out.println("점수 평균 = " + avg);
    }
}

main() 메소드의 String[] 매개변수 용도

명령 프롬프트나 터미널에서 명령어로 프로그램을 실행할 때는 매개변수가 필요할 수 있다.

이 경우에 main 함수의 String[] args 매개변수가 사용된다.

다음과 같이 Sum 클래스에 매개변수를 넘길 때 args 매개변수에는 { "10", "20" } 값 목록이 전달된다.

java Sum 10 20

package ch05.sec11;

public class MainStringArrayArgument {
    public static void main(String[] args) {
        if(args.length != 2) {
            System.out.println("프로그램 입력값이 부족");
            System.exit(0);
        }

        String strNum1 = args[0];
        String strNum2 = args[1];

        int num1 = Integer.parseInt(strNum1);
        int num2 = Integer.parseInt(strNum1);

        int result = num1 + num2;
        System.out.println(num1 + " + " + num2 + " = " + result);
    }
}

열거 타입

데이터 중에는 요일이나 계절 수와 같이 몇 가지로 한정된 값을 갖는 경우가 있다.

이와 같이 한정된 값을 갖는 타입을 열거 타입이라고 한다.

열거 상수는 열거 타입으로 사용할 수 잇는 한정된 값을 말한다. 관례적으로 알파벳으로 정의하며, 모두 대문자로 작성한다. 만약 여러 단어로 구성될 경우 언더바로 연결한다.

public enum LoginResult {
	LOGIN_SUCCESS,
    LOGIN_FAILED
}

열거 타입도 하나의 데이터 타입으므로 변수를 선언하고 사용해야 한다.

package ch05.sec12;

public enum Week {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}
package ch05.sec12;

import java.util.Calendar;
public class WeekExample {
    public static void main(String[] args) {
        Week today = null;

        Calendar cal = Calendar.getInstance();

        int week = cal.get(Calendar.DAY_OF_WEEK);

        switch (week) {
            case 1:
                today = Week.SUNDAY;
                break;
            case 2:
                today = Week.MONDAY;
                break;
            case 3:
                today = Week.TUESDAY;
                break;
            case 4:
                today = Week.WEDNESDAY;
                break;
            case 5:
                today = Week.THURSDAY;
                break;
            case 6:
                today = Week.FRIDAY;
                break;
            case 7:
                today = Week.SATURDAY;
                break;
        }

        // 열거 타입 변수를 사용
        if (today == Week.SUNDAY) {
            System.out.println("일요일에는 축구를 합니다.");
        } else {
            System.out.println("열심히 자바를 공부합니다.");
        }
    }
}

문제

  1. 참조 타입에 대한 설명으로 틀린 것은 무엇입니까?
    ➊ 참조 타입에는 배열, 열거, 클래스, 인터페이스가 있다.
    ➋ 참조 타입 변수의 메모리 생성 위치는 스택이다.
    ➌ 참조 타입에서 ==, != 연산자는 객체 번지를 비교한다.
    ➍ 참조 타입은 null 값으로 초기화할 수 없다.
  • 답 : ➍
  1. 자바에서 메모리 사용에 대한 설명으로 틀린 것은 무엇입니까?
    ➊ 로컬 변수는 스택 영역에 생성되며 실행 블록이 끝나면 소멸된다.
    ➋ 메소드 코드나 상수, 열거 상수는 정적(메소드) 영역에 생성된다.
    ➌ 참조되지 않는 객체는 프로그램에서 직접 소멸 코드를 작성하는 것이 좋다.
    ➍ 배열 및 객체는 힙 영역에 생성된다.
  • 답 : ➌
  1. String 타입에 대한 설명으로 틀린 것은 무엇입니까?
    ➊ String은 클래스이므로 참조 타입이다.
    ➋ String 타입의 문자열 비교는 ==를 사용해야 한다.
    ➌ 동일한 문자열 리터럴을 저장하는 변수는 동일한 String 객체를 참조한다.
    ➍ new String (“문자열”)은 문자열이 동일하더라도 다른 String 객체를 생성한다.
  • 답 : ➋
  1. 배열을 생성하는 방법으로 틀린 것은 무엇입니까?
    ➊ int[ ] array = { 1, 2, 3 };
    ➋ int[ ] array; array = { 1, 2, 3 };
    ➌ int[ ] array = new int[3];
    ➍ int[ ][ ] array = new int[3][2];
  • 답 : ➋
  1. 배열의 기본 초기값에 대한 설명으로 틀린 것은 무엇입니까?
    ➊ 정수 타입 배열 항목의 기본 초기값은 0이다.
    ➋ 실수 타입 배열 항목의 기본 초기값은 0.0f 또는 0.0이다.
    ➌ boolean 타입 배열 항목의 기본 초기값은 true이다.
    ➍ 참조 타입 배열 항목의 기본 초기값은 null이다.
  • 답 : ➌
  1. 다음은 배열 길이를 출력하는 코드입니다. 실행 결과를 작성해보세요.
int[][] array = {
 {95, 86},
 {83, 92, 96},
 {78, 83, 93, 87, 88}
};
System.out.println(array.length);
System.out.println(array[2].length);
  • 답 :
    3
    5
  1. 주어진 배열 항목에서 최대값을 출력하는 코드를 작성해보세요(for 문 이용).
int[] array = { 1, 5, 3, 8, 2 };
  • 답 :
int max = -1;
for(int i=0; i<array.length; i++) {
	if(array[i] > max) 
    	max = array[i];
}
System.out.println(max);
  1. 주어진 배열 항목의 전체 합과 평균을 구해 출력하는 코드를 작성해보세요(중첩 for 문 이용).
int[][] array = {
 {95, 86},
 {83, 92, 96},
 {78, 83, 93, 87, 88}
};
  • 답 :
public class Main {
    public static void main(String[] args) {
        int[][] array = {
                {95, 86},
                {83, 92, 96},
                {78, 83, 93, 87, 88}
        };

        int cnt = 0;
        int sum = 0;

        for(int[] layer : array) {
            cnt += layer.length;
            for(int num : layer) {
                sum += num;
            }
        }

        double avg = (double)sum / cnt;
        System.out.println("전체 합 : " + sum);
        System.out.println("평균 : " + avg);
    }
}
  1. 학생들의 점수를 분석하는 프로그램을 만들려고 합니다. 키보드로부터 학생 수와 각 학생들의 점
    수를 입력받고 while 문과 Scanner의 nextLine() 메소드를 이용해서 최고 점수 및 평균 점수를
    출력하는 코드를 작성해보세요.
  • 답 :
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        boolean run = true;

        int studentCount = 0;
        int[] scores = null;
        Scanner scanner = new Scanner(System.in);

        while(run) {
            System.out.println("--------------------------------------------------------------");
            System.out.println("1.학생수 | 2.점수입력 | 3.점수리스트 | 4.분석 | 5.종료");
            System.out.println("--------------------------------------------------------------");
            System.out.print("선택> ");

            int selectNo = scanner.nextInt();

            if(selectNo == 1) {
                System.out.print("학생수> ");
                studentCount = scanner.nextInt();
                scores = new int[studentCount];
            } else if(selectNo == 2) {
                for(int i=0; i<scores.length; i++) {
                    System.out.print("scores[" + i + "]> ");
                    scores[i] = scanner.nextInt();
                }
            } else if(selectNo == 3) {
                for(int i=0; i<scores.length; i++) {
                    System.out.println("scores[" + i + "]: " + scores[i]);
                }
            } else if(selectNo == 4) {
                int max = 0;
                int sum = 0;
                double avg = 0;
                for(int i= 0; i<scores.length; i++) {
                    max = ( max<scores[i] ) ? scores[i] : max;
                    sum += scores[i];
                }
                avg = (double) sum / studentCount;
                System.out.println("최고 점수: " + max);
                System.out.println("평균 점수: " + avg);
            } else if(selectNo == 5) {
                run = false;
            }
        }
        System.out.println("프로그램 종료");
    }
}
profile
사는게 쉽지가 않네요

0개의 댓글