Optional의 등장 이유

자바로 프로그래밍을 하면서 NullPointerException을 안 겪어 본 사람은 없을 것이다. 그만큼 null은 오류를 자주 발생시킬 위험이 있으며 개발하는 입장에서도 상당 귀찮다.

 

null 때문에 발생할 수 있는 문제는 다음과 같다.

1. NullPointerException을 발생시킬 수 있다.

2. 반환된 객체를 처리할 때 if(obj == null){ } 등의 확인 코드가 증가한다. => 가독성이 낮아진다.

 

이러한 점을 해소하고자 등장한 java.util.Optional은 null이 반환되지 않게 하여 오류를 발생시키지 않고, 호출하는 쪽에서도 명시적으로 빈 값이 반환될 수 있음을 알게 하는 것이다.

Optional : 정상적인 결과물인 객체가 반환될 수도 있고, 아니면 빈 값이 반환될 수도 있다는 의미

 

Optional 객체 생성

1. 빈 Optional

Optional<Car> optCar = Optional.empty();

 

2. null이 아닌 객체로 Optional 만들기 : car가 null이라면 NullPointerException이 발생한다.

Optional<Car> otpCar = Optional.of(car);

 

3. null값으로 Optional 만들기 : car가 null이라면 빈 Optional 객체가 반환된다.

Optional<Car> otpCar = Optional.ofNullable(car);

 

Optional 실용 예제

메소드의 결과물로 Optional 반환

DB에서 데이터를 조회하거나 기타 로직으로 생성된 객체를 반환할 때 null이 반환되지 않게 하기 위해 Optional을 반환

// Object를 반환하지 않고 Optional로 한번 감싸서 반환한다.
//  >> Object객체는 빈값(null)이 될 수 있음을 알려준다.
public Optional<Object> getObject(String key){
    return Optional.ofNullable(map.get(key));
}

 

메소드를 호출하는 쪽에서도 if else로 분기하여 처리하지 않고 비즈니스 로직에 따라 처리

비어있는 Optional이 반환되면 Optional의 orEleseGet()으로 빈 객체를 생성하여 반환이 가능하다.

만일 빈 객체가 허용되지 않는다면 orElseThrow()로 오류를 쓰로우하고 종료할 수도 있다.

public Object run(){
    Optional<Object> optValue = getObject("key"); // 위의 getObject()를 호출

    // 1. 빈 객체를 생성하여 반환
    Object value = optValue.orElseGet(Object::new);
}
public Object run(){
    Optional<Object> optValue = getObject("key"); // 위의 getObject()를 호출

    // 2. 오류로 종료
    optValue.orElseThrow(() -> new NoSuchKeyException("해당 키의 값이 없습니다."));
}

 

 

컬렉션은 Optional로 감싸지 말고 빈 컬렉션을 반환

만일 컬렉션 데이터가 없다면 Optional으로 컬렉션을 감싸는 것보다는 빈 컬렉션을 반환하는 것이 효율적이다. (null은 반환하지 않는다.)

// 컬렉션에 데이터가 없으면 Optional로 감싸지 않는다.
List<Object> list = getList();
return Optional.ofNullable(list);

// 대신 빈 컬렉션을 반환한다.
List<Object> list = getList();
return list != null ? list : Collections.emptyList();

 

 

 

 

 


FileWriter에서 flush()와 close()의 차이점

FileWriter를 사용 후에는 flush()와 close()를 호출하는데요 두 메소드의 차이점에 대해 알아봅니다.

 

- flush() : FileWriter 내부 버퍼의 내용을 파일에 writer합니다. flush()를 호출하지 않는다면 내용이 버퍼에만 남고 파일에는 쓰이지 않는 상황이 나올 수 있습니다.

 

- close() : FileWriter는 스트림을 이용하여 파일의 내용을 읽어들입니다. 이때 close()를 호출하여 스트림을 닫으면 그 스트림을 다시 이용하여 파일에 쓰는 것이 불가능합니다.

파일은 파일시스템이나 기타 다른 곳에 있으므로 이 내용을 스트림으로 읽어 들이는데 메모리를 소모합니다. 작업이 끝나면 close()를 호출하여 스트림을 닫아 종료된 작업에 대해 메모리를 확보해야 합니다.


[Java] 파일 생성, 파일 읽기(File, FileReader. FileWriter)

이전에 자바에서 파일과 디렉터리를 추상화한 File 클래스에 대해 정리했었는데,

이번에는 생성한 파일에 내용을 작성하는 것에 대해 정리합니다.


1. 파일 생성 및 쓰기(FileWriter, BufferedWriter)

