jsch 라이브러를 이용하여 자주 쓰는 기능을 작성

Feature

  • ssh 접속하여 shell 명령을 실행 시키고 stdout 결과를 String List 가져온다
  • 여러 개의 shell 명령을 순차적으로 실행 시키고 stdout 결과를 가져온다
  • 로컬 파일을 ssh로 복사한다
  • ssh로 원격으로 파일을 로컬로 가져온다.
  • ssh key쌍(개인키와 공개키)을  생성한다.

Requirements

Code

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import com.jcraft.jsch.Session;

public class JSchUtil {
	private String hostname;
	private String username;
	private String identity=null;
	private String password=null;
	private boolean isDebugMode=false;

	public void enableDebug(){
		isDebugMode=true;
	}

	public void disableDebug(){
		isDebugMode=false;
	}

	public String getHostname() {
		return hostname;
	}

	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}

	public String getIdentity() {
		return identity;
	}

	public void setIdentity(String identity) {
		this.identity = identity;
		this.password =null;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
		this.identity=null;
	}

	public JSchUtil(){ }

	public JSchUtil(String username,String hostname){
		this.username = username;
		this.hostname = hostname;
	}

	public void setPortForwardingL(int port,String host,int hostport){

	}

	private Session getSession() throws JSchException{
		JSch jsch=new JSch();
		if (identity!=null) {
			jsch.addIdentity(identity);
			//jsch.setKnownHosts(new ByteArrayInputStream(hostname.getBytes()));
		}

		Session session=jsch.getSession(username, hostname, 22);
		session.setConfig("StrictHostKeyChecking", "no");
		if (password!=null)	session.setPassword(password);
		return session;
	}

	public String exec(String command){
		return exec(new String[] {command}).get(0);
	}
	public List<String> exec(List<String> commands){
		return exec(commands.toArray(new String[]{}));
	}


	public List<String> exec(String[] commands) {
		List<String> ret = new ArrayList<String>();
		try{
			Session session = getSession();
			session.connect();
			for (String command:commands){
				ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
				channelExec.setPty(true);
				if (isDebugMode) System.out.println("command : "+command);
				channelExec.setCommand(command);
				InputStream inputStream = channelExec.getInputStream();
				//InputStream ext = channelExec.getExtInputStream();
				InputStream err = channelExec.getErrStream();
				channelExec.connect(3000);

				if (isDebugMode) System.out.print("stdout : ");
				String output="";
				byte[] buf = new byte[1024];
				int length;
				while ((length=inputStream.read(buf))!=-1){
					output+=new String(buf,0,length);
					if (isDebugMode) System.out.print(new String(buf,0,length));
				}
				if (isDebugMode) System.out.println("\nerr : "+IOUtils.toString(err));
				ret.add(StringUtils.chop(output));
				channelExec.disconnect();
			}
			session.disconnect();
		}catch(Exception e){
			e.printStackTrace();
		}
		return ret;
	}


	/* ---- generate ssh keypair  --------- */

	public Map<String,String> keyGen(String algorithm, String passphrase,String comment){
		String privateKeyString = "";
		String publicKeyString = "";

		int type=0;
		if(algorithm.toLowerCase().equals("rsa")) type=KeyPair.RSA;
		else if(algorithm.toLowerCase().equals("dsa"))type=KeyPair.DSA;
		else {
			System.err.println("does not support "+algorithm+" algorithm");
			return null;
		}

		JSch jsch=new JSch();
		try{
			KeyPair kpair=KeyPair.genKeyPair(jsch, type);
			kpair.setPassphrase(passphrase);
			ByteArrayOutputStream priout = new ByteArrayOutputStream();
			ByteArrayOutputStream pubout = new ByteArrayOutputStream();
			kpair.writePrivateKey(priout);
			kpair.writePublicKey(pubout, comment);

			privateKeyString = priout.toString();
			publicKeyString = pubout.toString();
			if (isDebugMode) {
				System.out.println("Private Key : \n"+privateKeyString);
				System.out.println("Public Key : \n"+publicKeyString);
				System.out.println("Finger print: "+kpair.getFingerPrint());
			}
			kpair.dispose();
		}
		catch(Exception e){
			e.printStackTrace();
			//System.out.println(e);
		}

		Map<String,String> map = new HashMap<String,String>();
		map.put("privateKey",privateKeyString);
		map.put("publicKey",publicKeyString);
		return map;
	}

	/* Scp ----------------------- */

	public String scpFrom(String rfile){
		String str ="";
		try{
			File lfile = File.createTempFile("temp", ".tmp");
			BufferedReader br = new BufferedReader(new FileReader(lfile));
			scpFrom(rfile,lfile);
			String line;
			while((line=br.readLine())!=null) str+=line+"\n";
			br.close();
			lfile.delete();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		return str;
	}
	public void scpFrom(String rfile,File lfile){
		//usage: java ScpFrom user@remotehost:file1 file2
		FileOutputStream fos=null;
		try{

			Session session = getSession();
			// username and password will be given via UserInfo interface.
			session.connect();

			// exec 'scp -f rfile' remotely
			String command="scp -f "+rfile;
			Channel channel=session.openChannel("exec");
			((ChannelExec)channel).setCommand(command);

			// get I/O streams for remote scp
			OutputStream out=channel.getOutputStream();
			InputStream in=channel.getInputStream();

			channel.connect();

			byte[] buf=new byte[1024];

			// invoke '\0'
			buf[0]=0; out.write(buf, 0, 1); out.flush();

			while(true){
				int c=checkAck(in);
				if(c!='C'){
					break;
				}

				// read '0644 '
				in.read(buf, 0, 5);

				long filesize=0L;
				while(true){
					if(in.read(buf, 0, 1)<0){
						// error
						break;
					}
					if(buf[0]==' ')break;
					filesize=filesize*10L+(long)(buf[0]-'0');
				}

				String file=null;
				for(int i=0;;i++){
					in.read(buf, i, 1);
					if(buf[i]==(byte)0x0a){
						file=new String(buf, 0, i);
						break;
					}
				}

				//System.out.println("filesize="+filesize+", file="+file);

				// invoke '\0'
				buf[0]=0; out.write(buf, 0, 1); out.flush();

				// read a content of lfile

				fos=new FileOutputStream(lfile);
				int foo;
				while(true){
					if(buf.length<filesize) foo=buf.length;
					else foo=(int)filesize;
					foo=in.read(buf, 0, foo);
					if(foo<0){
						// error
						break;
					}
					fos.write(buf, 0, foo);
					filesize-=foo;
					if(filesize==0L) break;
				}
				fos.close();
				fos=null;

				if(checkAck(in)!=0){
					return;
				}

				// invoke '\0'
				buf[0]=0; out.write(buf, 0, 1); out.flush();
			}

			session.disconnect();
		}
		catch(Exception e){
			System.out.println(e);
			try{if(fos!=null)fos.close();}catch(Exception ee){}
		}
	}

	public void scpTo(String content, String rfile){
		try{
			File tfile = File.createTempFile("prefix", ".tmp");
			FileWriter fw = new FileWriter(tfile);
			fw.write(content);
			fw.close();
			scpTo(tfile,rfile);
			tfile.delete();
		}catch(IOException e){
			e.printStackTrace();
			return;
		}
	}

	public void scpTo(File lfile, String rfile){
		// ScpTo file1 user@remotehost:file2

		FileInputStream fis=null;
		try{
			Session session = getSession();
			session.connect();

			boolean ptimestamp = true;

			// exec 'scp -t rfile' remotely
			String command="scp " + (ptimestamp ? "-p" :"") +" -t "+rfile;
			Channel channel=session.openChannel("exec");
			((ChannelExec)channel).setCommand(command);

			// get I/O streams for remote scp
			OutputStream out=channel.getOutputStream();
			InputStream in=channel.getInputStream();

			channel.connect();

			if(checkAck(in)!=0){
				//System.exit(0);
				return;
			}

			String filename = lfile.getName();
			if(ptimestamp){
				command="T "+(lfile.lastModified()/1000)+" 0";
				// The access time should be sent here,
				// but it is not accessible with JavaAPI ;-<
				command+=(" "+(lfile.lastModified()/1000)+" 0\n");
				out.write(command.getBytes()); out.flush();
				if(checkAck(in)!=0){
					//System.exit(0);
					return;
				}
			}

			// invoke "C0644 filesize filename", where filename should not include '/'
			long filesize=lfile.length();
			command="C0644 "+filesize+" ";
			if(filename.lastIndexOf('/')>0){
				command+=filename.substring(filename.lastIndexOf('/')+1);
			}
			else{
				command+=filename;
			}
			command+="\n";
			out.write(command.getBytes()); out.flush();
			if(checkAck(in)!=0){
				//System.exit(0);
				return;
			}

			// invoke a content of lfile
			fis=new FileInputStream(lfile);
			byte[] buf=new byte[1024];
			while(true){
				int len=fis.read(buf, 0, buf.length);
				if(len<=0) break;
				out.write(buf, 0, len); //out.flush();
			}
			fis.close();
			fis=null;
			// invoke '\0'
			buf[0]=0; out.write(buf, 0, 1); out.flush();
			if(checkAck(in)!=0){
				return;
			}
			out.close();

			channel.disconnect();
			session.disconnect();
		}
		catch(Exception e){
			System.out.println(e);
			try{if(fis!=null)fis.close();}catch(Exception ee){}
		}
	}

	private int checkAck(InputStream in) throws IOException{
		int b=in.read();
		// b may be 0 for success,
		//          1 for error,
		//          2 for fatal error,
		//          -1
		if(b==0) return b;
		if(b==-1) return b;

		if(b==1 || b==2){
			StringBuffer sb=new StringBuffer();
			int c;
			do {
				c=in.read();
				sb.append((char)c);
			}
			while(c!='\n');
			if(b==1){ // error
				System.out.print(sb.toString());
			}
			if(b==2){ // fatal error
				System.out.print(sb.toString());
			}
		}
		return b;
	}
}
Posted by 김민우 julingks

