본문 바로가기
뒷북 정리 (국비 교육)/java

[Java] step15. Swing

by 규글 2022. 4. 25.

step15. Swing

 이번 단계에서 배울 것은 GUI (Graphic User Interface)이다. 이것을 배우기는 하지만, 이 친구를 이용해서 무언가를 할 일은 거의 없을 것이다. 그냥 java를 연습해본다고 생각하면 된다고 했다.

 

	public static void main(String[] args) {
		// JFrame 객체 생성하고 
		JFrame f=new JFrame("Hello Frame");
		// 초기 위치와 크기 지정 
		f.setBounds(100, 100, 500, 500);
		// 프레임을 닫았을때 자동으로 프로세스 종료 되도록 
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 레이아웃 매니저를 사용 하지 않도록 설정 
		f.setLayout(null);
		// 버튼 객체 생성
		JButton btn=new JButton("눌러보셈");
		// 버튼의 위치와 크기 지정 
		btn.setBounds(10, 10, 100, 40);
		// 프레임에 버튼 추가하기
		f.add(btn);
		
		// 프레임이 보이도록 한다. 
		f.setVisible(true);
		
		new MyFrame("나만의 프레임");
	}
	
	public static class MyFrame extends JFrame{
		// 생성자
		public MyFrame(String title) {
			super(title);
			// 초기화 작업
			setBounds(200, 200, 500, 500);
			setDefaultCloseOperation(EXIT_ON_CLOSE);
			setVisible(true);
			
			setLayout(null);
			JButton btn=new JButton("눌러보셈");
			btn.setBounds(10, 10, 100, 40);
			add(btn);
		}
	}

 예시의 두 가지는 모두 같은 결과를 낸다. 즉, 한 번의 run에 frame이 두 개 나타나게 된다. 그리고 frame을 하나라도 닫으면 두 frame 모두 닫히게 된다. 이는 frame 관련 process가 동일 process라는 것을 의미한다.

 먼저 main method로 만들어진 예시는 frame 객체의 참조값에 점을 붙여서, 두 번째 inner class로 만들어두고 해당 객체를 new로 만들어낸 예시는 this에 점을 붙여서 method를 사용했다. 두 번째의 경우는 이 'this.' 이 생략되었다고 할 수 있다.

 이러한 별 기능도 없는 frame 하나를 만들기 위해서 C 언어의 경우는 1000줄쯤 필요하다고 했다. 어떻게 만들어지는지는 몰라도 된댔다. 알 수도 없다고 했다. 하지만 java에서는 JFrame을 상속받아서 다 해주는 것이다.

 

- 01

import javax.swing.JButton;
import javax.swing.JFrame;

public class MyFrame extends JFrame{	
	// 생성자
	public MyFrame(String title) {
		super(title);
		setBounds(100, 100, 500, 500);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		// 프레임의 레이아웃 메니저를 사용하지 않기 때문에 UI 를 절대 좌표에 직접 배치해야 한다.
		setLayout(null);
		
		JButton btn1=new JButton("버튼1");
		// 버튼의 위치
		btn1.setLocation(10, 10);
		// 버튼의 크기
		btn1.setSize(100, 30);
		add(btn1); // 프레임에 btn1 추가 하기 
		
		JButton btn2=new JButton("버튼2");
		// 버튼의 위치와 크기를 한번에 지정하기 
		btn2.setBounds(120, 10, 100, 30);
		add(btn2); // 프레임에 btn2 추가 하기
		
		JButton btn3=new JButton("버튼3");
		// 버튼의 위치와 크기를 한번에 지정하기 
		btn3.setBounds(230, 10, 100, 30);
		add(btn3); // 프레임에 btn3 추가 하기
		
		JButton btn4=new JButton("버튼4");
		// 버튼의 위치와 크기를 한번에 지정하기 
		btn4.setBounds(340, 10, 100, 30);
		add(btn4); // 프레임에 btn3 추가 하기
		
		setVisible(true);
	}
	
	public static void main(String[] args) {
		new MyFrame("나의 프레임");
	}
}

 버튼의 위치와 크기를 따로 지정하려고 한다면 setLocation( ), setSize( ) method를 사용해도 좋지만, 굳이 번거롭게 그럴 필요 없이 동시에 setBountds( ) method를 이용하면 좋다. 해당 객체의 위치와 크기를 직접 지정한다.

 

 

