1. 객체지향언어
- 코드의 재사용성이 높다.
- 코드의 관리가 용이하다.
- 신뢰성이 높은 프로그래밍을 가능하게 한다.
객체지향은 현실 세계를 객체 단위로 모델링하여 소프트웨어를 설계하는 방식 객체는 데이터(필드)와 동작(메서드)을 함께 포함하며, 주요 목적은 재사용성, 유지보수성, 확장성 향상이다.
2. 객체지향 프로그래밍(OOP)의 4가지 특징
| 추상화 | 공통된 속성과 기능을 추출하여 모델링 (예: 동물 → 소리내다()) |
| 캡슐화 | 데이터와 메서드를 하나로 묶고 외부에서 접근 제한 (private, getter/setter) |
| 상속 | 기존 클래스의 속성과 기능을 재사용 (코드 중복 최소화) |
| 다형성 | 같은 인터페이스로 다른 구현을 실행 (오버라이딩, 오버로딩) |
3. 객체지향 설계 원칙(SOLID) 5가지
| S: 단일 책임 원칙 (SRP) | 클래스는 하나의 책임만 가져야 한다. |
| O: 개방-폐쇄 원칙 (OCP) | 확장에는 열려 있고, 수정에는 닫혀 있어야 한다. |
| L: 리스코프 치환 원칙 (LSP) | 자식 클래스는 부모 클래스를 대체할 수 있어야 한다. |
| I: 인터페이스 분리 원칙 (ISP) | 인터페이스는 클라이언트에 맞게 분리되어야 한다. |
| D: 의존 역전 원칙 (DIP) | 고수준 모듈은 저수준 모듈에 의존하면 안 되고, 추상에 의존해야 한다. |
4. 오버라이딩(Overriding)과 오버로딩(Overloading)의 차이점
| 정의 | 상속받은 메서드를 재정의 | 같은 이름의 메서드를 매개변수 다르게 여러 개 정의 |
| 조건 | 메서드명, 매개변수, 반환형 모두 동일 | 메서드명 같고, 매개변수 개수나 타입 다름 |
| 사용 목적 | 부모 클래스 기능을 자식 클래스에 맞게 수정 | 하나의 이름으로 다양한 방식의 호출 가능 |
| 키워드 필요 | @Override (선택) | 없음 |
2. 클래스와 객체
2.1 클래스와 객체의 정의와 용도
클래스란?
- 객체를 정의해 놓은 것(설계도)
클래스의 용도
- 객체를 생성하는데 사용
객체의 정의
- 실제로 존재하는 것. 사물 또는 개념
객체의 용도
- 객체가 가지고 있는 기능과 속성에 따라 다름
클래스의 구성 멤버
- 필드: 객체의 데이터가 저장되는 곳으로 생성자와 메소드 전체에서 사용되며 객체와 함께 존재한다.
- 생성자: new 연산자로 호출되는 부분으로 객체 생성 시 초기화를 담당한다.
- 메소드: 객체 간의 데이터를 전달하는 수단으로 호출한 곳으로부터 매개값을 받아 실행하고, 결과값을 리턴할 수 있다.
2.2 객체와 인스턴스
클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화라고 하며, 클래스로부터 만들어진 객체를 클래스의 인스턴스라고 한다.
2.3 인스턴스의 생성과 사용
클래스명 변수명 = new 클래스명();
new 연산자를 이용해서 객체를 생성하면 메모리 힙(heap) 영역에 객체가 생성되고
참조 타입인 클래스 변수에 객체의 번지가 저장된다.
3. 변수와 메서드
3.1 선언위치에 따른 변수의 종류

인스턴스변수는 인스턴스가 생성될 때마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만,
클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.
3.2 JVM 메모리 구조

