자바 파일 입출력



안녕하세요 열코입니다.

이번 시간에는 자바에서 파일 처리하는 방법에 대해 알아보겠습니다.

자바에서 파일 입출력을 하기 위해서 사용되는 클래스는 여러가지가 있는데요

그 종류에는 입력에 대한 클래스가 다음과 같이 존재하며,


[Writer 클래스]

Writer ┬ BufferedWriter

           └ OutputStreamWriter ─ FileWriter


출력에 대한 클래스는 다음과 같이 존재합니다.


[Reader 클래스]

Reader ┬ BufferedReader

            └ InputStreamReader ─ FileReader


입력과 출력 클래스들은 각각 Writer, Reader 클래스에서 파생됐습니다.

이중 BufferedReaderBufferedWriter는 이름에서처럼 버퍼를 사용하여 파일 입,출력을 수행하며

속도적인면에서 향상된 모습을 보입니다.

오늘은 파일 입,출력으로 BufferedReader와 BufferedWriter의 사용법에 대해 알아보도록 하겠습니다.


1. 파일 입력(BufferedWriter)

텍스트파일에 텍스트를 입력하기 위해서 먼저 BufferedWriter 객체를 다음과 같이 선언해 줍니다.


BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));


BufferedWriter와 FileWriter 클래스를 사용하기 위해서 java.io.BufferedWriter와 java.io.FileWriter를 import 해줍니다.

그리고 BufferedWriter 객체를 선언해 주면 try, catch문을 사용하거나 throws를 선언하여 예외처리를 해 주어야 합니다.

저는 다음과 같이 IOException을 main함수에서 throws 해 주었습니다.



public static void main(String[] arg) throws IOException {

BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));


이대로 실행을 해 보면 해당 프로젝트 폴더에 test라는 텍스트 파일이 하나 생성되는것을 확인 할 수 있습니다.



보시면 아시겠지만 크기가 0KB입니다.

왜냐구요? 

아무것도 쓰지 않았으니까요 ㅎㅎ 아직은 객체를 생성하여 텍스트 파일을 만들어 주었을 뿐입니다.

자 그럼 텍스트파일에 텍스트를 한번 작성 해 보겠습니다.


