제너릭은 모든 어떤 타입의 자료형을 사용하게 될지 불분명할 때 또는 자료형을 미리 지정하지 않고 사용하기 위해

JAVA5 부터 등장했다.

 

제너릭은 컬렉션, 람다식 등 수 많은 곳에서 사용할 수 있고 자료형을 미리 지정하지 않기에 많은 장점을 갖는다.

 

1. 제너릭의 장점

1). 폭 넓은 사용 범위

제너릭은 자료형을 미리 지정하지 않을 수 있어, 자료형을 사용하는 대부분의 위치는 제너릭으로 대체가 가능하다.

자료형을 사용하는 위치는 클래스, 인터페이스, 메소드 파라미터 등을 말한다.

 

2) 강력한 컴파일 진행

자바 컴파일러는 코드상에서 잘못 사용된 타입의 사용으로 발생하는 에러(버그)에 대해 강하게 컴파일을 진행한다.

따라서 프로그램을 구현하는데 자료형 사용에 대한 안정성이 높아진다.

 

3) 형변환(Casting)이 필요없다.

형변환의 과정은 처리속도를 저하시킨다. 하지만 제너릭을 사용하면 형변환이 필요없기에 처리 속도가 향상된다. 또한 형변환으로 인해 발생하는 런타임 에러를 사전에 예방이 가능하다.

 

2. 제너릭 클래스(인터페이스)

제너릭 타입을 파라미터로 가지는 클래스와 인터페이스를 의미한다. 타입이란 < > 사이에 위치한 파라미터이다.

public class ClassName <T>
interface InterfaceName <T>
 

코드에서 <T> 가 타입 파라미터이며 제너릭을 사용한 클래스 또는 인터페이스임을 나타낸다. 이렇게 제너릭으로

선언된 클래스는 어떤 자료형이 들어오면 자동으로 들어온 자료에 맞는 자료형으로 변환된다.

 

<제너릭을 사용한 Box 클래스 선언>

public class Box <T> {
	private T tdata;	// T : Type Parameter

	public T getTdata() {
		return tdata;
	}

	public void setTdata(T tdata) {
		this.tdata = tdata;
	}
	
}
 

먼저 Box<T> 클래스를 보면 제너릭을 사용한 클래스라는 것을 볼 수 있다. 또한 어떤 자료형인지는 모르지만

T 라는 단어가 각 메소드에서 자료형을 들어갈 자리를 차지한 것을 볼 수 있다.

이제 main 을 살펴보자.

 

<Box 클래스를 사용하는 main 클래스>

public class CFClass {

	public static void main(String[] args) {
		GenericMethod();
  }
        public static void GenericMethod() {
		
		 Box<Integer> b1 = new Box<Integer>();
		 b1.setTdata(10);
		 System.out.println("<Integer> : " + b1.getTdata());
		 
		 Box<Float>b2 = new Box<Float>();
		 b2.setTdata(3.14f);
		 System.out.println("<Float> : " + b2.getTdata());
	}
}
 

 

main 에서 GenericMethod() 를 보면 Box<Integer> 또는 Box<Float> 가 들어간 것을 볼 수 있다.

이제 실행 결과를 보면 정상적으로 입력해준 정수와 실수가 출력된 것을 볼 수 있다.

이는 <T> 가 때로는 정수형 자료형이 되기도 하며 실수형 자료형이 되기도 한다는 것을 알 수 있다.

즉, T는 '어떤' 자료형에 대한 대표 이름이며 제너릭 클래스의 인스턴스를 만들 때 정확한 형태가 정해지는 것이다.

 

3. 제너릭 타입 파라미터 명명 규칙

타입 명명 규칙은 일반적으로 변수를 만드는 방법과 같은 규칙을 갖는다. 하지만 암묵적으로 자주 사용하는 이름은

다음과 같다.

1) E : element (컬렉션 프레임워크에서 사용)

2) K: Key(데이터베이스 키값 등에서 사용)

3) N: Number

4) T: Type (자료형)

5) V: Value (값)

 

'JAVA' 카테고리의 다른 글

BufferedInputStream  (0) 2022.08.28
BigDecimal  (0) 2022.08.28
Wrapper 클래스  (0) 2022.08.28
StringBuffer  (0) 2022.08.28
String 클래스 문자열 메소드  (0) 2022.08.28

 

Wrapper 클래스란 기본 자료형을 클래스로 정의한 것을 말한다.

즉, 흔히 써온 int, char, boolean 등을 클래스로 정의한 것이다.

 

1. wrapper 클래스가 필요한 이유

만약 어떤 객체를 관리하기 위해 ArrayList 를 만들어 담는다고 생각해보자.

그럼 아래와 같이 코드를 작성하게 될 것이다.

ArrayList<Userclass> user = new ArrayList<>();
 

만약 리스트에 담을 데이터가 1,2,3 ~ 과 같은 일반 정수형 데이터이면 어떻게 해야 할까?

단순히 생각해 <Userclass> 대신에 <int> 로 바꾸면 아래처럼 빨간 줄이 그어진다.

.

이러한 문제를 막기 위해서는 기본 자료형을 클래스(인스턴스)처럼 사용이 가능하여야 한다.

따라서 기본 자료형을 클래스로 만들어 사용하는 이유는 인스턴스를 만들기 위해서 이다.

 

또는 형변환에서도 사용이 가능하다.

              	String ss = "2000"; 
		int d = Integer.parseInt(ss);
		
		System.out.println(ss + 1);
		System.out.println(d + 1);
 

위의 코드를 보면 Integer 클래스의 메소드인 parseInt() 를 통해 문자열을 정수형으로 변환하는 것을 볼 수 있다.

정말 변환된 것인가를 확인하기 위해 출력에서 +1 을 진행하였다.

실행 결과를 보면 문자열 ss + 1은 문자열 뒤에 1이 추가된 20001 이며 정수 d+1 은 2000+1 = 2001 로

결과가 다른 것을 확인할 수 있다.

 

2. wrapper 클래스

Wrapper 클래스라고 부르는 클래스는 기본 자료형에 대한 클래스를 포괄적으로 말하는 것이다.

