어제는 인스턴스를 통해 객체를 생성하고 할당된 주소값을 참조변수에 담아 해당 클래스의 변수의 초기화를 진행하는 방식을 배웠다. 하지만 이런 방식은 번거로운 구조라는 생각이 들었다. 인스턴스 변수에 값을 넣기 위해서 매번 참조변수를 통해 변수를 하나하나 입력해줘야 하는 일이 100개가 되고 1000개가 되면 여간 고생스러운 일이 아닐 수 없기 때문이다. 그래서 오늘은 생성자라는 것을 통해 어떤 식으로 변수 값을 넣어 주는지, 그리고 기존의 방식 또한 어떻게 동작하고 있었는지에 대해 알아보겠다.

<기본 생성자 작성법>
클래스명( ) { }

- 생성자 메서드(함수)의 이름은 클래스와 동일해야 한다.
- 기본 생성자를 작성하지 않았다면 프로그램이 자동으로 구현해둔다. (단, 하나라도 생성자가 있을 경우 만들지 않음)
- 반환하는 값이 없기 때문에 return타입도 존재하지 않는다. (데이터 타입이 없음)
- 호출되면 클래스가 가지고 있는 변수의 초기화를 진행한다.

 

   먼저 기본 생성자부터 알아보자. 지난 시간에는 new와 참조변수만 있으면 하나의 인스턴스를 만드는 건 쉬웠다. 하지만 그냥 new를 써서 객체를 만드는 것이 그냥 띡 하고 되는 일이 아니었다. 바로 생성자가 있어야만 인스턴스를 만들 수 있었는데, 기존 클래스에 사용자가 기본 생성자를 작성하지 않았더라도 프로그램에서 디폴트 값으로 기본 생성자를 만들어주어 가능했던 것이었다. 여기서 생성자란 인스턴스를 생성할 때 호출되면 클래스가 가진 변수의 값을 자동으로 초기화 시켜주는 메서드의 일종이라고 보면 된다. 다만 void가 붙은 것처럼 return값이 없다는 것도 하나의 특징이라고 할 수 있다.

 

 

   기본 생성자가 메서드라는 것을 알았다. 그렇다면 메서드가 가질 수 있는 매개변수를 통해 실제 인스턴스 변수에 값을 담는 것도 가능하지 않을까? exam_39라는 클래스 안에 세 개의 인스턴스 변수가 있다. 모델은 고정이기 때문에 변수를 선언함과 동시에 초기화를 진행했고, 나머지 두 값은 사용자의 데이터를 받기 위해 초기화를 진행하지 않았다. 그리고 메서드를 작성하는 것 처럼 클래스명을 메서드명으로 정하고 매개변수 값에 받을 데이터 타입과 변수명을 입력해준다.

 

   여기서 매개변수에 있는 변수명과 클래스에 선언되어 있는 변수명이 같지만 서로 다른 지역에 선언되었기 때문에 중복 사용이 가능하다. 기본적으로 함수 내에 작성되는 변수는 지역변수이고, 클래스에 선언된 변수는 전역변수기 때문. 하지만 이름이 같기 때문에 함수 안에서 color를 불러오면 지역변수만 선택할 수 있게 된다. 여기서 파라미터를 받아 올 매개변수의 값을 인스턴스 변수의 값으로 대입하고 싶어 this라는 명령어를 배우게 됐다. this는 자바스크립트에서도 사용했지만 사용하는 위치에 따라 인식하는 범위가 다른데, 이 곳에서는 함수 밖의 전역을 의미한다. 그러니 this.변수명을 입력하게 되면 전역에 있는 변수를 가져올 수 있게 되는 것이다. 이렇게 작성해서 하나의 생성자가 완성되었다, 그렇다면 직접 데이터를 넣어보자.

 

   일반 메서드를 사용하는 것 처럼, new를 붙여 생성한 객체의 괄호 안에 직접 파라미터를 입력하니 참조변수를 통해 넣은 값처럼 똑같이 출력된다. 만들어둔 생성자 덕분에 참조변수를 하나하나 입력하여 수정할 일이 없어진 것이다. 하지만 이렇게 임의로 생성자를 만들어 사용한 뒤, 기존에 생성하던 것 처럼 생성하게 되면 '해당 메서드를 찾을 수 없습니다.' 라는 에러가 뜨게 된다. 기본적으로 만들어 주던 기본 생성자가 사용자가 작성함으로써 이번에는 만들어지지 않았기 때문이다. 만약 파라미터 값을 받아오는 형식이 아니라 직접 입력하고 싶을 때 인스턴스를 생성하고 싶다면 추가적으로 클래스명과 함께 기본생성자 메서드를 작성하면 된다.

 

   하지만 여기서 의문이 들 것이다. 과연 같은 클래스명을 가진 메서드가 둘, 셋... 혹은 그 이상 존재해도 되는걸까?

 

   메서드명이 같다면 당연히 안된다. 하지만 메서드마다 가질 수 있는 매개변수가 서로 다르다면, 혹은 생성자가 아닌 일반 메서드에서 return값을 받아 올 데이터 타입이 다르다면 가능하다.

 

   클래스 함수로 세 개의 메서드를 작성했다. 하지만 세 개의 메서드의 이름은 모두 sum으로 동일하다. 하지만 출력했을 때 에러없이 잘 작동되는 것을 확인할 수 있는데, 바로 세 개의 메서드가 가진 매개변수와 return의 데이터 타입이 다르기 때문이다. 첫 번째 sum 함수는 매개변수를 두 개를 가지고 있고, 두 번째와 세번째는 세 개의 매개변수를 가지고 있다. 그리고 두 번째는 정수로, 세 번째는 실수로 데이터 타입을 정하고 함수를 사용할 때 해당하는 값을 넣어주면 메서드에 입력된 기능이 제대로 동작하는 것을 볼 수 있었다. 이렇듯 구조와 타입은 다르지만 이름이 같은 메서드를 여러개 정의하는 것을 오버로딩라고 부른다. 다만 함수를 사용할 때는 입력될 값의 데이터 형식을 알아야 사용할 수 있다는 것이 옥에 티이긴 하나, ctrl을 누른 채 함수를 클릭하면 해당 메서드의 구조를 보여주니까 참고하도록 하자.

   어떠한 제품을 만들기 위해서는 그 제품에 맞는 설계도가 필요하다. 앞으로 Java에서 만들 프로젝트를 위해서도 이 설계도가 필요한데 이를 여기서는 클래스(class)라고 부른다. 즉, 프로젝트를 만들기 위한 설계도로 클래스를 생성해야 하며 클래스 안에는 각 객체가 프로젝트 내에서 동작할 수 있는 고유한 속성(변수) 기능(함수)을 포함시킬 수 있다. 그렇다면 이 설계도를 바탕으로 실제 객체를 생성하여 사용하기 위해서는 어떻게 해야할까.

 

   여기 두 개의 클래스가 있다. 하나는 객체에 필요한 속성과 기능을 만들어 두었고(왼쪽), 하나는 main() 함수로 영역에 포함된 기능들을 실행시키기 위해 작성(오른쪽)되었다. 클래스에 작성된 모든 속성과 기능은 기본적으로 동작하지 않는다. static(미리 준비된 상태)을 가지지 못한 변수와 함수들은 프로젝트의 기능이 구현 될 ram메모리에 사실상 존재하지 않기 때문인데, 이를 객체화시켜 ram 메모리에 클래스를 구현해내는 것을 '인스턴스' 라고 부른다. 인스턴스(객체)가 생성된 클래스는 그 때부터 가지고 있는 모든 속성과 기능을 꺼내어 초기화를 할 수도 있고, 함수를 실행시킬 수도 있다.

 

