Backend
home

2024. 7. 15

복습

객체지향 실습
package com.oop.basic; public class OOPExample1 { public static void main(String[] args) { // 클래스(Korean) 만들기 Korean k1 = new Korean("최인규", "990101-1001234"); System.out.println(k1.name); // 이름 System.out.println(k1.gender); // 성별 System.out.println(k1.birthYear); // 1999 System.out.println(k1.birthMonth); // 1 System.out.println(k1.birthDay); // 1 System.out.println(Korean.nation); // 정적 멤버 } } package com.oop.basic; public class Korean { static String nation = "Korean"; public String name; public String gender; public int birthYear; public int birthMonth; public int birthDay; Korean(String name, String number) { this.name = name; int genderCode = Integer.parseInt(number.substring(7, 8)); if (genderCode % 2 == 0) { this.gender = "여자"; } else { this.gender = "남자"; } int year = Integer.parseInt(number.substring(0, 2)); this.birthYear = 1900 + year; if (genderCode >= 3) { this.birthYear = 2000 + year; } int month = Integer.parseInt(number.substring(2, 4)); this.birthMonth = month; int day = Integer.parseInt(number.substring(4, 6)); this.birthDay = day; } }
Java
복사

인스턴스 멤버

객체 내부에서는 인스턴스 멤버에 접근하기 위해 this 키워드를 사용한다.
객체는 자신을 ‘this’ 라고 한다.
package com.oop.basic; public class OOPExample2 { public static void main(String[] args) { Car myCar = new Car("소나타"); // myCar.model // 소나타 myCar.gas = 100; myCar.speed = 100; // 이렇게 사용 권장하지 않음 myCar.run(); } } package com.oop.basic; public class Car { String model; int gas, speed; Car(String model) { this.model = model; } void setSpeed(int speed) { this.speed = speed; } void run() { setSpeed(100); System.out.println(model + " 이/가 달립니다. (시속: " + speed + "km/h)"); } }
Java
복사

정적 멤버

정적 멤버는 객체 참조 변수를 통해서 접근도 가능하지만 클래스 이름으로 접근하는 것이 정석이다.
정적 멤버는 객체 생성없이 클래스 이름과 객체 접근 연산자(.)로 접근 가능하다.
package com.oop.staticmember; public class SmartPhone { static String company = "Apple"; static String model = "iPhone16"; // static String info = company + "-" + model; // Tip static String info; static { System.out.println("정적 블록을 실행합니다. (클래스가 메모리로 로딩될 때 자동 실행)"); info = company + "-" + model; } } package com.oop.staticmember; public class SmartPhoneExample { public static void main(String[] args) { System.out.println(SmartPhone.company); System.out.println(SmartPhone.model); System.out.println(SmartPhone.info); } }
Java
복사
정적 필드 값을 변경하는 것은 일반적이지 않음
package com.oop.staticmember; public class ClassName { String instanceField = "인스턴스"; void instanceMethod() { // 인스턴스 메소드는 객체가 생성된 뒤에 호출! // 따라서 인스턴스 필드를 사용할 수 있다. System.out.println(instanceField); // 정적 필드도 사용할 수 있다. System.out.println(staticField); instanceField = "instance"; // staticField = "instance"; // 정적 필드 값을 변경하는 것은 일반적이지 않음 System.out.println(instanceField); // System.out.println(staticField); } static String staticField = "정적"; static void staticMethod1() { // 중요: 정적 메소드에서는 인스턴스 멤버(필드, 메소드)를 사용할 수 없다!!! // System.out.println(instanceField); // 정적 메소드에서는 this도 사용할 수 없다!! // System.out.println(this.instanceField); } static void staticMethod1() { // 중요: 정적 메소드에서는 인스턴스 멤버(필드, 메소드)를 사용할 수 없다!!! // System.out.println(instanceField); // 정적 메소드에서는 this도 사용할 수 없다!! // System.out.println(this.instanceField); // 중요!! 오직 정적 멤버만이 사용 가능하다. System.out.println(staticField); // static 메소드도 사용 가능하다. staticMethod2(); // 굳이 인스턴스를 사용해야 한다면, 인스턴스를 여기서 생성해서 사용해야 한다. ClassName cn = new ClassName(); cn.instanceField = "생성 후 인스턴스 필드 사용"; System.out.println(cn.instanceField); cn.instanceMethod(); } static void staticMethod2() { staticField = "static"; } }
Java
복사

final 필드와 상수

필드 선언 시에 초기값 대입
public class ClassName { final int final_field_1 = 10; }
Java
복사
생성자에서 초기값 대입
public class ClassName { final int final_field_2; ClassName() { FINAL_FIELD_2 = 10; } }
Java
복사
상수명은 모두 대문자로 작성하는 것이 관례이며, 단어가 혼합된 경우에는 언더스코어(_) 로 단어를 연결해서 사용
static final double PI = 3.14159; static final double MAX_SPEED = 200;
Java
복사

접근 제한자

접근 제한자
제한 대상
제한 범위
public
클래스, 필드, 생성자, 메소드
없음
protected
필드, 생성자, 메소드
같은 패키지이거나, 자식 객체만 사용 가능
default
클래스, 필드, 생성자, 메소드
같은 패키지에서만 사용 가능
private
필드, 생성자, 메소드
객체 내부에서만 사용 가능
실습
package a; public class A { byte byteValue = 1; short shortValue = 1; int intValue = 1; // public은 다른 패키지에서도 접근 가능 ClassName cn1 = new ClassName(byteValue); // default는 같은 패키지 내에서만 접근 가능 ClassName cn2 = new ClassName(shortValue); // private는 객체 내부에서만 접근 가능 // ClassName cn3 = new ClassName(intValue); }
Java
복사
package a; public class ClassName { byte byteValue; short shortValue; int intValue; public ClassName(byte byteValue) { this.byteValue = byteValue; } ClassName(short shortValue) { this.shortValue = shortValue; } private ClassName(int intValue) { this.intValue = intValue; } // == 같은 객체(클래스) 내에서 생성자 호출 == // public은 다른 패키지에서도 접근 가능 ClassName cn1 = new ClassName(1); // default는 같은 패키지 내에서만 접근 가능 ClassName cn2 = new ClassName(1); // private는 객체 내부에서만 접근 가능 ClassName cn3 = new ClassName(1); }
Java
복사
package b; import a.ClassName; public class B { byte byteValue = 1; short shortValue = 1; int intValue = 1; // public은 다른 패키지에서도 접근 가능 ClassName cn1 = new ClassName(byteValue); // default는 같은 패키지 내에서만 접근 가능 // ClassName cn2 = new ClassName(shortValue); // private는 객체 내부에서만 접근 가능 // ClassName cn3 = new ClassName(intValue); }
Java
복사

Setter, Getter

객체의 필드를 외부에서 마음대로 읽고 변경하게 될 경우 객체의 무결성이 깨질 수 있다.
자동차의 속력은 음수가 될 수 없는데, 외부에서 음수로 변경하면 객체의 무결성에 위배된다.
Car myCar = new Car(); myCar.speed = -100;
Java
복사
이러한 문제점으로 인해, 객체지향 프로그래밍에서는 외부에서 직접적인 필드의 접근을 차단한다. 그 대신에 메소드를 통해 필드에 접근할 수 있다.
메소드를 통한 필드 접근을 하면, 메소드가 데이터 값을 검증해서 유효한 값만 필드값으로 저장할 수 있도록 만들 수 있다 (나중에 프로젝트 시 이 부분이 적용됨)
이러한 메소드를 Setter라 한다
private int speed; public void setSpeed(int speed) { this.speed = speed > 0 ? speed : 0; }
Java
복사
외부에서 객체의 필드를 읽을 때 역시 메소드가 필요한 경우가 있다.
필드값이 객체 외부에서 사용하기에 적절하지 않은 경우, 메소드를 이용해 적절한 형태로 변환하여 반환할 수 있다.
이러한 메소드를 Getter라 한다.
private int speed; public void setSpeed(int speed) { this.speed = speed >? 0 ? speed : 0; } public int getSpeed() { return speed; }
Java
복사
Getter, Setter 기본 작성법
public class ClassName { private int fieldName1; private boolean fieldName2; public int getFieldName1() { return fieldName1;} public void setFieldName1(int fieldName1) { this.fieldName1 = fieldName1; public boolean isFieldName2() { return fieldName2; } public void setFieldName2(boolean fieldName2) { this.fieldName2 = fieldName2; } }
Java
복사

싱글톤 패턴

클래스의 객체를 단 하나만 생성하고, 해당 객체를 어디서든지 접근할 수 있도록 하는 디자인 패턴이 싱글톤 패턴이다.
외부에서 객체를 생성할 수 없게 만든다.
주로 애플리케이션 전체에서 하나의 객체만 존재해야 하는 경우에 사용한다.
생성자를 private 으로 접근을 제한하여 외부에서 new 연산자로 객체를 생성할 수 없도록 하는 것이다.
싱글톤 패턴을 사용하는 이유
공통된 자원을 관리하거나 설정 값을 유지할 수 있다.
불필요한 객체 생성을 방지하여 메모리를 절약할 수 있다.
package com.oop.singleton; public class Singleton { private static Singleton st = new Singleton(); // private 생성자 private Singleton() { } // 인스턴스를 얻는 정적 메소드 public static Singleton getInstance() { if (st == null) { st = new Singleton(); } return st; } } package com.oop.singleton; import java.util.Calendar; public class SingletonExample { public static void main(String[] args) { // Singleton st = Singleton.st; // static이기 때문에 가져올 수 있음, 단 임의로 변경할 수 있어서 주의해야 함! - private으로 막아놓음 Singleton st1 = Singleton.getInstance(); Singleton st2 = Singleton.getInstance(); System.out.println(st1 == st2); // 참고 : Calendar.getInstance(); } }
Java
복사

실습

정적 메소드, 메소드 오버로딩
package com.oop.practice2; public class PrinterExample { public static void main(String[] args) { // Printer p = new Printer(); // p.println(10); // 나와라 10 // p.println(true); // 나와라 true // p.println(5.7); // 나와라 5.7 // p.println("홍길동"); // 나와라 홍길동 Printer.println(10); Printer.println(true); Printer.println(5.7); Printer.println("홍길동"); } } package com.oop.practice2; // static으로 변경해야 Printer 메소드로 값을 출력할 수 있음 public class Printer { private static final String MSG = "출력 : "; public Printer() { } public static void println(int value) { System.out.println(MSG + value); } public static void println(boolean boolValue) { System.out.println(MSG + boolValue); } public static void println(double doubleValue) { System.out.println(MSG + doubleValue); } public static void println(String strValue) { System.out.println(MSG + strValue); } }
Java
복사
Account 실습
package com.oop.practice3; public class AccountExample { public static void main(String[] args) { Account acc = new Account(); acc.setBalance(10000); System.out.println("현재 잔고: " + acc.getBalance()); // 현재 잔고: 10000 acc.setBalance(-100); System.out.println("현재 잔고: " + acc.getBalance()); // 현재 잔고: 10000 } } package com.oop.practice3; public class Account { private int balance; final int MIN_BALANCE = 0; final int MAX_BALANCE = 1_000_000; public int getBalance() { return balance; } public void setBalance(int balance) { if (balance > MIN_BALANCE && MAX_BALANCE >= balance) { this.balance = balance; } } }
Java
복사

상속

부모가 자식에게 물려주는 행위
객체지향 프로그램에서 부모 클래스의 필드와 메소드를 자식 클래스에게 물려주는 것을 말한다.
이미 잘 개발되어 있는 클래스를 재사용해서 새로운 클래스를 만들기에 중복된 코드를 줄여준다.
프로그램에서는 자식이 부모를 선택해 상속을 받는다.
자식 클래스를 선언할 때 extends 키워드 뒤에 부모 클래스를 기술한다.
다른 언어와 달리, Java는 다중 상속을 허용하지 않는다. 따라서 하나의 부모 클래스만 상속할 수 있다.
public class Child extends Parents { }
Java
복사
예제
// Parents.java package com.oop.inheritance; public class Parents { String eyeColor = "갈색"; void walk() { System.out.println("뒤뚱뒤뚱"); } }
Java
복사
// Child.java package com.oop.inheritance; public class Child extends Parents { String job = "개발자"; void hello() { System.out.println("안녕하세요"); } }
Java
복사
package com.oop.inheritance; public class InheritanceExample { public static void main(String[] args) { Parents parents = new Parents(); System.out.println(parents.eyeColor); parents.walk(); System.out.println(); Child child = new Child(); System.out.println(child.job); child.hello(); System.out.println(child.eyeColor); child.walk(); } }
Java
복사
부모가 없이는 자식이 있을 수 없다. 따라서 자식 객체를 생성하면 부모 객체가 먼저 생성되고 나서 자식 객체가 생성된다.
// Phone.java package com.oop.inheritance2; public class Phone { public String model, color; public Phone() { System.out.println("Phone 기본 생성자"); } public void bell() { System.out.println("벨이 울립니다."); } public void sendVoice(String message) { System.out.println("본인: " + message); } public void receiveVoice(String message) { System.out.println("상대방: " + message); } public void hangUp() { System.out.println("전화를 끊습니다."); } }
Java
복사
// SmartPhone.java package com.oop.inheritance2; public class SmartPhone extends Phone { public boolean wifi; public SmartPhone(String model, String color) { System.out.println("스마트폰 생성자"); this.model = model; this.color = color; } public void setWifi(boolean Wifi) { if (wifi) { System.out.println("와이파이를 켭니다."); } else { System.out.println("와이파이를 끕니다."); } this.wifi = wifi; } public void internet() { System.out.println("인터넷에 연결합니다."); } }
Java
복사
package com.oop.inheritance2; public class SmartPhoneExample { public static void main(String[] args) { SmartPhone sp = new SmartPhone("갤럭시", "다크그린"); System.out.println("와이파이 상태 : " + sp.wifi); sp.bell(); sp.sendVoice("여보세요"); sp.receiveVoice("야 나 누구누구인데..."); sp.sendVoice("ㅇㅇ"); sp.hangUp(); sp.setWifi(!sp.wifi); sp.internet(); } }
Java
복사
오버라이딩
부모 클래스의 모든 메소드가 자식 클래스에 알맞게 설계되어 있으면 좋겠지만 어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수 있다.
이러한 메소드는 자식클래스에서 재정의해서 사용해야 한다. 이것을 메소드 오버라이딩이라고 한다.
규칙
부모 메소드의 선언부(리턴 타입, 메소드 이름, 매개변수)와 동일해야 한다.
접근 제한을 더 강하게 오버라이딩하는 것은 불가능하다.
새로운 예외를 throws 할 수 없다.