class 클래스
- 클래스 원리
- this , 메소드
- 접근제어자
- 상속
- super
- 오버로딩, 오버라이딩
- gtter , stter
- static
클래스, 객체
자바프로그램은 클래스로부터 객체를 생성하여 프로그램을 작성합니다.
객체를 생성하기 위해서는 클래스를 작성하여야 합니다.
# 클래스 구성
클래스는 멤버변수, 생성자, 메서드 3가지 요소로 구성
[접근 제어자] class 클래스명 {
// 필드
[접근 제어자] 데이터 타입 필드명;
// 생성자
[접근 제어자] 클래스명(매개변수들) {
// 생성자 코드
}
// 메소드
[접근 제어자] 반환 타입 메소드명(매개변수들) {
// 메소드 코드
}
}
# 접근 제어자
접근 제어자는 클래스, 메소드, 필드 등에 대한 접근을 제어하는 키워드입니다.
- public: 모든 클래스에서 접근 가능
- protected: 같은 패키지 내부와 상속받은 클래스에서 접근 가능
- default: 같은 패키지 내부에서만 접근 가능
- private: 같은 클래스 내부에서만 접근 가능
public (공개) : 클래스, 메소드, 필드에 public 접근 제어자를 붙이면, 이는 모든 다른 클래스에서 접근 가능합니다.
public class MyClass {
public int myField;
public void myMethod() {
// 메소드 내용
}
}
private (비공개) : private 접근 제어자를 가진 멤버는 선언된 클래스 내에서만 접근할 수 있습니다. 다른 클래스에서는 접근할 수 없습니다.
public class MyClass {
private int myField;
private void myMethod() {
// 메소드 내용
}
}
default (패키지 전용) : 접근 제어자를 명시하지 않으면 기본적으로 default 접근 제어자를 가지며, 이는 같은 패키지 내에서만 접근할 수 있습니다.
class MyClass {
int myField; // default 접근 제어자
void myMethod() {
// 메소드 내용
}
}
protected (보호) : protected 접근 제어자를 가진 멤버는 같은 패키지 내의 클래스와 이를 상속받는 하위 클래스에서 접근할 수 있습니다.
public class MyClass {
protected int myField;
protected void myMethod() {
// 메소드 내용
}
}
접근 범위 요약
접근 제어자같은 클래스 같은 패키지하위 클래스모든 클래스
접근 제어자 | 같은 클래스 | 같은 패키지 | 하위 클래스 | 모든 클래스 |
public | O | O | O | O |
protected | O | O | O | X |
default | O | O | X | X |
private | O | X | X | X |
각 접근 제어자는 클래스 멤버의 캡슐화 수준을 결정하며, 코드의 재사용성과 보안성을 높이는데 중요한 역할을 합니다.
객체 선언과 생성
// 1. 클래스 참조 변수 선언
MyClass myObject;
// 2. new 키워드를 사용하여 객체 생성
myObject = new MyClass();
// 3. 참조 변수에 객체 할당
// 이 단계는 위의 두 단계를 합쳐서 한 번에 작성할 수도 있습니다.
MyClass myObject = new MyClass();
클래스 선언
public class Person {
String name;
int age;
// 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 메소드
public void introduce() {
System.out.println("안녕하세요, 저는 " + name + "이고, 나이는 " + age + "살입니다.");
}
}
객체 생성
public class Main {
public static void main(String[] args) {
// 객체 선언 및 생성
Person person1 = new Person("홍길동", 25);
Person person2 = new Person("김영희", 30);
// 메소드 호출
person1.introduce();
person2.introduce();
}
}
결과
안녕하세요, 저는 홍길동이고, 나이는 25살입니다.
안녕하세요, 저는 김영희이고, 나이는 30살입니다.
객체 배열 선언과 생성
public class Main {
public static void main(String[] args) {
// Person 객체 배열 선언 및 생성
Person[] people = new Person[2];
// 각 배열 요소에 Person 객체 생성
people[0] = new Person("홍길동", 25);
people[1] = new Person("김영희", 30);
// 각 객체의 메소드 호출
for (Person person : people) {
person.introduce();
}
}
}
# this
this 키워드는 객체 자신을 참조하는 데 사용됩니다.
- 인스턴스 변수와 메소드 매개변수 구분
- 생성자에서 다른 생성자 호출
- 현재 객체의 참조 반환
인스턴스 변수와 메소드 매개변수 구분
클래스의 인스턴스 변수와 메서드 매개변수의 이름이 동일할 때, this 키워드를 사용하여 인스턴스 변수를 참조할 수 있습니다.
public class Person {
private String name;
private int age;
// 생성자
public Person(String name, int age) {
this.name = name; // this.name은 인스턴스 변수, name은 매개변수
this.age = age; // this.age는 인스턴스 변수, age는 매개변수
}
// 메소드
public void setName(String name) {
this.name = name; // this.name은 인스턴스 변수, name은 매개변수
}
public void setAge(int age) {
this.age = age; // this.age는 인스턴스 변수, age는 매개변수
}
public void introduce() {
System.out.println("안녕하세요, 저는 " + this.name + "이고, 나이는 " + this.age + "살입니다.");
}
}
생성자에서 다른 생성자 호출
this 키워드를 사용하여 같은 클래스의 다른 생성자를 호출할 수 있습니다. 이를 통해 코드의 중복을 줄일 수 있습니다.
public class Person {
private String name;
private int age;
// 기본 생성자
public Person() {
this("이름없음", 0); // 다른 생성자 호출
}
// 매개변수가 있는 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void introduce() {
System.out.println("안녕하세요, 저는 " + this.name + "이고, 나이는 " + this.age + "살입니다.");
}
}
현재 객체의 참조 반환
메소드 내에서 현재 객체의 참조를 반환할 때 this를 사용할 수 있습니다. 이는 메소드 체이닝을 구현할 때 유용합니다.
public class Person {
private String name;
private int age;
public Person setName(String name) {
this.name = name;
return this; // 현재 객체의 참조 반환
}
public Person setAge(int age) {
this.age = age;
return this; // 현재 객체의 참조 반환
}
public void introduce() {
System.out.println("안녕하세요, 저는 " + this.name + "이고, 나이는 " + this.age + "살입니다.");
}
public static void main(String[] args) {
// 메소드 체이닝
Person person = new Person();
person.setName("홍길동").setAge(25).introduce();
}
}
결과
안녕하세요, 저는 홍길동이고, 나이는 25살입니다.
# 오버로딩과 오버라이딩
오버로딩 (Overloading)
오버로딩은 같은 이름의 메소드를 여러 개 정의하되, 매개변수의 타입이나 개수, 순서를 다르게 하는 것입니다. 이는 컴파일 시점에 결정되며, 주로 메소드의 이름을 재사용하면서 다양한 입력을 처리하기 위해 사용됩니다.
class MathUtils {
// 두 개의 정수를 더하는 메소드
int add(int a, int b) {
return a + b;
}
// 세 개의 정수를 더하는 메소드
int add(int a, int b, int c) {
return a + b + c;
}
// 두 개의 실수를 더하는 메소드
double add(double a, double b) {
return a + b;
}
}
오버라이딩 (Overriding)
오버라이딩은 부모 클래스에 정의된 메소드를 자식 클래스에서 재정의하는 것입니다. 이는 런타임 시점에 결정되며, 주로 상속 관계에서 부모 클래스의 메소드를 자식 클래스에서 재정의하여 동작을 변경하기 위해 사용됩니다.
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
오버로딩과 오버라이딩의 차이점
비교 항목오버로딩 (Overloading)오버라이딩 (Overriding)
비교항목 | 오버로딩 | 오버라이딩 |
정의 | 같은 이름의 메소드를 매개변수의 타입, 개수, 순서를 다르게 정의하는 것 | 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것 |
시점 | 컴파일 시점에 결정 | 런타임 시점에 결정 |
관계 | 같은 클래스 내에서 발생 | 상속 관계에 있는 클래스 간에 발생 |
목적 | 다양한 입력을 처리하기 위해 | 상속받은 메소드의 동작을 변경하기 위해 |
애노테이션 | 사용하지 않음 | @Override 애노테이션을 사용하여 명시적으로 재정의 표시 |
접근 제한자 | 상관없음 | 부모 클래스의 메소드와 동일하거나 더 좁은 접근 제한자를 가질 수 없음 |
반환 타입 | 다를 수 있음 | 부모 클래스의 메소드와 동일하거나 더 좁은 반환 타입 (공변 반환 타입) 가능 |
# getter 와 setter
getter와 setter는 객체의 필드에 접근하고 수정하는 메소드입니다. 이 메소드는 캡슐화를 유지하면서 객체의 데이터를 안전하게 관리할 수 있게 해줍니다. 캡슐화는 객체 지향 프로그래밍의 중요한 원칙 중 하나로, 데이터의 직접적인 접근을 막고, 대신 메소드를 통해 접근하게 함으로써 데이터의 무결성을 유지할 수 있습니다.
Getter 메소드
- 역할: 객체의 필드 값을 반환합니다.
- 이름 규칙: 보통 get 접두사를 붙이고 필드 이름의 첫 글자를 대문자로 합니다.
public class Person {
private String name;
private int age;
// Getter for name
public String getName() {
return name;
}
// Getter for age
public int getAge() {
return age;
}
}
Setter 메소드
- 역할: 객체의 필드 값을 설정(수정)합니다.
- 이름 규칙: 보통 set 접두사를 붙이고 필드 이름의 첫 글자를 대문자로 합니다.
public class Person {
private String name;
private int age;
// Setter for name
public void setName(String name) {
this.name = name;
}
// Setter for age
public void setAge(int age) {
this.age = age;
}
}
Getter와 Setter의 사용 예제
public class Main {
public static void main(String[] args) {
// Person 객체 생성
Person person = new Person();
// Setter 메소드를 사용하여 필드 값 설정
person.setName("홍길동");
person.setAge(25);
// Getter 메소드를 사용하여 필드 값 가져오기
System.out.println("이름: " + person.getName());
System.out.println("나이: " + person.getAge());
}
}
결과
이름: 홍길동
나이: 25
장점
- 캡슐화: 직접 필드에 접근하지 않고 메소드를 통해 접근하므로, 데이터의 무결성을 유지할 수 있습니다.
- 유효성 검사: setter 메소드 안에서 값의 유효성을 검사할 수 있습니다.
- 읽기 전용/쓰기 전용: getter만 제공하여 읽기 전용 필드로 만들거나, setter만 제공하여 쓰기 전용 필드로 만들 수 있습니다.
- 디버깅 용이: 필드 접근 로직을 메소드에 넣음으로써 디버깅과 로깅을 쉽게 할 수 있습니다.
유효성 검사
public class Person {
private String name;
private int age;
// Getter for name
public String getName() {
return name;
}
// Setter for name
public void setName(String name) {
if (name != null && !name.isEmpty()) {
this.name = name;
} else {
System.out.println("유효한 이름을 입력하세요.");
}
}
// Getter for age
public int getAge() {
return age;
}
// Setter for age
public void setAge(int age) {
if (age > 0) {
this.age = age;
} else {
System.out.println("유효한 나이를 입력하세요.");
}
}
}
# 상속
상속은 객체 지향 프로그래밍(OOP)의 중요한 개념 중 하나로, 기존 클래스의 특성과 동작을 새로운 클래스에 물려주는 것을 의미합니다. 상속을 통해 코드의 재사용성을 높이고, 계층 구조를 만들어 프로그램의 구조를 더 체계적으로 만들 수 있습니다.
상속의 기본 개념
- 부모 클래스 (Superclass, Base Class)
- 다른 클래스에 상속되는 클래스입니다.
- 일반적으로 상위 클래스라고도 합니다.
- 자식 클래스 (Subclass, Derived Class)
- 부모 클래스를 상속받는 클래스입니다.
- 부모 클래스의 모든 멤버 변수와 메소드를 물려받으며, 추가적인 멤버 변수와 메소드를 가질 수 있습니다.
상속의 문법
자바에서 상속을 구현하려면 extends 키워드를 사용합니다. 자식 클래스는 부모 클래스의 멤버(필드와 메소드)를 상속받으며, 필요에 따라 이러한 멤버를 오버라이딩(재정의)할 수 있습니다.
public class 부모클래스 {
// 필드
private String name;
// 생성자
public 부모클래스(String name) {
this.name = name;
}
// 메소드
public void display() {
System.out.println("부모 클래스: " + name);
}
}
public class 자식클래스 extends 부모클래스 {
// 추가 필드
private int age;
// 생성자
public 자식클래스(String name, int age) {
super(name); // 부모 클래스의 생성자 호출
this.age = age;
}
// 메소드 오버라이딩
@Override
public void display() {
super.display(); // 부모 클래스의 메소드 호출
System.out.println("자식 클래스: 나이 = " + age);
}
}
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println("동물이 소리를 냅니다.");
}
}
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 부모 클래스의 생성자 호출
this.breed = breed;
}
@Override
public void makeSound() {
System.out.println(name + "가 멍멍 짖습니다.");
}
public void displayBreed() {
System.out.println(name + "는 " + breed + "입니다.");
}
}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog("바둑이", "진돗개");
myDog.makeSound(); // 오버라이딩된 메소드 호출
myDog.displayBreed();
}
}
바둑이가 멍멍 짖습니다.
바둑이는 진돗개입니다.
상속의 장점
- 코드 재사용성 증가: 기존 클래스의 코드를 재사용할 수 있어 중복 코드를 줄이고 유지보수를 쉽게 합니다.
- 계층 구조 형성: 상속을 통해 클래스 간의 계층 구조를 형성하여 체계적인 코드 구조를 만들 수 있습니다.
- 다형성 (Polymorphism): 상속을 통해 다형성을 구현할 수 있으며, 이는 코드의 유연성과 확장성을 높여줍니다.
상속의 단점
- 강한 결합: 상속은 부모 클래스와 자식 클래스 간의 강한 결합을 초래할 수 있어, 부모 클래스의 변경이 자식 클래스에 영향을 미칠 수 있습니다.
- 설계의 어려움: 적절한 계층 구조를 설계하는 것이 어려울 수 있으며, 잘못된 상속 구조는 코드의 복잡성을 증가시킬 수 있습니다.
상속 관련 주요 용어
- 오버라이딩 (Overriding): 자식 클래스가 부모 클래스의 메소드를 재정의하는 것.
- super: 부모 클래스의 멤버에 접근하거나 부모 클래스의 생성자를 호출할 때 사용.
- 다형성 (Polymorphism): 같은 타입의 객체가 다양한 형태로 동작할 수 있게 하는 특성. 보통 상속과 함께 사용됨.
# 메소드 오버라이딩
메소드 오버라이딩(Method Overriding)은 객체 지향 프로그래밍(OOP)에서 상속받은 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것을 의미합니다. 이를 통해 자식 클래스는 부모 클래스의 메소드 기능을 자신에 맞게 변경할 수 있습니다. 메소드 오버라이딩은 다형성(Polymorphism)을 구현하는 중요한 방법 중 하나입니다.
메소드 오버라이딩의 규칙
- 메소드 시그니처 동일: 오버라이딩하는 메소드는 부모 클래스의 메소드와 동일한 이름, 매개변수 리스트, 반환 타입을 가져야 합니다.
- 접근 제어자: 오버라이딩하는 메소드의 접근 제어자는 부모 클래스의 메소드보다 더 좁은 범위로 설정할 수 없습니다. 예를 들어, 부모 클래스의 메소드가 public이면 자식 클래스의 메소드도 public이어야 합니다.
- 예외: 자식 클래스의 메소드는 부모 클래스의 메소드보다 더 많은 예외를 던질 수 없습니다.
@Override 어노테이션
@Override 어노테이션은 자바 컴파일러에게 해당 메소드가 부모 클래스의 메소드를 오버라이딩하고 있음을 알리는 역할을 합니다. 이는 오타나 잘못된 메소드 시그니처로 인한 오류를 방지하는 데 도움이 됩니다.
부모 클래스 메소드 호출
자식 클래스에서 부모 클래스의 메소드를 호출하려면 super 키워드를 사용합니다.
오버라이딩과 오버로딩의 차이점
- 오버라이딩 (Overriding): 상속 관계에서 자식 클래스가 부모 클래스의 메소드를 재정의하는 것.
- 오버로딩 (Overloading): 같은 클래스 내에서 같은 이름의 메소드를 여러 개 정의하되, 매개변수의 타입, 개수, 순서가 다르게 하는 것.
메소드 오버라이딩은 자바에서 다형성을 구현하는 중요한 방법입니다. 이를 통해 자식 클래스는 부모 클래스의 메소드를 재정의하여 더 구체적이고 적합한 동작을 구현할 수 있습니다. @Override 어노테이션을 사용하면 코드의 가독성과 안정성을 높일 수 있습니다.
# super
super 키워드는 자식 클래스에서 부모 클래스의 멤버(변수, 메소드)나 생성자에 접근할 때 사용됩니다.
- 부모 클래스의 변수에 접근: 자식 클래스에서 부모 클래스의 변수를 사용할 때.
- 부모 클래스의 메소드 호출: 자식 클래스에서 부모 클래스의 메소드를 호출할 때.
- 부모 클래스의 생성자 호출: 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출할 때.
부모 클래스의 변수에 접근
부모 클래스의 인스턴스 변수를 자식 클래스에서 사용할 때 super를 사용할 수 있습니다.
부모 클래스의 메소드 호출
자식 클래스에서 부모 클래스의 메소드를 호출할 때 super를 사용할 수 있습니다.
부모 클래스의 생성자 호출
자식 클래스의 생성자에서 부모 클래스의 생성자를 호출할 때 super를 사용합니다. 이는 부모 클래스의 필드를 초기화하거나 부모 클래스의 생성자에서 수행하는 작업을 수행하기 위해 필요합니다.
문제
1. 필드명 : private
2. getter , setter : 값 가져오기, 변경하기
3. printBookInfo : 책정보 출력 메서드
# static
1. 메모리 절약
- 공유 데이터: 클래스의 모든 인스턴스가 동일한 데이터를 공유해야 할 때 static 변수를 사용합니다. 이를 통해 동일한 데이터를 여러 번 저장할 필요가 없어 메모리를 절약할 수 있습니다.
- 예시: 모든 인스턴스가 같은 상수를 공유할 때 사용합니다. 예를 들어, 모든 동물의 왕국 이름이 "Animalia"인 경우.
2. 공통 기능 제공
- 유틸리티 메서드: 특정 객체와 무관하게 공통적인 기능을 제공하는 메서드를 정의할 때 static 메서드를 사용합니다. 이러한 메서드는 인스턴스를 생성하지 않고도 호출할 수 있습니다.
- 예시: 수학적 계산을 위한 Math 클래스의 Math.sqrt()와 같은 메서드들.
3. 클래스 수준의 접근
- 클래스 변수 및 메서드: 특정 객체가 아닌 클래스 수준에서 접근할 수 있는 변수나 메서드를 정의할 때 static을 사용합니다. 이를 통해 인스턴스 없이도 클래스 자체로 접근할 수 있습니다.
- 예시: 생성된 객체의 수를 추적하는 카운터 변수.
4. 초기화 블록
- 정적 초기화 블록: 클래스가 처음 로드될 때 단 한 번 실행되는 초기화 블록을 정의할 때 사용합니다. 이를 통해 클래스 변수의 복잡한 초기화를 할 수 있습니다.
- 예시: 데이터베이스 연결 설정과 같은 리소스를 클래스 로드 시 초기화하는 경우
Animal 클래스
public class Animal {
// 모든 동물이 공유하는 static 변수
private static String kingdom = "Animalia"; // 동물의 왕국 (모든 동물에게 동일한 값)
// 인스턴스 변수
private String name;
private String species;
// 생성자
public Animal(String name, String species) {
this.name = name;
this.species = species;
}
// static 메서드: 왕국 이름 반환
public static String getKingdom() {
return kingdom;
}
// static 메서드: 왕국 이름 설정
public static void setKingdom(String newKingdom) {
kingdom = newKingdom;
}
// 인스턴스 메서드: 동물의 이름을 반환
public String getName() {
return name;
}
// 인스턴스 메서드: 동물의 종을 반환
public String getSpecies() {
return species;
}
}
AnimalTest
// main 메서드: static 멤버 사용 예시
public static void main(String[] args) {
// Animal 클래스의 static 변수와 메서드 사용
System.out.println("동물의 왕국: " + Animal.getKingdom()); // 출력: 동물의 왕국: Animalia
Animal animal1 = new Animal("사자", "Felidae");
Animal animal2 = new Animal("코끼리", "Elephantidae");
System.out.println("동물 1: " + animal1.getName() + " - " + animal1.getSpecies());
System.out.println("동물 2: " + animal2.getName() + " - " + animal2.getSpecies());
// static 메서드를 사용하여 왕국 이름 변경
Animal.setKingdom("Animal Kingdom");
System.out.println("변경된 동물의 왕국: " + Animal.getKingdom());
// 출력: 변경된 동물의 왕국: Animal Kingdom
}
- static 변수 kingdom:
- kingdom 변수는 모든 Animal 인스턴스가 공유하는 변수입니다.
- Animalia라는 값으로 초기화되어 있으며, 모든 동물에게 동일한 왕국을 나타냅니다.
- static 변수이기 때문에 클래스의 모든 인스턴스가 이 변수를 공유합니다.
- static 메서드 getKingdom():
- 이 메서드는 현재 kingdom 값을 반환합니다.
- static 메서드이기 때문에 Animal 클래스의 인스턴스를 생성하지 않고도 호출할 수 있습니다.
- static 메서드 setKingdom(String newKingdom):
- 이 메서드는 kingdom 값을 새 값으로 설정합니다.
- static 메서드이기 때문에 인스턴스 생성 없이 호출할 수 있습니다.
- 생성자:
- 생성자는 동물의 이름과 종을 매개변수로 받아 해당 인스턴스 변수를 초기화합니다.
- 인스턴스 메서드 getName()와 getSpecies():
- 이 메서드들은 각각 동물의 이름과 종을 반환합니다.
- main 메서드:
- main 메서드에서는 Animal 클래스의 static 변수와 메서드를 사용하여 왕국 이름을 출력하고 변경합니다.
- 또한, 두 Animal 인스턴스를 생성하고 각각의 정보를 출력합니다.
'java' 카테고리의 다른 글
[ JAVA ] 클래스 (0) | 2024.05.17 |
---|---|
[ java ] Array ( 배열 ) , 메서드 (0) | 2024.01.31 |
[ java ] 제어문 - 조건문 , 반복문, 분기문 (1) | 2024.01.29 |
[ java ] 자바 프로그램 실행 , 변수와 연산자, 형변환 (1) | 2024.01.08 |
[ java ] 인텔리제이 프로그램 설치 및 설정 (0) | 2024.01.07 |