멀티캠퍼스 - [현대이지웰] 자바 풀스택 개발자 과정 6회차를 수료하며

 

처음 멀티캠퍼스 자바 풀스택 개발자 과정을 신청했을 때는 과연 끝까지 완수할 수 있을까 걱정이 있었지만,

생각보다 6개월은 금방 흘러갔고,

지금은 적어도 개발자가 어떤 일을 하는 사람인지, 그리고 앞으로 내가 어떤 방향으로 성장해 나가야 할지에 대해서는 분명한 답을 가지게 되었다

 

기초적인 html, css로 시작해서 Java, Spring boot, Oracle DB, React 등을 배웠다.

단순히 문법을 가르치는 수업이 아니라, 실습위주로 진행되었다.

작은 이론 설명이 끝나면 기억이 휘발되지 않도록 개인과제가 주어져 수업내용을 복습할 수 있었다.  

아는 내용이 하나도 없는 비전공자가 듣는 수업치고는 생각보다 많은 내용을 한꺼번에 학습하기 때문에,

내가 이 내용을 정말 아는게 맞는지 확인하는 단계가 꼭 필수적이라고 생각한다.

아주 방금 들은 내용이라고 하더라도 직접 써보지 않으면 며칠 뒤, 몇 주 뒤에는 정말 생각이 나지 않는다.

위 사진은 내 필기 노트의 리스트이다. 그날그날 배운 것을 복습했고 이런식으로 정리하는 습관을 들이니 나중에 다시 찾아볼 때 꽤 유용했다.

 

팀프로젝트

"접근성 중심의 중개형 쇼핑몰" 이라는 주제를 잡고 5명의 팀프로젝트를 수행하였다.

다들 열심히 하는 좋은 팀원들을 만나서 생각보다 재밌게 개발을 할 수 있었다.

어느 팀에 들어가더라도 이렇게 프로젝트의 처음부터 끝까지 만들 수 있는 기회는 잘 없을 것 같다.

새로운 개발 경험으로서 정말 소중한 시간이었고 무언가를 담당하여 개발한다는 것에 두려움을 갖지 않게 되었다.

와이어프레임

내가 담당한 것은 db 스키마 초기 설계, 와이어프레임 설계, 회원 / 접근성 기능 설계 정도가 있다.

정규 개발 시간 외에도 시간을 내서 개발을 하곤 했는데 1등 팀이라는 결과가 나와서 너무 뿌듯했다.

접근성 쪽으로는 기존의 웹 사이트를 보고 연구하기도 했고, WCAG(웹 콘텐츠 접근성 지침)을 참고하거나

'UI/UX 사용자를 생각하게 하지마!' 그리고, '웹 접근성 바이블'이라는 두 권의 책을 읽고 적용해보기도 했다.

접근성 설정엔 다음과 같은 세팅이 있다.

  • 스크린 리더
  • 기본대비
  • 스마트 대비
  • 링크 강조
  • 글자 크기
  • 자간간격
  • 커서 강조
  • 텍스트 정렬
  • 행 높이

 

접근성 설정 기능은

1. local Storage를 활용해 비로그인 사용자가 창을 껐다 켜도 편리하게 적용될 수 있도록 화면 하단에 플로팅 된 버튼을 클릭하면 언제든지 조정할 수 있게끔 하였고,

2. 추가로 로그인한 회원에 한해서 접근성 프로필을 따로 세팅하여 db에 저장하여 저장하기/불러오기 기능을 쓸 수 있도록 하였다.

 

우리 팀의 차별화 전략은 '기능 개발은 기본으로 깔고, 어떤 가치를 줄 수 있느냐'가 관건이었던 것 같다.

가장 중요한 핵심 두 가지를 꼽자면 '접근성 기능이 잘 기능하느냐', 그리고 '중개형 플랫폼인만큼 판매자의 신뢰도가 어떻게 잘 보장되느냐'였다.

 

사용한 기술 스택은 다음과 같다.

 

 

이 과정을 통해 코딩을 배웠다라는 말보다는 개발자가 어떤 생각으로 일하는지 알게 되었다는 표현이 더 잘 어울리는 것 같다.

또 좋았던 점은 기술 교육 말고도 실제 취업과 실무를 고려한 다양한 특강이 함께 제공되었다는 점이다. 

이력서와 포트폴리오 작성, 개발자 취업 트렌드, 기업의 개발 문화, msa(마이크로서비스 아키텍처) 등등 실무 중심 특강이 너무 만족스러웠다.

강사님과 매니저님도 굉장히 친절하게 수강생들을 케어해주셨고, 적응 기간 동안은 녹화 강의도 제공되어 복습이나 수업내용을 따라가는데 큰 걱정이 없었다.

모두의 도움 덕에 처음 개발을 시작하는 나도 안정적으로 수업에 적응할 수 있었고 무사히 수료할 수 있었다.

이제 막 첫 발을 뗀 단계지만, 이때의 마음을 잊지 않고 계속 배우고 성장해나가고 싶다.

 

 
에러(Error)란 자바 프로그램 실행 시 의도하지 않게 발생하는 문제점을 말한다.
크게 두 가지로 나뉘는데, → 에러(error)와 예외(exception)
  • 에러 : 실행 시 중요 기능 수행을 불가능하게 할 수 있는 큰 문제, 프로그래머가 해결할 수 없다.
  • 예외(버그) : 에러보다 상대적으로 작은 문제. 프로그래머가 해결할 수 있다.
 

예외 처리

예외 처리의 목적 : 예외 발생 시 비정상 종료되는 것을 막고 예외와 관련 없는 기능은 정상 작동하도록 처리하는 작업
빠르게 디버깅할 수도 있다.

 

예외 처리 방법
  • 프로그램 실행 시 발생하는 예외들을 각각의 Exception 클래스로 제공한다.
  • 해당 예외가 발생하면 JVM이 해당 예외에 대한 클래스 객체를 만들어서 프로그램으로 전달
  • 프로그래머는 해당 예외 객체를 받아서 예외 처리
 

예외 클래스들의  계층 구조

 

 
  • Exception 이 예외 클래스의 최상위 클래스
 
 

Exception 클래스 객체 생성 시

생성자
  • Exception() : 예외 메세지 없이 객체를 생성
  • Exception(String message) : 예외 생성시 예외 메시지를 예외 객체에 전달
  • Exception(String message, Throwable casue) : 예외 생성시 메시지와 원인을 예외 객체에 전달
 
메서드
  • public String getMessage() : 예외 발생 시 전달된 메시지를 리턴
  • public Throwable getCasue() : 예외 발생 시 전달된 예외 원인을 리턴
  • public void printStackTrace() : 예외 발생 시 예외 발생 이력(backgrace)을 출력, 주로 개발 시 디버깅용으로 사용 
 

자바 예외 종류

