대게 프로그램에서 각 문장들은 위에서 아래로 순차적으로 실행된다. 이 때 제어문(Control Statement)을 사용하여 각 문장들의 실행 순서를 변경할 수 있다. 아래는 프로그램에서 나타나는 3가지의 제어 구조이다.



< 순차 구조 >


◆ ─> 『문장 A』 ─> 『문장 B』 ─> 




< 선택 구조 >


   ┌  true ─> 『문장 A』 ─┐

 ─> 【조건식】  ┤         

   └ false ─> 『문장 B』 ─┘




반복 구조 >


        ┌ false ───> 

 ┬> 【조건식】 ─ true ─> 『문장 A』 ┐

  └─<




제어문은 크게 조건문과 반복문으로 나뉘어 진다. 조건식의 산출값은 true 나 false 가 나오게 해야 한다.



▶ 조건문 : 조건에 따라 여러 개의 실행문(실행 경로) 중 하나(특정 경로)를 선택해야 하는 경우


1. if 문


# 기본 형태


if ( 조건식 )

문장1; // 조건식이 true 면 실행



# 조건에 따라 두 개 이상의 문장이 실행되어야 할 경우


if ( 조건식 ) {

문장1; // 조건식이 true 면 실행

문장2;
문장3;
}


# 중첩 if 문


if ( 조건식1 )
if ( 조건식2 ) // 조건식1이 true 면 실행 

문장1; // 조건식2가 true 면 실행



if ( 조건식1 ) {
if ( 조건식2 ) { // 조건식1이 true 면 실행

문장1; // 조건식2가 true 면 실행

문장2;

if ( 조건식3 ) { 

문장3; // 조건식3이 true 면 실행

}

}

}


2. if-else 문


# 기본 형태


if ( 조건식 )

문장1; // 조건식이 true 면 실행

else

문장2; // 조건식이 false 면 실행



# 조건에 따라 두 개 이상의 문장이 실행되어야 할 경우 - 복합문(Compound Statement)


if ( 조건식 ) {

문장 1; // 조건식이 true 면 실행

문장 2; // 이러한 문장 그룹핑을 복합문 또는 블록(block)이라고 한다.

}

else {

문장 3; // 조건식이 false 면 실행

문장 4;
}


# 간단한 조건문일 경우 조건 연산자로도 표현이 가능하다.


System.out.println( x > 50 ? "x = 50" : "x != 50");


# 중첩 if-else 문


if ( 조건식1 ) {

문장 1; // 조건식1이 true 면 실행


if ( 조건식2 ) {

문장 2; // 조건식2가 true 면 실행

문장 3;

}

else {

문장 4; // 조건식2가 false 면 실행

문장 5;
}

}

else {

문장 3; // 조건식1이 false 면 실행

문장 7;
}


3. switch 문


# switch 문은 여러 개의 실행문 중 해당 조건에 맞는 실행문을 선택한다.


# 기본 형태


number = 5;


