본문 바로가기

학원/복기

0417 - StringBuffer 클래스,Wrapper 클래스, Integer 클래스 / 스레드, 다중스레드, 스레드 동기화

StringBuffer 클래스 

• StringBuffer 클래스 : 문자열을 저장하기 위한 클래스

• String 클래스와의 차이점 

: StringBuffer 클래스는 StringBuffer 객체에 저장된 문자열들을 직접 조작하기 위한 관련 메소드를 제공하고,
  String 객체는 저장된 문자열을 조작하기 위한 메소드를 제공하지 않고 조작결과를 반환하는 메소드를 제공한다. 

→ String 클래스보다 StringBuffer 클래스의 메소드가 문자열 처리 속도가 우수

 

문자열을 조작할 땐 StringBuffer가 가독성이 훨씬 좋다. (데이터 처리 할 때 등)
처리 끝난 후, 출력할 때 toString메소드 이용해 String으로 바꿔서 쓴다. 

 

 

StirngBuffer 클래스는 new 연산자로 생성자를 호출하여 객체를 생성해야 한다.

public class StringBufferApp {
	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer("ABC");
	}
}

 

• StringBuffer.toString() : StringBuffer 객체에 저장된 문자열을 반환하는 메소드 

public class StringBufferApp {
	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer("ABC");
       
        	System.out.println("sb.toString() = " + sb.toString()); //sb.toString() = ABC
		
        	//StringBuffer 객체가 저장된 참조변수를 출력할 경우 toString 메소드 자동 호출
		System.out.println("sb = " + sb); //sb = ABC
        
        	//StringBuffer 객체에 저장된 문자열을 반환받아 String 객체로 생성하여 참조변수 저장 
		String str = sb.toString(); //StringBuffer 객체 >>  String 객체로 바꿔진다
		System.out.println("str = " + str); //str = ABC
	}
}

 


• StringBuffer.append(Object o) : StringBuffer 객체에 저장된 문자열에 매개변수로 전달받은 값을 추가하는 메소드 
→  String 객체에 저장된 문자열에 += 연산자를 사용한 효과와 동일 


• StringBuffer.insert(int index, Object o) 

: StringBuffer 객체에 저장된 문자열에 매개변수로 전달받은 문자열을  원하는 위치(첨자)에 삽입하는 메소드 

 

• StringBuffer.deleteCharAt(int index, Object o) 

: StringBuffer 객체에 저장된 문자열에서  매개변수로 전달받은 위치(첨자)의 문자를 제거하는 메소드 (문자 하나만 제거) 

 

• StringBuffer.delete(int beginIndex, int endIndex)

 : StringBuffer 객체에 저장된 문자열에서  매개변수로 전달받은 시작첨자부터 종료첨자 범위의 문자열을 제거하는 메소드 

 

 

• StringBuffer.reverse() : StringBuffer 객체에 저장된 문자열의 문자를 역순으로 나열되도록 저장하는 메소드 

 

public class StringBufferApp {
	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer("ABC");
        
       		//StringBuffer.append(Object o) : StringBuffer 객체에 저장된 문자열에 매개변수로 
		//전달받은 값을 추가하는 메소드 
        	sb.append("DEF");
		System.out.println("sb = " + sb); //sb = ABCDEF
        
       	 	str+="DEF";
		System.out.println("str = " + str); //str = ABCDEF
        
        	//StringBuffer.insert(int index, Object o) : StringBuffer 객체에 저장된 문자열에
		//매개변수로 전달받은 문자열을 원하는 위치(첨자)에 삽입하는 메소드 
		sb.insert(4, "X"); 
		System.out.println("sb = " +sb); //sb = ABCDXEF
        
        	//StringBuffer.deleteCharAt(int index, Object o) : StringBuffer 객체에 저장된 문자열
		//에서 매개변수로 전달받은 위치(첨자)의 문자를 제거하는 메소드 (문자 하나만 제거) 
        	sb.deleteCharAt(2); 
		System.out.println("sb = " +sb); //sb = ABDXEF
        
        	//StringBuffer.delete(int beginIndex, int endIndex) : StringBuffer 객체에 저장된 
		//문자열 에서 매개변수로 전달받은 시작첨자부터 종료첨자 범위의 문자열을 제거하는 메소드 
       		sb.delete(4, 6);
		System.out.println("sb = " +sb); //sb = ABDX
        
        	//StringBuffer.reverse() : StringBuffer 객체에 저장된 문자열의 문자를 역순으로 
		//나열되도록 저장하는 메소드 
		sb.reverse();
		System.out.println("sb = " +sb); //sb = XDBA
	}
}

 


