제네릭 프로그래밍

 

제네릭 프로그래밍(Generic Programming) : 작성한 코드를 다양한 타입의 객체에 대해 재사용하는 객체 지향 기법이다.

 

제네릭은 자바 버전 1.5부터 추가된 기능으로, 복잡한 애플리케이션을 개발할 때 발생하는 여러 가지 버그들을 많이 줄일 수 있다.

 

제네릭은 안드로이드와 같은 애플리케이션을 개발할 때 많이 사용되므로 정확하게 알고 있어야 한다.

 

- C++ 언어의 템플레이트와 거의 유사한 기능이다.

 

 

 

제네릭

 

제네릭(Generic) : 클래스를 정의할 때, 구체적인 타입(type)을 적지 않고 변수 형태로 적어 놓는 것이다

 

클래스를 선언하여 객체를 생성할 때, 구체적인 타입을 기재한다. 즉, 타입을 어떤 클래스 종류의 매개변수로 보는 것이다.

 

기존의 방법 : 이전에는 아래와 같이 Object 타입으로 객체를 받아서 다형성을 이용했는데, 그 이유는 모든 객체가 Object 클래스를 상속하므로 다양한 형태의 데이터를 담을 수 있었기 때문이다.

 

public class PrevBox {

private Object data;


public void set(Object data) { 

this.data = data; 

}


public Object get() { 

return data; 

}

}

  • , get() 메소드로 저장한 객체를 반환받을 때 원하는 타입으로 캐스팅(Casting)해야 하는 번거로움이 있었다.

 

제네릭 기법


//제네릭 클래스

public class Box<T> { // T는 타입을 의미한다.

private T data;


public void set(T data) { 

this.data = data; 

}


public T get() { 

return data; 

}

}

  • 위의 클래스를 선언할 때아래와 같이 구체적인 타입을 기재하여 제네릭 클래스를 사용한다.

Box<String> strBox = new Box<String>(); // String 타입만 저장한다.

Box<Integer> intBox = new Box<Integer>(); // Integer 타입만 저장한다.

 

제네릭 클래스(Genenric class)에서는 타입을 변수로 표시한다. 이것을 타입 매개변수(type parameter)”라고 하며, 타입 매개변수는 객체 생성 시에 프로그래머에 의하여 결정된다.

 

 

 

타입 매개변수의 표기

 

제네릭 클래스는 여러 개의 타입 매개변수를 가질 수 있으나 타입의 이름은 클래스나 인터페이스 안에서 유일하여야 한다.

 

관례에 의하여 타입의 이름은 하나의 대문자로 한다.

 

대문자로 하는 이유는 변수의 이름과 타입의 이름을 구별할 수 있게 하기 위함이다.

 

아래는 일반적으로 널리 사용되는 타입의 이름들이다.

  • E Element(요소 : 자바 컬렉션 라이브러리에서 많이 사용된다.)

  • K Key

  • N Number

  • T Type

  • V Value

  • S, U, V 2번째, 3번째, 4번째 타입

 

다이아몬드 : 자바 SE 7버전부터는 제네릭 클래스의 생성자를 호출할 때, 타입 인수를 구체적으로 주지 않아도 된다. 컴파일러가 문맥에서 타입을 추측하기 때문이다. “<>”를 다이아몬드라고 표현한다.

 

Box<String> Box = new Box<>(); //Box<String> strBox = new Box<String>(); 와 같다.

 

다중 타입 매개변수(Multiple Type Parameters)

interface Pair<K, V> {
	public K getKey();
	public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {
	private K key;
	private V value;
 
	public OrderedPair(K key, V value) {
		this.key = key;
		this.value = value;
	}
 
	public K getKey() { 
		return key; 
	}
	
	public V getValue() { 
		return value; 
	}
}


public class PairTest {
	public static void main(String[] args) {
		Pair<String, Integer> pair1 = new OrderedPair<String, Integer>(“Even”, 8);
		Pair<String, String> pair2 = new OrderedPair<String, String>(“Hi”, “nice~”);
		 
		//pair1과 pair2는 인터페이서 Pair 참조 변수로 선언되었다.
		//new OrderPair<String, Integer>은 K를 String으로 실체화하고, V를 Integer로 실체화한다.
		//오토박싱(autoboxing)에 의하여 int(위의 값 8)가 Integer 객체로 자동 변환된다.
		//오토박싱이란 기초 자료형을 대응되는 클래스 객체로 자동 변환해주는 기능이다.
		 
		//또는 아래와 같은 방식도 가능하다.
		 
		//Pair<String, Integer> pair1 = new OrderedPair<>(“Even”, 8);
		//Pair<String, String> pair2 = new OrderedPair<>(“Hi”, “nice~”);
 	}
}


- Raw 타입 : 타입 매개변수가 없는 제네릭 클래스의 이름이다.

 

Box<Integer> intBox = new Box<>(); //기존 방식

Box rawBox = new Box(); //Raw 방식

  • 주의할 점은 처음부터 제네릭 클래스가 아니면 Raw 타입이라고 하지 않는다.

  • Raw 타입은 JDK 5.0 이전에는 제네릭이 없었기 때문에 이전 코드와 호환성을 유지하기 위해 등장하였다. , 타입을 주지 않으면 무조건 Object 타입으로 간주하는 것이다.

 

 

 

제네릭 메소드

 

일반 클래스의 메소드에서도 타입 매개변수를 사용하여 제네릭 메소드를 정의할 수 있다.

 

제네릭 메소드에서의 타입 매개변수의 범위는 메소드 내부로 제한된다.

 

아래는 실제 Array 클래스에 있는 제네릭 메소드의 일부이다.

 

public class Array {

...

public static <T> T getLast(T[] a)

{

return a[a.length-1];

}

...

}

 

타입 매개변수(<T>)는 반드시 메소드의 수식자(public, static)와 반환형(T) 사이에 위치되어야 한다.


