iterator 사용시에 iterator 구동되는 동안에 insert, update, delete 를 시도하면 ConcurrentModificationException 이 발생합니다.

iterator 가 부모의 내용이 변경되지 않는 것을 전제로 하기 때문인데, 이 때 Iterator 로 빼낼 객체의 clone 을 만들어서 사용하면 해결됩니다.

Iterator it = ((ArrayList<String>)list.clone()).iterator();
while(it.hasNext()) {
    it.remove();
}


Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

  1. 스파게티코더 2011.01.07 22:25  댓글주소  수정/삭제  댓글쓰기

    clone을 하면 deep copy를 하는 것인가?
    아니면 인스턴스의 레퍼런스만 추가하는거?

  2. BlogIcon 박상구 2011.01.11 13:06  댓글주소  수정/삭제  댓글쓰기

    얘기했듯이 셸로우 카피 인가벼.ㅎ

  3. 지나가던 공돌이 2011.07.26 10:31  댓글주소  수정/삭제  댓글쓰기

    덕분에 에러를 하나 해결했습니다. 감사합니다.

  4. BlogIcon 피의화요일 2011.12.22 11:01 신고  댓글주소  수정/삭제  댓글쓰기

    상현아 올만이다.
    오류 난거 찾다보니 네 블로그까지 왔구나 ㅋㅋㅋㅋ

  5. BlogIcon Gucci Guccissima 2012.08.21 16:54  댓글주소  수정/삭제  댓글쓰기

    이 문서는 위대한 작성

  6. 장원준 2012.10.01 16:11  댓글주소  수정/삭제  댓글쓰기

    http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html
    iterator.remove 를 쓰는게 정석이라고 나오는 구만 ^-^

  7. BlogIcon sweertate 2013.07.02 19:30  댓글주소  수정/삭제  댓글쓰기

    sdfsdfsdfsdfsdfsdfsdfsdfsdf

  8. BlogIcon sweertate 2013.07.04 00:59  댓글주소  수정/삭제  댓글쓰기

    For anybody who values comfort over glamour, flip flops are heaven-sent. No other type of footwear is more comfortable. They are available in every color and can be made out of any material. You can have a pair to use at home, and more classy ones for social engagements.
    Past designs of flip flops were not very foot-friendly. A wearer would often need a few weeks to get used to a pair. Fortunately, modern footwear manufacturers have incorporated comfort in their designs making flip flops more popular. They can now be found in homes throughout out the world.
    Being easy to slip-on and off is just one of the lovely attributes of flip flops. It doesnt matter if they get wet and they allow women to show off their newly painted toenails. Their popularity has prompted almost all footwear companies to manufacture them.
    Some expensive models are available for brand-conscious individuals and some less costly models are available for thrifty costumers. It all depends on you; you can wear what kind of flip flops that satisfy your taste. But no matter how much they cost, they are still adored by countless consumers.
    It would be wonderful if society allowed people to wear flip flops whenever and wherever they went to. Unfortunately they are not allowed at workplaces and are considered to be informal by fashion authorities, limiting the places where you can wear them. I think that our feet would really appreciate it we could wear them more often.
    If you walk through any department store or shoe store and you notice the tons of sandals available. You'll notice that flip flops come in every color and material, which you could easily view in display in most living rooms. They may vary in price range and but are still a girls best friend.

자바에서 가비지 컬렉션은 언제 동작할지 알 수 없다.

idle 이거나 메모리가 부족하면 지 멋대로 동작한다고 한다.

하지만 강제로 동작을 시킬 수는 있다.

System.gc();

필요할 때 사용하자.
Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

  1. 바나나용 2009.10.05 16:58  댓글주소  수정/삭제  댓글쓰기

    고객님 System.gc()는 함부로 호출하시면 안됩니다. ㅋㅋㅋㅋㅋㅋ

자바에서 static 으로 변수를 선언하면 어떻게 될까.

여지껏 정확한 동작을 잘 모르고 있었다.

정확한 동작을 살펴본다.

static 으로 멤버변수를 선언하면 그 변수는 인스턴스 레벨이 아닌 클래스 레벨이 된다.

다시말해 클래스 자체에 메모리가 할당되어 변수로 가지게 되는 것이다.

코드를 보자.

public class StaticFieldTest
{
 int total = 0;
 static int grandTotal = 0;
 void accumulate(int amount)
 {
  total += amount;
  grandTotal += amount;
 }

}

