# Main
package com.ja.stm.main;
import com.ja.stm.component.Wrapper;
public class Main { // 1)
public static void main(String[] args) {
// Wrapper w = new Wrapper();
// w.run(); ---> 변수가 필요할 때.
new Wrapper().run(); // --> 실행 시켜야 할 때.
}
}
# IoUtil
package com.ja.stm.util;
import java.util.Scanner;
public class IoUtil { // (--> input. output. util)
private static Scanner scanner = new Scanner(System.in);
// 4) 자주사용할 코드. 매서드마다 매번 부르기 부담있었지?? 위로 올려서 인스턴스로 생성해주자!
// 근데 밑에가 static이면 인스턴스는 못쓰니까 얘도 static붙여주기
public static void println(String text) {
// 2) syso를 바깥에서 쓰지말고, 이 매서드를 사용해야하겠지
// 2-1) static 변수는 같은 패키지 내에서 어디에서나 접근할 수 있음
// 2-3) 패키지 외부에서 static 변수에 접근하려면 해당 변수를 public으로 선언해야겠지
System.out.println(text);
}
public static String input(String text) {
System.out.print(text);
String input = scanner.nextLine();
return input;
// 3)
// return scanner.nextLine(); 으로 써도됨. 이렇게 쓸 수 있도록 하자!
// scanner.nextLine(); --> 이거 자체가 String이니까.
}
public static void pause() { // 3-1)
System.out.print("계속하시려면 enter를 입력하세요 > ");
scanner.nextLine();
}
# Wrapper
public class Wrapper {
// public void test(){
// IoUtil.println("안녕"); // syso용 메서드 호출
// String input = IoUtil.input("입력 > "); // input용 메서드 호출 -> input값을 return해주는 애.
// IoUtil.pause(); // enter치면 종료? 돌아가는? 메서드 호출
//
// System.out.println(input); ---> run의 String input변수는 그 return값을 출력한다
// }
private CommandHandler commandHandler = new CommandHandler();
// 11) case마다 --> new CommandHandler().addStudentInfo();
// 이렇게 써도 실행은! 되겠지만..... case를 나가는 순간 메모리가 소멸된다.
// ★★★ 그니까 맨위로 올려서 인스턴스 변수로 지정해줘야함 ★★★. class명과 똑같이 쓰되, 맨앞은 소문자!
// private CommandHandler commandHandler; --> 이렇게만 두면 null값으로, exception error난다.
// Wrapper를 호출하는 순간 commandHandler 인스턴스 변수가 생성된다. (강한 관계)
// commandHandler는 Wrapper의 주소값을 받는다.
public void run() {
printWelcomeMessage(); // 5-1)
while(true) { // 7) 뼈대를 만들자
printCommandMenu(); // 8-1)
String input = IoUtil.input("사용하려는 메뉴의 번호를 입력하세요 > ");
if(input.equals("q")) break; // 8-2) 중괄호 하나면 굳이 안써도 되긴함.
switch(input) { // 9) if문인데, 웬만하면 switch case는 일반적으로 잘사용하지 않는다!
case "1":
commandHandler.addStudentInfo();
break;
case "2":
commandHandler.printStudentList();
break;
case "3":
commandHandler.searchStudentInfo();
break;
case "4":
commandHandler.deleteStudentInfo();
break;
case "5":
commandHandler.printStatistics();
break;
default:
IoUtil.println("잘못된 입력입니다. 다시 입력하세요"); // 9-1)
}
IoUtil.pause(); // 9-2) 이건 매번 나와야하는 입력이니까 case 바깥에 있어야겠지?
}
printByeMessage(); // 6-1)
}
private void printCommandMenu() { // 8)
IoUtil.println("==== MENU ====");
IoUtil.println("1. 정보 입력");
IoUtil.println("2. 리스트 출력");
IoUtil.println("3. 정보 검색");
IoUtil.println("4. 정보 삭제");
IoUtil.println("5. 점수 통계");
IoUtil.println("q. 프로그램 종료");
}
private void printWelcomeMessage() { // 5)
IoUtil.println("===== 학생정보 관리 프로그램 V2 =====");
}
private void printByeMessage() {
// 6) scnner.close();는 굳이굳이 쓰려면 쓸수는있짐나 여기서는 안해도 될듯여
IoUtil.println(" ");
IoUtil.println("프로그램을 이용해주셔서 감사합니다");
IoUtil.println("=== 프로그램이 종료됩니다 ===");
// Scanner.close(); --> "cannot make a static reference to the non-static method"
}
# Command Handler
public class CommandHandler { // 10) 각 번호에 해당하는 메서드를 만드는 곳, 뼈대 먼저 만들기
private DataModel dataModel = new DataModel();
// 15) datamodel 클래스가 commandhandler에 완전히 종속되도록!
public void addStudentInfo() { // 12) 메서드 내용 잡기
IoUtil.println("=== 정보득록(현재 입력된 학생 수 : " + dataModel.getCount() + ") ===");
String name = IoUtil.input("이름을 입력하세요 > ");
int age = Integer.parseInt(IoUtil.input("나이를 입력하세요 > "));
int score = Integer.parseInt(IoUtil.input("점수를 입력하세요 > "));
StudentDto student = new StudentDto(name, age, score);
// 14) 생성자 선언을 해주고.... 이제 배열을 만들어야하는데 어디다가 할까?
// --> array관리하는 클래스를 또 만들어야겠찌?
dataModel.add(student); // 15-1)
IoUtil.println("학생 정보가 등록되었습니다");
}
public void printStudentList() {
IoUtil.println("=== 학생정보 리스트 출력 ==="); // 17)
// int count = dataModel.getCount(); // 17-9) 이제 count변수를 굳이 안써도된다!
StudentDto[] list = dataModel.getList();
// list[0] = null;
--> 17-2) 누군가 이런식으로 실수를 할 경우? 원본 studentList가 손상된다.
// 17-5) list[0] = null; 을 해도 원본은 유지 되겠지.
// list[1].setAge(70);
--> 17-6) 또!!! 원복이 손상되는 코드를 써놨다?
// (cloneList를 한번만 따라가면 원본에 도달하니까 원본손상이 쉽다)
// for(int x = 0 ; x < count ; x++) { --> 18)이제 이거말고 foreach쓰자
for(StudentDto studentDto : list) {
// StudentDto studentDto = list[x]; 18-1) 이것도 필요 x
String text = "이름 : " + studentDto.getName();
// 17-1) +++써서 길~게 썼던거. += 가능하다!!! ★
text += ", / 나이 : " + studentDto.getAge();
text += ", / 점수 : " + studentDto.getScore();
IoUtil.println(text);
}
IoUtil.println("정보 입력된 학생 수 : " + list.length); // 18-2) count대신 length쓰면 되겠지?
}
public void searchStudentInfo() { // 19)
IoUtil.println("=== 학생 정보 검색 ===");
String search = IoUtil.input("검색할 학생의 이름을 입력하세요 > ");
StudentDto[] list = dataModel.getList(); // 19-1) 깊은 복사했던거 끌고오기
int searchCount = 0;
for(StudentDto studentDto : list) {
if(!studentDto.getName().contains(search)) continue;
// 19-2) 동일한 내용이 없으면 계속 반복 돌기, 있으면 출력.
String text = "이름 : " + studentDto.getName();
text += ", / 나이 : " + studentDto.getAge();
text += ", / 점수 : " + studentDto.getScore();
IoUtil.println(text);
searchCount++;
}
IoUtil.println("검색 된 학생 수 : " + searchCount);
}
public void deleteStudentInfo() {
IoUtil.println("=== 학생 정보 삭제 ===");
// 20) 복사본을 삭제하면 의미가 없겠찌? 원본을 복사하도록하자
--> 그걸 수행할 메소드 만들러가기~
String delete = IoUtil.input("삭제할 학생의 이름을 입력하세요 > ");
StudentDto[] list = dataModel.getList();
// ++ 삭제할 학생이름을 검색했어? 뭐가 삭제되는지 보여주고싶어.
for(StudentDto studentDto : list) {
if(!studentDto.getName().equals(delete)) continue;
// ++ 검색 했던 그 코드 그대로 복붙해옴!!!
(count만 delete로 바꿔주면되겠지)
String text = "이름 : " + studentDto.getName();
text += ", / 나이 : " + studentDto.getAge();
text += ", / 점수 : " + studentDto.getScore();
IoUtil.println(text);
}
int count = dataModel.remove(delete);
// 20-4) return한 값을 변수로 받아
IoUtil.println(count + "명의 학생 정보가 삭제되었습니다");
}
public void printStatistics() {
IoUtil.println("=== 통 계 ==="); // 21) 합, 평균
StudentDto[] list = dataModel.getList();
int sum = 0;
for(StudentDto studentDto : list) {
sum += studentDto.getScore();
}
double average = sum/(double)list.length;
IoUtil.println("총 인원 : " + list.length);
IoUtil.println("평균 점수 : " + average);
}
# Student Dto
package com.ja.stm.dto;
// DTO, VO : // 13)
// 개념적 데이터 구조체로서의 클래스.
// 일반적으로 필드(인스턴스 변수 생성하는 곳)가 중요하다!
public class StudentDto {
// Data transfer object : 이름, 나이, 성적을 한번에 묶을 class
private String name;
private int age;
private int score;
public StudentDto() {
// 13-1) 기본 생성자는 안쓰더라도 만들어주자
}
public StudentDto(String name, int age, int score) {
// 13-2) 모든 인스턴스를 포함한 생성자도 기본!!!
this.name = name;
this.age = age;
this.score = score;
}
public void setName(String name) { // 13-3) setter, getter
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setScore(int score) {
this.score = score;
}
public int getScore() {
return score;
}
}
# Data Model
public class DataModel { // 14-1) array를 관리하는 클래스.
private StudentDto[] studentList = new StudentDto[10];
// --> 밑에다 넣으면 메모리의 life cycle이 매우 짧아지므로, 멤버로 승격시켜줘. (어차피 계~속 쓰는거라 계속 new하는것도 일이야.)
// --> StudentDto의 인스턴스가 10개 생기는게 아님!!! (이건 new.. 를 10개 만들어야함)
// --> StudentDto 주소값을 다룰 수 있는 참조주소가 10개 생긴거임 (= null 10개)
private int count;
public void add(StudentDto studentDto) { // 14-2) studentDto의 참조주소를 받을거야
// StudentDto student1 = new StudentDto("학생1", 20, 90);
// StudentDto student2 = new StudentDto("학생2", 21, 85);
// StudentDto student3 = new StudentDto("학생3", 22, 88);
//
// arrangeInfo(student1);
// arrangeInfo(student2);
// arrangeInfo(student3);
// for(int x = 0 ; x < count ; x++){
// studentList[x] = studentDto;
// }
// --> 이건안돼. studentDto 객체를 모든 인덱스에 동일한 내용으로 덮어쓰게 되잖아.
// for 루프는 여러 개의 학생 정보를 studentList 배열에 추가하는 방법을 나타냄
// 즉, '여러 학생 정보를 한 번에 처리하고 배열에 저장'하는 루프
studentList[count] = studentDto;
count++;
// 14-3) 한줄의 정보를 입력받으면 그 정보가 List[0]에 들어갈거야
// count를 계속 쌓을 수 있겠지
// studentDto 객체를 studentList 배열의 다음 위치에 저장하고 count를 증가시킴
// 이 메서드는 학생 '정보를 하나씩 studentList 배열에 추가'하는 역할을 함
if(studentList.length >= count) {
// == 이 아주 정확한 표현이긴하지만, 버그가 무섭잖아?
StudentDto[] newList = new StudentDto[studentList.length *2];
for(int x = 0 ; x < count ; x++) {
newList[x] = studentList[x]; // 14-4) 배열길이를 늘리고, 내용을 복사하는 과정
}
studentList = newList; // 14-5) 복사는했으니, 옛날 USB는 버려야겠지
}
}
public StudentDto[] getList() {
// 16) StudentDto의 배열(= 참조주소를)을 return하는것 ★★★★
// --> StudnetDto가 아니라, 배열의 참조주소를 return하는것을 잊지마요오오
// --> list를 return을 하고싶으니까, StudentDto[]를 타입으로 써야지?
// return studentList; // 17-3) 원본은 그대로 남도록, 복사본을 만들어서 list를 return할거야
StudentDto[] cloneList = new StudentDto[count];
// for(int x = 0 ; x < count ; x++) {
// 17-4) (light하게)복사하는 과정(있는것만 복사하면되니까 그냥 count까지만 복사하자)
//
// cloneList[x] = studentList[x];
// }
// ---> 즉, cloneList를 한번만 따라가면 원본에 도달하니까 원본손상이 쉽다는 얘기지?
//
// return cloneList;
// 17-7) deep하게 복사하자 (깊은 복사 과정....)
// --> list만 복사되는게아니라, StudentDto에 입력받은 값 자체가 복사되야한다는거!
for(int x = 0 ; x < count ; x++) {
StudentDto newStudentDto = new StudentDto
(
studentList[x].getName(),
studentList[x].getAge(),
studentList[x].getScore()
);
cloneList[x] = newStudentDto;
}
return cloneList; // 17-8) 전체를 복사한 list를 return.....
}
public int remove(String name) {
// 20-1) return으로 몇명지웠는지 돌려주기위해 int타입으로 메서드를 만들자
int removeCount = 0;
for(int x = 0 ; x < count ; x++) {
StudentDto studentDto = studentList[x];
if(studentDto.getName().equals(name)) {
// 20-2) x위치부터 정보를 밀어넣자!(= 삭제)
for(int y = x ; y < count -1 ; y++) {
studentList[y] = studentList[y+1];
// 20-3) [0]의 이름을 삭제? [0] = [1] 이걸 반복해서 [0]을 밀어내자.★★★
}
count--;
x--;
removeCount++;
}
}
return removeCount;
}
'JAVA > DAY 14 _ 23.09.05' 카테고리의 다른 글
Debug mode (0) | 2023.09.11 |
---|