D:\MyWork 경로 하위에 Test.txt 파일을 생성하고 내용을 작성합니다.

FileWriter의 생성자는 두번째 인자로 boolean 타입을 받는데, 이는 파일에 내용을 이어붙혀서 작성할지 처음부터 덮어씌워서 작성할지 여부를 결정합니다. 디폴트는 덮어씌워 작성입니다. (기존 내용이 날아감)

BufferedWriter는 파일에 문자열을 쓰기위해 편리한 메소드를 제공합니다. 예를들면 newLine() 처럼 줄바꿈을 추가할 수 있죠.

public class FileWriterTest {
    public static void main(String[] args) throws IOException{
        String filePath = "D:/MyWork/Test.txt";

        File file = new File(filePath); // File객체 생성
        if(!file.exists()){ // 파일이 존재하지 않으면
            file.createNewFile(); // 신규생성
        }

        // BufferedWriter 생성
        BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));

        // 파일에 쓰기
        writer.write("하이루!");
        writer.newLine();
        writer.write("반가워!");
        writer.newLine();

        // 버퍼 및 스트림 뒷정리
        writer.flush(); // 버퍼의 남은 데이터를 모두 쓰기
        writer.close(); // 스트림 종료
    }
}
하이루!
반가워!

2. 파일 읽기(FileReader, BufferedReader)

1번에서 작성한 내용을 읽어 콘솔에 출력하는 예제입니다. 파일을 읽기위해 FileReader, 한줄 단위로 읽어오는 편리한 readLine() 메소드를 사용하기 위해 BufferedReader를 사용했습니다.

public class FileReaderTest {
    public static void main(String[] args) throws IOException{
        String filePath = "D:/MyWork/Test.txt";

        File file = new File(filePath); // File객체 생성
        if(file.exists()){ // 파일이 존재하면
            BufferedReader reader = new BufferedReader(new FileReader(file));

            System.out.println("파일내용 출력------------------");
            String line = null;
            while ((line = reader.readLine()) != null){
                System.out.println(line);
            }
            System.out.println("------------------------------");

            reader.close();
        }
    }
}

 

 


 

 

 

 

 

 

StringTokenizer 기본 및 사용법

StringTokenizer 클래스는 문자열을 구분자를 이용하여 쪼갤 때 사용할 수 있다. (쪼갠다. = 파싱한다.)

예를 들어 "Hi I'm Yangs!!" 라는 문자열을 " "(공백)을 구분자로 "HI", "I'm", "Yangs!!" 이렇게 3개로 쪼개는 것이 가능하다.

Token을 구분자에 의해 쪼개진 단어라고 생각하면 된다.

 

StringTokenizer 생성

- StringTokenizer(String str) : 파싱 할 문자열을 인자로 받는다. 구분자를 지정하지 않았으므로 스페이스, 탭, 줄바꿈, 캐리지 리턴 등 기본 구분자가 적용된다.

- StringTokenizer(String str, String delim) : 파싱할 문자열과 구분자를 인자로 받는다.

- StringTokenizer(String str, String delim, boolean flag) : flag는 구분자 자체도 토큰으로 인식하게 할지 여부를 정한다. 예를 들어 true라면 "Hi I'm Yangs!!"는 공백을 포함하여"HI", " ", "I'm", " ", "Yangs!!" 이렇게 5개의 토큰으로 파싱 된다.

String source = "Hi I'm Yangs!!";

StringTokenizer tokenizer1 = new StringTokenizer(source);
while(tokenizer1.hasMoreTokens()){
    System.out.println("tokenizer1's token : " + tokenizer1.nextToken());
}

StringTokenizer tokenizer2 = new StringTokenizer(source, " ");
while(tokenizer2.hasMoreTokens()){
    System.out.println("tokenizer2's token : " + tokenizer2.nextToken());
}

StringTokenizer tokenizer3 = new StringTokenizer(source, " ", true);
while(tokenizer3.hasMoreTokens()){
    System.out.println("tokenizer3's token : " + tokenizer3.nextToken());
}
// 출력결과
tokenizer1's token : Hi
tokenizer1's token : I'm
tokenizer1's token : Yangs!!

tokenizer2's token : Hi
tokenizer2's token : I'm
tokenizer2's token : Yangs!!

tokenizer3's token : Hi
tokenizer3's token :  
tokenizer3's token : I'm
tokenizer3's token :  
tokenizer3's token : Yangs!!

StringTokenizer 사용법

StringTokenizer를 이용하여 문자열 -> 배열로 파싱하기