여기서 기본 자료형에 대한 클래스는 다음과 같다.

  • 0열 선택0열 다음에 열 추가
  • 1열 선택1열 다음에 열 추가
  • 0행 선택0행 다음에 행 추가
  • 1행 선택1행 다음에 행 추가
  • 2행 선택2행 다음에 행 추가
  • 3행 선택3행 다음에 행 추가
  • 4행 선택4행 다음에 행 추가
  • 5행 선택5행 다음에 행 추가
  • 6행 선택6행 다음에 행 추가
  • 7행 선택7행 다음에 행 추가
  • 8행 선택8행 다음에 행 추가
자료형
클래스 명
short
Short
int
Integer
long
Long
float
Float
double
Double
boolean
Boolean
char
Character
byte
Byte

 

표를 보면 바로 알 수 있듯이 자료형의 첫 글자를 대문자로 바꾸면 된다.

물론 줄임말인 int 와 char 는 원래 단어로 써주면 된다.

 

3. 형변환 해주는 메소드 parse()

위에서 잠깐 언급했지만 각 자료형 클레스에는 다른 자료형을 자신의 형태로 변환해주는 메소드가 존재한다.

                String ss = "2000"; 
		int d = Integer.parseInt(ss);
		
		System.out.println(Double.parseDouble(ss));
		System.out.println(Float.parseFloat(ss));
		System.out.println(Short.parseShort(ss));
 

간단하게 문자열을 정수 또는 실수로 변환하여 출력하는 코드이다.

메소드를 보면 클래스마다 메소드 이름이 유사한 것을 볼 수 있다.

parse() 메소드는 static 이므로 사용하기 위해서는 별도로 import 또는 인스턴스가 필요하지 않다.

 

※Charcater 클래스에는 parse() 메소드가 없다.

 

'JAVA' 카테고리의 다른 글

BigDecimal  (0) 2022.08.28
제너릭  (0) 2022.08.28
StringBuffer  (0) 2022.08.28
String 클래스 문자열 메소드  (0) 2022.08.28
JAVA - ORACLE 연동  (0) 2022.08.28

 

기존에 사용한 String 과 많이 비슷해 보이지만 다른 StringBuffer 에 대해 정리한다.

 

StringBuffer 란 String 처럼 문자열을 취급하는 클래스이다. 하지만 둘의 차이점은 가변성인가, 불변성인가 라는 큰 차이가 존재한다.

 

1. String과 StringBuffer 의 차이점

우선 결론부터 말하자면 String 불변성(immutable)이며 StringBuffer 가변성(mutable)이다.

즉, 값이 변할 수 있는가 없는가의 차이를 갖는다.

 

아래의 코드를 살펴보자.

                String s1 = "";
		s1 += "Hello";
		s1 += " World";
		s1 += " Welcome";

		System.out.println(s1);
 

위 코드에서 s1은 String 클래스의 인스턴스이다. 이 인스턴스를 이용해 다른 문자열들을 붙여 출력한 내용이다.

하지만 String은 불변성을 가지고 있는데 출력 결과는 변한 것을 볼 수 있다.

중요한 것은 String은 '불변성이면서 완성된 문자열'이라는 것이다.

즉, s1에 마지막 문장인 "Welcom" 이 추가되면서 완성되며 값이 변할수 없는 것이다.

하지만 이 과정에서는 문제가 발생한다. 위의 코드를 실제 내부에서 처리되는 과정대로 풀어보면 다음과 같다.

                s1 = new StringBuffer(s1).append("Hello").toString();
		s1 = new StringBuffer(s1).append(" World").toString();
		s1 = new StringBuffer(s1).append(" Welcome").toString();
                System.out.println(s1);
 

코드에서 보면 단순히 문자열을 넣은 것이 아닌 StringBuffer 객체를 생성하여 문자열을 붙인 것을 확인할 수 있다.

※append() 메소드는 문자열을 붙여주는 기능을 수행한다. 자세한 사항은 아래에서 자세히 다룰 예정이다.

 

내부에서 발생하는 일을 보면 세 가지의 문장을 붙이기 위해서 StringBuffer 객체 3개를 생성하는 것을 볼 수 있다.

이 객체들은 문자열을 합치는 과정이 완료되면 자동으로 소멸(Garbage Collection)한다.

즉, 새로운 버퍼를 생성하는데 메모리의 낭비가 발생하고 추가적인 작업이 발생하게 된다.

 

하지만 애초에 StringBuffer 를 사용한다면 이런 비용적인 문제를 해결할 수 있다.

                StringBuffer sb1 = new StringBuffer();
		sb1.append("Hello");
		sb1.append(" world");
		sb1.append(" Welcome");
		System.out.println(sb1);
 

실행 결과를 보면 같은 결과가 나온 것을 확인할 수 있다.

StringBuffer 클래스는 부적으로 String 객체가 아닌 char[ ](문자형 배열) 을 사용하여 내용의 추가 또는 삭제와 같은 변환이 가능(가변성)하다.

지난 포스팅에서 String 의 메소드를 정리하며 나온 subString() 과 replace 메소드를 사용하는 방법을 정리했다.

불변성인 String을 두 메소드를 통해 내용을 변경할 수 있던 이유는 위에서 말한바와 같이 새로운 객체를 생성하여 변경할 수 있던 것이다. 하지만 StringBuffer 의 경우 문자열 내용을 변경해도 새로운 객체를 만들지 않아도 된다.

즉, 비용적으로 String의 문자열을 변경하는 것 보다 훨씬 저렴하게 내용의 변경이 가능하다는 것이다.

따라서 빈번하게 문자열의 변경이 필요한 작업이라면 String 보다는 StringBuffer 를 사용하는 편이 좋다.

 

2. StringBuffer 선언하기 & 버퍼 크기 설정하기

버퍼를 만드는 방법은 총 3가지가 있다.

StringBuffer도 클래스이기에 인스턴스를 생성하며 생성자를 통해서 값 또는 버퍼의 사이즈를 설정할 수 있다.

                //16문자를 담을 수 있는 공간 생성
		StringBuffer strb1 = new StringBuffer();
		//100문자를 담을 수 있는 공간 생성
		StringBuffer strb2 = new StringBuffer(100);
		//"abcd" 가 저장된 공간 생성
		StringBuffer strb3 = new StringBuffer("abcd");
 

