2023-05-10
1. 파일 입출력
- 파일 입출력 : Scanner, System.out.print() 방식이 아닌 파일을 통해 입력과 출력을 합니다.
- 파일 입출력 방식엔 BufferedReader , BufferedWriter와 FileReader, FileWriter이 있습니다.
- FileWriter를 단독으로 사용 가능하지만 BufferedWriter와 같이 사용 시 속도가 빠름
- BufferedReader : 버퍼를 사용한 입력 클래스
- BufferedWriter : 버퍼를 사용한 출력 클래스
- FileReader : 전달한 경로의 파일을 읽어들이기 위한 클래스이며, 텍스트 파일을 자바로 읽어올 때 사용
- 전달한 경로에 파일이 없다면 FileNotFoundException 발생
- FileWriter : 전달한 경로의 파일을 출력하기 위한 클래스
- 전달한 경로에 파일이 없다면 새롭게 만든 후 실행
- 파일이 이미 존재한다면 덮어쓰게 됨
//사용방법 - 입력 BufferedReader br = new BufferedReader(new FileReader(파일이름 및 경로));
//사용방법 - 출력 BufferedWriter bw = new BufferedWriter(new FileWirter(파일이름 및 경로));
1-1. 파일 경로 (절대경로와 상대경로)
상대경로
- 상대경로 : 특정파일이나 폴더를 기준으로 주소를 표현하는법으로 “/” , “./” , “../” 을 사용
- / : 루트 ex) C:\
- ./ : 현재 폴더 ex) C:\Program Files\Java
- ../ : 상위 폴더 ex) C:\Program Files
절대경로
- 절대경로 : 어떠한 웹페이지나 파일이 갖고있는 고유한 경로
- http://www.google.com ( 웹페이지의 절대경로 )
- C:\Program Files\Java ( 파일의 절대경로 )
2. 예외 처리 (throws IOException)
- ‘throws’ 는 메소드나 생성자에서 발생할 수 있는 예외를 호출한 메소드로 던지는 것을 의미합니다.
- ‘IOException’ 은 Java에서 발생할 수 있는 예외 클래스 중 하나입니다. 입출력 작업 중 발생하는 예외를 처리하기 위해 사용됩니다.
즉, ‘throws IOException’이란 ‘예외를 던진다(처리한다)’ 라고 생각해 볼 수 있습니다.
- Input, Output(입출력)을 하는 과정에서 발생하는 예외
- 파일이 존재하지 않는경우
- 파일에 읽기 또는 쓰기 권한이 없는 경우
- 네트워크를 이용한 통신 작업을 할 경우
- 장점
- thorws를 이용하면 해당 메소드에서 발생할 수 있는 예외를 명확히 알 수 있습니다.
- 또한 코드를 간결하게 유지할 수 있도록 해주며, 예외 처리를 중복해서 작성하지 않아도 되게 한답니다.
- 단점
- 예외 처리를 호출하는 쪽에서 해야 하기 때문에 코드가 중복되거나 누락이 될 수 있습니다.
- 호출하는 쪽에서 예외 처리를 하지 않으면 런타임 예외가 발생할 수 있습니다.
입출력 작업을 수행하는 코드에서는 IOException 예외를 처리하는 것이 중요합니다. 호출하는 쪽에서 예외 처리를 하지 않는다면 컴파일 오류가 발생할 수 있기 때문이죠.
try catch
- 사실 예외 처리 방법에는 두 가지가 있습니다.두 번째는 ‘try catch’ 문을 사용하는 방법이 있습니다.
- 첫 번째는 위에서 설명한 ‘throws IOException’ 방법과
try catch 문 또한 프로그램에서 예외가 발생할 가능성이 있는 코드를 감싸고, 예외가 발생하면 해당 예외를 처리하는 코드를 제공하는 블록입니다.
- ‘try catch’ 문 예시
- try : 예외가 발생할 가능성이 있는 코드 블록을 시작하는 예약어입니다.
- catch: 예외가 발생했을 때 실행할 코드 블록을 정의하는 예약어입니다. catch 다음에는 괄호로 감싸진 예외 식별자가 위치합니다. 이 식별자를 사용하여 발생한 예외에 대한 정보를 얻을 수 있습니다.
- finally: 예외가 발생 유무 없이 항상 실행되는 코드 블록을 정의하는 예약어입니다.
- try블록 안에서 예외가 발생하면, 예외 객체가 생성됩니다.
- 예외 객체는 catch블록으로 전달됩니다.
- catch블록에서 예외 객체를 처리합니다. 이후 finally블록이 실행됩니다.
- try { // 예외 발생 예상 코드 } catch (해당 예외 클래스) { // 예외 처리 코드 } finally { // 항상 실행되는 코드 }
- 장점
- 구체적인 예외 처리가 가능합니다.
- 코드의 가독성을 높여주고 예외 처리 코드를 간소화 시켜줍니다.
- 예외가 발생해도 finally 블록 안의 코드는 무조건 실행할 수 있습니다.
- 단점
- 남용한다면 코드의 가독성이 떨어지고, 성능이 저하 될 수 있습니다.
- 루프(while, for) 안에 try-catch 를 넣으면 느려질 수 있으므로 지양해야 합니다.
try catch 문에서 예외를 다시 던지는 경우에는 첫 번째 방법인 throw 문을 사용합니다. throw문에는 예외 타입과 예외 메시지를 포함한 예외 객체를 전달할 수 있습니다.
인터페이스
- 자바에서 인터페이스란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미합니다.
- 인터페이스의 내용을 실제로 구현한 클래스를 구현 클래스라고 합니다.
Abstract와의 차이점
- abstract 클래스는 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있습니다.
- 반면 인터페이스는 오로지 abstract 메소드와 final 선언된 필드만을 포함할 수 있습니다.
- abstract 클래스는 다중 상속을 지원하지 않습니다.
- 반면 인터페이스는 다중 상속을 지원합니다.
인터페이스의 선언
- 인터페이스를 선언할 때에는 접근 제어자와 함께 interface 키워드를 사용하면 됩니다.
- 코드
- public interface InterfaceExample{ //final 선언이 되어 있어야 한다! public static final finalVariableEx = 10; //abstract 선언이 되어 있어야 한다. public abstract void methodEx(int paramEx); } //구현할 필요가 없으므로 중괄호 { } 대신 세미콜론 ; 으로 끝마친다.
인터페이스 예시
- 코드
- 간단한 출력을 담당하는 print() 메소드만을 가지고 있는 인터페이스입니다.
import java.io.IOException; public class ConsolePrinter implements Printer{ @Override public void print(String[] lines) throws IOException { for(int i = 0; i < lines.length; i++) System.out.print(lines[i]); } }
- Printer 인터페이스를 implements하는 구현 클래스입니다.
- import java.io.IOException; public interface Printer { void print(String[] lines) throws IOException; }
인터페이스의 장점
- 일관되고 정형화된 개발을 위한 표준화가 가능합니다
💡 예를 들어 결제를 하는 기능을 추가하고자 할 때, 결제 방법에 따라 여러 개의 클래스를 만들어야 할 것 입니다. 이때, 인터페이스를 하나의 양식처럼 사용하여 일관된 동작을 구현하도록 설계할 수 있습니다.
기능의 확장이 용이하며, 개발 시간이 단축됩니다.
예를 들어 결제 인터페이스가 존재하는 상황에서, 카드 결제 기능을 추가한다면 해당 결제 인터페이스를 implement 받아서 Overide하는 방식으로 기능을 확장해 나갈 수 있습니다.</aside>DI, IOC 부분에서 자세히 설명하겠지만, 메소드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문입니다.이는 여러 코드가 동시에 개발되는 환경에서 시간이 단축되는 효과가 있습니다.
따라서 구현 클래스가 완성되기를 기다리면서 코딩하지 않아도 됩니다.
💡 일단 인터페이스가 작성되면, 구현 클래스가 없더라도 프로그램 작성이 가능합니다.
기존 코드를 크게 수정하지 않으면서도 추가적인 기능을 구현할 수 있다는 점에서 매우 안전하고 편한 방법이라고 할 수 있습니다.
💡 인터페이스가 표준화된 양식 역할을 하기 때문에 추가적인 기능 구현이 매우 용이합니다.
4. IOC(제어의 역전)와 DI(의존성 주입)
IOC(Inversion of control, 제어의 역전)
- 프로그램 내부에 존재하는 객체들과 그 객체들의 의존성을 프레임워크나 컨테이너와 같은 외부에서 제어하는 것입니다.
- Spring에서 자주 사용합니다.
- → Spring 전엔 개발자가 프로그램의 흐름(애플리케이션 코드) 제어 주체
- 제어권이 컨테이너로 넘어감(Spring에서 추후 배울 것)
- IOC를 통해 DI(의존성 주입) 사용 가능
DI(Dependency Injection, 의존성 주입)
- 외부에서 객체 간 의존성 주입
-
- 콘솔에 hello와 Bye 출력
- 입력한 문자 n번 반복하는 메소드 생성
public class HelloPrinter { //파일에도 저장하고싶고, 콘솔에도 출력하고 싶음 public void print(String message){ //원하는 메세지를 입력받아 콘솔에 출력 System.out.println(message); } public void repeatMessage(int n, String message){ //반복 for (int i = 0; i < n; i++) { print(message); } } public static void main(String[] args) { HelloPrinter hp = new HelloPrinter(); hp.repeatMessage(5, "hello"); } }
-
**// HelloPrinter 클래스에 Printer2 인터페이스 선언** public class HelloPrinter { **Printer2 printer; // 의존성 주입 -> 인터페이스 선언** //파일에도 저장하고싶고, 콘솔에도 출력하고 싶음 public void print(String message){ //원하는 메세지를 입력받아 콘솔에 출력 System.out.println(message); } public void repeatMessage(int n, String message){ //반복 for (int i = 0; i < n; i++) { print(message); } } public static void main(String[] args) { HelloPrinter hp = new HelloPrinter(); hp.repeatMessage(5, "hello"); } }
- Printer2에 빨간줄 뜰 시, Win: Alt + Enter / Mac: option+enter >create interface
- → Printer2 인터페이스 생성(단축기 이용)
- 확장만 할 때, 인터페이스 사용(변경 X)
- 아직 인터페이스 구현이 이루어지지 않았지만, 프로그램 작성 가능
- import java.io.IOException; public interface Printer2 { void print(String message) throws IOException; }
- DI 예시. 메시지 출력하기(3) DI 설계> ConsolePrinter 인터페이스 구현체
- ConsolePrinter: Printer2 인터페이스 작동하기 위한 클래스
**// HelloPrinter 클래스에 ConsolePrinter 구현체 선언 및 연결** public class HelloPrinter { **Printer2 printer = new ConsolePrinter(); // 구현체 클래스 선언(new)** //파일에도 저장하고싶고, 콘솔에도 출력하고 싶음 public void repeatMessage(int n, String message){ //반복 for (int i = 0; i < n; i++) { **printer.print(message); // 구현체 연결** } } public static void main(String[] args) { HelloPrinter hp = new HelloPrinter(); hp.repeatMessage(5, "hello"); } }
- System.out.println( ) → printer.print( ) 변경(구현체 연결)
- public class ConsolePrinter implements Printer2 { @Override public void print(String message) { System.out.println(message); } }
- DI 예시. 메시지 출력하기(4) DI 설계> 생성자로 DI 받기
- 생성자(Constructor) 단축키 이용(생성자 만들 이름 + Alt + Enter)
- DI 예시. 메시지 출력하기(5) DI 설계> FilePrinter 구현
import java.io.IOException; public class HelloPrinter { // 파일 저장, 콘솔 출력 // OCP(개방 폐쇄 원칙)에 의해 확장만 되는 곳 Printer2 printer; public HelloPrinter(Printer2 printer) { this.printer = printer; } // 예외처리 **throws IOException** **public void repeatMessage(int n, String message) throws IOException {** for(int i = 0; i < n; i++) { printer.print(message); } } // 예외처리 **throws IOException** **public static void main(String[] args) throws IOException {** //ConsolePrinter -> FilePrinter 변경 **** HelloPrinter hp = new HelloPrinter(**new FilePrinter()**); hp.repeatMessage(5, "Hello"); } }
- new FileWriter 빨간줄 → **단축키(Alt+Enter)**로 import throws IOException 생성
- 콘솔창에는 아무것도 출력 X (파일에 출력했기 때문)
5. OOP(객체 지향 프로그래밍)의 원칙 - SOLID
- SRP(Single Responsibility Principle): 단일 책임 원칙
- OCP**(Open Closed Principle): 개방 폐쇄 원칙**
- 변화에는 닫혀있고 확장에는 열려 있음→ 역할에 따라 분리해서 구현하는 것 (유연성, 유지보수성 증가)
- → 기존 코드를 변경하지 않고 기능을 추가하는 설계 원칙
- 자신의 확장은 열려 있고, 주변 변화는 닫혀 있어야 함
- LSP(Liskov Substitution Principle): 리스코프 치환 원칙
- ISP(Interface Segregation Principle): 인터페이스 분리 원칙
- DIP**(Dependency Inversion Principle): 의존 역전 원칙**
- 의존 관계를 맺을 때 변화하기 쉬운 것보단 변화하기 어려운 것에 의존→ 구현보단 역할에 의존 (추상화 그대로 둔 상태로 구체화를 그때마다 변경)
- → 변하기 쉬운 것: 구현 클래스 / 변하기 어려운 것: 인터페이스(추상화)
- 역전은 절차지향 프로그래밍은 구체적인 것에 의존했지만 **객체지향 프로그래밍(OOP)**에서는 이와 반대이기 때문
- 자신보다 변하기 쉬운 것에 의존 X