Wrapper 클래스

• Wrapper 클래스

 : 원시형을 클래스로 선언한 자료형을 일괄적으로 표현하기 위한 이름 
 → Byte, Short, Integer, Long, Float, Double, Character, Boolean 
 → 원시형 대신 객체로 표현하기 위해 사용하는 클래스 

• Integer 클래스

 : 정수값(int)을 저장하기 위한 메소드  

• Integer.valueOf(Object i) : 매개변수로 값을 전달받아 정수값이 저장된 Integer 객체를 반환하는 메소드  
• Integer.intValue() : Integer 객체에 저장된 정수값을 반환하는 메소드
Integer num1 = Integer.valueOf(100);
Integer num2 = Integer.valueOf("200"); //문자열을 전달받아도 내부적으로 정수값으로 저장
		
Integer num3 = Integer.valueOf(num1.intValue()+num2.intValue());
System.out.println("num3 = " + num3.intValue()); //num3 = 300

=> 너무 불편한 방법임 

 

•  Wrapper 클래스는 오토박싱과 오토언박싱 기능을 제공받아 객체를 생성하거나 사용이 가능하다 

-오토박싱(AutoBoxing) : 원시형 리터럴(값)을 자동으로 Wrapper 클래스의 객체로 생성하여 반환하는 기능  
-오토언박싱(AutoUnBoxing) : JVM이 Wrapper 클래스의 객체에 저장된 값을 원시형 리터럴로 반환하는 기능  

 

public class WrapperApp {	
	public static void main(String[] args) {
		Integer num1 = 100, num2 = 200;  //오토박싱
		Integer num3 = num1 + num2; //오토언박싱 
		System.out.println("num3 = " + num3); //num3 = 300
	}
}

 

•  Integer.valueOf(String s, int radix) 

: 매개변수로 전달받은 값으로 원하는 진수의 문자열을 정수값으로 변환해 Integer 객체에 저장하여 반환하는 메소드 

(radix : 진수)
 → 매개변수로 진수가 전달되지 않을 경우 문자열을 10진수로 인식하여 처리 
 → 정수값으로 변환되지 못하는 문자열이 전달된 경우 NumberFormatException 발생 

public class WrapperApp {
	public static void main(String[] args) {
		Integer num = Integer.valueOf("ABC" , 16); //int num = 0xABC; 와 같다 
		//Integer 객체에 저장된 정수값을 반환받아 10진수로 출력 
		//(저장은 16진수로 되어있어도 출력은 무조건 10진수로 출력)
		System.out.println("num = " + num); //num = 2748
	}
}


*10진수가 아닌 다른 형태의 문자열로 출력하고 싶을 때 

• Integer.toOctalString(int i) : 매개변수로 전달된 정수값을 8진수 형태의 문자열로 변환하여 반환하는 메소드 
• Integer.toHexString(int i) : 매개변수로 전달된 정수값을 16진수 형태의 문자열로 변환하여 반환하는 메소드 
 Integer.toBinaryString(int i) : 매개변수로 전달된 정수값을 2진수 형태의 문자열로 변환하여 반환하는 메소드 

 

public class WrapperApp {
	public static void main(String[] args) {
        Integer su = 50;

        System.out.println("su(10진수) = " + su); //su(10진수) = 50

        //Integer.toOctalString(int i) : 매개변수로 전달된 정수값을 8진수 형태의 문자열로 변환하여 반환하는 메소드 
        System.out.println("su(8진수) = " + Integer.toOctalString(su)); //su(8진수) = 62
        
        //Integer.toHexString(int i) : 매개변수로 전달된 정수값을 16진수 형태의 문자열로 변환하여 반환하는 메소드 
        System.out.println("su(16진수) = " + Integer.toHexString(su)); //su(16진수) = 32
        
        //Integer.toBinaryString(int i) : 매개변수로 전달된 정수값을 2진수 형태의 문자열로 변환하여 반환하는 메소드 
        System.out.println("su(2진수) = " + Integer.toBinaryString(su)); //su(2진수) = 110010

        su = -50;
        System.out.println("su(2진수) = " + Integer.toBinaryString(su)); //su(2진수) = 11111111111111111111111111001110
	}
}

 

 

• ★ Integer.parseInt(String s) : 매개변수로 문자열을 전달받아 정수값으로 변환하여 반환하는 메소드 

  정수값으로 변환되지 못하는 문자열이 전달된 경우 NumberFormatException 발생 