가장 많이 사용되는 문자열을 파싱하여 배열에 담는 예제이다.

hasMoreTokens()로 총토큰의 개수를 구하고, nextToken()으로 한 토큰씩 꺼낼 수 있다.

StringTokenizer는 구분자에 의해서 파싱 후 빈 토큰은 버리는 동작을 확인할 수 있다.

String source = "|문자열||에서|배열로|갑니다|";

// StringTokenizer 생성
StringTokenizer tokenizer = new StringTokenizer(source, "|");
System.out.println("총 토큰 갯수 : " + tokenizer.countTokens()); // 총 토큰 갯수 : 4

String[] arr = new String[4]; // 결과 배열
int idx = 0;
while (tokenizer.hasMoreTokens()){
    arr[idx] = tokenizer.nextToken(); // 배열에 한 토큰씩 담기
    idx++;
}

System.out.println(Arrays.toString(arr)); // [문자열, 에서, 배열로, 갑니다]

 

 

여러 구분자로 파싱하기

StringTokenizer 생성자 구분자를 여러 개 지정하면 된다. > new StringTokenizer(source, ",|;!")

String source = "|문자열,,에서|배열로;갑니다!";

StringTokenizer tokenizer = new StringTokenizer(source, ",|;!");
System.out.println("총 토큰 갯수 : " + tokenizer.countTokens()); // 총 토큰 갯수 : 4

String[] arr = new String[4];
int idx = 0;
while (tokenizer.hasMoreTokens()){
    arr[idx] = tokenizer.nextToken();
    idx++;
}

System.out.println(Arrays.toString(arr)); // [문자열, 에서, 배열로, 갑니다]

 

 


 

 

[Java] File 클래스 기본

Java.io.File 클래스는 자바에서 파일시스템의 파일이나 디렉터리(폴더)를 추상화 한 클래스이다.

즉, File 클래스를 통하여 파일시스템의 파일이나 디렉터리를 조작(삭제, 파일명변경 등)을 할 수 있다.

 

File 클래스를 통해 할 수 있는 작업이 많은 만큼 제공되는 메소드들도 다양하다. File 클래스의 생성과 케이스별 사용법에 대해 정리한다.

 


File 객체 생성

아래 경로처럼 Test.txt 파일을 생성하고 이 파일의 File 객체를 생성한다.

D:\MyWork\Test.txt

public class File_Practice {
    public static void main(String[] args) {
        String parentPath = "D:/MyWork";
        String filePath = "D:/MyWork/Test.txt";
        String fileNm = "Test.txt";

        // File(File parent, String child) 생성자
        File file1 = new File(parentPath, fileNm);

        // File(String pathname) 생성자
        File file2 = new File(filePath);

        // File(String parent, String child) 생성자
        File parent = new File(parentPath);
        File file3 = new File(parent, fileNm);

        if(file1.exists() && file2.exists() && file3.exists()){
            System.out.println("모두 생성 완료!");
        }
    }
}

file.exist() 는 파일의 존재여부를 검사하는 메소드를 이처럼 File 클래스에서는 파일에 관한 여러 편리한 메소드를 제공하도 있다.

아래부터는 이런 메소드들을 활용하여 어떤 작업을 할 수 있는지 케이스별로 살펴본다.


File 객체 활용

아래 소스코드를 실행하기 위한 디폴트 폴더/파일 구성은 아래와 같습니다.

D:/MyWork (Dir)
D:/MyWork/Test.txt (File)
D:/MyWork/MyDirectory (Dir)

1. 파일 또는 디렉터리의 존재유무 확인 및 기본정보 출력

public class Check_Default_Info {
    public static void main(String[] args) {
        String rootPath = "D:/MyWork";

        File rootDir = new File(rootPath);

        // 파일명
        System.out.println(rootDir.getName()); // MyWork

        // 절대경로
        System.out.println(rootDir.getAbsolutePath()); // D:\MyWork

        // 경로
        System.out.println(rootDir.getPath()); // D:\MyWork

        // 존재여부
        System.out.println(rootDir.exists()); // true

        // 읽기/쓰기/실행 가능여부
        if (rootDir.exists()) { // 존재하면
            System.out.println(rootDir.canRead()); // true
            System.out.println(rootDir.canWrite()); // true
            System.out.println(rootDir.canExecute()); // true
        }
    }
}

 


2. 디렉터리의 내부 내용물(파일, 디렉터리) 출력

디렉터리의 경우 listFiles() 메소드로 내부 파일(디렉터리 포함) 목록을 가져올 수 있으며 for문으로 순회하며 작업을 할 수 있다.