컴파일러 검사 유무에 따른 예외 종류
1. 일반 예외(checked Exception)
: 코드 작성 시 반드시 예외 처리해주어야 한다.
  •  ClassNotFoundException : 객체 생성 시 해당 클래스가 존재하지 않으면 발생
  • InterruptedException : 스레드 실행 중 발생하는 예외
  • IOException : 자바 입출력 작업 도중 발생하는 예외
  • FileNotFoundException : 입출력 작업 시 해당 파일이 존재하지 않을 때 발생하는 예외
  • SQLException  : 데이터베이스 연동 작업 시 발생하는 예외

 

2. 실행 예외(Unchecked/RuntimeException)

: 예외 처리 생략 가능(JVM이 자동으로),

 java.lang.RuntimeException 클래스를 상속 받는 예외 클래스
  • ArrayIndexOutOfBoundsException : 배열 선언 시 생성된 요소보다 큰 요소에 접근 시 발생
  • ArithmeticException : 수치 타입 데이터를 0으로 나눌 때 발생하는 예외
  • NullPointerException : 변경하려는 문자열이 수치형 데이터 형태가 아닐 때 발생하는 예외
  • ClassCastException : 인스턴스와 다른 종류의 클래스로 캐스팅 시 발생하는 예외
 

자바 예외 처리 방법

try-catch 문으로 예외 처리
try {
 //예외 발생 코드 (1) → JVM (2)
} catch(예외클래스 변수) { ← (3)
//예외 처리 코드(4)
}
//예외와 관련없는 코드(5)
  1. 예외가 발생하는 JVM에게 예외를 던진다.
  2. JVM은 발생한 예외를 분석한 후, 해당하는 예외 객체(인스턴스)를 생성
  3. JVM은 생성한 예외 객체를 예외가 발생한 곳으로 돌려준다.
  4. JVM이 던진 예외를 catch문에서 받아서 예외를 처리
  5. 예외를 처리한 후, 다른 코드는 정상 실행된다.
 

다중catch문

한 번에 여러 예외 처리
try {
 …
 //실행문1
 //실행문2
 //실행문3
 …
} catch(FileNotFoundException e) {
 //예외 처리1
} catch(IOException e) {
 //예외 처리2
} catch(Exception e) {
 //예외 처리3
}
다중 catch문은 순서대로 예외를 검사하므로 상위 예외 클래스 매개변수 타입에 업캐스팅이 적용될 수 있다.
주의! 상위 클래스 타입매개변수를 가지는 catch문을 뒤에 위치시켜야 한다. 
 

멀티 catch 기능 

JDK7부터 하나의 catch 블록에서 여러 개의 예외를 동시에 처리할 수있는 멀티(multi) catch 기능이 추가됨 
“|” 연산자로 여러 개의 예외 클래스를 나열 
 

finally 문

예외가 발생하든, 하지 않든 반드시 실행해야하는 기능에 사용
주로 외부 장치 연동 후 마무리 작업 시 주로 사용
try {

//실행문1
//실행문2
//실행문3

} catch(FileNotFoundException e) {
 //예외 처리1
} catch(IOException e) {
 //예외 처리2
} catch(Exception e) {
 //예외 처리3
} finally {
 //실행 코드
}
 

자동 리소스 닫기

자바에서 외부 장치 연동 시 반드시 예외 처리를 해주어야 한다. 연동이 끝나면 관련된 자원을 해제해야 한다.
이때 자동 리소스 닫기 기능(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)

클래스 설명
FileReaderFileWriter 파일 읽기/쓰기
BufferedReaderBufferedWriter 버퍼로 읽기/쓰기
InputStreamOutputStream 바이트 기반 입출력
PrintWriter 텍스트 출력 스트림

 

파일 채널/버퍼 (java.nio)

클래스 설명
FileChannel 파일 채널
Scanner 입력 스트림 스캐너

 

네트워크/DB 관련

클래스
설명
SocketServerSocket 네트워크 소켓
Connection (JDBC) DB 연결 객체
PreparedStatementResultSet 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("프로그램 종료");
	}

}
 

 

java.util 패키지의 클래스들 

  • StringTokenizer : 문자열을 구분자로 분리하는데 사용되는 클래스
  • Calendar : 운영체제의 날짜와 시간 관련된 기능을 제공하는 클래스
  • Date : 날짜와 시간 관련딘 기능을 제공하는 클래스

 

1 StringTokenizer

String 클래스에서 사용한 spli() 메서드와 동일한 기능
 
메서드
  • 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}
 

String 클래스

자바에서 가장 많이 사용되는 데이터는 문자열
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() 메서드에 예외 처리를 해주어야 한다.
 
 
실행결과
sec01.ex07.Student
Student
sec01.ex07

sec01.ex07.Student
Student
sec01.ex07

sec01.ex07.Student
Student
sec01.ex07
 
+) Class 클래스는 자바 리플렉션에서 많이 사용된다.
+) Class 객체는 해당 클래스의 경로 정보를 가지고 있기 때문에 다른 리소스 파일(xml, 이미지, 설정 파일 등)의 경로를 쉽게 얻을 수 있다.
→ 이런 기능은 웹 애플리케이션에서 데이터베이스 연동 시 설정 파일에서 연결 정보를 읽어오거나 안드로이드의 화면에서 이미지 파일에 접근하는 경우 많이 사용된다.
 
 

필수 사용 클래스

자바에선 프로그래밍 시 자주 사용되는 기능을 API 클래스로 만들어서 제공한다.
  • Object 클래스
  • Wrapper 클래스
  • Math 클래스
  • String 클래스
  • StringBuffer 클래스 & StringBuilder 클래스
  • System 클래스
  • Class 클래스
  • java.util 패키지의 클래스
  • 참조 타입 배열과 객체를 리턴하는 메서드
  • 열거 타입
  • has-a 관계 클래스
 

Object 클래스

정의 : 모든 클래스의 최상위 클래스(root class)
  • java.lang 패키지에 위치
  • 명시적으로 구현하지 않으면 컴파일 시 클래스가 자동으로 상속받는다.
  • Object 클래스의 멤버는 모든 클래스에서 사용 가능
 

Object에서 제공하는 메서드들

  • protected Object clone() : 객체 자신을 복사한 후에 리턴
  • boolean equals(Object obj) : 객체들의 실제 값을 비교
  • public String toString() : 객체를 문자열로 표현 (기본은 클래스명@해시코드)
  • protected void finalize() : 객체 자신에 대해 더 이상 참조할 경우가 없을 때 garbage colelctor에 의해 호출된다.
  • Class<?>getClass() : 객체 자신의 클래스명을 얻는다.
  • int hashCode() : 객체 자신의 해시 코드를 얻는다.
  • void notify() : 대기 상태의 스레드에게 실행 준비를 알린다.
  • void wait() : 다른 스레드가 notify()나 notifyAll()를 호출할 때까지 현재 실행하는 스레드를 대기 상태에 머물게 한다.
 
