⭐Swing을 활용해 간단한 게임을 만들어봤다.

 

요청사항
----- ----- -----

캐릭터가 방향키에 맞게 움직이도록 만들것

움직이는 장애물을 만들것

캐릭터와 장애물이 충돌시 캐릭터가 삭제되도록 할 것

 

🤔기억해야 할 내용

쓰레드, While문을 활용해

사용자가 조작하지 않아도 스스로 움직이는 장애물을 설계할 수 있다.

 

1. 클래스 생성

JFrame 상속

KeyListener 구현

package _game;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class GameFrame extends JFrame implements KeyListener {

 

2. 변수 선언

이미지를 움직이기 위해

BufferImage 클래스를 불러왔다.

 

자동으로 움직이는 장애물을 구현하기 위해

내부클래스도 변수로 선언한다.

 

또한 컴포넌트들의 좌표값도 변수로 설정했다.

 

조건문 적용을 위해 플래그 값도 선언해둔다.

//변수
private BufferedImage backgroundImage;
private BufferedImage player1;
private BufferedImage player2;

private ImagePanel imagePanel;

private int playerX = 200;
private int playerY = 300;

private int player2X = 100;
private int player2Y = 300;

private boolean flag = true;

 

3. 생성자

생성자는 가장 먼저 실행되는 구문이다.

start() 구문으로 내부클래스 안에 만들어둔 run() 구문이 수행되도록 한다.

//생성자
public GameFrame() {
    initDate();
    setInitLayout();
    addEventListener();

    //메인작업자가 서브작업자를 생성
    Thread thread1 = new Thread(imagePanel);
    thread1.start();
    //imagePanel 안에 구현한 run() 메서드가 시작된다.
}

 

4. 메서드 - initData

생성자 호출시 실행

각 요소들을 생성한다.

 

ImageIO.read(new File(주소값))

은 주소값이 틀리면 런타임 오류를 발생시킨다.

try-catch 구문으로 예외처리한다.

//메서드
private void initDate() {
    setSize(1000, 600);
    setDefaultCloseOperation(3);
    setResizable(false);

    try {
        backgroundImage = ImageIO.read(new File("images/background.png"));
        player1 = ImageIO.read(new File("images/player1.png"));
        player2 = ImageIO.read(new File("images/player2.png"));

        //TODO player 이미지도 올려야 함
    } catch (IOException e) {
        e.printStackTrace();
    }

    imagePanel = new ImagePanel();

}//initDate

 

4-1. 메서드 setInitLayout

역시 생성자 호출시 실행

디자인을 정의한다.

 

setLayout을 지정하지 않았으므로

기본배치관리자인 BorderLayout이 적용됐다.

 

setLayout(new BorderLayout());

이런식으로 직접 넣어줘도 된다.

private void setInitLayout() {

    add(imagePanel);

    setVisible(true);

}//setInitLayout

 

4-2. 메서드 addEventListener

역시 생성자 호출시 실행

프로그램이 키 값을 인지하도록 만든다.

private void addEventListener() {

    /*
    keyListener는 인터페이스다.

    자바 문법에서는
    인터페이스나 추상클래스를
    구현클래스, 즉 객체로 사용하는 문법을 제공한다.

    new KeyListener() {
    추상 메서드를 구현 메서드로 오버라이드}
     */

    addKeyListener(this);
    /*
    JFrame 자체에
    KeyEventListener를 등록한다.
     */
}//addEventListener

 

5. 인터페이스의 추상메서드 오버라이드

키가 눌려질 때

KeyPressed

메서드에서 

정해진 키값(방향키)을 인식하면

캐릭터의 좌표값이 변화하도록 설계했다.

 

KeyEvent.VK_UP

은 위쪽방향키의 주소값을 가리킨다.

 

변한 값이 화면에 나타나도록

키를 입력할때마다

repaint() 가 호출되도록 한다.

@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}//keyReleased

@Override
public void keyPressed(KeyEvent e) {
    System.out.println("키 코드: " + e.getKeyCode());

    //TODO 화살표만 추출해 낼 예정

    int code = e.getKeyCode();

    if (code == KeyEvent.VK_UP) {
        playerY -= 10;
    } else if (code == KeyEvent.VK_DOWN) {
        playerY += 10;
    } else if (code == KeyEvent.VK_LEFT) {
        playerX -= 10;
    } else if (code == KeyEvent.VK_RIGHT) {
        playerX += 10;
    }

    //리페인트
    repaint();

}//keyPressed

 

6.내부 클래스

JPanel 상속

Runnable 구현

내부클래스를 만들고

여기에 서브작업자가 할 일을 명시했다.

//내부클래스
private class ImagePanel extends JPanel implements Runnable {

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.drawImage(backgroundImage, 0, 0, 1000, 600, null);
        g.drawImage(player1, playerX, playerY, 50, 50, null);
        g.drawImage(player2, player2X, player2Y, 50, 50, null);
    }

    @Override
    public void run() {
        //서브작업자가 해야하는 일을 명시하도록 약속돼 있다.

        boolean direction = false;
        //direction true 오른쪽, false 왼쪽

        while (flag) {

            if (direction == true) {
                player2X += 5;
            } else {
                player2X -= 5;
            }

            if (player2X >= 800) {
                direction = false;
            }

            if (player2X <= 100) {
                direction = true;
            }

            if (Math.abs(playerX - player2X) < 25 && Math.abs(playerY - player2Y) < 25) {
                player1 = null;
                System.out.println(playerX+ "," + playerY);
                System.out.println(player2X + "," + player2Y);
            }

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            repaint();

        }//end of while

    }//run

}//end of inner class

 

7. 메인함수

    //메인
    public static void main(String[] args) {

        new GameFrame();

    }//end of main

}//end of outer class

 

 

예제2)

부활버튼을 달아서 캐릭터가 죽어도 계속 살아날수 있게 해봤다.

 

바꿀 점

JButton을 추가

 

구조
===== ===== ===== ===== ===== ===== 
JFrame 상속/ KeyListener 구현

//변수
BufferImage (3), 내부클래스, 좌표변수, 플래그

//생성자 (3 + Thread)

//메서드 (3)

//오버라이드
KeyPressed

//내부클래스
JPanel 상속/ Runnable 구현

//내부클래스 오버라이드
paintComponent
run (While)

//메인

 

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

JButton을 추가했을 뿐인데 KeyListener가 동작하지 않는 문제가 발생했다.

 

해결방법.

 

initData() 메서드에

setFocusable(true);
requestFocusInWindow();

구문을 넣어 포커스를 되돌렸다.

 

부활 뒤에도

requestFocusInWindow();

구문을 넣어 포커스를 되돌렸다.

 

챗지피티에 따르면

KeyListener가 아닌 KeyBinding을 사용하면

포커스 문제를 원천차단할 수 있다.

package _my;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * 구조
 * =====
 * JFrame 상속/ KeyListener 구현
 * <p>
 * //변수
 * BufferImage (3), 내부클래스, 좌표변수, 플래그
 * <p>
 * //생성자 (3 + Thread)
 * <p>
 * //메서드 (3)
 * <p>
 * //오버라이드
 * KeyPressed
 * <p>
 * //내부클래스
 * JPanel 상속/ Runnable 구현
 * <p>
 * //내부클래스 오버라이드
 * paintComponent
 * run (While)
 * <p>
 * //메인
 */
public class Image_Game extends JFrame implements KeyListener, ActionListener {

    //멤버변수
    private JButton button;

    private BufferedImage backImg;
    private BufferedImage player1Img;
    private BufferedImage player2Img;

    private ImagePanel imagePanel;

    private int player1X = 400;
    private int player1Y = 50;

    private int player2X = 300;
    private int player2Y = 400;

    private boolean flag = true;

    //생성자
    public Image_Game() {
        initData();
        setInitLayout();
        addEventListener();
        addActionListener();

        Thread thread1 = new Thread(imagePanel);
        thread1.start();

    }

    //메서드
    private void initData() {
        setSize(1000, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        button = new JButton("부활버튼");

        try {
            backImg = ImageIO.read(new File("images/background.png"));
            player1Img = ImageIO.read(new File("images/player1.png"));
            player2Img = ImageIO.read(new File("images/player2.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        imagePanel = new ImagePanel();

    }//initData

    private void setInitLayout() {

        add(button, BorderLayout.NORTH);

        add(imagePanel);

        setVisible(true);

        setFocusable(true);
        requestFocusInWindow();

    }//setInitLayout

    private void addEventListener() {

        addKeyListener(this);

    }//addEventListener

    private void addActionListener() {

        button.addActionListener(this);

    }


    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println("키코드:" + e.getKeyCode());

        int keyNo = e.getKeyCode();

        if (keyNo == KeyEvent.VK_UP) {
            player1Y -= 10;
        } else if (keyNo == KeyEvent.VK_DOWN) {
            player1Y += 10;
        } else if (keyNo == KeyEvent.VK_LEFT) {
            player1X -= 10;
        } else if (keyNo == KeyEvent.VK_RIGHT) {
            player1X += 10;

        }

        repaint();

    }//keyPressed

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        player1X = 250; player1Y = 250;

        try {
            player1Img = ImageIO.read(new File("images/player1.png"));
            requestFocusInWindow();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }

    }


    //내부클래스
    private class ImagePanel extends JPanel implements Runnable {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            g.drawImage(backImg, 0, 0, 1000, 600, null);
            g.drawImage(player1Img, player1X, player1Y, 100, 100, null);
            g.drawImage(player2Img, player2X, player2Y, 100, 100, null);
        }

        @Override
        public void run() {

            boolean direct = true;

            while (flag) {

                if (direct == true) {
                    player2X += 5;
                } else {
                    player2X -= 5;
                }

                if (player2X >= 800) {
                    direct = false;
                }

                if (player2X <= 100) {
                    direct = true;
                }

                if ((Math.abs(player1X - player2X) <= 25) && (Math.abs(player1Y - player2Y) <= 25)) {
                    System.out.println("사망지점:"+player1X +","+player1Y);
                    player1Img = null;
                }

                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                repaint();

            }//while
        }//run
    }//inner

    //메인
    public static void main(String[] args) {
        new Image_Game();
    }//end of main
}//end of GF

 

 

예제2-2) 챗지티피가 제안한 KeyBinding 방식의 코드

