개발하고 싶은 초심자

220825 D+3 연산자와 콘솔 입출력(I/O), 참조자료형(String, Array, Enum), 메모리 영역, 형 변환 본문

기술개념정리(in Java)

220825 D+3 연산자와 콘솔 입출력(I/O), 참조자료형(String, Array, Enum), 메모리 영역, 형 변환

정새얀 2022. 8. 25. 17:23

1. 연산자

: 하나의 값 또는 여러 개의 값을 피연산자로 하여 새로운 값을 만들어내는 기호.

 

① 산술 연산자

: 사칙연산에 사용되는 연산자와 나머지 연산자(%)

int num1 = 1;
int num2 = num1 + 2; // num2 -> 3
int num3 = num2 - 1; // num3 -> 2
int num4 = num3 * 3; // num4 -> 9
int num5 = num4 / 2; // num5 -> 4
int num6 = num5 % 3; // num6 -> 1

// 우항에 0은 존재할 수 없다(어떤 수를 0으로 나눌 수 없다)
// 좌항과 우항이 모두 int형이면 그 결과도 int형이다
// 소수점 이하의 값은 버려지고 몫만 결과로 반환된다
int    num1 = 9 / 0;   // 에러
double num2 = 9.0 / 0; // Infinity

// 좌항이나 우항 중 하나라도 실수 타입이 존재하면 실수 타입이 아닌 값도 실수 타입으로 자동으로 형 변환되어 계산된다
// 실수간의 나눗셈 연산이 되기 때문에 소수점이 버려지지 않은 온전한 값이 결과값으로 반환된다
int    num3 = 9 % 0;   // 에러
double num4 = 9.0 % 0; // 에러

double num1 = 9 / 2;   // 4.0
int    num2 = 9 / 2;   // 4
double num3 = 9.0 / 2; // 4.5

 

② 증감 연산자

: 변수의 값을 1씩 증가 / 감소시키는 연산자

// 후위형 증감연산자
// 기존의 값을 먼저 적용시키고 그 다음 증감 연산을 수행한다
num1++; // num1 = num1 + 1;

// 전위형 증감연산자
// 증감 연산을 먼저 수행하고 그 결과값을 적용시킨다
++num1; // num1 = num1 + 1;

num2--; // num2 = num2 - 1;
--num2; // num2 = num2 - 1;
// num1에 전위형 증감연산자 ++를 사용했으므로, num1의 값이 1만큼 먼저 증가된 다음 그 결과값이 prefix에 할당된다.
// num1의 값은 2, prefix의 값도 2가 된다.
int num1 = 1;
int prefix = ++num1;

// num2에 후위형 증감연산자 ++를 사용했으므로, num2의 값이 먼저 postfix에 할당된 다음에 num2의 값이 1 증가한다.
// num2의 값은 2, postfix의 값은 1이 된다.
int num2 = 1;
int postfix = num2++;

③ 복합 대입 연산자

// num1~5의 값을 모두 10이라고 한다면,
num1 += 3; // num1 -> 13
num2 -= 3; // num2 -> 7
num3 *= 3; // num3 -> 30
num4 /= 3; // num4 -> 3
num5 %= 3; // num5 -> 1

④ 비교 연산자

: boolean타입으로 평가될 수 있는 조건식에 사용된다.

‣ 대소 비교 연산자

: boolean을 제외한 나머지 기본 타입에 모두 사용할 수 있다.

→ 이항 비교만 가능하다.

x가 1보다 크고 5보다 작다를 표현할 때는 논리 연산자 &&을 사용하여야 한다.
1 < x && x < 5

‣ 등가 비교 연산자

: 두 항의 값이 동등한 지 여부를 판단할 때 사용하며 모든 타입에 사용할 수 있다.

⇒ 기본 타입과 참조 타입 간에는 등가 비교 연산자를 사용할 수 없다

(기본 타입의 값끼리 / 참조 타입의 값끼리만 사용 가능)

3 == 5; // false
3 == 3; // true

3 !== 3 // false
3 !== 5 // true

 

⑤ 논리 연산자
: &&(and), ||(or), !(not)

→ 공통적으로 boolean타입을 반환한다

x y x && y x || y !x !y !!x
(!!y)
false false false false true true false
false true false true      
true false false true      
true true true true      

 

2. 연산자 우선순위

우선순위 연산자 내용
1 (),[] 괄호 / 대괄호
2 !, ~, ++, -- 부정/ 증감 연산자
3 *, /, % 곱셈 / 나눗셈 연산자
4 <, <=, >, >= 대소 비교 연산자
5 && AND 연산자
6 || OR 연산자
7 ? : 조건 연산자
8 =, +=, -=, /=, %= 대입/할당 연산자
참 또는 거짓) ? 참일 때 결과 : 거짓일 때 결과
int num = (1 + 2 == 3 && 4 + 1 * 2 == 6) ? 3 + 4 : 5 + 6;
System.out.println(num); // 7

3. 콘솔 입출력

① 콘솔 출력

‣ System.out.print()

: 소괄호 안의 내용을 단순 출력함(줄 바꿈 X)

→ 실행했을 때 소괄호 안의 내용이 출력되고 커서는 출력된 내용의 뒤로 이동함.

 