 public static <T> T getLast(T[] a)


제네릭 메소드를 호출하기 위해서는 실제 타입을 꺽쇠괄호 안에 넣어주어도 되고 생략하여도 된다.

 

String[] name = {“김철수”, “김영희”, “김숙자”, “김말년”};

String last = Array.<String>getLast(name);

String last = Array.getLast(name); // 컴파일러는 이미 타입 정보를 알고 있다!

 

한정된 타입 매개변수 : 타입 매개변수로 전달되는 타입의 종류를 제한하기 위한 기능으로 extends 키워드를 사용한다.

  • 아래는 한정된 타입 매개변수를 표현하는데 잘못된 예시이다. Array 클래스로 계속 예를 들면,

public class Array {

...

public static <T> T getMax(T[] a) {

if (a == null || a.length = 0) return null;

 

T largest = a[0];

 

for(int i = 1; i < a.length; i++) {

if (largest.compareTo(a[i]) > 0) largest = a[i];

}


return largest;

}

...

}

  • 위의 예시에서 TcompareTo()라고 하는 Comparable 인터페이스를 구현해야 한다. 따라서 클래스의 범위를 Comparable 인터페이스를 구현한 클래스로 제한하는 것이 바람직하다.

  • 아래는 위의 메소드 getMax()를 올바르게 변경한 것이다.

public static <T extends Comparable> T getMax(T[] a) {

...

}

  • “T extends Comparable”은 타입 TComparable 인터페이스를 구현한 클래스들에 대해서만 호출될 수 있음을 의미한다.

  • implements 키워드가 아닌 extends 키워드를 사용하는 것에 주의해야 한다. 이를 통해 타입 매개변수에 상속관계가 성립함을 추측할 수 있다.

 

 

 

제네릭과 상속

 

- 제네릭에서는 타입 매개변수에 상속관계가 성립한다.


 예를 들어 Number를 타입 매개변수로 주어 객체를 생성했다면, Number의 자식 클래스인 Integer, Double, Float 객체도 모두 처리할 수 있다. 맨 처음 작성했던 제네릭 클래스인 Box 를 사용해보자.


public class Box<T> {

private T data;


public void set(T data) { 

this.data = data; 

}


public T get() { 

return data; 

}

}

 

Box<Number> box = new Box<Number>(); // 객체 생성 시 구체적인 타입을 기재한다.

box.add(new Integer(10)); // 타입의 하위 클래스들도 모두 처리된다.

box.add(new Double(10.1));

box.add(new Float(0.0));

 

- 제네릭에서의 상속에서는 한 가지 주의할 점이 있다.


 타입 매개변수에서 상속관계가 성립하는 것과 어떤 타입 매개변수를 가진 제네릭 클래스에서 상속관계가 성립하는 것은 서로 다르다는 것이다.

 

public void exMethod(Box<Number> number) { ... }

 

 위와 같은 메소드에서는 Box<Integer>Box<Double>과 같은 제네릭 클래스는 매개변수로 대입할 수 없다. 그 이유는 Integer 와 Double은 Number와 상속관계가 성립하지만 Box<Integer> 와 Box<Double>은 Box<Number> 와 상속관계가 성립하지 않기 때문이다. 쉽게 아래의 그림을 보면 차이를 알 수 있다.



 그러나 Box<Number>와 Box<Integer>, Box<Double> 사이에 상속관계가 성립할 수 있다.

 

제네릭 클래스의 상속 : 제네릭 클래스들 간의 상속도 일반 클래스처럼 extendsimplements 키워드를 사용하여 표시할 수 있다아래는 나중에 배울 컬렉션 클래스의 일부이다.


ArrayList<E> implements List<E> { ... }

List<E> extends Collection<E> { ... }

 

 즉, Collection ← List<String> ← ArrayList<String> 순서로 부모 ← 자식 상속관계가 생긴다.

 

 

 

와일드 카드

 

와일드 카드(Wild Card) : 제네릭을 사용하는 코드에서 타입 매개변수를 기재하는 꺽쇠괄호 속 물음표(?)로 표현되며, 카드 게임에서 조커와 유사한 역할을 한다. , 어떤 타입이던지 나타낼 수 있다.

 

와일드 카드는 매개변수, 필드, 지역 변수의 타입을 나타내는 등 다양하게 사용된다.

 

상한이 있는 와일드 카드 : 전체 타입이 아닌 일정한 상한이 있는 타입을 표시하는데 사용된다


 코드를 작성할 때, <? extends (상한)> 과 같이 작성한다. 예를 들어 List<Integer>, List<Double>, List<Number>에만 적용되는 메소드를 작성하고 싶다면, Integer, Double 클래스는 모두 Number 클래스를 상속받기 때문에 아래와 같이 작성하면 된다.

 

public static void exMethod(List<? extends Number> list) { ... }

 

 List<? extends Number>의 의미는 Number를 상속받은 어떤 클래스도 ? 자리에 올 수 있다는 것을 의미한다. 이는List<Number>보다는 적용 대상을 넓힌 것이다. List<Number>는 타입 매개변수로 Number에 대해서만 매치되지만 List<? extends Number>Number의 자식클래스까지도 타입 매개변수로 매치되기 때문이다.

 

public static double sumOfList(List<? extends Number> list) {

double s = 0.0;

 

for(Number n : list) {

s += n.doubleValue();

}

 

return s;

}


public static void main(String[] args) {

List<Integer> li = Arrays.asList(1, 2, 3, 4, 5);

System.out.println(“sum = ” + sumOfList(li));

}

 

[출력 결과]

List<Integer> 타입의 li 가 List<? extends Number> 타입을 매개변수로 받는 sumOfList의 메소드에 적용된다.



하한이 있는 와일드 카드 : 특정한 타입을 가진 모든 객체에 대해 작동할 때 사용되는 카드로, <? super (하한)>와 같은 문법을 사용한다.