참고로
java.lang 패키지 핵심 클래스들
  • Object(모든 클래스의 조상)
  • String/StringBuilder/StringBuffer (문자열 처리)
  • Math, System (유틸리티)
  • Wrapper (Integer, Double 등)
  • Throwable/Exception/Error (예외 처리)
  • Thread/Runnable (멀티스레딩)
  • 기타 : Runtime, Enum, Class
 

equals() 메서드 

  • 기본 타입 데이터 값 비교 : ==
  • 참조 타입 데이터 값 비교  : equals() (==는 위치값이 같은지 비교)
 
  1. Object 클래스의 equals() 메서드는 ‘==’으로 구현되어 있다.
  2. 참조 데이터 비교 시 위치값이 비교된다.
  3. 실제 값을 비교하려면 메서드를 오버라이딩 해야한다.
  4. 대부분의 API 클래스는 미리 equals() 메서드를 오버라이딩해서 제공한다.
 
예를 들어 Student 클래스에서 name과 grade를 동시에 비교하는 equals()메서드를 오버라이딩하고 싶다
→ 
public class Student {
	String name;
	int grade;

	// ...
	@Override
	public boolean equals(Object obj) {
		boolean result = false;
		if (obj instanceof Student) {
			Student s = (Student)obj;
			if(name.equals(s.name)&& (grade == s.grade)) {
				result = true;
			}
		}
	}

}
  1. equals 메서드를 오버라이딩하며 Object obj 객체를 받는다.
  2. result 값는 false 디폴트
  3. obj가 Student의 인스턴스 일때 obj 다운캐스팅
  4. 이름 비교(equals), 점수 비교(==) 둘 다 맞으면 true
 
 

toString() 메서드

예를 들어 객체의 필드값을 한꺼번에 출력해야할 경우 toString() 사용
 
  • Object 클래스에서 제공
  • 인스턴스의 필드 정보를 문자열로 출력
  • 참조 타입 변수를 println() 메서드에 전달하면 자동으로 toString() 메서드가 호출된다.
 
근데 Object 클래스의 toString() 메서드의 실제 기능은 클래스 객체의 클래스명과 객체의 해시코드를 출력하는 기능이다
→ 본 형태는 “클래스명@해시코드”
즉 형식만 선언되어 있을 뿐 필드값을 출력하진 않는다.
상황에 맞게 오버라이딩하여 사용한다.
 
+) 해시코드(hashCode) : 클래스 객체 생성 시, JVM이 클래스 객체에 부여하는 고유 번호
 
예를 들어, toString() 메서드를 오버라이딩하지 않고 사용했을 때

 

 
출력값 : 클래스명과 해시코드가 출력된다.
sec01.ex04.Student@6576fe71
sec01.ex04.Student@6576fe71
 

오버라이딩 했을 때
+
@Override
	public String toString() {
		String info = "이름: " + name + ", 학년: " + grade;
		return info;
	}
→ 
출력값
이름: 이순신, 학년: 24
이름: 이순신, 학년: 24
 

 

Clone() 메서드 

Object 클래스의 clone() 메서드는 실행 중 생성된 클래스 객체를 따로 복사(깊은 복사)하는 기능을 제공
 
기본적으로 clone()을 그냥 이용하면 얕은 복사 (Shallow copy)가 된다.

 

+) 예외로 Array의 경우 clone()메서드를 바로 사용해도 deep copy가 된다.
+) Arrays.copyOf(), copyOfRange() 메서드도 deep copy
 
깊은 복사를 위한 3가지 조건
  1. Cloneable 인터페이스를 implements하기
  2. clone() 메서드를 override 하기
  3. try-catch 구문을 이용해 CloneNotSupportedException 던지기
 
예제
 
출력값
이름: 이순신, 학년: 3
이름: 이순신, 학년: 3

이름: 이순신, 학년: 2
이름: 이순신, 학년: 3
값 변경을 공유하지 않는다 → 깊은 복사가 된다.
 
  1. Student 클래스는 Cloneable 인터페이스 구현
    : “ 이 클래스는 clone()을 사용할거다”라는 신호를 컴파일러에게 준다.
    만약 구현하지 않으면  CloneNotSupportedException이 발생
  2. Cloneable을 구현하는 것만으로는 예외가 없어지지 않는다. 호출하는 쪽에서 반드시 예외 처리(try-catch 또는 throws)
 

Wrapper 클래스

기본 타입 데이터 별로 편리한 기능을 제공하는 클래스
+) 자바에선 8가지 기본 타입 데이터 제공됨.
 
기본 타입 데이터 래퍼 클래스
boolean Boolean
byte Byte
char Character
short Short
int Integer
long Long
float Float
double Double
 
 

Integer 클래스

Integer 래퍼 클래스
실행 결과
 
+)그 밖의 Integer 클래스의 여러 메소드들
  • int intValue() : Integer 객체의 갑슬 정수값으로 리턴
  • long longValue() : Integer 객체의 값을 long 데이터로 변환하여 리턴
  • static int max(int a, int b) : 두 정수 중 큰 값을 리턴
  • static int min(int a, int b) : 두 정수 중 작은 값을 리턴
  • static int parseInt(String s) : 문자열을 정수로 변환한 후 리턴
  • static String toBinary(int a)  : 정수를 2진수로 변환한 후 문자열로 리턴
  • static String toHexString(int a) : 정수를 16진수로 변환한 후 문자열로 리턴
  • static String toOctalString(int a) : 정수를 8진수로 변환한 후 문자열로 리턴
 

Float 클래스도 마찬가지

Float.MAX_VALUE
Float.parseFloat(…)
Float.toString(…)
 

Math 클래스

java.lang.Math 클래스 : 정적 메서드로 객체 생성 없이 바로 사용
 
상수
  • static final double E : 자연 지수 e의 값
  • static final double PI : 원주율 파이값
메서드
  • static double abs(double a) : a의 double 타입 절대값을 리턴
  • static int abs(int a) : a의 int 타입 절대값을 리턴
  • static double ceil(double a) : a보다 큰 정수 중 가장 작은 정수를 double 타입 실수로 리턴
  • static double floor(double a) : a보다 작은 정수 중 가장 큰 정수를 double 타입 실수로 리턴
  • static double max(double a, double b) : 두 수 중 큰 값을 리턴
  • static double min(double a, double b) : 두 수 중 작은 값을 리턴
  • static double pow(double a, double b) : a의 b 제곱(power)의 값을 리턴
  • static double random() : 0.0에서 1.0 사이의 난수를 리턴
  • static long round(double a) : a의 소수점 아래의 수를 반올림한 후 long 타입 정수로 리턴
  • static double sqrt(double a) : a의 제곱근을 리턴

 

Math 클래스 예제
 
실행결과
 

Random 클래스 

지금까지는 Math 클래스의 random() 메소드를 사용해왔다.
자바는 따로 java.util 패키지에 있는 Random 클래스로 더 많은 난수 기능 제공
 
생성자
  • Random() : 디폴트 생성자. 현재 시간을 초깃값으로 하는 난수 발생기 객체를 생성
  • Random(long seed) : long 타입의 seed값을 매개변수로 받아 객체를 생성