기본으로 생성하면 16개의 문자를 담을 수 있지만 실제로 사용하면 초과해서도 사용할 수 있다.

하지만 기본 사이즈를 벗어나면 오버헤드(과부화)가 발생하여 내부적으로 버퍼를 새로 만드는 작업을 수행한다.

이는 추가적인 비용이 발생하여 비효율적이므로 최초 생성 시 넉넉하게 사이즈를 잡아주는 것이 좋다.

 

 

3. StringBuffer의 메소드

StringBuffer는 가변성이다. 즉, 내용 변경이 자유롭기 때문에 내용을 변경하는 작업을 수행하는 메소드가 많다.

 

1) 값을 문자열로 변환하여 저장 append()

위에서 잠깐 언급했지만 append() 메소드는 문자열에 새로운 내용을 추가할 수 있다. 또 한 가지 기능으로는

문자형으로 변환한다는 것이다.

아래의 코드에서 보면 12345 는 정수이지만 실행 결과에서는 문자열로 변경되어 내용이 합쳐진 것을 볼 수 있다.

                StringBuffer sb1 = new StringBuffer();
		sb1.append("Hello");
		sb1.append(" world");
		sb1.append(" Welcome");
		sb1.append(12345);
		System.out.println(sb1);
 

2) 버퍼의 전체 크기 구하기 capacity()

capacity()를 사용하면 버퍼에 할당된 전체 크기를 구할 수 있다.

                StringBuffer sb1 = new StringBuffer();
		sb1.append("Hello");
		sb1.append(" world");
		sb1.append(" Welcome");
		sb1.append(12345);
		System.out.println("버퍼크기 : " + sb1.capacity());
		
		System.out.println(sb1);
 

StringBuffer의 인스턴스를 생성한 부분을 보면 16개의 글자를 담는 기본 사이즈로 생성된 것을 볼 수 있다.

그 후 인스턴스 sb1에 문자열을 추가 후 확인해 보면 아래의 실행 결과를 볼 수 있다.

오버헤드가 발생한 버퍼의 크기는 다음의 공식에 따라 크기가 재설정된다.

NewCapacity = (oldCapacity * 2) + 2

따라서 글자의 수가 24개지만 버퍼는 초기의 16개 이므로 (16*2)+2 하여 34의 사이즈가 잡힌다.

※동일한 버퍼에서 오버헤드가 자주 발생하여 일정 크기를 넘기면 입력된 문자열 만큼의 사이즈가 나오는 듯 하다.

 

3) 버퍼에 들어간 문자의 갯수 구하기 length()

버퍼에 실제로 들어간 문자의 갯수를 구한다. 사용 방법이나 메소드의 기능은 String 과 동일하다.

                StringBuffer sb1 = new StringBuffer();
		sb1.append("Hello");
		sb1.append(" world");
		sb1.append(" Welcome");
		sb1.append(12345);
		System.out.println("버퍼크기 : " + sb1.capacity());
		System.out.println("문자열의 길이 : " + sb1.length());
		System.out.println(sb1);
 

 

4) 지정된 위치의 문자 수정하기 setCharAt()

String 클래스와 동일하게 지정된 위치의 문자가 무엇인지 찾아 수정한다.

                StringBuffer sb1 = new StringBuffer();
		sb1.append(12345);
		sb1.setCharAt(3, '사');
		System.out.println(sb1); 
 

번외로 charAt() 메소드도 있다. 이 메소드는 인자로 입력된 index 번호의 위치에 어떤 단어가 있는지 찾아준다.

                System.out.println(sb1.charAt(3));
 

 

5) 문자열 중간에 값 바꾸기 replace()

charAt()이 특정 자리의 문자를 변경하였다면 replace() 는 범위를 지정하여 변경이 가능하다.

                StringBuffer sb1 = new StringBuffer();
		sb1.append(12345);
		sb1.replace(2, 4, "사");
		System.out.println(sb1);
 

실행 결과를 보면 2~4 번째 까지의 글자가 "사" 로 변경된 것을 볼 수 있다.

변경되는 단어 또한 setCharAt()과 달리 문자열인 것을 볼 수 있다.

 

6) 지정된 범위만큼 문자열 삭제 delete()

특정 자리에 있는 문자열을 삭제하는 메소드이다. 삭제된 문자열의 공간은 문자열 뒷쪽의 다른 문자열이 자동으로 붙는다.

                StringBuffer sb1 = new StringBuffer();
		
		sb1.append(12345);
		System.out.println(sb1);
		
		StringBuffer tsb = sb1.delete(1,3);
		System.out.println(tsb);
 

7) 원하는 위치에 문자 추가하기 insert()

StringBuffer는 가변성이므로 문자의 추가 또한 적은 비용으로 가능하다.

                StringBuffer sb1 = new StringBuffer();
		
		sb1.append(12345);
		System.out.println(sb1);
		
		sb1.insert(3, ".");
		System.out.println(sb1);
 
 

8) 문자열의 순서를 역순으로 변경 reverse()

말 그대로 문자열의 순서를 역순으로 재정렬한다.

                StringBuffer sb1 = new StringBuffer();
		
		sb1.append(12345);
		System.out.println(sb1);
		
		sb1.reverse();
		System.out.println(sb1);
 

 

9) 원하는 범위의 문자열 추출 subString()

String 클래스와 마찬가지로 원하는 범위까지의 문자열을 추출할 수 있다.

                StringBuffer sb1 = new StringBuffer();
		
		sb1.append(12345);
		System.out.println(sb1);
		System.out.println(sb1.substring(1, 3));
 
메소드 원형
기능
반환 값
append(String str)
값을 문자열로 변환하여 저장
Stringbuffer
capacity()
용량. 전체 공간의 크기
int
length()
들어가 있는 문자의 개수.
int
charAt(int index)
지정된 위치의 문자 구하기.
char
setCharAt(int index, char ch)
index 위치에 문자 변경
void
delete(int start, int end)
지정된 범위 만큼 문자열 삭제
Stringbuffer
insert(int offset, String str)
추가할 위치에 값을 삽입
Stringbuffer
replace(int start, int end, String str)
시작 ~ 끝 까지 변경할 문자열로 문자 치환
Stringbuffer
reverse()
문자열의 순서를 역순으로 변경
Stringbuffer
substring(int start, int end)
전체 문자열에서 원하는 문자열을 추출
String

 

