단순 텍스트 만을 가진 레이블 컴포넌트 생성
JLabel textLabel = new JLabel("사랑합니다");
이미지를 가진 레이블 컴포넌트 생성
ImageIcon image = new ImageIcon("images/sunset.jpg");
JLabel imageLabel = new JLabel(image);
수평 정렬 값을 가진 레이블 컴포넌트 생성
ImageIcon image = new ImageIcon("images/sunset.jpg");
JLabel label = new JLabel("사랑합니다", image, SwingConstants.CENTER);
이미지 파일로부터 이미지를 읽기 위해 ImageIcon클래스 사용합니다.
다룰 수 있는 이미지 : png, gif, jpg
sunset.jpg의 경로명이"images/sunset.jpg"인경우
public class LabelEx extends JFrame {
public LabelEx() {
setTitle("레이블 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new FlowLayout());
JLabel textLabel = new JLabel("사랑합니다.");
ImageIcon beauty = new ImageIcon("images/beauty.jpg");
JLabel imageLabel = new JLabel(beauty);
ImageIcon normalIcon = new ImageIcon("images/normalIcon.gif");
JLabel label = new JLabel("보고싶으면 전화하세요",
normalIcon, SwingConstants.CENTER);
c.add(textLabel);
c.add(imageLabel);
c.add(label);
setSize(400,600);
setVisible(true);
}
public static void main(String [] args) {
new LabelEx();
}
}
● JCheckBox
: 선택(selected)과 비선택(deselected)의 두 상태만 가지는 체크 버튼 입니다.
class JCheckBoxText extends JFrame {
JTextField jtf ;
JCheckBoxText(){ // 중복선택 가능
JCheckBox jcb1 = new JCheckBox("java");
JCheckBox jcb2 = new JCheckBox("r");
JCheckBox jcb3 = new JCheckBox("python");
JCheckBox jcb4 = new JCheckBox("db");
jtf = new JTextField(10);
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(jcb1);
container.add(jcb2);
container.add(jcb3);
container.add(jcb4);
container.add(jtf);
-
setTitle("JCheckjBox");
setSize(500,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class JCheckBoxText_ {
public static void main(String[] args) {
new JCheckBoxText();
}
}
출력값 :
JCheckBox에서 Item 이벤트 처리)
Item 이벤트
:체크 박스나 라디오버튼의 선택 상태가 바뀔 발생하는 이벤트 입니다.
- 마우스나 키보드로 체크박스를 선택 상태를 바꾸는 경우
- 프로그램에서 선택 상태를 바꾸는 경우
위의 코드 + ItemListener
class JCheckBoxText extends JFrame implements ItemListener{
JTextField jtf ;
JCheckBoxText(){
JCheckBox jcb1 = new JCheckBox("java"); // 중복선택 가능
JCheckBox jcb2 = new JCheckBox("r");
JCheckBox jcb3 = new JCheckBox("python");
JCheckBox jcb4 = new JCheckBox("db");
jtf = new JTextField(10);
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(jcb1);
container.add(jcb2);
container.add(jcb3);
container.add(jcb4);
container.add(jtf);
// 리스너 등록
jcb1.addItemListener(this);
jcb2.addItemListener(this);
jcb3.addItemListener(this);
jcb4.addItemListener(this);
setTitle("JChecjBox 실습");
setSize(500,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
@Override
public void itemStateChanged(ItemEvent e) {
// System.out.println("이벤트 발생");
// 체크 박스를 선택하면 텍스트 상자에 선택한 내용이 표시된다.
JCheckBox selectCB = (JCheckBox)e.getItem();
// 반환형이 object 이다 = 어떤 컴퍼넌트라도 처리 가능하다.
// 반환형이 obhect 이므로 강제 형변형을 해준다.
jtf.setText(selectCB.getText());
switch(selectCB.getText()){
case "java":
System.out.println("java해보자");
break;
case "r":
System.out.println("r해보자");
break;
case "python":
System.out.println("python해보자");
break;
case "db":
System.out.println("db해보자");
break;
}
}
}
public class JCheckBoxText_ {
public static void main(String[] args) {
new JCheckBoxText();
}
}
ActionListener 와 같은 방식으로 method를 overriding 하여 기능을 추가 합니다.
출력값:
● JRadioButton
: 여러 버튼으로 그룹을 형성하고, 하나만 선택되는 버튼, 다른 버튼이 선택되면 이전에 선택된 버튼은 자동으로 해제됩니다.
체크박스와의 차이점
- 체크 박스는 각 체크박스마다 선택/해제 가능 합니다.
- 라디오 버튼은 그룹에 속한 버튼 중 하나만 선택 상태가 됩나다.
- 이미지를 가진 라디오버튼의 생성 및 다루기는 체크박스와 완전히 동일합니다.
버튼 그룹 객체 생성
ButtonGroup group = new ButtonGroup();
라디오버튼 컴포넌트 생성
JRadioButton apple= new JRadioButton("사과");
JRadioButton pear= new JRadioButton("배");
JRadioButton cherry= new JRadioButton("체리");
라디오 버튼을 버튼 그룹에 삽입
group.add(apple);
group.add(pear);
group.add(cherry);
라디오 버튼을 컨테이너에 삽입
container.add(apple);
container.add(pear);
container.add(cherry);
예시)
class JRadioButtonTest extends JFrame implements ActionListener{
JTextField jtf;
JRadioButtonTest(){
// 버튼 그룹 객체를 생성하여 그룹화할 컴포넌트 추가 (하나만 선택된다)
ButtonGroup bg = new ButtonGroup(); // 버튼을 그룹화
JRadioButton jrb1 = new JRadioButton("승마");
JRadioButton jrb2 = new JRadioButton("골프");
JRadioButton jrb3 = new JRadioButton("스쿠버");
JRadioButton jrb4 = new JRadioButton("글라이딩");
jtf = new JTextField(10);
// JRadioButton은 그룹화 하여 하나만 선택되게 한다.
bg.add(jrb1); bg.add(jrb2); bg.add(jrb3); bg.add(jrb4);
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(jrb1);
container.add(jrb2);
container.add(jrb3);
container.add(jrb4);
container.add(jtf);
jrb1.addActionListener(this);
jrb2.addActionListener(this);
jrb3.addActionListener(this);
jrb4.addActionListener(this);
setTitle("JRadioButton 실습");
setSize(350,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
jtf.setText(e.getActionCommand());
}
}
public class JRadioButton_ {
public static void main(String[] args) {
new JRadioButtonTest();
}
}
결과값:
● JTextField
: 한줄 짜리 텍스트(문자열) 입력 창을 구현한 컴포넌트 입니다.
- 텍스트 입력 도중 키가 입력되면 Action 이벤트 발생합니다.
- 입력 가능한 문자 개수와 입력 창의 크기는 서로 다릅니다.
public class TextFieldEx extends JFrame {
public TextFieldEx() {
setTitle("텍스트필드 만들기 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(new JLabel("이름 "));
c.add(new JTextField(20));
c.add(new JLabel("학과 "));
c.add(new JTextField("컴퓨터공학과 ", 20));
c.add(new JLabel("주소 "));
c.add(new JTextField("서울시 ...", 20));
setSize(300,150);
setVisible(true);
}
public static void main(String [] args) {
new TextFieldEx();
}
}
출력값:
JTextField의주요 메소드
- 문자열 편집 불가능하게 하기 : JTextField.setEditable(false);
- 입력 창에 문자열 출력 : JTextField.setText("hello");
- 문자열의 폰트 지정 : JTextField.setFont(new Font("고딕체", Font.ITALIC, 20);
● TextArea
: 여러 줄을 입력할 수 있는 텍스트 입력 창입니다.
- JScrollPane컴포넌트에 삽입하면 스크롤바 지원됩니다.
class JTextFieldTextArea extends JFrame{
JTextField jtf;
JTextArea jta;
JTextFieldTextArea(){
jtf = new JTextField(10);
jta = new JTextArea(7,20);
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(jtf);
container.add(jta);
setTitle("텍스트 필드 & 텍스트 Area 실습");
setSize(500,200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class JTextFieldTextArea_ {
public static void main(String[] args) {
new JTextFieldTextArea();
}
}
jtf = new JTextField(10); 크기가 10인 텍스트 필드 객체 생성(10글자가 들어갈 수 있는 크기) , 한 라인의 입력을 받을 수 있는 컨퍼넌트입니다. 숫자가 입력되기 전에는 아주 작게 보여줍니다.
jta = new JTextArea(7,20); // (7,20) 7행 20열의 텍스트에어리어 생성 , 화면상 7줄이 보입니다.
아쉬운점 : enter를 누르지 않으면 다음 줄로 넘어가지 않습니다.
출력값:
● JComboBox
: 풀다운 형태의 리스트 컴포넌트를 만들어주는 클래스 입니다.
- 항목(list)를 선택하는 이벤트 처리 ItemListener를 이용
- 텍스트 필드와 버튼, 그리고 드롭다운 리스트로 구성
public class ComboBoxEx extends JFrame {
private String [] fruits = {"apple", "banana", "kiwi", "mango", "pear",
"peach", "berry", "strawberry", "blackberry"};
private String [] names = {"kitae", "jaemoon", "hyosoo", "namyun"};
public ComboBoxEx() {
setTitle("콤보박스 만들기 예제");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new FlowLayout());
JComboBox<String> strCombo = new JComboBox<String>(fruits);
c.add(strCombo);
JComboBox<String> nameCombo = new JComboBox<String>();
for(int i=0; i<names.length; i++)
nameCombo.addItem(names[i]);
c.add(nameCombo);
setSize(300,300);
setVisible(true);
}
public static void main(String [] args) {
new ComboBoxEx();
}
}
출력값:
● JList
: 여러 개의 아이템을 리스트 형식으로 보여주고 선택하는 컴포넌트 입니다.
- JComboBox와 기본적으로 같은 기능
- JScrollPane에 Jlist를 삽입하여 스크롤 가능
class JListTest extends JFrame{
String[] sport = {"농구" , "축구" , "배구" , "야구", "테니스", "핸드볼"};
JListTest(){
JList<String> jl = new JList<String> (sport); // 제너릭으로 정의해서 사용.
jl.setVisibleRowCount(3); // 화면에 보여줄 행 수 정의.
JScrollPane jp = new JScrollPane(jl); // 스크롤바 적용
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(jp);
// JList가 탑제된 JScrollPane을 저장시켜줘야 정상적으로 동작이 진행된다.
setTitle("리스트 상자 실습");
setSize(200,200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class JList_ {
public static void main(String[] args) {
new JListTest();
}
}
JList는 하나의 배열로 저장 되는 것이기 때문에 같은 자료형으로 저장해야 합니다. 그래서 제너릭으로 선언해줍니다 .
결과값:
● JTabbedPane
: 그룹 폴더 컴포넌트를 만들어주는 클래스입니다.
class JPanelTest extends JFrame{
JPanelTest(){
Container container = getContentPane();
container.setLayout(new BorderLayout());
// 첫번째 패널 객체 생성 및 컴포넌트 추가
JPanel jp1 = new JPanel();
jp1.setLayout(new GridLayout(5,1));
jp1.add(new JButton("JAVA"));
jp1.add(new JButton("R"));
jp1.add(new JButton("PYTHON"));
jp1.add(new JButton("DB"));
jp1.add(new JButton("WEB"));
// 두번쨰 패널 객체 및 컴포넌트 추가
JPanel jp2 = new JPanel();
jp2.setLayout(new GridLayout(4,1));
jp2.add(new JRadioButton("java")); // JRadioButton 하나만 선택 가능 (ex, 성별)
jp2.add(new JRadioButton("r"));
jp2.add(new JRadioButton("python"));
jp2.add(new JRadioButton("db"));
// 2개의 패널을 J프레임컨테이너에 탑재
container.add(jp1,BorderLayout.WEST);
container.add(jp2,BorderLayout.EAST);
setTitle("패널 컨테이너 실습");
setSize(500,180);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class JPanelTest_ {
public static void main(String[] args) {
new JPanelTest();
}
}
컨테이너 : 컴포넌트를 담을 수 있는 클래스 - Frame, Panel(컨테이너 컴포넌트)
- java.awt.Container 상속
- 컨포넌트가 컨테이너에 탑재 되어야 UI 출력이 가능합니다.
UI 프로그램 샘플)
● 스윙 패키지 사용을 위한 import문
- import java.awt.*; 그래픽 처리를 위한 클래스들의 경로명
- import javax.swing.*; 스윙 컴포넌트 클래스들의 경로명
- import javax.swing.event.*; 스윙 이벤트를 위한 경로명
● 스윙 프레임
모든 스윙 컴포넌트를 담는 최상위 GUI 컨테이너
- JFrame을 상속받아 구현
- 컴포넌트가 화면에 보이려면 스윙 프레임에 부착되어야 합니다.
- 프레임을 닫으면 프레임 내의 모든 컴포넌트가 보이지 않게 됩니다.
스윙 프레임(JFrame) 기본 구성
프레임 – 스윙 프로그램의 기본 틀
메뉴바– 메뉴들이 부착되는 공간
컨텐트 팬 – GUI 컴포넌트들이 부착되는 공간
// 컨테이너의 구조 형성(프로그램 시작과 동시에 ui 구현)
class ButtonTest extends JFrame{
ButtonTest(){
// 버튼, 레이블 객체 생성 (컴포넌트의 요소-> 자료형)
JButton male = new JButton("남자"); // 남자 버튼
JButton female = new JButton("여자"); // 여자 버튼
JLabel label = new JLabel("당신의 성별은?"); // 문자열을 출력
// Frame 컨테이너 객체 생성
Container container = getContentPane();
// 배치관리자 생성: 자동 컴포넌트 요소 배치
FlowLayout layout = new FlowLayout();
container.setLayout(layout);
// 컨테이너는 컴포넌트를 플로우 레이아웃에 맞추어 배치해준다.
// 컨테이너에 컴포넌트 추가 (.add)
container.add(male);
container.add(female);
container.add(label);
// 컨테이너의 제목, 크기, 종료, 보이기(출력)
setTitle("버튼 컴포넌트 테스트"); // 컨테이너 제목
setSize(300,100); // 컨테이너 크기(가로, 세로)
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 컨테이너 종료(안보이게만 되어 있던 기능 -> 완전 종료)
setVisible(true); // 컨테이너 보이기(출력)
}
}
public class Swing01 {
public static void main(String[] args) {
new ButtonTest();
}
}
Frame 컨테이너 객체 생성 (getContentPane()) Container container = getContentPane(); 안에서 new 해서 컨테이너를 만들어서 주소값을 return해줍니다. 최소 한개의 컨테이너가 생성이 되면 이 메서드를 통해서 new 하는 것이 아니라 하나의 컨테이너로 사용합니다.
(calender 컨셉 동일)
결과값
위의 결과에 추가적인 기능을 넣어보도록 하겠습니다. 남자, 여자의 버튼이 눌리면 단신의 성별은? 남자, 여자 로 출력되도록 하겠습니다.
이러한 추가적인 정보를 넣는 것을 컴포넌트에 이벤트 처리한다고 합니다.
- 이벤트란 : 특정 컴포넌트에서 발생하는 사건을 의미하며, 버튼의 클릭, 텍스트 상자에 커서가 들어오는 경우등을 예로 들 수 있습니다.
이벤트 처리 절차
1. 이벤트와 연관된 인터페이스를 사용하여 클래스 작성
2. 컴포넌트 객체 생성
3. 하단 객체에 리스너를 등록
4. 이벤트 처리할 처리 루틴을 작성
class ButtonEvent extends JFrame implements ActionListener{
JLabel result; // 생성자 밖에서도 사용하기 위해서 field에 선언
ButtonEvent(){
JButton male = new JButton("남자");
JButton female = new JButton("여자");
JLabel label = new JLabel("당신의 성별은?");
result = new JLabel();
Container container = getContentPane();
FlowLayout layout = new FlowLayout();
container.setLayout(layout);
container.add(male);
container.add(female);
container.add(label);
container.add(result);
// 이벤트 처리를 위한 버튼에 리스너 등록
male.addActionListener(this);
female.addActionListener(this);
setTitle("버튼 컴포넌트 테스트");
setSize(300,100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
result.setText(e.getActionCommand());
}
}
public class ButtonEvent_ {
public static void main(String[] args) {
new ButtonEvent();
}
}
위에 똑같은 코드에 implements ActionListener 다형성을 통한 오버라이딩으로 되어진 method를 통해서 이벤트를 구현하게 해줍니다.
male.addActionListener(this); 자신 객체 안에서 이벤트 처리의 동작을 처리해줍니다. 리스터 등록이 끝이 나면 actionPerformed(); method가 호출합니다. female.addActionListener(this); 위의 코드와 동일한 처리를 해줍니다.
public void actionPerformed(ActionEvent e) { = (ActionEvent e) : java가 선언한 참조변수의 e 의 주소값을 불러옵니다. 버튼이 눌렸다 라는 의미로 해석하며, ActionListener 정의 되어 있는 추상메서드 : 다형성에 의한 오버라이딩 , 버튼이 눌렸을 때의 기능을 Method를 통해서 구현합니다. 메서드만 오버라이딩해서 기능만 정의가 된것이지 실질적인 이벤트 기능이 탑재 된 것이 아니므로 addActionListener(); 를 꼭 입력해야합니다.
결과값:
이미지 파일을 이용한 Swing 구현)
class JImageTest extends JFrame{
JImageTest(){
// ImageIcon ii = new ImageIcon("images/korea.gif"); ./ 뺀다고 해서 문제 되지 않는다.
ImageIcon ii = new ImageIcon("./images/korea.gif");
// (./) default 위치에서 시작하겠다.
// "버튼 이름" 과 이미지 아이콘으로 버튼 객체 생성
// 2. 버튼에 아이콘 삽입
JButton korea = new JButton("클릭", ii);
// 이미지를 넣을 때 ("입력할 text" , 이미지file);
Container container = getContentPane();
// method를 통해서 container 객체를 가져온다.
container.setLayout(new FlowLayout());
container.add(korea);
setTitle("대한민국");
setSize(500,180);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class JImage_ {
public static void main(String[] args) {
new JImageTest();
}
}
ImageIcon 참조변수 = new ImageIcon("./ 폴더명 / 파일이름" );
폴더의 위치 default 값:
ImageIcon ii = new ImageIcon("./images/korea.gif");
- 패킷 교환 네트워크에서 송신 호스트와 수신 호스트가 데이터를 주고 받는 것을 관장하는 프로토콜입니다.
- TCP보다 하위 레벨 프로토콜
+) IP 주소 : 네트워크 상에서 유일하게 식별될 수 있는 컴퓨터 주소입니다.
- 숫자로 구성된 주소로 4개의 숫자가 ‘.’으로 연결되어있습니다. 예) 192.156.11.15
- 숫자로 된 주소는 기억하기 어려우므로 www.naver.com과 같은 문자열로 구성된 도메인 이름으로 바꿔 사용합니다.
●포트
- 통신하는 프로그램 간에 가상의 연결단 포트 생성합니다.
- IP 주소는 네트워크 상의 컴퓨터 또는 시스템을 식별하는 주소입니다.
- 포트 번호를 이용하여 통신할 응용프로그램 식별합니다.
- 모든 응용프로그램은 하나 이상의 포트 생성 가능합니다.
- 포트를 이용하여 상대방 응용프로그램과 데이터 교환합니다.
● 클라이언트/서버(client/server) - 컴퓨터간의 관계를 역할(role)로 구분하는 개념으로 서비스를 제공하는 쪽이 서버, 제공받는 쪽이 클라이언트입니다. - 제공하는 서비스의 종류에 따라 메일서버(email server), 파일서버(file server), 웹서버(web server) 등이 있습니다. - 전용서버를 두는 것을 ‘서버기반 모델’, 전용서버없이 각 클라이언트가 서버역할까지 동시에 수행하는 것을
‘P2P 모델’이라고 합니다.
● 소켓프로그래밍
소켓 (socket)
: TCP/IP 네트워크를 이용하여 쉽게 통신 프로그램을 작성하도록 지원하는 기반 기술을 말합니다.
특징
- 두 응용프로그램 간의 양방향 통신 링크의 한쪽 끝 단
- 소켓끼리데이터를 주고받음
- 소켓은 특정 IP 포트 번호와 결합
- 자바로 소켓 통신할 수 있는 라이브러리 지원
- 소켓 종류 : 서버 소켓과 클라이언트 소켓
- 전화할 때 양쪽에 전화기가 필요한 것처럼, 프로세스간의 통신에서도 양쪽에 소켓이 필요
예시) Socket 클래스(서버/ 클라이언트)
서버)
public class SocketServer { // 서버로 구성해서 동작.
public static void main(String[] args) {
try {
ServerSocket serverScoket = new ServerSocket(9001);
// 서버로 동작되게 구성하도록 해준다.
// ServerSocket 인스턴스 되는 순간 서버로 구현되어지도록 한다(환경구축 완료)
while(true) { // 클라이언트의 요청이 오면 바로 처리 할 수 있도록 무한대기 한다.
Socket socket = serverScoket.accept();
ObjectInputStream InStream =
new ObjectInputStream(socket.getInputStream());
// 프로그램과 socket 사이의 통로로 내용을 꺼내온다.
String msg = InStream.readUTF();
// 유니코드로 되어있는 문자열을 읽어오도록 한다.
System.out.println("클라이언트가 전송해 온 메세지 : " + msg);
if(msg.equals("exit")) {break;}
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeUTF("홍길동이 전달한 메세지 : " + msg + "잘 받았어요.");
// writeUTF 그냥 두면 buffer 가 찰 때까지 기다린다.
// 실시간 처리
outStream.flush();
// 사용후 close();
outStream.close();
InStream.close();
socket.close();
}
serverScoket.close();
} catch (IOException e) {
e.printStackTrace();
}
ServerSocket serverScoket = new ServerSocket(9001); // 서버로 동작되게 구성하도록 해줍니다. ( ) 안의 숫자는 포트번호로 9001 번으로 셋팅 , 1000~9999 부터는 임의로 번호를 부여해서 사용할 수 있으며, 안정적으로는 5000이후 번호로 셋팅 하는 것이 좋습니다. 9001 포트번호로 이 프로그램을 사용합니다. (정보 전달) - 운영체지는 포트 번호로 프로그램들을 관리한다.
주의) DB 설치할 때 default 값으로 8080으로 되어있으므로 8080을 사용하지 않아야 합니다.
Scoket socket = serverScoket.accept();
참조변수를 선언해서 전달 받은 주소값을 저장한다. 메모리에는 클라이언트의 정보가 모두 들어있습니다. 서버는 대기하고 클라이언트가 요청할 때까지 대기하다가 요청해오면 그 정보를 메모리에 담아서(안에서 new) Scoket 단위로 저장하여 그 주소값을 return 해서 socket에 저장해줍니다.
outStream.writeUTF("홍길도이 전달한 메세지 : " + msg + "잘 받았어요."); writeUTF 그냥 두면 buffer 가 찰 때까지 기다립니다 하지만 채팅 프로그램에서는 퍼포먼스보다 속도가 중요하기에
실시간으로 보내는 flush();를 사용합니다.단, flush();는 퍼포먼스가 많이 떨어지므로 상황에 맞춰서 사용하는 것이 좋습니다.
클라이언트)
public class SocketClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 9001);
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeUTF("홍길동");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
String msg = inStream.readUTF();
System.out.println("서버로부터 전송되어진 메세지 :");
System.out.println(msg);
inStream.close();
outStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
위의 서버와 연동을 하기 위해서 Socket socket = new Socket("localhost", 9001); "localhost" = 내 pc
저의 pc의 서버와 연결하기 위해서 "localhost"를 사용하였고, 다른 pc와 연결해주고 싶을 때는 "localhost" 자리에 pc IP를 넣어주면 됩니다.
선언한 배열을 read(); 에 넣어서 아무것도 넣지 않았을 때 1byte 씩 읽어오지만 buf을 넣어주면 읽어온 데이터를 1024개까지 쌓아서 buf가 꽉차면 읽어온 data의 개수 만큼 return 해줍니다. 이것을 통해 시간을 많이 단축할 수 있습니다
2) DataStream
: 자바의 기본 자료형 데이터를 바이트 스트림으로 입출력하는 기능을 제공합니다.
- DataInputStream과 DataOutputStream 은 FilterInputStream과 FilterOutputStream을 상속하고 있어, 객체 생성시에 InputStream과 OutputStream을 매개변수 인자를 가집니다.
이 클래스와 입출력 장치를 대상으로 하는 입출력 클래스를 같이 이용하면 자바의 기본 자료형 데이터를 파일 등 입출력 장치로 직접 입출력할 수 있습니다.
public class DataFilterStream {
public static void main(String[] args) {
try {
OutputStream out = new FileOutputStream("data.bin");
// file 이 없으면 입력한 이름으로 file 을 만들어준다.
// file이 이미 존재하면 원래 있던 내용을 다 지우고 내용을 입력한다.
DataOutputStream filterOut = new DataOutputStream(out);
// OutputStream을 연결.
// out.write(365); // 별도의 자료형 입력이 없을 때는 int로 저장된다.
// out.close();
filterOut.writeInt(365); // : 정수값 저장
filterOut.writeDouble(3.14); // : 실수값 저장
filterOut.close(); // 필터를 close 하면 연결된 Stream 도 close 된다.
InputStream in = new FileInputStream("data.bin");
DataInputStream filterIn = new DataInputStream(in);
// InputStream 연결.
System.out.println(filterIn.readInt());
System.out.println(filterIn.readDouble());
filterIn.close();
} catch (IOException e) {
e.printStackTrace();
}
FileInputStream / FileOutputStream 은 byte[] 단위의 데이터만 입/출력을 할 수 있었습니다. 하지만 DataStream Filter를 적용함으로써, 자바 기본 자료형(char, int, long, ...) 으로 데이터를 입력하고 출력할 수 있게 되었습니다.
FileStream은 1 Byte 단위로 입/출력이 이루어지면 기계적인 동작이 많아지므로 효율이 떨어지게 됩니다. 또한, 사용자가 일일이 버퍼와 크기를 지정하여 입출력을 하게 되는 것도 정적이고 불편합니다. 이러한 문제점을 해결하기 위해서 사용하는 것이 BufferedStream입니다.
3) BufferedStream
public class ByteBufferedFileCopy {
public static void main(String[] args) {
// 퍼포먼스의 문제를 최소화 하여 결과값을 가질 수 있도록 한다.
try {
int copyByte = 0;
InputStream in= new FileInputStream("Fonts.zip");
BufferedInputStream bufferIn = new BufferedInputStream(in);
OutputStream out = new FileOutputStream("Copy1.zip");
BufferedOutputStream bufferOut = new BufferedOutputStream(out);
while(true) {
int bData = bufferIn.read();
if(bData == -1) {
break;
}
bufferOut.write(bData);
copyByte+= bData;
}
bufferIn.close();
bufferOut.close();
System.out.println("복사된 바이트 크기 : " + copyByte );
} catch (IOException e) {
e.printStackTrace();
}
사용자가 BufferedInputStream과 BufferedOutputStream을 이용하여 프로그램을 작성하면 1 바이트씩 읽고 쓰는 모든 작업이 하드 디스크 파일이 아닌 내부적인 버퍼를 대상으로 발생하며, 필요에 따라 버퍼와 하드 디스크 파일간에 입출력이 간헐적으로 발생하므로 전체적인 입출력 성능이 동적으로 향상되도록 해줍니다.
4) FileWriter : 운영체제의 포멧으로 데이터를 저장하도록 합니다.
public class FileWriterStream {
public static void main(String[] args) {
char ch1 = 'A';
char ch2 = 'B';
// 윈도우 저장할 때, 영어는 1byte 로 처리,
// 한국어는 2byte 다른 나라는 3byte로 처리되는 곳도 있다.
try {
Writer out = new FileWriter("hyper.txt");
out.write(ch1);
out.write(ch2);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
FileWriter 운영체제가 관리하는 문자열 포멧으로 운영체제가 파일에 담게 처리합니다. 운영체제가 저장하고 있기 때문에 깨지지 않고 저장됩니다. char 자료형 2개 이므로 2byte 로 저장합니다. 메모장으로 읽을 때는 깨지지 않고 잘 보이지만 java에서 그대로 읽어내면 깨져서 보입니다.
java의 형식으로 저장되는 것이 아닙니다. 만약 java의 형태였다면 4byte로 저장되고, 깨져보였을 것입니다.
쉽게 설명해서 하나의 프로세스 안에서 2가지 이상의 일을 수행하는 것과 같은 것과 같은 효과를 주는 것을 의미합니다.
Java Thread
- 자바 가상 기계(JVM)에 의해 스케쥴되는실행 단위의 코드 블럭입니다.
- 스레드의 생명 주기는 JVM에 의해 관리됩니다.
JVM과 멀티스레드의 관계
- Thread 는 운영체제와 관련있는 기능으로 java 이외에 thread를 구현해주는 프로그램이 없습니다.
- 하나의 JVM은 하나의 자바 응용프로그램만 실행합니다.
- 자바 응용프로그램이 시작될 때 JVM이 함께 실행합니다.
- 자바 응용프로그램이 종료하면 JVM도 함께 종료합니다.
- 하나의 응용프로그램은 하나 이상의 스레드로 구성 가성합니다.
-- Thread 클래스 작성(Thread 클래스 상속. 새 클래스 작성)
class TimerThread extends Thread {
-- Thread 코드 작성
run() 메소드 오버라이딩
@Override
public void run() { // run() 오버라이딩
-- Thread 객체 생성
TimerThread th = new TimerThread();
-- Thread 시작(start() 메소드 호출)
th.start();
run() 메소드오버라이딩
- run() 메소드를Thread 코드라고 부릅니다.
- run() 메소드에서Thread 실행 시작합니다.
- run(); 을 통해서 main 2개인것과 같은 효과를 가지게 해주며, main과 동시 수행합니다.
start() 메소드 호출
- Thread로 작동 시작하고 JVM에 의해 스케줄되기 시작합니다.
- start(); 호출하러 가자마자 바로 복귀를 시켜서 다음 명령어를 수행하고, 운영체제에게 명령수행하기 전 준비하도록 요청합니다.
- Thread 수행하기 전 메모리들을 운영체제에게 요청해서 thead의 메모리를 할당받아 스레드의 run을 호출하게끔 구현이 되어져있습니다. main은 main 대로 수행하고, start로 바로 복귀 시켜 두개가 함께 수행하도록 합니다.
class SumThead extends Thread{
String threadName;
int start, end;
SumThead(String threadName, int start, int end){
this.threadName = threadName;
this.start = start;
this.end = end;
}
@Override
public void run() {
int sum = 0;
for(int i = start ; i <= end; i++) {
sum+=i;
System.out.println(threadName);
}
System.out.printf("%s => %d ~ %d 까지의 합 : %d \n", threadName, start, end, sum);
}
}
public class ThreadUnderstand {
public static void main(String[] args) {
SumThead thread1 = new SumThead ("쓰레드1", 1, 10); // main 안에서 new 하는 컨셉은 변함없다.
// 1~10까지의 합
SumThead thread2 = new SumThead ("쓰레드2", 11, 20);
// 11~20까지의 합
int sum = 0;
thread1.start();
thread2.start();
for(int i = 1 ; i <= 50 ; i++) {
sum += i;
System.out.println("main");
}
System.out.println("main() 메서드 실행 => 1~50까지의 합 : " + sum);
System.out.println("프로그램 종료.");
}
}
결과값 : main, thread1, thread2 함께 수행하는 효과를 주지만 출력되는 순서는 일정하게 나타나지 않습니다. 이것은
현재 운영되는 프로그램에 따라서 다른 결과를 나타나게 됩니다. 결과의 순서는 운영체제의 알고리즘만 알 수 있습니다. 결과값을 통해서 3개의 작업이 골고루 수행되는 효과를 보여줄 수 있습니다.
자료형을 선언했을 때, 그 자료형을 thread 로 상속받고 싶을 때 다른 class 를 상속받았을 때 사용법)
=Runnable 사용
class Sum {
int num;
Sum(){num=0;}
public void addNum(int num) {this. num += num;}
public int getNum() {return num;}
}
class AddThread extends Sum implements Runnable{
AddThread(int start , int end){
this.start = start;
this.end = end;
}
int start, end;
@Override
public void run() {
for(int i = start; i <= end ; i++){
addNum(i);
}System.out.printf("%d ~ %d 의 총 합은 : %d \n", start, end, num);
}
}
public class RunnalbleThread {
public static void main(String[] args) {
AddThread at1 = new AddThread(0,50); // thread 기능이 탑재 되어 있지 않다.
// runnable을 상속받는 AddThread을 사용하여 run을 사용한다.
AddThread at2 = new AddThread(51,100);
Thread thread1 = new Thread(at1); // run 을 수행시킬 코드를 만들어줘야 한다.
Thread thread2 = new Thread(at2);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// join() method는 try~each 문으로 감싸줘야하며,
// join() method는 run(); method가 수행될때까지 기다린다.
System.out.println("0~100까지의 합 : " + (at1.getNum()+at2.getNum()));
※ thread기능을 탑재 시키기 위한 유일한 방법
- Sum 을 상속 받아서 thread를 상속하지 못할 때 implements Runnable 인터페이스 사용합니다. - Runnable 안에 run(); 추상method 하나 들어가있습니다. 또한 thread와는 상관이 없으며 단지 run method 만
그 이유는 thread1, thread2 , main 함께 시작하기 때문에 컴퓨터의 상태에 따라서 언제 실행될지 알 수 없습니다. thread1, thread2이 구동되기 전에 main은 벌써
System.out.println("0~100까지의 합 : " + (at1.getNum()+at2.getNum())); 실행할수도 때문에 결과가 항상 다르게 출력합니다. thread1, thread2가 구동이 되고 나서 System.out.println("0~100까지의 합 : " + (at1.getNum()+at2.getNum())); 실행 되어야지 올바른 결과값이 나타납니다.
위의 코드를 해결 우선순위를 넣어서 코드를 만들어보겠습니다.
우선순위)
class MessageSendingThread extends Thread{
String message;
// 우선순위 부여(절대적인 것이 아닌 가중치정도록 생각하자.)
MessageSendingThread(String message, int prio){
this.message = message;
setPriority(prio); // 값을 다시 setting 할 수 있게 만들어준다.
}
@Override
public void run() { // main 2개 효과
for(int i = 0; i < 1000; i++) {
System.out.printf("%s(%d) \n" , message, getPriority());
}
}
}
public class PriorityTest {
public static void main(String[] args) {
// 우선순위
MessageSendingThread tr1 = new MessageSendingThread ("First", Thread.MAX_PRIORITY);
// ("First", 10);
MessageSendingThread tr2 = new MessageSendingThread ("Second", Thread.NORM_PRIORITY);
// ("Second", 5);
MessageSendingThread tr3 = new MessageSendingThread ("Third", Thread.MIN_PRIORITY);
// ("Third", 1);
tr1.start();
tr2.start();
tr3.start();
}
}
- setPriority(prio) 을 생성자에 초기화 시켜줍으로 run(); method안에 숫자를 넣어 우선순위 대로 출력하도록 만들어줍니다.
- 우선순위가 부여되었다고 해서 Thread의 특성상 결과에 있어서 정확하게 순서에 맞춰서 출력되지는 않습니다. 각각 1000가지 씩 수행되어야 하는데, 우선순위가 낮다고 해서 무조건 늦게 나오는 것이 아니라는 점을 염두해두면 좋겠습니다.
동기화)
: 하나의 thread 의 동작이 완전히 진행된 이후 순서를 넘기는 것을 의미합니다. (synchronized)
class Sum{
int num;
Sum(){num=0;}
public synchronized void addNum(int num) {this. num += num;}
public int getNum() {return num;}
}
class AdderThread extends Thread{
Sum sumInst ;
int start, end;
AdderThread(Sum sum, int s , int e){
this.sumInst = sum;
this.start = s;
this.end = e;
}
@Override
public void run() {
for(int i =start ; i<= end ; i++) {
sumInst.addNum(i);
}
}
}
public class ThreadHeapMultiAccess {
public static void main(String[] args) {
Sum sum = new Sum();
AdderThread at1 = new AdderThread(sum , 1 , 5000); // sum의 주소값이 저장
AdderThread at2 = new AdderThread(sum , 5001 , 10000); // sum의 주소값이 저장
at1.start();
at2.start();
try {
at1.join();
at2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
최종적으로 업데이트가 균일하게 진행되면 문제가 없지만 그렇지 못하게 때문에 나오는 문제점입니다.
위의 코드를 보면 at1 과 at2 가 같은 data를 가지고 수행하는데 Thread의 특성으로 인해서 올바른 값이 출력되지 않습니다. 이러한 문제점을 해결하기 위해서 동기화를 하나의 값이 정확히 수행되고 나서 다음 값이 진행되록 만들수 있습니다.
- 위와 같이 둘 이상의 thread 가 heap 영역하나의 data 를 바라보도록 접근해서 처리하게 되면 문제가 생기므로 동시화를 고려해야 합니다. 왜냐하면 순서가 왔다갔다 하면서 수행을 하게 되면 결과 값이 날아가버리기 때문입니다.
- synchronized 를 활용하여 동작이 완료되서 복귀 될 때까지 순서가 넘어가지 않도록 할 수 있습니다. 단, 제어권을 넘기지 않고 다 수행한 이후 제어권을 넘기도록 하면 출력 속도가 느려지기 때문에 꼭 필요한 경우에 사용하는 것을 권장합니다.