intro : 부트캠프 4일차 내용 정리 및 기록.
수업 내용 기록
static 키워드
static 변수는 클래스가 로딩시에 초기화 되며, 클래스의 모든 객체가 공유한다. 인스턴스 변수와는 달리 객체에 속하지 않고 클래스에 속하는 변수이다. 클래스가 로드될 때 초기화되고, 프로그램 종료시까지 메모리에 유지된다.
static 변수예제
class Example {
static int count = 0; // static 변수
public Example() {
count++; // 모든 객체가 공유
}
}
public class Main {
public static void main(String[] args) {
Example obj1 = new Example();
Example obj2 = new Example();
Example obj3 = new Example();
System.out.println("Count: " + Example.count); // 출력: Count: 3
}
}
static 메서드
클래스 레벨에서 호출되는 메서드로 인스턴스화 없이 접근 가능하다. 인스턴스 변수나 메서드를 직접 접근 가능하다.
class MathUtil {
static int add(int a, int b) {
return a + b; // static 메서드는 인스턴스 없이 호출 가능
}
}
public class Main {
public static void main(String[] args) {
System.out.println(MathUtil.add(5, 10)); // 출력: 15
}
}
final 키워드
static final 변수 (상수)
final 변수는 한번 초기화되면 값을 변경할 수 없는 상수가 된다. 특징으로는 변수선언시 즉시 초기화 하거나, 생성자에서 초기화 가능하다. 또한 한번 할당된 값은 다른 값으로 변경할 수 없다. 보통 대문자 이름과 언더스코어로 작성된다.
public class FinalVariableExample {
public static void main(String[] args) {
final int constantValue = 10; // 초기화
System.out.println("Constant: " + constantValue);
// constantValue = 20; // 오류: final 변수는 값을 변경할 수 없음
}
}
static final 메서드
가장 큰 특징으로는 서브클래스에서 재정의 (오버라이딩)이 불가하다, 이렇게 메서드에 final을 사용하는 이유는 메서드의 구현을 고정해서 상속받는 클래스에서 변경하지 못하도록 보호에 목적이 있다.
class Parent {
public final void display() {
System.out.println("This is a final method.");
}
}
class Child extends Parent {
// @Override
// public void display() { // 오류: final 메서드는 오버라이딩 불가
// System.out.println("Cannot override final method.");
// }
}
접근 제한자
public : 어디에서든 접근 가능.
protected : 같은 패키지 + 하위 클래스에서만 접근 가능 (상속관계)
default : 같은 패키지에서만 접근 가능
private : 클래스 내부에서만 접근 가능
가장 헷갈리는 protected와 default 에제로 이해해보기
protected 키워드 예제 (상속관계가 포인트)
// package1/Parent.java
package package1;
public class Parent {
protected void display() {
System.out.println("Protected method");
}
}
// 같은 패키지
package package1;
public class SamePackage {
public void test() {
Parent parent = new Parent();
parent.display(); // 같은 패키지에서 접근 가능
}
}
// 다른 패키지 - 상속 관계
package package2;
import package1.Parent;
public class Child extends Parent {
public void test() {
display(); // 하위 클래스에서 접근 가능
}
}
// 다른 패키지 - 상속 관계가 아님
package package2;
import package1.Parent;
public class NonChild {
public void test() {
Parent parent = new Parent();
// parent.display(); // 오류: 상속 관계가 아니므로 접근 불가
}
}
default 키워드 예제 (같은 패키지가 포인트)
// package1/Example.java
package package1;
class Example { // default 접근 제한자
void display() {
System.out.println("Default method");
}
}
// 같은 패키지
package package1;
public class Main {
public static void main(String[] args) {
Example example = new Example();
example.display(); // 같은 패키지에서 접근 가능
}
}
// 다른 패키지
package package2;
import package1.Example;
public class Main {
public static void main(String[] args) {
// Example example = new Example(); // 오류: 다른 패키지에서는 접근 불가
}
}
상속
상속은 Java의 객체지향프로그래밍에서 핵심 개념 중 하나로, 기존 클래스의 멤버변수와 메서드를 자식 클래스가 물려받아 사용하는 것을 의미한다. 상속은 코드 재사용성을 높이고 계층적인 구조를 통해 코드의 논리적 설계를 용이하게 만든다. 다만 Java에서는 단일 상속만을 허용하고, 결합도가 높아지는 단점이 존재한다.
아래 코드를 보면, Child는 Parent 클래스를 상속받는다. Child 클래스는 diplay
메서드가 존재하지 않지만, 상속을 통해 상위 클래스의 메서드를 물려받아 실행 할 수 있다.
class Parent {
int value = 10;
void display() {
System.out.println("Parent value: " + value);
}
}
class Child extends Parent {
void show() {
System.out.println("Child value: " + value);
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.display(); // 부모 클래스의 메서드 호출
child.show(); // 자식 클래스의 메서드 호출
}
}
추가적으로 상속에서는 상위 클래스에 기본생성자가 아닌, 매개변수가 있는 생성자가 존재하는 경우 해당 클래스를 상속받는 클래스에서 상위 클래스의 생성자를 호출해주어야 한다. 해당 내용과 관련된 예제 코드는 다음과 같다.
class Parent {
int value;
// 매개변수가 있는 생성자
Parent(int value) {
this.value = value;
System.out.println("Parent Constructor called with value: " + value);
}
}
class Child extends Parent {
int childValue;
// 자식 클래스 생성자
Child(int value, int childValue) {
super(value); // 명시적으로 부모 클래스의 생성자를 호출
this.childValue = childValue;
System.out.println("Child Constructor called with childValue: " + childValue);
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child(10, 20);
}
}
상속에 대한 개념을 알아가면서 오버라이딩(overriding) 개념이 더 부각되게 된다. 부모 클래스의 메서드를 자식 클래스에서 재정의하여 다른 동작을 수행하도록 하는 것을 말한다. 다만 부모 메서드와 동일한 이름, 반환타입, 매개변수를 가져야 한다.
다음 예시는 부모 클래스의 메서드를 자식 클래스에서 재정의 하는 예시이다.
class Parent {
void display() {
System.out.println("This is the parent class method.");
}
}
class Child extends Parent {
@Override
void display() {
System.out.println("This is the overridden method in the child class.");
}
}
public class Main {
public static void main(String[] args) {
Parent parent = new Parent();
parent.display();
Child child = new Child();
child.display();
Parent polymorphic = new Child();
polymorphic.display(); // 다형성
}
}
// 출력문 결과
This is the parent class method.
This is the overridden method in the child class.
This is the overridden method in the child class.
위 코드에서 실행결과는 결과는 재정의된 메서드가 실행됨에 따라 Child가 인스턴스화된 객체는
This is the overridden method in the child class.
가 출력된다.