package _my;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Image_Game2 extends JFrame implements ActionListener {

    //멤버변수
    private JButton button;

    private BufferedImage backImg;
    private BufferedImage player1Img;
    private BufferedImage player2Img;

    private ImagePanel imagePanel;

    private int player1X = 400;
    private int player1Y = 50;

    private int player2X = 300;
    private int player2Y = 400;

    private boolean flag = true;

    //생성자
    public Image_Game2() {
        initData();
        setInitLayout();
        addActionListener();
        initKeyBindings();

        Thread thread1 = new Thread(imagePanel);
        thread1.start();

    }

    //메서드
    private void initData() {
        setSize(1000, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        button = new JButton("부활버튼");

        try {
            backImg = ImageIO.read(new File("images/background.png"));
            player1Img = ImageIO.read(new File("images/player1.png"));
            player2Img = ImageIO.read(new File("images/player2.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        imagePanel = new ImagePanel();

    }//initData

    private void setInitLayout() {

        add(button, BorderLayout.NORTH);

        add(imagePanel);

        setVisible(true);

    }//setInitLayout

    private void addActionListener() {

        button.addActionListener(this);
        
    }//addActionListener

    private void initKeyBindings() {
        InputMap inputMap = imagePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = imagePanel.getActionMap();

        inputMap.put(KeyStroke.getKeyStroke("UP"), "moveUp");
        inputMap.put(KeyStroke.getKeyStroke("DOWN"), "moveDown");
        inputMap.put(KeyStroke.getKeyStroke("LEFT"), "moveLeft");
        inputMap.put(KeyStroke.getKeyStroke("RIGHT"), "moveRight");

        actionMap.put("moveUp", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                player1Y -= 10;
                repaint();
            }
        });

        actionMap.put("moveDown", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                player1Y += 10;
                repaint();
            }
        });

        actionMap.put("moveLeft", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                player1X -= 10;
                repaint();
            }
        });

        actionMap.put("moveRight", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                player1X += 10;
                repaint();
            }
        });
    }//initKeyBindings

    @Override
    public void actionPerformed(ActionEvent e) {

        player1X = 250; player1Y = 250;

        try {
            player1Img = ImageIO.read(new File("images/player1.png"));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }//actionPerformed

    //내부클래스
    private class ImagePanel extends JPanel implements Runnable {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            g.drawImage(backImg, 0, 0, 1000, 600, null);
            g.drawImage(player1Img, player1X, player1Y, 100, 100, null);
            g.drawImage(player2Img, player2X, player2Y, 100, 100, null);
        }//paintComponent

        @Override
        public void run() {

            boolean direct = true;

            while (flag) {

                if (direct == true) {
                    player2X += 5;
                } else {
                    player2X -= 5;
                }

                if (player2X >= 800) {
                    direct = false;
                }

                if (player2X <= 100) {
                    direct = true;
                }

                if ((Math.abs(player1X - player2X) <= 25) && (Math.abs(player1Y - player2Y) <= 25)) {
                    System.out.println("사망지점:"+player1X +","+player1Y);
                    player1Img = null;
                }

                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                repaint();

            }//while
        }//run
    }//inner

    //메인
    public static void main(String[] args) {
        new Image_Game2();
    }//end of main
}//end of GF

키바인딩 방식을 사용하면 매번 포커스를 되돌리지 않아도 된다.

 

⭐Swing을 활용해 로또번호를 생성하는 프로그램을 만들어봤다.

요청사항
----- ----- -----
버튼을 누르면 번호 6개 출력되도록 만들 것

보더레이아웃

타이틀

패널

기능들을 활용할 것

 

🤔알아둘 개념

 

단일 책임 원칙 SRP
Single Responsibility Principle

클래스를 설계할 때의 원칙
클래스는 가능한 단 하나의 책임을 가지는 것이 좋다

 

 

1. 단일 책임 원칙에 따라

난수를 생성하는 클래스를 따로 만들었다.

 

중복을 방지하기 위해

이중 for 문을 알 필요가 있다.

package _lotto.ch01;

import java.util.Arrays;
import java.util.Random;

/**
 * 4.29
 * 난수를 만들어주는 프로그램을 따로 만들어본다.
 * <p>
 * 단일 책임 원칙
 * SRP
 * Single Responsibility Principle
 * 클래스를 설계할 때의 원칙
 * 클래스는 가능한 단 하나의 책임을 가지는 것이 좋다.
 */
public class LottoRandomNumber {

    //상수
    final int LOTTO_NUMBER_COUNT = 6;

    //변수

    //생성자

    //메서드
    public int[] creatNumber() { //6개의 숫자묶음을 만들기 위해 배열 활용

        int[] lottoWinNum = new int[LOTTO_NUMBER_COUNT];

        Random random = new Random();

        //for문이 돌아간 횟수 확인해보자
        int forCount = 0;

        for (int i = 0; i < lottoWinNum.length; i++) {

            lottoWinNum[i] = random.nextInt(45) + 1;

            /*
            outer for 1회차 순회
                j < i, 0 < 0 반복문이 수행되지 않는다.
             */
            for (int j = 0; j < i; j++) {
                /*
                최초 outer for 가 2회차 수행될 때
                inner for는 1회차 수행된다.
                 */
                if (lottoWinNum[j] == lottoWinNum[i]) {
                    i--;
                    /*
                    값이 중복된 상태라면
                    outer for 의 i 값을 내려
                    한 번 더 반복문을 수행하도록 한다.
                     */
                }

                forCount++; // for문이 돌아간 횟수 계산

            }//inner for 끝

            /*
            중복을 없애보자

            0번과 1번이 같은가?
            0번과 2번이 같은가?
            0번과 3번이 같은가?
                만약 같다면 다시 번호 생성!
            0번과 4번이 같은가?
            0번과 5번이 같은가?

            2중 for문?
             */

        }//outer for

//        lottoWinNum[0] = random.nextInt(45) + 1;
//        lottoWinNum[1] = random.nextInt(45) + 1;
//        lottoWinNum[2] = random.nextInt(45) + 1;
//        lottoWinNum[3] = random.nextInt(45) + 1;
//        lottoWinNum[4] = random.nextInt(45) + 1;
//        lottoWinNum[5] = random.nextInt(45) + 1;

        System.out.println("for문이 돌아간 횟수는: " + forCount);

        //오름차순 정렬
        Arrays.sort(lottoWinNum);

        return lottoWinNum;
    }

    //메인
    public static void main(String[] args) {

        LottoRandomNumber lottoRandomNumber = new LottoRandomNumber();
        int[] result = lottoRandomNumber.creatNumber();

        for (int i = 0; i < result.length; i++) {
            System.out.print(result[i] + " ");
        }

    }//end of main
}//end of class

 

2. 생성자와 구현부가 들어간 클래스를 설계한다.

 

가)JFrame 상속, ActionListener 구현

package _lotto.ch02;
/**
 * 4.30
 */
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class LottoFrame extends JFrame implements ActionListener {

 

 

나) 멤버변수 선언

//member
private int testNumber = 45;

private JButton button;

private LottoRandomNumber lottoRandomNumber1;

private int[] result = new int[6];

 

다. 생성자

//constructor
public LottoFrame() {
    initData();
    setInitLayout();
    addEventListener();
}

 

라. 메서드

//method
    private void initData() {
        setTitle("로또게임");
        setSize(360, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        button = new JButton("게임시작");

        lottoRandomNumber1 = new LottoRandomNumber();
    }

    private void setInitLayout() {
//        add(button, BorderLayout.NORTH);
        setLayout(new FlowLayout());

        add(button);

        setVisible(true); //마지막에 실행
    }

    private void addEventListener() {
        button.addActionListener(this);
    }

 

마. 인터페이스 추상메서드 구현

ActionListener에 딸려오는

actionPerformed 메서드에

버튼을 눌렀을 시 나타나는 동작을 구현한다.

 

paint 메서드에는

글씨를 쓰는 기능을 구현했다.

@Override
public void actionPerformed(ActionEvent e) {

    System.out.println("그림을 그려라");

    result = lottoRandomNumber1.creatNumber();

    repaint();
}

@Override
public void paint(Graphics g) {
    super.paint(g);

    Font f = new Font("궁서체", Font.BOLD, 25);

    g.setFont(f);

    g.drawString(result[0] + " ", 50, 100);
    g.drawString(result[1] + " ", 100, 100);
    g.drawString(result[2] + " ", 150, 100);
    g.drawString(result[3] + " ", 200, 100);
    g.drawString(result[4] + " ", 250, 100);
    g.drawString(result[5] + " ", 300, 100);
}

 

바. 메인함수

//main
    public static void main(String[] args) {

        new LottoFrame();

    }//end of main
}//end of class

 