public static void main(String[] arg) throws IOException {

BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\users\\simt\\desktop\\test.txt"));

bw.write("열코의 프로그래밍 일기");

bw.newLine();

bw.write("자바 파일 입출력");

bw.write("\r\n");

bw.write("다같이 열코합시다! ^-^");

bw.close();


파일에 데이터를 쓸 때에는 write() 함수를 사용합니다.

개행(줄바꿈)을 할 때에는 "\r\n"을 write하거나 newLine() 함수를 사용합니다.

그리고 항상 강조해왔던 객체를 모두 사용하고 난 후 close()로 닫아주기! 

그럼 파일에 잘 쓰여졌는지 확인 해 볼까요?


☞ 출력 결과


보시는 것처럼 아주 잘 작성되었습니다.


*flush()에 대해

BufferedWriter는 데이터 스트림을 버퍼에 저장 해 두었다가 가득 차면 디스크에 쓰는 방식입니다.

flush()함수는 버퍼가 가득 차지 않아도 명시적으로 디스크에 내용을 쓰라는 함수인데,

자주 사용하면 메모리와 속도적인 측면에서 많이 비 효율적입니다.

또한 위와 같은 간단한 코드에서는 close()함수를 호출하여 객체를 닫아주면 자동으로 객체를 닫기 전

flush()함수를 호출하게 됩니다. 무분별한 flush()함수 호출을 지양하고, 꼭 필요한 시점에 사용하도록 합시다.



2. 파일 출력(BufferedReader)

파일을 쓰는것도 중요하지만 읽어오는것도 그만큼 중요하겠죠? ㅎㅎㅎ

파일 읽기(출력)에 대해 알아봅시다.

파일 읽기를 위해 BufferedReader 객체를 다음과 같이 선언해 줍니다.


BufferedReader br = new BufferedReader(new FileReader("test.txt"));


BufferedReader 와 FileReader 클래스를 사용하기 위해 java.io.BufferedReader와 java.io.FileReader를 import 해줍니다.

또한 FileReader 객체를 만들어 줌으로써 FileNotFoundException에 대해 예외처리를 해 주어야하는데

이번에는 try, catch문을 사용해서 예외처리를 해 보겠습니다.


public static void main(String[] arg)  {

try {

BufferedReader br = new BufferedReader(new FileReader("test.txt"));

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}


이와 같이 BufferedReader 객체를 선언하고 FileReader를 통해 파일을 열면 파일 출력을 위한 준비가 모두 끝났습니다.

한번 텍스트 파일을 열어 콘솔창에 표준 출력으로 출력해 보겠습니다.


public static void main(String[] arg)  {

try {

BufferedReader br = new BufferedReader(new FileReader("test.txt"));

String line = null;

while((line = br.readLine()) != null) {

System.out.println(line);

}

br.close();

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}


텍스트 파일을 열어 문자열 변수에 한줄씩 읽어 파일의 끝까지 반복문을 통해 읽어옵니다.

읽어 온 데이터를 콘솔 화면에 출력하고 사용이 다 끝난 후 close()함수로 객체를 닫아주었습니다.

또한 BufferedReader의 readLine()함수를 사용하기 위해 IOException을 처리해주어야 하는데,

try-catch문에서 IOException에 대해 catch문을 추가하여 처리했습니다.


☞ 출력 결과

열코의 프로그래밍 일기

자바 파일 입출력

다같이 열코합시다! ^-^





이상 '자바 파일 입출력'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.





안녕하세요 열코입니다.

이번시간에는 자바의 가비지 컬렉션에 대해 알아보겠습니다.


우리가 C/C++ 프로그래밍을 할 때 메모리 누수(Memory Leak)를 막기 위해 객체를 생성한 후 

사용하지 않는 객체를 파괴(메모리 해제)를 프로그래머가 직접 해주어야 했습니다.


하지만 Java에서는 JVM(Java Virtual Machine)이 구성된 JRE(Java Runtime Environment)가 

제공되며 JVM 구성 요소 중 하나인 가비지 컬렉션(Garbage Collection)이 자동으로 

사용하지 않는 객체를 파괴합니다.


JVM의 가비지 컬렉션은 다음과 같은 단계로 메모리 해제 작업을 수행합니다.

1. 마킹 작업 : 사용중인 메모리와 사용 하지않는 메모리를 식별합니다.

2. 일반 삭제 : 참조되지 않는 객체를 제거하고 빈 공간에 대한 포인터를 남겨둡니다.

+ 압축 삭제 : 삭제 된 객체 외 나머지 객체를 메모리 공간을 효율적으로 사용하며 삭제


또한 JVM의 가비지 컬렉션은 객체들을 세대로 나누어 처리합니다.

- 젊은 세대 : 새로운 모든 객체를 할당하는 곳입니다. 젊은 세대가 가득차면 가비지 컬렉션을 수행하며 

매우 빠른 속도로 진행됩니다. 사소한(가벼운) 가비지 컬렉션이 수행됩니다.

- 이전 세대 : 오랫동안 생존한 객체를 저장합니다. 젊은 세대에서 생성된 객체에 임계 값을 주어지고 어느정도

연령에 도달하면 이전 세대로 이동합니다. 이전 세대의 객체를 수집하는 것을 주요 가비지 컬렉션이라고 합니다.


세대 별 가비지 컬렉션은 다음과 같은 과정으로 수행됩니다.

1. 새로운 객체는 에덴(Eden) 공간에 할당되며, 그 외 두 개의 생존 공간이 모두 비어있는 상태입니다.

2. 에덴 공간이 가득차게 되면 사소한(가벼운) 가비지 컬렉션이 시작됩니다.

3. 참조된 객체는 첫번째 생존 공간으로 이동되며 참조되지 않는 객체는 모두 삭제됩니다.

4. 가비지 컬렉션이 수행되는 동안 첫번째 생존 공간의 객체들은 특정 임계값이 넘어가면 두번째 생존 공간으로 이동합니다.

5. 두 번째 생존 공간의 객체들도 특정 임계값이 넘어가면 이전 세대로 넘어가게 됩니다.

6. 이전 세대의 객체가 일정 크기 이상 수집되면 주요 가비지 컬렉션이 수행됩니다.


또한 다음과 같이 JVM에게 가비지 컬렉션을 실행하도록 요청할 수 있습니다.

System.gc() 메소드 또는 Runtime.getRuntime().gc() 메소드 사용 (수행되는 가비지 컬렉션은 동일합니다.)



아래는 객체를 가비지 컬렉션의 대상으로 만드는 방법입니다.

1. 메소드 내부에서 생성된 객체

class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


static void show() {

GCTest gct1 = new GCTest("gct1");

show2();

}

static void show2() {

GCTest gct2 = new GCTest("gct2");

}


public static void main(String[] args) {

show();

System.gc(); //  gct1과 gct2는 가비지 컬렉션의 대상이 됨.

}

}

위 코드와 같이 메소드가 호출된 후 내부에 일부 객체가 만들어지고 메소드가 종료됬을 때, 메소드 내부의 객체들은

익명이 되어 가비지 컬렉션의 대상이 됩니다.


2. 참조 변수 재 지정

Class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


public static void main(String[] args) {

GCTest gct1 = new GCTest("gct1");

GCTest gct2 = new GCTest("gct2");


gct1 = gct2;


System.gc();

}

}

위 코드는 한 객체의 참조가 다른 객체를 참조할 때 이전 객체는 더 이상 참조를 갖지 못해 가비지 컬렉션의 대상이 됩니다.


3. 참조 변수 무효화

Class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


public static void main(String[] args) {

GCTest gct1 = new GCTest("gct1");


gct1 = null;


System.gc();

}

}