※추가

1.

문자열을 취급하는 클래스는 StringBuilder 라는 클래스도 존재한다.

문자열이 처리되는 속도는 StringBuilder > StringBuffer > String 순으로 좋다.

StringBuilder 는 StringBuffer와 비슷하지만 StringBuilder의 경우는 단일 쓰레드 환경에서만 사용 가능하다.

따라서 단일 쓰레드 환경에서 처리속도에 민감한 작업이 필요할 때 StringBuilder를 사용한다.

 

2.

StringBuffer 의 비용적인 장점은 자바 내에서 처리 뿐 아닌 DB의 쿼리문 등을 처리할 때 유용하다.

 

'JAVA' 카테고리의 다른 글

제너릭  (0) 2022.08.28
Wrapper 클래스  (0) 2022.08.28
String 클래스 문자열 메소드  (0) 2022.08.28
JAVA - ORACLE 연동  (0) 2022.08.28
싱글톤 패턴  (0) 2022.08.28

 

문자열을 저장하기 위해 사용하는 String 클래스에는 문자열을 처리하기 위한 여러 메소드가 존재한다.

그 중 비교적 사용이 빈번한 메소드 12가지에 대해 정리한다.

 

1. 두 문자열 비교 equals()

같음을 '==' 연산자로 비교하면 두 문자열의 주소를 비교하게 된다.

실제 값인 문자열을 비교하기 위해서는 equals 메소드를 사용해야 비교가 가능하다.

                String str = "hi";
		String str0 = "hi";
		if(str.equals(str0)) {
			System.out.println("같습니다.");
		}
		else {
			System.out.println("다릅니다.");
		}
 

 

2. 문자열 병합하기 concat()

두 문자열을 하나의 문자열로 더해주는 메소드이다.

                String str1 = "Hello";
		String str2 = " World!";
		
		// 문자열 병합
		String str3 = str1.concat(str2);
		System.out.println(str3);
 

동일한 기능으로는 + 연산자를 사용하는 방법도 있다.

                String str4 = str1 + str2;
		System.out.println(str4);
 

실제로는 동일한 기능이기 때문에 더 간편한 + 연산자를 사용하는 것이 일반적이다.

 

3. 지정된 위치(index)의 문자를 구해주는 메소드 charAt()

문자열에서 특정 위치의 문자를 구하고 싶다면 charAt 메소드를 사용한다.

                char ch;
		String str0 = "012345";
		ch = str0.charAt(4);
		System.out.println(ch);
 

위의 코드는 4번째 인덱스에 위치한 문자를 구하는 코드이다.

4. 문자열 검사 메소드 contains()

특정 문자열로 데이터를 검색할 때 유용하게 사용할 수 있다.

boolean 형을 반환하여 찾는 문자열이 있을 경우 True, 없으면 False 를 반환한다.

                String str5 = "abdcefgh";
		boolean b = str5.contains("f");
		System.out.println(b);
 

 

5. 문자(열)을 검사하여 위치를 알려주는 메소드 indexOF()

찾은 문자열의 시작 위치를 알려준다. int를 반환하는 메소드로 찾는 문자가 있을 경우 해당 자리의 인덱스를,

없으면 -1을 리턴한다.

                String str = "abdcefgh";
		// 문자열 위치값
		int idx = str.indexOf("g");
		System.out.println(idx);
 

6. 문자열 길이를 구하는 메소드 length()

C에서 strlen() 함수와 동일한 기능을 한다.

문자열의 사이즈를 구하며 사이즈를 int형으로 반환한다.

                String str6 = "안녕!";
		int size = str6.length();
		System.out.println(size);
 

여기서 주목할 점은 실행 결과가 3으로 나온 점이다.

C 에서는 문자를 기본 1바이트로 취급하며 한글과 같은 문자는 기본 2바이트로 하여 취급하여 C에서의 결과는

안녕! 은 2(안) + 2(녕) + 1(!) 으로 총 5를 반환하지만 자바에서는 문자 기본 단위가 2바이트므로 3이 리턴된다.

 

7. 문자열 중 문자(열) 변환 메소드 replace()

특정 문자열을 검색하여 해당 위치에 새로운 문자열로 치환한다.

                String str5 = "abdcefgh";
		String str7 = str5.replace("efgh", "xyzxyzxyz");
		System.out.println(str7);
 

추가로 비슷한 메소드 중 replaceAll() 이라는 메소드도 있다.

이 메소드는 [ ] 를 사용하여 일정 수식처럼 사용이 가능하다.

자주 쓰이는 수식은 다음과 같다.

1) ^ : 비트연산에서 부정을 뜻하는 기호. "[^a]" 로 사용 시 a 가 아닌 문자를 의미한다.

2) - : 특정 범위를 나타내는 기호. "[0-9]" 로 사용 시 0~9 까지를 의미한다.

                String str8 = "a0s1d2a3s4d5a6";
		String str9 = str8.replaceAll("[^a]","A");
		System.out.println(str9);
		
		String str10 = str8.replaceAll("[0-9]","ZZ");
		System.out.println(str10);
 

 

8. 문자열 분리 메소드 split()

문자열을 나눌 기준을 정하여 문자열을 문자열 배열로 변경한다. 또한 문자열 배열로 변경되므로 문자열을 변환 후

담을 때는 문자열 배열을 선언하여 담아야 한다.

split() 메소드는 특정 문자를 변환할 때 주로 사용된다.

JSP처럼 < >을 기준으로 코드를 작성하는 언어를 예를 들면 웹에서 < >를 사용하는 실행 코드를 댓글로 입력할 경우

백엔드(서버) 에서는 문장을 수행하여 실행결과를 프론트엔드(클라이언트)에게 보내줄 수도 있다. 이럴 경우 해킹등의 문제가 발생할 위험이 크므로 split()으로 < >를 다른 특수문자로 변환하여 이같은 문제를 해결할 수 있다.

              	String str11 = "dog,cat:chicken pig";
		String[] strArr = str11.split("[:, ]");
		
                System.out.println("1번 인덱스" + strArr[1]);
		
		for(String s : strArr) {
			System.out.println(s);
		}
 

코드를 보면 replaceAll() 메소드처럼 [ ]를 사용할 수 있다.