MySql 쿼리 결과 출력하기

import java.sql.*;
import java.util.ArrayList;

public class JdbcTest {

	private String URL = "jdbc:mysql://localhost";
	private String USERNAME = "user";
	private String PASSWORD = "password";
	private Connection conn;
	public JdbcTest(){
		openConnection();
	}

	public void openConnection(){
		try{
			Class.forName("com.mysql.jdbc.Driver").newInstance();
			conn=DriverManager.getConnection(URL,USERNAME,PASSWORD);
			if(!conn.isClosed())
				System.out.println("Successfully connected to "+ "MySQL server using TCP/IP...");
		}catch(Exception e){
			System.err.println("Exception : "+e.getMessage());
		}
	}

	public void close(){
		try{
			if(conn!=null) conn.close();
		}catch(SQLException e){}
	}

	private void writeResultSet(ResultSet resultSet) throws SQLException {
		int columnCount = resultSet.getMetaData().getColumnCount();
		if (columnCount<1) return;
		System.out.println("--------------------------------------");
		ArrayList <String> labels = new ArrayList<String>();
		for  (int i = 1; i<= columnCount; i++){
			String label = resultSet.getMetaData().getColumnLabel(i);
			System.out.print(label+"\t");
			labels.add(label);
		}
		System.out.println("\n--------------------------------------");
		while(resultSet.next()){
			for(String label:labels)
				System.out.print(resultSet.getString(label)+"\t");
			System.out.println();
		}
		System.out.println("--------------------------------------\n");
	}

	private void executeQuery(String query){
		System.out.println("Execute Query : "+query);
		try{
			Statement st=conn.createStatement();
			ResultSet resultSet = st.executeQuery(query);
			writeResultSet(resultSet);
			resultSet.close();
			st.close();
		}catch(Exception e){
			System.out.println("Exception : "+e.getMessage());
		}
	}
	public static void main(String [] args){
		JdbcTest db = new JdbcTest();
		db.executeQuery("show databases;");
		db.executeQuery("use mysql");
		db.executeQuery("show tables;");
		db.executeQuery("select * from user;");
		db.close();
	}
}
Posted by 김민우 julingks
TAG db, java, mysql

Java : Meta Annotation

Java 2010.08.18 18:35
자바에서는 3개의 표준 어노테이션과 (@Override, @Deprecated, @SuppressWarning)과 4개의 메타-어노테이션을 제공한다.  이번에는 메타- 어노테이션에 대해서 알아보자.

@Target
 어노테이션을 적용할 대상을 지정한다. 가능한 ElementType은 다음과 같다.
  • CONSTRUCTOR : 생성자 선언부
  • FIELD                : enum 상수를 포함한 필드 선언부
  • LOCAL_VARIABLE : 지역 변수 선언부
  • METHOD       : 메서드 선언부
  • PACKAGE     : 패키지 선언부
  • PARAMETER : 파라미터 선언부
  • TYPE            : 클래스, 인터페이스 또는 enum 선언부

@Retention
 어노테이션 정보가 보관되는 기간을 지정한다. 가능한 RetentionPolicy 인자는 다음과 같다
  • SOURCE   : 어노테이션이 컴파일러에서 버려진다.
  • CLASS     : 클래스 안에 어노테이션은 사용되지만 VM에서는 버려진다
  • RUNTIME : VM에서 유지가 되므로 리플렉션으로 사용될 수 있다.

@Documented
 해당 어노테이션을 Javadoc에 포함한다.

@Inherited
 서브 클래스가 부모 어노테이션을 상속받도록 한다.


Posted by 김민우 julingks
다음은 어노테이션의 정의이다.

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NewAnnotation {
	
}
@ 심볼을 제외하면 @NewAnnotation 은 빈 인터페이스 정의와 동일하다.
어노테이션의 정의 역시 @Target, @Retention 이라는 메타-어노테이션이 필요하다. 

@Target 은 어노테이션의 적용 대상을 정의한다 (메소드나, 필드, 생성자, 패키지 등)
@Retention 은 해당 어노테이션이 소스코드, 클래스 파일, 런타임 중 어디에서 적용되는지를 정의한다

어노테이션은 일반적으로 어노테이션 안의 값을 지정하는 요소를 가지고 있다.
다음은  @UseCase 태그의 예제이다.
import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
	public int id();
	public String description() default "no description";
	
}
@UseCase 태그는 int 형의 id 요소와  String 형의 description 요소를 가지고 있다.