<인스턴스 생성 방법>
   클래스 이름(데이터 타입) 참조변수명;
   참조변수명 = new 클래스 이름( ); //new로 생성된 객체가 부여받은 주소를 참조변수에 저장
   ex) exam_31 mycar = new exam_31( );

 

   여기서 새로운 변수가 나오게 되는데 바로 참조변수이다. 참조변수란, 인스턴스가 생성됨과 동시에 할당 받는 주소값을 저장하는 공간이다. new로 생성된 인스턴스는 하나가 아니라 여러개 존재할 수 있는데, 모양이 같을 뿐 각자 고유한 기능과 속성을 가진 다른 객체기 때문에 생성될 때마다 고유한 주소값을 받는다. 이를 언제든 호출하기 위해서 변수에 담아야 하는데 이 주소를 받는 변수가 바로 참조변수인 것이다. (변수에 담지 않으면 사용이 되지 않으므로)

<인스턴스 접근 방법>
   인스턴스 변수에 접근할 때 : 참조변수.변수명
   인스턴스 함수에 접근할 때 : 참조변수.함수명( )

 

   이런 식으로 주소를 가진 참조변수가 해당 클래스에 찾아 들어가 가지고 있는 변수와 함수를 꺼내어 활용할 수 있도록 도와준다. 즉, 참조변수는 클래스(객체)를 대신하는 것과 비슷하다 볼 수 있다.

   new 객체 생성으로 같은 클래스를 들고왔지만 mycar1과 mycar2는 서로 다른 객체이다. 각자 고유의 속성을 가질 수 있어 색상을 다르게 지정할 수도 있고, 고유한 기능을 함으로써 속도도 다르게 지정할 수 있다. 하지만 이 클래스가 자동차의 설계도라고 가정해보면 다른 자동차여도 똑같이 변하지 않는 값이 있을 것이다. 바로 바퀴가 네 개라는 점은 어느 자동차든 변하지 않는 사실이니까 말이다. 그렇다면 이런 부분을 각 참조변수를 불러와서 하나하나 바퀴가 4개라고 입력을 해주어야 할까? 이런 편의성을 고려해서 그런지 클래스가 가질 수 있는 변수는 인스턴스 변수 외에 클래스 변수라는 것이 존재한다.

