본문 바로가기

Java/이론 정리

[JAVA SE – 반복&제어문 / 메소드와 변수의 범위 / 배열(Array) / Package / Method Overriding 살펴보기]

자바 2Week(2010.3.8 ~ 3.12)

 

지난 주 복습 1 –강제 형 변환

class TestDataType{

 

byte b=10;

byte c=20;

byte bc=b+c;

 

public static void main(String[] args){

}

} 

class TestDataType{

 

byte b=10;

byte c=20;

byte bc=(byte)(b+c);

 

public static void main(String[] args){

}

} 

<Before>

<After>

상단 좌측 프로그램을 실행하게 되면 아래와 같은 오류가 난다.

 

---------- javac ----------

TestDataType.java:6: possible loss of precision

found : int

required: byte

byte bc=b+c;

^

1 error 

 

이는 기본적으로 JVM이 계산식에서 자료형을 명시하지 않으면 자동적으로 int형 계산을 수행하기 때문인데, 이를 해결하는 하나의 방법으로 상단 우측과 같이 강제 형변환을 수행하면 된다.

 

 

비교연산자 & vs &&

Boolean b= false & (조건2)

(두 번째 조건의 참/거짓에 상관없이 결과는 False 이지만 JVM은 뒤의 조건2도 검사한다.)

 

반면! 아래와 같이 '&&' 비교연산자를 사용할 경우,

Boolean b= false && (조건2)

(두 번째 조건의 참/거짓에 상관없이 결과는 False 이므로 JVM은 뒤의 조건을 검사하지 않고, b에 false를 할당한다. 그러므로 성능을 위해 &&을 주로 사용하는 것이 바람직하다.)

 

class OperationTest{

public static void main(String[] args){

int x=10;

boolean b= x>5;

System.out.println(b);

 

int i=0;

 

//boolean b1=(x>5) & ((10/i)<3);

//boolean b1=(x>5) && ((10/i)<3);

//boolean b1=(x>5) | ((10/i)<3);

/** 위 3가지 경우 오류가 발생하지만 아래의 or 연산을 실행하면

결과값이 도출되는 것을 확인할 수 있다. */

boolean b1=(x>5) || ((10/i)<3);

System.out.println(b1);

}

} 

 

실행흐름의 컨트롤(제어문/반복문)

>> 제어문

  • 분기문(조건문) – If (else) & switch case

    Switch case의 경우 동등비교(== , !=)에 유용적이다.

  • 반복문 – for & while & do~ while

 

**자바에서는 if (조건) 에서 '조건의 결과값'에는..

  • 'True /False' 만이 올 수 있다.
  • (범위/동등)비교

    Ex) if (x>=5) && (x<10)

 

예제로 풀어보는 제어문 <학점 계산 Logic>

class GradeCount{

public static void main(String[] args){

int grade=84;

String a;

if(grade>=90){

a="A";

}

else if(grade<90 &&grade>=80){

a="B";

}

else if(grade<80&&grade>=70){

a="C";

}

else if(grade<70&&grade>=60){

a="D";

}

else{

a="F";

}

System.out.println("당신의 점수는"+a);

}

} 

 

예제로 풀어보는 제어문 (/*** 1~4의 랜덤한 값을 생성하여 해당 경우의 수마다 출력 결과를 다르게 출력한다.*/) <if 문을 통한 해결>

class RandomCount{

public static void main(String[] args){

//Math.random()의 범위는 0.0 ~ 0.99999....

 

double d=Math.random();

System.out.println(d);

 

int i=(int)(d*4+1); // 0.0 ~3.99999 , 1.0 ~4.99999

System.out.println(i);

 

if (i==1){

System.out.println("Spade");

}

else if (i==2){

System.out.println("Cloval");

}

else if (i==3){

System.out.println("heart");

}

else

System.out.println("Diamond");

}} 

Switch 문을 통한 해결

class RandomCount{

public static void main(String[] args){

double d=Math.random();

int i=(int)(d*4+1);

System.out.println(i);

 

switch (i)

{

case 1:

System.out.println("Spade");

break;

case 2:

System.out.println("Clover");

break;

case 3:

System.out.println("Heart");

break;

default:

System.out.println("Diamond");

}

}

}

/*** Switch 문 이용시의 주의사항

기본적으로 Switch 문은 Case(n)이 실행되면 그 아래 Case(n+1)문들도 다 같이 실행되므로

해당 Case만을 실행시키기 위해서는 break문을 통해 빠져나와야 한다.

Switch 문에 올 수 있는 데이터 타입은 int, short ,char, byte, & enum(=즉, int 보다 작거나 같은 타입)만 올 수 있다.*/

 

 

제어문 예제 3 (월에 따른 마지막 날짜 구하기.)

class MonthCheck{

public static void main(String[] args){

double randomMonth=Math.random();

int month =(int)(randomMonth*10);

 

//SWITCH 문을 사용한 방법

 

switch (month){

case 1:

case 3:

case 5:

case 7:

case 8:

case 10:

case 12:

System.out.println(month+"월은 31일까지 입니다");

break;

case 4:

case 6:

case 9:

case 11:

System.out.println(month+"월은 30일까지 입니다");

break;

case 2:

System.out.println(month+"월은 28일까지 입니다");

break;

default :

System.out.println(month + "월은 존재하지 않습니다");

}

 

 

// if...if else를 이용한 방법

 

if (month==1 ||month==3 || month==5 || month==7 || month==8 || month==10 ||month==12){

System.out.println(month+"월은 31일까지 입니다");

}

else if (month==4 ||month==6 || month==9 || month==11){

System.out.println(month+"월은 30일까지 입니다");

}

else if (month==2){

System.out.println(month+"월은 28일까지 입니다");

}

else

System.out.println(month+"월은 존재하지 않습니다.");

}

} 

 