public class Check_DirectoryContents_Info {
    public static void main(String[] args) {
        String rootPath = "D:/MyWork";
        File rootDir = new File(rootPath);

        if(rootDir.isDirectory()){ // 디렉터리면
            File[] contents = rootDir.listFiles(); // 파일목록을 가져온다.
            for(File file : contents){
                System.out.println("파일명 : " + file.getName());
                System.out.println("절대경로 : " + file.getAbsolutePath());
                System.out.println("파일여부 : " + file.isFile());
                System.out.println("------------------------------");
            }
        }
    }
}
// 출력결과
파일명 : MyDirectory
절대경로 : D:\MyWork\MyDirectory
파일여부 : false
------------------------------
파일명 : Test.txt
절대경로 : D:\MyWork\Test.txt
파일여부 : true
------------------------------

3. 디렉터리 및 파일 생성

mkdir() 메소드를 사용하여 디렉터리를 생성한다. mkdirs() 메소드도 제공되며 차이점은 mkdirs()는 필요한 상위의 디렉터리가 없으면 같이 만든다는 것에 있다.

createNewFile() 메소드는 신규 파일을 생성할 수 있다. 

length() 메소드는 파일의 용량을 체크할 수 있으며 신규 파일의 경우 빈 파일이므로 0이 출력된다.