3. 리팩토링

상수를 선언해 더 깔끔하고 간결한 코드를 짜봤다.

package _lotto.ch03;

import java.util.Arrays;
import java.util.Random;

public class LottoRandomNumber {

    //상수
    static final int LOTTO_NUMBER_COUNT = 6;

    //메서드
    public int[] creatNumber() { //6개의 숫자묶음을 만들기 위해 배열 활용

        int[] lottoWinNum = new int[LOTTO_NUMBER_COUNT];

        Random random = new Random();

        for (int i = 0; i < lottoWinNum.length; i++) {

            lottoWinNum[i] = random.nextInt(45) + 1;

            for (int j = 0; j < i; j++) {

                if (lottoWinNum[j] == lottoWinNum[i]) {
                    i--;

                }

            }//inner for 끝

        }//outer for

        //오름차순 정렬
        Arrays.sort(lottoWinNum);

        return lottoWinNum;
    }

    //메인
    public static void main(String[] args) {

        LottoRandomNumber lottoRandomNumber = new LottoRandomNumber();
        int[] result = lottoRandomNumber.creatNumber();

        for (int i = 0; i < result.length; i++) {
            System.out.print(result[i] + " ");
        }

    }//end of main
}//end of class

 

구현부

package _lotto.ch03;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class LottoFrame extends JFrame implements ActionListener {

    //member
    private static final String INITIAL_MESSAGE = "로또 번호 생성";
    private static final String FONT_NAME = "명조";
    private static final int FONT_SIZE = 20;

    private JButton button;
    private LottoRandomNumber lottoRandomNumber;

    private boolean isInitState = true; // 초기상태 플래그

    private int[] currentNo;

    //constructor
    public LottoFrame() {
        initData();
        setInitLayout();
        addEventListener();
    }

    //method
    private void initData() {
        setTitle("로또게임");
        setSize(360, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        button = new JButton("게임시작");

        lottoRandomNumber = new LottoRandomNumber();

        currentNo = new int[LottoRandomNumber.LOTTO_NUMBER_COUNT];
    }

    private void setInitLayout() {
        setLayout(new BorderLayout()); //기본 배치관리자

        add(button, BorderLayout.NORTH);

        setVisible(true); //마지막에 실행
    }

    private void addEventListener() {
        button.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        isInitState = false;

        currentNo = lottoRandomNumber.creatNumber();

        repaint();
    }

    @Override
    public void paint(Graphics g) {

        super.paint(g);

        g.setFont(new Font(FONT_NAME, Font.BOLD, FONT_SIZE));

        if (isInitState) {
            g.drawString(INITIAL_MESSAGE, 100, 100);
        } else {

            for (int i = 0; i < currentNo.length; i++) {
                g.drawString(currentNo[i] + " ", (i + 1) * 50, 100);

            }//for

        }//if

    }//paint

    //main
    public static void main(String[] args) {

        new LottoFrame();

    }//end of main
}//end of class

 

 

4. 단일책임원칙 SRP 을 위배해

한 클래스에 모든 기능을 담는 것을 연습해봤다.

 

package _my;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;

public class LottoMachine extends JFrame implements ActionListener {

    /*
    구조
    ----- ----- -----
    JFrame 상속 ActionListener 구현

    멤버변수
    JButton
    배열

    생성자

    메서드
    makeNo(난수생성)

    initData
    setInitLayout
    addActionListener

    오버라이드
    action Performed
    paint

    메인
     */

    //멤버변수
    private JButton button;

    private boolean isInitState = true;

    private int[] currentNo;

    //생성자
    public LottoMachine() {

        initData();
        setInintLayout();
        addActionListener();

    }

    //메서드 - makeNo
    public int[] makeNo() {

        int[] lottoNo = new int[6];
        Random ran = new Random();

        for (int i = 0; i < 6; i++) {

            lottoNo[i] = ran.nextInt(45) + 1;

            for (int j = 0; j < i; j++) {

                if (lottoNo[i] == lottoNo[j]) {

                    i--; //중복 제거

                }//if

            }//inner

        }//outer
        Arrays.sort(lottoNo);

        return lottoNo;
    }//makeNo

    private void initData() {

        setTitle("로또머신");
        setSize(360, 150);
        setDefaultCloseOperation(3);
        setResizable(false);

        button = new JButton("게임시작");
        currentNo = new int[6];
    }

    private void setInintLayout() {

        add(button, BorderLayout.NORTH);

        setVisible(true);
    }

    private void addActionListener() {

        button.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        isInitState = false;

        currentNo = makeNo();

        repaint();
    }

    @Override
    public void paint(Graphics g) {

        super.paint(g);

        g.setFont(new Font("궁서체", Font.BOLD, 30));

        if (isInitState) {

            g.drawString("로또번호생성", 100, 100);

        } else {

            for (int i = 0; i < 6; i++) {

                g.drawString(currentNo[i] + " ", (i+1)*50, 100);

            }//for

        }//if

    }//paint

    //메인
    public static void main(String[] args) {

        new LottoMachine();

    }

}//end of class LM

⭐KeyListener
키보드를 눌렀을 때 호출되는 메서드를 가지고 있는 인터페이스

keyTyped
keyPressed
keyReleased
3개 메서드를 구현할 수 있다.

 

메서드 안에

if문을 넣어 각 키 값에 맞는 구문을 설계할 수 있다. 

if (e.getKeyCode() == KeyEvent.VK_UP) {

System.out.println("위쪽 화살표 누름");
}

 

예제) 방향키 4개를 누르면 서로 다른 메시지가 호출되는 구문

----- -----  -----  -----  -----  -----  -----  -----  -----  ----- 

⭐구조

 

상속 JFrame/ 구현 KeyListener

멤버변수

     JTextArea

생성자 (initData, setInitLayout, addEventListener)

메소드

     initData (컴포넌트 정의)

     setInitLayout (디자인)

     addEventListener (상호작용)

오버라이드

     keyRealeased

메인코드

-----  -----  -----  -----  -----  -----  -----  -----  -----  ----- 

 

package _swing2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
 * 4.29
 */
public class MyKeyEventFrame extends JFrame implements KeyListener {

    //member
    private final int FRAME_SIZE = 500;
    private JTextArea textArea;

    //constructor
    public MyKeyEventFrame() {

        initData();
        setInitLayout();
        addEventListener();
    }

    //method
    private void initData() {
        
        setSize(FRAME_SIZE, FRAME_SIZE);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        //프레임 사이즈 고정

        textArea = new JTextArea();
    }

    private void setInitLayout() {
        
        setLayout(new BorderLayout());
        add(textArea);

        // 마지막에 실행
        setVisible(true);
    }

    private void addEventListener() {

        textArea.addKeyListener(this); //키보드 누르면 반응
    }

    //override
    @Override
    public void keyTyped(KeyEvent e) {} //🤷‍♂️

    @Override
    public void keyPressed(KeyEvent e) {} //🤷‍♂️

    @Override
    public void keyReleased(KeyEvent e) {

        System.out.println("KeyCode: " + e.getKeyCode());
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            System.out.println("위쪽 화살표 누름");

        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            System.out.println("아래쪽 화살표 누름");

        } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            System.out.println("왼쪽 화살표 누름");

        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            System.out.println("오른쪽 화살표 누름");

        }
    }//keyReleased

    //main - test code
    public static void main(String[] args) {

        new MyKeyEventFrame();

    }//end of main
}//end of class

textArea 위에 키보드를 입력할 수 있다.
키보드 입력값이 시스템창에 출력된 모습

⭐액션리스너 인터페이스
ActionListener


자바 개발자들이
운영체제(마우스 제어 주체)와
상호작용할 수 있도록 에비해둔 기능이다.

 

이를 위해 운영체제와 약속된 추상 메서드를 오버라이드 한다.
만약 이벤트가 발생되면 이 메서드를 수행하도록 하기 위해서다.

⭐콜백
callback

직접 수행하는 것이 아니라
미리 정해져 있는 정보(객체)를 받을 수 있게 하는 것

💀단, 어떤 컴포넌트가 이벤트를 실행시킬 것인지
먼저 등록해둬야 한다.

 

예제) 버튼에 상호작용 기능을 넣어보자

----- -----  -----  -----  -----  -----  -----  -----  -----  ----- 

⭐구조

 

상속 JFrame/ 구현 ActionListener

멤버변수

     JButton

생성자 (initData, setInitLayout, addEventListener)

메소드

     initData (컴포넌트 정의)

     setInitLayout (디자인)

     addEventListener (상호작용)

오버라이드

     actionPerformed

메인코드

-----  -----  -----  -----  -----  -----  -----  -----  -----  ----- 

package _swing2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 4.29
 * 자바는 단일 상속만을 지원한다.
 * (Object 제외)
 *
 * 이벤트 리스너 예제 코드를 만들면서 이해해 보자
 *
 * 액션리스너 인터페이스
 * ActionListener
 * 자바 개발자들이
 * 운영체제(마우스 제어 주체)와
 * 상호작용할 수 있도록 에비해둔 기능이다.
 */
public class ColorChangeFrame extends JFrame implements ActionListener {