두 변수와 하나의 연산자를 입력 받고, 입력받은 연산자를 통한 if와 switch문을 사용한 해결.

class SwitchCalc{

public static void main(String[] args){

System.out.println("SWITCH 문을 통한 방법입니다");

calc(10,5,'+');

calc(10,5,'-');

calc(10,5,'*');

calc(10,5,'/');

 

System.out.println("IF 문을 통한 방법입니다");

calc1(20,5,'+');

calc1(20,5,'-');

calc1(20,5,'*');

calc1(20,5,'/');

}

 

//i,j는 피연산자, op는 사칙 연산자(+,-,/,*)

//argument로 받은 3개의 데이터를 이용해 계산한 결과를 출력.

public static void calc(int i, int j, char op){

switch(op){

case '+' :

System.out.println(i + "+"+ j+ "=" +(i+j));

// ("i+j="+(i+j))와 같다.

break;

 

case '-' :

System.out.println(i + "-"+ j+ "=" +(i-j));

break;

 

case '*' :

System.out.println(i + "*"+ j+ "=" +(i*j));

break;

 

case '/' :

System.out.println(i + "/"+ j+ "=" +(i/j));

break;

 

default :

System.out.println("잘못된 연산자입니다.");

}

} 

public static void calc1(int i1, int j1, char op1){

if (op1=='+'){

System.out.println(i1 + "+"+ j1+ "=" +(i1+j1));

}

 

else if (op1=='-'){

System.out.println(i1 + "-"+ j1+ "=" +(i1-j1));

}

 

else if (op1=='*'){

System.out.println(i1 + "*"+ j1+ "=" +(i1*j1));

}

 

else if (op1=='/'){

System.out.println(i1 + "/"+ j1+ "=" +(i1/j1));

}

 

else

System.out.println("잘못된 연산자 입니다.");

}

} 

 

for문은 반복횟수를 알 때, while은 반복횟수를 모를 때 while문을 사용하는 것이 좋다.

위 두 문이 조건이 만족할 동안 반복한다면,

Do~ while문은 우선 먼가 실행한 후, 조건을 비교한다. 그리고 무조건 한번은 실행(do)된다.

 

While과 For문을 사용하여 1~10까지 더하는 프로그램 생성하기

class WhileSum{

public static void main(String[] args){

 

int num=0;

int sum=0;

 

while(num<10){

num=num+1;

sum=sum+num;

}// num이 10보다 작을 때, num값을 1씩 증가시키면서 그 값들을 sum에 저장.

System.out.println(num);

System.out.println(sum);

 

int count=0;

int sumCount=0;

 

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

count=count+1;

sumCount=sumCount+count;

}

System.out.println(count);

System.out.println(sumCount);

}

} 

 

하나의 단을 선택하여 구구단 출력하기 ( for & while 두 가지 방법 모두 사용)

Gugudan.java

class Gugudan{

public static void main(String[] args){

int s=2; // 몇 단을 출력할지 설정

 

for (int i=1;i<10 ;i++ ){

int a=i;

System.out.println(s + "" +"*" + i + "=" + s*i); }

System.out.println("for문을 통해 " + s +"단이 출력되었습니다");

 

int ws=s;

int si=1;

 

while (si<10){

System.out.println(ws + "" +"*" + si + "=" + ws*si);

si++;

}

System.out.println("While문을 통해 " + s +"단이 출력되었습니다");

 

//이중 For문을 사용한 전체 구구단 출력

 

int DANSU;

int JISU;

 

for (DANSU=2;DANSU<10 ;DANSU++ ){

for (JISU=1;JISU<10 ;JISU++ ){

System.out.println(DANSU+"" +"*"+ JISU +"="+ DANSU*JISU);

}System.out.println(DANSU + "단이 출력되었습니다");

}

System.out.println("이중 FOR문을 통해 " + s +"단이 출력되었습니다");

 

 

// For - While 이중 반복문

 

int DAN;

 

for (DAN=2;DAN<10 ;DAN++ ){

int JI=1;

while (JI<10){

System.out.println(DAN + "" + "*" +JI + "=" + DAN*JI+ " ");

JI++;

}

}

System.out.println("이중 FOR - While문을 통해 " + s +"단이 출력되었습니다");

}

} 

 

 

Continue와 break

Continue는 조건문에서 '조건을 다시 검색하라'는 의미로, 코드에서 continue문을 만나게 되면 다시 위로 올라가 조건문을 실행하게 된다. 반면 break문의 경우 break문을 만나게 되면 continue와 달리 위 조건문을 빠져나가게 된다. 이들 둘의 차이 및 사용처를 알고 사용함이 중요하다.

While (i<10){

조건절

Continue;

Break;

}

 

