딸기말차
[Java] 3. static, final, 다중 클래스 본문
엔코아 플레이데이터(Encore Playdata) Backend 2기 백엔드 개발 부트캠프 (playdata.io)
백엔드 개발 부트캠프
백엔드 기초부터 배포까지! 매력있는 백엔드 개발자 포트폴리오를 완성하여 취업하세요.
playdata.io
1. 복습
수업을 시작하기 전 저번주에 수강한 변수, 배열, 조건 및 반복문, String, 클래스, 메서드를 전체적으로 복습하였다.
우선 변수를 관리함에 있어 데이터 타입과 데이터 타입 변환(형 변환)은 너무나 많이 사용하기 때문에, 그 종류에 관해 숙지해두기 위해 정리해 보았다.
* 기본 데이터 타입
* 정수 : byte(1) short(2) int(4) long(8)
* 실수 : float(4) double(8)
* 논리 : boolean
* 문자 : char(2)
* char a = 45; -> 문자는 유니코드 값이기 떄문에 이런식으로 사용가능하다.
* int t = a; -> char 타입을 int타입으로 알아서 바꿔버린 것. 암묵적 형변환에 속한다.
* int t = (int)a; -> 이런식으로 작성해야 명시적 형변환이 된다.
*
* 참조(클래스) 데이터 타입 -> 반드시 클래스가 존재하고 있어야한다.
* 문자열 : String
*
* 데이터 타입 변환 (형변환)
* up : 작은 타입이 큰 타입으로 변하는 것. ex) byte a = 10; short b = 20; -> b = a;
* down : 큰 타입이 작은 타입으로 변하는 것. ex) a = b;
* 명시적 : 암묵적보단 명시적을 사용하자.
* 암묵적 : 위의 b = a; 처럼 자바에게 알아서 변환하라고 시키는 것. 개발자가 직접 확인하며 하는 것이 아니기 때문에 위험한 형변환이다.
*
* MemberClass m1 = new MemberClass();
* Object m2 = new MemberClass(); -> MemberClass 라는 자식클래스를 부모인 Object 로 선언했기 때문에 up scale 이라 할 수 있다.
* MemberClass m3 = (MemberClass)m2(); -> 조상타입을 자식타입으로 강제로 변환하는 것. 명시적 형변환에 속한다.
그리고 웹 개발을 함에 있어 수많은 데이터가 문자열로 전송되기 때문에, 문자열을 다룰 줄 아는건 필수라 생각한다.
특히 파이썬같은 타 언어에선 문자열을 == 으로 비교가 가능하지만, Java에선 불가능하다는 점이나, 분명 String은 클래스인데 primitive type처럼 사용 가능한 이유 등 사소하지만 중요한 내용들을 정리해보았다.
* 문자열 다루기 : String.class -> 자바에서 제공해주는 클래스
* String s1 = new String("문자열"); -> 원래 이게 정식 코드지만, 문자열을 너무 많이 쓰다보니 String s2 = "문자열"; 이렇게 써도 되게했다.
*
* 문자열 비교 : s1.equals("비교 할 문자열")
* String s1 = new String("abcd");
* 이렇게 선언할 경우 "abcd"라는 문자열이 저장되어있는게 아니라 String으로 감싸진 객체가 저장된다.
* 하지만 브라우저 상에서 String input = "abcd" 라고 들어온다면, 이 부분은 순수 문자열로 들어올 가능성이 높다.
* 때문에 s1과 input을 == 으로 비교해버리면, false가 나올 확률이 굉장히 높아진다.
* 이 상황에서, equals로 비교를 한다면 String 객체를 뜯어내 순수 문자열만 비교하기 때문에 에러가 나지 않는다.
*
* 문자열 분리 : String.split("구분자")
* String text = "<html><head></head><body></body></html>";
* String[] splits = text.split("<body>") -> 이와같이 특정 단어로도 분리 가능하다. 분리 후 구분자 부분은 사라진다.
* splits[0] = "<html><head></head>", splits[1] = "</body></html>"
*
* String text = "문, 자, 열, 분, 리";
* String[] splits = text1.split(" ") -> 띄어쓰기 2개가 기준이 될 수 있다.
* splits[0] = "문, 자, 열," splits[1] = "분, 리"
*
* 문자열 추출 : String.subString()
* 1. 지정위치부터 마지막까지 : String.subString(startIndex)
* 2. 지정위치부터 지정위치까지 : String.subString(startIndex, endIndex)
*
* String text = "문자열의 일부분"
* String end = text.subString(5); // "일부분"
* String between = text.subString(2, 4); // "열의"
*
* String에 저장되는 문자열은 내부에 index 번호가 배열처럼 자동 부여된다.
Java를 사용해 개발을 함에 있어서, 클래스들 사이의 데이터 흐름을 따라가는건 필수라 할 수있다.
* 클래스의 종류
* 1. main()을 포함하는 클래스 : 하나의 프로젝트에 단 하나만 존재해야 한다. -> 프로젝트 실행 시 초기 진입점이기 때문
* 2. 일반 클래스 : main()을 포함하는 클래스의 일을 분리시키고자 사용한다.
* 1) 클래스 선언부
* 2) 변수(클래스 영역 변수)
* 3) 생성자 : 클래스를 new 연산자로 객체화 시킬 때, 자동 호출되는 메서드
* 4) 메서드 : 클래스 내 또는 다른 클래스에서 호출하여 사용되는 메서드
* 3. interface 클래스 : 객체의 유연성(데이터 타입)을 부여하기 위해 사용, 선언 시 'class' 키워드 대신 'interface'를 이용하여 선언
* 1) interface 선언부
* 2) 상수 : 한번 설정한 값을 변경하지 못하도록 선언된 변수
* 3) 추상메서드
* 4. 추상클래스 : 일반클래스와 interface의 기능을 묶어놓은 클래스, 'class' 키워드 앞에 추상을 뜻하는 'abstract'를 붙인다.
* 1) abstract class 키워드를 이용하여 클래스 선언
* 2) 변수(클래스변수)
* 3) 생성자 -> new로 객체 생성 불가능, 상속에서 사용되는 생성자이다.
* 4) 메서드
* 5) 추상클래스
위 설명의 interface는, backend 개발을 함에 있어서 절대 빠질 수 없는, 중요한 부분이다. interface가 존재하기 때문에 개발자들은 구현체 위주로 개발이 가능하고, 나아가 객체지향 프로그래밍의 핵심요소 중 하나인 다형성을 구현하는데 매우 중요한 역할을 하기 때문이다.
* 자바에서 생성되는 모든 클래스는 내부적으로 java.lang.Object.class를 이용(상속)하여 선언된다.
* 따라서 모든 클래스의 객체는 Object 타입으로 저장 가능하다.
* 단, 클래스 내부에서 선언된 변수, 메서드들은 사용 불가능하다. (생성자는 호출한다)
* Object 타입으로 저장받은 객체의 변수, 메서드들을 사용하려면 반드시 원래 타입으로 형변환을 해야한다.
* 이러한 단점을 해결하기 위해 사용하는 것이 interface이다.
클래스를 만들었으면, 우리는 new 키워드를 통해 객체를 생성하여 사용하게 된다. 때문에 객체의 선언부터 할당까지 어떠한 과정으로 만들어지고, 사용할 수 있는지 놓치지 않아야한다.
* 객체 생성 순서
* MemberClass m1 = new MemberClass()
* 1. new MemberClass(); 에서 객체 생성이 완료되어야 한다.
* 2. MemberClass와 동일한 이름을 가지는 클래스 파일이 존재하는지를 검색
* 3. 존재하면 MemberClass 내부에 선언할 수 있는 것은 모두 선언
* 4. 선언 완료 후 MemberClass 내부에 MemberClass() 생성자를 호출한다.
* 5. new 연산자를 이용하여 객체 생성을 완료한다.
* 6. 생성 완료된 객체를 동일 형태의 구조를 갖는 m1에 대입(할당)
이렇게 클래스를 만들고 객체를 만드는 것 까지 성공했다면, 이제는 여러 클래스들 사이에서 구현 된 메서드들을 이용한 데이터의 흐름을 놓치지 않아야한다. 때문에 수업 중 사용하는 클래스들을 어떤 순서로 흐름을 따라가며 구현하면 좋을지에 대해 간략히 정리해보았다.
* 1. 전체적인 구조 잡기
* 2. 메서드는 기본구조만 만들어 놓고, 추후 변경
* 3. 정보클래스(멤버클래스 같은 것)
* 4. 데이터클래스 : 정보클래스를 이용하여 전체 데이터 저장
* 5. MainClass에서 각 데이터가 제대로 들어갔는지 확인 및 각 메서드가 잘 호출되는지 확인
2. static 과 final
Java로 사용할 수 있는 메모리영역엔 대표적으로 stack 영역, heap 영역, static 영역이 존재한다.
1. Static 영역
- 프로그램의 시작부터 종료까지 메모리가 해제되지 않고 남아있음
- 때문에 어디서든 사용가능한 데이터지만, 무분별하게 사용하면 메모리 부족현상이 나타날 수 있다.
2. Heap 영역
- JVM이 관리하는, reference(참조) type의 데이터 타입을 갖는 객체나 배열 등이 저장되는 공간이다.
- 메모리의 호출이 끝나도 삭제되지 않고 유지되지만, 어떠한 참조도 없다면 JVM의 GC에 의해 메모리가 해제된다.
3. Stack 영역
- primitive 타입에 해당하는 지역변수, 매개변수 등이 저장되는 공간으로, 일반적으로 호출했을 때 메모리에 적재되고 호출이 종료되면 메모리에서 사라진다.
final은 초기 설정값을 변경하지 않아야하는 경우 사용하는 키워드이다.
/*
* 이 변수 앞에 static이 없다면 main 메서드 안에서 사용할 수 없다. main 메서드는 static 영역에 선언되어 있기 때문이다.
* static이 붙은 변수는 모든 객체의 공유 변수가 된다.
* 즉, static을 붙여두면 객체 선언을 안해도 되서 편리하지만, 공유변수가 되기 때문에 주의해서 사용해야한다.
*/
static String s = "aaaas";
static int a = 10;
static final int b = 100; // final : 초기 설정값을 변경하지 못하도록 막아주는 키워드
public static void main(String[] args) {
MemberClass m1 = new MemberClass();
Object m2 = new MemberClass();
MemberClass m3 = (MemberClass) m2; // 조상타입은 자식타입으로 못들어가기 때문에 형변환을 해야한다.
m1.a = 1000;
System.out.println(m1.a + " " + m3.a);
/*
* static 변수는 일반변수와 다르게 생성되는 위치가 다르다.
* static 변수는 객체 내부에 선언되는게 아니라 클래스 내부에 선언된다.
* 때문에 객체의 생성 필요없이 클래스 단에서 직접 접근이 가능하다.
*/
m1.b = 20000;
System.out.println(m1.b + " " + m3.b);
MemberClass.b = 123456; // 클래스 단에서 직접 static 변수에 접근
System.out.println(m1.b + " " + m3.b);
System.out.println(s);
a = 3300;
// b = 4400; // final이 붙은 변수는 초기에 한번 값을 지정하면 더이상 수정 불가능하다.(상수)
변수 뿐만아니라 메서드 또한 static으로 선언하여 객체를 생성하지 않고 다른 클래스에서 사용할 수 있다.
public static ArrayList<Integer> age(MemberClass[] members) {
ArrayList<Integer> ages = new ArrayList<Integer>();
// 주민번호 앞자리 중 2개, 뒷자리 중 2개를 뽑아 비교 및 계산
for (int i = 0; i < members.length; i++) {
int firstNo = Integer.parseInt(members[i].firstNo.substring(0, 2));
String lastNo = members[i].lastNo.substring(0, 1);
if (lastNo.equals("1") || lastNo.equals("2"))
ages.add(2023 - (1900 + firstNo));
else
ages.add(2023 - (2000 + firstNo));
}
return ages;
}
3. 다중 클래스 실습_1) 학생들의 성적 합 구하기
내주신 실습은 학생 데이터에서 모든 학생들의 성적을 합하는 것이었다. 이를 위해 StudentClass, DataClass, MethodClass, MainClass, PrintClass 총 5개의 클래스를 선언하였다.
우선 데이터를 객체 형태로 저장하기 위해선 class를 이용해야 하기 때문에, 우선 원하는 데이터 구조를 가진 StudentClass를 구성하였다.
/*
* 한 회원의 정보를 하나로 묶어주는 역할
* Model 역할
*/
public class StudentClass {
// 클래스 변수는 자동초기화 되긴 하지만, 위험할 수 있기 때문에 직접 초기화해주는게 좋다.
String sNo = null;
String firstNo = null;
String lastNo = null;
String sName = null;
int year = 0;
String local = null;
int grade = 0;
public StudentClass() {
}
public StudentClass(String sNo, String jumin, String sName, int year, String local, int grade) {
this.sNo = sNo;
String[] tmp = jumin.split("-");
this.firstNo = tmp[0];
this.lastNo = tmp[1];
this.sName = sName;
this.year = year;
this.local = local;
this.grade = grade;
}
}
StudentClass의 구조로 데이터를 저장하기 위해, DataClass에 StudentClass 배열을 만들고 데이터들을 저장했다. 금일 수업 중엔 강사님께서는 일반적인 Array를 사용하셨지만, 데이터를 관리하는데는 ArrayList가 더 효율적이기 때문에 개인실습은 ArrayList를 사용하였다.
import java.util.ArrayList;
/*
* DataClass : 모든 회원의 정보를 보유하는 역할
* 추후, MySQL로 변경
*/
public class DataClass {
ArrayList<StudentClass> students = new ArrayList<StudentClass>();
public DataClass() {
this.initStudent();
}
public void initStudent() {
this.students.add(new StudentClass("1292001", "900424-1825409", "김광식", 3, "서울", 92));
this.students.add(new StudentClass("1292002", "900305-1730021", "김정현", 3, "서울", 20));
this.students.add(new StudentClass("1292003", "891021-2308302", "김현정", 4, "대전", 55));
this.students.add(new StudentClass("1292301", "890902-2704012", "김현정", 2, "대구", 78));
this.students.add(new StudentClass("1292303", "910715-1524390", "박광수", 3, "광주", 54));
this.students.add(new StudentClass("1292305", "921011-1809003", "김우주", 4, "부산", 88));
this.students.add(new StudentClass("1292501", "900825-1506390", "박철수", 3, "대전", 73));
this.students.add(new StudentClass("1292502", "911011-1809003", "백태성", 3, "서울", 95));
}
}
이 후 저장된 학생들의 리스트에서 성적만 뽑아 전부 더해주는 로직이 필요하고, 이를 MethodClass에 구현하였다.
import java.util.ArrayList;
/*
* MethodClass : 프로젝트에 필요한 공통적 메소드들만 보유하는 클래스
*/
public class MethodClass {
public MethodClass() { // 기본 생성자
}
public static int sumGrade(ArrayList<StudentClass> students) {
int sum = 0;
for (StudentClass student : students)
sum += student.grade;
return sum;
}
}
남은 작업은 MainClass에서 만들어 둔 클래스 및 메서드를 호출해 로직을 실행시키고, PrintClass를 통해 출력하는 일이다.
우리는 데이터를 원하는 형태로 저장하기 위해 StudentClass를 만들었지만, 실제 저장을 위해 만든 클래스는 DataClass라는 것을 헷갈리지 않아야한다.
// 실제 데이터는 dataclass 객체 내의 MemberClass에 존재
DataClass dataClass = new DataClass();
int sum = MethodClass.sumGrade(dataClass.students);
PrintClass.prn(sum);
위 코드는 실제 데이터가 저장되어있는 DataClass 객체를 선언하고, 해당 데이터를 MethodClass에서 만든 로직에 인자로 넘겨 나온 결과를 최종적으로 PrintClass에 있는 출력문의 인자로 넘겨 출력을 하는 과정이다.
/*
* PrintClass : 출력에 해당하는 메서드들만 보유
* 추후, HTML과 같은 GUI, UI들로 대체
* View 역할
*/
public class PrintClass {
public PrintClass() { // 기본생성자
}
public static void prn(int sum) {
System.out.println("성적 합: " + sum);
}
}
4. 다중 클래스 실습_2) 할인율을 적용한 실판매가 구하기
2번째로 주신 실습은 책들의 출판사, 저자, 가격, 할인율 등이 저장 된 데이터에서 가격과 할인율을 이용해 해당 책의 실판매가를 구하는 실습이었다. 이를 위해 BookClass, DataClass, MethodClass, MainClass, PrintClass 총 5개의 클래스를 선언하였다.
1번 실습과 마찬가지로, 데이터를 원하는 형태로 저장하기 위한 BookClass를 먼저 구성해주었다.
public class BookClass {
String title = null; // 책 제목
String author = null; // 저자
String press = null; // 출판사
int price = 0; // 정가
String image = null; // 책표지
int dc = 0; // 할인율
public BookClass() {
}
public BookClass(String title, String author, String press, int price, String image, int dc) {
this.title = title;
this.author = author;
this.press = press;
this.price = price;
this.image = image;
this.dc = dc;
}
}
이 후 실제 데이터를 저장하기 위한 DataClass를 구성하였다. 마찬가지로 데이터를 관리하기 효율적인 ArrayList를 사용해 구성하였다.
import java.util.ArrayList;
public class DataClass {
ArrayList<BookClass> books = new ArrayList<BookClass>();
public DataClass() {
this.init();
}
public void init() {
books.add(new BookClass("DO it HTML 5 CSS 3", "고경희", "이지스퍼블리싱", 16800, "06365234.jpg", 20));
books.add(new BookClass("모던 웹 디자인을 위한 HTML5 CSS3 입문", "윤인성", "한빛미디어", 30000, "06992821.jpg", 21));
books.add(new BookClass("HTML5 CSS3와 함께하는 드림위버 CS6 무작정 따라하기", "고경희", "길벗", 24000, "07056591.jpg", 10));
books.add(new BookClass("Head First HTML5 Programming", "엘리자베스 롭슨, 에릭 프리먼", "한빛미디어", 34000, "06950729.jpg", 20));
books.add(new BookClass("만들면서 배우는 HTML5 CSS3 jQuery", "야무", "한빛미디어", 25000, "06837215.jpg", 23));
books.add(new BookClass("HTML5 CSS3", "양용석", "로드북", 25000, "06741081.jpg", 15));
books.add(new BookClass("HTML5 CSS3 WebGL로 재미있게 배우는 HTML5 게임 프로그래밍", "제이콥 세이드린", "제이펍", 30000, "06980245.jpg", 10));
books.add(new BookClass("HTML5 캔버스 완벽 가이드", "데이비드 기어리", "위키북스", 40000, "07085557.jpg", 10));
books.add(new BookClass("올인원웹실무가이드 HTML 5 CSS 3", "나인환 김은영 외 1명", "제우미디어", 28000, "06630397.jpg", 16));
books.add(new BookClass("HTML5와 CSS3로 작성하는 반응형 웹 디자인", "벤 프레인", "에이콘출판", 30000, "06983417.jpg", 20));
books.add(new BookClass("HTML5가 보이는 그림책","ANK Co Ltd", "성안당", 31500, "06992821.jpg", 21));
books.add(new BookClass("HTML5 Canvas", "스티브 펄튼, 제프 펄튼", "한빛미디어", 38000, "06806523.jpg", 20));
books.add(new BookClass("세르게이의 HTML5 CSS3 퀵 레퍼런스", "세르게이 마브로디", "제이펍", 20000, "07114432.jpg", 10));
books.add(new BookClass("이제 실전이다 HTML5 CSS3 사이트제작의 모든것", "양용석", "로드북", 25000, "06880352.jpg", 10));
books.add(new BookClass("iOS와 안드로이드를 위한 HTML5", "로빈 닉슨", "한빛미디어", 33000, "07041351.jpg", 10));
}
}
데이터가 준비되었으니 MethodClass에 로직을 구현해 실제 할인이 적용된 실판매가를 구해야한다.
사용한 기본로직은 실판매가 = 정가 - (정가 * 할인율)로, 좀 더 단순하게 구현하기 위해 실판매가 = 정가 * (100 - 할인율) * 0.01로 변경해 계산 결과를 바로 return해 주었다.
유의할 점은 메서드의 인자는 int형이지만 계산식엔 소수가 들어가기 때문에, 실수형으로 return을 해야한다는 점이다.
public class MethodClass {
public MethodClass() {
}
public static double realPriceMethod(int price, int dc) {
return price * (100 - dc) * 0.01;
}
}
로직 구현을 완료하였으니 MainClass에서 데이터 객체를 생성하고, 반복문을 통해 한 데이터의 정가, 할인율을 MethodClass로 넘겨 실판매가를 구한 결과를 PrintClass로 책의 제목과 함께 넘겨, 해당 책의 이름과 실판매가를 출력만 하면 된다.
public class MainClass {
public static void main(String[] args) {
DataClass dataClass = new DataClass();
for (BookClass book : dataClass.books)
PrintClass.prn(book.title, MethodClass.realPriceMethod(book.price, book.dc));
}
}
위 코드에서 일반적인 for 문이 아니라 for-each문을 사용하였는데, 직접 데이터를 수정하는 작업엔 일반적인 for문이 필요하겠지만, 단순 탐색 후 확인 혹은 전달을 하는 과정은 for-each문이 훨씬 효율적이고 코드도 간결하다고 생각했기 때문이다.
public class PrintClass {
public PrintClass() {
}
public static void prn(String title, double price) {
System.out.println("제목: " + title + " 실 판매가: " + price);
}
}
5. 3일차 후기
계속 생각하게 되지만 웹 개발, 특히 Back-end 개발에서 Java는 빼놓을 수 없고, 수많은 클래스들 사이에서 데이터의 흐름을 놓치지 않고 잘 추적하며 개발하는 것은 정말 쉽지 않은 것 같다. 실습을 진행하면서도 내가 만든 메서드인데 어디서 갖고왔는지 헷갈리는 경우도 있었고, 사용중인 메서드가 정말 그 클래스에 존재하는게 맞는지 수시로 확인을 했기 때문이다.
때문에 오늘 진행했던 실습 내용이 정말 중요하다고 느꼈다. 다행히 강사님께서 이번주 내내 이런 유형의 실습을 진행한다고 하셨으니, 클래스 간의 데이터 흐름을 놓치지 않게 확실히 훈련하여, 앞으로 배울 Spring 및 Spring boot를 진행할 때 막힘 없는 결과가 나오면 좋을 것 같다.
'Bootcamp > Java' 카테고리의 다른 글
[Java] 6. 상속, Interface, 추상 클래스 (0) | 2023.06.30 |
---|---|
[Java] 5. 파일 입출력, 다중 클래스 (0) | 2023.06.29 |
[Java] 4. ArrayList, 접근 제어자, 다중 클래스 (0) | 2023.06.28 |
[Java] 2. split, substring, 클래스, 메서드 (0) | 2023.06.25 |
[Java] 1. 변수, 배열, 조건 및 반복문 (0) | 2023.06.22 |