어노테이션은 다음과 같이 요소(Element)를 파라미터 처럼 입력 받을 수 있다.
@UserCase (id = 47, description = '"no description")
요소를 가지지 않는 어노테이션은 표식 어노테이션(marker annotation)이라고 불린다.

어노테이션 요소의 허용되는 타입은 다음과 같다
  • Primitive Type (int,float,boolean ...)
  • String
  • Class
  • enum
  • Annotion
  • 위의 타입의 배열
이 밖의 타입을 사용하면 컴파일러에서 에러가 발생한다. Wrapper 클래스도 사용할 수 없다.


Resource : 
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어
Related Links :

Posted by 김민우 julingks
Behavioral 패턴은 객체들이 각각 다른 객체와 상호작용하는 방식을 규정한다.
이 패턴은 각각 다른 객체와 통신하는 방법과 객체의 책임을 지정해서 복잡합 behavior들을 관리할 수 있게 도와준다.

The Observer pattern
 옵저버는 매우 일반적인 패턴이다.
 당신은 전형적으로 Model/View/Controller 아키텍처 애플리케이션에서 이 패턴을 사용하게 된다. 
 이 설계의 Model/View 부분은 데이타 그 자체와 표현을 분리하도록 의도되어 있다.

 예를들어, 데이타베이스에 데이타를 테이블, 또는 그래프 등 다양한 포멧으로 보여져야 한는 경우를 생각해보자.
옵저버 패턴은 디스플레이 클래스가 데이타를 책임지는 클래스에 자신이 등록한다. 그래서 데이타가 변경될 때마다 공지 될 수 있도록 해서 디스플레이가 업데이트 될 수 있도록 한다.

자바 API는  AWT/Swing 클래스들의 event 모델에서 이 패턴을 사용한다.

자바 API는 관찰하는 것을 원하는 객체가 상속 받을 수 있는 Observable 클래스를 제공한다. 
Observable이 제공하는 메소드는 다음과 같다.
  • addObserver(Observer o) 는 Observable 객체가 자신을 등록하기 위해서 호출한다.
  • setChanged() 는 Observable 객체가 변경되었음을 표시한다.
  • hasChanged()  Observable 객체가 변경되었는지를 검사한다.
  • notifyObservers() 는 Observable 객체의 hasChanged()에 따라서 변경되면 모든 관찰자에게 공지한다. 
변경이 되었을 때 Observable 객체가 호출 할 수 있는 하나의 메서드를 포함한 Observer 인터페이스가 제공되어야 한다.  (Observer는 Observerable 클래스에 자신을 등록한다.)

public void update(Observable o, Object arg)

다음 예제는 온도의 변화가 감지 되었을 때의 센서가 어떻게 디스플레이 클래스에 공지하는 지 보여준다.

import java.util.*;

class Sensor extends Observable {
	private int temp = 68;
	void takeReading()
	{
		double d;
		d =Math.random();
		if(d>0.75)
		{
			temp++;
			setChanged();
		}
		else if (d<0.25)
		{
			temp--;
			setChanged();
		}
		System.out.print("[Temp: " + temp + "]");
	}
	public int getReading()
	{
		return temp;
	}
}

public class Display implements Observer {
	public void update(Observable o, Object arg)
	{
		System.out.print("New Temp: " + ((Sensor) o).getReading());
	}
	public static void main(String []ac)
	{
		Sensor sensor = new Sensor();
		Display display = new Display();
		// register observer with observable class
		sensor.addObserver(display);
		// Simulate measuring temp over time
		for(int i=0; i < 20; i++)
		{
			sensor.takeReading();
			sensor.notifyObservers();
			System.out.println();
		}
	}
}

The Strategy and Template patterns

 정해진 behavior들의 다른 구현을 할 수 있게 해준다는 점에서 이 두 패턴은 비슷한다. 그러나 둘의 의도는 다르다.

 스트레티지는 실시간에 다이나믹하게 선택되는 알고리즘이나 오퍼레이션의 다른 구현을 위해서 사용된다. 
대체로 공통적인 behavior는 추상 클레스와 concrete 서브 클래스들로 구현이 된다.
클아이언트는 일반적으로 어떤 전략이 사용가능하고 선택되어야 하는지 알고 있다.

 예를 들면, 추상클래스 Sensor 는 측정치를 가져오도록 정의한다. 콘크리트 서브클래스들은 다른 기술로 구현되는 것이 요구된다. 그 중 하나는 실행 평균을 제공하고, 또 다른 하나는 즉시 측정치를 제공한다. 또 다른 하나는 어떤 시간의 주기로 피크 값을 제공한다.


 템플릿 패턴의 의도는 스트레티지에서 처럼 behavior에게 다른 방식으로 구현될 수 있도록 하는 것은 아니다.
그러나, 정해진 behavior들이 구현되는 것을 보장한다. 스트레티지는 다양성에 초점을 맞추지만, 템플릿은 동일성을 강조하는데 초점을 맞춘다.

 템플릿 패턴은 추상클래스로서 구현이 된다. 이 패턴은  청사진을 제공할 때나 concrete 서브 클래스들의 아웃라인을 제공할 때 종종 사용된다. 또 애플리케이션 프레임웍과 같은 시스템의 hook을 구현할 때 사용되기도 한다.



Resource : 
Posted by 김민우 julingks
 어노테이션은 메타데이터를 별도의 문서가 제공하는 것이 아닌 소스코드 파일과 결합하려는 최근 경향을 반영한 것이다. 또한 C# 등의 다른 언어에 비해 부족한 점을 보완하기 위함이기도 하다.

 어노테이션은 자바 SE5가 제공하는 핵심적인 변화 중 하나이며 자바 언어로 표현할 수 없지만 프로그램에서 전체적으로 표현해야 할 데이터를 기술하는 방법을 제공한다. 

어노테이션을 사용하면
  • 컴파일러가 테스트하고 검증해야 하는 부가 정보를 정해진 형식으로 설명하는 것이 가능하다. 
  • 설명 파일이나 새로운 클래스 정의를 생성하여 공통 코드를 작성하는 부담을 줄이는 용도로도 활용할 수 있다.
  • 소스코드에 메타데이터를 보관할 수 있으며 컴파일 타임의 체크 뿐 아니라 어노테이션 API를 사용하여 코드의 가독성을 높일 수 있다.

비록 자바 SE5가 제공하는 메터데이터의 종류는 많지 않지만 개발자가 자유로운 추가가 가능하다. (예, 스프링 어노테이션)

java.lang은 크게 세가지 목적의 어노테이션을 제공한다.
  • @Overide :  베이스 클래스의 메서드를 오버라이드한 것을 표시한다. 메서드 이름을 잘못 표기하거나 시그니처를 잘못 지정한 경우 컴파일 에러가 발생한다.
  • @Deprecated : 해당 요소가 사용될 경우 컴파일러가 경고를 발생시킨다
  • @SuppressWarning : 부적절한 컴파일러의 경고를 제거하기 위해 사용한다. 

추가적인 4개의 어노테이션(@Target, @Retention, @Docuemented , @Inherited)은 새로운 어노테이션을 생성할 때 사용된다.

반복적인 작업에 관계된 클래스나 인터페이스 기술서를 만들 때는 프로세스의 자동화와 간략화를 위해 어노테이션을 사용한다. EJB에서는 어노테이셔능ㄹ 사용하여 상당수의 반복적인 추가 작업을 제거할 수 있다.

어노테이션은 기존의 XDoclet과 같은 어노테이션 스타일의 독릿(Doclet)을 생성하는 독립적인 시스템을 대체할 수 있다. 반면에 어노테이션은 언어 자체에서 제공하며, 구조화 되어 있기 때문에 컴파일 타임의 체크가 가능하다. 모든 정보를 주석이 아닌 소스코드에 저장할 수 있다는 점은 코드의 간결함과 유지 보수의 편의성을 높여 준다.

Reference
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어
Posted by 김민우 julingks
Structural patterns은 클래스들과 객체들의 조직을 규정한다.
이 패턴들은 
  • 클래스들이 어떻게 다른 클래스로 부터 상속을 받을 것인가
  • 또는 클래스들이 어떻게 다른 클래스로 부터 조합될 것인가
에 관련이 있다.

structual pattern들은 공통적으로 Adapter, Proxy, Decorator 패턴들을 포함한다. 이 패턴들은 클라이언트 클래스와 그것을 이용하기를 원하는 클래스 사이에 간접 방법의 계층을 도입하다는 점에서 비슷하다.
그러나 이 패턴들의 의도는 다르다. 
  • Adapter는 클라이언트 클래스가 사용하기 쉽게 하기 위해서 클래스의 인터페이스를 수정하는 방법을 쓴다.
  • Decorator는 클라이언트 클래스에 과도하게 영향을 끼치지 않는 클래스의 behavior를 추가하는 방법을 쓴다.
  • Proxy는 또 다른 클래스에 투명하게 staind-in을 제공하는 방법을 쓴다.

The Adapter pattern

 어뎁터 패턴은 비슷하지만 같지는 않은 클래스의 재사용을 위해서 사용된다.
오리지널 클래스는 클라이언트 클래스가 필요한 behavior를 지원할 수 있는 능력이 있지만 클라이언트 클래스가 기대하는 인터페이스를 가지고 있지 않을 때 사용한다.  
이런 경우 오리지널 클래스를 대체하는 것은 불가능하거나 실용적이지 않다. 그래서 인터페이스를 맞춰줄 어뎁터가 필요한 것이다. 어뎁터 클래스에서는 적합하지 않은 인터페이스를 변경할 때 사용된다.
 예제가 있다.  어뎁터 클래스는 oldClass를 감싼다. 클라이언트 클래스가 NewInterface에 정의된 newMethod() 메서드를 이용하여 oldClass의 OldMethod()를 호출 할 수 있다.

public class OldClassAdapter implements NewInterface {
	private OldClass ref;
	public OldClassAdapter(OldClass oc)
	{
		ref = oc;
	}
	public void NewMethod()
	{
		ref.OldMethod();
	}
}




The Proxy and Decorator patterns

프록시는 또 다른 클래스를 위한 direct stand-in 이다. 공통적인 인터페이스 또는 추상 클래스를 구현하기 때문에 프록시는 클래스로서 같은 인터페이스를 가진다. 클라이언트 객체는 프록시를 사용하는지를 알지 못한다.
프록시는 클라이언트가 엄격한 접근이 필요하거나 원격 프로세스일 일 때, 사용하기를 윈하는 클래스의 접근이 보이도록 중계 되어야 할때 사용된다. 




 데코레이터는 프록시와 비슷하게 또 다른 클래스의 stand-in 이다. 데코레이션 또한 클래스로서 같은 인터페이스를 가진다. 그러나 의도는 다르다. 데코레이터 패턴의 목적은 오리지널 클래스의 기능을 확장하는데 있다. 클라이언트 클래스에게 투명한 방식으로 기능을 확장한다.

자바 API에서 데코레이터 패턴의 예는 input과 output stream 처리를 위한 클래스에서 찾을 수 있다.
예를 들어 BufferedReader()는 파일로 부터 텍스트를 편리하고 효율적으로 읽을 수 있도록 한다.

BufferedReader in = new BufferedReader(new FileReader("file.txt"));


The Composite pattern

 컴포짓 패턴은 복잡한 객체를 위한 순환적인 컴포지션을 규정한다.
이 패턴의 의도는 모든 컴포넌트 객체가 동일한 방식으로 처리될 수 있도록 하는데 있다.
이 패턴에 참여하는 모든 객체는 공통적인 behavior들을 정의하는 공통적인 추상 컴포넌트 클래스로 부터 파생된다. 
이런 방식으로 부분-전체 hierarchy에 관계를 밀어넣는 것은 시스템이 다루는 객체의 타입을 최소화 한다.

예를 들면, 페인트 프로그램의 클라이언트는 선을 그리는 것을 요청하는 것과 같은 방식으로 컴포짓 객체를 포함하는 또 다른 객체에 요청할 수 있다.





Resource :

Posted by 김민우 julingks
 이 분류에 속하는 패턴들은 객체가 생성될 방식에 대해서 규정한다.
 전형적으로 클래스 인스턴스화에 자세한 것들은 추상 슈퍼 클래스에 의해 캡슐화 된다. 그리고 클라이언트 클래스에게는 숨겨진다. 클라이언트 클래스는 오직 추상 클래스나 인터페이스에 대해서만 알수 있다. 클라이언트 클래스는 객체의 구현 클래스의 특정한 타입은 알 수 없다.

The Singleton pattern
 이 패턴은 객체의 생성을 단속하기 위해서 객체 생성을 캡슐화 할때 사용된다. 
 이것은 단 한번의 객체 생성을 보장할 뿐 아니라 늦은 인스턴스화(lazy instanciation)를 할 수 있게 한다. 즉 객체의 인스턴스화는 실제 사용될 때까지 늦춰질 수 있다. 이것은 생성자가 원격 데이타베이스 접속 같이 비용이 많이 드는 오퍼레이션을 수행할 때 특히 유용한다. 

이 코드는 싱글톤 패턴이 중복없는 순차적인 숫자를 생성하는 카운터를 생성할 때 어떻게 사용될 수 있는지를 보여준다.

// Sequence.java
public class Sequence {
	private static Sequence instance;
	private static int counter;
	private Sequence()
	{
		counter = 0; // May be necessary to obtain
		// starting value elsewhere...
	}
	public static synchronized Sequence getInstance()
	{
		if(instance==null) // Lazy instantiation
		{
			instance = new Sequence();
		}
		return instance;
	}
	public static synchronized int getNext()
	{
		return ++counter;
	}
}
이 구현에 대해서 알아야 할 몇가지는 다음과 같다
  • Synchronized 메서드들은 클래스가 thread-safe인 경우만 사용한다.
  • 이 클래스는 생성자가 private이기 때문에 서브 클래스가 될 수 없다.  이것은 보호해야 할 자원에 따라서 좋은 점이 될 수도, 아닐 수도 있다. 서브클래스로 만들기 위해서는 생성자의 가시성을 protected로 바꿔야 한다.
  • 객체 직렬화(serialization)은 문제를 발생시킬 수 있다. 만약 싱글톤이 직렬화되거나 한번 이상 역직렬화(deserialization) 된다면, 싱글톤이 아닌 여러개의 객체가 생기게 된다.

The Factory Method pattern
 싱글톤 패턴 외에도 Creational pattern에는 또다른 일반적인 예제가 있다. 바로 Factory Method이다
이 패턴은 인스턴스화 되어야 되는 몇개의 호환적인 클래스들 중 하나를 실시간으로 결정해야 할 때 사용된다.
 이 패턴은 자바 API 전체에서 사용된다. 예를 들면 Collator 추상 클래스의 getInstance() 메서드는 java,util.Locale.getDefault()에 의해 결정 되는 기본 locale에 따라서 적절한 collation 객체를 반환한다.

Collator defaultCollator = getInstance();

 반환되는 콘크리트 클래스(Concrete class)는 실제로 항상 Collator나 RuleBasedCollator의 서브 클래스이다. 그러나 구현의 디테일은 중요하지 않다. Collator 추상 클래스에 의해 정의된 인터페이스는 그것을 사용하기 위해서 필요한 모든 것이다.


Resource :
Posted by 김민우 julingks
UML은 객체 지향 설계에서 표준 다이어그램을 만드는 도구가 되었다. UML로 정의되는 다이어그램의 다양한 타입이 있지만 디자인 패턴을 공부하는 데는 클래스 다이어그램만으로도 충분하다. 

클래스 다이어그램에서 클래스는 3개의 칸막이로 된 박스로 묘사된다.

  • 위 칸은 클래스 이름을 포함한다. 만약 추상 클래스일 경우는 이태릭으로 표시한다.
  • 중간 칸은 클래스의 attribute들을 포함한다. (또는 프로퍼티나 변수로 불리는)
  • 밑 칸은 클래스의 메서드들을 포함한다 (또는 오퍼레이션으로 불리는) 클래스 이름같이 추상 메서드인 경우는 italic이다.

경우에 따라 클래스 이름과 메서들만 보여주고 프로퍼티들은 생략하기도 한다.
또는  클래스 이름만 보여주고 프로프터와 메서드 둘다 생략하기도 한다.
이런 생략은 전체의 개념적인 관계를 표현하는 경우에는 일반적이다.

클래스간의 상호적인 관계는 선을 그래서 나타낸다. 보통 타입이 정해지지 않은 개념적인 연관에서는 간단한 선은 서로 연관되어 있음을 가리킨다. 선은 연관에 대한 좀 더 특정한 정보를 제공하도록  변경 할 수 있다.
선의 열린 화살표를 추가하여 방향성을 나타낼 수 있다.
또는 삼각형 화살표를 추가하여 특성화 또는 서브클래싱을 할 수 있다
기본적인 숫자( 또는 다수를 나타내는 별표) 는 각각 선의 양 끝에 추가 될 수 있다. 이것은 일대일, 일대다 같은 관계를 나타낸다.

다음 다어그램은 이런 연관의 다른 타입들을 보여준다.




Resources

Posted by 김민우 julingks
 디자인 패턴은 설계자인 크리스토퍼 알랙산더가 그의 책인 A Pattern Language: Towns, Buildings, Construction (Oxford University Press,1977) 에 처음으로 소개되었다.

 설계 할 때  되풀이 되는 문제의 추상적인 해결책인 패턴이라고 불리는 이 개념은 다른 분야의 연구자의 주목을 이 끌었는데, 특히 1980년 중후반 객체 지향 소프트웨어를 개발자들의 이목을 끌었습니다.

소프트웨어 디자인 패턴에 대한 연구는 아마도 객체 지향 설계의 가장 영향력있는 책이 무엇인가로 귀결되는데
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides가 쓴  Design Patterns : Elements of Reusable Object-Oriented Software  (Addison-Wesley, 1995) 이 그 중에 하나 일것 입니다.  이 네명의 저자들은 종종 Gang of Four (또는 GoF)로 부릅니다. 23 디자인 패턴을 설명하는 부분이 이 책에 가장 핵심입니다. 다른 최신의 책들은 이 레파토리에서 좀 더 중요한 부분이나 더 문제들에 특화된 타입의 범위를 확장시킵니다. 

 Mark Grand, in Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML 는 동시성의 문제에 대한 패턴을 추가했습니다.

 Core J2EE Patterns : Best Practices and Design Strategies by Deepak Alur, John Crupi, Dan Malks 
는 Java 2 엔터프라이즈 기술을 이용한 multi-tier 프로그램을 위한 패턴에 초점을 맞췄습니다. 

 새로운 패턴을 수집하고 연구를 계속하는 활동적인 패턴 커뮤니티가 있다. 이들 커뮤니티는 패턴들을 퍼트리는 데 선도하고 있습니다. 특히 Hillside Group 은 전문가의 지도 아래 패턴을 초심자에게 소개하는 컨퍼런스를 포함한 많은 컴퍼런스를 후원하고 있습니다.


References
Posted by 김민우 julingks

Java : manifest file

Java 2010.08.13 19:07
자바 플랫폼에서 manifest 파일은 JAR 아카이브 안에 들어있는 특정한 파일이다.
데이터와 관련된 패키지를 정의할때 사용된다.
name-value pair들로 구성된 메타데이타 파일이다.

JAR 파일을 생성할 때 자동적으로 기본 manifest 파일이 생성된다. 하나의 아카이브 안에는 오직 하나의 manifest 파일이 존재하고 META-INF 디텍토리에 있어야 한다. JDK 1.2 에서는 기본 manifest 파일은 매우 간단한다.

Manifest-Version : 1.0

모든 엔트리는 name-value 쌍이고 콜론(:) 으로 구분된다.

manifest는 아카이브안에 압축된 다른 파일에 대한 정보도 포함 할 수 있다. 
mainfest에 기록된 파일 정보가 무엇인지는 JAR파일을 사용하는 의도에 따라 달라진다.

JAR 파일을 사용하는 목적에 따라서, 기본 manifest는 수정되어야 한다.
만약 JAR 파일이 아카이빙(저장)의 목적으로만 생성되었다면, MANIFEST.MF 파일 사용할 의미가 없다.

단순히 아카이빙과 압축만을 위해서 JAR 파일의 사용하는 않는다.
여러가지 목적이 있을 수 있다.
  • Applications Bundled as JAR Files
  • Download Extensions
  • Package Sealing
  • Package Versioning
  • Specify Dependencies
각각의 목적에 따라서 Manifest의 헤더에는 다른 정보가 들어가게 된다.

References

Posted by 김민우 julingks

Java : JAR (Java ARchive)

Java 2010.08.13 18:54
Zip 포맷은 다수의 파일 그룹을 하나의 압축된 파일로 묶는 JAR(Java ARchive) 파일 포맷에서도 사용된다.
JAR는 플랫폼에 제한을 받지 않는다. 그러므로 JAR 파일이 다른 플랫폼에서 작동하는지 염려할 필요가 없다.
JAR에는 자바 클래스 파일 뿐 아니라 오디오나 이미지 파일 등 모든 파일을 포함할 수 있다.

JAR 파일에 포함된 각각의 엔트리들은 보안을 위해 전자 서명을 기록할 수 있다.

JAR 파일은 압축된 파일의 묶을 가지고 있는 하나의 파일과 파일들에 대한 설명을 가지고 있는 'manifest'로 구성되어 있다. (사용자가 이파일을 만들 수도 있지만, 일반적으로 jar 프로그램이 자동으로 생성한다)

SUN의 JDK에서 제공되는 jar 유틸리티는 지정한 파일을 JAR 파일 포맷으로 압축한다.  (아카이브 생성, 내용 보기, 파일 추출 등을 할 수 있다.)

Related links
Posted by 김민우 julingks

자바에서는 입출력 라이브러리는 스트림을 읽고 쓸 때 압축 형식을 지원한다.
다음은 파일을 읽어들여서  GZIP 압축 형식으로 출력하는 코드다.

    try {
        // Create the GZIP output stream
        String outFilename = "outfile.gzip";
        GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(outFilename));
    
        // Open the input file
        String inFilename = "infilename";
        FileInputStream in = new FileInputStream(inFilename);
    
        // Transfer bytes from the input file to the GZIP output stream
        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
        }
        in.close();
    
        // Complete the GZIP file
        out.finish();
        out.close();
    } catch (IOException e) {
    }

