오늘의 학습 키워드
- 배열
- 컬렉션
- 객체와 클래스
- 상속
- 인터페이스
배열
- 자바에서의 배열 선언방식 (2가지)
// 일반적인 선언방식
int[] intArray; // 정수 배열
long[] longArray;
double[] doubleArray; // 실수 배열
char[] charArray; // 문자 배열
String[] stringArray; // 문자열 배열
// 2번째 선언방식
int intArray[]; // 정수 배열
long longArray[];
double doubleArray[]; // 실수 배열
char charArray[]; // 문자 배열
String stringArray[]; // 문자열 배열
- 배열의 생성
참조형 변수들처럼 new를 통해 생성, [ ] 통해 크기 지정 가능
//배열 생성후 초기화하면 배열의 주소가 할당된다.
int[] intArray = new int[3]; // 초기값 {0,0,0}
boolean[] boolArray = new boolean[3]; // 초기값 {false, false, false}
String[] stringArray = new String[3]; // 초기값 {"","",""}
//배열 선언만 해놓고 나중에 초기화를 시킬수도 있다.
int[] intArray2;
intArray2 = new int[3]; // 초기값 {0,0,0}
- 배열의 초기화 (3가지 방식)
- 중괄호 { }를 사용해서 초기화 하는 방식
- 반복문 for 문을 사용해서 초기화 하는 방식 (또는 향상된 for문)
- Arrays.fill 메소드를 사용해서 초기화 하는 방식
import java.util.Arrays; // Arrays 클래스를 import 해주세요!
public class Main {
public static void main(String[] args) {
//1. 배열에 특정값 대입하며 선언
int[] intArray = {1, 2, 3, 4, 5};
String[] stringArray = {"a", "b", "c", "d"};
//2-1. for문을 통해 값을 대입
for (int i = 0; i < intArray.length; i++) {
intArray[i] = i;
}
//2-2. 향상된 for문을 통한 배열 출력
for (int i : intArray) {
System.out.print(i); // 01234
}
//3. 배열의 주소를 모두 같은값으로 초기화
Arrays.fill(intArray, 1);//배열의 모든 값을 1로 초기화
for (int i : intArray) {
System.out.print(i); // 11111
}
}
}
- 얕은 복사와 깊은 복사
배열은 참조형 변수이기 때문에 변수에 실제 값이 아닌 실제값의 주소값을 가진다
따라서 배열 변수 간 = 연산자를 사용해서 복사를 하게 되면 주소값만 복사해 대입하게 된다
결과적으로 주소값이 같아짐( 같은 주소를 통해 실제값을 공유함 ) 현상을 얕은 복사라고 한다
깊은 복사는 반대로 주소값은 다르게 가지고 그 실제 값만 같게 복사되는 것
깊은복사 하는법
int[] a = { 1, 2, 3, 4 };
int[] b = new int[a.length];
for (int i = 0; i < a.length; i++) {
b[i] = a[i]; // 깊은 복사
}
b[0] = 3; // b 배열의 0번째 순번값을 3으로 수정했습니다. (1 -> 3)
System.out.println(a[0]); // 출력 1 <- 깊은 복사를 했기때문에 a 배열은 그대로 입니다.
- 다차원배열(가변배열)
Java에서는 2차원 배열을 생성할 때 열의 길이를 생략하여, 행마다 다른 길이의 배열을 요소로 저장할 수 있다
// 선언 및 초기화
int[][] array = new int[3][];
// 배열 원소마다 각기다른 크기로 지정 가능합니다.
array[0] = new int[2];
array[1] = new int[4];
array[2] = new int[1];
// 중괄호 초기화할때도 원소배열들의 크기를 각기 다르게 생성 가능합니다.
int[][] array2 = {
{10, 20},
{10, 20, 30, 40},
{10}
};
컬렉션
다수의 참조형 데이터를 배열보다 더 쉽고 효과적으로 처리할 수 있는 기능을 가지고 있는 집합
- 컬렉션의 종류
- List [ ArrayList, LinkedList, Stack ]
- Set [ HastSet, TreeSet 과 연계 ] ( 생성자 x )
- Queue [ LinkedList 와 연계 ] ( 생성자 x )
- Map [ HashMap, TreeMap 과 연계 ]
- 각 컬렉션 선언 및 생성
ArrayList<Integer> intList = new ArrayList<Integer>(); // ArrayList 선언 및 생성
LinkedList<Integer> linkedList = new LinkedList<>(); // LinkedList 선언 및 생성
Stack<Integer> intStack = new Stack<Integer>(); // Stack 선언 및 생성
Queue<Integer> intQueue = new LinkedList<Integer>(); // Queue 선언 및 생성
Set<Integer> intSet = new HashSet<Integer>(); // Set 선언 및 생성
Map<String, Integer> intMap = new HashMap<>(); // Map 선언 및 생성
- 컬렉션 특징정리
>> Array 와 ArrayList는 정적배열, 동적배열의 차이 ( 동적배열은 배열의 크기지정이 유동적이다
ex) 실행중 크기 늘리기 )
>> LinkedList는 조회는 ArrayList보다 느리나 값의 추가나 삭제는 더 빠르다
>> Stack은 최근 저장된 데이터를 나열하고 싶거나, 데이터 중복 처리를 막고 싶을 때 유용하다
>> Queue는 Stack과 거의 유사, FIFO 부분만 차이가 있고, 생성자가 없다
>> Set은 순서가 없고, 중복이 없는 자료들의 집합, 생성자가 없다
(순서가 없기때문에 향상된 for문 등 사용시 입력 데이터 순서가 보장안된다)
>> Map은 key와 value 페어를 가진 자료집합 key값은 유니크함이 보장되어야 하며 동일 키 값으로 값이 들어오면
가장 나중값이 기존값을 덮어쓰는식으로 동작
- 2주차 HW 코드
public static void main(String[] args) {
LinkedList<String> recipeList = new LinkedList<String>();
Set<String> recipeSet = new LinkedHashSet<>(); // 기존 Hashset은 순서보장X
Map<Integer,String> recipeMap = new HashMap<>();
int recipeNum = 0;
Scanner scan = new Scanner(System.in);
System.out.print("자료구조명 입력: ");
String text = scan.nextLine();
String recipeName = "";
String recipe = "";
switch (text) {
case "List" -> {
System.out.print("레시피 제목 입력: ");
recipeName = scan.nextLine();
recipeList.add(recipeName);
}
case "Set" -> {
System.out.print("레시피 제목 입력: ");
recipeName = scan.nextLine();
recipeSet.add(recipeName);
}
case "Map" -> {
System.out.print("레시피 제목 입력: ");
recipeName = scan.nextLine();
recipeMap.put(0,recipeName);
}
default -> {
System.out.println("잘못된 입력입니다");
return;
}
}
switch(text){
case "List" ->{
while(!recipe.equals("끝"))
{
System.out.println(++recipeNum + ". 레시피입력:");
recipe = scan.nextLine();
if(!recipe.equals("끝"))
recipeList.add(recipe);
}
}
case "Set" ->{
while(!recipe.equals("끝"))
{
System.out.println(++recipeNum + ". 레시피입력:");
recipe = scan.nextLine();
if(!recipe.equals("끝"))
recipeSet.add(recipe);
}
}
case "Map" ->{
while(!recipe.equals("끝"))
{
System.out.println(++recipeNum + ". 레시피입력:");
recipe = scan.nextLine();
if(!recipe.equals("끝"))
recipeMap.put(recipeNum,recipe);
}
}
default -> {}
}
switch (text) {
case "List" -> {
int i=0;
for(String str : recipeList){
if(i == 0) {
System.out.println(text +" 으로 저장된 " + str);
i++;
}
else {
System.out.println(i + ". " + str);
i++;
}
}
}
case "Set" -> {
int i=0;
for(String str : recipeSet){
if(i == 0) {
System.out.println(text +" 으로 저장된 " + str);
i++;
}
else {
System.out.println(i + ". " + str);
i++;
}
}
}
case "Map" -> {
int i=0;
for(String str : recipeMap.values()){
if(i == 0) {
System.out.println(text +" 으로 저장된 " + str);
i++;
}
else {
System.out.println(i + ". " + str);
i++;
}
}
}
default -> {
}
}
}
자바의 switch문은 c계열과 달리 문자열로 case 지정이 가능해서 매우 편하게 사용할 수 있었다
추가로 향상된 switch문 -> 을 통해 가독성 증가
객체와 클래스
- 객체지향에 대해
객체지향 프로그래밍의 4가지 필수개념, 캡슐화 / 상속 / 다형성 / 추상화
객체(인스턴스)를 생성하기 위해서 설계도가 필요하다, 그 설계도가 바로 클래스
- 필드와 생성자와 메소드
클래스의 구성 멤버는 필드, 생성자, 메소드가 있다
여기서 필드는 객체가 가지고 있어야 할 속성 (데이터 타입을 가지고 있는 값) 을 선언한 영역
생성자는 객체를 생성하는 방식을 정의하는 것이다
객체가 가지고 있어야 할 행위 / 또는 할수 있는 기능(행위)를 메소드에 정의한다
- 클래스의 멤버(인스턴스 vs 클래스)
멤버 = 필드 + 메서드
- 인스턴스 멤버 = 인스턴스 필드 + 인스턴스 메서드
- 클래스 멤버 = 클래스 필드 + 클래스 메서드
인스턴스 멤버 & 클래스 멤버
- 필드와 메서드는 선언하는 방법에 따라서 인스턴스 멤버와 클래스 멤버로 구분할 수 있다
- 인스턴스 멤버는 객체 생성 후에 사용할 수 있고 클래스 멤버는 객체 생성 없이도 사용할 수 있다
//클래스는 Java의 클래스 로더에 의해 메서드 영역에 저장되고 사용됩니다.
// 클래스 멤버 선언 Car 클래스
static String company = "GENESIS"; // 자동차 회사 : GENESIS
String getCompany() {
return "(주)" + company;
}
// 클래스 멤버 사용
Car.company = "Audi";
String companyName = Car.setCompany("Benz");
final은 ‘최종적’ 이라는 의미
Java에서 상수로 지정하는 키워드로 사용되며 반드시 초기값을 지정해야한다
final String company = "GENESIS"; // 상수 선언
.....
Car car = new Car();
System.out.println(car.company);
- 생성자의 생성 및 활용, (+ - this와 this())
객체 초기화의 역할, 기본생성자: 사용자가 생성자를 선언하지 않는다면 컴파일러가 바이트코드 파일에 자동으로 추가시 킨다 반대로 하나라도 생성자가 선언되어 있다면 기본생성자는 자동생성 안된다
this() 는 자신의 생성자를 호출하는 키워드
// 코드중복이 심한 코드 -> this() 를 활용해 중복을 줄일 수 있다.
public Car(String model) {
this.model = model;
this.color = "Blue";
this.price = 50000000;
}
public Car(String model, String color) {
this.model = model;
this.color = color;
this.price = 50000000;
}
public Car(String model, String color, double price) {
this.model = model;
this.color = color;
this.price = price;
}
// 이런식
public Car(String model) {
this(model, "Blue", 50000000);
}
public Car(String model, String color) {
this(model, color, 100000000);
}
public Car(String model, String color, double price) {
this.model = model;
this.color = color;
this.price = price;
}
this() 키워드를 사용해서 다른 생성자를 호출할 때는 반드시 해당 생성자의 첫 줄에 작성되어야 합니다.
- 그외 기타
접근 제어자 : public, protected, default, private
기타 제어자 : static, final, abstract
패키지란 클래스의 일부분이면서 클래스를 식별해 주는 용도이다
패키지는 상위 패키지와 하위 패키지를 도트(.)로 구분한
// 상위패키지 oop의 pk1 패키지와 pk2 패키지, 똑같은 Car클래스가 있지만 다르게 구분가능
package oop.main;
public class Main {
public static void main(String[] args) {
oop.pk1.Car car = new oop.pk1.Car();
car.horn(); // pk1 빵빵
oop.pk2.Car car2 = new oop.pk2.Car();
car2.horn(); // pk2 빵빵
}
}
import는 다른 패키지에 있는 클래스를 사용하기 위해 명시하는 키워드
- 위에서 살펴본 oop.pk1이라는 패키지와 oop.pk2라는 패키지를 import로 명시한다면..
- import oop.pk1.Car;, import oop.pk2.Car;
- 클래스 이름을 생략하고 *를 사용하여 import oop.pk1.*; 이렇게 표현하면 oop.pk1 패키지 아래에 있는 모든 클래스를 사용할 수 있다
상속
- 클래스 간의 관계와 상속
상속을 적절히 사용하기 위해 구분해야할 관계 -> 상속관계 ( is - a ) 와 포함관계 (has - a ) 의 차이
여기서 포함관계의 경우 상속을 해야하는지 햇갈리기 쉬운데 포함관계의 경우 상속을 하지 않는다
예시
상속관계 :
고래와 포유류의 관계 > 고래는 포유류다 O <상속> , 고래는 포유류를 가지고 있다 X < 포함 >
포함관계 :
자동차와 타이어, 핸들 의 관계 > 자동차는 타이어(or 핸들이다) X <상속>,
자동차는 타이어를 가지고있다 O < 포함 >
Java는 다중상속을 허용 X
final Class 와 final 메소드 = 클래스에 final 키워드를 지정하여 선언시 최종적인(더이상 수정못하는) 것이 되므로 자식이 상속할 수 없게된다 (왜냐하면 상속이라는 것 자체가 자식이 받아서 재정의 하기위함이 목적이기 때문)
- 오버라이딩
부모클래스로부터 상속받은 메소드의 내용을 재정의 하는 것을 오버라이딩이라 한다
오버라이딩을 하기 위해서는 아래 조건들을 만족해야 합니다.
1. 선언부가 부모 클래스의 메서드와 일치해야 합니다.
2. 접근 제어자를 부모 클래스의 메서드 보다 좁은 범위로 변경할 수 없습니다.
3. 예외는 부모 클래스의 메서드 보다 많이 선언할 수 없습니다.
public class SportsCar extends Car{
String engine;
public void booster() {
System.out.println("엔진 " + engine + " 부앙~\n");
}
public SportsCar(String engine) {
this.engine = engine;
}
@Override << 오버라이딩시 이것 꼭 써줘야함, (애노테이션)
public double brakePedal() {
speed = 100;
System.out.println("스포츠카에 브레이크란 없다");
return speed;
}
@Override
public void horn() {
booster();
}
}
super 와 super() : this 와 this() 의 경우와 똑같은 범주, 대신 부모클래스의 멤버를 참조할 수 있다
자식클래스의 객체가 생성될 때 부모클래스 멤버들의 초기화 작업이 먼저 수행되야함 (생성자가 먼저실행)
따라서 자식클래스 생성자 내부에서 가장먼저 부모클래스 생성자가 실행된다 (super 사용 안하면 자동삽입 실행)
- 다형성
참조 변수의 타입 변환 (자동 / 강제 )
부모 타입 변수 = 자식 타입 객체; 는 자동으로 부모 타입으로 변환이 일어난다
- 자식 객체는 부모 객체의 멤버를 상속받기 때문에 부모와 동일하게 취급될 수 있다.
- 예를 들어 포유류 클래스를 상속받은 고래 클래스가 있다면 포유류 고래 = 고래 객체; 가 성립될 수 있다
- 왜냐하면 고래 객체는 포유류의 특징인 모유 수유 행위를 가지고 있기 때문
- 다만 주의할 점은 부모 타입 변수로 자식 객체의 멤버에 접근할 때는 부모 클래스에 선언된 즉, 상속받은 멤버만 접근할 수 있다.
// 자동 변환이 되는 경우 = 부모타입 = 자식타입;
public static void main(String[] args) {
// 고래는 포유류이기 때문에 포유류 타입으로 변환될 수 있습니다.
Mammal mammal = new Whale();
// 하지만 포유류 전부가 바다에 살고 수영을 할 수 있는 것은 아니기 때문에
// 수영 하다 메서드는 실행 불가
// 즉, 부모 클래스에 swimming이 선언되어있지 않아서 사용 불가능합니다.
mammalia.swimming(); // 오류 발생
// 반대로 모든 포유류가 전부 고래 처럼 수영이 가능한 것이 아니기 때문에 타입변환이 불가능합니다.
// 즉, 부모타입의 객체는 자식타입의 변수로 변환될 수 없습니다.
Whale whale = new Mammal(); // 오류 발생
mammal.feeding();
}
- 다만 무조건 강제 타입 변환을 할 수 있는 것은 아닙니다.
- 자식 타입 객체가 부모 타입으로 자동 타입 변환된 후 다시 자식 타입으로 변환될 때만 강제 타입 변환이 가능합 니다.
- 부모 타입 변수로는 자식 타입 객체의 고유한 멤버를 사용할 수 없기 때문에 사용이 필요한 경우가 생겼을 때 강 제 타입 변환을 사용합니다.
// 강제 타입 변환을 해야하는 경우 : 자식 타입 = 부모 타입;
// 자식타입객체가 자동 타입변환된 부모타입의 변수
Mammal mammal = new Whale();
mammal.feeding();
// 자식객체 고래의 수영 기능을 사용하고 싶다면
// 다시 자식타입으로 강제 타입변환을 하면된다.
Whale whale = (Whale) mammal;
whale.swimming();
위 개념을 활용하여 코드의 '다형성' 을 구현할 수 있다.
public Car(Tire tire) {
this.tire = tire;
}
...
Car car1 = new Car(new KiaTire("KIA"));
Car car2 = new Car(new HankookTire("HANKOOK"));
위의 car는 생성자로 Tire (기아, 한국 타이어의 부모클래스)를 받는데 이를 통해
car 인스턴스는 Tire에서 공통으로 제공하는 메소드 ex) getTireName() 등을
그저 특별한 호출이나 지정 없이 car1.getTireName() , car2.getTireName() 로 서로 다른
타이어 정보를 얻을 수 있다
만약 Tire라는 부모클래스없이 직접 KiaTire와 HankookTire를 받았다면 Car 내부에 Kia, Hankook
두 클래스 타입을 필드로 가지고 있어야함 (만약 타이어 종류가 100가지라면? 100가지를 모두 필드에..
- Instance of 명령어 : 다형성 기능으로 인해 해당 클래스 객체의 원래 클래스를 체크하고 싶을 때 사용(True/false 반환)
인터페이스
인터페이스는 두 객체를 연결해 주는 다리 역할
예시
사람과 삼성티비, 엘지티비 객체가 존재한다고 생각해 보겠습니다. 사람 객체는 멀티 리모컨 인터페이스를 통해서 삼성티비 객체의 채널을 변경할 수 있습니다. 이때 삼성티비가 아니라 엘지티비로 객체가 교체된다고 해도 채널을 변경할 수 있습니다.
+
상속 관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야 할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장해 줄 수 있습니다.
인터페이스의 모든 멤버 변수는 public static final 이어야 한다 (생략가능)
모든 메소드는 public abstract 이어야 한다
인터페이스 간의 상속이 가능 (인터페이스간 상속은 implements가 아니라 extends 키워드 사용)
다중상속 가능
- 자동 타입 변환
인터페이스도 클래스의 경우와 마찬가지로 구현체(인터페이스를 implements 받은 클래스 객체)와의 타입 변환규칙 동일
public class Main {
public static void main(String[] args) {
// A 인터페이스에 구현체 B 대입
A a1 = new B();
a1.a();
// a1.b(); // 불가능
System.out.println("\nB 강제 타입변환");
B b = (B) a1;
b.a();
b.b(); // 강제 타입변환으로 사용 가능
System.out.println();
// A 인터페이스에 구편체 B를 상속받은 C 대입
A a2 = new C();
a2.a();
//a2.b(); // 불가능
//a2.c(); // 불가능
System.out.println("\nC 강제 타입변환");
C c = (C) a2;
c.a();
c.b(); // 강제 타입변환으로 사용 가능
c.c(); // 강제 타입변환으로 사용 가능
interface A {
void a();
}
class B implements A {
@Override
public void a() {
System.out.println("B.a()");
}
public void b() {
System.out.println("B.b()");
}
}
class C extends B {
public void c() {
System.out.println("C.c()");
}
}
}
}
- 디폴트 메소드와 static 메소드
디폴트 메소드는 추상메소드의 기본적인 구현을 제공하는 메소드
인터페이스에 정의 됐다면 implements한 클래스에서 override 사용하지 않고 바로 사용가능
static 메소드: 디폴트와 마찬가지인데 대신 인터페이스 이름으로 바로 접근해서 사용
- 3주차 HW 코드
//컬큘레이터 클래스 내용 (단계별 수정사항 반영)
public class Calculator {
/*
클래스간 결합도, 의존성 역전 원칙 지키기
현재 코드는 각 4개의 클래스로 책임(기능)은 분할됐지만 4개의 클래스는 실제 기능구현을
하고있는 저수준 모듈(=클래스, 특징: 빈번한 수정가능성) 이므로 그 수정사항이 이 클래스에도
영향을 줄수있다 => 의존성이 높다 그러므로 추상화 계층을 중간다리로 하여 의존성을 낮출 필요가 있음
*/
// 의존성 문제 유발!
// AddOperation addOper;
// SubstractOperation subOper;
// MultiplyOperation multiOper;
// DivideOperation divideOper;
AbstractOperation operation;
Calculator(AbstractOperation oper) {
operation = oper;
}
// public double calculate(String operator, int firstNumber, int secondNumber){
// double result = 0;
//단일 책임원칙 무시, 모든 사칙연산 기능(책임)을 이 클래스에서 구현한코드, 좋지않다
// switch(operator){
// case "+" -> result = firstNumber+secondNumber;
// case "-" -> result = firstNumber-secondNumber;
// case "*" -> result = firstNumber*secondNumber;
// case "/" -> result = (double)firstNumber/secondNumber;
// case "%" -> result = firstNumber%secondNumber;
// default -> System.out.println("잘못된 연산자입니다");
// }
// return result;
//사칙연산 클래스를 따로 만들어서 책임(기능) 분산 성공
// but 의존성 문제가 있음
// double result = 0;
// switch(operator) {
// case "+" -> result = addOper.operate(firstNumber, secondNumber);
// case "-" -> result = subOper.operate(firstNumber, secondNumber);
// case "*" -> result = multiOper.operate(firstNumber,secondNumber);
// case "/" -> result = divideOper.operate(firstNumber, secondNumber);
// case "%" -> result = firstNumber % secondNumber;
// default -> System.out.println("잘못된 연산자입니다");
// }
// return result;
// }
public double calculate(int firstNumber, int secondNumber) {
double result = 0;
result = operation.operate(firstNumber,secondNumber);
return result;
}
}
public abstract class AbstractOperation {
public abstract double operate(int firstNum, int secondNum);
}
public class AddOperation extends AbstractOperation {
@Override
public double operate(int firstNum, int secondNum){
return firstNum+secondNum;
}
}
public class SubstractOperation extends AbstractOperation {
@Override
public double operate(int firstNum, int secondNum){
return firstNum-secondNum;
}
}
public class MultiplyOperation extends AbstractOperation {
@Override
public double operate(int firstNum, int secondNum){
return firstNum*secondNum;
}
}
public class DivideOperation extends AbstractOperation {
@Override
public double operate(int firstNum, int secondNum){
return (double)firstNum/secondNum;
}
}
// 메인 실행 코드 부분
public static void main(String[] args){
AbstractOperation operation;
Calculator calculator;
double result = 0;
int firstNum = 0, secondNum = 0;
String operator;
Scanner scan = new Scanner(System.in);
System.out.print("원하는 연산을 입력해주세요: ");
operator = scan.nextLine();
System.out.print("피연산자를 입력해주세요: ");
firstNum = scan.nextInt();
System.out.print("피연산자를 입력해주세요: ");
secondNum = scan.nextInt();
switch(operator){
case "+" -> operation = new AddOperation();
case "-" -> operation = new SubstractOperation();
case "*" -> operation = new MultiplyOperation();
case "/" -> operation = new DivideOperation();
default -> {
System.out.println("잘못된 연산자입니다");
return;
}
}
calculator = new Calculator(operation);
result = calculator.calculate(firstNum,secondNum);
System.out.println("결과: " + result);
}
강의 햇갈렸던 부분
// 클래스 C 구현체를 조작
A test1 = new C();
test1.numChange();
// 조작 대상을 D로 교체
test1 = new D();
test1.numChange();
// LG TV 구현체를 조작
MultiRemoteController mrc = new LgTv("LG");
mrc.turnOnOff();
mrc.volumeUp();
mrc.channelDown();
mrc.channelUp();
mrc.turnOnOff();
// 조작 대상을 Samsung TV로 교체
System.out.println("\n<Samsung TV로 교체>");
mrc = new SamsungTv("Samsung");
mrc.turnOnOff();
mrc.channelUp();
mrc.volumeDown();
mrc.volumeUp();
mrc.turnOnOff();
실행창
LG Tv 전원이 켜졌습니다.
현재 볼륨은 1
현재 채널은 0
현재 채널은 1
LG Tv 전원이 종료되었습니다.
<Samsung TV로 교체>
Samsung Tv 전원이 켜졌습니다.
현재 채널은 2 << 하필 요놈때문에 좀 햇갈렷는데 위 코드를 보니 삼성은 채널다운이 없어서 그냥 그랫던것
현재 볼륨은 0
현재 볼륨은 1
Samsung Tv 전원이 종료되었습니다.
강의 도중 LgTv로 조작하다 SamsumgTv로 조작시 부모필드인 TV값이 유지된다는 부분?의 설명이 있었는데
했갈렸다, new로 생성된 LgTv()와 SamsungTv()의 부모인 Tv의 필드값을 위 코드에서 mrc로 서로 바꿔가며 동작할 때
Tv 필드를 공유한다는 쪽으로 들리다보니 이부분이 햇갈렸는데 테스트 결과 각 부모필드는
역시 따로따로 존재한다는부분 확인할수 있었음
'TIL > Web Back' 카테고리의 다른 글
[Sparta] 내일배움캠프 6일차 TIL (0) | 2024.04.26 |
---|---|
[Sparta] 내일배움캠프 5일차 TIL (0) | 2024.04.25 |
[Sparta] 내일배움캠프 4일차 TIL (0) | 2024.04.24 |
[Sparta] TIL 2일차 (0) | 2024.04.22 |
[Sparta] TIL 1일차 (1) | 2024.04.19 |