- 02

		// 레이 아웃 메니저를 FlowLayout 으로 지정한다.
		// setLayout(new FlowLayout(0));
		setLayout(new FlowLayout(FlowLayout.LEFT));
		
		JButton btn1=new JButton("버튼1");
		add(btn1);
		
		JButton btn2=new JButton("버튼2");
		add(btn2);
		
		JButton btn3=new JButton("버튼3");
		add(btn3);
		
		JButton btn4=new JButton("버튼4");
		add(btn4);

 Layout을 FlowLayout 객체를 만들어서 넣어줄 수도 있다. 코드 이미지에 보이는 파란색 대문자로 된 것은 static final constant이다.

 

 해당 class에 대한 정보를 찾아보면 각 static final constant에 숫자가 배정되어 있음을 볼 수 있다. 이는 아무런 의미가 없을 숫자에 의미를 부여하는 것이다.

 

 setBounts( ) method의 경우 non-static method로 앞에 'this.' 이 생략된 경우이다. 그런데 이미지를 자세히 보면(잘 안보이겠지만) this.EXIT_ON_CLOSE 아래에 희미한 붉은 줄이 보이고, 해당 코드 줄 왼쪽에 메시지가 하나 있음을 확인할 수 있다. 이 메시지는 위 이미지와 같이 뜬다. 왜 그럴까?

  사실 여기에서 this는 MyFrame 객체를 가리킨다. 당연하다. 물론 MyFrame에도 EXIT_ON_CLOSE가 있긴 한데, 앞서 말했듯 파란색 친구는 static final constant field이다. 즉 static 영역에 있는 것이기 때문에 this로 접근하는 것이 아니다. 그래도 봐주는 것이긴 한데, 봐주면서도 겁을 주려고 하는 것이기에 붉은 표시를 하는 것이라고 했다.

 

 

- 03

		// 레이아웃 메니저를 GridLayout 으로 지정하기 (2행 2열)
		setLayout(new GridLayout(2, 2));

 Layout을 GridLayout 객체를 넣어줄 수도 있다. 이 경우 2행 2열 격자 배열이 된다.

 

 

- 04

		// 레이아웃 메니저를 BorderLayout 으로 지정하기 
		setLayout(new BorderLayout());
		
		JButton btn1=new JButton("버튼1");
		add(btn1, BorderLayout.NORTH);
		
		JButton btn2=new JButton("버튼2");
		add(btn2, BorderLayout.WEST);
		
		JButton btn3=new JButton("버튼3");
		add(btn3, BorderLayout.EAST);
		
		JButton btn4=new JButton("버튼4");
		add(btn4, BorderLayout.SOUTH);

 Layout에 BorderLayout 객체를 넣어줄 수 있다. 이때 BorderLayout에는 예시의 네 가지에 center까지 더해서 총 다섯 가지의 옵션이 있다. BorderLayout.NORTH의 자리는 "North" 가 된다.

 

 

- 05

	// default  생성자
	public MyFrame() {
		// MyFrame 의 레이아웃 메니저 지정하기
		setLayout(new FlowLayout());
		// 버튼
		JButton sendBtn=new JButton("전송");
		JButton removeBtn=new JButton("삭제");
		// 프레임에 버튼 추가하기 ( FlowLayout 의 영향을 받는다 )
		add(sendBtn);
		add(removeBtn);
		
		JOptionPane.showMessageDialog(this, "오잉?");
		
		// ActionListener 인터페이스 type  의 참조값 
		ActionListener listener=new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// System.out.println("전송 합니다~");
				JOptionPane.showMessageDialog(MyFrame.this, "전송합니다~");
			}
		};
		
		sendBtn.addActionListener(listener);
		// lambda 식 활용해 보기 
		removeBtn.addActionListener((e)->{
			JOptionPane.showMessageDialog(MyFrame.this, "삭제합니다~");
		});
	}
	
	public static void main(String[] args) {
		// MyFrame 클래스를 이용해서 객체 생성하고 참조값을 지역변수 frame 에 담기 
		MyFrame frame=new MyFrame();
		// 프레임의 제목 설정
		frame.setTitle("나의 프레임");
		// 프레임을 닫으면 자동으로 프로세스가 종료 되도록 한다.
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setBounds(100, 100, 500, 500);
		frame.setVisible(true);
	}

  이 예시의 경우 알림 dialog가 뜨고, 버튼이 반응한다. 알림 dialog는 showMessageDialog( ) method를 사용한 것인데, ActionListener 객체 안과 밖에서 인자로 넣는 this의 꼴이 다른 것을 볼 수 있다. 그것은 ActionListener 객체 안에서의 this는 해당 객체를 가리키기 때문에, MyFrame 객체를 참조하기 위해서는 그냥 this를 쓰면 안되기 때문에 다른 꼴이 된 것이다. 그리고 이 ActionListener 객체는 anonymous class의 꼴로 만들어져 있다.

 javascript에서도 비슷한 내용인 addEventListener를 다뤘었다. 다른 점은 javascript에서는 function이 단독으로 존재할 수 있었지만 java에서는 method 혼자 단독으로 존재하지 못하고 class 안에 존재해야 한다는 점이다.

 Lambda 식 표현도 있는데, 이는 웹 프로그래밍에서는 잘 안쓸 것이고, 안드로이드 개발이라면 자주 쓸 것이라고 했다.

 

 