 예를 들어 Integer 객체를 가질 수 있는 모든 객체를 리스트에 추가하는 메소드를 작성한다면, List<Integer>, List<Number>, List<Object>와 같은 Integer 값을 가지고 있는 모든 객체에 대하여 해당 메소드를 적용시킬 수 있다.

 

public static void addNumbers(List<? super Integer> list) {

for(int i = 1; I <= 10; i++) {

list.add(i);

}

}

 


한도가 없는 와일드 카드 : 모든 타입에 매치되는 와일드 카드로, 단순히 <?> 표현된다. , List<?> 라고 코드를 작성하면 모든 타입의 리스트를 사용하게 되는 것이다.


 주의할 점은 List<?>List<Object>는 혼동할 수 있으나 차이가 있다는 것이다.

 

public static void printList(List<Object> list) {

for(Object element : list) {

System.out.println(element + ", ");

}


System.out.println();

}

 

 위의 경우 printList() 메소드는 Object 객체의 리스트(List<Object>)만 출력할 수 있다. 그 이유는 List<Integer>, List<String>, List<Double>와 같은 클래스들은 List<Object>의 자식이 아니기 때문이다.


 앞에서 언급했듯이 타입 매개변수 간의 상속관계가 성립한 것이지 클래스간의 상속관계는 성립되지 않았다.


 아래는 모든 타입에 매치되는 리스트를 매개변수로 받을 수 있는 올바르게 작성된 코드이다.

 

public static void printList(List<?> list) {

for(Object element : list) {

System.out.println(element + “, ”);

}


System.out.println();

}

 

 





 

패키지(Package)

 

키지(package) : 관련 있는 클래스나 인터페이스들을 묶은 것이다.

 

클래스를 찾아서 사용하기 쉬워지며 서로 다른 내용의 동일 이름의 충돌을 막을 수 있고 접근을 제어할 수 있다.

 

자바가 제공하는 라이브러리도 기능별로 패키지로 묶여서 제공되고 있다.

  • 기초적인 기능 제공 패키지 : java.lang

  • 네트워크 담당 기능 패키지 : java.net

 

- 그러나 사용자가 직접 패키지를 만들어 사용할 수도 있다. src 폴더 아래에 패키지 폴더를 생성하면, 그 아래 소스 파일 맨 위에 package 키워드를 기재해야 한다.


package (패키지명); //끝에 세미콜론 필수


public class (클래스명) {

...

}


- 사용자 라이브러리 패키지의 예는 아래와 같다.

 

package graphics;


//Drawable.java 파일로 저장

public interface Drawable {

...

} 


package graphics;


//Shape.java 파일로 저장

public abstract class Shape {

...

}


package graphics;


//Circle.java 파일로 저장

public class Circle extends Shape implements Drawable {

...

}


package graphics;


//Rectangle.java 파일로 저장

public class Rectangle extends Shape implements Drawable {

...

}



-  패키지를 이용하면 개발자는 다양한 장점을 가질 수 있다.

  • 개발자들은 클래스들이 서로 연관되어 있음을 쉽게 알 수 있다.

  • 개발자들은 그래픽을 제공하는 클래스들을 쉽게 찾을 수 있다.

  • 패키지마다 독립적인 이름 공간을 가지므로 다른 패키지의 동일한 이름의 클래스가 있더라도 충돌을 일으키지 않는다.


- 각각의 패키지는 접근에 제약을 가할 수 있으므로 동일 패키지 안의 클래스들은 서로 간에 자유롭게 사용할 수 있고 다른 패키지는 그러지 못하게 설정할 수 있다.

 

위에서 지정한 패키지는 실제로 자바의 java.awt 패키지에 있는 클래스와 동일한 이름의 클래스를 가지고 있다. 그러나 자바는 서로 다른 패키지에서 동일한 이름의 클래스를 허용한다.

  • 기존의 클래스 이름 : java.awt.Rectangle

  • 사용자 클래스 이름 : graphics.Rectangle

 

, 패키지 이름이 같으면 동일한 이름이 허용되지 않으며, 이를 위한 규칙이 존재한다.

  • 패키지의 이름은 클래스나 인터페이스의 이름과의 중복을 피하기 위해 일반적으로 소문자만 사용한다.

  • 패키지 이름으로 인터넷 도메인 이름의 역순을 사용한다. 기업에서는 회사명.부서명.프로젝트이름으로 짓는 경우가 많다. 예를 들어, A회사의 Network 부서의 wifi라는 프로젝트이름을 가지고 패키지를 짓는다면 network.A.wifi 가 패키지명이 될 것이다.

  • 자바 언어 자체의 패키지는 javajavax로 시작한다.


 

 

패키지 사용하기

 

패키지 안에 포함된 클래스인터페이스패키지 멤버라고 불린다.

 

- 위에서 설명했듯이 패키지 내에서는 클래스와 인터페이스 간의 참조가 자유롭지만 다른 패키지 간의 멤버에 서로 접근할 때는 제약이 가해진다. 따라서 제약 조건인 아래의 세 가지 방법을 통해 외부 패키지로 접근할 수 있다.

  • 경로까지 포함하는 완전한 이름으로 참조하는 방법

  • 원하는 패키지 멤버만을 import 하는 방법