switch( number ) { // switch 안의 괄호(수식)는 문자나 문자열도 가능하다.

case 1 :

문장1;

break; // break; 문장은 빠져나가는 것으로 없을 경우 아래의 case 도 실행된다.

case 2 :

문장2;

continue; // continue; 문장은 다시 switch(수식) 문으로 돌아가는 것으로 break; 문장과는 다르다.

case 3 :

문장3;

break;

default : // switch 안에 수식의 결과가 어떤 case 문에도 해당되지 않는 경우에 실행된다. 

문장4; // 예상치 못한 결과에 대비하여 default문은 작성하는 것이 좋다. (작성 여부는 자유)

break;




▶ 반복문 : 어떤 조건이 유지되거나 정해진 횟수만큼 문장을 반복(Looping)해야 하는 경우


1. while 문


# 조건식이 참이면 블록을 반복적으로 실행한다.


while ( 조건식 ) {

반복문장;

}


# 무한 루프


while (true) {

문장1;

문장2;

문장3;

}



2. do-while 문


# 문장을 우선 실행한 뒤에 조건식을 검사한다.


# 기본 형태 - 끝에 세미콜론(;) 필수.


do {

반복문장1; // 처음에는 무조건 실행!

} while ( 조건식 );



3. for 문


# 정해진 횟수만큼 반복할 때 사용된다.


# 기본 형태

 - 초기식 → 조건식 ─ true → 문장1; → 문장2; → 증감식 → 조건식 ─ true → 문장1; → 문장2; → 증감식 

→ 조건식 ─ false → 반복문 종료


for ( 초기식(1) ; 조건식(2) ; 증감식(5) ) { // 괄호 안은 처음 루프돌 때 순서입니다.

문장1;  (3)

문장2;  (4)

}


# 예제


for ( int i = 0; i < 5; i++ ) {

System.out.println(" i 의 값은 : "+ i);

}


# 무한 루프


for ( ; ; ) {

문장1;

문장2;

}


프로그램에서 데이터를 처리하여 결과를 산출하는 것을 연산(Operations)이라고 한다.


- 연산자(Operator) : 어떤 연산을 나타내는 표시나 기호 

- 피연산자(Operand) : 해당 연산식에서 연산되는 데이터

- 연산식(Expressions) : 연산자와 피연산자를 이용하여 연산의 과정을 표현한 것



* 연산자의 종류


 유형

연산자

피연산자 수 

기능

예시 

과값

산술 연산자 

+, -, *, /, % 

2개 (이항)

사칙연산과 나머지 계산 

z = x + y

 숫자

부호 연산자

+, - 

1개 (단항) 

양수 및 음수의 표현

x = -x

 숫자

문자열 연산자

2개 (이항) 

두 문자열을 연결 

 "안녕" + "하세요"

문자열

대입 연산자

=, +=, -=, *=, /=

%=, &=, ^=, |=, <<=, >>= 

2개 (이항) 

좌변의 변수에 우변의 값을 대입

x += 1  (x = x + 1)

x &= y  (x = x & y)

 

증감 연산자

++, -- 

1개 (단항)

1만큼 증가(++) 또는 감소(--) 

++x (선증가 후연산)

x++ (선연산 후증가)

숫자 

비교 연산자 

==, !=, <, >, >=, <=, instanceof 

2개 (이항) 

값의 비교

x >= y  (x가 y보다 크거나 같은가)

boolean - true/false

논리 연산자 

!, &, |, &&, || 

단항 또는 이항

NOT(!), AND(&, &&), OR(|, ||) 연산

밑에 추가 설명

boolean - true/false

조건 연산자

(조건식) ? T : F 

3개 (삼항) 

조건식이 참이면 T, 거짓이면 F

(x > y) ? x : y

 

비트 연산자 

~, &, |, ^ 

단항 또는 이항

비트 XOR, AND, OR, NOT 연산 

밑에 추가 설명

숫자, boolean 

쉬프트 연산자 

<<, >>, >>> 

2개 (이항) 

비트 이동 연산자로 비트를 좌측, 우측으로 밀어서 이동 

밑에 추가 설명

숫자 




▶ 산술 연산자


1. 자바는 리터럴 간의 연산은 타입 변환 없이 계산한다.

ex) char c = 'A' + 1; // c에는 유니코드 66인 'B' 가 저장된다.

(But.) char c2 = c + 1; // c는 int타입으로 변환되고 1과 연산되므로 산출 타입은 int이다. 따라서 컴파일 에러.

위의 경우, c를 int타입으로 캐스팅(Casting)해서 char 타입으로 변환해야 한다.


2. 오버플로우 탐지 : 산출 타입(Type)으로 표현할 수 없는 값이 산출되면, 오버플로우가 발생하고 쓰레기값을 얻는다.


3. / 또는 % 연산자 사용 시 우측 피연산자는 0을 사용할 수 없다. 0으로 나누게 되면 예외(ArithmeticException)가 발생한다. 또한 실수 타입인 0.0 또는 0.0f로 나누면 예외가 발생하지 않고, / 연산의 결과는 Infinity(무한대) 값을 가지며, % 연산의 결과는 NaN(Not a Number)을 가진다. 이 때는 데이터가 엉망이 되므로 그 다음의 연산을 수행해선 안된다. 따라서 부동소수점(실수)을 입력받을 때 입력값의 NaN 여부를 조사하고 연산을 수행해야 한다.

(But.) NaN인지 조사할 때 == 연산자를 사용해선 안되며, Double.isNaN() 을 사용해야 한다. 그 이유는 NaN는 != 연산자를 제외한 모든 비교 연산자에서 false를 리턴하기 때문이다.



▶ 비교 연산자


1. 비교 연산자에서는 연산을 수행하기 전에 타입 변환을 통해 피연산자의 타입을 일치시킨다.


2. 예외 : 0.2 == 0.2f 의 경우 이진 포맷의 가수를 사용하는 모든 부동소수점 타입은 0.1을 정확히 표현할 수 없기 때문에 0.1f는 0.1의 근사값으로 나타나므로 피연산자를 모두 float 타입으로 강제 타입 변환한 후 비교 연산을 수행해야 한다.


