NIO를 이용한 파일 쓰고 읽기(GatheringByteChannel, ScatteringByteChannel)



보통 자바가 C에 비에 느린 이유중 하나가 IO가 JVM 내부에 IO버퍼를 두었기 때문이다. java에서 IO 프로그램을 할 때 JVM의 내부 버퍼를 이용하지 않고 직접 운영체제의 데이터를 접근해 속도 개선을 한 것이 NIO이다. 이를 사용해 파일을 쓰고 읽기를 사용법을 익혀보도록 하겠다. 


NIO를 이용한 파일 쓰고 일기의 3가지 클래스


nio를 사용 하기 위해서는 GatheringByteChannel, ScatteringByteChannel, ByteBuffer 3가지 클래스를 이용한다. 우선 전체 예제 부터 살펴 보겠다.


[전체 예제]

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.*;


public class ChannelFile {

public static void main(String[] args) {

try {

//NIO 함수를 이용해 파일 쓰기

FileOutputStream fo = new FileOutputStream("test.txt");

GatheringByteChannel gchannel = fo.getChannel();

ByteBuffer wheader = ByteBuffer.allocate(30);

ByteBuffer wbody = ByteBuffer.allocate(30);

ByteBuffer[] wbuffers = { wheader, wbody };

wheader.put("header is hello world".getBytes());

wbody.put("body is welcome to channel".getBytes());

wheader.flip();

wbody.flip();

gchannel.write(wbuffers);

gchannel.close();

//NIO 함수를 이용해 파일 읽기

FileInputStream fin = new FileInputStream("test.txt");

ScatteringByteChannel schannel = fin.getChannel();

ByteBuffer rheader = ByteBuffer.allocateDirect(10);

ByteBuffer rbody = ByteBuffer.allocateDirect(10);

ByteBuffer[] rbuffers = { rheader, rbody };

int readCount = (int) schannel.read(rbuffers);

System.out.println("cnt : " + readCount );

schannel.close();

rheader.flip();

rbody.flip();

byte[] byteHeaderBuffer = new byte[10];

byte[] byteBodyBuffer = new byte[10];

rheader.get(byteHeaderBuffer);

rbody.get(byteBodyBuffer);

System.out.println("header : " + new String(byteHeaderBuffer));

System.out.println("body : " + new String(byteBodyBuffer));

}catch( IOException e ) {

System.out.println(e);

}

}

}


[실행 결과]

cnt : 20

header : header is 

body : hello worl


[파일 쓰는 방법]

10자리 byte 만큼 메모리 공간 확보

//NIO 함수를 이용해 파일 쓰기


//디스크에 test.txt 파일을 만들어 채널 형식으로 변환한다.

FileOutputStream fo = new FileOutputStream("test.txt");

GatheringByteChannel gchannel = fo.getChannel();

//nio는 byte단위로만 사용할 수 있어 ByteBuffer형식으로 메모리를 생성한다.

ByteBuffer wheader = ByteBuffer.allocate(30);

ByteBuffer wbody = ByteBuffer.allocate(30);

ByteBuffer[] wbuffers = { wheader, wbody };

//생성한 메모리에 2군데에 각각 값을 입력한다.

wheader.put("header is hello world".getBytes());

wbody.put("body is welcome to channel".getBytes());

//메모리에 값을 입력하면 buffer가 가지고 있는 내부 position 값이 변해서 0으로 초기화 시킨다.

wheader.flip();

wbody.flip();

//채널 클래스로 디스크 파일에 직접 입력하고 닫는다.

gchannel.write(wbuffers);

gchannel.close();


[파일 읽는 방법]

position 0~2까지 순차적으로 데이터가 10, 11, 12를 넣는다. 이때 position의 위치도 바뀐다.

//디스크에 test.txt 파일을 읽고 채널 형식으로 변환한다.

FileInputStream fin = new FileInputStream("test.txt");

ScatteringByteChannel schannel = fin.getChannel();

//nio는 byte단위로만 사용할 수 있어 ByteBuffer형식으로 메모리를 생성한다.

ByteBuffer rheader = ByteBuffer.allocateDirect(10);

ByteBuffer rbody = ByteBuffer.allocateDirect(10);

ByteBuffer[] rbuffers = { rheader, rbody };

//파일의 내용을 채널을 통해 버퍼에 담는다.

int readCount = (int) schannel.read(rbuffers);

System.out.println("cnt : " + readCount );

schannel.close();

//각 데이터 버퍼의 positon 값이 변동 되어 0으로 초기화 한다.

rheader.flip();

rbody.flip();

//byte 버퍼로 부터 byte값을 얻어오기 위해 byte 형식으로 메모리 생성

byte[] byteHeaderBuffer = new byte[10];

byte[] byteBodyBuffer = new byte[10];

//각 버퍼에서 데이터를 얻어온다.

rheader.get(byteHeaderBuffer);

rbody.get(byteBodyBuffer);

//byte를 스트링 형식으로 변환해 출력한다.