- 06

public class MyFrame extends JFrame implements ActionListener{	
	// default  생성자
	public MyFrame() {
		// MyFrame 의 레이아웃 메니저 지정하기
		setLayout(new FlowLayout());
		// 버튼
		JButton sendBtn=new JButton("전송");
		JButton removeBtn=new JButton("삭제");
		
		// 버튼에 command 등록하기(어떤 기능을 하는 버튼인지)
		sendBtn.setActionCommand("send");
		removeBtn.setActionCommand("remove");
		
		// 프레임에 버튼 추가하기 ( FlowLayout 의 영향을 받는다 )
		add(sendBtn);
		add(removeBtn);
	
		// MyFrame 객체의 참조값을 전달해서 리스너 등록하기 
		sendBtn.addActionListener(this);
		removeBtn.addActionListener(this);
	}
	
	(main method 중략)
    
	// ActionListener 인터페이스를 구현 해서 강제 오버라이드된 메소드 
	@Override
	public void actionPerformed(ActionEvent e) {
		// JOptionPane.showMessageDialog(this, "버튼을 눌렀네?");
		
		// 액션 command 읽어오기
		String command=e.getActionCommand();
		
		if(command.equals("send")) {// 전송 버튼을 눌렀을때 
			JOptionPane.showMessageDialog(this, "전송합니다.");
		}else if(command.equals("remove")){// 삭제 버튼을 눌렀을때 
			JOptionPane.showMessageDialog(this, "삭제합니다.");
		}
	}
}

 이번 예시는 이전 예시와 같은 동작이다. 여기서 짚고 넘어가고자 하는 것은 비교를 할 때 사용하는 equals( ) 이다. 문자열이 같은지 비교를 하기 위해서는 '==' 와 같은 동등 비교는 불가능하다. 그래서 method를 사용해서 비교를 하는 것이다. 이를 이용해서 알아서 actionListener의 actionPerformed를 수행하게 하는 예시이다. 이 actionPerformed method는 ActionListener를 implements 했기 때문에 강제로 override 해야한다.

 

- 07

public class MyFrame extends JFrame implements ActionListener{
	// 필드 정의하기
	JButton sendBtn;
	JButton removeBtn;

	// default  생성자
	public MyFrame() {
		// MyFrame 의 레이아웃 메니저 지정하기
		setLayout(new FlowLayout());
		// 버튼의 참조값을 필드에 저장하기 
		this.sendBtn=new JButton("전송");
		removeBtn=new JButton("삭제");
		
		// 프레임에 버튼 추가하기 ( FlowLayout 의 영향을 받는다 )
		add(sendBtn);
		add(removeBtn);
	
		// MyFrame 객체의 참조값을 전달해서 리스너 등록하기 
		sendBtn.addActionListener(this);
		removeBtn.addActionListener(this);
	}
	
	(main method 중략)
    