이번 코드에서 사용한 "[:, ]"는 : 와 , 와 ' '(공백) 이 포함된 단어를 기준으로 하여 문자열 배열로 만들어 준다.

추가로 split()과 비슷한 용도로 사용하는 StringTokenizer 클래스도 있다.

아래 코드에서 동일한 문자열로 사용하여 실행 결과를 보면 동일한 기능을 수행하는 것을 알 수 있다.

둘의 차이점은 문자열을 문자열 배열로 나누는 가 에 대한 차이이므로 필요에 따라 원하는 방식을 사용하면 된다.

                String str11 = "dog,cat,chicken,pig";
		
		StringTokenizer stk = new StringTokenizer(str11);
		while(stk.hasMoreTokens()) {
			System.out.println(stk.nextToken(","));
		}
 

9. 문자열 추출 메소드 subString()

전체 문자열에서 원하는 문자열만 추출하는 용도로 사용한다.

원하는 문자열은 문자열의 인덱스를 통해 어디에서 어디까지 추출할 지 지정하게 된다.

                String str11 = "dog,cat,chicken,pig";
		String subStr1 = str11.substring(0,10);
		System.out.println(subStr1);
 

범위를 0 ~ 10 까지 지정하여 실행 결과에서는 d ~ h 까지 출력된 것을 볼 수 있다.

 

10. 공백 제거 메소드 trim()

말 그대로 공백을 제거해주는 메소드이다. 단, 문자열의 가장 앞과 맨 뒷부분의 공백만을 삭제한다.

별로 필요없을 듯 한 기능이지만 네트워크에서 문자열을 send, receive 할 경우 노이즈 등의 문제로 문자열에

공백이 추가되어 들어오는 경우가 생긴다. 이 때 원하는 문자열을 나타내기 위해 공백을 제거할 필요가 있다.

                String str12 = "      ab  cd             ";
		String str13 = str12.trim();
		System.out.println(str13);
 

11. 대소문자 변환 메소드 toLowerCase() , toUpperCase()

이름 그대로 대문자 -> 소문자 , 소문자 -> 대문자 로 변환한다.

 

String str12 = "ab  cd";
		
		String upperStr = str12.toUpperCase();
		System.out.println(upperStr);
		
		String LowerStr = str12.toLowerCase();
		System.out.println(LowerStr);
 

 

12. 기본 자료형을 문자열로 변환하는 메소드 valueOf()

형변환을 하는 메소드로 만약 1234 라는 정수가 있다면 이를 "1234" 로 변환해주는 메소드이다.

이 메소드를 대신하여 "" 을 통해 동일한 기능을 수행할 수도 있다. 결국 그냥 편한거 쓰면 된다.

                int num = 12345678;
		String str14 = String.valueOf(num);
		System.out.println(str14);
		
		String str15 = "" + num;
		System.out.println(str15);
 
 
 
메소드 명
기능
반환형
equals()
두 문자열의 값이 동일한지 확인
boolean
charAt()
특정 인덱스의 문자를 알려줌.
char
contains()
전체 문자열에서 특정 문자열을 검색
boolean
indexOF()
전체 문자열에서 특정 문자열을 찾아 시작 위치를 알려줌.
int
length()
문자열의 길이를 구함.
int
replace()
문자열에서 특정 문자로 치환
String
split()
기준에 따라 문자열을 문자열 배열로 변경
String
subString()
전체 문자열에서 원하는 범위의 문자열을 추출
String
trim()
문자열의 앞, 뒤의 공백을 제거
String
valueOf
기본 자료형을 문자열로 변환 (Statinc)
String
toLowerCase()
대문자를 소문자로 변환
String
toUpperCase()
소문자를 대문자로 변환
String

 

'JAVA' 카테고리의 다른 글

Wrapper 클래스  (0) 2022.08.28
StringBuffer  (0) 2022.08.28
JAVA - ORACLE 연동  (0) 2022.08.28
싱글톤 패턴  (0) 2022.08.28
인터페이스  (0) 2022.08.28

 

이번 시간에는 자바와 데이터베이스를 연동하여 사용하는 방법에 대해 정리한다.

참고로 DB는 SQL Develper 를 사용하여 진행하였다.

 

1. 연결할 DB 생성

DB와 연결하기 위해 임시로 작업할 DB와 테이블을 생성하였다.

 

<1. sys 에서 권한 설정>

drop user dev CASCADE;--계정 삭제

--계정 생성
CREATE user dev
IDENTIFIED by "1234"
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP
QUOTA UNLIMITED ON USERS;

--계정에 관리자 권한 부여
GRANT DBA TO dev;
 

<2. dev 에서 테이블 생성>

-- 간단한 사용자 정보 저장용 테이블
CREATE TABLE USERTBL
(
    u_id VARCHAR2(20) NOT NULL,
    u_name VARCHAR2(10) NOT NULL,
    u_pass VARCHAR2(20) NOT NULL,
    
    CONSTRAINT ut_pk PRIMARY KEY (u_id)
);

-- 테이블에 데이터 입력
INSERT INTO USERTBL VALUES 
('tester', '홍길동', '1234');
-- 입력, 수정, 삭제 승인
commit;

-- 테이블 데이터 가져오기
SELECT * FROM usertbl;

INSERT INTO USERTBL VALUES ('test01', '고길동', '1234');
 

SQL Developer 에서 작성한 쿼리문이다. 만약 이해가 안된다면 이전에 작성한 DB 정리 포스팅을 보자.

 

2. jdbc(Java Database Connectivity) 설정하기.

자바에서 DB 와 연결하기 위해서는 ojdbc6-11.2.0.3.jar 이라는 파일이 필요하다.

jdbc란 자바에서 데이터베이스에 접속할 수 있도록 지원하는 API(Application Programming Interface) 이다.

 

jdbc를 이클립스에서 설정하는 방법은 아래의 링크를 참고하자.

https://blog.naver.com/jsg910111/221697866013

이미지 썸네일 삭제
쿠키님, 코딩 좀 해주세요..! : 네이버 블로그

이름은 쿠키 종족은 고양이 잘하는 건 골골송. 블로그 너무 어렵다.

blog.naver.com

 

3. DB에 접속하기

설정이 끝났다면 아래의 코드를 작성한다.

