
<예제1>
package achat.step1;
import java.util.List;
import java.util.Vector;
class Customer extends Thread{
}
public class List2 {
List<Customer> list = new Vector<>();
void methodA(){// static이 있으면 this 못 쓰니까 일단 메소드를 빼자
this.add(this);
}
public static void main(String[] args) {
List2 list2 = new List2();
list2.methodA();
}
}
Q. this.add(this);의 에러를 해결하려면 어떻게 해야할까?
A1. List list = new Vector<>();
-> List list = new Vector<>();
<예제>
package achat.step1;
import java.util.ArrayList;
import java.util.List;
class Kiwi{
String name = null; // 골드키위, 초록키위
}
class Tomato {
}
public class List3 {
List<String> bread = new ArrayList<>();
List<Kiwi> kiwis = new ArrayList<>();
List<Tomato> tomatos = new ArrayList<>();
void methodA() {
bread.add("소보로");
Kiwi kiwi = new Kiwi();
kiwi.name = "골드키위";
kiwis.add(kiwi); // bread.add(kiwi); 이러면 안 된다
Kiwi kiwi2 = new Kiwi();
kiwi2.name = "초록키위";
kiwis.add(kiwi2);
Tomato tomato = new Tomato();
tomatos.add(tomato);
}
// 메인메소드는 개발자에게 제어권이 없다
public static void main(String[] args) {
List3 list3 = new List3();
list3.methodA(); // 여기를 막으면 0 출력
System.out.println(list3.bread.size()); //1
System.out.println(list3.kiwis.size()); //1 -> 2(골드키위, 초록키위)
System.out.println(list3.tomatos.size()); //1
}
}
A코스(서버)
B코스(클라이언트)
ChatServer cs = new ChatServer();
cs.initDisplay(); // 화면열림
cs.start();
cs.run(); // new Vector<>();
server = new ServerSocket(3002);
waiting 기다림..
기다림이 끝났다
socket = server.accept(); // while문에 처리함 - 무한루프(break)
// while문 안에서 인스턴스화 할 것
ChatServerThread cst = new ChatServerThread(this); // 장애 원인이 된다 - 안정화
// insert here -> 생성자 호출 - 현재 메모리에 로딩중인 원본 - charServer가 넘어감
this.cs = cs;
this.client = socket;
oos, ois 생성한다(인스턴스화) - 그런데 소켓에 의존한다 - 소켓 없이는 인스턴스화 불가함
cst.start()
ChatClient
new Socket("172.16.2.3",3002) -> server가 클라이언트 존재인지함
모든 메세지는 서버를 경유하여 받는다 - web개발 -> 외부(소셜로그인, 검색 API, 오픈소스) 서비스를 이용하기
actionPerformed(말하기-메세지입력-엔터친다) - oos.writeObject();
ChatServer - 내 안에는 oos, ois가 없다
ChatServerThread - 생성자(while문:ChatServerThread(this)로 cs를 받음
인스턴스화를 하여 받으면 복제본이 생성됨 - 원본이 아니다
-client 소켓생성(ChatServer생성됨) - oos, ois 생성함 - 듣고 말할 수 있는 곳이다
run() 재정의 -> protocol(100|200|500) 찾아내고 - StringTokenizer
while() {
switch(int-100|200|500){
case 100:
case 200:
case 500:{
cs.globalList.remove(this);
}
}
}
<ChatServer>
package achat.step1;
import java.awt.Color;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
//왜 스레드를 굳이 상속받는 건가요? - 화면처리하는 메인메소드와 통신채널 설정하는 코드 분리
//혜택 - 화면처리와 네트워크 처리 분리한다 - 안전한 서비스 제공하기 위해서
public class ChatServer extends Thread {
//List의 구현체 클래스는 ArrayList(스레드불안전-조치가 없다, 배려가 없다.추가로직이 포함안됨), Vector(동시접속자를 배려하여 0.1초라도 먼저 들어오면 관리를 해준다.읽기 쓰기 속도가 느리다.)
//챗서버에 접속해온 사용자에 대한 정보를 담는 벡터 선언임
//다수의 클라이언트 요청을 처리하는 서버를 개발할때 사용함
List<ChatServerThread> globalList = null;
ServerSocket server = null;//run 메소드 사용함 - 39번라인에서 인스턴스화함- 가게문을 열었음
//서버는 들은내용을 말하는 것이다.
//실제로 듣고 말하기는 ChatServerThread에서 처리한다.(내가 그렇게 설계했다.)
//run메소드에서 서버소켓에 접속해온 클라이언트측의 소켓정보를 받게 되니까 - 메소드 내부에서 사용하려면 전변으로 하자
Socket socket = null;
JFrame jf = new JFrame();
JTextArea jta_log = new JTextArea(10,60);
JScrollPane jsp_log = new JScrollPane(jta_log
, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
public ChatServer() {}
@Override
public void run() {//통신에 대한 처리 - 지연, 끊김, 노이즈, 대기
//서버에 접속해온 클라이언트 스레드 정보를 관리할 벡터 생성하기
globalList = new Vector<>();//ChatServerThread담기 -동시에 3명이 들어온다면
boolean isStop = false;
try {
//만일 3002번 포트를 오라클 서버에서 점유하고 있다면 예외가 발생할 것이다.
server = new ServerSocket(3002);
jta_log.append("Server Ready.........\n");//대기 중 - 손이 오기를 기다린다. - 다음코드가 실행기회를 못갖는다. - 흐름을 방해함 - 장애처리
while(!isStop) {
//서버소켓에 접속해온 사용자의 소켓정보를 담는다. - 멍때림
//Socket s = new Socket("172.16.2.3", 3002);- ChatClient - 입장하기
//socket은 ChatServer에서 선언한 전변인데 어떻게 클라이언트 소켓을 주소번지를 쥐고 있는걸까?
socket = server.accept();
jta_log.append("client info:"+socket+"\n");//여기 소켓은 클라이언트소켓을 쥐고 있다.
//아래에서 this는 ChatServer가리킨다
System.out.println(this);//왜 파라미터로 넘기지? - socket필요하니까-> oos, ois
//다형성
Thread tst = new ChatServerThread(this);//this- ChatServer -why?: 협업 - 서버에 접속한 소켓정보를 ChatServerThread에서 사용해야 하니까 this로 넘긴다.
//생성자 호출이 끝나고 나면 즉시 start()호출된다.-> run()호출한다.
//100|kiwi
tst.start();//ChatServerThread의 run호출된다.
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void initDisplay() {
jta_log.setLineWrap(true);
jf.setBackground(Color.orange);
jf.add("Center", jsp_log);
jf.setTitle("서버측 로그 출력화면 제공...");
jf.setSize(800, 600);
jf.setVisible(true);
}
//메인메소드도 스레드라 했잖아요?
//entry point - 시작점 - exe 생성가능함 - 경합벌어진다. - 끼어든다
public static void main(String[] args) {
ChatServer cs = new ChatServer();
System.out.println(cs);
//두 메소드가 호출되었을 때 지연이 발생되는 메소드는 start()이다.
//같은 메소드라도 처리되는 시간이 다르다- 스케쥴링이 필요함
//this.sleep(10000), this.wait(); this.notify();
//대기상태에 있는 손님이 있다.
cs.start();//여기서 대기탄다 - this.wait(); -> this.notify();잠자는 스레드를 깨운다
cs.initDisplay();
}
}
<ChatServerThread>
package achat.step1;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;
import java.util.StringTokenizer;
public class ChatServerThread extends Thread {
ChatServer cs = null;
Socket client = null;
ObjectOutputStream oos = null;//말할때 사용
ObjectInputStream ois = null;//들을 때 사용
//
String chatName = null;//현재 서버에 입장한 클라이언트 스레드의 닉네임 저장
//생성자의 파라미터
//public ChatServerThread(List<ChatServerThread> globalList, JTextArea jta_log) {
public ChatServerThread(ChatServer cs) {
cs.jta_log.append("ChatServerThread호출 "+"\n");
//생성자 안에서 globalList에 현재 입장한 키위를 추가한다.
this.cs = cs;
this.client = cs.socket;
cs.jta_log.append("client "+client+"\n");
try {
oos = new ObjectOutputStream(client.getOutputStream());
ois = new ObjectInputStream(client.getInputStream());
//100|kiwi -입장하기 처리를 생성자에서 벌써 하였다.
//생성자안이면 입장했다는걸 어떻게 증명한다는 건가요?
String msg = (String)ois.readObject();//누가 보낸걸 듣나요? ChatClient
cs.jta_log.append(msg+"\n");//로그를 남긴다(출력) - 추적하다
StringTokenizer st = new StringTokenizer(msg,"|");
st.nextToken();//100
chatName = st.nextToken();//kiwi
cs.jta_log.append(chatName+"님이 입장하였습니다.\n");
//내가 입장하기 전에 이미 입장해 있는 친구들 에게도 메시지를 전달해야 한다.
//kiwi가 입장했을땐 cs.globalList.size()가 0이라서 안탄다
for(ChatServerThread cst: cs.globalList) {//cs.globalList.size()=0
System.out.println(cst.chatName);
System.out.println(this.chatName);
this.send(100+"|"+cst.chatName);//나 입장했어요
}
//현재 서버에 입장한 클라이언트 스레드 추가하기
cs.globalList.add(this);//3명이 채워짐 - ChatServerThread -> globalList(ChatServer) -> this(kiwi or tomato or apple)
this.broadCasting(msg);
// apple이 입장했을때 이미 kiwi, tomato입장해 있다
//cs.globalList.add(this);//this는 kiwi, tomato, apple, 아님 이걸 어떻게 서버가 구분할 수 있나요?
//cs.globalList.add(this);//this는 ChatServerThread이다. - List<ChatServerThread> globalList
//cs.globalList.size();//3명
//cs.jta_log.append("");
//System.out.println(this.cs.globalList.size());//0
} catch (Exception e) {
e.printStackTrace();
}
}
//run메소드도 콜백메소드 입니다. - 개발자가 직접 호출하지 않아요
//시스템에서 대신 호출해 줌
@Override //annotation
public void run() {
//System.out.println("ChatServerThread 의 run호출");
cs.jta_log.append("ChatServerThread 의 run호출"+"\n");
//insert here - 듣고 말하기(방송)
//말하기 처리는 메소드로 분리(for문-3사람)시킨다.
boolean isStop = false;
String msg = null;
try {
start:
while(!isStop) {
msg = (String)ois.readObject();
cs.jta_log.append(msg+"\n");
//로그가 append될때마다 스크롤바가 현재 문자열의 위치를 자동으로 인식해서 이동처리
cs.jta_log.setCaretPosition(cs.jta_log.getDocument().getLength());
//언제 선언부와 생성부를 나누어서 작성하나요?
//msg가 null일 수도 있다.- 의심
StringTokenizer st = null;
int protocol = 0;
if(msg !=null) {
st = new StringTokenizer(msg, "|");
protocol = Integer.parseInt(st.nextToken());
}
switch(protocol) {
case 200:{
cs.jta_log.append("ChatServerThread : 200번 청취완료");
String nickName = st.nextToken();//kiwi
String message = st.nextToken();//오늘 스터디할까?
broadCasting(200+"|"+nickName+"|"+message);//oos.writeObject
}break;
case 500:{
}break start;
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
//서버가 클라이언트에게 들은 것을 방송(여러사람-스레드를 붙임)하는 것이다.
//왜 파라미터 자리인가? - 매번 입력되는 메세지가 달라진다. - 지역변수
//oos.writeObject(message==>"200|kiwi|apple|오늘 스터디할까?");
public void broadCasting(String message) {
//for(int i = 0;i<cs.globalList.size();i++) {//cs.globalList.size()=3
for(ChatServerThread cst:cs.globalList) {
cst.send(message);
//클라이언트 들에게 서버가 들은 메시지를 보낸다.- OutputStream
//ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
//oos.writeObject(message);
}
}
//DM을 처리하는데 필요한 메소드를 고려하여 메소드를 하나 더 추가해 본다.
public void send(String message) {
try {
oos.writeObject(message);
} catch (Exception e) {
e.printStackTrace();//stack에 쌓여 있는 로그 히스토리를 출력함
}
}
}
<ChatClient>
package achat.step1;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
import achat.step2.TalkClientThread;
//사용자가 하고싶은 말을 전달하는 클래스 설계이다.
public class ChatClient extends JFrame implements ActionListener{
////////////////통신과 관련한 전역변수 추가 시작//////////////
Socket socket = null;
ObjectOutputStream oos = null;//말 하고 싶을 때
ObjectInputStream ois = null;//듣기 할 때
String nickName= null;//닉네임 등록
////////////////통신과 관련한 전역변수 추가 끝 //////////////
JPanel jp_second = new JPanel();
JPanel jp_second_south = new JPanel();
JButton jbtn_one = new JButton("1:1");
JButton jbtn_change = new JButton("대화명변경");
JButton jbtn_font = new JButton("글자색");
JButton jbtn_exit = new JButton("나가기");
String cols[] = {"대화명"};
String data[][] = new String[0][1];
DefaultTableModel dtm = new DefaultTableModel(data,cols);
JTable jtb = new JTable(dtm);
JScrollPane jsp = new JScrollPane(jtb);
JPanel jp_first = new JPanel();
JPanel jp_first_south = new JPanel();
JTextField jtf_msg = new JTextField(20);//south속지 center
JButton jbtn_send = new JButton("전송");//south속지 east
JTextArea jta_display = null;
JScrollPane jsp_display = null;
//소켓 관련 초기화
public void init() {
try {
//서버측의 ip주소 작성하기
socket = new Socket("172.16.2.41",3002);//ServerSocket -> socket -> C.S.T client
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
//initDisplay에서 닉네임이 결정된 후 init메소드가 호출되므로
//서버에게 내가 입장한 사실을 알린다.(말하기)
oos.writeObject(100+"|"+nickName);//말하기 시전 - 서버한테 - 듣고 말하기
//서버에 말을 한 후 들을 준비를 한다.
ChatClientThread tct = new ChatClientThread(this);//아직 일이없다
tct.start();
} catch (Exception e) {
//예외가 발생했을 때 직접적인 원인되는 클래스명 출력하기
System.out.println(e.toString());
}
}
public void initDisplay() {//키위(양파|토마토)님의 창- 전변으로 하나?아님 지변으로 할까?
jtf_msg.addActionListener(this);
//사용자의 닉네임 받기
nickName = JOptionPane.showInputDialog("닉네임을 입력하세요.");
this.setLayout(new GridLayout(1,2));
jp_second.setLayout(new BorderLayout());
jp_second.add("Center",jsp);
jp_second_south.setLayout(new GridLayout(2,2));
jp_second_south.add(jbtn_one);
jp_second_south.add(jbtn_change);
jp_second_south.add(jbtn_font);
jp_second_south.add(jbtn_exit);
jp_second.add("South",jp_second_south);
jp_first.setLayout(new BorderLayout());
jp_first_south.setLayout(new BorderLayout());
jp_first_south.add("Center",jtf_msg);
jp_first_south.add("East",jbtn_send);
jta_display = new JTextArea();
jta_display.setLineWrap(true);
jta_display.setOpaque(false);
Font font = new Font("굴림체",Font.BOLD,16);
jta_display.setFont(font);
jsp_display = new JScrollPane(jta_display);
jp_first.add("Center",jsp_display);
jp_first.add("South",jp_first_south);
this.add(jp_first);
this.add(jp_second);
this.setTitle(nickName);
this.setSize(800, 550);
this.setVisible(true);
}
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
ChatClient cc = new ChatClient();
cc.initDisplay();
cc.init();
}
@Override
public void actionPerformed(ActionEvent e) {
//말하기 구현 - > oos.writeObject("200|kiwi|tomato|오늘 스터디할까?");//프로토콜설계
Object obj = e.getSource();
String msg = jtf_msg.getText();
//메시지 입력 후에 엔터 친거야?
if(jtf_msg == obj) {
try {
oos.writeObject(200+"|"+nickName+"|"+msg);
//메시지를 서버로 전송하고 나면 JTextField적힌 문자열은 지운다.
jtf_msg.setText("");
} catch (Exception e2) {
// TODO: handle exception
}
}
}
}
<ChatClientThread>
package achat.step1;
import java.util.StringTokenizer;
import java.util.Vector;
//서버가 한 말을 듣는 클래스 설계임
public class ChatClientThread extends Thread {
ChatClient cc = null;
public ChatClientThread(ChatClient cc) {
//아래 초기화를 생략하면 ChatClientThread클래스안에서 ChatClient
//의 변수나 메소드를 호출할 때 NullPointerException발동
this.cc = cc;
}
//서버에서 말한 내용을 들어봅시다.
//말하기는 ChatClient 의 actionPerformed에서 JTextField에 엔터 쳤을 때 처리합니다.
public void run() {
boolean isStop = false;
start:
while(!isStop) {
try {
String msg = "";
msg = (String)cc.ois.readObject();
StringTokenizer st = null;
int protocol = 0;//100|200|500(나가기)
if(msg !=null) {
st = new StringTokenizer(msg,"|");
protocol = Integer.parseInt(st.nextToken());//100
}
switch(protocol) {
case 100: {// 100|kiwi - 입장하기 처리
String nickName = st.nextToken();//kiwi
cc.jta_display.append(nickName+"님이 입장하였습니다.\n");
Vector<String> v = new Vector<>();
v.add(nickName);
cc.dtm.addRow(v);
}break;
case 200:{//다자간 대화 처리 - 청취하기 - 서버에서 보낸 메세지를 듣는곳
String nickName = st.nextToken();
String message = st.nextToken();
cc.jta_display.append("["+nickName+"] "+message+"\n");
cc.jta_display.setCaretPosition(cc.jta_display.getDocument().getLength());
}break;
case 500:{//나가기 처리
}break start;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
<예제-LIst>
package achat.step1;
import java.util.ArrayList;
import java.util.List;
class Kiwi{
String name = null; // 골드키위, 초록키위
}
class Tomato {
}
public class List3 {
List<String> bread = new ArrayList<>();
List<Kiwi> kiwis = new ArrayList<>();
List<Tomato> tomatos = new ArrayList<>();
void methodA() {
bread.add("소보로");
Kiwi kiwi = new Kiwi();
kiwi.name = "골드키위";
kiwis.add(kiwi); // bread.add(kiwi); 이러면 안 된다
Kiwi kiwi2 = new Kiwi();
kiwi2.name = "초록키위";
kiwis.add(kiwi2);
Tomato tomato = new Tomato();
tomatos.add(tomato);
}
void methodB() {
Kiwi kiwi = null;
int i = 1;
while(true) {
kiwi = new Kiwi();
System.out.println(kiwi);
i++;
if(i==5) {
break;
}
}
}
// 메인메소드는 개발자에게 제어권이 없다
public static void main(String[] args) {
List3 list3 = new List3();
list3.methodB();
}
}
<Map 예제>
package jdbc.oracle;
import java.util.HashMap;
import java.util.Map;
public class Map1 {
Map<String,Object> map = null; // map은 한 개 로우만 담는다 - myBatis(SQL 오픈소스-생산성이 좋음. 코딩양이 30%이상 줄어들어서) 쓰게 되면 map을 자주 쓰게 됨
// 컬럼 하나만도 담을 수 있다.
// 전변 map에 대해서 초기화 해 보시오
void methodA() {
// insert here
map = new HashMap<>();
map.put("b_title", "자바의 정석");
map.put("b_author", null);
map.put("b_price", 30000);
}
void methodAPrint() {
if(map!=null) {
System.out.println(map.get("b_title"));
System.out.println(map.get("b_author"));
System.out.println(map.get("b_price"));
} else {
System.out.println("조회할 수 없습니다.");
}
}
void methodB(Map<String,Object> pmap) {
}
Map<String,Object> methodC(Map<String,Object> pmap) {
Map<String,Object> rmap = null;
return rmap;
}
public static void main(String[] args) {
Map1 m1 = new Map1();
m1.methodA();
m1.methodAPrint();
}
}
SELECT seq_book1_no.nextval FROM dual; -- 여기는 ctrl+enter 계속 카운트 됨
SELECT max(empno)+1 FROM emp;
SELECT to_char(sysdate,'YYYY-MM-DD') FROM dual;
SELECT 1 FROM dual;
SELECT 1, 2, 3 FROM dual;
SELECT 1+2+3, 2*3, 5/2 FROM dual;