📍 24.04.11(목) ~ 24.04.12(금) : 상속, 오버라이딩, IS-A/HAS-A 관계, 다형성, package/import/접근제어자, 추상클래스, 인터페이스, try ~ catch ~ finally, throws, Thread
📍 24.04.22(월) ~ 24.04.26(금) : singleton, enum, generic
📍 24.04.11(목) 12일차
this / this() / super / super()
this | 참조변수 ex) this.멤버변수, this.멤버메소드() |
this.멤버변수 this.멤버메소드() |
this() | 생성자 호출 | |
super | 참조변수 | super.부모의 멤버변수 super.부모의 멤버메소드() |
super() | 부모의 생성자 호출 |
super 컴파일러 실행될 때 선언이 됨
상속
상속을 하기 위해 부모 클래스와 자식 클래스를 정의해보자
일단 부모 클래스인 Parent 클래스
필드와 생성자, 메소드로 정의해주었고,
생성자에는 인스턴스가 생성될 때
인스턴스를 생성할 때 매개변수를 받지 않을 때 실행되는 기본 생성자에는
필드에 있는 money 를 1_000_000 로 값 설정과 동시에 "[Parent] 매개 변수가 없는 생성자 실행됨" 문구가 출력될 것이다.
또 매개변수 1개를 받을 땐,
필드에 있는 money 를 1_000_000 로 값 설정과 동시에 "[Parent] 매개 변수가 1개인 생성자 실행됨" 문구가 출력되게 정의했다.
class Parent {
// field
int money;
// Constructor
public Parent() {
this.money = 1_000_000;
System.out.println("[Parent] 매개변수가 없는 생성자 실행됨");
}
public Parent(int money) {
this.money = money;
System.out.println("[Parent] 매개변수가 1개인 생성자 실행됨");
}
// method
void buy(int money) {
this.money -= money;
System.out.println("남은 돈은 " + this.money + "원 입니다.");
}
}
그 다음으로 자식 클래스인 Child 클래스
상속 받기 위해선 extends 를 사용해 누구로부터 상속받을지 정의해준다
class Child extends Parent {
//
}
자식 클래스인 Child 는
기본 생성자와 매개변수 1개 받는 생성자, 그리고 showMoney() 메소드를 정의해줬다
class Child extends Parent {
public Child() {
super(); // heap 영역에 할당, 부모의 생성자 중 매개 변수가 없는 생성자를 찾아감
System.out.println("[Child] 매개변수가 없는 생성자 실행됨");
}
public Child (int money) {
super(money); // 부모 생성자 호출
// super.money = money;
System.out.println("[Child] 매개변수가 1개인 생성자 실행됨");
}
void showMoney() {
System.out.println("부모님 돈 : " + super.money);
}
// super 를 빼도 되는 이유 : 지역변수를 찾아감, 호출한 메소드의 지역변수를 찾고, 없으면 부모의 메소드에서 찾음
// Child() 에 money 선언 + super 뺌 => super 호출 x
// super 를 씀으로써 부모의 멤버변수라는 것을 바로 알 수 있음
}
이제 main() 에서 원하는 값을 한 번 출력해보자
어디 부분에서 무엇이 실행되고 출력이 되는데 1번째 ~ 4번째로 나누었다
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("1번째");
Child c1 = new Child();
System.out.println("2번째");
c1.showMoney();
System.out.println("3번째");
Child c2 = new Child(500_000); // 부모의 매개변수가 없는 생성자 호출 -> 자식의 매개변수 1개인 생성자 호출
System.out.println("4번째");
c2.showMoney();
}
}
일단 1번째
System.out.println("1번째");
Child c1 = new Child();
이 부분은 무엇이 출력이 될까 생각해봐야한다
Child 타입의 c1 에 Child 타입으로 생성했다 , 매개 변수 없이 넣었으니
class Child extends Parent {
public Child() {
super(); // heap 영역에 할당, 부모의 생성자 중 매개 변수가 없는 생성자를 찾아감
System.out.println("[Child] 매개변수가 없는 생성자 실행됨");
}
...
}
이 부분이 실행될 것이다
근데 super() 의 의미가 뭘까 !
super() 메소드는 부모의 생성자 호출로,
heap 영역에 할당되면서 부모의 생성자 중 매개 변수가 없는 생성자를 찾아간다.
class Parent {
// field
...
// Constructor
public Parent() {
this.money = 1_000_000;
System.out.println("[Parent] 매개변수가 없는 생성자 실행됨");
}
...
}
그러면 이제 money 에 100000 이 값 설정 되면서 "[Parent] 매개 변수가 없는 생성자 실행됨" 문구 출력과 실행이 끝났으니
super(); 파트는 끝났다
이제 다음 줄로 "[Child] 매개변수가 없는 생성자 실행됨" 문구가 출력되면서 끝난다.
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("1번째");
Child c1 = new Child();
}
# 실행 결과
1번째
[Parent] 매개변수가 없는 생성자 실행됨
[Child] 매개변수가 없는 생성자 실행됨
이제 다음으로 2번째
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("1번째");
Child c1 = new Child();
System.out.println("2번째");
c1.showMoney();
}
}
c1 이 Child 타입으로 만들어졌다 했으니 Child 로 가서 showMoney () 메소드 부분을 확인해보자
class Child extends Parent {
...
void showMoney() {
System.out.println("부모님 돈 : " + super.money);
}
}
이전에 super() 는 부모의 생성자 호출, 그렇담 super. 는 뭘까
super. 는 참조변수라 생각하면 된다
부모의 필드에 money 가 있었는데 그걸 가져온다고 생각하면 된다
1번째 실행했을 때 인스턴스를 만들면서 값 설정을 했으니 그 값이 출력되면 된다
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("1번째");
Child c1 = new Child();
System.out.println("2번째");
c1.showMoney();
}
}
# 실행 결과
1번째
[Parent] 매개변수가 없는 생성자 실행됨
[Child] 매개변수가 없는 생성자 실행됨
2번째
부모님 돈 : 1000000
자 이제 3번째
c1 과 c2 는 다른 인스턴스이니 c1 부분은 삭제하고 !
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("3번째");
Child c2 = new Child(500_000); // 부모의 매개변수가 없는 생성자 호출 -> 자식의 매개변수 1개인 생성자 호출
}
}
이번엔 인스턴스 만들 때 매개변수가 1개를 받는다
이와 같이 Child 클래스 가서 매개변수 1개인 생성자를 확인 한다
this.money = money; 본인 클래스에 필드에 지정해주는 것처럼
부모클래스 필드에 값설정해주는 거다
근데
super() 하면 매개변수가 없는 생성자
super(매개변수) 하면 매개변수가 1개인 생성자
super(매개변수, 매개변수) 하면 매개변수 2개인 생성자
지금 코드를 보면 super( money) 이
public Child (int money) 에서 받은 money 가 들어간 것 !
class Parent {
// field
int money;
// Constructor
...
public Parent(int money) {
this.money = money;
System.out.println("[Parent] 매개변수가 1개인 생성자 실행됨");
}
}
class Child extends Parent {
...
public Child (int money) {
super(money); // 부모 생성자 호출
// super.money = money;
System.out.println("[Child] 매개변수가 1개인 생성자 실행됨");
}
...
}
그렇담 출력값은
super() 했으니까 부모부터 간 거 명심 !
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("3번째");
Child c2 = new Child(500_000); // 부모의 매개변수가 없는 생성자 호출 -> 자식의 매개변수 1개인 생성자 호출
}
}
# 실행 결과
3번째
[Parent] 매개변수가 1개인 생성자 실행됨
[Child] 매개변수가 1개인 생성자 실행됨
이제 4번째
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("3번째");
Child c2 = new Child(500_000); // 부모의 매개변수가 없는 생성자 호출 -> 자식의 매개변수 1개인 생성자 호출
System.out.println("4번째");
c2.showMoney();
}
}
자식 클래스에 정의된 메소드를 확인해보면
3번째에서 c2 를 만들면서 값설정을 해주었다
그래서 500000 이 저장되어있으니 부모님의 돈은 500000 가 된다
class Child extends Parent {
...
void showMoney() {
System.out.println("부모님 돈 : " + super.money);
}
...
}
public class InheritanceEx01 {
public static void main(String[] args) {
System.out.println("3번째");
Child c2 = new Child(500_000); // 부모의 매개변수가 없는 생성자 호출 -> 자식의 매개변수 1개인 생성자 호출
System.out.println("4번째");
c2.showMoney();
}
}
# 실행 결과
3번째
[Parent] 매개변수가 1개인 생성자 실행됨
[Child] 매개변수가 1개인 생성자 실행됨
4번째
부모님 돈 : 500000
상속 관계 : 부모의 생성자는 매개변수가 있지만, 자식의 생성자는 매개변수가 없을 때
class Super {
int num;
Super() { // ***
}
Super(int num) {
this.num = num;
}
void show() {
System.out.println(num);
}
}
class Sub extends Super {
// 아무것도 정의 안 했을 때
// : Implicit super constructor Super() is undefined for default constructor. Must define an explicit constructor
// : 상속 관계가 되면 기본 생성자에 super(); 가 되는데, 묵시적으로 호출되는 부모의 메소드에 기본 생성자가 없어서
// 부모의 메소드에 기본 생성자를 정의해줘야함 (+ *** 이 부분 추가)
// : 아래 코드 작성
}
public class InheritanceEx02 {
public static void main(String[] args) {
Super s = new Super(5);
System.out.println(s);
System.out.println(s.num);
}
}
# 실행 결과
Super@2f92e0f4
5
상속 관계 : 부모를 2개 둘 때
단일 상속 : 한 클래스에 하나의 부모를 둘 수 있음
다중 상속 : 허용 x
문제가 생기는 경우
부모의 메소드에서 같은 멤버변수가 있을 경우 문제가 생길 수 있음
다른 부모를 두고 싶을 때
상속을 안해도, 가져올 수 있음
class GrandParent {
//
}
class Parent {
//
}
class Child extends Parent, GrandParent { // Syntax error !
//
}
class ParentTwo {
int money = 1_000_000;
void buy (int moneny) {
this.money -= moneny;
System.out.println("남은 돈은 " + this.money + "원입니다.");
}
}
class ChildTwo extends ParentTwo{
void study() {
System.out.println("저는 공부하고 있습니다.");
}
// 오버로딩은 상속을 받지 않아도 오버로딩이 되어서 가능
void buy() { // 부모의 buy (int moneny) 과도 자식의 buy(int money) 도 오버로딩
System.out.println("<< method overloading >>");
System.out.println("용돈 조 ! 용돈조 !!!");
}
void buy(int money) {
System.out.println("<< method overriding >>");
System.out.println("부모님 용돈 " + money + "원 주세요 !");
}
}
public class InheritanceEx03 {
public static void main(String[] args) {
ChildTwo c1 = new ChildTwo();
c1.study();
c1.buy(100_000); // 실행 순서 : 자식 메소드 -> 부모 메소드
c1.buy();
}
}
# 실행 결과
저는 공부하고 있습니다.
<< method overriding >>
부모님 용돈 100000원 주세요 !
<< method overloading >>
용돈 조 ! 용돈조 !!!
대표 문자열 return 해주기
class Student { // 원래는 최상위 부모를 상속 받는 것 (object)
@Override
public String toString() {
return "Student class ...";
}
}
public class InheritanceEx04 {
public static void main(String[] args) {
Student s = new Student();
System.out.println(s);
System.out.println(s.toString());
}
}
# 실행 결과
Student class ...
Student class ...
어노테이션 @
1. 컴파일러에게 문법 에러를 체크하도록 정보를 제공한다.
2. 프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
3. 런타임에 특정 기능을 실행하도록 정보를 제공한다.
error !
: The method toString(int) of type Student must override or implement a supertype method
: toString(int num) 이 부분 에러
: 오버로딩 형태가 아니게 됨
@Override
public String toString(int num) {
return "Student class ...";
}
https://ittrue.tistory.com/156
상속 관계 : IS-A관계, HAS-A 관계
📍 24.04.12(금) 13일차
IS-A관계 / HAS-A 관계
포함관계(containment)라 말할 수 있음
class Gun {
int bullet; // 총알
public Gun(int bnum) {
bullet = bnum;
}
public void shoot() {
System.out.println("BBANG !");
bullet--;
}
}
// 상속 x / 포함관계
class Police {
// 필드 -> heap 영역
int handcuffs;
Gun pistol; // 참조 자료형 - 레퍼런스 타입, 필드에 참조변수가 온다면 객체에서 다른 객체를 관리한다는 것
// 생성자
public Police (int bnum, int bcuff) {
handcuffs = bcuff;
if (bnum != 0) {
pistol = new Gun(bnum);
} else {
pistol = null;
}
}
// 메소드
public void putHandcuff() {
System.out.println("SNAP");
handcuffs--;
}
public void shoot() {
if (pistol == null) {
System.out.println("Hut BBang");
} else {
pistol.shoot();
}
}
}
// 상속 o
//class Police extends Gun {
// int handcuffs; // 수갑
//
// public Police(int bnum, int bcuff) {
// super(bnum);
// handcuffs--;
// }
// public void putHandcuff() {
// System.out.println("SNAP !");
// handcuffs--;
// }
//}
public class InheritanceEx05 {
public static void main(String[] args) {
Police pman = new Police(5, 3);
pman.shoot();
pman.putHandcuff();
}
}
7) package 와 import, 접근 제어자
접근 제어자(접근 권한, 접근 제어 지시자) / access modifer
접근 제어자 | private (프라이빗) | default (디폴트) | protected (프로텍티드) | public (퍼블릭) |
접근 범위 | 클래스 내 | 같은 패키지 내 | 상속 다른 패키지일 때 상속이 허용함 |
누구나 |
비유 | 집 화장실 | 공중 화장실 |
IDE 에서 폴더와 패키지를 다르게 인식함
package : 관련이 있는 여러 class 파일 묶어서 관리
소문자로 지정, 한 단어
코드를 작성할 때 해당 파일에 반드시 패키지 선언
두 개 이상이 될 수 없음 (한 패키지만 사용 가능, 두개 안됨)
다른 파일에서 가져오는 방법
1. 객체 생성
2. 상속
다른 경로에 있는 파일 쓰고 싶을 때 : import
package two; // 현재 파일의 위치
import one.TestA; // 다른 타입의 위치(경로)
//import one.TestB; // 다른 타입의 위치(경로)
//import one.*; // 해당 타입의 모든 파일 가져오기(별표 아스테릭스)
public class TestC {
// field : 객체 생성
TestA a = new TestA();
// method
void show() {
// System.out.println(a.num1); // error ! private
// System.out.println(a.num2); // error ! default (같은 패키지 내)
// System.out.println(a.num3); // error ! protected (객체 생성에서 안됨)
System.out.println(a.num4); // public
}
}
같은 패키지에서 상속 (protected)
// 같은 패키지에서 상속
package one;
public class TestD extends TestA {
// method
void show() {
// System.out.println(num1); // error ! private
System.out.println(num2); // default (같은 패키지)
System.out.println(num3); // protected (같은 패키지 + 상속 )
System.out.println(num4); // public
}
}
다른 패키지에서 상속 (protected)
// 다른 패키지에서 상속 (protected)
package two;
import one.TestA;
public class TestE extends TestA {
// method
void show() {
// System.out.println(num1); // error ! private
// System.out.println(num2); // error ! default (같은 패키지가 아니라서)
System.out.println(num3); // protected (패키지가 달라도 상속이라서)
System.out.println(num4); // public
}
}
package per;
class Circle {
double rad;
final double Pl;
public Circle(double r) {
rad = r;
Pl = 3.14;
}
public double getPerimerter() {
return (rad*2) * Pl;
}
}
public class CirclePerimeter {
public static void main(String[] args) {
Circle c = new Circle(1.5);
System.out.println("반비름이 1.5인 원의 둘레 : " + c.getPerimerter());
}
}
# 실행 결과
반비름이 1.5인 원의 둘레 : 9.42
package area;
class Circle {
double rad;
final double Pl;
public Circle (double r) {
rad = r;
Pl = 3.14;
}
public double getArea() {
return (rad*rad) * Pl;
}
}
public class CircleArea {
public static void main(String[] args) {
Circle c = new Circle(1.5);
System.out.println("반비름이 1.5인 원의 넓이 : " + c.getArea());
}
}
# 실행 결과
반비름이 1.5인 원의 둘레 : 7.065
패키지 안에 패키지 만들기
Jar 파일
https://print-blue.tistory.com/151
6) 오버라이딩
/*
* [ 오버라이딩 ] // 상속 관계에서 나타남
*
* 자식의 오버라이딩된 메소드는/ 부모의 멤버 메서드의 접근 제어자보다/ 크거나 같아야 함
*
*/
에러 이유 : 상속 후 오버라이딩 할 때, 접근 권한이 감소하면 안돼서
private 예제
생성자가 private 일 경우 외부에서 인스턴스 생성을 금지한다는 것
만약 생성자가 private 일 경우 인스턴스 하나만 생성해서 쓴다는 것
ex) 스프링 할 때 내부적으로 오로지 하나의 인스턴스만 생성한게 싱글톤도 private 로 되어 있음
package modifier;
class Person {
private int age;
private String name;
public Person(int age, String name) {
// super(); // object 가 부모
this.age = age;
this.name = name;
}
}
public class Ex02 {
public static void main(String[] args) {
Person p = new Person(27, "jin");
// p.age = 26; // .age error ! private 의 접근 권한
}
}
8) 다형성
package polymorghism;
class Parent {
private int money = 1000000;
public void spend(int money) {
this.money -= money;
System.out.println("[Perent] 남은 돈은 " + this.money + "입니다");
}
public void work() {
System.out.println("[Perent] 열심히 일하는 중입니다.");
}
}
class Son extends Parent {
public void paly() {
System.out.println("[Son] 신나게 놀고 있어요 ~!");
}
@Override
public void spend(int money) {
System.out.println("[Son] 용돈 " + money + "원 주세요 ~~~");
}
}
class Daughter extends Parent {
public void study() {
System.out.println("[Daughter] 학원 공부하고 있어요 ~~~");
}
@Override
public void spend(int money) {
System.out.println("[Daughter] 학원비 " + money + "원 주세요 ~~~");
}
}
public class Ex01 {
public static void main(String[] args) {
Parent p = new Parent();
p.spend(100000);
Son s = new Son();
s.spend(200000);
Daughter d = new Daughter();
d.spend(300000);
System.out.println("\n<< 다형성 >> ");
Parent p1 = new Son(); // 문법 형태 : 부모의 class 참조변수 = new 자식 class();
p1.work();
// p1.play(); // 아들의 play 메소드는 없음, 자기것만 보고 아들 것은 못봄
p1.spend(100); // 자식의 멤버 중에 유일하게 쓸 수 있는 건 오버라이딩, 그래서 아들 class 가 호출됨
Parent p2 = new Daughter();
p2.work();
p2.spend(200);
}
}
# 실행 결과
[Perent] 남은 돈은 900000입니다
[Son] 용돈 200000원 주세요 ~~~
[Daughter] 학원비 300000원 주세요 ~~~
<< 다형성 >>
[Perent] 열심히 일하는 중입니다.
[Son] 용돈 100원 주세요 ~~~
[Perent] 열심히 일하는 중입니다.
[Daughter] 학원비 200원 주세요 ~~~
p1 이 아래 테이블을 찾아 자식의 spend 를 찾아가 실행
주소 | spend | |
~~~~ | ~~~~ | 부모 |
~~~~ | ~~~~ | 자식 |
📍 24.04.16(화) 15일차
https://print-blue.tistory.com/152
https://print-blue.tistory.com/153
https://docs.oracle.com/javase/8/docs/api/index.html
package polymorphism;
class Thing {
@Override
public String toString() {
return "[Thing] 나는 사물이다.";
}
}
class Person {
@Override
public String toString() {
return "[Person] 저는 사람입니다.";
}
}
public class Ex01 {
public static void printInfo(Object obj) { // 다형성 구현됨 : 부모에서 자식을 찾아감, 오버라이딩된 메서드
System.out.println("=============================");
System.out.println(obj);
System.out.println("=============================");
}
public static void main(String[] args) {
Thing thing = new Thing();
Person person = new Person();
printInfo(thing);
printInfo(person);
System.out.println(thing);
System.out.println(person);
}
}
package polymorphism;
class Animal {
// 멤버변수
protected int age;
protected String name;
public Animal() {}
public Animal(int age, String name) { // 매개변수가 없는 기본 생성자도 같이 생성해야함
this.age = age;
this.name = name;
}
}
class Cat extends Animal {
public Cat(int age, String name) {
super(age, name);
}
@Override
public String toString() {
return super.name;
}
}
class Dog extends Animal {
public Dog(int age, String name) {
super(age, name);
}
@Override
public String toString() {
return super.name;
}
}
public class Ex02 {
public static void showInfo(Animal animal) { // 다형성 구현
System.out.println("=============================");
System.out.println(animal);
System.out.println("=============================");
}
public static void main(String[] args) {
Cat mimi = new Cat(5, "미미");
Dog hoya = new Dog(3, "호야");
showInfo(mimi);
showInfo(hoya);
}
}
# 실행 결과
=============================
미미
=============================
=============================
호야
=============================
package polymorphism;
class FruitTwo {
protected String name;
protected int count;
public FruitTwo(String name, int count) {
this.name = name;
this.count = count;
}
public void show() {
System.out.println("[FruitTwo]...");
}
}
class BananaTwo extends FruitTwo {
public BananaTwo (String name, int count) {
super(name, count);
}
@Override
public String toString() {
return "[Banana] 바나나는 멸종 위기이다.";
}
public void show() {
System.out.println("=================================");
System.out.println("현재 과일은 " + super.name + "입니다.");
System.out.println("개수는 " + super.count + "개 있습니다.");
System.out.println("=================================");
}
}
class AppleTwo extends FruitTwo {
public AppleTwo (String name, int count) {
super(name, count);
}
@Override
public String toString() {
return "[Apple] 나는 사과다.";
}
public void show() {
System.out.println(">> 사과 종류는 " + super.name + "입니다.");
System.out.println(">> 수량은 " + super.count + "개 입니다");
}
}
public class Ex03Refactoring {
public static void fruitInfo(FruitTwo fruit) { // 다형성
System.out.println(fruit);
fruit.show(); // 부모의 show()도 있어야함
}
public static void main(String[] args) {
BananaTwo banana = new BananaTwo("레드 바나나", 10);
fruitInfo(banana);
AppleTwo apple = new AppleTwo("홍옥", 5);
fruitInfo(apple);
}
}
강제형변환
(Son) => type 연산자
. 접근 연산자
package polymorghism;
class Parent {
private int money = 1000000;
public void spend(int money) {
this.money -= money;
System.out.println("[Perent] 남은 돈은 " + this.money + "입니다");
}
public void work() {
System.out.println("[Perent] 열심히 일하는 중입니다.");
}
}
class Son extends Parent {
public void play() {
System.out.println("[Son] 신나게 놀고 있어요 ~!");
}
@Override
public void spend(int money) {
System.out.println("[Son] 용돈 " + money + "원 주세요 ~~~");
}
}
class Daughter extends Parent {
public void study() {
System.out.println("[Daughter] 학원 공부하고 있어요 ~~~");
}
@Override
public void spend(int money) {
System.out.println("[Daughter] 학원비 " + money + "원 주세요 ~~~");
}
}
public class Ex01 {
public static void main(String[] args) {
// 강제 형변환
// Son s1 = new Parent(); // error ! Type mismatch
// Son s2 = (Son)new Parent();
// s2.spend(1); // 실행 error ! 강제 형변환이 안됨, 다형성 x, 상속관계에서만 가능
// s2.paly(); // 실행 error !
System.out.println("<< 형변환 >>");
Son s3 = (Son)p1; // downcasting(강제) : 부모 타입 -> 자식 타입
s3.play();
s3.spend(150);
s3.work();
System.out.println();
Parent p4 = s3; // upcasting(자동) : 자식 타입 -> 부모타입
p4.spend(250);
p4.work();
}
}
형변환 가능 여부
[문법] : 참조변수 or 객체 instanceof 타입
package polymorghism;
class Parent {
private int money = 1000000;
public void spend(int money) {
this.money -= money;
System.out.println("[Perent] 남은 돈은 " + this.money + "입니다");
}
public void work() {
System.out.println("[Perent] 열심히 일하는 중입니다.");
}
}
class Son extends Parent {
public void play() {
System.out.println("[Son] 신나게 놀고 있어요 ~!");
}
@Override
public void spend(int money) {
System.out.println("[Son] 용돈 " + money + "원 주세요 ~~~");
}
}
class Daughter extends Parent {
public void study() {
System.out.println("[Daughter] 학원 공부하고 있어요 ~~~");
}
@Override
public void spend(int money) {
System.out.println("[Daughter] 학원비 " + money + "원 주세요 ~~~");
}
}
public class Ex01 {
public static void main(String[] args) {
Parent p = new Parent();
p.spend(100000);
Son s = new Son();
s.spend(200000);
// 형변환 가능 여부
// [문법] : 참조변수 or 객체 instanceof 타입
System.out.println("\n<< 형변환 가능 여부 >>");
System.out.println(p instanceof Parent);
System.out.println(p instanceof Son);
System.out.println(p instanceof Daughter);
System.out.println(p instanceof Object);
System.out.println();
System.out.println(s instanceof Parent);
System.out.println(s instanceof Son);
// System.out.println(s instanceof Daughter); // error ! 형제 사이라 관련이 없음
System.out.println(s instanceof Object);
}
}
package polymorphism;
class Product {
int price;
int bonusPoint;
Product(int price) {
this.price = price;
bonusPoint = (int)(price/10);
}
}
class Tv extends Product {
Tv () {
super(100);
}
public String toString() {
return "Tv";
}
}
class Computer extends Product {
Computer() {
super(200);
}
public String toString() {
return "Computer";
}
}
class Buyer {
int money = 1000;
int bonusPoint = 0;
void buy(Product p) {
if (money < p.price) {
System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
System.out.println(p + " 을/를 구입하셨습니다.");
}
}
public class Ex04 {
public static void main(String[] args) {
Buyer b = new Buyer();
b.buy(new Tv());
b.buy(new Computer());
System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
System.out.println("현재 보너스점수는 " + b.bonusPoint + "점입니다.");
}
}
📍 24.04.17(수) 16일차
https://print-blue.tistory.com/155
https://print-blue.tistory.com/156
9) 추상 클래스 abstract class
[ 추상 클래스 ] abstract class
- 추상 클래스 내 abstract method 가 있을 수도 있고, 없을 수도 있음
- 추상 클래스는 인스턴스 생성 불가능 (미완성된 메소드가 있어서 heap 영역에 할당x)
- 다형성 또는 상속 사용
[ 추상 메소드 ] abstract method
- body 가 없는 메소드 --> 미완성 메소드
- 반드시 abstract class 내 존내
- abstract method 가 있는 abstract class 를 상속 받은 자식 클래스는
반드시 abstract method overriding 해야 함 --> 강제성 ***
- 오버라이딩된 자식의 메소드는 부모의 추상 메소드 접근 제어자보다 같거나 커야 함
접근 제어자 ex) public default private ...
추상 클래스 안에 추상 메소드가 있어도 되고 없어도 됨
instance method | static method | abstract 메소드 | |
일반 클래스 | ㅇ | ㅇ | x |
추상 클래스 | ㅇ | ㅇ | ㅇ |
package abstractclass;
abstract class Parent {
// field
private int num; // instance field
private static int var; // static field
// abstract method (추상 클래스)
// 문법 : abstract 클래스명 + abstract 반환형 메소드명(매개변수); => {} 안씀
abstract void methodOne();
// abstract method : static 불가능
// abstract static void methodTwo(); // 추상메소드는 static, 즉 미완성이라 미리 할당할 수 없음
// method : instance method
public void methodThree() {
System.out.println("[Parent] methodThree() 실행 !");
}
// method : static method
public static void methodFour() {
System.out.println("[Parent] methodFour() 실행 !");
}
}
public class Ex01 {
public static void main(String[] args) {
}
}
에러 메세지 : The type Child must implement the inherited abstract method Parent.methodOne()
package abstractclass;
abstract class Parent {
// field
private int num; // instance field
private static int var; // static field
// abstract method (추상 클래스) : 미완성된 메소드
// 문법 : abstract 클래스명 + abstract 반환형 메소드명(매개변수); => {} 안씀
abstract void methodOne();
// abstract method : static 불가능
// abstract static void methodTwo(); // 추상메소드는 static, 즉 미완성이라 미리 할당할 수 없음
// method : instance method
public void methodThree() {
System.out.println("[Parent] methodThree() 실행 !");
}
// method : static method
public static void methodFour() {
System.out.println("[Parent] methodFour() 실행 !");
}
}
class Child extends Parent {
@Override // 오버라이딩 : 완성된 메소드
void methodOne() {
System.out.println("[Child] 오버라이딩된 추상 메서드");
}
}
부모의 추상 클래스에 추상 메소드가 있는데 자식의 일반 클래스에 강제성으로 오버라이딩으로 구현해야함
하지만 추상 클래스에 추상 메소드가 없으면 강제성은 없음
package abstractclass;
abstract class Parent { // 다형성 용도로 쓴 것
// field
private int num; // instance field
private static int var; // static field
// abstract method (추상 클래스) : 미완성된 메소드
// 문법 : abstract 클래스명 + abstract 반환형 메소드명(매개변수); => {} 안씀
// public abstract void methodOne(); error ! default < public , 자식에서 오버라이딩 된 메소드는 부모보다 같거나 커야함
public abstract void methodOne();
// abstract method : static 불가능
// abstract static void methodTwo(); // 추상메소드는 static, 즉 미완성이라 미리 할당할 수 없음
// method : instance method
public void methodThree() {
System.out.println("[Parent] methodThree() 실행 !");
}
// method : static method
public static void methodFour() {
System.out.println("[Parent] methodFour() 실행 !");
}
}
class Child extends Parent { // 추상 클래스에 추상 메소드가 있는데 구현하지 않아서
@Override // 오버라이딩 : 완성된 메소드
public void methodOne() {
System.out.println("[Child] 오버라이딩된 추상 메서드");
}
public void print() {
System.out.println("[Child] print() 실행 !!");
}
}
public class Ex01 {
public static void main(String[] args) {
// 1. 추상 클래스로 인스턴스 생성 불가능
//Parent p = new Parent(); // error ! Cannot instantiate the type Parent
// 2. 자식 클래스로 인스턴스 생성 가능
Child c = new Child();
c.methodOne();
c.methodThree(); // instance method
// c.methodFour(); // static method, static method 는 이렇게 호출 x
Parent.methodFour();
c.print();
// 3. 다형성
Parent p = new Child();
p.methodThree(); // 부모꺼 씀
p.methodOne(); // 자식꺼 씀
// p.print(); // error ! The method print() is undefined for the type Parent
}
}
10) 인터페이스 (추상클래스 업그레이드)
추상 클래스= 자료형
인터페이스 = 자료향
추상 클래스 기반으로 인터페이스가 나옴
📍 24.04.18(목) 17일차
https://print-blue.tistory.com/157
https://print-blue.tistory.com/158
https://print-blue.tistory.com/159
10) 인터페이스 (추상클래스 업그레이드)
[ interface ] type, 자료형
: abstract class 보다 엄격해진 자료형(왜 엄격하냐면 정해져있음)
: abstract class 의 업그레이드 된 자료형
: 인스턴스 생성 불가능 -> 상속 or 다형성 사용
: 다중 상속 허용
: 생성자 만들 수 없음(인스턴스가 만들어지지 않음)
: 추상 메서드를 가지고 있음
[ field ]
: public static final ==> constant(상수) ==> 대문자, 상수화된 필드
[ method ]
: public abstract ==> 추상 메소드
extends ↗ | class | ↖ x |
class | 상속 관계 | interface |
implements ↘ | interface | ↗ extends |
package interfacetype;
interface A {} // 연습용이라 땡겨 쓴 것, type 은 interface 이지만, 컴파일러는 class
interface B {}
interface C extends A {}
interface D extends A, B {} // 다중 상속 허용 o
class E {}
class F {}
class G extends E {}
// class H extends E, F {} // error ! 다중 상속 허용 x
// class I extend A {} // error ! extend 사용하려면 타입이 같아야 함
class J implements A {}
class K extends E implements A, B {} // class K 를 정의하면서
// class L implements A extends E {} // error ! class 를 정의하면서 상속을 받을 땐,
// 같은 타입이 먼저 와야 함 (순서가 있음)
// interface M implements E {} // error ! interface 는 interface 를 부모로 둘 수 없음
public class Ex01 {
public static void main(String[] args) {
}
}
interface Field {
// public static final
// int num1 = 100; 처럼 적지 않아도 public static final 가 됨
int num1 = 100; // interface field 는 쓰레기값 x, 기본적으로 static 으로 됨
static int num2 = 200;
final int num3 = 300;
public static final int num4 = 400; // 접근 제어자 public, sta
}
public class Ex02 {
public static void main(String[] args) {
// 인스턴스 생성
// interface f = new Field(); // error ! 추상 클래스, interface 인스턴스 생성 용도가 아님(할 수 x)
System.out.println(Field.num1);
System.out.println(Field.num2);
System.out.println(Field.num3);
System.out.println(Field.num4);
// Field.num1 = 5; // error ! fianl field
}
}
# 실행 결과
100
200
300
400
package interfacetype;
public interface Method {
// abstract method
// public abstract void one();
void one(); // 기본적으로 접근 제어자 public abstract 가 됨, 좋은 형태는 아님
public abstract void two();
// void defaultMethod() {} // error ! 바디가 없어야 함
// 오버라이딩 강제성 없음
// default method : Java 8 버전 부터 지원되는 형태가 있음
public default void defaultMethod() { // public 생략됨, 써라, 기본이 public
System.out.println("[method interface] default method 실행");
}
// static method : Java 8 버전 부터 지원되는 형태가 있음
static void staticMethod() { // public 생략됨
System.out.println("[method interface] static method 실행");
}
}
package interfacetype;
class Sub implements Method{
@Override
public void one() { // default -> public 로 넓혀줘야 함
System.out.println("[Sub] 오버라이딩된 메소드 one() 실행");
}
@Override
public void two() { // 추상 메소드를 구현할 건데 또 추상 쓰지 않음
System.out.println("[Sub] 오버라이딩된 메소드 two() 실행");
}
}
public class Ex03 {
public static void main(String[] args) {
// 1. insterface 인스턴스 생성 불가능
// Method m = new Method(); // 완벽하지 않은 추상 메소드라서
// 2. 다형성
Method m = new Sub();
m.one();
m.two();
m.defaultMethod();
// m.staticMethod(); // error !
Method.staticMethod();
}
}
# 실행 결과
[Sub] 오버라이딩된 메소드 one() 실행
[Sub] 오버라이딩된 메소드 two() 실행
[method interface] default method 실행
[method interface] static method 실행
package interfacetype;
public interface Ex04 {
// field
// 생성자를 통한 초기화 ? => x
// 초기값 설정해줘야함
// int num;
// interface 는 construtor(생성자) 를 갖지 않음
// construtor
// public Ex04() {
// }
}
package interfacetype;
interface Test {
public static final int num = 10; // public static final 라 값을 설정해줘야함
}
class SubTest implements Test {
// public SubTest(int num) { // 인스턴스 객체가 생성이 안됨
// super.num = num;
// }
}
public class Ex05 {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
빈 문자열 비교
package string;
public class EmptyString {
public static void main(String[] args) {
String one = ""; // 빈 문자열 , 주소 o
String two = " "; // 공백 , 주소 o
String three = null; // 문자열 없음 , 주소 x
// 출력
System.out.println("one >> " + one);
System.out.println("two >> " + two);
System.out.println("three >> " + three);
// [ 빈 문자열 확인 1 ] equals() : 문자열 비교
System.out.println("\n<< equals() : 문자열 비교 >>");
System.out.println("one : " + one.equals(""));
System.out.println("two : " + two.equals(""));
// error ! NPE(NullPointException)
// System.out.println("three : " + three.equals(""));
// [ 빈 문자열 확인 2 ] length() : 문자열 길이
System.out.println("\n<< length() : 문자열 길이 >>");
System.out.println("one : " + one.length());
System.out.println("two : " + two.length());
// error ! NPE(NullPointException)
// System.out.println("three : " + three.length());
// [ 빈 문자열 확인 3 ] isEmpty() : true, false 여부
// : Java 6 이후 추가
// if (one.lenth() == 0) => if (one.isEmpty())
System.out.println("\n<< isEmpty() : true, false 여부 >>");
System.out.println("one : " + one.isEmpty());
System.out.println("two : " + two.isEmpty());
// error ! NPE(NullPointException)
// System.out.println("three : " + three.isEmpty());
// [ 빈 문자열 확인 4 ] isBlank() : true, false 여부
// : Java 11 이후 추가
// : 빈 문자열 or 공백만으로 이뤄진 경우 true 리턴
// : 공백 = white space(화이트 스페이스)
System.out.println("\n<< isBlank() : true, false 여부 >>");
System.out.println("one : " + one.isBlank());
System.out.println("two : " + two.isBlank());
// error ! NPE(NullPointException)
// System.out.println("three : " + three.isBlank());
}
}
# 실행 결과
one >>
two >>
three >> null
<< equals() : 문자열 비교 >>
one : true
two : false
<< length() : 문자열 길이 >>
one : 0
two : 1
<< isEmpty() : true, false 여부 >>
one : true
two : false
<< isBlank() : true, false 여부 >>
one : true
two : true
11) 예외 처리
[예외 처리]
1. try ~ catch ~ finally(선택) : 직접 // 예측 가능
2. throws : 전가 -> 예외가 발생된 메소드를 호출한 메소드 // 예측 불가능
try ~ catch ~ finally
: 문법
: try 구문에 catch 는 여러 개 올 수 있고, finally 는 하나만 옴
try {
// 예외가 발생되는 명령어
} catch(예외클래스 참조변수) {
// 예외가 발생했을 때, 실행하고자 하는 명령어
} catch(예외클래스 참조변수) {
// 예외가 발생했을 때, 실행하고자 하는 명령어
} finally {
// 선택사항
// 예외 발생 여부와상관없이, 무조건 실행되는 영역
}
package exception;
import java.util.Scanner;
public class Ex02 {
public static void inputData() {
System.out.println("[inputData method] ==> 시작");
Scanner input = new Scanner(System.in);
System.out.print("정수 2개 입력 : ");
int n1 = input.nextInt();
int n2 = input.nextInt();
int result1 = 0;
int result2 = 0;
try {
// 예외가 발생되는 명령어
result1 = n1 / n2;
result2 = n1 % n2;
System.out.println("몫 : " + result1);
System.out.println("나머지 : " + result2);
} catch (ArithmeticException e) {
System.out.println("분모가 0으로 계산 불능");
}
input.close();
System.out.println("[inputData method] ==> 종료");
}
public static void main(String[] args) {
System.out.println("[main method] >> 시작");
inputData();
System.out.println("[main method] >> 끝");
}
}
# 실행 결과
[main method] >> 시작
[inputData method] ==> 시작
정수 2개 입력 : 5 0
분모가 0으로 계산 불능
[inputData method] ==> 종료
[main method] >> 끝
📍 24.04.19(금) 18일차
https://print-blue.tistory.com/160
https://print-blue.tistory.com/161
https://print-blue.tistory.com/162
11) 예외 처리 try ~ catch ~ finally
예외처리를 할 때 2개 이상의 에러가 생길 때 catch 부분에
Exception 을 넣으면 됨
하지만 Exception 를 위로 올리면 에러가 발생하는데 코드는 위에서 아래로 실행이 되는데
부모가 먼저오면 아래에 자식을 쓸 필요가 없어서 삭제하면 됨, 형제끼리는 괜찮음
catch (Exception e)
package exception;
import java.util.Scanner;
public class Ex02 {
public static void inputData() {
System.out.println("[inputData method] ==> 시작");
Scanner input = new Scanner(System.in);
System.out.print("정수 2개 입력 : ");
int n1 = input.nextInt();
int n2 = input.nextInt();
int result1 = 0;
int result2 = 0;
try {
// 예외가 발생되는 명령어
result1 = n1 / n2;
result2 = n1 % n2;
System.out.println("몫 : " + result1);
System.out.println("나머지 : " + result2);
// } catch (ArrayIndexOutOfBoundsException e){
// System.out.println("인덱스 확인하세요 !");
// System.out.println("e >> " + e);
// } catch (ArithmeticException e) {
// System.out.println("분모가 0으로 계산 불능");
// System.out.println("e >> " + e);
// e.printStackTrace();
} catch (Exception e) { // 다형성이 구현됨
System.out.println("[Exception] 영역 실행 !");
} finally {
// 예외 발생 여부와 상관없이 실행하고자 하는 코드
System.out.println("[finally] 예외 처리 종료");
}
input.close();
System.out.println("[inputData method] ==> 종료");
}
public static void main(String[] args) {
System.out.println("[main method] >> 시작");
inputData();
System.out.println("[main method] >> 끝");
}
}
# 실행 결과
[main method] >> 시작
[inputData method] ==> 시작
정수 2개 입력 : 5 0
[Exception] 영역 실행 !
[finally] 예외 처리 종료
[inputData method] ==> 종료
[main method] >> 끝
11) 예외 처리 throws
정확한 에러를 보기 위해서는 Excption 보다는 정확한 에러를 넣어주는 게 좋음
꼭 그렇게 하라는 뜻은 x
package exception;
/*
* [예외 처리]
* 1. try ~ catch ~ finally(선택) : 직접 // 예측 가능
* 2. throws : 전가 -> 예외가 발생된 메소드를 호출한 메소드 // 예측 불가능
*
* throws : 전가
* 예외가 발생되는 지점은 메소드
* 메서드 선언부에 정의하기
* : 문법
* 메소드명() throws ~~~ {
*
* }
*
*/
import java.util.Scanner;
public class Ex03 {
public static void inputData() throws ArithmeticException{
System.out.println("[inputData method] ==> 시작");
Scanner input = new Scanner(System.in);
System.out.print("정수 2개 입력 : ");
int n1 = input.nextInt();
int n2 = input.nextInt();
int result1 = 0;
int result2 = 0;
result1 = n1 / n2;
result2 = n1 % n2;
System.out.println("몫 : " + result1);
System.out.println("나머지 : " + result2);
input.close();
System.out.println("[inputData method] ==> 종료");
}
public static void main(String[] args) {
System.out.println("[main method] >> 시작");
try {
inputData();
} catch (ArithmeticException e) {
System.out.println("[main] 예외 처리 구문 실행");
}
System.out.println("[main method] >> 끝");
}
}
# 실행 결과
[main method] >> 시작
[inputData method] ==> 시작
정수 2개 입력 : 5 0
[main] 예외 처리 구문 실행
[main method] >> 끝
결과를 보면 [inputData method] ==> 시작 하고 끝나지 않았음
그렇담 실행되면서 에러 지점에서 빠져나오고 main 메소드로 바로 감
만약 에러가 여러개인 경우 콤마(,) 사용
자식, 부모 순으로 써야함 (→ 순으로 읽음) - 자동 형변환이 되는 것
public static void inputData() throws ArithmeticException, Exception {}
main 메소드에도 전가를 한다면 JVM 쪽으로 넘어가는데
JVM 쪽에서 딱히 예외 처리를 해주지 않아서 에러를 띄워줌
Thread
Thread.sleep(1000); // 1초
주석
/** 문서 주석 => class 파일에도 저장됨
/*
//
package exception;
import java.util.Scanner;
class NegativeNumberExcetion extends Exception { // Exception 부모를 둔 NegativeNumberExcetion 클래스
private static final long serialVersionUID = 1L; // 공유 번호 넣어줘야 함
public NegativeNumberExcetion(int number) {
super("음수 " + number + "를 입력하셨군요 !");
}
}
public class Ex02 {
public static void inputNumber() throws NegativeNumberExcetion {
Scanner input = new Scanner(System.in);
System.out.print("정수 1개를 입력하세요... ");
int userNumber = input.nextInt();
input.close();
if (userNumber < 0) {
throw new NegativeNumberExcetion(userNumber); // 인스턴스 객체를 생성하고 throw 후 NegativeNumberExcetion 넘어감
}
System.out.println("입력된 값 : " + userNumber);
}
public static void main(String[] args) {
System.out.println("[main] 시작");
try {
inputNumber();
} catch (NegativeNumberExcetion e) {
System.out.println("e >> " + e.getMessage());
}
System.out.println("[main] 종료");
}
}
# 실행 결과
[main] 시작
정수 1개를 입력하세요... -1
e >> 음수 -1를 입력하셨군요 !
[main] 종료
다운 캐스팅
throws Exception 만 정의했는데
catch (ArithmeticException e) 이 부분이 실행이 됐음
왤까
package exception;
import java.util.Scanner;
public class Ex03 {
public static void inputData() throws Exception {
System.out.println("[inputData method] ==> 시작");
Scanner input = new Scanner(System.in);
System.out.print("정수 2개 입력 : ");
int n1 = input.nextInt();
int n2 = input.nextInt();
int result1 = 0;
int result2 = 0;
result1 = n1 / n2;
result2 = n1 % n2;
System.out.println("몫 : " + result1);
System.out.println("나머지 : " + result2);
input.close();
System.out.println("[inputData method] ==> 종료");
}
public static void main(String[] args) {
System.out.println("[main method] >> 시작");
try {
inputData();
} catch (ArithmeticException e) {
System.out.println("[main] ArithmeticException 예외 처리 구문 실행");
} catch(Exception e) {
System.out.println("[main] Exception 예외 처리 구문 실행");
}
System.out.println("[main method] >> 끝");
}
}
# 실행 결과
[main method] >> 시작
[inputData method] ==> 시작
정수 2개 입력 : 5 0
[main] ArithmeticException 예외 처리 구문 실행
[main method] >> 끝
📍 24.04.22(월) 19일차
https://print-blue.tistory.com/163
<< 객체지향 프로그래밍 : 고급 >>
singleton : 객체의 인스턴스가 오직 1개만 생성되는 패턴
생성자는 외부에서 호출하지 못하도록 private 으로 지정
스프링에서 생성되는 객체 -> Bean (대부분 singleton)
장점(이점) | 단점(문제점) | |
singleton | - 메모리 측면 최초 한번의 new 연산자를 통해 고정된 메모리 영역을 사용하기 때문에 추후 해당 객체에 접근할 때 메모리 낭비를 방지할 수 있음 - 다른 클래스간에 데m,이터 공유가 쉬움 싱글톤 인스턴스가 전역으로 사용되는 인스턴스이기 때문에 다른 클래스의 인스턴스들이 접근하여 사용할 수 있다. (여러 클래스의 인스턴스에서 싱글톤 인스턴스의 데이터에 동시에 접근하게 되면 동시성 문제 발생 가능) |
- 싱굴톤 패턴을 구현하는 코드 자체가 많이 필요함 정적 팩토리 메서드에서 객체 생성을 확인하고 생성자를 호출하는 경우에 멀티스레딩 환경에서 발생할 수 있는 동시성 문제 해결을 위해 syncronized 키워드를 사용해야 함 - 테스트 하기 어려움 자원을 공유하고 있기 때문에 테스트가 결정적으로 격리된 환경에서 수행되려면 매번 인스턴스의 상태를 초기화시켜줘야함, 그렇지 않으면 어플리케이션 전역에서 상태를 공유하기 때문에 테스트가 온전하게 수행하지 못함 - 의존 관계상 클라이언트가 구체 클래스에 의존하게 됨 new 키워드를 직접 사용하여 클래스 안에서 객체를 생성하고 있으므로, SOLID 원칙 중 DIP 를 위반하게 되고, OCP 원칙 또한 위반할 가능성 높음 - 자식 클래스 만들 수 없음 - 내부 상태를 변경하기 어려움 |
https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/
[객체 생성 불가능] ===================
- private constructor: private 생성자
- abstract class : 추상 클래스
- interface : 인터페이스
======================================
// private constructor : private 생성자
class Cat {
private Cat() {
System.out.println("난 고양이 !");
}
}
public class Ex01 {
public static void main(String[] args) {
// Cat cat = new Cat();
}
}
// singleton 이 아니라 부연 설명하기 위해 코드 작성
package singleton;
class Dog {
// field
int age = 10;
// method
public void showAge() {
System.out.println("나이 >> " + age);
}
public static void createInstance() {
Dog dog = new Dog();
dog.showAge();
}
}
public class Ex02 {
// instance field
int num = 5;
// method
public static void main(String[] args) {
Ex02 type = new Ex02();
System.out.println(type.num);
Dog.createInstance();
}
}
클래스 외부에서 인스턴스 생성을 막기 위해 생성자 접근 제어자를 private 로 했음
package singleton;
// singleton
class Pattern {
// static Pattern meber_field = new Pattern(); // static 에 미리 올려둠
// 큰 문제점 : 값이 변경될 수 있음 ex) Pattern.meber_field = null;
private static Pattern meber_field = new Pattern(); // static 에 미리 올려둠
// 값변경을 하지 않기 위해서 private, 값 변경을 하기 위해선 메소드 사용하면 됨 (getPattern())
// Pattern 클래스가 로드 되자마자 static 에 올라가야함
private Pattern() {} // 인스턴스 생성 제어를 private 로 했음, 클래스 외부에서 인스턴스 생성을 막음
public static Pattern getPattern() { // 외부접근을 위해 static
System.out.println("[Pattern class 내] member_field >> " + meber_field); // 확인을 하기 위해서 쓴 코드
return meber_field;
}
@Override
public String toString() {
return "Pattern 클래스입니다.";
}
// [문제] 멤버변수(field) i 에 설정된 값을 외부에서 사용하려면 ?
// [방법 1] 인스턴스 멤버
// private int i = 100;
//
// public int getI() {
// return i;
// }
// [방법 2] static 멤버
private static int i = 100;
public static int getI() {
return i;
}
}
public class Ex03 {
public static void main(String[] args) {
// 1. 인스턴스 생성 불가능 : 생성자가 private
//Pattern p = new Pattern();
// 2. static 멤버 접근
//Pattern p = Pattern.meber_field;
//System.out.println("p >> " + p);
//Pattern.meber_field = null;
// 3. static method 를 통해 생성된 인스턴스 주소 리턴
Pattern p = Pattern.getPattern();
System.out.println("p >> " + p);
// [방법 1] 인스턴스 멤버
// System.out.println("인스턴스 field i 값 출력 >> " + p.getI());
// [방법 2] static 멤버
System.out.println("static field i 값 출력 >>" + Pattern.getI());
}
}
enum : 열거타입, 상수 관련된 것을 관리하기 위함
[enum] 열거형
- JDK 1.5 부터 지원
- 변수를 상수화
- 열거형 상수(constant)
- 멤버변수(field) 를 모두 대문자 사용
- 관련 있는 상수(constant) 를 하나의 이름으로 관리
- 첫 번째 멤버변수가 0부터 시작하여 1씩 자동으로 증가
예전에 개발자들이 interface 를 만들어 상수화 시켰음
자바 5버전부터 지원 시작
package enumtype;
import java.util.Scanner;
// enum 이 없는 형태
public class Ex01 {
public static void main(String[] args) {
System.out.println("1. 월, 2. 화, 3. 수, 4. 목, 5. 금, 6. 토, 7. 일");
System.out.print("오늘의 요일을 선택하세요 ... ");
Scanner input = new Scanner(System.in);
int day = input.nextInt();
switch(day) {
case Day.MON:
System.out.println("월) 주간 회의");
break;
case Day.TUE:
System.out.println("화) 프로젝트 기획 회의");
break;
case Day.WED:
System.out.println("수) 진행 사항 보고");
break;
case Day.THU:
System.out.println("목) 사내 동호회");
break;
case Day.FRI:
System.out.println("금) 프로젝트 마감");
break;
case Day.SAT:
System.out.println("토) 가족과 즐거운 시간");
break;
case Day.SUN:
System.out.println("일) 휴일입니다.");
break;
}
input.close();
}
}
package enumtype;
public interface Day {
// [field] public static final
int MON = 1;
int TUE = 2;
int WED = 3;
int THU = 4;
int FRI = 5;
int SAT = 6;
int SUN = 7;
}
📍 24.04.23(화) 20일차
enum 열거 타입
[field]
: public static final
: 값 변경 불가능
: field 는 enum 의 첫 라인에 작성
: switch 문에서 사용 가능
package enumtype;
/*
* [enum] 열거형
* - JDK 1.5 부터 지원
* - 변수를 상수화
* - 열거형 상수(constant)
* - 멤버변수(field) 를 모두 대문자 사용
* - 관련 있는 상수(constant) 를 하나의 이름으로 관리
* - 첫 번째 멤버변수가 0부터 시작하여 1씩 자동으로 증가
*/
class Season {
// 하나의 인스턴스 생성하여, 상수로 사용하고 싶다 !
// 주소가 출력된다 !
public static final Season SPRING = new Season(); // main 메소드에서 사용하기 위해 static, 값변경되지 않게 final
}
public class Ex01 {
public static void main(String[] args) {
System.out.println(Season.SPRING); // enumtype.Season@2f92e0f4
//Season.SPRING = null; // 변경되지 않기 위해 final 사용
}
}
package enumtype;
/*
* [enum] 열거형
* - JDK 1.5 부터 지원
* - 변수를 상수화
* - 열거형 상수(constant)
* - 멤버변수(field) 를 모두 대문자 사용
* - 관련 있는 상수(constant) 를 하나의 이름으로 관리
* - 첫 번째 멤버변수가 0부터 시작하여 1씩 자동으로 증가
*/
class SeasonTwo {
// 하나의 인스턴스 생성하여, 상수로 사용하고 싶다 !
// 주소가 출력된다 !
public static final SeasonTwo SPRING = new SeasonTwo("봄"); // 이것도 field 에 해당
public static final SeasonTwo SUMMER = new SeasonTwo("여름"); // 이것도 field 에 해당
public static final SeasonTwo FALL = new SeasonTwo("가을"); // 이것도 field 에 해당
public static final SeasonTwo WINTER = new SeasonTwo("겨울"); // 이것도 field 에 해당
// feild
private String name;
// constructor
private SeasonTwo(String name) { // public -> private, 외부에서 생성자 금지
this.name = name;
}
@Override
public String toString() { // "봄" 글자 출력하기 위해
return this.name;
}
}
public class Ex02 {
public static void main(String[] args) {
// System.out.println(SeasonTwo.SPRING);
// toString 전 출력값 : enumtype.SeasonTwo@2f92e0f4
// toString 후 출력값 : 봄
// new SeasonTwo("가을"); // 외부에서 생성자 만들고 싶지 않음
System.out.println(SeasonTwo.SPRING); // 봄, 상수처럼 사용 중
System.out.println(SeasonTwo.SUMMER); // 여름
System.out.println(SeasonTwo.FALL); // 가을
System.out.println(SeasonTwo.WINTER); // 겨울
SeasonTwo s = SeasonTwo.SPRING; //
System.out.println("봄 >> " + s);
// SeasonTwo currentSeason = SeasonTwo.SPRING;
// error ! int 나 String(주소) 가 와야함, 상수가 아닌 값은 올 수 없음
// switch (currentSeason) {
// case SeasonTwo.SPRING:
// System.out.println("봄이다");
// break;
// }
}
}
package enumtype;
/*
* [enum] 열거형
* - JDK 1.5 부터 지원
* - 변수를 상수화
* - 열거형 상수(constant)
* - 멤버변수(field) 를 모두 대문자 사용
* - 관련 있는 상수(constant) 를 하나의 이름으로 관리
* - 첫 번째 멤버변수가 0부터 시작하여 1씩 자동으로 증가
*/
enum SeasonThree { // enum : 타입, 컴파일러는 class 로 파일 저장됨
// private SeasonThree() {} error ! enum 은 field가 먼저 와야함
/*
* [field]
* : public static final
* : 값 변경 불가능
* : field 는 enum 의 첫 라인에 작성
* : switch 문에서 사용 가능
*/
SPRING, SUMMER, FALL, WINTER; // 내부적으로 ; 붙여줌, 하지만 다음 코드 작성할 땐 마지막에 ; 붙이기
//public static final SeasonThree SPRING = new SeasonThree("SPRING");
// public SeasonThree() -> error ! enum 의 접근 제어자는 public 올 수가 없음
// enum 의 접근 제어자는 private 으로 할 것
// 생성자 필요없는데 확인하기 위해서 넣어둔 것
private SeasonThree() { // enum 은 무조건 field가 첫번째로 와야하고, 나머지 코드는 그 뒤로 할 것
System.out.println("생성자 실행");
}
}
public class Ex03 {
public static void main(String[] args) {
// 인스턴스 생성 불가능, interface 처럼 인스턴스 생성하는 타입이 아님
//SeasonThree seasonThree = new SeasonThree();
//SeasonThree.SPRING = null; error ! 값 변경 불가능
System.out.println(SeasonThree.SPRING); // SPRING, 문자열 그대로 나옴
System.out.println(SeasonThree.SUMMER);
System.out.println(SeasonThree.FALL);
System.out.println(SeasonThree.WINTER);
SeasonThree spring = SeasonThree.SPRING;
System.out.println("spring >> " + spring); // spring >> SPRING
SeasonThree spring2 = SeasonThree.SPRING;
// 알수 있는 사실 : 같은 값을 리턴해준다
if (spring == spring2) {
System.out.println("같다");
} else {
System.err.println("다르다");
}
System.out.println(SeasonThree.SPRING.getDeclaringClass()); // class enumtype.SeasonThree
System.out.println(SeasonThree.SPRING.getDeclaringClass().getClass()); // class java.lang.Class
System.out.println(SeasonThree.SPRING.getDeclaringClass().getName()); // enumtype.SeasonThree
// 열거형 상수명 리턴 (field 에 저장된 값)
System.out.println(SeasonThree.SPRING.name()); // SPRING, 필드에 저장된 값
// 1차원 배열
System.out.println(SeasonThree.values()); // [Lenumtype.SeasonThree;@85ede7b
// 향상된 for 문
// ordinal() : 서수
for (SeasonThree idx: SeasonThree.values()) {
System.out.println(idx); // 참조변수 필드 출력
System.out.println(idx.ordinal()); // 첫 번째 멤버변수가 0부터 시작하여 1씩 자동으로 증가 확인 방법
}
}
}
# 실행 결과
생성자 실행
생성자 실행
생성자 실행
생성자 실행
SPRING
SUMMER
FALL
WINTER
spring >> SPRING
같다
class enumtype.SeasonThree
class java.lang.Class
enumtype.SeasonThree
SPRING
[Lenumtype.SeasonThree;@85ede7b
SPRING
0
SUMMER
1
FALL
2
WINTER
3
필드 + 생성자 + toString
// 파일 형태 : enum
// Ex05 파일과 함께 보기
package enumtype;
public enum NumberTwo {
ONE(1), TWO(2), TRHEE(3), FOUR(4); // 필드의 값을 지정하기 위해 field 와 매겨변수 1개 받는 생성자 정의하기
// private int number;
private Integer number;
private NumberTwo(Integer number) {
this.number = number;
}
// error ! int 인데 string 이라서
// @Override
// public String toString() {
// return this.number;
// }
@Override
public String toString() {
return number.toString();
}
// (String)this.number + private int number;
// error ! primitive 타입을 referance 타입으로 바꿀 수 없음
// (String)this.number + private Integer number;
// error ! referance 타입으로 바꿔도 상속 관계여야함
public Integer getNumber() {
return number;
}
}
// NumberTwo 파일과 함께 보기
package enumtype;
public class Ex05 {
public static void main(String[] args) {
System.out.println("[field 에 저장된 값]");
for (NumberTwo n: NumberTwo.values()) {
System.out.println(n);
}
System.out.println("\n[서수]");
for (NumberTwo n: NumberTwo.values()) {
System.out.println(n.ordinal());
}
System.out.println("\n[getNumber() 호출]");
for (NumberTwo n: NumberTwo.values()) {
System.out.println(n.getNumber()); // 타입 Integer
}
// if (NumberTwo.ONE == 1) 이든 if (NumberTwo.ONE == "1")
// error ! enum 의 좋은 점, type 을 쓰면 저장된 값은 1이지만 실제 타입은 NumberTwo 임
if (NumberTwo.ONE == NumberTwo.FOUR) {
System.out.println("같다");
} else {
System.out.println("다르다");
}
if (NumberTwo.ONE.getNumber() == 1) {
System.out.println("같다");
} else {
System.out.println("다르다");
}
}
}
# 실행 결과
[field 에 저장된 값]
1
2
3
4
[서수]
0
1
2
3
[getNumber() 호출]
1
2
3
4
다르다
같다
generic
https://print-blue.tistory.com/165
자료화를 일반화 시킨 것
-> 타입을 지정하지 않아서 재사용이 가능하고, 형변환이 줄어든다, 코드의 유연성이 높아짐
[generic] 제너릭
: type 을 일반화(generalize)함
: type 은 컴파일 할 때 결정이 됨 (미리 지정 x)
: generic 은 type 을 외부에서 결정(지정)
: JDK 1.5 부터 도입(지원)
: 보통 한글자를 씀 <T>
: 타입 매개변수라 불림
package generic;
class Test<T> { // 제너릭 클래스라 불림
private T element; // 이 부분은 구체화 하지 않음(type 지정x)
public void setElement(T element) {
this.element = element;
}
public T getElement() {
return element;
}
}
public class Ex01 {
public static void main(String[] args) {
Test<String> t = new Test<String>();
t.setElement("ㅎㅇ");
System.out.println(t.getElement());
//Test<int> t2 = new Test<int>(); error ! primitive type 못씀
Test<Integer> t2 = new Test<Integer>();
t2.setElement(5); // boxing
System.out.println(t2.getElement());
//t2.setElement("ㅎㅇ"); error ! t2 의 type 은 Integer
}
}
# 실행 결과
ㅎㅇ
5
Object 로 하면 되는데 제네릭을 사용하는 이유
Object 로 받으면서 형변환이 되는데 여기서 다형성이 이루어짐
매번 다운 캐스팅 해줘야함
package generic;
class TestTwo {
private Object element;
public void setElement(Object element) { // 다형성 구현
this.element = element;
}
public Object getElement() {
return element;
}
}
public class Ex02 {
public static void main(String[] args) {
TestTwo t = new TestTwo();
t.setElement("ㅎㅇ");
System.out.println(t.getElement());
//String t_value = t.getElement(); error ! String t_value / Object t.getElement(), 다운 캐스팅 해줘야함
String t_value = (String)t.getElement();
System.out.println("t_value >> " + t_value);
TestTwo t2 = new TestTwo();
t2.setElement(5);
System.out.println(t2.getElement());
Integer t2_value = (Integer)t2.getElement();
System.out.println("t2_value >> " + t2_value);
}
}
# 실행 결과
ㅎㅇ
t_value >> ㅎㅇ
5
t2_value >> 5
package generic;
class Apple {
@Override
public String toString() {
return "사과는 맛있당.";
}
}
class Banana {
@Override
public String toString() {
return "바나나는 멸종 위기이다.";
}
}
class InstanceType {
private int count; // 인스턴스 생성되면서 0으로 저장
// ====================================================
// 1. [parameter type : Apple]
// public void showInstanceType(Apple apple) { 애플은 받을 수 있지만, 바나나는 못받음
// System.out.println("type >> " + apple);
// count++;
// }
// 2. [parameter type : Object] 애플과 바나나는 받을 수 있지만 형변환을 해주는 상황들이 생김
// public void showInstanceType(Object object) { // 다형성
// System.out.println("type >> " + object);
// count++;
// }
// 3. [parameter type : generic type]
public <T> void showInstanceType(T type) {
System.out.println("type >> " + type);
count++;
}
// ====================================================
public void showCount() {
System.out.println("count >> " + count);
}
}
public class Ex03 {
public static void main(String[] args) {
Apple apple = new Apple();
Banana banana = new Banana();
InstanceType in = new InstanceType();
// in.showInstanceType(apple);
in.<Apple>showInstanceType(apple);
in.showCount();
// in.showInstanceType(banana);
in.<Banana>showInstanceType(banana);
in.showCount();
}
}
# 실행 결과
type >> 사과는 맛있당.
count >> 1
type >> 바나나는 멸종 위기이다.
count >> 2
타입 제한 주기
package generic;
interface Available {
// 추상 클래스
void selfIntroduction();
}
class Tree {
private String sort;
private int age;
public Tree() {}
public Tree(String sort, int age) {
this.sort = sort;
this.age = age;
}
public void info() {
System.out.printf("[INFO] 종류 : %s, 나이 : %d\n\n", sort, age);
}
}
class Maple extends Tree implements Available {
public Maple(String sort, int age) {
super(sort, age);
}
@Override
public void selfIntroduction() {
System.out.println("나는 단충이다 !");
}
}
class Pine extends Tree implements Available {
public Pine(String sort, int age) {
super(sort, age);
}
@Override
public void selfIntroduction() {
System.out.println("나는 소나무이다 !");
}
}
public class Ex04 {
// ==================================================================================
// [자료형에 안전하지 않은 코드]
// public static <T> void callSelfIntroduction(T t) { // main 메소드에서 호출하기 위해 static
// ((Available)t).selfIntroduction();
// }
//
// public static <T> void callInfo(T t) {
// ((Tree)t).info();
// }
// [자료형에 안전한 코드] 타입 제한 두기
// : class 와 interface 를 구분하지 않고, 키워드 extends 사용
// 내 생각 : 컴파일 할 때 결정이 되기 때문에 상속 개념이 없는듯
// T 에는 Available 포함한 Available 의 자식이 와야함
// T 는 Available 포함한 Available 의 자식이 와야한다고 제한을 두는 거지, 상속 개념 x
public static <T extends Available> void callSelfIntroduction(T t) { // Available interfave
((Available)t).selfIntroduction();
}
public static <T extends Tree> void callInfo(T t) { // Tree class
((Tree)t).info();
}
// ==================================================================================
public static void main(String[] args) {
Maple maple = new Maple("당단풍나무", 25);
Pine pine = new Pine("금강고로쇠", 33);
callSelfIntroduction(maple);
callInfo(maple);
callSelfIntroduction(pine);
callInfo(pine);
// 런타임(실행) error ! 받은 건 String 인데, ((Available)t) 형변환 되지 않음
// 이러한 경우를 [자료형에 안전하지 않은 코드] 라 불림
// 빨간줄 안뜸
//String name = "홍길동";
//callSelfIntroduction(name);
// 컴파일 error !
// 코드에 타입 제한을 뒀으니 이제 빨간줄
//String name = "홍길동";
//callSelfIntroduction(name);
//callInfo(name);
}
}
# 실행 결과
나는 단충이다 !
[INFO] 종류 : 당단풍나무, 나이 : 25
나는 소나무이다 !
[INFO] 종류 : 금강고로쇠, 나이 : 33
1. 제네릭 타입은 해당 {} 내에서만 유효
: 제네릭 타입 선언 형태
: 1-1. 인터페이스
: 1-2. 클래스
: 1-3. 메소드
https://velog.io/@lastella/%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D
package generic;
// 1-1. [제네릭 인터페이스]
interface InterfaceName <T> {}
// 1-2. [제네릭 클래스]
class ClassOne <T> {}
// => 제네릭 인터페이스 T 와 제네릭 클래스의 T 는 다름
public class Ex05 {
// 1-3. [제네릭 메소드]
public static <T> void show(T t) {
System.out.println(t);
}
public static void main(String[] args) {
}
}
package generic;
만약 클래스 상속을 한다면 ?
// 클래스 상속
class ClassSuper {}
class ClassSub extends ClassSuper {}
public class Ex05 {
// =====================================================================
// [extends 와 super] 타입 제한두기 (상속 개념 아님)
// [타입 중심]
// <T extends 타입> : 가능
// <T super 타입> : 불가능
// 부가 설명
// : T 에는 타입에 들어가는 클래스와 해당 클래스 부모만이 올 수 있는데,
// 최대 Object 클래스가 올 수 있음
// : T 에는 항상 최상위 클래스가 지정되므로 super 를 사용하여 타입을 제한 것이
// 의미가 없게 됨 -> 따라서 JAVA 는 super 타입을 지정하는 것을 허용하지 x
public <T extends ClassSuper> void one(T t) {}
// error ! Syntax error on token "super", , expected / 문법 불가능 !
// 제네릭 메소드는 super 를 못쓰지만 제네릭 클래스는 super 사용 가능
//public <T super ClassSuper> void two(T t) {}
// =====================================================================
// [기능(동작) 중점] 타입보다는 기능(동작) 그 자체에 초점을 둔 메소드
// 제네릭 인터페이스 또는 제네릭 클래스를 파라미터의 타입으로 사용
// ? : 와일드 카드(Unbounded wildcard)
// <? extends 타입> : 가능
// <? super 타입> : 가능
// 제네릭 메서드 아님
// ex) ClassOne 클래스가 왔으니 제네릭 클래스
// three(ClassOne<T> name) error !
public void three(ClassOne<?> name) {}
// ClassOne 제네릭 클래스는 ClassSuper 를 포함한 자식도 올 수 있음
// 자기를 포함한 이하
public void four(ClassOne<? extends ClassSuper> name) {}
// 자기를 포함한 이상
public void five(ClassOne<? super ClassSub> name) {}
// =====================================================================
// 제네릭 타입(타입 파라미터)을 타입 제한(한정)
// 제네릭 클래스가 온다면
public <T extends ClassOne<?>> void six(T t) {}
// =====================================================================
public static void main(String[] args) {
}
}
package generic;
/*
* 1. 제네릭 타입은 해당 {} 내에서만 유효
* : 제네릭 타입 선언 형태
* : 1-1. 인터페이스
* : 1-2. 클래스
* : 1-3. 메소드
*
*/
// 1-1. [제네릭 인터페이스]
interface InterfaceName <T> {}
// 1-2. [제네릭 클래스]
class ClassOne <T> {}
// => 제네릭 인터페이스 T 와 제네릭 클래스의 T 는 다름
// 2. 제네릭 타입은 2개 이상 가능
class ClassTwo <K, V> {}
// 클래스 상속
class ClassSuper {}
class ClassSub extends ClassSuper {}
public class Ex05 {
// 1-3. [제네릭 메소드]
public static <T> void show(T t) {
System.out.println(t);
}
// =====================================================================
// [extends 와 super] 타입 제한두기 (상속 개념 아님)
// [타입 중심]
// <T extends 타입> : 가능
// <T super 타입> : 불가능
// 부가 설명
// : T 에는 타입에 들어가는 클래스와 해당 클래스 부모만이 올 수 있는데,
// 최대 Object 클래스가 올 수 있음
// : T 에는 항상 최상위 클래스가 지정되므로 super 를 사용하여 타입을 제한 것이
// 의미가 없게 됨 -> 따라서 JAVA 는 super 타입을 지정하는 것을 허용하지 x
public <T extends ClassSuper> void one(T t) {}
// error ! Syntax error on token "super", , expected / 문법 불가능 !
// 제네릭 메소드는 super 를 못쓰지만 제네릭 클래스는 super 사용 가능
//public <T super ClassSuper> void two(T t) {}
// =====================================================================
// [기능(동작) 중점] 타입보다는 기능(동작) 그 자체에 초점을 둔 메소드
// 제네릭 인터페이스 또는 제네릭 클래스를 파라미터의 타입으로 사용
// ? : 와일드 카드(wildcard)
// ClassOne<?> : 비한정적 와일드 카드 타입(Unbounded wuldcard type)
// ? : 모든지 와도 돼용 ~
// <? extends 타입> : 가능
// <? super 타입> : 가능
// 제네릭 메서드 아님
// ex) ClassOne 클래스가 왔으니 제네릭 클래스
// three(ClassOne<T> name) error !
public void three(ClassOne<?> name) {}
// ClassOne 제네릭 클래스는 ClassSuper 를 포함한 자식도 올 수 있음
// 자기를 포함한 이하
public void four(ClassOne<? extends ClassSuper> name) {}
// 자기를 포함한 이상
public void five(ClassOne<? super ClassSub> name) {}
// =====================================================================
// 제네릭 타입(타입 파라미터)을 타입 제한(한정)
// 제네릭 클래스가 온다면
public <T extends ClassOne<?>> void six(T t) {}
// =====================================================================
public static void main(String[] args) {
}
}
제네릭 클래스 사용할 때
package generic;
class Thing {
private String id;
private String name;
public Thing() {} // 자식때문에 넣어줌
public Thing(String id, String name) {
this.id = id;
this.name = name;
}
}
class Tv extends Thing {
@Override
public String toString() {
return "나는 새로운 TV !";
}
}
class Pen extends Thing {
@Override
public String toString() {
return "나는 평생 사용 가능한 Pen !";
}
}
// 제네릭 클래스
// T : 타입 파라미터
class Box <T> {
private T item;
public void store(T item) {
System.out.println("[박스에 상품을 담는다.]");
this.item = item;
}
public T out() {
System.out.println("[박스에서 상품을 꺼내다.]");
return item;
}
}
public class Ex06 {
// (Box<? extends Thing> box) 에서 Box는 제네릭 클래스
public static void openBox(Box<? extends Thing> box) { // Thing 을 포함한 자식(Tv, Pen)
Thing item = box.out(); // 다형성 구현이 되기 때문에 Thing 사용, 일부러 out() 호출하게끔 만들었음
System.out.println(item);
}
public static void main(String[] args) {
//Box<Tv> one = new Box(); // 추정하지만, 그래도 지정해주는 것이 좋음
Box<Tv> one = new Box<Tv>();
one.store(new Tv());
openBox(one);
System.out.println();
Box<Pen> two = new Box<Pen>();
two.store(new Pen());
openBox(two);
Box<Apple> apple = new Box<Apple>();
apple.store(new Apple());
//openBox(apple); error ! 타입이 Apple 인데, Box 외 타입 올 수 없음
}
}
# 실행 결과
[박스에 상품을 담는다.]
[박스에서 상품을 꺼내다.]
나는 새로운 TV !
[박스에 상품을 담는다.]
[박스에서 상품을 꺼내다.]
나는 평생 사용 가능한 Pen !
[박스에 상품을 담는다.]
📍 24.04.24(수) 21일차
https://print-blue.tistory.com/166
https://print-blue.tistory.com/167
📍 24.04.25(목) 22일차
https://print-blue.tistory.com/169
https://print-blue.tistory.com/170
https://print-blue.tistory.com/168
https://print-blue.tistory.com/171
'Language > JAVA' 카테고리의 다른 글
JAVA 자바 활용 백엔드 개발 기초 연습 문제 - 생성자를 이용해 부모 Class 에 private 타입의 멤버변수 접근하기 (0) | 2024.04.16 |
---|---|
[JAVA] JAR 파일 (0) | 2024.04.12 |
JAVA 프로그래밍 기초 연습 문제 - Parameter (0) | 2024.04.09 |
JAVA 프로그래밍 기초 연습 문제 - 향상된 for문 (0) | 2024.04.09 |
JAVA 프로그래밍 기초 연습 문제 - 2차원 배열 생성과 동시에 초기화하기 (0) | 2024.04.08 |