자 이번주 시작이다!!!!!!!!가보자!!!!!!!!!!
클래스 간의 관계를 알아야한다.
유산을 물려주는 행위
코드 중복을 제거! 재사용성이 크게 증가하여 생산성과 유지보수성에 매우 유리!!!
1. 부모 클래스에 새로운 필드와 메서드가 추가되면 자식 클래스는 이를 상속받아 사용할 수 있다.
2. 자식 클래스에 새로운 필드와 메서드가 추가되어도 부모 클래스는 어떠한 영향도 받지 않는다.
3. 따라서 자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.
= 상속 받아서 사업을 할수있는거잖아>????그러니 자식이 더 크겠지
잘 기억해둘것!!!!!!!!
Java는 다중상속을 허용하지 않는다!!!!!!!!!!!!!!!!
간단히 이해하면 부모는 자식이 여러명 있을 수있지만 자식이 부모가 여러명...?쉽지않다.
다중상속을 허용하면 클래스간의 관계가 복잡해지는 문제
클래스에 final
키워드를 지정하여 선언하면 최종적인 클래스가 됨으로 더 이상 상속할 수 없는 클래스
메서드에 final
키워드를 지정하여 선언하면 최종적인 메서드가 됨으로 더 이상 🌟오버라이딩🌟할 수 없는 메서드
Object 클래스는 Java 내 모든 클래스들의 최상위 부모 클래스
얄코님이 말하길 조상님격이라고 했다.
부모 클래스로부터 상속받은 메서드의 내용을 재정의 하는 것을 오버라이딩
부모 클래스의 메서드를 그대로 사용 가능하지만 자식 클래스의 상황에 맞게 변경을 해야하는 경우 오버라이딩을 사용
@ <-이게 뭐지? 에노테이션 (annotaion)
this(인스턴스), this()(생성자) 랑 비슷
super는 부모 클래스의 멤버를 참조할 수 있는 키워드
super는 부모를 건드리고
this는 거기있는 그냥 자식을 건드린다!
super(…)는 부모 클래스의 생성자를 호출할 수 있는 키워드
super();
를 자식 클래스 생성자 첫 줄에 자동으로 추가부모타입 변수 = 자식타입객체;
는 자동으로 부모타입으로 변환
포유류 고래 = 고래객체;
가 성립될 수 있습니다.자식타입 변수 = (자식타입) 부모타입객체;
다형성이란 ‘여러 가지 형태를 가질 수 있는 능력’
참조변수와 타입변환을 활용하여 사용 가능!!!!
다형성 기능으로 인해 해당 클래스 객체의 원래 클래스명을 체크하는것이 필요한데 이때 사용할 수 있는 명령어가 instance of
{대상 객체}
instance of
{클래스 이름}
와 같은 형태로 사용하면 응답값은 boolean !!!!!abstract
키워드를 사용하여 추상 클래스를 선언
추상 메서드-추상 메서드는 아직 구현되지 않은 미완성된 메서드
abstract
키워드를 사용하여 추상 메서드를 선언
public abstract class 추상클래스명 {
abstract 리턴타입 메서드이름(매개변수, ...);
}
추상 클래스 상속
인터페이스는 두 객체를 연결해주는 다리 역할
좋은 예제이다!!!!
interface 키워드를 사용하여 인터페이스를 선언
모든 멤버변수는 public static final(생략가능)
모든 메서드는 public abstract (생략가능)
생략되는 제어자는 컴파일러가 자동으로 추가(좋은 프로그램이네)
implements 키워드를 사용하여 인터페이스를 구현
인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩
만약 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경
인터페이스간의 상속은 implements 가 아니라 extends 키워드를 사용
인터페이스는 클래스와는 다르게 다중 상속이 가능
static의 특성 그대로 인터페이스의 static 메서드 또한 객체 없이 호출이 가능
구현객체타입 변수 = (구현객체타입) 인터페이스변수;
예제로 설명해주셔서
만약 다시금 생각하며 복습하고 싶으면
인텔리제이에 interFinal을 볼것!!!
이 자식 클래스는 부모 클래스로 Tv를 상속하고 있고 또한 인터페이스로 리코컨도 사용하는중이다.
public class Calculator {
private AbstractOperation operation;
public Calculator(AbstractOperation operation) {
this.operation = operation;
}
public double calculate(int firstNumber, int secondNumber) {
return operation.operate(firstNumber, secondNumber);
}
}
Calculator
는 주입 받은 연산 객체를 이용해서 연산을 수행하는 클래스
연산 객체는 AbstractOperation
타입으로, 어떤 연산을 수행할 것인지를 결정
calculate 메서드는 주입 받은 연산 객체의 operate
메서드를 호출하여 연산 결과를 반환
public abstract class AbstractOperation {
public abstract double operate(int firstNumber, int secondNumber);
}
AbstractOperation
은 추상 연산 클래스
이 클래스를 상속받는 모든 연산 클래스는 operate
메서드를 구현
public class AddOperation extends AbstractOperation {
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
}
더하기 연산을 수행하는 클래스
public class SubstractOperation extends AbstractOperation {
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
}
빼기 연산을 수행하는 클래스
public class MultiplyOperation extends AbstractOperation {
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
}
곱하기 연산을 수행하는 클래스
public class SubstractOperation extends AbstractOperation {
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
}
나누기 연산을 수행하는 클래스
public class Main {
public static void main(String[] args) {
Calculator addCalculator = new Calculator(new AddOperation());
System.out.println(addCalculator.calculate(5, 3)); // 출력: 8.0
Calculator subCalculator = new Calculator(new SubstractOperation());
System.out.println(subCalculator.calculate(5, 3)); // 출력: 2.0
Calculator mulCalculator = new Calculator(new MultiplyOperation());
System.out.println(mulCalculator.calculate(5, 3)); // 출력: 15.0
Calculator divCalculator = new Calculator(new DivideOperation());
System.out.println(divCalculator.calculate(5, 3)); // 출력: 1.6666666666666667
}
}
여러 연산 객체를 생성하고, 각각의 연산 객체를 Calculator
에 주입하여 연산 결과를 출력
이렇게 3주차가 마무리 되고
오류(Error)
시스템 레벨에서, 또는 주로 환경적인 이유로 발생
발생하는경우 일반적으로 회복이 불가능
에러가 발생한 경우 우리는 어떠한 에러로 프로그램이 종료되었는지를 확인하고 대응
예외(Exception)
코드레벨에서 할 수 있는 문제상황에 대한 대응은 “예외처리”
예외의 종류(이게 참 많다...하지만 크게는 두개!)
컴파일 에러!!!
컴파일이란
자바 프로그래밍 언어의 규칙을 지키지 않았기 때문에 발생
컴파일 에러가 발생하는 경우 해결 방법은 문법에 맞게 다시 작성하는 것
런타임 에러(코테하면서 젤 많이 본 에러....)
문법적인 오류는 아니라서, 컴파일은 잘 되었지만 “프로그램”이 실행도중 맞닥뜨리게 되는 예외
예외처리 관점에서 예외의 종류
확인된 예외(Checked Exception)
컴파일 시점에 확인하는 예외
반드시 예외 처리를 해줘야하는 예외
Checked Exception
에 대한 예외처리를 하지 않으면 컴파일 에러
가 발생
미확인된 예외(Unchecked Exception)
런타임 시점에 확인되는 예외
예외 처리가 반드시 필요하지 않은 예외
throws : 던지다!(예외를 던지다!/발생시키다)
throws | throw |
---|---|
메서드 이름 뒤에 붙어 이 메서드가 어떠한 예외사항을 던질 수 있는지 알려주는 예약어 입니다. | 메서드 안에서, 실제로 예외 객체를 던질 때 사용하는 예약어 입니다. |
여러 종류의 예외사항을 적을 수 있습니다. | 실제로 던지는 예외 객체 하나와 같이 써야 합니다. |
일반 메서드의 return 키워드처럼 throw 아래의 구문들은 실행되지 않고, throw문과 함께 메서드가 종료됩니다. |
try{
위험한 메소드의 실행을 "시도"
}catch() {
예외가 발생하면, "잡아서" handling
}finally {
예외의 발생 여부와 상관없이, 실행시켜야 하는 코드
}
try {
// "시도" 해보는 코드가 들어가는 블럭입니다.
ourClass.thisMethodIsDangerous();
} catch (OurBadException e) {
// 예외가 발생하는경우 "handling" 하는 코드가 들어가는 블럭입니다.
// 즉 try 블럭 내의 구문을 실행하다가 예외가 발생하면
// 예외가 발생한 줄에서 바로 코드 실행을 멈추고
// 여기 있는 catch 블럭 내의 코드가 실행됩니다.
System.out.println(e.getMessage());
} finally {
// 무조건 실행되는 코드가 들어가는 블럭입니다.
System.out.println("우리는 방금 예외를 handling 했습니다!");
}
정리!!!!
Checked Exception
예외가 발생하는 상황을 “인지” 했고, 어떠한 에러인지 “정의”
메서드를 선언 할 때 예외가 발생하는 위험한 메서드라는 것을 “알린다.(throws/throw)”.
checked exception을 정의하고, 알렸으니 이 메서드를 사용 할 때 예외처리를 하지 않으면 컴파일 에러가 발생!!!!!!!!!!!!!!
자바의 Throwable Class
Object
클래스에서 시작Throwable
클래스가 Object 클래스를 상속Throwable
클래스의 자식으로 앞서배운 에러(Error
)와 예외(Exception
) 클래스Error
) 클래스와 예외(Exception
) 클래스는 각각 IOError
클래스, RuntimeException
클래스와 같이 구분하여 처리
정말 많이 보게 될 예외들....
Java 예외 리스트
// 출처 : https://programming.guide/java/list-of-java-exceptions.html
java.io
IOException
CharConversionException
EOFException
FileNotFoundException
InterruptedIOException
ObjectStreamException
InvalidClassException
InvalidObjectException
NotActiveException
NotSerializableException
OptionalDataException
StreamCorruptedException
WriteAbortedException
SyncFailedException
UnsupportedEncodingException
UTFDataFormatException
UncheckedIOException
java.lang
ReflectiveOperationException
ClassNotFoundException
InstantiationException
IllegalAccessException
InvocationTargetException
NoSuchFieldException
NoSuchMethodException
CloneNotSupportedException
InterruptedException
산술 예외
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException
StringIndexOutOfBoundsException
ArrayStoreException
ClassCastException
EnumConstantNotPresentException
IllegalArgumentException
IllegalThreadStateException
NumberFormatException
IllegalMonitorStateException
IllegalStateException
NegativeArraySizeException
NullPointerException
SecurityException
TypeNotPresentException
UnsupportedOperationException
java.net
HttpRetryException
SocketTimeoutException
MalformedURLException
ProtocolException
SocketException
BindException
ConnectException
NoRouteToHostException
PortUnreachableException
UnknownHostException
UnknownServiceException
URISyntaxException
java.text
ParseException
java.time
DateTimeException
java.time.zone
ZoneRulesException
연결된 예외 (Chained Exception)
예외는 다른 예외를 유발할 수 있다.
원인 예외를 다루기 위한 메소드
initCause()
getCause()
실질적으로 예외를 처리하는 방법으로는 예외 복구
, 예외 처리 회피
, 예외 전환
이 있다.
//예외 복구하기
public String getDataFromAnotherServer(String dataPath) {
try {
return anotherServerClient.getData(dataPath).toString();
} catch (GetDataException e) {
return defaultData;
}
}
//실제로 try-catch로 예외를 처리하고 프로그램을 정상 상태로 복구하는 방법
//가장 기본적인 방식이지만, 현실적으로 복구가 가능한 상황이 아닌 경우가 많거나
//최소한의 대응만 가능한 경우가 많기 때문에 자주 사용되지는 않는다...
//예외 처리 회피하기
public void someMethod() throws Exception { ... }
public void someIrresponsibleMethod() throws Exception {
this.someMethod();
}
// 관심사를 분리해서 한 레이어에서 처리하기 위해서 이렇게 에러를 회피해서 그대로 흘러 보내는 경우도 있습니다.
//예외 전환하기
public void someMethod() throws IOException { ... }
public void someResponsibleMethod() throws MoreSpecificException {
try {
this.someMethod();
} catch (IOException e) {
throw new MoreSpecificException(e.getMessage());
}
}
//예외처리 회피하기의 방법과 비슷하지만, 조금더 적절한 예외를 던져주는 경우
//보통은 예외처리에 더 신경쓰고싶은 경우나,
//오히려 RuntimeException처럼 일괄적으로 처리하기 편한 예외로 바꿔서 던지고 싶은 경우 사용
제네릭의 효용
아니 듣다보니 쫌 화나네?파이썬이랑 자바스크립트는 왜케 효용성이 좋아?
하지만...에러많이 난다니 힘내보자구
// 제네릭을 사용한 클래스 선언
public class Box<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
// 사용 예:
Box<String> stringBox = new Box<>();
stringBox.setData("Hello, Generics!");
String str = stringBox.getData();
제한된 타입 파라미터(Bounded Type Parameter):
특정 타입의 하위 클래스만 허용하고 싶을 때 사용
public class NumberBox<T extends Number> {
private T data;
주의사항
기본 타입(primitive type)은 제네릭 타입 파라미터로 사용될 수 없다 (int, char 등)
실행 시(runtime) 제네릭의 타입 정보는 제거!!! 타입 소거(type erasure)
제네릭에서 <>사이에 들어가는 변수명 T는 타입 변수!
Generic 클래스를 원시 타입
객체의 static 멤버에 사용 할 수 없다
제네릭 배열을 생성 할 수 없다.
다수의 타입변수를 사용 할 수 있다
다형성 즉 상속과 타입의 관계는 그대로 적용
와일드 카드를 통해 제네릭의 제한을 구체적으로 정할 수 있다
와일드카드(Wildcard):
제네릭 타입의 제한된 유연성을 확보
<?>
: 모든 타입이 허용. (예: List<?>
)
<? extends T>
: T와 T의 하위 타입만 허용됩니다.
<? super T>
: T와 T의 상위 타입만 허용됩니다.
메서드를 스코프로 제네릭을 별도로 선언 할 수 있다
검색에는 유리하고, 수정/삭제는 불리한 자료구조
정한 규칙을 규정한 “리스트” 자료구조가 바로 “스택”, ”큐”
결론은 생각하며 써야한다...그냥 냅다 이리 쓰라해서 쓰는건 도움이 안된다!
Integer num = new Integer(17); // Boxing
int n = num.intValue(); // UnBoxing
Character ch = 'X'; // AutoBoxing
char c = ch; // AutoUnBoxing
Wrapper 클래스는 Java의 기본 타입(primitive types)을 객체로 포장(wrap)하는데 사용되는 클래스
Byte
, Short
, Integer
, Long
– 정수 타입
Float
, Double
– 부동 소수점 타입
Character
– 문자 타입
Boolean
– 불리언 타입
기본 타입을 객체로 사용할 필요가 있을 때 (예: 컬렉션 자료구조에서 사용)
타입에 관련된 유용한 메서드를 제공 (예: Integer.parseInt(), Character.isDigit())
null
값을 허용해야 할 때
오토박싱: 기본 타입의 값을 자동으로 해당 Wrapper 객체로 변환하는 것.
오토언박싱: Wrapper 객체를 자동으로 기본 타입의 값으로 변환하는 것.
Wrapper 객체는 immutable(불변) 값이 변할 때마다 새로운 객체가 생성.
객체가 필요한 경우(예: 컬렉션에서 사용)에는 Wrapper 클래스를 사용
오토박싱과 오토언박싱이 편리하긴 하지만, 무분별하게 사용될 경우 성능 저하의 원인이 된다
import java.util.regex.Pattern;
public class Parser {
private static final String OPERATION_REG = "[+\\-*/]";
private static final String NUMBER_REG = "^[0-9]*$";
private final Calculator calculator = new Calculator();
public Parser parseFirstNum(String firstInput) {
// 구현 1.
}
public Parser parseSecondNum(String secondInput) {
// 구현 1.
}
public Parser parseOperator(String operationInput) {
// 구현 1.
}
public double executeCalculator() {
return calculator.calculate();
}
}
를 혼자 뚱땅해보면
import java.util.regex.Pattern;
public class Parser {
private static final String OPERATION_REG = "[+\\-*/]";
private static final String NUMBER_REG = "^[0-9]*$";
private final Calculator calculator = new Calculator();
public Parser parseFirstNum(String firstInput) throws BadInputException {
if (!Pattern.matches(NUMBER_REG, firstInput)) {
throw new BadInputException("숫자");
}
calculator.setFirstNumber(Integer.parseInt(firstInput));
return this;
}
public Parser parseSecondNum(String secondInput) throws BadInputException {
if (!Pattern.matches(NUMBER_REG, secondInput)) {
throw new BadInputException("숫자");
}
calculator.setSecondNumber(Integer.parseInt(secondInput));
return this;
}
public Parser parseOperator(String operationInput) throws BadInputException {
if (!Pattern.matches(OPERATION_REG, operationInput)) {
throw new BadInputException("연산자 (+, -, *, / 중 하나)");
}
switch (operationInput) {
case "+":
calculator.setOperation(new AddOperation());
break;
case "-":
calculator.setOperation(new SubstractOperation());
break;
case "*":
calculator.setOperation(new MultiplyOperation());
break;
case "/":
calculator.setOperation(new DivideOperation());
break;
}
return this;
}
public double executeCalculator() {
return calculator.calculate();
}
}
public class Main {
public static void main(String[] args) {
boolean calculateEnded = false;
// 구현 2.
}
}
public class Main {
public static void main(String[] args) {
boolean calculateEnded = false;
// 구현 2.
while (!calculateEnded) {
try {
calculateEnded = CalculatorApp.start();
} catch (BadInputException e) {
System.out.println(e.getMessage());
// 사용자에게 다시 입력을 받기 위해 계속한다.
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
calculateEnded = true; // 0으로 나누기 시도시 프로그램 종료
} catch (Exception e) {
System.out.println("알 수 없는 오류 발생: " + e.getMessage());
calculateEnded = true; // 알 수 없는 오류 발생시 프로그램 종료
}
}
}
}
후... 복습은 진즉에 끝나버렸고 새로운 개념들에 듣게 되니 머리가 아프구나?
몹시아프다 그렇다 아프다 이말이다
그래서 오늘은 4주차까지만 할수있었다.
내일은 5주차까지 마무리를 할 생각으로 열심히 달려봐야겠다!!!!!!!