System.out.println("header : " + new String(byteHeaderBuffer));

System.out.println("body : " + new String(byteBodyBuffer));



반응형

[Design Pattern] 싱글톤(Singleton) 패턴



프로그램 내에서 1개의 인스턴스만 생성 하기 위해서 필요한 것이 Singleton 패턴 입니다. 프로그램의 전역에서 사용될 설정 정보 값은 Singleton 패턴을 이용해 한개만 생성해 정보를 공유 하는데도 필요합니다. 사용자가 주의해서 1개만 생성하는것이 아닌 프로그램적으로 보증하는 방법을 제공해 줍니다.


싱글톤 패턴



싱글톤 패턴을 이용해 클래스를 만들어 보고 인스턴스가 1개 이상 생성되는지 확인해 보자.


[Singleton 구현]

싱글톤 클래스의 생성자는 private로 되어 있다. 이것은 싱글톤 클래스 외부에서 생성자를 호출을 금지하기 위해서다. 해당 패턴을 이용하면 프로그래머가 실수를 해도 인스턴스가 1개만 생성되도록 보증을 해주고 있다.


public class Singleton {

private static Singleton singleton = new Singleton();

private Singleton() {

System.out.println("인스턴스를 생성했습니다.");

}

public static Singleton getInstance() {

return singleton;

}

}



[Main 구현]


public class Main {

public static void main(String[] args) {

Singleton sing1 = Singleton.getInstance();

Singleton sing2 = Singleton.getInstance();

if ( sing1 == sing2 ) {

System.out.println("같은 인스턴스 입니다.");

} else {

System.out.println("다른 인스턴스 입니다.");

}

}

}



[실행 결과]


인스턴스를 생성했습니다.

같은 인스턴스 입니다.




실행 결과와 같이 sing1, sing2에서 2번 객체를 빼왔는데 인스턴스는 단 한번만 생성 된 것을 확인 할 수 있다.




반응형

'프로그램 > Design Pattern' 카테고리의 다른 글

[디자인패턴] Factory Method 패턴  (0) 2018.06.03
[디자인패턴] Template Method 패턴  (0) 2018.06.02
[디자인패턴] Adapter 패턴  (0) 2018.06.01
[디자인패턴] Iterator  (0) 2018.05.22

ByteBuffer 사용법



ByteBuffer 사용법에 대해 알아보겠다. 이렇게 ByteBuffer를 자세히 설명하는 이유는 java에서 NIO를 이용하기 위해서다. 보통 자바가 C에 비에 느린 이유중 하나가 IO가 JVM 내부에 IO버퍼를 두었기 때문이다. 


ByteBuffer 초기화와 사용법



[전체 예제]

import java.nio.ByteBuffer;


public class NioBuffer1 {


public static void main(String[] args) {

ByteBuffer buf = ByteBuffer.allocate(10);

System.out.println("position[" + buf.position() +"] Limit["+ buf.limit()+"] Capacity["+buf.capacity()+"]" );

buf.mark(); //나중에 찾아 오기 위해 현재 위치를 지정해 둔다. (현재 위치는 0 )

//순차적으로 데이터 넣기 -> 데이터를 넣을 때 마다 position이 바뀐다.

buf.put((byte)10);

System.out.println("put result -> position[" + buf.position() +"]");


buf.put((byte)11);

System.out.println("put result -> position[" + buf.position() +"]");

buf.put((byte)12);

System.out.println("put result -> position[" + buf.position() +"]");

//mark 해 두었던 위치로 이동

buf.reset();

System.out.println("put reset -> position[" + buf.position() +"]");

System.out.println("");

System.out.println("데이터 들어간 결과 확인");

//데이터를 get 할 때 마다 position이 바뀐다.

for( int i = 0 ; i < 5 ; i++ ) {

System.out.println("position[" + buf.position() +"] value["+ buf.get() + "]");

}

//지정한 위치에 데이터에 넣기

buf.put(2, (byte)22);

buf.put(3, (byte)23);

buf.put(4, (byte)24);

System.out.println("");

System.out.println("데이터 들어간 결과 확인");

for( int i = 0 ; i < 5 ; i++ ) {

System.out.println("position[" + i +"] value["+ buf.get(i) + "]");

}

}

}


[실행 결과]

position[0] Limit[10] Capacity[10]

put result -> position[1]

put result -> position[2]

put result -> position[3]

put reset -> position[0]


데이터 들어간 결과 확인

position[0] value[10]

position[1] value[11]

position[2] value[12]

position[3] value[0]

position[4] value[0]


데이터 들어간 결과 확인

position[0] value[10]

position[1] value[11]

position[2] value[22]

position[3] value[23]

position[4] value[24]


[메모리 크기 설정 및 초기화 된 값 확인]

10자리 byte 만큼 메모리 공간 확보

ByteBuffer buf = ByteBuffer.allocate(10);

System.out.println("position[" + buf.position() +"] Limit["+ buf.limit()+"] Capacity["+buf.capacity()+"]" );