메서드
  • boolean nextBoolean() : boolean 타입의 난수를 리턴
  • double nextDouble() : double 타입의 난수를 리턴
  • float nextFloat() : float 타입의 난수를 리턴
  • int nextInt() : int 타입의 난수를 리턴
  • long nextLong() : long 타입의 난수를 리턴
  • int nextInt(int bound) : 0과 한계값(bound) 사이(한계값은 미포함)의 난수를 리턴
 
Random 클래스 예제
 
실행 결과
755473468
2
5
0.2642843929746739
false
3

 

인터페이스

추상 클래스가 발전된 개념

 

인터페이스는 프로그램 설계 시 프로그램 전체 구조의 일관성과 통일성을 부여하는데 사용됨
 

 

정의 : interface 키워드로 선언된 추상 클래스
  • 인스턴스 생성 불가능
  • 멤버로는 상수와 추상 메서드만 허용
  • 업캐스팅 참조 변수로 사용할 수 있다
  • 하위 클래스에선 implements(구현) 관계이다
  • 하위 클래스에선 여러 개의 인터페이스를 구현할 수 있다(다중 인터페이스)
    → 자바의 단일 상속 기능을 보완
  • 인터페이스를 상속 받아 클래스 구현하면 인터페이스의 모든 추상메소드를 구현해야 한다.
 
용도 : 기능만을 하위 클래스에 강제하기 위해서 사용
 
인터페이스 내에서 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;
			//내부 클래스의 메서드에서 외부 클래스 멤버에 자유롭게 접근가능
		}
	}

}
 

내부 클래스 인스턴스 생성

외부클래스타입 참조변수1 = new 외부클래스 생성자();
외부클래스명.내부클래스명 참조변수2 = 참조변수1.new 내부클래스 생성자();
 
 

로컬 내부 클래스

외부 클래스의 메서드 구현 블록 내에서 정의된 클래스
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();
Outer.Inner inner = new Outer.Inner();
바깥 클래스 멤버 접근 바깥 클래스의 인스턴스 멤버에 접근 가능 바깥 클래스의 정적 멤버에만 접근 가능
구분내부 클래스 (non-static)정적 내부 클래스 (stati
  • 내부 클래스는 참조변수1(outer)로 생성
  • 정적 내부 클래스는 외부 클래스(Outter)로 생성
 

 


2.내부 인터페이스 

클래스의 멤버로 인터페이스 선언
static으로 지정할 수 있다.
주로 화면에서 이벤트를 처리하는 용도로 사용
형식:
class 외부클래스명{

 static interface 인터페이스명 {
 상수;
 추상메서드();
 …
 }

}
 
Button
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("수정하기");
	}

}
 
왜 여기서 ButtonEventListener가 아니라 Handler가 들어갈까?
button.addButtonEventListener(new ButtonEventHandler());
→ 인터페이스는 설계도일 뿐, 생성자를 만들 수 없다
→ 대신 인터페이스를 구현한 클래스의 생성자는 만들 수 있다.
 
근데 만약 재사용할 필요가 없는 간결한 코드라면 익명객체를 써서 interface 생성자를 만들 수 있다.

 

 
 

익명객체 

자바 화면에서 발생하는 이벤트 처리 시 많이 사용되는 익명(anonymous) 객체
 
화면에서 버튼 클릭시 발생하는 이벤트 처리 클래스 인스턴스는 처리가 끝난 후 메모리만 차지한다는 문제점
→ 재사용할 필요가 없는경우 익명객체를 쓰면 유용하다.
코드가 간결하고 재사용하지 않는 경우
 
클래스 이름이 없다.
인터페이스타입 참조변수 = new 인터페이스() {…(구현)};
  • 구현 클래스가 필요 없다
  • 익명 클래스는 클래스 정의 + 인스턴스 생성을 동시에 하는 표현=> 하나의 "문장(statement)" 이기 때문에, 끝에 ;(세미콜론)을 붙여야 문장이 완성된다.
ButtonEventListener
package sec05.ex01;

public interface ButtonEventListener {
	public void btnClicked(String btnName);

}
 
ButtonEventHandler
package sec05.ex01;

public class ButtonEventHandler {
	Button button = new Button();
	ButtonEventListener listener = new ButtonEventListener() {	//인터페이스를 익명 객체로 구현
	@Override
	public void btnClicked(String btnName) {
		System.out.println(btnName + " 버튼을 클릭합니다.");
	}
	};
}
원래 인터페이스는 생성자를 못만들지만 익명객체 즉, 동시에 정의+구현까지 하는 경우 메모리를 차지하지 않는 인터페이스 생성자를 만들 수 있다!
 
Button
package sec05.ex01;

public class Button {
	ButtonEventListener listener;
	public void addButtonEventListener(ButtonEventListener listener) {
		this.listener = listener;
	}
	
	public void btnClicked(String btnName) {
		listener.btnClicked(btnName);
	}
}
 
실행클래스
package sec05.ex01;

public class ButtonTest {
	public static void main(String[] args) {
		ButtonEventHandler handler = new ButtonEventHandler();
		
		handler.button.btnClicked("등록하기");
		handler.button.btnClicked("수정하기");
	}

}
 
 
+) 팁
익명 객체가 사용되는 메서드의 매개변수와 지역변수는 반드시 final로 지정해야 한다. final로 지정하지 않으면 컴파일 시 자동으로 지정됨.
→ 익명객체에서 이들 변수에 접근해서 값을 변경하면 에러 발생

 

 

다형성

큰 타입 변수에 작은 타입값을 저장하면 자동으로 큰 타입으로 변환되는 데,
비슷하게 상속 관계의 클래스들 사이에서도 일어나는 변환.
 
정의 : 클래스의 상속 구조에서 부모 클래스 타입 참조 변수가 자식 클래스의 인스턴스를 가리킬 수 있는 기능
  • instanceof 연산자가 함께 사용된다
 

업캐스팅 Upcasting

부모 클래스 타입 변수 = 자식 클래스 타입 변수(or 자식 클래스 인스턴스)
Student s = new College(”홍길동”, 2, 22) ← s가 자식 클래스 인스턴스를 받을 수 있다.
업캐스팅된 상태에서 메서드 호출 시 JVM은 항상 하위 클래스의 오버라이딩된 메서드를 먼저 호출

 

 
1) 참조 변수 s1을 이용해서 Student 인스턴스의 getStudInfo() 메서드를 호출
2) JVM은 다시 자식 클래스에 오버라이딩된 getStudInfo() 메서드가 있는지 확인
3) 있으면 자식 클래스의 getStudInfo() 메서드를 호출
 
→ s가 부모 타입이므로 메서드를 호출하면 부모 클래스의 메서드가 우선 호출될거라 생각하지만
자식 클래스에 오버라이딩된 것이 우선이다

 

다운캐스팅 Downcasting