3. String 객체의 문자열 비교할 때는 ==가 아닌 equlas() 메소드를 사용해야 한다. == 사용 시 객체의 번지값을 비교하므로 번지가 다른 객체일 경우 같은 문자열 상수라도 false가 나온다.



▶ 논리 연산자


1. AND (논리곱) - && 또는 & : 피연산자 모두 true일 경우 연산 결과로 true를 반환한다.


2. OR (논리합) - || 또는 | : 피연산자 중 적어도 하나가 true일 경우 연산 결과로 true를 반환한다.


3. XOR (배타적 논리합) - ^ : 피연산자가 하나는 true이고 다른 게 false일 경우에만 연산 결과는 true를 반환한다. (서로 다를 경우)


4. NOT (논리부정) - ! : 피연산자의 논리값을 바꾼다. true 이면 false ,  false이면 true로 변환한다.



▶ 비트 연산자


1. 비트 연산자는 데이터를 비트(bit) 단위로 연산한다.


2. 비트 반전 연산자, 논리 부정(~)

# 피연산자를 이진수로 표현했을 때, 0을 1로, 1은 0으로 반전한다.

# 정수 타입(byte, short, int, long)의 연산자에게만 사용된다.

# 연산 후, 부호 비트인 최상위 비트를 포함해서 모든 비트가 반전된다.

# 비트 반전 연산자의 산출 타입은 int 타입이다.

# 비트 반전 연산자의 산출값에 1을 더하면 부호가 반대인 정수를 얻는다.


3. 논리곱(AND) - & : 두 비트 모두 1일 경우에만 연산 결과가 1이 된다.


4. 논리합(OR) - | : 두 비트 중 적어도 하나가 1이면 연산 결과는 1이 된다.


5. 배타적논리합(XOR) - ^ : 두 비트 중 하나는 1이고 다른 하나가 0일 경우 연산 결과는 1이 된다.



▶ 쉬프트 연산자(비트 이동 연산자)


1. a << b : 정수 a를 이진법으로 표현했을 때, 각 비트를 b만큼 왼쪽으로 이동시키고 빈자리는 0으로 채워진다.


2. a >> b : 정수 a를 이진법으로 표현했을 때, 각 비트를 b만큼 오른쪽로 이동시키고 빈자리는 정수 a의 최상위 부호 비트와 같은 값으로 채워진다.


3. a >>> b : 정수 a의 각 비트를 b만큼 오른쪽으로 이동시키고 빈자리는 0으로 채워진다.



* 연산 방향 및 우선 순위


 연산자

연산 방향

우선 순위 

증감(++, --), 부호(+, -), 비트(~), 논리(!) 

← 

 높음












낮음

산술(*, /, %)

산술(+, -)

쉬프트(<<, >>, >>>) 

비교(<, >, <=, >=, instanceof) 

비교(==, !=) 

논리(&)

논리(^)

논리(|) 

논리(&&) 

논리(||) 

조건( ? : ) 

대입(=, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=, >>>=)






타입 변환 : 데이터 타입을 다른 데이터 타입으로 바꾸는 것.



- 자동 타입 변환(Promotion)


 : 프로그램 실행 중에 자동으로 타입 변환이 일어나는 것을 의미한다.



큰 크기의 타입 ← 작은 크기의 타입


* 여기서 크기는 사용하는 메모리 크기(바이트 수)를 말한다.


즉, byte ▶ short  int  long  float  double 순서 또는 char  int 순서로 자동 타입 변환이 일어난다.


참고로 long(8 byte)이 float(4 byte)보다 크지만 표현할 수 있는 숫자의 범위가 float이 더 크기때문에 float이 더 큰 타입으로 표시된 것이다. 또한, 정수 타입이 실수 타입으로 변환될 경우 자연수 뒤에 소수점과 소수점이하가 표시된다.


int i = 200;

double d = i; // 이때 d는 200.0이 된다. 



자동 타입 변환에서는 딱 한 가지 예외가 있는데, char는 2 바이트의 크기를 가지지만 char의 범위는 0~65535이므로 음수가 저장될 수없다. 즉, 음수가 저장될 수 있는 byte 타입은 char 타입으로 자동 변환시킬 수 없다.




Q) 데이터 손실 유무는 ?


A) 변환 이전의 값은 변환 이후에도 그대로 보존된다. 쉽게 말하자면 작은 양동이의 물을 큰 양동이로 옮긴다고 해도 물의 양은 변하지 않는 것과 같은 것이다.




- 강제 타입 변환(Casting)


 : 큰 데이터 타입을 작은 데이터 타입으로 쪼개어 강제로 저장하는 것을 의미한다.



큰 크기의 타입 → 작은 크기의 타입