‣ System.out.println()

: 소괄호 안의 내용을 출력하고 줄 바꿈 함

→ 실행했을 때 소괄호 안의 내용이 출력되고 커서는 다음 줄로 이동함.

 

‣ System out.printf()

: 지시자(specifier / 형식 지정자)를 이용해

변수의 값을 여러 형식으로 출력해주는 메서드. f는 formatted의 약자.

 

✷ 지시자

:  값을 어떤 형식으로 출력할 것인지 지정하기 위해 사용한다.
→ 실제로 출력되는 값이 아니고 값을 변환하여 자신의 위치에 출력해주는 기능을 한다.

지시자 출력 포맷
%b 불리언
%d 10진수
%o 8진수
%x, %X 16진수
%c 문자
%s 문자열
%n 줄바꿈

String.format()

: 문자열의 형식을 설정하는 메서드

System.out.printf("%s%n", "Stardew Valley"); // 줄바꿈이 됨
System.out.printf("%s%n", "Emily" + "Johnson");
System.out.printf("%d%n", 3+6); 
System.out.printf("지금은 %s입니다", 2022 + "year"); // 자동 타입 변환이 일어남
System.out.printf("나는 %c%s입니다", '에밀리', "존슨"); // 여러 개의 인자를 넣을 수 있다.

② 콘솔 입력

import java.util.Scanner;                 // Scanner 클래스를 가져온다.

Scanner scanner = new Scanner(System.in); // Scanner 클래스의 인스턴스를 생성한다.
String inputValue = scanner.nextLine();   // 입력한 내용이 inputValue에 저장된다.

System.out.println(inputValue);           // 입력한 문자열이 출력된다.

‣ import java.util.Scanner;

데이터를 입력받는 기능을 작은 프로그램으로 만들어둔 것이 Scanner는 java.util이라는 위치에 저장되어 있는데, Scanner를 사용하려면 먼저 작성하고 있는 소스코드 안으로 Scanner를 불러와야 한다.

→ import java.util.Scanner;는 java.util에 있는 Scanner를 이 소스 코드로 불러와라 라는 의미다.

 

‣ Scanner scanner = new Scanner(System.in);

클래스를 통해 객체를 만들어 낼 때에는 new 연산자를 사용하며, 그 결과물로 만들어진 객체를 인스턴스라고 한다.

클래스에 new 연산자를 적용하여 인스턴스를 만드는 것을 클래스를 인스턴스화 한다라고 표현한다.

→ 불러온 Scanner 클래스를 new 연산자를 통해 인스턴스를 생성하고,

생성된 인스턴스를 변수 scanner에 할당하는 코드이다. 이제 데이터를 입력받기 위한 준비 과정이 끝난 것이다.

 

Scanner는 클래스이다. 클래스는 객체를 찍어낼 수 있는 일종의 틀이며,

사용하고자 하는 Scanner 클래스의 데이터 입력 기능은 Scanner 클래스 자체가 아니라,

그것을 통해 만들어낸 객체에 존재한다.

→ 데이터 입력 기능을 사용하려면 Scanner 클래스를 통해 객체를 먼저 만들어야 한다.

 

‣ String inputValue = scanner.nextLine();

Scanner 클래스를 인스턴스 화한 scanner에는 nextLine()이라는 메서드가 존재하는데,

이는 콘솔을 통해 문자열 데이터를 입력받는 기능을 수행한다.

→ scanner.nextLine()은 문자열을 입력받기 위한 코드이며,

입력받은 문자열은 inputValue라는 변수에 할당되어 저장된다.

scanner에는 문자열을 입력받는 nextLine()뿐만 아니라,

정수형을 입력받을 수 있는 nextInt(), 실수형을 입력받을 수 있는 nextFloat()등의 메서드들도 존재한다.

 

4. 참조 자료형

 기본 타입으로 선언된 변수와 참조 타입으로 선언된 변수의 차이점

: 저장되는 값이 무엇이냐를 따진다.

→ 기본 타입을 이용해 선언된 변수는 실제 값을 변수에 저장

→ 참조 타입을 이용해 선언된 변수는 메모리의 주소를 값으로 갖는다.

 

 JVM이 가지는 메모리 영역

‣ 메서드 영역

: JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역.

→ 코드에서 사용되는 클래스들을 클래스 로더로 읽어 클래스별로 분류하여 저장함.

 런타임 상수풀, 필드 데이터, 메서드 데이터, 메서드 코드, 생성자 코드 등

 

 힙 영역

: 객체, 배열이 생성되는 영역.

 힙 영역에 생성된 객체, 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조함.

 참조하는 변수 / 필드가 없으면 연결고리가 없는 객체가 되기 때문에

JVM은 가비지 컬렉터를 실행시켜 쓰레기 객체를 힙 영역에서 자동 제거한다.

 자바에서는 힙 영역에서 사용되지 않는 배열이나 객체와 같은 참조 자료형을 쓰레기(Garbage)로 취급,

이를 가비지 컬렉터(Garbage Collector)가 적절한 시점에 자동으로 수집하여 가용 메모리를 반환, 주기 특정은 어려움.

 

‣ JVM 스택 영역