public class WrapperApp {
	public static void main(String[] args) {
    	String str1 = "100", str2 = "200";
		//문자열 결합 - 문자열에서 더하기는 결합
		System.out.println("합계 = " + str1 + str2); //합계 = 100200
		
		//Integer.parseInt(String s) : 매개변수로 문자열을 전달받아 정수값으로 변환하여 반환하는 메소드 
		System.out.println("합계 = " + (Integer.parseInt(str1) +  Integer.parseInt(str2))); //합계 = 300	
    }
}

 


스레드(Thread)

• 스레드(Thread) 

: 프로세스의 명령을 실행하기 위한 최소의 작업단위 - 프로그램의 흐름 

 

• 프로세스(Process) : 메모리에 저장된 프로그램 -  중앙처리장치(CPU)에 의해 실행되기 위한 명령 

프로세스는 실행 중인 프로그램을 의미한다. 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행중인 것을 말한다.  프로세스는 자원(메모리,CPU..)과 스레드로 구성되어 있으며, 모든 프로세스는 최소한 하나의 스레드를 가지고 있다.

 

• 단일 스레드 프로그램

→ JVM에 의해 생성된 main 스레드를 이용하여 main() 메소드를 호출하여 명령 실행 
→ main() 메소드가 종료되면 main 스레드는 자동 소멸 - 프로그램 종료 

-Thread 클래스 : 스레드 관련 정보를 저장하고 스레드 관련 제어 기능을 메소드로 제공하기 위한 클래스 
-Thread.currentThread() : 현재 명령을 읽어 처리하는 스레드에 대한 Thread 객체를 반환하는 메소드 
-Thread.getName() : Thread 객체에 저장된 스레드의 이름(고유값)을 반환하는 메소드

 

단일 스레드 프로그램 흐름 예시) 

package xyz.itwill.lang.thread;

public class SingleThread {
	public void display() {
		System.out.println("SingleThread 클래스의 display() 메소드 시작");
		System.out.println("[" + Thread.currentThread().getName() + "] 스레드에 의해 "
				+ "SingleThread 클래스의 display() 메소드의 명령 실행");
		System.out.println("SingleThread 클래스의 display() 메소드 종료");
	}
}

package xyz.itwill.lang.thread;

public class SingleThreadApp {
	public static void main(String[] args) {
		System.out.println("SingleThreadApp 클래스의 main 메소드 시작");
		System.out.println("[" + Thread.currentThread().getName() 
				+ "] 스레드에 의해 SingleThreadApp 클래스의 main() 메소드의 명령 실행");
		
        	//객체를 생성하여 메소드를 호출한 경우 스레드가 메소드로 이동하여 메소드의 명령 실행 
		// => 메소드의 명령을 모두 실행한 후 다시 현재 위치로 되돌아와 나머지 명령 실행 
		new SingleThread().display(); // 객체를 생성하여 직접 메소드 호출
		
		System.out.println("SingleThreadApp 클래스의 main 메소드 종료");
	} 
}

 

출력결과

 

package xyz.itwill.lang.thread;

public class SingleThread {
	public void display() {
	for(char i = 'a'; i<= 'z'; i++) {
			System.out.print(i); 
		}
	}
}

package xyz.itwill.lang.thread;

public class SingleThreadApp {
	public static void main(String[] args) {
		for(char i = 'A'; i<= 'Z'; i++) {
			System.out.print(i); 
		}
		//단일 스레드이기 때문에 대문자 출력하는 명령이 모두 실행된 후 소문자를 출력하는 명령이 실행된다.
		new SingleThread().display(); 
        	//ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
	} 
}

 

 

• 다중 스레드 프로그램

→ 프로그램 개발자가 직접 스레드를 생성하여 다수의 명령을 동시에 실행되도록(실행되는것처럼) 만든 프로그램 
→ 프로그램의 모든 스레드가 소멸돼야만 프로그램이 종료된다.
→ GUI 프로그램 및 Web 프로그램 등은 다중 스레드 프로그램으로 작성된다.

• 프로그램 개발자가 스레드를 생성하여 명령을 실행하는 방법 

1) Thread 클래스 이용 

1. Thread 클래스를 상속받은 자식클래스 작성  
2. Thread 클래스를 상속받은 자식클래스에서 run() 메소드를 오버라이드 선언 
 → run() 메소드에는 프로그램 개발자에 의해 생성된 스레드가 실행하기 위한 명령을 작성  
3. Thread 클래스를 상속받은 자식클래스로 객체를 생성 - Thread(부모클래스) 객체가 생성된다. 
4. Thread 객체로 start() 메소드 호출 - Thread 객체를 이용하여 새로운 스레드 생성  
 → 생성된 스레드는 자동으로 Thread 객체의 run() 메소드를 호출하여 run() 메소드의 명령 실행

 

