Загружаем каталог…
Загружаем каталог…
Java를 제대로 이해하려면 문법보다 객체지향 프로그래밍(OOP) 을 이해하는 것이 중요하다. 객체지향의 핵심은 데이터를 따로 두고 함수를 따로 두는 것이 아니라, 서로 관련 있는 상태와 행동을 하나의 객체로 묶어 프로그램을 구성하는 것 이다. 1. 클래스와 객체 클래스는 객체를 만들기 위한 설계도처럼 생각할 수 있다. class Product { String name; int price; } 객체는 new 로 생성한다. Product product = new Product(); product.name = "Keyboard"; product.price = 89000; 같은 클래스로 여러 객체를 만들 수 있고 각 객체는 서로 다른 값을 가질 수 있다. Product keyboard = new Product(); Product mouse = new Product(); 2. 인스턴스 변수와 인스턴스 메소드 객체마다 서로 다른 상태를 가지는 필드는 인스턴스 변수다. class User { String name; int point; void addPoint(int amount) { point += amount; } } User user = new User(); user.name = "developer"; user.point = 100; user.addPoint(50); user.point 와 user.addPoint() 처럼 . 을 이용해 객체의 멤버에 접근한다. 3. static : 객체가 아니라 클래스에 속하는 값 모든 객체가 공유해야 하는 값이나 기능은 static 으로 선언할 수 있다. class User { static int userCount = 0; } System.out.println(User.userCount); 클래스 메소드도 객체 없이 호출할 수 있다. class IdGenerator { static String create(String prefix, int id) { return prefix + "-" + id; } } String orderId = IdGenerator.create("ORDER", 1001); 인스턴스에 의존하지 않는 기능이라면 static 이 자연스러울 수 있지만, 모든 것을 static 으로 만들면 객체지향 설계의 장점을 잃기 쉽다. 4. this : 현재 객체를 가리키기 필드 이름과 매개변수 이름이 같을 때 this 로 현재 객체의 필드를 구분할 수 있다. class User { String name; void setName(String name) { this.name = name; } } this.name 은 현재 객체의 필드, 오른쪽 name 은 메소드가 받은 매개변수다. 5. 생성자로 객체의 초기 상태 보장하기 생성자는 객체가 만들어질 때 호출된다. class Product { String name; int price; Product(String name, int price) { this.name = name; this.price = price; } } Product product = new Product("Keyboard", 89000); 필수값을 생성자에서 받게 하면 불완전한 객체가 만들어질 가능성을 줄일 수 있다. 6. 캡슐화와 Getter/Setter 필드를 외부에 직접 공개하기보다 private 으로 감추고 필요한 접근 방법만 제공할 수 있다. class Account { private int balance; public int getBalance() { return balance; } public void deposit(int amount) { if (amount <= 0) { return; } balance += amount; } } 모든 필드에 기계적으로 Getter/Setter를 만드는 것보다 객체가 스스로 유효한 상태를 유지하도록 의미 있는 메소드를 제공하는 것 이 더 좋은 객체지향 설계인 경우가 많다. 7. 접근 제어자 접근 제어자 접근 범위 private 같은 클래스 내부 package-private 같은 패키지 내부 protected 같은 패키지 + 다른 패키지의 하위 클래스 public 어디서든 접근 가능 아무 접근 제어자도 쓰지 않은 경우를 흔히 default 라고 부르지만, Java 문법의 키워드 default 와 혼동하지 않도록 package-private 라고 부르는 편이 정확하다. 8. 패키지와 import 패키지는 관련 클래스를 묶어 이름 충돌을 줄이고 구조를 만든다. package com.example.order; 다른 패키지의 클래스를 사용할 때는 import 를 사용할 수 있다. import java.util.ArrayList; 프로젝트가 커질수록 기능이나 도메인 기준으로 패키지를 나누게 된다. com.example ├── user ├── order └── payment 9. 상속: 기존 기능을 확장하기 class Notification { void send() { System.out.println("알림 전송"); } } class EmailNotification extends Notification { String email; } EmailNotification 은 Notification 의 기능을 물려받는다. 하지만 코드 재사용만을 이유로 무조건 상속을 선택하면 결합도가 높아질 수 있다. 실제 설계에서는 is-a 관계가 자연스러운지 먼저 생각하는 것이 좋다. 10. 오버라이딩 자식 클래스는 부모 메소드를 다시 정의할 수 있다. class Notification { public void send() { System.out.println("기본 알림"); } } class EmailNotification extends Notification { @Override public void send() { System.out.println("이메일 알림"); } } @Override 는 필수는 아니지만 실수를 컴파일 단계에서 찾는 데 도움이 되므로 붙이는 것이 좋다. 11. 다형성 부모 타입 변수로 자식 객체를 가리킬 수 있다. Notification notification = new EmailNotification(); notification.send(); 실제 객체가 EmailNotification 이므로 오버라이딩된 메소드가 실행된다. 이 성질을 사용하면 여러 구현을 같은 방식으로 다룰 수 있다. Notification[] notifications = { new EmailNotification(), new SmsNotification() }; for (Notification notification : notifications) { notification.send(); } 호출하는 쪽은 구체적인 구현 세부사항보다 Notification 이라는 공통 타입에 의존할 수 있다. 12. super : 부모 클래스에 접근하기 부모 생성자를 호출할 수 있다. class User { final String id; User(String id) { this.id = id; } } class AdminUser extends User { String role; AdminUser(String id, String role) { super(id); this.role = role; } } super(...) 는 부모 생성자를 호출한다. super.method() 처럼 부모의 메소드에 접근할 수도 있다. 13. 참조와 객체 공유 참조 자료형 변수를 대입하면 객체 자체가 복사되는 것이 아니라 같은 객체를 가리키게 될 수 있다. User a = new User(); a.name = "A"; User b = a; b.name = "B"; System.out.println(a.name); // B a 와 b 가 같은 객체를 참조하기 때문이다. 이 개념은 이후 컬렉션, 메소드 전달, 불변 객체를 이해할 때 매우 중요하다. 14. final 의 세 가지 사용 변수 재대입할 수 없다. final int maxRetry = 3; 참조 변수에 final 을 붙이면 참조를 다른 객체로 바꿀 수 없다는 뜻 이지 객체 내부까지 자동으로 불변이 되는 것은 아니다. 메소드 자식 클래스에서 오버라이딩할 수 없다. public final void validate() { // ... } 클래스 상속할 수 없다. final class TokenParser { } 15. enum : 정해진 값의 집합 문자열로 상태를 관리하면 오타를 컴파일러가 잡지 못한다. String status = "PAID"; 정해진 값만 허용하려면 enum 이 안전하다. enum OrderStatus { CREATED, PAID, SHIPPED, CANCELED } OrderStatus status = OrderStatus.PAID; 16. 추상 클래스 공통 상태와 구현을 가지면서 일부 동작은 자식에게 맡기고 싶을 때 사용할 수 있다. abstract class PaymentProcessor { abstract boolean pay(int amount); public void log() { System.out.println("결제 처리"); } } class CardPaymentProcessor extends PaymentProcessor { @Override public boolean pay(int amount) { return amount > 0; } } 추상 클래스 자체는 직접 인스턴스화할 수 없다. 17. 인터페이스 인터페이스는 구현체가 따라야 할 동작의 계약을 표현하기 좋다. interface Storage { void save(String data); } class FileStorage implements Storage { @Override public void save(String data) { System.out.println("파일 저장: " + data); } } 인터페이스의 추상 메소드는 기본적으로 public 이므로 구현 메소드 역시 접근 범위를 더 좁힐 수 없다. 하나의 클래스는 여러 인터페이스를 구현할 수 있다. class CloudStorage implements Storage, AutoCloseable { @Override public void save(String data) { } @Override public void close() { } } 추상 클래스와 인터페이스, 무엇을 선택할까? 공통 상태와 구현을 공유하는 밀접한 계층 구조 가 필요하면 추상 클래스가 자연스럽고, 서로 다른 클래스들이 공통된 행동 계약 을 가져야 한다면 인터페이스가 유연하다. 둘 중 하나만 고집하기보다 역할에 맞게 함께 사용하는 경우가 많다. 핵심 정리 클래스는 데이터와 동작을 묶고, 객체는 클래스로부터 만들어진 인스턴스다. static 멤버는 객체가 아니라 클래스에 속한다. this 는 현재 객체, super 는 부모 부분에 접근할 때 사용한다. 생성자는 객체의 초기 상태를 설정한다. private 과 의미 있는 메소드를 사용하면 객체의 상태를 안전하게 관리할 수 있다. 상속과 오버라이딩을 통해 기능을 확장할 수 있다. 다형성은 부모 타입으로 여러 자식 구현을 동일하게 다루게 해준다. 추상 클래스와 인터페이스는 공통 규칙과 구현 구조를 설계할 때 사용한다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
Java 객체지향 핵심: 클래스, 상속, 다형성, 추상 클래스와 인터페이스. Java를 제대로 이해하려면 문법보다 객체지향 프로그래밍(OOP) 을 이해하는 것이 중요하다. 객체지향의 핵심은 데이터를 따로 두고 함수를 따로 두는 것이 아니라, 서로 관련 있는 상태와 행동을 하나의 객체로 묶어 프로그램을 구성하는 것 이다. 1. 클래스와 객체 클래스는 객체를 만들기 위한 설계도처럼 생각할 수 있다. class Product { String name; int price; } 객체는 new 로 생성한다. Product product = new Product(); product.name = "Keyboard"; product.price = 89000; 같은 클래스로 여러 객체를 만들 수 있고 각 객체는 서로 다른…
Открыть источник