본 게시글은 대학 전공수업을 들으며 노션에 정리한 내용을 블로그로 옮긴 것으로, 에디터 차이로 인해 일부 수식, 서식, 내용 등이 빠져 있을 수 있습니다.
노션 웹을 통해 최적화된 형태로 읽으시길 권장드립니다.(➡️ 노션 링크)
4강 - static, final, 오버로딩/오버라이딩, 상속, this, super, 메소드체이닝
클래스 정의와 사용(2)
static 필드
정적 필드 or 클래스 변수라고 칭함
클래스의 모든 객체가 공유하는 데이터
객체 생성이 없어도 항상 사용 가능
어떤 객체도 값을 변경할 수 있음
예시
public class Circle { // 인스턴스 변수: 각 객체가 개별적으로 소유 private int radius; private float pie = 3.14f; // 클래스 변수: 모든 객체가 공유 private static int instanceCount = 0; // 생성자: 인스턴스 변수를 초기화하고, 클래스 변수 증가 public Circle(int radius) { this.radius = radius; instanceCount++; }
사용법
- 클래스이름.정적필드 ex) Cricle.instanceCount
- 객체변수.정적필드 ex) c2.instanceCount
static 메소드
정적 메소드 or 클래스 메소드라고 칭함
- non-static메소드와 달리, 객체와 무관하게 호출되고 실행된다.
- 메소드 몸체에서 this를 사용할 수 없음.
- static 메소드는 static 필드를 다룸!
호출 방법
클래스이름.정적메소드() ex) Math.sqrt(2.0);
Integer.parseInt("12")
final 필드
상수 데이터!
선언할 때 초기값을 지정해줘야 한다.
자주 static과 함께 사용 됨.
final static doulbe PI = 3.141592;
final 메소드
- 자식클래스로 상속은 가능
- but 자식클래스에서 재정의(오버라이딩)할 수 없음.
필드 초기화
객체를 생성할 때 데이터 필드에 초기값을 지정
객체 초기화를 위해 초기화 블록을 사용할 수 있음.
- 초기값 지정을 위한 코드. 임의 위치
- static 필드는 static 초기화 블록을 사용.
코드
package lecture04; public class FieldInitialization { // 클래스 변수(static 변수) 선언 및 초기화 private static int classVariable = 10; // 인스턴스 변수 선언 private int instanceVariable; // 인스턴스 초기화 블록: 객체가 생성될 때마다 실행 { instanceVariable = 20; // 인스턴스 변수 초기화 System.out.println("인스턴스 초기화 블록 실행: instanceVariable = " + instanceVariable); } // static 초기화 블록: 클래스가 로드될 때 실행 static { classVariable = 30; // 클래스 변수 초기화 System.out.println("정적 초기화 블록 실행: classVariable = " + classVariable); } // 생성자: 객체를 생성할 때 호출되는 메서드 public FieldInitialization() { System.out.println("생성자 호출: instanceVariable = " + instanceVariable); } public static void main(String[] args) { // 클래스 변수 출력 System.out.println("클래스 변수 값: " + classVariable); // 클래스의 인스턴스 생성 FieldInitialization obj = new FieldInitialization(); } }
실행 결과(순서에 유념!)
정적 초기화 블록 실행: classVariable = 30 클래스 변수 값: 30 인스턴스 초기화 블록 실행: instanceVariable = 20 생성자 호출: instanceVariable = 20
순서
- static 필드 선언문에서 초기화 (프로그램 시작)
- 클래스 변수는 프로그램이 시작될 때 초기화 됨
- static 초기화 블록 실행
- non-static 필드 초기화 (객체 생성)
- non-static 초기화 블록 실행
- 생성자 호출
메소드 오버로딩
매개변수 개수 또는 매개변수 자료형이 다르면 같은 이름의 메소드를 여러 개 정의 가능
- 반환형과 접근제어자는 기준x
예시 코드/설명
public class PrintStream extends FilterOutputStream implements Appendable, Closeable { ...생략 public void println() { newLine(); } ...생략 public void println(int x) { if (getClass() == PrintStream.class) { writeln(String.valueOf(x)); } else { synchronized (this) { print(x); newLine(); } } } ...생략 }
자바는 위처럼 오버로딩 되어있기 때문에
println()
라는 하나의 메서드가 여러 자료형을 커버해준다!
클래스의 재사용 방법 - 합성과 상속
합성
- 기존 클래스를 새로운 클래스 정의에서 데이터 필드의 자료형으로 사용.
- “has-a” 관계 : 자동차는 엔진을 가지고(포함하고) 있다.
- 예시 : 자동차와 엔진 / 선과 점
핵심은!!!!
private Engine engine; // 엔진 클래스의 인스턴스를 데이터 필드로 포함
예시 코드
package lecture04; public class HasARelation { // 엔진 클래스 public class Engine { private String type; public Engine(String type) { this.type = type; } public void start() { System.out.println("Engine starts"); } // 기타 엔진 관련 메서드들... } // 자동차 클래스 public class Car { private String brand; private String model; private Engine engine; // 엔진 클래스의 인스턴스를 데이터 필드로 포함 public Car(String brand, String model, Engine engine) { this.brand = brand; this.model = model; this.engine = engine; } public void start() { System.out.println("Car starts"); engine.start(); // 엔진을 시작하는 메서드 호출 } // 기타 자동차 관련 메서드들... } public static void main(String[] args) { // 엔진 인스턴스 생성 Engine engine = new HasARelation().new Engine("V4"); // 자동차 인스턴스 생성 및 엔진 인스턴스 주입 Car car = new HasARelation().new Car("Kia", "K3", engine); // 자동차 시작 car.start(); } }
실행 결과
Car starts Engine starts
당연하게도 Car 먼저 실행 후 Engine의 메서드가 실행됨.
상속
- 클래스의 재사용방법(2), 기존 클래스를 확장 or 특화 하는 것!
- 부모 클래스를 사용하여 자식 클래스를 정의한다. 즉, 상속이란 부모-자식의 관계!
- 재사용성 + 유지보수성 + 확장성.
- “is a” 관계 : 자식 is a 부모.
- 상속받을 때
extends
를 사용!
class Manager extends Employee {...}
//매니저도 직원이다.
자식클래스가 부모클래스의 필드와 메소드를 상속 받는다!
✅부모클래스로부터 상속받은 메소드를 재정의(오버라이딩)할 수 있음.
✅클래스 상속은 단일 상속만 가능
✅인터페이스 : 다중 상속 가능
예시 코드
public class InheritanceSuper { private int superVar1; public int superVar2; }
public class InheritanceSub extends InheritanceSuper { private int subVar; public int subVar2; }
public class InheritMain { public static void main(String[] args) { InheritanceSub sub = new InheritanceSub(); //private 변수에는 접근 불가 // sub.superVar = 10; sub.superVar2 = 20; // sub.subVar = 30; sub.subVar2 = 40; } }
sub 객체에 부모클래스의 필드를 상속받음!
메소드 오버라이딩
- 부모로부터 상속받은 메소드 몸체를 자식클래스에서 재정의
- ex. 도형 클래스(부모)와 사각형 클래스(자식)일 때, get넓이()라는 도형클래스의 메소드를 사각형클래스에서 재정의함.
- 오버라이딩 할 경우, 같은 이름이지만 다른 기능을 수행함.
✅ 메소드 이름, 인자 갯수와 자료형, 반환형이 같은 메소드이여야 한다.
단, 반환형은 자식클래스도 가능
접근제어자의 접근 범위는 같거나 넓어져야함.
예시코드 : 도형과 사각형, getArea()메서드 오버라이딩
public class OverRiding { public static void main(String args[]) { //둘 다 가능(다형성) Shape tri = new Triangle(); // Triangle tri = new Triangle(); System.out.println(tri.getArea(3.0, 4.0)); } } class Shape { public double getArea(double h, double w) { return h * w; } } class Triangle extends Shape { public double getArea(double h, double w) { return h * w * 0.5; } }
this
메소드 호출시, 숨은 인자로 this가 메소드에 전달됨.
this는 현재 객체(자기 자신)에 대한 참조값을 가지고 있음!
✅static 메소드에서는 사용 불가!
예시
// 도형 클래스 class Shape { private String name; // 생성자 public Shape(String name) { // 인스턴스 변수에 전달받은 이름 할당 this.name = name; } // 메서드 public void printName() { // 현재 도형 객체의 이름 출력 System.out.println("Shape: " + this.name); } } // 메인 클래스 public class Main { public static void main(String[] args) { // 객체 생성 Shape triangle = new Shape("Triangle"); Shape square = new Shape("Square"); // 메서드 호출 triangle.printName(); // "Shape: Triangle" 출력 square.printName(); // "Shape: Square" 출력 } }
핵심부분
public void printName() { // 현재 도형 객체의 이름 출력 System.out.println("Shape: " + this.name); }
super
- this와 같지만 자료형이 부모클래스의 유형.
- 자식클래스의 인스턴스 메소드나 생성자에서 사용됨.
✅ this와 마찬가지로, static 메소드에서는 사용 불가!
부모 클래스에서 오버로딩 당한 메소드를 호출하거나, 상속 되었으나 감춰진 필드에 접근할 때 필요함.
예시
class Child extends Parent { // 생성자 public Child(int number) { super(number); // 부모 클래스의 생성자 호출 } // 메서드 public void printNumber() { super.printNumber(); // 부모 클래스의 메서드 호출 System.out.println("Child's number: " + number); } }
this()
- 같은 클래스의 다른 생성자를 호출
super()
- 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출.
- 상속받은 데이터 필드를 초기화하기 위해 사용
✅ 자식 클래스의 생성자에서, 부모클래스 생성자 호출이 없다 ⇒ super()가 자동 호출
예시 코드
package lecture04; // 원(Circle) 클래스 class Circle { // 인스턴스 변수: 반지름 private double radius; // 클래스 변수: 파이 값 (상수) private static final double PI = 3.14; // 생성자: 반지름을 초기화 public Circle(double radius) { this.radius = radius; } // 원의 넓이를 계산하는 메서드 public double getArea() { return PI * radius * radius; } // 반지름 값을 반환하는 메서드 public double getRadius() { return radius; } } // ------------------------------------ // 실린더(Cylinder) 클래스: Circle을 상속받음 class Cylinder extends Circle { // 인스턴스 변수: 높이 private double height; // 클래스 변수: 파이 값 (상수) static final double PI = 3.14; // 기본 생성자: 반지름과 높이를 기본 값으로 초기화 public Cylinder() { super(1.0); // 부모 클래스의 기본 생성자를 호출 (Circle 클래스에는 기본 생성자가 없으므로 1.0을 전달) height = 1.0; } // 매개변수가 있는 생성자: 반지름과 높이를 초기화 public Cylinder(double radius, double height) { super(radius); // 부모 클래스의 생성자를 호출하여 반지름을 초기화 this.height = height; } // 실린더의 겉넓이를 계산하는 메서드 public double getArea() { return 2 * PI * getRadius() * height + 2 * super.getArea(); // 원기둥의 측면적과 두 개의 원의 넓이를 더함 } // 실린더의 부피를 계산하는 메서드 public double getVolume() { return super.getArea() * height; // 원의 넓이에 높이를 곱함 } } // 메인 클래스 public class Super { // 메인 메서드: 프로그램 실행 시작 지점 public static void main(String args[]) { // 새로운 Cylinder 객체를 생성 Cylinder cylinder = new Cylinder(3, 10); // 그 부피를 출력 System.out.println("실린더 부피 : " + cylinder.getVolume()); // 반지름이 3이고 높이가 10인 실린더의 부피를 출력 } }
메소드 체이닝
하나의 명령문에서 동일 객체에 대해 연속적으로 메소드 호출
✅ 메소드 체이닝에 쓰이는 메소드는 현재 객체의 참조값(this)를 반환해야함!
p.setName(”홍길동”).setAge(30).setAddress(”서울”)
코드
체이닝 vs 그냥
public static void main(String[] args) { Person person = new Person(); // 그냥했을떄 person.setName("김길동"); person.setAge(90); person.setAddress("부산"); person.display(); //메소드 체이닝 person.setName("홍길동").setAge(30).setAddress("서울").display(); // Method chaining }
전제조건 : 메소드가 현재 객체를 반환해야한다! (
return this;
)public Person setName(String name) { this.name = name; return this; // Returning the current object } public Person setAge(int age) { this.age = age; return this; // Returning the current object } public Person setAddress(String address) { Address = address; return this; }
lombok을 사용한다면 @Accessors(chain = true) 달면 된다.
(Getter, Setter는 당연 필수)
요약
클래스를 정의할 때, 클래스의 모든 객체가 공유하는 데이터는 static 데이터 필드로 정의한다.
클래스 정의에 있는 데이터 필드의 선언문, 초기화 블록, 생성자를 통해서 객체 생성 시 필요한 데이터 필드의 초깃값을 지정할 수 있다.
메소드 오버로딩이란 한 클래스에서 이름이 같은 여러 메소드가 존재하는 상황을 말한다.
이때 오버로딩된 메소드끼리는 매개변수 목록이 달라 구별될 수 있다.
부모로부터 상속받은 메소드의 몸체를 자식 클래스에서 다시 정의하는 것을 메소드 오버라이딩이라 한다.
인스턴스 메소드와 생성자에서 숨은 인자인 this를 사용할 수 있다.
this( )는 같은 클래스에 있는 다른 생성자를, super( )는 자식 클래스 생성자에서 부모 클래스의 생성자를 호출하기 위한 것이다.
'Back-End > Java' 카테고리의 다른 글
(전공 정리) 7강 - 패키지와 예외처리 (0) | 2024.06.22 |
---|---|
(전공 정리) 6강 - 제네릭, 람다식 (0) | 2024.06.22 |
(전공정리) 5강 - 추상 클래스, 인터페이스, 다형성, enum, 익명클래스, 중첩클래스 (0) | 2024.06.22 |
(전공 정리) 3강 - 배열, String, Scanner, 클래스, 접근제어자, 생성자 (0) | 2024.06.22 |
(전공 정리) 2강 - 변수, 자료형, 연산자, 반복문, 제어문 (0) | 2024.06.22 |
자바 Servlet 기초 예제(doGet : 데이터 조회) (0) | 2023.08.31 |