문이 있다고 가정한다면 이 절에선 절대로 break문이 실행될 수 없는 구조가 되는 것이다.

 

Continue와 Break문의 사용 예제) 1~00까지 3의 배수만 출력한다.(MultiThree.java)

class MultiThree{

public static void main(String[] args){

int VAR;

//Break문을 통한 처리

for (VAR=1;VAR<101 ;VAR++ ){

while(VAR%3==0){

System.out.print(VAR + " // ");

break;

}

}

VAR=1;

//continue문을 통한 처리

for (VAR=1;VAR<101 ;VAR++ ){

if(VAR%3!=0){

continue;//입력받은 VAR값이 3으로 나누어 떨어지지 않으면 다시 for 조건절로 이동하여 조건을 다시 수행.

}

System.out.print(VAR + " &");

}

}

} 

이중 반복문의 구조

  • 외부 반복문은 천천히 반복되는 내용으로 구성되고, 빠르게 변화되며 반복되어지는 내용들은

    내부 반복문으로 쓰이면 된다.

 

이중 반복문을 사용하여 Asterisk 문자를 10행5열로 출력하기

class Asterisk{

public static void main(String[] args){

for (int j=1; j<11; j++){

for (int i=1;i<6; i++){

System.out.print("*");

}System.out.println("");

}

}

}

 

메소드와 변수의 스코프(Scope)

 

변수 : 데이터가 저장될 저장소로 크게 3가지로 나뉠 수 있다.

[ Instance Member Var(속성) & Local Variable & Class Member Variable ]

 

선언 위치

저장 메모리 위치

Default 초기화 여부

SCOPE(사용범위)

Instance Member Variable

Class Block 내

Heap

O

(묵시적으로 기본DataType에 따라 값이 들어감)

객체와 Scope가 동일

(객체가 생성되어 소멸될 때 까지)

Local Variable

(=automatic

,temporary

,Stack Variable)

Method Block 내

Stack

X

(명시적으로 기본DataType에 따라 값을 넣어줘야 함)

Method가 실행되는 동안.(로컬 변수가 선언된 곳부터 Method가 종료될 때 까지)

 

Supplement)

Local Variable은 명시적 초기화가 이루어지지 않으면 Stack에 Data가 할당되지 않는다.

 

Stack ?!

  • 현재 실행 중인 Method의 Local Variable들이 저장되어지는 메모리 영역.

 

Array

>> Array

  • 같은 Type(종류)의 Data(값)들을 모아서 하나의 변수로 접근

  • 자바에서는 배열을 객체로 처리한다.
  • 필요성

    관리해야 할 데이터가 매우 많을 때, 이를 하나씩 변수를 선언하고 할당하는 것은 비효율적이다.

    하지만 배열을 사용하게 되면 단! 한번에 선언 및 할당이 가능하다.

 

선언 방법

  1. 변수선언

    Ex) DataType[] 변수명; or DataType 변수명[];

    Int [ ] i; or int i[ ];

  1. 배열객체 생성

    변수명 = new DataType[LENGTH(=관리할 수 있는 Data의 개수)]

    i = new int[5];

     

    (1)~(2)단계 통합 : int [ ] i=new int[5];

  1. 값 할당

    변수명[index] =value

    Ex) i[3]=10;

     

    (1)~(3)단계 통합하기(=배열에 들어갈 값들을 알 고 있을 때):

int [ ] i={10,30,24,93,89};

int [ ] i=new int[]{10,30,24,93,89};

 

위 조건을 보듯이 배열은 클래스 없이 객체를 생성할 수 있다.

배열의 Attribute인 Length는

Int [] i=new int[10] 이란 코드가 있다면

System.out.println(i.Length); 을 하게 되면 10이 찍히는 것이다.

 

위의 내용들을 복습하기 위한 간단한 배열 실습

class TempTest{

public static void main(String[] args) {

//크기가 3이고 접근하기 위한 변수명이 ARRAY_TEST인 배열을 생성한다.

int [] ARRAY_TEST=new int[3];

System.out.println("배열의 길이는 = " +ARRAY_TEST.length);

//생성된 ARRAY_TEST 배열의 크기를 알아본다.

 

ARRAY_TEST[0]=10; //배열 공간의 첫 번째 방에 '10'을 할당하고 출력한다.

System.out.println("배열의 첫번째 방에는 = " +ARRAY_TEST[0]);

 

ArrayT(); //ArrayT 메소드를 호출

}

public static void ArrayT(){

//STRarray라는 변수를 선언하고, 객체배열을 생성하고, 그 값들을 할당한다.

String[] STRarray=new String[]{"A","B","C","D","E"};

System.out.println(STRarray[3]);

}

} 

 

위에서 i의 데이터타입은 int형 [](=배열)이고, i[0]의 데이터 타입은 int이다!

 

배열 심화학습 (배열 모두 보이기, 총점, 평균구하기 ,반복문 사용)