압축에 관한 입출력 클래스들은 다음과 같다.
  • CheckedInputStream   - InputStream에 대한 채크섬을 생성한다.
  • CheckedOutputStream - OutputStream에 대한 채크섬을 생성한다.
  • DeflaterOutputStream   - 압축 클래스들의 기반 클래스이다
  • ZIPOutputStream    -  데이터를 Zip 파일 포맷으로 압축하는 DeflaterOutputStream이다.
  • GZIPOutputStream  -  데이터를 GZip 파일 포맷으로 압축하는 DelfaterOutputStream이다.
  • InflaterInputStream - 압축 헤제 클래스의 기반 클래스
  • ZIPInputStream     - Zip 파일 포맷으로 압축된 데이터를 압축 해제하는 InflaterInputStream이다
  • GZIPInputStream  - GZip 파일 포맷으로 압축된 데이터를 압축 해제하는 InflaterInputStream이다

References
Posted by 김민우 julingks
지난 포스트에서는 자바 입출력에서 InputStream과 OutputStream 클래스에 대해서 알아봤다.  이번에는 Reader와 Writer 클래스에 대해서 알아보자.

Reader와 Writer

 자바 1.1.에 이르러 기본적인 입출력 라이브러리에 큰 변화가 생겼는데, 바로 Reader와 Writer 클래스다.