: 메서드를 호출할 때마다 프레임을 추가하고 메서드가 종료되면 해당 프레임을 제거하는 동작 수행.

 스레드: 프로세스 내 일종의 코드가 실행되는 흐름.

→ 각 스레드마다 하나씩 존재, 스레드가 시작될 때 할당됨

 만약 자바 프로그램에서 추가적으로 스레드를 생성하지 않았을 때는 main 스레드만 존재한다.

⇒ 이 말은 JVM 스택도 하나라는 의미다.

 

 기본 타입 변수는 스택 영역에 직접 값을 가지고 있지만,

참조 타입 변수는 값이 아니라 힙 영역이나 메소드 영역의 객체 주소를 가진다.

int[] scores = {100, 95, 90}

배열 변수인 scores는 스택 영역에 생성되지만 100, 95, 90을 갖는 배열은 힙 영역에 생성되고,

배열 변수 scores에는 배열의 힙 영역 주소가 저장된다.

 

ex)

// 기본 타입
int age = 33;
double price = 120.5;

// 참조 타입
String name = "김코딩";
String hobby = "독서";

메모리 상에서 변수들이 갖는 값을 그림으로 표현한 것

→ price와 age는 직접 변수에 값을 저장하지만(각각 double와 int라는 기본 타입의 값을 가지기 때문에),
String 클래스 변수인 name과 hobby는 힙 영역의 String 객체 주소 값을 가지고 있다.
⇒ String 클래스 변수를 참조 타입 변수라고 한다.

 

‣ 참조 타입(reference type)

: 객체를 참조한다. String, 배열, enum, 클래스, 인터페이스 등.
변수에 할당한 데이터가 객체면 참조 타입

 

5. String 타입

: 큰따옴표""로 감싸진 문자열의 타입, char의 배열을 의미함.

// String 타입의 선언과 저장
String 변수;
변수 = "문자열";

String 변수 = "문자열";
String 변수 = new String("문자열");

String name1 = "Haley";
String name2 = new String("Haley");

System.out.print(name1); // "Haley"
System.out.print(name2); // "Haley"

문자열 리터럴을 직접 할당하는 방식이든, String 클래스의 인스턴스를 생성하여 할당하는 방법이든

공통적으로 참조 타입의 변수에 할당된다.

⇒ 실제 문자열의 내용을 값으로 가지고 있는 것이 아니라, 문자열이 존재하는 메모리 공간 상의 주소값을 저장하고 있다.

하지만 실제로 문자열을 출력해보면 주소값이 아닌 문자열의 내용이 출력되는데
이는 String 타입의 변수를 참조하면 String 클래스의 메서드인 toString()이 자동으로 호출되기 때문이다. 
toString()이 자동으로 호출되면  String 타입의 변수가 저장하고 있는 주소값에 위치한 String 인스턴스의 내용을 

문자열로 변환해준다.

 

🚫 문자열이 직접 String 변수에 저장되는 것이 아님.

즉, 문자열을 String 변수에 저장한다는 말은 틀린 말이 되는 것. 🚫

→ 문자열은 String의 객체로 생성되고, 변수는 이 객체의 주소를 참조한다. String 타입도 그렇기 때문에 참조 자료형이다.

문자열을 저장할 경우 문자열 리터럴을 사용할 수 있지만, new 연산자를 사용해 직접 String 객체를 생성시킬 수 있다.

// 문자열 리터럴로 생성하느냐 new 키워드로 생성하느냐에 따라
// 비교연산자(==)의 결과가 달라질 수 있음을 알려주는 예시
String name1 = "Haley";
String name2 = "Haley";
String name3 = new String("Haley");

//case 1
System.out.println(name1 == name2) // -> true

name1과 name2는 동일한 문자열 리터럴로 생성된 객체를 참조하기 때문에,
비교 연산자(==)로 비교하였을 때 결과가 true로 나온다.

//case 2
System.out.println(name1 == name3) // -> false

name3은 new 키워드로 String 객체를 별도로 생성했기 때문에 주소 값이 다르다.
따라서 비교 연산자(==)로 비교하였을 때, name1과 name3은 false의 값을 가지게 된다.
// 동일한 문자열 리터럴을 두 변수에 할당하는 경우
// 두 변수는 같은 문자열의 참조값을 공유한다
// name1과 name2가 저장하게 되는 문자열의 주소값이 같다
String name1 = "Haley";
String name2 = "Haley";

// String 클래스의 인스턴스를 생성하게 되는 경우
// 문자열의 내용이 같아도 별개의 인스턴스가 따로 생성된다
// name3와 name4가 할당받게 되는 인스턴스의 참조값이 서로 달라 서로 다른 인스턴스의 주소값을 저장하고 있게 된다
String name3 = new String("Haley");
String name4 = new String("Haley");

// name1이 문자열 리터럴을 직접 할당받음
// 비교의 대상이 되는 우항도 내용이 같은 문자열
// 문자열 리터럴은 내용이 같다면 같은 주소값을 가진다
boolean comparison1 = name1 == "Haley"; // true

// name1과 name2가 내용이 같은 문자열 리터럴을 직접 할당받은 변수이므로
// 두 변수가 같은 문자열의 참조값을 가진다
boolean comparison2 = name1 == name2;   // true

