✅ 클래스로 묶인 변수 공유하기 |
public class Program{
public static void main(String[] args) {
Exam exam = null;// 3) 초기화 알람이 떠서 초기화를 하면 널로 처리됨. 공간이없음!
exam.kor = 30; // 1) 불가 > 2) 초기화진행
// ---------------------4) 이대로 실행하면 NullPointerException 이 뜬다
Exam exam1 = new Exam(); // 공간마련
exam1.kor=30; // 공간 내부에 있는 kor공간 사용
}
}
public class Exam {
int kor;
int eng;
int math;
}
👉 클래스에 변수를 묶어 사용할 때에는 메인메서드에서 객체를 꼭 새로 생성해준 후, 해당 객체의 변수로 사용해주어야 한다. (class Exam의 복제본이 exam1인 셈.)
👉 우리는 복제본인 exam1의 kor에 '값을 대입'하여 공유하고 사용한다.

✅ csv 파일 읽어와서 자료저장하기 (변수에 담긴 값 공유) |
public class Program0629 {
public static void main(String[] args) throws IOException {
{//성적로드
FileInputStream fis = new FileInputStream("res/ex09/exam.data");
Scanner fscan = new Scanner(fis);
fscan.nextLine(); // 첫줄날리기
// exam.kor = fscan.nextInt(); 콤마로 구분되어있어서 숫자만 읽어올 수 없다
String line = fscan.nextLine();
String[] tokens = line.split(","); // split하면 문자형 배열로 반환
}
{// 들어갈 객체 공간 생성
Exam exam = new Exam();
exam.kor = Integer.valueOf(tokens[0]); // 문자열을 int형식으로 변환 valueOf는 객체다
exam.eng = Integer.parseInt(tokens[1]);
exam.math = Integer.valueOf(tokens[2]);
fscan.close();
fis.close();
}
{// 성적 콘솔 출력
int size = index; // 의미에 맞게 대입해서 사용하는게 한눈에 더 잘 볼 수 있다.
for (int i = 0; i < size; i++) {
int kor = exam.kor;
int eng = exam.eng;
int math = exam.math;
int total = exam.total;
float avg = exam.avg;
System.out.printf("국어: %d, 영어: %d, 수학: %d\n", kor, eng, math);
}
}// main ends
}
👉 로드해와서 하나씩 자른 값을 ▶ Exam 클래스인 exam.kor, exam.eng, exam.math에 담아서 값을 공유하고 ▶ 공유된 값을 출력파트에서 지역변수에 담아 프린트에 사용한다.
✅ csv 파일 읽어와서 자료저장하기 (구조체 배열 활용하기) |
✨ 여기선 객체배열을 생성하고 배열에 객체를 하나씩 연결하여 객체 안의 값을 이용하는 연습을 한다. 참조변수와 연결될때는 값대입이 아닌 '참조대입'이라는 점을 생각하고 해당 이름이 어떤 객체를 가리키고 있는지를 항상 기억하기.
public class Program0629 {
public static void main(String[] args) throws IOException {
// 성적로드
Exam[] exams = new Exam[20]; // 구조체 배열 생성
int index = 0;
{
FileInputStream fis = new FileInputStream("res/ex09/exam.data");
Scanner fscan = new Scanner(fis);
fscan.nextLine(); // 첫줄날리기
// 반복해서 읽어와서 대입. hasNextLine()로 다음라인확인 (검증필요)
while (fscan.hasNextLine()) {
String line = fscan.nextLine();
String[] tokens = line.split(","); // split하면 문자형 배열로 반환
// 들어갈 객체 공간 생성
Exam exam = new Exam(); // 반복문안에 선언된 exam은 하나만 만들어지고 new Exam()만 새로 만들어진다
// 구조체 직접 연산
exam.kor = Integer.valueOf(tokens[0]);
exam.eng = Integer.parseInt(tokens[1]);
exam.math = Integer.valueOf(tokens[2]);
exam.total = exam.kor + exam.eng + exam.math; // 값대입을 해줘야 클래스에도 대입이된다.
exam.avg = exam.total / 3f;
// 지역변수를 이용해서 구조체연산을 좀더 보기쉽게 만들 수 있다
//int total = exam.kor+exam.eng+exam.math;
//exam.total = total;
exams[index] = exam; // 참조대입!!! 값대입과 분리해서 생각할것 exams[index++] = exam; 이렇게도 가능
index++;
}
fscan.close();
fis.close();
//=======================================================================
// 성적 콘솔 출력
{
int size = index;
for (int i = 0; i < size; i++) {
Exam exam = exams[i];
int kor = exam.kor;
int eng = exam.eng;/
int math = exam.math;
int total = exam.total;/
float avg = exam.avg;
System.out.printf("국어: %d, 영어: %d, 수학: %d, 총점: %d, 평균: %.2f\n", kor, eng, math, exam.total,
exam.avg);
}
}
👉 이해할때 가장 어려웠던 부분은 exams[index] = exam; index++; 이다. 메인함수안에 같이 있으니 인덱스는 ++; 하면 값이 하나씩 올라가는걸 알겠는데 exam객체를 왜 배열에 넣어주나...하다가 값대입이 아닌 변수대입임을 새삼 다시 깨달았다. 즉 생성된 exam객체에 exams[i]이름표를 공유함!
✅ 기존 코드에서 총점과 평균을 구해 출력할 때 실수했던 부분 |
내가 잘못 생각했던 코드 ▼
// 성적로드
Exam[] exams = new Exam[20];
int index = 0;
{
FileInputStream fis = new FileInputStream("res/ex09/exam.data");
Scanner fscan = new Scanner(fis);
fscan.nextLine(); // 첫줄날리기
while (fscan.hasNextLine()) {
String line = fscan.nextLine();
String[] tokens = line.split(",");
Exam exam = new Exam();
exam.kor = Integer.valueOf(tokens[0]);
exam.eng = Integer.parseInt(tokens[1]);
exam.math = Integer.valueOf(tokens[2]);
int total = exam.total;
float avg = exam.avg; // 출력시 총점과 평균 모두 0만 출력되었다
exams[index] = exam;
index++;
}
fscan.close();
fis.close();
======================
public class Exam {
int kor;
int eng;
int math;
int total= kor+eng+math;
float avg = (kor+eng+math)/3f;
}
👉 총점과 평균을 구하라기에, 일단 클래스에 total과 avg를 묶어서 사용해주면 될 것 같았고, 메인메서드에서 exam.kor, exam.eng, exam.math값이 Exam클래스로 넘어갈 것이므로, int total = kor+eng+math;로 선언해놓으면 내부에서 처리되어 exam.total을 가져다 쓸 수 있을줄 알았다.
그러나 계속 total, avg모두 0만 떴고..... 디버깅을 해도 구간을 건너뛰고 0이 뜨길래 대체 뭔가 싶어서 한참을 쳐다보다가, 프로그램은 메인'메서드'에서 시작한다는데서 유레카가 터졌다.
우리는 항상 메인메서드에서 연산을 했으니 연산주체가 메인메서드라는걸 눈치를 못채고 있었는데, 새로 선언된 메서드들은 메인메서드 밖에서 따로 돌아가니까 값을 받아서 지역변수를 가지고 조리하잖아...? 메인 밖에 있으면서도 이게 가능한 이유는 그나마 연산이 가능한 '메서드'라서 그랬던 거고, 지금은 메인메서드고 일반메서드고 뭣도 없는, 그러니까 실행기능 없이 선언만 되어있는 클래스에서 연산을 하려 했으니 당연히 자동으로 연산이 될 리가 없었다. 메서드를 연결해 쓰는것처럼 클래스를 연결해 쓴다고 생각하니 자동연산이 된다고 생각했나보다...
만약 내가 원하는 방향대로 해당 클래스에서 자동으로 연산이 되도록 만들고 싶다면, Exam class 안에 메서드를 선언해주면 된다. 아래와 같이 코드를 고치면 됨! (학원에선 생각이 안났는데 지금 이 글을 쓰면서 메서드를 써보니 된다. 캬캬캬...🤣😁🤣😁🤣😁🤣😁 아니 이거 너무 당연한건데 그땐 왜 그랬지...?)
public class Exam {
int kor;
int eng;
int math;
int total() { // 합계를 구하는 메서드 생성
return kor+eng+math;
}
// int total= kor+eng+math; 이렇게 쓰는건 연산이 일어나지 않음
// float avg = (kor+eng+math)/3f;
}
while (fscan.hasNextLine()) {
String line = fscan.nextLine();
String[] tokens = line.split(",");
// 들어갈 객체 공간 생성
Exam exam = new Exam();
exam.kor = Integer.valueOf(tokens[0]);
exam.eng = Integer.parseInt(tokens[1]);
exam.math = Integer.valueOf(tokens[2]);
int total = exam.total(); // exam.total()의 메서드를 소환해서 변수에 대입해서 활용
System.out.printf("total:%d\n", total); // total 값 출력
exams[index] = exam; // 참조대입!!! 값대입과 분리해서 생각할것
index++;
}
하지만 메서드를 쓰지 않는다면 아래와 같이 직접 exam.kor+exam.eng+exam.math로 계산하고 exam.total에 값대입시켜주면 된다.
exam.kor = Integer.valueOf(tokens[0]);
exam.eng = Integer.parseInt(tokens[1]);
exam.math = Integer.valueOf(tokens[2]);
exam.total = exam.kor + exam.eng + exam.math; // 값대입을 해줘야 클래스에도 대입이되어 공유된다
exam.avg = exam.total / 3f;
// 지역변수를 이용해서 구조체연산을 좀더 보기쉽게 만들 수 있다
int total = exam.kor+exam.eng+exam.math;
exam.total = total;
✅ 구조체 배열 만들때 실수한 부분 (복습하며 생성한 코드 첨부)
// 잘못된 코드
public static void main(String[] args) throws FileNotFoundException {
// ExamList [] list = new Exam[13]; // EcamList클래스로 옮겨감
// int index = 0;
ExamList examList = new ExamList();
loadScore(examList); //이 메서드에서 객체와 연결하려고 하면 오류가 뜸
Exam exam = new Exam();
examList.list = exam; // 형식 불일치로 오류 뜸
printScore(examList);
}
//=====================================================
// ExamList 클래스
public class ExamList {
Exam[] list = new Exam[13];
int index = 0;
}
👉 복습을 하면서 내가 자꾸 헷갈려하는 부분을 알아냈다.
Exam객체가 들어갈 배열을 만들어야 한다는건 아는데, 배열이라는 생각때문에 자꾸 ExamList[] list = new ExamList[13]; 이렇게 선언한다. 그리고 ExamList 클래스를 만들어서 배열과 인덱스를 함께 붙여넣기 했는데, list를 사용하려고 했더니 exam객체와 연결되는 부분인 examList.list = exam; 이 자꾸 불일치로 뜨는거다... 분명히 배열 객체를 만들어서 넣는건데 왜 불일치일까, 한참 생각했는데 내가 타입을 잘못 선언했다는걸 깨달았다.
int[ ] arr = int a; |
int 타입 배열에는 int값이 들어갈 수 있듯이, 배열에 들어가려면 같은 형식을 지니고 있어야 한다.
따라서 배열은 객체인 Exam과 동일한 Exam[ ] 으로 선언해줘야 했다 (ExamList[ ] 라서 틀림)
Exam[ ] 과 index가 들어가는 클래스명은 ExamList여도 됨! 하지만 그 안에 선언된 배열은 넣으려는 객체와 같은 형식이어야 한다.
// 올바른 코드
public static void main(String[] args) throws FileNotFoundException {
// Exam[] list = new Exam[13]; // int[] arr=int a가 들어갈 수 있듯이 Exam객체가 들어가는 배열은 Exam[]
// int index = 0;
ExamList examList = new ExamList(); // 여기는 객체 배열의 객체를 선언하므로 클래스명이 타입이 됨
loadScore(examList);
// 성적 로드 코드중 일부
while (fscan.hasNext()) {
String[] lineArr = fscan.nextLine().split(",");
Exam exam = new Exam(); // 객체 생성
// 객체에 값 넣어주기
exam.kor = Integer.valueOf(lineArr[0]);
exam.eng = Integer.valueOf(lineArr[1]);
exam.math = Integer.valueOf(lineArr[2]);
// 객체 배열에 객체 연결하기
examList.list[examList.index] = exam; // examList.list의 타입이 Exam이므로 Exam객체가 들어갈 수 있다
// 인덱스 올리기
examList.index++;
}
printScore(examList);
}
'😵 ~23.11.10' 카테고리의 다른 글
0703 | 함수 오버로드 / 재귀함수 / 배포 코드 재사용 및 라이브러리 생성하여 ClassPath에 적용하기 (0) | 2023.07.03 |
---|---|
0630 | 연습 문제 > 객체 배열과 객체 내 변수를 활용하기 (0) | 2023.07.01 |
0628 | 전역변수 개념 익히기 / 클래스 변수를 선언하여 공유하기 (2) | 2023.06.30 |
0627 | 메서드 만들고 호출하기 (0) | 2023.06.27 |
0626 | 이차배열의 톱니형 배열 구조 / 변수의 생명주기 (0) | 2023.06.27 |