Resource Links
- 강의 자료 :
Chapter2 · master · easyspubjava / javacoursework · GitLab
1. What I learned today
Part 2. 객체지향 프로그래밍
객체와 객체지향 프로그래밍
객체(Object) : 구체적, 추상적 데이터의 단위
<aside> ✅ 객체 지향 프로그램 vs 절차 지향 프로그래밍
</aside>
객체 지향 프로그래밍 : 객체들 사이의 interaction 위주로 프로그래밍 ⇒ 실생활에 잘 반영 가능
구현 방법
- 객체를 정의하고
- 객체가 제공하는 기능을 구현하고
- 객체가 제공하는 기능들간의 소통을 통해 협력 구현
절차 지향 프로그래밍 : 시간이나 사건의 흐름에 따른 프로그래밍
생활 속에서 객체 찾아 클래스로 구현하기
- 객체 찾기
- 온라인 쇼핑몰에 회원 로그인을 하고 여러 판매자가 판매하고 있는 제품 중 하나를 골라 주문을 한다
<aside> ❕ 보이지 않는 객체도 존재할 수 있다 (추론해야함)
</aside>
- 클래스는 객체의 청사진이다.
public class Student{ // 접근 제어자 : public
int id; // 멤버 변수
String name;
int grade;
}
✔️ 속성을 꺼내는 것이 쉬운 것은 아님
✔️ 패키지 이름 : 소문자 / 클래스 이름: 대문자 / 변수, 메서드 : 카멜 notation (ex : studentName)
함수와 메서드
메서드는 함수의 종류 중 하나
함수
- 하나의 기능을 수행하는 일련의 코드, 호출해서 사용 가능
- ⇒ 공통의 기능을 구현하여 코드 길이를 줄이고 수정하기 쉽다.
int add(int n1, int n2){ // 반환값, 매개변수, 인자
// 함수 몸체
int result = n1+n2;
return result;
}
- 클래스를 만들고 함수 실행시키기
메서드 (= 멤버 함수)
클래스 내부에 구현되는 함수
<aside> 💡 함수는 단독 모듈이지만, 메서드는 클래스 안에 속해있고 멤버변수를 이용하여 구현한다.
</aside>
Student.java
public class Student {
// 멤버 변수
public int studentID;
public String studentName;
public String address;
// 메서드
public void showStudentId() {
System.out.println("studentID = " + studentID);
System.out.println("studentName = " + studentName);
System.out.println("address = " + address);
}
// getter
public String getStudentName() {
return studentName;
}
// setter
public void setStudentName(String name) {
studentName = name;
}
}
Main.java - 테스트
public class Main {
public static void main(String[] args) {
Student student = new Student(); // 인스턴스
// 값 세팅
student.studentID=12345;
student.setStudentName("kim");
student.address = "광주 광산구";
// 메서드 호출
student.showStudentInfo();
}
}
<aside> 🧩 인스턴스 : 클래스를 기반으로 생성되는 객체 (new 키워드 이용)
</aside>
- 동적 메모리 : Heap vs Stack
Heap - new로 생성된 인스턴스는 heap에 할당, 할당된 메모리는 해제 ( C-free(), Java-Garbage Collector 수거)
Stack - 함수와 지역변수 위치, 함수 수행이 끝나면 반환
생성자(Constructor)
- 객체를 생성하기위해 new와 함께 호출됨
- 반환값이 없고 클래스의 이름과 동일하다
기본 생성자 : “ 클래스에 생성자가 없는 경우에 “ 컴파일러가 생성자 코드를 넣어준다. 매개변수와 구현부가 없다.
생성자 : 객체 생성시 호출되어 객체 속성을 지정할 수 있다. ( 여러개 생성가능 )
<aside> ❕ 멤버변수는 선언 후 값을 지정하지 않고 출력했을때 기본값이 출력되지만, 지역변수는 선언 후 값을 지정하지 않고 출력하면 오류를 발생시킨다.
</aside>
ch08. 복습해봅시다 🎃
- 키가 180, 몸무게 78kg 남성, 이름은 Tomas, 나이는 37세 객체 만들기
Person.java
public class Person {
int height;
int weight;
String name;
int age;
public Person(int height, int weight, String name, int age) {
this.height = height;
this.weight = weight;
this.name = name;
this.age = age;
}
public void showInfo() {
System.out.println("height = " + height);
System.out.println("weight = " + weight);
System.out.println("name = " + name);
System.out.println("age = " + age);
}
}
Main.java
public class Main {
public static void main(String[] args) {
Person person = new Person(180, 78, "Tomas", 37);
person.showInfo();
}
}
출력화면
- 음식점 배달 주문
Order.java
public class Order {
double orderId;
Long phoneNum;
String address;
int orderDate; //주문 날짜 20230414
Long orderTime;
int price;
int menuNum;
public Order(double orderId, Long phoneNum, String address, int orderDate, Long orderTime, int price, int menuNum) {
this.orderId = orderId;
this.phoneNum = phoneNum;
this.address = address;
this.orderDate = orderDate;
this.orderTime = orderTime;
this.price = price;
this.menuNum = menuNum;
}
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", phoneNum=" + phoneNum +
", address='" + address + '\\'' +
", orderDate=" + orderDate +
", orderTime=" + orderTime +
", price=" + price +
", menuNum=" + menuNum +
'}';
}
}
Main.java
public class Main {
public static void main(String[] args) {
Order order = new Order(202011020003l, 01012341234l, "광주 광역시 OO동 111-222", 20230414, 123456l, 35000, 0003);
System.out.println("order = " + order);
}
}
출력화면
고민 지점 🤔
- 날짜를 저장할 더 좋은 구조? (Date와 Calender? Time)
- Date 타입 설명:
- phoneNum의 경우에 21억을 넘어가 long을 사용할 수 밖에 없었는데 int와 long 사이의 자료형을 사용할 수 있을까? (메모리를 더 아낄 수 있을까?) ⇒ int를 넘어가는 변수들은 그냥 long을 사용하는 것 같다.
- java int. long 데이터 유형 : https://www.delftstack.com/ko/howto/java/long-vs-int-java/
참조 자료형 변수
기본 자료형 : int,long,float,double
참조 자료형 : String, Date, Student (해당 변수에 대해 생성해야함)
접근 제어 지시자와 정보 은닉(information hiding)
접근 제어 지시자(access modifier)
- private: 같은 클래스 내부에서만 접근 가능 (외부 X, 상속 X)
- 수정할 수 있는 메서드를 public으로 제공
- 멤버변수를 바로 접근하는 것을 막고, 메서드에서 접근하여 잘못된 값이 입력되는 오류를 막을 수 있다.
- default: 같은 패키지 내부에서만 접근 가능 (상속관계이어도 다른 패키지이면 접근 X)
- protected : 같은 패키지나 상속관계의 클래스에서 접근가능
- public : 클래스 외부 어디서나 접근가능
BirthDay.java
public class BirthDay {
private int day;
private int month;
private int year;
private boolean isValid; // 기본값 false
//getter and setter 메서드
public int getDay() {
return day;
}
public void setDay(int day) {
if(month<1 || month>31) isValid = false; //유효하지 않은 날짜
else {
isValid = true;
this.day = day;
}
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
if(month<1 || month>12) isValid = false;
else {
isValid = true;
this.month = month;
}
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public void showDate(){
if (isValid) { // 유효한 날짜이면
System.out.println("year = " + year);
System.out.println("month = " + month);
System.out.println("day = " + day);
}
else System.out.println("유효하지 않은 날짜입니다.");
}
}
Main.java
public class Main {
public static void main(String[] args) {
BirthDay date = new BirthDay();
date.setDay(2019);
date.setMonth(13); // isValie=false => 유효하지않은 날짜입니다.
date.setDay(29);
}
}
캡슐화(encapsulation)
- 꼭 필요한 정보와 기능만 외부에 오픈
- 외부에 통합된 인터페이스만을 제공하여 일관된 기능을 구현하게 함
Report.java
public class Report{
private void makeHeader(){} // 굳이 오픈할 필요없는 메서드는 private
private void generateBody(){}
private void makeFooter(){}
public void makeReport(){ // 클라이언트 접근 메서드(public)
makeHeader();
generateBody();
makeFooter();
}
}
Main.java
public class Main {
public static void main(String[] args) {
Report report = new Report();
report.makeReport();
}
}
<aside> 🧩 메서드와 속성을 어떻게 공개하는가 ⇒ 객체 관리, 사용 시 편리함과 연관
</aside>
객체 자신을 가리키는 this
- 인스턴스 자신의 메모리 가리킴
- 생성자에서 또다른 생성자를 호출할때 사용 (this)
- 자신의 주소(참조값) 반환
Person.java
public class Person {
int height;
int weight;
String name;
int age;
public Person(){
// height=178; 아직 인스턴스 생성 전이므로 오류 발생
this(178, 34, "kim", 25); // 다른 생성자 호출
}
public Person(int height, int weight, String name, int age) {
this.height = height;
this.weight = weight;
this.name = name;
this.age = age;
}
public Person getPerson(){
return this; //객체 반환
}
public void showInfo() {
System.out.println("height = " + height);
System.out.println("weight = " + weight);
System.out.println("name = " + name);
System.out.println("age = " + age);
}
}
Main.java
public class Main {
public static void main(String[] args) {
Person p1 = new Person(); // 객체 인스턴스 생성
p1.showInfo();
System.out.println("p1 = " + p1);
Person p2 = p1.getPerson(); // 만들었던 인스턴스 대입
System.out.println("p2 = " + p2); // p1과 같은 인스턴스
}
}
출력결과
✔️ 인스턴스 출력 결과 : 클래스이름@인스턴스주소
객체간의 협력(collaboration)
- 협력을 위해서는 필요한 메세지를 전송하고 이를 처리하는 기능이 구현되어야 함
- 매개변수로 객체가 전달되는 경우 발생
- 예제
- 학생이 버스를 탄다.
- 학생의 속성은 이름, 학년, 가진 돈이 있다.
- 버스의 속성은 버스 번호, 승객 수, 수입이 있다.
- 학생의 가진돈은 버스비만큼 줄어들고, 버스의 수입은 학생의 버스비로 인해 그만큼 증가한다.
Student.java
public class Student {
// 멤버 변수
public String studentName;
int money;
// 생성자
public Student(String studentName, int money) {
this.studentName = studentName;
this.money = money;
}
public void takeBus(Bus bus){ // 버스를 탄다
bus.take(1000);
this.money-=1000;
}
public void takeSubway(Subway subway) { // 지하철을 탄다
subway.take(1200);
this.money-=1200;
}
public void showInfo(){
System.out.println("==학생 정보 시작==");
System.out.println("studentName = " + studentName);
System.out.println("money = " + money);
System.out.println("==학생 정보 끝==");
}
}
Bus.java
public class Bus {
int busNum;
int passengerCount;
int money;
public Bus(int busNum) {
this.busNum = busNum;
}
public void take(int money) { // 버스를 탄다
this.money += money;
passengerCount++;
}
public void showBusInfo(){
System.out.println("==Bus==");
System.out.println("busNum = " + busNum);
System.out.println("passengerCount = " + passengerCount);
System.out.println("money = " + money);
}
}
Subway.java
public class Subway {
int lineNum;
int passengerCount;
int money;
public Subway(int lineNum) {
this.lineNum = lineNum;
}
public void take(int money) { // 버스를 탄다
this.money += money;
passengerCount++;
}
public void showSubwayInfo(){
System.out.println("==Subway==");
System.out.println("lineNum = " + lineNum);
System.out.println("passengerCount = " + passengerCount);
System.out.println("money = " + money);
}
}
Main.java
public class Main {
public static void main(String[] args) {
Student studentJ = new Student("James", 5000);
Student studentT = new Student("Tomas", 10000);
Bus bus100 = new Bus(100);
Bus bus500 = new Bus(500);
studentJ.takeBus(bus100); //버스 탄다
Subway greenSubway = new Subway(2);
studentT.takeSubway(greenSubway); //지하철 탄다
//출력
studentJ.showInfo();
studentT.showInfo();
bus100.showBusInfo();
greenSubway.showSubwayInfo();
}
}
ch15. 복습해봅시다 🎃
문제
앞의 예제에서 Edward는 지각을 해서 택시를 타야 했습니다.
20000원을 가지고 있었는데 10000원을 택시비로 사용했습니다.
택시는 '잘나간다 운수' 회사 택시를 탔습니다.
Student.java
public class Student {
// 멤버 변수
public String studentName;
int money;
// 생성자
public Student(String studentName, int money) {
this.studentName = studentName;
this.money = money;
}
public void takeTaxi(Taxi taxi) {
taxi.take(10000); // 택시 객체와 상호작용
this.money-=10000;
}
public void showInfo(){
System.out.println("==학생 정보 시작==");
System.out.println("studentName = " + studentName);
System.out.println("money = " + money);
System.out.println("==학생 정보 끝==");
}
}
Taxi.java
public class Taxi {
String companyName;
int money;
public Taxi(String companyName, int money) {
this.companyName = companyName;
this.money = money;
}
public void take(int money) {
this.money += 10000;
}
public void showInfo(){
System.out.println("==Taxi==");
System.out.println("name = " + companyName);
System.out.println("money = " + money);
}
}
Main.java
public class Main {
public static void main(String[] args) {
Student studentE = new Student("Edward", 20000);
Taxi blueTaxi = new Taxi("잘나간다 운수", 0);
// 학생이 택시를 탄다
studentE.takeTaxi(blueTaxi);
// 출력
studentE.showInfo();
blueTaxi.showInfo();
}
}
출력 결과
생각해본점 🤔
- 객체 택시와 학생에서 서로 상호작용 하는 코드를 각각 만들어야한다.
- 학생이 택시를 타므로 학생 객체부터 메서드를 시작한다(takeTaxi)
static 변수
- 여러 인스턴스에서 공통으로 사용하는 변수를 만들고 싶을때 사용
- ex ) 기준값 - id
- static 변수는 Stack이 아닌 Data 영역에 존재하므로 프로그램 종료 시점까지 존재한다.
- 참고 : 생성 위치 Heap과 Data, StackData : 상수, Literal, static 변수 생성 위치
- Stack : 지역변수, 함수 생성 위치 - 함수 종료시 소멸
- Heap : 인스턴스 생성 위치, 인스턴스 종료시 소멸
- 인스턴스 생성과 상관없이 사용가능하므로 클래스 이름으로 직접 참조한다.
공통 변수 static 예제 1
Employee.java
public class Employee {
public static int serialNum = 1000;
private int employeeId;
private String employeeName;
private String department;
//getter and setter
public static int getSerialNum() {
return serialNum;
}
public static void setSerialNum(int serialNum) {
Employee.serialNum = serialNum;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Main.java
public class Main {
public static void main(String[] args) {
Employee employeeLee = new Employee();
employeeLee.setEmployeeName("lee");
System.out.println("employeeLee.serialNum = " + employeeLee.serialNum); //1000
Employee employeeKim = new Employee();
employeeKim.setEmployeeName("kim");
employeeKim.serialNum++; //static 변수인 serialNum 값을 다른 인스턴스에서 증가시키면? => 반영O
System.out.println("employeeKim.serialNum = " + employeeKim.serialNum); //1001
System.out.println("employeeKim.serialNum = " + employeeKim.serialNum); //1001
}
}
공통 변수 static 예제 2
Employee.java
public class Employee {
public static int serialNum = 1000;
private int employeeId;
private String employeeName;
private String department;
public Employee() {
employeeId=serialNum++; // 인스턴스 생성시마다 serialNum 1씩 증가
}
//getter and setter
public static int getSerialNum() {
return serialNum;
}
public static void setSerialNum(int serialNum) {
Employee.serialNum = serialNum;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Main.java
public class Main {
public static void main(String[] args) {
Employee employeeLee = new Employee();
employeeLee.setEmployeeName("lee");
Employee employeeKim = new Employee();
employeeKim.setEmployeeName("kim");
System.out.println("employeeLee.getEmployeeId() = " + employeeLee.getEmployeeId()); //1000
System.out.println("employeeKim.getEmployeeId() = " + employeeKim.getEmployeeId()); //1001
}
}
<aside> ✅ static 변수를 이용해 인스턴스마다 고유한 serialNum을 할당할 수 있다.
</aside>
static 메서드
- static 메서드는 인스턴스 생성과 무관하게 클래스 이름으로 호출될 수 있다.
- ⇒ 인스턴스 생성 전에 호출될 수 있으므로 static 메서드 내부에서는 인스턴스 변수를 사용할 수 없다.
Employee.java
public class Employee{
...
public static int getSerialNum() { //static 메서드
int i=0; // 지역 변수는 가능
// employeeName="lee" : 인스턴스 변수 사용은 오류 유발
return serialNum;
}
}
Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Employee.getSerialNum() = " + Employee.getSerialNum());
}
}
<aside> ✅ static 메서드는 인스턴스 생성되지않아도 클래스 이름으로 호출 가능
</aside>
static 응용 - 싱글톤(singleton) 패턴
- 프로그램에서 인스턴스가 단 한 개만 생성되어야 하는 경우 사용하는 디자인 패턴
- ⇒ static 변수, 메서드를 활용하여 구현 할 수 있음
Company.java
public class Company {
// 회사는 중복되지않고 유일하게 각각 존재한다.
private static Company instance = new Company();
private Company(){} //생성자 막음
public static Company getInstance() { // 인스턴스 생성 없이 호출가능한 static 메서드
if (instance==null) instance = new Company(); // 인스턴스가 없으면 만들어줌
return instance;
}
}
Main.java
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
Company c1 = Company.getInstance();
Company c2 = Company.getInstance();
System.out.println("c1 = " + c1);
System.out.println("c2 = " + c2); //같은 인스턴스 반환
Calendar cal = Calendar.getInstance(); // calender도 싱글톤이다.
}
}
출력 결과
같은 인스턴스 반환
ch19. 복습해봅시다 🎃
문제
설명에 따른 객체를 구현하여 테스트 코드가 실행되도록 구현하기
자동차 공장이 있습니다. 자동차 공장은 유일한 객체이고, 이 공장에서 생산되는 자동차는 제작될 때마다 고유의 번호가 부여됩니다.
자동차 번호가 10001부터 시작되어 자동차가 생산될 때마다 10002, 10003 이렇게 번호가 붙도록 자동차 공장 클래스, 자동차 클래스를 구현하세요
다음 CarFactoryTest.java 테스트 코드가 수행 되도록 합니다.
CarFactoryTest.java - 테스트 코드
public class CarFactoryTest {
public static void main(String[] args) {
CarFactory factory = CarFactory.getInstance();
Car mySonata = factory.createCar();
Car yourSonata = factory.createCar();
System.out.println(mySonata.getCarNum()); //10001 출력
System.out.println(yourSonata.getCarNum()); //10002 출력
}
}
코드
CarFactory.java
public class CarFactory { // 싱글톤
private static CarFactory instance =new CarFactory();
private CarFactory(){}
public static CarFactory getInstance(){
if(instance==null)
instance = new CarFactory();
return instance;
}
public Car createCar(){
Car c = new Car();
return c;
}
}
Car.java
public class Car {
private static int serialNum=10000; //static 변수
int carNum;
public Car() {
serialNum++;
carNum = serialNum;
}
public int getCarNum(){
return carNum;
}
}
주어진 테스트 코드 실행 결과
생각해볼 점🤔
- static 변수가 너무 많아지면 발생되는 문제
- static 변수는 나쁘다?
- 편리함때문에 너무 많은 static 변수를 사용하는 것은 좋지 않다.
- 상수의 경우 값이 바뀌지 않으므로 final static을 사용해 선언한다.
- Static 사용을 피해야 하는 이유
- static을 잘 사용하는 방법
- 필요한 경우에만 사용하기
- ex) 상수, 한번만 로딩되는것이 좋은 설정파일 정보, 자주 바뀌지 않는 조회빈도 높은 db 데이터
- 필요한 경우에만 사용하기
- static 변수는 나쁘다?
- static 변수와 메서드 관리 방법
- 소프트웨어 설계 도구
2. Challenging (학습하며 어려웠던 점)
- 강사님이 잘 설명해주셔서 아직 어려운 점은 없습니다.
'Spring > 카테캠 - TIL' 카테고리의 다른 글
TIL [0511- 0514] : Spring Basic + Spring MVC 1 (2) | 2023.05.14 |
---|---|
TIL [0504-0505] : 객체지향 프로그래밍(예외처리, 로그, IO 스트림, 직렬화) (0) | 2023.05.06 |
TIL [0503-0504] : 객체지향 프로그래밍(내부클래스, 람다식, 스트림) (0) | 2023.05.05 |
TIL (0424 - 0429) : 객체지향 프로그래밍 (0) | 2023.04.29 |
TIL 0417 - 0423 (4) | 2023.04.23 |