    /*
    구조
    ----- 
    멤버
    생성자
    메서드
    이너
    메인
     */

    //member
    JButton button1 = new JButton();

    //constructor
    public ColorChangeFrame() {
        initData();
        setInitLayout();
        addEventListener();
    }


    //method
    private void initData() {
        setSize(500,500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        button1 = new JButton("button1");
    }

    private void setInitLayout() {

        //배치관리자 기본
        setLayout(new FlowLayout());

        add(button1);

        //가장 마지막에 실행
        setVisible(true);
    }

    private void addEventListener() {

        button1.addActionListener(this);
    }
    /*
    운영체제와 약속된 추상 메서드를 오버라이드 한다.

    이는 이벤트가 발생되면 이 메서드를 수행하도록 하기 위해서다.

    ⭐콜백
    callback
    직접 수행하는 것이 아니라
    미리 정해져 있는 정보(객체)를 받을 수 있게 하는 것

    💀단, 어떤 컴포넌트가 이벤트를 실행시킬 것인지
    먼저 등록해둬야 한다.
     */
    @Override
    public void actionPerformed(ActionEvent e) {

        System.out.println("actionPerformed() 메서드 호출");
        System.out.println(e.toString());
    }

    //main
    public static void main(String[] args) {

        ColorChangeFrame colorChangeFrame = new ColorChangeFrame();

    }//end of main
}//end of class

버튼을 누를때마다 아래와 같은 메시지가 시스템창에 호출된다.

 

모든 수식을 생성자에 때려박을 수도 있다.
특정부분을 분리해서 짜두면
나중에 고칠때 편할 수 있다.

addEventListener
이 메서드의 역할은
이벤트 리스너만을 등록 처리하는 것이다.

 

예제2) 두개의 버튼을 다르게 인식하도록 코드를 짜봤다.

----- -----  -----  -----  -----  -----  -----  -----  -----  ----- 

⭐구조

 

상속 JFrame/ 구현 ActionListener

멤버변수

     JButton

     JPanel

생성자 (initData, setInitLayout, addEventListener)

메소드

     initData (컴포넌트 정의)

     setInitLayout (디자인)

     addEventListener (상호작용)

오버라이드

     actionPerformed

메인코드

-----  -----  -----  -----  -----  -----  -----  -----  -----  ----- 

 

본문중 구현된 actionPerformed 메서드의 매개변수 e를 통해 출력값이나 주소값을 구할 수 있다.

출력값을 비교하도록 하드코딩할 수도 있지만

주소값을 비교하는 편이 깔끔할 수 있다.

 

주소값을 구하려면

e.getSource를

JButton으로

다운캐스팅 해야 한다.

 

다운캐스팅은

부모 클래스 타입을
자식 클래스 타입으로 변환하는 것이다.

JButton selectedButton = (JButton)e.getSource();

매개변수 e의 출력값을 문자열과 직접 비교하는 방법

 

e.getSource 를 JButton으로 다운캐스팅하여 주소값을 비교하는 방법

package _swing2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 4.29
 * 버튼을 눌러 색깔을 바꿔보자
 */
public class ColarChangeFrame2 extends JFrame implements ActionListener {

    //member
    private JButton button1;
    private JButton button2;

    private JPanel panel1;

    //cons
    public ColarChangeFrame2() {
        initData();
        setInitLayout();
        addEventListener();
    }

    //meth
    private void initData() {
        setSize(500,500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        panel1 = new JPanel();

        button1 = new JButton("검정");
        button2 = new JButton("노랑");
    }

    private void setInitLayout() {
        setLayout(new BorderLayout());

        panel1.setBackground(Color.YELLOW);
        add(panel1, BorderLayout.CENTER);

        add(button1, BorderLayout.NORTH);
        add(button2, BorderLayout.SOUTH);

        //젤 마지막
        setVisible(true);
    }

    /*
    모든 수식을 생성자에 때려박을 수도 있다.
    특정부분을 분리해서 짜두면
    나중에 고칠때 편할 수 있다.

    addEventListener
    이 메서드의 역할은
    이벤트 리스너만을 등록 처리하는 것이다.
     */
    private void addEventListener() {

        button1.addActionListener(this); // 다형성
        button2.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.getSource());

        /*
        e.getSource를
        JButton으로 다운캐스팅
         */
        JButton selectedButton = (JButton)e.getSource();

        System.out.println(selectedButton);
        System.out.println(selectedButton.getText());

        //같은 객체의 주소값을 비교
        if (selectedButton == button1) {

            panel1.setBackground(Color.BLACK);

            //같은 객체의 문자열을 비교
        } else if (selectedButton.getText().equals(button2.getText())) {

            panel1.setBackground(Color.YELLOW);
        }

    }

    //main
    public static void main(String[] args) {
        ColarChangeFrame2 colarChangeFrame2 = new ColarChangeFrame2();
    }//end of main
}//end of class

button1을 누르자 panel1의 배경색이 검은색으로 바뀌었다.
button2을 누르자 panel1의 배경색이 노란색으로 바뀌었다.

 

 

예제3) 배경색을 바꿔주는 7개 버튼 넣어보기

----- -----  -----  -----  -----  -----  -----  -----  -----  ----- 

⭐구조

 

상속 JFrame/ 구현 ActionListener

멤버변수

     JButton

생성자 (initData, setInitLayout, addEventListener)

메소드

     initData (컴포넌트 정의)

     setInitLayout (디자인)

     addEventListener (상호작용)

오버라이드

     actionPerformed

메인코드

-----  -----  -----  -----  -----  -----  -----  -----  -----  ----- 

👍7개의 버튼을 원하는 위치에 배치하려면

setLayout의 배치관리자를

BorderLayout(동서남북중 5방배치) 나

FlowLayout(수평/수직배치) 보다는

 

null값을 줘서

수동으로 배치하는게 더 효과적일 수 있다.

이렇게 하면 컴포넌트들의 좌표, 크기 등도 수동으로 입력해줘야 한다.

 

👍 또한 버튼이 많은 만큼 for 반복문을 활용해서 코드 수를 줄여본다.

 

👍 한편 JFrame 의 프레임을 직접 수정하려면

getContentPane()

이라는 구문을 활용해야 한다.

package _my._swing;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 4.29
 * 무지개버튼 만들어보기
 */
public class Rainbow extends JFrame implements ActionListener {

    //member
    private JButton[] buttons = new JButton[7];

    private String[] colors = {"빨","주","노","초","파","남","보"};

    //constructor
    public Rainbow() {
        initData();
        setInitLayout();
        addEventListener();
    }

    //method
    private void initData() {
        setSize(365,350);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        for (int i = 0; i < buttons.length; i++) {
            buttons[i] = new JButton(colors[i]);
        }
    }

    private void setInitLayout() {

        setLayout(null);

        for (int i = 0; i < buttons.length; i++) {
            buttons[i].setLocation(i*50, 0);
            buttons[i].setSize(50, 50);
            add(buttons[i]);
        }

        //마지막
        setVisible(true);
    }

    private void addEventListener() {

        for (int i = 0; i < buttons.length; i++) {
            buttons[i].addActionListener(this);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        JButton selecB = (JButton)e.getSource();

        if (selecB == buttons[0]) {
            getContentPane().setBackground(Color.RED);
        } else if (selecB == buttons[1]) {
            getContentPane().setBackground(Color.ORANGE);
        } else if (selecB == buttons[2]) {
            getContentPane().setBackground(Color.YELLOW);
        } else if (selecB == buttons[3]) {
            getContentPane().setBackground(Color.GREEN);
        } else if (selecB == buttons[4]) {
            getContentPane().setBackground(Color.BLUE);
        } else if (selecB == buttons[5]) {
            getContentPane().setBackground(new Color(8,37,103));
        } else if (selecB == buttons[6]) {
            getContentPane().setBackground(new Color(109,0,255));
        }
    }

    //main
    public static void main(String[] args) {

        new Rainbow();

    }//end of main
}//end of class

초기화면
빨강 버튼을 눌렀을때
노랑 버튼을 눌렀을때
파랑 버튼을 눌렀을때
보라 버튼을 눌렀을때

 

⭐JLabel 이용해
이미지를 다뤄본다.

Jlabel.add() 메서드를 이용하면 이미지를 겹칠 수 있다.
좌표 기준으로 배치관리자를 세팅하려면
null 값을 세팅해야 한다.

💀이너클래스를 만들지 않고
JLabel을 이용해 이미지를 그리면
이미지 사이즈를 조절할 수 없다.

👍미리 조절된 이미지를 사용해야 한다.

 

예제) 배경이미지 위에 캐릭터이미지 올리기

----- -----  -----  -----  -----  -----  -----  -----  -----  ----- 

⭐구조

 

상속 JFrame

멤버변수

     JLabel

생성자 (initData, setInitLayout)

메소드

     initData (컴포넌트 정의)

     setInitLayout (디자인)

메인코드

-----  -----  -----  -----  -----  -----  -----  -----  -----  ----- 

💀서순에 주의하자

package _swing2;

import javax.swing.*;

/**
 * 4.29
 * ⭐JLabel 이용해
 * 이미지를 다뤄본다.
 *
 * Jlabel.add() 메서드를 이용하면 이미지를 겹칠 수 있다.
 * 좌표 기준으로 배치관리자를 세팅하려면
 * null 값을 세팅해야 한다.
 *
 * 💀이너클래스를 만들지 않고
 * JLabel을 이용해 이미지를 그리면
 * 이미지 사이즈를 조절할 수 없다.
 *
 * 👍미리 조절된 이미지를 사용해야 한다.
 *
 */