// name1은 문자열 리터럴을 할당 받은 변수, name3는 String 클래스를 통해 인스턴스를 생성하여 할당받은 변수
// 두 변수는 다른 주소값을 저장하고 있게 된다
/ * String 클래스로 인스턴스를 생성하면 항상 별개의 인스턴스가 생성된다 * /
boolean comparison3 = name1 == name3;   // false

// 두 변수 모두 String 클래스로 인스턴스를 생성하여 할당받은 변수
// 두 변수는 서로 다른 주소값을 저장하게 된다
boolean comparison4 = name3 == name4;   // false

// equals()메소드를 활용하여 name1과 문자열 리터럴의 내용이 같은지 비교
// equals()메소드는 내용이 같은지만을 비교함
boolean comparison5 = name1.equals("Haley"); // true

// name1과 name3, name3과 name4는 참조값이 다르지만 내용이 같기 때문에 true
boolean comparison6 = name1.equals(name3);   // true
boolean comparison7 = name3.equals(name4);   // true

✷ 등가 비교 연산자(==)와 equals() 메서드

등가 비교 연산자

: 좌항 == 우항의 형태로 사용할 수 있으며,

좌항의 값과 우항의 값이 일치하는지 검사하여 일치하면 true를, 일치하지 않으면 false를 반환한다.

양쪽의 항에는 변수 또는 값 등이 위치할 수 있다.

equals() 메서드

: . 앞의 변수가 저장하고 있는 문자열의 내용과 () 안의 문자열의 내용이 같은지 비교하여

같으면 true를 다르면 false를 반환한다.

 

6. String클래스의 메서드

(표로 간단하게 정리한 주요 메서드)

메소드 이름 설명
char charAt(int index) 해당 문자열의 특정 인덱스에 해당하는 문자를 반환함.
int compareTo(String str) 해당 문자열을 인수로 전달된 문자열과 사전 편찬 순으로 비교함.
String concat(String str) 해당 문자열의 뒤에 인수로 전달된 문자열을 추가한 새로운 문자열을 반환함.
String[] split(String regex) 해당 문자열을 전달된 정규 표현식(regular expression)에 따라 나눠서 반환함.
indexOf(char char) /indexOf(String str) 해당 문자열에서 특정 문자나 문자열이 처음으로 등장하는 위치의 인덱스를 반환
String substring(int begin, int end) 해당 문자열의 전달된 시작 인덱스부터 마지막 인덱스까지를 새로운 문자열로 반환함.
length() 해당 문자열의 길이를 반환함.

① charAt()

만약 해당 문자열의 길이보다 큰 인덱스나 음수를 전달하면 오류가 발생한다.

String str = new String("Leah");
System.out.println("문자열 : " + str); // "문자열 : Leah"

System.out.println(str.charAt(0)); // 'L'
System.out.println(str.charAt(1)); // 'e'
System.out.println(str.charAt(2)); // 'a'
System.out.println(str.charAt(3)); // 'h'

System.out.println("\ncharAt() 메소드 호출 후 문자열 : " + str);

② compareTo()

문자열을 비교할 때 대소문자를 구분하여 비교한다.

→ 두 문자열이 같으면 0을 반환, 해당 문자열이 인수로 전달된 문자열보다 작으면 음수, 크면 양수를 반환함

⇒ 대소문자를 구분하지 않기를 원하면 compareToIgonoreCase() 사용.

String str = new String("abcd");
System.out.println("문자열 : " + str);
System.out.println(str.compareTo("bcef"));
System.out.println(str.compareTo("abcd") + "\n");
System.out.println(str.compareTo("Abcd"));
System.out.println(str.compareToIgnoreCase("Abcd"));
System.out.println("compareTo() 메소드 호출 후 문자열 : " + str);

③ concat()

만약 인수로 전달된 문자열의 길이가 0이면 해당 문자열을 그대로 반환한다.

String str = new String("Stardew");
System.out.println("문자열 : " + str);
System.out.println(str.concat("Valley"));
System.out.println("concat() 메소드 호출 후 문자열 : " + str);

④ indexOf()

만약 해당 문자열에 전달된 문자나 문자열이 포함되어 있지 않으면 -1을 반환한다.

String str = new String("Stardew Valley");
System.out.println("문자열 : " + str);
System.out.println(str.indexOf('V'));
System.out.println(str.indexOf('y'));
System.out.println(str.indexOf("Valley"));
System.out.println("indexOf() 메소드 호출 후 원본 문자열 : " + str);

⑤ trim()

: 해당 문자열의 맨 앞과 맨 뒤에 포함된 모든 공백 문자를 제거해준다.

String str = new String(" Ridgeside     ");
System.out.println("문자열 : " + str);
System.out.println(str + '|');
System.out.println(str.trim() + '|');
System.out.println("trim() 메소드 호출 후 문자열 : " + str);

⑥ toLowerCase() / toUpperCase()

String str = new String("PellicanCity");
System.out.println("문자열 : " + str);
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
System.out.println("두 메소드 호출 후 문자열 : " + str);

7. Array(배열) 타입

: 동일한 타입의 값들을 하나의 묶음으로 묶은 자료 구조.

‣ 변수에 한 개의 데이터만 저장할 수 있다.