public class Mkdir {
    public static void main(String[] args) {
        String rootPath = "D:/MyWork";
        File rootDir = new File(rootPath);

        // 디렉터리 생성시작
        File newDir = new File(rootDir, "newDir"); // 생성할 디렉터리
        System.out.println("신규 디렉터리 생성여부 : " + newDir.exists()); // false

        boolean isMake = newDir.mkdir(); // 디렉터리 생성
        System.out.println("신규 디렉터리 생성결과 : " + isMake); // true
        System.out.println("신규 디렉터리 생성여부1 : " + newDir.exists()); // true
        System.out.println("신규 디렉터리 생성여부2 : " + newDir.isDirectory()); // true

        // 파일 생성시작
        File newFile = new File(newDir, "newFile");
        System.out.println("신규 파일 생성여부 : " + newFile.exists()); // false

        try {
            newFile.createNewFile(); // 파일생성
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("신규 파일 생성여부 : " + newFile.exists()); // true
        System.out.println("신규 파일 용량 : " + newFile.length()); // 0
        System.out.println("신규 파일 경로 : " + newFile.getAbsolutePath()); // D:\MyWork\newDir\newFile
    }
}

 


4. 파일명 변경 및 삭제

createNewFile()로 새로운 파일 생성 후 delete() 메소드로 삭제한다.

public class Rename_Delete {
    public static void main(String[] args) throws IOException {
        String rootPath = "D:/MyWork";
        File rootDir = new File(rootPath);

        File file = new File(rootDir, "tmpFile");
        file.createNewFile();
        System.out.println("삭제전 파일 존재여부 : " + file.exists()); // true

        // 파일삭제 시작
        boolean isDel = file.delete();
        System.out.println("파일 삭제 여부 : " + isDel); // true
        System.out.println("삭제후 파일 존재여부 : " + file.exists()); // false
    }
}

 


 

Java String reverse (문자열 뒤집기)

자바에서 문자열을 다루며 빈번하게 하는 작업 중 하나가 뒤집기(reverse)이다.

자바에서 문자열을 뒤집을 수 있는 방법 중 몇 가지를 소개한다.

(String 클래스에 reverse() 메서드가 있으면 좋을 텐데 없다.)

 

문자열 뒤집기 예시 :  "abcde" -> "edcba"


1. StringBuilder, StringBuffer 를 이용

reverse() 메소드를 제공하는 StringBuilder/StringBuffer 클래스를 이용한다. 

바꾸고 싶은 문자열을 이용하여 StringBuilder 또는 StringBuffer 객체를 생성 후에 제공되는 reverse() 메서드를 사용한다. 

public class String_Reverse_Practice {
    public static void main(String[] args) {
        String str = "abcde";

        // reverse
        StringBuffer sb = new StringBuffer(str);
        String reversedStr = sb.reverse().toString();

        System.out.println(reversedStr); // edcba
    }
}

StringBuffer에 대한 추가 설명은 기존에 정리한 링크를 남겨둔다.

2020/12/13 - [Dev/JAVA] - StringBuffer(StringBuilder) 클래스 기본 및 사용

 

StringBuffer(StringBuilder) 클래스 기본 및 사용

StringBuffer(StringBuilder) 클래스 기본 및 사용 StringBuffer(StringBuilder) 클래스는 문자열에 대한 많은 편의기능을 제공한다. 그래서 필요에 따라 문자열을 작업할 때 String 객체가 아닌 StringBuffer(Str..

yangbox.tistory.com


2. char[] 로 변환하여 뒤집기

String 문자열을 char배열로 변환 후 역순으로 새로운 char배열에 저장하여 문자열로 변환한다.

좀 돌아가는 느낌이 들지만 참고로 봐둔다.

public class String_Reverse_Practice {
    public static void main(String[] args) {
        String str = "abcde";

        char[] arr = str.toCharArray(); // String -> char[]
        char[] reversedArr = new char[arr.length];
        for(int i=0; i<arr.length; i++){
            reversedArr[arr.length-1-i] = arr[i];
        }

        String reversedStr = new String(reversedArr);
        System.out.println(reversedStr); // edcba
    }
}

 


3. List로 변환 후 Collections.reverse(list) 이용하여 뒤집기

reverse 메소드를 제공하는 Collections 클래스를 이용하기 위해 List<Character> 타입으로 변환 후 뒤집기를 수행한다.

public class String_Reverse_Practice {
    public static void main(String[] args) {
        String str = "abcde";

        char[] arr = str.toCharArray(); // String -> char[]
        List<Character> list = new ArrayList<>();
        for(char each : arr){ // char[] -> List
            list.add(each);
        }

        // reverse
        Collections.reverse(list);

        ListIterator li = list.listIterator();
        while(li.hasNext()){
            System.out.print(li.next()); // edcba
        }
    }
}

 


 

 

StringBuffer(StringBuilder) 클래스 기본 및 사용

StringBuffer(StringBuilder) 클래스는 문자열에 대한 많은 편의기능을 제공한다. 그래서 필요에 따라 문자열을 작업할 때 String 객체가 아닌 StringBuffer(StringBuilder) 객체를 사용경우가 종종 있다.


String과 StringBuffer(StringBuilder)의 차이

String과 StringBuffer(StringBuilder)의 차이점은 일단 String은 고정된 길이의 불변(immutable) 문자열이다.

불변이므로 String 객체를 변경시 실제로 변경되는 것이 아닌 새로운 String 객체를 생성한다. 반면에 StringBuffer와 StringBuilder는 변경이 가능하다.(String 처럼 새로운 객체를 생성하는 것이 아닌 그 객체 자체가 변경된다.)

그러므로 문자열의 변경이 잦은 작업이라면 String 대신 StringBuffer 또는 StringBuilder의 사용을 고려한다.

 

StringBuffer와 StringBuilder의 차이는 멀티스레드 환경에서 thread-safe 여부가 다르다. StringBuffer는 thread-safe 하므로 여러 쓰레드에서 동시에 해당 문자열에 접근한다면 사용을 고려하고, 그렇지 않다면 StringBuilder를 사용하는 것이 성능에 더 유리하다. (성능과 thread-safe는 반비례라고 생각하면 된다.)

 

정리하면

1. 문자열 변경이 빈번하지 않는다면 String 사용을 고려

2. 문자열이 빈번하게 변경되면서 멀티쓰레드 환경이라면 StringBuffer 사용을 고려

3. 문자열이 빈번하게 변경되면서 멀티쓰레드 환경이 아니라면 StringBuilder 사용을 고려


StringBuffer 생성

StringBuffer는 3가지 생성자로 생성할 수 있다. StringBuffer와 StringBuilder는 사용법이 대동소이 하므로 아래부터는 StringBuffer를 대표로 정리한다.

 

1. 기본 생성자

StringBuffer sb = new StringBuffer();

 

2. int size 값을 인자로 하는 생성자 : int 타입의 값으로 buffer의 사이즈(capacity)를 지정한다. 

StringBuffer sb = new StringBuffer(20);

 

3. String 문자열을 인자로 하는 생성자 : 제일 많이 사용하는 생성자

StringBuffer sb = new StringBuffer("Yangs");

StringBuffer 메소드

많이 사용되는 메소드를 정리해본다.

 

1. append() : 문자열을 추가하기 위해 사용한다.

최초 빈 문자열로 생성 후 "Yangs" 문자열이 추가되었다. 이 후 "World"는 "Yangs" 문자열 뒤에 추가되어 "YangsWorld"가 된다.

public class StringBuffer_Practice {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        System.out.println("최초 : " + sb.toString()); // 최초 :

        sb.append("Yangs");
        System.out.println("append 후 : " + sb.toString()); // append 후 : Yangs
        
        sb.append("World");
        System.out.println("append 후 : " + sb.toString()); // append 후 : YangsWorld
    }
}

 

2. length(), capacity() : StringBuffer의 문자열 길이나 전체 할당된 capacity를 구할 때 사용된다. capacity는 StringBuffer 내부의 문자열을 저장하기 위한 버퍼의 총 갯수로 생각하면 된다. 신경쓰지 않아도 문자열의 길이가 늘어나면 같이 증가한다.

최초 설정된 10의 capacity를 넘어서는 문자열이 append되자 자동으로 capacity가 22로 증가했다.

public class StringBuffer_Practice {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer(10); // 최초 capacity는 10으로 설정
        System.out.println("최초 문자열길이 : " + sb.length() + ", 총 capacity : " + sb.capacity()); // 최초 문자열길이 : 0, 총 capacity : 10