인스턴스 변수
- 인스턴스마다 가지는 고유한 변수, 고유한 저장 공간을 가진다.

- 인스턴스 변수 선언 방식은 기존 변수 선언 방식과 동일하다.

클래스 변수
- 모든 인스턴스가 공통된 값을 공유하는 변수, 저장 공간은 하나 뿐이다.

- 인스턴스를 생성하지 않아도 메모리에 존재하는 변수이다. (static으로 생성)

- 참조변수 없이 불러올 수 있지만 참조변수로 불러올 수 없는 건 아니다.
  (그러나 굳이 참조변수를 생성하여 불러 올 필요가 없다)

 

   위와 같은 설명과 마찬가지로 클래스 변수는 어떤 인스턴스라도 같은 값을 가지는 공통된 변수를 말한다. 생성할 때 일반 변수와 똑같이 생성하되, 앞에 static을 붙여 언제든 사용할 수 있는 상태로 만들어 두어 별도의 객체 생성 과정이 없어도 바로 바로 호출이 가능하도록 설정한다. 이렇게 되면 각 인스턴스가 가질 값이 공통된 하나의 저장소로 줄어드니 리소스 또한 절약할 수 있다는 장점이 있다.

   자동차의 속도는(speed) 각 인스턴스의 고유의 값을 가져야 하기 때문에 인스턴스 변수로 선언했고, 바퀴는 어느 인스턴스든 4개로 동일하기 때문에 static을 붙여 선언과 동시에 초기화를 했다. 이를 콘솔창에 나타내기 위해 가져오기 위해서 고유의 값을 가진 speed 변수는 인스턴스를 생성하여 불러와야 하지만, wheel 값은 이미 생성되어 메모리에 기록된 상태기 때문에 클래스명과 함께 변수를 불러오면 4라는 숫자가 콘솔창에 뜨게 된다. 물론 참조변수 내에서 불러오는 것도 불가능하지 않다. mycar2로 참조변수를 만들어 똑같이 불러내면 별 다른 오류 없이 작동하는 걸 확인할 수 있다. (어쨌든 해당 클래스에 부여된 속성이기 때문에 어느 인스턴스에서 호출해도 나오게 된다. 하지만 굳이 번거롭게 할 필요가 없기 때문에 클래스명으로 호출하는 것이 정석이다.)

 

   다만 저장 공간은 하나기 때문에 참조변수를 통해서 값을 바꾸어도 모든 인스턴스에서 해당 변수값은 바뀌어 출력되게 된다.

 

   이렇게 변수를 불러오는 방법을 알아봤으니 이제 함수에 대해서 조금 더 알아보자. 메서드라고 부르는 이 함수는 특정한 작업을 실행하는 코드를 가지고 있어 사용자가 넣은 값을 해당 기능에 대입하고 나온 값을 반환(return)하는 역할을 한다. 변수처럼 저장되어 언제든 쓸 수 있는 메서드는 클래스를 구성하는 요소에 있어 매우 중요하다.

