자바프로그래밍(CPA240)
©copyright 2010
교과목 소개
한국기술교육대학교 컴퓨터공학부 김상진
강의목표 1학년에는 두 학기를 걸쳐 C언어를 통해 기초적인 프로그래밍 능력과 기술을 배양하였음 2학년 1학기에는 C++ 언어를 통해 객체지향 프로그래밍 언어 언어를 학습하였음 이번 학기에는 자바 언어를 통해 프로그래밍 능력과 기술(특히, 기술(특히 객체지향 프로그래밍 능력과 기술), 문제해결 능력을 한 단계 업그레이드함 자바 프로그래밍 언어에 대한 이해 객체지향 프로그래밍 능력 향상 문제 분석 및 해결 능력 향상 설계 능력 향상
2/13
객체지향 vs. 구조화 프로그래밍 예) 학교 성적 관리 시스템 구조화 프로그래밍 (structured programming) 필요한 기능을 찾는다. 학생 검색 기능 성적 계산 기능 정렬 기능 … 이런 기능들을 모듈화 듈화 (함수로 구현)한다.
객체지향 프로그래밍 (object-oriented programming)
할 수 있는 객체로 모델링할 것을 찾는다. 학생 교수 과목 … 그 다음 각 객체의 행위를 정의한다. (클래스로 구현)
3/13
강의정보 교수 정보 연구실: 제4공학관 B301호 전화번호: 교내 1490 (041-560-1490) 전자우편: sangjin@kut.ac.kr 강의홈페이지: 강의홈페이지 el.kut.ac.kr http://infosec.kut.ac.kr/sangjin/class/java1002/ 교재 천인국, 하상호, Power Java, 인피니티북스, 2009
4/13
강의정보 – 평가방법 강의 평가 방법 출석: 5%, 숙제: 10%, 실습: 10%, 개인 프로젝트: 20 %, 중간: 25%, 실습시험/기말: 30% 참고. 바뀔 수도 있음 프로그래밍 숙제는 수업 날짜 하루 전 밤 12시까지 전자우편으로 제출되어야 인정됨 1시간 이상 지각할 경우 지각 처리함 합당한 사유를 사전에 통보하면 결석 처리하지 않음 결석할 경우에도 실습 내용을 숙제로 전부 제출해야 함 P Pre-requiste i t C/C++ 언어 강의 준비물 강의 노트를 강의 홈페이지에서 다운받아 준비해야 함.
5/13
강의정보 – 부교재 C. S. Horstmann, Big Java, 3/E, Wiley, 2007. C. S. Horstmann and G. Cornell, Core Java 2 Vol. 1: F d Fundamentals, t l 8/E, 8/E P Prentice ti H Hall, ll 2007 2007. H.M. Deitel and P.J. Deitel, JAVA: How to program, 8/E, Prentice Hall, 2009. W. Savitch, Absolute Java, 4/E, Addison Wesley, 2007. K. Sierra, B. Bates, Head First Java, 2nd Ed., O’Reilly, 2005.
6/13
강의정보 – 부교재 J. Gosling et al., The Java Language Specification, 3rd Ed., Addison Wesley, 2005. J Bl J. Bloch, h Eff Effective ti J Java, 2/E 2/E, Addi Addision i W Wesley, l 2008 2008. 마에바시 가즈야, 자바를 지배하는 핵심원리, 영진.COM, 2003. 조효은, 초보자를 위한 Java 200제, 정보문화사, 2005
7/13
강의계획 1
강의소개
9
인터페이스
2
자바소개
10 상속
3
객체와 클래스 소개
11 예외
4
기본적인 데이터 타입
12 범용 프로그래밍
5
결정문, 반복문
13 파일처리
6
배열
14 애플릿
7
중간시험
15 실습/기말시험
8
클래스 기초
16 프로젝트 발표
8/13
설계 과제 설계 안내서 제공 2인(무조건) 과제 자바 언어를 이용 객체지향적으로 설계해야 함 사용자 인터페이스는 중요하지 않음 문제 정의와 문제를 해결하는 창의적 알고리즘이 있어야 함 예) 엘리베이터 시뮬레이션 일정표 4주까지 주제 제안서 제출 중간고사 이후 중간보고서 제출 기말고사 이후 최종보고서 제출 및 결과시연 양식 제공함
9/13
사용할 소프트웨어 자바(java.sun.com) J2SE 버전(J2EE, J2ME): 최신 버전 JDK 6 Update 21 http://www.oracle.com/technetwork/java/javase/downloads/ index.html JDK(Java 2 Development Kit) Java2 documentation 설치방법: 특이사항 없음 설치방법 개발환경 Eclipse(www.eclipse.org/downloads) Eclipse 설치하기 전에 JDK가 설치되어 있어야 함 helios 설치 (eclipse-java-helios-win32.zip) Eclipse 설치방법 설치하고자 위치에 압축 해제 eclipse.exe 파일 실행
10/13 10 /13
eclipse 사용법
단계 1. 프로젝트 생성하기 File 메뉴 – New – Java Project 선택
단계 1-1. 1 1 프로젝트 이름
단계 1 1-2 2. 프로젝트 생성 위치
11/13 11 /13
eclipse 사용법 – 계속 단계 2-1. 2 1 패키지 이름
단계 2. 2 프로젝트를 생성한 후에는 프로그램을 구성할 클래스들을 정의해야 함. 자바 프로그래밍의 기본 단위는 클래 임 클래스임
단계 2-2. 2 2 클래스 이름 단계 2-3. main 메소드 포함여부
12/13 12 /13
eclipse 사용법 – 계속
단계 2-4. 편집기를 이용하여 클래스를 작성함
단계 3. 3 구현한 자바 프로그램을 실행하는 방법
실행 결과는 Console에 보통 나타남 13/13 13 /13
©copyright 2010
자바프로그래밍(CPA240)
개요
한국기술교육대학교 컴퓨터공학부 김상진
교육목표 자바 프로그래밍 언어의 특성을 익힘 자바 탄생 배경과 목적을 알면 자바가 왜 어떤 특정한 특성을 가지고 있는지 알 수 있음 첫 자바 프로그램을 컴파일하고 실행해봄으로써 자바 언어에 대한 기본적인 개념을 익힘
2/32
자바의 역사 Sun Microsystems 사의 James Gosling과 Patrick Naughton이 개발한 고급 프로그래밍 언어 프로젝트 코드명: 코드명 Green G (1991년에 시작) 처음에 언어 이름은 “Oak”임 원래는 지능형 TV와 같은 가정용 가전 제품에 사용될 언어로 개발됨 각 제조업체는 다른 CPU를 사용할 수 있으므로 특정 컴퓨터 구조에 구 에 독립성을 가지는 것이 가장 중요한 중 한 설계 목 목표이었음 목표 이었음 기계어와 독립적인 중간 코드 코드(intermediate code)를 생성하는 이동 가능 언어 개발 (WORA, Write-Once Run Anywhere) 다른 말로 바이트 코드 코드(byte-code)라 함 구현된 프로그램의 강건성이 중요함 C++ 기반: 객체지향 언어 C++와 달리 비객체지향적 요소가 없음 업계 반응: 냉대
3/32
자바의 역사 – 계속 WWW의 급성장 Naughton 등은 이 언어를 이용하여 1994년도에 HotJava라고 하는 웹 브라우저를 개발함 이 브라우저는 기존 브라우저와 달리 자바로 작성된 애플릿 애플릿(applet) 이란 프로그램을 웹에서 다운받아 실행할 수 있음 1995년 Netscape 브라우저가 도입 애플릿의 등장으로 동적 콘텐츠가 매우 확대됨 안전성과 이식성이 중요한 이슈 현재는 마이크로소프트 인터넷 익스플로러에서도 지원
4/32
자바의 역사 – 계속 1996년: Sun Microsystems 사는 자바의 첫 버전 출시 (1.0) 실제 응용을 개발하기에는 기능이 미흡 1997년에 1.1버전 출시 1998년: Java 2 출시 (1.2) J2SE(C J2SE(Core/Desktop), /D kt ) J2EE(E J2EE(Enterprise), t i ) J2ME(Mobile) J2ME(M bil ) 세 종류의 버전을 제공 2004년: 년 Java 5.0 출시 ((1.5), ), 초기에 기에 “Tiger”라는 g 라는 이름 이름으로 개발됨 2006년: Java 6.0 출시, “Mustang”라는 이름으로 개발됨 현재 update 21이 최종 버전(2010. 7) 2009년: 오라클, Sun Microsystems 인수 2010년에 Java 7.0 출시 예정, “Dolphin”이라는 이름으로 개발됨
5/32
자바의 역사 – 계속 자바 표준 라이브러리에 있는 클래스의 개수
4000 3500 3000 2500 2000 1500 1000 500 0 Java 1.02 250 클래스 매우 느림 애플릿 개념 처음 도입
Java 1.1 500 클래스 조금 빨라짐 인기 상승 GUI 기능 제공
Java 2 Java 5 (1.2-1.4) (1.5 이상) 2300 클래스 3562 클래스 훨씬 빨라짐 다양한 기능 추가 3종류 제공 (J2SE, J2EE, J2ME) 웹 기반/모바일 응용에 널리 사용
6/32
자바 라이브러리의 중요성 avoid reinventing the wheel 가능한 기존에 구현된 클래스를 이용해야 함 재사용은 객체지향 프로그래밍의 핵심적인 이점 중 하나 본인이 직접 작성한 클래스나 메소드 대신에 Java API를 사용하면 첫째 프로그램 성능을 향상시킬 수 있음 첫째, Java API들은 검증된 것이므로 보통 프로그래머들이 작성한 것들보다 것들 다 우수함 (범용성 vs. 특수성) 둘째, 호환성이 높아짐 Java API 뿐만 아니라 웹에는 수 많은 재사용 가능한 클래스들이 존재함
7/32
자바의 장점 단순: C++에서 모호하거나 자주 사용되지 않는 기능 제거 완전한 객체지향 언어: C++보다 더 라이브러리가 클래스 형태로 제공됨 (C는 함수 형태로 제공) 강건성 향상(bug-free): 오류가 발생할 확률을 줄이는 것이 목적 타입의 강제성 자바 수동 메모리 할당과 해제 제거 소스 코드 garbage collection 포인터 연산 제거(cf. 제거(cf off-by-one off by one error) Java 할당 연산과 비교 연산 혼동 제거 compiler 다중 상속 제거, 대신 인터페이스 개념 도입 플랫폼 독립성 JVM JVM JVM 이식성이 높음 해석 기반(cf. 기반(cf 컴파일) 다중 쓰레드 지원 분산 지원: TCP/IP 통신 지원 8/32
자바에 대한 오해 자바는 HTML의 확장이다. 자바는 배우기 쉬운 프로그래밍 언어이다. 자바는 프로그램하기 쉬운 환경을 제공한다. 자바는 모든 플랫폼을 위한 범용 프로그래밍 언어가 될 것이다. 자바는 또 다른 프로그래밍 언어이다. 언어이다 모든 자바 프로그램들은 웹 페이지 내에서 수행된다. 웹과 관련된 프로그램을 개발하기 위해 많이 사용되지만 자바는 범용 프로그래밍 언어이다. 자바 애플릿은 보안적으로 매우 위험하다. 자바스크립트는 자바의 단순 버전이다.
9/32
OOP의 기본 개념 프로그램은 객체 객체(object)로 구성됨 객체는 행위(behavior), 상태(state), 식별자(identity)를 가짐 캡슐화(encapsulation): 데이터와 행위를 하나로 결합 캡슐화 객체는 메시지를 받아 그것을 처리해줌 내부 구현에 대해서는 don’t d ’t care 한 객체는 다른 객체의 내부 데이터를 절대 직접 조작하지 않음 객체간에 상호작용을 통해 원하는 목적을 달성함 객체가 어떤 상태를 가지고 있고 어떤 행위를 할 수 있는지 알려주기 위해 클래스 클래스(class)를 정의함 객체지향 프로그래밍에서는 프로그램의 목적을 달성하기 위해 필요한 객체를 식별하고, 이들 간에 어떤 상호작용이 필요한지 설계하는 것이 가장 중 중요함 함
10/32 10 /32
OOP의 기본 개념 – 계속 클래스 클래스(class) 같은 종류의 객체들의 모임 클래스는 이름을 가지며, 멤버 변수(instance variable, field, attribute)와 메소드로 구성됨 클래스 간에 관계 사용(use-a) 포함(has-a, 함( , aggregation) gg g ) 상속(is-a, inheritance) 인스턴스(instance) 클래스의 한 객체를 그 클래스의 인스턴스라 함 Rule of thumb: 클래스 이름은 명사, 메소드의 이름은 동사
11/32 11 /32
OOP vs. Structured Programming void f(){ Point x; … printPoint(&x); } /* f() */ 구조화 프로그래밍 함수(기능) 위주 void printPoint(Point *p){ 데이터와 그것을 처리하는 함수가 분리 printf("(%d, %d)", p->x, p->y); typedef struct{ int i t x; int y; } Point;
} /* printPoint */ void f(){ Point x = new Point(); public class Point{ … private int x; i () x.print(); private int y; } /* f() */ public void print(){ 객체지향 프로그래밍 y p ( ( , %d)", ) , x,, y); System.out.printf("(%d, 데이터와 행위 결합 } // print 데이터 위주 } // class Point 실세계를 보다 가깝게 모델링
12/32 12 /32
자바의 코드 구조 자바 소스 파일(.java 확장자)에 클래스 클래스(class)를 정의함 한 소스 파일에 여러 개의 클래스를 정의할 수 있음 이 중 한 클래스의 이름은 파일 이름과 동일해야 하며, 이 클래스는 유일하게 public이라는 키워드를 사용하여 정의해야 함 클래스 내에는 멤버 변수 변수(member variable)와 멤버 함수 함수(member function)를 선언하고 정의함 소스 파일 멤버 함수는 다른 말로 메소드 메소드(method)라 함 클래스 1 여러 개의 멤버 변수와 멤버 함수를 정의할 수 있음 메소드 1 멤버 함수는 프로그램 문장들로 구성 구성되며 그 함수가 구성되며, 프로그램 문장들 실행되었을 때 무엇을 어떻게 수행해야 하는지를 메소드 2 나타냄 프로그램 문장들 장 클래스 2
13/32 13 /32
Hello.java
public class Hello { public static void main(String[] args) { // 콘솔 윈도우에 Hello, World 출력 System.out.println("Hello, World!"); } // main } // class Hello
자바는 대소문자를 구별함. main과 Main은 다른 식별자임 자바 프로그램을 작성하는 방법에 대한 제약은 없음 단어와 단어 사이에 공백과 줄바꿈의 수는 아무런 의미가 없음 그러나 적절한 형식 형식을 갖추어 작성하 작성하는 것이 바 바람직함 러나 들여쓰기 여 기등적 직 중괄호 { }는 프로그래밍 블록을 나타내며, {를 입력하였으면 }를 바로 입력하고 블록 내에서는 들여쓰기를 하는 것이 중요함 eclipse의 li 의 경우 {를 입력한 다음에 enter t 키를 누르면 }이 자동으로 입력됨
14/32 14 /32
Hello.java – 계속 #include <stdio.h>
C언어 버전
void main(){ /* 콘솔 윈도우에 Hello, World 출력 */ printf("Hello, World!"); } /* main */
#include <iostream> using std::cout;
C++ 버전
void main(){ // 콘솔 윈도우에 Hello, World 출력 cout << "Hello,, World!";; } // main
자바는 다음과 같은 세 종류의 주석을 제공함 // end-of-line 주석 (C++와 동일한 형태의 주석) /* … */ C와 동일한 형태의 전통적인 주석 /** … */ javadoc 형태의 주석 javadoc 프로그램을 이용하여 소스를 html 형태의 문서로 바꿀 수 있음 주석은 컴파일러가 무시하는 부분임 그러나 중요한 러나 문서화 측면에서 매우 중 한 요소임 임 15/32 15 /32
Hello.java – 계속 public class Hello{ public static void main(String[] args){ // 콘솔 윈도우에 윈 에 Hello, World 출력 System.out.printf("Hello, World!"); } // main } // class Hello
클래스 클래스(class)는 객체지향 프로그래밍의 기본 요소임 OOP에서는 여러 개의 클래스를 정의하여 프로그램을 작성함 Hello는 클래스의 이름임 보통 이름은 대 대문자로 시작함 통 클래스 래 이 자 시작 public 키워드는 클래스의 종류를 나타내는 접근 권한 지정자 (access specifier)임 (vs. private) 한 파일에 여러 개의 클래스를 정의할 수 있음 하지만 public 클래스는 오직 한 개만 정의할 수 있음 또한 public 클래스의 이름과 파일 이름이 같아야 함 16/32 16 /32
public class Hello{ public static void main(String[] args){ …
public, static: 메소드의 종류를 나타내는 지정자 public: bli 누구나 호출할 수 있는 메소드임을 나타내는 지정자 static: 클래스 메소드임을 나타내는 지정자 인스턴스를 인 턴 를 생성하지 않 않고도 호출할 출할 수 있 있는 메 메소드 나중에 보다 자세히 설명함 이 지정자들을 제외하면 C에서 함수를 정의하는 것과 동일함 main 메소드는 반드시 public static 메소드이어야 함 void: 메소드가 값을 반환하지 않는다는 것을 의미함 main: 메소드의 이름으로서, 모든 자바 응용 프로그램은 하나의 main 메소드가 있어야 함 (C언어와 같음) main 메소드는 프로그램의 시작 위치를 나타내며, main 메소드가 있 는 클래스만 실행가능 모든 자바 클래스에 main 메소드를 정의할 필요는 없음 프로그램에서 여러 개의 클래스를 정의하여 사용하는 경우, 보통 그 중 하나만 main 메소드를 가짐 17/32 17 /32
Hello.java – 계속 public class Hello{ public pub c stat static c void o d main(String[] a (St g[] a args){ gs){ System.out.printf("Hello, World!"); } // main } // class Hello
String[] args: 메소드의 파라미터(parameter) 명령줄 인자를 위한 파라미터 예1.1) javac Hello.java 여기서 Hello.java가 javac 프로그램의 인자가 됨 S i 은 타입 이름임 String은 String[]은 String 배열 타입을 나타냄 String은 자바에서 제공하는 기본 타입은 아님 String은 자바 라이브러리에서 제공하는 클래스 이름임 args는 파라미터 변수의 이름임 18/32 18 /32
Hello.java – 계속 public class Hello{ public static void main(String[] args){ System out printf("Hello System.out.printf( Hello, World! World!"); ); } // main } // class Hello
메소드의 몸체(body)는 중괄호 ‘{}’ 안에 작성함 자바에서 프로그램 문장은 세미콜론 ‘;’으로 끝남 System은 클래스 이름이고, 이름이고 out은 객체 이름임 System은 시스템 자원을 접근할 때 사용하는 자바 라이브러리에서 제공하는 클래스임 class variable vs. instance variable println: 메소드 이름 일반적으로는 클래스의 인스턴스를 생성한 다음에 메소드를 호출해야 함 이 경우 경우는 인스턴스를 를 생성하지 않 않고 호출하고 출하 있음 19/32 19 /32
printf, println, print 메소드 큰 따옴표 "…"로 둘러싸인 일련의 문자를 문자열 문자열(string)이라 함 콘솔에 출력할 때, printf, println, print 메소드를 사용할 수 있음 printf는 java 5부터 제공됨 printf의 사용법은 C와 거의 유사함 println과 i tl 과 print의 i t의 차이점은 println은 i tl 은 데이터 출력 후 줄바꿈을 해줌 println과 print 메소드는 문자열 뿐만 아니라 다양한 타입의 데이터를 출력할 수 있음 System.out.println(3+4); System.out.println("3+4");
20/32 20 /32
printf, println, print 메소드 – 계속 확장 문자열 문자열(escape sequences): C/C++와 동일 ‘\’ 문자를 확장 문자라 하며, ‘\’을 포함한 문자열 나열을 확장 문자열이라 하며, 출력하고자 하는 문자를 문자열 내에 직접 나타낼 수 없을 때 사용한다. 예) Hello, Hello "World World "!를 !를 출력하고 싶다. 싶다 System.out.println("Hello, \"World\"!");
21/32 21 /32
자바 라이브러리에 있는 클래스 사용 Hello.java에서는 String, System 두 개의 자바 라이브러리 정의되어 있는 클래스를 사용하고 있음 보통 C/C++의 경우 라이브러리에 정의된 함수를 사용하기 위해서는 #include를 통해 함수와 관련된 정보를 컴파일러에게 알려주어야 함 자바에서 유사한 역할(import 문)을 하는 것이 있지만 몇 클래스들은 이와 같은 것을 이용하지 않고 사용할 수 있음 j java.lang l 패키지에 정의된 클래스는 import문을 i t문을 사용하지 않고 사용할 수 있음
22/32 22 /32
오류 경우1) System.ouch.println("Hello, World!"); 경우1) ouch라는 단어를 컴파일러가 이해하지 못한다. 컴파일 시간 오류, 문법 오류 오류(syntax error) System.ouch cannot be resolved 경우2) 경우 2) System.out.println("Hello, S t t i tl ("H ll World!); W ld!) 큰 따옴표가 끝나지 않고 있음 이 문장 때문에 이 문장이 아닌 다른 이웃 문장에서 거짓 오류 메시지가 출력될 수 있음 경우3) 경우 3) System.out.println("Hell, World!"); 어떤 오류 메시지도 출력되지 않음 실행 시간 오류, 논리 오류 오류(logical error) 가장 흔한 오류 철자 오류, 오류 자바는 대소문자를 구분!!! 컴파일러에서 출력하여 주는 오류 메시지에 대한 이해가 중요 디버깅을 할 줄 알아야 함 (통합개발환경의 디버거 활용 능력 필 필요)) 23/32 23 /32
컴파일 과정 javac hello.java
java hello
클래스 파일 편집기
자바소스 코드
컴파일러
실행 인터프리터
라이브러리 파일
컴파일러는 자바소스 코드(*.java)를 번역하여 확장자가 *.class인 바이트 코드를 생성함 자바 라이브러리는 다른 누군가에 의해 프로그래밍되고 바이트 코드로 컴파일된 코드들의 모임임 (System.out.printf) 자바 가상 기계 기계(JVM, Java Virtual Machine)는 바이트 코드를 차례로 실제 기계 코드로 번역하면서 실행함 (번역방식 vs. 컴파일방식)
24/32 24 /32
컴파일 과정 java hello
실행
java 명령어에 의해 JVM이 실행되며, 다음 세 가지 세부과정을 통해 자바 프로그램이 실행됨 단계 1. 메모리에 관련 .class 파일들을 적재함 단계 2. 2 바이트 코드를 검사함 단계 3. 실제 실행 단계: 이 때 JIT JIT(Just-In-Time) 기술을 사용함 자바를 이용한 프로그래밍에서는 두 단계 번역이 이루어짐 javac: 자바 코드 바이트 코드 JVM: 바이트 코드 기계어
25/32 25 /32
JIT 기술 JVM은 번역 방식으로 바이트 코드를 실행함 번역 방식은 컴파일 방식에 비해 이식성이 높다는 장점이 있지만 느리다는 단점이 있음 JVM은 이것을 극복하기 위해 JIT(Just-In-Time) 기술을 사용함 JVM은 실행시간에 바이트 코드를 차례로 기계어로 번역하여 실행하지만 한번 기계어로 번역된 메소드가 다시 실행되면 다시 기계어로 번역하지 않고 이전에 번역한 것을 활용함 이렇게 한번 번역된 부분을 다시 번역하지 않고 이미 번역되어 있는 기계 코드를 활용하는 기법을 JIT라 함
26/32 26 /32
두 번째 프로그램 import java.util.Scanner; public class AddIntegers { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.print("정수를 입력하시오: "); int n1 = in.nextInt(); (); System.out.print("정수를 입력하시오: "); int n2 = in.nextInt(); int sum = n1 + n2; S t System.out.printf("%d t i tf("%d + %d = %d%n", %d% " n1, 1 n2, 2 sum); ) } // main } // class AddIntegers
27/32 27 /32
패키지 자바 프로그램은 여러 개의 클래스로 구성됨 프로그램이 커질 수록 클래스 수가 많아지므로 단순히 여러 개의 파일로 관리하는 것은 힘듦 자바는 이를 위해 패키지라는 기능을 제공함 패키지는 일련의 클래스들을 계층구조로 묶어 관리할 수 있도록 해줌 자바에서 라이브러리는 패키지 형태로 제공됨 자바 라이브러리에 정의되어 있는 클래스를 사용하기 위해서는 컴파일러에게 필요한 정보를 제공해 주어야 함 이를 위해 import 문을 사용함 (C/C++에서 #include와 유사)
28/32 28 /32
import import 문의 문법 import packageName.ClassName; 같은 디렉토리에 있는 클래스는 자동으로 같은 패키지에 속하게 되지만 package 문을 통해 파일에 정의된 클래스들이 속하는 패키지를 별도로 정의할 수 있음 #include와 달리 일반 프로그래밍 문장임 (;로 끝남) 의문.. String, System.out 등은 import 문 없이 사용하였음 의문 이 두 클래스는 java.lang 패키지에 포함되어 있으며, java.lang 패키지에 있는 클래스는 자동으로 import됨 i import t 문 없이 완전 경로를 주어 클래스를 사용할 수 있음 java.awt.Rectangle box; 자바는 CLASSPATH라는 환경변수를 통해 패키지의 위치를 찾음
29/32 29 /32
import – 계속 사용하는 각 클래스마다 별도의 import 문을 사용할 수 있고, 다음과 같은 형태로 어떤 패키지에 있는 모든 클래스를 한번에 import할 수도 있음 예) import java.awt.*; 위와 같이 하면 java.awt에 java awt에 있는 모든 클래스가 프로그램에 포함되는 것은 아님. java.awt 패키지 중에서 프로그램 내에서 사용하는 클래스만 포함됨 다음은 문법 오류임 import java.*.*;
30/32 30 /32
import – 계속 예를 들어 어떤 자바 프로그램에 다음과 같이 두 개의 패키지가 포함되었다고 하자. import java.awt.*; import java.io.*; 프로그램 내에 어떤 클래스를 보면 이 클래스가 어느 패키지에 있는지 알 수 있는가? 알 수 없음 만약 서로 다른 패키지에 같은 이름의 클래스가 있으면 위와 같이 포함하면 오류가 발생함 따라서 이런 형태로 import하기 보다는 import하고자 하는 클래스를 명백하게 표현하는 것이 바람직함 예) import java.awt.Rectangle;
31/32 31 /32
new Scanner in = new Scanner(System.in); … int n1 = in.nextInt();
Scanner는 입력을 처리하기 위해 사용하는 클래스 객체지향에서 클래스는 객체를 정의하는 틀일 뿐이므로, 실제 데이터를 입력받기 위해서는 이를 처리할 객체를 생성해야 함 객체의 생성은 일반적으로 new 연산자를 사용함 new 연산자는 보통 생성자 메소드를 피연산자로 사용함 System.in 객체(표준입력: 키보드)를 y 객체( 준입력 키 )를 이용하여 Scanner 객체를 생성하면 이 객체를 이용하여 키보드 입력을 처리할 수 있음 객체를 생성한 다음에는 필요한 메소드를 호출하여 필요한 일을 수행함 nextInt는 정수를 입력받을 때 사용하는 메소드
32/32 32 /32
©copyright 2010
자바프로그래밍(CPA240)
객체
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 자바의 탄생 배경 가전 제품에 사용될 프로그래밍 언어 특정 컴퓨터 구조와 독립성: 중간코드, JVM에 의한 해석 방식 으로 실행 코드의 의 강건성( 강건성(bug-free) g ) 객체지향 언어: 비객체지향 요소가 없음 한 파일에 여러 개의 클래스를 정의할 수 있지만 이 중에 public 클래스는 하나 밖에 있을 수 없으며, 없으며 그 클래스의 이름은 파일 이름과 같아야 함 C/C++ 언어와 마찬가지로 main 메소드에 의해 프로그램의 시작 위치를 나타냄. 나타냄 이 메소드는 public static 메소드이어야 함 모든 클래스가 main 메소드를 가질 필요는 없음 main 메소드를 가지는 클래스는 실행 가능 자바 라이브러리에서 제공하는 클래스를 사용하기 위해서는 해당 클래스를 import 해야 함. 단, java.lang 패키지에 있는 클래스는 p 하지 않아 않아도 됨 import하지 2/27
교육목표 타입과 변수 식별자 명명 규칙 객체의 생성과 객체 변수 원시타입과 참조타입 프로그램은 단지 숫자, 문자열만을 조작하지 않는다 보통 은행 계정, 않는다. 계정 인사기록과 같은 복잡한 데이터 항목을 다룬다. 이런 데이터 항목을 객체지향 프로그래밍에서는 객체로 모델링하여 조작한다.
3/27
타입과 변수 자바에서 사용되는 모든 값은 어떤 특정 자료유형 또는 타입 타입(type)에 속함 예) "IME"는 String 타입, System.out은 PrintStream 타입 프로그래밍에서 종종 값을 저장한 후에 나중에 다시 사용해야 하는 경우가 있음. 있음 이를 위해 변수 변수(variable)라는 개념을 사용함 변수: 어떤 값을 저장하는 곳 변수 컴퓨터에서 값이 저장되는 곳은 주기억장치 변수 선언할 때에는 타입정보(어떤 종류의 값을 저장할 것인지 )와 이름(나중에 이 이름을 통해 접근)을 제공해야 함 예) String name; PrintStream screen = System.out; int age = 18;
변수는 screen처럼 선언과 동시에 초기화될 수 있음 변수에는 변수 타입에 해당하는 값만 저장할 수 있음
long i t int
4/27
식별자 프로그램을 작성할 때 프로그래머가 직접 정의하는 이름을 식별자라 함 예) 변수 이름, 이름 클래스 클래 이름, 이름 메소드 메 이름 등 명명 규칙 영문자, 영 자, 숫자, 밑줄문자(_), 자(_), dollar 기 기호($)만을 ( ) 사용할 수 있 있으며, 며, 숫자로 시작할 수 없음 예) _total, sum, box01 등은 유효, 12dice, white white-box box 등은 유효하지 않음 중간에 공백 문자가 포함될 수 없음 public과 같은 자바 키워드는 사용할 수 없음 대소문자를 구분함 식별자의 종류에 따라 통일성 있게 이름을 정의하는 것이 바람직함 식별자의 형태만 보면 이것이 어떤 종류의 식별자인지 알 수 있도록 함
5/27
자바 키워드 목록 boolean
byte
char
double
float
int
long
short
public
private
protected
abstract
final
native
static
strictfp
synchronized
transient
volatile
if
else
do
while
switch
case
default
for
break
continue
assert
class
extends
p implements
instanceof
new
package
super
this
catch
finally
try
throw
throws
return
void
const
goto
enum
6/27
변수 – 계속 변수 사용 측면에서 C/C++와 차이점 자바에서는 타입이 매우 엄격하다. 예) // JAVA int age; age = 5.5; 5 5 // error
// C/C++ int age; 5; // OK, OK 5가 저장됨 age = 5 5.5; // C++ warning을 표시해줌
초기화되지 않은 변수의 사용에 대해 매우 엄격함 예2.6) // JAVA int age; S t System.out.println(age); t i tl ( ) // error // C/C++ intt age; printf("%d\n", age); // 쓰레기 출력 // C++ warning을 표시해줌
7/27
객체지향 프로그래밍 컴퓨터가 어떤 유용한 일을 하기 위해서는 그것을 할 수 있도록 방법을 알려주어야 함 보다 편리하게 방법을 알려주기 위해 고급 프로그래밍 언어를 개발하여 사용하고 있음 방법이란 보통 절차를 말함. 따라서 초기 프로그래밍 기법은 절차 위주였음. 즉, 어떤 함수들을 위주 함 만들어야 하는지가 초점이었음 프로그램이 제공해야 하는 기능이 복잡해지고, 프로그램에서 처리하는 데이터가 복 처리하 복잡해짐에 해 에 따라 다 다른 형태의 프로그램 패러다임이 필요하여 객체지향 개념이 등장함 객체지향 프로그래밍 (데이터+절차를 하나로 묶음) 실제와 보다 유사하게 모델링할 수 있음 객체지향 프로그래밍에서는 객체라는 것들을 생성하여 그들간에 상호작용을 통해 우리가 원하는 일을 수행하도록 함 즉 객체는 프로그램이 실행되었을 때 수명을 가지는 것임 즉, 객체가 어떤 행위를 할 수 있고, 어떤 데이터를 가지고 있는지 정의하기 위해 클래스를 정의함 8/27
객체 객체 프로그래밍 측면에서 보면 프로그램에서 메소드를 호출하여 객체: 조작할 수 있는 개체를 말함 (메소드 호출, 메시지 전송) invoking a message, sending a message
System.out.println(…) y p ( )
object j public interface
void println(int); p ( ) void println(double)
같은 이름의 메소드가 여러 개 있을 수 있음 다른 종류의 객체는 다른 종류의 메소드를 제공함 다른 종류의 객체들이 같은 이름의 메소드를 제공할 수도 있으며, 보통 그 메소드들은 각 객체에 맞는 행동을 함 같은 종류의 객체들은 클래스로 분류됨 보통 객체는 생성을 해야 사용할 수 있음 예외) System.out (사용자가 직접 생성하지 않음)
9/27
객체 – 계속 – 계속 같은 이름의 메소드가 여러 개 있을 수 있음. 이와 같은 현상을 오버로딩(overloading)이라 함 오버로딩 다른 종류의 객체는 다른 종류의 메소드를 제공함 PrintStream
String "Sangjin"
println print printf i tf
length
객체의 공개 인터페이스: 외부에서 객체를 조작하기 위해 사용할 수 있는 메소드 객체를 사용하는 입장에서는 내부 구현을 몰라도 됨 println이 내부적으로 어떻게 구현되어 있는지 몰라도 사용할 수 있음 외부에서 사용할 수 없고 내부적으로 사용되는 인터페이스도 있음 10/27 10 /27
객체의 생성 객체를 생성할 때 사용하는 연산자 문법.. new ClassName(construction parameter) 문법 예) new Rectangle(5, 10, 20, 30) new 연산자 뒤에 ‘ClassName(…)’ 부분은 보다 정확하게 말하면 클래스에 정의되어 있는 생성자 메소드임 객체마다 그 객체를 생성할 때 제공해야 하는 인수가 다름 예) new Car("SM5", 2005); 어떤 클래스는 한 가지 이상의 방법으로 객체를 생성할 수 있도록 해줌
11/27 11 /27
객체의 생성 – 계속 객체를 생성하는 이유는 그 객체의 메소드를 호출하여 뭔가 유용한 일을 하기 위함 보통 통 한번 사용하고 사용하 버리는 버리 것이 아니라 여러 번 사용할 필요성이 필 성이 있기 때문에 객체를 생성하는 것임 따라서 생성한 객체를 유지하는 객체 변수 변수(object 변수( j variable)가 )가 필 필요함 함 보다 정확하게 말하면 객체 참조변수 참조변수(object reference variable)가 필요함 예) Rectangle box = new Rectangle(5, Rectangle(5 10 10, 20 20, 30); 비고.. int value; 비고 객체 참조변수는 초기화되어야 사용할 수 있음 예) Rectangle box = new Rectangle(10,10,100,100); 비고.. 자바의 모든 변수는 기본적으로 초기화되어야 함 비고 여러 개의 객체 참조변수가 같은 객체를 참조할 수 있음 예) Rectangle r = box;
12/27 12 /27
객체 변수 객체 참조변수는 객체를 저장하는 곳: 보다 정확하게 말하면 객체가 저장되어 있는 주소를 저장하는 곳 비고.. C언어에서 포인터와 같은 개념이지만 포인터와 달리 산술 비고 연산이 가능하지 않음 자바의 모든 타입은 크게 원시 타입 타입(atomic type)과 주기억장치 참조 타입 타입(reference type)으로 분류됨 0 a
10
b
?
int a = 10; Rectangle b;
1
7 a
b
c
Rectangle c = new Rectangle(10,10,100,100);
10 10 100 100
7
13/27 13 /27
원시타입 vs. 참조타입 원시타입은 변수 이름이 저장되어 있는 값 자체를 의미하는 경우를 말하며, 참조타입은 변수 이름이 저장되어 있는 값 보다는 값이 저장 되어 있는 위치 정보를 의미하는 경우를 말함 제시된 8가지의 원시 타입의 자바에 총 8가지의 원시 타입이 있다. 변수를 제외하고는 자바에서 메 리 크기 메모리 기 값의 범위
사용되는 모든 든 변수는 참조 타입임
타입이름
값의 종류
boolean
true, false
1 byte
해당사항 없음
char
문자(유니코드)
2 bytes
모든 유니코드 문자
byte
정수
1 byte
short
정수
2 bytes
–32,768 32,767
int
정수
4 bytes
–2,147,483,648 2,147,483,648
long
정수
8 bytes
–9,223,372,036,854,775,808 9,223,372,036,854,775,807
float
부동소수점 수
4 bytes
3.4028234710+38 1.4023984610-45
double
부동소수점 수
8 bytes
1.7676931348623157010+308 4.9406564584124654410-324
–128 127
자바의 원시타입의 크기는 항상 고정되어 있다. 이유: 이식성, 기계와 독립성 14/27 14 /27
쓰레기 수집 new 연산자를 이용하여 객체를 생성한다는 것은 다른 말로 그 객체의 상태를 저장할 공간을 메모리에 할당한다는 것을 의미함 new 연산자를 이용하여 생성된 객체가 할당되는 메모리 공간을 쓰레기 수집가능 힙(garbage collectible heap)이라 함 예) R t l b 1 = new Rectangle(10, R t l (10 10 Rectangle box1 10, 100 100, 100) 100);
box1
box2
Rectangle box2 = new Rectangle(30, 20, 50, 50); Rectangle r = box2; box1 = box2; 힙에 할당된 공간을 가리키고 있는 참조변수가 더 이상 없으면 그 공간은 쓰레기가 되며, 이 공간들은 JVM에 의해 자동 자동으로 반납된다 반납된다. 이 과정을 쓰레기 레기 수집(garbage collection)이라 함 수집 쓰레기 수집 대상
비고. 객체 변수들은 메소드의 호출을 처리하기 위해 할당된 스택 프레임에 유지됨
r 쓰레기 수집가능 힙
15/27 15 /27
객체를 조작, 사용하기 객체는 생성한 다음에만 사용할 수 있음 (보통 new 연산자를 이용하여 생성함) 객체를 생성한 후에 아무것도 하지 않으면 우리가 원하는 것을 할 수 없음. 따라서 객체가 무언가 유용한 일을 하도록 해야 함 보통 객체에 정의된(그 클래스에 정의된) 메소드를 호출하여 어떤 일을 수행함 이 때 ‘.’ 연산자를 이용함 import java.awt.Rectangle; public class MoveTest{ public static void main(String[] p ( g[] args){ g ){ Rectangle box = new Rectangle(10, 10, 100, 100); box.translate(10,10); System.out.println(box); } // main } // class MoveTest
16/27 16 /27
자바 프로그램의 종류 독립 응용 프로그램 보통 C/C++에서는 윈도우와 콘솔 프로그램을 구분하지만 자바에서는 윈도우 관련 기능(그래픽 사용자 인터페이스)을 콘솔 위주의 프로그램에서도 비교적 쉽게 사용할 수 있음 애플릿: 웹 브라우저 내에서 실행되는 프로그램
17/27 17 /27
간단한 그래픽 응용 프로그램 그래픽 응용 프로그램은 프레임 윈도우라는 곳에 정보를 보여줌 자바에서 프레임 윈도우를 하나 생성하는 방법은 다음과 같음 import javax.swing.JFrame; public class SimpleFrame{ public static void main(String[] args){ JFrame frame = new JFrame(); f frame.setSize(300, tSi (300 400) 400); frame.setTitle("My First Frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); ( ); } // main } // SimpleFrame
18/27 18 /27
프레임에 그리기 프레임에 직접 그릴 수 없고 그리고 싶은 것에 해당하는 컴포넌트를 만들어 이것을 프레임에 추가해야 함 컴포넌트는 javax.swing.JComponent 클래스를 이용함 하지만 우리가 원하는 컴포넌트를 만들기 위해서는 JComponent를 상속받는 새 클래스를 만들어 주어야 함 import java.awt.Rectangle; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JComponent; public class RectangleComponent extends JComponent{ public void paintComponent(Graphics g){ Graphics2D g2 = (Graphics2D)g; Rectangle box = new Rectangle(5, 10, 20, 30); g2.draw(box); box.translate(15, 25); g2.draw(box); } // paintComponent } // RectangleComponent 19/27 19 /27
프레임에 컴포넌트 추가하기 import javax.swing.JFrame; public class SimpleFrame{ public static void main(String[] args){ JFrame frame = new JFrame(); frame.setSize(300, 400); frame.setTitle("My First Frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); RectangleComponent component = new RectangleComponent(); frame.add(component); frame.setVisible(true); } // main } // SimpleFrame
20/27 20 /27
자바의 좌표 시스템 +x
(0,0) (x, ( y)) +y
height width
자바에서 좌표는 pixel 단위를 주로 사용함 픽셀(pixel, picture element)이란 화면의 한 점을 말함 cf. 1024 760 (해상도) Java2D 도형 클래스는 int 타입의 pixel 단위를 사용하지 않고 float 타입의 pixel 단위를 사용함 큰 차이는 없지만 도형 계산의 정확도를 높일 수 있음
21/27 21 /27
RectangleComponent 윈도우 프로그래밍은 사건중심 프로그래밍이라 함 어떤 특정 사건(예: 마우스 클릭)이 발생하면 그 사건을 처리하는 메소드가 자동 호출되어 처리하는 형태를 말함 일반적으로 윈도우의 모든 요소는 처음 표시될 때, 다시 그려야 할 때, 크기가 변할 때 요소를 새롭게 그리라고 하는 사건이 발생됨 JComponent 객체들은 위와 같은 사건이 발생하였을 때 paintComponent 메소드가 자동으로 호출됨 윈도우에 무언가를 그릴 때 색깔, 굵기 등의 정보가 필요함 자바에서는 Graphics 클래스의 객체가 이와 같은 그래픽 상태를 유지하고 있으며, 있으며 이 객체는 자신의 상태를 이용하여 다양한 도형을 그려주는 메소드를 가지고 있음 향상된 기능을 사용하기 위해 Graphics 객체 대신에 보통 G Graphics2D hi 2D 객체를 사용함 예) Graphics2D g2 = (Graphics2D)g; 강제 타입 변환 사용 (나중에 자세히 설명) 22/27 22 /27
색깔 변경 및 채우기 java.awt 패키지의 Color 클래스의 객체를 이용하여 색깔 정보를 나타낼 수 있음. 이 클래스는 RGB 모델을 이용하여 색깔을 나타냄 예) Color blue = new Color(0.0F, 0.0F, 1.0F); Color red = new Color(255, 0, 0);
주 색깔의 정도는 0.0부터 1.0까지 float 값으로 지정하거나 0부터 255까지 정수로 지정할 수 있음 편의를 위해 Color 클래스는 자주 사용하는 다양한 색깔을 미리 만들 어 제공하고 있음 예) Color.RED, Color.BLUE, … 정의한 색깔로 모양을 그리고 싶으면 Graphics G hi 클래스의 setColor tC l 메소드를 이용함. 주어진 색깔로 도형의 내부를 채워 그리고 싶으면 draw 대신에 fill 메소드를 이용함 예) g2.setColor(blue); g2.draw(box);
g2.setColor(red); g2.fill(box);
23/27 23 /27
다른 도형 그리기 – 타원 타원은 java.awt.geom 패키지에서 제공하는 Ellipse2D.Double 또는 Ellipse2D.Float를 사용하여 나타냄 예) Ellipse2D.Double egg = new Ellipse2D.Double(5, 10, 15, 20); 문법.. Ellipse2D.Double(double x, double y, double width, double height) 문법 타원은 그 타원을 포함하는 직사각형의 기준점, 기준점 폭, 폭 높이를 주어 생성함 Ellipse2D.Double 객체는 Rectangle 객체와 마찬가지로 Graphics2D 객체의 draw 메소드를 이용하여 그림 예2.20) g2.draw(egg); 원은 폭과 높이를 같도록 해주면 됨 (x,y) height width dt 24/27 24 /27
다른 도형 그리기 – 선 선은 java.awt.geom 패키지에서 제공하는 Line2D.Double 또는 Line2D.Float를 사용하여 나타냄 예) Line2D.Double line = new Line2D.Double(10, 10, 100, 100);
문법. Line2D.Double(double x1, double y1, double x2, double y2) 문법. 선은 시작점과 끝점을 주어 생성함 예) Point2D.Double 또는 Point2D.Float 객체를 이용하여 좌표를 유지하는 객체를 이용하여 생성할 수 있음 Point2D.Double from = new Point2D.Double(10, 10); Point2D.Double to = new Point2D.Double(100, 100); Line2D.Double line = new Line2D.Double(from, to);
문법. Line2D.Double(Point2D.Double p1, Point2D.Double p2) 문법. 선도 Graphics2D 객체의 draw 메소드를 이용하여 그림 예) g2.draw(line); 선의 굵기는 g2.setStroke(new g2 setStroke(new BasicStroke(4.0F))와 BasicStroke(4 0F))와 같이 변경 가능 25/27 25 /27
폰트 어떤 문자열을 화면에 출력하고 싶으면 Graphics2D 클래스의 drawString 메소드를 사용함 예) g2.drawString("Applet", 50, 100); 문자열이 출력되는 기본위치의 좌표를 주어야 함
Applet pp
기본선(baseline)
기본위치(base point)
출력되는 문자열의 폰트는 java.awt 패키지에서 제공하는 Font 클래스를 색깔을 변경할 때와 같은 방법으로 사용함 Font 객체를 생성하기 위해서는 1) 폰트 이름, 2) 폰트 스타일 ((Font.PLAIN,, Font.BOLD,, Font.ITALIC,, Font.ITALIC+Font.BOLD), ), 폰트의 크기를 제공해야 함
26/27 26 /27
폰트 – 계속 예) Font 객체 생성 Font myfont = new Font("Arial", Font.BOLD, 48); 폰트 이름은 Serif, SansSerif, Monospaced, Dialog, DialogInput 과 같은 논리적 이름을 사용하거나 Arial, Times New Roman, 바탕 굴림과 같은 실제 폰트 이름을 사용할 수 있음 바탕, 폰트의 크기는 수치로 나타냄. 단위는 point이며, 1point = 1/72inch 이다. 출력되는 문자열의 폰트를 변경할 때에는 Graphics2D 클래스의 setFont 메소드를 이용함 예) g2.setFont(myfont); g2 setFont(myfont);
27/27 27 /27
©copyright 2010
자바프로그래밍(CPA240) NOTE 03
클래스
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 자바에서 객체를 생성할 때 보통 new 연산자를 사용함. new는 생성자 호출을 피연산자로 가지며, 그 결과로 쓰레기 수집 가능 힙에 새 객체의 상태를 저장하기 위한 공간을 확보함 다른 언어와 달리 확보한 주기억장치의 공간을 프로그래머가 직접 반납하지 않아 않아도 됨 ((쓰레기 레기 수집) 자바에서 모든 타입은 크게 원시타입과 참조타입으로 구분됨 자바는 총 8개의 원시타입을 제공함 원시타입의 변수는 변수 자체에 값을 유지하지만, 참조타입의 변수는 값 대신에 그 값이 저장되어 있는 위치를 유지함
2/28
교육목표 클래스 구현 절차 이해 간단한 메소드 구현 방법 이해 멤버 변수와 지역 변수를 접근하는 방법 이해 주석의 중요성 이해
객체가 어떤 행위를 할 수 있으며, 어떤 상태를 유지하는지 설명하여 주는 것이 클래스이다.
3/28
클래스 정의하기 public class Greeter{ public String sayHello(){ String message = "Hello, World!"; return message; } // sayHello } // class Greeter
클래스는 객체가 어떤 정보를 가지고 있고, 어떤 행위들을 할 수 있는지 정의해주는 틀임 Dog
클래스는 멤버 변수(객체의 상태)와 메소드(객체의 행위)를 포함함 메소드의 정의는 다음과 같은 요소로 구성됨 접 권한 지시자(예: 접근 지시자 예 public) 반환 타입(예: String) 메소드의 이름(예: sayHello) 파라미터 목록 메소드의 몸체: 중괄호 내에 작성한다.
breed name bark()
4/28
클래스 정의하기 – 계속 메소드의 접근 권한 지시자는 보통 public을 사용함 메소드는 객체들 간에 상호작용을 하는 수단이므로 외부에서 이 메소드를 호출할 수 있도록 public으로 bli 으로 선언함 반환 타입 어떤 값도 반환하지 않을 경우에는 반환 타입을 void로 사용함 매개변수목록 각 매개변수는 일반 변수와 마찬가지로 타입과 이름을 가지며, 콤마( )로 구분됨 콤마(,)로 메소도의 몸체 메소드의 반환 타입이 void가 아니면 몸체에 하나 이상의 return문이 포함되어야 함 반환 타입이 void이어도 그 위치에서 메소드를 종료하기 위해 “return;”문을 이용할 수 있음 (C와 동일) return 문을 이용하여 메소드를 호출한 호출자에게 메소드의 결과 값을 반환해줄 수 있음 return이 이 반환하는 값의 타입은 메 메소드의 의 반환 타입과 같아야 함 5/28
return문 관련 오류 예)
public int f(void){ … return "haha"; // 오류 }
public int f(void){ … } // return 문이 없으면 오류
public int f(void){ … if(…) return 1; } 함수가 반환하는 값이 있는 경우 함수가 종료되는 모든 경우에 return 문이 있어야 함
public void f(void){ int n; … if(…) return; … return n; // 오류 } public void f(void){ int n; … if(…){ return; System.out.println(n); // 오류 } … } 위 println은 어떤 경우에도 실행되지 않음 (unreachable code)
6/28
클래스 정의하기 – 계속 메소드의 종류 생성자(constructor): 객체를 생성할 때 사용하는 메소드 생성자 메소드의 이름이 클래스 이름과 같음 정의할 때 반환 타입이 없음 접근자( 접근자(accessor): 접근자 ) 객체의 상태를 열람할 때 사용하는 메소드 다른 말로 observer라 함 수정자(mutator): 수정자( 수정자 ) 객체의 상태를 변경할 때 사용하는 메 메소드 다른 말로 transformer라 함 예외처리를 배운 이후에는 수정자의 반환 타입은 void가 되며, 접근자는 열람하는 데이터에 따라 void가 아닌 반환 타입을 가지게 됨
7/28
정의한 클래스 검사하기 파일 내에 유일한 public 클래스 내부에 main 메소드가 정의되어 있지 않으면 실행할 수 없음 보통 대부분의 클래스는 main 메소드를 가지고 있지 않음 다음과 같은 테스트 클래스를 만들어 Greeter 클래스를 검사할 수 있음 public class GreeterTest{ public static void main(String[] p ( g[] args){ g ){ Greeter worldGreeter = new Greeter(); System.out.println(worldGreeter.sayHello()); } // main } // GreeterTest G t T t
8/28
멤버 변수 객체는 자신의 상태를 저장하기 위해 멤버 변수를 사용함 각 객체마다 별도로 존재함 멤버 변수의 정의는 다음과 같은 요소로 구성됨 접근 권한 지시자(예: private) 변수의 타입(예: String) 변수의 이름(예: name) +의 피연산자가 문자열이면 문자열의
결합을 의미한다. 예) “Internet Internet Media”+ Media + " Engineering Engineering” “Internet Media Engineering”
public class Greeter{ private String name; public Greeter(String aName){ name = aName; } public String sayHello(){ String message = "Hello, " + name + "!"; return message; } // sayHello } // class Greeter
9/28
멤버 변수 – 계속 멤버 변수의 접근 권한 지시자 멤버 변수의 접근 권한 지시자는 보통 private을 사용한다. private 변수는 외부에서는 직접 접근할 수 없다. 예) Greeter g = new Greeter(); g.name = "Sangjin"; 따라서 private i t 변수는 public bli 메소드를 통해 접근해야 함 만약 name 멤버 변수가 public 멤버 변수이면 g.name = "Sangjin"과 g gj 과 같은 방법을 직접 접근할 수 있음 데이터의 형태를 숨기고 데이터의 접근을 위한 메소드를 제공하는 과정을 캡슐화 캡슐화(encapsulation)라 함 다시 말하면, 말하면 데이터와 행위를 하나로 결합 결합하고 데이터의 구현과 결합하고, 행위의 구현을 사용자로부터 숨기는 것을 말함 캡슐화는 정해지지 않은 방법 방법으로 데이터가 조작되어 작되어 객체가 잘못 된 상태로 진입하는 것을 방지해줌 즉, 프로그램의 오류를 줄여주는 효과가 있음
10/28 10 /28
private vs. public 멤버(변수, 함수)가 private으로 선언되어 있으면 외부에서 절대 접근할 수 없음. 반대로 public으로 선언되어 있으면 외부에서 직접 접근할 수 있음 멤버 변수가 private으로 선언되어 있으면 직접 접근할 수 없으므로 그것의 것의 값을 외부 외부로부터 부터 숨기는 것이 목적이라 목적이라고 생각할 수 있음 있음. 즉, 보안에서 비밀성과 같은 개념으로 오해할 수 있음 private으로 선언하는 것은 멤버 변수의 현재 값을 숨기고자 하는 것이 아니라 변수의 값을 임의로 조작하지 못하도록 하기 위함임 이것은 객체가 잘못된 상태로 진입하는 것을 방지하여 프로그램의 오류가 발생하는 것을 줄이기 위함임
11/28 11 /28
생성자(constructor) 생성자는 객체를 생성할 때 사용하는 메소드를 말함 객체를 생성할 때 그것의 멤버 변수들을 초기화하는 절차를 포함함 생성자의 이름은 클래스의 이름과 같음 대부분의 생성자도 public 접근 권한 지시자를 사용함 World “World” public class GreeterTest{ public static void main(String[] args){ Greeter worldGreeter = new Greeter("World"); Greeter johnGreeter = new Greeter("John"); System.out.println(worldGreeter.sayHello()); System.out.println(johnGreeter.sayHello()); } // main } // class GreeterTest
WorldGreeter
“John” JohnGreeter
12/28 12 /28
클래스 설계하기 은행 계좌 행위 분석 입금(deposit): 입력(금액), 출력(성공여부) 인출(withdraw): 입력(금액), 출력(성공여부) 잔액조회( tB l 잔액조회(getBalance): ) 입력(없음), 입력(없음) 출력(잔액) 이체 등 데이터 분석 잔액(balance): int 성명 계좌번호 등
13/28 13 /28
생성자 예) BankAccount SangjinsChecking = new BankAccount(); 이 경우 잔액이 0인 새 은행 계좌를 개설 예) 잔액이 10,000원인 새 은행 계좌를 개설하고 싶으면… BankAccount SangjinsChecking = new BankAccount(10000);
두 개의 생성자가 필요함 같은 이름의 두 개의 메소드를 정의할 수 있음 이것을 오버로딩 오버로딩(overloading)이라 함 오버로딩하기 위해서는 메소드의 서명이 달라야 함 메소드의 서명이란 메소드의 이름과 매개변수 목록으로 구성됨 반환 타입은 서명에 포함되지 않음 따라서 반환 타입만 다른 두 메소드를 같이 정의할 수 없음 컴파일러 입장에서 생각해보면 어떤 경우에만 오버로딩이 가능한지 쉽게 이해할 수 있음 (번역할 때 모호함이 없어야 함)
14/28 14 /28
생성자 – 계속 예) 다음과 같은 두 메소드는 같은 클래스에 함께 정의할 수 없음 int func() {} double func() {} 매개변수가 없는 생성자를 기본 생성자 생성자(default constructor)라 함 주의.. 아무런 생성자를 정의하지 않으면 자동으로 기본 생성자가 주의 제공됨 하지만 기본 생성자를 정의하지 않 않고 다른 생성자만 정의한 경우에는 기본 생성자를 제공해주지 않음 public class A{ private int val; void set(int n) { val = n; } int get() { return val; } } // A
public class B{ private int val; public B(int n) { val = n; } void set(int n) { val = n; } int get() { return val; } } // B void f(){ A a = new A(); // ok B() // error B b = new B(); } // f 15/28 15 /28
생성자 – 계속 String 클래스는 유일하게 new 연산자를 사용하지 않고 객체를 생성 할 수 있음 예) String St i name = "Sangjin "S ji Kim"; Ki " 다음과 같이 생성할 수도 있지만 대부분 new를 사용하지 않음 String g name = new String("Sangjin g( gj Kim"); ) 생성자는 객체가 처음 생성될 때에만 호출할 수 있음 즉, new 연산자을 통해서만 호출할 수 있음 따라서 다음은 오류이다. 오류이다 즉, 즉 일반 메소드처럼 호출할 수 없음 BankAccount sangjinsAccount = new BankAccount(); sangjinsAccount.deposit(10000); ji A t B kA t() sangjinsAccount.BankAccount();
그러나 new를 이용하여 객체를 다시 생성하는 것은 가능함 BankAccount sangjinsAccount = new BankAccount(); sangjinsAccount.deposit(10000); garbage sangjinsAccount = new BankAccount();
16/28 16 /28
BankAccount 클래스 사용의 예 BankAccount sangjinsAccount = new BankAccount(10000); BankAccount jinheesAccount = new BankAccount(5500); int transferAmount = 500; sangjinsAccount.withdraw(transferAmount); jinheesAccount deposit(transferAmount); jinheesAccount.deposit(transferAmount);
내부적으로 withdraw, deposit이 어떻게 이루어지는지를 사용하는 측에서는 알 필요가 없음
17/28 17 /28
클래스 구현하기 클래스는 보통 다음과 같이 public class 클래스이름{ 멤버변수 생성자 메소드 } 또는 는 다음과 같이 작성함 public class 클래스이름{ 생성자 메소드 멤버변수 }
public class BankAccount{ private int balance; public BankAccount(){ balance = 0; } // BankAccount() public BankAccount(int amount){ if(amount<0) balance = 0; else balance = amount; } // BankAccount(int) public boolean deposit(int amount){ if(amount<0) return false; balance = balance + amount; return true; } // deposit public boolean withdraw(int amount){ if(amount<0) return false; int tmp = balance – amount; if(tmp<0) return false; else balance = tmp; return true; } // withdraw } // class BankAccount
18/28 18 /28
javadoc을 이용한 주석달기 javadoc은 java 파일을 처리하여 HTML 문서를 만들어 줌 javadoc에서는 /** … */ 형태의 주석을 사용함 첫 번째 문장을 무조건 첫 라인으로 복사함 따라서 첫 문장은 메소드의 용도에 대한 요약 설명을 함 @ @param, @ t @return, @th @throws와 와 같은 태그를 이용하여 매개변수와 반환 값을 설명함 @param 변수명 변수설명 @p @return 반환값 설명 javadoc의 실행 javadoc BankAccount.java eclipse에서는 project 메뉴에서 generate javadoc 선택
19/28 19 /28
변수의 종류 어떤 종류의 변수이던지 값을 유지한다는 측면에서는 같지만 변수의 가시영역과 생명주기 측면에서는 차이가 있음 변수의 종류 멤버 변수: balance 각 객체는 자신만의 멤버 변수를 가짐 예외. 같은 종류의 객체들이 모두 공유하는 멤버 변수도 있음 생명주기(lifetime): 생명주기( 생명주기 ((lifetime): ) 객체의 생명주기와 같음 가시영역(scope): 가시영역 (scope): 클래스 내에서 어디든지 사용할 수 있음 초기화하지 않으면 자동으로 초기화됨 문자타입을 포함 수를 나타내는 변수는 0, boolean 타입은 false, 참조변수는 null로 초기화
20/28 20 /28
변수의 종류 – 계속 지역 변수: withdraw 메소드의 tmp 생명주기:: 메소드의 생명주기와 같음 생명주기 가시영역:: 선언된 블록 내에서 선언한 위치 이후부터 사용할 수 가시영역 있음 모든 지역변수는 반드시 초기화해야 함. 초기화하지 않고 사용하면 컴파일러는 오류 메시지를 줌 멤버 변수 이름과 같은 이름의 지역 변수를 정의하면 멤버 변수를 접근할 수 없게 됨 비고.. Java에는 C와 달리 전역 변수가 없음 비고 비고.. Java는 C와 달리 지역 변수 선언 위치에 대한 제한이 없음 비고 매개 변수: deposit 메소드의 amount 생명주기:: 메 생명주기 메소드의 의 생명주기와 같음 가시영역:: 메소드의 끝까지 사용할 수 있음 가시영역 전달받은 인수값으로 초기화됨 21/28 21 /28
가시영역의 예 class Test{{ … private int memberVar; … public bli void id memberFunc(int b F (i t paramVar){ V ){ int localVar; … { int blockVar; … } … } … }
변수 종류에 따른 가시영역
class Test{ private int x; … public void func(){ int x; // ok int n; … { int n; // error int p; … } { int p; // ok … } … } … }
22/28 22 /28
메소드의 숨겨진 인수 public void deposit(int amount){ balance = balance + amount; } // deposit
위 메소드에서 balance의 정확한 의미는? sangjinsAccount.deposit(500)을 수행하면 이 때의 balance는 sangjinsAccount 객체의 balance를 의미함 따라서 이 경우 balance는 sangjinsAccount.balance를 의미함 컴파일러는 이것을 알기 위해 매개변수 목록에 직접 나열되어 있지 않는 숨겨져 있는 매개변수를 사용함 직접 나열되어 있는 매개변수를 explicit parameter라 하고, 숨겨져 있는 매개변수를 implicit i li i parameter라 라함
23/28 23 /28
메소드의 숨겨진 인수 – 계속 sangjinsAccount.deposit(500)와 같은 호출을 deposit(sangjinsAccount, 500)과 같은 형태로 바꾸어 번역하며, d deposit it 메소드의 정의는 다음과 같이 바꾸어 번역함 public void deposit(BankAccount this, int amount){ this balance = this.balance this.balance this balance + amount; } // deposit
즉, 숨겨진 인수는 컴파일러를 위한 요소이다. 하지만 이 요소를 프로그래머도 필요하면 사용할 수 있음 숨겨진 인수는 this 키워드를 이용하여 접근할 수 있음 예외.. static 메소드는 숨겨진 인수가 없음 예외
24/28 24 /28
메소드의 숨겨진 인수 – 계속 public BankAccount(int balance){ balance = balance; // ? } // BankAccount(int)
위 예에서 파라미터 변수 수 balance와 와 인스턴스 변수 수 balance의 의이 이름이 이 같음. 이 경우에는 가시영역이 작은 파라미터 변수가 인스턴스 변수를 사용할 수 없음 이것을 극복하기 위해서도 아래 예처럼 컴파일러가 사용하는 숨겨져 있는 인수 this를 사용할 수 있음 여기서 this.balance는 인스턴스 변수를 나타내며, balance는 파라미터 변수를 변 나타냄 public BankAccount(int balance){ this.balance = balance; // ok } // BankAccount(int)
25/28 25 /28
생성자에서 다른 생성자를 호출하기 public BankAccount(){ this(0); } // BankAccount() public BankAccount(int initialBalance){ balance = initialBalance; } // BankAccount(int)
위 예처럼 this 키워드를 이용하여 하나의 생성자에서 다른 생성자를 호출할 수 있음 (코드 중복 줄일 수 있는 효과적인 방법) 제한.. 이와 같이 this를 이용한 생성자의 호출은 반드시 생성자의 제한 첫 문장 문장이어야 함 주의.. 일반 메소드에서는 this를 이용한 이와 같은 형태의 호출은 주의 가능하지 않음 비고.. 위 예에서 balance를 생성자에서 초기화하지 않으면 자동으 비고 로 0으로 초기화됨. 하지만 가독성을 위해 자동 초기화를 사용할 수 있어도 사용하지 않는 것이 바람직함 26/28 26 /28
CashRegister 금전등록기 모델링 고객이 여러 개의 상품을 구입할 수 있음 구입한 각 가격의 상품을 입력하고, 고객으로부터 돈을 받아 거스름돈을 주어야 함 public class CashRegisterTest{ public static void main(String[] args){ CashRegister register = new CashRegister(); register.recordPurchase(1000); i t dP h (1000) register.recordPurchase(550); register.enterPayment(5000); int change = register.giveChange(); System.out.printf("거스름: %d%n", change); } // main() } // CashRegisterTest
27/28 27 /28
SimpleFrame 확장 얼굴 그리기 public class Face{ private int xLeft; private int yTop; public Face(int x, x int y){ xLeft = x; yTop = y; } // Face(int, int) public void draw(Graphics2D g2){ // 완성하시오. } // draw(Graphics2D) public class FaceComponent extends JComponent { } // Face public void paintComponent(Graphics g){ Graphics2D g2 = (Graphics2D)g; Face face1 = new Face(10,10); Face face2 f = new Face(100,100); ( ) face1.draw(g2); face2.draw(g2); } // paintComponent() } // FaceComponent 28/28 28 /28
©copyright 2010
자바프로그래밍(CPA240) NOTE 04
기본 데이터 타입
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 객체의 멤버변수는 private, 메소드는 public으로 보통 선언함 이유.. 멤버 변수가 private으로 선언되어 있으면 직접 접근할 수 이유 없으므로 임의로 조작할 수가 없음. 따라서 객체가 잘못된 상태로 진입하여 오류가 발생할 확률을 줄여줌 생성자 메소드는 객체를 생성할 때 사용되는 메소드로서 객체의 상태를 초기화해주는 역할을 함 자바에서 크게 변수는 지역변수, 파라미터 변수, 멤버변수로 분류할 수 있으며, 있으며 변수의 이해에 있어 가장 중요한 요소 중 하나는 변수의 수명 수명(lifetime)과 가시영역 가시영역(scope)임 일반 멤버함수는 숨겨진 인수를 가지며, this라는 라는 키워 키워드를 를 통해 이 인수를 접근할 수 있음 이 인수는 호출된 멤버함수가 어떤 객체를 조작해야 하는지 나타내 줌 이 인수는 컴파일러에 의해 주로 사용되지만, 프로그래머도 필요에 따라 사용할 수 있음 2/41
교육목표 int와 double 타입의 제한 인식 자바에서 산술 표현식 String 타입을 이용한 문자열 처리 char 타입 자바에서 입출력 대화창을 이용한 입출력 콘솔을 이용한 입출력
자바의 기본 타입은 그 크기가 항상 고정되어 있음 자바는 타입에 대해 매우 엄격함 즉, 자바에서 정보손실이 일어날 수 있는 대입은 기본적으로 오류임
3/41
int 타입 int 타입: 4 바이트 범위: –2,147,483,648 +2,147,483,647 위 두 값을 프로그램 내에서 사용하고 싶으면 다음을 이용할 수 있음 Integer.MIN_VALUE, Integer.MAX_VALUE I t Integer는 는 java.lang j l 패키지에 포함되어 있는 클래스 상수 표현 16진수로 상수를 표현할 때에는 0x 접두사를 사용하고, 사용하고 8진수로 상수를 표현할 때에는 0 접두사를 사용함 비고.. 자바의 모든 원시 타입의 크기는 자바가 수행되는 기계와 비고 상관없이 항상 고정되어 있음
4/41
오버플로우 주어진 타입에 저장할 수 있는 범위를 벗어난 경우 발생하는 현상을 오버플로우(overflow)라 함 오버플로우 예) int n = 1000000; System.out.println(n*n);
// -727379968
오버플로우가 발생하여도 실행시간 오류가 발생하지 않음 따라서 프로그래밍할 래밍할 때 오버플로우가 버플 우가 발생하지 않 않도록 록 주의해야 함 예) byte n = 200; byte n = 100; n = n*n;
// 컴파일 error
byte n = 100; System.out.println(n*n);
// 10000
// 컴파일 error
범위 벗어난 대입에 대해서는 컴파일 오류로 처리해줌
5/41
double 타입 double 타입: 8 바이트 범위: 1.7676931348623157010+308 4.9406564584124654410-324 정확도: 15자리 정도의 유효숫자만 표현 가능 예) double p = 3E16; double d = p – 5; System.out.println(p); System.out.println(d); System.out.println(p – d);
// 30,000,000,000,000,000 // 29,999,999,999,999,995 // 3.0E16 // 2.9999999999999996E16 // 4.0 (?)
Java 5부터는 16진수로 부동소수점 상수를 과학적 표기법으로 나타낼 수 있다. 단, e, E 대신에 p, P를 사용한다. 예) 10진수 0.125는 0 125는 0x1.0p–3으로 0x1 0p 3으로 나타낼 수 있다. 있다 0x1.0p–3의 의미 1.022–3
6/41
수의 범위와 정확도 부동소수점(floating point) 표현방식
부호(1비트) 지수(8비트)
가수(23비트)
예) 8비트: 1/2/5 3.7510 = 11.112 = 1.111 21 0 10 11100 위 예서 알 수 있듯이 가수 부분으로 나타낼 수 있는 수는 총 25이며, 지수 부분이 나타낼 수 있는 값의 범위는 –1부터 1부터 2임 양수, 음수, 지수를 고려하여도 전체적으로 8비트를 사용하므로 나타낼 수 있는 수는 총 28임 따라서 부동소수점으로 나타낼 수 있는 실수는 매우 제한적임 round-off 오류 –1 2 보다 작은 수는 표현할 수 없음 (underflow) overflow
overflow 0
7/41
수의 범위와 정확도 – 계속 우리는 보통 10진수를 사용하지만 컴퓨터 2진수를 사용함 10진수를 2진수로 바꾸는 방법
2
100 50
0
25
0
12
1
6
0
3
0
1
1
10010 = 11001002 100
50 2 0 (25 2 0) 2 0 25 22 0 21 0 20 (12 2 1) 22 0 21 0 20 12 23 1 22 0 21 0 20
(6 2 0) 23 1 22 0 21 0 20
8/41
수의 범위와 정확도 – 계속 소수부분을 2진수로 표현하기 예) 0.35 = 0.01011010110… 10 예) 0.7510 = 0.112 0.75 2 = 1.5 0.5 2 = 1.0 = 2-1 + 2-2 = 0.5 05+0 0.25 25
예) round-off 오류 double f = 4.35; System.out.println(100*f);
0.35 2 0.7 2 0.4 2 0.8 2 0.6 2 0.2 022
= 0.7 = 1.4 = 0.8 = 1.6 = 1.2 = 0.4 04
비고 10진수에서도 1/3은 비고. 표현할 수 없음
= 2-2 + 2-4 + 2-5 + … = 0.25 + 0.0625 + 0.03125 + … = 0.34375 + …
// 434.99999999999994
9/41
수의 범위와 정확도 – 계속 예) double w = x * y / z; 위 식은 x * y 부분 때문에 최종 값은 overflow가 발생하지 않는 값인 경우에도 발생할 수 있음 자바에서 double은 64비트로 표현됨 CPU들은 이런 문제 때문에 80비트 부동소수점 레지스터를 사용하여 중간 결과를 80비트 레지스터에 유지한 다음 그 결과에서 z를 나눈 후 다시 이것을 64비트로 절삭함. 하지만 이 결과는 계속 64비트 레지스터만 사용하는 경우와 결과가 다를 수 있음 결과가 다르면 자바의 목표 중 하나인 호환성에 위배됨 따라서 초기 JVM은 항상 중간 결과도 64비트로 절삭하는 방법을 선택하여 어느 컴퓨터나 동일한 결과를 얻도록 하였음 하지만 이렇게 하면 정확성이 떨어지기 때문에 중간 결과 값을 기본적으로는 절삭하지 않음 중간 결과도 절삭하기를 원하면 strictfp 키워드를 사용할 수 있음 예) public static strictfp void main(String[] args) {} 이 main에 있는 모든 부동소수점 연산은 중간 결과를 절삭한다. 10/41 10 /41
int, double 외의 수 타입 long: 정수 타입 (8 바이트) 범위: –9,223,372,036,854,775,808 9,223,372,036,854,775,807 long 타입의 상수는 L 접미사를 사용함 예) long price = 300000000000000L; short: 정수 타입 (2 바이트) 범위: –32,768 32,767 byte: 정수 타입 (1 바이트) float f; f = 25.6; 25 6; // error 범위 –128 범위: 128 127 f = 25.6F; // ok float: 부동소수점 타입 (4 바이트) 정확도와 정확 와 범위가 double의 의 대략 반 정 정도 정확도가 떨어지므로 중요한 계산에 사용되기 어려움 float 타입 상수는 F 접미사를 사용한다. 예) 3.402F 접미사가 없는 부동소수점 상수는 double 타입으로 고려됨
11/41 11 /41
BigInteger, BigDecimal java.math 패키지에서 제공하는 BigInteger와 BigDecimal 클래스는 원시타입 int와 double과 달리 범위나 정확도에 대한 제한이 없음 그러나 계산에 소요되는 시간은 int와 double에 비교해서 느림 BigInteger와 BigDecimal은 +, –, *, / 연산 대신에 add, subtract, multiply divide 메소드를 이용하여 사칙 연산을 수행함 multiply, 예)
BigDecimal p1 = new BigDecimal(3E16); BigDecimal p2 = new BigDecimal(5); BigDecimal p3 = p1.subtract(p2); // BigDecimal p3 = p1.subtract(BigDecimal.valueOf(5)); BigDecimal p4 = p1.subtract(p3); System out println(p4); // 5 System.out.println(p4); valueOf al eOf 메소드는 일반 수를
BigDecimal d = new BigDecimal("4.35"); // BigDecimal d = new BigDecimal(4.35); BigDecimal e = new BigDecimal(100); BigDecimal f = d.multiply(e); System.out.println(f); // 435.00
BigDecimal 수로 만들어 줌 (객체를 생성하는 또 다른 방법)
12/41 12 /41
대입 연산자 대입 연산자(assignment operator) variable = expression 모든 변수이름을 할당 연산자 왼쪽에 사용할 수 있는 것은 아님 예) 상수 변수 (다음 슬라이드) 표현식의 결과값의 타입이 변수 타입으로 자동 변환이 가능하지 않으면 컴파일 시간 오류가 발생함 예) int a = "babo";; 할당 연산자의 결합순서는 오른쪽에서 왼쪽임 a = b = c; 이면 b = c를 먼저 평가함
13/41 13 /41
타입 변환 자바에서는 타입 검사를 강화하였음 예) 자동변환 int n = 2; double f = n;
문법오류 double f = 2.0; int n = f; // error
자바에서는 서로 다른 타입은 일반적으로 대입할 수 없음 다음 순서 상에 앞에 있는 타입을 뒤 타입에 대입할 경우에는 예외적으로 가능하며, 이 때에는 자동으로 타입 변환이 일어남 byte short int long float double 하지만 반대 방향으로 대입될 경우에는 C/C++와 달리 자바에서는 오류임 따라서 자바에서는 반대 방향의 대입이 필요할 경우에는 강제 타입 변환을 해주어야 함 이런 타입 검사의 강화는 실수를 줄여주는 효과가 있음 14/41 14 /41
타입 변환 – 계속 타입 변환 문법 (typeName) expression 다음 두 문장은 결과가 다름 예) double f = 13.75; int n = 0; n = (int)(f * 10); n = (int)f * 10;
double f = 2.5; 2 5; int n = 0; n = (int)f;
// 137 // 130
반올림하고 싶을 경우에는 Math M th 클래스의 round d 메소드를 사용함 예) double f = 13.75; int n = 0; n = (int)f; n = (int)Math.round(f);
// 13 // 14
15/41 15 /41
대입 연산자 – 계속 복합 할당 연산자: +=, –= 등 E1 op= E2 E1 = E1 op (E2) 평가방법: E1 = (T)(E1 op (E2)) 예) int x = 3; x += 4.6; x += 4.6의 4 6의 평가 순서 단계 1. x+4.6을 평가, 이 때 x는 double 타입으로 자동 변환 되어 평가됨 단계 2. 결과를 int로 타입 변환함 단계 3. 최종 결과를 x에 대입함 int x = 20; int y = 5; 예) int n = 3; n += 2.5; n = 2.5;;
// OK // error
x = x / y + 5;
// x = 9
int x = 20;; int y = 5; x /= y + 5;
// x = 2
16/41 16 /41
상수 상수 변수: 한번 값이 할당되면 더 이상 변경할 수 없는 변수 상수 변수의 선언은 final 키워드를 이용함 예) final double PI = 3.142; 상수 변수의 이름은 주로 모두 대문자로 표현함 이런 상수들은 클래스의 객체마다 필요 없다. 없다 따라서 static t ti 변수로 선언하여 공간 낭비를 줄이는 것이 바람직함 예) static final double E = 2.718;; static 변수는 클래스 변수라 하며, 각 객체마다 중복되어 공간이 확보되지 않음 상수들은 또한 보통 public으로 bli 으로 선언함. 선언함 public으로 bli 으로 선언하면 외부에서 직접 접근할 수 있지만 값이 변경될 수 없는 상수이므로 외부에서 조작할 수 없어 안전에 문제가 없음 예) public static final double E = 2.718;
17/41 17 /41
static 멤버변수 public class A{ private int n; private final int MAX = 10; f }
public class A{ private int n; public static final int MAX = 10; f }
public p blic class Test{ public static void main(String[] args){ A a1 = new A(); A a2 = new A(); (); } // main } // class Test
public p blic class Test{ public static void main(String[] args){ A a1 = new A(); A a2 = new A(); (); } // main } // class Test a1
a2
a1
a2 A MAX
n
n
MAX
MAX
n
n
18/41 18 /41
static 멤버변수 – 계속 static 멤버변수는 각 객체마다 중복되지 않음 따라서 객체변수를 이용하여 접근할 수도 있고, 클래스 이름을 이용하여 접근할 수 있음 (보통 클래스 이름으로 접근하는 것이 좋음) 또한 객체를 생성하지 않고 접근할 수 있음 예) public class A{ private int n; public static final int MAX = 10; }
public class Test{ public static void main(String[] args){ System.out.println(A.MAX); A a1 = new A(); A a2 = new A(); () System.out.println(a1.MAX); } // main } // class Test
19/41 19 /41
상수 – 계속 Programming Tip. 매직 수(magic number)를 number)를 사용하지 마라 마라. (매직 수란 아무런 설명 없이 등장하는 수치 상수이다.) 프로그램의 가독성 저해 C 언어에서 #define을 이용하여 선언한 상수와 차이점 자바의 상수 변수는 값을 변경할 수 없는 변수이므로 타입 검사가 가능하지만 #define은 타입 검사가 가능하지 않음 #define과 달리 공간이 필요하다. #define은 전처리 과정에서 대체된 후에 번역됨
20/41 20 /41
산술과 수학 함수 이항 산술 연산의 두 피연산자의 타입이 다르면 하나의 타입이 다른 타입으로 자동 변환됨. 이 때 정보 손실이 없는 쪽으로 변환됨 나머지 연산(%)에서 두 피연산자는 모두 정수이어야 함 피연산자 중 하나가 음수일 경우에는 (a / b) * b + a % b == a가 되도록 a % b 값이 결정됨 예) (–14/3)*3 + ? == –12 + ? == –14, –14 % 3 –2 예) (14/–3)* –3 + ? == 12 + ? == 14, , 14 % –3 2, 나눗셈 연산(/)에서 두 피연산자가 모두 정수이면 정수 나눗셈을 함 정수 나눗셈은 결과가 정수이며, 대응되는 실수 나눗셈에서 소수 부분을 무시한 값이 됨 sqrt, pow, sin, cos, tan와 같은 자주 사용하는 수학 함수는 java.lang 패키지에 정의되어 있는 Math 클래스에 정의되어 있음 또 Math.PI, Math.E와 같은 자주 사용하는 상수도 Math 클래스에 정의되어 있음
21/41 21 /41
산술과 수학 함수 – 계속 Programming Tip. 공통적으로 등장하는 코드는 별도로 분리하는 것이 좋음 예) x1 = (–b + Math.sqrt(b * b – 4 * a * c)) / (2 * a); x2 = (–b – Math.sqrt(b * b – 4 * a * c)) / (2 * a);
double root = Math.sqrt(b * b – 4 * a * c); x1 = (–b ( + root)) / (2 ( * a); ) x2 = (–b – root) / (2 * a);
두 가지 이유 1. 계산량 감소 2 철자오류 감소 2. 이 방법의 한 가지 단점: 추가 공간이 필요하다.
코드의 중복을 최대한 줄이는 것이 유지보수하기에 용이함 자주 반복되는 일련의 문장은 함수로 구현하는 것을 생각해 볼 수 있음
22/41 22 /41
String 타입 String 타입은 java.lang 패키지에서 제공하는 클래스 원시 타입이 아니고 참조 타입임 new 연산자를 사용하지 않고 생성함 (매우 예외적인 것) 문자열을 구성하는 문자의 개수 예) String message = "Hello"; Hello ; int n = message.length(); // 5 문자열 결합 연산자 + 예) St i message = "Hello String "H ll " + "World!"; "W ld!" // "Hello World!" 예) String a = "WorldCup"; int y = 2002; a = a + y; // "WorldCup2002" + 연산자의 피연산자 중 하나는 문자열 타입이고 다른 하나는 원시 타입이면 원시 타입의 피연산자는 자동으로 문자열 타입으로 변환된 후에 문자열 결합이 이루어짐
23/41 23 /41
String 타입 – 계속 Programming Tip. 예) 정수를 문자열로 변화하는 쉬운 방법 int n = 10; String s = ""+n; // "10" St i 타입은 불변 객체 String 객체(i 객체(immutable t bl object)임 bj t)임 불변 객체: 한번 생성된 이후에 상태(값)를 변경할 수 없는 객체 String 타입의 내부 상태인 문자열을 구성하는 문자를 변경할 수 없음 만약 클래스가 상태를 변경할 수 있는 메소드를 제공해주지 않으면 그 클래스의 인스턴스들은 불변 객체가 됨 비고.. String 클래스 대신에 StringBuffer 클래스를 사용하면 비고 문자열을 구성하는 문자를 변경할 수 있음
24/41 24 /41
String 타입 – 유용한 메소드 문자열의 모든 영문자를 소문자로 변환해주는 메소드 String toLowerCase(); 예) String greeting = "Hello"; System.out.println(greeting.toLowerCase()); // "hello" 출력 System out println(greeting); // 불변객체이므로 "Hello" System.out.println(greeting); Hello 출력
문자열의 모든 영문자를 대문자로 변환해주는 메소드 String toLowerCase(); 예) String greeting = "Hello"; System.out.println(greeting.toUpperCase()); 문자열에서 특정 위치에 있는 문자를 얻는 메소드 메 char charAt(int index); 예) String g greeting g g = "Hello";; char c = greeting.charAt(1); // 'e' 색인의 시작은 0임 25/41 25 /41
String 타입 – 계속 문자열 중에 일부분을 축출해주는 메소드 String substring(int beginIndex, int endIndex); 예) String greeting = "Hello World!"; String sub = greeting.substring(0,5); // "Hello" 끝 색인은 부분문자열에 포함하지 않음 String substring(int beginIndex); 예) String g sub = g greeting.substring(6); g g( ); // "World!" 시작위치부터 문자열 끝까지의 부분문자열을 생성해줌 String 타입은 == 연산자를 이용하여 내부 상태의 같음을 비교하지 않고 equals l 메소드를 이용하여 비교함 대소문자 구분 없이 비교하고자 하면 equalsIgnoreCase를 사용함
26/41 26 /41
String 타입 – 계속 예) String g = "Hello"; if(g.equals("hello")) if(g.equals( hello )) … if(g.equalsIgnoreCase("hello")) …
// false // true
String St i 타입에 대한 사전적 비교는 compareTo T 메소드를 이용함 예) String g = "banana"; if(g compareTo("apple")>0) if(g.compareTo( apple )>0) … if(g.compareTo("banana")==0) … if(g.compareTo("grape")<0) …
// true // true // true
compareTo도 대소문자 구분 없이 비교하고 싶으면 compareToIgnoreCase를 사용함 자바의 사전식 비교는 숫자, 숫자 대문자, 대문자 소문자 순이며, 순이며 문자열이 긴 것이 뒤에 있는 것으로 간주됨
27/41 27 /41
객체의 비교 예)
String g1 = new String("Hello"); String g2 = new String("Hello"); if(g1 == g2) … // false if(g1.equals(g2)) … // true
g1
g2
Hello
Hello
String g1 = "Hello"; String g2 = "Hello"; if(g1 == g2) … if(g1.equals(g2)) …
g1==g2 == 연산자를 이용한 두 객체 변수의 비교는 두 객체 변수가 동일한 객체를 가리키고 있는지에 대한 비교 g1.equals(g2) equals 메소드를 이용한 비교는 두 객체의 내부 상태가 같은지에 대한 비교
// true // true
28/41 28 /41
String 타입 – 계속 문자열의 특정 문자의 등장 위치 검색 int indexOf(int ch); 주어진 문자 ch가 문자열에 처음으로 등장한 위치를 반환함. 만약 없으면 –1를 반환함 int indexOf(int ch, ch int fromIndex); 위 메소드와 달리 주어진 색인 위치부터 검색함 예) 문자열에 특정 문자가 등장한 빈 빈도 계산하기 int charFrequency(String s, int ch){ int count = 0; i t index int i d = s.indexOf(ch); i d Of( h) while(index!= –1){ count++; index = s.indexOf(ch, index index+1); 1); } // while return count; } // charFrequency;
29/41 29 /41
원시 타입의 wrapper 클래스 java.lang 패키지는 자바의 원시 타입을 객체로 다룰 수 있도록 해주는 다음과 같은 wrapper 클래스를 제공함 Byte, Integer, Short, Long, Double, Float, Character, Boolean Integer와 Character를 제외하고는 원시 타입 이름의 첫 문자를 대문자로 바꾸면 그것의 wrapper 클래스의 이름이 됨 Wrapper 클래스들은 기본 생성자를 제공하지 않음 이들 wrapper 클래스는 여러 가지 유용한 메소드를 제공하여 줌 문자열 "19"를 정수로 변환하는 방법 String input = "19"; int n = Integer.parseInt(input); Integer parseInt(input); 문자열 "3.14"를 부동소수점 수로 변환하는 방법 String g input p = "3.14";; double pi = Double.parseDouble(input);
30/41 30 /41
printf 메소드를 이용한 출력 예)
출력결과: 10 50000 10, 50000, 12 12.25, 25 3 3.14, 14 s 10, 50000, 12.250000, 3.140000, s
public static void main(String[] args){ int a = 10; long b = 50000L; float f = 12.25F; double d = 3.14; char c = 's'; System.out.println(a+", "+b+", "+f+", "+d+", "+c); System.out.printf( %d, %d, %f, %c%n" %c%n , a, System out printf("%d %d %f, %f %f a b, b f, f d, d c); } // main
// Java 2 // Java 5
printf의 문법 (Java 5.0부터 제공) printf(format string, arguments) 출력 형식: %[argument_index$][flag][width]conversion [ ]: ] 이 부분은 선택사항
31/41 31 /41
printf의 conversion 인자 종류
설명
b, B
general
보통 boolean 변수를 출력할 때 사용. true/false로 출력
h H h,
general
인자가 null이면 ll이면 null로 ll로 출력되고, 출력되고 null이 ll이 아니면 integer.toHexString(arg.hashCode())의 결과가 출력
s, S
general
보통 문자열을 출력할 때 사용. arg.toString()의 결과 출력
c, C
character
유니코드 문자
d
integral
10진수 정수
o
integral g
8진수 진수 정수
x, X
integral
16진수 정수
e, E
floating point
과학적 표기법
f
fl ti floating point i t
부동소수점 표기법
g, G
floating point
과학적 표기법 또는 부동소수점 표기법 중 하나로 출력
a, A
floating point
16진수 형태로 부동소수점 표기법
t, T
date/time
conversion
%
percent
%를 출력
n
line separator
\n과 동일 32/41 32 /41
printf의 flag flag
general
Character
Integral
Floating point
Date/ Time
–
y
y
y
y
y
#
y
y
(Formattable)
(o,x,X)
설명
왼쪽 정렬
y
+
y*
y
부호기호 출력
공백
y*
y
양수일 때 앞에 공백문자 추가
0
y
y
0을 을 채우기 문자 문자로 사용
,
y (d)
y
지역 표기법에 따라 출력
(
y*
(a,A 제외) y
음수는 괄호 내에 표현
(a,A 제외)
*:: d d, o, o x, x X가 BigInteger 타입에 적용한 경우, 경우 또는 d를 byte, Byte, short, Short, int, Integer, long, Long에 적용한 경우
33/41 33 /41
printf 메소드를 이용한 출력 – 계속 예) argument_index 형식과 인자의 순서나 개수가 일치하지 않는 경우 사용 public static void main(String[] args){ System.out.printf("%1$f, %1$.3e%n", 77.435678); // Java 5 } // main 출력결과: 77.435678, 7.744e+01
예) flag 사용 예 public static void main(String[] args){ y ( ) %1$-6d, %2$06d, %2$+6d%n", -34, 34); System.out.printf("%1$6d, System.out.printf("%,d\n", 100000); 출력결과: } // main -34, -34 , 000034, +34 100,000
34/41 34 /41
자바에서 sprintf 예)
public static void main(String[] args){ int n = 10; String s = String.format("%d", n); // s = "10" } // main public static void main(String[] args){ int n = 10;; String s = "" + n; // s = "10" } // main
35/41 35 /41
대화창을 이용한 입력 javax.swing 패키지에서 제공하는 JOptionPane 클래스를 이용한다. 예) 정수 입력받기 String input = JOptionPane.showInputDialog("정수를 입력하세요!"); int n = Integer.parseInt(input);
기본적으로 입력한 데이터를 문자열로 처리하기 때문에 원하는 타입이 문자열이 아니면 필요한 필 한 변환을 해야 함 취소 버튼을 누르면 input 값은 null 값을 가지게 됨 null은 참조 타입의 초기값 대화창과 같은 그래픽 사용자 인터페이스를 사용할 경우에는 System.exit(0)을 이용하여 main 함수를 종료해야 함 36/41 36 /41
대화창을 이용한 출력 JOptionPane 클래스의 showMessageDialog 메소드를 이용 예) import javax.swing.JOptionPane; public class DialogDemo{ public static void main(String[] args){ String score = JOptionPane.showInputDialog("점수를 입력하세요!"); g p ( ) int n = Integer.parseInt(score); if(n<60) JOptionPane.showMessageDialog(null, "열심히 해야 겠군요!"); System.exit(0); } // main } // class DialogDemo
37/41 37 /41
콘솔을 이용한 입력 – 계속 Java 5에서 java.util 패키지에 있는 Scanner라는 클래스를 통해 보다 쉽게 콘솔로부터 데이터를 입력 받을 수 있도록 해줌 Scanner는 공백문자를 구분 문자로 이용하여 토큰 단위로 입력을 처리하여 줌 useDelimeter 메소드를 이용하여 구분 문자를 변경할 수 있음 예) import java.util.Scanner; public class ConsoleDemo{{ p public static void main(String[] args){ Scanner input = new Scanner(System.in); int n = input.nextInt(); boolean nextBoolean() () double d = input.nextDouble(); input nextDouble(); byte nextByte() String s = input.nextLine(); short nextShort() … int nextInt() } // main long nextLong() } // class ConsoleDemo float nextFloat() double nextDouble() String nextLine() String next()
38/41 38 /41
콘솔을 이용한 입력 – 계속 예) import java.util.Scanner; public class ConsoleDemo{ public static void main(String[] args){ Scanner input = new Scanner(System.in); Scanner(System in); int n = input.nextInt(); String s1 = input.nextLine(); String g s2 = input.nextLine(); p (); … } // main } // class ConsoleDemo
입력 4 Michael Peter Simon John n=4 s1="" s2="Michael"
nextLine()를 제외한 나머지 메소드들은 줄 단위로 처리하지 않음
39/41 39 /41
문자 타입 자바에서 문자를 처리하기 위해 char 타입을 제공함 C언어에서 char는 1byte이며, ASCII 코드를 사용하여 문자를 나타냄 자바에서 char는 2byte이며, Unicode를 사용함 Unicode는 다중 언어를 하나의 코드로 표현할 수 있도록 하기 위해 만든 코드임 한국어, 중국어, 일어 때문에 현재 Unicode는 모든 다중 언어를 2byte로 표현할 수 없다. 이를 극복할 수 있는 방법이 Java 5부터 제공됨 문자 상수는 작은 따옴표를 사용함 (('a'와 a 와 "a"는 a 는 다름) 예) char c = 'a'; Unicode 자체를 나타내고 싶으면 확장문자를 사용함 예) char c = '\u99E9';
40/41 40 /41
boolean 타입 boolean 타입은 true, false 두 가지 값만 가질 수 있음 C에서는 if 문의 조건식에 x가 정수변수일 때 if(x)와 같은 형태를 사용할 수 있지만 자바에서는 허용되지 않음 자바의 조건식은 반드시 boolean 타입으로 평가되어야 함 boolean 타입 변수에 정수를 대입할 수 없음 예) boolean flag = false; … if(flag==true)
boolean flag = false; … if(flag)
예) boolean isOdd(int n){ if(n%2 1) return true; if(n%2==1) else return false; }
boolean isOdd(int n){ return (n%2==1); t ( %2 1) }
41/41 41 /41
자바프로그래밍(CPA240) NOTE 04-01
©copyright 2010
GUI 두번째
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
교육목표 버튼 GUI 조작하기 Layout, panel 개념 이해하기 메뉴 GUI 조작하기
2/41
버튼 처리 import javax.swing.JFrame;
import javax.swing.JButton;
public class SimpleFrame{ public static void main(String[] args){ JFrame frame = new JFrame(); frame setSize(300 400); frame.setSize(300, frame.setTitle("My First Frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); g p component p = new RectangleComponent(); g p () RectangleComponent frame.add(component); frame.setVisible(true); } // main JButton endButton = new Jbutton( Jbutton("Click Click to End Program Program"); ); } // SimpleFrame endButton.addActionListener(new EndButtonListener()); frame.add(endButton);
3/41
버튼 처리 import java.awt.event.ActionListener; import p jjava.awt.event.ActionEvent;; public class EndButtonListener implements ActionListener{ public void actionPerformed(ActionEvent e){ System exit(0); System.exit(0); } // actionPerformed } // EndButtonListener
4/41
Layout Manager frame에 add 메소드를 이용하여 버튼과 같은 다양한 GUI를 추가할 수 있음 여러 개의 GUI를 추가할 경우에는 이들을 적절하게 배치하여야 함 추가된 GUI가 어떻게 정렬되어 표현되는지는 layout 관리자를 이용함 BorderLayout.NORTH Layout 관리자의 종류 BorderLayout: BorderLayout.CENTER y FlowLayout GridLayout: 행렬형태 BorderLayout.SOUTH B d L BorderLayout의 t의 경우 모든 영역을 다 사용할 필요는 없음
5/41
Panel Layout으로 영역을 구분하였을 경우, 한 영역에는 한 GUI만 add될 수 있음 만약 한 영역에 여러 GUI를 포함하고 싶으면 여러 GUI를 한 panel에 추가하고, panel을 해당 영역에 add함 한 panel에 여러 GUI를 포함하기 위해서는 layout 관리자를 이용하여 영역을 나누어야 함
6/41
public class PanelDemo { PanelDemo public static void main(String[] args) { (); JFrame frame = new JFrame(); frame.setSize(300, 400); frame.setTitle("My First Frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame setLa o t(ne BorderLayout()); frame.setLayout(new BorderLa o t()) JPanel screenPanel = new JPanel(); screenPanel.setBackground(Color.GRAY); g ( ); frame.add(screenPanel, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(1,3)); JButton blueButton = new JButton("Blue"); blueButton.setBackground(Color.BLUE); JButton redButton = new JButton("Red"); g ( ); redButton.setBackground(Color.RED); JButton whiteButton = new JButton("White"); whiteButton.setBackground(Color.WHITE); buttonPanel.add(blueButton); buttonPanel add(redButton); buttonPanel.add(redButton); buttonPanel.add(whiteButton); frame.add(buttonPanel, BorderLayout.SOUTH); frame.setVisible(true); ( ) } } 7/41
ButtonListener myListener = new ButtonListener(screenPanel); JButton blueButton = new JButton("Blue"); ( ) blueButton.setBackground(Color.BLUE); blueButton.addActionListener(myListener); class ButtonListener implements ActionListener{ private JPanel screenPanel; public ButtonListener(JPanel panel){ screenPanel = panel; } public void actionPerformed(ActionEvent e){ g buttonString g = e.getActionCommand(); g (); String if(buttonString.equals("Blue")) screenPanel.setBackground(Color.BLUE); else if(buttonString.equals("Red")) screenPanel setBackground(Color RED); screenPanel.setBackground(Color.RED); else if(buttonString.equals("White")) screenPanel.setBackground(Color.WHITE); else screenPanel.setBackground(Color.GRAY); } // actionPerformed } // ButtonListener
8/41
메뉴 메뉴는 menuBar, menu, menuItem으로 구성됨 menuBar에는 여러 개의 menu를 추가할 수 있고, 각 menu에는 여러 개의 menuItem을 It 을 추가할 수 있음 각각 javax.swing.JMenuBar, javax.swing.JMenu, j javax.swing.JMenuItem으로 i JM It 으로 처리함 menuBar은 add를 통해 frame에 menuBar 추가할 수 있지만 보통은 setJMenuBar 메소드를 메 를 이용함 이 함 menu men Item menuItem
9/41
MenuDemo JMenu colorMenu = new JMenu( JMenu("Change Change Color"); Color ); JMenuItem blueChoice = new JMenuItem("Blue"); blueChoice.addActionListener(myListener); JMenuItem redChoice = new JMenuItem("Red"); redChoice.addActionListener(myListener); dCh i ddA ti Li t ( Li t ) JMenuItem whiteChoice = new JMenuItem("White"); whiteChoice.addActionListener(myListener); colorMenu.add(blueChoice); colorMenu.add(redChoice); colorMenu.add(whiteChoice); JMenuBar menuBar = new JMenuBar(); menuBar.add(colorMenu); B dd( l M ) frame.setJMenuBar(menuBar);
10/41 10 /41
JLabel, JTextField 텍스트를 처리하기 위한 GUI로 JLabel과 JTextField가 있음 JLabel은 출력만 가능하고 입력을 받을 수 없지만 JTextField는 텍스트 입력을 받을 수 있음 둘 다 javax.swing 패키지에 포함되어 있는 클래스임
JLabel
JTextField
11/41 11 /41
JTextFieldDemo JPanel textDemoPanel = new JPanel(); textDemoPanel.setLayout(new GridLayout(2,1)); JPanel nameInputPanel = new JPanel(); nameInputPanel.setLayout(new I tP l tL t( G idL GridLayout(1,2)); t(1 2)) JLabel nameLabel = new JLabel("Your Name"); nameInputPanel.add(nameLabel); JTextField name = new JTextField(32); nameInputPanel.add(name); JLabel output = new JLabel();
12/41 12 /41
class ButtonListener implements ActionListener{ private JPanel screenPanel; private JLabel output; private JTextField input; public ButtonListener(JPanel p ( panel){ p ){ screenPanel = panel; } public void actionPerformed(ActionEvent e){ String buttonString = e.getActionCommand(); e getActionCommand(); if(buttonString.equals("Blue")) screenPanel.setBackground(Color.BLUE); ( g q ( )){ else if(buttonString.equals("Red")){ screenPanel.setBackground(Color.RED); String name = input.getText(); output.setText("Hello, "+name); output setHorizontalAlignment(SwingConstants CENTER); output.setHorizontalAlignment(SwingConstants.CENTER); } else if(buttonString.equals("White")) screenPanel.setBackground(Color.WHITE); else screenPanel.setBackground(Color.GRAY); } // actionPerformed } // ButtonListener 13/41 13 /41
©copyright 2010
자바프로그래밍(CPA240) NOTE 05
결정문 반복문 결정문,
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 자바는 총 8개의 원시타입을 제공함. 각 원시타입의 변수는 그 크기가 고정되어 있으며, 자바는 타입에 대해 매우 엄격함 컴퓨터로 부동소수점 수를 표현하는 것에는 한계가 있음. 있음 이것 때문에 발생하는 오류를 round-off 오류라 함 복합 할당 연산자를 평가하는 과정에 강제 타입 변환이 포함되어 있어, x가 가 정수 변수일 때 x = 4.5는 4 5는 오류이지만 x += 4.5는 4 5는 오류가 아님 자바에서 상수를 정의할 때에는 상수 변수를 사용하며, 상수 변수는 final 키워드를 이용하여 정의함 추가적으로 public static 키워드를 이용하여 정의함 static 멤버 변수는 각 객체마다 별도로 유지되지 않고, 하나만 유지되며 모든 객체가 이 변수를 공유함 따라서 객체를 생성하지 않고 접근할 수 있으며, 객체 변수 대신에 클래스 이름을 이용하여 접근할 수 있음 Java 5에서는 C의 printf와 매우 유사한 printf 메소드를 제공하며, 제공하며 보다 쉽게 콘솔로부터 입력을 받을 수 있도록 Scanner 클래스를 제공해줌
2/37
교육목표 if, switch 문 정수, 부동소수점 수, 문자열, 객체를 비교하는 방법 불 표현식 표현식의 평가 while, hil do d while, hil for f 문 String vs. StringBuffer Random
3/37
개요 보통 프로그램 문장은 순차적으로 실행(sequential 실행 execution)됨 자바를 포함하여 대부분의 프로그래밍언어는 순차적으로 실행하지 않고 다음에 실행할 문장을 지정할 수 있음 이것을 제어의 이동 이동(transfer of control)이라 함 하지만 초기 프로그래밍 환경에서는 무계획적인 제어의 이동 때문에 많은 문제점이 발생하였음 특히, goto는 제어를 매우 광범위한 범위로 이동시킬 수 있었음 Bohm과 Jacopini는 goto 문장을 사용하지 않고, 순차적 실행, 선택문, 반복문만을 이용하여 프로그래밍을 할 수 있다는 것을 증명함 그 이후부터는 goto 문장을 프로그래밍에서 사용하는 경우가 거의 없으며, 자바는 goto 문을 제공하지 않음 자바는 if, switch 두 종류의 선택문을 제공하고, while, do while, for 세 종류의 반복문을 제공함 이 외에 제어의 이동하게 하는 것은 메소드 호출과 break, continue, return 문과 같은 분기문이 있음 4/37
if 문 if 문은 C언어의 if 문과 그 구조가 같다. 예) if(amount<=balance) balance –= amount; 예) if(x>=0) y = x; else y = –x; if 문의 조건식은 반드시 boolean으로 평가되는 표현식이어야 함 비고.. C/C++는 비고 C/C 는 int로 i t로 평가되어도 됨 x가 정수변수일 때 if(x%2){
if(x%2){
… } // OK in C/C++
… } // error in JAVA
if(x%2==1){ … } // OK in C/C++ and JAVA
5/37
dangling-else 문제 else는 가장 가까운 if와 연관된다. if(x<=0) if(x==0) sign = 0; else sign = –1; 1; if(x<=0){ if(x==0) sign = 0; else sign = –1; }
if(x<=0){ if(x==0) sign = 0; } else sign = –1;
이 문제를 dangling-else 문제라 함 Tip. 코드를 작성할 때에는 적절한 들여쓰기를 해야 하며, 때로는 가독성을 높이기 위해 불필요한 중괄호를 사용해야 함
6/37
선택연산자 선택연산자: ?: 문법.. (conditional expression) ? expression : expression 문법 예) y = (x>=0) ? x : –x; 선택연산자는 보통 다음과 같은 if문 대신에 사용됨 if(condition) x = expressionT; else x = expressionF; x = ((condition)) ? expression p p T : expression F; if(x>=0) y = x; else y = –x;;
y = (x>=0) ? x : –x;
if(grade>=60) System.out.println("passed"); else System.out.println( System out println("failed"); failed ); System.out.println((grade>=60) ? "passed" : "failed");
7/37
if 문 – 계속 비교연산자: >, >=, <, <=, ==, != 흔한 프로그래밍 오류 연산자 기호 사이에 공백을 두는 것은 오류임 예) > =, < =, = =, ! = 연산자 기호의 순서를 바꾸는 것은 오류임 예) =>, =<, =! 비교 연산자 ==와 와 대입 연산자 =를 를 혼동하여 사용할 경우 논리 또는 문법 오류가 발생할 수 있음 UML diagram - decision symbol - initial state symbol - final fi l state t t symbol b l
8/37
if 문 – 계속 부동소수점 수의 비교 부동소수점 수는 round-off 오류 때문에 비교하기가 힘듦 예) double r = Math.sqrt(2); double d = r * r – 2; if(d 0) System.out.println("sqrt(2)^2 if(d==0) S t t i tl (" t(2)^2 – 2 = 0") 0"); else System.out.println("sqrt(2)^2 – 2 != 0");
극복방법 매우 작은 수 를 정의한 다음 두 수의 차이가 이 수보다 작은지 비교 예) final double EPSILON = 1E–14; if(Math.abs(x–y) <= EPSILON) …
9/37
객체의 비교 자바 라이브러리에서 제공되는 클래스가 equals 메소드를 제공하면 그 클래스의 객체는 이 메소드를 이용해서 상태가 같은지 비교할 수 있음 비고.. 대부분의 클래스가 제공하는 equals, compareTo 등과 비고 같은 메 메소드는 는 그 기능이 항상 동일함 ((why?) y ) 예) Rectangle box1 = new Rectangle(10, 10, 100, 100); Rectangle r = box1; Rectangle box2 = new Rectangle(10, Rectangle(10 10 10, 100 100, 100);
box1 == r (참) box1 == box2 (거짓) box1.equals(box2) (참) box1과 과 box2의 의 내부 상태가 같음 box1과 box2는 다른 객체를 가리킴
box1
r
box2
10/37 10 /37
null 여부 검사 객체 변수를 포함한 참조 변수는 null 값을 가지고 있을 수 있음 객체 변수의 값이 null인 경우에는 특정한 객체를 가리키고 있지 않다는 것을 의미함 예) if(account == null) … 종종 메소드가 유효한 객체를 반환하지 못할 경우에는 null를 대신 반환하는 경우가 많음 예) JOptionPane의 showInputDialog 메소드에서 사용자가 cancel 버튼을 누르면 null 값을 반환함 String input = JOptionPane.showInputDialog("…"); if(input == null) …
11/37 11 /37
다중 선택 C 언어와 마찬가지로 일련의 비교를 위해 else if를 활용할 수 있음 예) if(score >= 90) return "A"; A ; else if(score >= 80) return "B"; else if(score >= 70) return "C"; else return "F";
else if를 이용한 다중 선택의 경우에는 중간에 중단될 수 있으므로 일련의 if 문 문보다 다 효율적임 율적임 (비 (비교 횟수 측면에서) 예) if(grade=='A') System.out.println("very good"); else if(grade=='B') y p ( g ); System.out.println("good"); else System.out.println("poor");
if(grade=='A') (g ) System.out.println("very good"); if(grade=='B') System.out.println("good"); if(grade=='F') System.out.println("poor");
12/37 12 /37
다중 선택 – 계속 데이터 따라 다중 선택의 순서를 달리 하면 효율성을 높일 수 있음 예) if(grade=='A') System.out.println("very good"); else if(grade== if(grade=='B') B) System.out.println("good"); else System.out.println("poor"); 성적이 좋은 학생들이 더 많은 경우
if(grade=='F') System.out.println("poor"); else if(grade== if(grade=='B') B) System.out.println("good"); else System.out.println("very good"); 성적이 나쁜 학생들이 더 많은 경우
13/37 13 /37
다중 선택 – 계속 단일 정수(문자) 값을 여러 개의 상수와 비교할 경우에는 다중 if-else 문 대신에 switch 문을 사용할 수 있음 예) int digit; … switch(digit){ case 1: System.out.println("one"); break; case 2: System.out.println("two"); break; default: System.out.println("error"); break; } // switch it h
default 절의 위치는 꼭 switch 문의 마지막 절일 필요는 없지만 마지막 절로 사용하는 것이 좋다.
switch 문의 case 절에는 상수 표현식만 사용할 수 있음 예5) int digit, a; … switch(digit){ case 1–1: System.out.println("one"); break; // ok case a–1: System.out.println("two"); break; // error default: System.out.println("error"); break; } // switch 14/37 14 /37
다중 선택 – 계속 즉, switch 문에서 부동소수점 수나 문자열을 비교할 수 없음 예) String g name;; … switch(name){ case "one": … break; // error } // switch
case 절에서 break를 사용하지 않으면 그 다음 case 절이 수행됨 이런 현상을 “fall “f ll through” th h” 행위라 함 예) switch(month){ case 4: case 6: case 9: case 11: return 30; case 2: return 28; default: return 31; } // switch
default 절은 선택 사항이지만 항상 사용하는 것이 바람직함
15/37 15 /37
불 표현식 활용하기 자바의 boolean 타입은 원시 타입이며, 값으로 true와 false만 가질 수 있음 자바의 비교 표현식은 그 결과로 불 값을 가짐 예) System.out.println(amount < 1000); 비고.. boolean이라는 이름은 논리에 관한 수학의 선구자 George 비고 Boole의 이름에서 비롯되었음 술어 메소드(predicate method): boolean 값을 반환하는 메소드를 말함 보통 술어 메소드의 이름은 is 접두사를 사용함 예) if(Character.isUpperCase(ch)) if(Character isUpperCase(ch)) … 불 타입 변수 활용하기 예) boolean married;; if(married) … // if(!married) … 이 때 if(married == true) 식으로 사용하지 않음 16/37 16 /37
불 표현식 활용하기 – 계속 논리 연산자: &&, ||, ! 논리곱(&&) 논리합(||) 논리곱과 논리합 연산자는 short-circuit 평가를 함 다른 말로 지연 평가 평가(l 평가(lazy evaluation)이라 l ti )이라 함 예) if(input != null && Integer.parseInt(input)>0) … 논리부정(!) &, I 연산자: &&, || 연산자와 동일하지만 이 연산자들은 short-circuit 평가를 하지 않음 비고.. 이들 연산자는 비트 논리곱, 비트 논리합 연산자로도 사용됨 비고 드모르간의 법칙 !(A && B) !A || !B !(A || B) !A && !B
17/37 17 /37
불 표현식 활용하기 – 계속 a, b, c가 정수 변수일 때 C/C++에서 (a<b<c)은 문법적으로 오류가 아니지만 자바에서는 문법적 오류임 자바에서 우선순위와 결합순서는 C/C++와 대부분 같음 자바나 C/C++ 모두 (a<b)을 먼저 평가하는데, C/C++는 정수 0 또는 1이 결과 값인 반면에 자바는 true 또는 false가 결과 값이 됨 그런데 런데 true/false와 와 정수 c를 를비 비교할 할수없 없으므로 자바는 문법적 오류임 (a<b<c)는 (a<b)&&(b<c)로 작성하는 것이 올바른 표현방식임
18/37 18 /37
표현식의 평가 왼쪽 피연산자를 항상 먼저 평가함 예) int a = 9; a += (a=3); System.out.println(a); int b = 9; b = b + (b ( = 3); ) System.out.println(b);
출력결과 12 12
C/C++에서는 이항 연산자의 피연산자의 평가 순서는 정의되어 있지 않음
파라미터 목록은 항상 왼쪽에서 오른쪽으로 평가됨 예) public class Test{ 출력결과
public static void main(String[] args){ good, good, bad String s = "good"; print3(s, s, s = "bad"); } print3(String ( g a,, String g b,, String g c){ ){ static void p System.out.printf("%s, %s, %s", a, b, c); }
}
19/37 19 /37
표현식의 평가 – 계속 모든 피연산자를 평가한 다음에 연산자를 적용함 + 연산자는 그것의 피연산자 중 하나가 문자열이면 다른 피연산자를 문자열로 변환함 예) 1+2+" boys"의 평가 결과는 "3 boys" 자바에서는 항상 논리 이동을 함 C/C++에서 <<, >> 비트 이동 연산자는 컴파일러에 따라 산술 이동 (부호 비트 고려)을 할 수도 있고, 논리 이동(부호 비트 고려 없음)할 수도 있음 또 이동 연산자의 왼쪽 피연산자가 long이 아닌 이상 오른쪽 피연산자는 항상 int로 간주함 예) 1 << 35는 1 << 3과 동일함
20/37 20 /37
표현식의 평가 – 계속 C와 마찬가지로 단일 증가 연산자 ++, 단일 감소 연산자 --을 제공함 예) x++;는 x += 1; 그리고 x = x+1;과 같음 이 연산자는 피연산자 앞에 올 수도 있고, 뒤에 올 수도 있음 그러나 그것의 의미와 우선순위는 다름 예) i t n = 3; int 3 int y = 1 + n++; // y = 4, n = 4
예)
int n = 3;; int y = 1 + ++n; // y = 5, n = 4
피연산자 앞에 사용되는 경우가 뒤에 사용되는 경우보다 우선순위가 높음 이 연산자는 하나의 변수를 피연산자로 가져야 힘 예) 다음은 문법 오류임 i t x = 3; int 3 int y = ++(x+1);
21/37 21 /37
우선순위
연산자
결합성
종류
1
. () [ ]
왼쪽에서 오른쪽
멤버접근, 함수, 배열접근
2
(postfix) ++ --
오른쪽에서 왼쪽
단항 연산자
3
(p (prefix) ) ++ -+–! ~
오른쪽에서 왼쪽
단항 연산자
4
(type)
오른쪽에서 왼쪽
타입변환 연산자
5
* / %
왼쪽에서 오른쪽
곱셈 계열 연산자
6
+ –
왼쪽에서 오른쪽
덧셈 계열 연산자
7
<< >> >>>
왼쪽에서 오른쪽
비트 이동
8
< <= > >= instanceof i t f
왼쪽에서 오른쪽
비교
9
== !=
왼쪽에서 오른쪽
비교
10
&
왼쪽에서 오른쪽
비트 논리곱, 논리곱
11
^
왼쪽에서 오른쪽
비트 XOR, XOR
12
|
왼쪽에서 오른쪽
비트 논리합, 논리합
13
&&
왼쪽에서 오른쪽
논리곱(short-circuit)
14
||
왼쪽에서 오른쪽
논리합(short-circuit)
15
?:
오른쪽에서 왼쪽
조건
16
= += -= *= //= % %=
오른쪽에서 왼쪽
대입
22/37 22 /37
반복문 반복문이란 일련의 문장들을 되풀이하여 실행할 수 있도록 해주는 프로그램 구조를 말함 어떤 조건이 만족되면 되풀이되는 과정이 종료됨 아무리 되풀이하여도 조건이 종료되지 않으면 프로그램은 종료되지 않고 이 과정만 계속 되풀이함 이와 같은 현상을 무한루프 무한루프라 하며, 무한루프가 되지 않도록 반복문을 작성할 때에는 주의해야 함 따라서 반복문의 크게 제어줄 제어줄(control line)과 몸체 몸체(body)로 구성됨 제어줄은 반복을 종료하는 조건을 제어함 몸체는 제어줄에 의해 계속 되풀이되는 프로그램 문장들로서 프로그램의 가독성을 높이기 위해 별도의 줄에 작성하며, 다른 문장들을 구별하기 위해 들여쓰기를 함
23/37 23 /37
while 루프 while 반복문의 구조
false condition
while(condition){ body }
true
condition이 false가 될 때까지 반복함 몸체가 한번도 실행되지 않을 수 있음 모든 든 반복문에 사용되는 조건식은 건식은 if 문의 조건식과 마찬가지로 boolean으로 평가되어야 함 while(1){ body } // OK in C, but error in JAVA
body y
while(true){ ( ){ body }
실제 웹서버처럼 무한루프를 통해 구현되는 프로그램도 있다.
24/37 24 /37
off-by-one error 예) 투자가 두 배가 되기 위한 년 수를 계산하라 int years = 0; while(balance<targetBalance){ years++; double interest = balance * rate / 100; balance = balance + interest; }
years의 값을 0으로 초기화해야 할지 1로 초기화해야 할지? balance<targetBalance로 비교해야 할지 balance<=targetBalance로 비교해야 할지? 단순한 사례를 생각해보면 그 답을 쉽게 얻을 수 있음 초기 잔액이 100이고, 이자율이 50%라 하자 1:150, 2: 225 years는 0부터 시작하는 것이 옳음 초기 잔액이 100이고, 이자율이 100%라 하자 1:200 balance<targetBalance로 비교해야 함 25/37 25 /37
do while 루프 do while 반복문의 구조는 다음과 같음 do{
body
body }while(condition);
condition이 diti 이 false가 f l 가 될 때까지 반복함 do while 문의 경우에는 몸체가 한 문장이더라 문장이더라도 항상 중괄 중괄호를 를 사용하는 것이 바람직함 몸체가 최소한 한번은 실행됨
false true
condition diti
true false
26/37 26 /37
for 루프 init
for 문의 구조 condition
for(init; condition; step){ body }
false
true body y
condition이 false가 될 때까지 반복함 init, condition, step 부분은 모두 생략할 수 있다. 그러나 for 문은 원래 의도대로 사용하는 것이 가장 좋음
step
for(int i=0; i<n; i++) { body }
몸체가 한번도 실행되지 않을 수 있음 모든 for 문은 다음과 같이 while 문으로 바꿀 수 있음 init; while(condition){ body t step; } 27/37 27 /37
for 루프 – 계속 for 문의 헤더에 선언된 변수의 가시영역은 for 문 내로 제한됨 for(int i=0; i<n; i++){ … } // i는 더 이상 정의되어 있지 않다.
블록 영역 영역(block scope): 영역( p ) 지역변수는 그것이 정의되어 있는 블록 내에서만 유효함 이 때문에 다른 for 문에서는 같은 이름의 변수를 다시 선언할 수 있음
void f(){ int n;; … { int k; i t n; // error int … } // k는 는 더 이상 정의되어 있지 않다 않다. } void f(){ … { int k; … } {
for(int i=0;i<10;i++) { … } for(int i=0;i<20;i++) { … }
int k; // ok … } }
28/37 28 /37
for 루프 – 계속
int j = 10; for(int i=0; i<10; i++){ … j--; }
for 문의 헤더에 같은 타입의 여러 개의 변수를 선언할 수 있음 예) for(int i=0, j=10; i<=10; i++, j--){ … } 이런 형태의 for 문은 가독성을 저해할 수 있음 Tip for 문의 헤더에는 반복 횟수와 관련된 변수만 선언하고, Tip. 선언하고 다른 요소들은 헤더에 포함하지 않음 다른 타입의 변수를 여러 개 선언할 수는 없음 예) for(int i=0, double s=0.0; i<10; i++} { … } // 오류 루프 종료 조건에서 != 연산자를 사용하는 것은 피해야 함 예) for(int f (i t i=0; i 0 i!=n; i! i 2){ … } i+=2){ n이 짝수가 아니면 무한루프가 됨 반복 횟수와 관련된 변수를 for 문 몸체에서 변경하지 않아야 함 자바에서는 C/C++와 달리 ,가 연산자가 아님
29/37 29 /37
for 루프 – 계속 루프가 반복되는 횟수 (가정. b>a) for(i=a; i<b; i++){ … }
b–a
for(i=a; i<b; i+=c){ … }
(b–a)/c
ffor(i=a; (i i< i<=b; b i++){ i ){ … }
b–a+1
ffor(i=a; (i i< i<=b; b i+=c){ i ){ … }
(b–a)/c+1
for(i=0; i<10; i++){ … }
10–0=10
for(i=0; i<=10; i++){ … }
10–0+1=11
for(i=10; i<=100; i+=5){ … } (100–10)/5+1=19
30/37 30 /37
break, continue 문 break 문: 루프 중간에서 루프를 빠져나올 때 사용하는 문 switch 문의 case, default 절에서도 사용 continue 문: 루프 중간에서 루프 끝으로 이동할 때 사용하는 문 예) for 문의 경우 for 문 중간에서 continue 문을 만나면 그 다음에 for 문의 step 부분이 실행됨 중첩된 루프인 경우에는 break와 continue는 해당 문이 포함되어 있는 가장 안쪽 루프에만 적용됨
31/37 31 /37
break, continue 문 – 계속 레이블된 break문: break문은 break가 속한 가장 안쪽 루프에만 적용됨. 종종 매우 중첩된 내부 루프에서 전체 루프 밖으로 빠져 나와야 하는 경우가 있음. 있음 이 때에는 레이블을 이용한 break문을 b k문을 사용할 수 있음 이 때 레이블은 빠져 나 나오고자 자 하는 루 루프의 의 끝이 아니라 시작에 붙임 예) label: while(…){ for(…){ if(…) break; // 이 if문이 이 참이면 for루프만 만 종료됨 됨 if(…) break label; // 이 if문이 참이면 while루프까지 종료됨 } }
32/37 32 /37
반복문 작성시 주의사항 반복 횟수가 정해져 있는 경우에는 for 문을 사용하고, 그렇지 않은 경우에는 while 문을 사용함. 이 때 최소 한번 반복된다는 것을 강조하고 싶으면 do d while hil 문을 사용함 반복문 내에 평가 값이 변하지 않는 표현식은 반복문 전으로 옮기는 것이 바람직함 모든 루프는 break와 continue 문을 사용하지 않고도 작성할 수 있음 가급적 사용하지 않는 것이 바람직함
33/37 33 /37
String vs. StringBuffer String과 StringBuffer는 모두 문자열을 처리할 때 사용하는 클래스임 차이점 String은 불변 객체이지만 StringBuffer는 불변 객체가 아님 String은 new 연산자를 사용하지 않고 생성할 수 있지만 StringBuffer는 반드시 new 연산자를 사용해야 함 StringBuffer는 + 연산자를 사용할 수 없음. cf. append 예) String name = "hello"; StringBuffer bName = new StringBuffer("hello"); bN bName.setCharAt(1,‘a’); tCh At(1 ‘ ’) name = "hallo"; hello name
hallo bName
hallo
34/37 34 /37
String vs. StringBuffer StringBuffer bstr1 = new StringBuffer("test");
test str1
test
String str1 = "test";
bstr1 StringBuffer bstr2;
String str2;
str2
bstr2 test
test
str2 = str1;
str1
bstr1
str2
bstr2 test
str1
str2
tests
str1 = str1 + "s";
bstr2 = bstr1;
bstr1.append("s");
bstr1 tests str1+ "s"
bstr2
35/37 35 /37
입력의 처리 일련의 입력 받기 boolean done = false; while(!done){ String input = read input; if(end of input indicated) done = true; else{ process input; } }
loop-and-a-half 문제: 루트 중간에서 루프를 끝내야 하는 경우 while(true){ ( ){ String input = read input; if(end of input indicated) break; else{ process input; } }
input = read input; while(input!=null){ hil (i t! ll){ process input; input = read input; } while((input = read input)!=null){ process input; } 36/37 36 /37
난수 발생 자바에서 난수 발생은 java.util 패키지에 있는 Random 클래스를 이용함 예) Random generator = new Random(); int d = 1 + generator.nextInt(6); Random 클래스의 nextInt 메소드는 0부터 주어진 정수 정수–1 1 사이에 난수를 발생시켜줌 컴퓨터를 이용하여 발생되는 난수는 실제 난수라고 볼 수 없음 컴퓨터는 유한 상태 기계이다. 따라서 생성되는 난수는 매우 긴 주기를 가지는 수임 예) Random generator = new Random(); int d = (int)(1 + 5 * generator.nextDouble()); Random 클래스의 nextDouble 메소드는 0에서 1 사이에 난수를 발생시켜줌 Random 클래스 대신에 Math.random() 메소드를 사용할 수 있음
37/37 37 /37
©copyright 2010
자바프로그래밍(CPA240) NOTE 06
복합타입: 배열/열거형 타입
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 if 문과 반복문의 조건식은 반드시 boolean 타입으로 평가되어야 함 반복문을 작성할 때 정해진 특정 회수를 반복할 경우에는 for 문을 사용하고, 나머지는 while 문을 사용함. 예외적으로 최소 한번 실행된다는 것을 강조하고 싶으면 do while 문을 사용함 표현식을 평가하기 위해서는 연산자의 우선순위와 결합순서를 알고 있어야 함. 또한 자바에서는 이항 연산자의 두 피연산자 중 왼쪽 피연산자를 항상 먼저 평가하며, 파라미터를 평가할 때에도 왼쪽 파라미터부터 항상 먼저 평가함 다중 선택 if 문의 사용이 일련의 if 문보다 효율적임 다중 선택 if 문의 순서가 효율성에 율성에 영향을 줌
2/41
교육목표 배열 일차원 배열 새로운 for 문 배열의 복사 파라미터로 배열의 전달 객체 배열 final과 배열 다차원 배열 enum 타입 문자열을 토큰 단위로 처리하는 방법
3/41
배열 원시타입 원시타입(primitive type)은 한 가지 값만 저장함 sum 같은 종류의 여러 개의 값을 하나의 데이터로 취급(하나의 하나의 이름을 이용하여 접근 접근)할 수 있도록 해주는 타입을 배열 배열(array)이라 함 배열처럼 여러 개의 값으로 구성되는 타입을 복합 타입 타입(composite type)이라 함 예를 들어 40명의 학생의 성적을 처리해야 하면, 40개의 정수 변수를 사용하여 처리할 수 있지만 이렇게 하는 것은 번거로움 대신 정수 40개로 구성된 배열을 선언하여 사용하는 것이 편리함 자바에서 배열 변수는 참조 타입임
학생성적
4/41
배열 배열의 특성 동질 구조 구조(homogeneous structure): 구조에 있는 모든 요소는 같은 타입임 예) 일차원 배열에서 첫 슬롯에 개수를, 나머지 슬롯에 정수 값을 저장한 배열 물리적으로 동질 구조이지만 논리적으로는 동질 구조가 아님 요소들간에 순서가 존재 존재함 배열의 요소는 위치에 의해 접근됨 (index: 0부터 시작) 배열의 용량은 컴파일 시간에 정해짐 배열의 모든 슬롯에 유효한 요소가 들어있을 필요는 없음 배열의 용량을 변경할 수 없음 임의 접근 제공 제공: 모든 요소를 바로 접근할 수 있음 용량이 고정되어 있고 동질 구조이기 때문에 가능한 것임
5/41
일차원 배열의 선언 자바에서 배열은 참조 타입임 예) int[] numbers; C처럼 int numbers[];와 같이 선언할 수도 있음 다른 타입과 마찬가지로 생성되어야 사용할 수 있음 예) numbers b = new int[8]; i t[8] C/C++처럼 int numbers[10];과 같이 new를 사용하지 않고, 선언과 동시에 생성하는 것은 가능하지 않음 모두 동적배열 (이것의 장점은?) 선언과 생성을 동시에 할 수 있음 예) int[] numbers = new int[8]; new를 이용하여 생성된 배열의 초기값은? 기본 값으로 자동으로 초기화됨
6/41
일차원 배열 – 계속 용량이 0인 배열을 선언할 수 있음 예) int[] tmp = new int[0]; 비고.. 용량이 0인 배열과 null은 다름 비고 변수를 이용하여 배열의 용량을 지정할 수 있음 예)
Scanner keyboard = new Scanner(System.in); int capacity = keyboard.nextInt(); int[] [] list = new int[capacity]; [ p y];
비고. C/C++에서는 동적 배열을 생성할 때에만 가능함 비고. 자바에서 모든 배열은 동적으로 생성된 배열임
7/41
배열 선택식 배열의 요소 접근: [ ] 연산자와 색인 이용 문법.. arrayVariableName[expression] 문법 대입연산자 왼쪽에 올 경우와 오른쪽에 올 경우에 따라 그 의미가 다름 예) numbers[2] [ ] = 5;; 예) value = numbers[i]; 색인은 0부터 시작 시작함 배열의 범위를 벗어난 접근은 문법적 오류가 아니라 실행 시간 오류임. 이 때 자바는 예외를 발생시킴 예외: ArrayIndexOutofBoundException 유효한 색인: 0부터 (용량–1)까지 실제 사용해야 할 색인 색인:: 0부터 0부터 (크기 크기–1) 1)까지 까지 하지만 자바에서 배열은 항상 동적배열이므로 용량과 크기가 항상 같도록 프로그래밍할 수 있음 [ ] 연산자 내에 표현식을 사용할 때 더욱 주의해야 함 8/41
일차원 배열 – 계속 배열의 용량은 public 멤버변수인 length를 이용하여 언제든지 얻을 수 있음 Why? 자바에서 배열은 객체임 예) numbers.length 사용자는 이 변수의 값을 변경할 수 없음 예) numbers.length = 10; // error 비고.. String에서 비고 비 g에서 문자열의 길이는 length() g () 메 메소드를 를 이용
9/41
일차원 배열 – 계속 자바에서 배열은 객체임 예) int[] list1 = new int[5]; int[] list2 = new int[0]; int[] list3 = null; int n2 = list2.length; // ok int n3 = list3.length; // error list1
list3
list2
null ll
5 length
0 length
10/41 10 /41
새로운 형태의 for 문 Java 5에서는 다음과 같은 특수한 형태의 for 문을 제공함 문법.. for(variable: collection) statement 문법 영어로 이와 같은 for 문을 "for each" 루프라 함 for 문이 반복할 때마다 variable은 배열의 각 요소를 차례로 가리키게 됨 주의.. 이 구문에서 variable은 색인 변수가 아님 주의 따라서 variable의 의 타입은 collection의 의 타입과 일치하거나 강제 타입 변환 없이 대입할 수 있는 타입이어야 함 예) 정수 배열에 있는 모든 항에 대해 반복하고 싶은 경우 int[] list = new int[10]; … for(int i=0; i<list.length; i++){ System out println(list[i]); System.out.println(list[i]); } // Java 2
int[] list = new int[10]; … for(int element: list){ System out println(element); System.out.println(element); } // Java 5
11/41 11 /41
새로운 형태의 for 문 – 계속 예) for의 특수 구문에서 variable과 collection의 타입 관계 int[] [] list = new int[10]; [ ]; … for(double element: list){ // ok System.out.println(element); } // for
double[] [] list = new double[10]; [ ]; … for(int element: list){ // error System.out.println(element); } // for
예) collection이 원시타입이 아니어도 가능함 String[] list = new String[10]; … for(String s: list){ System.out.println(s); } // for
Rectangle[] list = new Rectangle[10]; … for(Rectangle box: list){ System.out.println(box); } // for
전통 for 문을 반드시 사용해야 하는 경우 경우 1. 1 배열의 용량과 크기가 다른 경우. 경우 자바에서는 모두 동적 배열이기 때문에 용량과 크기가 같은 경우가 대부분임 경우 2. for 문 내에서 색인 변수가 필 필요할 할 경우가 있음 12/41 12 /41
새로운 형태의 for 문 – 계속 public static void main(String[] args) { int[] listt = new int[10]; i t[] li i t[10] for(int element: list){ // element = list[i]; g error element = 1;; // logical } // for } // main 새로운 형태의 for 문에서 variable을 L-value로 사용하면 그것은 collection에 영향을 주지 않음
class A{ private int val; public pub c A(int ( t n)) { val a = n;; } public void set(int n) { val = n; } public int get() { return val; } } // class A public bli class l T t{ Test{ public static void main(String[] args) { A[] list = new A[10]; ( i=0;; i<list.length; g ; i++){ ){ for(int list[i] = new A(10); } // for for(A a: list){ // a = list[i]; li t[i] a.set(5); } // for ( a: list){ ){ for(A System.out.println(a.get()); } // for } // main } // class Test 13/41 13 /41
일차원 배열 – 계속 초기값을 제공하여 생성할 수 있다. 예) int[] numbers = new int[] {5, 7, 4, 3, 10, 22, –6, –3}; 초기값을 사용하는 경우에는 용량을 지정할 수 없음 배열의 용량은 초기값의 개수에 의해 자동으로 결정됨 따라서 numbers의 용량은 8임 다음과 같이 new int[] 부분을 생략할 수도 있음 예) int[] [] numbers = {{5,, 7,, 4,, 3,, 10,, 22,, –6,, –3}; }; 비고.. C/C++와 달리 배열의 일부분만을 초기화할 수 없음 비고 대입문을 이용하여 초기화하는 것보다 효율적임 int[] age = {2, 12, 10}; 이 형태가 더 효율적
int[] age = new int[3]; age[0] = 2; g age[1] = 12; age[2] = 10;
14/41 14 /41
문자 배열과 문자열 문자 배열은 String 타입의 객체가 아님 예) void f1(String s){ … } void f2(){ char[] fruit = {'a', 'p', 'p', 'l', 'e'}; String s = fruit; // error f1(f f1(fruit); ) // error }
다음과 같은 문자 배열을 문자열로 변환할 수 있음 예) char[] fruit = {'a', 'p', 'p', 'l', 'e'};
이 처럼 String도 new 연산자를 사용하여 생성할 수 있지만 보통 new를 생략한다. 물론 이 경우에는 생략할 수 없음
String s1 = new String(fruit); // ok St i s2 2 = new String(fruit, St i (f it 2 k "ple" " l " String 2, 3) 3); // ok
문자 배열은 문자열처럼 출력할 수 있음 예) char[] fruit = {'a', 'p', 'p', 'l', 'e'}; String s = "apple"; System.out.println(fruit); S t System.out.println(s); t i tl ( )
결과: apple apple 15/41 15 /41
배열의 복사 두 배열 변수가 같은 타입이면 상호 대입할 수 있음 예) int[] numbers = {1, 2, 3, 4}; numbers int[] values = numbers; values는 numbers와 같은 배열을 참조하게 됨 한 배열에 있는 모든 값을 다른 배열로 복사하고 싶으면 values System.arraycopy 메소드를 이용함 예) int[] numbers = {1,2,3,4,5}; i t[] values int[] l = {11 {11,12,13,14,15}; 12 13 14 15} System.arraycopy(numbers,0,values,2,3); numbers
1 2 3 4 5
values
11 12 13 14 15
numbers
1 2 3 4 5
values
1 2 3 4
11 12 1 2 3
System.arraycopy(소스배열, 소스배열의 시작위치, 목적배열 목적배열의 시작위치, 목적배열, 시작위치 복사할 요소의 개수) 16/41 16 /41
파라미터로 배열의 전달 자바에서 배열은 참조 타입이므로 인자로 메소드에 전달하여 메소드 내에서 배열을 조작하면 메소드가 끝난 후에도 조작 결과가 계속 유지됨 주의.. 자바에서 모든 인자는 call-by-value 형식으로 전달됨. 주의 call-by-value y 방식에서는 인자의 값이 대응되는 파라미터 변수 변수로 복사됨 예) void f1(int[] a){ a[2] = 10; 결과: 1,2,10,4 } id f2(){ void int[] A = {1,2,3,4}; f1(A); ( for(int i=0;; i<4;; i++)) System.out.println(A[i]); }
A 1 2 3 10 4
f2
a
쓰레기 수집 가능 힙
f1
17/41 17 /41
파라미터로 배열의 전달 – 계속 예) void f1(int[] a){ int[] B = {5,6,7,8}; a = B; } 결과: 1,2,3,4 void id f2(){ int[] A = {1,2,3,4}; f1(A); for(int ii=0; 0; i<4; i 4; i++) i ) System.out.println(A[i]); } call-by-value call by value 형식으로 전달되므로 절대 참조 변수를 인자로 전달하여 이 변수가 다른 곳을 가리키도록 변경할 수 없음
A f2 5 6 7 8
a B
1 2 3 4
쓰레기 수집 가능 힙
f1
자바에서 8개의 원시타입을 제외한 나머지는 모두 참조타입이므로 call-by-value 방식으로 전달되면 call-by-reference 방식으로 전달하는 것과 유사한 측면이 있음 18/41 18 /41
자바에서는 const 개념이 없음 C/C++와 달리 자바에서는 전달되는 배열의 내용을 메소드에서 수정하지 못하도록 만들거나, 이 메소드는 전달받는 배열의 내용을 변경하지 않는다는 것을 나타낼 수 없음
19/41 19 /41
배열의 전달과 length 멤버 C/C++의 경우
void main(void) { int n[100] = {0}; printf("main:%d\n" printf( main:%d\n , sizeof(n)); void f(int n[]){ f(n); printf("f:%d\n", sizeof(n)); 출력결과: } } main:400 C/C++에서도 주소로 배열을 전달함. f:4 하지만 배열을 전달받은 함수에서 대응되는 매개변수는 포인터이며, 배열로 인식되지 않음. 따라서 항상 배열을 전달할 때 용량 또는 크기 정보를 함께 전달해야 함 출력결과: 예) int findMax(int n[], int size); main:100 f:100 자바의 경우 public static void f(int[] n){ System.out.printf("f:%d\n", n.length); 자바에서 배열은 객체로 전달되므로 } 전달받은 함수에서도 배열로 인식되며 public static void main(String[] args) { length 멤버를 이용하여 배열의 용량을 int[] n = new int[100]; 알 수 있지만 배열의 크기는 알 수 없음. System.out.printf("main:%d\n", n.length); 다만, 항상 동적할당하기 때문에 f(n); 배열의 용량과 크기가 같도록 하여 } 사용하기가 용이하기 때문에 크기를 전달할 필요가 없는 경우가 많음 20/41 20 /41
배열을 반환하는 메소드 배열을 메소드의 결과로 반환할 수 있다. 예) int[] doubleTheSize(int[] a){
a b doubleTheSize
f A
C와 달리 자바에서 배열은 항상 동적할당 방식이므로 배열의 반환이 가능함
int[] b = new int[a.length*2]; System.arraycopy(a,0,b,0,a.length); return b; void f(){ } // doubleTheSize() d bl Th Si () int[] A = {1,2,3,4}; A = doubleTheSize(A); A[4] = 5; 1 for(int i=0; i<5; i++) 2 System.out.println(A[i]); } // f() 3 1 4 2 배열을 메소드에서 반환활 때, 5 3 문제가 있는 경우 null 또는 용량이 0인 배열을 반환해 4 줄 수 있음
쓰레기 수집 가능 힙 21/41 21 /41
객체 배열 예) Circle[] allCircles = new Circle[5]; Circle 객체에 대한 참조를 5개까지 저장할 수 있는 배열이 생성됨. 5개의 Circle 객체가 생성된 것은 아님 이 때 각 항은 null로 초기화됨 각 개별 객체를 생성하고자 하면 추가로 다음과 같은 코드를 실행해야 함 for(int i=0; i<allCircles.length; i<allCircles length; i++){ allCircles[i] = new Circle(); } // for
22/41 22 /41
main 메소드의 파라미터 main 메소드의 파라미터는 다음과 같음 public static void main(String[] args) 즉, main 메소드는 문자열 배열을 인자로 받음 예) HaHa라는 자바 프로그램을 다음과 같이 실행하였다고 하자 j java H H Do HaHa D R Rae M Me 또한 HaHa 프로그램이 다음과 같다고 하자. public bli class l H H { HaHa{ public static void main(String[] args){ for(String s: args){ System.out.println(s); } // for } // main } // class HaHa
결과: Do Rae Me
주의. C/C++와 달리 프로그램 이름이 인자에 포함되지 않음
23/41 23 /41
final과 배열 예)
void f(){ final int[] numbers = new int[5]; numbers[3] = 4; // ok numbers = new int[3]; // error } // f()
finall int[] fi i t[] numbers b = new int[5]에서 i t[5]에서 final은 fi l은 numbers와 b 와 연관이 있는 것이지 배열 자체와 있는 것은 아님
이 부분만 final
24/41 24 /41
다차원 배열 2차원 배열은 행과 열로 구성된 테이블을 나타내기 위해 사용함 2차원 배열의 선언 예) double[][] alpha = new double[100][10]; 첫 번째 [100]은 행을 두 번째 [10]은 열을 나타냄 2차원 배열의 접근 alpha[0][5] = 36.4; 행과 열의 개수 행의 개수: alpha.length 열의 개수: alpha[i].length
25/41 25 /41
다차원 배열 – 계속 다차원 배열도 초기값을 제공하여 생성할 수 있음 예) int[][] matrix = new int[][]{ {1,0,0},{0,1,0},{0,0,1} };
int[][] matrix = { {1,0,0},{0,1,0},{0,0,1} };
일반적으로 2차원 배열의 각 행의 용량은 같도록 만듬 예) int[][] a = new int[10][5]; 그러나 각 행의 용량이 다를 수도 있음 예) int[][] a = new int[2][]; a[0] = new int[5]; a[1] 1 = new int[3]; int[][] matrix = new int[][]{ {1,0,0,1,2},{0,1,0},{0,5} }; matrix[1][3] = 5; // error
1
0
0
0
1
0
0
5
1
2
26/41 26 /41
파라미터로 다차원 배열의 전달 다차원 배열도 일차원 배열과 같은 방법으로 메소드의 파라미터로 전달할 수 있음 예) public static void showMatrix(int[][] a){ for(int row=0; row<a.length; row++){ for(int col=0; col<a[row].length; col++) System out printf("%d System.out.printf( %d, ", a[row][col]); System.out.println(); C/C++와 달리 다차원 배열일 때도 } // for 행의 수 만큼 매개변수를 선언할 때 모든 용량을 } // showMatrix 생략할 수 있음
다차원 배열을 메소드의 결과로 반환할 수도 있음 예) public static int[][] subMatrix(int[][] a, int size){ int[][] temp = new int[size][size]; for(int row=0; row<size; row++) for(int col=0; col<size; col++) temp[row][col] = a[row][col]; return temp; } // subMatrix
27/41 27 /41
C/C++ 배열과 차이점 자바에서는 배열도 객체임 객체 배열을 전달받은 함수에서도 length 멤버를 통해 배열의 용량을 알 수 있음 다차원 배열을 인자로 사용하기 위해 매개변수를 선언할 때에도 용량 정보를 표시하지 않음 자바에서는 배열을 생성할 때 초기화 목록을 사용할 경우에는 배열의 용량을 지정할 수 없음 C/C++에서는 에서 초기화 기화 목록 목록을 사용하여 사용하여도 용량 용량을 지정할 수 있 있으며, 며 초기값의 개수가 부족하면 나머지는 자동 초기화됨 자바의 모든 배열은 동적으로 할당되는 배열임 배열 따라서 변수를 이용하여 배열의 용량을 지정할 수 있음 자바에서는 배열을 함수의 결과로 반환할 반환 수 있음 C/C++에서는 동적 할당된 배열만 가능함 자바에서 배열 이름은 수정가능한 L-value임 value l 임 (참조변수이므로) 참 변수이 C/C++에서는 배열 이름은 상수 포인터임 자바에서 열의 크기가 기가 각각 다른 2차원 차원 배열을 만들 수 있음 28/41 28 /41
Arrays java.util 패키지에 정의된 Arrays 클래스에는 배열을 처리할 때 유용하게 사용할 수 있는 여러 static 메소드를 정의하고 있음 void sort(type[] a); 여기서 type은 8개의 원시타입 중 하나임. 즉, 8개의 메소드가 오버로드되어 있음 빠른 정렬을 이용하여 오름차순으로 배열의 요소들을 정렬하여 줌 문제점.. 배열의 모든 항에 유효한 데이터가 있는 경우에만 사용 문제점 가능함 void sort(type[] a, int fromIndex, int toIndex); 주어진 색인 사이에 있는 데이터만 정렬함 void sort(type[] a)의 문제점을 극복할 수 있음
29/41 29 /41
Arrays – 계속 int binarySearch(type[] a, type v); 이진 검색을 하여 주어진 v를 배열에서 찾음 void sort(type[] a)와 같은 문제점을 가지고 있음 void fill(type[] a, type v); 주어진 배열의 모든 항을 v로 로 초기화 해줌 예) int[] list = new int[100]; Arrays.fill(list, y ( , 1); ); boolean equals(type[] a1, type[] a2); 두 배열의 모든 항이 같은지 비교하여 줌 void sort(type[] a)와 같은 문제점을 가지고 있음
30/41 30 /41
가변의 인수를 받는 메소드 정의 문법. type… 배열명 문법. 예) public bli static t ti int i t max(int… (i t list){ li t){ if(list.length == 0){ int n0 = max(); // error System.exit(1); int n1 = max(10, 15); } int n2 = max(10, 3, 20, 14); int maxVal = list[0]; for(int i=1; i<list.length; i++) if(maxVal<list[i]) maxVal = list[i]; return maxVal; } // max
31/41 31 /41
자바에서 열거형 만들기 (Java 2) 자바 2에서는 열거형 타입을 제공하지 않았지만 자바 5부터는 열거형 타입을 제공함 자바 2에서는 다음과 같이 상수를 정의하여 열거형 대신에 사용할 수 있음 시도 1. 1 public class Alignment{ public static final int LEFT = 0; public static final int CENTER = 1;; p public static final int RIGHT = 2; } // class Alignment
-1, 3 등 0, 1, 2가 아닌 다른 값이 전달될 수도 있음
void setAlignment(int ( align){ ){ if(align = Alignment.LEFT){ … } } // setAlignment
32/41 32 /41
자바에서 열거형 만들기 – 계속 시도 2.
이 타입의 객체를 생성할 수 없도록 만듦
public class Alignment{ private Alignment() {} public static final Alignment LEFT = new Alignment(); public static final Alignment CENTER = new Alignment(); public static final Alignment RIGHT = new Alignment(); } // class Alignment void id setAlignment(Alignment tAli t(Ali t a){ ){ if(a == Alignment.LEFT){ … 시도 1의 문제점이 모두 } 해결됨 } // setAlignment 참조의 비교 (주소의 비교) 따라서 final로 선언되어야 함
33/41 33 /41
자바에서 열거형 만들기 – 계속 시도 3. public class Alignment{ private int value; private Alignment(int v) { value = v; } public static final Alignment LEFT = new Alignment(0); public static final Alignment CENTER = new Alignment(1); public static final Alignment RIGHT = new Alignment(2); public boolean equals(Object obj){ Alignment o = (Alignment)obj; return (o.value (o value == this.value); this value); } // equals } // Alignment void setAlignment(Alignment a){ if(a.equals(Alignment.LEFT)){ 시도 2는 객체를 파일시스템에 … 저장된 후에 복원되는 경우에는 } 올바르게 동작하지 않을 수 } // setAlignment 있음 즉, 있음. 즉 주소를 이용한 비교를 하고 있기 때문에 이런 문제가 발생할 수 있음
34/41 34 /41
열거형 타입 Java 5 public enum Alignment{ LEFT, LEFT CENTER, CENTER RIGHT }
void setAlignment(Alignment a){ if(a == Alignment.LEFT){ … } } // setAlignment
C/C++의 차이점. 각 열거형 상수에 정수 상수가 할당되지 않음 자바에서 열거형 타입은 클래스를 정의하는 또 다른 형태이며, 각 상수는 그 클래스의 인스턴스임
35/41 35 /41
열거형 타입 – 계속 Java 5부터는 열거형 타입을 지원함 예) public enum Size {SMALL {SMALL, MEDIUM, MEDIUM LARGE}; Size s = Size.MEDIUM; enum Size {SMALL, MEDIUM, LARGE}; Size s = MEDIUM; C++
Java 5에서 enum 타입은 실제 모두 Enum 클래스의 자식 클래스임. 즉, enum은 은 클래 클래스를 를 정의하는 특수한 방법이 방법이고,, 이렇게 정의된 클래스는 열거한 상수들만큼의 객체만 존재하고, 더 이상 이 클래스의 객체를 만들 수 없음 Enum 클래스는 여러 개의 메소드가 정의되어 있으며, 있으며 새롭게 정의된 열거형은 이들 클래스를 상속받으므로 이들을 사용할 수 있음 예) System.out.println(s); // SMALL 출력 Enum 클래스에 정의된 toString 메소드는 열거형 변수에 할당된 열거형 상수를 문자열로 반환하여 줌
36/41 36 /41
열거형 타입 – 계속 열거형은 클래스를 정의하는 특수한 방법이므로 열거형 역시 기존 클래스처럼 생성자, 메소드, 멤버변수 등을 추가할 수 있음 단, 생성자는 private이어야 함 (상수를 추가할 수 없도록) 예) enum Size{ SMALL("S"), MEDIUM("M"), LARGE("L"); private Size(String abbrev) { abbreviation = abbrev; } public getAbbrev() abbreviation; } bli String St i tAbb () { return t bb i ti private String abbreviation; } // enum Size public static void main(String[] args){ Size s = Size.LARGE; System.out.printf("%s, %s", s, s.getAbbrev()); // LARGE, L 출력 } // main i
37/41 37 /41
열거형 타입과 switch 문 열거형 타입은 C/C++와 달리 정수 상수가 아니지만 자바에서는 switch문의 case 절 상수로 사용할 수 있음 예) enum WeekDay {SUN, MON, TUE, WED, THU, FRI, SAT}; Scanner keyboard = new Scanner(System.in); System out println("오늘은 System.out.println( 오늘은 무슨 요일? 요일?"); ); String answer = keyboard.nextLine(); answer = answer.toUpperCase(); WeekDay wday = WeekDay.valueOf(answer); switch(wday){ it h( d ){ case SUN: // case WeekDay.SUN: case SAT: System.out.println("오늘은 노는 날"); break; default: System.out.println("오늘은 공부하는 날"); break; } // switch
38/41 38 /41
String Tokenization 여러 단어로 구성된 문자열에서 각 단어를 처리하기 위해 java.util.StringTokenizer 클래스를 사용할 수 있음 Scanner 클래스가 제공되기 전에는 데이터를 입력 받기 위해 널리 사용됨 I love 예) 사용법
Java Programming.
StringTokenizer tokenizer = new StringTokenizer( ); StringTokenizer("II love Java Programming. Programming "); while(tokenizer.hasMoreTokens()){ System.out.println(tokenizer.nextToken()); 반복자(iterator) }
39/41 39 /41
String Tokenization – 계속 StringTokenizer 클래스는 기본적으로 공백문자를 이용하여 토큰을 분리함. 토큰 분리자를 변경하고 싶으면 생성할 때 분리자를 지정할 수 있음 예) michael StringTokenizer tokenizer = new StringTokenizer("michael|jane|john","|"); while(tokenizer.hasMoreTokens()){ System.out.println(tokenizer.nextToken()); }
jane john
이 경우에는 "a|b"처럼 분리자인 쉼표 다음에 공백 문자가 없어야 함 여러 개의 분리자를 사용할 수도 있음 예) StringTokenizer St i T k i tokenizer t k i = new StringTokenizer("michael|jane,john","|,");
40/41 40 /41
String의 split() 메소드 이용 String의 split() 메소드를 이용하여도 토큰 단위의 처리가 가능함 예) String input = "apple banana grape"; String[] tokens = input.split(" "); for(String token: tokens){ System out println(token); System.out.println(token); }
결과: apple banana grape
String g input p = "apple|banana|grape"; pp | |g p ; String[] tokens = input.split("\\|"); for(String token: tokens){ System.out.println(token); } 정규표현식 형태의 패턴 정규표현식에서는 |, +, * 등은 특수 기호로 사용됨
41/41 41 /41
©copyright 2010
자바프로그래밍(CPA240) NOTE 07
클래스
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 자바 배열과 C/C++ 배열과 가장 큰 차이점 하나, 자바 배열은 객체다 배열만 전달받은 메소드에서 배열의 용량을 알 수 있음 둘, 자바 배열은 모두 동적 배열이다. 배열의 용량과 크기를 일치시키는 것이 쉬움 초기값을 나열하여 배열을 생성할 수 있지만 이 때 용량을 지 정할 수 없음 자바 5부터는 새로운 형태의 for 문을 제공 그 밖의 자바 배열의 특징 new를 이용하여 생성된 배열은 기본값으로 초기화됨 자바에서 2차원 배열에서 각 행의 용량이 다를 수 있다. 열거형 타입의 열거형 상수는 실제 상수가 아니라 객체임 열거형 타입을 정의하는 것은 클래스를 정의하는 또 다른 방법이므로 일반 클래스와 마찬가지로 멤버변수, 메소드 등을 정의할 수 있음 2/48
교육목표 클래스의 종류 높은 응집성, 낮은 결합성의 의미 접근자, 수정자 메소드 메소드의 사전조건, 사후조건 static t ti 메소드 static 멤버변수 변수의 가시영역(scope) 패키지
3/48
클래스 클래스는 객체를 생성하기 위한 틀로서, int와 같은 새로운 타입을 정의하는 것임 타입은 그 타입의 데이터가 가질 수 있는 값들의 집합인 도메인과 타입에 적용할 수 있는 연산들의 집합에 의해 정의됨 클래스 타입의 값은 객체가 됨 객체의 생성은 대부분 new 연산자를 이용하지만 객체를 생성할 때 new 연산자를 사용하지 않는 경우도 있음 new 연산자는 생성자 메소드를 피연산자로 사용함
4/48
객체를 생성하는 방법 객체를 생성하는 4가지 방법 방법 1. new 연산자를 이용하는 방법 (가장 기본적인 방법) 방법 2. String과 같이 new 연산자를 사용하지 않고 생성하는 방법 매우 예외적인 경우 사용하지 않는 것이 아니라 생략된 경우도 있음 방법 3. 객체를 생성하는 메소드를 사용하여 생성하는 방법 예7.1) 7 1) NumberFormat formatter = NumberFormat.getNumberInstance(); NumberFormat getNumberInstance(); 실제적으로는 그 메소드에서 내에서 new 연산자를 사용함 방법 4. clone 메소드를 사용하여 생성하는 방법 (강의노트 09 참조)
5/48
클래스 선택하기 Rule of thumb: thumb 클래스 이름은 명사, 메소드의 이름은 동사 클래스의 종류 종류1. 종류 1. 단일 개념을 나타내는 클래스 예) 수학적 개념: Point, Rectangle, Ellipse 예) 일상 개체: 개체 BankAccount, B kA t Purse P 앞으로 할 일이 이전에 종류 2. Actor: 어떤 작업을 해주는 클래스 수행한 일에 영향 받을 경우 이것은 객체로 예) Scanner, Scanner StringTokenizer, StringTokenizer Random 모델링되어야 함 종류 3. 유틸리티 클래스: 객체를 전혀 생성하지 - 상태가 그 정보를 유지 않는 클래스 모든 메소드가 static 메소드임 예) Math 클래스 종류 4. 4 프로그램을 시작하기 위해 만든 클래스: main 메소드를 포함하는 클래스 종류 3과 4는 실제 클래스로 보기 어려움 6/48
클래스 선택하기 – 계속 잘못 선택된 클래스 작성해야 하는 프로그램을 하나의 클래스로 모델링하는 것 예) GradeProgram 클래스 어떤 하나의 행동을 클래스로 모델링하는 것 예) ComputeGrade C t G d 클래스: 클래스 학점을 계산해주는 클래스 어떤 클래스의 메소드로 모델링되어야 함
7/48
Cohesion, Coupling 클래스는 높은 응집성 응집성(high cohesion), 낮은 결합성(low 결합성 coupling)을 가져야 한다. 중요한 소프트웨어공학 개념 응집성: 단일 개념을 나타내야 한다는 것을 말함 응집성 결합성: 다른 클래스와 관계를 말하는 것으로 낮은 결합성이란 결합성 서로에게 영향을 주는 것이 적다는 것을 말함 결합성이 높은 두 클래스 중 한 클래스를 변경하게 되면 다른 클래스도 함께 변경해야 할 확률이 높음 Quality Tip. 일관성 이름과 파라미터의 일관성을 유지해주는 것이 좋다. 좋다 예) JOPtionPane.showInputDialog(promptString) JOptionPane.showMessageDialog(null, messageString) 별로 좋지 않은 형태
8/48
클래스 간의 관계 주문시스템 클래스: 상품, 주문, 고객 등 의존(“uses-a”): 한 클래스가 다른 클래스를 사용할 경우 의존 가장 일반적인 관계 멤버 변수가 아닌 다른 클래스의 객체를 메소드의 인자로 받아 사용하거나 메소드 내에서 다른 클래스의 객체를 생성하여 사용하는 경우 포함(“has-a”): 한 클래스의 객체가 다른 클래스의 객체를 포함하고 포함 있는 경우 예) 주문에는 여러 상품이 포함됨 멤버 변수의 타입이 다른 클래스인 경우 상속(“is-a”): 상속( 상속 ) 한 클래 클래스가 가 다른 클래 클래스를 를 상속한 경우 예) 긴급주문은 주문 클래스를 상속함
9/48
클래스의 메소드 클래스의 멤버변수, 특히 인스턴스 변수(instance variable)는 private으로 지정하여 외부에서 정의되어 있지 않은 방법으로 조작할 수 없도록 해야 함 (오류를 줄이기 위한 목적) 클래스의 메소드는 보통 외부와 상호작용하기 위해 제공되는 수단이므로 대부분 p 수단이 public으로 지정됨 만약 외부와 상호작용하기 위한 수단이 아니고, 내부적으로만 필요한 메소드일 경우에는 private으로 지정하는 것이 바람직함 즉 public일 즉, bli 일 필요가 없으면 항상 private으로 i t 으로 지정함
10/48 10 /48
Accessor, Mutator 접근자 메소드(accessor): 객체의 상태를 변경하지 않고 상태만 열람하는 메소드 예) String 클래스의 charAt 메소드 수정자 메소드(mutator): 객체의 상태를 변경하는 메소드 수정자 메소드는 잘못된 변경이 이루어질 수 없도록 보장해 주어야 함 예) BankAccount 클래스의 withdraw 메소드 수정자 메소드가 하나도 없는 클래스를 불변 클래스 클래스(immutable class)라 함 예) String 클래스 보통 반환 타입이 void임. 접근자와 확실히 구분됨 수정 성공 여부를 나타내는 boolean 타입은 타입은? 예외 처리 기법 사용 (강의노트 11)
11/48 11 /48
생성자 객체를 생성할 때 사용되는 메소드이며, new 연산자의 피연산자로 사용됨. 이 메소드에서는 보통 객체의 멤버변수를 초기화함 생성자의 이름은 클래스 이름과 같으며 같으며, 다른 메소드들의 정의와 달리 반환 타입이 없음 외부에서 객체를 생성할 수 있어야 하므로 생성자들은 보통 public 메소드로 정의함 모든 생성자를 private로 선언하여 외부에서 객체를 생성할 수 없도록 만들 수도 있음 (Java (J 2에서 열거형 타입 정의 참조.) 참조 ) 생성자의 종류 기본 생성자 생성자(default constructor): 매개변수를 하나도 사용하지 않는 생성자를 말함 복사 생성자 생성자(copy constructor): 매개변수로 같은 클래스 타입의 객체를 받아 주어진 객체와 동일한 내부 상태를 가지는 객체를 생성해주는 생성자를 말함 (C++에서는 매우 큰 역할을 함)
12/48 12 /48
일반 멤버변수의 초기화 각 객체마다 다른 값으로 초기화될 수 있는 멤버변수: 생성자 예) BankAccount의 balance 멤버 public class A{ // 명백한 초기화 각 객체마다 같은 값으로 초기화해야 하는 멤버변수: private int a = 1; 명백한 초기화 또는 초기화 블록 private int b; 같은 값으로 초기화되지만 각 객체는 나중에 서로 public A(int v){ 다른 값을 유지하는 경우를 말함 b = v; } // 생성자 같은 값으로 초기화되어 변경되지 않는다면 } // class A 이것은 상수 상수로 정의하는 것이 바람직함 예) 월 단위 로그북: 특정 월에 각 날마다 특정 정보를 유지 예: 날마다 운동한 시간, 날마다 마신 커피잔의 수 이를 위해 int[] entry를 멤버변수를 유지함 월에 있는 날의 수에 따라 생성자에서 초기화할 수 있지만 최소 28이고, 최대 31이므로 낭비되는 공간이 많이 없으므로 다음과 같이 명백한 초기화를 할 수 있음 (다른 언어의 경우) int[] entry = new int[31]; 자동 초기화는 기화는 사용하지 않는 것이 바람직함 13/48 13 /48
생성자 – 계속 생성자를 하나도 정의하지 않으면 시스템에서 기본 생성자를 제공하여 줌 기본 생성자가 아닌 다른 생성자가 정의되어 있으면 기본 생성자를 제공하여 주지 않음 예) public class A{ private int value; public int get(){ return value; } // get } // class A
public class B{ private int value; public B(int n){ value = n; } // B(int) public int g get(){ (){ return value; } // get } // class A
A a = new A(); // ok B b1 = new B(); // error B b2 = new B(10); // ok A는 생성자를 하나도 정의하지 않고 있으므로 있 로시 시스템에서 템에서 기본 생성자를 제공해줌 B는 기본 생성자는 정의하고 있지 않지만 정수 하나를 인수로 사용하는 생성자를 정의하고 있음. 있음 따라서 이 경우에는 시스템은 기본 생성자를 제공해 주지 않음
14/48 14 /48
생성자 – 계속 this를 이용하여 한 생성자에서 다른 생성자를 호출할 수 있음 제한점.. this를 이용한 다른 생성자의 호출은 반드시 생성자의 제한점 첫 문장이 되어야 함 (강의노트 03 참조) 예) 클래스 A는 int 타입의 멤버변수 a와 b를 가짐 a는 모든 객체마다 동일하게 2로 초기화됨 b는 종종 1로 초기화되지만 객체마다 다르게 초기화될 수 있음 public class A{ private int a = 2; private int b; public A(){ this(1); } bli A(int A(i n){ ){ b = n; } public } // class A
super 키워드를 이용하여 부모 클래스의 생성자를 호출할 수 있으며 this를 이용한 생성자의 호출과 마찬가지로 반드시 생성자의 첫 문장이 되어야 함 (강의노트 09 참조) 15/48 15 /48
생성자 – 계속 예)
public class Time{ public Time(){ this(0); } private int hour; public Time(int h){ this(h,0); } private int minute; public Time(int h, int m){ this(h,m,0); } private int second; public Time(){ (){ this(0,0,0); ( ) } public Time(int h){ this(h,0,0); } public Time(int h, int m){ this(h,m,0); } public Time(int h, int m, int s){ setTime(h,m,s); } public void setTime(int h, int m, int s){ setHour(h); setMinute(m); setSecond(s); tS d( ) } public void setHour(int h){ hour = ((h ((h>=0 0 && h<24) h 24) ? h : 0); } public void setMinute(int m){ minute = ((m>=0 && m<60) ? m : 0); } public void setSecond(int s){ second = ((s>=0 && s<60) ? s : 0); } } // class A 16/48 16 /48
final 멤버 변수 public class Increment{ private int total = 0; private final int INCREMENT; public Increment(int incrementValue){ INCREMENT = incrementValue; } public void addIncrementToTotal(int h){ total += INCREMENT; } public String toString(){ return total+""; } } // class Increment
INCREMENT는 생성자에서 초기화된 이후 값이 변경될 수 없음 따라서 public 멤버로 정의하여도 아무런 문제가 없음 배열의 length g 멤버가 이와 같은 방식으로 구현됨
멤버 변수 중 각 객체마다 초기화된 이후에 값이 고정되는 멤버 변수는 final 멤버로 선언함 모든 클래스마다 동일한 값으로 초기화되는 final 멤버는 static final 멤버로 선언함 17/48 17 /48
Side-effect 메소드의 side-effect: 메소드가 호출된 객체 외에 다른 객체의 상태를 변경하면 side-effect가 있는 메소드라 한다. 예) BankAccount의 transfer 메소드 public class BankAccount{ … public void transfer(int amount, BankAccount other){ balance –= amount; // withdraw(amount) other.balance += amount; // other.deposit(amount) } // transfer … } // class BankAccount BankAccount peter = new BankAccount(1000); BankAccount tom = new BankAccount(500); peter.transfer(200, tom);
Side effect는 최소화하는 것이 바람직하다. Side-effect는 바람직하다
18/48 18 /48
Side-effect – 계속 예) 값의 출력 System.out.printf("잔액: %,d원", peter.getBalance());
Public class BankAccount{ … public void printBalance(){ System.out.printf(" y p ( 잔액 잔액: %,d원", , 원 , balance); } // printBalance … }
printBalance의 문제점 출력 형태가 고정되어 있음 S t System.out에 t에 의존함 (높은 결합성) cf. f 애플릿 예) 따라서 다음과 같이 오류 메시지를 메소드 내에서 출력하는 것은 바람직하지 않음 예외 처리 public void deposit(int amount){ if(amount<0) System.out.println("오류: …"); else balance += amount; } // deposit
19/48 19 /48
Call-by-value vs. Call-by-Reference 자바에서는 모든 파라미터는 call-by-value 방식으로 전달됨 따라서 C에서는 다음이 가능하지만 자바에서는 가능하지 않음 void swap(int *a, int *b){ int tmp = *a; *a = *b; } // swap
*b = tmp;
어떤 교재에서는 원시 타입은 call-by-value 방식으로, 참조 타입은 call-by-reference y 방식으로 전달된다 방식 전달된다고 말하 말하고 있지만 이것은 정확한 것이 아님 sAccount void transfer(int amount, BankAccount other){ b l balance -= amount; t int newBalance = other.balance + amount; other = new BankAccount(newBalance); // sorry } // transfer BankAccount sAccount = new BankAccount(10000); BankAccount jAccount = new BankAccount(10000); A tt f (500 jAccount); jA t) sAccount.transfer(500,
jAccount
other amount
20/48 20 /48
Call-by-value vs. Call-by-reference 결론 메소드는 다른 메소드의 지역변수(스택에 유지되는 원시타입 및 참 조타입 변수)의 값을 수정할 수 없음 메소드는 참조타입의 매개변수를 이용하여 객체의 상태(배열 포함)를 변경하면 메소드가 종료된 후에도 그 결과가 반영됨 예) public class A{ private int value; public A(int n) { value = n; } public set(int n) { value = n; } public get() { return value; } } // class A
void f1(int n, n A b){ 결과: 3, 5 n = 10; b.set(5); b = new A(7); ( ); } // f1 void f2(){ A a = new A(3); int val = 3; f1(val, a); System.out.printf("%d, %d", val, a.get()); } // f2 21/48 21 /48
Preconditions 사전조건 사전조건(precondition): 메소드의 호출자가 호출하기 전에 지켜야 하는 요구사항 사전조건이 만족되지 않았을 때에는 그 결과에 대한 책임을 지지 않는다. 예) BankAccount의 deposit 메소드의 사전조건: amount>0 사전조건이 위배된 경우에는 어떻게? 방법1. 방법 1. 예외 처리 (효율성이 떨어질 수 있음) 방법2. 방법 2. 아무것도 하지 않음 (위험함) public void deposit(int amount){ if(amount<0) throw new illegalAmountException(); balance = balance + amount; } // deposit(int) public void deposit(int amount){ assert amount>=0; balance = balance + amount; } // deposit(int)
public void deposit(int amount){ if(amount>0) balance = balance + amount; } // deposit(int) ( ) 22/48 22 /48
PostConditions 사후조건 사후조건(postcondition): 사전조건에 따라 메소드가 호출되었을 때 메소드 실행 후 보장되는 조건 종류 1. 반환 값의 정확성 /** * balance = balance + amount 종류 2. 객체의 상태 * @param amount 입금할 금액 * @precondition: amount>=0 * @postcondition getBalance()>=0 */ public void deposit(int amount){ } // deposit(int) /** * 이 계좌의 현재 잔액을 반환함 * @return 현재 잔액 * @postcondition: return value = account balance *// public int getBalance() { } // getBalance()
23/48 23 /48
Class invariant invariant: 변하지 않는 조건 루프 invariant: 루프가 반복될 때 동안 변하지 않는 조건 int sum = 0; // loop invariant : sum = 1+..+i for(int o ( t i=1;; i<n;; i++)) sum su += i;;
클래스 invariant: 이 클래스가 객체가 생성된 이후 각 객체가 항상 만족하는 조건 건 // Class invariant : getBalance()>=0 public class BankAccount{ … }
24/48 24 /48
static 메소드 static 메소드: 숨겨진 파라미터 this가 없는 메소드 다른 말로 클래스 메소드 메소드(비고 비고.. 인스턴스 메소드)라 함 static 메소드는 클래스 이름을 이용하여 호출할 수 있음 객체 변수를 통해 호출할 수도 있지만 이것은 바람직하지 않음 예) Math.sqrt(x), M th t( ) Integer.parseInt(input) I t I t(i t) Math.sqrt(2.0)을 사용하기 위해 다음과 같이 해야 한다면 Math math = new Math(); double val = math.sqrt(2.0);
StringTokenizer처럼(이 St i T k i 처럼(이 객체는 문자열을 어디까지 처리하였는지 유지함) 객체에 일을 시키는 장점은 객체가 어떤 데이터를 계속 유지해 주는데 있다.
매우 번거롭다. 여기서 Math는 상태를 갖지 않고, 주어진 인자값만을 이용하여 필요한 계산을 수행함. 즉, 상태를 갖지 않으면 객체를 등장시킬 필요가 없음
25/48 25 /48
static 메소드 – 계속 static 메소드가 사용되는 경우 경우 1. 객체의 상태를 접근할 필요가 없는 메소드 경우 2. 클래스의 static 멤버변수만 접근하는 메소드 그러면 static 메소드는 어떤 클래스에? 방법 1. 1 Math와 M th와 같은 새 클래스를 정의함 방법 2. 이 메소드를 사용할 클래스에 포함함
26/48 26 /48
main이 static 메소드인 이유 그러면 왜 main 메소드는 static 메소드인가? main은 프로그램이 시작될 때 처음으로 호출되는 메소드임. 그런데 프로그램이 시작되면 어떤 객체도 존재하지 않으므로, 이 메소드는 static이어야 함 뿐만 아니라 main은 어떤 객체의 상태를 조작하지 않음 Factory 메소드: 객체를 생성하는 메소드 예) NumberFormat formatter = NumberFormat.getNumberInstance(); 여기서 생성자를 사용하지 않고 static 메소드를 이용하여 객체를 생성하고 있음 이유 1. 1 생성자의 이름은 항상 클래스 이름으로 고정되어 있음 이유 2. 생성자와 달리 하위 클래스를 생성할 수 있음 (강의노트 09 참조) 이유 3. 객체를 매번 생성하지 않을 수 있음 (객체생성관리 가능) 기존에 생성된 객체의 주소를…
27/48 27 /48
static 메소드 사용시 주의사항 주의. static 메소드 내에서 일반 메소드를 호출하는 것은 오류임 주의. 일반 메소드에서는 static 메소드를 호출할 수 있음 public class StaticTest{ public void f(){ System.out.println("instance method"); } public static void main(String[] args){ f(); } The method f() from the type StaticTest is not static }
static이라는 영어의 뜻과 클래스 메소드와 무슨 관계가 있는가? 없다. 단지, C++에서 이 키워드를 사용하고 있기 때문에 자바의 개발자가 별 생각 없이… 원래 static은 C에서 수명이 함수 수명이 아닌 프로그램 수명인 지역변수를 나타내기 위해 사용됨
28/48 28 /48
static 멤버변수 예) BankAccount 클래스 계좌번호를 유지하는 accountNumber라는 멤버변수를 사용하고 싶음. 이 때 객체가 생성될 때마다 순차적으로 계좌번호를 부여하고 싶음. How? 마지막으로 할당된 계좌번호를 기억하고 있어야 함 C 생각: 전역변수 클래스 생각: static 멤버변수 static 멤버변수는 특정 객체에 속하지 않고 클래스에 속함 public class BankAccount{ private int balance; private int accountNumber; private static int lastAssignedNumber; … } // BankAccount
lastAssignedNumber
BankAccount a ccou t 29/48 29 /48
static 멤버변수 – 계속 클래스의 모든 객체는 자신만의 멤버변수를 가짐. 하지만 static 멤버변수는 클래스의 모든 객체가 공유함 즉, 특정 static 멤버변수는 아무리 객체를 많이 생성하더라도 그 클래스에 하나만 존재함 static 멤버변수는 가능하면 사용하지 않는 것이 바람직함 클래스의 모든 메소드는 static 멤버변수를 접근할 수 있음 static 멤버변수도 대부분 private으로 선언됨 클래스에서 사용되는 상수는 객체마다 중복되어 공간이 할당될 필요가 없으므로 static으로 선언됨 예) public static final double PI = 3.14; 3 14; 보통 상수는 외부에서 접근할 수 있도록 public으로 선언함 상수이므로 외부에서 접근하여 상수이 접근하여도 변경할 수 없음
30/48 30 /48
static 멤버변수를 초기화하는 방법 방법 1. 생성자: 객체가 생성될 때마다 초기화되므로 static 멤버변수는 이 방법으로 초기화할 수 없음 방법 2. 명백한 초기화 public class BankAccount{ … private static int lastAssignedNumber = 0; } // BankAccount
명백한 초기화는 생성자가 호출되기 전에 이루어짐. 특히 static 멤버변수의 초기화는 생성자가 처음으로 호출되기 전에 초기화됨 방법 3. 3 자동 초기화 사용: 사용 자동 초기화 값이 아닌 다른 값으로 초기화하고 싶은 경우에는 이 방법을 사용할 수 없음 방법 4. 초기화 기화 블록 사용 명백한 초기화나 초기화 블록은 클래스 내에 선언된 순서로 초기화가 이루어짐
31/48 31 /48
static 멤버변수를 초기화하는 방법 – 계속 초기화 블록 초기화 블록 내에 일반 프로그래밍 문장을 사용할 수 있음 static 멤버변수는 static 초기화 블록을 사용할 수 있음 public class BankAccount{ private static int lastAssignedNumber; static{ lastAssignedNumber = 0; } // initialization block … } // BankAccount
public class BankAccount{ private int balance; private i t int i t accountNumber; tN b { balance = 0; accountNumber accou t u be = 0; } // initialization block … } // BankAccount public class list{ private int[] elements; { elements = new int[10]; for(int i=0; i<10; i++) elements = 1; } // initialization i iti li ti bl block k … } // list
32/48 32 /48
멤버변수를 초기화하는 방법 선택의 원칙 일반 멤버변수 각 객체마다 다른 값으로 초기화되어야 하는 경우에는 생성자에서 초기화함 초기화된 이후 변경되지 않는 경우에는 final 멤버변수로 선언하고 생성자에서 초기화함 모든 객체마다 동일한 값으로 초기화되는 경우에는 명백한 초기화 또는 초기화 블록을 사용함 모든 객체마다 동일한 값으로 초기화된 이후 변경되지 않는 경우 클래스 상수변수로 선언하고 명백한 초기화를 사용함 static 멤버변수 명백한 초기화 또는 초기화 블록을 사용함 자동 초기화의 활용은 가급적 사용하지 않음
33/48 33 /48
static import Java 5부터 import할 때 static 키워드를 사용할 수 있음 즉, Java 5부터 클래스뿐만 아니라 static 메소드와 멤버변수를 import할 수 있음 Math 클래스는 java.lang 패키지에 포함되어 예) 있으므로 실제 있 실제로 import할 p 할필 필요가 가 없다 없다. import java.lang.Math; // Java 2 … Math.sqrt(Math.pow(x,2)+Math.pow(y,2)); import p static jjava.lang.Math.sqrt; g q // Java 5 import static java.lang.Math.pow; // Java 5 … sqrt(pow(x 2)+pow(y 2)); sqrt(pow(x,2)+pow(y,2));
34/48 34 /48
전역변수 만들기 C와 같은 언어는 전역변수(global variable)를 제공하지만 자바는 전역변수를 제공하지 않음 하지만 다음과 같이 전역 변수를 흉내 낼 수 있음 public class Global{ public static int var; }
var 변수는 p public이므로 이 다른 클래 클래스에서 에서 참 참조할 할 수 있음 따라서 Global.var는 C의 전역 변수처럼 프로그램 안의 어디에서 든 참조할 수 있음 비고.. 전역 변수의 사용은 바람직하지 않음 (그냥 재미로 참고만 비고 하세요)
35/48 35 /48
자바의 메모리 모델 – 스택 스택 스택(stack)은 쌓아 올려 가는 자료구조로서 데이터의 추가와 삭제가 항상 맨 위에서 이루어짐 메소드가 호출되면 그 프로그램의 스택이라는 메모리 공간에 메소드의 스택 프레임이 생성됨 이 프레임에는 파리미터 변수, 변수 지역 변수, 변수 중간 계산 값들을 저장하게 되며, 메소드가 종료되면 stack 이 공간을 반납하게 됨 따라서 지역변수의 수명이 temporary operands 메소드의 수명하고 같음 frame local variables h() 예) f() () 메 메소드에서 에서 이 메 메소드가 가 parameter t variables i bl 종료되기 전에 g() 메소드가 호출되고, temporary operands 이 메소드가 종료되기 전에 h() g() 메소드가 호출되면 다음과 같은 3개의 local variables parameter variables 스택 프레임이 스택 공간에 쌓아져 있게 됨 temporary operands f()
local variables parameter variables
36/48 36 /48
자바의 메모리 모델 – static 필드 각 클래스는 static 멤버를 가질 수 있음. 자바는 이들을 유지하기 위해 static 필드 영역이라는 메모리 공간을 사용함 static 필드는 프로그램이 실행될 때부터 끝날 때까지 계속 유지됨 따라서 이 공간은 고정되어 있다고 생각하면 됨 자바에서 클래스는 일반적으로 동적 링크(dynamic linking)됨. A class 즉, 클래스 파일은 처음에 한꺼번에 static variables 메모리에 적재되지 않고, 않고 그것이 것이 필요할 B class 때 읽혀지고 링크됨 static variables 주의.. 실제 별 별도의 필드 영역이 주의 의 static 필 C class 있는 것은 아니고 힙이라는 메모리 static variables 공간에 이 영역이 유지됨. 하지만 스택 힙과 성격이 다르기 때문에 스택, 이렇게 이해하는 것이 오히려 명백함 클래스가 load될 때마다 증가한다.
37/48 37 /48
자바의 메모리 모델 – 힙 프로그램이 실행되는 동안에 new를 이용하여 동적으로 메모리 공간을 요구하면 힙이라는 공간에 그 공간을 확보해주며, 2장에서 설명한 바와 같이 쓰레기 수집가능 힙을 사용함. 사용함 따라서 사용자는 확보한 공간을 나중에 직접 반납할 필요가 없음 객체나 배열은 모두 두 이 공간에 할당된다 할당된다. 힙의 경우에는 단편화(fragmentation)가 발생할 수 있으며, JVM은 이를 해소하기 위해 컴팩션을 할 수도 있음 단편화 때문에 stack
남은 전체적인 공간이 충분하지만 연속적인 큰 공간을 확보할 수 없다.
heap
컴팩션
38/48 38 /48
가시영역 변수의 가시영역 가시영역(scope): 변수를 참조할 수 있는 프로그램의 영역을 말함 지역변수의 가시영역은 선언된 위치부터 선언된 위치의 블록이 끝나 는 곳까지임 (블록 블록 영역 영역) 같은 이름의 두 개의 지역변수의 가시영역은 중첩될 수 없음 예) void f(){ int a = 0; { int a = 3; // 위 a와 범위가 중첩되므로 오류 } } // f
영역이 중첩되지 않으면 같은 이름의 지역변수를 사용할 수 있음 예) void f(){ { int a = 3; } { int a = 5; } } // f
39/48 39 /48
가시영역 – 계속 멤버변수의 가시영역 클래스의 메소드 내에서는 모든 멤버변수와 메소드를 단순 이름으로 접근할 수 있다. 단순 이름은 실제로 숨겨진 인수 this가 생략된 형태이다. 예) balance this.balance this balance 클래스 밖에서 클래스의 메소드나 멤버변수를 접근하기 위해서는 완전한 이름(qualified name)을 사용해야 함 완전한 이름이란 클래스 이름이나 객체변수의 이름과 . 연산자를 함께 사용하는 형태를 말함 예) Math.sqrt(), Math sqrt() peter.deposit(100) peter deposit(100) 접근 권한에 따라 접근할 수 없을 수 있음 클래스 내에서는 그 클래 클래 클래스의 의인 인스턴스인 턴 인 경우(매개변수 경우(매개변수로 받은 경우, 지역변수로 선언한 경우)에는 완전한 이름으로 접근 권한과 무관하게 멤버변수나 메소드를 접근할 수 있음
40/48 40 /48
가시영역 – 계속 public class Test{{ p public static void main(String[] args) { Pair a1 = new Pair(5,7); Pair a2 = new Pair(3,1); a1.swap(a2); a2.n1 = 10; // error } // swap } // class Pair
예)
public class Pair{{ p private int n1; private int n2; public Pair(int x, int y){ n1 = x; n2 = y; } public void swap(Pair o) { Pair tmp = new Pair(0,0); Pair(0 0); tmp.n1 = n1; // tmp.n1 = this.n1 tmp.n2 = n2; // tmp.n2 = this.n2 n1 = o.n1; // this.n1 = o.n1 n2 = o o.n2; n2; // this.n2 this n2 = o o.n2 n2 o.n1 = tmp.n1; o.n2 = tmp.n2; } // swap } // class Pair
41/48 41 /48
가시영역 – 계속 가시영역의 중첩 같은 이름의 두 개의 변수의 가시영역이 중첩될 수 있는 유일한 경우는 지역변수와 멤버변수의 중첩임 이 경우 지역변수가 멤버변수를 숨긴다고 함 (shadowing) this를 이용하면 중첩된 멤버변수를 여전히 접근할 수 있음 주의.. 같은 이름의 두 개의 지역변수의 가시영역은 절대로 주의 중첩될 수 없음 (C++와 다름) 예) public class A{ public void f() { int a = 3; a = this.a + 5; } private int a = 0; }
a = this.a + 5;에서 a는 지역변수 a를 말하며, this.a는 멤버변수 a를 말함
42/48 42 /48
패키지 자바 프로그램은 여러 개의 클래스로 구성됨 프로그램이 커질 수록 클래스 수가 많아지므로 단순히 여러 개의 파일로 관리하는 것이 힘듦 자바는 이를 위해 패키지라는 기능을 제공해줌 패키지는 일련의 클래스들을 계층구조로 묶어 관리할 수 있도록 해줌 자바에서 라이브러리는 패키지 형태로 제공됨 클래스를 어떤 패키지에 포함시키고자 하면 소스 파일의 첫 문장에 다음과 같은 package 문을 포함해야 함 예) package 패키지이름; 같은 디렉토리에 포함된 클래스는 package 문이 없어도 자동으로 하나의 패키지에 포함됨. 이것을 기본 패키지 패키지(default package)라 함
43/48 43 /48
패키지 – 계속 어떤 패키지에 포함되어 있는 클래스를 사용하고 싶으면 그것의 완전 이름(예: java.awt.Color)을 사용하거나 import 문을 사용해야 함 import 하는 원칙 java lang에 있는 클래스는 import 하지 않고 사용할 수 있음 java.lang에 가독성을 위해 java.awt.* 형태보다는 완전 이름 형태로 import 하는 것이 바람직함 같은 패키지에 있는 클래스는 import 하지 않고 사용함
44/48 44 /48
혼란스러운 점 자바에서 점 ‘.’은 다음과 같은 용도로 사용된다. 용도 1. 패키지 이름 사이: 예) java.util 용도 2. 패키지와 클래스 이름 사이: 예) java.lang.Math 용도 3. 클래스와 내부 클래스 사이: 예) Ellipse2D.Double 용도 4. 4 클래스와 멤버변수 사이: 사이 예) Math.PI M th PI 용도 5. 객체와 메소드, 객체와 멤버변수 사이: 예) peter.deposit() 예) java.lang.System.out.println(x); java lang System out println(x); println()은 () 때문에 메소드임을 쉽게 알 수 있음 out은 뒤에 메소드 이름이 있으므로 객체 아니면 클래스임 out은 System의 public static final 멤버변수임 System은 뒤에 객체 이름이 오므로 객체 아니면 클래스 클래스임. 구별방법 클래스 이름은 대문자로 시작하고, 구별방법: 시작하고 객체 이름은 소문자로 시작함
45/48 45 /48
패키지 – 계속 패키지를 사용하는 이유 이유 1. 여러 클래스를 관리하기에 편리한 방법 이유 2. 이름 충돌을 피할 수 있음 서로 다른 패키지에는 같은 이름의 클래스를 사용할 수 있음 완전 이름을 사용하면 이름이 같아도 혼동되지 않음 패키지의 위치를 찾는 방법 자바 컴파일러가 제대로 설정되어 있고, 있고 자바에서 제공하는 표준 클래스만 사용하면 패키지의 위치를 찾는 방법에 대해 걱정할 필요가 없음 패키지는 패키지 이름과 일치하는 서브 디렉토리에 존재함 이 디렉토리를 컴파일러가 찾을 수 있도록 해주어야 함 CLASSPATH라는 환경변수를 사용함
46/48 46 /48
패키지 – 계속 예) ime.kut.sangjin이라는 자바 패키지를 만들고 싶음 ime\kut\sangjin이 위치할 디렉토리를 결정함 c:\classdir에 위치하기로 하면 set CLASSPATH=c:\classdir;. 을 설정함 주의할 점: 점 “.”을 “ ”을 반드시 포함해야 함 그 다음 c:\classdir\ime\kut\sangjin이라는 디렉토리를 생성함 이 패키지에 Numeric이라는 클래스를 만들고 싶으면 다음을 Numeric.java 파일의 첫 줄에 포함한 후에 package ime.kut.sangjin; 컴파일하여 확장자가 class인 파일을 이 디렉토리로 옮겨야 함 c:\classdir\ime\kut\sangjin\Numeric.class
이 클래스를 사용하고 싶은 자바 프로그램에서는 다음과 같은 import 문을 사용함 import ime.kut.sangjin.Numeric; 47/48 47 /48
패키지 – 계속 클래스 라이브러리 등은 .jar 또는 .zip으로 압축되어 제공되는 경우가 있음. 이 때 CLASSPATH 설정은 압축파일이 있는 디렉토리가 아니라 압축파일까지 CLASSPATH에 설정해주어야 함 예) myclass.zip에 여러 패키지들이 압축되어 제공되어 있다. 이 파일이 c:\javalib j 디렉토리에 디렉 리에 있는 경우에는 CLASSPATH를 를 다음과 같이 설정해주어야 한다. set CLASSPATH=.;C:\javalib\myclass.zip E li Eclipse에서는 에서는 CLASSPATH를 인식하지 못함. 못함 대신 project를 j t를 선택하여 폴더, jar, zip 파일을 import함 c:\classdir\ime\kut\sangjin\Numeric.class가 gj 가있 있고,, 이 클래 클래스의 의 패키지가 ime.kut.sangjin이면 c:\classdir\ime 폴더를 import함
48/48 48 /48
자바프로그래밍(CPA240) NOTE 08
©copyright 2010
인터페이스와 다형성
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 자바에서 모든 인자는 call-by-value 형태로 전달됨 생성자 일반 멤버변수 중 객체마다 다른 값으로 초기화되어야 하는 경우에는 생성자에서 초기화하고, 객체마다 같은 값으로 초기화 되어야 하는 경우에는 명백한 초기화나 초기화 블록을 이용함 아무런 생성자를 제공하지 않으면 시스템에서 기본 생성자를 제공하여 줌 static 메소드는 숨겨진 인수 this가 없음 static 메소드는 객체의 상태를 접근할 필요가 없는 메소드를 정의할 때, 때 또는 클래스의 static 멤버변수를 조작하는 메소드를 정의할 때 사용함 static 멤버는 명백한 초기화 또는 static 초기화 블록을 이용하여 초기화해야 함 자바의 메모리는 크개 스택, 힙, static 필드 영역으로 구분됨
2/38
교육목표 Object 클래스 interface 다형성(polymorphism) 전략 객체(strategy object) 내부 클래스(inner 클래스(i class) l ) 익명의 내부 클래스(anonymous inner class)
3/38
범용 정렬 예) 다음과 같은 클래스를 생각하여 보자. public class GenericSorter{ public static void sort(int[] array){ … } // sort } // class GenericSorter
static 메소드이므로 객체를 생성하지 않고 호출할 수 있음 문제점: 정수 타입의 배열만 정렬할 수 있음 문제점 해결책: Object 클래스를 이용 해결책 public class GenericSorter{{ p public static void sort(Object[] array){ … } // sort 범용 프로그래밍은 코드 중복을 } // class GenericSorter 줄이는 매우 효과적인 방법임
4/38
범용 정렬 – 계속 Java 5의 5의 해결책 해결책: template 사용 (범용 메소드) public class GenericSorter{ public static <T> void sort(T[] array){ … } // sort } // class GenericSorter
template이 진보된 형태이지만 template이 없을 때 존재하던 문제점은 여전히 남아 있음 즉, template이 제공되어도 범용 프로그래밍을 하기 위해서는 여전히 interface 개념이 자바에서 필요함 (나중에 자세히 설명함) 공통 연산의 제공
5/38
Object 클래스 모든 자바 클래스는 기본적으로 Object 클래스를 상속 받음 즉, Object 클래스는 상속 계층구조에서 가장 최상위에 있는 클래스임 그러나 다음과 같이 작성할 필요는 없음 class BankAccount extends Object{ … B 클래스가 A 클래스의 자손 클래스이면 B 클래스의 인스턴스는 A 클래스 타입의 객체 변수에 (강제 타입 변환 없이) 대입할 수 있음 하지만 A 클래스의 인스턴스를 B 클래스 타입의 객체 변수에는 (강제 타입 변환을 하지 않고는) 대입할 수 없음 예) class People{ People p1; … } // class People class Student extends People{ … } // class Student
People p2 = new People(); Student s1; Student s2 = new Student(); p1 = s2; // OK s1 = p2; // ERROR
6/38
Object 클래스 – 계속 따라서 모든 객체는 Object 타입의 객체 변수에 대입할 수 있음 Template 기능이 제공되기 전에는 Object 타입의 변수는 임의의 객체를 참조할 수 있으므로 범용 자료구조나 범용 메소드를 만들 때 많이 사용됨 Object 타입을 이용하여 범용 자료구조나 범용 메소드를 만들 때 문제점 원시 타입은 Object 타입의 객체 변수에 대입할 수 없음 해결책.. Wrapper 클래스를 이용함 해결책 Object 클래스에 대해서는 강의노트 09에서 보다 자세히 설명함
7/38
범용 정렬 – 계속 예) 다음과 같은 클래스를 생각하여 보자. public class GenericSorter{ public static void sort(Object[] array){ for(int i = 0; i<array.length-1; i++) for(int j = i+1; i<array.length; j++) if( if(array[j].compareTo(array[i])<0){ [j] T ( [i]) 0){ Object tmp = array[j]; array[j] = array[i]; array[i] = tmp; } } // sort } // class GenericSorter
문제점 정렬하고자 하는 객체가 compareTo 메소드를 제공하지 문제점: 않을 수 있음 해결책: compareTo 메소드를 제공하는 객체의 배열만 인자로 해결책 받을 수 있도록 함. How? interface 8/38
범용 프로그래밍 범용 프로그래밍에서 필요한 것 다양한 종류의 데이터를 모두 저장할 수 있는 데이터 타입이 필요하다. Java 2에서 해결책: Object 클래스 Java 5에서 해결책: template 처리할 데이터들이 어떤 특정 메소드를 제공해야 함 해결책: interface 사용 해결책
9/38
interface interface는 interface를 구현하는 클래스가 반드시 제공해야 하는 메소드의 목록을 말함 interface는 클래스와 유사하게 정의하지만 interface는 멤버 변수를 가질 수 없으며, 메소드의 내용을 정의할 수 없음. 없음 이렇게 내용을 정의할 수 없는 메소드를 추상 메소드 메소드(abstract method)라 함 interface의 메소드는 자동적으로 public임 interface에 상수는 정의할 수 있음 interface의 상수는 자동으로 public static final임 따라서 인스턴스를 생성할 수 없다 없다.. 하지만 interface와 추상 클래스는 다른 것임
10/38 10 /38
interface – 계속 예) public interface Comparable{ int compareTo(Object other); } // interface Comparable
public interface Comparable Comparable<T>{ T { int compareTo(T other); // Java 5 } // interface Comparable
public bli static t ti void id sort(Comparable[] t(C bl [] array){ ){ … } public static void sort(Comparable<T>[] array){ … } Comparable 인터페이스는 java.lang java lang 패키지에서 제공되는 인터페이스임 클래스는 특정한 interface를 구현할 수 있음 구현할 수 있는 interface 수에 제한은 없음 비고.. 오직 하나의 클래스만 상속 받을 수 있음 비고 interface를 구현하면 그 interface에 정의되어 있는 모든 메소드를 무조건 구현해야 함. 이 때 implements 키워드를 사용함 비고.. 상속을 할 때에는 extends 키워드를 사용함 비고 11/38 11 /38
interface의 구현 예)
class BankAccount implements Comparable{ public int compareTo(Object other){ B kA BankAccount t o = (B (BankAccount)other; kA t) th if(this==o) return 0; if(balance<o.balance) return -1; else if(balance if(balance>o.balance) o.balance) return 1; else return 0; // return balance – o.balance; } // compareTo … 비고. compareTo 메소드에서 인자 other이 } // class BankAccount null이면 NullPointerException 예외가 발생하며, BankAccount 타입으로 타입 변환할 수 없으면 ClassCastException Cl C tE ti 예외가 발생함
BankAccount는 B kA t는 Comparable C bl interface를 i t f 를 구현하고 있으므로 Comparable 타입으로 변환될 수 있음
12/38 12 /38
interface의 구현 – 계속 예)
class BankAccount implements Comparable<BankAccount>{ public int compareTo(BankAccount other){ // Java 5 if(thi if(this==other) th ) return t 0 0; return balance – other.balance; } // compareTo … } // class BankAccount 비고. Java 2 때와 달리 타입 변환이 필요 없음 비고 template 기능이 없을 때에는 각 클래스마다 다른 종류의 인자가 필요하므로 인자 타입을 j 정의할 수 밖에 없었음 Object로 자세한 것은 강의노트 11에서 다시 설명함
13/38 13 /38
compareTo 메소드 compareTo 메소드는 두 객체를 비교할 때 사용하는 메소드로서 다음을 반환해야 함 음수: 호출된 객체가 파라미터로 전달된 객체보다 앞에 위치할 때 예) "hello".compareTo("help") 0: 호출된 객체와 파라미터로 전달된 객체의 순위가 같을 때 양수: 호출된 객체가 파라미터로 전달된 객체보다 뒤에 위치할 때 이메 메소드를 를 구현할 때 다음이 만족되 만족되도록 록 구현해야 함 (비대칭성) 동일 객체의 비교는 항상 0을 반환해야 함 (3분법) 두 객체 o1과 o2를 비교하였을 때, o1의 순위가 o2와 같거나 앞에 있거나, 같거나, 있거나 뒤에 있어야 함 (추이성) 객체 o1이 o2 앞에 있고, o2는 o3 앞에 있으면 o1은 o3 앞에 있어야 함
14/38 14 /38
타입 변환 interface 타입의 변수를 선언할 수 있음 어떤 클래스가 어떤 interface를 구현하였으면 그 interface 타입의 변수에 강제 타입 변환 없이 대입할 수 있음 예) BankAccount account = new BankAccount(10000); Comparable x = account; // OK x = new Rectangle(5, 10, 20, 30); // ERROR
Rectangle은 g 은 Comparable p interface를 를 구현하 구현하고 있지 않음 특정 타입의 변수(class 또는 interface)를 통해서는 그 타입에 정의되어 있는 메소드만 호출할 수 있음 변수가 실제 가리키고 있는 타입은 호출 가능 여부를 고려할 때에는 중요하지 않음 예) BankAccount account = new BankAccount(10000); Comparable x = account; System.out.printf("%,d원", x.getBalance()); // ERROR
15/38 15 /38
타입 변환 – 계속 종종 interface 타입을 다시 원래의 객체 타입으로 변환할 필요가 있음. 이 경우에는 강제 타입 변환을 해주어야 함 예) BankAccount a = (BankAccount) x; System.out.printf("%,d원", a.getBalance()); // OK
이 때 x가 BankAccount 객체를 참조하고 있지 않으면 ClassCastException 예외가 발생함 instanceof 연산자는 어떤 객체가 어떤 클래스의 인스턴스인지를 검사해 줌 B 클래스가 A의 후손 클래스이면 B 타입의 객체 b에 대해 b instanceof A의 평가 결과는 true임 이런 결과를 원하지 않으면 getClass() 메소드를 이용하여 비교함 if( instanceof if(x i t f BankAccount){ B kA t){ 예) BankAccount a = (BankAccount) x; System.out.printf("%,d원", a.getBalance());
} 16/38 16 /38
다형성 변수의 타입이 interface인 경우가 종종 있다. 예를 들어 Comparable x; 와 같은 변수 x가 가 있으면 x가 가 참조하는 객체의 타입은 Comparable이 C bl 이 아님. x가 참조하는 객체의 타입은 Comparable를 구현한 클래스의 인스턴스임 x는 그것의 수명 동안 다른 타입의 객체를 가리킬 수 있다. x = new BankAccount(10000); x = new Date(2004,12,25); Date(2004 12 25); 비고.. interface의 인스턴스는 생성할 수 없음 비고 Comparable x = new Comparable(); // ERROR interface 타입의 변수 x가 어떤 타입의 객체를 가리키는지 알지 못하는 경우에도 x를 이용하여 interface에 정의된 메소드를 호출할 수 있음 예) int val = x.compareTo(y);
17/38 17 /38
다형성 – 계속 interface 타입의 변수 x를 이용해서는 interface에 정의된 메소드만 호출할 수 있다. 예) Comparable<BankAccount> x = new BankAccount(10000); Comparable<BankAccount> y = new BankAccount(5000); int comp = x.compareTo(y); x compareTo(y); // ok int n = x.getBalance(); // error
int val = x.compareTo(y); p (y); x가 어떤 객체를 참조하느냐에 따라 어떤 메소드가 호출되는지 결정됨 이와 같이 참조하고 있는 실제 객체에 따라 호출되는 메소드가 결정되는 특성을 다형성 다형성(polymorphism)이라 함 객체의 실제 타입에 따라 이름이 같더라 같더라도 그 행위는 다를 수 있음 예) open the door, open the window
18/38 18 /38
다형성 – 계속 Early binding vs. Late binding 예) 메소드 오버로딩 BankAccount account = new BankAccount(); BankAccount account = new BankAccount(1000); 위와 같은 경우 컴파일러는 파라미터의 타입을 보고 어떤 생성자를 호출할지 결정할 수 있다. 이처럼 컴파일러가 호출될 메소드를 결정하는 것을 early binding이라 binding 함 예) x.compareTo(y) 이 경우에는 x가 어떤 타입의 객체를 참조하고 있는지에 따라 실제 호출될 메소드가 결정됨. 결정됨 그러나 많은 경우 컴파일러는 x가 어떤 타입의 객체를 참조하는지 알 수 없다. 따라서 이 경우에는 프로그램이 실행될 때 JVM이 어떤 메소드를 호출할지 결정함. 이렇게 컴파일러가 결정하지 않고 실행될 때 호출되는 메소드를 결정하는 것을 late binding이라 binding 함
19/38 19 /38
interface를 이용한 상수의 정의 예)
public interface Week{ int SUN = 0; int MON = 1; int TUE = 2; int WED = 3; int THU = 4; int FRI = 5; int SAT = 6; }
interface의 상수는 자동으로 public static final이다. C/C++의 열거형 개념과는 차이가 있다. enum Week {SUN, MON, TUE, WED, THU, FRI, SAT};
Java 5부터는 열거형 타입을 제공함 (강의노트 06 참조)
20/38 20 /38
Marker Interface Marker interface란 내용이 빈 인터페이스를 말함 이 인터페이스는 어떤 특정 메소드를 제공한다는 것을 나타내기 위해 사용하지 않고, 클래스가 어떤 특성을 가지고 있다는 것을 나타내기 위해 사용됨 예) Serializable, Serializable Cloneable
21/38 21 /38
통계 정보 // 일련의 데이터의 평균을 계산하여 주는 클래스 public class DataSet{ private p ate doub double e su sum = 0; private double count = 0; private Measurable max = null; public DataSet(){ } // DataSet D t S t public void add(Measurable x){ sum = sum + x.measure(); ( || if(count==0 max.measure()<x.measure()) max = x; count++; } // add dd public double getAverage(){ if(count == 0) return 0; else return sum / count;; } // getAverage public Measurable getMaximum() { return max; } // getMaximum } // class DataSet
public interface Measurable{{ p double measure(); } // interface Measurable
장점 Measurable interface를 구현하는 모든 클래스의 평균을 계산할 수 있다. 단점 1. Rectangle과 같은 기존 클래스나 직접 수정할 수 없는 클래스는 Measurable interface를 구현하도록 만들 수 없다. 2. 한 가지 방법으로만 측정할 수 있다. Employee은 연봉에 의해 또는 나이에 의해 측정할 수 있다.
22/38 22 /38
Strategy Object 이 문제를 극복하기 위해 측정을 담당하는 별도의 객체를 만듬 public interface Measurer{ double measure(Object anObject); } // interface Measurer public class RectangleMeasurer implements Measurer{ public double measure(Object anObject){ Rectangle aRectangle = (Rectangle)anObject; g g () double area = aRectangle.getWidth() * aRectangle.getHeight(); return area; } // measure } // class RectangleMeasurer
RectangleMeasurer 객체는 어떤 계산을 위한 특정한 전략을 수행 해주기 때문에 전략 객체 객체(strategy object)라 함
23/38 23 /38
Strategy Object Measurer m = new RectangleMeasurer(); DataSet data = new DataSet(m); data.add(new Rectangle(5, 10, 20, 30)); data.add(new Rectangle(10, 20, 30, 40)); …
public class DataSet{ private double sum = 0; private double count = 0; private Object max = null; private Measurer measurer; public DataSet(Measurer aMeasurer){ measurer = aMeasurer; } // DataSet public void add(Object x){ sum = sum + measurer.measure(x); measurer measure(x); if(count==0 || measurer.measure(max)<measurer.measure(x)) max = x; count++; } // add public double getAverage(){ if(count == 0) return 0; else return sum / count; } // getAverage public Object getMaximum() { return max; } } // class DataSet
24/38 24 /38
Strategy Object class BankAccountMeasurer implements Measurer{ public double measure(Object anObject){ BankAccount a = (BankAccount)anObject; public class DataSet{ return a.balance; private double sum = 0; } // measure private double count = 0; } // BankAccountMeasurer private Object max = null; private Measurer measurer; public DataSet(Measurer aMeasurer){ measurer = aMeasurer; } // DataSet public void add(Object x){ sum = sum + measurer.measure(x); measurer measure(x); if(count==0 || measurer.measure(max)<measurer.measure(x)) max = x; count++; Measurer m = new BankAccountMeasurer(); DataSet data = new DataSet(m); } // add data.add(new BankAccount(10000)); public double getAverage(){ data.add(new BankAccount(5000)); if(count == 0) return 0; … else return sum / count; } // getAverage public Object getMaximum() { return max; } } // class DataSet
25/38 25 /38
Inner Class RectangleMeasure 클래스는 어떤 상태를 가지지 않는 매우 간단한 클래스임 이런 클래스는 아주 제한적인 용도로 사용되기 때문에 이 클래스를 사용하는 메소드 내에 정의할 수 있음 public static void main(String[] args){ class RectangleMeasurer implements Measurer{ … } // RectangleMeasurer g Measurer m = new RectangleMeasurer(); DataSet data = new DataSet(m); … } // main
이처럼 어떤 클래스 내부에 정의된 클래스를 내부 클래스 클래스(inner class) 라함
26/38 26 /38
Inner Class – 계속 내부 클래스를 사용하는 이유 이유 1. 내부 클래스의 객체는 그것을 생성한 객체의 모든 데이터를 접근할 수 있음 이유 2. 내부 클래스는 같은 패키지에 있는 다른 클래스로부터 숨길 수 있음 이유 3. 사건 중심 프로그램을 작성할 때 유용하게 사용됨
27/38 27 /38
Inner class의 두 가지 형태 class OuterClassName{ method signature{ … class InnerClassName{ … } // inner class … } … 형태 1. 1 }
class OuterClassName{ accessSpecifier class InnerClassName{ … } // inner class … }
형태 2. 2
형태 1의 내부 클래스의 메소드는 그것이 정의된 메소드의 지역 변수를 접근할 수 있다 이 때 지역 변수는 final이어야 있다. fi l이어야 한다. 한다 형태 1과 형태 2의 내부 클래스의 메소드는 외부 클래스의 메소드가 접근할 수 있는 모든 데이터를 접근할 수 있다. 즉, 내부 클래스는 외부 클래스에 대한 참조를 가지고 있다 있다. 중요. 형태 1의 내부 클래스는 public, private과 같은 access specifier를 사용하지 않는다.
28/38 28 /38
public class Outer{ public class Test{ private int oVal; public static void main(String[] args){ private Inner x; Outer obj = new Outer(2, 3); private class Inner{ obj.g(3); private int iVal; obj.print(); public Inner(int n){ } // main } // Test iVal = n; 결과: oVal = 6, iVal = 9 } public void f(int n){ obj iVal = oVal + n; 내부 클래스는 외부 클래스의 } private 멤버를 접근할 수 있다. oVal X } // class Inner 6 2 public Outer(int n1, n1 int n2){ oVal = n1; x = new Inner(n2); } 외부 클래스는 내부 클래스의 private 멤버를 접근할 수 있다. public void g(int n){ 3 9 oVal = x.iVal + n; iVal Outer x.f(n); } public void print(){ 내부 클래스의 객체가 생성될 때 System.out.printf("oVal = %d, iVal = %d", 이 객체의 외부 객체에 대한 참조는 oVal, x.iVal); 현재 객체의 this값으로 설정된다. } } // class Outer 29/38 29 /38
public class Outer{ private int val; i t Inner I private x; private class Inner{ private int val; public Inner(int n){ 앞 슬라이드와 동일한 프로그램이다. val = n; 이 경우 내부 클래스의 멤버와 } 외부 클래스의 멤버 이름이 충돌한다. public void f(int n){ 이 때 내부 클래스에서 val을 사용하면 vall = Outer.this.val O t thi l + n; 이것은 내부 클래스에 정의된 멤버를 } 의미한다. 외부 클래스의 멤버를 } // class Inner 접근하고 싶으면 외부 객체에 대한 public Outer(int n1, int n2){ 참조를 사용해야 한다. val = n1; 외부 객체에 대한 참조는 다음과 같은 x = new Inner(n2); 형태를 취한다. } 외부클래스이름.this 외부클래 이름 public bli void id g(int (i t n){ ){ val = x.val + n; x.f(n); } public void print(){ System.out.printf("oVal = %d, iVal = %d", oVal, x.iVal); } } // class Outer 30/38 30 /38
타이머 사건의 처리 javax.swing 패키지에 있는 Timer 클래스는 정해진 시간 간격마다 사건을 발생하여 줌 객체를 주기적으로 갱신하고 싶을 때 유용하게 사용할 수 있음 타이머 사건이 발생하면 타이머는 타이머 사건을 기다리고 있는 event listener에게 사건의 발생을 통보하여 줌 자바는 사용자들이 쉽게 event listener를 만들 수 있게 다음과 같은 interface를 제공함 public interface ActionListener{ void actionPerformed(ActionEvent event); } // interface ActionListener class MyListener implements ActionListener{ void actionPerformed(ActionEvent event){ … // 타이머 사건이 발생하였을 때 // 하고 싶은 작업 } MyListener listener = new MyListener(); } // class MyListener Timer t = new Timer(interval, listener); t.start(); 31/38 31 /38
타이머 사건의 처리 – 계속 public class TimerTest{ public static void main(String[] args){ class c ass Cou CountDown t o implements p e e ts ActionListener{ ct o ste e { private int count; public CountDown(int initialCount){ count = initialCount; } public void actionPerformed(ActionEvent event){ if(count>=0) System.out.println(count); ( ) System.out.println("Lift y p ( off"); ); if(count==0) count--; } } // class CountDown C CountDown tD li listener t = new CountDown(10); C tD (10) Timer t = new Timer(1000, listener); // milliseconds t.start(); JOptionPane.showMessageDialog(null, p g g( , "Quit?"); Q ); System.exit(0); } // main import java.awt.event.ActionEvent; } // class TimerTest import java.awt.event.ActionListener; javax swing JOptionPane; import javax.swing.JOptionPane; import javax.swing.Timer;
32/38 32 /38
타이머 사건의 처리 – 계속 InterestAdder 클래 클래스는 는 내부 클래스이므로 이 클래스가 선언된 메소드의 지역변수를 접근할 수 있다. 하지만 오직 final인 인 지역변수만 접근할 수 있다.
public class TimerTest{{ p private static final double RATE = 5; public static void main(String[] args){ final BankAccount account = new BankAccount(1000); class InterestAdder implements ActionListener{ public void actionPerformed(ActionEvent event){ double interest = account.getBalance() * RATE / 100; p ( ); account.deposit(interest); System.out.printf("Balance = %d%n", account.getBalance()); } // actionPerformed } // class InterestAdder InterestAdder listener = new InterestAdder(); Timer t = new Timer(1000, listener); t.start(); p g g( , "Quit?"); ); JOptionPane.showMessageDialog(null, System.exit(0); } // main import java.awt.event.ActionEvent; } // class TimerTest
import java.awt.event.ActionListener; import javax.swing.JOptionPane; import javax.swing.Timer;
33/38 33 /38
타이머 사건의 처리 – 계속 final BankAccount account = new BankAccount(1000); final 변수란 한번 할당하면 변경할 수 없는 변수를 말한다. 그러나 이 경우 final인 것은 account라는 객체 변수이지 객체 자체가 아님 예) final BankAccount account1 = new BankAccount(1000); BankAccount account2 = new BankAccount(500); account1 = account2; // error account1.deposit(500); t1 d it(500) // ok k
34/38 34 /38
Anonymous Inner Class 이름이 없으면 anonymous라는 용어를 사용한다. 예) Coin aCoin = new Coin(10); data.add(aCoin); 같은 메소드 내에 aCoin이 다시 사용될 필요가 없으면 다음과 같은 문으로 바꿀 수 있음 data.add(new Coin(10)); 이 때 "new Coin(10)"은 이름이 없는 객체(anonymous object)임
35/38 35 /38
Anonymous Inner Class – 계속 RectangleMeasurer처럼 단일 인스턴스만 필요한 경우에는 인스턴스를 생성할 때 클래스를 정의할 수 있음. 이처럼 이름이 없는 클래스를 anonymous class라 l 라함 권장되는 프로그램 스타일은 아니다. public static void main(String[] args){ Measurer m = new Measurer() { public double measure(Object anObject){ … } // measure }; DataSet data = new DataSet(m); … } // main
36/38 36 /38
Builder public class Time{ 생성자가 받아야 하는 매개변수가 private final int hour; private final int minute; 많을 경우 사용할 수 있는 기법 private final int second; public static class Builder{ private int hour = 0; private int minute = 0; Time t = new Time.Builder().hour(6).minute(30).build(); private int second = 0; public Builder(){} public Builder hour(int h){ hour = ((h>=0 && h<24) ? h : 0); return this; } public Builder minute(int m){ minute = ((m>=0 && m<60) ? m : 0); return this; } public Builder second(int s){ second = ((s>=0 && s<60) ? s : 0); return this; } public Time build(){ return new Time(this); } } // Builder private Time(Builder builder){ hour = builder.hour; minute = builder.minute; second = builder.minute; } } // class A
37/38 37 /38
static inner 클래스 내부 클래스가 그것이 정의된 외부 클래스에 대한 참조가 필요 없으면, 즉, 내부 클래스에서 외부 클래스에 정의된 멤버변수나 메소드를 접근할 필요가 없으면 내부 클래스를 정의할 때 static t ti 키워드를 사용할 수 있음 이런 내부 클래 클래스를 를 “nested class”라고 라 불리며, 단순히 한 클래 클래스를 를 다른 클래스 내부에 정의하기 위해 사용된 경우라 볼 수 있음 예) class ArrayAlg{ private static class Pair{ … } // class l Pair P i … } // class ArrayAlg
38/38 38 /38
©copyright 2010
자바프로그래밍(CPA240) NOTE 08-01
GUI 세 번째
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
교육목표 JTextArea Scroll Bar ImageIcon JCheckBox JR di B tt JRadioButton JComboBox JList
2/10
JTextArea 두 번째 Gui 노트에서 설명한 JTextField는 한 줄의 텍스트만 입력 가능 JTextArea는 다중 라인을 입력할 수 있음 JTextArea를 생성할 때에는 보여질 라인 수, 각 라인별 문자 수 등을 설정할 수 있음 예) JTextField nameField = new JTextField(30); JTextArea editField = new JTextArea(5,30);
유용한 메소드 public String getText() public bli boolean b l i Edit bl () isEditable() public void setEditable(boolean argument) public void setText(String text)
3/10
Scroll Bar JScrollPane를 사용하면 JTextArea에 scroll bar를 추가할 수 있음 JPanel textPanel = new JPanel(); JTextArea memo = new JTextArea(5,32); JScrollPane scrollPane = new JScrollPane(memo); scrollPane.setHorizontalScrollBarPolicy( JScrollPane HORIZONTAL SCROLLBAR ALWAYS); JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); scrollPane.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); textPanel.add(scrollPane); frame.add(textPanel, BorderLayout.CENTER); JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS JScrollPane.HORIZONTAL_SCROLLBAR_NEVER JScrollPane.HORIZONTAL_SCROLLBAR_NEEDED
4/10
아이콘 포함 버튼 버튼에 아이콘을 포함할 수 있음 JButton clearButton = new JButton("Clear"); ( ); ImageIcon happyIcon = new ImageIcon("smile.jpg"); clearButton.setIcon(happyIcon);
JL b l도 같은 방법으로 아이콘을 포함할 수 있음 JLabel도
5/10
특수 TextField JPasswordField JPasswordField는 JTextField와 동일하지만 입력할 때 입력한 내용 대신에 *가 표시됨 패스워드와 같이 입력되는 내용을 숨길 때 사용됨
6/10
JCheckBox 생성은 버튼과 유사 다만, ActionListener 대신에 ItemListener로 이벤트 처리 actionPerformed 메소드 대신에 itemStateChanged 메소드 itemStateChanged 메소드 내에서는 isSelected 메소드를 활용함 JCheckBox boldChkBox = new JCheckBox("bold"); JCheckBox italicChkBox = new JCheckBox( italic ); JCheckBox("italic"); CheckBoxHandler handler = new CheckBoxHandler(); boldChkBox.addItemListener(handler);
7/10
JRadioButton CheckBox는 서로에 영향을 받지 않지만 RadioButton은 서로 영향을 받음 이에 그룹으로 묶어 관리하며, 그룹 중 하나의 버튼만 활성화 가능 JRadioButton plainRadioBtn = new JRadioButton( JRadioButton("plain"); plain ); JRadioButton boldRadioBtn = new JRadioButton("bold"); JRadioButton italicRadioBtn = new JRadioButton("italic"); JRadioButton bothRadioBtn = new JRadioButton("bold+italic"); ButtonGroup fontStyleGroup = new ButtonGroup(); fontStyleGroup.add(plainRadioBtn); fontStyleGroup.add(boldRadioBtn); fontStyleGroup.add(italicRadioBtn); fontStyleGroup.add(bothRadioBtn);
8/10
JComboBox 리스트에서 하나를 선택할 때 사용함 ItemListener로 이벤트 처리 String[] monthAbbr = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", JUL , "AUG", AUG , "SEPT", SEPT , "OCT", OCT , "NOV", NOV , "DEC"}; DEC }; "JUL", JComboBox monthComboBox = new JComboBox(monthAbbr);
public void itemStateChanged(ItemEvent e){ if(e.getStateChange()==ItemEvent.SELECTED){ textField setText( textField.setText( monthAbbr[monthComboBox.getSelectedIndex()]); } } 9/10
JList Combo Box와 유사하지만 다중 선택이 가능함 ListSelectionListener로 이벤트 처리 다중 선택의 경우에는 리스트에서 선택하고 다른 버튼을 통해 선택된 항목(getSelectedValues)들을 알아낼 수 있음 progSkillList = new JList(progLang); progSkillList.setVisibleRowCount(5); progSkillList.setSelectionMode( Li tS l ti M d l MULTIPLE INTERVAL SELECTION) ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); … listPanel.add(new JScrollPane(progSkillList));
10/10 10 /10
©copyright 2010
자바프로그래밍(CPA240) NOTE 09
상속
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 interface는 어떤 특정 메소드들을 제공하는 클래스들을 분류하기 위해 사용됨. 예를 들어 Comparable를 구현하는 클래스들은 모두 compareTo T 메소드를 가지고 있음 interface는 클래스와 유사하게 정의하지만 일반 멤버변수를 가질 수 없으며, 없 며, 메 메소드의 의 내용을 정의할 수 없음 없음. 따라서 interface의 의 인스턴스는 생성할 수 없음 interface 타입 변수에 이 interface를 구현한(implement) 클래스의 객체를 대입할 수 있음. 있음 이 때 이 변수를 통해 호출할 수 있는 메소드는 오직 interface에 정의되어 있는 메소드 뿐임 내부 클래스(inner class)는 메소드 내 또는 다른 클래스 내에 정의된 클래스를 말함. 이 메소드 외에는 사용되지 않는 클래스, 이 클래스 외에는 사용되지 않는 클래스는 이렇게 정의하여 사용할 수 있음 한 클래스 내에 정의된 내부 클래스는 그것의 외부 클래스 멤버를 접근 권한과 무관하게 접근할 수 있음. 한 메소드 내에 정의된 내부 클래스는 그 메소드의 final 지역변수를 접근할 수 있음
2/48
교육목표 상속 추상 클래스 접근 제어 Object 클래스의 toString, equals, clone 메소드
3/48
상속 상속 상속(inheritance): 객체지향 프로그래밍에서 가장 근본이 되는 개념 상속은 기존 클래스를 이용하여 새 클래스를 만들 수 있게 해주며, 이를 통해 기존 코드를 재사용(반복하여 정의하지 않고)할 수 있음 예) 저축계좌 클래스를 만들고자 함. 저축계좌는 은행계좌의 특수한 경우이므로 은행계좌를 상속하여 만들 수 있음 public class SavingsAccount extends BankAccount{ … } // class SavingsAccount
이 때 SavingsAccount는 서브/자식/파생(sub/child/derived) 클래스라 하며, 하며 BankAccount는 슈퍼/부모/기저 (super/parent/base) 클래스라 함 (is is--a 관계 관계) 상속할 때 자바에서 extends라는 키워드를 사용함
4/48
상속 – 계속 상속계층도(inheritance hierarchy): 클래스 간에 상속 관계를 나타내는 도표 (UML에서 사용하는 표기법) 비고.. 자바에서 특정한 클래스를 상속받지 않는 클래스는 비고 기본적으로 Object 클래스를 상속받음 자식 클래스의 객체가 부모 클래스의 객체보다 큼 자식 클래스의 객체는 자신만의 멤버변수뿐만 Object 아니라 부모 클래스의 모든 멤버변수를 가지고 있음 자식 클래스의 객체가 부모 클래스의 객체보다 많은 기능을 가지고 있음 BankAccount 자식 클래스의 객체는 자신만의 공개 메소드를 제공할 뿐만 아니라 부모 클래스의 공개 메소드를 모두 제공함 비고.. 슈퍼/서브는 집합 개념에서 온 용어임 비고 Savings Account
5/48
상속 – 계속 예) 상속의 예
People
Employee
Faculty
Student
Staff
Under graduate Student
Graduate Student
6/48
상속 – 계속 예) 다음과 같은 상속계층도를 고려하여 보자. 예금계좌: 월마다 이자를 지급함 이자 지급하는 메소드 필요 BankAccount 당좌계좌: 예금계좌와 달리 이자가 없으며, 없으며 거래마다 비용을 지불해야 함. 매월 일정한 거래 수까지는 비용 지불 없이 이루어질 S i Savings Ch ki Checking 수 있음 Account Account 비용을 청구하는 메소드 필요 둘 다 공통적으로 일반적인 계좌 특성을 가지고 있음 예: 둘 다 잔액 정보가 필요함
7/48
상속 – 계속 public class BankAccount{ private int balance; public bli BankAccount(){ B kA t(){ this(0); } // BankAccount() public BankAccount(int amount){ balance = amount; } // BankAccount(int) public int getBalance(){ return t b l balance; } // getBalance public void deposit(int amount){ balance += amount; } // deposit public void withdraw(int amount){ balance -= amount; } // withdraw ithd } // class BankAccount
public class SavingsAccount extends BankAccount{ private double interestRate; public SavingsAccount( int amount, double rate){ super(amount); interestRate = rate; } // SavingsAccount public void addInterest(){ int interest = getBalance() * interestRate / 100; deposit(interest); } // addInterest } // class SavingsAccount
8/48
상속 – 계속 BankAccount SangjinsAccount = new BankAccount(10000); SavingsAccount S i A t PetersAccount P t A t = new SavingsAccount(5000,10); S i A t(5000 10) SangjinsAccount.withdraw(1000); PetersAccount.deposit(1000); (); PetersAccount.addInterest();
SangjinsAccount
PetersAccount
balance
int getBalance(); void deposit(int amount); void withdraw(int amount);
balance
5000
10000
interestRate
int getBalance(); void deposit(int amount); void withdraw(int amount); void addInterest();
10.0
9/48
super 키워드 super 키워드 부모 클래스의 멤버변수나 메소드를 접근할 때 사용됨. 보통 자식 클래스에 정의된 멤버변수나 메소드의 이름이 부모에 정의된 이름과 충돌이 있는 경우에만 사용 사용됨 부모에 정의된 멤버변수나 메소드가 private이면 super를 이용하여도 접근할 수 없음 이용하여 비고.. this (this는 객체에 대한 참조이지만 super는 아님) 비고 super 키워드를 이용하여 부모 클래스의 생성자를 호출할 수 있음 보통 자식 클래스에서는 부모 클래스에 정의되어 있는 멤버 변수를 초기화하기 위해 이 방식을 많이 사용함 this를 이용한 생성자 호출과 마찬가지로 생성자의 첫 문장 이어야 함 자식 클래스에서 부모 클래스의 생성자를 호출하지 않으면 부모 클래스의 기본 생성자가 기본적으로 호출됨 이 때 부모 클래스에 기본 생성자가 없으면 오류가 발생함 발생 super.super와 같은 형태로 조상의 멤버를 접근할 수 없음 10/48 10 /48
public class Parent{ private int p p pVal; public Parent(){ this(0); } // Parent public Parent(int n){ pVal = n; } // Parent(int) public void f(int ( n){ ){ pVal += n; } // f } // class Parent
public class Child extends Parent{ private int cVal; public bli Child(){ 부모 클래스의 생성자를 this(0); 호출하고 있지 않으므로 } // Child 부모 클래스 기본 생성자가 public Child(int n){ 자동으로 호출됨 super(n); cVal = n; } // Child(int) public bli void id f(int f(i t n){ ){ super.f(n)이 아니라 super.f(n); f(n)을 호출하면 재귀 cVal -= n; 호출하는 형태가 됨 } // f } // class Child
public class Parent{{ p private int pVal; public Parent(int n){ pVal = n; } // Parent(int) } // class Parent
public class Child extends Parent{{ p private int cVal; public Child(int n){ 부모 클래스의 생성자를 cVal = n; 호출하고 하 있지 않 않으므로 } // Child(int) 부모 클래스 기본 생성자가 } // class Child 자동으로 호출되어야 하는데 부모 클래스는 기본 생성자가 정의되어 있지 않으므로 오류가 발생함 11/48 11 /48
상속 – 계속 다음 메소드의 내용을 자세히 살펴보자. public void addInterest(){ i t interest int i t t = getBalance() tB l () * iinterestRate t tR t / 100; 100 deposit(interest); } // addInterest
BankAccount의 B kA t의 balance b l 멤버 변수는 private이므로 i t 이므로 그것의 자식 클래스도 이름을 이용하여 접근할 수 없음 이메 메소드 내에서 g getBalance(), (), deposit() p () 메 메소드 호출은 출은 단순 이름으로 호출되고 있음. 따라서 실제는 this.getBalance()와 같음. 이것은 SavingsAccount의 getBalance() 메소드를 호출한다는 것을 의미함 SavingsAccount는 getBalance(), deposit() 메소드가 정의되어 있지 않음. SavingsAccount는 이 메소드들을 상속받고 있음 이렇게 하는 이유는 상속하더라도 기존에 설정된 제약은 그대로 유지되도록 하기 위함임. 즉, private 멤버는 오직 그 클래스에 정의된 p public 메 메소드를 를 통해서만 수정되어야 함 12/48 12 /48
상속 – 계속 CheckingAccount의 경우 입금을 하거나 인출한 횟수를 기억해야 함. 따라서 BankAccount의 deposit과 withdraw 메소드를 그대로 사용할 수 없음 이 경우 CheckingAccount는 deposit과 withdraw 메소드를 다시 작성해야 함 함. 이처럼 자식 클래 클래스가 가부 부모 클래 클래스에 에 정의되어 있는 메소도의 서명과 같은 메소드를 정의하는 것을 메소드 오버라이딩(overriding)이라 함 오버라이딩 비고.. 오버로딩(overloading) 비고 이 경우 다음과 같은 코드에서 CheckingAccount PetersAccount = new CheckingAccount(); PetersAccount.deposit(1000);
deposit()은 BankAccount가 아닌 CheckingAccount에 정의된 메소드임
13/48 13 /48
상속 – 계속 자식 클래스에 메소드를 정의할 때 가능한 시나리오. 경우 1. 부모 클래스의 메소드를 오버라이드 함 경우 2. 부모 클래스의 메소드를 상속 받음 메소드를 오버라이드하지 않는 이상 자동으로 모든 메소드를 상속함 경우 3. 부모 클래스에 없는 새 메소드를 정의함 자식 클래 클래스에 에 멤버변수를 정의할 때 가능한 시나리 시나리오. 경우 1. 부모 클래스의 멤버변수를 상속 받음 부모 클래스의 모든 멤버변수는 자동으로 상속 받음 경우 2. 새 멤버변수를 정의함 메소드와 달리 부모 클래스에 있는 멤버변수와 같은 이름의 멤버변수를 정의하면 같은 이름의 멤버변수가 두 개 존재하게 된다. 이것은 여러 가지 부작용을 초래할 수 있음
14/48 14 /48
상속 – 계속
public class CheckingAccount extends BankAccount{ private int transCount = 0; p private static final int FREE_TRANS = 3; private static final int TRANS_FEE = 50; public CheckingAccount(int amount){ super(amount); } // CheckingAccount(int) public void deposit(int amount){ transCount++; super.deposit(amount); } // deposit public void withdraw(int amount){ transCount++; super.withdraw(amount); } // withdraw public void deductFee(){ if(transCount>FREE_TRANS){ int fees = TRANS_FEE * (transCount – FREE_TRANS); super withdraw(fees); super.withdraw(fees); } transCount = 0; } // deductFee } // class CheckingAccount 15/48 15 /48
상속 – 타입변환 클래스의 객체는 조상 클래스의 객체로 자동 타입 변환될 수 있음 예) SavingsAccount PetersAccount = new SavingsAccount(10); BankAccount anAccount = PetersAccount; Object anObject = PetersAccount; anAccount deposit(1000); // ok anAccount.deposit(1000); anAccount.addInterest(); // error
이 경우 상위 클래스는 하위 클래스에 정의된 메소드를 알지 못한다. 따라서 anAccount.deposit(1000)은 가능하지만 anAccount.addInterest()은 가능하지 않음 CheckingAccount PetersAccount = new CheckingAccount(10); BankAccount anAccount = PetersAccount; anAccount.deposit(10000);
이 경우 호출되는 deposit은 CheckingAccount에 정의된 deposit임 16/48 16 /48
상속 – 타입변환(계속) 예) 다음과 메소드가 BankAccount에 정의되어 있다고 하자. public void transfer(int p ( amount, BankAccount other){ ){ withdraw(amount); other.deposit(amount); } // transfer
다음이 모두 가능함 BankAccount PetersAccount = …;; BankAccount SaraAccount = …; SaraAccount.transfer(1000, PetersAccount); CheckingAccount TomsAccount = …; SaraAccount.transfer(1000, TomsAccount);
17/48 17 /48
상속 – 다형성 예) 다형성 BankAccount[] accounts = new BankAccount[10]; accounts[0] = new SavingsAccount(2500,10); accounts[1] = new SavingsAccount(3000,12); accounts[2] = new CheckingAccount(10000); accounts[3] = new CheckingAccount(5000); accounts[4] = new BankAccount(2500); for(int i=0; i<5; i++){ accounts[i].deposit(1000); }
18/48 18 /48
객체 변수를 이용한 메소드의 호출 객체 변수를 이용하여 호출할 수 있는 메소드 객체 변수 타입과 메소드의 접근 제어에 의해 결정 객체 변수 타입의 클래스에 정의된 메소드 또는 이 클래스의 조상 클래스에 정의된 public 메소드 객체 변수가 실제로 가리키는 객체의 타입과 무관함 객체 변수를 이용하여 메소드를 호출할 때 실제 호출되는 메소드 객체 변수가 실제 실제로 가리키는 객체의 타입에 의해 결정 객체 변수의 타입과 무관함 인터페이스 변수를 이용하여 호출할 수 있는 메소드 인터페이스에 정의된 메소드만 호출 가능 인터페이스 변수를 이용하여 메소드를 호출할 때 실제 호출되는 메소드 인터페이스 변수가 실제로 가리키는 객체의 타입에 의해 결정
19/48 19 /48
public class A{ public void f(){ System.out.println("class Syste out p t ( c ass A:f()"); () ); } // f } // class A public interface C { void g(); } // interface C public class B extends A implements C{ public void f(){ System.out.println("class B:f()"); } // f public void g(){ System.out.println("class B:g()"); } // g public void h(){ System.out.println("class B:h()"); } // h } // class B
public bli class l T t{ Test{ public static void main(String[] args){ A a1 = new A(); a1.f(); a1 = new B(); a1.f(); a1.h(); // error C c1 1 = new B(); B() c1.g(); c1.h(); // error } // main } // class Test 결과 class A:f() class B:f(); class B:g();
20/48 20 /48
추상 클래스 상속 계층도에서 위쪽으로 갈 수록 점점 일반화됨 너무 일반화되어 더 이상 인스턴스를 가지는 클래스가 아닌 상속을 위하여 만들어진 클래스가 존재하게 됨 이런 클래스를 추상 클래스 클래스(abstract class)라 함 자바 문법적 측면에서 보면 추상 메소드 메소드(abstract method) (내용이 정의되어 있지 않는)를 가지는 클래스를 말함 추상 클래스의 모든 메소드가 추상 메소드일 필요는 없음 비고.. concrete class 비고 추상 클래스는 인스턴스를 생성할 수 없음 Message
TextMessage
VoiceMessage
MotionMessage
21/48 21 /48
추상 클래스 – 계속 abstract class Message{ private String sender; public Message(String from){ sender = from; } // Message public abstract void play(); public String getSender(){ return sender; } // getSender } // class Message Message[] msgs = new Message[5]; msgs[0] = new TextMessage(…); msgs[1] = new VoiceMessage(…); msgs[2] = new VoiceMessage(…); VoiceMessage( ); msgs[3] = new TextMessage(…); msgs[4] = new MotionMessage(…); for(int i=0; i<5; i++){ [i] l () msgs[i].play(); }
class TextMessage extends Messages{ private String text; … public void play(){ System.out.println(text); } // play } // class TextMessage class VoiceMessage extends Messages{ … public void play(){ … } } // class VoiceMessage class MotionMessage extends Messages{ … public void play(){ … } } // class MotionMessage
22/48 22 /48
final 메소드, final 클래스 클래스의 메소드를 final로 선언하면 이 클래스를 상속받는 클래스는 이 메소드를 오버라이드할 수 없음 클래스 자체를 final로 선언하면 이 클래스를 상속받을 수 없음 예) public final class String { … } 자바 라이브러리에서 제공하는 대부분의 클래스는 final 클래스로 선언되어 있음 public class C extends A{ public class A{ private int val; public A(){ val = 0; } // A public final void f(int n){ val += n; } // f } // class A
public final class B{ private int val; public B(){ val = 0; } // B public void f(int n){ val += n; } // f } // class B
public C(){ vall = 0; 0 } // A public void f(int n){ // error val += n; } // f } // class A public class D extends B{ // error … } // class D 23/48 23 /48
접근 제어 자바에서 제공하는 접근 제어 수준의 네 종류 public: 누구나 접근 가능 private: 그 클래스의 메소드만 접근 가능 protected: 클래스를 상속받은 클래스의 메소드와 같은 패키지 내에 있는 클래스의 메소드만 접근 가능 default (패키지 접근): 위 세 가지 access specifier를 지정하지 않는 경우로서, 같은 패키지 내에 있는 모든 메소드는 접근 접근할 수 있음 특수한 경우를 제외하고는 public 또는 private를 사용함 멤버 변수와 메소드는 기본적으로 private, private 메소드 중에 공개되어야 하는 메소드는 public으로
24/48 24 /48
접근 제어 – 계속 parent
parent
parent
protected int xxx;
private int xxx;
int xxx;
child
child
child
protected int xxx;
private int xxx;
int xxx;
parent public int xxx;
child public int xxx;
부모에 정의된 모든 멤버 변수는 자식에 상속된다. 하지만 부모의 정의된 멤버 변수가 private이면 자식 클래스에서 직접 접근할 super의 package 수 없다. 또 부모의 정의된 멤버 변수가 default일 때 자식이 부모와 다른 패키지에 소속되어 있다면 역시 직접 접근할 수 없다.
25/48 25 /48
접근 제어 – 계속 package somepackage;
class A private int n1; protected int n2; public int n3; int n4;
class B protected int n2; public int n3; int n4
class C n2, n3, n4를 완전이름으로 접근할 수 있음
class D protected t t d int i t n2; 2 public int n3;
public void f(A a){ int i = a.n2; int j = a.n3; a n3; int k = a.n4; }
public void f(A a){ int j = a.n3; }
class E n3만 완전이름으로 접근할 수 있음
26/48 26 /48
접근 제어 – 계속 메소드를 오버라이드할 때 접근 제어를 보다 제한적으로 바꿀 수 없음 예) public class BankAccount{ public void withdraw(int amount){ … } … } // class BankAccount public class CheckingAccount extends BankAccount{ private void withdraw(int amount){ … } // error … } // class CheckingAccount
자식 클래스에서 메소드 오버라이딩에 대한 접근 제어 관련 요구사항 원메 메소드가 가p public이면 이면 오버라이된 버라이된 메 메소드도 p public이어야 이어야 함 원 메소드가 protected이면 오버라이된 메소드는 public 또는 protected이어야 함 원 메소드가 default이면 오버라이된 메소드는 public, public protected, protected 또는 default이어야 함. private 메소드는 오버라이드할 수 없음 27/48 27 /48
상속 – 설계 Tip 공통된 메소드와 멤버변수는 조상 클래스에 정의함 protected 멤버를 가급적 사용하지 않음 자식 클래스를 부모 클래스가 제한할 수 없음 어떤 멤버를 private로 선언하면 자식 클래스는 이것을 존중할 수 밖에 없음 같은 패키지에 있으면 접근할 수 있음 비고.. 자바에서만 이런 특성이 있음 비고 비 상속은 코드의 재사용을 가능하게 해주지만 단순히 코드의 재사용만 을 목적으로 상속하는 것은 피해야 함 상속은 is-a i 관계로서 논리적으로도 is-a i 관계가 성립하는 경우에만 상속해야 함 상속되는 모든 든메 메소드가 가 의미가 있는 경우에만 상속함
28/48 28 /48
Object 클래스 자바에서 다른 클래스를 명백하게 상속받지 않는 경우에는 자동으로 Object 클래스를 상속 받는다. java.lang.Object 클래스에서 제공하는 유용한 메소드 public String toString() 객체에 대한 문자열 표현을 제공한다. 제공한다 public boolean equals(Object other) 객체의 내부 상태가 같은지 비 비교함 함 public int hashCode() 객체들을 서로 구분할 수 있는 정수 값을 제공함 protected Object clone() 객체를 복사할 때 사용됨 public final Class getClass() 객체의 클래스에 대한 정보를 가진 Class 객체를 반환함
29/48 29 /48
toString 메소드 toString 메소드의 사용 용도 디버깅 목적: System.out을 이용한 출력 x가 임의의 객체일 때 System.out.println(x)를 호출하면 println 메소드는 x.toString() 메소드를 호출함 Object 클래스에서 제공하는 메소드이므로 모든 객체에 대해 이 메소드를 호출할 수 있음 그러나 직접 오버로드하지 않은 경우에는 유용한 문자열을 얻지 못함 따라서 클래스를 만들 때에는 이 메소드를 항상 오버로드하는 것이 바람직함 예) BankAccount public String p g toString(){ g(){ return "BankAccount[balance="+balance+"]"; } // toString
30/48 30 /48
equals 메소드 이 메소드는 두 객체의 내부 상태가 같은지 비교할 때 사용됨 Object 클래스에 정의되어 있는 equals 메소드는 두 객체 변수가 같은 객체를 가리키고 있는지 검사함 예) public int search(int size, Object[] list, Object x){ for(int i=0; i<size; i++) if(list[i].equals(x)) return i; return -1; } // search h
문제점. 이 메소드로 전달된 객체 클래스에 equals 메소드가 문제점. 오버라이드되어 있지 않다면 Object Obj t 클래스에 정의되어 있는 메소드가 호출됨 비고.. Java 5에서는 비고 비 에서는 위와 같은 search 메 메소드를 를 template p 기능을 이용하여 정의할 수 있다. 하지만 이 경우에도 해당 클래스에 equals 메소드가 정의되어 있지 않다면 문제가 발생할 수 있음
31/48 31 /48
equals 메소드 파라미터가 Object로 선언되어 있으므로 Object를 해당 클래스로 변환해야 한다. 만약 잘못된 객체가 올 수 있으므로 무조건 변환하기 전에 변환이 가능한지 검사해야 함 이 검사는 instanceof 연산자로 검사할 수 있다. 그러나 이 경우에 하위 클래 클래스도 오류 류 없이 통과하게 됨 됨. 따라서 instanceof 대신에 Object 클래스의 getClass() 메소드를 이용하는 것이 좋음 예) Date 클래스 public boolean equals(Object other){ if(other==null || getClass() != other.getClass()) 자식 클래스의 경우 return false; if(thi == other) if(this th ) return t ttrue; public boolean equals(Object other){ Date o = (Date)other; if(!super.equals(other)) return false; return year==o.year&& … month==o.month&& return …; day==o.day; } // equals } // equals
32/48 32 /48
equals 메소드 – 계속 예) BankAccount와 SavingsAccount public boolean equals(Object other){ if(other==null || getClass() != other.getClass()) return false; if(this == other) return true; B kA BankAccount t o = (B (BankAccount)other; kA t) th return balance==o.balance; } // BankAccount::equals public boolean equals(Object other){ if(!super.equals(other)) return false; SavingsAccount o = (SavingsAccount)other; return interestRate == o.interestRate; } // SavingsAccount::equals
33/48 33 /48
instanceof VS. getClass() public class A{ … }
public class B extends A{ … }
public class C extends B{ … }
public class Test{ public static void main(String[] args){ A a = new A(); B b1 = new B(); B b2 = new B(); B() C c = new C(); System.out.println(a instanceof A); System.out.println(b1 instanceof A); System.out.println(c instanceof A); System.out.println(a.getClass() == b1.getClass()); System.out.println(b1.getClass() == b2.getClass()); } // main i } // class Test
결과 true true t true false true
34/48 34 /48
hashCode 두 개의 서로 다른 객체일 경우에는 두 객체의 hashCode 값이 달라야 함 Object에 정의된 hashCode는 객체의 주기억장치 주소를 이용하여 계산함 하지만 이 hashCode는 equals 메소드와 서로 호환되어야 함 즉, x와 y가 같은 타입의 객체일 때 x.equals(y)가 true이면 x.hashCode()==y.hashCode() 역시 true이어야 함 예) BankAccount의 경우 balance만을 이용하여 equals를 비교할 경우에는 다음과 같이 재정의해야 함 public int hashCode(){ Integer i = Integer.valueOf(balance); return i.hashCode(); } Integer 클래스의 hashCode 메소드는 Integer 클래스의 equals 메소드와 호환되기 때문에 이렇게 하는 것이 일반적인 방법이다. 적 방 이다
public int hashCode(){ Integer y = Integer.valueOf(year); Integer m = Integer.valueOf(month); g d = Integer.valueOf(day); g ( y); Integer return y.hashCode()^m.hashCode() ^d.hashCode(); } // Date::hashCode();
35/48 35 /48
clone 메소드 상태가 같은 또 다른 객체를 생성할 때 사용하는 메소드 Clone이 만족해야 할 규칙 규칙 1. x.clone() != x 새 객체의 생성이 필요하다는 것을 의미함 규칙 2. 2 x.clone().getClass() l () tCl () == x.getClass() tCl () 동일한 클래스의 인스턴스를 생성해야 함 규칙 3. 3 x.clone().equals(x) x clone() equals(x) == true 이 규칙은 반드시 만족되어야 할 규칙은 아니지만 보통 만족되도록 clone 메소드를 정의함 이와 같은 규칙들이 제대로 만족되기 위해서는 클래스 계층구조에 있 는 모든 클래스들이 올바른 clone 메소드를 제공해야 함
36/48 36 /48
예) BankAccount 클래스
주의. Java 2에서 clone의 반환타입은 public BankAccount clone(){ j 이다 따라서 사용할 때 강제 Object이다. BankAccount B kA t cloned l d = new BankAccount(); B kA t() 타입 변환을 해주어야 했다. cloned.balance = balance; return cloned; } // clone BankAccount account1 = new BankAccount(10000); // Java 2: BankAccount account2 = (BankAccount)account1.clone(); BankAccount account2 = account1.clone(); BankAccount account3 = account1;
위 예와 같은 clone의 문제점: 자식 클래스에 대해서는 동작하지 않음 SavingsAccount g account1 = new SavingsAccount(10, g ( , 10.0); ); SavingsAccount account2 = account2.clone();
해결책 1. 자식 클래스에서 clone 메소드를 public으로 재정의해야 함 해결책 2. Object의 clone 메소드를 이용함
37/48 37 /48
clone 메소드 – 계속 Object의 clone 메소드의 문제점 문제점 1. protected 메소드임 문제점 2. Object 클래스에 정의되어 있는 clone 메소드는 같은 타입의 객체를 자동으로 생성하고 인스턴스 필드의 값을 자동으로 복사함 (shallow copy) 클래스의 멤버 변수 중에 참조 타입이 있으면 문제가 발생할 수 있음 예) 문제점 1. public class A{ … // clone 메소드를 // 오버라이드 하지 않고 있다. public void f(){ A a = (A)clone(); } } // class A
public class Test{ public static void main(String[] args){ A a1 = new A(); A a2 = (A)a1.clone(); // error } // main } // class Test
38/48 38 /48
public class A{ private int n; public A(int p ( val){ ){ n = val;; } public void set(int val){ n = val; } public A clone() throws CloneNotSupportException{ A cloned = ((A)super.clone(); ) p (); return cloned; } } // class A public class B{ private int n; private A a; public B(int p ( val){ ){ n = val; A a = new A(val+1); } public void set(int p ( val){ ){ n = val; a.set(val+2); } public B clone() p () throws CloneNotSupportException{ B cloned = (B)super.clone(); return cloned; } } // class B
public class Test{ public static void main(String[] p ( g[] args){ g ){ A a1 = new A(5); A a2 = a1.clone(); a2.set(10); ( ); B b1 = new B(3); B b2 = b1.clone(); b2.set(5); } // main } // class Test
a1
a2
n
n
5
10 5
b1
n
3
a
b2
n
3 5
a
n 4 7
39/48 39 /48
clone 메소드 – 계속 이런 shallow copy의 문제점 때문에 Object의 clone 메소드가 protected로 선언되어 있음 clone을 오버라이드하는 경우에는 Cloneable 인터페이스를 구현한다고 표기함 public class B implements Cloneable{ public class B implements Cloneable{ private int n; private A a; … public B clone(){ try{ } B cloned = (B)super.clone(); cloned.a = (A)a.clone(); return cloned; } catch(CloneNotSupportedException e){ return null; // can’t happen } } }
… public B clone() throws CloneNotSupportException { B cloned = (B)super.clone(); cloned.a = (A)a.clone(); return cloned; }
Cloneable 인터페이스는 일반 인터페이스 와 달리 marker k 인터페이스이다. 인터페이스이다 final 클래스가 아니면 catch하지 않고 공표하는 것이 바람직하다 바람직하다. (예외 부분에서 다시 설명함)
40/48 40 /48
copy constructor 복사 생성자 생성자(copy constructor)란 파라미터로 같은 클래스 타입의 객체를 받아 주어진 객체와 동일한 내부 상태를 가지는 객체를 생성해주는 생성자임 (C++에서도 (C 에서도 매우 중요한 역할을 함) 예) clone 대신 복사 생성자를 사용할 수 있음 public class A implements Cloneable{ private int n; public A(int val){ n = val; } public bli A clone(){ l (){ … } } // class A
public p blic class Test{ public static void main( String[] args){ A a1 = new A(5); ( ); A a2 = a1.clone(); B b1 = new B(3); B b2 = new B(b1); } } // class Test
public class B{ private int n; public B(){ n = 0; } public B(int val){ n = val; l } public B(B b){ if(b == null) n = 0; else n = b.n; } } // class B
41/48 41 /48
오버라이딩과 반환 타입 Java 5 이전에는 자식 클래스에서 메소드를 재정의할 때 반환 타입이 정확하게 일치해야 했음 Java 5부터는 부모 클래스에 정의된 메소드의 반환 타입으로 타입 변환이 가능한 타입이면 정확하게 일치하지 않아도 됨 주의.. 파라미터의 타입은 정확하게 일치하여야 함 주의 예) public class A{ public A f(){ … } public void g(A x){ … } // Object 클래스의 boolean equals(Object other)를 재정의한 메소드가 아님 public bli boolean b l equals(A l (A o){ ){ … } } public class B extends A{{ p public B f(){ … } public void g(B x){ … } }
// A f()를 재정의한 메소드임 // void g(A x)를 재정의한 메소드가 아님
42/48 42 /48
interface vs. abstract class 추상 클래스를 이용하여 범용 정렬 메소드를 만드는 방법 public bli abstract b t t class l S t bl { Sortable{ public abstract int compareTo( Sortable b); } // Sortable S t bl public class ArrayAlg{ public static void sort(Sortable[] a) { … } // sort } // ArrayAlg public bli class l E l Employee extends t d Sortable{ S t bl { … public int compareTo(Sortable b) {…} } // class Employee
public bli class l E l EmployeeSortTest{ S tT t{ public static void main(String[] args){ Employee[] staff = new Employee[10]; E l [10] … ArrayAlg.sort(staff); … } } 만약 Employee가 People이라는 클래스를 상속받아야 한다면 위와 같이 할 수 없다. 자바에서 클래스는 클래 오직 직 하나의 부모 부 클래스 클래 만을 가질 수 있다.
43/48 43 /48
interface vs. abstract class 인터페이스를 이용하여 범용 정렬 메소드를 만드는 방법 public interface Comparable{ int compareTo(Object b); } // interface Comparable public class ArrayAlg{ public static void sort( Comparable[] a) { … } } // class ArrayAlg public class Employee extends People implements Comparable{ … public int compareTo(Object b) {…} { } } // class Employee
public class EmployeeSortTest{ public static void main(String[] args){ Employee[] staff = new Employee[10]; … ArrayAlg.sort(staff); … } // main } // class EmployeeSortTest
44/48 44 /48
interface vs. abstract class Java 5의 경우 public interface Comparable<T>{ public class EmployeeSortTest{ public int compareTo(T b); public static void main(String[] args){ } // interface Comparable<T> Employee[] staff public class ArrayAlg{ = new Employee[10]; public static … <T extends Comparable<T>> ArrayAlg.sort(staff); void sort(T[] a){ … … } // main } } // class EmployeeSortTest } // class ArrayAlg y g public class Employee implements Comparable<Employee>{ … public int compareTo(Employee b) {…} } // class Employee
45/48 45 /48
Class 클래스 자바는 클래스에 대한 실행시간 표현을 Class라는 클래스의 인스턴스로 가지고 있음 java.lang.Class의 주요 메소드 String getName(): 클래스의 이름을 반환하여 줌 Class getSuperclass(): 클래스의 슈퍼클래스에 해당하는 Class 인스턴스를 반환하여 줌 예) BankAccount account = new BankAccount(10000); Class c1 = account.getClass(); System.out.printf("%s, System.out.printf( %s, %s", %s , c1.getName(), c1.getSuperclass().getName()); 출력결과: BankAccount, java.lang.Object
46/48 46 /48
상속과 배열 상속 관계가 있는 클래스의 배열은 상속 관계가 있음 Shape 클래스가 Rectangle, Circle클래스의 부모 클래스라 가정하자. 예) Shape[] s1 = new Rectangle[10]; Rectangle[] r = new Rectangle[10]; Shape[] s2 = r; s2[0] = new Circle(); // 문법적으로 오류가 아님. <문제점>
배열은 자신의 형식을 알고 있음 예) 다음 프로그램에서 두 출력의 결과는 모두 [LRectangle;임 Rectangle[] r = new Rectangle[10]; Shape[] s = r; System.out.println(r.getClass().getName()); System.out.println(s.getClass().getName());
따라서 첫 예에서 마지막 문장은 문법 오류는 아니지만 실행시간에 ArrayStoreException y p 예외가 발생함 47/48 47 /48
상속과 인터페이스 부모 클래스가 특정 인터페이스 A를 구현하고 있으면 그것의 자식 클래스는 A를 구현한다고 지정하지 않아도 자식 클래스는 A를 구현하고 있는 클래스가 됨 예) class A implements Comparable{ … } class B extends A{ … }
Comparable x = new B(); // ok
48/48 48 /48
자바프로그래밍(CPA240) NOTE 10
©copyright 2010
예외 / 테스팅
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
교육목표 예외 처리의 기본 개념 throw catch 예외의 종류 예외 처리 방법 예외 처리 Tip 새 예외 클래스 만들기 테스팅 단위 테스팅 printStackTrace Logger 클래스 assertt Eclipse의 디버깅 기능
2/40
지난 시간 복습 상속은 한 클래스를 이용하여 또 다른 클래스를 정의할 수 있도록 해줌. 이를 통해 코드를 재사용할 수 있게 됨 오버라이딩이란 부모 클래스에 정의되어 있는 메소드를 자식 클래스에서 재정의하는 것을 말함 접근 권한을 보다 제한적으로 바꿀 수 없으며, 없으며 final 메소드는 재정의할 수 없음 객체 변수를 통해서는 그 타입에 정의되어 있는 메소드 또는 그 조상 클래스에 정의되어 메소드만 호출할 수 있음. 있음 하지만 실제 호출되는 메소드는 객체 변수가 어떤 타입을 가리키고 있는지에 따라 결정됨 물론 p public 메 메소드만 만 호출할 출할 수 있음 상속과 접근 권한: private, public, protected, default(package) 추상 클래스는 하나 이상의 추상 메소드를 가지는 클래스를 말하며, 추상 클래스의 자식 클래스는 부모 클래스의 모든 추상 메소드를 정의하지 않으면 이 클래스 역시 추상 클래스로 정의해 주어야 함
3/40
개요 메소드를 구현할 때 정상적인 경우만 생각하여 먼저 구현함 그 다음 어떤 예외적인 상황이 발생할 수 있는지 고려한 다음 이를 처리할 수 있도록 예외처리를 추가함 중요.. 발생할 수 있는 예외를 생각해낼 수 있는 것도 프로그래머의 중요 중요 기술 중 하나임
4/40
예외 처리 (기본개념) 예외 예외(exception)란 정상적인 경우에는 일어나지 않으며, 드물게 발생하는 문제를 말함 가장 큰 목적: 예외가 발생하여도 프로그램이 계속 수행될 수 있도록 하는 것 프로그램에 오류가 발생하였을 때 최소한 다음은 보장해야 함 사용자에게 오류 사실을 알림 현재 작업을 저장 우아하게 종료할 수 있도록 해줌
5/40
예외 처리 (기본개념) – 계속 사용자들은 프로그램이 실행되는 동안 오류가 발생하여도 합리적인 행동을 취하길 기대함 기대하는 행동 안전한 상태로 되돌아간 후에 다른 명령들을 수행할 수 있도록 해줌 현재 작업을 저장하고 우아하게 종료할 수 있도록 해줌 예외 처리 기법의 목적 목적: 오류가 발생된 위치에서 그 오류를 처리할 수 있는 처리자에게 제어를 넘기는 것 프로그래밍할 때 고려해야 하는 문제들 사용자 입력 오류 장치 오류: 예) 프린터의 용지 부족 물리적 제한 제한: 예) 하 하드디스크 디 공간 부족 코드 오류: 예) a[-1]
6/40
예외 처리 (기본개념) – 계속 일반적인 오류 처리 기법 작업 수행; 제대로 수행되지 않았으면 오류 처리; 처리 예) status = func(); if(status != SUCCESS){ ( ){ error(status); return; }
문제점 번거로움 적절한 오류 코드를 반환하는 것이 어려울 수 있음 호출하는 측에서 검사를 하지 않을 수도 있음 프로그램의 가독성이 저하됨 유지보수, 디버깅 어려움 정상적인 경우에도 성능에 영향을 줌
7/40
예외 처리 (기본개념)
– 계속
예외 처리 기법 프로그램의 주 로직에서 오류 처리 부분을 제외시킬 수 있음 프로그램의 가독성, 가독성 수정용이성 향상 예외와 관련된 보다 많은 정보를 유지할 수 있음 예외 처리는 프로그램 램 문장이 수행될 때 발생할 수 있는 동기성 오류 류 (synchronous error)를 처리하기 위한 수단 예) 범위가 벗어난 배열 색인 사용, 0 나누기 예외 처리는 마우스 클릭과 같은 비동기적 사건을 처리하기 위한 수단은 아님 언제, 어디서 일어날지 모르는 는 예외는 처리할 수 없음 예외처리 기법을 제공함으로써 궁극에는 모든 프로그래머가 같은 방법으로 예외를 처리하기를 기대함 다른 사람의 오류처리 방법을 쉽게 이해할 수 있음
8/40
throw 자바에서 예외가 발생하면 throw 문을 이용하여 예외와 관련된 정보를 캡슐화하고 있는 객체를 전달하여 예외가 발생한 사실을 알려 처리할 수 있도록 해줌 throw 문이 실행되면 이것을 처리하는 곳까지 제어가 바로 이동됨 예) ListFullException public class ListFullException extends Exception { public ListFullException(String p p ( g s){ ){ super(s); } // ListFullException(String) } // ListFullException
public void append(Point newPoint) throws ListFullException { if(isFull()) ( ()) throw new ListFullException("ListException p ( p on append"); pp ); … } // append(Point)
9/40
catch 예외가 발생하면 바로 다음 블록에서 감지 감지(catch)하여 처리하거나 다시 다음 외부 블록으로 재차 전달 전달(throw)됨 메소드 밖으로 예외가 전달되면 메소드를 호출한 메소드에게 전달됨 만약 main 밖으로 전달되어 JVM으로 전달되면 JVM은 오류 메시지를 출력하고 프로그램을 종료함 예) JVM try{ Point(100 100)); append(new Point(100, } catch(ListFullException e){ System.out.println(e.getMessage()); }
main
method1
method2
강제 종료 stack trace 출력
예외 발생 catch하면 바로 정지
Throwable 클래스에 정의되어 있는 메소드
10/40 10 /40
catch – 계속 try{ append(new Point(100, 100)); } catch(ListFullException e){ System.out.println(e.getMessage()); }
catch 블록의 파라미터: 하나의 catch 블록은 하나의 파라미터 밖에 정의할 수 없다.
catch 블록은 메소드의 정의와 유사함 catch 블록은 블록 파라미터에 정의된 인자 타입에 해당하는 예외가 발생하면 그 예외를 처리하여 주는 블록임 인자 타입에 해당하는 예외란 인자 타입과 그것의 모든 후손 타입을 말함 타입 try 블록 다음에는 여러 개의 catch 블록이 올 수 있음. 이 때 그 순서가 중요함 중요 비고.. switch의 case 절과 유사 비고
11/40 11 /40
예외의 종류 자바의 모든 예외 객체는 java.lang.Throwable 클래스의 자손 클래스임 Throwable 클래스에 제공하는 유용 메소드 getMessage: 예외와 관련된 오류 메시지를 반환해줌 printStackTrace: 예외가 발생하게된 경로를 출력하여 줌 Throwable +getMessage() +printStackTrace()
Error
IOException
Exception
RuntimeException
NoSuchFieldException NoSuchMethodException illegalAccessExcepetion
CloneNotSupported Exception
ClassNotFound Exception
12/40 12 /40
예외의 종류 – 계속 RuntimeException
Arithmetic Exception
ClassCast Exception
Enum Constant NotPresent Exception
Illegal Argument Exception
Illegal State Exception
Number Format Exception
Index OutofBounds Exception
ArrayIndex OutofBounds Exception
Array Store Exception
Stringndex OutofBounds Exception
NullPointer Exception
NoSuch Element E Exception ti
IOException의 자식클래스: EOFException, FileNotFoundException, MalformedURLException, UnknownHostException 등
13/40 13 /40
예외
언제 발생
g g p IllegalArgumentException
null이 이아 아닌 적절하지 않 않은 매개 매개변수가 수가 전달되었을 되었을 경우
IllegalStateException
메소드가 호출되기에 객체가 적합한 상태가 아닌 경우
NullPointerException
매개변수의 값이 null인 경우
IndexOutOfBoundsException
색인관련 매개변수가 유효범위를 벗어난 경우
Aih ArithmeticException i E i
산술연산의 피연산자의 값이 적절하지 않은 경우
NoSuchElementException
더 이상 원소가 없을 때 다음 원소를 요청한 경우 next()와 같은 메소드를 호출할 때 발생가능
14/40 14 /40
예외의 종류
– 계속
예외의 분류 오류: 자바 시스템 내에 오류나 자원 고갈을 나타냄 오류는 처리 대상이 아님. 예) OutOfMemoryError 실행시간 예외: 프로그래밍 오류에서 비롯됨 그 밖에 예외: 예외 외부적 요인 checked vs. unchecked 예외 RuntimeException 클래스와 그것의 서브 클래스, 클래스 Error 클래스와 그것의 서브 클래스는 unchecked 예외 예외임. 이런 예외는 컴파일러에게 처리 방법을 알려주지 않아도 됨 IOE IOException ti 클래스처럼 위에서 언급되지 않은 Throwable의 Th bl 의 후손 클래스는 checked 예외 예외임. 이런 예외는 컴파일러에게 처리 방법을 반드시 알려주어야 함
15/40 15 /40
예외의 종류
– 계속
checked 예외: 직접 제어할 수 없는 예외들을 말함 즉, 아무리 주의하여도 발생 가능한 예외를 말함 또 보통 복구가 가능함 예) FileNotFoundException은 프로그래머가 제어할 수 있는 예외는 아님. 아님 프로그래머가 파일 명을 바꾸던 폴더 위치를 변경하여 극복할 수 있음 대부분 입출력을 처리할 때 발생함 unchecked 예외: 보통 프로그래머의 부주의에 의해 발생할 수 있는 예외들을 말함 예) ArrayIndexOutofBoundsException은 보통 프로그래머의 실수로 발생하는 예외임 항상 프로그래머의 부주의에 의해 발생하는 것은 아님 예) 사용자의 입력실수(Integer.parseInt의 NumberFormatException)
16/40 16 /40
예외 처리 방법 방법 1. JVM에게 처리 위임 (상위에 처리 위임) 예10.12) public static void main(String[] args) throws IOException throws를 이용하여 이 메소드가 IOException을 발생시킬 수 있다는 것을 시스템에게 알려줌 처리하는 (복구하는) 방법을 정확하게 모를 경우에는 이 방법이 가장 바람직함. 섣불리 복구하다가 오히려 더 큰 문제를 초래할 수 있음 메소드 내에서 발생 가능한 모든 예외를 공표할 필요는 없음 직접 처리하는 예외는 공표하지 않음 checked 예외가 메소드 내에서 발생될 수 있으면 이것은 반드시 공표하여야 함
17/40 17 /40
예외 처리 방법 – 계속 공표해야 하는 예외가 여러 개 있으면 ‘,’로 구분하여 나열함 예) void f() throws IOException, ListFullException{ … }
여러 개의 예외를 나열할 때 상속 계층구조를 고려해야 함 예) void f() throws IOException, FileNotFoundException{ … }
FileNotFoundException은 IOException의 자식 클래스이므로 이 경우에는 IOException만 공표하면 됨
18/40 18 /40
예외 처리 방법 – 계속 예외가 발생하는 경우 경우 1. 예외를 발생시킬 수 있는 메소드를 호출할 경우 예) Scanner 클래스의 nextInt 메소드 경우 2. 메소드 내에서 직접 checked 예외를 발견하고 throw 문을 이용하여 예외를 발생시킴 경우 3. 프로그래밍 오류: 예) a/0 경우 4. 자바 내부의 오류 류 catch를 를해 해도 소용이 용이 없음 경우 1과 2의 경우 메소드를 사용할 사용자에게 예외가 발생할 수 있다는 것을 알려주어야 한다. 경우 3과 4는 공표하지 않아도 됨 주의.. 주의 부모 클래스의 메소드를 자식 클래스에서 오버라이드할 때 자식 클래 클래스의 의메 메소드는 는부 부모 클래 클래스의 의메 메소드보다 다 많은 종류의 checked 예외를 발생시킬 수 없음
19/40 19 /40
예외 처리 방법 – 계속 방법 2. 직접 처리 예외를 발생시킬 가능성이 있는 코드와 와 이 예외가 발생되었을 때 실행되지 않아야 하는 코드
try{ …
이 블록의 문장 중 하나가 IOException 예외를 발생하면
} catch(IOException e){ System.out.println("Exception: " + e.getMessage()); System.exit(); 최소한 하나의 catch 블록 또는 } finally 블록이 try 블록 다음에 와야 함 catch(RuntimeException e){ … IOException의 클래스에 p 의 후손 클래 에 해당하는 } 모든 예외는 이 블록에서 catch됨 finally{ … 예외가 발생하지 않은 경우: try finally } 보통 자원을 반납하기 위해 사용 예) 파일 닫기
예외가 발생하였으며, 발생하였으며 그것에 해당하는 catch 블록이 있는 경우: try 일부 catch finally 예외가 발생하였지만, 그것에 해당하는 catch 블록이 없는 경우: try 일부 finally 상위로 전달 즉, finally 블록은 예외 발생 여부와 관계없이 무조건 실행되는 블록임
20/40 20 /40
예외 처리 방법 – 계속 예) catch 블록 순서의 중요성 try{ } catch(Exception e){ System out println(e getMessage()); System.out.println(e.getMessage()); } catch(IOException e){ System.out.println(e.getMessage()); }
이 예제의 경우 IOException이 발생하여도 두 번째 catch 블록은 실행되지 않음. IOException은 Exception의 후손 클래스이므로 IOException 예외 객체도 첫 번째 catch 블록에서 처리됨
상속계층도에서 후손 클래스에 해당하는 예외일 수록 그것의 catch 블록을 먼저 작성해야 함
21/40 21 /40
예외 처리 Tip 자바 라이브러리를 사용할 때에는 각 메소드가 어떤 예외를 발생하는 지 알아보고 사용해야 함 프로그래머가 작성한 메소드에서 특정 예외를 공표하고 있는 메소드를 호출하면 그 예외를 직접 처리하거나 작성한 메소드에서 그 예외를 공 공표해야 해야 함 예외를 무시하는 것은 바람직하지 않음 예) image g loadImage(String g ( g s){ ){ try{ …} catch(Exception e){} }
22/40 22 /40
예외 처리 Tip – 계속 예외를 작은 단위로 관리하는 것은 비효율적임 예) for(i=0; i<100; i++){ try{ n = s.pop(); } catch(EmptyStackException e){} try{ System.out.println(n); } catch(IOException e){} } 이 예제는 작은 단위로 관리하는 것이 비효율적이라는 것을 보여주기 위해 억지 억지로 만든 예임
try{ for(i=0; i<100; i++){ n = s.pop(); s pop(); System.out.println(n); } } catch(EmptyStackException e){} catch(IOException e){}
pop은 스택이라는 자료구조에서 저장되어 있는 요소를 제거하는 메소드이며, EmptyStackException은 자료구조에 요소가 하나도 없을 때 요소를 제거하고자 하면 발생하는 예외임
23/40 23 /40
예외 처리 Tip – 계속 예외 처리 기법을 다른 목적으로 활용하는 것은 절대 삼가 해야 함 예) try{ for(int i = 0; ;i++){ a[i].play(); } } catch(ArrayIndexOutofBoundsException e){}
24/40 24 /40
시스템에서 제공하는 예외 사용하기 예) IllegalArgumentException public void withdraw(int amount){ if(amount>balance){ IllegalArgumentException e = new IllegalArgumentException("인출: "+amount+"현재잔액: "+balance); throw e; th } // if(잔액부족) balance -= amount; } // withdraw public void withdraw(int amount){ if(amount>balance){ throw new IllegalArgumentException("인출: "+amount+"현재잔액: "+balance); } // if(잔액부족) balance -= amount; } // withdraw
25/40 25 /40
새 예외 클래스 만들기 은행 계좌에서 금액을 인출할 때 잔액이 부족할 경우 발생되는 InsufficientFundsException 예외를 고려하여 보자. 예) public void withdraw(int amount){ if(amount>balance){ throw new InsufficientFundsException( "인출: "+amount+"현재잔액: "+balance); } // if(잔액부족) balance -= amount; }
이 예외는 checked와 unchecked 예외 중 어떤 것이 되어야 하는가? 외부 사건에 의한 예외인가 프로그래머의 부주의에 의한 예외인가? 프로그래머는 withdraw를 호출할 때 다음을 통해 확인한 다음에 호출할 수 있음 amount account.getBalance() amount<=account.getBalance() 따라서 unchecked 예외가 되어야 함
26/40 26 /40
꼭 새로 만들어야 하나? 예외 클래스를 새롭게 만들어야 하나 아니면 시스템에서 제공하는 예외 중 하나를 사용해야 하나? 시스템에서 제공되는 예외 중 적합한 이름의 예외가 있으면 사용하면 됨 시스템에서 제공되는 예외 중 적합한 이름이 없거나 시스템에서 제공되는 예외가 가지고 있는 기능 외에 추가 기능이 필요한 경우에는 새 예외를 정의하여 사용함 새로운 예외 클래스를 정의하여 사용할 때와 시스템에서 제공하는 예외를 사용할 때의 가장 큰 차이점 그것을 것을 catch할 할 때 그 이유 외에 다른 이유 때문에 때문에도 발생할 수 있다는 점을 유념해야 함 예를 들어 illegalArgumentException을 이용하여 잔액 부족 예외 상황을 처리하면 잔액 부족이 아닌 경우에도 해당 예외가 발생할 수 있음
27/40 27 /40
새 예외 클래스 만들기 – 계속 예) InsufficientFundsException public class InsufficientFundsException extends RuntimeException{ public InsufficientFundsException(){ super("잔액 부족"); } // InsufficientFundsException() public InSufficientFundsException(String msg){ super(msg); } // InsufficientFundsException(String) } // class InsufficientFundsException
어떤 종류의 예외인지에 따라 상속받을 클래스가 결정됨 checked 예외이면 Exception 클래스를 상속받음 unchecked 예외이면 RuntimeException 클래스를 상속받음 보통 예외 클래스는 기본 생성자를 포함하여 두 개의 생성자를 제공 생성자는 getMessage tM 메소드가 반환할 메시지를 설정해야 함 기본 생성자는 이 예외 객체가 기본적으로 반환할 메시지를 설정함 28/40 28 /40
새 예외 클래스 만들기 – 계속 예외 객체는 앞 슬라이드에서 살펴본 바와 같이 항상 예외와 관련된 정보가 기록된 문자열을 가지고 있음 그러나 예외와 관련된 정보를 꼭 문자열 형태로만 유지할 필요는 없음 그 외에 다양한 정보를 유지할 수 있음 예) public class InsufficientFundsException extends RuntimeException{ private int insufficientAmount; public InsufficientFundsException(){ ff (){ super("Insufficient funds exception"); } // InsufficientFundsException() public InsufficientFundsException(String msg, int amount){ super(msg); insufficientAmount = amount; } // InsufficientFundsException(String, int) public bli int i t getInsufficientAmount(){ tI ffi i tA t(){ return insufficientAmount; } // getInsufficientAmount() } // class InsufficientFundsException 29/40 29 /40
예외처리 추가 Tip 경우 1. 프로그래머가 반드시 예외를 발생해야 하는 경우 은행계좌 예에서 인출 메소드에서 잔액 부족 오류 경우 2. 프로그래머가 예외 발생하지 않아도 시스템에서 해주는 경우 경우 2.1. 시스템에서 해주는 대로 사용하는 경우 경우 2.2. 2 2 예외가 발생되지 않도록 하는 경우 (항상 가능하지 않음) int compareTo(Object o){ A a = ((A)o; ) … } // 경우 2.1
boolean equals(Object a){ ( == null)) return false; if(a … } // 경우 2.2
둘 다 인자 o 또는 a가 가 null이면 ll이면 예외 상황이 발생함. 발생함 equals의 경우에는 예외를 발생하지 않고 false를 반환하여도 equals 메소드의 논리에 위배되지 않는다. 하지만 compareTo 경우에는 예외 발생하지 않 않고 다른 값을 반환할 방법이 없음 없음. 그렇다고 하여 프로그래머가 직접 null인지 검사하여 NullPointerException을 발생할 필요는 없음
30/40 30 /40
예외 발생과 상속 예) clone은 다음과 같이 두 가지 방법으로 정의할 수 있음 public class A implements p p Cloneable{{ public class A implements Cloneable{ … … public A clone(){ public A clone() try{ throws CloneNotSupportException { return ((A)super.clone(); ) p (); return (B)super.clone(); (B)super clone(); } } // clone catch(CloneNotSupportedException e){ } return null; } } // clone CloneNotSupportedException은 checked 예외임. }
따라서 공표하거나 직접 처리해 주어야 함. 만약 직접 처리한 경우에는 그것의 자식 클래스에서 clone 기능을 지원하고 싶지 않는 경우에는 CloneNotSupportedException을 발생할 수 없음. Why? (슬라이드 15.) 부모 클래스의 메소드를 자식 클래스에서 오버라이드할 때 자식 클래스의 메소드는 부모 클래스의 메소드보다 많은 종류의 checked 예외를 발생시킬 수 없음. 따라서 직접 처리하기 보다는 후손 클래스가 예외를 발생할 수 있도록 공표하는 방법이 보다 바람직함.
31/40 31 /40
단위 검사 단위 검사 검사(unit testing): 클래스 또는 메소드를 독립적으로 검사하는 것을 말함 가장 중요하고 가장 널리 사용하는 검사 방법 메소드를 검사할 때 사용되는 파라미터 값을 제공하는 방법 사용자 입력 단점.. 오류를 발견하면 그것을 수정하고, 처음부터 다시 입력 단점 절차를 수행해야 함 루프: 어떤 범위에 있는 값을 검사 랜덤 값을 생성 파일 사용: 사용 입력 절차를 다시 수행해야 하는 번거로움이 없음
32/40 32 /40
검사의 정확성 단위 검사의 결과가 정확한지 어떻게 알 수 있나? 방법 1. 수동적으로 값을 구해서 비교함 단점.. 수동적으로 값을 계산하는 것이 어려울 수 있음 단점 해결책.. 쉽게 값을 구할 수 있는 값에 대한 입력만 검사함 해결책 방법 2. 2 다른 방법으로 값을 구하고 비교함 다른 방법은 항상 정확하게 값을 주어야 하며, 이렇게 신뢰성은 있지만 느린 린 방법을 오라클 오라클(oracle)이라 라클( 라클 )이라 함
33/40 33 /40
검사적용 범위 Regression Testing: 지난 테스트 사례를 보관한 다음에 새 버전에 대해 지난 테스트 사례를 다시 검사해보는 것을 말함 이와 같은 테스트를 하기 위해서는 파일을 이용하는 것이 좋음 블랙박스(black box) 검사 블랙박스 검사: 프로그램 내부 구조에 대한 고려 없이 검사해 보는 것을 말함 투명박스(white box) 검사 투명박스 검사: 프로그램 내부 구조에 대해 고려하여 검사를 하는 것을 말함 검사 적용범위(test coverage): 검사에 의해 최소한 한 번 수행되는 프로그램 부분을 말함 프로그램의 모든 부분은 최소한 한 번 검사되어야 함 if/else 분기가 있으면 if 부분이 실행되는 검사와 else 부분이 실행되는 검사를 모두 해야 함 프로그램을 작성하기 전에 테스트 사례를 작성하는 것이 프로그램이 해야 하는 일을 이해하는데 도움을 줄 수 있음
34/40 34 /40
프로그램 추적 프로그램이 어떻게 진행되는지 관찰하기 위한 방법 방법 1. System.out.println을 이용 예) if(status == SINGLE){ System.out.println("status: SINGLE"); … }
방법 2. 2 stack trace의 출력 예) 결과: f method g method java.lang.Throwable at TraceTest:g(TraceTest.java:4); at TraceTest:f(TraceTest.java:9); at TraceTest:main(TraceTest.java:12);
public class TraceTest{ public static void g(){ System out println("g System.out.println( g method method"); ); Throwable t = new Throwable(); t.printStackTrace(System.out); } // g public static void f(){ System.out.println("f method"); g(); } // f public static void main(String[] args){ f(); } // main } // class TraceTest 35/40 35 /40
Logger 클래스 추적 메시지의 출력을 통한 검사의 문제점은 검사가 완료되면 이들을 제거해야 하는데 있음 이 문제를 해결하기 위해 Logger 클래스를 사용할 수 있음 System.out으로 출력하는 대신에 Logger 클래스를 사용할 수 있음 이 클래스 java.util.logging java util logging 패키지에서 제공되는 클래스임 예) Logger를 통해 메시지를 출력하고 싶으면 다음과 같이 함 Logger.global.info("status: SINGLE"); Logger는 기본적으로 메시지를 출력하여 준다. 만약 출력을 중단 하고 싶으면 Logger.global.setLevel(Level.OFF)를 호출하면 됨 Level은 java.util.logging java util logging 패키지에서 제공되는 클래스임
36/40 36 /40
Logger 클래스 – 계속 예) import java.util.logging.Logger; public class LoggerTest{ public static void f(int n){ gg g ( method p param n: "+n); ); Logger.global.info("f } // f public static void main(String[] args){ f(10); } // main } public static void main(String[] p ( g[] args){ g ){ Logger.global.setLevel(Level.OFF); f(10); }
main을 이와 같이 바꾸면 더 이상 logging이 이루어지지 않음 37/40 37 /40
Assertion 주장 주장(assertion): 사실이라고 믿고 있는 가정을 말함 주장 검사는 주장된 것을 검사하여 그것의 거짓이면 프로그램을 종료하고 오류를 보고함 예) public void deposit(int amount){ assert amount>0;; balance = balance + amount; }
주장 기능을 이용하기 위해서는 JDK 버전이 1.4 1 4 이상이어야 함 JDK 1.4에서는 –source 1.4 옵션을 사용해야 컴파일됨 Java 5.0부터 자동으로 지원됨 JDK 1.4에서는 에서 주장 장 기능을 포함하여 함하여 실행하기 위해서는 위해서 다음과 같이 실행해야 함 java –enableassertions MyProg Eclipse에서는 Run…을 선택한 다음에 VM argument에 –enableassertions를 추가함
38/40 38 /40
Assertion – 계속
39/40 39 /40
디버거 Eclipse의 디버거 기능 Breakpoint의 추가와 제거 Run 메뉴에서 Toggle Line Breakpoint 사용 Run as 대신에 Debug as 사용하여 디버깅한다. D b Debug as로 로 실행하면 기본적으로 Debug D b perspective로 ti 로 전환함 디버깅은 step p into,, step p over,, step p return 등 다양한 방법으로 프로그램을 실행할 수 있음 Variables 창을 통해 멤버변수의 값, 지역변수의 값을 관찰할 수 있음
40/40 40 /40
©copyright 2010
자바프로그래밍(CPA240) NOTE 11
범용 프로그래밍
한국기술교육대학교 한국기술 육대학 컴퓨터공학부 김상진
지난 시간 복습 자바에서 예외는 크게 checked와 unchecked 두 종류가 있음. 사용자가 새 예외 클래스를 정의할 때 정의하고자 하는 예외가 어떤 종류의 예외인지 결정해야 한다. 한다 checked h k d 예외인 경우에는 Exception 클래스를 상속받아 정의하고, unchecked 예외인 경우에 는 RuntimeException 클래스를 상속받아 정의함 예외를 사전에 검사하여 예방할 수 있으면 unchecked 예외가 됨 checked 예외는 직접 처리하지 않을 경우에는 공표해야 함 catch t h 절의 순서가 매우 중요함 예외는 너무 작은 단위로 처리하는 것은 바람직하지 않음
2/30
교육목표 범용 프로그래밍 개요 ArrayList 범용 클래스 범용 메소드 타입 변수 제한 주의사항 wildcard
3/30
범용 프로그래밍 범용 프로그래밍 프로그래밍(generic programming): 다양한 타입의 객체에 대해 변경 없이 사용할 수 있는 코드를 구현하는 것 Java 5.0 이전까지는 상속과 인터페이스를 이용하여 범용 프로그래밍을 하였음 범용 프로그래밍에 필요한 요소 다양한 타입의 객체를 유지할 수 있는 범용 타입 Object 범용 프로그래밍에 대상되는 모든 타입의 객체가 공통적으로 특정 메소드를 제공해야 하는 경우 인터페이스 Java 5.0부터는 C++의 template과 유사한 기능 제공 Object 클래스 대신에 template 활용 Object 클래스는 배열 등에서는 여전히 필요함 인터페이스는 인터페이 는 여전히 필 필요함 함
4/30
범용 프로그래밍 – 계속 Java 5.0 이전 범용 프로그래밍의 문제점 문제점 1. 매번 타입 변환이 필요함 문제점 2. 오류 검사가 가능하지 않음 문제점 3. 오류 검사가 가능하지 않기 때문에 Object 배열에 서로 다른 타입의 객체를 유지할 수 있었음 ArrayList files = new ArrayList(); ... String fileName = (String)files.get(); // 문제점 1. files.add(new Integer(100)); // 문제점 2. files.add(new Double(10.5)); // 문제점 3.
ArrayList bj t object
bj t object
bj t object 5/30
범용 프로그래밍 – 계속 Java 5.0부터 제공된 type parameter라는 것을 이용하여 세 문제점을 모두 해결하고 있음 ArrayList<String> files = new ArrayList<String>(); ... String fileName = files.get(); files.add(new Integer(100)); // error
ArrayList
6/30
ArrayList java.util에 정의된 클래스로 배열과 유사하게 사용할 수 있음 예) 용량이 20인 문자열 배열을 선언하는 방법 String[] list1 = new String[20]; ArrayList<String> list2 = new ArrayList<String>(20);
배열처럼 사용할 수 있지만 배열 색인 연산자는 제공되지 않음 예) list1[index] = "Sangjin"; list2.set(index, "Sangjin"); String tmp1 = list1[index]; String tmp2 = list2.get(index);
여기서 sett 메소드는 기존 배열과 차이점이 있음 배열에 데이터를 처음 추가할 때에는 set을 사용할 수 없고, add를 사용해야 함 함. 예) list2.add("Jinhee"); ( ); 7/30
ArrayList 예) list2.add( list2 add("one"); one ); list2.add("two"); list2.add("three");
"one"
"two"
"three"
add는 맨 뒤에 추가
list2.add(0,"zero");
따라서 set은 이미 값이 할당되어 "zero" "one" one "two" two "three" three zero 있는 색인 위치에 있는 값의 내용만 변경 가능함 size i 메소드를 통해 현재 ArrayList에 A Li t에 있는 요소의 개수를 알 수 있음 예) for(int i=0; i<list2.size(); i++) System.out.println(list2.get(i)); S t t i tl (li t2 t(i))
새 타입의 for loop 사용 가능
빈 리스트인지 검사해주는 메소드: boolean isEmpty() 8/30
ArrayList 기타 메소드 void add(int index, T newElement); 주어진 색인위치에 데이터가 추가되며, 그 색인에 있던 요소를 포함하여 뒤에 있는 요소들은 하나씩 오른쪽으로 이동함 void remove(int ( index); ) 주어진 색인위치에 데이터를 삭제하며, 그 색인에 뒤에 있는 요소들은 하나씩 왼쪽으로 이동함 void removeRange(int fromIndex, fromIndex int toIndex); fromIndex 색인에 있는 데이터를 포함하여 toIndex 색인 바로 전에 있는 요소들을 모두 제거함 boolean remove(Object theElement); boolean contains(Object target); int indexOf(Object target); int lastIndexOf(Object target); 위 네 메소드는 모두 equals 메소드를 이용함
9/30
유용한 Generics java.util.Stack: peek, push, pop 예) import java.util.Stack; public class Test { public static void main(String[] args) { Stack<Integer> s = new Stack<Integer>(); s.add(5); s.add(3); s.add(7); System.out.println(s.pop()); System out println(s pop()); System.out.println(s.pop()); System.out.println(s.pop()); } }
출력결과 7 3 5
10/30 10 /30
유용한 Generics – 계속 java.util.ArrayDeque: add, poll, peek 예) import java.util.ArrayDeque; public class Test { public static void main(String[] args) { ArrayDeque<Integer> q = new ArrayDeque<Integer>(); q.add(5); q.add(3); 출력결과 q.add(7); 5 System.out.println(q.poll()); 3 System out println(q poll()); System.out.println(q.poll()); 7 System.out.println(q.poll()); } }
11/30 11 /30
유용한 Generics – 계속 java.util.PriorityQueue: add, poll, peek 예) import p jjava.util.PriorityQueue; y public class Test { public static void main(String[] args) { PriorityQueue<Integer> q = new PriorityQueue<Integer>(); // PriorityQueue<Integer> q // = new PriorityQueue<Integer>(10, new MyComparator<Integer>()); q q.add(5); ( ) q.add(3); 출력결과 q.add(7); 3 System.out.println(q.poll()); 5 System out println(q poll()); System.out.println(q.poll()); 7 System.out.println(q.poll()); } import java.util.Comparator; } class MyComparator<T extends Comparable<T>> implements Comparator<T>{ o1 T o2){ public int compare(T o1, return o2.compareTo(o1); }
12/30 12 /30
유용한 Generics – 계속 java.util.HashSet: add, contains, remove, size 예) import java.util.HashSet; public class Test { public static void main(String[] args) { HashSet<Integer> s = new HashSet<Integer>(); s.add(5); s.add(3); s.add(7); s.add(5); System out println(s size()); System.out.println(s.size()); System.out.println(s.contains(5)); s.remove(3); System.out.println(s.size()); } }
출력결과 3 true 2
13/30 13 /30
유용한 Generics – 계속 java.util.Vector java.util.LinkedList
14/30 14 /30
범용 클래스 예)
생성자를 정의할 때에는 Pair<T>()와 같이 사용하지 않음
public class Pair<T>{ private T first; private T second; public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; } public void setSecond(T newValue) { second = newValue; } } // class Pair<T>
Pair 클래스는 타입 변수 T를 하나 사용함. 이렇게 정의한 클래스를 범용 클래스 클래스(generic class)라 함 범용 클래스는 하나 이상의 타입 변수를 가질 수 있음 예) public class Pair<T, U>{ …
15/30 15 /30
범용 클래스 – 계속 타입 변수는 클래스 내에서 멤버변수, 지역변수, 메소드의 반환값, 매개변수의 타입으로 사용될 수 있다. 범용 클래스의 인스턴스를 선언할 때에는 타입 변수에 사용될 실제 타입을 지정해 주어야 한다. 예) Pair<String> 예) 범용 클래스를 이용한 프로그래밍의 예 public class ArrayAlg{ public static Pair<String> minmax(String[] a){ if(a == null || a.length == 0) return null; String min = a[0]; String max = a[1]; for(int i=1; i<a.length; i++){ if(min.compareTo(a[i])>0) min = a[i]; if(max.compareTo(a[i])<0) max = a[i]; } return new Pair<String>(min, max); } // minmax } // class ArrayAlg 16/30 16 /30
범용 메소드 타입 변수를 가지는 단일 메소드를 정의할 수 있음. 이와 같은 메소드를 범용 메소드 메소드(generic method)라 함 예) public class ArrayAlg{ public static <T> T getMiddle(T[] a){ if(a == null || a.length == 0) return null; return a[a.length/2]; } // g getMiddle } // class ArrayAlg
범용 메소드는 일반 클래스에 정의할 수 있고, 범용 클래스에 정의할 수도 있음 범용 메소드를 정의할 때 타입 변수는 메소드의 제한자 (public, static) 다음에 반환 타입 앞에 삽입함
17/30 17 /30
범용 메소드 – 계속 범용 메소드를 호출할 때에는 실제 타입을 메소드 이름 앞에 붙임 메소드 이름 앞에 실제 타입을 붙이지 않아도 됨 같은 클래스에 정의된 범용 메소드를 호출할 때에는 실제 타입을 메소드 이름 앞에 붙이지 않음 예) String[] names = {"apple", "banana", "strawberry"}; String middle = ArraryAlg.<String>getMiddle(names); ArraryAlg <String>getMiddle(names); 범용 프로그래밍을 할 때 범용 타입의 멤버변수를 가져야 하는 클래스가 있으면 범용 클래스로 구현하고, 그렇지 않으면 범용 메소드를 정의하여 사용함
18/30 18 /30
타입 변수의 제한 예)
public class ArrayAlg{ public static <T> T min(T[] a){ if( == null if(a ll || a.length l th == 0) return t null; ll T smallest = a[0]; for(int i=1; i<a.length; i++){ if(smallest.compareTo(a[i])>0) if(smallest.compareTo(a[i]) 0) smallest = a[i]; } // for return smallest; } // getMiddle } // class ArrayAlg l A Al
이 예에서 타입 변수 T에 대입되는 실제 타입은 compareTo 메소드를 가지고 있어야 함 이를 위해 타입 변수에 대한 제한을 표시할 수 있음 public ArrayAlg{ bli class l A Al { public static <T extends Comparable> T min(T[] a, int size){ … } // getMiddle } // class ArrayAlg 19/30 19 /30
타입 변수의 제한 타입 변수에 대한 제한은 (&)를 이용하여 다중으로 제한할 수 있음 예) T extends Comparable & Cloneable 제한에 사용되는 타입은 클래스, 인터페이스 모두 가능하지만 클래스는 오직 한 개만 가능하고, 반드시 첫 제한이 되어야 함 예) minmax를 범용 메소드로 public class ArrayAlg{ public static <T extends Comparable> Pair<T> minmax(T[] a){ if(a == null || a.length == 0) return null; T min = a[0]; T max = a[1]; f (i t i=1; for(int i 1 i<a.length; i l th i++){ i ){ if(min.compareTo(a[i])>0) min = a[i]; if(max.compareTo(a[i])<0) max = a[i]; } return new Pair<T>(min, max); } // minmax } // class ArrayAlg String[] s = {"apple", "grape", "banana"}; Pair<String> p = ArrayAlg.<String>minmax(s); 20/30 20 /30
범용 코드와 JVM C++에서 template 기능을 사용하면 실제 인스턴스마다 코드를 만들어 사용함. 이 현상을 “template code bloat”라 함 자바에서는 이 현상을 없애기 위해 각 인스턴스마다 코드를 만들지 않고 하나의 코드만 만들어 사용함 타입 변수가 제한자가 없는 경우에는 T를 Object로 바꾸어 사용하고, 제한자가 있는 경우에는 해당 제한자 타입으로 바꿈 이 때 여러 개의 제한자가 있는 경우에는 첫 번째 제한 타입으로 바꿈 따라서 여러 개의 제한 타입을 사용할 경우에는 어떤 타입을 바꿈. 첫 번째 제한으로 할지 신중하게 결정해야 함 JVM은 은 T를 를 Object로 j 바꾸어 처리하기 때문에 범용 클래 클래스의 의 메소드를 호출할 경우에는 자동으로 타입 변환 연산자를 추가함 즉, Java 2에서 프로그래머가 직접 했던 부분을 이제는 컴파일러가 대신 해줌
21/30 21 /30
주의사항 원시타입 타입 변수의 실제 타입으로 원시 타입을 사용할 수 없음 원시타입: 이에 대한 부분적인 해결책: wrapper 클래스와 autoboxing Java 5부터는 wrapper 타입의 변수에 그것에 대응되는 원시 타입을 대입하면 자동으로 변환해 주며, 이것을 autoboxing autoboxing이라 함 예) Integer n = 3; // Integer n = new Integer(3); // **ERROR** ArrayList<int> list = new ArrayList<int>() ArrayList<Integer> list = new ArrayList<Integer>(); list.add(3); // list.add(Integer.valueOf(3)); int n = list.get(0); // int n = list.get(0).intValue();
autoboxing은 t b i 은 산술 표현식에서도 이루어짐 예) Integer n = 3; n++;
// Integer n = new Integer(3);
22/30 22 /30
주의사항 – 계속 instanceof 연산자를 이용한 타입 확인 범용 클래스의 다양한 인스턴스를 구분하지 못함 예) Pair<String> a = …; Pair<Rectangle> b = …; if(a.getClass()==b.getClass()) if(a.getClass() b.getClass()) … // true
범용 클래스는 Throwable을 상속받을 수 없음. 즉, 예외를 정의할 때에는 범용 클래 클래스를 를 사용할 수 없음 catch 절에서는 타입 변수를 사용할 수 없음 예) catch(T e){ // error 범용 클래스의 객체 배열을 선언할 수 없음 예) Pair<String>[] table = new Pair<String>[10]; // error 범용 클래스의 객체들을 유지하고 싶으면 Object를 사용하거나 라이브러리에서 제공되는 범용 클래스를 이용함
23/30 23 /30
주의사항 – 계속 범용 타입을 생성할 수 없으며, 범용 배열을 만들 수 없음 예) public class Pair<T>{ private T first; private T second; public Pair() p () { first = new T(); (); second = new T(); (); } … } // class Pair<T>
// error
public class Queue<T>{ T[] elements; // Object[] elements; // ArrayList<T> elements; public Queue(int capacity){ elements = new T[capacity]; // error // elements = new Object[capacity]; // elements = new ArrayList<T>(capacity); } // Queue(int) } // class Queue
24/30 24 /30
주의사항 – 계속 static 메소드와 멤버변수에는 타입 변수를 사용할 수 없음 예) public MyMath<T bli class l M M th T extends t d Comparable>{ C bl { public static T max(T a, T b){ // error return (a.compareTo(b)>0) ? a : b; } // max private static T max = null; // error } // class MyMath public class MyMath{ public static <T extends Comparable<T>> T max(T a, T b){ return (a.compareTo(b)>0) ? a : b; } // max } // class MyMath
25/30 25 /30
주의사항 – 계속 클래스 S와 T가 상속 관계이더라도 그것을 실제 타입으로 사용하는 두 범용 클래스간에는 상속 관계가 형성되지 않음 예) Shape 클래스가 Rectangle 클래스의 부모 클래스이다. 하지만 Pair<Shape>은 Pair<Rectangle>의 부모 클래스가 아님 Wildcard 타입 예) People 클래스는 Professor, Staff, Student의 부모 클래스임. People의 쌍을 출력하고 싶다. 이 메소드는 Pair<Professor>, P i St ff 등은 출력할 수 있어야지만 Pair<Integer>, Pair<Staff> P i I t Pair<Rectangle> 등은 가능하지 않아야 함 public static void printFriend(Pair<People> p){ People p1 = p.getFirst(); People p2 = p.getSecond(); System.out.printf("%s와 %s는 친구", p1.getName(), p2.getName()); } // printFriend 상속 관계에 설명하였듯이 Pair<People>과 Pair<Student>는 상속 관계가 아니기 때문에 Pair<Student> Pair Student 타입의 객체는 이 메소드에 전달할 수 없음 26/30 26 /30
주의사항 – 계속 해결책. Wildcard 타입 사용 해결책. 예)
Pair<People> Pair<Professor> P i St d t Pair<Student> Pair<Staff> 타입의 객체를 전달할 수 있음
public static void printFriend(Pair<? extends People> p){ People p1 = p.getFirst(); p p2 p = p.getSecond(); pg () People System.out.printf("%s와 %s는 친구", p1.getName(), p2.getName()); } // printFriend
Wildcard를 Wild d를 이용한 부모 클래스에 대한 제한 예) public static void minmax(Student[] a, a Pair<? super Student> p){ if(a == null || a.length == 0) return null; Student min = a[0]; Student max = a[1]; Pair<People> Pair<People>, f ( for(int i=1; i<a.length; i++){ ){ Pair<Student> 타입의 if(min.compareTo(a[i])>0) min = a[i]; 객체만을 전달할 수 있음 if(max.compareTo(a[i])<0) max = a[i]; } p.setFirst(min); p.setSecond(max); } // minmax
27/30 27 /30
주의사항 – 계속 제한이 없는 Wildcard의 사용 예)
public p blic static boolean hasNull(Pair<?> hasN ll(Pair<?> p){ return (p.getFirst() == null || p.getSecond() == null); } // hasNull
public static <T> boolean hasNull(Pair<T> p){ return (p.getFirst() == null || p.getSecond() == null); } // hasNull
예) public static void swap(Pair<?> p){ ? t = p.getFirst(); // error p.setFirst(p.getSecond()); p.setSecond(t); } // swap
public static <T> void swapHelper(Pair<T> p){ Tt=p p.getFirst(); g (); p.setFirst(p.getSecond()); p.setSecond(t); } // swapHelper public static void swapHelper(Pair<?> p){ swapHelper(p); } // swap
28/30 28 /30
public class Queue<T>{ public static int DEF_CAPACITY DEF CAPACITY = 10; private ArrayList<T> list; private int capacity = DEF_CAPACITY; // 큐의 용량 public Queue(){ this(DEF CAPACITY); this(DEF_CAPACITY); } // Queue() public Queue(int theCapacity){ if(capacity>0) capacity = theCapacity; list = new ArrayList<T>(capacity); } // Queue(int) public void enq(T obj) throws QueueOverflowException{ if(isFull()) throw new QueueOverflowException(); list add(obj); list.add(obj); } // enqueue public T deq() throws QueueUnderflowException{ if(isEmpty(()) throw new QueueUnderflowException(); T tmp = list.get(0); list.remove(0); return tmp; } // dequeue public boolean isEmpty(){ return list.size() list.size()==0;} 0;} public boolean isFull(){ return list.size()==capacity; } public void clear(){ list.clear(); } } // class Queue
29/30 29 /30
public class ArrayAlg{ public static <T extends Comparable<T>> p p void sort(T[] ( [] a){ ){ if(a == null || a.length == 0) throw new IllegalArgumentException(); for(int i = 0; i<a.length-1; i++) for(int j = i+1; i<a i<a.length; length; j++) if(array[j].compareTo(array[i])<0){ T tmp = array[j]; y[j] = array[i]; y[ ]; array[j] array[i] = tmp; } } // sort public static <T> int search(T[] list list, T x){ for(int i=0; i<list.length; i++) if(list[i].equals(x)) return i; return -1;; } // search } // class ArrayAlg
30/30 30 /30
자바프로그래밍(CPA240) NOTE 12
©copyright 2010
파일 처리
한국기술교육대학교 한국기술 육대학 컴퓨 컴퓨터 터공학부 김상진
교육목표 텍스트 파일과 이진 파일 FileReader/FileWriter FileInputStream/FileOutputStream BufferedReader/BufferedWriter D t I DataInputStream/DataOutputStream tSt /D t O t tSt PrinterWriter 파일 Open/Save 대화창 객체의 저장과 읽기 Serialization/Deserialization
2/24
파일 처리의 기본 개념 데이터를 파일에 저장하는 두 가지 방법 방법 1. 텍스트 방식: 사람이 읽을 수 있는 형태로 저장하는 방식 예) 정수 12,345: ‘1’, ‘2’, ‘3’, ‘4’, ‘5’ 방법 2. 이진 방식: 컴퓨터가 이해할 수 있는 형태로 저장하는 방식 예) 정수 12,345: 00 00 48 57 방법 1은 방법 2보다 공간이 많이 소요됨 보통 텍스트 파일은 시스템 마다 저장 방식에 차이가 없으나, 이진 파일은 시스템마다 차이가 있을 수 있음 방법 1은 Reader와 Writer 클래스를 이용하고, 방법 2는 InputStream 과 OutputStream 클래스를 이용하여 보통 처리함 이 클래스들은 클래 들은 모두 java.io 패키지에 포함되어 함되어 있음 C의 fprintf, fscanf와 유사한 기능을 원하면 PrinterWriter와 Scanner 클래스를 사용함 비고.. Stream이란 입출력 장치나 파일과 프로그램 간에 데이터의 비고 흐름을 처리해주는 객체를 말함
3/24
PrintWriter 이용 PrintWriter의 printf를 이용하여 텍스트 파일에 데이터를 저장할 수 있음 새 파일의 생성 예) 또는 기존 파일의 내용을 잃게 됨 PrintWriter out = new PrintWriter(new File("datafile")); // PrintWriter FileWriter("datafile", P i tW it outt = new PrintWriter(new P i tW it ( Fil W it ("d t fil " ttrue)); )) int age1 = 20; 새 파일이 아니라 기존 파일 뒤에 String name1 = "Sangjin Kim"; 데이터를 추가하고자 할 경우 out.printf("%s p ( %d%n",, name,, age); g ); out.close(); Scanner in = new Scanner(new File("datafile")); St i name2 String 2 = in.next(); i t() int age2 = in.nextInt(); System.out.printf("age = %d, name = %s", age2, name2); in.close(); (); 파일의 사용이 끝나면 반드시 파일을 닫아주어야 함 프로그램이 정상적으로 끝나면 파일을 자동으로 닫아주지만 이것에 의존하는 것은 바람직하지 않음
4/24
Scanner 이용 텍스트 파일에서 DataInputStream처럼 원시타입 단위로 입력을 처리하고 싶은 경우에는 Scanner를 이용할 수 있음 Scanner boolean nextBoolean() byte nextByte() double nextDouble() float nextFloat() () int nextInt() int nextInt(int radix) long nextLong() long nextLong(int radix) String nextLine() String next() …
Scanner boolean hasNextInt() boolean hasNext() …
5/24
파일복사 Scanner reader = new Scanner(new File( File("input input.txt txt")); )); PrintWriter writer = new PrintWriter(new File("output.txt")); while(reader.hasNextLine()){ String g s = reader.nextLine(); () writer.println(s); } // while (파일 끝까지) reader.close(); writer close(); writer.close();
6/30
파일 처리의 예 예)
File f = new File("input"); FileInp tStream in = new FileInputStream ne FileInputStream(f); FileInp tStream(f) // FileReader reader = new FileReader(f); 파일복사 프로 프로그램 램
텍스트 파일
이진 파일
FileReader reader = new FileReader("input.txt"); FileWriter writer = new FileWriter("output.txt"); int next = reader.read(); char c; while(next != -1){ c = (char)next; writer.write(c); next = reader.read(); reader read(); } // while (파일 끝까지) reader.close(); writer.close();
FileInputStream in = new FileInputStream("input"); FileOutputStream out = new FileOutputStream("output"); int next = in.read(); byte b; while(next != -1){ b = (byte)next; out.write(b); next = in.read(); in read(); } // while (파일 끝까지) in.close(); out.close();
상대적 경로는 사용자의 현재 디렉토리(폴더)를 기준으로 해석함 read() 메소드는 읽은 값을 정수 타입에 저장하여 반환하여 줌 텍스트 파일의 경우에는 문자 단위로 이진 파일의 경우에 바이트 단위로 데이터를 읽음 이 메소드의 반환 값이 -1이면 파일의 끝을 만났다는 것을 의미함 7/24
public static void main(String[] args) throws IOException{ FileReader reader = null; FileWriter writer = null;; try{ reader = new FileReader("input.txt"); 직접 예외를 처리하고 있으므로 불필요. writer = new FileWriter("output.txt"); 직접 처리하지 않으면 공표해야 함 } catch(IOException e){ FileReader의 생성자는 1) 읽을 System.out.println(e.getMessage()); System.exit(1); 파일이 없거나, 2) 일반 파일이 아니라 } 디렉토리이거나, 3) 기타 다른 이유로 try{ 열 수 없으면 FileNotFoundException int next = reader.read(); 예외를 발생한다. char c; while(next ( != -1){ ){ FileWriter의 생성자는 1) 인자로 주어진 c = (char)next; 파일명이 일반 파일이 아니라 writer.write(c); 디렉토리이거나, 2) 기타 다른 이유로 next = reader.read(); } // while (파일 끝까지) 생성할 수 없 없으면 면 IOException p 예외를 reader.close(); 발생함 writer.close(); } 입출력 관련 예외는 checked 예외이며, catch(IOException e){ java io 패키지에 포함되어 있다. java.io 있다 System.out.println(e.getMessage()); } finally{ reader.close(); writer.close(); } } 8/24
BufferedReader/BufferedWriter 앞 슬라이드에 있는 프로그램의 문제점: 문자 단위로 처리하기 때문에 효율성이 떨어짐 기본적인 클래스를 사용하여 파일 처리를 하면 문자 단위 또는 바이트 단위로만 데이터를 처리할 수 있음 BufferedReader를 이용하면 줄 단위로 데이터를 처리할 수 있음 예) readLine 메소드는 줄 단위로 FileReader reader = new FileReader("input.txt"); BufferedReader in = new BufferedReader(reader); FileWriter writer = new FileWriter("output.txt"); BufferedWriter out = new BufferedWriter(writer); String nextLine = in.readLine(); in readLine(); while(nextLine != null){ out.write(nextLine,0,nextLine.length()); out.newLine(); nextLine = in.readLine(); } // while (파일 끝까지) in.close(); out close(); out.close();
읽어 문자열을 반환하여 준다. 파일 끝을 을 만나면 null을 을 반환하여 줌 BufferedWriter는 readLine에 대응되는 writeLine 메소드가 없음 write(String ( g s,, int off,, int len); ); s: 출력할 문자열 off: 시작 색인 len: 출력할 길이
9/24
버퍼의 개념 버퍼를 사용하지 않는 개념: Writer 측면 data data
data
data
FileWriter
data
버퍼를 사용하는 개념
File data
data
data FileWriter
data
data
data
BufferedWriter
data data File
10/24 10 /24
BufferedReader in = null; BufferedWriter out = null; try{ in = new BufferedReader(new FileReader("input.txt")); out = new BufferedWriter(new FileWriter("output.txt")); } catch(FileNotFoundException e){ System.out.println("File input.txt or output.txt could not be opened"); System.exit(1); } catch(SecurityException e){ System.out.println("File input.txt or output.txt could not be opened"); System.exit(1); } try{ String nextLine = in.readLine(); while(nextLine != null){ out.write(nextLine,0,nextLine.length()); out newLine(); out.newLine(); nextLine = in.readLine(); } } catch(IOException e){ System.out.println(e.getMessage()); } finally{ in close(); in.close(); out.close(); }
11/24 11 /24
DataOutputStream/DataInputStream 이진 파일에 데이터를 저장하고 읽을 때 FileOutputStream과 FileInputStream을 사용할 수 있으며, 텍스트 파일의 경우에는 Fil R d 와 FileWriter를 FileReader와 Fil W it 를 사용할 수 있음 이 경우 바이트 단위 또는 문자 단위/ 지정된 크기의 일련의 바이트 또는 는 일련의 문자 단위 입출력인 read와 와 write만 만 사용할 수 있음 텍스트 파일의 경우에는 BufferedReader와 BufferedWriter를 이용하면 줄 단위 또는 문자열 단위의 입출력을 할 수 있음 readLine, dLi write을 it 을 사용함 DataInputStream, DataOutputStream을 이용하면 원시타입 단위로 이진 파일에 대해 DataInputStream DataOutputStream 입출력을 할 수 있음 boolean readBoolean() void writeBoolean(boolean b) byte readByte() char readChar() double readDouble() float readFloat() int readInt() String readUTF() …
void writeByte(int b) void writeChar(int c) void writeDouble(Double d) void writeFloat(float f) void writeInt(int i) void writeUTF(String s) …
12/24 12 /24
DataOutputStream/DataInputStream 예) DataOutputStream dout = new DataOutputStream(new FileOutputStream("datafile")); int age1 = 20; String g name1 = "Sangjin gj Kim"; dout.writeInt(age1); dout.writeUTF(name1); dout.close(); DataInputStream din = new DataInputStream(new FileInputStream("datafile")); int age2 = din.readInt(); String name2 = din.readUTF(); System.out.printf("age = %d, name = %s", age2, name2); din.close();
13/24 13 /24
File 클래스 java.io 패키지에 있는 File 클래스는 파일시스템에 있는 파일을 나타내기 위해 사용함 이 클래스는 파일의 내용이 아니라 파일 자체로 조작하기 위해 사용됨 File dir = new File("ime"); 예) 새 폴더 만들기 dir.mkdir();
예) 폴더에 있는 내용 나열하기 if(dir.isDirectory()){ String[] contents = dir.list(); for(int i=0; i<contents.length; i++){ System.out.println(contents[i]); } // for } // dir이 File 타입 객체로서 폴더를 나터내고 있는 경우 boolean isDeleted = f.delete(); 예) 파일 또는 폴더 삭제하기 예) 파일 또는 폴더의 완전 경로 얻기 System.out.println(dir.getAbsolutePath()); 14/24 14 /24
File Open/Save Dialog javax.swing 패키지에서 제공하는 JFileChooser를 사용하면 기본적인 파일 열기와 저장 대화창을 사용할 수 있음 예) 파일 열기 대화창 JFileChooser chooser = new JFileChooser(); FileReader reader = null; if(chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION){ File selectedFile = chooser.getSelectedFile(); reader = new FileReader(selectedFile); } …
File 클래스는 java.io 패키지에서 제공하는 클래스임 파일 저장 대화창은 showOpenDialog 대신에 showSaveDialog을 사용함
15/24 15 /24
객체 읽기/쓰기 한 프로그램에서 생성한 데이터를 저장한 후에 나중에 다시 사용할 필요가 종종 있음 자바의 경우 모든 데이터가 객체로 관리되기 때문에 프로그램 실행 동안 생명을 가진 객체들을 파일 시스템에 저장하여 데이터를 보통 저장하게 됨 자바에서 데이터를 저장할 때 사용하는 두 가지 방법 방법 1. 데이터를 생성한 자바 프로그램에서만 오직 그 데이터를 사용할 경우에는 자바에서 제공하는 serialization i li ti 기법을 이용하여 저장함 방법 2. 데이터를 생성한 자바 프로그램 램 뿐만 아니라 다른 프로그램에서도 사용할 경우에는 일반 텍스트 파일(또는 이진 파일)에 저장함
16/24 16 /24
객체 읽기/쓰기 – 계속 객체 단위로 객체를 이진 파일로부터 읽거나 저장할 때에는 ObjectInputStream, ObjectOutputStream 클래스를 사용함 이를 통해 객체를 저장하는 것을 serialization이라 함 0110100101101110 ObjectOutputStream
0110100 1011011 10
FileOutputStream File
이들 클래스를 이용하여 객체에 대한 파일 처리를 하기 위해서는 객체는 Serializable 인터페이스를 구현한 클래스의 인스턴스이어야 함. 만약 객체가 다른 객체를 멤버로 가지고 있으면 그 객체 역시 Serializable 타입이어야 함
17/24 17 /24
객체 읽기/쓰기 – 계속 Serializable 인터페이스는 marker 인터페이스임 비고.. marker 인터페이스: 내용인 빈 인터페이스 비고 보통 인터페이스는 특정한 종류의 메소드를 제공한다는 것을 외부에 알려주기 위해 사용함. marker 인터페이스는 객체가 어떤 메소드를 제공한다는 것을 보장하여 주는 것이 아니라 어떤 특징을 가진다는 것을 나타냄 Serializable 인터페이스를 구현한 객체는 ObjectInputStream과 Obj tO t tSt ObjectOutputStream을 을 이용하여 파일에 읽고 쓸 수 있다는 것을 말함 Serializable 인터페이 인터페이스를 를 구현하지 않는 객체는 이들 클래 클래스를 를 이용하여 저장할 수 없으므로, 저장할 필요가 없는 객체들은 이 인터페이스를 구현한다고 선언하지 않으면 됨
18/24 18 /24
객체 읽기/쓰기 – 계속 Serialization을 이용하여 파일에 저장된 객체를 읽어 다시 생명을 가지도록 하는 것을 deserialization이라 한다.
0110100101101110 ObjectInputStream 해당 클래스를 찾아 적재함. 적재함 JVM이 해당 클래스를 찾을 수 없으면 예외를 발생함
0110100 1011011 10
FileInputStream File
19/24 19 /24
객체 읽기/쓰기 – 계속 참조 타입의 멤버 변수를 가지고 있는 클래스를 저장하고 싶은 경우에는 다음과 같은 현상이 발생할 것으로 걱정될 수 있음 저장 후와 저장하고 읽은 다음에 모습 저장하기 전 모습
비고. clone의 shallow/deep copying과 반대 개념 비고. 그러나 ObjectOutputStream과 ObjectInputStream은 자동으로 이런 현상이 발생하지 않도록 해줌
20/24 20 /24
객체 읽기/쓰기 – 계속 다음과 같은 객체가 있다고 가정하자. 이 때 객체 a를 저장하면 그것이 멤버로 가지고 있는 객체들, 그 객체들이 가지고 있는 객체들까지 모두 저장됨 a 즉, serialization은 object graph 전체를 저장함 Serialization은 “all-or-nothing”임 즉, object graph 전체를 모두 저장할 수 있으면 모두 저장하고, 저장하고 그렇지 않으면 아무것도 저장하지 않음 객체의 멤버변수 중에 serialization할 할때 저장할 필요가 없거나 저장할 수 없는 변수가 있으면 transient 키워드를 사용하여 선언함
21/24 21 /24
객체 읽기/쓰기 – 계속 저장할 수 없는 멤버변수란? Serializable 타입이 아닌 객체는 저장할 수 없음 Serializable 타입이 아닌 이유 이유 1. 클래스를 정의할 때 Serializable 구현하였다고 선언하지 않은 경우 이유 2. 근본적으로 저장할 수 없는 객체들 예) 네 네트워크 워 연결 정 정보,, 파일 객체 static 멤버변수는 저장되지 않음 객체 타입의 transient 멤버변수를 가지는 객체를 파일로부터 읽으면 이 멤버변수는 null값을 ll값을 가지게 됨
22/24 22 /24
객체 읽기/쓰기 – 계속 예) ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("datafile")); BankAccount acount1 = new BankAccount(10000); BankAccount acount2 = new BankAccount(5000); ( ) out.writeObject(account1); out.writeObject(account2); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("datafile")); BankAccount acount3 = (BankAccount)in.readObject(); BankAccount acount4 = (BankAccount)in.readObject(); in.close();
23/24 23 /24
객체 배열의 읽기/쓰기 예) ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("datafile")); BankAccount[] a = new BankAccount[2]; [ ] = new BankAccount(10000); ( ) a[0] a[1] = new BankAccount(5000); out.writeObject(a); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("datafile")); BankAccount[] b = (BankAccount[])in.readObject(); in.close();
24/24 24 /24