‣ 저장해야 할 데이터의 개수가 많아지면 그만큼의 더 많은 변수가 필요해지고, 배열이라는 타입을 사용한다.

→ 배열도 객체이므로 힙 영역에 생성되고 배열 변수는 힙 영역의 배열 객체를 참조하게 된다.

참조할 배열 객체가 없다면 배열 변수는 null 값으로 초기화될 수 있다.

⇒ 각 값들이 같은 의미를 지니면서 서로 연관성이 있을 때, 이 값들을 하나로 묶을 수 있고 묶인 값들의 집합이 배열.

 

① 배열 선언 작성 형태

// 타입[ ] 변수;
// 타입 변수[ ];

int[] intArray;
String[] strArray;

int intArray[];
String strArray[];

② 배열 생성 방법

‣ 값 목록으로 배열 생성

중괄호{}를 통해 값들을 묶을 수 있음.

데이터타입[] 변수 = { 값0, 값1, 값2, 값3 ...}; 
String[] names = {"헤일리", "에밀리"}

‣ new 연산자로 배열 생성

타입[] 변수 = new 타입[길이];
int[] intArray = new int[5];

→ 배열 생성 후, 새로운 값을 저장할 때는 대입 연산자를 사용하여 작성한다.

변수[인덱스] = 값;
int[] scores = new int[3];
scores[0] = 96;
scores[1] = 2039;
scores[2] = 1;

✷ 배열의 차원

: 배열이 중첩된 정도를 차원이라고 한다.

배열이 중첩되었다 == 배열의 요소가 또 다른 배열인 경우

‣ 1차원 배열

: 배열이 중첩이 없는 경우 = 배열의 요소가 배열이 아닌 경우

{1, 2, 3, 4}
→ 배열의 각 요소는 모두 정수형의 값이다

‣ 2차원 배열(2차원 이상의 배열을 다차원 배열이라고 한다)

: 배열이 한 번 중첩된 경우 = 배열의 요소가 배열인 경우

{{1, 2, 3, 4}, {5, 6, 7, 8}}
→ 배열의 각 요소로 배열이 들어가 있다

③ 배열의 선언과 초기화

: 타입 뒤에 대괄호를 붙여 선언하고 초기화할 수 있다.

// 배열을 가리킬 참조 변수를 선언한다
double[] temperatureOfJuly;

// new double[31]은 총 31개의 double형 값을 저장할 수 있는 배열이 생성된다는 뜻이다
// 배열의 모든 요소는 double형의 기본값인 0.0으로 초기화 되어있다

// 대입 연산자(=)에 의해 생성된 첫번째 요소의 주소값이 참조 변수에 할당된다
// 참조변수는 배열의 맨 첫번째 요소를 가리키게 된다
temperatureOfJuly = new double[31];

// 위에 작성한 두 줄의 코드와 이 한 줄의 코드의 결과는 같다
// 선언과 초기화를 한 줄의 코드로 작성할 수 있다
double[] temperatureOfJuly = new double[31];

✷ 왜 temperatureOfJuly는 참조 변수여야 할까?

‣ 자바에서 배열은 참조 타입에 해당하기 때문에

double[]이라는 배열 선언 문법으로 선언한 변수는 선언 이후 생성될 배열의 주소값을 담을 참조 변수가 된다.

→ int, double과 같은 기본 타입의 경우,

기본 타입의 값을 저장할 변수를 선언하는 시점에서 얼마 큼의 메모리 공간을 확보해야 하는지 컴퓨터가 알 수 있다.

변수 이름 앞에 타입이 붙어 있고, 각 타입별로 크기가 정해져 있기 때문이다.

 

하지만 참조 타입의 경우에는 배열을 선언하는 시점에서 배열이 몇 개의 요소를 가질지 컴퓨터가 알 수 없다.

배열을 선언하면 이후 생성될 배열의 주소값을 담을 메모리 공간만 확보되고,

이후 배열이 생성되고 나서 해당 배열의 시작 주소값이 참조 변수에 할당된다.

// 실제 값을 넣어 초기화하는 방법
double[] temperatureOfJuly = new double[] { 27.4, 30.1, 31.1, 32.4, ..., 31.8 };

// 선언과 초기화를 하나의 문장으로 할 때에 한해 new double[]을 생략할 수 있다.
double[] temperatureOfJuly = { 27.4, 30.1, 31.1, 32.4, ..., 31.8 };
① 참조 변수 temperauterOfJuly가 선언된다
② 배열이 생성되고 중괄호{}안의 값으로 배열 요소들의 값이 초기화된다
③ 생성된 배열의 첫 번째 요소의 주소값이 참조 변수에 할당된다

첫 번째 요소는 temperatureOfJuly[0]으로 접근할 수 있으며, 값은 27.4
두 번째 요소는 temperatureOfJuly[1]로 접근할 수 있으며, 값은 30.1
세 번째 요소는 temperatureOfJuly[2]로 접근할 수 있으며, 값은 31.1
네 번째 요소는 temperatureOfJuly[3]으로 접근할 수 있으며, 값은 32.4
마지막 요소는 temperatureOfJuly[30]으로 접근할 수 있으며, 값은 31.8
→ 참조 변수가 가진 배열 첫 번째 요소의 주소값에 인덱스 * 요소의 크기를 더하여 얻을 수 있는 수를 주소로 가진 요소의 값을 읽어온다.

 