(void - 반환하지 않음) 반환(return)타입 메서드명(함수이름) ( 매개변수 ) { return 값; }

 

   메서드의 생성 방법은 변수와 똑같이 데이터 타입을 결정해주어야 하는데, 이 값은 기능에서 반환받을 값의 데이터 형식을 정하는 것이다. 매개변수는 해당 메서드가 호출되며 입력받은 파라미터(값)을 담을 변수를 뜻한다. 여기서 매개변수의 데이터 타입을 정수로 받고 반환하는 값의 데이터 타입을 실수로 한다면 사용자는 정수를 입력했지만 출력될 값은 소숫점이 붙은 실수로 출력된다는 것이다. 여기서 매개변수의 괄호 부분은 받을 재료값이 필요하지 않으면 비워놔도 되지만 해당 메서드를 호출 할 경우에는 꼭 괄호를 이어 붙여줘야 한다. 그리고 메서드가 실행되면 return값을 사용자에게 전달해주기 때문에 해당 값을 변수에 저장하고 사용하거나, 혹은 값만 화면에 출력하고 끝내버리는 등 다양한 활용이 가능하다.

 

   여기서 메서드 또한 클래스의 자식으로써 기능하기 때문에 어느 위치에 있느냐에 따라 인스턴스 메서드가 될 수도 있고, 클래스 메서드가 될 수도 있다. 선언되는 방식은 위에서 말한 변수와 동일하다. 특이하게 void라는 명령어를 타입 앞에 선언할 수 있는데 이건 return값을 주지 않겠다는 의미라고 한다. 물론 함수를 호출하게 되면 내부에 작성된 코드는 실행되지만 return값을 강제하지 않는다는 의미로 보면 편할 것 같다.

 

   그렇다면 이제 인스턴스와 클래스에 대해 배운것을 종합적으로 정리해보겠다. 인스턴스 변수(혹은 함수)는 사용자가 new를 사용해 객체를 생성하기 전까지는 메모리에 존재하지 않기 때문에 static으로 선언된 함수에서 인스턴스 변수를 불러오면 에러가 뜬다. 이미 메모리에서 동작할 준비를 마친 함수 안에 선언되지 못한 인스턴스 변수는 존재할 수 없기 때문이다. 하지만 반대로 인스턴스 함수 안에 인스턴스 변수를 사용하는 건 가능하다. 어차피 둘 모두 new로 객체 생성을 선언하면 메모리에 같이 쓰여지기 때문이다. 이렇듯 둘 사이의 구조를 이해하는 것에 초점을 맞춘다면 변수에 관해서는 헷갈릴 일은 없을 듯 하다.

   변수는 보통 단일 자료를 담을 수 있지만 여러 자료를 담아야 할 경우에는 어떻게 해야 할까? 그럴 때는 배열변수를 사용해야 한다. 데이터 타입에 배열을 지정하면 같은 타입의 자료를 모아 하나의 변수에 저장하는 것이 가능한데 이렇게 저장된 배열변수의 값을 꺼내오고 활용하기 위해서는 반복문이 필요하다. 마치 어제 배운 제어문(조건문)의 조건식에  boolean(참과 거짓)값을 넣기 위해 연산자를 활용하는 것처럼.

  배열의 길이를 지정한 경우 배열의 길이를 지정하지 않은 경우