grandTotal 을 static 으로 선언했다.

이렇게 되면 이 클래스의 인스턴스를 여러개 생성해도 grandTotal 의 값은 하나인 것이다.

왜냐하면 static 변수는 클래스 레벨에 존재하기 때문이다.

public class StaticFieldTestDrive
{
 public static void main(String[] args)
 {
  StaticFieldTest obj1 = new StaticFieldTest();
  StaticFieldTest obj2 = new StaticFieldTest();
  obj1.accumulate(10);
  obj2.accumulate(20);

  System.out.println("obj1.total = " + obj1.total);
  System.out.println("obj1.grandTotal = " + obj1.grandTotal);
  System.out.println("obj2.total = " + obj2.total);
  System.out.println("obj2.grandTotal = " + obj2.grandTotal);

 }
}

위 코드를 실행하면

---------- Java Running ----------
obj1.total = 10
obj1.grandTotal = 30
obj2.total = 20
obj2.grandTotal = 30

Output completed (0 sec consumed) - Normal Termination

위에서 보는 것과 같이 같은 값의 grandTotal 이 출력된다.

또 다르게 obj1, obj2 라고 코딩하지 않고 클래스 이름 즉 StaticFieldTest.grandTotal 을 직접 출력해도 같은 30 이 출력된다.

더불어 설명하면 static 변수나 메소드를 접근하는 방법은 클래스 이름을 이용하는 것이다.

인스턴스의 생성과는 상관없이 클래스 이름을 통해 접근할 수 있다.

static 으로 선언한 변수나 메소드는 인스턴스가 아닌 클래스 레벨에 존재한다. ( 모든 인스턴스에서 공유되는 자원이다. )

Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

  1. Richpapa 2009.09.22 17:52  댓글주소  수정/삭제  댓글쓰기

    그렇게 하지 않아도, main 메소드를 생각하면 쉬울 듯. 메인을 가지고 있는 클래스의 인스턴스보다 먼저 실행되어야만하는 이유 !!

    그리고 static {} 사용해서 실행해보면 bp 잡지 않는 이상 트레이스가 안되기 때문에 이것도 쉽게 알 수 있겠죠.

  2. 스파게티코드 2009.09.23 14:09  댓글주소  수정/삭제  댓글쓰기

    싱글턴으로 DB 작업하는 메소드 구현할때도 사용했었지...
    기억 안나나 ㅋㅋㅋ

  3. BlogIcon 행복한 프로그래머 궁금쟁이박 2009.09.23 16:16 신고  댓글주소  수정/삭제  댓글쓰기

    아 생각났다.ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

간만에 자바 포스팅.. ㅋ

졸업작품 관련해서 스크린 캡쳐하는 모듈을 간단하게 구현하여 보았다.

자바를 이용해서 스크린 캡쳐라든지, 마우스, 키보드 제어 같은 저수준의 동작들을

네트워크를 통해서 제어할 수 있느냐가

이번 졸작의 핵심사항인데.. 과연 성공할 수 있을지 흥미진진.

우선 스크린 캡쳐 코드.

package capture;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import com.sun.image.codec.jpeg.*;

public class Main extends JPanel implements Runnable, ActionListener {

    JButton btn_capture;
    Image img = null;