  • 패키지 전체를 import 하는 방법

 

완전한 이름으로 참조 : 외부 패키지에 있는 클래스를 사용하려면 클래스의 완전한 이름을 써주어야 한다. 예를 들어 아까 만들었던 graphics 패키지의 Rectangle 클래스를 외부 패키지에서 사용한다면 아래와 같이 기재해야 한다.


graphics.Rectangle rect = new graphics.Rectangle();


자주 사용하지 않는 클래스의 경우 이 방식을 주로 사용한다.



패키지 멤버를 import : 외부 패키지의 특정한 멤버를 import 하려면 다음과 같은 문장을 사용한다.


import graphics.Rectangle;

...

Rectangle rect = new Rectangle();


 

전체 패키지 import : 하나의 패키지 안의 모든 멤버를 사용하고자 할 때 사용한다.


import graphics.*;

...

Circle circle = new Circle();

Rectangle rect = new Rectangle();


 

똑같은 이름의 클래스를 가지는 패키지가 동시에 import 된 경우, 모호성을 제거하기 위해 정식 이름을 사용하여야 한다.


import A;

import B;

...

A.ClassOrder = new A.ClassOrder(); //패키지 AClassOrder 클래스 사용

B.ClassOrder = new B.ClassOrder(); //패키지 BClassOrder 클래스 사용

 

 

 

계층 구조의 패키지

 

하나의 패키지 안에 또 다른 패키지가 있을 수 있다. 그러나 상위 패키지라는 개념이 자바에는 적용되지 않는다.

 

예를 들어 java.awt 패키지 안에는 font 패키지가 있으나 java.awt 패키지만 import해서는 font 패키지를 사용할 수 없다. , font 패키지와 awt 패키지 모두 사용하고 싶다면 아래와 같은 방법이어야 한다.

  • java.awt.*;

  • java.awt.font.*;

쉽게 말해, java.awt 패키지와 java.awt.font 패키지는 서로 다른 패키지이다.

 

 

 

정적 import 문장

 

클래스 안에 정의된 정적 상수정적 메소드를 사용하는 경우에는 일반적으로 클래스 이름을 앞에 적어서 사용한다.

 

예를 들어, java.lang.Math 클래스 안에는 PI가 상수로 정의되어 있고, sin(), cos(), tan()와 같은 수많은 정적 메소드들이 정의되어 있다. 이들을 사용하려면 아래와 같이 클래스 이름을 앞에 붙여야 한다.

 

double r = Math.cos(Math.PI*theta);



정적 import 문장을 사용하면 클래스 이름은 생략하여도 된다.

 

import static java.lang.Math.*;

...

double r = cos(PI*theta);

 

너무 남용하면 읽기 어려운 코드가 되니 주의해야 한다.



 

소스 파일과 클래스 파일 관리

 

자바는 계층 디렉토리 구조를 이용하여 소스 파일과 클래스 파일을 관리한다.

 

, 패키지의 계층 구조를 반영한 디렉토리 구조에 소스들을 저장한다.

 

실제 자바 파일은 (기반 디렉토리)\(패키지명)\(클래스명).java 로 저장된다.

 

여기서 기반 디렉토리란 작업 디렉토리가 된다. 예를 들어, 이클립스에서 설정했던 워크스페이스(workspace)가 작업 디렉토리이다.

 

패키지의 멤버의 완전한 이름과 파일의 경로 이름과는 일치하여야 한다.

  • 완전한 이름 : graphics.Rectangle

  • 파일의 경로 이름 : graphics\Rectangle.java

 

소스 파일을 컴파일하면 컴파일러는 각 클래스들을 서로 다른 출력 파일로 저장한다. 출력 파일의 이름은 클래스 이름과 같고, 확장자는 “.class”이다.

 

소스 파일들과 마찬가지로 클래스 파일도 패키지 이름을 반영하는 디렉토리 구조에 저장된다. 참고로 클래스 파일들에 대한 경로는 반드시 자바 소스 파일에 대한 경로와 같을 필요는 없다.

  • 소스 파일 경로 → C:\source\com\company\graphics\Rectangle.java

  • 클래스 파일 경로  C:\class\com\company\graphics\Rectangle.class

 

, 소스와 클래스 파일을 서로 다른 위치에 저장하려면 컴파일러와 자바 가상 기계(JVM)가 이 파일들을 찾을 수 있도록 설정하여야 한다.

 

위에 서술한 클래스 파일 경로의 일부분 “C:\class”는 실행에 필요한 클래스 파일들이 저장되는 디렉토리이다. 이를 클래스 경로(class path)”라고 하며, 시스템 변수 CLASSPATH에 저장되어 있어야 한다.

 

자바 컴파일러와 자바 가상 기계(JVM)는 모두 .class 파일에 대한 경로를 만들 때, 클래스 경로에 패키지 이름을 붙여서 만든다. , 클래스 경로가 “C:\class”로 설정하고 패키지 이름이 com.company.graphics 라면 컴파일러와 JVM이 클래스 파일을 찾는 디렉토리는 다음과 같다.

C:\class\com\company\graphics

 

클래스 경로의 디폴트 값으로 컴파일러와 JVM은 현재 디렉토리와 자바 플랫폼 클래스들을 포함하고 있는 JAR 파일을 탐색한다.

 

 

 

JAR 파일

 

클래스 파일은 JAR(Java archive) 파일 형태로 저장될 수 있다.

 

- JAR 파일은 여러 개의 클래스 파일을 디렉토리의 계층 구조를 유지한 채로 압축하여서 가지고 있을 수 있다.

 

실행 파일(.exe)을 만들 때도 JAR 파일이 이용된다.

 

 

 

자바에서 지원하는 패키지


java.applet

 애플릿을 생성하는 데 필요한 클래스

 java.awt

 그래픽과 이미지를 위한 클래스

 java.beans