2) Runnable 인터페이스 이용 

→ 클래스가 이미 다른 클래스를 상속받아 Thread 클래스를 상속받지 못하는 경우 Runnable 인터페이스를 상속받아 
새로운 스레드를 생성하기 위한 방법 (다중상속의 문제 때문)

1. Runnable 인터페이스를 상속받은 자식클래스 작성 
2. Runnable 인터페이스를 상속받은 자식클래스에서 run() 메소드를 오버라이드 선언 
 → run() 메소드에는 프로그램 개발자에 의해 생성된 스레드가 실행하기 위한 명령 작성 
3. Thread 클래스로 Thread 객체를 생성 - Thread 클래스의 생성자 중 매개변수에 Runnable 인터페이스를 상속받은 
   자식클래스의 객체를 전달하여 Thread 객체를 생성하는 생성자 이용 
4. Thread 객체로 start() 메소드 호출

 

• Thread.start() / Thread.sleep(long ms)

• Thread.start() : Thread 객체로 새로운 스레드를 생성하기 위한 메소드 
 => 생성된 스레드는 자동으로 Thread 객체의 run() 메소드(숨겨진 메소드)를 호출
 => 자식클래스의 오버라이드 선언된 run() 메소드가 호출되어 명령 실행
 
•Thread.sleep(long ms) : 매개변수로 전달된 시간(ms)동안 스레드를 일시중지하는 메소드 
 => 메소드에서 InterruptedException 전달 - 일반 예외이므로 예외처리를 하지 않으면 에러 발생 
 => main 메소드에 의해 전달된 예외는 JVM에 의해 자동으로 예외 처리

 

package xyz.itwill.lang.thread;
//Thread 클래스 상속받는 자식클래스 MultiThreadOne 선언
public class MultiThreadOne extends Thread{  
	//run() 메소드를 오버라이드 선언 
	@Override
	public void run() { //프로그램 개발자에 의해 생성된 스레드가 실행하기 위한 명령을 작성  
		for(char i = 'a'; i<= 'z'; i++) {
                    System.out.print(i); 

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                    e.printStackTrace();
                } 
		}
	}
}

package xyz.itwill.lang.thread;
//Runnable 인터페이스 상속받는 자식클래스 MultiThreadTwo 선언 
public class MultiThreadTwo implements Runnable { 
	//run() 메소드를 오버라이드 선언 
	@Override
	public void run() { //프로그램 개발자에 의해 생성된 스레드가 실행하기 위한 명령을 작성  
		for(char i = 'a'; i<= 'z'; i++) {
		System.out.print(i); 
			
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
		}
	}
}

package xyz.itwill.lang.thread;
public class MultiThreadApp {
	//JVM에 의해 main 스레드가 자동으로 생성되어 main() 메소드를 호출해 main() 메소드에 작성된 명령 실행
	// => main 메소드에 의해 전달된 예외는 JVM에 의해 자동으로 예외 처리 
	public static void main(String[] args) throws InterruptedException {
    
    	/*
		MultiThreadOne one = new MultiThreadOne();
        
		one.start();
		//하나의 Thread 객체는 하나의 스레드만 생성 가능 
		// => Thread 객체를 사용하여 start 메소드를 여러번 호출할 경우 IllegalThreadStateException 예외 발생
		// => 다중 스레드 프로그램에서 예외가 발생된 경우 예외가 발생된 스레드만 소멸 
		one.start(); //예외가 발생되면 main 스레드만 소멸
        */
        
		//Thread 객체를 사용하여 start() 메소드외에 다른 메소드를 호출하지 않을 경우 참조변수에 
		//객체를 저장하지 않고 객체를 생성하여 직접 메소드 호출 (더 효율적) 
		new MultiThreadOne().start();
		new MultiThreadOne().start();  
		
        	//Thread 클래스로 Thread 객체를 생성
		new Thread(new MultiThreadTwo()).start(); //Thread 객체로 start() 메소드 호출

		for(char i = 'A'; i<= 'Z'; i++) {
			System.out.print(i);
			
			//CPU가 읽는 속도에 따라 결과가 달라진다
            
			Thread.sleep(500); //0.5초동안 명령 중지 
		}
		
		//총 4개의 스레드 존재
		
		//대문자 출력 후 멈추고, 소문자 출력 후 멈추고...
		//출력 예시: aaAabBbbccCcddDdeEeeffFfggGghhHhiiIijjJjkkKkllLlmmMmnnNnooOoppPpqqQqrrRrssSsttTtuuUuvvVvwwWwxxXxyyYyzzZz
		// => 대문자와 소문자가 함께 출력 
		
		//main 스레드와 새로운 스레드 중 CPU에게 먼저 전달되는 명령이 출력되는 것이다. 
		//스레드가 명령을 읽어들이는 것이 뭐냐에 따라 달라진다! 
	}
}

 

