- 라이브러리 활용
- 객체 배열
오늘은 기본문법을 바탕으로 라이브러리를 실습해보는 시간을 가졌다.
드디어 뭔가 하는 것 같다는 느낌에 설레어 버리는 내 마음....:)
물론 엄청 세련되고 어려운 것을 만질 수는 능력이 딸리지만
그래도 차근 차근 하나씩 해나가면서 사람은 성장하는 것이다~
그럼 본론을 시작하자.
오늘 다룰 ‘라이브러리’는 다른 사람들이 만들어놓은 클래스들의 집합체이다.
단어 뜻 그대로 도서관의 책을 ‘지식의 설계도’ 우리가 꺼내서 보고 익혀서 응용하는 것이다.
그럼 내장 라이브러리부터 간단하게 시작하자.
public class Exam_01 {
public static void main(String[] args) throws Exception{
Robot r = new Robot();
처음 꺼낸 클래스는 Robot
이다. 그 뜻처럼 컴퓨터와 관련된 기능들이 내장되어 있다.
이제 해당 클래스의 기능과 속성을 이용하기 위해 객체를 생성하고 r
이라는 참조변수를 연결해주자.
그리고 기능 하나를 불러와 실행해보자.
for(int i = 0; i < 100; i++) {
r.mouseMove(500+i, 500);
r.delay(10);
}
내가 선택한 메서드는 좌표값을 입력받아 마우스 커서를 이동시키는 것과 실제 실행에 있어 지연을 주는 기능이다.
한 가지 배운 것은 반복문을 통해 다양하게 사용할 수 있다는 것이다.
이 점에서 코드를 짜는 실력도 중요하지만, 있는 걸 가지고 응용할 줄 아는 아이디어를 기르고, 다른 사람의 응용법도 배워야 한다는 걸 느꼈다.
이제 외부 라이브러리로 나가보자.
만약 우리가 음악을 재생하는 프로그램을 만들고 싶다고 하자.
하지만 이를 만들려면 인코딩 방식부터 하나, 하나 깊게 원리를 알아야 하는데, 이는 객체지향이 원하는 방식은 아니다.
따라서 우리가 인터넷에서 검색해서 필요한 설계도들을 찾아야 한다.
먼저 구글에 ‘java mp3 player’라고 쳐보자. 그럼 다양한 결과가 나오겠지만 아쉽게도
우리가 정확히 원하는 결과를 찾기는 어렵다. 이래서 검색하는 능력도 길러야 한다.
일단은 수업이기 때문에 강사님의 안내에 따라 jlayer
을 다운로드한다.
이때, 확장자가 .jar
인데 실행 파일일 때도 있지만 주로 라이브러리의 확장자로 쓰인다.
다운 받았으면, 사용법을 검색해서 코드를 한번 복사해보자.
근데 막상 코드를 복사하면 에러가 날 것이다. 이는 우리가 다운받은 라이브러리가 프로젝트 내부에 존재하지 않기 때문이다. 따라서 사용하는 프로젝트에 연결해주자.
연결하는 방법은 간단하다. 프로젝트를 우클릭하여 properties
/ java build path
/ Libraies
/ Add External JARS
에 기존에 다운받은 라이브러리를 추가해주면 해결된다.
(내장의 경우, ctrl + shit + O 하면 자동 연결)
"그럼 음악을 재생해볼까?"
라는 생각도 잠시, 이내 오류가 발생할 것이다.
이 역시 프로젝트 내부에 파일이 없기 때문이니, 이클립스에 파일을 붙여넣어주면 된다.
그럼 이제 원하는 음악이 재생이 된다.
물론 재생만 있을 뿐 중간의 멈춤은 없으니 주의하도록 하자.
그럼 우리의 코드는 어떻게 작동이 되는 걸까?
FileInputStream fis = new FileInputStream("music.mp3");
Player playMP3 = new Player(fis);
playMP3.play();
일단 불러온 클래스의 내부구조는 알 수가 없다. 확인해보면 바이너리 파일이라 읽지도 못한다.
따라서 배운 것을 기반으로 해석하자면 두 가지를 알 수 있다.
FileInputStream
와 Player
는 클래스명이다. 그 중 전자는 내부 라이브러리, 후자는 외부 라이브러리이다.
그래서 우리는 new
를 통해 객체를 생성하고 한다.
이때 Player 은 생성될 때 fis
를 통해 FileInputStream
의 인스턴스와 연결된다.
그리고 이 인스턴스는 프로젝트 내부의 동일 명칭의 mp3 파일을 입력받는다.
따라서 play()
라는 메서드는 FileInputStream
의 인스턴스를 이용해 재생하는 메서드이다.
지난 시간 Student
클래스를 만들고, 객체를 생성할 때 생성자를 이용해서 private
로 정보은닉된 변수에 데이터값을 입력해줬다.
public class Student {
private String name;
private int Kor;
private int Eng;
private int Math;
}
여기서 한 가지 의문이 든다.
만약 사용할 객체가 늘어난다면, 매번 생성자를 이용해 인스턴스를 생성하면 변수를 계속 만드는 거랑 별반 다르지 않나?
그렇다면 인스턴트도 배열을 이용할 수 있지 않을까?
이를 위해 먼저 각 데이터의 연결구조를 생각해보자.
다음은 Student
클래스를 이용해 인스턴스를 만든 코드이다.
Student tom = new Student("Tom", 90, 90, 70);
Student mike = new Student("mike", 80, 40, 50);
Student jane = new Student("jane", 50, 90, 70);
논리적으로 코드를 파헤치면 먼저 stack
메모리에 각 개체에 대한 주소값이 저장되어 있고, heap
의 인스턴스의 변수엔 생성자를 통해 입력한 값이 들어있다.
이때 new
뒤에 오는 모든 것은 주소다.
하지만 이 경우 앞서 설명한 것처럼 비효율적이다.
그럼 인스턴스를 가리키는 각 참조변수들 배열로 묶어보면 어떨까?
Student[] stds = new Student[]{tom, mike, jane}
이렇게 되면 stds
배열의 각 인덱스에 Student
인스턴스의 주소값이 담기게 되었다.
따라서 이제 인스턴스를 제어하고 접근할 수 있는 방식은 두 가지이다.
Stack
의 참조변수 3개를 통하는 방식Stack
의 배열 참조변수 stds
를 통해 인덱스로 접근하는 방식Student tom = new Student("Tom", 90, 90, 70);
Student mike = new Student("mike", 80, 40, 50);
Student jane = new Student("jane", 50, 90, 70);
Student[] stds = new Student[3];
stds[0] = tom;
stds[1] = mike;
stds[2] = jane;
그러나 굳이 참조변수 3개를 응용할 필요가 없다.
stds[0] = new Student("Tom", 90, 90, 70);
stds[1] = new Student("mike", 80, 40, 50);
stds[2] = new Student("jane", 50, 90, 70);
이렇게 사용하는 것이 훨씬 가독성도 높고 이해하기 편한데, 이를 객체 배열이라고 한다.
따라서 배열을 반복문으로 사용할 수 있다.
for (int i=0; i<stds.length; i++) {
System.out.println("이름 : " + stds[i].getName());
System.out.println("국어 : " + stds[i].getKor());
System.out.println("영어 : " + stds[i].getEng());
System.out.println("수학 : " + stds[i].getMath());
이렇게 반복문으로 정보은닉된 값들을 불러올 수 있다.
더 나아가 아예 생성자를 배열로 받을 수 있다.
잠시 Student
클래스의 명시 생성자를 다음과 같이 수정하자. 기존의 국어, 영어, 수학 점수를 아예 int[] score
라는 멤버필드를 만들자.
private int[] score;
public Student(String name, int[] score) {
}
이제 객체를 생성하면서 동시에 국어, 영어, 수학 점수를 배열로 입력해줄 수 있다.
int[] score = new int[]{90, 30, 20}
Student stds = new Student(“Tom”, score)
논리적으로 표현하면 다음과 같은 이미지가 된다.
즉, 객체를 생성하면서 만들어진 배열의 주소가 score
가 동시에 저장되는 것이다.
그래서 아예 객체를 생성하면서 동시에 배열까지 함께 생성해줄 수 있다.
Student stds1 = new Student("Tom", new int[]{90, 30, 20});
Student stds2 = new Student("mike" new int[]{80, 40, 50});
Student stds3 = new Student("jane", new int[]{50, 80, 70});
이렇게 3개의 인스턴스를 만들고 각 인스턴스마다 다른 배열의 주소를 입력해준다.
근데 여기선 배열의 참조변수를 설정하지 않았기 때문에 오직 객체를 통해서만 제어할 수 있고, 인스턴스 한 개당 배열 한 개씩 생긴다. stds.getScore[];
이것도 비효율적이다. 따라서 배열에 각 객체를 다시 연결해주자.
다만 이때는 배열 생성하면서 동시에 객체를 만들자.
이는 위와 반대로 배열에 객체를 묶는 것이다. 그래서 하나의 배열로 여러 개를 제어할 수 있다.
Student[] stds = new Student[]{
new Student("Tom", 90, 90, 70),
new Student("mike", 80, 40, 50),
new Student("jane", 50, 90, 70)
}