 자바빈즈 구조에 기초한 컴포넌트를 개발하는 데 필요한 클래스

 java.io

 입력과 출력 스트림을 위한 클래스

 java.lang

 자바 프로그래밍 언어에 필수적인 클래스

 java.math

 수학에 관련된 클래스

 java.net

 네트워킹 클래스

 java.nio

 새로운 네트워킹 클래스

 java.rmi

 원격 메소드 호출(RMI) 관련 클래스

 java.security

 보안 프레임워크를 위한 클래스와 인터페이스

 java.sql

 데이터베이스에 저장된 데이터를 접근하기 위한 클래스

 java.util

 날짜, 난수 생성기 등의 유틸리티 클래스

 javax.imageio

 자바 이미지 입출력 API

 javax.net

 네트워킹 애플리케이션을 위한 클래스

 javax.swing

 스윙 컴포넌트를 위한 클래스

 javax.xml 

 XML을 지원하는 패키


 


 

자주 사용되는 java.lang 패키지

 

- 위의 표에 나와있듯 java.lang 패키지는 자바 프로그래밍 언어에 필수적인 클래스를 포함하고 있다.


 클래스 이름

설명 

 Object

 기초적인 메소드를 제공하는 모든 클래스의 최상위 클래스

 Math

 각종 수학 함수들을 포함하는 클래스

 지수나 로그제곱근삼각함수 등과 같은 기본적인 수치 연산을 위한 메소드들을 제공한다.

 Wrapper

 Integer와 같이 기초 자료형을 감싸서 제공하는 랩퍼 클래스

 String, StringBuffer

 문자열을 다루는 클래스

 System

 시스템 정보를 제공하거나 입출력을 제공하는 클래스

 Thread

 스레드 기능을 제공하는 클래스

 Class

 객체를 생성한 클래스에 대한 정보를 얻기 위한 클래스



- Class 클래스 : 실행 중인 자바 애플리케이션 안에 포함된 클래스들과 인터페이스를 나타낸다. Class는 생성자가 없다. 대신에 해당 객체는 자바 가상 기계에 의하여 자동적으로 생성된다. 객체 자체를 출력, 객체의 이름만 출력 등을 수행하는 메소드가 포함된다.


 

- System 클래스 : 실행 시스템과 관련된 속성과 메소드를 제공한다. 예시로 우리가 자주 사용하는 콘솔 출력 메소드인 System.out.println() 이 바로 System 클래스가 제공하는 메소드 중의 하나이다. System 클래스 안에 들어 있는 outPrintStream 타입의 객체로 정적 변수로 선언되어 있으며 모니터를 나타낸다. 반면, inInputStream 타입의 객체로 정적 변수이며 키보드를 나타낸다.


 필드

 static PrintStream err

 표준 오류 출력 스트림

 static InputStream in

 표준 입력 스트림

 static PrintStream out 

 표준 출력 스트림

 주요 메소드

 static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

 지정된 소스 배열을 목적지 배열로 복사한다.

 static long currentTimeMillis()

 밀리초 단위로 현재 시각을 반환한다.

 static void exit(int status)

 현재 실행 중인 자바 가상 기계를 중단한다.

 static String getenv(String name)

 지정된 환경 변수의 값을 얻는다.

 static String getProperty(String key)

 키에 의해서 지정된 시스템의 특성을 얻는다.

 static long nanoTime()

 나노초 단위로 현재 시각을 반환한다.

 


Wrapper 클래스 : 기초 자료형을 객체로 포장할 때 사용하는 클래스이다.

 

기초자료형

 랩퍼 클래스

 byte

 Byte

 short

 Short

 int

 Integer

 long

 Long

 float

 Float

 double

 Double

 char

 Character

 boolean

 Boolean

 void

 Void


  • 랩퍼 클래스에서 가장 많이 사용되는 메소드는 기초 자료형을 문자열로 변환하거나, 문자열을 기초 자료형으로 변환하는 메소드이다.

Integer 클래스 제공하는 일부 메소드

 static int intValue()

 int형으로 변환한다.

 static double doubleValue()

 double형으로 변환한다.

 static float floatValue()

 float형으로 변환한다.

 static int parseInt(String s)

 문자열을 int형으로 변환한다.

 static String toBinaryString(int i)

 int형의 정수를 2진수 형태의 문자열로 변환한다.

 static String toHexString(int i)

 int형의 정수를 16진수 형태의 문자열로 변환한다.

 static String toOctalString(int i)

 int형의 정수를 8진수 형태의 문자열로 변환한다.

 static String toString(int i)

 int형의 정수를 10진수 형태의 문자열로 변환한다.

 static Integer valueOf(String s)

 문자열 s를 Integer 객체로 변환한다.

 static Integer valueOf(String s, int radix)

 문자열 s radix 법의 Integer객체로 변환한다. 

  • 이 메소드들은 많이 사용되고, 정적 메소드이므로 객체를 생성하지 않고 클래스의 이름에 도트 연산자를 붙여서 사용할 수 있다.

  • 자바는 Wrapper 객체와 기초 자료형 사이의 변환을 자동으로 하여 주는 오토 박싱(auto-boxing)"기능을 제공한다.

Integer box;

box = 10; //정수를 자동으로 Integer 객체로 포장한다.(boxing)

System.out.println(box+1); //box는 자동으로 int형으로 변환된다.(unboxing)



- StringBuffer 클래스 : 변경 가능한 문자열을 저장하는 클래스이다.


 String 클래스는 주로 상수 문자열, 즉 변경이 불가능한 문자열을 나타낼 때 사용된다. String 클래스의 경우 빈번하게 문자열을 변경하는 경우에는 비효율적일 수 있다. 예를 들어 문자열 연산자인 + 연산자를 사용할 경우, 연산을 할 때마다 새로운 객체가 생성되고 기존의 String 객체도 힙 영역에 남아있게 되므로 기존 객체를 사용하지 않으면 메모리 공간을 낭비하게 된다