즉, byte ◀ short  int  long ◀ float ◀ double 순서 또는 char ◀ int 순서로 강제 타입 변환이 일어난다.



# 사용 방법


작은 크기 타입 = (작은 크기 타입) 큰 크기 타입;


int i = 214748364;

byte b = (byte) i;


위의 경우 끝의 1 byte만 byte 타입 변수에 담게 된다. 즉, 변환되어질 데이터(작은 크기의 타입)보다 큰 것을 강제 타입 변환을 할 때 기존의 값은 보존되지 않는다.



# 주의 사항


int -> float으로 강제 타입 변환을 할 때,


float : 부호(1비트) + 지수(8비트) + 가수(32비트)


로 구성되어 있기 때문에 int 값이 123456780 처럼 가수 23비트로 표현불가능한 숫자일 경우 근사값으로 변환된다. 이 점은 정밀도 손실을 발생시키기 때문에 모든 int값을 안전하게 실수 타입으로 변환시키려면 double 타입을 사용하는 것이 현명하다. 


double : 부호(1비트) + 지수(11비트) + 가수(52비트)


그 이유는 int값은 4바이트 = 32비트 이므로 double의 가수 52비트보다 항상 작기 때문에 정밀도 손실없이 변환이 가능하다.



헷갈린다면 다른 타입으로 강제 타입 변환 시 변환될 타입(작은 크기의 타입)의 최대값과 최소값을 넘어가는지 확인하면 된다.

아래는 기본타입의 최대값과 최소값을 나타낸 표이다.



 기본 타입

최대값 상수 

최소값 상수 

byte 

Byte.MAX_VALUE 

Byte.MIN_VALUE 

short

Short.MAX_VALUE 

Short.MIN_VALUE 

int 

Integer.MAX_VALUE 

Integer.MIN_VALUE 

long 

Long.MAX_VALUE 

Long.MIN_VALUE  

float 

Float.MAX_VALUE 

Float.MIN_VALUE 

double 

Double.MAX_VALUE

 Double.MIN_VALUE 





더블 버퍼링(Double Buffering)은 이중 버퍼링이라 불리기도 하며, 그래픽 객체에 이미지를 그릴 때 사용되는 기법이다.



Q) 왜 사용하는가 ? 


A) API를 시작하다보면 비트맵 이미지를 사용하게 된다. 그 때 이미지들이 전환되면서 영상처럼 부드럽게 움직일 거라 생각하지만 실제로 이미지들이 움직일 때마다 화면이 깜빡이는 현상이 눈에 들어온다. 쉽게 말하자면 아래와 같은 상황인 것이다.


▶ 게임 캐릭터이미지를 구현할 때 이미지를 움직이게 하고 싶다.

그러나 캐릭터가 띄엄띄엄 움직임과 동시에 깜빡거리는 화면

때문에 게임할 맛이 안난다.



그 이유는 컴퓨터가 이미지를 지웠다가 새 이미지를 다시 그리고 하는 방식을 반복하기 때문이다.


즉, 이미지를 그리는 데 시간이 소요되므로 이미지의 출력이 잦을수록 깜빡거리는 현상이 심해진다.


이에 대한 해결방안으로 버퍼 역할을 해줄 메모리 장치 컨텍스트(보이지 않는 화면)를 하나 더 사용하여 그곳에 이미지를 그리고, 기존화면을 유지하다가 이미지가 완성되면 실제 화면 장치 컨텍스트로 한꺼번에 베껴 그리는 것이다. 


아래는 이를 그림으로 표현한 것이다.






Q) 어떻게 사용하는가 ?


A) 자바를 예로 들자면,


//FIELDS

Image buffImage;

Graphics buffg;


//CONSTRUCTOR

(클래스명) {

repaint(); // 호출 시 repaint() -> update(g) -> paint(g) 순서로 메소드가 호출된다.

}


//METHODS

public void paint(Graphics g) {

if(buffg == null) {

buffImage = createImage(이미지 넓이, 이미지 높이); //버퍼링용 이미지 생성


if(buffImage == null) System.out.println("더블 버퍼링용 오프 스크린 생성 실패");

else buffg = buffImage.getGraphics(); 

//이미지를 생성해도 그래픽 객체를 얻어야 이미지 위에 그리고자하는 것을 그릴 수 있다.

}

update(g); // 이렇게 하면 반복적인 메소드 실행이 가능하다.

}


public void update(Graphics g) {


buffg.drawImage(그릴 이미지, 0, 0, this);


g.drawImage(buffImage, 0, 0, this); //실제 화면(g)으로 오프스크(buffg)에 그려진 이미지(buffImage)를 옮김.

}

+ Recent posts