    // 생성자. UI 배치.
    public Main() {
        this.btn_capture = new JButton("영상캡쳐");
        this.btn_capture.addActionListener(this);
        this.setLayout(new BorderLayout());
        this.add(this.btn_capture, BorderLayout.SOUTH);
    }

    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        if (cmd.equals("영상캡쳐")) {
            System.out.println("영상을 캡쳐합니다..");
            this.capture();                     // 영상캡처 버튼이 눌리면 캡쳐.
        }
    }

    private void drawImage(Image img, int x, int y) {
        Graphics g = this.getGraphics();
        g.drawImage(img, 0, 0, x, y, this);
        this.paint(g);
        this.repaint();
    }

    public void paint(Graphics g) {
        if (this.img != null) {
            g.drawImage(this.img, 0, 0, this.img.getWidth(this), this.img.getHeight(this), this);
        }
    }

    public void capture() {
        Robot robot;
        BufferedImage bufImage = null;
        try {
            robot = new Robot();
            Rectangle area = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());

            bufImage = robot.createScreenCapture(area);     // Robot 클래스를 이용하여 스크린 캡쳐.

            //Graphics2D g2d = bufImage.createGraphics();
            int w = this.getWidth();
            int h = this.getHeight();

            this.img = bufImage.getScaledInstance(w, h - 20, Image.SCALE_DEFAULT);
            //this.repaint();
            this.drawImage(img, w, h);
            saveJPEGfile("cap.jpg", bufImage);              // JPG 파일로 변환하여 저장.
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static boolean saveJPEGfile(String filename, BufferedImage bi) {
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(filename);
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
            param.setQuality(1.0f, false);
            encoder.setJPEGEncodeParam(param);

            encoder.encode(bi);
            out.close();
        } catch (Exception ex) {
            System.out.println("Error saving JPEG : " + ex.getMessage());
            return false;
        }
        return true;
    }

    public void run() {
        while (true) {
            this.setBackground(Color.RED);
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
            }
            this.setBackground(Color.GREEN);

            try {
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }

    }

    public static void createFrame() {
        JFrame frame = new JFrame("Jv");
        JFrame.setDefaultLookAndFeelDecorated(true);
        Container cont = frame.getContentPane();
        cont.setLayout(new BorderLayout());
        Main mm = new Main();
        //new Thread(mm).start();
        cont.add(mm, BorderLayout.CENTER);

        frame.setSize(400, 400);
        frame.setVisible(true);
    }

    public static void main(String... v) {
        //new Main();
        JFrame.setDefaultLookAndFeelDecorated(true);
        createFrame();
    }
}


이 코드의 핵심은 바로 이곳.
robot = new Robot();
Rectangle area = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
bufImage = robot.createScreenCapture(area);     // Robot 클래스를 이용하여 스크린 캡쳐.

Robot 클래스를 생성하고, 현재 화면의 사이즈를 Rectangle 클래스로 받아 createScreenCapture 메소드의 인자로 넘기면 BufferedImage 인스턴스를 리턴하여 이미지를 얻을수 있다.

다음은 실행화면.

사용자 삽입 이미지


바탕화면을 캡쳐한것이 Panel의 Center에 위치하게 된다.
또한 이 이미지는 JPG 파일로 변환되어 저장되게 된다.(saveJPEGfile 메소드)

참고로 Robot 클래스는 1.3 부터 기본으로 자바에 포함되어 배포되고 있으며 JavaDoc에 설명이 자세하게 되어 있다. 궁금하면 찾아보도록 ㅎㅎ

여기는 Robot 클래스를 잘 설명해놓은 사이트.

출처 : http://semtle.tistory.com  셈틀쟁님이의 블로그
Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

  1. 2011.03.29 23:01  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

자바에서는 크게 다음 세 가지의 입출력을 다룬다.

1. 1 byte 단위 입출력
2. 2 byte 단위 입출력
3. 객체 단위 입출력


  • 1 byte 출력

콘솔 출력용
FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
dos.write...


파일 출력용
File file = new File("파일명");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
dos.write...


네트워크 출력용
Socket soc = new Socket(..);
BufferedOutputStream bos = new BufferedOutputStream(soc.getOutputStream());
DataOutputStream dos = new DataOutputStream(bos);
dos.write...


  • 1 byte 입력

콘솔 입력용
FileInputStream fis = new FileInputStream(FileDescriptor.in);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
dos.write...


파일 입력용
File file = new File("파일명");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
dos.write...


네트워크 입력용
Socket soc = new Socket(..);
BufferedInputStream bis = new BufferedInputStream(soc.getInputStream());
DataInputStream dis = new DataInputStream(bis);
dos.write...


  • 2 byte 출력

콘솔 출력용
OutputStreamWriter osw = new OutputStreamWriter(System.out);
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println(..);


파일 출력용
File file = new File("파일명");
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter pw = new PrintWriter(bw);
pw.println(...);


네트워크 출력용
Socket soc = new Socket(..);
OutputStreamWriter osw = new OutputStreamWriter(soc.getOutputStream());
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println(..);


  • 2 byte 입력

콘솔 입력용
InputStreamReader isr = new InputStreamReader(System.out);
BufferedReader br = new BufferedReader(isr);
br.readLine();


파일 입력용
File file = new File("파일명");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
br.readLine();