 이 때 변경 가능한 문자열을 위한 위 상황의 해결책으로 StringBufferStringBuilder 클래스가 제공된다. 두 클래스는 유사한 메소드를 가지고 있으나 속도 면에서 차이가 있다. 이는 나중에 알아보도록 하고, 가장 큰 차이는 StringBuffer는 다중 스레드 환경에서 안전하다는 것이고, 다중 스레드 환경이 아닐 때는 StringBuilder가 더 효율적이다. 따라서 스레드를 사용하지 않는다면 StringBuilder 객체를 사용하는 것이 좋다.


 StringBufferStringBuilder 객체 모두 내부적으로 문자열을 저장하는 메모리를 가지고 있다. 이 메모리를 버퍼(buffer)라고 하며, 가변적이기 때문에 문자열 연산에서 빼진 문자열은 저장하지 않고 지운다. , 버퍼의 크기는 자동적으로 조정된다.


  • StringBuffer 클래스 설명 (메소드의 경우 StringBuilder 클래스의 메소드명과 일치하므로 필요할 때 StringBuilder 클래스의 해당 메소드이름을 호출하여도 된다.)

 생성자

 StringBuffer()

 버퍼가 비어 있는 StringBuffer 객체를 생성한다.

 StringBuffer(String s)

 매개변수로 넘겨 받은 문자열을 초기값으로 버퍼에 저장한  StringBuffer 객체를 생성한다.

 메소드

 StringBuffer append(String s)

 인수는 먼저 문자열로 변환되어서 문자열 뒤에 추가된다.

 StringBuffer append(char[] str)

 StringBuffer append(char[] str, int offset, int len)

 StringBuffer delete(int start, int end)

 지정된 문자를 삭제한다.

 StringBuffer deleteCharAt(int index)

 StringBuffer insert(int offset, char[] str)

 offset은 시작 위치를 의미하며, offset부터 두 번째 매개변수로 주어진 배열을 문자열로 변환한 뒤 삽입한다.

 StringBuffer insert(int index, char[] str, int offset, int len)

 StringBuffer replace(int start, int end, String s)

 start부터 end까지의 위치를 문자열 s로 변경한다이 때 start위치는 포함되나 end 위치는 포함되지 않는다.

 void setCharAt(int index, char c)  해당 인덱스의 문자를 매개변수로 받은 문자(c)로 설정
 StringBuffer reverse() 지정된 문자들의 순서를 역순으로 한다.





java.util 패키지

 

- Random 클래스 : 난수를 얻는데 사용된다

  • 48 비트 길이의 시드(seed)를 사용하여 알고리즘으로는 변형된 선형 합동 수식을 이용한다

  • 동일한 시드를 이용하여서 두 개의 Random 객체가 생성된다면 동일한 난수를 발생하게 된다.

 생성자

 Random()

 새로운 난수 발생기 객체를 생성한다.

 Random(longseed seed)

 주어진 시드를 사용하는 새로운 난수 발생기 객체를 생성한다.

 메소드

 protected int next(int bits)

 다음 난수를 반환한다.

 boolean nextBoolean()

 boolean 형의 다음 난수를 반환한다.

 void nextBytes(byte[] bytes)

 byte형의 난수를 발생하여서 주어진 배열을 채운다.

 double nextDouble()

 double 형의 0.0과 1.0 사이의 난수를 반환한다.

 float nextFloat()

 float 형의 0.0과 1.0 사이의 난수를 반환한다.

 int nextInt()

 int형의 난수를 반환한다.

 int nextIng(int n)

 0과 n사이의 int형의 난수를 발생한다.

 long nextLong()

 long 형의 난수를 발생한다.

 void setSeed(long seed) 

 시드를 설정한다.


  • Random 예시

import java.util.Random;
 
public class RandomTest {
	public static void main(String[] args) {
		Random random = new Random();
		for(int i = 0; i < 10; i++) {
			System.out.println(random.nextInt(100)); //0~100사이의 난수를 발생한다.
		}
	}
}


- Arrays 클래스 : 배열을 다루는 다양한 메소드들을 가지고 있다. 대표적으로 정렬 및 탐색과 같은 메소드가 있고, 배열을 리스트로 보게 하는 정적 팩토리 메소드도 제공한다. 이 클래스는 다음에 배울 자바 컬렉션 프레임 워크에 속한다.


메소드

 static List asList(Object[] a)

 주어진 배열을 고정 길이의 리스트로 변환한다.

 static int binarySearch(int[] a, int key)

 주어진 값을 int형의 배열에서 이진 탐색한다.

 static int binarySearch(Object[] a, Object key)

 Object 타입의 배열에서 key를 이진 탐색하여 해당 key의 인덱스를 반환한다.

 static int[] copyOf(int[] original, int len)

 주어진 배열을 새로운 크기의 배열에 길이 len만큼 복사한다. (0~len)

 static int[] copyOfRange(int[] original, int from, int to)

 배열에서 주어진 구간의 값들을 새로운 배열로 복사한다.

 static boolean equals(int[] a, int[] a2)

 주어진 두 개의 배열이 같으면 true를 반환한다.

 static void fill(int[] a, int val)  주어진 val 값을 가지고 배열을 채운다.
 static void sort(int[] a) 지정된 int형의 배열을 정렬한다.


  • 위의 메소드는 double 형의 배열, float 형의 배열 등의 다양한 타입의 배열에도 타입만 다르게 하여 사용가능하다.

  • 정적메소드이므로 "클래스이름.메소드이름" 으로 호출해야 한다.