class ArrayTest1{

public static void main(String[] args){

//int[] 선언, 생성

//1. 선언

int [] intArray;

//2. 배열 객체 생성

intArray=new int[5];

 

System.out.println(intArray.length);

System.out.println(intArray[0]);

// intArray[0]이 Heap에 저장되는 Primitive Data Type이므로 즉, Heap에 저장되므로 Default 초기화 값인 0이 출력된다.

 

//3. 값 할당.

intArray[0]=10;

intArray[1]=20;

intArray[2]=30;

intArray[3]=40;

intArray[4]=50;

System.out.println(intArray[0]);

 

//위 1~3과정 통합하여 한 줄에 해결하기!

 

int intArray2[]={10,20,30,40,50,60,70};

System.out.println(intArray2[0]);

 

int intArray3[]=new int[]{100,200,300,400};

System.out.println(intArray3[0]);

 

System.out.println("intAray3의 모든 값들을 출력하기");

 

//for문을 이용한 intArray3 배열값들 출력하기

 

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

System.out.println(intArray3[i]);

}

System.out.println("intAray3의 모든 값들을 출력 및 평균 계산하기ver.2");

int sum=0;

for (int idx=0; intArray3.length>idx ; idx++ ){

System.out.println(intArray3[idx]);

sum=sum+intArray3[idx];

}

 

System.out.println("Total : AVG =" + sum+ ":"+ sum/intArray3.length);

//만약 값에 소수점이 나온다면

System.out.println("Total : AVG =" + sum+ ":"+ (double)sum/intArray3.length);

 

System.out.println("intAray2의 모든 값들을 출력하기");

 

//While 문을 통한 intArray2 배열값들 출력하기

 

int i=0;

while (intArray2.length>i){

System.out.println(intArray2[i]);

i++;

}

}

} 

 

Caller Method = Go (new int[]{10,20,30,40}

Worker Method = Go(int[]i)

 

위 Worker에서 Argument로 int형 배열 i를 입력 받아야 하고, Caller에서는 go Method를 사용하기 위해 배열을 선언하는데 이 때, 미리 배열을 생성하지 않고, Caller에 접근할 때 바로 배열을 생성할 때는 위의 Caller Argument처럼 바로 생성하여 입력 가능하다.

 

 

For~ Each 문

배열의 첫번째 index부터 마지막 index까지 모두 다 하나씩 뽑아낼 때, 사용되며, JDK v1.5 부터 추가됨.( Array와 Collection에서 사용된다.)

값의 입력은 안되고, 조회만 가능하다. 또한 특정 IDX만 가져올 수 없고 전체(ALL)만을 가져올 수 있다.

 

for

(

변수선언:

객체

)

{ }

   

콜렉션

)

{ }

 

 

Subject : 랜덤함수를 사용하여 int형 배열에 0~9까지의 숫자를 입력받고, 해당 숫자들이 도출되는 빈도수를 출력하라.