네트워크 입력용
Socket soc = new Socket(..);
InputStreamReader isr = new InputStreamReader(soc.getInputStream());
BufferedReader br = new BufferedReader(isr);
br.readLine();


 

  • 객체 출력

파일 출력용
File file = new File("파일명");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(...);


네트워크 출력용
Socket soc = new Socket(..);
BufferedOutputStream bos = new BufferedOutputStream(soc.getOutputStream());
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(...);



  • 객체 입력

파일 입력용
File file = new File("파일명");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(bis);
try {
 Object obj = ois.readObject();
}
catch(ClassNotFoundException ee){}


네트워크 입력용
Socket soc = new Socket(..);
BufferedInputStream bis = new BufferedInputStream(soc.getInputStream());
ObjectInputStream ois = new ObjectInputStream(bis);
try {
 Object obj = ois.readObject();
}
catch(ClassNotFoundException ee){}

Posted by 행복한 프로그래머 궁금쟁이박
TAG I/O, Java, 자바

댓글을 달아 주세요

사용자 삽입 이미지

MVC 를 철저히 구현한 스윙 컴포넌트 한번에 보기
Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

JDK 에서 가장 많이 쓰이는 패턴 중 하나인 옵저버(Observer) 패턴에 대해 설명한다.

헤드퍼스트 디자인 패턴 책 참고^^

날씨를 디스플레이해주는 기상 모니터링 애플리케이션 개발을 예로 들었다.

이 애플리케이션은 크게 기상 스테이션, Weather Data 객체, 디스플레이 객체

세 요소로 이루어진다.

WeatherData 클래스이다.

WeatherData :

getTemperature()

getHumidity()

getPressure()

measurementsChanged()

위의 세가지 게터메소드는 각각 최근에 측정된 온도, 습도, 기압 값을 리턴하는 메소드이다.

measurementsChanged 메소드는 기상 관측값이 갱신될 때마다 알려주기 위한 메소드이다.

WeatherData 객체를 개발한 사람들이 우리가 추가해야 할 부분에 대한 힌트를 남겨준 것이다.

우리는 이 메소드를 현재 조건, 기상통계, 기상예측 세가지 디스플레이를 갱신할 수 있도록

구현해야 한다.

또한 시스템이 확장 가능해야한다. 다른 개발자들이 별도의 디스플레이 항목을 만들 수 있도록 해야 하고

사용자들이 애플리케이션에 마음대로 디스플레이 항목을 추가/제거할 수 있도록 해야한다.

다음은 대강 구현해 본 measurementsChanged 메소드이다.

public class WeatherData {

       // 인스턴스 변수 선언

       public void meaasurementsChanged() {

                  float temp = getTemperature();
                  float humidity = getHumidity();
                  float pressure = getPressure();

                  // 디스플레이 갱신
                  curentConditionsDisplay.update(temp, humidity, pressure);
                  statisticsDisplay.update(temp, humidity, pressure);
                  forecastDisplay.update(temp, humidity, pressure);
         }
  
         // 기타 메소드

}

이 코드에는 문제가 있다.

구체적인 구현에 맞춰서 코딩했기 때문에 프로그램을 고치지 않고는 다른 디스플레이 항목을

추가/제거할 수 없는 것이다.

출판사 + 구독자 = 옵저버 패턴

이러한 신문 구독 메커니즘을 제대로 이해한다면 옵저버 패턴을 쉽게 이해할 수 있다.

출판사를 주제(subject), 구독자를 옵저버(observer) 라고 부른다는 것을 외워두자.

주제 객체에서는 일부 데이터를 관리한다.

옵저버 객체들은 주제 객체를 구독하고 있으며 ( 주제 객체에 등록되어 있으며 ) 주제의

데이터가 바뀌면 갱신 내용을 전달 받는다.

Duck 이라는 객체는 옵저버가 되기를 원한다. 주제객체에개 자기도 옵저버가 되고싶다고 이야기하고,

공식적인 옵저버가 된다. 이제 주제 객체의 값이 바뀌면 Duck 을 비롯한 모든 옵저버들이 주제 객체의 값이

바뀌었다는 연락을 받게 된다.

Mouse 라는 객체는 옵저버 목록에서 탈퇴하고 싶다는 요청을한다. 그러면 주제 객체에서 Mouse 의 요청을

받아들여 옵저버 집합에서 제거시킨다. 주제 객체게 또 새로운 값이 들어오면 이제 Mouse 한테는