선언 데이터타입[ ] 변수명 = new 데이터타입 [길이]
(ex. int[ ] array = new int[5];)
데이터타입[ ] 변수명 = {값,값,값...}
선언과 초기화를 동시에 진행할 수 있다.
(ex. int[ ] array = {10,20,30};)
초기화 변수명[인덱스(주소)] = 값;
(ex. array[2] = 10;)

 

   배열에 길이를 지정해두고 인덱스값을 참조하여 초기화하거나, 애초 선언과 동시에 중괄호를 활용하여 값을 순서대로 넣을 수 있다. 단, 길이를 지정했을경우 변수로 값이 바뀌지 않는 이상 그 배열에서 더 늘어날 수 없다. 이를테면 int[5]로 5개의 배열을 만들겠다 선언한 뒤 인덱스로 array[5]의 값을 넣게 되어도 출력할 때 인덱스의 범위를 초과했다는 에러창이 뜨게 된다. 자바스크립트에서는 길이를 지정한 뒤 초과해도 큰 문제가 없었지만 자바는 이 점에서 조금 차이가 있는 것 같다.

 

   배열 변수에 담긴 값을 함수에 불러오거나 활용하려면 각 값이 담겨있는 주소, 즉 인덱스를 변수명 뒤에 대괄호를 붙여 호출할 수 있다. 하지만 적은 수의 배열의 값을 꺼내올 때는 [1], [2]... 하나씩 입력하는 것이 쉬울 수 있어도 만약 100개, 1000개, 혹은 그 이상의 값이 들어가 있다면 어떻게 하나하나 호출할 수 있을까.

 

   그래서 오늘은 반복문을 배우게 됐다. 반복문의 종류도 몇 가지가 있다지만 말에서 알 수 있듯 특정 작업을 반복하기 위해 사용하는 명령어다. 이 반복문을 활용하면 배열변수에 있는 값을 꺼내어 활용하는 것은 정말 쉬워진다.

   일단 for문의 기본적인 이용 방법이다. for문은 초기화와 조건식을 통해 참과 거짓을 가려내는 과정을 한번 거치게 된다. 만약 여기서 참이 나오면 중괄호 영역에 쓰인 구문이 실행되고 증감식으로 돌아가 변수의 값을 증가시키거나 혹은 감소시키게 된다. 그러고는 다시 조건식으로 넘어가 증감식을 거친 변수의 값에 따라 참과 거짓을 가려내고 다시 참일 경우 실행, 증감, 조건식 비교, 이 과정을 거짓이 나올 때까지 반복한다. 그래서 반복문이라고 불리는 것이다. 그렇다면 조건식 비교문에 배열이 가진 주소길이 만큼 지정할 수 있다면 활용하는 게 가능하지 않을까?

   개발에서 0은 의미 없는 숫자가 아니다. 0과 1 사이에도 수가 존재하기 때문이다. 그렇기에 배열이 가진 인덱스도 0부터 시작하는데, 이를 활용하기 위해서 늘 초기값을 0으로 주는 습관을 들여야 한다. 그리고 반복할 횟수를 조건식으로 정해야 하는데 직접 3이라고 쓸 수도 있지만 만약 배열의 길이를 모르는 상태라면 .length를 통해 해당 배열변수의 길이를 자동으로 식에 대입할 수 있게 된다. 이렇게만 하면 반복문을 통해 배열변수의 값을 가져오는 것이 가능하다.