  • 추가로 java.lang.reflect 패키지 안에 Array 라는 클래스가 존재한다. 따라서 import 할 때 스펠링을 잘보고 Arrays 클래스와 Array 클래스를 구별하는데 주의해야 한다.

 

- Date 클래스 : 밀리초 단위로 현재 시각을 나타낸다. Date 객체를 연도, , 일로 변환하는 메소드가 있다. 현재는 국제화에 맞지 않아 권장되지 않는다.


Date date = new Date(); //현재 날짜를 Date 객체로 생성한다.



- Calendar 클래스 : 추상 크래스로서 날짜와 시간에 대한 정보를 가지고 있고 특정 시각을 연도, , 일 등으로 변한하는 메소드도 가지고 있다. 시각은 197011일부터 흘러온 시간으로 나타낸다.

 

Calendar calendar = Calendar.getInstance(); //현재 시각을 나타내는 Calendar 객체 얻는 방법

  • int 형의 정적 상수들이 선언되어 있고 이 상수들을 이용하여서 특정 시각에서 날짜와 시간에 대한 정보를 추출할 수 있다. 상수에 대한 정보는 아래의 사이트를 참조하면 자세히 나와있다.

https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html

 


- StringTokenizer 클래스 : 문자열을 분석하여 토큰으로 분리시켜 주는 기능을 제공한다

  • 토큰이란 문법적으로 더 이상 나눌 수 없는 기본적인 언어요소를 의미하며, 키워드, 연산자 또는 구두점이 될 수 있다. 예를 들어 오늘은 2017612일입니다.”라는 문자열에서 “2017”, “6”, “12”만 따로 가져오고 싶을 때 이 클래스를 사용하면 된다.

 

생성자

 StringTokenizer(String str)

 주어진 문자열을 위한 StringTokenizer 객체를 생성한다구분자의 디폴트 값은 공백(“ ”)이다.

 StringTokenizer(String str, String delim)

 주어진 문자열을 구분자 delim을 기준으로 분할하는 StringTokenizer 객체를 생성한다.

 StringTokenizer(String str, String delim, boolean returnDelims)

 returnDelims는 분리자를 포함하여 분할할 거면 true를 포함하지 않고 분할할 거면 false로 설정하면 된다.

 메소드

 int countTokens()

 문자열에 존재하는 토큰의 개수를 반환한다.

 boolean hasMoreTokens()

 다음 토큰을 가지는지를 반환한다.

 String nextToken()

 다음 토큰을 반환한다.

 String nextToken(String delim)

 다음 토큰을 반환하고 분리자를 delim으로 변경한다.


  • StringTokenizer 예시

import java.util.*;
 
public class StringTest {
    public static void main(String[] args) {
        StringTokenizer st = new StringTokenizer(“I just wanna be a programmer”);

        while(st.hasMoreTokens()) { //공백으로 분할했을 때 토큰이 남아있다면,
            System.out.println(st.nextToken()); //다음 토큰을 가져온다.
        }
    }
} 


결과

I

just

wanna

be

a

programmer









 JTree

 



트리는 계층적인 데이터를 표시하는데 사용되는 컴포넌트이다.

 

- JTree는 단순히 데이터의 뷰만을 제공하기 때문에 트리 객체 안에는 데이터가 저장되지 않는다.

 

각각의 트리는 하나의 루트 노드(root node)를 가지고 있다다른 노드들(child)은 루트 노드에서 파생된다.

 

노드는 자신의 자식 노드를 가질 수 있고사용자가 노드를 클릭하면 자식 노드를 보이게 할 수도 있다.

 

트리는 스크롤 페인(JScrollPane) 속에 넣는 것이 일반적이다.

 

노드의 확장 상태는 이벤트를 사용하여 감지할 수 있다.


 JTree

 생성자

 JTree()

 비어있는 트리를 생성한다.

 JTree(TreeNode root)

 매개변수로 주어진 노드를 루트 노드로 하는 트리를 생성한다.

 메소드

 void setVisibleRowCount(int row)

 화면에 나타나는 행의 수를 설정한다.

 Object getLastSelectedPathComponent()

 현재 선택된 노드를 반환한다.

 ThreeSelectionModel getSelectionModel()

 트리의 선택 모델을 반환한다.

 Object getUserObject()

 노드로 지정된 객체를 반환한다

 보통 이벤트 처리 시 노드의 이름을 가져올 때 사용한다.

 노드 생성자

 DefaultMutableTreeNode()

 비어있는 트리 노드를 생성한다.

 DefaultMutableTreeNode(Object obj)

 지정된 객체를 가지고 트리 노드를 생성한다.

 노드 수정 메소드

 void add(TreeNode child)

 자식 노드를 추가한다.

 TreeNode getFirstChild() 현재 노드의 첫 번째 자식 노드를 반환한다.
 DefaultMutableTreeNode getNextSibling() 현재 노드의 다음 형제 노드를 반환한다.
 TreeNode getParent() 현재 노드의 부모 노드를 반환한다.

 이벤트 처리

 addTreeSelectionListener(리스너 타입)

 노드를 선택할 때마다 트리 선택 이벤트(TreeSelectionEvent)가 발생하며, TreeSelectionListener 인터페이스를 구현함으로서 이벤트를 처리한다.






- JTree 예제

public class TreeTest extends JFrame implements addTreeSelectionListener {

	//FIELDS
	private JPanel treePanel = new JPanel();
	private JScrollPane treeScroll;
	private JTree tree;
	private JLabel label;
	private DefaultTreeModel model; //트리 모델 인스턴스
	