class ArrayCount{

public static void main(String[] args){

//데이터 타입이 int 형 배열인 10개짜리 공간을 가지는 arrayRandomCount 배열을 선언

int [] arrayRandomCount=new int[10];

 

//arrayRandomCount 배열에 랜덤으로 값들을 입력받는다.

for (int s=0;s<arrayRandomCount.length ; s++){

//크기가 정수 double형인 randomNum 변수에 값들을 랜덤으로 입력받고

double randomNum=Math.random();

//이 값에 10을 곱하고 이를 int형으로 변환 후 int형 변수 y에 할당한다.

int y=(int)(randomNum*10);

//y에 저장된 값들을 arrayRandomCount배열에 다시 할당한다.

arrayRandomCount[s]=y;

}

 

System.out.println("입력된 배열의 수는 총 " + " " + arrayRandomCount.length + "개 이며, 숫자는");

 

for (int e : arrayRandomCount ){

System.out.print(e +"\t");

}System.out.println("\n----------------------------------");

 

//각 숫자의 총 도출된 개수를 저장할 변수들을 초기화한다.

int s1=0; int s2=0; int s3=0;int s4=0;int s5=0;int s6=0;int s7=0;int s8=0; int s9=0; int s10=0;

 

 

/* 가장 기본적인 방법

for (int k=0 ;k<arrayRandomCount.length ;k++ ){

switch(arrayRandomCount[k]){

case(0):

s1=s1+1;

break;

case(1):

s2=s2+1;

break;

case(2):

s3=s3+1;

break;

case(3):

s4=s4+1;

break;

case(4):

s5=s5+1;

break;

case(5):

s6=s6+1;

break;

case(6):

s7=s7+1;

break;

case(7):

s8=s8+1;

break;

case(8):

s9=s9+1;

break;

case(9):

s10=s10+1;

break;

}

}

System.out.println("0이 " + s1 + "" +"번 나왔습니다");

System.out.println("1이 " + s2 + "" +"번 나왔습니다");

System.out.println("2이 " + s3 + "" +"번 나왔습니다");

System.out.println("3이 " + s4 + "" +"번 나왔습니다");

System.out.println("4이 " + s5 + "" +"번 나왔습니다");

System.out.println("5이 " + s6 + "" +"번 나왔습니다");

System.out.println("6이 " + s7 + "" +"번 나왔습니다");

System.out.println("7이 " + s8 + "" +"번 나왔습니다");

System.out.println("8이 " + s9 + "" +"번 나왔습니다");

System.out.println("9이 " + s10 + "" +"번 나왔습니다");

*/

 

 

//좀 더 쉬운 방법

/* 이중 for문을 사용 각 숫자(0~9)별로 배열의 첫 값 부터 마지막 값까지 비교하여 그 발생빈도를 출력한다. 로직을 풀어보면)

 

0 == arrayRandomCount[0] 부터arrayRandomCount[10] 까지의 값을 비교하여 '0'이 나온 숫자를 카운트하여 저장.

1 == arrayRandomCount[0] 부터arrayRandomCount[10] 까지의 값을 비교하여 '1'이 나온 숫자를 카운트하여 저장.

........................

9 == arrayRandomCount[0] 부터arrayRandomCount[10] 까지의 값을 비교하여 '9'이 나온 숫자를 카운트하여 저장.*/

 

for(int susJa=0;susJa<arrayRandomCount.length; susJa++){

int sum=0;

for(int idx=0; idx<arrayRandomCount.length; idx++){

if(susJa==arrayRandomCount[idx])

sum++;

} // 0~9까지의 숫자들의 갯수를 검사하여 증가

System.out.println(susJa +"은 " +sum +"개입니다.");

} // 0~9까지의 검사한 갯수를 출력

 

/* for 문을 통해 arrayRandomCount의 길이만큼 돌리고 내부 switch문을 통해 도출될 숫자의 경우들을 뽑고, 다시 이들을 다 누적시킨다.*/

 

 

//If 문을 통한 해결

if(i[idx] ==0){

s0++;

}

else if (i[idx[\] ==1){

s1++;

}

}

} 

 

예제2

RateTest.java

: 배열에 나열된 int 값들을 열거하고, 그 배열된 값들이 해당 배열에서 차지하는 비중을 "*" 및 숫자로 %를 표시한다.

class RateTest{

 

public static void main(String[] args) {

int data[]={10,30,20,80,50};

 

System.out.print("입력된 배열들 : ");

for (int e : data ) {

System.out.print(e + "\t");

}System.out.println("");

 

double sum=0;// 합을 넣을 변수

//합을 구하는 로직-반복문 이용

for (int k=0;k<data.length ;k++ ){

sum=sum+data[k];

}System.out.println("배열의 총 합은 = " + "" +sum);

 

 

[설명 1-1 ]

이중 For문을 사용하여 배열의 길이만큼 첫 번째 for 문을 돌면서 배열의 각index에 들어있는 data값들의 비율을 출력하고, 두 번째로 각 rate에 입력된 숫자가 나올 때까지 '*(=Asterisk)"문자를 출력하도록 하고 해당 값이 나오면 별표를 그만 찍도록 한다.

이러한 Logic으로 해당 배열의 마지막 Index값에 저장된 Data값 비율까지 다 출력한다.

 

for ( int z=0; z<data.length; z++){

double rate=0;

//rate=(int)((data[z]/sum)*100);

//rate에 해당 data[z]의 값이 몇 %인지 계산한 값을 입력한다.

rate=Math.round((data[z]/sum)*100); //Math.round 함수를 써서 소수점 자리를 반올림한다.

 

System.out.print("Rate of " + data[z]+ ""+":" +"\t" + rate );

 

for (int y=0; y<rate; y++ ){

System.out.print("*");

}

System.out.println("");

}

}

} 

 

 

6/45 Lotto Program 실습

class Lotto{

public static void main(String[] args)

{

int[] Inums=new int[6];

int newnum,temp;

 

//값 생성 -중복체크

for (int a=0; a<Inums.length; a++)

{

newnum=(int)(Math.random()*45)+1;

for (int c=0; c<a;c++ )

{

if(newnum==Inums[c])

{

newnum=(int)(Math.random()*45)+1;

c=0;

}

}

Inums[a]=newnum;

}

//정렬

for (int i=0;i<Inums.length;i++ )

{

for (int j=0;j<i ;j++ )

{

if(Inums[j]>Inums[i])

{

temp=Inums[i];

Inums[i]=Inums[j];

Inums[j]=temp;

}

}

}

 

for (int e:Inums )

{

System.out.print(e + "\t");

}

}

} 

 

 

 

Class Path & Package

>> Package

  • Class들의 묶음.(=Class File들의 묶음이 좀 더 명확한 표현)으로 Windows의 Directory의 개념.
  • 사용 이유: 관리목적상의 용이함. 중복파일명 Class의 구분.
  • 분류기준: 프로그램별, 프로그램 내 기능별,

 

  • Package 선언
  • 코드에서 작성하는 클래스가 묶일 패키지를 선언해야 한다.
  • 소스코드 첫 명령어로 와야 한다.

    (예외: 주석과 공백허용. 즉 프로그램에 영향을 미치는 요소에서는 무조건 첫 번째로 와야 한다.)

  • 구문

package root경로 [.subpath.subpath…];

ex)package kr.or.kosta.ui;

 

Class의 Fully Name : package명.class명

 

 

Javac –d 옵션

-d <directory> Specify where to place generated class files

컴파일을 할 때, 해당 Directory에 *.class 파일을 저장한다. 이때 저장 Directory가 존재하지 않을 시 생성하여 넣는다. '.'을 쓸 경우 현 Directory에 저장한다.

Ex) d:\src\javac –d c:\saveclass\1\2\3 ClassExe

를 하게 되면 해당 class 파일을 컴파일 위치인 d:\src에 넣는 게 아닌

C:\saveclass\1\2\3에 넣는다.

 

Package 실습예제 (Car.java & CarDriver.java)

package kr.or.kosta;