public class MyFrame extends JFrame {

    //member
    private JLabel backgroundMap;
    private JLabel player;

    //constructor
    public MyFrame() {

        initData();
        setinitLayout();
    }

    //method
    private void initData() {
        setTitle("JLabel을 이용한 이미지 사용 연습");
        setSize(500,500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ImageIcon backgroundIcon1 = new ImageIcon("images/background.png");
        ImageIcon playerIcon1 = new ImageIcon("images/player1.png");

        //JLabel
        backgroundMap = new JLabel(backgroundIcon1);
        player = new JLabel(playerIcon1);

        backgroundMap.setSize(500,500);
        backgroundMap.setLocation(0,0);

        player.setSize(100,100);
        player.setLocation(200,200);

        //가장 마지막에 실행
        setVisible(true);
    }

    private void setinitLayout() {

        /*
        좌표기반으로 세팅하기 위해 널값을 줬다.
        💀이때는 컴포넌트의 크기를 일일히 지정해줘야 한다.
         */
        setLayout(null);

        //루트 패널에 붙이기
        add(player);
        add(backgroundMap);
        /*
        💀서순
        먼저 쓴 놈이 젤 위에 나온다.
         */
    }

    //inner(🤷‍♂️)

    //main - 테스트 코드 작성
    public static void main(String[] args) {

        MyFrame myFrame = new MyFrame();

    }//end of main
}//end of class

⭐ 스윙
Swing

자바에서 GUI를 만들어주는 도구

⭐ 스윙의 JComponent
JFrame
JPanel
Jbutton
등등...

화면과 관련된 작업을 할때
배치관리자 Layout을 이해해야 한다.
Component 들을 어떻게 배치할 것인지

⭐동서남북 배치

배치관리자 개념중에
보더레이아웃이라는 개념이 있다.
BorderLayout은 컴포넌트를
동서남북 가운데로 배치하는 레이아웃이다.

프레임 (패널)
우리가 생성한 JButton 객체를
프레임에 붙인다.
add(buttons[i], directions[i]);

예제) 보더레이아웃과 배열 응용

package _swing;

import javax.swing.*;
import java.awt.*;

/**
 * 4.28
 * 화면과 관련된 작업을 할때
 * 배치관리자 Layout을 이해해야 한다.
 * Component 들을 어떻게 배치할 것인지
 */
public class BorderLayoutEx extends JFrame {

    //member
    JButton[] buttons;
    String[] directions = {BorderLayout.WEST, BorderLayout.EAST, BorderLayout.CENTER,
            BorderLayout.NORTH, BorderLayout.SOUTH};

    //constructor
    public BorderLayoutEx() {
        setTitle("borderLayout 연습");
        setSize(600, 600);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        initData(); // 1번
        setInitLayout();// 2번 서순 주의
    }

    //method
    private void initData() {
        buttons = new JButton[5];

        for (int i = 0; i < buttons.length; i++) {
            buttons[i] = new JButton("button" + (i + 1));
        }
//        buttons[0] = new JButton("button1");
//        buttons[1] = new JButton("button2");
//        buttons[2] = new JButton("button3");
//        buttons[3] = new JButton("button4");
//        buttons[4] = new JButton("button5");
    }

    private void setInitLayout() {
        /*
        배치관리자 개념중에
        보더레이아웃이라는 개념이 있다.
        BorderLayout은 컴포넌트를
        동서남북 가운데로 배치하는 레이아웃이다.
         */
        setLayout(new BorderLayout());
        /*
        프레임 (패널)
        우리가 생성한 JButton 객체를
        프레임에 붙인다.
         */

        for (int i = 0; i < 5; i++) {
            add(buttons[i], directions[i]);
        }
//        add(buttons[0], {BorderLayout.WEST);
//        add(buttons[1], {BorderLayout.EAST);
//        add(buttons[2], {BorderLayout.CENTER);
//        add(buttons[3], {BorderLayout.NORTH);
//        add(buttons[4], {BorderLayout.SOUTH);
    }

    //main thread - 테스트 코드 작성
    public static void main(String[] args) {

        BorderLayoutEx borderLayoutEx = new BorderLayoutEx();

    }//end of main

}//end of class

 


⭐수평, 수직으로 배치

배치관리자 FlowLayout
컴포넌트들을 수평, 수직으로 배치

예제) 수평 수직 배치

package _swing;

import javax.swing.*;
import java.awt.*;

/**
 * 4.28
 * 배치관리자 FlowLayout
 * 컴포넌트들을 수평, 수직으로 배치
 */
public class MyComponents extends JFrame {

    //member - 가장 많이 쓰이는 기능들
    private JButton button;
    private JLabel label;
    private JTextField textField;
    private JPasswordField passwordField;
    private JCheckBox checkBox;

    //constructor
    public MyComponents() {

        initData();
        setInitLayout();
    }

    //method
    private void initData() {
        setTitle("컴포넌트 확인");
        setSize(800,800);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        button = new JButton("JButton");
        label = new JLabel("글자를 띄우는 컴포넌트");
        textField = new JTextField("아이디를 입력하세요", 30);
        passwordField = new JPasswordField("비밀번호를 입력하세요", 40);
        checkBox = new JCheckBox("동의");

    }
    // JFrame 의 배치관리자를 결정해주는 메서드 (레이아웃)
    private void setInitLayout() {
        setLayout(new FlowLayout(FlowLayout.LEFT,30,30));

        //프레임에 붙여넣기
        add(button);
        add(label);
        add(textField);
        add(passwordField);
        add(checkBox);
    }

    //main thread - 테스트 코드 작성
    public static void main(String[] args) {

        MyComponents myComponents = new MyComponents();

    }//end of main

}//end of class

 


⭐좌표값으로 배치

배치관리자 설정을 아무것도 안하면
또는 null값을 세팅하면
좌표 기준으로 배치할 수 있다.

좌표 값으로 배치하려면
반드시 null 값을 입력하자

좌표값을 선택하려면
먼저 컴포넌트의 사이즈를 결정해야 한다.

그 다음
배치관리자가 좌표값이기 때문에
x,y 값을 지정해줘야 한다.

예제) 좌표값으로 배치

package _swing;

import javax.swing.*;

/**
 * 4.28
 * 배치관리자 설정을 아무것도 안하면
 * 또는 null값을 세팅하면
 * 좌표 기준으로 배치할 수 있다.
 */
public class NoLayoutEx01 extends JFrame{

    //member
    private JButton button1;
    private JButton button2;
    private JButton button3;

    //constructor
    public NoLayoutEx01() {

        initData();
        setInitLayout();
    }

    //method
    private void initData() {
        setTitle("좌표값으로 버튼 배치하기");
        setSize(316,339);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        button1 = new JButton("button1");
        button2 = new JButton("button2");
        button3 = new JButton("button3");
    }

    private void setInitLayout() {
        /*
        좌표 값으로 배치하려면
        반드시 null 값을 입력하자

        좌표값을 선택하려면
        먼저 컴포넌트의 사이즈를 결정해야 한다.

        그 다음
        배치관리자가 좌표값이기 때문에
        x,y 값을 지정해줘야 한다.
         */
        setLayout(null);

        button1.setSize(100,100);
        button2.setSize(100,100);
        button3.setSize(100,100);
        //setter 메서드

        button1.setLocation(0,0);
        button2.setLocation(100,100);
        button3.setLocation(200,200);

        //패널에 붙이기
        add(button1);
        add(button2);
        add(button3);
    }

    //main
    public static void main(String[] args) {

        new NoLayoutEx01();

    }//end of main

}//end of class



⭐ 패널
panel
기본 배치관리자로는 유연한 배치가 안되는 문제점

JPanel
패널을 여러개 나눈 다음
각 패널마다 배치관리자를 할당하는 방법이 있다.

GridLayout
루트패널의 그리드를 나누는 배치관리자: 예) 2행 1열

 


예제)패널별로 다른 배열 테스트

package _swing;

import javax.swing.*;
import java.awt.*;

/**
 * 4.28
 * 지금까지 배치관리자에 대해 알아봤다면
 * 기본 배치관리자로는 유연한 배치가 안되는 문제점
 *
 * JPanel
 * 패널을 여러개 나눈 다음
 * 각 패널마다 배치관리자를 할당하는 방법이 있다.
 *
 * GridLayout
 * 루트패널의 그리드를 나누는 배치관리자: 예) 2행 1열
 *
 */
public class MyPanelEx1 extends JFrame {

    //member
    private JButton button1;
    private JButton button2;
    private JButton button3;
    private JButton button4;

    /*
    패널을 사용하면
    컴포넌트들을 그룹화시킬 수 있다.
    각각의 배치관리자를 추가해 관리할 수 있다.
     */

    private JPanel panel1;
    private JPanel panel2;

    //constructor
    public MyPanelEx1() {

        initData();
        setinitLayout();
    }