	//CONSTRUCTOR
	TreeTest() {
		setTitle("트리 예제");
		setSize(400, 500);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		DefaultMutableTreeNode root = new DefaultMutableTreeNode("플레이 리스트");
		DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("힙합");
		DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("재즈");
		DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("락");
		DefaultMutableTreeNode child4 = new DefaultMutableTreeNode("가요");
		DefaultMutableTreeNode child5 = new DefaultMutableTreeNode("팝송");
		DefaultMutableTreeNode child6 = new DefaultMutableTreeNode("동요");

		root.add(child1);
		root.add(child2);
		root.add(child3);
		root.add(child4);
		root.add(child5);
		root.add(child6);

		DefaultMutableTreeNode child6child1 = new DefaultMutableTreeNode("아기공룡둘리");
		DefaultMutableTreeNode child6child2 = new DefaultMutableTreeNode("네모의 꿈");
		DefaultMutableTreeNode child6child3 = new DefaultMutableTreeNode("민들레꽃");
		
		child6.add(child6child1);
		child6.add(child6child2);
		child6.add(child6child3);

		tree = new JTree(root);

		/* 또는 아래와 같이 하여도 된다.
		tree = new JTree();
		model = new DefaultTreeModel(root);
		tree.setModel(model);
		*/

		tree.setVisibleRowCount(10);
		tree.addTreeSelectionListener(this);
		
		treeScroll = new JScrollPane(tree);
		panel.add(treeScroll);
		panel.add(label);
		add(panel);

		setVisible(true);
	}

	//METHODS
	@Override
	public void valueChanged(TreeSelectionEvent e) {
		DefaultMutableTreeNode node; 
		node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
		
		if(node == null) return;

		String nodeName = (String) node.getUserObject(); //캐스팅은 꼭해주어야 한다.
		label.setText("선택된 노드 : " + nodeName);
	}

	public static void main(String[] args) {
		new TreeTest();
	}
}





 

 

 

 JSpinner




스피너는 두 개의 화살표가 붙은 텍스트 필드이다.

 

텍스트 필드는 숫자를 표현하고 있고 사용자는 화살표를 클릭하여 값들을 증가 또는 감소시킬 수 있다.

 

주로 지정된 범위에서 개수를 정하거나날짜를 선택할 때 사용한다그 외 다른 용도로도 사용가능하다.

 

JSpinner

 생성자

  JSpinner()

  정수를 가지고 있는 SpinnerNumberModel이 초기값으로 0이 되어 새로운 스피너 객체를 생성한다.

  JSpinner(SpinnerModel sm)

 새로운 스피너 모델을 지정받은 스피너 객체를 생성한다.

 메소드

  void setValue(Object obj)

 값을 설정한다.

  Object getValue()

 선택된 값을 반환한다.

  Object getNextValue() 

 선택된 값의 다음 값을 반환한다.

  Object getPreviousValue()

 선택된 값의 이전 값을 반환한다.


 

스피너 모델(SpinnerModel를 상속받는다.)


String[] names = {“김철수”, “김영희”, “김숙자”, “김자몽”};

SpinnerListModel listModel = new SpinnerListModel(names);

 

SpinnerNumberModel numberModel = new SpinnerNumberModel(0, 1, 2, 3, 4, 5);


//최소날짜(start)와 최대날짜(end)를 지정할 수 있다. Calendar 객체는 증가&감소가 된다.

SpinnerDataModel dateModel = new SpinnerDateModel(value, start, end, Calendar.YEAR); 


- 스피너 모델은 총 4가지가 있다.

  • AbstractSpinnerModel

  • SpinnerDateModel

  • SpinnerListModel

  • SpinnerNumberModel







- JSpinner 예제

 

public class SpinnerTest extends JFrame {

	//FIELDS
	private String[] name = {"김철수", "김숙자", "김영희", "김자몽"};
	private JPanel panel = new JPanel();
	private JSpinner spinner;

	//CONSTRUCTOR
	SpinnerTest() {
		setTitle("스피너 예제");
		setSize(500, 200);
		setDefaultCloseOperation(JFrame.EXITE_ON_CLOSE);

		// List Model
		SpinnerListModel listModel = new SpinnerListModel(name);
		spinner = new JSpinner(listModel);
		panel.add(spinner);

		// Number Model
		SpinnerNumberModel numberModel = new SpinnerNumberModel(1, 2, 3, 4, 5, 6);
		spinner = new JSpinner(numberModel);
		panel.add(spinner);

		// Date Model
		Calendar calendar = Calendar.getInstance(); //갤린더 객체 얻기
		Date value = calendar.getTime(); //현재 시간 얻기
		
		calendar.add(Calendar.YEAR, -50); // 50년 전의 날짜 얻기
		Date start = calendar.getTime(); //50년 전의 날짜를 최소 날짜로 지정
		
		calendar.add(Calendar.YEAR, 100); // 50년 뒤의 날짜 얻기 +50하면 현재날짜가됨.
		Date end = calendar.getTime(); //50년 뒤의 날짜를 최대 날짜로 지정

		SpinnerDateModel dateModel = new SpinnerDateModel(value, start, end, Calnedar.YEAR);
		spinner = new JSpinner(datemodel);
		spinner.setEditor(new JSpinner.DateEditor(spinner, "yyyy/MM/dd")); //날짜 편집기 지정
		panel.add(spinner);

		add(panel);
		setVisible(true);
	}

	//METHODS
	public static void main(String[] args) {
		new SpinnerTest();
	}
}


'Programming Language > JAVA' 카테고리의 다른 글

29. JTree - 트리  (0) 2017.06.29
28. JSlider - 슬라이더  (0) 2017.06.29
26. JComboBox - 콤보박스  (0) 2017.06.29
25. JList - 리스트  (0) 2017.06.25
24. JScrollPane - 스크롤 페인  (0) 2017.06.25

+ Recent posts