④ 배열 길이

: 배열에 저장할 수 있는 전체 항목의 수. 배열이 가진 요소의 개수.

→ 배열 객체의 length 필드를 사용하면 코드에서 배열의 길이를 얻을 수 있다.

→ length는 읽기 전용 필드이기 때문에 값을 바꿀 수 없다.

배열변수.length

int[] intArray = {0, 1, 2, 3, 4};
System.out.println(intArray.length); // 5

✷ Arrays.toString()

: 배열의 주소값이 아닌 배열의 내용을 출력하기 위한 메서드.

double[] values = {1.0, 1.1, 1.2};
System.out.println(values.toString()); // [D@46a49e6 같은 참조값이 출력된다
System.out.println(Arrays.toString(values)); // [1.0, 1.1, 1.2]이 출력된다

 

⑤ 객체를 참조하는 배열

기본 타입(byte, char, short, int, long, float, double, boolean) 배열은 각 항목에 직접 값을 가지고 있다.

‣ 참조 타입(클래스, 인터페이스) 배열은 각 항목에 객체의 힙 주소를 가지고 있다.

// 배열 변수 strArray를 선언하고 5개의 문자열을 참조하는 배열을 생성하는 코드
String[] strArray = new String[5];
strArray[0]= "Haley";
strArray[1]= "Emily";
strArray[2]= "Harvey";
strArray[3]= "Alex";
strArray[4]= "Leah";

⇒ String[] 배열의 항목도 결국 String 변수와 동일하게 취급되어야 한다.

 

⑥ 배열 복사

‣ 자바에서의 배열은 한 번 생성하면 크기를 변경할 수 없음.

→ 더 많은 저장공간이 필요할 때 큰 배열을 새로 만들고 이전 배열로부터 항목 값들을 복사한다.

→ 항목 값들을 복사할 때 for문을 사용하거나 System.arraycopy() 메서드를 사용한다.

✷ 얕은 복사(shallow copy)

: 배열은 복사될 때 복사되는 값이 객체의 주소이고, 새 배열의 항목은 이전 배열의 항목이 참조하는 객체와 동일하다.

ex)

public class Main {
  public static void main(String[] args) {
    int[] oldIntArr = {1,2,3};
    int[] newIntArr = new int[5];
    for(int i =0; i<oldIntArr.length; i++){
      newIntArr[i] = oldIntArr[i];
    }
    System.out.println(Arrays.toString(newIntArr)); // [1,2,3,0,0]
    
    // System.arraycopy(복사대상 배열, 복사를 시작할 인덱스, 붙여넣기할 배열, 복사대상 배열의 길이)
    System.arraycopy(oldIntArr, 0, newIntArr, 0, oldIntArr.length); // [1,2,3,0,0]
  }
}

⑦ 2차원 배열

‣ 2차원 배열의 선언과 초기화

int[][] kcal;
kcal = new int[31][3];

int[][] kcal = new int[31][3];

위의 예제 코드가 실행되면 다음과 같은 2차원 배열이 만들어지는데,

내부 배열은 int형의 기본값인 0을 3개씩 저장하고 있으며, 외부 배열은 내부 배열({ 0, 0, 0 }) 31개를 저장하고 있다.

⇒ 내부 배열은 3개의 0을 요소로 가지지만, 외부 배열의 각 요소는 내부 배열의 주소값을 저장하고 있다.

{
	{ 0, 0, 0 },
	{ 0, 0, 0 },
	{ 0, 0, 0 },

	...

	{ 0, 0, 0 }
}

값을 넣어 초기화를 하는 경우 다음과 같이 코드를 작성할 수 있다.

int[][] kcal = new int[][] { 
  { 1982, 2098, 2130 }, 
  { 2242, 2431, 2198 }, 
  { 2365, 1997, 1932 },
	
	...

  { 2278, 2391, 2006 }
}

// 선언과 초기화를 하나의 문장으로 할 때에 한해 new int[][]를 생략할 수 있다. 
int[][] kcal = { 
  { 1982, 2098, 2130 }, 
  { 2242, 2431, 2198 }, 
  { 2365, 1997, 1932 },
	
	...

  { 2278, 2391, 2006 }
}

⑧ 가변 배열

: 배열이 2차원 이상일 때 마지막 차수에 해당하는 배열의 길이를 고정하지 않아도 되는 배열.

// 내부 배열의 크기를 자유롭게 지정할 수 있는 외부 배열만 생성된 상태
int[][] ages = new int[5][];
System.out.println("Arrays.toString(ages) = " + Arrays.toString(ages));

// 결과
// Arrays.toString(ages) = [null, null, null, null, null]]
// 내부 배열을 생성할 때 new int[]를 이용하여 외부 배열의 각 요소에 할당해준다.
int[][] ages = new int[5][];

ages[0] = new int[5];
ages[1] = new int[6];
ages[2] = new int[7];
ages[3] = new int[8];  
ages[4] = new int[9];