• 다중 스레드 프로그램의 문제점

→ 다수의 스레드가 run() 메소드의 명령을 실행할 경우 같은 객체의 메소드를 호출하여 필드값(공유값)을 변경할 경우 
잘못된 처리결과가 발생할 수 있다. 

해결법) 스레드 동기화를 이용하여 스레드에 대한 메소드 호출 제어

 

• 스레드 동기화(Thread Synchronize)

  : 스레드에 의해 메소드 호출시 메소드의 모든 명령을 실행하기 전까지 다른 스레드로 메소드를 호출하지 못하도록  방지하는 기능 
→ 다른 스레드가 이미 메소드를 호출하여 실행중인 경우 스레드를 일시 중지하여 기존 스레드의 메소드 처리가  끝난 후 일시 중지된 스레드가 실행되도록 락(Lock)기능 제공 

• 스레드 동기화 처리방법

1. synchronized 키워드 사용하여 메소드 선언 - 동기화 메소드 (Synchronized Method)

형식) 접근제한자 synchronized 반환형 메소드명(자료형 매개변수,...) { 명령; ... }

 

만약 메소드를 고칠 수 없는 경우, 2번째 방법 사용

 

2. synchronized 키워드로 블럭을 설정하여 메소드 호출 

형식) synchronized(객체) { 객체.메소드명(값,...); ... }

→ synchronized 영역의 객체로 호출되는 모든 메소드는 동기화 처리되어 실행

 

package xyz.itwill.lang.thread;

//은행계좌정보(잔액)를 저장하기 위한 클래스 - 입금 및 출력 관련 메소드 작성 
public class Account {
	private int balance; //잔액 
	
	public Account() {
		// TODO Auto-generated constructor stub
	}

	public Account(int balance) {
		super();
		this.balance = balance;
	}

	public int getBalance() {
		return balance;
	}

	public void setBalance(int balance) {
		this.balance = balance;
	}
	
	//입금 처리 메소드 - 매개변수로 입금자와 입금액을 전달받아 처리 
	public synchronized void deposit(String name,int amount) { //동기화 메소드 - synchronized 키워드 사용하여 메소드 선언
		balance += amount;
		System.out.println("[입금]" + name + "님이" + amount + "원 입금하여 잔액은 " + balance + "원입니다.");
	}
	
	//출금 처리 메소드 - 매개변수로 출금자와 입금액을 전달받아 처리 
	public void withDraw(String name,int amount) {  
		if(balance < amount) {
			System.out.println("[에러]" + name + "님, 잔액이 " + balance + "원 남아 " + amount + "원을 출금할 수 없습니다.");
			return; //return 반드시 작성해야함 
		}
		balance -= amount;
		System.out.println("[출금]" + name + "님에게" + amount + "원 출금되어 잔액은 " + balance + "원입니다.");
	}
	
}

package xyz.itwill.lang.thread;

//은행계좌 사용자정보(은행계좌, 사용자명)를 저장하기 위한 클래스 
public class AccountUser extends Thread {
	private Account account; //은행계좌정보 - 포함관계 
	private String userName; //사용자명
	
	public AccountUser() {
		// TODO Auto-generated constructor stub
	}

	public AccountUser(Account account, String userName) {
		super();
		this.account = account;
		this.userName = userName;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}
	
	@Override
	public void run() {
		//프로그램 개발자에 의해 생성된 스레드로 run() 메소드를 호출하여 명령 실행 
		// => 은행계좌 사용자가 사용하는 은행계좌에 입금 처리하는 메소드 호출 
		//account.deposit(userName, 5000);
		
		synchronized (account) { //2번째 스레드 동기화 방법-synchronized 키워드로 블럭을 설정하여 메소드 호출
		account.withDraw(userName, 5000);
		}
	}

}

package xyz.itwill.lang.thread;

public class AccountUserApp {
	public static void main(String[] args) {
		//Account 클래스로 객체를 생성하여 저장 - 은행계좌 생성
		Account account = new Account(10000); //잔액 : 10000원 
        
            	//스레드를 여러 개 생성하여 동시에 입금(출금) 처리 
		// => 새로운 스레드가 생성되어 run() 메소드의 명령 실행  
		new AccountUser(account,"홍길동").start(); 
		new AccountUser(account,"임꺽정").start();
		new AccountUser(account,"전우치").start();
        
      }
}

 

출력결과