위 코드는 객체의 참조 변수가 null로 변경되었기 때문에 가비지 컬렉션의 대상이 됩니다.



4. 익명 객체

Class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


public static void main(String[] args) {

new GCTest("gct1");


System.gc();

}

}

익명 객체의 참조는 어느곳에도 저장되지 않기 때문에 가비지 컬렉션의 대상이 됩니다.



이상 '자바 가비지 컬렉션'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.





안녕하세요 열코입니다.


이번에는 자바에서 크기가 동적인 배열을 사용할 때 주로 사용하는 

두가지 클래스 벡터(Vector)와 어레이리스트(ArrayList)에 대해 

비교분석 해보겠습니다.


먼저 벡터에 대한 설명입니다.


☞ Vector란?

- 벡터 클래스는 예전의 자바에서 제공했던 레거시 클래스입니다.

- 레거시 클래스란 Collections 프레임워크가 포함되어 있지 않던 초기 자바 버전에서 정의한 인터페이스입니다.

- 현재는 재구성 및 설계되어서 현재의 Collections 프레임워크와 완벽하게 호환됩니다.


 Vector의 특징

- 필요에 따라 크기를 동적으로 조절할 수 있는 동적배열을 구현합니다.

- 배열과 마찬가지로 정수 인덱스를 이용하여 배열에 액세스 할 수 있습니다.

- 동기화(Thread Safe) 되어있으며 한번에 하나의 스레드만 벡터의 메소드를 호출 할 수 있습니다.


※ 벡터에 대한 사용법은 추후에 포스팅하겠습니다.



다음 어레이리스트에 대한 설명입니다.


☞ ArrayList란?

- Collections 프레임워크의 일부이며 java.uitl 패키지 내에 존재합니다.

- 벡터와 마찬가지로 동적 배열을 사용하기 위해 사용됩니다.


☞ ArrayList의 특징

- 자바 표준 배열보다 약간 느릴수 있지만 배열에서 많은 조작이 필요로할때 유용하게 사용됩니다.

- 기본 데이터 타입(int, char 등)에 대해 만들수 없기때문에 Integer, Object 등의 객체에 대해 참조해서 사용합니다.


※ 어레이리스트에 대한 사용법은 추후에 포스팅하겠습니다.



이렇게 비슷한 두 클래스 벡터와 어레이리스트의 차이점은 무엇이며 어떤 상황에서 어떤 클래스를 사용 야 하는