업캐스팅된 상태에서 자식 클래스의 필드나 메서드에 접근하기 위해선 다시 자식 클래스 타입으로 변환해주어야 한다.
 
자식클래스타입변수 = (자식클래스)부모클래스타입 인스턴스
 

다형성을 이용한 학생 시험 점수 구하기

Person
 
Student
Middle
 
College
 
실행클래스

 

 

 

 


 

추상 클래스

 

abstract

정의  : ‘기능은 없고, 선언만 되어 있다’
  • 클래스와 메서드를 추상으로 사용
 
  • 구현 메서드 : 구현부 블록({})이 있으면 구현 메서드
  • 추상 메서드 : abstract 지정자에, 구현부 블록이 없으면 추상 메서드

 

추상 메서드는 단독으로 사용되는 일은 없고, 추상 클래스 내에서 사용된다.
 

추상 클래스

: 클래스 선언 시 abstract로 지정한 클래스
  • 더 이상 (new) 인스턴스 생성이 불가능
  • 추상 메서드가 없어도 추상 클래스로 선언 가능
  • 추상 메서드가 있으면 그 클래스는 반드시 추상클래스로 선언해야한다.
  • 일반적으로 부모 클래스가 추상 클래스로 선언되어 업캐스팅으로 자주 사용됨
 
용도
  • 자식 클래스에서 구현해야할 기능을 부모 클래스의 추상 메서드의 형식을 빌려와서 일관성 있게 구현
  • 프로그램 소스 코드 구조를 일정하게 유지할 수 있으므로 표준화 가능
 
프로그램은 개발자가 각 기능을 알아서 구현하기보다는 프로젝트의 규모가 점점 커질 것을 고려하여 유지보수나 확장성을 위해 누구나 쉽고 빠르게 소스 내용을 이해하고 관리할 수 있도록 소스의 구조를 일정하게 유지해야 한다.
 
 
강조!
추상 클래스는 직접 인스턴스 생성은 불가능하지만, 상속받는 자식 클래스의 인스턴스를 생성하면 정상적으로 추상 클래스의 인스턴스가 메모리에 생성된다.
 
예)
 
1. Student라는 부모 클래스에
public abstract getTeacher(); 선언

 

2. Middle이라는 자식 클래스에서 기능 구현

@Override
public String getTeacher() {
 return “담임 선생님:  “ + teacher;
}

 

3. College 자식 클래스에서 기능 구현

@Override
public String getTeacher() {
 return “지도 교수님:  “ + teacher;
}
 

4. 실행 클래스

  • Student 인스턴스 생성 불가능
  • Middle과 College의 오버라이딩한 getTeacher 메서드 호출
 

 

 

동적 로딩

: 특정 객체(인스턴스)가 컴파일 시에 실행 파일에 포함되지 않고, 프로그램 실행 중 JVM에 의해 동적으로 메모리에 적재되는 방법
 
용도: 작은 메모리 용량으로도 큰 프로그램을 실행시킬 수 있음.
 
JVM은 시작과 동시에 모든 클래스 인스턴스를 메모리에 생성하는 것이 아니라 해당 인스턴스를 생성하는 코드에서 동적으로 메모리에 생성
그리고 더 이상 사용되지 않으면 JVM에서 자동으로 삭제가비지 컬렉터(Garbage Collector)
 
동적 로딩의 반대 개념 : 정적 로딩 (대표언어: C)

 

C언어로 만든 프로그램을 실행하면 모든 실행 파일의 기능이 메모리에 로드되어 항상 존재

 

 → 빠름
반면, 동적로딩은 자주 하면 실행이 느려질 수 있다.
이 문제점을 개선한 static!
 

Static 정적 지정자

: 인스턴스 생성과 상관없이 프로그램 실행 시 메모리에 필드나 메서드를 생성
  • static으로 지정된 멤버들은 프로그램 실행 시 자동으로 메모리에 생성
  • 종료 시 메모리에서 소멸
  • 인스턴스 생성과 무관
 
지정 대상
  • 필드: 인스턴스 간의 데이터 공유 시 사용 (상수)
  • 메서드 : 인스턴스 생성 없이 메서드 사용
  • 클래스 : 내부 클래스로 사용 시
 
static을 사용하는 대표적인 경우가 main() 메서드
다른 클래스에서 static으로 지정된 멤버에 접근하려면 클래스명과 도트 연산자로 접근
클래스명.필드;
클래스명.메서드;
 
+) 정적 메서드에선 같은 정적 필드에는 접근할 수 있으나, 아직 메모리에 생성되지 않은 인스턴스 필드를 사용하면 에러 발생
 
정적 메서드로 대표적인 것이 자바 API에서 제공하는 Math 클래스의 메서드들
 

싱글톤 Singleton

프로그램에서 한 개의 객체만 존재해야 할 경우 싱글톤 패턴을 이용
작성 규칙
  • 인스턴스를 생성할 클래스의 생성자와 필드는 private으로 선언
  • 인스턴스에 접근할 메서드는 public으로 선언
  • 외부 클래스에서는 new를 통한 인스턴스 생성은 불가능
  • 생성된 인스턴스는 getInstance() 메서드를 통해 접근
 
Earth 예제
package sec02.ex03;

public class Earth {
	private static Earth ourEarth = null;	//싱글톤 객체의 필드는 private
	public static int EARTH_RADIUS = 6371;
	
	private Earth() {
		System.out.println("지구 객체 생성");
	}
	
	public static Earth getInstance() {	//getInstance() 메서드 호출 시 객체가 없으면 생성, 이미 존재하면 객체 위치 리턴
		if(ourEarth == null) {
			ourEarth = new Earth();
		}
		
		return ourEarth;
	}

}
 
실행 클래스
package sec02.ex03;

public class SingletonTest {
	public static void main(String[] args) {
		//Earth myEarth = new Earth(); 싱글톤 객체는 외부에서 생성할 수 없다
		System.out.println("첫 번째 getInstance() 호출");
		Earth myEarth = Earth.getInstance();
		
		System.out.println("두 번째 getInstance() 호출");
		Earth yourEarth = Earth.getInstance();
		
		if(myEarth == yourEarth) {	//두 변수의 위치값 비교
			System.out.println("두 지구 객체는 같은 객체입니다.");
		} else {
			System.out.println("두 지구 객체는 다른 객체입니다.");
		}
		
	System.out.println("지구 반지름: "+ Earth.EARTH_RADIUS + "km");
	}

}
 
 

final 지정자

: ‘금지’의 의미를 가지는 지정자
  • 클래스 : 상속을 금지
  • 메서드 : 오버라이딩 금지
  • 필드 : 값 변경을금지 (상수로 지정)
 
자바에서 사용되는 상수 형식
  • 상수는 모든 클래스 인스턴스에서 접근해야 하므로 public 으로 지정
  • 실행 시 메모리에 존재해야 하므로 static으로 지정
  • 값을 변경할 수 없으므로 final로 지정