package com.dao;

import java.sql.*;

import com.dto.User;

public class Database {	// DAO 클래스
	
	// ojdbc에서 DB 접속에 필요한 드라이버를 생성
	private String driver = "oracle.jdbc.driver.OracleDriver";	
	// 접속 ip 및 포트번호 설정 , sid
	private String url = "jdbc:oracle:thin:@localhost:1521:xe";	
	private String dbuser = "dev";
	private String dbpass = "1234";
	
	//DBMS 처리를 위한 변수
	// 접속용 인스턴스. 드라이버 매니져가 
        // Connection 인터페이스의 참조 인스턴스 생성 후 conn 사용
	private Connection conn;			
	// SQL 쿼리 실행용 쿼리문 실행기	 + SQL 에 데이터를 전송	
	private PreparedStatement pstmt;	
	// select 쿼리 실행 결과 저장
	private ResultSet rs;				
	
	//생성자에서 드라이버를 로드
	public Database() {
		try {
                       // 드라이버 매니져에게 드라이버를 넘겨 줌
			Class.forName(driver);		 
		} catch (ClassNotFoundException e) {
			//e.printStackTrace();
			System.out.println("jdbc driver load fail!");
		}			
	}
        public void getConnection() {
		if(conn == null) {
			try {
				conn = DriverManager.getConnection(url, dbuser, dbpass);
			} catch (SQLException e) {
				//e.printStackTrace();
				System.out.println("Oracle connection fail!");
			}
		}
	}

}
 

위의 Database 클래스의 멤버를 보면 다음과 같다.

1) driver : ojdbc 에서 경로를 참조하여 DB와 접속에 필요한 드라이버를 생성

2) url : DB와 연결을 위한 접속 경로를 설정한다. 아래의 이미지를 참고하자.

.

3) dbuser : 접속할 DB 의 이름이다.

4) dbpass : DB 접속을 위한 패스워드이다.

 

여기까지가 DB 접속에 반드시 필요한 설정을 하는 내용이다. 물론 변수의 이름은 바뀌어도 된다.

하지만 변수에 들어가는 문자열의 경우 자신의 DB 환경에 맞게 설정을 해줘야한다.

 

다음은 접속에 필요한 인스턴스를 생성하는 작업이다.

1) Connection conn : DB와 연결을 담당하는 인스턴스 생성한다..

2) PreparedStatement pstmt : DB에 쿼리문을 전송하거나 쿼리문을 실행할 때 사용된다.

3) ResultSet rs : SELECT 쿼리의 실행 결과를 저장한다.

 

※ 참고로 conn, pstmt, rs 는 인스턴스이다. 하지만 Connection 등은 인터페이스 이다.

하지만 인터페이스에서는 일반적으로 인스턴스 생성이 불가능하다. 하지만 위 코드는 에러가 발생하지 않는다.

그 이유는 Connection 등의 문장이 실행되면 드라이버 매니져가 생성되어 conn 등의 인스턴스 변수와

주소를 공유하기 때문에 사용이 가능한 듯 하다.

 

생성자에서는 Class.forName() 메소드를 통해 드라이브 매니져에게 새로 생성한 드라이브 매니져를

넘겨주는 것을 확인할 수 있다.

 

마지막으로 getConnection() 메소드를 호출하면 설정한 url, dbuser, dbpass 를 통해 DB와 접속할 수 있다.

 

4. 전체 코드 보기

핵심적인 DB 연결 방법을 살펴보았다면 이젠 연결된 DB를 사용할 때이다.

아래의 프로젝트는 단순히 회원가입과 로그인 기능을 구현한 예제이다. 설명은 주석으로 대체한다.

(DB는 1 에서 작성한 테이블을 사용한다.)

 

<DTO Class :: User Class>

DB와 데이터를 주고 받기 위한 저장용 Class 생성

package com.dto;

public class User {
	private String u_id;
	private String u_name;
	private String u_pass;
	
	
	public String getU_id() {
		return u_id;
	}
	public void setU_id(String u_id) {
		this.u_id = u_id;
	}
	public String getU_name() {
		return u_name;
	}
	public void setU_name(String u_name) {
		this.u_name = u_name;
	}
	public String getU_pass() {
		return u_pass;
	}
	public void setU_pass(String u_pass) {
		this.u_pass = u_pass;
	}
}
 

<DAO Class :: Database Class>

DB와 데이터를 주고 받기 위해 필요한 작업을 처리.

package com.dao;

import java.sql.*;
import com.dto.User;
public class Database {	// DAO 클래스
	
	// ojdbc 
	private String driver = "oracle.jdbc.driver.OracleDriver";	
	// 접속 ip 및 포트번호 설정 , sid
	private String url = "jdbc:oracle:thin:@localhost:1521:xe";	
	private String dbuser = "dev";
	private String dbpass = "1234";
	
	//DBMS 처리를 위한 변수
	// 접속용 인스턴스. 드라이버 매니져가 Connection 인터페이스의 참조 인스턴스 생성 후 conn 사용
	private Connection conn;			
	// SQL 쿼리 실행용 쿼리문 실행기	 + SQL 에 데이터를 전송	
	private PreparedStatement pstmt;	
	// select 쿼리 실행 결과 저장
	private ResultSet rs;				
	
	//생성자에서 드라이버를 로드
	public Database() {
		try {
			Class.forName(driver);		// 드라이버 매니져에게 드라이버를 넘겨 줌 
		} catch (ClassNotFoundException e) {
			//e.printStackTrace();
			System.out.println("jdbc driver load fail!");
		}			
	}
	
	public void getConnection() {
		if(conn == null) {
			try {
				conn = DriverManager.getConnection(url, dbuser, dbpass);
			} catch (SQLException e) {
				//e.printStackTrace();
				System.out.println("Oracle connection fail!");
			}
		}
	}
	
	public void closeDB() {
		try {
			// DB 종료 시 진행 순서 결과종료 -> 쿼리실행기 종료 -> 접속 종료
			if(rs != null) rs.close();
			if(pstmt != null) pstmt.close();
			if(conn != null) conn.close();
		} catch (SQLException e) {
			//e.printStackTrace();
		}
	}
	
