오버로딩(Overloading) | 오버라이딩(Overriding) |
- 한 클래스 내에 같은 메소드명으로 정의할 수 있는 방법 - 매개변수의 자료형의 개수, 순서가 다르게 작성되어 있어야 함 - 단, 매개변수의 이름, 접근제한자, 반환형은 메소드 오버로딩에 영향을 주지 않음 |
- 상속받고 있는 부모 클래스의 메소드를 자식 클래스에서 재정의 - 부모 메소드명과 메소드명이 동일해야 함 - 매개변수의 자료형, 개수, 순서, 반환형이 동일해야 함 - 부모 메소드의 접근 제한자와 같거나 공유 범위가 더 커야 함 |
상속 예시
public class Desktop extends Product {
// 필드부(부모 클래스의 필드는 따로 정의할 필요 없음)
private int price;
// 생성자부(기본 생성자, 매개변수 생성자)
public Desktop() {
}
public Desktop(String brand, String name, int price) {
super(brand, name);
this.price = price;
// 메소드부(자식 클래스 필드의 setter, getter 메소드와 toString() 오버라이딩)
public void setPrice(int price) {
this.price = price;
}
public int getPrice(){
return price;
}
@Override
public String toString(){
return super.toString() + ", price: " + price;
}
}
*다형성
부모클래스명 대변할이름 = new 자식클래스명(); // UpCasting
*추상클래스 == 객체 생성 불가능!
public abstract class 클래스명 {
추상클래스명 대변할이름 = new 추상클래스명(); // 불가!
추상클래스명 대변할이름 = new 자식클래스(); // 가능!
*인터페이스
public interface 인터페이스명 {
// 필드부
final 자료형 필드명 = 값; // 어차피 상수 필드만 선언 가능하기 때문에 public static은 묵시적으로 쓰지 않음
// 메소드부
void 메소드명(); // 어차피 추상 메소드만 선언 가능하기 때문에 public static은 묵시적으로 쓰지 않음
}
*예외 처리
-시스템 에러: 소스코드가 아닌 컴퓨터 등 기계상의 오작동으로 심각한 오류
- 컴파일 에러: 소스코드상의 문법 에러 => 소스코드의 수정으로 해결 가능
- 런타임 에러: 소스코드상으로는 문제 없으나 프로그램 실행 중 발생하는 에러
충분히 예상 가능한 에러( UncheckedException)로 예외를 후처리할 수도 있으나 if문인 조건문으로 선처리하는 것이 권장됨
-논리 에러: 소스코드도 문제 없고 실행 중에도 이상은 없으나 내가 짠 프로그램 코드상 반대로 작동
=> 이외의 사람이 아닌 외부 매체와의 입출력 과정 중 나타나는 IOException에 대한 예상 불가능한 에러(CheckedException)에 대한 예외처리를 반드시 해 줘야 함
1) public void 메소드명() throws 예외처리할예외이름 {
2) try {
실행할코드 (예외가 발생될 법한 구문이 포함되어 있음);
} catch (발생할예외클래스명 변수명) {
해당 예외가 발생할 경우 실행할 구문;
*입출력
기반 스트림
1) 바이트 스트림: 1byte 통로(입력: XXXInputStream, 출력: XXXOutputStream)
2) 문자스트림: 2byte 통로(입력: XXXReader, 출력: XXXWriter)
바이트 스트림 파일 출력 문자 스트림 사용 시 FileOutputStream 대신 FileWriter 메소드 |
바이트 스트림 파일 입력 문자 스트림 사용 시 FileInputStream 대신 FileReader 메소드 |
0. 변수 선언 및 null값 초기화 | 0. 변수 선언 및 null값 초기화 |
FileOutputStream fout = null; try { |
FileInputStream fin = null; try { |
1. FileOutputStream 객체 생성 ( == 연결 통로 만들기) | 1. FileInputStream 객체 생성 ( == 연결 통로 만들기) |
fout = new FileOutputStream("a_byte.txt"); - 존재하지 않는 파일명을 적을 시 파일 생성 후 통로 만들어짐 - 존재하는 파일명을 적을 시 바로 통로 만들어짐 |
fin = new FileInputStream("a_byte.txt"); - 존재하지 않는 파일명을 적을 시 오류 남!! - 반드시 존재하는 파일명을 적어야 함 |
2. 연결 통로로 데이터를 출력: write() 메소드 사용 | 2. 읽어들이기 (== 입력받기: read() 메소드) |
fout.write(97); fout.write('b'); } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); |
int value = 0; while((value = fin.read()) != -1) { System.out.println(value); } } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); |
3. 스트림을 다 이용했으면 자원 반납 (== 연결 끊기, finally 구문 이용) |
3. 스트림을 다 이용했으면 자원 반납 (== 연결 끊기, finally 구문 이용) |
} finally { try { fout.close(); } catch (IOException e) { e.printStackTrace(); } } |
} finally { try { fin.close(); } catch (IOException e) { e.printStackTrace(); } } |
보조 스트림 파일 출력 |
보조 스트림 파일 입력 |
0. 스트림 변수 선언 및 초기화를 코드 한줄로 줄여서 표현 | 0. 스트림 변수 선언 및 초기화를 코드 한줄로 줄여서 표현 |
BufferedWriter bw = null; try { |
BufferedReader br = null; try { |
1. 기반스트림 객체 생성(메인 연결 통로 만들기) 2. 보조스트림 객체 생성(매개변수로 기반스트림객체를 제시) => 1, 2단계 한 번에 표현 가능 |
1. 기반스트림 객체 생성(메인 연결 통로 만들기) 2. 보조스트림 객체 생성(매개변수로 기반스트림객체를 제시) => 1, 2단계 한 번에 표현 가능 |
bw = new BufferedWriter(new FileWriter((c_buffer.txt")); | br = new BufferedReader(new FileReader("c_buffer.txt")); |
3. 출력 | 3. 입력 : BufferedReader 클래스에서 제공하는 메소드로 읽어들이겠다 |
bw.write("안녕하세요."); } catch (IOExeption e) { e.printStackTrace(); } finally { |
String value = null; // ""로 초기화해도 무방함 while ((value = br.readLine()) != null) { System.out.println(value); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } finally { |
4. 자원 반납(반드시) => finally 블록 안에 작성 | 4. 연결 통로를 끊겠다 == 자원 반납(반드시) => finally 블럭에 작성 |
try { bw.close(); } catch (IOException e) { e.printStackTracer(); } } |
try { br.close(); } catch (IOException e) { e.printStackTrace(); } |
보조 스트림 객체 배열 출력 |
보조 스트림 객체 배열 입력 |
직렬화 선언 (NotSerializableException 발생 방지를 위함) |
|
public class 객체클래스명 implements Serializable { | |
try ~ with ~ recource 구문 버전 | try ~ with ~ recource 구문 버전 |
테스트용 객체 배열 | EOFExeption(End of file): 파일 내용이 끝났음에도 자꾸 출력하고자 할 때 발생하는 오류 => IOException의 자식임 |
Phone[] ph = new Phone[3]; try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("phones.txt"))) { // EOFException이 발생할 때까지만 반복을 돌리기 // 예측 불가능한 오류이기 때문에 정확한 조건을 세울 수 없음 while (true) { // 그래서 일단은 무한 반복 System.out.println(ois.readObject()); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (EOFException e) { // while문 무한반복 하다가 언젠가 EOFException이 발생하는 순간 이쪽으로 들어옴 System.out.println("파일을 다 읽었습니다."); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } // catch문은 한 구문이 출력된 후 try ~ catch문을 빠져나오기 때문에 후에는 프로그램 종료가 출력됨 System.out.println("프로그램 종료"); |
|
Phone[] arr = new Phone[3]; // [0] [1] [2] | |
테스트용 데이터 담기 | |
arr[0] = new Phone("아이폰", 1300000); arr[1] = new Phone("갤럭시", 1500000); arr[2] = new Phone("플립폰", 2000000); try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("phones.txt"))){ |
|
반복문 이용해서 내보내기 |
|
for(int i =0; i < arr.length; i++) { oos.writeObject(arr[i]); System.out.println(arr[i]); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } |
ServerProgram 클래스
package com.kh.chap02.tcp.run;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
// 서버용 프로그램
public class ServerProgram {
/*
* *TCP(Transmission Control Protocol)
* - 서버, 클라이언트간의 1:1 소켓 통신 방식
* - 데이터를 교환하기에 앞서서 서버, 클라이언트에 연결되어 있어야 함
* (항상 서버가 먼저 실행되어 클라이언트의 요청을 기다릴 것)
* - 신뢰성 있는 데이터 전달 가능
*
* *Socket
* - 프로세스 간의 통신을 담당
* - Socket 프로그래밍을 위한 기반스트림인 Input / OutputStream을 가지고 있음
* - 기반스트림만으로는 제한이 있으니 보조스트림을 추가시켜 성능을 향상시킬 것
*
* *ServerSocket
* - 포트번호와 연결(Bind: 엮다)되어 외부의 연결 요청을 기다리다 연결 요청이 들어오면 수락해 줄 용도
* 수락하는 순간 Socket 객체가 생성됨!
*/
// 0. 소켓 만들기
// 1. 스트림 열기
// 2. 볼 일 보기
// 3. 스트림 끊기
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 1) 이 프로그램에서 사용할 port번호 지정
// => 서버측에서 몇 번 포트로 통로를 열겠냐는 의미
int port = 3000;
ServerSocket server = null;
BufferedReader br = null;
PrintWriter pw = null;
// 2) ServerSocket 객체 생성 및 포트 결합(Bind)
// => 앞으로 클라이언트의 연결 요청을 받아 줄 용도로 사용됨
try {
server = new ServerSocket(port);
// 3) 클라이언트로부터 접속 요청이 들어올 때까지 대기 상태
System.out.println("클라이언트의 요청을 기다리고 있습니다..");
// 4) 연결 요청이 오면 요청을 수락 후 해당 클라이언트와 통신할 수 있는 Socket 객체가 생성됨
Socket socket = server.accept(); // socket == 클라이언트와 통신하기 위한 소켓 객체
// 상대방의 정보 얻기: getInetAddress() 메소드 사용
System.out.println(socket.getInetAddress().getHostAddress() + " 가 연결을 요청함..");
// ----- 이 시점까지 연결하겠다고 약속은 된 상태, 아직 스트림은 열리지 않음 -----
// 5) 클라이언트와 통신할 수 있는 입력용, 출력용 스트림을 생성 => socket 객체에서 제공해 줌
// 6) 보조스트림을 추가하여 성능 개선
// 입력용 스트림(클라이언트로부터 전달된 값을 한 줄 단위로 읽어들일 수 있게끔)
// socket.getInputStream()
// + 1byte 와 2byte 사이의 호환이 가능한 보조 스트림(InputStreamReader)
// BufferedReader
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 출력용 스트림 (클라이언트에게 값을 전달할 용도)
// socket.getOutputStream()
// + 1byte 와 2byte 사이의 호환이 가능한 보조 스트림(OutputStreamWriter)
// PrintWriter
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
// => 기반스트림에 성능 향상을 위해 보조스트림을 각각 2개씩 붙여 줌
// 7) 클라이언트와 스트림을 통해 읽고 쓰기
// => 통신을 주거니 받거니 반복을 위한 반복문
while(true) {
// 클라이언트로부터 전달된 메시지가 있을 경우 서버측에서 읽어들이기(input)
String message = br.readLine();
System.out.println("클라이언트로부터 전달받은 메시지: " + message);
// 반대로 클라이언트에게 데이터를 전달하기(output)
System.out.print("클라이언트에게 보낼 내용: ");
String sendMessage = sc.nextLine();
pw.println(sendMessage); // 클라이언트에게 1줄 단위로 출력
pw.flush(); // 현재 스트림에 남아 있는 잔여 데이터를 강제로 내보내는 역할을 해 주는 메소드
// 여태까지는 입출력 후 자원 반납 해 주었기 때문에 flush 메소드를 쓰지 않았음
// 자원 반납 시 자동으로 flush()가 진행되기 때문!
// 지금은 주고받고 해야 하니까 스트림 막힐까 봐 flush() 메소드를 사용함
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8) 통신 종료를 위한 자원 반납(생성된 순서의 역순으로)
try {
pw.close();
br.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ClientProgram 클래스
package com.kh.chap02.tcp.run;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class ClientProgram {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Socket socket = null;
BufferedReader br = null;
PrintWriter pw = null;
// 1) 접속하고자 하는 서버의 IP주소, port번호 지정
// 요청하고자 하는 서버의 IP주소: 192.168.40.30 또는 127.0.0.1 (루프백 IP) 또는 localhost
String serverIP = "192.168.40.124";
int port = 3000;
// 2) 서버에 연결 요청을 보내는 구문
// => 요청하고자 하는 서버의 IP주소와 port번호를 제시하면서 Socket 객체 생성
try {
socket = new Socket(serverIP, port);
// => Socket 객체 생성 시 연결이 제대로 되었다면 객체가 잘 생성될 것
// 연결이 실패할 경우에는 null 값이 Socket 객체에 담김
// NullPointerException은 충분히 예상 가능한 오류이기 때문에 조건문으로 선처리
if(socket != null) { // 연결이 잘되었을 경우 => 통신 진행
System.out.println("서버와 연결 성공!");
// 3) 서버와 통신할 수 있는 입력용 스트림, 출력용 스트림 열기
// 4) 보조 스트림을 추가하여 성능 개선
// 입력용 스트림
// socket.getInputStream() + InputStreamReader + BufferedReader
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 출력용 스트림
// socket.getOutputStream() + OutputStreamWriter + PrintWriter
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
// 반복문
while(true) {
// 5) 스트림을 통해 읽고 쓰기
// 서버에게 데이터를 전달 (output)
System.out.print("서버에게 보낼 내용: ");
String sendMessage = sc.nextLine();
pw.println(sendMessage);
pw.flush();
// 서버로부터 전달된 메시지를 읽어들이기 (input)
String message = br.readLine();
System.out.println("서버로부터 전달받은 메시지: " + message);
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
pw.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}