연락이 되지 않는다.

그렇다면 옵저버 패턴의 정확한 정의를 알아보자

옵저버 패턴(Observer Pattern) 에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한

테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to_many) 의존성을 정의한다.


옵저버 패턴을 구현하는 방법에는 여러 가지가 있지만, 대부분 주제(Subject) 인터페이스와 옵저버(Observer)

인터페이스가 들어있는 클래스 디자인을 바탕으로 한다.

다음은 옵저버 패턴 다이어그램이다.


사용자 삽입 이미지

이 다이어그램과 인대다 관계는 무슨 관계가 있는가?

- 옵저버 패턴에서 상태를 저장하고 지배하는 것은 주제 객체이다. 따라서 상태가 들어있는 객체는 하나만

있을 수 있다. 하지만 옵저버는 사용하긴 하지만 반드시 상태를 가지고 있어야 하는 것은 아니다. 따라서

옵저버는 여러 개가 있을 수 있으며, 주제 객체에서 상태가 바뀌었다는 것을 알려주기를 기다리는, 주제에

의존적인 성질을 가지게 되는 것이다. 그러므로 하나의 주제와 여러 개의 옵저버가 연관된, 일대다 관계가

성립되는 것이다.

의존성과는 무슨 상관이 있나?

- 데이터의 주인은 주제(Subject) 이다. 옵저버는 데이터가 변경되었을 때 주제에서 갱신해 주기를 기다리는

입장이기 때문에 의존성을 가진다고 할 수 있다. 이런 방법을 사용하면 여러 객체에서 동일단 데이터를 제어하

도록 하는 것에 비해 더 깔끔한 객체지향 디자인을 만들 수 있게 된다.

디자인의 원칙 중에는 이러한 것이 있다.

"서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 ( Loose coupling ) 디자인을

사용해야 한다."


옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어 있는 객체 디자인을 제공한다.

주제와 옵저버는 서로 독립적으로 재사용할 수 있다. 주제나 옵저버를 다른 용도로 활용할 일이 있다고

해도 손쉽게 재사용할 수 있다. 그 둘이 서로 단단하게 결합되어 있지 않기 때문이다.

주제나 옵저버가 바뀌더라도 서로한테 영향을 미치지는 않는다. 둘이 서로 느슨하게 결합되어 있기

때문에 주제 혹은 옵저버 인터페이스를 구현한다는 조건만 만족된다면 어떻게 바꿔도 문제가 생기는

일은 없는 것이다.

느슨하게 결합하는 디자인을 사용하면 객체 사이의 상호 의존성을 최소화할 수 있기 때문에 변경사항

이 겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있게 된다!.

다음은 기상 스테이션의 다이어그램 설계이다.

사용자 삽입 이미지

이 다이어그램을 바탕으로 코드를 만들어 보자.

public interface Subject {

         public void registerObserver(Observer o);     // 옵저버를 등록
         public void removeObserver(Observer o);     // 옵저버를 제거
         public void notifyObservers();  // 주제 객체의 상태가 변경되었을 때 모든 옵저버들에게 알리는 메소드
}

public interface Observer {
          // Observer 인터페이스는 모든 옵저버 클래스에서 구현해야 한다.
          // 따라서 모든 옵저버는 update() 메소드를 구현해야 한다.
          public void update(float temp, float humidity, float pressure);

}

public interface DisplayElement {
          // display() 메소드 하나만 갖는다. 디스플레이 항목을 화면에 표시해야 하는 경우 호출한다.
          public void display();

}

WeatherData 에서 Subject 인터페이스 구현하기

public class WeatherData implements Subject {
 
 private ArrayList observers;
 private float temperatures;
 private float humidity;
 private float pressure;
 
 public WeatherData() {
  observers = new ArrayList();
 }
 
 public void registerObserver(Observer o) {
  observers.add(o);
 }
 
 public void removeObserver(Observer o) {
  int i = observers.indexOf(o);
  if(i >= 0) {
   observers.remove(i);
  }
 }
 
 /*
  * 상태에 대해 모든 옵저버들한테 알려주는 부분
  * 모두 Observer 인터페이스를 구현하는, 즉 update() 메소드가 있는
  * 객체들이므로 손쉽게 알려줄 수 있습니다.
  */
 public void notifyObservers() {
  for(int i = 0 ; i < observers.size() ; i++) {
   Observer observer = (Observer)observers.get(i);
   observer.update(temperatures, humidity, pressure);
  }
 }
 