	// ActionListener 인터페이스를 구현 해서 강제 오버라이드된 메소드 
	@Override
	public void actionPerformed(ActionEvent e) {
		// 이벤트가 발생한 UI(JButton) 의 참조값을 얻어온다.
		Object obj=e.getSource();
		
		if(obj == sendBtn) {// 전송 버튼을 눌렀다면 
			JOptionPane.showMessageDialog(this, "전송합니다.");
		}else if(obj == removeBtn) {// 삭제 버튼을 눌렀다면 
			JOptionPane.showMessageDialog(this, "삭제합니다.");
		}
	
	}
}

 마찬가지로 같은 결과를 낸다. 이번에는 action command 없이 같은 동작을 하게끔 한다. 먼저 field에 버튼의 참조값을 저장해두고, 해당 참조값을 비교하는 방식의 예시이다.

 

- 08

public class MyFrame extends JFrame implements ActionListener{
	JTextField inputMsg;

	// default  생성자
	public MyFrame() {
		setLayout(new FlowLayout());
		// 문자열 한줄을 입력할수 있는 JTextField
		inputMsg=new JTextField(10);
		add(inputMsg);
		
		JButton sendBtn=new JButton("전송");
		sendBtn.addActionListener(this);
		add(sendBtn);
	}
	
	(main method 중략)
    
	// ActionListener 인터페이스를 구현 해서 강제 오버라이드된 메소드 
	@Override
	public void actionPerformed(ActionEvent e) {
		// JTextField 에 입력한 문자열을 읽어와야한다.
		String msg=inputMsg.getText();
		
		JOptionPane.showMessageDialog(this, msg);
	}
}

 문자열을 입력할 수 있는 JTextField 객체를 사용한 예시이다. 예시에서처럼 입력받은 메시지를 field에 담아야 하는데, local variable에 담게되면 method에서 들고 올 수 없게 된다. 이 field와 local variable의 차이를 기억하자.

 

 

- 09

public class MyFrame extends JFrame 
					implements ActionListener, KeyListener{
	// 필드
	JTextField inputMsg;
	JLabel lab1;
	
	//default  생성자
	public MyFrame() {
		setLayout(new FlowLayout(FlowLayout.LEFT));
		// 문자열 한줄을 입력할수 있는 JTextField
		inputMsg=new JTextField(10);
		add(inputMsg);
		
		inputMsg.addKeyListener(this);
		
		JButton sendBtn=new JButton("전송");
		sendBtn.addActionListener(this);
		add(sendBtn);
		
		// JLabel 객체 생성
		lab1=new JLabel("label입니다.");
		add(lab1);
		
	}
	
	(main method 중략)

	// ActionListener 인터페이스를 구현 해서 강제 오버라이드된 메소드 
	@Override
	public void actionPerformed(ActionEvent e) {
		// JTextField 에 입력한 문자열을 읽어와야한다.
		String msg=inputMsg.getText();
		// 읽어온 문자열을 JLabel 에 추가하기
		lab1.setText(msg);
		// 입력창 문자열 삭제 하기 
		inputMsg.setText("");
	}
	// 키를 눌렀을때 호출되는 메소드 
	@Override
	public void keyPressed(KeyEvent e) {
		System.out.println("pressed");
		// 눌러진 키보드의 코드값을 읽어온다.
		int code=e.getKeyCode();
		if(code == KeyEvent.VK_ENTER) {// 엔터키를 눌렀다면 
			// JTextField 에 입력한 문자열을 읽어와야한다.
			String msg=inputMsg.getText();
			// 읽어온 문자열을 JLabel 에 추가하기
			lab1.setText(msg);
			// 입력창 문자열 삭제 하기 
			inputMsg.setText("");
		}
	}
	@Override
	public void keyReleased(KeyEvent e) {
		System.out.println("released");
	}
	@Override
	public void keyTyped(KeyEvent e) {
		System.out.println("typed");
	}
}

 이번 예시에서는 키보드 타이핑까지 가능한 KeyListener를 implements 했다. 그래서 강제로 keyPressed( )와 keyReleased( ), keyTyped( ) method를 override 했다. 

 

 

- 10