        sb.append("12345678"); // append() 메소드로 문자열을 이어붙힌다.
        System.out.println(sb.toString()); // 12345678
        System.out.println("1. 문자열길이 : " + sb.length() + ", 총 capacity : " + sb.capacity()); // 1. 문자열길이 : 8, 총 capacity : 10

        sb.append("901"); // append() 메소드로 문자열을 이어붙힌다.
        System.out.println(sb.toString()); // 12345678901
        System.out.println("2. 문자열길이 : " + sb.length() + ", 총 capacity : " + sb.capacity()); // 2. 문자열길이 : 11, 총 capacity : 22
    }
}

 

3. insert() : 문자열을 맨 뒤에 삽입하는 append()와 달리 시작 인덱스를 지정하여 원하는 위치에 삽입할 수 있다.

public class StringBuffer_Practice {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Yangs"); // 최초 "Yangs"로 생성
        System.out.println(sb.toString()); // Yangs

        sb.append("World"); // 맨 뒤에 추가
        System.out.println(sb.toString()); // YangsWorld

        sb.insert(5, "Happy"); // 5번째 인덱스에 Happy를 추가(기존 'W' 자리였지만 "Happy"가 추가되면서 밀려났다.)
        System.out.println(sb.toString()); // YangsHappyWorld
    }
}

 

4. reverse() : 문자열을 뒤집을 때 사용한다.

public class StringBuffer_Practice {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Yangs"); // 최초 "Yangs"로 생성
        System.out.println(sb.toString()); // Yangs

        System.out.println("뒤집어 보자 : " + sb.reverse()); // sgnaY
    }
}

 

5. delete(), deleteCharAt() : 인덱스를 지정하여 문자열의 일부분을 삭제하거나 한 문자만 삭제할 때 사용한다.

문자열의 인덱스를 0부터 시작함에 유의한다.

deleteCharAt(int start, int end) 에서 start 인덱스부터 (end-1) 인덱스까지가 삭제됨에 유의한다.

public class StringBuffer_Practice {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("YangsWorld"); // 최초 "YangsWorld"로 생성
        System.out.println(sb.toString()); // YangsWorld

        sb.delete(1,3); // "an" 삭제
        System.out.println(sb.toString()); // YgsWorld

        sb.deleteCharAt(3); // 3번째 인덱스인 'W' 삭제
        System.out.println(sb.toString()); // Ygsorld
    }
}

 

6. replace() : 문자열의 일부분을 다른 문자열로 치환할 수 있다.

문자열의 인덱스는 0부터 시작하며, start 인덱스부터 (end-1) 인덱스까지가 치환됨에 유의한다.

public class StringBuffer_Practice {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("YangsWorld"); // 최초 "YangsWorld"로 생성
        System.out.println(sb.toString()); // YangsWorld

        sb.replace(1,5, "ZZYX"); // "angs"를 "ZZYX"로 치환
        System.out.println(sb.toString()); // YZZYXWorld
    }
}

 


 

String 객체에는 다양한 메소드들이 있는데 한번씩 살펴보고자 한다.

 

1. int length() : 스트링 객체의 문자 갯수에 대해 반환한다.

"Hello".length();  // 5

 

2. Char charAt(int i) : i번째 인덱스에 있는 문자를 반환한다. 예시의 1번째 인덱스는 'e' 이다.(인덱스는 0부터 시작)

"Hello".charAt(1); // e

 

3. String substring(int i) : i번째 인덱스의 문자부터 끝까지를 반환한다.

"Hello".substring(3); // lo

 

4. String substring(int i, int j) : i번째 인덱스부터 j-1번째 인덱스를 반환한다. 1번째 인덱스의 문자는 'e'익고 (3-1)번째 문자는 'l'이다.

"Hello".substring(1,3); // el

 

