소켓은 TCP를 위한 구조이다. TCP를 사용하여 응용 프로그램끼리 통신을 하기 위해서는 먼저 연결을 하여야 하는데 연결을 하기 위해서느 연결 끝점(End point)가 있어야 한다. 이 연결끝점이 바로 Socket이다.
Socket 클래스
생성자
설명
Socket (String host, int port)
호스트 이름이 host이고 포트 번호가 port인 새로운 소켓을 생성한다
Socket (InetAddress address, int port)
InetAddress에 기술된 주소로 새로운 소켓을 생성한다
메소드
설명
InputStream getInputStream()
소켓이 사용하는 입력 스트림을 반환한다
OutputStream getOutputStream()
소켓이 사용하는 출력 스트림을 반환한다
Inetaddress getInetAddress()
소켓이 연결되어 있는 인터넷 주소를 반환한다
public int getLocalPort()
소켓이 연결되어 있는 포트 번호를 반환한다
public int getPort()
원격 컴퓨터의 포트번호를 반환한다
pbulic InetAddress getLocalAddress()
소케싱 연결되어 있는 인터넷 주소를 반환한다
ServerSocket 클래스
생성자
설명
public ServerSocket(int port) throws IOException
포트번호 Port에 대해 ServerSocket의 새로운 인스턴스를 만든다. 포트번호 0은 비어있는 포트번호를 사용한다는 의미이다. queue는 서버가 받을 수 있는 입력 연결의 개수를 의미한다. (디폴트는 50연결이다) address는 컴퓨터의 인터넷 주소를 나타낸다.
public ServerSocket(int port, int queue) throws IOException
public ServerSocket(int port, int queue, InetAddress address ) throws IOException
메소드
설명
public socket accept()
접속요청을 받는다
public void close()
ServerSocket을 닫는다
public Inetaddress getInetAddress()
소켓이 연결되어 있는 인터넷 주소를 반환한다
public int getSoTimeout()
소켓에 대한 타임아웃값을 밀리 초로 반환하거나 설정한다.
소켓클래스를 이용한 채팅방
서버)
package network;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
public class TCPIPChatMultichatServer {
// 접속 사용자 정보를 저장 : 메세지를 전체에게 보내기 위해서
// 저장정보 : name, OutputStream
// Map<String, Object>로 저장
HashMap<String, Object> clients;
public TCPIPChatMultichatServer() {
clients = new HashMap<String, Object>();
Collections.synchronizedMap(clients); // 스레드 동기화에 안전한 상태로 해주는메서드
}
public void start() throws IOException {
// Server Socket 생성
// 사용자의 요청이 있으면 Socket 생성해서 연결 > Clients 사용자 정보를 저장
// 연결 > Clients 사용자 정보를 저장 스레드로 처리
ServerSocket serverSocket = new ServerSocket(7777);
System.out.println("Chatting Server Start!");
Socket socket = null;
while(true) {
// 요청이 들어올 때까지 대기 하다가 요청이 들어오면 새로운 소켓을 생성해서 반환
socket = serverSocket.accept();
// map에 정보저장, 접속한 사람들에게 메세지를 전송
System.out.println("["+socket.getInetAddress()+socket.getPort()+"] 사용자 접속");
ServerReceiver receiver = new ServerReceiver(socket);
receiver.start();
}
}
void SendToAll(String msg) {
// 일괄처리 - Map은 순서가 없다 - Map이 가지고 있는 key를 Set으로 만든다.
Set<String> keys = clients.keySet();
Iterator<String> itr = keys.iterator();
while (itr.hasNext()) {
DataOutputStream out = (DataOutputStream) clients.get(itr.next()); // 키값가져오기
try {
out.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 내부클래스 : 데이터 받아서 저장, 메시지 전체 발송
private class ServerReceiver extends Thread {
// Socket, InputStream, OutputStream
Socket socket;
DataOutputStream out;
DataInputStream in;
public ServerReceiver(Socket socket) {
this.socket = socket;
try {
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
String name=null; // 접속한 사용자 이름
try {
name= in.readUTF(); // 이름을 스트림을 통해 받는다
clients.put(name, out); // map에 사용자 정보 저장
SendToAll(">>>>>>>>>> " +name+"님이 접속하셨습니다.");// 내부클래스에서는 외부 클래스의 멤버를 참조할 수 있다.
while(in !=null ) {
SendToAll(in.readUTF());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
clients.remove(name);
System.out.println(name+"님이 나가셨습니다.");
}
}
}
public static void main(String[] args) throws IOException {
new TCPIPChatMultichatServer().start();
}
}
클라이언트)
package network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import thread.threadMain;
public class TCPIPMulitiChatClient {
public static void main(String[] args) {
String serverIp = "192.168.0.32"; // localhost > 호스트자신의 주소
//Socket연결
try {
Socket socket = new Socket(serverIp, 7777);
// 메세지를 받는 스레드
Thread receiver = new ClientReceiver(socket);
// 메세지를 보내는 스레드
Thread sender = new ClientSenderThread(socket, "포그바");
sender.start();
receiver.start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientSenderThread extends Thread{
// 보내기 스레드는 Socket, OutputStream, name도 필요하다.
Socket socket;
DataOutputStream out;
String name;
public ClientSenderThread(Socket s, String name) {
this.socket = s;
try {
out = new DataOutputStream(s.getOutputStream());
this.name = name;
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
Scanner sc = new Scanner(System.in);
try {
// 접속하면 이름을 서버에 전송
if(out != null) {
out.writeUTF(name);
}
while(out!=null) {
out.writeUTF("["+name+"]"+sc.nextLine());
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientReceiver extends Thread {
// 메세지를 받아서 콘솔에 출력
// Socket, InputStream, 필요
Socket socket;
DataInputStream in;
public ClientReceiver(Socket socket) {
this.socket = socket;
try {
in = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (in != null){
try {
System.out.println(in.readUTF());
} catch (IOException e) {
}
}
}
}
– 자바에서 스레드를 사용하는 이유는 비동기적 작동 방식(독립스레드)에 대한 개념이 없음 • 프로그래밍 방식이 자바에서는 사용이 가능하지 않음 • 비동기적 작동 방식 구현 : ‘스레딩 기술의 사용 방법’ 숙지 – 자바가 실행되는 기반인 자바가상머신(Java virtual machine, 이하 JVM) 자체가 하나의 프로세스임 • 언어적 차원 : 스레드의 동기화 지원 가능 • 다중 스레드 프로그램을 쉽고 명료하게 만들 수 있음
다중 쓰레드
– 시분할 기법(Time-Shared) • 멀티태스킹 및 멀티스레드를 가능하게 하는 기법 • 아주 짧은 시간 간격을 두고 여러 개의 프로그램을 전환하면서 실행 • 빠른 속도 때문에 두 개 이상의 프로그램이 동시에 실행되는 것처럼 느껴짐 • 프로그램의 실행을 전환하는 것은 OS가 담당함
– 다중 스레드의 이점 • 자원을 효율적으로 사용할 수 있음 • 사용자에 대한 응답성이 향상됨 • 작업이 분리되어 코드가 간결함
– 다중 스레드의 단점 • 동기화에 주의해야 함 • 교착상태가 발생하지 않도록 주의해야 함 • 각 스레드가 효율적으로 고르게 실행될 수 있게 해야 함
–다중 스레드의 사용 예
• 스마트폰 게임 – 메인스레드 : 게임을 하기 위한 UI부분을 그려줌 – 그래픽 부분 담당 코드 : 순차적으로 실행, UI를 그리는 서버통신을 담당하는 소켓부분을 방치하는 수 밖에 없음. – 통신을 담당하는 스레드를 따로 하나를 두어 일정한 시간단위로 체크할 수 있도록 해야 함.
• 영상통신
– 영상을 받아 화면에 출력해 주는 코드와 영상을 생성하여 보내주는 코드를 만드는 경우
> 적어도 2개의 작업이 동시에 일어난다는 것을 알 수 있음 – 두 가지 이상의 일을 구현하기 위해 다중 스레드를 사용함
< 스레드 사용 방법 >
예제 extends Thread
package thread;
// 쓰레드 클래스의 정의는 thread클래스를 상속해서 정의
public class showThread extends Thread {
String threadName;
public showThread(String name) {
this.threadName=name;
}
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println("안녕하세요" + threadName + "입니다.");
try {
// object클래스의 sleep() 메서드 : 현재 스레드를 1/1000초 간격으로 멈춤
sleep(100);// 100/1000초 > 1/10초
} catch (InterruptedException e) {
e.printStackTrace();
}
} System.out.println(threadName + " 종료 ★");
}
}
package thread;
public class threadMain {
public static void main(String[] args) {
// Thread 생성
showThread st1 =new showThread("1번 스레드");
showThread st2 =new showThread("2번 스레드");
st1.start();
st2.start();
for(int i=0; i<100; i++) {
System.out.println("메인스레드");
}
System.out.println("★끝★");
}
}
< 출력 >
main스레드에 Thread.sleep(100)을 똑같이 넣어주면 셋이 번갈아서 실행됨
예제 implements Runnable
package thread;
// 쓰레드 클래스 정의할 때 상속이 필요한 경우 Runnable 인터페이스를 구현해서 스레드를 생성할 수 있다.
public class ShowRunnable implements Runnable {
public void run() {
for(int i=0; i<100; i++) {
System.out.println("러너블스레드");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("★끝★");
}
}
package thread;
public class threadMain2 {
public static void main(String[] args) {
// Runnable 인터페이스를 이용한 스레드
Runnable target = new ShowRunnable();
Thread st3 = new Thread(target);
st3.start();
for(int i=0; i<100; i++) {
System.out.println("메인스레드");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("★끝★");
}
}
<출력 / 100개 반복>
스레드 우선순위 설정
// 우선순위 설정 1-10 : 기본5
st1.setPriority(Thread.MAX_PRIORITY);
st2.setPriority(Thread.MIN_PRIORITY);
<출력> ~ 속도가 빨라서 별차이가 없지만 방법은 ARADUZA ~
t1.join(); // t1 스레드가 종료될때까지 다른 스레드는 멈춘상태
예제)
package thread;
public class Sum {
int num;
Sum(){
num=0;
}
void addNum(int n) {
num+=n;
}
int getNum() {
return num;
}
public void run() {
// TODO Auto-generated method stub
}
}
package thread;
public class Adderthread extends Sum implements Runnable {
int start;
int end;
Adderthread(int n1, int n2){
start=n1;
end=n2;
}
@Override
public void run() {
for(int i=start; i<=end;i++) {
addNum(i);
}
}
}
package thread;
public class AdderThreadMain {
public static void main(String[] args) {
Adderthread at1 = new Adderthread(1, 500000);
Adderthread at2 = new Adderthread(500000, 1000000);
// 쓰래드 생성
Thread t1 = new Thread(at1);
Thread t2 = new Thread(at2);
t1.start();
t2.start();
try {
t1.join(); // t1 쓰래드가 종료될 때 까지 다른 쓰래드는 멈춤상태
t2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("1~100 합은 : " + (at1.getNum()+at2.getNum()) );
}
}
<출력>
1~100 합은 : 1784793664
멀티스레드 예제
package thread;
import javax.swing.JOptionPane;
public class ThreadTestMain1 {
// 다른 스레드에서도 참조가 가능한 클래스 변수
public static boolean check = false; // 10초 지나면 꺼지게하려고 만듦 (안에서사용하려고)
public static void main(String[] args) {
// String age= JOptionPane.showInputDialog("나이를 입력해주세요");
// int ageNumber=Integer.parseInt(age);
// System.out.println("저의 나이는 " + age + "세 입니다.");
// for(int i=10; i>0; i--) {
// System.out.println(i);
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// } // 나이쓰는 창뜨고 > 숫자셈
InputAgeThread iat= new InputAgeThread();
CountDownThread cdt=new CountDownThread();
iat.start();
cdt.start();
}
}
class InputAgeThread extends Thread{
@Override
public void run() {
System.out.println("10초 안에 입력하세요.");
String age= JOptionPane.showInputDialog("나이를 입력해주세요");
System.out.println("저의 나이는 " + age + "세 입니다.");
ThreadTestMain1.check=true;
}
}
class CountDownThread extends Thread {
@Override
public void run() {
for(int i=10; i>0; i--) {
if(ThreadTestMain1.check) {
break; // 입력되면 숫자세기 STOP
}
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.exit(0);
}
}
'
스레드 동기화
Note that this implementation is not synchronized API 문서에는 해당 클래스의 인스턴스가 둘 이상의 쓰레드가 동시에 접근을 해도 문제가 발생하지 않는지를 명시하고 있다. 따라서 쓰레드 기반의 프로그래밍을 한다면, 특정 클래스의 사용에 앞서 쓰레드에 안전한지를 확인 해야 한다.
ex) String buffer / builder
쓰레드의 동기화 - synchronized - 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락(lock)을 걸어서 데이터의 일관성을 유지하는 것.
아래 예제)
Sum 클래스의 Void addNum에 synchronized가 없으면 동기화가 되지 않아서 매번 값이 다르다..
그러나 엄청난 성능의 감소를 동반한다.
package thread;
public class Sum {
int num;
Sum(){
num=0;
}
// 동기화~!
synchronized void addNum(int n) {
num+=n;
}
int getNum() {
return num;
}
public void run() {
// TODO Auto-generated method stub
}
}
package thread;
public class AdderThread1 extends Thread {
Sum sum;
int start;
int end;
public AdderThread1(Sum sum, int start, int end) {
this.sum = sum;
this.start = start;
this.end = end;
}
@Override
public void run() {
for (int i = start; i <= end; i++) {
sum.addNum(i);
}
}
}
package thread;
public class AdderThreadhMain2 {
public static void main(String[] args) {
Sum sum = new Sum();
AdderThread1 t1 = new AdderThread1(sum, 1, 5000);
AdderThread1 t2 = new AdderThread1(sum, 5001, 10000);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1-100합 "+ sum.getNum());
}
}
<출력>
1-100합 50005000
동기화블럭
public class Calculator {
int opplCnt=0; // 몇번의 연산을 했나요?
int opmiCnt=0;
public int add(int n1, int n2) {
synchronized (this) { // key
opplCnt++;
}
// opCnt++;
return n1+n2;
}
public int min(int n1, int n2) {
synchronized (obj) { // 참조값이 달라서 다르다.
opmiCnt++;
}
// opCnt++;
return n1-n2;
}
Object obj = new Object(); // 새로운 동기화 키 생성
}
만약 opplCnt / opmiCnt가 아니라 똑같이 opCnt++;를 사용하면 똑같이 this(둘다같은거면OK)를 넣으면 된다
num1도 쓰고 num2도 쓰기 위해(효율성) 두개의 Key로 분리해 주는 것이라고 생각하면 되는듯하다.
package io;
import java.io.File;
public class FileMove {
public static void main(String[] args) {
// File 클래스는 경로를 저장한다.
// 파일의 경로, 폴더의 경로
// 파일 또는 폴더의 속성을 변경하거나 삭제, 생성이 가능하다.
// 현재 존재하는 파일의 경로를 생성
File myFile = new File("C:"+File.separator+"myJava\\my.bin");
// myFile.delete();
if(!myFile.exists()) {
System.out.println("원본 파일이 준비되어 있지 않습니다.");
System.out.println("프로그램을 종료합니다.");
return;
}
System.out.println("파일 존재");
File reDir = new File("c:\\yourJava");
reDir.mkdir(); // 해당 경로에 폴더가 있으면 그대로 유지, 없으면 생성
System.out.println("폴더 생성 성공");
//파일을 이동할 새로운 경로 생성
File refile = new File(reDir, "Your.bin");
System.out.println("파일 존재 유무 : " + refile.exists());
//파일 이동
myFile.renameTo(refile);
if(refile.exists()) {
System.out.println("파일 이동 성공");
}
}
}
package io;
import java.io.Serializable;
public class Circle implements Serializable{
//implements Serializable : 직렬화의 대상이다. 인스턴스 저장이 가능하다.
// 직렬화를 하기 위해서는 반드시 해줘야함..
int x;
int y;
double rad;
public Circle(int x, int y, double r) {
this.x=x;
this.y=y;
this.rad=r;
}
public void showCircleInfo() {
System.out.printf("[%d,%d]", x,y);
System.out.println("rad : " + rad);
}
}
package io;
import java.io.Serializable;
public class Circle2 implements Serializable {
// implements Serializable : 직렬화의 대상이다. 인스턴스 저장이 가능하다.
// 직렬화를 하기 위해서는 반드시 해줘야함..
// transient Point p; 직렬화대상에서 제외
Point p;
double rad;
public Circle2(int x, int y, double r) {
p = new Point(x, y);
this.rad = r;
}
public void showCircleInfo() {
System.out.println("rad : " + rad);
}
}
package io;
import java.io.Serializable;
public class Point implements Serializable {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class ObjectSerializable {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// 인스턴스 저장을 위한 스트림 생성
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Object.ser"));
// 인스턴스를 저장
out.writeObject(new Circle(1,1,2.4));
out.writeObject(new Circle(2,2,4.8));
out.writeObject(new String("String class implements Serializable"));
// String 클래스도 직렬화 가능
out.writeObject(new Circle2(3,3,6.9));
out.close(); // 저장~!
// 인스턴스 복원
ObjectInputStream in = new ObjectInputStream(new FileInputStream("Object.ser"));
// Circle c1 = (Circle) in.readObject(); // 형변환
Object o1 = in.readObject();
Circle c1 =null;
if(o1 instanceof Circle) {
c1 = (Circle)o1;
}
Circle c2 = (Circle) in.readObject();
String str= (String) in.readObject();
Object o2 = in.readObject();
Circle2 c3=null;
if(o2 instanceof Circle2) {
c3= (Circle2)o2;
}
// 복원된 인스턴스 출력
c1.showCircleInfo();
c2.showCircleInfo();
System.out.println(str);
c3.showCircleInfo();
}
}
예제2)
package io;
import java.beans.Transient;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class PersonInfo implements Serializable {
String name;
transient String secretInfo;
int age;
transient int secretNum;
public PersonInfo(String name, String secretInfo, int age, int secretNum) {
this.name = name;
this.secretInfo = secretInfo;
this.age = age;
this.secretNum = secretNum;
}
public void showInfo() {
System.out.println("name : " + name);
System.out.println("secretInfo : " + secretInfo);
System.out.println("age : " + age);
System.out.println("secretNum : " + secretNum);
}
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// 인스턴스 저장 스트림 생성
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("PersonInfo.ser"));
PersonInfo info = new PersonInfo("김태형", "뽀선이애인", 24, 1);
info.showInfo();
// 인스턴스 저장
out.writeObject(info);
out.close();
// 인스턴스 복원을 위한 스트림 생성
ObjectInputStream in = new ObjectInputStream(new FileInputStream("PersonInfo.ser"));
// 복원
PersonInfo reInfo = (PersonInfo) in.readObject();
// 복원된 인스턴스의 정보 출력
reInfo.showInfo();
}
}
•유니코드로 된 문자를 입·출력 하는 스트림 •2바이트를 입·출력 - 세계 모든 언어로 구성된 파일을 입·출력 하기에 적합 •이미지, 동영상과 같은 바이너리 데이터는 입·출력 할 수 없음 •문자 데이터만 입·출력 가능
Reader의 대표적인 메소드
public int read() throws IOException public abstract int read(char[] cbuf, int off, int len) throws IOException
Writer의 대표적인 메소드
public int write(int c) throws IOException
public abstract void write(char[] cbuf, int off, int len) throws IOException
BufferedWriter
package io;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class StringWriter {
public static void main(String[] args) throws IOException {
// 필터스트림 : 기본 스트림 writer가 필요하다.
Writer writer = new FileWriter("String.txt"); // 기본스트림
BufferedWriter out = new BufferedWriter(writer); // 필터스트림
out.write("Cos ah ah I’m in the stars tonight");
out.newLine();
out.write("So watch me bring the fire and set the night alight");
out.newLine();
out.write("Shining through the city with a little funk and soul");
out.newLine();
out.write("So I’mma light it up like dynamite, woah");
out.newLine();
out.newLine();
out.write("BTS - Dynamite");
// 스트림닫기
out.close();
}
}
BufferedReader
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.BufferedReader;
public class StringReader {
public static void main(String[] args) throws IOException{
// 문자 기반 기본 스트림 인스턴스 생성
Reader reader = new FileReader("String.txt");
// 필터스트림
BufferedReader in = new BufferedReader(reader);
// 한행의 문자열을 담을 변수
String str=null;
while(true) {
//한줄씩 읽어 문자열을 반환, 없으면 null
str=in.readLine();
if(str==null) {
break;
}
System.out.println(str);
}
}
}
문자 필터 스트림 PrintWriter
package io;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWriterStream {
public static void main(String[] args) throws IOException {
PrintWriter out = new PrintWriter(new FileWriter("Print.txt"));
out.printf("안녕하세요 저는 %d살 %s입니다.",12,"철수");
out.println();
out.print("저는 자바를 좋아합니다");
out.println("IO 재미있네요.");
out.close();
System.out.println("파일작성 완료");
}
}
스트림(stream)이란? - 데이터를 운반(입출력)하는데 사용되는 연결통로 - 연속적인 데이터의 흐름을 물(stream)에 비유해서 붙여진 이름 - 하나의 스트림으로 입출력을 동시에 수행할 수 없다.(단방향 통신) - 입출력을 동시에 수행하려면, 2개의 스트림이 필요하다.
•자바의 스트림 –입·출력 장치와 프로그램을 연결하며, 이들 사이의 데이터 흐름을 처리하는 소프트웨어 모듈 –데이터를 처리하기 위한 공통된 방법을 제공함 –입력 스트림 : 입력 장치로부터 자바 프로그램으로 전달되는 데이터의 흐름 혹은 데이터 전송 소프트웨어 모듈 –출력 스트림 : 자바 프로그램에서 출력 장치로 보내는 데이터의 흐름 또는 데이터 전송 소프트웨어 모듈
자바 입·출력 스트림의 특징 –스트림은 FIFO 구조 • FIFO : 먼저 들어간 것이 먼저 나오는 형태 → 데이터의 순서가 바뀌지 않음 –스트림은 단방향 • 읽기, 쓰기가 동시에 되지 않음 • 읽는 스트림과 쓰는 스트림을 하나씩 열어 사용해야 함 –스트림은 지연될 수 있음 • 스트림은 넣어진 데이터가 처리되기 전까지는 스트림에 사용되는 스레드가 지연상태에 빠짐 • 네트워크 내에서는 데이터가 모두 전송되기 전까지 네트워크 스레드는 지연상태를 유지함
•InputStream 클래스의 대표적인 두 메소드
public abstract int read() throws IOException
public void close() throws IOException
•OutputStream 클래스의 대표적인 메소드
public abstract void write(int b) throws IOException
public void close() throws IOException
자바 입·출력 스트림의 종류
–바이트 스트림
•문자 단위/바이트 단위 •바이트 스트림은 1바이트를 입·출력 할 수 있는 스트림 •자바에서 입·출력 스트림을 통해 흘러가는 데이터의 기본 단위 •일반적으로 바이트로 구성된 파일을 처리하기에 적합
•바이트 스트림 클래스 –JAVA . IO 패키지 포함 –InputStream/OutputStream
>추상 클래스로써 바이트 입·출력 스트림을 다루는 모든 클래스의 슈퍼 클래스 –FileInputStream/FileOutputStream
>파일로 부터 바이트 단위로 읽거나 저장하는 클래스
>바이너리 파일을 입·출력을 할 때 사용
package io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ByteFileCopy {
public static void main(String[] args) throws IOException {
// 읽어올 대상 파일의 InputStream 인스턴스를 생성한다.
InputStream in = new FileInputStream("org.pdf");
// 출력 대상 파일의 OutpurStream 인스턴스 생성
OutputStream out = new FileOutputStream("org_copy.pdf");
// 필터스트림 인스턴스 생성
BufferedInputStream bin = new BufferedInputStream(in, 1024*3);
BufferedOutputStream bout = new BufferedOutputStream(out, 1024*3);
int copyByte=0; // 복사한 사이즈
int bData=0; // 원본에서 복사한 byte 사이즈의 데이터
// byte[] buf = new byte[1024]; // 1kb 버퍼생성
// int readLength = 0; // 얼마만큼 읽어왔는지.
System.out.println("복사를 시작합니다.");
while(true) {
bData = in.read(); // 더이상 가져올 데이터가 없으면 -1을 반환
// readLength = in.read(buf);
if(bData==-1) {
// if(readLength==-1) {
break;
}
out.write(bData); // 출력 : 파일에 바이터리 코드를 쓴다.
// out.write(buf,0,readLength);
// copyByte+= readLength;
copyByte++;
}
in.close(); // 스트림 인스턴스 소멸
out.close();
System.out.println("복사가 완료되었습니다.");
System.out.println("복사된 사이즈 : " + copyByte + "byte");
}
}
List<E>를 구현하는 클래스들과 달리 Set<E>를 구현하는 클래스들은 데이터의 중복저장을 허용하지 않는다.
(단, 동일 데이터에 대한 기준은 프로그래머가 정의)
package Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetTest {
public static void main(String[] args) {
// HashSet 인스턴스 생성 : 인스턴스를 저장하기 위한 저장 공간 생성
// set : 집합, 저장순서 유지하지않는다.
Set<String> set = new HashSet<String>();
set.add("First");
set.add("Second");
set.add("Third");
set.add("First"); // 중복이라 저장되지 않는다.
System.out.println("set 요소의 개수 : "+ set.size());
Iterator<String> itr=set.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
<출력>
set 요소의 개수 : 3 Second Third First
// Set<e>는 저장하는 순서를 가지지 않기 때문에 // 일괄처리를 위해서는 Iterator<E> 인스턴스를 이용해서 처리한다. // Collection<E>에는 Iterater() 메서드를 가지고 있고 Iterator<E>를 반환한다.
HashSet<E>클래스의 동등비교
String에는 Hashcode() 가 있어서 지 알아서 중복 분류해주지만 나머지는 해시코드를 만들어줘야한다
package Collection;
public class SimpleNumber {
int num;
public SimpleNumber(int num) {
this.num = num;
}
@Override
public String toString() {
return String.valueOf(num);
}
}
package Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SimpleNumberMain {
public static void main(String[] args) {
// set 인스턴스 생성
Set<SimpleNumber> set=new HashSet<SimpleNumber>();
set.add(new SimpleNumber(10));
set.add(new SimpleNumber(20));
set.add(new SimpleNumber(10));
System.out.println("Set 요소의 개수 : "+set.size());
Iterator<SimpleNumber> itr = set.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
<출력>
Set 요소의 개수 : 3 10 10 20
<SimpleNumber 수정>
쉬프트 알트 o > gernerate hashcode() and equals()
package Collection;
public class SimpleNumber {
int num;
// Set 동등 비교를 위한 Hashcode(), equals() 오버라이딩
@Override
public int hashCode() {
// final int prime = 31;
// int result = 1;
// result = prime * result + num;
return num%3; // 0 or 1 or 2
}
@Override
public boolean equals(Object obj) {
if (this == obj) // 참조 주소값 == 전달받은 참조 주소값
return true;
if (obj == null) // 들어오는 객체가 null ex) set.add(null)인 경우
return false;
if (getClass() != obj.getClass()) // 클래스 이름비교, 같은 타입인가
return false;
SimpleNumber other = (SimpleNumber) obj;
if (num != other.num)
return false;
return true;
}
public SimpleNumber(int num) {
this.num = num;
}
@Override
public String toString() {
return String.valueOf(num);
}
}
기본 자료형 정보를 이용해서 제네릭 인스턴스 생성 불가능! 따라서 Wraper클래스를 기반으로 컬렉션 인스턴스를 생성한다. (Int 말고 ⭕Integer⭕)
- ArrayList <어떤Type> 이름
형식
기능
이름.get(index)
index에 저장된 저장값 불러오기
이름.size
저장된 요소의 갯수
이름.remove(index)
index에 저장된 것 삭제
이름.add(추가할값)
추가
이름.add(index, 추가할값)
index에 추가할 값을 추가 (그 뒤는 index 하나씩 알아서 뒤로 밀림)
이름.containts(어떤값)
어떤값이 추가하는지 true/false 반환
이름.set(index,수정값)
index 자리에 있는 값을 수정값으로 수정
이름.indexOf(특정값)
특정값이 잇는 index를 반환
이름.isEmpty();
값을 가지고 있으면 true 아니면 false
이름clear();
모든 원소 삭제
package Collection;
import java.util.ArrayList;
public class ArrayListTest {
public static void main(String[] args) {
// 인스턴스 저장을 목적으로 하는 클래스
// List<E> : 저장 순서 유지 (반복문이용 용이), 중복저장 허용
//ArrayList<Integer> list = new ArrayList<Integer>();
//List<Integer> list = new ArrayList();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println("list의 첫번째 요소의 값 : " + list.get(0));
System.out.println("list 요소의 갯수 : " + list.size());
System.out.println("list 출력");
for (Integer i : list) {
System.out.println(i);
}
// 삭제
list.remove(2);
System.out.println("삭제 후 list 출력");
for (Integer i : list) {
System.out.println(i);
}
}
}
<출력>
list의 첫번째 요소의 값 : 1 list 요소의 갯수 : 4 list 출력 1 2 3 4 삭제 후 list 출력 1 2 4
자바에서 컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미합니다, 즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것입니다. 이러한 컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현됩니다.
컬렉션 프레임워크 주요 인터페이스
컬렉션 프레임워크에서는 데이터를 저장하는 자료 구조에 따라 다음과 같은 핵심이 되는 주요 인터페이스를 정의하고 있습니다.
1. List 인터페이스
2. Set 인터페이스
3. Map 인터페이스
이 중에서 List와 Set 인터페이스는 모두 Collection 인터페이스를 상속받지만, 구조상의 차이로 인해 Map 인터페이스는 별도로 정의됩니다. 따라서 List 인터페이스와 Set 인터페이스의 공통된 부분을 Collection 인터페이스에서 정의하고 있습니다. 자바 컬렉션 프레임워크의 주요 인터페이스에 대한 더 자세한 사항은 다음 페이지를 참고하면 됩니다.
자바에서 컬렉션 프레임워크를 구성하고 있는 인터페이스 간의 상속 관계는 다음 그림과 같습니다.
위의 그림에서 <E>나 <K, V>라는 것은 컬렉션 프레임워크를 구성하는 모든 클래스가 제네릭으로 표현되어 있음을 알려줍니다.
주요 인터페이스의 간략한 특징
자바에서 컬렉션 프레임워크를 구성하고 있는 주요 인터페이스의 간략한 특징은 다음과 같습니다.
List<E>
순서가 있는 데이터의 집합으로, 데이터의 중복을 허용함.
Vector, ArrayList, LinkedList, Stack, Queue
Set<E>
순서가 없는 데이터의 집합으로, 데이터의 중복을 허용하지 않음.
HashSet, TreeSet
Map<K, V>
키와 값의 한 쌍으로 이루어지는 데이터의 집합으로, 순서가 없음.
이때 키는 중복을 허용하지 않지만, 값은 중복될 수 있음.
HashMap, TreeMap, Hashtable, Properties
컬렉션 클래스(collection class)
컬렉션 프레임워크에 속하는 인터페이스를 구현한 클래스를 컬렉션 클래스(collection class)라고 합니다. 컬렉션 프레임워크의 모든 컬렉션 클래스는 List와 Set, Map 인터페이스 중 하나의 인터페이스를 구현하고 있습니다. 또한, 클래스 이름에도 구현한 인터페이스의 이름이 포함되므로 바로 구분할 수 있습니다. Vector나 Hashtable과 같은 컬렉션 클래스는 예전부터 사용해 왔으므로, 기존 코드와의 호환을 위해 아직도 남아 있습니다. 하지만 기존에 사용하던 컬렉션 클래스를 사용하는 것보다는 새로 추가된 ArrayList나 HashMap 클래스를 사용하는 것이 성능 면에서도 더 나은 결과를 얻을 수 있습니다. 다음 예제는 ArrayList 클래스를 이용하여 리스트를 생성하고 조작하는 예제입니다.
예제
import java.util.*;
public class Collection01 {
public static void main(String[] args) {
// 리스트 생성
ArrayList<String> arrList = new ArrayList<String>();
// 리스트에 요소의 저장
arrList.add("넷");
arrList.add("둘");
arrList.add("셋");
arrList.add("하나");
// 리스트 요소의 출력
for(int i = 0; i < arrList.size(); i++) {
System.out.println(arrList.get(i));
}
}
}
실행 결과
넷
둘
셋
하나
Collection 인터페이스
List와 Set 인터페이스의 많은 공통된 부분을 Collection 인터페이스에서 정의하고, 두 인터페이스는 그것을 상속받습니다. 따라서 Collection 인터페이스는 컬렉션을 다루는데 가장 기본적인 동작들을 정의하고, 그것을 메소드로 제공하고 있습니다. Collection 인터페이스에서 제공하는 주요 메소드는 다음과 같습니다.
package generic;
public class Apple {
private int weight; // 사과의 무게
Apple(int weight){
this.weight= weight;
}
public void showWeight() {
System.out.println("사과의 무게 : " + weight);
}
}
package generic;
public class Orange {
private int sugarContent; // 오렌지 당도
Orange(int sugar){
this.sugarContent=sugar;
}
public void showsugarContent() {
System.out.println("오렌지의 당도 : " + sugarContent);
}
}
package generic;
public class AppleBox {
Apple apple;
public void store(Apple apple) {
this.apple=apple;
}
public Apple PullOut() {
return apple;
}
}
package generic;
public class OrangeBox {
Orange orange;
public void store(Orange orange) {
this.orange=orange;
}
public Orange PullOut() {
return orange;
}
}
package generic;
public class FruitBoxMain {
public static void main(String[] args) {
AppleBox appleBox = new AppleBox();
appleBox.store(new Apple(10));
Apple apple = appleBox.PullOut();
apple.showWeight();
OrangeBox orangeBox = new OrangeBox();
// orangeBox.store(new String("오렌지"));
orangeBox.store(new Orange(100));
Orange orange = orangeBox.PullOut();
orange.showsugarContent();
}
}
2.
fruitBox를 만들어서 사과와 오렌지를 각각 집어넣어줄 수 있지만,
아무거나 들어가도 빨간줄이 생기지 않지만, 실행하면 에러가 난다.
package generic;
public class FruitBox {
Object fruit;
public void store(Object fruit) {
this.fruit=fruit;
}
public Object PullOut() {
return fruit;
}
}
package generic;
public class FruitBoxMain {
public static void main(String[] args) {
FruitBox box1 = new FruitBox();
box1.store(new Apple(100));
Apple apple = (Apple)box1.PullOut();
apple.showWeight();
FruitBox box2 = new FruitBox();
box2.store("Apple"); // Apple 타입만 들어가야 한다. (아무거나 들어가도 ㅇㅋ인게 문제점..)
Orange orange = (Orange)box2.PullOut();//형변환
orange.showsugarContent();
}
}
3.
제네릭을 이용해 FuritBox<T>를 생성해주면
형변환도 필요없고 알아서 올바르지 않은 타입이 들어가면 빨간줄이 생긴다.
package generic;
public class FruitBox <T> { // T > Apple / Orange
T fruit; // Apple fruit
public void store(T fruit) { // store(Apple fruit)
this.fruit=fruit;
}
public T PullOut() { // public Apple PullOut()
return fruit;
}
}
package generic;
public class FruitBoxMain {
public static void main(String[] args) {
FruitBox<Apple> box1 = new FruitBox<Apple>();
// box1.store("Apple"); 에러남
box1.store(new Apple(10));
Apple apple = box1.PullOut();
apple.showWeight();
FruitBox<Orange> box2 = new FruitBox<Orange>();
// box2.store(new Apple(10)); // Orange만 들어갈 수 있게 됨
box2.store(new Orange(100));
Orange orange = box2.PullOut(); //형변환도필요없다.
orange.showsugarContent();
}
}
4.
FruitBox<T>에 초기화 생성자를 추가하면 더욱 편리하다.
package generic;
public class FruitBox<T> { // T > Apple / Orange
T fruit; // Apple fruit
FruitBox(T fruit){ // FruitBox(Apple fruit){
this.fruit=fruit;
}
public void store(T fruit) { // store(Apple fruit)
this.fruit=fruit;
}
public T PullOut() { // public Apple PullOut()
return fruit;
}
}
package generic;
public class FruitBoxMain {
public static void main(String[] args) {
FruitBox<Apple> box1 = new FruitBox<Apple>(new Apple(10));
Apple apple = box1.PullOut();
apple.showWeight();
FruitBox<Orange> box2 = new FruitBox<Orange>(new Orange(100));
Orange orange = box2.PullOut(); //형변환도필요없다.
orange.showsugarContent();
}
}