0. 대화
0-1. UML
- UML (Unified Modeling Language) = “통합 모델링 언어”
- 소프트웨어 구조(클래스, 관계, 흐름 등)를 그림으로 표현하는 표준 방식
- UML 중 가장 중요한 것: 클래스 다이어그램
0-2. F5로 실행한 main의 결과물은 어디로 가는가.
- 자바 프로그램은 main()을 실행하면,
- JVM이 새로운 메모리 공간(힙 & 스택)을 만들어서
- 모든 객체들을 새로 생성하고
- 실행이 끝나면 통째로 사라진다.
- 이를 유지하고 싶으면 DB와 연동해서 데이터를 유지시킬 필요가 있는 것이다.
0-3. Serializable이란
- 객체를 파일로 저장하거나, 네트워크로 보내기 위해 '바이트(byte) 형태로 변환할 수 있도록 해주는 표시(마커)**
- 이 클래스는 저장 가능하다!"라고 자바에게 알려주는 신호.
- 부모가 구현(implements)했으면 자식도 저장 가능하다.
- 이는 인터페이스 이므로 “해야 할 일을 정해 놓은 명세서(설계도)”이를 따랐음을 말하는 것은 "저장 능력을 보증"하는 것과 같다.
0-4. 직렬화(Serialization)와 역직렬화(Deserialization)
- 직렬화 = 객체를 저장 가능한 데이터로 바꾸는 작업 (나열된 0101처럼 데이터를 일직선으로 만든다는 의미 (직선배열), serial -> series로 만들다.)
- 역직렬화 = 그 데이터를 다시 객체로 만드는 작업
- serialVersionUID: 직렬화된 객체를 역직렬화할 때 저장된 클래스와 현재 클래스가 “같은 클래스인지” 확인하기 위한 버전 ID이다.
- FileOutputStream("devices.dat")
- “ devices.dat라는 통 파일 뚜껑을 열고, 쓰기용 호스를 연결해서 그 안에 데이터를 부을 준비를 하는 것!”
1. 자바 코딩테스트
1-1. qr code
- 문자열 control은 웬만하면 StringBuilder가 빠르다.
public String solution(int q, int r, String code) {
StringBuilder answer = new StringBuilder();
for (int i = 0; i < code.length(); i++){
if (i % q == r){
answer.append(code.charAt(i));
}
}
return answer.toString();
}
1-2. 수열과 구간 쿼리 4
public int[] solution(int[] arr, int[][] queries) {
int[] answer = {};
for (int[] x : queries){
for (int i = x[0]; i <= x[1]; i++){
if (i % x[2] == 0){
arr[i] += 1;
}
}
}
return arr;
}
1-3. 배열 만들기 6
- 리스트에서 특정 위치의 원소를 꺼낼 때는 get으로 꺼낸다. 배열만 인덱스로 접근 가능
public int[] solution(int[] arr) {
List <Integer> stk = new ArrayList<>();
int i = 0;
while (i < arr.length){
if (stk.size() == 0){
stk.add(arr[i]);
i += 1;
}
else if (stk.size() > 0 && stk.get(stk.size()-1) == arr[i] ){
stk.remove(stk.size()-1);
i += 1;
}
else if (stk.size() > 0 && stk.get(stk.size()-1) != arr[i]){
stk.add(arr[i]);
i += 1;
}
}
if (stk.size() == 0){
return new int[]{-1};
}
return stk.stream().mapToInt(Integer::intValue).toArray();
}
1-4. 왼쪽 오른쪽
public String[] solution(String[] str_list) {
String[] answer = {};
for (int i = 0; i < str_list.length; i++){
if (str_list[i].equals("l")){
answer = Arrays.copyOf(str_list, i);
break;
}
else if (str_list[i].equals("r")){
answer = Arrays.copyOfRange(str_list, i + 1, str_list.length);
break;
}
}
return answer;
}
1-5. 문자 개수 세기
- char은 그냥 비교해도 아스키로 비교되고 아예 아스키로 연산도 된다는 걸 기억하기, 애초에 아스키랑 매우 가깝다고 생각하자.
public int[] solution(String my_string) {
int[] answer = new int[52];
for (char i : my_string.toCharArray()){
if (i <= 'z' && i >= 'a'){
answer[i - 'a' + 26] += 1;
}
else {
answer[i-'A'] += 1;
}
}
return answer;
}
2. 객체지향 프로그래밍의 이해 (1)
2-1. Override, 객체 형변환
- @Override는 필수는 아니지만 실수 방지를 위해 항상 쓰는 것이 좋다.
- 부모로 형변환해도 오버라이딩된 메서드는 자식 기능이 실행되는 것을 동적 바인딩(Dynamic Binding)이라고 한다.
- 다운캐스팅 전에는 꼭 instanceof를 체크해야 한다.
- 하위 클래스에서 반드시 구현해야 할 메서드는 abstract로 선언한다.
- // TODO:는 IDE에서 할 일 추적용으로 많이 씀. 표준 주석 태그는 아니지만 실무에서 많이 사용됨.
Protoss 예제
public class Protoss {
private String name;
private int hp;
private int speed;
private int dps;
public Protoss(String name, int hp, int speed, int dps) {
this.name = name;
this.hp = hp;
this.speed = speed;
this.dps = dps;
System.out.printf(">> 유닛이 생성되었습니다. --> 이름: %s, 체력: %d, 공격력: %d\n", this.name, this.hp, this.dps);
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getHp() {
return this.hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getSpeed() {
return this.speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getDps() {
return this.dps;
}
public void setDps(int dps) {
this.dps = dps;
}
public void move(String position){
System.out.printf("%s(이)가 %s까지 이동합니다.\n", this.name, position);
}
public void attack(String target){
System.out.printf("%s(이)가 %s(을)를 공격합니다. >> 데미지: %d\n", this.name, target, this.dps);
}
public String toString(){
return "{"+
" name='" + getName() + "'" +
", hp='" + getHp() + "'" +
", speed ='" + getSpeed() + "'" +
", dps ='" + getDps()+ "'" +
"}";
}
}
Zirot 예제
public class Zilot extends Protoss{
public Zilot(String name, int hp, int speed, int dps){
super(name, hp, speed, dps);
}
@Override
public void move(String target){
System.out.printf("[Zilot] %s(이)가 %s까지 빠른 속도로 이동합니다.\n", this.getName(), target);
}
@Override
public void attack(String target){
System.out.printf("[Zilot] %s(이)가 %s를 칼로 찌릅니다. >> 데미지:%d.\n", this.getName(), target, this.getDps());
}
public void swardAttack(String target){
System.out.printf("[질럿의 고유 스킬] %s(이)가 %s를 칼로 공격합니다. >> 데미지:%d.\n", this.getName(), target,
this.getDps() + 10);
}
}
Dragon 예제
public class Dragon extends Protoss {
public Dragon(String name, int hp, int speed, int dps){
super(name, hp, speed, dps);
}
@Override
public void move(String target){
System.out.printf("[Dragon] %s(이)가 %s까지 빠른 속도로 이동합니다.\n", this.getName(), target);
}
@Override
public void attack(String target){
super.attack(target);
System.out.println("원거리 공격을 위한 포탄 발사");
}
public void swardAttack(String target){
System.out.printf("[드라군의 고유 스킬] %s(이)가 %s에게 포탄을 발사합니다. >> 데미지:%d.\n", this.getName(), target,
this.getDps() + 10);
}
}
테스트 예제
public static void main(String[] args) {
Protoss p1 = new Protoss("프로브1", 100, 20, 10);
p1.move("테란본진");
p1.attack("테란본진");
System.out.println(p1.toString());
System.out.println("--------------");
Zilot z1 = new Zilot("질럿1", 500, 300, 120);
z1.move("테란본진");
z1.attack("테란본진");
z1.swardAttack("테란본진");
System.out.println(z1.toString());
System.out.println("--------------");
Dragon d1 = new Dragon("드라군1", 100, 20, 10);
d1.move("테란본진");
d1.attack("테란본진");
d1.fireAttack("테란본진");
System.out.println(d1.toString());
System.out.println("--------------");
}
2-1-1. 객체 형변환
- 자식 -> 부모 형변환하면 (Upcasting) 오버라이딩한건 자식 기능이 사용된다. 자식이 독자적으로 만든기능은 잠긴다. // 부모랑 비슷한 것만 남는다.
- Boxing은 int -> integer처럼 기본형이 객체로 될때 많이 쓰인다.
- 역변환시 최초 자식 클래스로 돌아가야한다.
- ArrayList<> = new List<>(); 이렇게 부모를 자식으로 바꾸는건 안된다.
객체 형변환 예제
public class Ex10_객체형변환 {
public static void main(String[] args) {
Protoss p0 = new Protoss("프로브1호", 50, 30, 2);
Zilot z = new Zilot("질럿 1호", 300, 150, 120);
Protoss p1 = z;
Protoss p2 = new Dragon("드라군1호", 280, 120, 170);
p0.move("저그 본진");
p0.attack("저그 본진");
System.out.println("----------");
p1.move("저그 본진");
p1.attack("저그 본진");
System.out.println("----------");
p2.move("저그 본진");
p2.attack("저그 본진");
System.out.println("----------");
Zilot rz = (Zilot) p1;
Dragon rd = (Dragon) p2;
rz.swardAttack("저그 본진");
rd.fireAttack("저그 본진");
}
}
2-1-2. 객체 배열
- 객체 배열은 지정된 클래스의 객체로 할당해야 한다.
- 서로 다른 클래스의 객체를 하나의 배열에 저장하기 위해서는 같은 부모로 부터 파생된 경우만 가능하고 이때 강제로 upcasting된다.
객체 배열 예제
public class Ex11_객체배열 {
public static void main(String[] args) {
Zilot[] z = new Zilot[3];
z[0] = new Zilot("질럿 1호", 150, 100, 120);
z[1] = new Zilot("질럿 2호", 160, 90, 140);
z[2] = new Zilot("질럿 3호", 170, 80, 160);
for (int i = 0; i < z.length; i++){
z[i].move("테란 본진");
z[i].attack("테란 본진");
z[i].swardAttack("테란 본진");
if (i + 1 < z.length){
System.out.println("---------------");
}
}
}
}
그룹지정 예제
- instanceof는 원래 해당 클래스의 객체인지 검사한다.
- 부모 배열에 자식을 넣으면 강제로 업캐스팅된다.
public class Ex12_그룹지정 {
public static void main(String[] args) {
Protoss[] p = new Protoss[5];
p[0] = new Zilot("질럿 1호", 150, 100, 120);
p[1] = new Dragon("드라군 1호", 150, 80, 200);
p[2] = new Zilot("질럿 2호", 120, 100, 150);
p[3] = new Dragon("드라군 2호", 170, 110, 240);
p[4] = new Zilot("질럿 4호", 110, 130, 120);
for (int i = 0; i < p.length; i++){
p[i].move("저그 본진");
p[i].attack("저그 본진");
if (p[i] instanceof Zilot) {
Zilot z = (Zilot) p[i];
z.swardAttack("저그 본진");
}
else if(p[i] instanceof Dragon){
Dragon d = (Dragon) p[i];
d.fireAttack("저그 본진");
}
}
}
}
2-2. 클래스와 객체 연습문제
2-2-1. 동물원 사파리 대탐험
- // TODO: 하위 클래스에서 구현 예정 이렇게 하위 클래스에서 구현 예정인 매서드를 관례적으로 표현한다.
public class Animal {
public void move(){
}
public void makeSound(){
}
public static void main(String[] args) {
Animal[] a = new Animal[3];
Lion l = new Lion();
Elephant e = new Elephant();
Penguin p = new Penguin();
a[0] = l;
a[1] = e;
a[2] = p;
for (int i = 0; i < a.length; i++){
a[i].move();
a[i].makeSound();
if (a[i] instanceof Penguin){
Penguin x = (Penguin) a[i];
x.swim();
}
}
}
}
class Lion extends Animal{
@Override
public void move(){
System.out.println("사자가 네 발로 달려갑니다.");
}
@Override
public void makeSound(){
System.out.println("사자가 포효합니다.");
}
}
class Elephant extends Animal{
@Override
public void move(){
System.out.println("코끼리가 천천히 걷습니다.");
}
@Override
public void makeSound(){
System.out.println("코끼리가 뿌웁~하고 웁니다.");
}
}
class Penguin extends Animal{
@Override
public void move(){
System.out.println("팽귄이 미끄러지듯 이동합니다.");
}
@Override
public void makeSound(){
System.out.println("팽귄이 삐약삐약 웁니다.");
}
public void swim(){
System.out.println("펭귄이 헤엄칩니다!!");
}
}
2-2-2. 쇼핑몰 상품 클래스 만들기
- 다운 캐스팅 ((Food)i).checkExpiration(); 이렇게 하면된다.
public class Product_test {
public static void main(String[] args) {
Product[] p = new Product[3];
Electronics e = new Electronics();
Clothing c = new Clothing();
Food f = new Food();
p[0]= e;
p[1]= c;
p[2]= f;
for (Product i : p){
i.showInfo();
if (i instanceof Food){
((Food)i).checkExpiration();
}
}
}
}
public class Product {
public void showInfo(){
System.out.println("상품 정보 출력");
}
}
public class Electronics extends Product {
@Override
public void showInfo(){
System.out.println("전자체품입니다. 최신기기");
}
}
public class Clothing extends Product{
@Override
public void showInfo(){
System.out.println("옷 입니다, 계절별 신상품!");
}
}
public class Food extends Product{
@Override
public void showInfo(){
System.out.println("식품입니다. 유통기한을 확인하세요!");
}
public void checkExpiration(){
System.out.println("유통기한을 확인하는 중입니다...");
}
}
2-2-3. 음식 주문 시스템
- 마지막에서 println(i)를 했을 때 %s: 패티와 빵이 조화를 이루는 메뉴\n여기에 \n이 하나 더 붙는 거라 엔터가 한번 더 쳐진다.
- 따라서 둘 중에 하나를 빼야 엔터없이 딱 붙여서 문자들을 출력할 수 있다.
- ctrl . 누르면 부모 생성자를 생성할 수 있다. 또 오버라이딩 할 것을 한방에 만들 수도 있다.
public class MenuTest {
public static void main(String[] args) {
MenuItem[] m = new MenuItem[3];
Burger b = new Burger("치즈버거", 4000);
Pizza p = new Pizza("페퍼로니 피자", 10000);
Salad s = new Salad("그린 셀러드", 5000);
m[0]= b;
m[1]= p;
m[2]= s;
int answer = 0;
for (MenuItem i : m){
System.out.print(i);
answer += i.getPrice();
}
System.out.printf("총 가격: %d원\n", answer);
System.out.println("옵션 적용:");
for (MenuItem i : m){
if (i instanceof Burger){
((Burger) i).addOption();
}
else if (i instanceof Pizza){
((Pizza) i).addOption();
}
else if (i instanceof Salad){
((Salad) i).addOption();
}
}
}
}
public class MenuItem {
private String name;
private int price;
MenuItem(String name, int price){
setName(name);
setPrice(price);
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return this.price;
}
public void setPrice(int price) {
this.price = price;
}
public void addOption(){
System.out.println("옵션 추가 기능은 각 음식에 따라 다릅니다.");
}
@Override
public String toString() {
return String.format("%s: 메뉴설명\n", this.name);
}
}
public class Burger extends MenuItem{
Burger(String name, int price){
super(name, price);
}
@Override
public String toString(){
return String.format("%s: 패티와 빵이 조화를 이루는 메뉴\n", getName());
}
@Override
public void addOption(){
System.out.printf("\t%s -> 치즈추가\n", getName());
}
}
public class Pizza extends MenuItem {
Pizza(String name, int price){
super(name, price);
}
@Override
public String toString(){
return String.format("%s: 치즈 듬뿍, 모두의 인기 메뉴\n", getName());
}
@Override
public void addOption(){
System.out.printf("\t%s -> 엣지 변경: 치즈 크러스트\n", getName());
}
}
public class Salad extends MenuItem{
Salad(String name, int price){
super(name, price);
}
@Override
public String toString(){
return String.format("%s: 신선한 채소로 만든 건강식\n", getName());
}
@Override
public void addOption(){
System.out.printf("\t%s -> 드레싱 선택: 발사믹\n", getName());
}
}
2-2-4. 추가 실습 : 스마트 헬스케어 센터 관리 시트넴 만들기
import java.util.ArrayList;
import java.util.List;
public class HealthCenterSystem {
private List <HealthService> chart;
HealthCenterSystem(){
chart = new ArrayList<>();
chart.add(new EquipmentRental("체스트 프레스", 50000));
chart.add(new GroupExercise("요가", 100000));
chart.add(new Supplement("녹차", 5000, 120));
chart.add(new FitnessTest("팝스", 3000));
}
public List<HealthService> getChart() {
return this.chart;
}
public void setChart(List<HealthService> chart) {
this.chart = chart;
}
public void programList(){
System.out.println("----전체 프로그램 목록----");
for (HealthService i : chart){
System.out.println(i);
}
System.out.println("------------------------");
}
public void allUse(){
System.out.println("모든 프로그램을 실행합니다.");
for (HealthService i : chart){
i.use();
if (i instanceof EquipmentRental){
((EquipmentRental)i).checkAvailable();
}
else if (i instanceof GroupExercise){
((GroupExercise)i).setInstructorName("김범수");
}
else if (i instanceof Supplement){
((Supplement)i).checkExpiration();
}
else if (i instanceof FitnessTest){
((FitnessTest)i).setDurationMinutes(120);
}
}
}
}
public class GroupExercise extends HealthService {
private String instructorName;
public String getInstructorName() {
return this.instructorName;
}
public void setInstructorName(String instructorName) {
this.instructorName = instructorName;
}
GroupExercise(String name, int price){
super(name, price);
System.out.println("헬스 프로그램 신청 프로그램이 신설되었습니다.");
}
public void showInfo(){
System.out.println("헬스 프로그램을 예약합니다.\n");
}
public void use(){
System.out.printf("선생님 이름과 함께 다시 사용해주세요");
}
public void use(String name){
setInstructorName(name);
System.out.printf("[%s]선생님으로 예약되었습니다.\n", this.instructorName);
}
public void reserveSlot(){
System.out.printf("[%s]선생님으로 예약 가능한 날짜는 총 17일 있습니다.\n", this.instructorName);
}
@Override
public String toString(){
return String.format("%s: %d원에 운동 프로그램이 구성되어 있습니다. 선생님을 말씀하시고 얼른 신청하세요 !", getName(), getPrice());
}
}
public class Supplement extends HealthService {
private int expirationDate;
public int getExpirationDate() {
return this.expirationDate;
}
public void setExpirationDate(int expirationDate) {
this.expirationDate = expirationDate;
}
Supplement(String name, int price, int expirationDate){
super(name, price);
this.expirationDate = expirationDate;
System.out.println("건강식품 판매 프로그램이 신설되었습니다.");
}
public void showInfo(){
System.out.println("건강 식품을 구매할 수 있습니다.\n");
}
public void use(){
System.out.printf("건강 식품을 한 개 구매하였습니다.\n");
}
public void checkExpiration(){
System.out.printf("해당 건강식품의 유효기간은 모두 %d일 입니다.\n", this.expirationDate);
}
@Override
public String toString(){
return String.format("%s: %d원에 건강식품을 구매할 수 있습니다! %d일 남았습니다!", getName(), getPrice(), this.expirationDate);
}
}
public abstract class HealthService {
private String name;
private int price;
HealthService(String name, int price){
this.name = name;
this.price = price;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return this.price;
}
public void setPrice(int price) {
this.price = price;
}
public abstract void showInfo();
public abstract void use();
@Override
public String toString(){
return String.format("%s: %d원", this.name, this.price);
}
}
public class EquipmentRental extends HealthService{
private boolean available = true;
public boolean isAvailable() {
return this.available;
}
public void setAvailable(boolean available) {
this.available = available;
}
EquipmentRental(String name, int price){
super(name, price);
System.out.println("운동기구 대여 프로그램이 신설되었습니다.");
}
public void showInfo(){
System.out.println("필요한 헬스기구를 예약합니다.\n");
}
public void use(){
if (available){
System.out.println("신청하신 물품 대여에 성공하셨습니다. 대여는 1인 1물건만 가능합니다.");
setAvailable(false);
}
else {
System.out.println("이미 물품을 대여한 상태입니다. 물품을 반납하고 이용해주세요.");
}
}
public void checkAvailable(){
System.out.printf("회원님의 물품 대여 서비스 이용은 %s 합니다", isAvailable() ? "가능": "불가능");
}
@Override
public String toString(){
return String.format("%s: %d원에 서비스 예약 가능합니다.", getName(), getPrice());
}
}
2-2-5. 스마트 홈기기 제어 시스템 만들기
- 이렇게 자식에서 부모의 기능을 재정의할 거라면 그냥 안해도 알아서 상속받는다.
@Override
public void showStatus() {
super.showStatus();
}
@Override
public void turnOff() {
super.turnOff();
}
@Override
public void turnOn() {
super.turnOn();
}
public class SmartHomeTest {
public static void main(String[] args) {
SmartDevice[] devices = new SmartDevice[3];
devices[0] = new SmartLight("스마트 전등");
devices[1] = new SmartSpeaker("스마트 스피커");
devices[2] = new SmartThermostat("스마트 온도조절기");
for (int i = 0; i < devices.length; i++) {
devices[i].turnOn();
}
for (int i = 0; i < devices.length; i++) {
devices[i].showStatus();
}
System.out.println();
SmartLight light = (SmartLight) devices[0];
light.changeColor("파란색");
SmartSpeaker speaker = (SmartSpeaker) devices[1];
speaker.playMusic("Jazz");
SmartThermostat thermostat = (SmartThermostat) devices[2];
thermostat.setTemperature(24);
}
}
class SmartDevice {
private String name;
private boolean isOn;
SmartDevice(String name){
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public boolean getIsOn() {
return this.isOn;
}
public void setIsOn(boolean isOn) {
this.isOn = isOn;
}
public void turnOn(){
this.isOn = true;
System.out.printf("%s가 켜졌습니다.\n", this.name);
}
public void turnOff(){
this.isOn = false;
System.out.printf("%s가 꺼졌습니다.\n", this.name);
}
public void showStatus(){
System.out.printf("%s의 상태: %s입니다.\n", this.name, this.isOn ? "ON" : "OFF");
}
}
class SmartLight extends SmartDevice {
SmartLight(String name){
super(name);
}
public void changeColor(String color){
if (!getIsOn()){
System.out.printf("%s가 꺼져있어 색상을 변경할 수 없습니다.\n", getName());
}
else {
System.out.printf("%s의 색상이 %s로 변경되었습니다.\n", getName(), color);
}
}
}
class SmartSpeaker extends SmartDevice {
SmartSpeaker(String name){
super(name);
}
public void playMusic(String song){
if (!getIsOn()){
System.out.printf("%s가 꺼져있어 음악을 틀 수 없습니다.\n", getName());
}
else {
System.out.printf("%s에서 %s를 재생합니다.\n", getName(), song);
}
}
}
class SmartThermostat extends SmartDevice {
SmartThermostat(String name){
super(name);
}
public void setTemperature(int degree){
if (!getIsOn()){
System.out.printf("%s가 꺼져있어 온도를 조절할 수 없습니다.\n", getName());
}
else {
System.out.printf("%s가 온도를 %d로 조정합니다.\n", getName(), degree);
}
}
}