//현 Directory의 하위 kr\or\kosta 밑에 Car.class 파일이 생성되도록 하며, 클래스로 묶인다. 단, 컴파일시에 옵션을 사용해서 컴파일해야만 목표한 Directory로 이동하는 것이다. 그러므로 컴파일시 " D:\SRC\0311>javac -d . Car.java " 로 해야 한다.

 

public class Car{

int speed;

public void go(){

System.out.println(speed+"km/h 로 갑니다");

}

 

public void stop(){

System.out.println("Car Stop");

}

}

//별도의 패키지작업이 없으면 자동으로 묶이긴 하지만, 이를 익명 패키지로 묶인다고 하고 해당 Directory내에서만 묶인다.

class CarDriver{

public static void main(String[] args){

//Car myCar=new Car(); //변경전 Source

kr.or.kosta.Car myCar=new kr.or.kosta.Car(); //변경 후 Source

myCar.go();

myCar.stop();

}

}

 

/*

---------- javac ----------

CarDriver.java:12: cannot access Car

bad class file: .\Car.java

file does not contain class Car

Please remove or make sure it appears in the correct subdirectory of the classpath.

Car myCar=new Car();

^

1 error

 

컴파일을 실행하게 되면 위와 같은 문제가 발생하게 되는데, 이는 Car.class 파일을 JVM이 찾지 못하기 때문에 발생하는 문제의 일종이다. 그러므로 Car 클래스를 사용할 때에는 kr.or.kosta.Car 로 명시해주어야 한다.

 

*/

 

위 실습의 소스에서 보듯이 매번 Car 클래스의 경로를 지정하는 방법은 번거롭다. 그렇기에 import를 사용하여 간단히 해결하여 사용한다.

 

Import= 다른 Package의 class를 code에 기술할 때 이 class의 경로를 알려주는 것으로 import는 class 단위로 한다. 또한 import 구문은 class선언보다 먼저 와야 한다.(package는 첫 줄에 한번만 올 수 있고, import는 여러 Line 선언 가능하다.)

 

import rootpackage명[.subpackage명…..].class이름 [or *];

이 때 *는 Class에만 적용되고, Package에는 적용되지 않는다.

Ex) A.B.C.a클래스 A.B.C.D.b 클래스가 있을 때

Import A.B.C.*을 선언하게 되면 D.b 클래스는 포함되지 않는다.

 

앞선 예제의 package명을 일일이 명시하던 것을 import로 수정해보면

import kr.or.kosta.Car;

class CarDriver{

public static void main(String[] args) {

Car myCar=new Car(); //변경전 Source

myCar.go();

myCar.stop();

}

} 

로 간단히 해결된다.

만일 위의 CarDriver.java 파일을 kr.or.kosta.Car 클래스가 있는 곳이 아닌 다른 곳에서 실행하게 되면 실행이 되지 않는다. 이는 JVM이 kr.or.kosta의 위치를 모르기 때문이다. 이를 위해서는 OS의 환경변수에서 'CLASSPATH'에 해당 경로 (만약 kr.or.kosta.Car가 D:\src\0311에 존재한다고 가정)를 지정해 주어야 한다.

 

매번 ClASSPATH를 설정하기 번거롭다면, 이를 좀 더 쉽게 해결하는 방법이 있다.

윈도우 탐색기로 C:\Program Files (x86)\Java\jre6\lib\ext를 확인할 수 있는데, 이 위치에 jar 압축파일로 넣어두면 된다. 이때 압축은 class 파일 뿐 아니라 Package를 포함해야 한다.

 

간단한 jar 사용법

(압축하기) Jar –cvf FileName Target

(압축풀기) Jar –xvf 파일명

 

C:\Users\Administrator>jar

사용법: jar {ctxui}[vfm0Me] [jar 파일] [manifest 파일] [시작 지점] [-C 디렉토리] 파일 ...

옵션:

-c 새 아카이브 만들기

-t 아카이브에 대한 내용 목록 테이블

-x 명명된(또는 모든) 파일을 아카이브에서 추출

-u 기존 아카이브 업데이트

-v 표준 출력에 세부 정보 표시 출력 생성

-f 아카이브 파일 이름 지정

-m 지정한 매니페스트 파일로부터 매니페스트 정보 포함

-e jar 실행 파일에 번들로 제공된 독립 실행형 응용 프로그램의

응용 프로그램 시작 지점 지정

-0 저장 전용, ZIP 압축 사용 안 함

-M 항목에 대해 매니페스트 파일을 만들지 않음

-i 지정한 jar 파일에 대한 색인 정보 생성

-C 지정한 디렉토리로 변경하고 다음 파일 포함

어떤 파일이 디렉토리면 재귀적으로 처리됩니다.

매니페스트 파일 이름, 아카이브 파일 이름 및 시작 지점 이름은

'm', 'f' 및 'e' 플래그와 동일한 순서로 지정됩니다.

 

예 1: classes.jar라는 아카이브에 두 클래스 파일을 아카이브하는 방법:

jar cvf classes.jar Foo.class Bar.class

예 2: 기존의 매니페스트 파일 'mymanifest'를 사용하여

foo/ 디렉토리의 모든 파일을 'classes.jar'로 아카이브하는 방법:

jar cvfm classes.jar mymanifest -C foo/ . 

 

 

Ex)