public class MyFrame extends JFrame 
					implements ActionListener, KeyListener{
	// 필드
	JTextField inputMsg;
	JLabel lab1;
	DefaultListModel<String> model;
	JList<String> list;
	
	// static final 상수
	static final String COMMAND_SEND="send";
	static final String COMMAND_REMOVE="remove";
	
	// default  생성자
	public MyFrame() {
		setLayout(new BorderLayout());
		// 문자열 한줄을 입력할수 있는 JTextField
		inputMsg=new JTextField(10);
		inputMsg.addKeyListener(this);
		
		JButton sendBtn=new JButton("전송");
		sendBtn.setActionCommand(COMMAND_SEND);
		sendBtn.addActionListener(this);
		// 삭제 버튼
		JButton removeBtn=new JButton("선택 삭제");
		removeBtn.setActionCommand(COMMAND_REMOVE);
		removeBtn.addActionListener(this);
		// 삭제 버튼 하단에 배치하기 
		add(removeBtn, BorderLayout.SOUTH);
		
		// JLabel 객체 생성
		lab1=new JLabel("label입니다.");
		
		// JPanel 객체 생성
		JPanel panel=new JPanel();
		// 패널도 레이아웃을 지정할수 있다( 기본값은 FlowLayout 가운데 정렬이다 )
		panel.setLayout(new FlowLayout(FlowLayout.LEFT));
		// JPanel 에 UI 추가 하고 
		panel.add(inputMsg);
		panel.add(sendBtn);
		panel.add(lab1);
		// 패널에 배경색 지정하기 
		panel.setBackground(Color.YELLOW);
		
		// JFrame 에 JPanel 을 북쪽에 배치하기 
		add(panel, BorderLayout.NORTH);
		// 목록을 출력할수 있는 JList 
		list=new JList<String>();
		
		// 기본 모델 객체 ( 목록에 출력할 data 를 가지고 있는 객체 )
		model=new DefaultListModel<String>();
		model.addElement("김구라");
		model.addElement("해골");
		model.addElement("원숭이");
		
		// 목록에 모델 연결하기 
		list.setModel(model);
		
		// 스크롤 패널에 목록 넣어주기
		JScrollPane sc=new JScrollPane(list, 
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		
		// 스크롤 패널을 프레임의 가운데에 배치하기 
		add(sc, BorderLayout.CENTER);
	}
	
	(main method 중략)

	// ActionListener 인터페이스를 구현 해서 강제 오버라이드된 메소드 
	@Override
	public void actionPerformed(ActionEvent e) {
		// 눌러진 버튼의 command  를 읽어온다.
		String command=e.getActionCommand();
		if(command.equals(COMMAND_SEND)) {// 전송 버튼을 눌렀을때
			send();
		}else if(command.equals(COMMAND_REMOVE)) {// 삭제 버튼을 눌렀을때
			// JList 객체에게 선택된 item 이 있는지 있다면 몇번째 아이템이 선택되었는지
			// 물어 봐야 한다. (메소드를 이용해서 알아낸다)
			int selectedIndex=list.getSelectedIndex();
			if(selectedIndex >= 0) {// 선택된 cell 이 있을때 
				// 정말로 삭제 할것인지 물어본다.
				int result=JOptionPane.showConfirmDialog(this, "삭제 할겨?");
				if(result==JOptionPane.YES_OPTION) {
					// JList 에 연결된 모델에서 해당 인덱스를 삭제한다. 
					model.remove(selectedIndex);
				}	
			}else {//선택된 cell 이 없을때
				JOptionPane.showMessageDialog(this, "삭제할 cell 을 선택하세요");
			}
		}
	}
	
	// 메소드 추가
	public void send() {
		// JTextField 에 입력한 문자열을 읽어와야한다.
		String msg=inputMsg.getText();
		// 읽어온 문자열을 JLabel 에 추가하기
		lab1.setText(msg);
		// 입력창 문자열 삭제 하기 
		inputMsg.setText("");
		// 모델에 입력한 문자열 추가하기
		model.addElement(msg);
	}
	
	// 키를 눌렀을때 호출되는 메소드 
	@Override
	public void keyPressed(KeyEvent e) {
		// System.out.println("pressed");
		// 눌러진 키보드의 코드값을 읽어온다.
		int code=e.getKeyCode();
		if(code == KeyEvent.VK_ENTER) {//엔터키를 눌렀다면 
			send();
		}
	}
	@Override
	public void keyReleased(KeyEvent e) {
		// System.out.println("released");
	}
	@Override
	public void keyTyped(KeyEvent e) {
		// System.out.println("typed");
	}
}

 중간의 기본 데이터를 넣어주는 코드를 보면, model 객체에 data를 넣어주고, 또 그 model 객체를 list에 연결해주고 있다. 즉, 어떤 기능 추가도 객체로 인식한다는 것이라고 했다. 객체 안에 기능(method)이 있고, 객체의 참조값을 전달해주면 그 기능을 사용하게끔 하는 것이라 했다.

 그리고 send( ) method의 경우 field의 것들을 부품으로 활용하고 있다.

 

앞으로 코딩을 할 때 nullPointerException이 많이 발생할 것이다. 어떤 변수가 null인데 method를 사용하려 한다든지, 참조값을 미처 넣지 않아서 null일 수도 있다. 이렇게 내용을 정리했지만, java에서 중요한 부분은 step04. ~ 14. 까지이다.

 

 

- 11

public class MyFrame extends JFrame implements ActionListener{
	// 필드
	JTextField tf_num1, tf_num2;
	JLabel label_result;
	
	// default  생성자
	public MyFrame() {
		// 프레임의 레이아웃 법칙 설정하기 
		setLayout(new BorderLayout());
		
		// JPanel
		JPanel topPanel=new JPanel();
		topPanel.setBackground(Color.YELLOW);
		//Panel 을 북쪽에 배치하기 
		add(topPanel, BorderLayout.NORTH);
		
		// JTextField 객체를 만들에서 JPanel 에 추가하기 
		tf_num1=new JTextField(10);
		topPanel.add(tf_num1);
		// 기능 버튼 객체를 만들어서 JPanel 에 추가하기
		JButton plusBtn=new JButton("+");
		JButton minusBtn=new JButton("-");
		JButton multiBtn=new JButton("*");
		JButton divideBtn=new JButton("/");
		topPanel.add(plusBtn);
		topPanel.add(minusBtn);
		topPanel.add(multiBtn);
		topPanel.add(divideBtn);
		// 두번째 JTextField  만들어서 패널에 추가 하기 
		tf_num2=new JTextField(10);
		topPanel.add(tf_num2);
		// JLabel
		JLabel label1=new JLabel("=");
		label_result=new JLabel("0");
		// 패널에 라벨 추가하기
		topPanel.add(label1);
		topPanel.add(label_result);
		
		// 버튼에 리스너 등록하기
		plusBtn.addActionListener(this);
		minusBtn.addActionListener(this);
		multiBtn.addActionListener(this);
		divideBtn.addActionListener(this);
		// 버튼에 액션 command 지정하기
		plusBtn.setActionCommand("plus");
		minusBtn.setActionCommand("minus");
		multiBtn.setActionCommand("multi");
		divideBtn.setActionCommand("divide");
		
	}

	(main method 중략)

	// ActionListener 인터페이스를 구현 해서 강제 오버라이드된 메소드 
	@Override
	public void actionPerformed(ActionEvent e) {
		try {
			// JTextField 에 입력한 문자열을 읽어와서 숫자(실수)로 바꿔준다.
			double num1=Double.parseDouble(tf_num1.getText());
			double num2=Double.parseDouble(tf_num2.getText());
			// 연산의 결과값을 담을 지역 변수
			double result=0;
			// 눌러진 버튼의 command 읽어오기
			String command=e.getActionCommand();
			if(command.equals("plus")) {
				result=num1+num2;
			}else if(command.equals("minus")) {
				result=num1-num2;
			}else if(command.equals("multi")) {
				result=num1*num2;
			}else if(command.equals("divide")) {
				result=num1/num2;
			}
			// 결과 값을 JLabel 에 출력하기 
			label_result.setText(Double.toString(result));
		}catch(Exception exe) {
			JOptionPane.showMessageDialog(this, "숫자 형식으로 입력해 주세요");
		}
	}
}

 앞선 예시들과 비슷한 전개를 통한 계산기 예시이다.

 

'뒷북 정리 (국비 교육) > java' 카테고리의 다른 글

[Java] step17. InputOutput  (0) 2022.04.25
[Java] step16. Thread  (0) 2022.04.25
[Java] step14. Exception  (0) 2022.04.24
[Java] step13. Util Class  (0) 2022.04.22
[Java] step12. Generic Class  (0) 2022.04.22

댓글