이것은 기존의 InputStream과 OutputStream을 대체하는 것이라고 생각할 수 있다.

 InputStream, OutputStream은 바이트 단위의 입출력에 중심적인 기능을 수행하고 Reader, Writer는 유니코드 캐릭터 기반의 입출력을 수행한다.  (하지만, 여전히 InputStream과 OutputStream은 중요한 클래스이다)

 Reader와 Writer를 사용하는 가장 큰 이유는 국제화 이다. 기존의 입출력 라이브러리는 오직 8비트 스트림만 지원할 뿐 16비트 유니코드 캐릭턴는 정상적으로 처리하지 못했다.  모든 입출력 오퍼레이션의 유니코드 지원을 위해서 Reader와 Writer가 개발되었다.

 InputStream을 Reader로 OutputStream을 Writer로 변환하는 어뎁터 클래스를 제공한다. 
  • InputStreamReader
  • OutputStreamWriter


Reader 클래스를 종류는 다음과 같다.
  • Reader
  • FileReader
  • StringReader
  • CharArrayReader
  • PipedReader
  • BufferedReader
  • LineNumberReader
  • PushbackReader
Writer 클래스의 종류는 다음과 같다
  • Writer
  • FileWriter
  • StringWriter
  • CharArrayWriter
  • PipedWriter
  • BufferedWriter
  • PrintWriter

Referece
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어

Posted by 김민우 julingks
 입출력 시스템의 어려움에는 다양한 원인있다.  상이한 소스, 파일, 콘솔, 네트웍, 입출력 디바이스들이 혼재한다.
이런 디바이스들과의 통신에 순차 액세스, 랜덤 액세스, 버처, 바이너리, 문자, 줄단위, 단어 단위 등의 다양한 방식을 사용해야한다.

 자바 라이브러리를 디자인한 사람들은 이런 문제를 다양한 클래스를 만들어서 해결했다. 사실 자바 입출력에는 많은 클래스가 있어서, 프로그래머 입장에서 처음 접할대 어느 것을 선택해야하는지 헛갈릴 때가 있다.  (하지만, 사실 자바 입출력 디자인이 클래스의 범람을 막아주고 있기도 하다.)

 결과적으로 자바의 입출력 개념을 이해하고 적절하게 사용하기 위해서는 상당히 많은 클래스를 이해해야 한다. 이런 이해가 없다면, 어떤 시점에 어떤 것을 사용하고, 어떤 것을 사용하지 말아야 할지 에 대한 판단을 내릴 수 가 없다.

자바 입출력 라이브러는 바이트 기반의 라이브러리에서 유니코드 문자 기반이라는 큰 변화가 있다.
  • InputStream, OutputStream은 바이트 단위의 입출력에 중심적은 기능을 수행한다. 
  • Reader, Writer는 유니코드 캐릭터 기반의 입출력을 수행한다.

프로그래밍 언어의 입출력 라이브러리는 종종 데이터를 조각 단위로 생성하거나 읽어들이는 기능을 제공하는 추상적인 스트림으로 데이터 소스를 표현한다.

JDK 문서의 클래스 계층을 보면 자바에서 
  • 입력 클래스들은 InputStream 또는 Reader 클래스를 상속받는다.
    • 단일 byte 또는 byte의 배열로 데이터를 읽어들이는 read() 메서드를 가진다.
  • 출력 클래스들은 OutputStream 또는 Writer 클래스를 상속받는다.
    • 단일 byte 또는 byte의 배열로 데이터를 출력하는 write() 메서드를 가진다.

하지만 실제로 기본적으로 제공하는 read(), write() 메서드를 사용하는 일은 흔치 않다. 
그래서 스트림 객체를 만들때는 원하는 입출력 메소드를 가지는 클래스를 선택해서 사용해야 한다.

이제부터 InputStream과 OuputStream의 서브 클래스들은 어떤 것들이 있는지 살펴보겠다.

InputStream

InputStream은 다양한 소스로 부터 입력을 받아들인다.
  • byte의 배열
  • String 객체
  • 파일
  • 파이프
  • 다른 스트림의 시퀀스
  • 인터넷 커넥션 등

클래스기능생성자의 인자생성자의 인자
ByteArrayInputStream 메모리 버퍼를 InputStream 처럼 사용한다 byte를 추출한 버퍼 데이터 소스로서 FilterInputStream과 연결하여 유용한 인터페이스를 제공한다.
StringBufferInputStream String을 InputStream으로 변환한다 String. 실제 내부 구현에서는 StringBuffer를 사용한다 데이터 소스로 사용. FilterInputStream과 연결하여 유용한 인터페잇를 제공한다.
FileInputStream 파일에서 정보를 읽어 들인다. 파일 이름의 String 표현, File 또는 FileDescriptor 객체 데이터 소스로 사용. FilterInputStream과 연결하여 유용한 인터페이스를 제공한다.
PipeInputStream 연결된 PipeOutputStream의 데이터를 읽어 들인다. 파이프의 개념을 구현한 것이다. PipedOutputStream 데이터 소스로 사용. FilterInputStrea과 연결하여 유용한 인터페이스를 제공한다
SequenceInputStream 둘 이상의 InputStream을 하나의 InputStream으로 변환한다.
데이터 소스로 사용. FilterInputStream과 연결하여 유용한 인터페이스를 제공한다.
FilterInputStream 다른 InputStream에 유용한 기능을 제공하는 데코레이터를 위한 인터페이스 - -