D:\>jar -cvf src.jar kr

추가된 manifest

추가 중: d:/SRC/(내부 = 0) (외부= 0)(0%가 저장되었습니다.)

추가 중: d:/SRC/03.02/(내부 = 0) (외부= 0)(0%가 저장되었습니다.)

추가 중: d:/SRC/03.02/HelloWorld.class(내부 = 492) (외부= 357)(27%가 감소되었습니다.)

 

 

해당 압축파일을 이제 C:\Program Files (x86)\Java\jre6\lib\ext 위치로 이동시킨 후, 인터프리터 해보면 잘 실행되는 것을 확인할 수 있다.

이렇게 프로그램을 작성함에 있어 필요할 때 마다 불러다 쓰는 것을 API라 한다.

 

 

접근제어 지시자와 정보은닉, 그리고 캡슐화

 

>> 접근 제한자(Access Modifier)

이제껏 배운 내용으로는 Caller가 Worker를 마냥 불러다 일을 시킬 수 있었지만, Worker에서 접근 제한자를 사용하게 되면, 개나 소나 모든 Caller가 다 일을 시킬 수는 없게 된다.(=Caller의 범위를 지정하는 제한자.)

 

접근 제한자는 Class나 Method, Variable에 붙을 수 있다. 단, Variable 중 Local Variable에는 붙을 수 없다. 이는 Local의 경우 외부에서는 원천적으로 접근이 불가능하기 때문이다.

 

접근범위

UML 표기법

한 CLASS 내에서의 접근

같은 Package 내에서의 접근

다른 Package내에서의 접근

(접근범위에 관련된 세부사항)

private 

매우 좁다

O

X

X

package friendly

좁다

 

O

O

X

protected

넓다

#

O

O

조건부

(상속일 때 O)

public

매우 넓다

+

O

O

O

(package friendly는 Default값으로 아무것도 쓰지 않으면 이 값이 기본적으로 들어간 것이다.)

 

Class의 접근 제한자

pulic & package friendly 을 사용할 수 있다.

public 제한자 이용시

  1. 접근제한 규칙을 가진다.
  2. Source File의 이름이 Class명과 동일해야 지정해야 한다.
  3. 한 Source File 내에서는 public Class는 반드시! 하나이어야 한다.

 

 

Source File Layout

 

[package 선언]

Source File의 Dir.

Import 구문

0~n 번 올 수 있다.

Class 선언 및 구현

1~n번 가능. 단. Public class는 1번만 가능.

Public 클래스의 식별자는 파일명과 동일해야!

 

 

>> 정보은닉(=Information Hiding)

[Attribute는 Private으로 하고 그 attribute에 값을 조회하는 get Method와 값을 변경하는 set Method는 public Method로 제공하라!]<- 규칙처럼 명문화된 시장표준.

 

객체가 가지는 정보(Attribute의 비중이 90%를 상회)를 숨기는 것.

주요 목적은 외부 객체에서 자신의 정보를 변경하는 것을 방지하기 위함이다.

 

정보은닉을 위한 실습예제 (GoodsStock.java)

 

//상품 제고를 관리하는 클래스 //GoodsStock.java

 

public class GoodsStock{

 

String goodsName; //상품명

private int stockAmount; //재고량

public GoodsStock(){}

public GoodsStock(String gn,int am){

goodsName=gn;

stockAmount=am;

}

}

Order.java

 

class GoodsManager{

public static void main(String[] args)

{

Order goodsOrder=new Order();

goodsOrder.order(100);

//order의 조건IF문에서 재고량보다 많은 수량의 주문이 들어올 경우, 문제가 발생하는 것을 확인할 수 있다.

/* 이를 방지하기 위해서는 아래와 같이 GoodsStock 클래스의 Attribute들을 private로 선언하여 외부에서 접근을 차단시키도록 한다.

private int stockAmount;

 

그리고 다시 Compile 해보면

 

Order.java:19: stockAmount has private access in GoodsStock

System.out.println("주문시작:재고량:"+gs.stockAmount);

 

메세지와 함께 Comile 자체가 되지 않게 된다. 하지만 여기서의 문제는 컴파일 자체가 되지 않으므로 조회 자체가 되지 않는 문제가 발생하게 되는 것이다.

이를 방지하기 위해서는 컴파일 오류를 없애고, 특정 데이터에 접근은 가능하도록 해주어야 한다. 이를 위해서는! private로 제한되어진 attribute에 접근하는 이유는 크게 2가지로 살펴볼 수 있다.

 

즉, 조회와 변경을 위한 것으로

조회의 경우 (method 이름을 관례적으로 get___으로 시작) caller가 worker에게 넘겨주는 argument는 주지 않는다.

변경의 경우 (method 이름을 관례적으로 set___으로 시작) caller가 worker에게 변경해야할 argument를 같이 넘겨준다.

<< 5w1h에 의해 일반 타입들은 get/set을 접두어로 쓰지만, Boolean 타입의 경우 be동사(이다/아니다)의 결과이므로 접두어로 is를 쓴다.>>*/

}

}

 