 /*
  * 기상 스테이션으로부터 갱신된 측정치를 받으면 옵저버들한테 알립니다.
  */
 public void measurementsChanged() {
  notifyObservers();
 }
 
 public void setMeasurements(float temperature, float humidity, float pressure) {
  this.temperatures = temperature;
  this.humidity = humidity;
  this.pressure = pressure;
  measurementsChanged();
 }
}

디스플에이 항목 만들기


public class CurrentConditionsDisplay implements Observer, DisplayElement {
 
 private float temperature;
 private float humidity;
 private Subject weatherData;
 
 
 /**
  * 생성자에 weatherData 라는 주제 객체가 전달되며,
  * 그 객체를 써서 디스플레이를
  * 옵저버로 등록한다.
  */
 public CurrentConditionsDisplay(Subject weatherData) {
  this.weatherData = weatherData;
  weatherData.registerObserver(this);
 }
 
 public void update(float temperature, float humidity, float pressure) {
  this.temperature = temperature;
  this.humidity = humidity;
  display();
 }
 
 public void display() {
  System.out.println("Current conditions: " + temperature +
    "F degrees and " + humidity + " % humidity");
 }
}

테스트용 기상 스테이션 클래스


public class WeatherStation {

 public static void main(String [] args) {
  WeatherData weatherData = new WeatherData();
 
  CurrentConditionsDisplay currentDisplay =
   new CurrentConditionsDisplay(weatherData);
 
 // 코드 생성해야 함
 // StatisticsDisplay statisticsDisplay =
 //  new StatisticsDisplay(weatherData);
 // ForecastDisplay forecastDisplay =
 //    new ForecastDisplay(weatherData);
 
  // 새로운 기상 측정 값이 들어왔다고 가정
  weatherData.setMeasurements(80, 65, 30.4f);
  weatherData.setMeasurements(82, 60, 26.2f);
  weatherData.setMeasurements(78, 90, 29.3f);
 
 }
}

이제 주제 객체와 옵저버가 직접 대화를 하고 있게 되었다.

옵저버들이 Observer 인터페이스를 구현하고 있기 때문인 것이다.

이러한 옵저버 패턴은 사실 자바에 내장되어있다.

java.util 패키지에 들어있는 Observer 인터페이스와 Observable 클래스가 그것이다.

다음은 자바 내장 기능을 활용한 기상 스테이션 구현 코드이다.


import java.util.*;

public class WeatherDataExt extends Observable { // Observable 의 서브클래스
 
 private float temperature;
 private float humidity;
 private float pressure;
 
 public WeatherDataExt() {
  // 이제 생성자에서 옵저버들을 저장하기 위한 자료구조를 만들 필요가 없다.
 }
 
 public void measurementsChanged() {
  setChanged(); // 상태가 바뀌었다는 것을 알린다.
  notifyObservers();
 }
 
 public void setMeasurements(float temperature, float humidity, float pressure) {
  this.temperature = temperature;
  this.humidity = humidity;
  this.pressure = pressure;
  measurementsChanged();
 }

 
 //
 // 옵저버가 WeatherData 객체의 상태를 알아낼 때는 이 메소드를 사용한다.
 //
 public float getTemperature() {
  return temperature;
 }

 public float getHumidity() {
  return humidity;
 }

 public float getPressure() {
  return pressure;
 }
}

import java.util.*;

public class CurrentConditionsDisplayExt implements Observer, DisplayElement {

 Observable observable;
 private float temperature;
 private float humidity;
 
 public CurrentConditionsDisplayExt(Observable observable) {
  this.observable = observable;
  observable.addObserver(this);
 }
 
 public void update(Observable obs, Object arg) {
  if(obs instanceof WeatherDataExt) {
   WeatherDataExt weatherData = (WeatherDataExt)obs;
   this.temperature = weatherData.getTemperature();
   this.humidity = weatherData.getHumidity();
   display();
  }
 }
 
 public void display() {
  System.out.println("Current conditions : " +
    temperature + "F degrees and " + humidity + "% humidity");
 }
}

이번에는 java.util.Observable 의 단점에 대해 알아본다.

Observable 은 인터페이스가 아닌 클래스인 데다가, 어떤 인터페이스를 구현하는 것도 아니다.