OutputStream

OutputStream은 byte 배열, 파일, 파이프에 데이터를 출력하는 클래스다.

클래스기능생성자의 인자생성자의 인자
ByteArrayOutputStream 메모리의 버퍼를 생성한다.
스트림을 통하여 전송하는 모든 데이터는 버퍼에 저장이 된다.
버퍼의 크기를 선택적으로 지정한다. 데이터의 목적지를 지정. FilterOutputStream 객체와 연결하여 유용한 인터페이스를 제공한다.
FileOutputStream 파일에 정보를 저장한다 파일 이름의 String 표현, File 또는 FileDescriptor 객체 데이터의 목적지를 지정. 
FilterOutputStream 객체와 연결하여 유용한 인터페이스를 제공한다.
PipedOutputStream 여기에 기록하는 모든 정보는 연결된 PipedInputStream의 입력으로 사용된다. 파이프 개념을 구현한 것 PipedInputStream 데이터의 목적지를 지정. FilteroutputStream 객체와 연결하여 유용한 인터페이스를 제공한다.
FilterOutputStream 다른 OutputStream에 유용한 기능을 제공하는 데코레이터를 위한 인터페이스 - -

Reader, Writer 클래스는 다음에 알아보자 :-)

reference
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어
Posted by 김민우 julingks

Java : File class

Java 2010.08.10 18:24
File 클래스는 실제 파일에 대한 참조를 하고 있다고 생각되지만, 실제로는 파일의 경로가 저장되어 있을 뿐이다.
File 객체는 특정 파일의 이름이나 디렉토리 안에 존재하는 파일의 이름들을 표현한다. list() 메서드를 이용하면 파일 이름의 배열을 만들어 낼 수 있다.

디렉토리 목록을 보려할 때 File 객체는 두가지 방법을 사용할 수 있다.
listFiles() 메서드를 호출 하여 안에 포함된 모든 파일의 목록을 얻을 수 있다.
선택적으로 목록을 보기를 원한다면 FilenameFilter 인터페이스의 구현 클래스를 사용하면 된다.

FilenameFilter 인터페이스는 단순한 구조를 가지고 있다
public interface FilenameFilter {
  boolean accept(File dir, String name);
}
FilenameFilter의 구현 클래스를 File 클래스의 list() 메서드를 실행할 때 파라메터로 넘겨주면 된다.

listFiles() 메서드에서는 accept() 메서드를 호출하여 파일 목록에 어떤 파일 이름을 포함할 것인가를 판단한다.
accept에서 true를 반환하면 포함되고 false를 반환하면 포함되지 않는다.