/*

class Person

private String name;

 

조회 Method :

public String getName(){

}

 

변경 Method :

public String setName(String n){

name=n;

}

 

 

Name Property ={ name + getName() + setName()}를 포함하는 개념.

 

접근자가 없을때에는 p.name="홍길동" 이라는 식으로 직접 변경을 했지만

접근자를 있을 때에는 p.setName("홍길동") 이라는 Method를 통해서 변경하도록 시키는 것이 정보은닉의 방법이다.

위 두가지 방법의 결정적 차이는 전자는 worker 클래스의 값을 직접적으로 변화시키는 것이라면

후자는 Worker 클래스에서 해당 Method를 통해 입력되어질 값에 대해서 적용 유무를 지정할 수 있다.

다시 말해

setName ( String n){

if(n=="장동건 || n=="원빈")

name =n;

else {

System.out.println(n + "이라는 이름은 싫습니다. 사양하것습니다");

}

라는 논리적 로직이 성립하는 것이다.

 

위와 같은 방식으로 프로그램을 작성하는게 습관화되어 있다.

즉 , Instance Member Variable은 Private로, Instance Member Method는 Public으로 선언하여 사용하는게 좋다.

*/ 

GoodsManager

//주문을 관리하는 클래스

public class Order{

GoodsStock gs;

public Order(){

gs=new GoodsStock("노트북",20);

}

//주문 메소드

public void order(int orderAmount){

//GoodsStock에서 재고량을 체크하여 주문처리 및 재고량 체크

 

System.out.println("주문시작:재고량:"+gs.stockAmount);

int stockAm=gs.stockAmount;

if(stockAm <orderAmount){

//제고량보다 주문량이 많으면 주문이 안되어야 하는게 정상이지만, 코드에서 주문량과 기존 제고량을 더해 제고량에 입력하도록 변경시킨다.

gs.stockAmount= stockAm+orderAmount;

}

//주문

System.out.println("재고량 체크 후 재고량:"+gs.stockAmount);

gs.stockAmount=gs.stockAmount-orderAmount;

System.out.println("주문 처리 후 재고량:"+gs.stockAmount);

System.out.println("주문 처리를 완료합니다.");

}

} 

 

객체는 ( 크게 값을 저장하는 것과 / 비즈니스로직을 처리하는 게 있는데 전자를 Value Object)=VO)라 한다. VO를 위해서는 Property만 작성하면 된다. VO 를 다른 말로 Data Transfer Object(=DTO)라고도 한다. 풀어쓰면 데이터들을 옮길 때 사용하는 객체를 의미한다. 또한 일반적으로 VO는 Set/Get Method를 제외한 Attribute만을 가지고 있다.

DTO : Return Data나 Argument로 Data 이동시 사용.

( (-) Attribute + setter/getter ) Property

 

비즈니스로직을 처리하는 객체는 업무처리객체이다. 즉, 클라이언트들이 원하는 작업을 처리해주는 것이다.

 

Attribute들의 포인트가 Value Object라면 Method들의 포인트는 Business Logic 객체라 할 수 있다.

 

캡슐화(Encapsulation)

  • 정보은닉를 포함하는 상위 개념으로 OOP의 3대 개념(캡슐화, 상속, 다형성)의 하나이다.

  • 객체의 내부 구현은 감추고 사용법만 보여준다.
  • 사용이유는 우선 Caller에게 편의성을 제공하기 위함이다. 일일이 복잡한 세부 Logic들로 머리 아프게 하지 않고, 사용법(선언문 Part의 개념)과 실행결과만 알면 내부구현을 몰라도 호출 가능하도록 하는 것이다.
  •  

Method Overloading

 

(Method & Constructor) Overloading

Method overloading :

Constructor overloading : argument가 다르면 같은 이름을 사용할 수 있다. 이를 주로 사용하는 예시는Method가 비슷하거나 같은 기능을 하는 동작들에 적용한다. 식별자의 본래 기능인 구분하는 역할을 할 수 없도록 이름을 똑같이 지어도 되는 것은, Argument를 통해서도 식별이 가능하기 때문이다.

 

키워드 this

사용처

  1. 생성자에서 Overloading된 다른 생성자 호출.

    (참고! 이와 동일한 기능을 하는 다른 키워드로 super가 있다.)

This (값 [ , , , ….. ,]

주의점: 생성자 구현부의 첫 명령어로 와야한다.

 

  1. 객체참조변수로 사용( 여기서 this는 로컬변수이고, 따라 Stack에서 사용)

    (필드에서 많이 사용되는 방법이므로 필히 알아야 한다.)

    Ex)public Student(String n)

    name=n;

    <- 콜러의 메소드에서 n값을 입력받아 name이란 변수에 넣는다.

     

    public Student(String name)

    name=name;

    <- n이란 변수가 가독성이 부족하므로 name으로 변환한다. 그랬더니 Caller에서 가져온 Local 변수와 Worker에서 저장될 Attribute 변수의 식별자명이 중복되어, 오류가 발생하게 된다.

    그러므로 JVM이 혼란스럽지 않도록 워커의 변수에 this 키워드를 사용함으로써 구분을 쉽게 하여 준다.

     

    public Student(String name)

    this.name=name; (여기서 this는 로컬변수가 아닌 instance(Attribute) 변수임을 알려준다. 일반적으로 this는 jvm이 보이지 않게 다 붙여서 작동한다. 하지만 위와 같이 변수명이 중복될 경우 명시적으로 알려주어야 JVM이 헷갈리지 않게 되는 거다.)