public static final 타입 상수명 = 값;
  • 상수명은 대문자와 “_”를 이어서 만드는 것이 관례
 
예)
public static final int SAFE_SPEED = 60;
public static final String SERVER_IP_ADDR = “192.0.0.1”;
 
+) 참고로 자바에서 60, 3.14, “Hello”와 같이 구체적인 값은 리터럴(literal)이라고 한다.
 

클래스들 간에 데이터를 전달하는 클래스

VO(Value Object) 클래스
정의 : 
  • 여러 다른 타입의 데이터를 다른 클래스로 전달 시 사용
  • TO(Transfer Object) 또는 DTO(Data Transfer Object)라고도 한다.
형식
  • 필드를 private로 선언
  • 생성자를 구현
  • 각 필드에 대한 getter/setter를 구현
 
예) MemberVO 클래스
package member;

public class MemberVO {
	String id;
	String password;
	String name;
	String address;
	String phoneNum;
	


	public MemberVO (String id, String password, String name, String address, String phoneNum) {
		this.id = id;
		this.password = password;
		this.name = name;
		this.address = address;
		this.phoneNum = phoneNum;
	}
	
	//getter와 setter는 생략

}
 

'멀티캠퍼스-JAVA 풀스택 > JAVA' 카테고리의 다른 글

Java 기초(8)-필수 사용 클래스  (0) 2025.12.04
Java 기초(7) - 인터페이스  (0) 2025.11.25
Java 기초(5) - 객체 지향  (0) 2025.11.12
Java 기초(4) - 배열  (0) 2025.11.12
Java 기초(3)  (0) 2025.10.28

메서드

 : 자주 사용되는 기능은 미리 만들어 놓고 필요시 처리할 데이터만 전달해 주면 쉽게 원하는 결과를 얻게 해 주는 기능 제공
 
크게 선언부(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
x의 복사본 값 10이 전달되므로 num을 변경해도 x는 변하지 않는다!
 
 
 
참조 타입 매개변수 경우
public void changeArray(int[] arr) {
    arr[0] = 100;
}

//호출 시 
int[] nums = {1, 2, 3};
changeArray(nums);
System.out.println(nums[0]);  // 100
nums의 주소가 전달되므로 배열 값을 변경하면 원본도 바뀐다.
 
 
String은 왜 ‘예외적으로’ 기본 타입처럼 동작할까?
불변 객체(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 그리기)
  • Lucidchartdraw.io (웹 기반 다이어그램 도구)
 

 

super

자식 클래스에서 부모 클래스 인스턴스를 가리킬 때 사용하는 키워드
명시적으로 호출하지 않으면 컴파일 시 컴파일러가 자동으로 super() 추가
반드시 생성자의 첫 줄에 작성해야 한다.

 

 

this

자기 자신의 인스턴스를 가리킬 때 사용
  • 자신의 다른 생성자 호출(생성자 첫 줄에서)
  • 자신의 필드 호출
 

오버라이딩 메서드

메서드의 이름만 빌려온 후, 매서드의 구현은 자식 클래스에서 재정의해서 사용
  • 매서드의 이름, 리턴 타입, 매개변수(순서 개수, 타입)이 동일해야  한다.
  • static, final, private가 지정된 메서드는 오버라이딩 할 수 없다.
  • @Override 어노테이션(annotation)은 생략 가능하지만 가독성을 위해 적는다.
 
오버라이딩 메서드의 접근 제한자
  • 일반적으로 부모 메서드의 접근 제한자를 그대로 사용
  • 단, 변경하는 경우 부모 메서드의 접근 제한자보다 더 넓은 접근을 허용하는 제한자만 가능
 
 
 
 
예시 : 도형의 넓이를 구하는 오버라이딩 메서드
 
부모 클래스 Shape
 

 

Rect
 
Tri ← override 사용 (사용하지 않으면 실행 클래스가 복잡해진다)
 
실행 클래스
 
 

접근 제한자

아무것도 지정하지 않으면 default 제한자로 지정
정의 : 클래스와 클래스의 멤버(필드, 메서드, 생성자)에 접근하는 범위를 제한하는 용도로 사용하는 지정자
  • public : 모든 외부 클래스에서 접근 가능
  • protected : 같은 패키지에 있는 클래스와 상속 관계의 클래스들만 접근 가능
  • default : 같은 패키지에 있는 클래스만 접근 가능
  • private : 같은 클래스 내에서만 접근 가능
 

캡슐화

객체 지향 개념 중에 하나
정의 : 접근 지정자를 사용하여 클래스의 필드나 메서드에 접근하는 방법을 일정하게 함으로써프로그램이 일관성있게 동작 및 관리되도록 한다. 
(사용자가 임의로 조작하는 것을 배제)
 
예: 
  1. 캡슐화를 사용하지 않을때
 
→ 이렇게 되면 + 자리에 실수로 *를 넣을 수도 있고 코드 자체가 복잡해진다.
 
 
 
하지만 캡슐화를 사용하면
 
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());
	}

}
 
 
 

'멀티캠퍼스-JAVA 풀스택 > JAVA' 카테고리의 다른 글

Java 기초(7) - 인터페이스  (0) 2025.11.25
Java 기초(6)- 객체지향(2)  (0) 2025.11.18
Java 기초(4) - 배열  (0) 2025.11.12
Java 기초(3)  (0) 2025.10.28
JAVA 기초(2)  (1) 2025.10.23

배열

같은 타입의 데이터를 한꺼번에 다룰 수 있는 방법을 배열로 제공
배열은 크게
1차원 배열  (기본 타입 배열, 참조 타입 배열)
 다차원 배열 (기본 타입 배열, 참조 타입 배열)
로 나뉜다. 

 

 

1차원 배열 생성

int[] score = new int[5];
→ 배열 선언과 동시에 new 연산자로 배열 생성
 

 

[]는 데이터 타입 뒤, 변수명 뒤에 붙을 수 있다.
즉, int score[] = new int[5] 도 가능

  • 배열을 선언한다고 배열이 연속적으로 생성되지 않는다.
  • 배열의 첫 번째 요소의 인덱스는 1이 아니라 0
  • new 연산자 없이 선언과 동시에 값을 초기화할 수 있다.
int[] score = {50,60,70,80,90};
 
이렇게 초기화하지 않으면 데이터 타입에 따라 자동으로 값이 할당됨 (최초의 초기값)
  • byte, short, int, long, char : 0(0L)
  • float, double : 0.0(0.0F)
  • boolean : false
  • 참조타입 : null
 

1차원 배열 접근

score[0] = 77;
System.out.println(score[0]);
  • 배열 요소에는 선언한 데이터 타입과 동일한 데이터만 저장 가능 (score[0] = 77.8 X)
  • 생성된 요소의 크기의 범위 내에서만 접근 가능 
int[] score = {50,60,70,80,90};
System.out.println(score[5]); X (6번째 요소는 존재하지 않는다.) 

 

 

배열의 길이 length