(물론 i의 값이 인덱스를 대신하기 때문에 배열변수를 부르는 방식으로 대괄호 안에 i를 넣어주어야 정삭 작동이 된다.)

 

   하지만 반복할 구문이 있다는 것 외에, 이런 배열을 불러오기 위한 for문은 쓰다보면 번거롭지 않을 수가 없다. 그래서 이를 보완하고자 나온 기능이 바로 foreach문이다. foreach문은 기본적으로 for문과 같이 동작하지만 식을 적는 부분에 있어 좀 더 간편화되어 있다.

   자바스크립트에서는 forEach문이라고 따로 썼는데 자바에서는 for문과 똑같이 적어주면 된다. 다만 식이 다를 뿐인데, 먼저 받아 올 데이터의 타입을 적고 임의 변수를 선언한다(꼭 i가 아니어도 된다! for문도 그렇다). 그런다음 콜론을 찍고 배열 변수명을 적어주면 알아서 변수 안에 들어있는 값이 foreach에서 선언한 변수에 담기며 구문을 실행하게 된다. 언제까지? 변수가 가지고 있는 값을 모두 쓸 때까지. 따로 조건식을 쓰지 않아도 알아서 배열변수의 길이만큼 스스로 동작하는 것이다. 이렇게 기능을 돌리면 첫 번째 foreach문에서는 Java Hello Programming 이라는 글자가 공백마다 줄이 바뀐 채 출력되고 두 번째 foreach문에서는 변수에 담긴 모든 값이 더해진 15가 화면에 출력된다.

 

   이런 반복문에는 for문만 있냐면 그건 아니다, while문이 있는데 for문은 반복할 횟수를 정해서 돌리는 것이라면 while문은 정해진 반복횟수가 없다는 것이 특징이다. 물론 while문을 for문처럼 동작하는 것도 가능하지만 일단 기본적인 활용은 그렇다는 것. while문은 조건식에 담긴 값이 true면 무조건 반복하기 때문에 따로 false가 나올 수 있는 환경을 만들어 주어야 한다.

 

   여기서 for문과 while문의 공통점이 보일 것이다, 바로 true와 false값에 따라 작동 여부가 갈린다는 것이다. 이는 if도 마찬가지로 모두 조건식에 비교 연산자를 대입하여 참과 거짓을 받아오거나 boolean값을 직접 입력하는 것으로 문장을 실행시킨다. 그러니 조건식의 자리엔 근본적으로 true와 false를 받는 공간이 되겠다. 위에서 말한대로 while문은 true를 받으면 무한 반복하게 된다, 여기서 빠져나오기 위해 이 true값을 false로 바꿔주는 작업이 필요한데 그것이 여의치 않다면 break를 쓰는 방법이 있다.

break 문 반복문 안에서 사용할 시 반복을 멈추고 해당 코드를 끝낸다.
contiune 문 반복을 일시적으로 멈춘다, 해당 반복문만 멈추고 다시 증감식으로 돌아가 반복을 진행한다.

 

   아직 배운 것이 부족해 더 세세한 사항을 배우지는 못하고 추후에 다시 이 포스터를 확인해야 할 일이 생길 것 같다.

 

   이렇듯 배열에 접근하는 방법을 반복문과 함께 알아 보았는데 단순 늘어놓는 것만 했던 배열변수는 대괄호를 추가하면 다차원의 배열로 만들 수도 있었다. 다차원은 대괄호를 추가한 만큼 2차원, 3차원... 계속 늘어나는 것 같지만 실제로 그렇게 많이 쓰이지는 않고 3차원 이상은 잘 쓰는 경우가 없다고 한다.

 

   그렇다면 2차원 배열의 사용법은 어떻게 될까, 처음에는 긴가민가 했지만 우리가 늘 친숙하게 사용하는 엑셀을 예로 들면 의외로 간단했다. 첫 번째 배열을 엑셀의 열이라 생각하고 두 번째 배열을 행으로 두면 계산하기 편했다. 아래는 그렇게 이미지 메이킹을 하고 작성하여 출력까지 해 본 구문이다.

   배열의 크기를 미리 주는 방법도 있겠지만 일단 초기화를 동시에 해주는 방향으로 작성해보았다. 스스로 4열 3행을 쓴다 생각하고 모든 값을 화면에 출력하는 반복문도 작성했는데 이해한 것이 맞았는지 출력하는 데 문제가 없었다. 일단 기본적으로 배열의 길이는 1차원일 때의 기준인 가로, 즉 열을 기준으로 잡는다. 나는 4열을 기준으로 작성하였으니 길이는 4, 행은 3으로 잡았기 때문에 해당 배열의 길이에서 -1한 값을 두번째 for문의 조건으로 삼았다. 이렇게 쓰는 for문을 중첩 for문이라고도 한다.

+ Recent posts