안타깝게도 활용도와 재사용성에 있어서 제약조건으로 작용하는 몇 가지 문제점이 있다.

Observable 은 클래스이다.

Observable 이 클래스이기 때문에 서브클래스를 만들어야 한다는 점이 문제가 된다.

이미 다른 수퍼클래스를 확장하고 있는 클래스에 Observable 의 기능을 추가할 수 없기 때문이다.

그래서 재사용성에 제약이 생기게 되는 것이다.

또한 Observable 인터페이스라는 것이 없기 때문에 자바에 내장된 Observer API 하고 잘 맞는 클래스를

직접 구현하는 것이 불가능하다. java.util 구현을 다른 구현으로 바꾸는 것도 불가능하다.

그렇다면 어떻게 해야 할까?

java.util.Observable 을 확장한 클래스를 쓸 수 있는 상황이라면 Observable API 를 쓰는 것도 괜찮을

것이다. 하지만 앞에서 했던 것처럼 직접 구현할 수도 있다. 둘 중 어떤 방법을 쓰든 옵저버 패턴만 제대로

알고있다면 그 패턴을 활용하는 API 는 어떤 것이든 잘 활용할 수 있을 것이다.

Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

  1. BlogIcon replica watches 2013.01.22 16:56  댓글주소  수정/삭제  댓글쓰기

    난 여기 이렇게 다른 사람들이 읽을 수에 내 사이트로 연결되는 링크를 넣어. 내 독자에 대한 같은 행복합니다.

mysql.org 에서 먼저 DB 와 GUI 툴 정도를 다운받아 설치한다.

MySQL Community Server 에서 버전을 선택하여 다운받아 설치하면 된다.

또한 GUI Tools 도 알맞은 버전을 다운받아 설치한다.

설치과정은 생략^^;

mysql connector 은 mysql tools 디렉토리에 첨부되어있으니 프로젝트 라이브러리에 add 시켜야한다.

샘플 테이블을 만든 뒤 mysql 을 연동하는 프로그램을 만들어 보았다.


package prj_jdbc;
import java.sql.*;
/**
 *
 * @author 컴퓨터과학부
 */
public class HelloJDBC {

    String id, pwd;
   