배열명.length
int[]  score = (50,60,70,80,90};
System.out.println(score.length);
5개의 요소를 가지고 있으니 5가 출력된다.
(배열의 길이는 상수이므로 변경 시 에러)
 
배열의 요소의 총합을 구하는데 다음은 비효율적인 코드의 예다.
total = height[0] + height[1] + height[2] + height[3] + height[4] 
이를 다음과 같이 바꾼다.
(일반적으로 1차원 배열은 for문을 사용해서 다룬다.)
for(int i = 0; i < height.lenght; i++) {
    total += height[i];
}
 

 

 

배열을 역순으로 출력하는데 두가지 방법

1) 다른 배열을 이용해서 복붙 (temp[] 이용) 
 
 
 
2) 같은 배열 내에 인덱스의 값을 ‘교환’해서 저장 (temp 이용) 
 
 
 
ㄴ for문 이용하여 교환  : 5번만 반복하면 된다. 1→ 10,  2→ 9,  3→ 8,  4→  7,  6→ 5
 
temp = num[i]; 1~5값 저장(일시 저장)
num[i] = num[len - 1 - i];  6~10을 1~5부분에 저장
num[len -1 -i] = temp; 6~10에 일시 저장한 1~5를 저장
 
 
 
 
 
 
오름차순 정렬 예제
 
 
 
 

Math.random()을 이용하여 오름차순 정렬

 
 
Math 클래스의 random 메서드는 0에서 1 사이의 double 타입 실수를 리턴
0.0 <= Math.random() < 1.0
 
-> 1에서 100의 숫자를 랜덤으로 얻고 싶으면 먼저 모든 항에 100을 곱한다.
0.0 * 100 <= Math.random() * 100 < 1.0 * 100
 
->  소수점 이하를 제거하기 위해 int타입으로 변환
(int)0.0 <= (int)Math.random() * 100 < (int)(1.0 * 100)
 
->  1을 더해주면 최종적으로 1에서 100 사이의 값 리턴
0 + 1 <= (int)Math.random() * 100 + 1 < (int)(1.0 * 100) + 1
 
 
 
 
+) 시작 정수와 끝 정수를 다르게 하고 싶다면
int randInt = (int)(Math.random() * (end - begin + 1)) + begin;
→ 20에서 200의 임의의 정수
int randInt = (int)(Math.random() * (200 - 20 + 1)) + 20;
 
 
 
Math.random()을 이용하여 오름차순 정렬
 
 
 
 
 결과

 

다차원 배열

 

2차원 배열 선언

  • int[][] score;
  • int score[][];    score = int[2][3]     //2는 1차원 배열 요소 크기, 3은 2차원 배열 요소 크기
 
 
두 행렬의 합 구하기
 
 
결과
 
 
 
 
2차원 배열 초기화 예제
 
arr
2 2 2 2 
2 2 2 1
2 2 1 1
2 1 1 1
 
위와 같이 출력하려면?
 
1) 2의 개수가 행이 증가할수록 감소
2) 2를 가지는 인덱스 특징(합이 4를 넘지 않는다.)
 

 


배열 복사 예제

 

예상 결과

2 2 2 2
1 2 2 2
1 1 2 2
1 1 1 2
 
 i(열)가 0일 때 j(행)에 1이 없고
[1,0] / [2,0],[2,1] / [3,0],[3,1],[3,2] 에 1이 있다.
즉, i > j이면 1.
 
 
2차원 배열 두 대각선 합 구하기
arr[0][0] + arr[1][1] + arr[2][2] + … 우하향 대각선은 두 인덱스 값이 같다
arr[0][4] + arr[1][3] + arr[2][2] + … 우상향 대각선은 두 인덱스 합이 4 (=arr.length - 1)
*2차원 배열에 관련된 문제는 먼저 두 인덱스의 관계로부터 규칙성을 찾아야 한다.
 
 

배열 복사

 

System.arraycopy() 메서드

System.arraycopy(원본배열, 원본시작인덱스, 복사배열, 복사시작인덱스, 복사할개수);
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[5];

System.arraycopy(arr1, 1, arr2, 0, 3);
  • arr1을 원본 배열로
  • 인덱스 1부터 복사한다 (2,3,4,5)
  • arr2에
  • 인덱스 0부터
  • 3개만 (2,3,4)
결과
arr2 = {2, 3, 4, 0, 0}
 
이대로는 1차원 배열만 복사 가능
2차원으로는 for문을 이용해준다.
for (int i = 0; i < original.length; i++) {
    System.arraycopy(original[i], 0, copy[i], 0, original[i].length);
}
 
 
+) 얕은 복사와 깊은 복사
int[][] arr1 = {
    {1, 2},
    {3, 4}
};

int[][] arr2 = arr1; // 얕은 복사(참조)

arr2[0][0] = 99;
System.out.println(arr1[0][0]);  // 99 출력됨!
얕은 복사는 하나를 바꾸면 원본도 바뀜
 
int[][] arr1 = {
    {1, 2},
    {3, 4}
};

int[][] arr2 = new int[arr1.length][];
for (int i = 0; i < arr1.length; i++) {
    arr2[i] = new int[arr1[i].length]; //반복문 내에서 new 열 할당
    System.arraycopy(arr1[i], 0, arr2[i], 0, arr1[i].length);
}

arr2[0][0] = 99;
System.out.println(arr1[0][0]);
깊은 복사(Deep copy)는 값 자체를 완전히 새로운 공간에 복사
행마다 열의 개수가 다를 수도 있으니 깊은 복사 시에 반복문 안에서 행마다 열을 따로 할당

 

java.util.Arrays 클래스

  • equals() : 두 개의 1차원 배열 요소들의 값이 같은지 비교, 같으면 true
  • deepequals() : 두 2차원 이상 배열 요소들의 값이 같은지 비교, 같으면 true
  • toString() : 1차원 배열 요소들의 값들을 모두 출력
  • deepToString() : 2차원 이상 배열 요소들의 값들을 모두 출력
  • copyOf() : 배열 요소들의 값들을 복사
  • copyOfRange() : 지정한 범위의 배열 요소들의 값들을 복사
    단! 범위는 시작 인덱스부터 (마지막 인덱스 - 1)이다!
     copyOfRange(arr,0,5) → arr[0] ~ arr[4]를 출력
  • sort() : 배열 요소들의 값들을 오름차순으로 정렬
 
예시)
package sec06.ex01;

import java.util.Arrays;