지에 대해 알아보겠습니다.

실제로 두 클래스는 많은 차이점이 있지만 이 포스트에서는 주요 차이점에 대해서만 언급하겠습니다.


ArrayList와 Vector의 주요 차이점


1. 동기화(Synchronize)

Vector가 동기화 된다면 ArrayList는 동기화가 되지않은 상태입니다.

쉽게말해 Vector는 한번에 하나의 스레드만 엑세스(접근) 가능하며, ArrayList는 동시에 여러 스레드가 

작업할 수 있습니다.

ArrayList에서 여러 스레드가 동시에 엑세스하는 경우 개발자가 명시적으로 동기화하는 코드를 추가해야합니다.



2. 스레드 안전(Thread Safe)

스레드 안전이란 멀티 스레드 프로그래밍에서 여러 스레드가 동시에 접근이 이루어져도 프로그램 실행에 

문제가 없음을 뜻합니다.

앞서 말했듯이 Vector는 동기화 되어있기 때문에 한번에 하나의 스레드만 접근할 수 있기때문에 스레드 

안전합니다.

ArrayList는 동기화되지 않았기 때문에 명시적으로 동기화 할 필요가 있습니다.

 

3. 성능

ArrayList는 동기화 되지않았기 때문에 동기화 된 벡터보다 더 빠릅니다.


4. 크기 증가

Vector와 ArrayList 모두 동적 배열 클래스로 최대 인덱스를 초과할 때 추가되는 인덱스 수가 다릅니다.

Vector는 현재 배열의 크기의 100%가 증가하며, ArrayList의 경우 현재 배열의 크기의 50%가 증가합니다.



★ 결론

멀티스레드 환경이 아닌 경우 ArrayList를 사용하는것이 바람직합니다.

Vector를 사용하기 위한 명시적 요구 사항이 없는경우 ArrayList를 사용하도록 합시다.



이상 '자바 벡터와 어레이리스트의 비교'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.





자바에서 NullPointerException은 RuntimeException입니다. 특수한 널 값은 객체 참조에 할당할 수 있습니다. 

프로그램에 널값을 가지는 객체 참조를 사용하려고하면 NullPointerException이 throw됩니다.


null에 대해

- null이란 아무것도 없음을 의미합니다.(0또는 공백 : "")

- 모든 참조유형에 대한 기본 값은 null입니다.

- null은 유효한 객체 인스턴스가 아니므로 할당 되는 메모리가 없습니다.


+ 예외가 발생하는 경우

- null 객체에서 method를 호출하는 경우

- null 객체의 필드에 접근하거나 값을 변경하는 경우

- null 의 길이를 배열처럼 취하는 경우

- null 을 throw 하는 경우

- null 을 통해 동기화 할 경우


+ 그럼에도 null 값이 필요한 이유? 

- 널은 자바에서 사용되는 특수 값입니다. 주로 참조 변수의 값이 할당되지 않았음을 나타내는데 사용됩니다.

- Null Object 패턴(로그 시스템) 및 Singleton 패턴에 사용됩니다.

- 연결된 목록 및 트리와 같은 데이터 구조를 구현하는 데 사용됩니다.


+ NullPointerException을 피하는 방법

1. 문자열 비교 : String 변수와 리티럴문자를 비교할 때 다음과 같은 오류가 발생할 수 있습니다.


String ptr = null;

