22.12.11 배운 내용 정리
(변수 유효 범위, static 응용 - 싱글톤 패턴, 배열, 다차원 배열)
변수 유효 범위
지역 변수(로컬 변수) = 함수나 메서드 안에서만 사용할 수 있는 변수
멤버 변수(인스턴스 변수) = 클래스 안에서 사용하는 변수
static 변수(클래스 변수) = 여러 인스턴스에서 공통으로 사용할 수 있는 변수
이러한 변수는 어디에 어떻게 선언되느냐에 따라 유효 범위가 달라짐.
① 지역 변수의 유효 범위
지역 변수는 함수나 메서드 내부에 선언함 → 함수 밖에서 사용 불가
즉 하나의 함수에 선언한 지역 변수는 다른 함수에서 사용 불가능
스택 = 지역 변수가 생성되는 메모리
스택에 생성되는 지역 변수는 함수가 호출될 때 생성되었다가
함수가 반환되면 할당되었던 메모리 공간이 해제되면서 함께 없어짐.
② 멤버 변수의 유효 범위
멤버 변수는 클래스가 생성될 때 힙 메모리에 생성되는 변수로 클래스의 어느 메서드에서나 사용 가능함.
힙에 생성된 인스턴스가 가비지 컬렉터에 의해 수거되면 메모리에서 사라짐.
따라서 클래스 내부의 여러 메서드에서 사용할 변수는 멤버 변수로 선언하는 것이 좋음.
③ static 변수의 유효 범위
사용자가 프로그램을 실행하면 메모리에 프로그램이 상주하는데, 이때 프로그램 영역 중에 데이터 영역이 있음.
이 영역에는 상수나 문자열, static 변수가 생성됨.
인스턴스 변수는 객체가 생성되는 문장, 즉 new가 되어야 생성되지만,
static 변수는 클래스 생성과 상관없이 처음부터 데이터 영역 메모리에 생성됨.
따라서 인스턴스 변수와 static 변수는 사용하는 메모리가 다름.
이렇게 생성된 static 변수는 private이 아니라면 클래스 외부에서도 객체 생성과 무관하게 사용 가능함.
프로그램 실행이 끝난 뒤 메모리에서 내려가면(ex.워드 프로그램에서 [닫기]를 한 경우) static 변수도 소멸됨.
static 변수는 프로그램이 시작할 때부터 끝날 때까지 메모리에 상주하므로
크기가 너무 큰 변수를 static으로 선언하는 건 좋지 않음.
변수 유형에 따른 용도
변수 유형 | 선언 위치 | 사용 범위 | 메모리 | 생성과 소멸 |
지역 변수 (로컬 변수) |
함수 내부에 선언 | 함수 내부에서만 사용 | 스택 | 함수가 호출될 때 생성되고 함수가 끝나면 소멸함 |
멤버 변수 (인스턴스 변수) |
클래스 멤버 변수로 선언 | 클래스 내부에서 사용하고 private이 아니면 참조 변수로 다른 클래스에서 사용 가능 |
힙 | 인스턴스가 생성될 때 힙에 생성되고, 가비지 컬렉터가 메모리를 수거할 때 소멸함 |
static 변수 (클래스 변수) |
static 예약어를 사용하여 클래스 내부에 선언 |
클래스 내부에서 사용하 private이 아니면 클래스 이름으로 다른 클래스에서 사용 가능 |
데이터 영역 | 프로그램이 처음 시작할 때 상수와 함께 데이터 영역에 생성되고 프로그램이 끝나고 메모리를 해제할 때 소멸함 |
변수는 특성에 맞게 선언해서 사용하는 것이 중요함
함수에서 기능 구현을 위해 잠시 사용 → 지역 변수
클래스의 속성을 나타내고 각 인스턴스마다 다른 값을 가짐 → 멤버 변수
여러 인스턴스에서 공유해서 사용하도록 한 번만 생성되어야 함 → static 변수
싱글톤 패턴
싱글톤 패턴 = 객체 지향 프로그램에서 인스턴스를 단 하나만 생성하는 디자인 패턴
프레임워크 = 프로그램을 쉽게 개발하기 위해 구체적인 기능 설계와 구현을 미리 만들어 놓은 도구
디자인 패턴 = 객체 지향 프로그램을 어떻게해야 좀 더 유연하고 재활용성이 높은 프로그램을 만들 수 있는지 정리한 내용
※ static을 응용하여 프로그램 전반에서 사용하는 인스턴스를 하나만 구현
어떤 회사의 직원들을 객체 지향 프로그램으로 구현한다고 가정했을 때 직원은 여러명이지만 회사는 하나임.
이런 경우 직원 인스턴스는 여러 개를 생성하는 것이 당연하겠지만, 회사 객체는 하나만 생성해야함.
싱글톤 패턴으로 회사 클래스 구현
① 생성자를 private로 만들기
싱글톤 패턴에서는 생성자를 반드시 명시적으로 만들고 그 접근 제어자를 private로 지정해야 함.
그러면 생성자가 있으므로 컴파일러가 디폴트 생성자를 만들지 않고,
접근 제어자가 private이므로 외부 클래스에서 마음대로 Company 인스턴스를 생성할 수 없게됨.
즉 Company 클래스 내부에서만 이 클래스의 생성을 제어할 수 있음.
② 클래스 내부에 static으로 유일한 인스턴스 생성하기
위에 private로 외부 인스턴스를 생성할 수 없도록 만들었는데, 프로그램에서 사용할 인스턴스 하나는 필요함.
따라서 Company 클래스 내부에서 하나의 인스턴스를 생성함.
이 인스턴스가 프로그램 전체에서 사용할 유일한 인스턴스가 되며,
private로 선언하여 외부에서 이 인스턴스에 접근하지 못하도록 제한해야 인스턴스 오류 방지 가능
③ 외부에서 참조할 수 있는 public 메서드 만들기
private로 선언한 유일한 인스턴스를 외부에서도 사용할 수 있도록 설정해야 함.
이를 위해 public 메서드를 생성하고, 유일하게 생성한 인스턴스를 반환해 줌.
이때 인스턴스를 반환하는 메서드는 반드시 static으로 선언해야 함.
그 이유는 getInstance( ) 메서드는 인스턴스 생성과 상관없이 호출할 수 있어야 하기 때문.
④ 실제로 사용하는 코드 만들기
외부 클래스에서는 Company를 생성할 수 없으므로 static으로 제공되는 getInstance( ) 메서드를 호출함.
Company.getInstance( ); 와 같이 호출하면 반환 값으로 유일한 인스턴스를 받아옴
myCompany1과 myCompany2를 비교해 보면 같은 참조 값을 가지는 동일한 인스턴스임을 알 수 있음.
Company 클래스는 내부에 생성된 유일한 인스턴스 외에는 더 이상 인스턴스를 생성할 수 없음.
이와 같이 static을 사용하여 유일한 객체를 생성하는 싱글톤 패턴 구현 가능
6장 연습문제 풀이
① 클래스 내부에서 자신의 주소를 가리키는 예약어 = this
② 클래스에 여러 생성자가 오버 로드되어 있을 경우 하나의 생성자에서 다른 생성자를 호출할 때 사용 = this
③ 클래스 내부에 선언하는 static 변수는 생성되는 인스턴스마다 만들어지는 것이 아닌 여러 인스턴스가 공유하는 변수임.
따라서 클래스에 기반한 유일한 변수라는 의미로 클래스 변수라고도 함.
④ 지역 변수는 함수나 메서드 내부에서만 사용할 수 있고 스택 메모리에 생성됨.
멤버 변수중 static 예약어를 사용하는 static 데이터 영역 메모리에 생성됨.
배열
배열 = 자료를 순차적으로 관리하는 구조
※ 학교에 학생이 100명 있을 때 100명의 학번을 관리하는 방법
학번의 자료형을 정수라고 하면 학생이 100명일 때 int studentID1, int studentID2...int studentID100까지
변수 100개를 선언해서 사용해야하는데 이것은 매우 번거롭기 때문에
이때 사용하는 자료형이 바로 배열(array)임.
배열을 사용하면 자료형이 같은 자료 여러 개를 한 번에 관리할 수 있음. 즉 배열은 자료가 연속으로 나열된 자료 구조임.
배열 선언과 초기화
배열을 사용하려면 먼저 배열을 선언해야 하며, 배열도 변수와 마찬가지로 자료형을 함께 선언함.
ex. 자료형[ ] 배열 이름 = new 자료형[개수];
자료형 배열 이름[ ] = new 자료형[개수];
배열을 이루는 각각의 자료를 배열 요소라고 하며 배열요소는 자료형이 모두 같음.
먼저 저장하려는 자료의 성격에 맞게 자료형을 정하고 선언하려는 배열 요소 개수만큼 [ ]안에 적음.
new 예약어는 배열을 새로 만들라는 의미임.
※ 학생들의 학번을 배열로 선언하기
int[ ] studentIDs = new int[10]; //int형 요소가 10개인 배열 선언
배열을 선언하면 선언한 자료형과 배열 길이에 따라 메모리가 할당됨.
위의 경우 자료형이 int형이므로 배열 요소를 저장할 수 있는 공간의 크기는 전부 4바이트로 동일함.
배열 요소를 저장할 수 있는 공간이 총 10개이므로 이 배열을 위해 총 40바이트의 메모리가 할당되는 것임.
※ 배열 초기화하기
자바에서 배열을 선언하면 그와 동시에 각 요소의 값이 초기화됨.
배열의 자료형에 따라 정수는 0, 실수는 0.0, 객체 배열은 null로 초기화 되며,
배열 선언과 동시에 특정값으로 초기화할 수도 있음.
배열이 초기화 요소의 개수만큼 생성되므로 [ ]안의 개수는 생략함.
ex. int[ ] studentIDs = new int[ ] {101, 102, 103}; //개수는 생략함
다음과 같이 값을 넣어 초기화할 때 [ ]안에 개수를 쓰면 오류가 발생함.
ex. int[ ] studentIDs = new int[3] {101, 102, 103}; //오류 발생
선언과 동시에 초기화할 때 다음과 같이 new int[ ] 부분을 생략할 수도 있음.
int형 요소가 3개인 배열을 생성한다는 의미이므로 new int[ ]를 생략해도 됨.
ex. int[ ] studentIDs = {101, 102, 103}; //int형 요소가 3개인 배열 생성
하지만 배열의 자료형을 먼저 선언하고 초기화하는 경우에는 new int[ ] 생략 불가능
ex. int[ ] studentIDs;
studentIDs = new int[ ] {101, 102, 103}; //new int[ ] 생략 불가능
배열 사용하기
선언한 배열의 각 요소에 값을 넣을 때나 배열 요소에 있는 값을 가져올 때는 [ ] 을 사용함.
만약 배열의 첫 번째 요소에 값 10을 저장한다면 다음처럼 코드를 작성함.
ex. studentIDs[0] = 10; //배열의 첫 번째 요소에 값 10을 저장
※ 인덱스 연산자 [ ]
인덱스 연산 = 배열 이름에 [ ] 를 사용하는 것([ ]는 배열을 처음 선언할 때 사용한 연산자)
인덱스 연산자의 기능 = 배열 요소가 저장된 메모리 위치를 찾아 주는 역할
변수 이름으로 변수가 저장된 메모리 위치를 찾는 것처럼, 배열에서 [i] 인덱스 연산을 하면 i번째 요소의 위치를 찾아
해당 위치의 메모리에 값을 넣거나 이미 저장되어 있는 값을 가져와서 사용할 수 있음.
ex. int형으로 선언한 num 배열의 네 번째 요소에 값 25를 저장하고, 그 값을 가져와 int형 변수 age에 저장
→ num[3] = 25; //num 배열의 네 번째 요소[3] 에 값 25 저장
age = num[3]; //age 변수에 num 배열의 네 번째 요소[3] 값 저장
※ 배열의 물리적 위치와 논리적 위치는 같음
물리적 위치 = 배열이 메모리에서 실제 저장되는 곳
논리적 위치 = 이론상 배열 위치
배열은 요소 10개를 선언하면 사용하는 실제 값도 바로 이웃한 메모리에 놓임.
즉 '5 다음에 10이 있다'는 논리적 순서와 실제 메모리를 살펴보면
값 5가 놓인 메모리 주소에서 4바이트(int형 크기) 다음 메모리 주소에 값 10이 놓임.
※ 배열 순서는 0번 부터
배열 길이(처음에 선언한 배열 전체 요소 개수)가 n이라고 하면, 배열 순서는 0번부터 n-1번 까지임.
0번 요소를 배열의 첫 번째 요소라고함.
5행에서 int형 배열 num을 선언하고 1부터 10까지의 값으로 초기화 함.
배열 요소를 하나씩 가져와 출력하기 위해 7행에서 for 반복문을 사용함.
자바의 배열은 배열 길이를 나타내는 length 속성을 가지며, 배열 길이는 처음에 선언한 배열의 전체 요소 개수를 의미함.
전체 길이를 알고 싶은 배열 이름 뒤에 도트(.) 연산자를 붙이고 length 속성을 쓰면 배열 길이를 반환함.
for문의 조건에서 얼만큼 반복할지 결정해야 하는데, 배열 요소 끝까지 반복하기 위해 배열 전체 길이(length)를 넣음.
따라서 num.length 값은 10이 되며, 이렇게 배열 전체 길이만큼 수행문을 반복해야 할 때는
숫자를 직접 사용하는 것보다 length 속성을 사용하는 것이 좋음.
※ 전체 배열 길이와 유효한 요소 값
전체 배열 길이와 현재 배열에 유효한 값이 저장되어 있는 배열 요소 개수가 같다고 혼동하면 안됨.
배열 길이는 5인데 세 번째 요소 값까지만 대입했기 때문에 나머지 요소 값은 0.0임.
※ 배열의 유효한 요소 값 출력하기
유효한 값이 저장된 배열 요소 개수를 저장할 size 변수를 선언했기 때문에 세 번째 요소 값까지만 출력함.
문자 저장 배열 만들기
※ 문자 자료형 배열을 만들고 알파벳 대문자를 A부터 Z까지 저장한 후
각 요소 값을 알파벳 문자와 정수 값(아스키 코드 값)으로 출력하기(문자 자료형 배열은 char[ ]로 선언해야 함.)
6행에서 대문자 알파벳 26개를 저장하기 위해 문자형 배열을 선언함.
9행에서 for문을 사용해 각 배열 요소에 알파벳 문자를 저장함.
각 알파벳 문자는 실제 메모리에 아스키 코드 값으로 저장되기 때문에
ch 값에 1을 더하면(ch++) 1만큼 증가한 값이 배열에 저장됨.
13행의 for문은 alphabets 배열에 저장된 알파벳 문자와 그 문자에 해당하는 아스키 코드 값을 반복하여 출력함.
14행의 (int)alphabets[i] 문장에서 형변환 연산자 (int)는 배열에 저장된 chat형 문자를 int형 정수로 변환함.
객체 배열 사용하기
동일한 기본 자료형 변수 여러 개를 배열로 사용할 수 있듯이 참조 자료형 변수도 여러 개를 배열로 사용할 수 있음.
참조 자료형 변수 = 클래스형으로 선언하는 변수
Book 클래스 = 책 이름과 저자를 멤버 변수로 가지는 클래스
디폴트 생성자 외에도 책 이름과 저자 이름을 매개변수로 받는 생성자를 하나 더 구현함.(9행)
get(), set() 메서드 구현 = 다른 코드에서 이 클래스를 사용할 때 멤버 변수 값을 가져오거나 지정할 수 있도록함.
showBookInfo()메서드 구현 = 책의 정보 출력
6행의 코드 내용만 보면 Book 인스턴스가 5개 생성된 것처럼 보이지만, 실제로 인스턴스가 바로 생성되는 것은 아님.
이때 생성되는 것은, 인스턴스를 생성하면 그 인스턴스를 가리키는 주소 값이 있는데
위 6행의 코드는 각각의 Book 인스턴스 주소 값을 담을 공간 5개를 생성하는 문장임.
즉 이문장을 실행하면 Book 주소 값을 담을 공간이 5개 만들어지고
자동으로 각 공간은 '비어 있다'는 의미의 null 값으로 초기화 됨.
출력 값을 보면 위는 Book 인스턴스 멤버들이고, 아래는 Book 인스턴스를 저장한 메모리 공간 주소임.
배열 복사하기
※ 배열을 복사하는 경우
① 기존 배열과 자료형 및 배열 크기가 똑같은 배열을 새로 만들 때
② 배열의 모든 요소에 자료가 꽉 차서 더 큰 배열을 만들어 기존 배열에 저장된 자료를 가져오려 할 때
※ 배열을 복사하는 방법
① 기존 배열과 배열 길이가 같거나 더 긴 배열을 만들고 for문을 사용하여 각 요소 값을 반복해서 복사하는 방법
② System.arraycopy( )메서드를 사용하는 방법
※ System.arraycopy( src, srcPos, dest, destPos, length) 메서드에서 각 매개변수의 의미
매개변수 | 설명 |
src | 복사할 배열 이름 |
srcPos | 복사할 배열의 첫 번째 위치 |
dest | 복사해서 붙여 넣을 대상 배열 이름 |
destPos | 복사해서 대상 배열에 붙여 넣기를 시작할 첫 번째 위치 |
length | src에서 dest로 자료를 복사할 요소 개수 |
8행의 System.arraycopy( )에서 순서대로 복사할 배열, 복사할 첫 위치, 대상 배열, 붙여 넣을 첫 위치, 복사할 요소 개수임.
출력 결과를 보면 array2 배열의 첫 번째 요소 값인 1을 제외하고 나머지 요소 값만 변경된 것을 알 수 있음.
이때 복사할 대상 배열의 전체 길이가 복사할 요소 개수보다 작다면 오류가 남.
위 예제에서 만약 요소 5개를 복사한다고 코드를 수정하면 array2 배열 길이보다 요소 개수가 많아지므로 오류가 발생함.
※ 객체 배열 복사하기
객체 배열을 사용하려면 꼭 인스턴스를 생성해서 넣어야 한다 했는데
bookArray2 배열의 인스턴스를 따로 만들지 않았는데 각 요소 값이 잘 출력되고 있음.
※ 얕은 복사
배열을 복사해 출력하기 전 bookArray1 배열 요소 값 하나를 변경할 경우
★ 해설과 달리 20,21행을 추가했을 때 출력값이 정확하게 나왔음
출력화면을 봤을 때 bookArray1 배열 요소 값을 변경했는데 bookArray2 배열 요소 값도 변경된 것을 알 수 있음.
변경된 이유는 객체 배열의 요소에 저장된 값은 인스턴스 자체가 아니고 인스턴스의 주소 값이기 때문임.
따라서 객체 배열을 복사할 때 인스턴스를 따로 생성하는 게 아니라 기존 인스턴스의 주소 값만 복사함.
결국 두 배열의 서로 다른 요소가 같은 인스턴스를 가리키고 있으므로
복사되는 배열의 인스턴스 값이 변경되면 두 배열 모두 영향을 받는 것임.
이와 같은 복사를 주소 값만 복사한다고 해서 '얕은 복사'라고 함
※ 깊은 복사
깊은 복사 = 인스턴스를 따로 관리하고 싶다면 직접 인스턴스를 만들고 그 값을 복사해야함.
복사할 배열에 인스턴스를 따로 생성한 후 요소 값을 복사하면 복사한 배열 요소는 기존 배열 요소와
서로 다른 인스턴스를 가리키므로 기존 배열의 요소 값이 변경되어도 영향을 받지 않는다는 것을 알 수 있음.
향상된 for문과 배열
향상된 for문은 배열 요소 값을 순서대로 하나씩 가져와서 변수에 대입함.
따로 초기화와 종료 조건이 없기 때문에 모든 배열의 시작 요소부터 끝 요소까지 실행함.
String형으로 선언된 strArray 배열에 문자열 5개를 저장했음.
향상된 String형 lang 뱐수에 strArray 배열 요소 값을 순서대로 가져와 대입함.
lang 변수를 출력하면 strArray 배열에 저장된 값이 순서대로 출력됨.
다차원 배열
일차원 배열 = 행 하나로 이루어진 배열
이차원 배열 = 프로그램에서 평면을 구현하기 위해 사용가능(ex. 바둑이나 체스 게임, 네비게이션 지도 구현)
삼차원 배열 = 주로 공간을 나타내는 프로그램에서 활용
다차원 배열 = 이차원 이상으로 구현한 배열로 평면이나 공간 개념을 구현하는 데 사용함.
이차원 배열
ex. 2행 3열의 이차원 배열을 선언하는 코드와 논리구조
int[ ][ ] arr = new = new int[2][3]; //int = 자료형, arr = 배열 이름, [2][3] = 행과 열 개수
배열의 모든 요소를 참조하려면 각 행을 기준으로 열 값을 순회하면 됨.
이차원 배열을 초기화하려면 다음처럼 행과 열 개수에 맞추어서 중괄호 { } 안에 콤마(,)로 구분해 값을 적음.
이렇게 이차원 배열을 초기화하면 괄호 안에 적은 6개 값이 순서대로 arr 배열의 각 요소에 저장됨.
int[ ][ ] arr = {{1, 2, 3}, {4, 5, 6}};
arr[0][0] | arr[0][1] | arr[0][2] |
1 | 2 | 3 |
4 | 5 | 6 |
arr[1][0] | arr[1][1] | arr[1][2] |
중첩 for문은 배열 인덱스용으로 i, j 두 변수를 사용하는데 i는 행을, j는 열을 가리킴.
전체 배열 길이인 arr.length는 행의 개수를 각 행의 길이 arr[i].length는 열의 개수를 가리킴.
출력 값에서 첫 번째(0, 0, 0)는 1행 1열~1행 3열, 두 번째(0, 0, 0)는 2행 1열~2행 3열, 세 번째(2, 3)는 행과 열 길이임.
위 코드를 보면 이차원 배열을 선언만 하고 초기화를 따로 하지 않았기 때문에, 모두 0으로 자동 초기화된 것을 알 수 있음.
'코딩 국비 학원 > Do it! 자바 프로그래밍 입문' 카테고리의 다른 글
[Do it! 자바 프로그래밍 입문]ArrayList 클래스, 배열 응용 프로그램, 상속 (0) | 2022.12.12 |
---|---|
[Do it! 자바 프로그래밍 입문]this 예약어, 객체 간 협력, static 변수 (0) | 2022.12.10 |
[Do it! 자바 프로그래밍 입문]생성자, 참조 자료형, 정보 은닉 (0) | 2022.12.05 |
[Do it! 자바 프로그래밍 입문]함수, 메서드, 클래스와 인스턴스 (0) | 2022.12.04 |
[Do it! 자바 프로그래밍 입문]반복문, 클래스, 객체 (0) | 2022.12.03 |