5. String concat(String str) : 인자의 문자열을 끝에 병합하여 새로운 문자열을 반환한다.

"Hello".concat(" World"); // Hello World

 

6. int indexOf(String s) : 해당문자열이 첫번째로 나타는 인덱스를 반환한다. "Hello"가 처음 나타는 인덱스는 "Hi " 다음 3번째 인덱스이다. 뒤에 한번 더 나타나는 "Hello"는 무시된다.

"Hi Hello World Hello".indexOf("Hello"); // 3

 

7. int indexOf(String s, int i) : 6번과 유사하나 탐색하는 시작 인덱스를 지정한다. 4번째 인덱스부터 찾으므로 뒤에 "Hello"가 시작하는 인덱스인 15를 반환한다. 

"Hi Hello World Hello".indexOf("Hello", 4); // 15

 

8. int lastIndexOf(String s) : 인자의 문자열이 마지막으로 나타나는 인덱스를 반환한다. 마지막으로 나타나느 "Hello"의 시작 인덱스인 15가 반환된다.

"Hi Hello World Hello".lastIndexOf("Hello"); // 15

 

9. boolean equals(Object otherObj) : 인자의 스트링 객체와 동일한지 검증한다. 대소문자를 구분하여 검증한다.

"Hello".equals("hello"); // false

 

10. boolean equalsIgnoreCase(String anotherString) : 9번과 유사하나 대소문자를 구분하지 않고 검증한다.

"Hello".equalsIgnoreCase("hello"); // true

 

11. int compareTo(String anotherString) : 인자의 스트링과 사전상으로 비교한다. 사전상으로 비교한다는 의미는 abc.. 나 ㄱㄴㄷ.. 순으로 오름차순 비교하여 더 앞쪽이면 음수, 같으면 0, 뒷쪽이면 양수가 반환된다.

"Hello" <--> "Zello" 에서는 "H"가 "Z"보다 사전상으로 앞쪽에 위치하므로 음수가 반환됐다.

아래 예시에서는 'H'와 'Z' 또는 'A'와의 차이만큼 정수가 반환되지만 보통 (음수, 0, 양수) 이 정도로만 체크한다.

"Hello".compareTo("Zello"); // -18
"Hello".compareTo("Aello"); // 7
"Hello".compareTo("Hello"); // 0

 

12. int compareToIgnoreCase( String anotherString) : 11번과 유사하지만 비교시 대소문자를 구분하지 않고 비교한다.

"Hello".compareToIgnoreCase("hEllO"); // 0

 

13. String toLowerCase() : 스트링 객체를 소문자로 변경한 새로운 스트링 객체를 반환한다.

"HellO".toLowerCase(); // hello

 

14. String toUpperCase() : 스트링 객체를 대문자로 변경한 새로운 스트링 객체를 반환한다.

"HellO".toUpperCase(); // HELLO

 

16. String replace(char oldChar, char newChar) : 첫번째 인자의 문자를 찾아 두번째 인자로 치환한다.

"Hello".replace('l', 'z'); // hezzo

 


 

 

 

자바에서 String 객체는 불변(immutable)이다.

불변이라는 의미는 한번 생성되면 변경되지 않는다는 것을 말한다. 즉, String 문자열을 조작(서로 합치거나 나누거나)할 때마다 기존 객체가 변경되는 것처럼 보이지만 사실은 계속 새로 생성되는 것이다.

 

String 객체 생성 

1. 일반적인 스트링의 리터럴 생성

String s = "HI";

2. new 키워드를 이용한 생성

String s = new String("HI");

당연히 코드가 많아지는 2번보다 1번이 많이 사용된다.

 

String 생성자

1. String(byte[] byte_arr) : byte 배열을 이용(디코딩)하여 새로운 스트링 객체를 생생한다. 디코딩에는 디폴트 캐릭터셋이 사용된다.

System.out.println("사용되는 기본 캐릭터셋 : " + Charset.defaultCharset().name()); // UTF-8

byte[] byte_arr = {72, 101, 108, 108, 111}; // 스트링 생성
String str = new String(byte_arr);

System.out.println("생성된 스트링 : " + str); // Hello

 

2. String(byte[] byte_arr, Charset char_set) : 1번과 유사하나 디폴트 캐릭터셋이 아니라 인자로 캐릭터셋을 지정할 수 있다.

byte[] byte_arr = {72, 101, 108, 108, 111};
String str = new String(byte_arr, Charset.forName("UTF-8"));
System.out.println("생성된 스트링 : " + str); // Hello

 