        // 유저 정보를 DB 입력하는 메소드
	public boolean insertUser(User user) {
		int result = 0;
		String query = ("insert into usertbl values (?,?,?)"); // ? 는 값 위치
		try {
			pstmt = conn.prepareStatement(query);
			pstmt.setString(1, user.getU_id());   // value 1번
			pstmt.setString(2, user.getU_name()); // value 2번
			pstmt.setString(3, user.getU_pass()); // value 3번
			
			//쿼리문 실행
                        // insert, update, delete 절은 executeUpdate() 사용
                       	result = pstmt.executeUpdate();	
			
		} catch (SQLException e) {
			//e.printStackTrace();
			return false;
		}
		
		return true;
	}

	// 로그인할 정보를 탐색/비교
	public User selectUser(String id) {
		User user = null;
                //DB에서 입력한 id를 찾음 (? 는 입력한 id의 내용이 들어가는 자리)
		String query = "select * from usertbl where u_id = ?";
		try {
			pstmt = conn.prepareStatement(query);
			pstmt.setString(1,id);
			rs = pstmt.executeQuery();  // select는 executeQuery() 사용

			if(rs.next()) {	// DB에 데이터가 있으면 T / 없으면 F 
				user = new User();
				user.setU_id(id);
				user.setU_name(rs.getString("u_name"));
				user.setU_pass(rs.getString("u_pass"));
			}
			else {
				System.out.println("ID 가 없습니다.");
			}
			
		} catch (SQLException e) {
			//e.printStackTrace();
			System.out.println("process fail");
		}
		return user;
	}
}
 

<main Class> controller

메소드 호출을 통해 데이터를 전달하는 클래스.

package com;

import java.util.Scanner;

import com.dao.Database;
import com.dto.User;
import com.service.Service;
import com.view.View;

public class JdbcMain {

	
	public static Database db;	// DB 인스턴스 생성
	public static View dv = new View();	// view 인스턴스 생성
	public static Service ds = new Service(); // service 인스턴스 생성
	
	public static void main(String[] args) {
		
		db = new Database();	// 드라이버 로드
		db.getConnection();		// 오라클과 접속
		
		
		
		while(true) {
			dv.printMenu();
			int sel = dv.SelectMenu();
			
			if(sel == 0) {
				System.out.println("종료합니다.");
				break;
			}
			switch(sel) {
			case 1:	// login
				loginProc();	// 로그인 메소드
				break;
				
			case 2:	// join user
				regiProc();		// 회원가입 메소드
				break;
			}
			
		}
	}
	
	// 회원가입 메소드
	private static void regiProc() {
		User user = new User();	// 회원가입할 유저의 인스턴스 생성
		user = dv.inputRegiProc(user);	// 회원 정보 입력
		// 회원 정보를 DB 연결을 위해 Service에게 전달
		boolean b = ds.insertUserData(user);
		if(b) {
			dv.printRegiSuccess();	// 성공 문구 출력
		}
		else {
			dv.printRegiFail();	// 실패 문구 출력
		}
	}

	private static void loginProc() {
		User user = new User(); // 로그인할 유저의 인스턴스 생성
		// view에서 처리할 작업
		user = dv.inputLoginData(user);	// ID와 PW 입력
		// DB에서 검사를 위해 Service에 입력된 정보 제공
		user = ds.getLoginData(user);	
		if(user != null) {	// 입력된 정보가 DB에 있다면
			dv.printLoginSuccess(user);	// 로그인 성공 출력
		}
		else {
			dv.printLoginFail();	// 로그인 실패 출력
		}
	}
}
 

<Service Class>

컨트롤러에서 전달받은 데이터를 DB 처리를 위해 DB에 값 전달

package com.service;

import com.JdbcMain;
import com.dao.Database;
import com.dto.User;

public class Service {
	
	// 회원가입 메소드
	public boolean insertUserData(User user) {
		// 입력한 데이터를 DB에 전달 후 리턴값 반환
		boolean b = JdbcMain.db.insertUser(user);
		return b;
	}
	
	
	// 로그인 메소드
	public User getLoginData(User user){
		//Service에서 할 일

		String userId = user.getU_id(); // 입력받은 아이디 저장
		String userPass = user.getU_pass(); // 입력받은 비밀번호 저장
		
		//DB에 입력받은 id와 동일한 데이터가 있는지 체크
		User dbuser = JdbcMain.db.selectUser(userId);
		
		if(dbuser != null) { // 동일한게 있다면 비밀번호 체크
			if(userPass.equals(dbuser.getU_pass())) {
				// 비밀번호가 같다면 성공
				return dbuser;
			}
			// 실패했다면 null 반환
			else {
				return null;
			}
		}
		return null;
	}
}
 

<View Class>

화면에 출력 또는 데이터 입력을 담당하는 클래스.

package com.view;

import java.util.Scanner;

import com.JdbcMain;
import com.dto.User;

public class View {
	public static Scanner scan = new Scanner(System.in);

	public User inputRegiProc(User user) {
		
		// 여기서 부터 view 에서 할 작업
		scan.nextLine();			// 엔터키 스킵
		System.out.println("회원 가입");
		System.out.print("Id\t : ");
		user.setU_id(scan.nextLine());
		System.out.print("Name\t : ");
		user.setU_name(scan.nextLine());
		System.out.print("PW\t : ");
		user.setU_pass(scan.nextLine());
		
		return user;
	}
	
	public User inputLoginData(User user) {
		scan.nextLine();		
		System.out.println("로그인");
		System.out.print("Id\t : ");
		user.setU_id(scan.nextLine());
		System.out.print("PW\t : ");
		user.setU_pass(scan.nextLine());
		
		return user;
	}
	
	public void printMenu() {
		System.out.println("회원 관리 프로그램");
		System.out.println("=====================");
		System.out.println("1. 로그인");
		System.out.println("2. 회원가입");
		System.out.println("0. 종료");
		System.out.println("=====================");
		System.out.print("선택 >> :");
	}
	
	public static int SelectMenu() {
		return scan.nextInt();
	}
	
	public static void printRegiSuccess() {
		System.out.println("회원 등록 성공");
	}
	public static void printRegiFail() {
		System.out.println("회원 등록 실패");
	}
	
