mdn mvc - 표준
<예제1>
package com.step4;
public class Array1 {
public static void main(String[] args) {
int com[] = new int[3];
for(int i =0;i<3;i++) {
System.out.println("com["+i+"] = "+com[i]);
}
int my[] = {1,2,3};
// 가진 전부를 처리할 땐 개선된 for문 사용함
for(int i:my) {
System.out.print(i);
}
System.out.println();
int other[] = new int[] {1,2,3};
for(int i:other) {
System.out.println(i);
}
}
}
<예제2>
package com.step4;
// 메소드오버로딩은 같은 이름의 메소드나 생성자를 중복해서 선언할 수 있다
// 단, 이름은 같지만 반드시 파라미터의 개수나 타입은 달라야만 한다
class F {
F() {
}
F(int i) {
}
F(int i, int j) {
System.out.println(i+","+j); // 출력되었다는 건 내가 호출되었다는 것
}
F(boolean isOk) {
System.out.println(isOk);
}
F(String s, int i) {
}
}
public class Fmain {
public static void main(String[] args) {
F f = new F(1,2);
System.out.println(f);
f = new F(true); // f 앞에 F 안 쓰는 것에 대해 확실한 이해 필요
// 객체배열, 자료구조, Web or Hybrid - JSON 얘기할 때도 사용함
System.out.println(f); // 다른 주소번지를 가질 것이다 - 예측하기
}
}
<예제3-1>
package com.step4;
class GLogic {
int i = 1;
}
public class GView {
// 빨간 줄 뜨는 이유: 디폴트 생성자만 생략 가능함
GLogic gi = new GLogic(this); // 여기서 this는 현재 스레드 관리하는 클래스이고, 원본이라 보면 된다(나 자신) 현재 메모리에 로딩돼 있음
public static void main(String[] args) {
}
}
<예제3-2>
package com.step4;
class GLogic {
int i = 1;
GLogic(GView gv) { // 이때까지는 gv가 null
System.out.println(gv);
}
}
public class GView {
GView(){
GLogic gi = new GLogic(this);
}
public static void main(String[] args) {
// static 안에서는 this나 super(부모클래스 나타내는 수정자) 사용이 불가하고
// 이 말은 즉 생성자에서는 가능하다
GView gv = new GView();
}
}
<예제 3-3.this 설명+처리순서>
package com.step4;
class GLogic {
int i = 1;
GLogic(GView gv) { // 이때까지는 gv가 null // 5 // 11
System.out.println("GLogic:"+gv); // 6(출력) // 12(출력) // GView gv = new GView(); 여기 gv 주소번지와 같을까?
}
}
public class GView {
GView(){ // 3 // 9(또 경유)
// 2번 경유한다
GLogic gl = new GLogic(this); // 4 // 10
}
public static void main(String[] args) { // 1
// static 안에서는 this나 super(부모클래스 나타내는 수정자) 사용이 불가하고
// 이 말은 즉 생성자에서는 가능하다
// 이때 this는 앞에건가? 아님 뒤에꺼?
GView gv = new GView(); // 2 // 7
gv = new GView(); // 8
System.out.println(gv); // 12(출력)
}
}
<예제3-4.this 설명>
package com.step4;
class GLogic {
int i = 1;
GLogic(GView gv) { // 이때까지는 gv가 null
System.out.println("GLogic:"+gv); // GView gv = new GView(); 여기 gv 주소번지와 같을까?
System.out.println("GLogic i:"+gv.j); // GView gv = new GView(); 여기 gv 주소번지와 같을까?
}
}
public class GView {
int j = 1;
GView(){
GLogic gl = new GLogic(this);
}
public static void main(String[] args) {
// static 안에서는 this나 super(부모클래스 나타내는 수정자) 사용이 불가하고
// 이 말은 즉 생성자에서는 가능하다
// 이때 this는 앞에건가? 아님 뒤에꺼?
GView gv = new GView(); // 여기 주번과
gv = new GView(); // 여기 주번이 다르다
gv.j = 10; // 서로 다른 값을 가질 수 있다 - 이중적이다 - 좋지 않다
// 이걸 절대 방어하려면 static으로 하든가 아님 싱글톤으로 처리하시오 - 메모리 누수
System.out.println(gv);
}
}
<예제4-1>
package com.step4;
class Dept {
// 전변은 초기화를 생략할 수 있다. 왜냐면 생성자가 대신 해주니까
private int deptno;
private String dname;
private String loc;
Dept(){
}
Dept(int deptno, String dname, String loc){
// 현재 메모리에 활성화 되어있는(언제든 사용이 가능한, 준비가 되어있는) 원본의 객체를 참조하고 있다. 누가? this가
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
}
public class DeptMain {
public static void main(String[] args) {
// 인스턴스화를 안 된다 - 캡슐레이션(캡슐화): 그 사물을 정의하는 아주 중요한 고유명사. 이런 건 함부로 손대면 안됨
System.out.println(deptno);
}
}
<예제4-2>
package com.step4;
// lombok
class Dept {
// 전변은 초기화를 생략할 수 있다. 왜냐면 생성자가 대신 해주니까
private int deptno;
private String dname;
private String loc;
Dept(){
}
Dept(int deptno, String dname, String loc){
// 현재 메모리에 활성화 되어있는(언제든 사용이 가능한, 준비가 되어있는) 원본의 객체를 참조하고 있다. 누가? this가
this.deptno = deptno; // 이거 막으면 동기화가 안 되니까 10이 아니라 0이 나옴
this.dname = dname;
this.loc = loc;
}
public int getDeptno() { // getDeptno는 전변 // gettersetter는 전변 바라보고 있다
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
public class DeptMain {
/*
* 왜 10일까? 힌트 한 번 얘기해줘봄
* Dept 클래스에 getter와 setter를 두는 건 결국 전역변수를 사용하기 위함이다
* 부서관리 - ERP
* 업무담당자 - 사원수가 150명이라면, 이 사람들이 시스템 쓰는데 같은 정보 원할까? 아닐걸요
* -> 웹서비스. 웹이 돼야 동기화가 가능하니까.
* 150명 - 인스턴스를 갖는다 - 하나야 - 스레드로 지원함 - 싱글톤 - 영속성을 유지해야 한다면(오라클, Mysql,...), 세션과 쿠키(시간을 지정할 수 있다)
* 그 스레드에 그 인스턴스는 지변 성격을 갖는다
*/
public static void main(String[] args) {
// 퍼블릭이면 막 갖다 쓰면 되나? 아니. 인스턴스화 해야지.
Dept dept = new Dept();
// 생성자를 활용해서도 캡슐화된 전변을 재정의할 수 있다
// 이 때 생성자 내부에서는 반드시 전변과 생성자의 파라미터 값을 동기화 해줄 것- 이걸 빠뜨리면 다른 값을 가질테니까
// dept1 = new Dept(10,"개발1팀","부산"); -> 지금은 이게 아니고
Dept dept1 = new Dept(10,"개발1팀","부산"); // -> 이렇게 보는 게 나음
// 인스턴스화를 안 된다 - 캡슐레이션(캡슐화): 그 사물을 정의하는 아주 중요한 고유명사. 이런 건 함부로 손대면 안됨
System.out.println(dept.getDeptno()); // 0
// dept1=null; // 캔디데이트 상태 -> 넌 앞으로 쓰레기 값이 될거야-> dept1 = new Dept(); 이거 하면 앞에 new Dept(10,"개발1팀","부산"); 이 객체는 쓸 수 없다. 끊어졌기 때문에
// dept1.deptno = 0; // 이건 안 됨
dept1.setDeptno(100); // 이러면 100 출력 // 이걸 막으면 다시 10나옴 // 원시형 타입 변수 한 번에 하나만 담을 수 있다 // 위에가 dept1=null; 이걸로 되면 이건 NullPointerException
dept1.setDname("운영1팀");
dept1.setLoc("제주"); // 이러면 오버라이트인데.
System.out.println(dept1.getDeptno()); // 10 왜요?
System.out.println(dept1.getDeptno()+dept1.getDname()+dept1.getLoc());
System.out.println(dept.getDeptno()+dept.getDname()+dept.getLoc()); // null
}
}
package com.step4;
public class H {
// 사용자정의 클래스와 자바제공클래스 섞어쓰기
// 관심사가 변경되었다 -> 클래스 나누어서 연습할 것
// 클래스 쪼개기 -> 명분(MVC) -> 자바(스프링) -> 이 스프링이 제어역전(역제어), dependency, injection(DI) -> 스프링 핵심들
// git 공부하자 -> 나누어 개발하기 -> 웹개발 -> 클라이언트 사이드개발, 서버사이드 개발 필요하다
public static void main(String[] args) {
String msg = new String("안녕");
System.out.println(msg);
}
}
<예제5>
package com.step4;
class J {
// 선언부
int a;
// 왜 에러인가요? - 파라미터를 갖는 생성자는 JVM이 추측 불가함
Jmain jm = new Jmain(this);
J(){ // 디폴트 생성자는 파라미터가 없으니까 생략해도 JVM이 제공해줌
}
}
public class Jmain {
int b;
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
<예제5-2. 디폴트 생성자가 없어요>
package com.step4;
class J {
// 선언부
int a;
// 왜 에러인가요? - 파라미터를 갖는 생성자는 JVM이 추측 불가함
Jmain jm = new Jmain(this);
J(){ // 디폴트 생성자는 파라미터가 없으니까 생략해도 JVM이 제공해줌
}
}
public class Jmain {
int b;
J j = null;
Jmain(J j){
}
void methodA() {
// 파라미터로 넘어온 원본을 쓰고 싶다. 어떡하지?
// 원본이 뭘 의미하나요? - J가 Heap 메모리 영역에 활성화되었을 때 그 주소번지(캔디데이트 되지 않은, 호출했을 때 바로 사용할 수 있는
System.out.println(j);
}
public static void main(String[] args) {
Jmain jm = new Jmain(); // 디폴트 생성자가 없어요
jm.methodA();
}
}
<예제5-3.this.j=j;를 경유하지 않았으니까>
package com.step4;
class J {
// 선언부
int a;
// 왜 에러인가요? - 파라미터를 갖는 생성자는 JVM이 추측 불가함
Jmain jm = new Jmain(this);
J(){ // 디폴트 생성자는 파라미터가 없으니까 생략해도 JVM이 제공해줌
}
}
public class Jmain {
int b;
J j = null;
Jmain(){
}
Jmain(J j){
this.j=j;
}
void methodA() {
// 파라미터로 넘어온 원본을 쓰고 싶다. 어떡하지?
// 원본이 뭘 의미하나요? - J가 Heap 메모리 영역에 활성화되었을 때 그 주소번지(캔디데이트 되지 않은, 호출했을 때 바로 사용할 수 있는
System.out.println(j);
}
public static void main(String[] args) {
Jmain jm = new Jmain(); // 디폴트 생성자가 없어요
jm.methodA(); // null이 출력될 것이다. 왜냐하면 this.j=j;를 경유하지 않았으니까
}
}
<예제5-4>
package com.step4;
class J {
// 선언부
int a;
// 왜 에러인가요? - 파라미터를 갖는 생성자는 JVM이 추측 불가함
Jmain jm = new Jmain(this);
J(){ // 디폴트 생성자는 파라미터가 없으니까 생략해도 JVM이 제공해줌
}
}
public class Jmain {
int b;
J j = null;
Jmain(){
}
Jmain(J j){
this.j=j;
// b = 10; // 원래 0이었는데 이걸 써줌으로 값이 10으로 변함
this.b = 10; // 이렇게도 가능
}
void methodA() {
// 파라미터로 넘어온 원본을 쓰고 싶다. 어떡하지?
// 원본이 뭘 의미하나요? - J가 Heap 메모리 영역에 활성화되었을 때 그 주소번지(캔디데이트 되지 않은, 호출했을 때 바로 사용할 수 있는
System.out.println(j);
}
public static void main(String[] args) {
J j = new J();
Jmain jm = new Jmain(j);
System.out.println(jm.b); // 원래 0인데 b = 10; 이걸 넣음으로서 10 출력
jm.methodA(); // null이 출력될 것이다. 왜냐하면 this.j=j;를 경유하지 않았으니까
}
}
<예제5-5>
package com.step4;
class J {
// 선언부
int a;
// 왜 에러인가요? - 파라미터를 갖는 생성자는 JVM이 추측 불가함
Jmain jm = new Jmain(this);
J(){ // 디폴트 생성자는 파라미터가 없으니까 생략해도 JVM이 제공해줌
}
}
public class Jmain {
int b;
J j = null;
Jmain(){
// 텅텅 빈 깡통. 아무런 일도 하고 있지 않다. - 아무런(전변 j, b) 초기화도 하지 않았다
}
Jmain(J j){ // 생성자도 파라미터 자리를 갖는다. 이 파라미터 자리에 참조형을 넘길 수 있다
this.j=j;
// b = 10; // 원래 0이었는데 이걸 써줌으로 값이 10으로 변함
this.b = 10; // 이렇게도 가능
}
void methodA() {
// 파라미터로 넘어온 원본을 쓰고 싶다. 어떡하지?
// 원본이 뭘 의미하나요? - J가 Heap 메모리 영역에 활성화되었을 때 그 주소번지(캔디데이트 되지 않은, 호출했을 때 바로 사용할 수 있는
System.out.println(j);
}
public static void main(String[] args) {
J j = new J();
Jmain jm = new Jmain(j);
jm = new Jmain(); // 이걸 넣으면 어떻게 될까? // 생성자가 두 번 호출되는데 자바에서는 메소드오버로딩컨벤션이라서 같은 이름이더라도 다른 생성자 인식함
// 이걸 막으면 주소번지가 출력 됨
System.out.println(jm.b); // 0이 나옴-디폴트 생성자가 호출되면 0이 출력되고, 파라미터를 가진 생성자가 호출되면 10이 출력된다.
jm.methodA(); // null이 출력될 것이다. 왜냐하면 this.j=j;를 경유하지 않았으니까
}
}
<예제6>
package com.step4;
class K {
K(Kmain km) {
System.out.println("K===> "+km);
}
}
public class Kmain {
public static void main(String[] args) {
Kmain km = new Kmain();
km = new Kmain();
System.out.println("Kmain===> "+km); // 이 위치에 따라 주소번지가 다르게 출력되는 걸 확인해야 한다
K k = new K(km);
}
}
주소값이 같을까? 같다면 왜 같을까?
복습
<예제7-클래스 2개>
package com.step4;
// 인스턴스화가 A a = new A(); 인 것 알면 기초는 아는 것
// 역할은? a.전변호출한다 / a.사용자정의메소드 호출한다
class L {
int i = 1; // 전역변수 선언-외부클래스에서 접근 가능(단 인스턴스화 해야한다)
}
public class Lmain {
void methodA(L l) { // 메소드 구현은 좌중괄호 우중괄호로 끝남
System.out.println("호출성공"+l);
int j = l.i; // 대입연산자를 통해서 오른쪽의 값을 왼쪽에 대입하시오
System.out.println(j);
} // end of methoda()
public static void main(String[] args) { // 1
// Lmain 클래스에서 L클래스에 정의된 전변 i를 사용하고 싶다면?
// Lmain 안에서 L클래스가 인스턴스화 되어야 한다.(제어권을 Spring이 가짐 <--스프링이 대신 인스턴스화 해주는 것이다<-객체에 대한 라이프사이클을 관리해준다)
// 인스턴스화의 여러 방법 꼭 체크해본다
// 어떤 클래스명으로 인스턴스화를 진행할 건가요?
L l = new L(); // 지변의 성격이다
Lmain lm = new Lmain();
lm.methodA(l); // 메소드 호출은 세미콜론으로 끝난다
}
}

<예제8>
package com.step4;
class M {
int tot; // 총점 담기
double avg; // 평균 담기
}
class M1 {
void hap(int n1) { // 지역변수는 초기화 안 해줌
System.out.println("hap(n1)===>"+n1);
// System.out.println("hap()"+n2); // 지역변수여서
}
void hap(M1 m1) {
System.out.println("hap(M1)===>"+m1);
}
}
public class Mmain {
public static void main(String[] args) {
int n1 = 70;
int n2 = 80;
int n3 = 90;
M1 m1 = null; // M1 m1 = new M1(); 이걸 두 줄로 나눈 건데
// 여기 공백에서 시점(위치)의 문제가 발생할 수 있음
// m1.hap(); // null이라서 호출 불가능
System.out.println(m1); // null 출력 / 여기서 호출하면 NullPointerException
m1 = new M1();
System.out.println(m1);
// m1.hap(M1);
m1.hap(m1);
// 지변이더라도 파라미터를 통해서 원본을 넘기면 다른 메소드에서도 사용이 가능하다
}
}
<예제9 증감연산>
package com.step4;
public class Oper2 {
public static void main(String[] args) {
int i = 1;
// 대입이 먼저야 - 1
int j = ++i; // 2, 2 출력 // ++증감연산자 1씩 증가해주세요 - 변수 앞에 있으면 먼저 증가함
// 증감연산자가 앞에 있으면 먼저 1 증가시키고 대입한다
System.out.println(i+","+j);
j = i++; // 2, 1 출력
System.out.println(i+","+j);
j = --i;
// --i=> i = i-1;
System.out.println(i+","+j);
}
}
<예제10-1 증감연산>
package com.step4;
public class Oper1 {
public static void main(String[] args) {
int i = 1;
int j = 2;
if((++i>j)|(i<j--)) { //(2>2 or 2<2)
System.out.println("참");
} else {
System.out.println("거짓");
}
System.out.println(i+","+j);
}
}
<예제10-2 증감연산>
package com.step4;
public class Oper1 {
public static void main(String[] args) {
int i = 1;
int j = 2;
// 이거나 비교일 때는 첫번째 조건이 참이면 두 번째 조건은 따질 필요가 없다. 왜냐하면 어차피 실행 될 거니까
// 첫 번째 조건만 따지고도 실행문이 결정된다.
// 예) 아이디 중복검사 -
if((++i>=j)||(i<j--)) { // 2>=2 or 2<2
System.out.println("참");
} else {
System.out.println("거짓");
}
System.out.println(i+","+j); // 2,2 출력
}
}
<예제11 증감연산>
package com.step4;
public class Oper3 {
public static void main(String[] args) {
int i = 1; // i=1
int j = i++; // j=1 / 왜냐면 대입이 먼저니까. 무조건 ++이 뒤에 있으면 나중에 증가시킴
System.out.println(i+","+j);
if((i>++j)&(i++==j)) {
System.out.println("참일 때");
} else {
System.out.println("거짓일 때");
}
i+=j;
System.out.println(i+","+j);
}
}
/*
True & True -> True &엔퍼센트 연산자
True & False -> False
False & True -> False
False & False -> False
&& 두 개이면 &일 때와 똑같은데
처음 조건이 false이면 뒤에 조건이 참이어도 어차피 실행문이 실행되지 않는다
이러한 이유로 && 두 개를 사용하는 거다 - 일량을 줄여준다. (뒤에 있는 조건 따지지 않는다)
한 개든 두 개든 결과에는 영향이 전혀 없다
True | True -> True |파이프 연산자
True | False -> True
False | True -> True
False | False -> False
*/
<예제12 증감연산>
package com.step4;
public class Oper4 {
public static void main(String[] args) {
int i = 1;
int j = 10;
do {
System.out.println("if문 이전 - 나는 무조건 실행된다");
if( i>j ) break; // 1>10? false
System.out.println("if문 조건을 만족하면 나는 못 본다");
j--; // 9 8 7 6...
// ++i -> 2<5, 3<5, 4<5, 5<5
} while(++i < 5); // while문 조건에 j가 없다 - 중요하지 않다
System.out.println("요기");
// 실행후 i , j의 값은?
System.out.println(i+","+j);
}
}
<예제13-1. - i를 쓰지 말고..>
package com.step4;
public class UnitTest {
public static void main(String[] args) {
BaseBallGame bbg = new BaseBallGame();
String hint = null;
for(int i=0;i<3;i++) {
hint = bbg.account(i+"회차: "+"25"+ ++i);
System.out.println(hint);
}
// for(int i=0;i<10;i++) {
// bbg.ranCom();
// System.out.println(bbg.com[0]+""+bbg.com[1]+""+bbg.com[2]);
// }
}
}
<예제13-2. 해결>
package com.step4;
public class UnitTest {
public static void main(String[] args) {
BaseBallGame bbg = new BaseBallGame();
String hint = null;
int cnt = 0;
for(int i=0;i<3;i++) {
hint = bbg.account(++cnt + "회차: "+"25"+ ++i);
System.out.println(hint);
}
// for(int i=0;i<10;i++) {
// bbg.ranCom();
// System.out.println(bbg.com[0]+""+bbg.com[1]+""+bbg.com[2]);
// }
}
}
<예제14-1. This>
package com.step4;
import javax.swing.JFrame;
class ZipcodeApp{
// 선언부
JFrame jf = new JFrame();
// 생성자 - 생성자도 메소드처럼 좌중괄호 우중괄호로 영역을 가짐 - 실행문(초기화, for, if 포함)이 올 수 있다
ZipcodeApp(){
initDisplay();
// 메인메소드의 zc.initDisplay();를 여기에 가져올 수 있다. 대신 인스턴스화 필요없음
// 생성자에서도 메소드호출 가능하다 -> 뭐가 다르지? 같은건가? - 결과, 어떤 상황일 때 차이점 발견한다
// 선처리와 후처리 필수 파악
// 메소드호출순서를 정할 수 있어야한다(대기상태, 지연, 데드락..)
}
// 화면그리기
public void initDisplay() {
System.out.println("화면그리기");
jf.setSize(400, 600);
jf.setVisible(true);
}
}
public class ThisMain {
ThisMain(){
}
public static void main(String[] args) {
// initDisplay 호출
ZipcodeApp zc = new ZipcodeApp();
// zc.initDisplay();
}
}
<예제14-2. This>
package com.step4;
import javax.swing.JFrame;
class ZipcodeApp{
JFrame jf = new JFrame();
ZipcodeApp(ThisMain tm){
System.out.println(tm.i);
for(double d=0;d<99999999999999999999999999999.0;d++) {
System.out.println("커피를 내리는 시늉을 하고 있다"); // 그럼 이 시간동안 지연되는 것(지연, 대기, 순서대로..)
}
initDisplay();
}
public void initDisplay() {
System.out.println("화면그리기");
jf.setSize(400, 600);
jf.setVisible(true);
}
}
public class ThisMain {
int i = 1;
ThisMain(){
}
public static void main(String[] args) {
ThisMain tm = new ThisMain();
ZipcodeApp zc = new ZipcodeApp(tm); // static을 사용하지 않으면서 세 개의 나눠진 클래스가 한 몸처럼 움직이게 한다
}
}
ranCom(): void - com배열에 0부터 9사이의 채번된 값을 담기
-> BaseBallGameLogic.java
account(String):String - 사용자가 입력한 숫자와 정답을 비교하여 숫자가 존재하면 볼, 자리까지 일치하면 스트라이크를 증가시킴
파라미터는 사용자가 입력한 숫자이고 리턴타입은 힌트문이 출력됨
-> BaseBallGameLogic.java
initDisplay():void - 화면을 그린다(프론트엔드가 하는 일)
-> BaseBallGameView.java
exit():void - 게임 종료하기 구현(종료버튼을 누르거나 컴터 강제 종료하거나 또는 윈도우 창에 X버튼을 누르는 경우)-3가지 경우에 모두 사용되니까 메소드를 설계하였다
-> BaseBallGameEvent.java
actionPerformed(ActionEvent):void - 이벤트 처리시
-> BaseBallGameEvent.java
이거 왜 정리하는 거지?
클래스를 3개로 쪼갤 때 메소드를 적당한 클래스에 배치해야 하니까
-> 도식화하는 이유는 세 개의 클래스에 적절하게 배치해야 하므로 그려본다
3개로 쪼갰을 때 학습목표
1. 클래스 사이의 관계를 정확히 파악하여 필요한 곳에 인스턴스화를 할 수 있다.
2. 인스턴스화의 위치에 따라 NullPointerException이 발생할 수 있다는 걸 설명하고 해결할 수 있다
3. static 대신에 생성자 호출을 통해서 객체주입을 받아내는 코드를 작성할 수 있다
<메인>
package com.step4;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
// BaseBallGameView로 가야 하는 전변이 있고
// BaseBallGameLogic로 가야 하는 전변이 있고
// BaseBallGameEvent로 가야 하는 전변이 있다
import com.week2.BaseBallGameView;
public class BaseBallGame implements ActionListener { // ActionListener: 이벤트 처리 담당하는 메소드
// 선언부 - 전역변수의 성격을 가진다 (코드적으로 얘기하면 인스턴스변수.변수명으로 어떤 클래스에서든 사용 가능하다)
JFrame jf = new JFrame();
//이미지를 담은 물리적인 위치 선언하기.
String imgPath = "D:\\workspace_java\\dev_java\\src\\com\\week2\\";
// ImageIcon titleIcon = new ImageIcon(imgPath+"yagu.png");
//ImageIcon bg = new ImageIcon(imgPath+"dreamballpark.jpg");
Image img = null;
JMenuBar jmb = new JMenuBar();
JMenu jm_game = new JMenu("게임");
JMenuItem jmi_new = new JMenuItem("새게임");
JMenuItem jmi_dap = new JMenuItem("정답");
JMenuItem jmi_clear = new JMenuItem("지우기");
JMenuItem jmi_exit = new JMenuItem("나가기");
JMenu jm_info = new JMenu("도움말");
JMenuItem jmi_detail = new JMenuItem("야구숫자게임이란?");
JMenuItem jmi_create = new JMenuItem("만든사람들");
//중앙에 들어갈 속지 선언
JPanel jp_center = new JPanel();
//세자리 숫자를 입력 후 엔터를 쳤을때 사용자가 입력한 숫자와 숫자를 맞추기 위한 힌트문
//을 출력해줄 화면.
JTextArea jta_display = null;
JScrollPane jsp_display = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JTextField jtf_user = new JTextField();
//글꼴과 글꼴에 대한 스타일과 글자 크기를 정해줌.-인스턴스화를 통해서, 그 값들은 생성자의 파라미터로 결정됨
Font f = new Font("Thoma",Font.BOLD,14);
//동쪽에 들어갈 속지 생성하기
JPanel jp_east = new JPanel();
//새게임, 정답, 지우기, 나가기 버튼 추가하기
JButton jbtn_new = new JButton("새게임");
JButton jbtn_dap = new JButton("정답");
JButton jbtn_clear = new JButton("지우기");
JButton jbtn_exit = new JButton("나가기");
int my[] = new int[3];
int com[] = new int[3]; // 세 자리 임의의 숫자를 담을 1차 배열 선언 - BaseballGameLogic으로 보내야 하는지? 아님 BaseballGameEvent에 있어야 하는지
int cnt = 0;//++cnt 힌트 문장에서 순번을 출력하는 변수 - 야구 회차를 나타내는 전역변수. 왜? 그 게임이 유지되는 동안에는 변해야 한다
// 새 게임이 시작(새 게임을 알리는 메소드가 호출됨- 새 게임일 때 초기화 해야하는 변수가 반드시 존재함)되면 다시 0으로 초기화 해야 한다
// 생성자
// 사용자정의 메소드
// 세 자리 임의의 숫자를 채번하는 메소드 구현하기
// 중복을 제거하여 채번한 숫자를 1차 배열에 초기화하기
public void ranCom() { // 새게임 버튼이 눌리면 호출되고 또 최초 실행하자마자 바로 호출될 수도 있다
Random r = new Random();
com[0] = r.nextInt(10);
// do..while()문 조건검사를 나중에 하므로 무조건 한 번은 실행되고, while문은 먼저 조건 검사를 하니까 한 번도 실행이 안 될 수 있다
do {
com[1]=r.nextInt(10);
} while(com[0]==com[1]);
do {
com[2]=r.nextInt(10);
} while(com[0]==com[2]||com[1]==com[2]);
}
//사용자가 입력한 값을 판정하는 메소드를 구현해 봅시다.
public String account(String user) {
if(user.length()!=3) {
return "세자리 숫자를 입력하세요.";
}
//사용자가 jtf_user에 입력한 숫자는 보기에는 숫자 처럼 보여도 알맹이는 문자열로
//인식을 합니다. 그래서 형전환을 한 후 my[]배열에 담아야 함니다.
//문자열 "256"을 숫자로 담을 변수 선언
int temp = 0;
int strike = 0;//힌트로 사용될 스트라이크를 담을 변수 선언
int ball = 0;//볼을 담을 변수 선언
//strike와ball을 지역변수로 해야 하는건 매 회차 마다 값이 변해야 하기 때문이다.
try {
temp = Integer.parseInt(user);
} catch (NumberFormatException e) {
return "숫자만 입력하세요.";
}
return strike+"스 "+ball+"볼";
}
//나가기 버튼이나 나가기 메뉴 아이템을 선택(클릭)했을때 호출되는 메소드 구현
public void exit() {
}
// 여기에 화면을 그리는 메소드 입양
//화면을 그려주는 메소드 선언
public void initDisplay() {
jta_display = new JTextArea();
jsp_display = new JScrollPane(jta_display);
jta_display.setOpaque(false);
jf.setResizable(false);
//jf.setContentPane(new BgPanel());
//////////////// 툴바에 들어갈 이미지 버튼 추가하기 ///////////////
//////////////// 메뉴 바 추가 시작 /////////////////
jm_game.add(jmi_new);
jm_game.add(jmi_dap);
jm_game.add(jmi_clear);
jm_game.add(jmi_exit);
jm_info.add(jmi_detail);
jm_info.add(jmi_create);
jmb.add(jm_game);
jmb.add(jm_info);
//////////////// 메뉴 바 추가 끝 /////////////////
System.out.println("initDisplay 호출 성공");
//이벤트 소스와 이벤트 처리 클래스를 매핑하는 코드 추가
//EventHandler ehandler = new EventHandler();
//jtf_user.addActionListener(ehandler);//여기서 this는 자기자신 클래스를 가리킴.-BaseBallGame:내안에 actionPerformed
jtf_user.addActionListener(this);//여기서 this는 자기자신 클래스를 가리킴.-BaseBallGame:내안에 actionPerformed
jbtn_new.addActionListener(this);
jbtn_dap.addActionListener(this);
jbtn_clear.addActionListener(this);
jbtn_exit.addActionListener(this);
jmi_exit.addActionListener(this);
jbtn_new.setBackground(new Color(158,9,9));
jbtn_new.setForeground(new Color(212,212,212));
jbtn_dap.setBackground(new Color(7,84,170));
jbtn_dap.setForeground(new Color(212,212,212));
jbtn_clear.setBackground(new Color(19,99,57));
jbtn_clear.setForeground(new Color(212,212,212));
jbtn_exit.setBackground(new Color(54,54,54));
jbtn_exit.setForeground(new Color(212,212,212));
jp_east.setLayout(new GridLayout(4,1));
jp_east.add(jbtn_new);
jp_east.add(jbtn_dap);
jp_east.add(jbtn_clear);
jp_east.add(jbtn_exit);
jta_display.setFont(f);
jta_display.setBackground(new Color(255,255,200));
jta_display.setForeground(new Color(57,109,165));
jf.setJMenuBar(jmb);
jtf_user.setBackground(new Color(255,255,200));
jp_center.setBackground(Color.green);
jp_east.setBackground(Color.black);
jp_center.setLayout(new BorderLayout(0,10));
jp_center.add("Center",jsp_display);
jp_center.add("South",jtf_user);
jta_display.setLineWrap(true);
jf.setLayout(new BorderLayout(10,20));
jf.add("Center",jp_center);
jf.add("East",jp_east);
jf.setTitle("야구 숫자 게임 Ver1.0");
jf.setSize(400, 300);
jf.setVisible(true);
}
// @는 annotation이라고 읽는다
// 부모가 정의한 메소드를 재정의하는 것-실제 그 장치에 필요한 기능을 구현하시오
// 부모(추상클래스-여기서 추상은 '결정되지 않음' 의미)가 정의할 수 없으니(서로 다른 장치라서)
// 나를 구현하는 클래스마다 그 기능에 차이가 있을테니 그 클래스에서 오버라이딩 하시오
// 단, 부모의 메소드를 훼손해서는 안됨
// actionPerformed도 개발자가 직접 호출하는 메소드가 아니다
// 그럼 언제 누가 왜 호출하는 걸까요? - 콜백함수(javascript에서 쓰는 용어), 콜백메소드(java에서 쓰는 용어)
// 누가? JVM이
// 언제? 버튼을 눌렀을 때 혹은 JTextField에서 엔터를 쳤을 때를 JVM이 감지(하드웨어-키보드-입력장치)해 내고
// 그 때 인터셉트(끼어들어서)해서 필요한 처리를 개발자가 해낸다 = dependency injection(스프링 프레임워크)
@Override
public void actionPerformed(ActionEvent e) { // 파라미터 자리에 ActionEvent 객체는 JVM에서 이벤트가 감지되었을 때 대신 주입해줌
System.out.println("actionPerformed 호출 성공");
String label = e.getActionCommand();
System.out.println("너가 누른 버튼의 문자열은 "+label+ " 입니다.");
Object obj = e.getSource();//이벤트소스의 주소번지를담아줌.
String input = jtf_user.getText();
System.out.println(input);
//너 나가기 버튼이니?
if("나가기".equals(label)) {
}
//새게임을 누른거야?
else if(obj == jbtn_new) {
jtf_user.requestFocus();
}
//이벤트가 발생한 이벤트 소스의 문자열을 비교하기
else if(e.getSource()==jtf_user) {
// 엔터 이벤트가 적용되었을 때 후처리로 JTextField에 문자열을 빈문자열로 교체함
// JTextArea가 선언부에서는 null이었지만 화면을 그리는 initDisplay()에서 인스턴스화를(생성) 마쳤으므로
// NullPointerException이 발생하지 않았다 - UI 통해서 테스트 할 때 관전포인트
// jta_display.setText("문자열");
// 숫자를 입력하고 엔터를 쳤을 때 JVM이 actionPerformed를 호출하는 거니까 내부에서 출력해볼 수 있다
// System.out.println(jtf_user.getText());
System.out.println("엔터쳤을 때: "+jtf_user.getText());
// 재사용성을 위해서 메소드 호출할 수 있도록 연습 - 메소드로 이관했을 때 동일한 효과
// cnt = cnt + 1;
jta_display.append(++cnt+"회: "+jtf_user.getText()+"\n");
jtf_user.setText("");
}///////////입력하고 엔터 쳤을 때
else if(obj==jbtn_dap) {
}
} // end of actionPerformed
// 메인메소드 - 최소한의 코드만 작성하도록 연습할 것
public static void main(String[] args) {
BaseBallGame bbGame = new BaseBallGame();
bbGame.initDisplay();
}
}
<유닛테스트>
package com.step4;
public class UnitTest {
public static void main(String[] args) {
BaseBallGame bbg = new BaseBallGame();
for(int i=0;i<10;i++) {
bbg.ranCom();
System.out.println(bbg.com[0]+""+bbg.com[1]+""+bbg.com[2]);
}
}
}