[순차적으로 데이터 넣기]

position 0~2까지 순차적으로 데이터가 10, 11, 12를 넣는다. 이때 position의 위치도 바뀐다.

buf.put((byte)10);

System.out.println("put result -> position[" + buf.position() +"]");


buf.put((byte)11);

System.out.println("put result -> position[" + buf.position() +"]");

buf.put((byte)12);

System.out.println("put result -> position[" + buf.position() +"]");


[순차적으로 데이터 빼기]

position 0~4까지 순차적으로 데이터가 뺀다. 이때 position의 위치도 바뀐다.

for( int i = 0 ; i < 5 ; i++ ) {

System.out.println("position[" + buf.position() +"] value["+ buf.get() + "]");

}


[특정 위치에 데이터 넣기]

position 2에는 22, 3에는 23, 4에는 24를 넣는다. 

buf.put(2, (byte)22);

buf.put(3, (byte)23);

buf.put(4, (byte)24);


[특정 위치에 데이터 빼기]

position 0~4까지 데이터가 뺀다. 

for( int i = 0 ; i < 5 ; i++ ) {

System.out.println("position[" + i +"] value["+ buf.get(i) + "]");

}


반응형

[Design Pattern] Factory Method 패턴



팩토리 패턴을 이해하기 위해서는 우선 템플릿 패턴을 알아야한다. 왜냐하면 템플릿 패턴에 인스턴스 생성하는 부분까지 추상화 시켰기 때문이다.

팩토리 패턴은 처리 골격을 만들고 상속 받은 클래스가 구체적인 처리를 구현해 놓습니다. 또한 인스턴스를 생성하는 부분까지 맡겼기 때문에 해당 클래스를 호출하는 부분에서는 따로 인스턴스를 만드는 과정을 없앴습니다.


팩토리 메소드 패턴



다목적 공장이 있습니다. 이 공장을 가지고 있으면 철강, 차, 베터리 등등 모든 것을 만들 수 있다. 이 공장을 이용해 차를 만드는 과정을 프로그램화 해보겠다. 다목적 공장에 해당하는 Framework 패키지 안에 Factory와 Product가 있다. 이 공장들을 이용해 차를 만드는 것은 Car 패키지에 Factory를 상속 받은 CarFactory가 Product를 상속받은 Car를 만드는 과정을 알아보겠다.


[클래스 다이어그램]




[framework 패키지에 Product 구현]

다목적 공장에서 생산되는 상품 규격을 정의해 놓은 추상화 클래스 이다.

해당 규격에만 맞게 구현 할 수 있으면 Factory에서 어떠한 제품을 만들어 낼 수 있다.

package framework;


public abstract class Product {

public abstract void use();

}



[framework 패키지에 Factory 구현 ]

Factory 추상화 클래스에서는 Product를 생성한다. 이 클래스를 상속 받은 클래스는 해당 규격에 맞게 구현만 하면 어떠한 공장도 만들어 낼 수 있다.


package framework;


public abstract class Factory {

public final Product create(String owner) {

Product p = createProduct(owner);

registerProduct(p);

return p;

}

protected abstract Product createProduct(String owner);

protected abstract void registerProduct(Product product);

}



[car 패키지에 Car 클래스 구현]

Product를 상속 받아 실제로 차에 대한 기능을 구현 한다.


package car;

import framework.*;


public class Car extends Product {

private String owner;

Car(String owner) {

this.owner = owner;

System.out.println(owner + "의 차를 만듭니다.");

}

@Override

public void use() {

System.out.println(owner + "의 차를 이용합니다.");

}

public String getOwner() {

return owner;

}

}



[car 패키지에 CarFactory 클래스 구현]

Factory를 상속받은 CarFactory 클래스는 Car 클래스 인스턴스를 생성 한다. 더블어 ArrayList 클래스를 이용한 생성한 Car 주인에 대한 관리도 가능하다.


package car;

import framework.*;

import java.util.*;


public class CarFactory extends Factory{


private List owners = new ArrayList();

@Override

protected Product createProduct(String owner) {

return new Car(owner);

}


@Override

protected void registerProduct(Product product) {

owners.add(((Car)product).getOwner());

}

public List getOwners() {

return owners;

}


}



[Main 구현]

만들어 놓은 차 공장을 이용해 차를 생산해 보자.


import car.*;

import framework.*;


public class Main {


public static void main(String[] args) {

Factory factory = new CarFactory();

Car car1 = (Car) factory.create("김순희");

Car car2 = (Car) factory.create("박철수");

car1.use();

car2.use();

for( int i = 0 ; i < ((CarFactory)factory).getOwners().size() ; i++ ) {

System.out.println(((CarFactory)factory).getOwners().get(i));

}

}

}



[실행 결과]


김순희의 차를 만듭니다.

박철수의 차를 만듭니다.

김순희의 차를 이용합니다.

박철수의 차를 이용합니다.

김순희

박철수





반응형

+ Recent posts