    //method
    private void initData() {
        setTitle("패널연습해보기");
        setSize(500,500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        panel1 = new JPanel();
        panel2 = new JPanel();

        button1 = new JButton("button1");
        button2 = new JButton("button2");
        button3 = new JButton("button3");
        button4 = new JButton("button4");

    }

    private void setinitLayout() {

        //루트패널에 그리드를 나누는 배치관리자: 2행 1열
        setLayout(new GridLayout(2,1));

        //패널1,2 디자인
        panel1.setBackground(new Color(49, 137, 151));
        panel2.setBackground(Color.black);

        //버튼1~4 디자인
        button1.setBackground(new Color(65,23,0));
        button2.setBackground(new Color(65,23,0));
        button1.setForeground(Color.white);
        button2.setForeground(Color.white);
        button1.setSize(100,75);
        button2.setSize(100,75);

        button3.setBackground(Color.white);
        button4.setBackground(Color.white);

        //루트패널에 패널 삽입
        add(panel1);
        add(panel2);

        /*
        패널에 버튼을 삽입
        배치관리자를 설정하지 않았따면 기본 배치관리자가 세팅된다.
         */
        panel1.add(button1);
        panel1.add(button2);
        panel2.add(button3);
        panel2.add(button4);

       // 패널1 배치관리자 설정
//        panel1.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));

        /*
        panel1 <-- FlowLaout
        panel1 <-- null --> 컴포넌트 사이즈, 좌표값 ...
        null, flow
         */
        panel1.setLayout(null);
        button1.setLocation(30,0);
        button2.setLocation(160,0);

        //패널2 배치관리자 설정
        panel2.setLayout(new FlowLayout(FlowLayout.RIGHT, 30, 30));
    }

    //main - test code
    public static void main(String[] args) {

        MyPanelEx1 myPanelEx1 = new MyPanelEx1();

    }//end of main

}//end of class

 


예제) 집 그리기

package _my.swing;

import javax.swing.*;
import java.awt.*;
/**
 * 4.28
 *
 * 집그리기
 * 멤버변수, 생성자, 메서드, 내부클래스, 메인
 *
 */
public class MyPaintFrame extends JFrame {

    //member
    private Mypanel mypanel;

    private JPanel panel1;
    private JPanel panel2;

    //cons
    public MyPaintFrame() {

        initData();
        setInitLayout();
    }

    //method
    private void initData() {
        setTitle("집그리기");
        setSize(800,800);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        mypanel = new Mypanel();

        panel1 = new JPanel();
        panel2 = new JPanel();

    }

    private void setInitLayout() {
        add(mypanel);
    }

    //inner
    static class Mypanel extends JPanel {

        @Override
        public void paint(Graphics g) {
            super.paint(g);
//            setBackground(new Color(189,230,249));
            setBackground(new Color(61,47,80));

            //네모
            g.setColor(new Color(0,128,0));
            g.fillRect(100,400,550,250);
            g.fillRect(300,200,350,200);
            g.setColor(new Color(150,75,0));
            g.fillRect(0,650,1000,500);


            //1층창
            g.setColor(Color.blue);
            g.fillRect(150,550,200,50);
            g.fillRect(375,550,25,50);
            g.fillRect(425,550,25,50);
            //2층창
            g.fillRect(475, 450,125,50);
            g.fillRect(425, 450,25,50);
            g.fillRect(375, 450,25,50);
            //3층창
            g.fillRect(425, 300,25,50);
            g.fillRect(375, 300,25,50);
            //원
            g.setColor(Color.white);
            g.fillOval(25,25,175,175);
            g.setColor(new Color(61,47,80));
            g.fillOval(50,50,150,150);
            //선
            g.setColor(Color.red);
//            g.drawLine(100,400,300,300);
//            g.drawLine(300,100,650,200);
//            g.drawLine(300,100,300,200);
            //삼각형
            int[] intX = {100,300,300};
            int[] intY = {400,300,400};
            g.fillPolygon(intX,intY,3);
            int[] intX2 = {300,300,650};
            int[] intY2 = {100,200,200};
            g.fillPolygon(intX2,intY2,3);
            //둥근네모
            g.setColor(Color.black);
            g.fillRoundRect(660,550,100,100,50,50);
            //글자
            g.setColor(Color.red);
            g.drawString("💀💀💀💀💀",680,605);
            g.setFont(new Font("d2coding",Font.BOLD,25));
            g.drawString("051)912-1000",460,600);
            g.setColor(Color.black);
            g.drawString("그린컴퓨터학원",460,570);

        }
    }//end of inner

    //main
    public static void main(String[] args) {

        MyPaintFrame myPaintFrame = new MyPaintFrame();

    }//end of main
}//end of class

 

⭐내부 클래스
Inner Class

클래스 속 클래스

 

🤔Why?

⭐ 논리적인 그룹화
외부클래스 Outer Class 와 내부 클래스 Inner Class 를 논리적으로 묶어
논리적으로 그룹화한다.

👍이는 코드의 가독성과 구조화를 향상해
관련 클래스들을 유지하는데 도움을 준다.

주로 두 클래스가 연관이 크고 다른 곳에서 쓸일이 없을때 쓴다.

 

 

멤버 내부 클래스
Member Inner Class
주로 외부클래스 필드에 선언

✨정적 내부 클래스
Static Inner Class
외부 클래스의 필드에 선언되지만 static 키워드를 사용하여 선언되는 점이 다르다.
public static class xxx {
}

지역 내부 클래스
Local Inner Class
메서드 내부에 선언된다.

익명 내부 클래스
Anonymous Inner Class
이름이 없다. 주로 인터페이스나 추상클래스에 들어간다.

 

😎How

예제1) 멤버 내부 클래스 만들어보기

멤버 내부 클래스의 인스턴스는 외부 클래스의 인스턴스가 있어야 생성할 수 있다.

내부클래스를 메모리에 띄우지 못하는 모습

package _Inner;
/**
 * 4.25 inner 클래스 1강
 *
 * 멤버 내부 클래스
 */
public class OuterClass {

    private int num1 =10;
    /*
    inner 클래스란
    클래스 속 클래스다.

    멤버 내부 클래스를 만들어보자
     */
    class InnerClass {

        public void display() {
            System.out.println("inner class display() 호출" + num1);
            //외부 클래스에 있는 변수 호출
        }

    }//end of inner class

    //main thread
    public static void main(String[] args) {
//        InnerClass = innerClass = new InnerClass();
        //이너클래스를 그냥 메모리에 올릴 수는 없다.

        OuterClass OuterClass = new OuterClass();
        OuterClass.InnerClass innerClass = OuterClass.new InnerClass();
        
        innerClass.display();

    }//end of main
}//end of outter class

내부 클래스 실행 결과

 

예제2) 지역 내부 클래스 만들기

메서드 내부에 선언되며 선언된 메서드 내에서만 사용된다.

package _Inner;
/**
 * 4.25 inner 클래스 2강
 *
 * 지역 내부 클래스
 * Local Inner Class
 *
 * 메서드 안에서 생성됐다가
 * 메서드 호출이 끝나면 사라진다.
 */
public class OuterClass2 {

    public void display() {

        //지역 내부 클래스
        class LocalInnerClass {

            void printMessage() {
                System.out.println("지역 내부 클래스의 메서드 호출");
            }

        }//end of class

        /*
        LocalInnerClass 생성
        display() 메서드가 호출됐을 때 생성됐다고
        메서드 호출이 끝나면 사라진다.

         */
        LocalInnerClass inner = new LocalInnerClass();
        inner.printMessage();

    }//end of method display()

    //main
    public static void main(String[] args) {

        OuterClass2 outerClass2 = new OuterClass2();
        outerClass2.display(); //지역 내부클래스의 메서드 호출

    }//end of main

}//end of class OuterClass2

 

예제3)

인터페이스나 추상 클래스를 간편하게 구현할 때 사용한다.

package _Inner;
/**
 * 4.25 inner 클래스 2강
 * <p>
 * 익명 내부 클래스
 * 익명, 이름이 없다..
 */
public class OuterClass3 {

//    Runnable runnable; //인터페이스
    /*
    인터페이스는 기본적으로 인스턴스화 할 수 없다.
    하지만, 익명 내부 클래스로 구현할 수 있다.
     */

    public OuterClass3() {
        /*
        1.구현부를 만들어준다
        2.추상메서드를 구현메서드로 변경해준다
         */
        new Runnable() {
            @Override
            public void run() {
            }
        };
    }

    //main
    public static void main(String[] args) {
        /*
        익명 클래스란?
        익명, 이름이 없는 클래스다.
         */
        new OuterClass3(); //이것은 익명 클래스다.

    }//end of main

}//end of class

인터페이스를 그냥 인스턴스화 할 수는 없다.
인터페이스를 구현클래스로 생성해버렸다.

 

 

예제4) 정적 내부 클래스 만들기

static 키워드를 사용하여 선언된다.
이러면 외부 클래스의 인스턴스와 독립적으로 존재할 수 있다.

package _Inner;
/**
 * 4.25 inner 클래스 1강
 *
 * 정적 내부 클래스를 만들어보자
 * Static Inner Class
 */
public class Spaceship {

    private Engine innerEngine;
    // 우주선에서만 사용할 Engine 클래스를 만들고 싶어졌다.

    //method - 우주선 엔진 추가
    public void addengine(Engine engine) {
        innerEngine = engine;
    }

    //method - 우주선 출발
    public void startSpaceShip() {
        if (innerEngine == null) {
            System.out.println("엔진이 없습니다");
        } else {
            System.out.println("우주선이 출발합니다.");
        }
    }

    //정적 내부 클래스
    public static class Engine {

        public static int ENGINE_COUNT = 0;
        private int serialNumber;