if (ptr.equal("abc") // NullPointerException 발생!

{

System.out.println("SAME!");

}

else

{

System.out.println("NOT SAME!");

}


이는 다음과 같이 null객체 대신 리터럴문자에서 equal 메소드를 호출함으로써 해결할 수 있습니다.


String ptr = null;

if ("abc".equals(ptr))

{

System.out.println("SAME!");

}

else

{

System.out.println("NOT SAME!"); // "NOT SAME" 출력

}


2. try - catch문 사용 : 다음과 같이 예외처리문을 사용가능합니다.


public class Main {

public static void main(String[] arg) {

String s = null; 

try

System.out.println(getLength(s)); 

catch(IllegalArgumentException e) 

System.out.println("IllegalArgumentException caught"); // 예외처리 발생!!

}

public static int getLength(String s) 

if (s == null) 

{

throw new IllegalArgumentException("The argument cannot be null");

}

return s.length(); 

}


3. 삼항 연산자 사용 : null 값은 == 또는 != 의 연산자가 적용되기 때문이 이를 이용하여 다음과 같이 코드를 작성할 수 있습니다.


public class Main {

public static void main(String[] arg) {

String s = null; 

System.out.println(s.length()); // NullPointerException 발생!!

        

}


위 코드를 아래와 같이 수정합니다.


public class Main {

public static void main(String[] arg) {

String s = null; 

System.out.println((s == null) ? "0" : s.length()); // "0" 출력

        

}


이상 java.lang.NullPointerException에 대해 알아보았습니다.

자바 예외에 대해 더 알고싶은 분은 여기를 참고하세요.

또한 본 게시글에 대한 오류나 질문사항은 아래 댓글로 남겨주세요.

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.



자바는 배열의 작성 및 조작을 데이터 구조로 지원합니다. 

배열의 크기를 n이라고 했을 때 배열의 인덱스는 1부터 n까지가 아닌 0부터 n-1까지입니다. 

프로그래밍 중 ArrayIndexOutOfBoundsException이 가장 많이 발생하는 이유중 하나죠.

C/C++과 달리 자바는 인덱스가 배열의 크기보다 크거나 음수 인덱스에 대한 요청이 있으면 자바는 위의 예외를 발생시킵니다.

또한 이 예외는 자바 컴파일러는 검사하지않고 항상 런타임(실행도중)에 예외를 발생시킵니다.


다음은 Exception 발생의 예입니다.


int[] arr = {0, 1, 2, 3, 4};

arr[5] = 4; // 예외 발생!!


해당 프로그램 실행 시 다음과 같은 오류가 발생합니다.


Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 

at average.Main.main(Main.java:9)


arr이란 배열의 크기는 5입니다. 따라서 인덱스는 0부터 4(n-1)까지입니다.

하지만 배열의 5번째 인덱스에 접근하려고 시도했기 때문에 자바는 해당 예외를 throw한 것입니다.


또 다른 Exception 발생의 예입니다.


ArrayList<String> list = new ArrayList<>();

list.add("Hello");

list.add("Wolrd");

list.get(2); // 예외 발생!!


해당 프로그램 실행 시 다음과 같은 오류가 발생합니다.


Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 2, Size: 2 

at java.util.ArrayList.rangeCheck(ArrayList.java:657)

at java.util.ArrayList.get(ArrayList.java:433)

at average.Main.main(Main.java:11)


위 코드를 보시면 list 객체를 생성하고 2번 add해주었습니다. (list의 크기 = 2)

list의 크기가 2이므로 접근 가능한 인덱스는 0부터 1(2-1)까지입니다. 따라서 list의 2번째 인덱스에 접근하려고 했기 때문에 해당 예외를 throw한 것입니다.

그렇다면 프로그래머는 어떤식으로 이런 예외에 대해 올바르게 예외처리를 수행할 수 있을까요?


☞ 배열에 올바르게 접근하는 방법


반복문에서 다음과 같이 사용합니다.


for(int i = 0; i < arr.length; i++) { ... }


또한 foreach문을 사용할 수 있습니다. (foreach문에 대한 설명)


for(int i : arr) { ... }


Try-Catch문 사용하기 : 다음과 같이 사용 가능합니다.


import java.util.ArrayList;


public class Main {

public static void main(String[] arg) {

try {

ArrayList<String> list = new ArrayList<>();

list.add("Hello");

list.add("Wolrd");

list.get(2);

}

catch(IndexOutOfBoundsException e) {

System.out.println(e);

}

}

}


이상 java.lang.ArrayIndexOutOfBoundsException에 대해 알아보았습니다.

자바 예외에 대해 더 알고싶은 분은 여기를 참고하세요.

또한 본 게시글에 대한 오류나 질문사항은 아래 댓글로 남겨주세요.

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.



to Top