자바에서 외부 장치 연동 시 반드시 예외 처리를 해주어야 한다. 연동이 끝나면 관련된 자원을 해제해야 한다.
이때 자동 리소스 닫기 기능(try-with-resources)을 사용하면 이런 작업을 try-catch문이 알아서 해준다.
파일, 소켓, 데이터베이스 연결 같은 리소스를 자동으로 닫아주는 try문
try() 괄호 안에 넣은 리소스는 자동으로 닫힌다.
별도로 finally 블록에서 close() 안 해도 된다
단, 자동 리소스 닫기 기능을 사용하려면 클래스는 FileInputStream클래스처럼 반드시 Autocloseable 인터페이스의 close() 추상 메서드를 구현해야한다.(java.lang.AutoCloseable)
AutoCloseable 또는 Closeable 인터페이스를 구현한 클래스만 가능
인터페이스
설명
Closeable
java.io에 있음. I/O용
AutoCloseable
java.lang에 있음. 일반용
정리
자바에서 파일, 소켓, DB 연결 같은 리소스는 사용 후 반드시 닫아야 한다. 그걸 매번 finally에서 close() 쓰지 않고, 자동으로 처리해주는 문법이 try-with-resources, 자동 닫힘의 기준은 AutoCloseable 구현 여부
대표적으로 AutoCloseable 구현한 클래스들
입출력 관련 (java.io, java.nio)
클래스
설명
FileReader, FileWriter
파일 읽기/쓰기
BufferedReader, BufferedWriter
버퍼로 읽기/쓰기
InputStream, OutputStream
바이트 기반 입출력
PrintWriter
텍스트 출력 스트림
파일 채널/버퍼 (java.nio)
클래스
설명
FileChannel
파일 채널
Scanner
입력 스트림 스캐너
네트워크/DB 관련
클래스
설명
Socket, ServerSocket
네트워크 소켓
Connection (JDBC)
DB 연결 객체
PreparedStatement, ResultSet
SQL 실행/결과
Throws를 이용한 예외 처리 방법
자바 메서드 호출 과정
JVM은 현재 실행 중인 메서드명을 메모리의 스택(stack) 영역에 저장해둔다.
(후입선출)
(stie → method)
throws로 예외 처리하는 방법 : 메서드 호출 과정에서 예외가 발생하면 예외를 메서드를 호출한 곳으로 떠넘기는 방법
try-cath문으로 예외 처리 시 메서드의 기능이 많아지면 코드가 복잡해질 수 있다.
→ 이때 throws로 예외 처리 가독성을 높일 수 있다.
public 리턴타입 메서드명(…) throws 예외클래스1, 예외클래스2 { }
RuntimeException(실행예외) 계열은 throws를 생략해도 된다
실행 결과
점수를 입력하세요.
hello
숫자만 입력 가능합니다.
프로그램 종료
명시적 예외 처리 방법
지금까지 사용한 예외는 자바 자체의 문제에 의해 발생했다.
근데 정상적으로 실행되고 있는 상태임에도 임의로 예외를 발생시켜야하는 경우 ‘throw’키워드를 통해 명시적으로 예외를 발생시킨다.
throw new 예외클래스생성자([매개변수, …])
예제)
실행 결과
call() 메서드 시작
배열크기가 2
프로그램 종료
num[2] = 3; 을 주석처리 해제했을때 실행결과
call() 메서드 시작
java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
at ch16/sec05.ex01.ThrowTest.call(ThrowTest.java:12)
at ch16/sec05.ex01.ThrowTest.main(ThrowTest.java:23)
프로그램 종료
사용자 정의 예외 사용 방법
사용자 정의 예외 클래스 : 프로그래머가 프로그램에서 특정한 상황 발생 시 예외로 인식하기 위해 만든 클래스
일반 예외의 경우 Exception 클래스를 상속 받고, 실행 예외의 경우 RuntimeException 클래스를 상속 받아서 만든다.
예외 클래스 이름은 …Exception이라고 짓는다.
public class 예외클래스명 extends [Exception | RuntimeException] { public 예외클래스명 () { }
public 예외클래스명(String message) { super(message) }; }
예외 발생 시 예외 발생 시각와 메시지를 출력해 주는 사용자 정의 예외 클래스
import java.util.Calendar;
public class UserDefinedException extends Exception {
//사용자 정의 예외 클래스는 반드시 Exception 클래스를 상속받아야한다.
public UserDefinedException() {}
public UserDefinedException(String message) {
super(message);
}
//예외 발생 시각과 예외 메시지를 출력하는 메서드
public String getUserExceptionInfo() {
String errMsg = "예외 발생 시간: " + showTime() + " 예외 내용: " + super.getMessage();
return errMsg;
}
//예외 발생 시각을 출력하는 메소드
public static String showTime() {
String date = null;
String time = null;
Calendar cal = Calendar.getInstance();
int hour = cal.get(Calendar.HOUR);
int min = cal.get(Calendar.MINUTE);
int sec = cal.get(Calendar.SECOND);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int day = cal.get(Calendar.DATE);
date = year + "-" + month + "-" + day;
time = hour + ":" + min + ":" + sec;
return date + " " + time;
}
}
실행 클래스
import java.util.Scanner;
public class ScoreTest {
public static int calcScore(int score) throws UserDefinedException {
//예외 발생 시 예외를 호출한 곳으로 떠넘긴다.
if(score < 0 || 100 < score) {
throw new UserDefinedException("정확한 점수를 입력하세요.");
//범위를 벗어난 점수를 입력하면 강제로 사용자 정의 예외 발생
}
return score;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("시험 점수를 입력하세요");
int score = sc.nextInt();
try {
score = calcScore(score);
System.out.println("입력한 시험 점수는 " + score + "입니다.");
} catch(UserDefinedException e) {
System.out.println(e.getUserExceptionInfo());
} catch(Exception e) {
e.printStackTrace();
}
System.out.println("프로그램 종료");
}
}
int countTokens() : nextToken() 메서드가 호출될 수 있는 횟수를 계산
boolean hasMoreTokens() : StringTokenizer 객체에서 token이 있는지 확인
Object nextToken() : StringTokenizer 객체에서 token을 얻는다.
StringTokenizer 객체 생성 시 내부적으로 포인터가 생성되어 첫 번째 토큰 앞에 위치한다.
StringTokenizer 객체 생성시
---> 2033 05 22
nextToken() 메서드를 호출하면 포인터 다음의 토큰을 리턴한 후 포인터 이동
---> 05 22
실행 결과
사과
바나나
수박
참외
2033
05
22
+) DecimalFormat 클래스
숫자 포맷 형식은 API 문서 참고
DecimalFormat df = new DecimalFormat(”$#,###.#”); String strNum = df.format(1222333.44); //$1,222,333.4
2 Date 클래스
Calendar 클래스가 나오기 전 시간과 날짜 정보를 얻었지만 지금은 운영체제에서 제공하는 현재시간 정보를 얻는 목적으로 주로 사용
SimpleDateFormat 클래스를 사용하여 원하는 날짜 형식으로 출력
SimpleDateFormat() dateForm = new SimpleDateFormat(pattern); dataForm.format(new Date());
메서드
long getTime() : Date 객체의 시간을 리턴
void setTime() : Date 객체의 시간을 매개값으로 받은 시간으로 변경
날짜 형식(pattern) : 더 많은 형식은 API 문서 참고
import java.text.SimpleDateFormat;
‘yyyy.MM.dd G ‘at’ HH:mm:ss z” : 2001.07.04 AD at 12:08:56 PDT
“EEE, MMM d, “yy” : Wed, Jul 4, ‘01
“h:mm a” : 12:08 PM
“yyyyy.MMMMM.dd GGG hh:mm aaa” : 02001.July.04 AD 12:08 PM
헷갈리는 문자, 소문자 vs 대문자
패턴
의미
설명
M vs m
월 vs 분
M은 Month, m은 minute
H vs h
24시간 vs 12시간
H: 13, h: 1 (1시)
a
AM/PM
오전/오후 구분
z
시간대(TimeZone)
예: KST, GMT
‘ ‘
문자 그대로 출력
G
연호(Era) : AD,BC
Gregorian
실행 결과
25. 6. 15. 오전 3:33
2025. 06.15
УУУУ
06
15
2025년 06월 15일
03시 33분 21초
3 Calendar 클래스
Date 클래스의 단점 보완
시간과 날짜 정보를 얻거나 특정 나라의 특정 지역별(TimeZone) 시간 정보를 얻을 때 Calendar 클래스 이용
추상 클래스로 정의되어 있으므로 객체를 얻을 때는 getInstance() 정적 메서드를 호출해서 얻는다.
Calendar cal = Calendar.getInstance();
상수
AM : AM(오전) 표시 여부 설정
AM_PM : AM(오전), PM(오후) 표시 여부 설정
DATE : 달(Month)에서의 날짜(day)
HOUR_OF_DAY : 하루(Day) 기준의 시간
DAY_OF_WEEK : 주(Week)에서의 요일
DAY_OF_YEAR : 연(Year)을 기준으로 지난 일수
HOUR : 오전 또는 오후 기준에서 지난 시간
get() 메서드 호출 시 각각의 상수를 매개값으로 전달해서 날짜와 시간 정보를 얻는다.
Calendar 클래스에서 1월(month)은 0부터 시작하므로 1을 더해서 출력해야 한다.
실행결과
현재 날짜 : 2025년 6월 15일
현재 시간 : 4시 17분 43초
오늘의 요일: 일요일
+)자바에선 기존의 Date 클래스와 Calendar 클래스의 단점을 보완하기 위해 java.time 패키지의 LocalDate, LocalTime, LocalDateTime 클래스를 제공
참조 타입 배열과 객체를 리턴하는 메서드
참조 타입 배열
기본 타입 배열에는 기본 타입 데이터가 저장됐다.
참조 타입 배열에는 클래스의 객체가 저장된다.
Product 클래스 타입으로 배열을 선언하면 Product 클래스 객체만 저장할 수 있다.
실행 결과
배열에 저장한 제품 정보 출력
---------------------------
제품 번호: 0001
제품 이름: 스마트폰
제품 색상: 블랙
제품 수량: 100
제품 번호: 0002
제품 이름: 스마트 TV
제품 색상: 흰색
제품 수량: 200
제품 번호: 0003
제품 이름: 노트북
제품 색상: 은색
제품 수량: 100
메서드 매개변수로 참조 타입 배열 사용
Product 클래스 타입 배열이 메서드의 매개변수로 사용되는 예제
Object 클래스 타입 배열
모든 클래스의 최상위 클래스인 Object 클래스는 모든 클래스 객체를 업캐스팅으로 받을 수 있다.
다시 Object 타입 배열에 접근해서 클래스 객체를 얻으려면 각각의 하위 클래스 타입으로 다운캐스팅해야 한다.
(타입이 다른 클래스로 다운캐스팅 할 시 ClassCastException 예외 발생)
실행 결과
Object 배열에 저장한 정보 출력
-----------------------------
홍길동
제품 번호: 0001
제품 이름: 스마트폰
제품 색상: 블랙
제품 수량: 100
제품 번호: 0002
제품 이름: 스마트 TV
제품 색상: 흰색
제품 수량: 200
제품 번호: 0003
제품 이름: 노트북
제품 색상: 은색
제품 수량: 100
123
객체를 리턴하는 메서드
메서드는 호출한 곳으로 한 개의 값만 리턴한다. 여러 개의 값을 리턴하고 싶다면 클래스 객체의 각각의 필드에 값을 세팅해서 리턴하면 된다.
예제)
실행클래스
실행 결과
이순신의 시험 점수
국어: 80, 영어: 90, 총점: 170
홍길동의 시험 점수
국어: 70, 영어: 83, 총점: 153
열거 타입
열거 타입 정의 : 여러 개의 상수들을 모아서 만든 객체
여러 상수를 편리하게 관리
ordinal() 메서드를 이용해서 상수 순서(index)를 얻을 수 있다.
상수 인덱스는 0부터 시작
열거타입 선언 : ,(콤마)로 구분
접근제한자 enum 열거타입명 { 상수명1, 상수명2, 상수면3, … }
열거 타입 사용
열거타입명 변수 = 열거타입명.상수명
Day 예제)
public enum Day {
SUN, MON, TUE, WED, THU, FRI, SAT //나열한 순서대로 인덱스 저장, 첫 번
}
실행 클래스
public class DayTest {
//public static final String SUN = "sun"; //상수가 많을 경우 불편하다
public static void main(String[] args) {
System.out.println(Day.SUN);
System.out.println(Day.SUN.ordinal()); //상수의 index
System.out.println(Day.MON.ordinal());
Day day = Day.WED; //Day 열거 타입 변수를 선언
System.out.println(day.name());// name 메서드로 상수명 얻기
switch(day) {
case SUN:
System.out.println("일요일");
break;
case MON:
System.out.println("월요일");
break;
case TUE:
System.out.println("화요일");
break;
case WED:
System.out.println("수요일");
break;
case THU:
System.out.println("목요일");
break;
case FRI:
System.out.println("금요일");
break;
case SAT:
System.out.println("토요일");
break;
}
}
}
+) 스레드 상태를 열거 타입에 선언한 후 스레드 상태를 출력하는 코드
자바에서는 스레드 상태도 열거 타입으로 제공된다.
public enum ThreadState { NEW, RUNNABLE, WAITING, TIMED_WAITING, BLOCKED, TERMINATED}
1. 같은 문자열로 초기화했을때 : String은 위치를 가리키는 참조변수로 같은 문자열의 ‘위치’을 가리킨다.
String city1 =”서울시”; String city2 = “서울시”;
city1 → “서울시” ← city2
2. new 연산자로 객체를 생성해서 문자열을 가리켰을 경우 각각의 객체를 가리킨다.
String city1 = new String(”서울시”); String city2 = new String(”세종시”);
city1 → “서울시”
city2 → “세종시”
문자열 비교는 equals()
변경 불가성(Immutable) 불변성
int타입 변수는 다른 값을 대입했을 때 변경되지만
String 문자열의 경우 원래의 문자열을 변경하면 원래의 문자열은 메모리에 그대로 유지되고, 변경된 문자열이 따로 만들어진다.
예제)
실행결과
a = 3
b = 5
a = 5
b = 5
name =홍길동
name = 홍길동
result = 홍길동안녕
Hello World
String 클래스의 유용한 메서드
char charAt(int index) : index에 해당되는 문자를 리턴
int length() : 문자열의 길이를 리턴
int compareTo(String str) : 두 문자열의 철자를 비교
String concat(String str) : 두 문자열을 결합
boolean equals(Object obj) : 같은 문자열인지 판별
int indexOf(int ch) :문자열에서 문자열의 위치(index)를 리턴
int lastIndexOf(int ch) : 문자열을 우측에서 검색 시 인자로 전달된 문자열의 위치를 리턴
String replace(char oldChar, char new Char) : 문자열에서 특정 문자(oldChar)를 다른 문자(newChar)로 대체
String replace(Charsequence target, Charsequence relacement) : 문자열에서 특정 문자열(target)을 다른 문자(replacement)로 대체
String substring(int beginIdx, endIdx) : beginIdx에서 endIdx 사이에 위치한 문자열을 잘라서 리턴
String toLowerCase() : 문자열을 소문자로 변환 후 리턴
String toUpperCase() : 문자열을 대문자로 변환 후 리턴
String trim() : 문자열의 앞뒤 공백을 제거한 후 리턴
length() String str = “Hello world”; System.out.println(str.length()); //11 System.out.println(”hello world”.length()); //11 → 문자열에 직접 length() 메소드를 호출할 수 있다.
charAt() : 문자열의 첫번째 문자의 인덱스는 0부터 시작 → 문자열 길이에 해당하는 인덱스 전달하면 예외 발생 String str = “Hello World”; System.out.println(str.charAt(4)); //’o’ System.out.println(str.charAt(11)); //StringIndexOutOfBoundsException이 발생
indexOf() : 인자에 해당되는 문자열의 인덱스를 리턴, 해당하는 문자열이 없는 경우 -1리턴 String str = “Hello World” System.out.println(str.indexOf(”o”)); //4 System.out.println(str.indexOf(”World”)); //6 System.out.println(str.indexOf(”a”)); // -1
replace() : 매개값의 첫 번째 문자열을 인자의 두 번째 문자열로 대체 String str = “Hello World”; System.out.println(str.replace(”World”, “Java”)); // “Hello Java”
substring() : 문자열에서 첫 번째 인덱스부터 두 번째 인덱스 -1까지의 문자열 추출 String str = “Hello World”; System.out.println(str.substring(2,7)); //llo W
String 클래스 메서드 사용해보기
실행 결과
11
11
o
4
6
-1
Hello World and Java!!
Hello World and Java!!
hello world
HELLO WORLD
Hello Java
llo W
true
Hello World
Hello World
주민번호 뒷자리 번호 ‘*’로 표시하기
Split() 메서드
문자열을 특정한 구분자를 기준으로 분리
split() 으로 분리하면 각각의 분리된 문자열(토큰)은 배열로 저장된다.
실행결과
사과
바나나
귤
수박
인사부
회계부
개발부
관리부
스마트TV
스마트폰
노트북
태블릿
getBytes() 메서드
문자열의 각각의 문자를 사용할 경우
예: 한글 문자열을 인코딩이 다른 네트워크로 전송하거나 인코딩이 다른 데이터베이스에 저장할 때
getBytes() 메서드를 이용해 문자들을 byte 타입 배열로 얻을 수 있다.
(인코딩을 지정하지 않으면 사용하는 운영체제의 인코딩이 기본으로 적용된다)
byte[] encodeBytes= "자바 프로그래밍".getBytes();
String decodeName = new String(encodeBytes, "UTF-8"); //byte 데이터를 String 객체의 매개값으로 입력해서 원래 문자열로 복원
byte[] b= "hello".getBytes();
System.out.println(b.length); //5
String decodeName2 = new String(b, 2, 3, "UTF-8"); //llo
2인덱스부터 3문자를 추출, UTF-8로
“””연산자
jdk 8부터 생겼다. 문자열을 한꺼번에 여러 줄에 출력하는 기능
실행결과
안녕하세요
"자바"프로그래밍입니다.
안녕하세요.
"자바"
프로그래밍입니다.
+) String 타입 변수 str을 초기화 시
null로 초기화 : 메모리에 String 객체가 생성되지 않은 상태
“”로 초기화 : String 객체를 생성 후 길이가 0인 문자열을 가지는 객체
StringBuffer
StringBuffre 문자열은 new 연산자를 이용하여 메모리에 객체를 생성한 후 사용
특징 : 기존 문자열을 수정하면 문자열이 수정(mutable)된다.
사용 : StringBuffer city1 = new StringBuffer("서울시");
String 클래스와 달리 기존의 문자열을 바꾸면 새로운 문자열이 생성되지 않고, 기존의 문자열만 변경된다.
append() 메서드를 이용하여 문자열 수정
+) 바로 문자열 변경이 가능하므로 그만큼 빠르다. → 안드로이드와 같은 메모리 자원이 부족한 모바일 환경에서 문자열을 자주 변경할 경우 많이 사용된다.
실행결과
서울시
서울시 강남구
서울시 강남구 역삼동
StringBuilder 클래스
기능은 기본적으로 StringBuffer와 같다.
다른점은 StringBuffer는 멀티 스레드에 동기화 기능이 제공된다. → 그만큼 성능이 떨어진다.
StringBuilder 클래스는 동기화 기능이 제공되지 않는다.
→ 멀티 스레드 환경이 아니면 StringBuilder 클래스 타입 문자열을 사용하면 좋다.
System 클래스
운영체제에서 제공하는 기능 사용
기능
필드와 메서드
설명
표준 입출력
System.in System.out
키보드로 입력하고 콘솔(모니터)로 출력
System.err
콘솔(모니터)로 에러를 출력
환경변수 읽기
getenv()
운영체제의 설정된 환경 변수를 읽어온다
시스템 프로퍼티 읽기
getProperties()
프로그램의 환경 모드를 프로퍼티 형태로 읽어온다
현재 시각 측정
currentTimeMillis()
운영체제에서 현재 시각을 읽어온다
프로그램 실행 기능
exit()
프로그램 끝내는 기능과 가비지 컬렉터 관련 기능
보안 설정 기능
getSecurityManager()
자바 프로그램의 보안 관리자 설정 기능
기타
arraycopy()
배열을 효율적으로 복사하는 기능
프로그램 종료(exit)
exit() 메서드로 강제 종료시킬 수 있다.
매개변수값은 종료 상태값
0이면 정상종료
0이 아니면 비정상 종료
예제)
실행 결과
점수를 입력해주세요
90
점수: 90점
---
점수를 입력해주세요
120
점수를 잘못 입력했습니다.
현재 시각 출력하기(currentTimeMillis)
long time = System.currentTimeMillis();
currentTimeMillis() 메서드로 작업 시간 구하기
실행 결과
1에서 100000까지의 합: 5000050000
계산 소요 시간: 1ms
1에서 10000000까지의 합: 50000005000000
계산 소요 시간: 7ms
Class 클래스
자바는 클래스는 먼저 정의한 후 new 연산자를 이용해 클래스 객체(인스턴스)를 이용했다.
그러나 실행 중 메모리의 클래스 객체로부터 원래 클래스에 관한 정보를 얻어야 하는 경우가 있다.
클래스로부터 정보 얻기
Class class1 = student.class Class class2 = Class.forName(”sec01.ex01.Student”);
forName() 메서드로 Student 클래스로부터 정보를 얻는다.
forName()은 매개값으로 패키지명과 클래스명을 모두 입력해야한다.
객체로부터 정보 얻기
Student s1 = new Student(”이순신”, 3); Class class3 = s1.getClass();
클래스 객체를 가리키는 참조 변수의 getClass() 메서드를 호출하면 Class 객체를 얻을 수 있다.
getName() : 패키지명까지 포함한 클래스명을 얻는다.
getMethods() : 클래스에 선언된 메서드명을 배열로 얻는다.
getSimpleName() : 클래스명을 얻는다.
getPackageName() : 패키지명을 얻는다.
getResources() : 매개값에 지정된 리소스의 경로를 얻는다.
Class 클래스로 클래스 정보 얻기
forName() 메서드 사용 시 반드시 main() 메서드에 예외 처리를 해주어야 한다.
하위 클래스에선 여러 개의 인터페이스를 구현할 수 있다(다중 인터페이스) → 자바의 단일 상속 기능을 보완
인터페이스를 상속 받아 클래스 구현하면 인터페이스의 모든 추상메소드를 구현해야 한다.
용도 : 기능만을 하위 클래스에 강제하기 위해서 사용
인터페이스 내에서 final 지정자를 사용하지 않고 변수 선언과 동시에 초기화하면 상수로 인식
추상 메서드는 abstract 지정자를 생략해도 된다.
예)
Car interface
Truck : 구현 클래스
SportsCar : 구현 클래스
CarTest
결과
인터페이스로 회원 관리 기능 구현
Member class
MemberImpl class
package sec03.ex01;
public class MemberImpl implements Member {
@Override
public void joinMember() {
//회원 등록 기능
}
@Override
public void searchMember() {
//회원 조회 기능
}
@Override
public void modMember() {
//회원 수정 기능
}
@Override
public void delMember() {
//회원 삭제 기능
}
private void encodeMemberInfo() {
//회원 정보 암호화 기능
}
private String decodeMemberInfo() {
//회원 정보 복호화 기능
return "복호화된 회원 정보";
}
}
+) 변수이름에 주목 : 한 눈에 뜻과 기능을 알 수 있어야한다.
다중 인터페이스
Test1 interface
Test2 interface
구현 클래스 : Child class : 두개의 인터페이스를 모두 구현해야 한다.
ChildTest class (실행 클래스)
→ 구현 클래스가 구현하는 인터페이스는 모두 업캐스팅 참조 변수로 사용할 수 있다.
그러나 업캐스팅 상태에선 인터페이스에 선언된 추상 메서드만 호출 가능하다 .
인터페이스 간의 상속
인터페이스와 인터페이스의 계층 구조는 상속 구조이다.
구현 클래스에선 인터페이스와 상속 관계인 부모 인터페이스의 추상 메서드도 반드시 구현해야 한다.
구현 클래스 타입 변수는 모든 메서드를 호출 할 수 있다. (업캐스팅된 변수를 주의! → 다운캐스팅 후 호출)
인터페이스 다중 상속
클래스와 달리 다중 상속을 허용한다.
default 메서드와 static 메서드
사용하지 않는 인터페이스의 기능을 구현 클래스에서 모두 구현해주어야한다는 불편함
→ JDK8부터 디폴트 메서드와 static 메서드 도입
→ static이 붙으면 간단하게 클래스 혹은 인터페이스 이름.메서드
+) 오버라이딩이 아니다. 오버라이딩은 static이 없는 일반 메서드에서나 가능
public interface A {
public static void mS() {
System.out.println("A의 이름으로");
}
}
public interface B extends A {
public static void mS() {
System.out.println("B의 이름으로");
}
}
public class C implements B {
public static void mS() {
System.out.println("Class의 이름으로");
}
}
public class Test {
public static void main(String[] args) {
A.mS();
B.mS();
C.mS();
}
default 메서드 : Java8부터 인터페이스에 default 키워드를 이용해 구현이 포함된 메서드를 정의할 수 있다.
public interface A {
public default void mD() {
System.out.println("박쥐 메소드!");
}
}
public interface B extends A {
public void mD() //추상메소드!
}
public class C implements B {
@Override
public void mD() {
System.out.println("알고보니 박쥐였어!");
}
}
public class Test {
public static void main(String[] args) {
C c = new C();
c.mD(); //알고보니 박쥐였어!
}
default 메소드는 일반메소드처럼 상속, 오버라이딩도 되고 다시 추상메소드도 되는 특이한 메소드!
다시 추상이 되는건 ‘진짜 다시 추상으로 바꾸는 것’은 아니고, default 메서드를 무시하고 다시 추상화하는 방식으로 이해하면 된다.
예:
A는 default를 이용해 기본 구현을 제공
→ B는 그것을 받아들이지 않는다(추상화)
→ 이렇게 되면 B를 구현하는 클래스는 반드시 오버라이딩을 해야한다.
→ A를 참조변수로 생성자를 만들어도 새롭게 오버라이딩된 메소드가 실행된다
내부 클래스와 내부 인터페이스
1.내부 클래스
웹 프로그래밍에선 거의 사용하지 않지만 안드로이드 앱이나 PC용 자바 프로그램에서 주로 화면에서 사용자가 발생시킨 이벤트를 처리하는 용도로 사용된다.
형식:
class 외부클래스명 { … class 내부클래스명 { … } … }
내부 클래스 종류
멤버 내부 클래스 : 외부 클래스의 멤버로 사용
로컬 내부 클래스 : 외부 클래스의 메서드 내에서 정의된다.
정적 내부 클래스 : static으로 지정된 내부 클래스
익명 내부 클래스 : 내부 클래스가 이름 없이 사용된다.
멤버 내부 클래스
멤버 내부 클래스는 외부 클래스의 멤버이므로 같은 클래스에 있는 다른 멤버들에 자유롭게 접근 가능
package sec01.ex01;
public class Outter {
private String name;
private int age;
public Outter(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
/*
* public String getAddress() { //외부 클래스에선 내부 클래스에 접근할 수 x
* return address;
*/
//내부 클래스
public class Inner {
private String address;
public Inner(String address) {
this.address = address;
}
public String getUserInfo() {
return "이름: " + name + ", 나이: " + age + ", 주소: " + address;
//내부 클래스의 메서드에서 외부 클래스 멤버에 자유롭게 접근가능
}
}
}
package sec02.ex01;
public class Outter {
public void startChat() {
class ChattingThread { //로컬 내부 클래스는 메서드 내에서 구현됨
public void sendData() {
System.out.println("메시지를 송신합니다.");
}
public void receiveData() {
System.out.println("메시지를 수신합니다.");
}
}
ChattingThread chat = new ChattingThread(); //내부 클래스 인스턴스 생성 후 데이터 송수신
chat.sendData();
chat.receiveData();
}
public static void main(String[] args) {
Outter outter = new Outter(); //외부 클래스 인스턴스 생성
outter.startChat(); //메서드 호출 시 로컬 내부 클래스 인스턴스가 생성된다.
}
}
정적 내부 클래스
static으로 지정된 클래스 : main() 메서드가 실행되면 즉시 클래스가 메모리에 생성됨
package sec03.ex01;
public class Outter {
private String name;
private int age;
public Outter(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
static class Inner {
private String address; //인스턴스 필드
static String tel; //정적 필드
public Inner(String address) {
this.address = address;
}
public static String getTel() { //정적 메서드
return tel;
}
public String getUserInfo() {
return "주소: " + address + "\n"
+"전화번호: " + tel;
}
}
}
정적 내부 클래스는 프로그램 실행 시 바로 메모리에 생성되므로
외부클래스명.내부클래스명.멤버명으로 바로 접근할 수 있다.
또한 new 연산자를 이용해 인스턴스 생성도 가능
실행 클래스
package sec03.ex01;
public class InnterTest {
public static void main(String[] args) {
Outter.Inner.tel = "010-1111-2222"; //static 멤버는 바로 접근
String tel = Outter.Inner.getTel();
//Outter.Inner.address = "010-1111-2222"; 인스턴스 멤버 필드는 바로 접근할 수 없다.
System.out.println("전화번호: " + tel);
Outter outter = new Outter("이순신", 17);
Outter.Inner inner = new Outter.Inner("세종시 세종구");
String name = outter.getName();
int age = outter.getAge();
System.out.println("고객 정보");
System.out.println("---------------------------");
System.out.println("이름: " + name);
System.out.println("나이: " + age);
System.out.println(inner.getUserInfo());
}
}
구분
내부 클래스 (non-static)
정적 내부 클래스 (static)
바깥 클래스 인스턴스 필요?
필요함
필요 없음
생성 방식
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();
package sec04.ex01;
public class Button {
ButtonEventListener listener;
public void addButtonEventListener(ButtonEventListener listener) {
this.listener = listener;
}
public void btnClicked(String btnName) {
listener.btnClicked(btnName);
}
static interface ButtonEventListener { //내부 인터페이스
public void btnClicked(String btnName);
}
}
내부 인터페이스를 구현한 handler 클래스
package sec04.ex01;
public class ButtonEventHandler implement Button.ButtonEventListener{
@Override
public void btnClicked(String btnName) {
System.out.println(btnName + " 버튼을 클릭합니다.");
}
}
실행 클래스
package sec04.ex01;
public class ButtonTest {
public static void main(String[] args) {
Button button = new Button();
button.addButtonEventListener(new ButtonEventHandler());
button.btnClicked("등록하기");
button.btnClicked("수정하기");
}
}
: 자주 사용되는 기능은 미리 만들어 놓고 필요시 처리할 데이터만 전달해 주면 쉽게 원하는 결과를 얻게 해 주는 기능 제공
크게 선언부(header)와 구현부(body)로 나눌 수 있다.
리턴타입 메서드이름([매개변수1, 매개변수2,…]) { 지역변수
구현코드
리턴 문 }
선언부에서 매개변수나 리턴 타입은 생략 가능
구현부에서 리턴문 생략 가능
메서드 명명 규칙
첫 글자는 알파벳과 특수문자 ‘$’, ‘_’만 가능
대문자와 소문자 구분
카멜표기법 사용
가능한한 기능을 잘 나타내는 동사형으로 → calcScore()
예)
int increase(int x) { int result; //지역변수 : 메서드 구현 블록 내에서 선언한 변수, 구현 블록 내에서만 사용 가능 result = x + 1; return result; }
package sec01.ex01;
public class MethodEx1 {
public static void main(String[] args) {
MethodEx1 m = new MethodEx1();
m.print();
m.print();
m.print();
System.out.println("종료");
}
public void print() {
System.out.println("메서드를 호출합니다.");
}
}
궁금한 점 : 변수와 달리 메서드는 왜 나중에 정의해도 앞에서 읽힐 수 있는가?
자바는 순차적으로 run 실행하긴 하지만 가장 먼저 전체를 한 번 훑는다.(컴파일)
컴파일 후에 실행을 하므로 메서드는 정의만 돼있다면 어디서든지 호출 가능
(변수는 값을 메모리에 저장해야하는데 먼저 변수가 쓰이고 나중에 선언이 되면 값이 없기 때문에 에러)
+
클래스 = 설계도 (자동차를 정의, 실제로 자동차가 있는 것은 아니다)
public class Car{ String color; void drive() { System.out.println(”달립니다.”); } }
인스턴스 = 클래스로부터 만들어낸 객체(object), 즉, 설계도로 찍어낸 실제 물건
Car myCar = new Car();
인스턴스를 생성하면 다음이 가능하다.
myCar.color = “빨강”; → 변수 값 저장 및 조작
myCar.drive(); → 메서드 사용 : 자동차가 실제로 달리는 기능 수행
Car yourCar = new Car() → 여러 개 생성 가능
예제) 직육면체 3개 입력받아 부피값 구하기
package sec01.ex01;
import java.util.*;
public class Test2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Test2 m = new Test2();
System.out.println("width, length, height를 입력해주세요. (공백 구분)");
System.out.println("------------------------------------------");
for(int i = 1; i<=3; i++) {
System.out.print("직육면체" + i + " : ");
int width = sc.nextInt();
int length = sc.nextInt();
int height = sc.nextInt();
System.out.println("직육면체" + i + "의 부피는 : "
+ m.calcVolume(width, length, height));
}
}
public int calcVolume (int width, int length, int height) {
return width * length * height;
}
}
예제 ) 두 수의 정수를 입력받고 사이의 정수 합 구하기
package sec01.ex01;
import java.util.*;
public class Test3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Test3 m = new Test3();
System.out.println("두 정수를 입력하세요.");
System.out.print("시작 정수 : ");
int start = sc.nextInt();
System.out.print("끝 정수 : ");
int end = sc.nextInt();
m.sum(start, end);
}
public void sum(int start, int end) {
int sum =0;
for(int i = start + 1; i <end; i++) {
sum += i;
}
System.out.println("--------------결과 출력--------------");
System.out.println(start + "와 " + end + "사이의 정수들의 합 : " + sum);
}
}
start와 end 수를 받아서 sum이라는 int를 리턴하는 메서드만 생각했는데,
void로 지정해서 sysout으로 출력하는 기능을 넣을 수도 있구나!
문제4) 배열의 평균(실수형)을 구하는 메서드
package sec01.ex01;
import java.util.*;
public class Test4 {
public static void main(String[] args) {
Test4 m = new Test4();
int[] score1 = {45,67,78,89,91};
int[] score2 = {62,65,76,77,88};
System.out.println("score1의 평균 : " + m.average(score1));
System.out.println("score2의 평균 : " + m.average(score2));
}
public double average(int[] arr) {
double average = 0;
int sum = 0;
for(int i =0; i<arr.length; i++) {
sum += arr[i];
}
average = sum/(double)arr.length;
return average;
}
}
절차 지향 프로그래밍
일을 처리하는 순서와 과정이 중요
순서, 과정이 달라지면 새로운 작업 모델이 필요
컴퓨터의 작업 방식을 프로그래밍에 적용
기존 코드의 재사용성이 매우 낮음
객체 지향 프로그래밍
사람이 생각하고 행동하는 방식으로 프로그래밍
순서, 과정이 중요하지 않음
이해하기 쉽고, 모듈화(조립화)가 쉽다
기존 코드의 재사용성이 높고, 유지보수가 용이
설계 중심 언어
객체 지향 프로그래밍에서 자동차의 각각의 기능을 수행하는 부품이 클래스
각 기능을 클래스로 나누어 개발한 후 조립한다.
결론적으로 객체 지향 프로그래밍은 현실의 객체를 코드화(클래스화)시켜서 컴퓨터 시스템에서 실행되는 가상현실을 만드는 과정
단계
아이디어 도입 : 구현할 대상을 정한다.
아이디어 분석 : 주체의 관점에서 대상 분석
객체 추출 후 속성과 기능 찾기
객체 모델링 : 개발자 관점에서 객체의 속성과 행위를 정제한다.
클래스 변환 : 객체를 클래스로 변환한다.
인스턴스 생성 및 사용
클래스
= 객체 모델링을 통해 추출된 객체의 속성과 기능을 → 필드와 메서드로 변환한 것.
설계도(붕어빵 틀) → 이 틀을 가지고 만들어내는게 인스턴스(실제 객체)
클래스명 작성 규칙
클래스명의 첫 글자는 반드시 대문자이어야 한다.
하나 이상의 단어로 이루어져야한다.
첫 글자는 숫자가 사용될 수 없다.
‘_’, ‘$’ 외의 특수 문자를 사용할 수 없다.
자바 키워드나 자바에서 제공하는 클래스명은 사용할 수 없다.(MyString(O), String(x))
접근제한자 종류
+: public
#: protected
-: private
필드
필드는 클래스 내에 선언된 변수
멤버 변수, 속성(attribute)라고도 부른다.
접근 : 도트(.)연산자를 사용하여 접근한다.
필드의 타입에 따른 기본 초기값 : 0, false, null…정도이다.
필드와 지역(로컬) 변수의 차이점
지역변수
필드
선언 위치
메서드나 생성자 블록
클래스 블록
메모리 생성 시점
메서드나 생성자 호출 시 생성
인스턴스 생성 시 생성
메모리 소멸 시점
메서드나 생성자 종료 시 소멸
인스턴스가 메모리에서 삭제 시 소멸
접근 범위
메서드나 생성자 내부에서만 접근 가능
클래스 내부와 클래스 외부에서 접근 가능
메서드
메서드는 객체의 기능이 변환되어 구현된다
실행 클래스
클래스들을 조립하는 기능
main() 메서드를 포함하고 있으므로 가장 먼저 실행된다.
생성자
인스턴스 생성 시 new 다음에 ‘클래스명()’이라고 썼는데 이것이 생성자(기본 생성자)
즉, 클래스 인스턴스를 메모리에 생성하려면 생성자를 호출해야 한다.
생성자 정의
최초로 호출되면서 클래스 인스턴스를 생성한다.
반드시 클래스명과 동일해야 한다.
특징
메서드와 비슷한 기능을 가진다.
리턴 타입이 없다.
생성자를 구현해 주지 않으면 컴파일러가 컴파일 시 자동으로 기본 생성자를 추가
직접 생성자를 구현해준 경우 컴파일러는 더 이상 기본 생성자를 자동으로 추가해 주지 않는다.
생성자를 직접 구현할 경우 기본 생성자를 함께 만들어주는 것을 습관화하는 것이 좋다!
오버로딩 over loading
클래스 이름을 재사용해 여러 개의 생성자가 존재할 수 있다.
반드시 매개 변수의 타입, 개수, 순서가 달라야 한다.
메서드 오버로딩 또한 마찬가지
+) 리턴 타입만 다르면 같은 메서드로 인식한다.
패키지
클래스들을 기능별로 묶어서 관리
패키지의 물리적인 형태는 파일 시스템의 폴더로 나타낸다.
package 상위패키지명.하위패키지명;
최상단에 한 번만 선언해야 한다
숫자로 시작하면 안되고 ‘_’, ‘$’ 이외의 특수문자를 사용해선 안된다.
소문자로 작성하는 것이 관례
예) www.oracle.com 이라는 도메인명을 사용하는 회사에서 개발하는 프로그램에 대한 패키지명을 만들때 도메인명을 역순으로 배열
→ com.oracle.프로젝트명.기능명.클래스명
import문
다른 패키지에 있는 클래스를 이용하려면 import문 작성하여 미리 선언
*: 해당 패키지의 모든 클래스들을 컴파일 시 import
기본 타입 변수 : 변수에 실제 값을 저장
참조 타입 변수 : 변수에 인스턴스의 위치값을 저장
참조 타입 변수를 null로 초기화하면 어떤 인스턴스도 위치값도 지정되지 않았으므로 예외 발생
+) 같은 파일에 다른 클래스 작성 시 public으로 선언하지 않고 작성해야 한다.
문자열 매개변수
자바에서 매개변수 타입이 문자열인 경우 예외적으로 기본 타입 매개변수처럼 동작한다.
문자열은 참조 타입(String 객체) 이지만 불변(immutable) 이라서 기본타입처럼 보이는 것
기본 타입 매개변수의 경우
public void changeValue(int num) {
num = 100;
}
//호출시
int x = 10;
changeValue(x);
System.out.println(x); // 10
불변 객체(immutable)이기 때문! → 내부값 변경 불가, 다른 객체 참조시 원본에 영향 없음.
자바에서 String은 참조형이지만 실제로는 마치 값 타입처럼 동작한다.
클래스 실습
car, main, member,res 4개의 패키지 생성 후
각 패키지에 하나씩 클래스 생성 (Car, RentCarApp, Member, Reserve 생성)
RestCarApp (main)
package main;
import car.Car;
import member.Member;
import res.Reserve;
public class RentCarApp {
public static void main(String[] arg) {
//회원, 차, 예약 인스턴스 생성
Member member = new Member();
Car car = new Car();
Reserve reserve = new Reserve();
//회원, 차, 예약 정보 등록
member.regMember("lee", "1234", "이순신", "서울시 도봉구", "010-1111-2222");
car.regCarInfo("11가 1111", "소나타", "검정", 2000, "현대");
reserve.resCar("20220708", "11가1111", "2020-04-30", "2020-05-01", "2020-05-08");
//회원 정보 조회
String memberInfo = member.viewMember();
System.out.println(memberInfo);
//렌터카 정보 조회
String carInfo = car.checkCarInfo();
System.out.println(carInfo);
//예약 정보 조회
String resInfo = reserve.checkResInfo();
System.out.println(resInfo);
//회원, 차, 예약 정보 수정
//회원, 차 예약 정보 삭제
}
}
Car
package car;
public class Car {
String carNumber;
String carName;
String carColor;
int carSize;
String carMaker;
//생성자
public Car() {
}
public Car(String _carNumber, String _carName, String _carColor, int _carSize, String _carMaker) {
carNumber = _carNumber;
carName = _carName;
carColor = _carColor;
carSize = _carSize;
carMaker = _carMaker;
}
//차 정보 조회
public String checkCarInfo() {
System.out.println("\n렌터카 정보를 조회합니다.");
System.out.println("---------------------------");
String carInfo = "차 번호: " + carNumber
+"\n차 이름: " + carName
+"\n차 색상: " + carColor
+"\n차 크기: " + carSize
+"\n차 제조사: " + carMaker;
return carInfo;
}
//차 정보 등록
public void regCarInfo(String _carNumber, String _carName, String _carColor, int _carSize, String _carMaker) {
System.out.println("차 정보를 등록합니다.");
carNumber = _carNumber;
carName = _carName;
carColor = _carColor;
carSize = _carSize;
carMaker = _carMaker;
}
//차 정보 수정
public void modCarInfo() {
System.out.println("\n렌터카 정보를 수정합니다.");
}
//차 정보 삭제
public void delCarInfo() {
System.out.println("\n\n렌터카 정보를 삭제합니다.");
}
}
Member
package member;
public class Member {
String id;
String password;
String name;
String adress;
String phoneNum;
//생성자
public Member() {
}
public Member(String _id, String _password, String _name, String _adress, String _phoneNum) {
id = _id;
password = _password;
name = _name;
adress = _adress;
phoneNum = _phoneNum;
}
//회원정보 조회
public String viewMember() {
System.out.println("\n회원정보를 조회합니다.");
System.out.println("-----------------------");
String memberInfo = "아이디: " + id
+"\n비밀번호: " + password
+"\n이름: " + name
+"\n주소: " + adress
+"\n전화번호: " + phoneNum;
return memberInfo;
}
//회원정보 등록
public void regMember(String _id, String _password, String _name, String _adress, String _phoneNum) {
System.out.println("회원 가입합니다.");
id = _id;
password = _password;
name = _name;
adress = _adress;
phoneNum = _phoneNum;
}
//회원정보 수정
public void modMember() {
System.out.println("\n회원정보를 수정합니다.");
}
//회원정보 삭제
public void delMember() {
System.out.println("\n\n회원정보를 삭제합니다.");
}
}
Reserve
package res;
public class Reserve {
String resNumber;
String resCarNumber;
String resDate;
String useBeginDate;
String useEndDate;
public Reserve() {
}
public Reserve(String _resNumber, String _resCarNumber, String _resDate, String _useBeginDate, String _useEndDate) {
resNumber = _resNumber;
resCarNumber = _resCarNumber;
resDate = _resDate;
useBeginDate = _useBeginDate;
useEndDate = _useEndDate;
}
//차 예약 정보 조회 가능
public String checkResInfo() {
System.out.println("\n차 예약 정보를 조회합니다.");
System.out.println("----------------------");
String resInfo = "예약 번호 : " + resNumber
+"\n예약 차 번호 : " + resCarNumber
+"\n예약일자 : " + resDate
+"\n차 사용 시작 일자 : " + useBeginDate
+"\n차 반납 예정 일자 : " + useEndDate;
return resInfo;
}
//차 예약 기능
public void resCar(String _resNumber, String _resCarNumber, String _resDate, String _useBeginDate, String _useEndDate) {
System.out.println("차 예약 정보를 등록합니다.");
resNumber = _resNumber;
resCarNumber = _resCarNumber;
resDate = _resDate;
useBeginDate = _useBeginDate;
useEndDate = _useEndDate;
}
//차 예약 정보 수정
public void modResInfo() {
System.out.println("\n차 예약 정보를 수정합니다.");
}
//차 예약 정보 취소 기능
public void delResInfo() {
System.out.println("\n\n차 예약을 취소합니다.");
}
}
클래스 사이의 관계
is-a 관계 : 한 클래스가 다른 클래스를 구체적으로 설명한다.
use- a 관계 : 클래스가 다른 클래스를 사용
has-a 관계 : 다른 클래스를 소유하는 관계
더 많은 관계들이 존재…
클래스 상속
정의
is-a 관계가 성립되는 클래스 관계가 변환된 것
특정 클래스가 가지는 속성과 기능을 다른 클래스가 사용할 수 있게
용도
다른 클래스의 속성과 기능을 재사용 가능하다.
중복 코드 제거 → 가독성과 유지보수성이 높아지고 개발 기간 단축
만드는 법
일반화 : 공통점을 모아 놓은 클래스가 부모 클래스가 된다.
전문화 : 특정 클래스에서 각각의 하위 클래스로 분화 (자동차 → 승용차, 버스, 트럭)
형식
extends 키워드
+) 자바는 단일 상속만 지원하고, 다중 상속은 지원하지 않는다.
생성자는 상속되지 않는다.
일반적으로 부모 클래스 필드는 protected 접근 제한자 사용
예)
Student라는 부모 클래스를 만들고
Middle과 College라는 자식 클래스를 만든다.
이때 자식 클래스의 생성자를 호출하면 자동으로 부모 클래스의 생성자르 먼저 호출해서 부모 클래스의 인스턴스 생성 후, 자식 클래스 인스턴스가 생성된다.
Student 생성자 호출
Student 인스턴스 생성
College 생성자 호출
College 인스턴스 생성
(부모 인스턴스가 먼저 생성된 후, 자식 인스턴스가 생성된다)
+) UML(Unified Modeling Language)
= 소프트웨어를 설계할 때 사용하는 표준화된 시각적 언어
그 중에서도 “클래스 다이어그램”이 가장 많이 쓰이며, 그 안에 클래스 계층도가 있다.
클래스 계층도 = 상속관계를 트리구조로 보여주는 다이어그램
예)
class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }
class Bulldog extends Dog { }
→ UML 클래스 계층도
Animal ▲ ┌────┴────┐ Dog Cat ▲ Bulldog
UML 클래스 계층 구조는 사용자 입장에서 쉽게 이해하기 위한 표현일 뿐
실제 메모리에선 각각의 하위 클래스에 대해서 부모 클래스 인스턴스가 생성된다.
(자식 인스턴스들이 메모리에서 부모 인스턴스를 공유하는 것이 아님)
클래스 계층도를 그릴 수 있는 도구들:
StarUML (많이 쓰는 설계 툴)
PlantUML (코드로 UML 그리기)
Lucidchart, draw.io (웹 기반 다이어그램 도구)
super
자식 클래스에서 부모 클래스 인스턴스를 가리킬 때 사용하는 키워드
명시적으로 호출하지 않으면 컴파일 시 컴파일러가 자동으로 super() 추가
반드시 생성자의 첫 줄에 작성해야 한다.
this
자기 자신의 인스턴스를 가리킬 때 사용
자신의 다른 생성자 호출(생성자 첫 줄에서)
자신의 필드 호출
오버라이딩 메서드
메서드의 이름만 빌려온 후, 매서드의 구현은 자식 클래스에서 재정의해서 사용
매서드의 이름, 리턴 타입, 매개변수(순서 개수, 타입)이 동일해야 한다.
static, final, private가 지정된 메서드는 오버라이딩 할 수 없다.
@Override어노테이션(annotation)은 생략 가능하지만 가독성을 위해 적는다.
오버라이딩 메서드의 접근 제한자
일반적으로 부모 메서드의 접근 제한자를 그대로 사용
단, 변경하는 경우 부모 메서드의 접근 제한자보다 더 넓은 접근을 허용하는 제한자만 가능
예시 : 도형의 넓이를 구하는 오버라이딩 메서드
부모 클래스 Shape
Rect
Tri ← override 사용 (사용하지 않으면 실행 클래스가 복잡해진다)
실행 클래스
접근 제한자
아무것도 지정하지 않으면 default 제한자로 지정
정의 : 클래스와 클래스의 멤버(필드, 메서드, 생성자)에 접근하는 범위를 제한하는 용도로 사용하는 지정자
public : 모든 외부 클래스에서 접근 가능
protected : 같은 패키지에 있는 클래스와 상속 관계의 클래스들만 접근 가능
default : 같은 패키지에 있는 클래스만 접근 가능
private : 같은 클래스 내에서만 접근 가능
캡슐화
객체 지향 개념 중에 하나
정의 : 접근 지정자를 사용하여 클래스의 필드나 메서드에 접근하는 방법을 일정하게 함으로써프로그램이 일관성있게 동작 및 관리되도록 한다.
(사용자가 임의로 조작하는 것을 배제)
예:
캡슐화를 사용하지 않을때
→ 이렇게 되면 + 자리에 실수로 *를 넣을 수도 있고 코드 자체가 복잡해진다.
하지만 캡슐화를 사용하면
Data의 x, y값을 private으로 만들어서 메서드로 간접접근할 수 있도록 한다.
package sec04.ex02;
public class DateTest {
public static void main(String[] args) {
Data d = new Data(5,5);
d.setValue(); //메서드를 통해 value에 값을 저장
int value = d.getValue(); //메서드를 통해 값을 가지고 온다
System.out.println("value = " + value);
}
}
즉, 사용자는 내부의 세부적인 사항은 모르더라도 정해진 규칙대로만 실행문을 작성하면 원하는 기능을 얻을 수 있게 만든다.
이렇게 클래스의 필드에 접근할 땐 메서드를 통해 간접접근하는 것이 일반적.
이때 사용되는 메서드를 getter/setter 메서드
getter & setter
+)
charAt(i)
문자열에서 i번째 문자 하나 반환
indexOf("a")
"a"라는 문자의 위치 반환
substring(a,b)
a부터 b 전까지 문자열 잘라내기
get, set 메서드 활용해서 주민번호 “*” 처리해서 불러오기 예제
실행문
자동차를 이용한 상속 실습
Car 클래스
package sec05.ex01;
public class Car {
protected String carName;
protected String carNumber;
protected String carColor;
protected int velocity;
protected int carSize;
public Car() {
System.out.println("Car 클래스 생성자 호출");
}
public void speedUp(int speed) {
velocity += speed;
}
public void speedDown(int speed) {
velocity -= speed;
}
public void stop() {
velocity = 0;
}
//차 정보를 출력하는 메서드
public String getCarInfo() {
return "차 이름: " + carName +
", 차 번호: " + carNumber +
", 차 색상: " + carColor +
", 속도: " + velocity + "km/h" +
", 차 크기: " + carSize + "cc";
}
}
Sedan 클래스
package sec05.ex01;
public class Sedan extends Car{
public Sedan(String carName, String carNumber, String carColor, int velocity, int carSize) {
System.out.println("Sedan 클래스 생성자 호출");
super.carName = carName;
super.carNumber = carNumber;
super.carColor = carColor;
super.velocity = velocity;
super.carSize = carSize;
}
}
Truck 클래스
package sec05.ex01;
public class Truck extends Car{
private int load; //적재화물량
public Truck(String carName, String carNumber, String carColor, int carSize, int velocity, int load) {
System.out.println("Truck 클래스 생성자 호출");
super.carName = carName;
super.carNumber = carNumber;
super.carColor = carColor;
super.velocity = velocity;
super.carSize = carSize;
this.load = load;
}
public int getLoad() {
return load;
}
public void setLoad(int load) {
this.load = load;
}
//화물을 운반하는 메서드
public void carryLoad() {
if (load >0) {
load ++;
} else {
load = 0;
}
}
@Override
public String getCarInfo() {
return "차 이름: " + carName +
", 차 번호: " + carNumber +
", 차 색상: " + carColor +
", 속도: " + velocity + "km/h" +
", 차 크기: " + carSize + "cc" +
", 적재량: " + getLoad() + "톤";
}
}
Bus 클래스
package sec05.ex01;
public class Bus extends Car {
private int passenger; //승객 수
private int fee;
public Bus(String carName, String carNumber, String carColor,
int carSize, int velocity, int passenger, int fee) {
System.out.println("Bus 클래스 생성자 호출");
super.carName = carName;
super.carNumber = carNumber;
super.carColor = carColor;
super.velocity = velocity;
super.carSize = carSize;
this.passenger = passenger;
this.fee = passenger * fee;
}
public int getPassenger() {
return passenger;
}
public void setPassenger(int passenger) {
this.passenger = passenger;
}
public int getFee() {
return fee;
}
public void setFee(int fee) {
this.fee = fee;
}
//승객을 태우는 메서드
public void ridePassenger() {
if(passenger > 0) {
passenger++;
fee += 1000;
}
}
public void ridePassenger(int newPassenger) {
if(passenger > 0) {
passenger+= newPassenger;
fee += newPassenger * 1000;
}
}
@Override
public String getCarInfo() {
return "차 이름: " + carName +
", 차 번호: " + carNumber +
", 차 색상: " + carColor +
", 속도: " + velocity + "km/h" +
", 차 크기: " + carSize + "cc"+
", 승객 수: " + getPassenger() +"명, 승차 요금: " + getFee() + "원";
}
}
실행 클래스
package sec05.ex01;
public class CarTest {
public static void main(String[] args) {
//1. 변수 선언
Sedan myCar = null;
Truck truck = null;
Bus bus = null;
//2. 인스턴스 생성
myCar = new Sedan("아반떼", "111가 1111", "은색", 60, 2000);
truck = new Truck("1톤 포터", "222나 2222", "청색", 3000, 60, 1);
bus = new Bus("1001번 버스", "333다 3333", "노란색", 4000, 60, 20, 1000);
//3.메서드 호출
System.out.println(myCar.getCarInfo());
System.out.println(truck.getCarInfo() + ", 적재량: " + truck.getLoad() + "톤");
System.out.println();
truck.speedUp(10);
truck.carryLoad();
System.out.println("트럭과 버스 정보를 출력");
System.out.println(truck.getCarInfo());
System.out.println(bus.getCarInfo() );
//승객 여러명 탑승 후 버스 정보
System.out.println("\n승객 여러명 탑승 : 메서드 오버로딩 이용");
bus.ridePassenger(10);
System.out.println(bus.getCarInfo());
}
}