System.out.println("Arrays.toString(ages[0]) = " + Arrays.toString(ages[0]));
System.out.println("Arrays.toString(ages[1]) = " + Arrays.toString(ages[1]));
System.out.println("Arrays.toString(ages[2]) = " + Arrays.toString(ages[2]));
System.out.println("Arrays.toString(ages[3]) = " + Arrays.toString(ages[3]));
System.out.println("Arrays.toString(ages[4]) = " + Arrays.toString(ages[4]));

// 결과
// Arrays.toString(ages[0]) = [0, 0, 0, 0, 0]
// Arrays.toString(ages[1]) = [0, 0, 0, 0, 0, 0]
// Arrays.toString(ages[2]) = [0, 0, 0, 0, 0, 0, 0]
// Arrays.toString(ages[3]) = [0, 0, 0, 0, 0, 0, 0, 0]
// Arrays.toString(ages[4]) = [0, 0, 0, 0, 0, 0, 0, 0, 0]
// 가변 배열도 생성과 동시에 초기화 할 수 있다.
int[][] ages = {
  { 30, 32, 39, 59, 23 },
  { 31, 41, 52, 56, 72, 13 },
  { 45, 32, 84, 23, 13, 42, 55 },
  { 23, 41, 62, 64, 23, 51, 67, 98 },
  { 13, 14, 17, 84, 52, 37, 68, 66, 33 }
};

System.out.println("Arrays.toString(ages[0]) = " + Arrays.toString(ages[0]));
System.out.println("Arrays.toString(ages[1]) = " + Arrays.toString(ages[1]));
System.out.println("Arrays.toString(ages[2]) = " + Arrays.toString(ages[2]));
System.out.println("Arrays.toString(ages[3]) = " + Arrays.toString(ages[3]));
System.out.println("Arrays.toString(ages[4]) = " + Arrays.toString(ages[4]));

// 결과
// Arrays.toString(ages[0]) = [30, 32, 39, 59, 23]
// Arrays.toString(ages[1]) = [31, 41, 52, 56, 72, 13]
// Arrays.toString(ages[2]) = [45, 32, 84, 23, 13, 42, 55]
// Arrays.toString(ages[3]) = [23, 41, 62, 64, 23, 51, 67, 98]
// Arrays.toString(ages[4]) = [13, 14, 17, 84, 52, 37, 68, 66, 33]

 

8. Enum(열거) 타입

: 여러 상수들을 보다 편리하게 선언할 수 있게 만들어진 자바의 문법 요소. 서로 연관된 상수들의 집합.

→ 서로 관련있는 내용들을 모아 한 번에 간편하게 관리할 때 사용한다.

 몇 가지로 한정된 변하지 않는 데이터를 다루는 데 사용한다.

 

✷ JDK 1.5 이전 버전에서 enum 문법을 지원하지 않았기 때문에 

여러 상수를 정의하려면 public static final을 통해 전역변수로 상수를 설정하여 사용했다.

ex)

public static final int SPRING = 1; // 1, 2, 3, 4는 각 상수를 구분하기 위해 사용된 값
public static final int SUMMER = 2;
public static final int FALL = 3;
public static final int WINTER = 4;

// 정수값을 통해 상수를 할당했을 때 상수명이 중복되는 경우가 발생한다.
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int FALL   = 3;
public static final int WINTER = 4;

public static final int DJANGO  = 1;
public static final int SPRING  = 2; // 계절의 SPRING과 중복 발생 → 컴파일 에러
public static final int NEST    = 3;
public static final int EXPRESS = 4;
// interface를 사용하여 상수를 구분함으로써 일차적으로 해결할 수 있지만, 타입 안정성의 문제가 생긴다.
interface Seasons {
  int SPRING = 1, SUMMER = 2, FALL = 3, WINTER = 4;
}

interface Frameworks {
  int DJANGO = 1, SPRING = 2, NEST = 3, EXPRESS = 4;
}

// Seasons의 SPRING과 Frameworks의 SPRING이 의미적으로 다른 개념이지만 
// 이 둘을 비교했을 때 에러가 발생하지 않아 타입 안정성이 떨어진다. 
if (Seasons.SPRING == Frameworks.SPRING) {...생략...}

// 객체를 생성해주면 상수명 중복과 타입 안정성 문제를 해결할 수 있지만 
// 코드가 길어지고 사용자 정의 타입이라 switch문에 활용할 수 없음
class Seasons {
  public static final Seasons SPRING = new Seasons();
  public static final Seasons SUMMER = new Seasons();
  public static final Seasons FALL   = new Seasons();
  public static final Seasons WINTER = new Seasons();
}

class Frameworks {
  public static final Frameworks DJANGO  = new Frameworks();
  public static final Frameworks SPRING  = new Frameworks();
  public static final Frameworks NEST    = new Frameworks();
  public static final Frameworks EXPRESS = new Frameworks();
}
// 열거 타입 및 열거 상수 선언
public enum 열거타입명 { 열거상수...};

public enum Week { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,... };
// 관례적으로 열거 상수는 모두 대문자로 작성한다.

// 열거타입 변수;
Week today; 
Week day; 

// 열거타입 변수 = 열거타입.열거상수;
Week today = Week.MONDAY;