        public Engine() {
            ENGINE_COUNT ++;
            this.serialNumber = ENGINE_COUNT;
        }

        public void start() {
            System.out.println("엔진 " + serialNumber + "번 가동");
        }

    }//end of inner class

}//end of outer class
package _Inner;
/**
 * 4.25 inner 클래스 1강
 *
 * 정적 내부 클래스를 만들어보자
 * Static Inner Class
 */
public class SpaceshipMainTest {

    //main thread
    public static void main(String[] args) {
        /*
        멤버 내부 클래스의 문제점
        외부 클래스가 먼제 생성돼야 접근 가능하다.
        불편하다

        정적 내부 클래스를 사용한다면?
         */
        Spaceship s1 = new Spaceship();
        s1.startSpaceShip();

        Spaceship.Engine engine1 = new Spaceship.Engine();
        Spaceship.Engine engine2 = new Spaceship.Engine();
        Spaceship.Engine engine3 = new Spaceship.Engine();

        s1.addengine(engine1); //우주선 si 에 엔진 engine1 추가

        s1.startSpaceShip(); //우주선 si 가동

        /*
        Why? 정적 내부 클래스를 사용하는 이유?

        ⭐논리적인 그룹화

        정적 내부 클래스는
        외부 클래스와 논리적으로 관련있는 클래스를
        그룹화하는데 유용하다

        👍이는 코드의 가독성과 구조화를 향상해
        관련 클래스들을 유지하는데 도움을 준다.
         */

    }//end of main
}//end of class

 

⭐multi threading
여러 쓰레드가 동시에 수행되는 프로그래밍을 말한다.
여러 작업이 동시에 실행되는 효과를 만들 수 있다.

각각의 쓰레드는 자신만의 변수, 메서드 등 작업 공간을 가질 수 있다.

💀멀티쓰레딩 할 때의 주의점
각 쓰레드 사이에는 공유하는 자원을 가질 수 있는데
여러 쓰레드가 자원을 공유하여 작업을 수행한다면
서로 자원을 차지하려는
race condition
발생 가능🐶

이에 따라 의도하지 않은 동작이 발생시킬 수 있다.

⭐이를 해소하기 위해
동기화처리가 필요하다

How? synchronized 구문을 추가해주자

 

예제)

은행에서 하나의 계좌를 사용하는 연관관계 클래스(가족)들을 만들고

동기화 처리를 해봤다.

 

1.은행계좌클래스를 만들고 입금, 출금 메서드 작성

2.아빠, 엄마 클래스 작성

3.코드 실행부 작성

 

1. 은행계좌 클래스

package _thread.Bank;
/**
 * 4.25 쓰레드 3강
 *
 * 각 쓰레드가 공유하는 자원 만들어보기
 */
public class BankAccount {

    //
    int money = 100_000; //초기 잔액

    //getter
    public int getMoney() {
        return money;
    }

    //setter
    public void setMoney(int money) {
        this.money = money;
    }

    //method - 입금
    public synchronized void deposit(int money) {
        int currentMoney = getMoney();

        synchronized (this) {//동기화 블록 처리

        try {//예외처리
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

//        this.money += money;
        setMoney(currentMoney + money);
        System.out.println("입금후잔액: " + getMoney());

        }//동기화 블록 처리

    }//end of method - 입금

    //method - 출금
    public synchronized int withdraw(int money) {
        int currentMoney = getMoney();

        try {//예외처리
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //방어적 코드
        if (currentMoney < money) {
            System.out.println("잔액 부족");
            return 0;
        } else {
            setMoney(currentMoney - money);
            System.out.println("출금후잔액: " + getMoney());
            return 0;
        }

    }//end of method - 출금

}//end of class

 

2. 아빠 클래스

package _thread.Bank;
/**
 * 4.25 쓰레드 3강
 *
 * 각 쓰레드가 공유하는 자원 만들어보기
 */
public class Father extends Thread { //쓰레드 상속

    //prop
    BankAccount bankAccount; //연관관계

    //cons
    public Father(BankAccount bankAccount) {
        this.bankAccount = bankAccount;
    }

    //method - run override

    @Override
    public void run() {
        //계좌에 입금요청
        bankAccount.deposit(10_000);

    }

}//end of class Father

 

3. 엄마 클래스

package _thread.Bank;
/**
 * 4.25 쓰레드 3강
 *
 * 각 쓰레드가 공유하는 자원 만들어보기
 */
public class Mother extends Thread{

    BankAccount bankAccount; //연관관계

    public Mother(BankAccount bankAccount) {
        this.bankAccount = bankAccount;
    }

    @Override
    public void run() {
        //계좌에 출금요청
        bankAccount.withdraw(5_000);
    }
}//end of class Mother

 

4. 코드 실행부

package _thread.Bank;
/**
 * 4.25 쓰레드 3강
 *
 * 각 쓰레드가 공유하는 자원 만들어보기
 */
public class BankAccountMainTest {

    //main
    public static void main(String[] args) {

        //객체 생성
        BankAccount bankAccount = new BankAccount();
        Father father = new Father(bankAccount); // bankAccount 객체를 바라보고 있다.
        Mother mother = new Mother(bankAccount);

        //아빠가 입금을 요청하다
        father.start(); // 작업요청 3초 지연

        //엄마가 입금을 요청하다
        mother.start(); // 작업요청 0.5초 지연

        /*
        어떻게 될까?
        입금 출금을 동일한 계좌에서 처리한다.

        정상적인 상황이라면
        10만원 + 1만원 - 5천원 = 10만5천원으로 나와야 한다.

        💀단, 멀티스레딩 프로그램에서는
        race condition 상태가 걱정된다.

        동시에 같은 작업을 하면?
        임계영역에 걸린다

        🐶의도치 않은 결과를 야기할 수 있다.
         */

    }//end of main
}//end of class

 

멀티쓰레드 환경에서

동기화를 제대로 하지 않으면 의도치 않은 결과를 야기할 수 있다.

 

선언부에 synchronized 구문을 넣어 동기화 블록처리를 하면 의도된대로 코드가 실행된다.

public synchronized void deposit(int money) {

}

 

쓰레드
Thread

프로세스에서 작업을 수행하는 단위

⭐process
프로그램이 실행되면
OS로부터 메모리를 할당받아
프로세스 상태가 된다.

⭐thread
하나의 프로세스에는
하나 이상의 쓰레드를 가지고 된다.
쓰레드는 실제 작업을 수행하는 단위이다.

메모리[@프로세스@프로세스@프로세스]
ㄴ@프로세스[#쓰레드#쓰레드#쓰레드]

 

 

🤔Why?

단일 쓰레드 환경이라면
메모리 소모가 큰 작업을 할때 다른 작업을 할 수 없게 된다.
자바에서는 이럴때 run() 메소드를 활용해서 다른 작업을 동시에 할 수 있다.

 

 

                  Run: cpu는 하나의 작업만을 수행
                     |  
Start => Runnable <=> Not Runnable(sleep): 스케줄러가 메모리에 올라온 프로세스들의 작업순서를 결정
                     |
                  Dead

 

package _thread;
/**
 * 4.25
 * 쓰레드 Thread
 *
 */
public class WhatIsThread {

    /*
    Thread란

    ⭐process
    프로그램이 실행되면
    OS로부터 메모리를 할당받아
    프로세스 상태가 된다.

    ⭐thread
    하나의 프로세스에는
    하나 이상의 쓰레드를 가지고 된다.
    쓰레드는 실제 작업을 수행하는 단위이다.

    메모리[@프로세스@프로세스@프로세스]
            ㄴ@프로세스[#쓰레드#쓰레드#쓰레드]

    ⭐multi threading
    여러 쓰레드가 동시에 수행되는 프로그래밍을 말한다.
    여러 작업이 동시에 실행되는 효과를 만들 수 있다.

    각각의 쓰레드는 자신만의 변수, 메서드 등 작업 공간을 가질 수 있다.

    💀단, 멀티쓰레딩을 할 때 주의점이 있다.
    각 쓰레드 사이에는 공유하는 자원을 가질 수 있는데
    여러 쓰레드가 자원을 공유하여 작업을 수행한다면
    서로 자원을 차지하려는
    race condition
    발생 가능🐶

    이에 따라 의도하지 않은 동작이 발생시킬 수 있다.

     */
}

 

 

😎How? 1.상속을 활용해서 쓰레드 만들기 (작업자 만들기)

Thread 클래스 상속
class xxx extends Thread

1.Thread 상속
2.run() 메서드 재정의
3.Thread 객체 생성
4.start(호출)

서브 작업자가 해야 할 일은
run() 메서드 안에 정의한다.

그리고 start 메서드를 통해서 쓰레드한테 일을 시작하도록 명령한다.
그러면 다른 쓰레드가 run()에 정의된 일을 수행한다.

 

예제1) 상속을 통해 쓰레드 만들기

package _thread;
/**
 * 4.25
 * 쓰레드 2강
 * <p>
 * 1.상속을 활용해서 쓰레드 만들기 (작업자 만들기)
 */
public class Worker extends Thread {

    private String name;

    public Worker(String name) {
        this.name = name;
    }
    /*
    서브 작업자가 해야 할 일은
    run() 메서드 안에 정의한다.

    그리고 start 메서드를 통해서 쓰레드한테 일을 시작하도록 명령한다.
    그러면 다른 쓰레드가 run()에 정의된 일을 수행한다.
     */

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("worker" + name + ": " + i);

            try {
                Thread.sleep(1000); // 1000millis = 1초 쉬어라

            } catch (InterruptedException e1) {
                System.out.println("외부신호에 따른 예외발생");
                e1.printStackTrace(); //예외가 발생한 부분을 좀 더 상세히 출력

            }
        }
    }

}//end of class Worker
package _thread;
/**
 * 4.25
 * 쓰레드 2강
 * <p>
 * 1.상속을 활용해서 쓰레드 만들기 (작업자 만들기)
 */