그 밖에 제공하는 메서드들을 살펴보면 다음과 같다.
  • canRead()  
  • candWrite()
  • list()
  • delete(0
  • exists()
  • getname(0
  • getParent()
  • getParentFile()
  • getPath()
  • isAbsolute()
  • isDirectory()
  • isFile()
  • isHidden()
  • length()
  • list()
  • listFiles()
  • mkdir()
  • mkdirs()


Posted by 김민우 julingks
TAG File, java

Java 1.0/1.1 Containers

Java 2010.08.10 18:07
자바 1.0/1.1 버전의 구식 컨테이너는 사용하지 않는 편이 낫다.
하지만 오래된 자바코드를 지원하기 위해서 포함되어 있다.
앞으로 사용하지 않을 계획이더라도, 어떤 것들이 있는지 파악하는 것이 필요하다.
  • Vector
  • Enumeration
  • Hashtable
  • Stack
  • BitSet

Vector
자바 1.0/1.1 에서는 Vector만이 유일하게 스스로 크기를 확장하는 가졌었기 때문에 쓰임새가 많았다. 
오래된 자바코드를 지원하기 위해서 포함된 것임에도 Vector가 쓸만하다는 인식을 준다.
Vector 결점은 많다.

Enumeration
Enumeration 인터페이스는 Iterator 보다 작은 개념인데, 단지 두 개의 메소드만을 제공한다. 
boolean hasMoreElements()
Objec nextElement()    (다음 요소가 없으면 예외를 던짐)
Enumeration은 구현 클래스가 아닌 인터페이스이므로 새로운 자바 라이브러리에서도 기존의 Enumeration을 사용하는 경우도 있다.

Hashtable
HashMap과 유사하며, 새로 작성하는 코드에서는 HashMap을 사용하도록 하자.

Stack
Stack은 Vector와 함께 사용되는 것이 아닌 Stack 자체가 Vector를 상속했다는 것이다. 그러무로 Stack은 Vector의 기능 및 특성을 포함하여 Stack 자체의 기능을 가지고 있다. 
이러한 방식의 디자인이 좀더 효율적이여서인지 아니면 배포 전에 세심하게 리뷰를 하지 못한 엉성한 디자인의 결과인지는 판단하기 힘들다. 하지만 문제는 이런 나쁜 디자인이 여전히 제공되고 있다는 점에서 사용하지 않는 것이 좋다. 스택은 LinkedList 로도 충분히 대체가능 하다.

Bitset
많은 양의 on-ff 정보를 저장할 필요가 있을때 유용하다.
용량 관점에서만 유용하고, 속도는 기본 배열보다 조금 느리다.
이름을 부여할 수있는 고정된 크기의 플래그를 사요하는 경우라면 EnumSet을 사용하는 것이 더 나은 선택이다.
Bist만이 제공하는 오퍼레이션이 필요하거나 플래그의 개수를 예측할 수 없거나 이름을 부여하는 것이 부적절 할 때만 사용하도록 하자.

reference
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어


Posted by 김민우 julingks

Java : generic method

Java 2010.08.09 14:43
클래스에만 generic이 적용되는 것은 아니다. 클래스 내부의 메서드도 파라메터화를 할 수 있다. 이 때 클래스는 제너릭이 될 수도 있고 안될 수도 있다.

제네릭 메소드를 정의할 때는 반환 값 앞에 제너릭 파라미터 목록을 추가하면 된다.
public class GenericMethods{
public <T> void f(T x) { }
{

제너릭 클래스를 사용할 때는 객체 생성시 타입 파라미터를 반드시 지정해야 한다.
하지만 제너렉 메소드를 사용할 때는 파라미터 타입을 항상 지정할 필요는 없다.
컴파일러가 찾아줄 수 있기 때문이다. 이것을 타입 인자 추론(type argument inference)라고 한다.

primitive 데이터 타입을 인자로 주어 호출 하면 오토박싱이 적용되여 자동으로 wrapper 클래스 타입으로 변환된다.


Posted by 김민우 julingks
자바 표준 라이브러리는 핵심 클래스들의 조작을 위한 충분한 메서드를 제공하지 못한다.
그래서 아파치 commons lang은 이런 메서드들을 제공한다.
java.lang API를 확장하는 helper 유틸리티들이라고 보면 된다.

그 중 ArrayUtils 클래스를 살펴보자

ArrayUtils의 메서드들
  • add()         주어진 배열을 복사하고 새로운 배열에 끝에 주어진 요소를 추가한다.
  • addAll()     주어진 두 배열의 모든 요소를 새 배열에 추가한다.
  • clone()      배열을 복제한다 (null을 핸들링한다)
  • contains()  주어진 배열 안에 주어진 값이 있는지 검사한다. (null 핸들링)
  • getLength() 주어진 배열의 길이를 반환한다
  • indexOf()    주어진 배열에서 주어진 값의 인덱스를 찾는다.
  • isEmpty()   주어진 배열이 비어있거나 null인지 검사한다
  • isEquals()  equals 메서드를 사용하여 두 배열을 비교한다. (고차원 배열도 핸들링)
  • isNotEmpty()     배열이 비어있지 않거나 null이 아닌지를 검사한다
  • isSameLength() 두 배열이 같은 길이인지 비교한다. (null 배열은 길이가 0으로 다룬다)
  • isSameType()    두 배열이 같은 타입인지 비교한다.
  • lastIndexOf()      배열 안에 주어진 값의 마지막 인덱스를 찾는다.
  • nullToEmpty()    null 레퍼런스를 비어있는 배열로 바꾼다. (방어적인 프로그래밍 기술)
  • remove()           주어진 배열에서 특정 위치의 값을 삭제된 배열을 반환한다.
  • removeElement() 주어진 배열에서 특정 값의 첫번째 요소를 삭제된 배열을 반환한다.
  • reverse()     주어진 배열의 순서를 뒤집는다.
  • subarray()   시작 인덱스와 끝 인덱스 사이의 요소를 포함하는 새 배열을 만든다.
  • toMap()       주어진 배열을 Map으로 변환한다.
  • toObject()    주어진 기본형(primitive type)의 배열을 wrapper 객체 배열로 변환한다.(예 int[] -> Integer[])
  • toPrimitive() 주어진 Wrapper 객체 배열을 기본형(primitive) 배열로 변환 한다. (예 Integer[] -> int[])
  • toString()      배열을 문자열로 출력한다. (null 핸들링)

실제 자바 표준 라이브러리 Arrays에서 제공하는 메서드는 별로 없다. 검색과 정렬, 비교 정도이다.
사실 간단한 배열 조작을 위해서 위해서 List 로 변환하고 다시 배열로 변환하는 작업을 하는 경우가 종종 있다.
또는 배열의 간단한 조작이 불편해서 ArrayList를 사용하는 경우도 더러 있다.

ArrayUtils의 메서드들 중에 주목할 만 것은 add와 remove일 것이다. 
배열에서 요소를 삭제하거나 추가하는 것은 굉장히 번거로운 일인데 ArrayUtils을 사용하면 마치 리스트에서 요소를 추가, 삭제하는 조작을 할 수 있다.

그리고, 기본형(primitive type) 배열에서 wrapper 배열로 변환(또 그 반대로)도 쉽게 할 수 있다.

Reference

Posted by 김민우 julingks
자바 표준 라이브러리에는 배열을 복사하는 System.arraycopy() 라는 static 메서드가 있다.
직접 수작업으로 for 반복문을 사용하여 복사하는 것보다 월등히 빠르다.
모든 타입에 대하여 오버로드되어 있다.

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

arraycopy()의 인자는 순서대로 원본 배열, 원본 배열에서 복사를 시작할 위치, 대상 배열, 대상 배열에서 복사를 시작할 위치, 복사할 요소의 개수이다. 벼열의 크기를 벗어나면 예외가 발생한다.

import java.util.Arrays;
public class JakartaExample {
	public static void main(String[] args) {
		int [] src = new int[10];
		int [] dest= new int[10];
		Arrays.fill(src,1);
		System.arraycopy(src, 0, dest, 0, dest.length);
		System.out.println(Arrays.toString(dest));
	}
}
/* 결과 
 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
*/

reference
Posted by 김민우 julingks

Java : Arrays class

Java 2010.08.04 14:24
Arrays 클래스는 배열을 조작하는 다양한 메서드들을 포함한다. 
Arrays 에서 제공하는 메서드들을 알아보자. 오버로딩된 메서드를 제외하면 몇 개 안된다.
  • static List asList(Object[] a)  
  • static int binarySearch(Object[] a, Object key, Comparator c)
  • static boolean equals(Object[] a, Object[] a2) 
  • static void fill(Object[] a, int fromIndex, int toIndex, Object val)
  • static void sort(Object[] a, Comparator c) 
각각 기능은
  • asList() 배열을 List 컨테이너로 변환한다.
  • binarySearch() 정렬된 배열의 요소를 검색한다.
  • equals() 두 배열의 동일성 여부를 비교한다. 다차원 배열에서는 deepEquals()를 사용한다
  • fill() 배열을 특정 값으로 채운다
  • sort() 배열의 값을 정렬한다.
  • toString() 배열 요수의 String 표현을 반환한다.

reference
Posted by 김민우 julingks

Java : Generic

Java 2010.08.04 13:55
 제너렉 타입이 흔히 사용되는 곳은 아무래도 List, Set, Map 같은 컨테이너 클래스들일 것이다. 컨테이너에 넣을 때에 Object로 업 캐스팅 되어 타입 정보를 잃게 된다. 그리고 그 객체를 다시 꺼내 사용 할때 에는 프로그래머가 원래의 타입으로 다운 캐스팅을 해주어야 한다. 여기서 캐스팅이 잘 못 되면 ClassCastException이 발생한다. 이것은 런타임 에외 이므로 컴파일 시점이 아닌 런타임시에 발견된다. 이 문제를 해결 해 주는 것이 제너릭 타입이다.
 
Object 대신에 나중에 결정될 타입을 명시한다. 이것은 타입 정보를 파라메터 처럼 넘겨 준다고 하여, 파라미터화 타입(parameterized type)이라고 한다.

간단한 예는 다음과 같다.

제너릭을 사용하지 않을 경우
List myIntList = new LinkedList();
myIntList.add(new Integer(0));
Integer x = (Integer) myIntList.iterator().next();

제너릭을 사용하는 경우 (컨테이너에서 객체를 꺼낼때 캐스팅이 필요 없다)
List<Integer> myIntList = new LinkedList<Integer>();
myIntList.add(new Integer(0));
Integer x = myIntList.iterator().next();

기본형(primitive type)은 파라메터로 넘겨줄 수 없다. 대신에 Integer, Double 같은 wrapper 클래스를 사용한다.

제너릭을 사용하면 다운 캐스팅 시의 미스 캐스팅을 방지하여 안전한 컨테이너를 만들 수 있다.

inteface에도 제너릭을 사용할 수 있다.
다음은 java.util 패키지에 있는  List와 Iterator 의 인터페이스들이다.
public interface List<E> {
  void add(E x);
  Iterator<E> iterator();
}

public interface Iterator<E> { 
  E next();
  boolean hasNext();
}

제너릭에서 와일드카드(wildcard)에 대한 내용은 다음으로 미루자.

references

Posted by 김민우 julingks
자바는 destructor가 없다. 가비지 콜렉터를 통해서 메모리 자원을 JVM이 알아서 시스템에 환원한다.
자바가 아닌 경우에는 finally가 중요해 지는데, 할당 받은 메모리를 해제해야 가비지 발생을 방지 할 수 있기 때문이다.

그럼 자바에서는 왜 finally가 필요한 것일까?

답은 메모리가 아닌 다른 시스템 자원을 cleanup할 때 필요하다.
네트워크 접속을 끊는다든지, 스크린 상에 그려진 것을 복구한다든지 등에서 말이다. 

하지만 주의해야 할 점이 있다. finally는 예외 발생 여부와 상관없이 언제든지 클린업 코드를 수행한다. 
Posted by 김민우 julingks
TAG Finally, java

Java : Runtime Exception

Java 2010.08.04 10:46
자바의 Throwable은 예외로 던져질 수 있는 모든 것을 나타내는 클래스이다. Throwable 객체에는 두 가지 파생 클래스가 있는데, Error와 Exception 이다.
  • Error는 컴파일 에러나 시스템 에러라고 보면 된다. 따라서 catch 할 필요가 없다.
  • Exception은 기본적인 예외 타입으로 우리가 던지고 받을 수 있는 예외 타입이다. 




NullPointerException처럼 예외가 발생하면 자바가 자동으로 예외를 던지는 지는 Runtime Exeception이 있다. 이런 유형의 예외들은 Runtime Exception의 파생 클래스들이다.

Runtime Exception은 컴파일 시점에 확인되는 checked execption이 아니다.
따라서 메소드에 예외 명세(메소드 선언부 뒤에 throws 키워드를 통해서 어떤 예외가 던져지는지를 명시)를 지정할 필요가 없다.

Runtime Exception은 프로그램의 결함을 나타낸다. 컴파일 시점에는 잡아낼 수 없지만, 프로그램 실행 도중 프로그램 작성의 잘못으로 발생하는 것이다. 이런 경우는 다음과 같다.
  1. 예상할 수 없는 에러 (예, NullPointerException)
  2. 당연히 수정해야 하는 에러 (예, ArrayIndexoutOfBoundsException)
Runtime Exception은 catch하지 않으면 JVM에서 예외를 처리하게 되고 System.err를 통해서 출력 된다.

사실 프로그래머는 디버깅 할 때 이런 Runtime Exception을 잡아내는 데 많은 시간을 할애하게 된다.

프로그램 작성시 예외 부분을 어떻게 처리해야 하는 가의 문제는 어려운 문제 중 하나이다.
대부분 이 부분을 소홀히 넘기지만,  예외 처리 코드를 살펴보면 설계자와 개발팀의 실력의 수준을 가늠할 수 있다.


Posted by 김민우 julingks
자바 SE5 이후 에는 C의 printf() 문과 같은 출력 포멧을 사용할 수 있다.
format 메서드도 동일한 기능을 한다.

System.out.printf("Row 1: [%d, %f]\n", x, y);

// OR

System.out.format("Row 1: [%d, %f]\n", x, y);

그리고 util 패키지에 Formatter 클래스가 생겼다.
포맷 문자열과 데이터를 원하는 결과로 변환할 수 있다.

import java.util.Formatter;
public class FormatterExample {
	public static void main(String[] args) {
		Formatter formatter = new Formatter();
		formatter.format("%d %d",10,20);
		String str = formatter.toString();
	}
}

String.format() 을 이용해도 비슷한 결과를 얻을 수 있다.

Posted by 김민우 julingks

Java : Inner Class

Java 2010.08.03 16:17
클래스 내부에 클래스를 정의 할 수 있다. 이것을 내부 클래스(Inner class)라고 한다.
내부 클래스는 자바에서 소홀하게 넘어갈 수 있는 부분 중 하나이나.
특징을 살펴보자.

내부 클래스는 
  • 논리적으로 함께 속해야 하는 클래스를 그룹으로 묶어준다
  • 하나의 클래스 내부에서 다른 클래스의 가시성을 제어할 수 있게 해준다.

내부 클래스 사용시 몇가지 주의 사항이 있다.
  • 외곽 클래스의 맴버 메소드(static이 아닌)에서는 내부 클래스의 객체를 바로 참조하거나 생성할 수 있지만, 그 외의 곳에서는 반드시 '외곽클래스명.내부클래스명'의 형태로 해야 한다.
  • 내부 클래스는 외곽 클래스의 모든 요소에 대한 접근 권한을 갖는다.
    public class OuterClass {
    Object item;
    class InnerClass{
    public Object getItem(){
    return item;
    }
    }
    }
  • 외곽 클래스의 객체에 대한 참조가 필요하다면 '외곽클래스명.this'로 하면된다.
    public class OuterClass {
    Object item;
    public class InnerClass{ 
    Object item;
    public void method(){
    Object innerClassItem = this.item;
    Object outerClassItem = OuterClass.this.item;
    }
    }
    }
  • 외곽 클래스의 객체 없이는 내부 클래스의 객체를 생성할 수 없다. (static 내부 클래스 제외)
  • 외각 클래스의 static 메서드나 그 외에서 내부 클래스를 객체를 직접 생성하려면, 외곽 클래스 객체를 사용하여 '레퍼런스.new'를 이용하여 생성해야 한다.
    public class OuterClass {
    public class InnerClass{ }
    public InnerClass getInnerClassInstance(){
    return new InnerClass();
    }
    public static void main(String [] args){
    OuterClass outerClass = new OuterClass();
    OuterClass.InnerClass innerClass1 = outerClass.new InnerClass();
    OuterClass.InnerClass innerClass2 = outerClass.getInnerClassInstance();  
    }
    }

클래스에 다른 클래스를 포함시켜 단지 코드를 감추는 용도로만 쓰게 된다.
하지만, 내부 클래스의 용도는 다양하다.

특히 내부 클래스는 인터페이스와 같이 사용할 때 그 진가를 발휘한다. 
내부 클래스에서 인터페이스를 implements하면 인터페이스 구현을 완벽하게 감출 수 있다.
그리고 내부 클래스의 객체를 생성한 후 베이스 인터페이스 타입으로 업 캐스팅 하면 베이스 클래스 타입으로 업케스팅 했을 때처러 여러가지 장점을 이용할 수 있다.

그 밖에도, 메소드와 코드 블록 내에서 내부 클래스 사용하는 방법과 익명 내부 클래스 등은 다음에 알아보자.

reference
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어

Posted by 김민우 julingks

Java : interface

Java 2010.08.03 14:41
interface는 구현이 없는 완전한 추상 클래스다. 물론 객체 생성은 불가능 하다.

쉽게 놓칠 수 있는 몇 가지 특징을 짚어보자.
  • 인터페이스 이름과 소스 파일명이 같을 때에 interface 키워드 앞에 public을 지정할 수 있다.
  • 인터페이스에 필드를 포함 할 수 있다. 그러나 모든 필드는 자동적으로 static final(상수 필드)이 된다.
  • 인터페이스에 정의된 모든 맴버는 자동으로 public이 된다.
  • 여러 개의 인터페이스를 상속받을 때에는 같은 이름의 메서드가 있으면 에러가 된다. (오버로딩 제외)

Posted by 김민우 julingks

Java : abstract keyword

Java 2010.08.03 14:24
추상적이다(abstract)의 사전적 의미는 구체성이 없이 막연하고 일반적인 것을 말한다.
추상화에서는 구체적인 형태를 해체하여 대상에서 본질적이고 핵심적인 것만을 표현하고자 한다.
프로그래밍에서도 이런 개념을 빌려다 쓴다.

abstract 키워드는 메서드와 클래스에 사용할 수 있다.

추상 메서드(abstract method)는 구현은 없고 선언만 있다.
abstract void func();
실제 구현은 클래스를 상속 받은 파생 클래스에서 오버라이딩을 통해 구현한다.

추상 메서드를 하나라도 가지고 있는 클래스는 반드시 추상 클래스(abstract class)로 지정 되어야 한다.
추상 클래스로는 객체를 생성할 수 없다. 
추상 메서드가 없어도 abstract 키워드로 추상 클래스를 지정할 수 있다. (설계상 클래스 생성을 못하게 하기 위해서)


Posted by 김민우 julingks

Java : Polymorphism

Java 2010.08.03 13:47
상속을 통해 생성된 파생 타입의 객체를 처리할 때 그 객체의 타입을 베이스 타입으로 지정하면 유리할 때가 많다.
각각 파생 타입마다 따로 코드를 작성하지 않아도 되기 때문이다.

파생 타입을 새로 추가하더라도 베이스 타입의 코드에는 영향을 주지 않는다.
따라서 기존 코드에 영향을 주지 않고 시스템을 확장해 갈 수 있다.
이것은 시스템의 설계 능력을 크게 향상시키고 유지보수 비용을 줄일 수 있다.

다형성을 간단히 설명하자면 다음과 같다.
  • 서로 다른 타입의 객체들이 같은 메시지에 대해 제각기 다른 결과를 산출한다는 개념이다.
  • 여기서 "서로 다른 타입"은 "파생 타입"을, 같은 메시지는 "베이스 타입의 메서드 호출"을 의미한다.

파생 타입의 객체들은 베이스 타입으로 업 케스팅이 될 수 있고 베이스 타입 처럼 사용할 수 있지만 각각의 구현은 오버라이딩을 통해서 서로 다를 수 있다.

다형성을 설명하는 키워드로는 업 케스팅, 다운 케스팅, 오버라이딩, 상속 등이 있겠다.


reference
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어
Posted by 김민우 julingks

Java : final keyword

Java 2010.08.03 13:31
final은 변경할 수 없다는 것을 나타낸다. 변경을 못하게 하는 데는 두 가지 이유가 있을 수 있는데,
설계(design)_와 효율성(efficiency)이다. 

final키워드는 데이터, 메소드, 클래스에 사용될 수 있다.

final 데이터
상수를 사용하는 경우는 다음 두가지 이다.
  1. 절대 변하지 않는 값을 갖는 컴파일 시점의 상수
  2. 실행할 때 초기화되는 값이 될 수 있으며, 일단 초기화 되면 값을 변경하지 않는 경우
primitive type에 final 키워드를 사용하고, 정의하는 시점에 값을 정해 주어야 한다.
reference 라면 한번 객체를 가리키도록 하면 그 값을 바꿀 수 없다.
하지만 가리키는 객체의 값은 변경 될 수 있으며, 자바에서는 객체 자체를 상수로 만드는 방법은 없다. (비슷한 효과를 내도록 클래스를 만들 수는 있다.)

static 과 final을 같이 사용하면, 변경 될 수 없으며 클래스에서 단 하나만 존재한다.
static이면서 final인 필드는 영문대문자와 밑줄(_) 조합으로 명명하는게 관습이다. (예 MAX_INPUT_VALUE)

final이면서 초기화 값을 주지 않는 필드를 blank final이라고 하며, 사용되기 전에 반드시 초기화 되어야 하며, 한번 초기화 되면 변경될 수 없다. 이것은 final을 훨씬 더 유연하게 사용할 수 있게 해준다.

final 메서드
메서드에 final을 사용하는 이유는 단 하나이다.
  • 상속 받는 클래스에서 메서드 오바라이딩을 할 수 없게 만든다.
물론 final 메소드는 컴파일러가 inline 호출의 형태로 바꿔 효율적인 측면이 있지만. SE5 이후 JVM이 여러 가지 상황을 판단후 특별한 방법으로 최적화 하므로 프로그래머가 신경 쓸 필요는 없다.

final 클래스
클래스에 final을 사용하는 이유는 하나이다
  • 이 클래스로부터의 상속을 할 수 없다.
왜 클래스로 부터의 상속을 원하지 않거나 못하게 하는 것일까?
설계의 관점에서 세 가지를 생각할 수 있다.
  • 클래스에 대해 변경이 필요 하지 않을 때
  • 안전성을 고려
  • 코드의 보안성을 유지


reference
  • Thinking in JAVA, Burce Eckel, 심재철/최정국 공역, 사이텍미디어


Posted by 김민우 julingks
TAG Final, java