public class ArraysMethodTest {
	public static void main(String[] args) {
		int[] arr1 = {10,20,30,40,50};
		int[] arr2 = {10,20,30,40,50};
		int[] num1 = {10,20,30,40,55};
		boolean same = true;
		
		same = Arrays.equals(arr1, arr2);//equals()
		if(same == true) {
			System.out.println("두 배열은 같습니다.");
		} else {
			System.out.println("두 배열은 다릅니다.");
		}
		
		same = Arrays.equals(arr1, num1);
		if(same == true) {
			System.out.println("두 배열은 같습니다.");
		} else {
			System.out.println("두 배열은 다릅니다.");
		}
		
		System.out.println();
		System.out.println(Arrays.toString(arr1));	//toString()
		
		//copyOf()
		int[] arr3 = Arrays.copyOf(arr1, arr1.length);	//10,20,30,40,50
		int[] arr4 = Arrays.copyOf(arr1, arr1.length -1);	//10,20,30,40
		
//copyOfRange() : **범위는 (시작 인덱스)에서 (마지막 인덱스-1) 까지
		int[] arr5 = Arrays.copyOfRange(arr1, 0, 3);	//10,20,30
		int[] arr6 = Arrays.copyOfRange(arr1, 3, arr1.length);	//40,50
		int[] arr7 = Arrays.copyOfRange(arr1, 2, 4); 
// 50은 출력되지 않는다.
		
	}
}

 

결과

두 배열은 같습니다.
두 배열은 다릅니다.

[10, 20, 30, 40, 50]

 

 

 
 

'멀티캠퍼스-JAVA 풀스택 > JAVA' 카테고리의 다른 글

Java 기초(6)- 객체지향(2)  (0) 2025.11.18
Java 기초(5) - 객체 지향  (0) 2025.11.12
Java 기초(3)  (0) 2025.10.28
JAVA 기초(2)  (1) 2025.10.23
Java 기초 (1)  (0) 2025.08.19

 

Java는 이클립스나 intellij 환경에서 실행하는 것이 좋다.

 

지난번 연산자에 대해 배운 것을 간단한 실습으로 복습

 

섭씨 화씨 온도 구하기

- BASE는 변하지 않는 값이므로 final로 선언

- float 값은 값에 .0F 를 붙여준다

- String temp로 문자열을 입력받아서

- Float.parseFloat으로 강제 형변환

 

두 수 중 큰 양수 출력

 

num1 : temp1을 String으로 입력받아서 Integer.parseInt()로 강제 형변환

num2도 마찬가지로 값을 받아서

result에서 삼항 연산자를 통해 num1이 크거나 같으면 num1을 반환하도록 한다.

 

자릿수 합 구하기

 

 

total에

%= : 나머지 저장

10으로 나누고 또 나머지 저장 반복

 


 

조건문

  • if문
  • if-else문
  • if-else if문
  • switch문
  • 중첩 조건문

 

크게 if문과 switch 문이 있다.

실습을 통해 살펴보자.

 

학점 변환하기

 

 

다만 if문을 쓸 때 조건이 많아지면 가독성이 떨어지는데 이때 switch 문을 사용한다.

 

형식

switch(값) {

case 값1 :

 실행문1;
 ...
break; //break은 case문을 구분하는 역할
 case 값2 :
 실행문2;
 ...
 break;
 case 값3 :
 실행문3;
 ...
 break;
default : //어느 case에도 해당되지 않으면 실행. 생략 가능
 실행문n;
}
 
case문에는 콤마, 로 구분해서 여러 값을 사용할 수 있다.

 

switch문의 값으로 입력 가능한 타입은 byte, short, int, char 타입 정수와 문자열 (실수형x)

 

 

+) 중첩 조건문: 조건문 안에 조건문이 있는 것을 말한다.

 

 

 

 


 

반복문

  • for
  • while
  • do-while
 
for (초기화식; 조건식; 증감식)
for (int i = 0; i < 100; i++) {
 System.out.println(”Hello, World!”);
}
 
while은 실행블록{} 에서 초기화식, 조건식, 증감식을 따로 사용하지 않는다.
→ for문은 반복 횟수가 정해진 경우 사용하면 편리하다
while문은 반복 횟수보다는 조건식이 true이면 반복하는 경우에 사용
 
int i = 1;
while(i <= 5) {
 System.out.println(”i = “ + i);
 i++;
}
System.out.println(”while문 밖에서 i의 값 : “ + i);
  }
}

[결과]
i = 1
i = 2
i = 3
i = 4
i = 5
while문 밖에서 i의 값 : 6
 
do while : 무조건 한 번은 실행문을 실행한 후 마지막에 조건식 판별
do {
 실행문;
} while(조건식)
 

중첩 반복문

가장 많이 쓰이는 for문 중첩
 
시계 예제 while문

 

중접반복문 : 내부의 반복문이 실행하기 전에  min을 초기화해주는 것 주의

min= 60이 되면 min=0으로 되돌아가야함

 

 

 

break문과 continue문

 

 

가독성을 위해 무한 루프로 while(true)를 많이 사용 
for(;;)로도 무한 반복 가능
무한 루프는 반드시 break으로 빠져나올 조건이 있어야한다.
break은 가까운 반복문을 종료시킨다.
 
 
break으로 외부 for문 종료시키는 예제

외부 for문에 라벨을 지정해서 특정 반복문을 break 할 수 있다.

 

 

 


continue문

반복문 내에서 실행문을 건너뛸 때 사용
for문에서 continue문을 만나면 for문의 증감식으로 이동
while문은 조건식으로 이동
반복문(...) {
 ...
 continue
 ...
}

 

 

i가 5의 배수 (나머지가 0) 일 때마다 print 함수를 건너뜀

 

 

1부터 자연수를 더해서 최초로 1000이 되게 하는 숫자와 합

 

 

 

 

최대 공약수 구하기

최대 공약수 구하기

 

num1, num2를 입력받아서

작은 수를 num1에, 큰 수를 num2에 저장

num1만큼 반복문 진행 : num1과 num2를 i로 나누어떨어지면 공약수로 출력

if문으로 가장 큰 공약수를 gcd(great common division)에 저장

 

 

자연수에서 100번째 소수 출력
package sec05.ex05;

public class PrimeNumberTest2 {
	public static void main(String[] args) {	//자연수에서 100번째 소수 출력
		
		int num = 1, count = 0, count2 = 0;
		
		Outter:
		while(true) {
			count = 0;	//매 숫자마다 count(약수개수)가 초기화되어야한다!!
			for (int i = 1; i <= num; i++) {
				if(num % i == 0) {
					count++;	//약수 개수
				}
				
			}
			
			if(count == 2) {
				count2++;
				}
			
			num++;
			
			if(count2 == 100) {
				System.out.println(num-1);
				break Outter;
				
			}
			
		}
	}

}
헤맸던 부분 :
while문 밖에서 한 번 count(약수 개수) 선언하고 그 뒤로 초기화하지 않으니 이상한 결과가 나온다.
반복할 때 매번 count는 0으로 초기화해주어야 한다.

 

'멀티캠퍼스-JAVA 풀스택 > JAVA' 카테고리의 다른 글

Java 기초(6)- 객체지향(2)  (0) 2025.11.18
Java 기초(5) - 객체 지향  (0) 2025.11.12
Java 기초(4) - 배열  (0) 2025.11.12
JAVA 기초(2)  (1) 2025.10.23
Java 기초 (1)  (0) 2025.08.19

+ Recent posts