	public static void printLoginSuccess(User user) {
		System.out.println(user.getU_name() + "님 환영합니다.\n");
	}
	public static void printLoginFail() {
		System.out.println("로그인 실패");
	}
}
 

 

 

'JAVA' 카테고리의 다른 글

StringBuffer  (0) 2022.08.28
String 클래스 문자열 메소드  (0) 2022.08.28
싱글톤 패턴  (0) 2022.08.28
인터페이스  (0) 2022.08.28
추상화  (0) 2022.08.28

 

이번 시간에는 디자인 패턴 중 하나인 싱글톤 패턴에 대해 정리한다.

 

1. 싱글톤 패턴이란

우선 영어단어의 뜻은 다음과 같다.

.

단어의 뜻을 자바와 연관지어 풀어본다면 "단독으로만 존재할 수 있는 객체" 를 말한다.

즉, 같은 클래스의 인스턴스를 2개 이상 생성할 수 없도록 하는 것이다.

 

2. 싱글톤 패턴 구현 하기

코드로 보자면 디자인 패턴 중 아마 가장 심플하지 않을 까 싶다.

public class SingletonTest {
	private static SingletonTest instance;
	
	private SingletonTest() {
		System.out.println("싱글톤 클래스 생성자");
	}
	
	public static SingletonTest getInstance() {
		if(instance == null) {
			instance = new SingletonTest();
		}
		return instance;
	}	
}
 

위와 같이 코드를 작성하면 SingletonTest 클래스의 인스턴스는 단 1개만 생성될 수 있다.

 

싱글톤 패턴을 만들기 위한 방법은 다음과 같다.

1) SingletonTest 인스턴스에 접근하기 위한 SingletonTest 클래스형 참조변수인(instance)를 생성한다.

2) 생성자를 private 로 선언하여 외부에서 생성자를 호출할 수 없도록 막는다. (즉, new 를 통해 생성 불가)

3) 싱글톤 클래스에 접근(사용)하기 위한 getter 메소드를 생성한다.

4) getter 메소드를 static 으로 선언하여 참조변수(instance)에 접근할 수 있도록 한다.

(이 때 메소드가 static 이므로 참조변수 또한 static 으로 선언되어야 한다.)

5) 참조변수가 null 인 경우에만 new 가 가능하다. (최초 1회만 new 가능하다.)

6) 참조변수가 null 이 아니라면 참조변수를 리턴한다.

 

3. 싱글톤 인스턴스 사용하기.

싱글톤으로 생성한 인스턴스를 사용하는 방법은 static 으로 선언된 클래스 변수/메소드 와 사용 방법은 유사하다.

package com;

public class SingletonTest {
	private static SingletonTest instance;
	
	private int a = 0;
	
	private SingletonTest() {
		System.out.println("싱글톤 클래스 생성자");
	}
	
	public static SingletonTest getInstance() {
		if(instance == null) {
			instance = new SingletonTest();
		}
		return instance;
	}	
	
	public void printNum() {
		System.out.println(instance.a);
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
}
 

위의 코드에서 보면 멤버변수 a 에 값을 읽고 쓰는 getter / setter 메소드와 출력하는 printNum 메소드가 있다.

이제 외부에서 메소드에 접근하여 보자.

package com;

public class MainClass {

	public static void main(String[] args) {
		SingletonTest.getInstance().setA(10);
		
		System.out.println(SingletonTest.getInstance().getA());
		
		SingletonTest.getInstance().printNum();
	}

}
 

우선 static으로 선언된 클래스 메소드를 사용하는 방법처럼 클래스의 이름으로 접근을 시작한다.

그리고 getInstance() 메소드를 통해 싱글톤클래스의 멤버에 접근하는 것을 볼 수 있다.

 

4. 싱글톤을 사용하는 이유

단 하나의 인스턴스만 만들 수 있는 클래스가 무슨 의미가 있나 싶지만, 실제로는 매우 유용하게 사용이 가능하다.

예를 들어 어떤 데이터들을 저장하는 자료구조(여기선 ArrayList 라고 생각하자.)를 가진 클래스가 있다고 한다면,

해당 클래스를 new 를 통해 여러번 생성한다면 ArrayList 또한 여러 개가 생성된다.

이 때, 새로운 데이터가 추가되어 ArrayList 에 넣는 다면 어느 인스턴스의 ArrayList에 넣어야 할까?

반대로 출력을 위해 ArrayList에서 값을 꺼내온다면 어느 ArrayList에서 값을 가져와야 할까?

이러한 문제는 여러개의 ArrayList가 생기지 않는다면 근본적으로 발생하지 않는 문제이다. 즉, 싱글톤으로 생성된

인스턴스라면 충분히 해결할 수 있는 문제이다.

 

개인적인 경험담으로 서버를 구현하면서 여러 명의 접속자(세션)를 관리하기 위한 매니져(세션 매니져) 클래스에서 주로 발생했다. 접속자는 데이터처럼 어느 곳에 접속자의 정보(소켓 정보 등)를 저장해야하는데 저장 및 관리를

담당해주는 매니져가 여러개가 존재한다면 어느 매니져에서 접속자를 관리해야할지 문제가 발생하였다.

그 후 싱글톤 패턴을 알게되어 매니져를 단일로 만들 수 있는 싱글톤으로 구현하여 쉽게 문제를 해결할 수 있었다.

 

하지만 싱글톤 패턴을 너무 남용한다면 일명 '스파게티 코드' 가 되어 버린다.

스파게티 코드란 실제 스파게티 면처럼 코드가 서로 얽혀있어 풀어낼 수 없는 모습을 말한다.

즉, 편리하더라도 남용하는 것은 절대 올바르지 않기에 정말 필요한 때인지 고민해보고 사용해야 한다.

 

요약 1. 싱글톤이란 클래스의 인스턴스를 단 한 개만 생성할 수 있게 구현한 패턴.

요약 2. 데이터를 저장하기 위해 구현된 클래스 등에서 유용하게 사용이 가능.

요약 3. 남용하면 큰일 남.

 

'JAVA' 카테고리의 다른 글

String 클래스 문자열 메소드  (0) 2022.08.28
JAVA - ORACLE 연동  (0) 2022.08.28
인터페이스  (0) 2022.08.28
추상화  (0) 2022.08.28
다형성과 instanceof  (0) 2022.08.28

+ Recent posts