publicstaticvoidmain(String[] args) {NIOSample sample =newNIOSample();sample.basicWriteAndRead();}publicvoidbasicWriteAndRead() {String fileName ="dumpFile"+ separator +"text1.txt";try {writeFile(fileName,"NIO sample");readFile(fileName); } catch (Exception e) {e.printStackTrace(); }}publicvoidwriteFile(String fileName,String data) throws Exception { FileChannel channel = new FileOutputStream(fileName).getChannel(); // FileChannel 객체를 만들려면 FileOutputStream 클래스에 선언된 getChannel을 호출한다.
byte[] bytes =data.getBytes(); ByteBuffer buffer = ByteBuffer.wrap(bytes); // ByteBuffer 클래스에 static으로 선언된 wrap() 메소드를 호출하면 ByteBuffer 객체가 생성되고 매개변수로 저장할 byte 배열을 넘겨주면 된다.
channel.write(buffer); // FileChannel에 선언된 write() 메소드를 buffer에 넘겨주면 파일에 쓰게된다.channel.close();}publicvoidreadFile(String fileName) throws Exception { FileChannel channel = new FileInputStream(fileName).getChannel(); // FileInputStream 클래스에 선언된 getChannel() 메소드를 호출한다.
ByteBuffer buffer = ByteBuffer.allocate(1024); // ByteBuffer의 allocate() 메소드를 통해 buffer 객체를 만들고 데이터가 기본적으로 저장되는 크기를 지정한다
channel.read(buffer); // 채널에 버퍼를 넘겨 담을 버퍼를 알려준다.buffer.flip(); // buffer에 담겨있는 데이터의 가장 앞으로 이동한다while (buffer.hasRemaining()) { // 데이터가 더 남아 있는지 확인한다System.out.println((char)buffer.get()); // 한 바이트씩 데이터를 읽는다 }channel.close();}
Buffer에 대해 살펴보자
Buffer는 java.nio.Buffer 클래스를 확장해서 사용하는데 CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer 등이 존재하는데 이 안의 메소드들은 다음과 같다.
리턴 타입
메소드
설명
int
capacity()
버퍼에 담을 수 있는 크기 리턴
int
limit()
버퍼에서 읽거나 쓸 수 없는 첫 위치 리턴
int
position()
현재 버퍼의 위치 리턴
capacity는 버퍼의 크기
position은 현재의 위치
limit은 읽거나 쓸 수 없는 위치
리턴 타입
메소드
설명
Buffer
flip()
limit 값을 현재 position으로 지정한 후 position을 0으로 이동
Buffer
mark()
현재 position을 mark
Buffer
reset()
버퍼의 position을 mark한 곳으로 이동
Buffer
rewind()
현재 버퍼의 position을 0으로 이동
int
remaining()
limit-position 계산 결과를 리턴
boolean
hasRemaining()
position과 limit 값에 차이가 있을 경우 true
Buffer
clear()
버퍼를 지우고 position을 0으로 이동하며 limit을 버퍼의 크기로 변경
\
Java Lambda expression(람다식)
익명 클래스를 사용하면 가독성도 떨어지고 직접 일일이 써야하는 코드량이 늘어난다. 이런 단점을 보완하기 위해 람다식이 만들어졌다. 람다식은 익명 클래스로 전환 가능하며 익명 클래스는 람다 표현식으로 전환 가능하다. 그럼 람다 표현식 전에 익명 클래스부터 알아보자.
Nested 클래스
클래스 안의 클래스를 Nested 클래스라고 불린다. Nested class는 static nested 클래스와 내부 클래스로 구분되는데 이 둘은 static 여부로 구분된다.
내부 클래스는 또 다시 두 가지로 나뉘는데 이름이 있는 내부 클래스를 Local Class, 이름이 없는 클래스를 익명 클래스로 불린다.
다소 복잡해졌다. 객체를 생성하기 위해서 outer 객체를 만들고 그 객체를 통해 inner를 만들 수 있다.
이렇게 내부 클래스를 만들었던 이유는 캡슐화 때문이고 대부분 자바 GUI 때문이었다. 특정 버튼이 눌렸을 때 이벤트를 발생하는데 그 때의 작업을 정의하기 위해서 내부 클래스를 사용했다. 하지만 대부분의 버튼 하나당 작업은 하나로 귀결되기 때문에 익명 클래스를 만드는 것이 편하다. 익명 클래스는 Inner 클래스인데 개중 이름이 없는 클래스다.
이렇게 익명함수로 만들어버리면 MagicButtonListener를 만들지 않아도 된다.
익명함수는 메모리에 로드하는 클래스 갯수를 줄일 수 있기 때문에 속도 면에서 유리하다고 할 수 있다.
내부 클래스는 모두 다른 클래스에서 재사용할 일이 없을 때만 만들어 줘야 한다.
람다
람다는 익명클래스의 단점을 보완하기 위해 만들어 졌다. 다시말해 인터페이스에 메소드가 하나인 것들만 적용 가능하다.
@FunctionalInterface를 사용하면 이 인터페이스는 하나의 메소드만 선언할 수 있고 그 이상에선 컴파일 에러가 뜬다.
Runnable을 사용해서 람다를 사용해보면
@FunctionalInterfacepublicinterfaceRunnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */publicabstractvoidrun();}