복습
•
객체지향 실습
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 할 수 없다.