public class WorkerMainTest {
    //main thread - 일을 도맡아 하는 녀석
    public static void main(String[] args) {
        //사용하는 방법
        System.out.println("========== 메인 쓰레드 시작 ==========");

        //현재 쓰레드가 누구인가
        System.out.println(Thread.currentThread());

        //작업자 subthread 하나 만들기
        Worker worker = new Worker("워커1");
        worker.start(); //worker가 위임받은 작업을 시작하라는 명령어

        System.out.println("========== 메인 쓰레드 종료 ==========");


    }//end of main
}//end of class



😎 How? 2.인터페이스를 구현해 쓰레드 만들기 (작업자 만들기)
class xxx implement Runnable

1.Runnable 인터페이스 구현
2.run() 메서드 작성
3.Thread 객체 생성
4.start() 호출

💀인터페이스를 활용해서 쓰레드를 start() 시키려면
그냥은 start() 메서드가 없어서 동작을 시키지 못한다.
Thread subT1 = new Thread(worker2);
subT1.start();

👍인터페이스를 구현하는 방식이 더 일반적이다.

 

예제2) 인터페이스를 구현해 쓰레드 만들기

package _thread;
/**
 * 4.25
 * 쓰레드 2강
 * <p>
 * 2.인터페이스를 이용해 쓰레드 만들기 (작업자 만들기)
 */
public class Worker2 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 101; i++) {
            System.out.print(i + "\t");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (i % 10 == 0) {
                System.out.println();
            }
        }
    }

}//end of class

인터페이스를 구현한 쓰레드를 호출하는 방식은 조금 더 복잡하다

package _thread;
/**
 * 4.25
 * 쓰레드 2강
 * <p>
 * 2.인터페이스를 이용해 쓰레드 만들기 (작업자 만들기)
 */
public class Worker2MainTest {

    //main thread
    public static void main(String[] args) {

        // 상속을 통한 쓰레드 생성
//        Worker worker1 = new Worker("워커");
//        worker1.start();

        //사용하는 방법
        System.out.println("========== 메인 쓰레드 시작 ==========");
        //현재 쓰레드가 누구인가
        System.out.println(Thread.currentThread());

        // 인터페이스를 활용해서 쓰레드를 start() 시키는 방법
        Worker2 worker2 = new Worker2();
//        worker2.start();
        /*인터페이스를 사용했을 때
        start() 메서드가 없어서 동작을 시키지 못한다.
         */

        Thread subT1 = new Thread(worker2);
        subT1.start();
        System.out.println("========== 메인 쓰레드 종료 ==========");

    }//end of main
}//end of class

 

 

⭐예외처리
Exception

예상치 못한 상황, 예외를 처리하는 방법

이를 통해
💀 비정상 종료를 방지하고
💀 안정성과 신뢰성을 높일 수 있다.

How? 😃 try-catch 구문

why? 😒 사용자를 믿을 수 없기 때문


⭐finally 왜쓰는지?

자원해제를 위해..
💀 프로그램이 점점 느려지는 메모리 누수 방지


⭐ throw
예외를 강제로 발생시킨다

⭐ throws
메서드안에서 예외를 발생시킨다.

 

예외처리 예제1)

배열을 초과하는 예외를 발생시킨 뒤

이를 try - catch 구문으로 처리했다.

package _exception;
/**
 * 4.24
 * 예외처리 연습
 */
public class ArrayExceptionHandling {

    //main
    public static void main(String[] args) {

        //배열길이4, 인덱스길이5
        int[] arr = {1, 2, 3, 4, 5};

        /*
        예외처리

        try-catch
        try-catch-finally

        구문의 이해
         */
        try {
            // try 예외가 발생할 가능성이 있는 코드를 넣어준다
            for (int i = 0; i < 10; i++) {
                System.out.println("arr[" + i + "] = " + arr[i]);
            }
        } catch (ArrayIndexOutOfBoundsException e1) { //배열 초과 예외 코드
            System.out.println("배열의 범위를 벗어났다");

        } catch (Exception e2) {
            System.out.println(e2.getClass());

            // catch 예외가 발생했다면 예외처리 코드를 넣어준다
//            System.out.println("예외처리 - " + e.getMessage());

        }//end of try-catch

        System.out.println("비정상 종료되지 않았습니다");

    }//end of main
}//end of AEH

배열 범위를 벗어나 발생한 예외 ArrayIndexOutOfBoundsException
getclass() 메서드로 예외코드를 알아낼 수 있다.
예외처리 완료된 모습

 

예외처리 예제2)

try-catch-finally 구문을 사용해봤다.

package _exception;
/**
 * 4.24
 * 예외처리 연습
 */
import java.util.Scanner;

public class FinallyHandling {

    //main
    public static void main(String[] args) {

        /*
        try-catch-finally

         */
        Scanner scanner = new Scanner(System.in);

        try {
            System.out.print("숫자를 입력해주세요");
            int result = scanner.nextInt();
            System.out.println("입력한 숫자: " + result);

//            return;

        } catch (Exception e) {
            System.out.println("입력 오류 !!!");
        } finally {
            /*
            finally 반드시 수행돼야 하는 코드를 입력한다.
            심지어 return 키워드를 넣어도 finally 수행이 된다.
             */
            scanner.close(); //스트림을, 자원을 해제한다.
            System.out.println("자원을 해제했다");
        }

        System.out.println("프로그램이 비정상적으로 종료되지 않았음");

    }//end of main
}

숫자를 입력해 정상작동된 모습
숫자 입력란에 문자열을 입력해 InputMissmatchException 예외가 발생했다.
예외처리 완료된 모습

 

예외처리 예제3)

throw 구문을 이용해

예외를 일부러 발생시킬 수 있다.

package _exception;
/**
 * 4.24
 * throw
 * 예외클래스 직접 생성해서 발생시키기
 */
public class ThrowHandling {

    //main
    public static void main(String[] args) {

        boolean flag = true;

        if (flag) {
            /*필요에 의해
            직접 미리만든 예외 클래스를 생성할 수도 있다.
            new ArithmeticException();
             */
            throw new ArithmeticException(); // 향후 많이 쓰게 될 것

        }//end of if

    }//end of main
}//end of class

throw 구문으로 예외를 고의로 발생시켰다.

 

예외처리 예제4)

throws 구문을 이용해

메서드 안에서 예외를 발생시켜 봤다.

package _exception;
/**
 * 4.24
 * 예외처리 throws
 * 예외처리 던지기(미루기)
 */
public class Throws {

    //main
    public static void main(String[] args) {

        Calc calc = new Calc();

//        try {
//            int result = calc.divide(10, 0);
//            System.out.println("result: " + result);
//
//        } catch (Exception e) {
//            System.out.println(e.getClass());
//            System.out.println("에러발생");
//        }

        try {
            calc.divide(10, 0);
        } catch (Exception e) {
            System.out.println("인수로 0을 넣지 마시오");
        }


    }//end of main
}//end of class

class Calc {

    /*
    예외가 발생할 수 있는 코드에서
    직접 예외처리를 할 수 도 있지만

    사용하는 시점에
    알아서 적절하게 예외처리를 던질 수 있다.
     */
    public int divide(int n1, int n2) throws ArithmeticException, Exception {
        //이렇게 throws를 여러개 던질 수도 있다 - 강제성

        return n1 / n2;
    }

}//end of Calc

인텔리제이가 예외처리를 위한 try-catch 구문을 자동으로 만들어준다.
예외처리 완료된 모습.

 

예외처리 예제5)

사용자정의 예외를 만들고 이를 테스트해봤다.

 

DivideByZeroException이라는 클래스를 만들고

Exception을 상속했다.

 

또한 예외처리시 고유 메시지가 뜨도록 만들어봤다.

package _exception;
/**
 * 4.24
 * 사용자정의 예외클래스 만들기
 */
public class DivideByZeroException extends Exception { //예외 클래스 상속

    private String message;

    public DivideByZeroException(String message) {
        super(message);
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

}//end of class

 

코드 실행부

package _exception;
/**
 * 4.24
 * 사용자정의 예외클래스 만들기
 */
public class ExceptionTest1 {
    //main
    public static void main(String[] args) {
        /*
        사용자정의 예외클래스 사용해보기
         */
        int result = 0;

        Calc2 calc2 = new Calc2();
        try {
            calc2.divide(10, 0);
        } catch (DivideByZeroException e) {
            throw new RuntimeException(e);
        }

    }//end of main
}//end of class

class Calc2 {

    public int divide(int n1, int n2) throws DivideByZeroException {
        int result = 0;

        try {
            result = n1/ n2;

        } catch (Exception e) {
            //사용자정의 예외클래스 사용해보기
            throw new DivideByZeroException("0으로 나누지 마시오");
        }
        return result;
    }

}// end of Calc2

사용자정의 예외처리 메시지가 출력된 모습

+ Recent posts