    public HelloJDBC(String id, String pwd) {
        this.id = id;
        this.pwd = pwd;
        try {
            Class.forName("org.gjt.mm.mysql.Driver").newInstance();
                             
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
   
    public void getData() {
        try {
            String url = "jdbc:mysql://localhost:3306/contacts";
            String option = "?useUnicode=true&characterEncoding=KSC5601";
            url = url + option;
            Connection con = DriverManager.getConnection(url, id, pwd);
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery("select * from friends");
            while(rs.next()) {
                String name = rs.getString(1);
                String phone = rs.getString(2);
                String email = rs.getString(3);
                String company = rs.getString(4);
               
                System.out.println(name + " \t | " + phone + "\t | " + email + "\t | " + company);
            }
            rs.close();
            stmt.close();
            con.close();
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }
   
    public static void main(String [] args) {
        HelloJDBC hj = new HelloJDBC("root", "root");
        hj.getData();
    }
}
   
   

   

Posted by 행복한 프로그래머 궁금쟁이박
TAG Java, JDBC, MySQL

댓글을 달아 주세요

메소드의 오버라이딩, 하이딩, 오버로딩을 테스트 해 보았다.

오버라이딩과 하이딩의 차이점은 메소드를 static 으로 선언하지 않느냐, 하느냐의 차이이다.

즉 서브클래스의 인스턴스 메소드는 수퍼클래스의 인스턴스 메소드를 오버라이딩한다고 말하고,

서브클래스의 스태틱 메소드는 수퍼클래스의 스태틱 메소드를 하이딩한다고 말한다.

스태틱 메소드가 인스턴스메소드를 오버라이딩하거나 하이딩할 수는 없으며 그 역도 성립하지 않는다.

오버라이딩과 오버로딩의 차이점은 인자가 같으냐 마느냐의 차이이다.

인자가 같다면 메소드 오버라이딩이라 하고, 인자가 다르면 오버라이딩이라고 한다.



/**
 * 메소드 오버라이딩, 하이딩, 오버로딩 테스트
 */
public class ClassA {

 public void methodOne( int i) {
 
 }
 public void methodTwo(int i) {
  System.out.println("메소드2_클래스A");
 }
 public static void methodThree(int i) {
 
 
 }
 public static void methodFour(int i) {
  System.out.println("메소드4_클래스A");
 }
}


public class ClassB extends ClassA {

 /* 컴파일 에러
 public static void methodOne(int i) {
 
 }
public void methodThree(int i) {
 
 }*/
 public void methodTwo(int i) {                         /// 메소드 오버라이딩
  System.out.println("메소드2_클래스B");
 }
 
 
 public static void methodFour(int i) {                ///  메소드 하이딩
  System.out.println("메소드4_클래스B");
 }
 
 public void methodTwo() {                            /// 메소드 오버로딩
  System.out.println("메소드 오버로딩");
 }
 
 public static void methodFour() {                     /// 메소드 오버로딩
  System.out.println("메소드 오버로딩");
 }
 
 public ClassB() {
  methodTwo(1);
  methodTwo();
  super.methodTwo(1);
 
  methodFour(1);
  methodFour();
  super.methodFour(1);
 
 
 }
 
 public static void main(String [] args) {
 
  ClassB cb = new ClassB();
 
 
 }
}

결과는 다음과 같이 나올 것이다.

메소드2_클래스B
메소드 오버로딩
메소드2_클래스A
메소드4_클래스B
메소드 오버로딩
메소드4_클래스A

Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

제대로 좀 알고 있자;

오버라이드

메소드는 계약서이다.

오버라이딩 하려면 인자는 같아야 하고 이턴 유형도 호환 가능해야 한다.

오버라이드하는 메소드에서는 인자를 변경할 수 없다.

메소드를 더 접근하기 어렵게 만들면 안된다.(private로 설정하면 안되)

다형성이 작동하려면 하위클래스에서 상위 클래스의 메소드를 쓸때 그 메소드가 제대로 실행되어야 한다.

컴파일러에서 메소드 호출을 허가한 후에도 오버라이드하는 메소드의 인자와 리턴 형식이 같을 때만 그 메소드가 제대로 작동한다.

상위 클래스와 하위클래스의 이름이 같다.

매개변수의 타입과 개수, 순서가 같다.

리턴 타입이 같다.

상속받은 메소드의 내용을 변경할수 있다.


================================================

class A {

        void Pmethod() { }

}

class a extends A {

        void Pmethod() { }          //오버라이딩

        void Pmethod(int i) { }   //오버로딩

        void Cmethod() { }          //오버로딩

        void Cmethod(int i) { }    //오버로딩

      // 메소드이름이 같아도 매개변수가 달라서 다른 메소드 취급

}

================================================

   



오버로딩


이름이 같고 인자 목록이 다른 메소드 두 개 만들기.

상속이나 다형성과는 관계가 전혀 없다.

호출하는 쪽의 편의를 위해 같은 메소드를 서로 다른 인자 목록을 가진 여러 버전으로 만들수 있다.

int형을 인자로 받는 메소드를 호출하는 코드에 double을 받으려면 double형을 인자로 받는 메소드를 호출하는 버전의 메소드를 만들면 된다.

오버로드된 메소드의 여러가지(조건들)을 이행할 필요가 없기 때문에 접근단계 설정이 자유롭다.

상위클래스와의 메소드와 이름이 같아도 매개변수의 타입 개수 순서가 다르면 오버로딩이다.

상속 받은것 외에 기존에 없는 새로운 메소드를 정의한다.



시그니처(메소드 이름, 매개변수 리스트)가 다르기 때문에 다음은 각기 다른 메소드이다.

public class SquareOverload {
 public static int square(int n) {
  System.out.println("Integer Square");
  return n*n;
 }
 
 public static long square(long l) {
  System.out.println("long Square");
  return l*l;
 }
 
 public static double square(double d) {
  System.out.println("double Square");
  return d*d;
 }
 
 public static void main(String[] args) {
  int n = 5;
  long l = 100;
  double d = 1000.0;
 
  System.out.println("n Square = " + square(n));
  System.out.println("l Square = " + square(l));
  System.out.println("d Square = " + square(d));
 }
}


반환값도 시그니처의 한 부분이지만 메소드를 구분하는 요소로 쓰이지는 않는다.

다음은 같은 클래스 안에서 정의 될수 없다.

long square(long d)

int square(long d)

Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요