TIL 쓰기 전 오늘의 목표 !
- 목표
- 4주차 강의까지 듣기 → 3주차까지 들음,,
- 개인과제 Level 2까지 풀기 → 2 - 5까지,,
- 알고리즘 코드카타 1문제 풀기 → ok
< 12. 접근 제어자 >
- 제어자 → 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여해줌
- 접근 제어자 : public, protected, default, private → 멤버 또는 클래스에 사용, 외부에서 접근하지 못하도록 제한하며 클래스, 멤버 변수, 메서드, 생성자에 사용되고 지정되어 있지 않으면 default다. 클래스 내부에 선언된 데이터를 보호하기 위해서 사용하며 유효한 값을 유지하도록 함부로 변경하지 못하도록 접근을 제한하는 것이 필요하다.
- public : 접근 제한이 전혀 없다
- protected : 같은 패키지 내에서, 다른 패키지의 자손 클래스에서 접근이 가능
- default : 같은 패키지 내에서만 접근이 가능
- private : 같은 클래스 내에서만 접근이 가능
- ∴ 클래스 : public, default / 메서드&멤버변수 : public, protected, default, private
- 그 외 제어자 : static, final, abstract
- 접근 제어자는 단 하나만 사용할 수 있다
- Getter
- 외부에서 객체의 private 한 필드를 읽을 때 사용
private double speed; // 자동차 속도 , km/h
private char gear = 'P'; // 기어의 상태, P,R,N,D
private boolean lights; // 자동차 조명의 상태
private 한 필드
public String getModel() {
return model;
}
public String getColor() {
return color;
}
public double getPrice() {
return price;
}
getter 메소드로 불러오기 / get + 필드이름(첫 글자 대문자)
- Setter
- 외부에서 객체의 private 한 필드를 저장/수정할 필요가 있을 때 사용
public void setModel(String model) {
this.model = model;
}
public void setColor(String color) {
this.color = color;
}
public void setPrice(double price) {
this.price = price;
}
setter 메서드를 통해 값을 저장하거나 수정 / set + 필드이름(첫 글자 대문자)
- 사용 가능한 제어자
- 클래스 : public, default, final, abstract
- 메서드 : public, protected, default, private, final, abstract, static
- 멤버 변수 : public, protected, default, private, final, static
- 지역변수 : final
< 13. package & import >
- 패키지 : 클래스의 일부분이면서 클래스를 식별해주는 용도
- 상위 패키지와 하위 패키지를 도트(.)로 구분
package oop.pk1; // package 상위 패키지.하위 패키지; 선언
public class Car {
public void horn() {
System.out.println("pk1 빵빵");
}
}
oop.pk1.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.main.Main 클래스
- import : 다른 패키지에 있는 클래스를 사용하기 위해 명시하는 키워드
import oop.pk1.Car;
oop.pk1의 Car 클래스만 사용가능
import oop.pk1.*;
oop.pk1의 모든 클래스 사용가능
package oop.main;
import oop.pk1.Car;
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.horn(); // pk1 빵빵
oop.pk2.Car car2 = new oop.pk2.Car();
car2.horn(); // pk2 빵빵
}
}
oop.main.Main 클래스
< 14. 상속 >
**예제 코드
Car : 부모 클래스
public class Car {
String company; // 자동차 회사
private String model; // 자동차 모델
private String color; // 자동차 색상
private double price; // 자동차 가격
double speed; // 자동차 속도 , km/h
char gear = 'P'; // 기어의 상태, P,R,N,D
boolean lights; // 자동차 조명의 상태
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double gasPedal(double kmh, char type) {
changeGear(type);
speed = kmh;
return speed;
}
public double brakePedal() {
speed = 0;
return speed;
}
public char changeGear(char type) {
gear = type;
return gear;
}
public boolean onOffLights() {
lights = !lights;
return lights;
}
public void horn() {
System.out.println("빵빵");
}
}
1) 클래스 간의 관계와 상속
- 상속 : 부모 클래스와 필드와 메서드를 자식 클래스에게 물려준다.
- 정의 :
public class SportsCar extends Car{
String engine;
public void booster() {
System.out.println("엔진 " + engine + " 부앙~\n");
}
}
SportsCar : 자식 클래스
public class Main {
public static void main(String[] args) {
// 부모 클래스 객체에서 자식 클래스 멤버 사용
Car car = new Car();
// car.engine = "Orion"; // 오류
// car.booster(); // 오류
// 자식 클래스 객체 생성
SportsCar sportsCar = new SportsCar();
sportsCar.engine = "Orion";
sportsCar.booster();
// 자식 클래스 객체에서 부모 클래스 멤버 사용
sportsCar.company = "GENESIS";
sportsCar.setModel("GV80");
System.out.println("sportsCar.company = " + sportsCar.company);
System.out.println("sportsCar.getModel() = " + sportsCar.getModel());
System.out.println();
sportsCar.horn();
System.out.println(sportsCar.changeGear('D'));
}
}
Car : 부모 클래스 / SportsCar : 자식 클래스
자식클래스에 있는거 제외하고 부모클래스에 있다고 생각하면 됨
- 다중 상속 (자식이 여러부모에게 상속 받는 것)
- Java에서는 허용 x
- final 클래스와 final 메서드 → final은 바꿀 수 없다는 것을 기억하기 !
public final class Car {}
...
public class SportsCar extends Car{} // 오류가 발생합니다.
클래스에 final 키워드를 지정하여 선언하면 최종적인 클래스가 됨으로 더 이상 상속할 수 없는 클래스가 되고,
메서드에 final 키워드를 지정하여 선언하면 최종적인 메서드가 됨으로 더 이상 오버라이딩할 수 없는 메서드가 된다 !
- Object : 객체, Object 클래스를 의미
- Object 클래스는 모든 클래스들의 최상위 부모 클래스이다
- 따라서 모든 클래스는 Object 의 메서드를 사용할 수 있다
- 부모 클래스가 없는 자식 클래스는 컴파일러에 의해 자동으로 object 클래스를 상속 받음
- 몇가지의 object 클래스 메소드
Object clone() : 해당 객체의 복제본을 생성하여 반환함.
boolean equals(Object object) : 해당 객체와 전달받은 객체가 같은지 여부를 반환함.
Class getClass() : 해당 객체의 클래스 타입을 반환함.
int hashCode() : 자바에서 객체를 식별하는 정수값인 해시 코드를 반환함.
String toString() : 해당 객체의 정보를 문자열로 반환함. & Object 클래스에서는 클래스이름 @해쉬코드값 리턴함.
…
2) 오버라이딩
- 오버라이딩
- 부모 클래스로부터 상속받은 메서드의 내용을 재정의 하는 것
- 오버라이딩 하기 위한 조건
- 선언부가 부모 클래스의 메서드와 일치해야 함
- 접근 제어자를 부모 클래스의 메서드 보다 좁은 범위로 변경할 수 없다
- 예외는 부모 클래스의 메서드보다 많이 선언할 수 없다
SportsCar : 자식 클래스
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();
}
}
public class Main {
public static void main(String[] args) {
// 부모 클래스 자동차 객체 생성
Car car = new Car();
car.horn(); // 경적
System.out.println();
// 자식 클래스 스포츠카 객체 생성
SportsCar sportsCar = new SportsCar("Orion");
// 오버라이딩한 brakePedal(), horn() 메서드 호출
sportsCar.brakePedal();
sportsCar.horn();
}
}
//경적이 다르게 출력됨
- super
- 부모 클래스의 멤버를 참조하는 키워드
- 객체 내부 생성자 및 메서드에서 부모 클래스의 멤버에 접근하기 위해 사용
- 자식 클래스 내부에서 선언한 멤버와 부모 클래스에서 상속받은 멤버와 이름이 같을 경우 구분하기 위해 사용
// 부모 클래스 Car
String model; // 자동차 모델
String color; // 자동차 색상
double price; // 자동차 가격
// 자식 클래스 SportsCar
String model = "Ferrari"; // 자동차 모델
String color = "Red"; // 자동차 색상
double price = 300000000; // 자동차 가격
public void setCarInfo(String model, String color, double price) {
super.model = model; // model은 부모 필드에 set
super.color = color; // color는 부모 필드에 set
this.price = price; // price는 자식 필드에 set
}
자식 클래스
public class SportsCar extends Car{
String engine;
String model = "Ferrari"; // 자동차 모델
String color = "Red"; // 자동차 색상
double price = 300000000; // 자동차 가격
public SportsCar(String engine) {
this.engine = engine;
}
public void booster() {
System.out.println("엔진 " + engine + " 부앙~\n");
}
public void setCarInfo(String model, String color, double price) {
super.model = model; // model은 부모 필드에 set
super.color = color; // color는 부모 필드에 set
this.price = price; // price는 자식 필드에 set
}
@Override
public double brakePedal() {
speed = 100;
System.out.println("스포츠카에 브레이크란 없다");
return speed;
}
@Override
public void horn() {
booster();
}
}
Main 클래스
public class Main {
public static void main(String[] args) {
// 자식 클래스 스포츠카 객체 생성
SportsCar sportsCar = new SportsCar("Orion");
// 자식 객체의 model, color, price 초기값 확인
System.out.println("sportsCar.model = " + sportsCar.model); // Ferrari
System.out.println("sportsCar.color = " + sportsCar.color); // Red
System.out.println("sportsCar.price = " + sportsCar.price); // 3.0E8
System.out.println();
// setCarInfo 메서드 호출해서 부모 및 자식 필드 값 저장
sportsCar.setCarInfo("GV80", "Black", 50000000);
// this.price = price; 결과 확인
System.out.println("sportsCar.price = " + sportsCar.price); // 5.0E7
System.out.println();
// super.model = model; super.color = color;
// 결과 확인을 위해 자식 클래스 필드 model, color 확인 & 부모 클래스 메서드인 getModel(), getColor() 호출
// 자식 클래스 필드 값은 변화 없음.
System.out.println("sportsCar.model = " + sportsCar.model); // Ferrari
System.out.println("sportsCar.color = " + sportsCar.color); // Red
System.out.println();
// 부모 클래스 필드 값 저장됨.
System.out.println("sportsCar.getModel() = " + sportsCar.getModel()); // GV80
System.out.println("sportsCar.getColor() = " + sportsCar.getColor()); // Black
}
}
3) 다형성
- 여러 가지 형태를 가질 수 있는 능력으로 아래 처럼 부모 타이어 변수 = 자식 타이어 객체; 를 선언하여 자동 타입 변환된 변수를 사용하여 각각의 자식 객체에 재정의 된 메서드를 통해 다양한 승차감(타이어)를 가진 자동차를 생성한다.
- Car - 부모 , Tire - 자식
Tire tire = new HankookTire("HANKOOK");
Tire tire = new KiaTire("KIA");
public Car(Tire tire) {
this.tire = tire;
}
...
Car car1 = new Car(new KiaTire("KIA"));
Car car2 = new Car(new HankookTire("HANKOOK")); // Car 생성자에서 매개변수의 타입이 부모 타이어이기 떄문에 자식 타이어 객체들을 매개값으로 전달할 수 있다.
Tire getHankookTire() {
return new HankookTire("HANKOOK");
}
Tire getKiaTire() {
return new KiaTire("KIA");
}
...
Tire hankookTire = car1.getHankookTire();
KiaTire kiaTire = (KiaTire) car2.getKiaTire();
//반환 타입이 부모 타이어이기 때문에 자식 타이어 객체들을 반환값으로 지정할 수 있다
//자동 타입 변환이 된 반환값인 자식 타이어 객체를 강제 타입 변환할 수 있다
- 자동 타입 변환
- 부모 타입 변수 = 자식 타입 객체; 는 자동으로 부모 타입으로 변환
- 부모 타입 변수로 자식 객체의 멤버에 접근할 때는 부모 클래스에 선언된, 즉 상속받는 멤버만 접근 가능
//Mammal 부모 클래스
class Mammal {
// 포유류는 새끼를 낳고 모유수유를 한다.
public void feeding() {
System.out.println("모유수유를 합니다.");
}
}
// Whale : 자식 클래스
class Whale extends Mammal {
// 고래는 포유류 이면서 바다에 살며 수영이 가능하다.
public void swimming() {
System.out.println("수영하다.");
}
@Override
public void feeding() {
System.out.println("고래는 모유수유를 합니다.");
}
}
//Main 클래스
public class Main {
public static void main(String[] args) {
// 고래는 포유류이기 때문에 포유류 타입으로 변환될 수 있습니다.
Mammal mammal = new Whale();
// 하지만 포유류 전부가 바다에 살고 수영을 할 수 있는 것은 아니기 때문에
// 수영 하다 메서드는 실행 불가
// 즉, 부모 클래스에 swimming이 선언되어있지 않아서 사용 불가능합니다.
// mammal.swimming(); // 오류 발생
// 반대로 모든 포유류가 전부 고래 처럼 수영이 가능한 것이 아니기 때문에 타입변환이 불가능합니다.
// 즉, 부모타입의 객체는 자식타입의 변수로 변환될 수 없습니다.
// Whale whale = new Mammal(); // 오류 발생
mammal.feeding();
}
}
- 강제 타입 변환
- 자식 타입 변수 = (자식 타입) 부모 타입 객체;
// 자식타입객체가 자동 타입변환된 부모타입의 변수
Mammal mammal = new Whale();
mammal.feeding();
// 자식객체 고래의 수영 기능을 사용하고 싶다면
// 다시 자식타입으로 강제 타입변환을 하면된다.
Whale whale = (Whale) mammal;
whale.swimming();
- 하지만 항상 가능한건 아님
- 자식 타입 객체가 부모 타입으로 자동 타입 변환된 후 다시 자식 타입으로 변환될 때만 강제 타입 변환이 가능
- 부모 타입 변수로는 자식 타입 객체의 고유한 멤버를 사용할 수 없기 때문에 사용이 필요한 경우가 생겼을 때 강제 타입 변환을 사용
Mammal newMammal = new Mammal();
Whale newWhale = (Whale) newMammal; // ClassCastException 발생
자동 타입 변환된 부모 타입 변수가 아닌 부모 객체를 자식 타입의 변수로 강제 타입 변환하려고 하면 오류가 발생
- instanceof
- 다형성 기능으로 인해 해당 클래스 객체의 원래 클래스명을 체크하는 명령어
- {대상 객체} instance of {클래스 이름} → 응답값은 boolean
// 다형성
class Parent { }
class Child extends Parent { }
class Brother extends Parent { }
public class Main {
public static void main(String[] args) {
Parent pc = new Child(); // 다형성 허용 (자식 -> 부모)
Parent p = new Parent();
System.out.println(p instanceof Object); // true 출력
System.out.println(p instanceof Parent); // true 출력
System.out.println(p instanceof Child); // false 출력
Parent c = new Child();
System.out.println(c instanceof Object); // true 출력
System.out.println(c instanceof Parent); // true 출력
System.out.println(c instanceof Child); // true 출력
}
}
4) 추상 클래스
- 클래스-설계도, 추상 클래스-미완성된 설계도
- abstract 사용하여 선언
public abstract class 추상클래스명 {
}
- 추상 클래스는 자식 클래스에 상속되어 자식 클래스에 의해서만 완성될 수 있다.
- 추상 메서드 : 아직 구현되지 않은 미완성된 메서드
- 추상 클래스안에서만 선언 가능
public abstract class 추상클래스명 {
abstract 리턴타입 메서드이름(매개변수, ...);
}
추상 메서드는 {}가 없음 , 정의만 할 뿐 실행 내용은 없다
- 추상 클래스 상속
public class 클래스명 extends 추상클래스명 {
@Override
public 리턴타입 메서드이름(매개변수, ...) {
// 실행문
}
}
상속받는 클래스에서 추상 클래스의 추성 메서드는 반드시 오버라이딩이 되어야 한다.
##예제
public class BenzCar {
String company; // 자동차 회사 : GENESIS
String color; // 자동차 색상
double speed; // 자동차 속도 , km/h
public double gasPedal(double kmh) {
speed = kmh;
return speed;
}
public double brakePedal() {
speed = 0;
return speed;
}
public void horn() {
System.out.println("Benz 빵빵");
}
}
public class AudiCar {
String company; // 자동차 회사 : GENESIS
String color; // 자동차 색상
double speed; // 자동차 속도 , km/h
public double gasPedal(double kmh) {
speed = kmh;
return speed;
}
public double brakePedal() {
speed = 0;
return speed;
}
public void horn() {
System.out.println("Audi 빵빵");
}
}
public class ZenesisCar {
String company; // 자동차 회사 : GENESIS
String color; // 자동차 색상
double speed; // 자동차 속도 , km/h
public double gasPedal(double kmh) {
speed = kmh;
return speed;
}
public double brakePedal() {
speed = 0;
return speed;
}
public void horn() {
System.out.println("Zenesis 빵빵");
}
}
→ horn() 메서드 내용만 다 다름 , horn() 메서드를 추상 메서드로 선언하여 자식 클래스에서 재정의하기
< Car 추상 클래스 >
public abstract class Car {
String company; // 자동차 회사
String color; // 자동차 색상
double speed; // 자동차 속도 , km/h
public double gasPedal(double kmh) {
speed = kmh;
return speed;
}
public double brakePedal() {
speed = 0;
return speed;
}
public abstract void horn();
}
< 자식 클래스들 Override 하기 >
public class BenzCar extends Car {
@Override
public void horn() {
System.out.println("Benz 빵빵");
}
}
public class AudiCar extends Car {
@Override
public void horn() {
System.out.println("Audi 빵빵");
}
}
public class ZenesisCar extends Car {
@Override
public void horn() {
System.out.println("Zenesis 빵빵");
}
}
<Main 클래스>
public class Main {
public static void main(String[] args) {
Car car1 = new BenzCar();
car1.horn();
System.out.println();
Car car2 = new AudiCar();
car2.horn();
System.out.println();
Car car3 = new ZenesisCar();
car3.horn();
}
}
< 15. 인터페이스 >
- 인터페이스 : 두 객체를 연결해 주는 다리 역할 (ex. 멀티 모컨으로 삼성티비와 엘지티비 모두 채널을 바꿀 수 있는 것)
- 상속 관계가 없는 두 클래스들이 메서드를 구현할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장해준다
//인터페이스 선언
public interface 인터페이스명 {
}
- 인터페이스의 멤버
public interface 인터페이스명 {
public static final char A = 'A'; //모든 멤버 변수는 public static final 이다(생략도 가능)
static char B = 'B';
final char C = 'C';
char D = 'D';
void turnOn(); // public abstract void turnOn(); 모든 메서드는 public abstract 이어야 한다(생략도 가능, static&default 메서드 예외)
//생략되는 제어자는 컴파일러가 자동으로 추가해줌
}
- 추상 클래스와 마찬가지로 직접 인스턴스를 생성할 수 없어서 클래스에 구현되어 생성됨
- 인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩 되어야 함
- 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경 해주면 됨
public class 클래스명 implements 인터페이스명 {
// 추상 메서드 오버라이딩
@Override
public 리턴타입 메서드이름(매개변수, ...) {
// 실행문
}
}
- 인터페이스 간의 상속이 가능
- 인터페이스 간의 상속은 implements 이 아닌 extends 사용
- 다중 상속 가능
public class Main implements C {
@Override
public void a() {
System.out.println("A");
}
@Override
public void b() {
System.out.println("B");
}
}
interface A {
void a();
}
interface B {
void b();
}
interface C extends A, B { } // 인터페이스 C는 A,B 를 다중 상속 받음
//Main 클래스에서 인터페이스 C가 구현되면 a,b 추상 메서드가 오버라이딩 됨
- 인터페이스의 구현은 상속과 함께 사용 가능
public class Main extends D implements C {
@Override
public void a() {
System.out.println("A");
}
@Override
public void b() {
System.out.println("B");
}
@Override
void d() {
super.d();
}
public static void main(String[] args) {
Main main = new Main();
main.a();
main.b();
main.d();
}
}
interface A {
void a();
}
interface B {
void b();
}
interface C extends A, B {
}
class D {
void d() {
System.out.println("D");
}
}
- 디폴트 메서드
- 추상 메서드의 기본적인 구현을 제공하는 메서드
- 추상 메서드는 아니라 인터페이스의 구현체들에서 필수로 재정의 할 필요는 없다
public class Main implements A {
@Override
public void a() {
System.out.println("A");
}
public static void main(String[] args) {
Main main = new Main();
main.a();
// 디폴트 메서드 재정의 없이 바로 사용가능합니다.
main.aa();
}
}
interface A {
void a();
default void aa() {
System.out.println("AA");
}
}
- Static 메서드
- 인터페이스의 static 메서드도 static 의 특성 그대로 객체 없이 호출이 가능하다
public class Main implements A {
@Override
public void a() {
System.out.println("A");
}
public static void main(String[] args) {
Main main = new Main();
main.a();
main.aa();
System.out.println();
// static 메서드 aaa() 호출
A.aaa();
}
}
interface A {
void a();
default void aa() {
System.out.println("AA");
}
static void aaa() {
System.out.println("static method");
}
}
- 인터페이스의 다형성
- 자동 타입 변환 (인터페이스 변수 = 구현객체;)
public class Main {
public static void main(String[] args) {
// A 인터페이스에 구현체 B 대입
A a1 = new B();
// A 인터페이스에 구편체 B를 상속받은 C 대입
A a2 = new C();
}
}
interface A { }
class B implements A {}
class C extends B {}
- 강제 타입 변환 (구현 객체 타입 변수 = (구현 객체 타입) 인터페이스 변수;
public class Main {
public static void main(String[] args) {
// A 인터페이스에 구현체 B 대입
A a1 = new B();
a1.a();
// a1.b(); // 불가능 ; a1은 인터페이스 A 타입(자동 형변환)이기 때문에, a() 메서드만 가지고 있음
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()");
}
}
'TIL' 카테고리의 다른 글
TIL : 2024/08/01 Java Generic (0) | 2024.08.01 |
---|---|
TIL : 2024/7/31 JAVA 예외 처리 (0) | 2024.07.31 |
TIL : 2024/7/29 Java Class(2) (0) | 2024.07.30 |
TIL : 2024/7/25 Java Class(1) (0) | 2024.07.25 |
TIL : 2024/7/24 Java Collection (4) | 2024.07.24 |