3. String(byte[] byte_arr, String char_set_name) : 3번과 유사하게 캐릭터셋을 지정하지만 Charset 객체가 아닌 스트링으로 캐릭터셋을 지정한다.

byte[] byte_arr = {72, 101, 108, 108, 111};
String str = new String(byte_arr, "UTF-8");
System.out.println("생성된 스트링 : " + str); // Hello

 

4. String(byte[] byte_arr, int start_index, int length) :  인자의 바이트 배열중 일부분을 인덱스로 지정하여 스트링 객체를 생성한다.

byte[] byte_arr = {72, 101, 108, 108, 111};
String str = new String(byte_arr, 1, 3);
System.out.println("생성된 스트링 : " + str); // ell

 

5. String(byte[] byte_arr, int start_index, int length, Charset char_set) : 4번과 동일하나 Charset객체만 인자로 추가됐다.

byte[] byte_arr = {72, 101, 108, 108, 111};
String str = new String(byte_arr, 1, 3, Charset.forName("UTF-8"));
System.out.println("생성된 스트링 : " + str); // ell

 

6. String(byte[] byte_arr, int start_index, int length, String char_set_name) : 4번과 동일하나 캐릭터셋을 String 객체로 추가하여 지정한다.

byte[] byte_arr = {72, 101, 108, 108, 111};
String str = new String(byte_arr, 1, 3, "UTF-8");
System.out.println("생성된 스트링 : " + str); // ell

 

7. String(char[] char_arr) : 캐릭터 배열을 인자로 스트링 객체를 생성한다.

char char_arr[] = {'H', 'e', 'l', 'l', 'o'};
String str = new String(char_arr);
System.out.println("생성된 스트링 : " + str); // Hello

 

8. String(char[] char_array, int start_index, int count) : 7번과 유사하나 배열의 일부분을 지정한다.

char char_arr[] = {'H', 'e', 'l', 'l', 'o'};
String str = new String(char_arr, 1, 3);
System.out.println("생성된 스트링 : " + str); // ell

 

9. String(StringBuffer s_buffer) : StringBuffer 객체를 인자로 스트링 객체를 생성한다.

StringBuffer s_buffer = new StringBuffer("Hello");
String str = new String(s_buffer);
System.out.println("생성된 스트링 : " + str); // Hello

 

10. String(StringBuilder s_builder) : StringBuilder 객체를 인자로 스트링 객체를 생성한다.

StringBuilder s_builder = new StringBuilder("Hello");
String str = new String(s_builder);
System.out.println("생성된 스트링 : " + str); // Hello

 


읽어주셔서 감사합니다. 도움이 되셨다면 광고 클릭 부탁드립니다.

모두 힘내세요! : )

 

 

 

 

자바에서 콘솔로부터 값을 읽을 수 있는 3가지 방법을 소개하겠습니다.

 

각 방법마다 장단점이 있지만 가장 많이 사용되는 방법은 Scanner 클래스를 이용하는 방법입니다.

 

1. Scanner Class

가장 많이 사용되는 방법으로 Primitive 값들을 읽을 수 있는 편리한 메서드들을 제공합니다.(nextInt(), nextFloat() 등)

public class ConsoleInput_Scanner {
    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in); // Scanner 객체 선언

        String s = in.nextLine(); // 콘솔 문자열 입력 받기
        System.out.println("You entered string : "+s);

        int a = in.nextInt(); // 콘솔 int 입력 받기
        System.out.println("You entered integer : "+a);

        float b = in.nextFloat(); // 콘솔 float 입력 받기
        System.out.println("You entered float "+b);
    }
}

 

// 입력
하이
1
1.5
// 출력
You entered string : 하이
You entered integer : 1
You entered float 1.5

2. BufferedReader Class

JDK1.1 부터 있었던 BufferedReader를 이용할 수도 있습니다. Scanner와 같이 표준 입력 스트림인 System.in을 사용하며, InputStreamReader를 이용하여 선언해야 해서 읽기에 좀 불편할 수도 있습니다.

public class ConsoleInput_BufferedReader {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String input = reader.readLine();
        System.out.println("input : " + input);
    }
}

 

// 입력
하이
// 출력
input : 하이

3. Console Class

IDE 환경에서는 동작하지 않습니다. 터미널 환경에서 컴파일 후 class 파일을 실행하면 결과를 확인할 수 있습니다. 

public class ConsoleInput_Console {
    public static void main(String[] args) throws IOException {
        String name = System.console().readLine();
        System.out.println("input : " + name);
    }
}

 

// 입력
하이
// 출력
input : 하이

 

 


 

 

 

 

+ Recent posts