enum Seasons { SPRING, SUMMER, FALL, WINTER }
enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }
class Seasons {
  public static final Seasons SPRING = new Seasons();
  public static final Seasons SUMMER = new Seasons();
  public static final Seasons FALL   = new Seasons();
  public static final Seasons WINTER = new Seasons();
}

public class Main {
  public static void main(String[] args) {
    Seasons seasons = Seasons.SPRING;
    switch (seasons) {
      case Seasons.SPRING:
        System.out.println("봄");
        break;
      
      case Seasons.SUMMER:
        System.out.println("여름");
        break;
      
      case Seasons.FALL
        System.out.println("가을")
        break;
      
      case Seasons.WINTER:
        System.out.println("겨울");
        break;
      }
    }
}

/*
출력값
java: incompatible types: Seasons cannot be converted to int
*/
switch문의 조건이 char, byte, short, int, Character, Byte, Short, Integer, String, enum
타입만 가능하지만, seasons는 사용자 정의 타입이기 때문에 코드를 실행하면 호환되지 않는 타입이라는 에러가 발생한다.

enum Seasons {SPRING, SUMMER, FALL, WINTER}

public class Main {
  public static void main(String[] args) {
    Seasons seasons = Seasons.SPRING;
    switch (seasons) {
      case SPRING:
        System.out.println("봄");
        break;
      
      case SUMMER:
        System.out.println("여름");
        break;
      
      case FALL:
        System.out.println("가을");
        break;
      
      case WINTER:
        System.out.println("겨울");
        break;
       }
    }
}

// 출력값이 봄 으로 나온다.

 

→ 데이터 중 몇 가지로 한정된 값만을 갖는 경우에 열거 타입을 사용한다.

열거 타입도 참조 타입이기 때문에 열거 타입 변수에 null 값을 저장할 수 있다.

ex)

enum Level {
  LOW,
  MEDIUM,
  HIGH
}

public class Main {
  public static void main(String[] args) {
    Level myVar = Level.MEDIUM;

    switch(myVar) {
      case LOW:
        System.out.println("Low level");
        break;
      case MEDIUM:
         System.out.println("Medium level");
        break;
      case HIGH:
        System.out.println("High level");
        break;
    }
  }
}

→ myVar 변수에 할당된 열거 객체의 값으로 case를 나누고, 이에 따라 콘솔에 출력되는 값이 달라진다.

 

여러 상수들을 보다 편리하게 선언하고 관리할 수 있게하며, 상수명의 중복을 피하고, 타입에 대한 안정성을 보장하며,

같은 효과를 낼 수 있는 다른 코드에 반해 훨씬 더 간결하고 가독성이 좋은 코드를 작성할 수 있고, 

switch문에서도 작동이 가능하다.

 

✷ 열거 객체 메서드

: 열거 객체는 열거 상수의 문자열을 내부 데이터로 가지고 있다.

리턴 타입 메소드(매개변수) 설명
String name() 열거 객체가 가지고 있는 문자열을 리턴하며,
리턴되는 문자열은
열거타입을 정의할 때 사용한 상수 이름과 동일
int ordinal() 열거 객체의 순번(0부터 시작)을 리턴
int compareTo(비교값) 주어진 매개값과 비교해서 순번 차이를 리턴
열거 타입 valueOf(String name) 주어진 문자열의 열거 객체를 리턴
열거 배열 values() 모든 열거 객체들을 배열로 리턴

9. 형 변환
참고링크1

참고링크2-Char to Int Convert in Java

참고링크3-Int to Char Convert in Java


① 문자열을 실수, 정수로 변환

‣ String to int

String strNum = "10";
int parsedNum = Integer.parseInt(strNum);
//입력받은 인자값을 10진수의 Integer 객체형으로 변환하여 리턴

int valueOfNum = Integer.valueOf(strNum);
//입력받은 인자값을 Number 객체형으로 변환하여 리턴

String to double, float

String strNum = "10";
double doubleNum = Double.valueOf(strNum);
//입력받은 인자값을 double 객체형으로 변환하여 리턴

float floatNum = Float.valueOf(strNum);
//입력받은 인자값을 float 객체형으로 변환하여 리턴

String to long, short

String strNum = "10";
long longNum = Long.valueOf(strNum);
short shortNum = Short.valueOf(strNum);

② 실수, 정수를 문자열로 변환 

int to String

int num = 10;
String strNum = String.valueOf(num);
// 입력받은 인자값을 String 객체형으로 변환하여 리턴

String strNum = Integer.toString(num);
// Integer 메소드를 사용하여 String 객체형으로 변환하여 리턴

double, float to String

double dnum = 10.10;
float fnum = 10.10f;

String doubleNum = String.valueOf(dnum);
String doubleNum = Integer.toString(dnum);
// 각 타입의 메소드를 사용하여 입력값을 String 객체형으로 변환하여 리턴.

String floatNum = String.valueOf(fnum);
String floatNum = Integer.toString(fnum);
// 각 타입의 메소드를 사용하여 입력값을 String 객체형으로 변환하여 리턴.

Math.abs()

: 음수를 양수로, 양수는 양수 그대로 표시하여 절대값을 리턴해주는 java.lang.Math 클래스 안의 메서드 중 하나.

int 또는 long의 경우 최소 음수인 경우에는 절대값이 아닌 음수를 그대로 리턴해준다.

Comments