1. 메서드 영역
프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다. 이 때, 그 클래스의 클래스변수(cv)도 이 영역에 함께 생성된다.
2. 힙
인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다. 즉, 인스턴스변수(iv)들이 생성되는 공간이다.
3. 호출스택
호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면, 호출스택에 호출된 메서드를 이한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워진다.
3.3 기본형 매개변수와 참조형 매개변수
기본형 매개변수
변수의 값을 읽기만 할 수 있다.
class Data {
int x;
}
public class Main {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x);
change(d.x);
System.out.println("After change(d.x)");
System.out.println("main() : x = " + d.x); // 원본에 아무런 영향을 미치지 못함
}
static void change(int x) { // 기본형 매개변수
x = 1000; // 매개변수 x의 값이 변경된 것
System.out.println("change() : x = " + x);
}
}
참조형 매개변수
변수의 값을 읽고 변경할 수 있다.
class Data {
int x;
}
public class Main {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x);
change(d);
System.out.println("After change(d.x)");
System.out.println("main() : x = " + d.x);
}
static void change(Data d) { // 참조형 매개변수, 값이 저장된 주소를 넘겨줌
d.x = 1000;
System.out.println("change() : x = " + d.x);
}
}
4. 오버로딩
4.1 오버로딩
- 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것
4.2 오버로딩 조건
- 메서드 이름이 같아야 한다.
- 매개변수의 개수 또는 타입이 달라야 한다.
- 반환 타입은 상관없다.
5. 생성자
5.1 생성자
생성자는 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드이다.
따라서 인스턴스변수의 초기화 작업에 주로 사용되며, 인스턴스 생성 시에 실행되어야 할 작업을 위해서도 사용된다.
[참고] 인스턴스 초기화란, 인스턴스변수들을 초기화하는 것을 뜻한다.
생성자 역시 메서드처럼 클래스 내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 다르다.
그렇다고 해서 생성자 앞에 리턴값이 없음을 뜻하는 void를 사용하지는 않고, 단지 아무 것도 적지 않는다.
1. 생성자의 이름은 클래스의 이름과 같아야한다.
2. 생성자는 리턴 값이 없다.
[참고] 생성자도 메서드이기 때문에 리턴값이 없다는 의미의 void를 붙여야 하지만, 모든
생성자가 리턴값이 없으므로 void를 생략할 수 있게 한 것이다.
- 생성자 정의 방법
class ClassName {
ClassName(Type variableName, Type variableName, ...) {
// 인스턴스 생성 시 수행될 코드,
// 주로 인스턴스변수의 초기화 코드를 적는다.
}
}
- 예시
class Person {
private String name;
private int age;
Person() {
// 매개변수가 없는 기본 생성자
}
Person(String name, int age) { // 매개변수가 있는 생성자
this.name = name;
this.age = age;
}
}
- 매개변수가 없는 생성자: 인자를 전달받지 않으며 초기화 작업을 수행하지 않는다. 주로 필드의 초기값을 지정하지 않고 객체를 생성할 때 사용된다.
Person person1 = new Person();
1. 연산자 new에 의해서 메모리(heap)에 Person클래스의 인스턴스가 생성된다.
2. 생성자 Person()이 호출되어 수행된다.
3. 연산자 new의 결과로, 생성된 Person인스턴스의 주소가 반환되어 참조변수 person1에 저장된다.
- 매개변수가 있는 생성자: 전달받은 인자를 사용하여 객체의 멤버 변수를 초기화한다.
Person person2 = new Person("John", 25);
1. 연산자 new에 의해서 메모리(heap)에 Person클래스의 인스턴스가 생성된다.
2. 생성자 Person(String name, int age)이 호출되어 수행된다. 이 생성자는 "John"과 25라는 인자를 받아서 객체의 name과 age필드를 초기화한다.
3. 연산자 new의 결과로, 생성된 Person인스턴스의 주소가 반환되어 참조변수 person2에 저장된다.
[참고] 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
생성자는 단순히 인스턴스변수들의 초기화에 사용되는 조금 특별한 메서드일 뿐이다.
5.2 기본 생성자(default constructor)
컴파일 할 때, 소스파일(*.java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 아래와 같은 내용의 기본 생성자를 추가하여 컴파일 한다.
className() { }
Person() { }
[주의] 기본 생성자가 컴파일러에 의해서 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이다.
5.3 매개변수가 있는 생성자
생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있다. 인스턴스마다 각기 다른 값으로 초기화되어야 하는 경우가 많기 때문에 매개변수를 사용한 초기화는 매우 유용하다.
class Person {
private String name; // 이름
private int age; // 나이
Person() { } // 생성자
Person(String name, int age) { // 생성자
this.name = name;
this.age = age;
}
}
Person인스턴스를 생성할 때, 생성자 Person()을 사용한다면 인스턴스를 생성한 다음에 인스턴스변수들을 따로 초기화해주어야 하지만, 매개변수가 있는 생성자 Person(String name, int age)을 사용한다면 인스턴스를 생성하는 동시에 원하는 값으로 초기화를 할 수 있게 된다.
5.4 생성자에서 다른 생성자 호출하기 - this(), this
생성자 간에도 서로 호출이 가능하지만, 다음 두 가지 조건을 만족시켜야 한다.
1. 생성자의 이름으로 클래스 이름대신 this를 사용한다.
2. 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
Person(String name) {
age = 25; // 첫 번째 줄
Person(name, 25); // 에러1. 생성자의 두 번째 줄에서 다른 생성자 호출
} // 에러 2. this(name, 25);로 해야함
- 첫 번째 에러: 생성자 호출이 첫 번째 줄이 아닌 두 번째 줄에서 일어났다.
- 두 번째 에러: 생성자 내에서 다른 생성자를 호출할 때 this를 사용하지 않고 클래스 이름인 Person을 사용했다.
- 예시
public class Person {
private String name;
private int age;
public Person() {
this("John Doe", 30); // 다른 생성자 호출
}
public Person(String name) {
this(name, 30); // 다른 생성자 호출
}
public Person(String name, int age) {
this.name = name; // 인스턴스 변수에 인자 값 할당
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person();
System.out.println("person1의 Name: " + person1.name + ", person1의 Age: " + person1.age);
Person person2 = new Person("Jane Smith");
System.out.println("person2의 Name: " + person2.name + ", person2의 Age: " + person2.age);
Person person3 = new Person("Alex", 35);
System.out.println("person3의 Name: " + person3.name + ", person3의 Age: " + person3.age);
}
}
person1의 Name: John Doe, person1의 Age: 30
person2의 Name: Jane Smith, person2의 Age: 30
person3의 Name: Alex, person3의 Age: 35
this 인스턴스 자신을 가리키는 참조변수. 인스턴스의 주소가 저장되어 있다.
this(), this(매개변수) 생성자. 같은 클래스의 다른 생성자를 호출할 때 사용된다.
[참고] this는 참조변수이고, this()는 생성자이다.
5.5 생성자를 이용한 인스턴스의 복사
현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다. 두 인스턴스가 같은 상태를 갖는다는 것은 두 인스턴스의 모든 인스턴스 변수(상태)가 동일한 값을 갖고 있다는 것을 뜻한다.
하나의 클래스로부터 생성된 모든 인스턴스의 메서드와 클래스변수는 서로 동일하기 때문에 인스턴스간의 차이는, 인스턴스마다 각기 다른 값을 가질 수 있는 인스턴스변수 뿐이다.
public Person(Person person) {
name = person.name;
age = person.age;
}
인스턴스를 생성할 때는 다음의 2가지 사항을 결정해야한다.
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?
6. 변수의 초기화
6.1 변수의 초기화
변수를 선언하고 처음으로 값을 저장하는 것을 '변수의 초기화'라고 한다. 멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야 한다.
class iniTest {
int x; // 멤버변수 - 인스턴스변수
int y; // 멤버변수 - 인스턴스 변수
void method() {
int i; // 지역변수
int j = i; // ERROR. 지역변수를 초기화하지 않고 사용
}
}
멤버변수(클래스변수와 인스턴스변수)와 배열의 초기화는 선택이지만, 지역변수 초기화는 필수다.
- 각 타입의 기본값(default value)
- byte, short, int: 0
- long: 0L
- float: 0.0f
- double: 0.0d
- char: '\u0000'
- boolean: false
- 참조 타입 (예: 클래스, 배열): null
[참고] 다른 타입의 변수는 함께 선언하거나 초기화할 수 없다. (ex. int i=10, long j=0)
멤버변수의 초기화는 지역변수와 달리 여러가지 방법이 있는데 크게 명시적 초기화(explicit initialization), 초기화 블럭(initialization block), 생성자(constructor) 세 가지 방법이 존재한다. 앞으로 멤버변수 초기화에 대한 모든 방법에 대해 비교, 정리할 것이다.
6.2 명시적 초기화(explicit initalization)
변수를 선언과 동시에 초기화하는 것을 명시적초기화라고 한다. 가장 기본적이면서도 간단한 초기화 방법이므로 여러 초기화 방법 중에서 가장 우선적으로 고려되어야 한다.
public class ExplicitInitializationExample {
// 정수형 변수를 명시적으로 초기화
int number = 10;
// 문자열 변수를 명시적으로 초기화
String text = "Hello";
// 배열 변수를 명시적으로 초기화
int[] numbers = { 1, 2, 3, 4, 5 };
public static void main(String[] args) {
ExplicitInitializationExample example = new ExplicitInitializationExample();
System.out.println("Number: " + example.number);
System.out.println("Text: " + example.text);
System.out.print("Numbers: ");
for (int num : example.numbers) {
System.out.print(num + " ");
}
System.out.println();
}
}
Number: 10
Text: Hello
Numbers: 1 2 3 4 5
명시적 초기화가 간단하고 명료하긴 하지만, 보다 복잡한 초기화 작업이 필요할 때는 '초기화 블럭(initialization block)' 또는 생성자를 사용해야 한다.
6.3 초기화 블럭(initalization block)
초기화 블럭에는 '클래스 초기화 블럭'과 '인스턴스 초기화 블럭' 두 가지 종류가 있다. 클래스 초기화 블럭은 클래스변수의 초기화에 사용되고, 인스턴스 초기화 블럭은 인스턴스 변수의 초기화에 사용된다.
- 클래스 초기화 블럭: 클래스변수의 복잡한 초기화에 사용된다.
- 인스턴스 초기화 블럭: 인스턴스변수의 복잡한 초기화에 사용된다.
초기화 블럭을 작성하려면, 인스턴스 초기화 블럭은 클래스 내에 블럭{}을 만들고 그 안에 코드를 작성하기만 하면 된다. 그리고 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 static을 덧붙이기만 하면 된다.
초기화 블럭 내에는 메서드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용할 수 있으므로, 초기화 작업이 복잡하여 명시적 초기화만으로 부족한 경우 초기화 블럭을 사용한다.
class initBlock {
static { /* 클래스 초기화 블럭입니다. */ }
{ /* 인스턴스 초기화 블럭입니다. */ }
// ...
}
클래스 초기화 블록은 클래스가 처음으로 로드될 때 실행되며, 단 한 번 실행된다. 클래스 초기화 블록은 static 키워드로 표시된다. 클래스 초기화 블록은 클래스변수(static 변수)의 초기화나 클래스 수준의 작업을 수행하는 데 사용된다.
인스턴스 초기화 블록은 클래스의 각 인스턴스가 생성될 때마다 실행된다. 인스턴스 초기화 블록은 클래스 내부에 중괄호({})로 묶인 일반적인 코드 블록 형태로 작성된다. 인스턴스 초기화 블록은 인스턴스변수의 초기화나 인스턴스 수준의 작업을 수행하는 데 사용된다.
public class InitBlock {
static {
// 클래스 초기화 블록
System.out.println("Class initialization block");
}
{
// 인스턴스 초기화 블록
System.out.println("Instance initialization block");
}
public InitBlock() {
System.out.println("Constructor");
}
public static void main(String[] args) {
InitBlock obj1 = new InitBlock();
System.out.println("------");
InitBlock obj2 = new InitBlock();
}
}
Class initialization block
Instance initialization block
Constructor
------
Instance initialization block
Constructor
초기화 블럭은 코드의 중복을 제거해 준다는 장점이 있다. 코드의 중복을 제거하는 것은 코드의 신뢰성을 높여 주고, 오류 발생가능성을 줄여 준다는 장점이 있다. 즉, 재사용성을 높이고 중복을 제거하는 것, 이것이 바로 객체지향프로그래밍이 추구하는 궁국적인 목표이다.
6.4 멤버 변수의 초기화 시기와 순서
- 클래스변수의 초기화시점: 클래스가 처음 로딩될 때 단 한 번 초기화 된다.
- 인스턴스변수의 초기화시점: 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
- 클래스변수의 초기화순서: 기본값 → 명시적초기화 → 클래스 초기화 블럭
- 인스턴스변수의 초기화순서: 기본값 → 명시적초기화 → 인스턴스 초기화 블럭 → 생성자
프로그램 실행도중 클래스에 대한 정보가 요구될 때, 클래스는 메모리에 로딩된다. 예를 들면, 클래스 멤버를 사용했을 때, 인스턴스를 생성할 때 등이 이에 해당한다.
하지만, 해당 클래스가 이미 메모리에 로딩되어 있다면, 또다시 로딩하지 않는다. 물론 초기화도 다시 수행되지 않는다.
class InitializationExample {
// 클래스 변수 (static 변수)
static int classVar = 10;
// 인스턴스 변수
int instanceVar;
// 클래스 초기화 블록 (static initialization block)
static {
classVar = 20;
}
// 인스턴스 초기화 블록 (instance initialization block)
{
instanceVar = 30;
}
InitializationExample() {
instanceVar = 40;
}
public static void main(String[] args) {
InitializationExample obj1 = new InitializationExample();
System.out.println("classVar: " + classVar);
System.out.println("instanceVar: " + obj1.instanceVar);
InitializationExample obj2 = new InitializationExample();
System.out.println("classVar: " + classVar);
System.out.println("instanceVar: " + obj2.instanceVar);
}
}
classVar: 20
instanceVar: 40
classVar: 20
instanceVar: 40


'Java' 카테고리의 다른 글
| [Java] 자바의 추상 클래스 vs 인터페이스 완전 정복 (0) | 2025.10.09 |
|---|---|
| [JAVA] 데이터 추상화 vs 제어 추상화 (0) | 2025.10.09 |
| [Java] 클래스 메소드(static 메소드) vs 인스턴스 메소드 (0) | 2025.10.03 |
| [Java] 기본형 매개변수와 참조형 매개변수 (0) | 2025.10.03 |
| [Java] Primitive type(원시타입) vs. Reference type(참조타입) (3) | 2025.08.30 |