접근제어자
| 접근제어자 | 클래스 내부 | 패키지 내부 | 상속한 클래스 | 전체공개 |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ✅ | ❌ |
| default | ✅ | ✅ | ❌ | ❌ |
| private | ✅ | ❌ | ❌ | ❌ |
접근제어자 활용
public class Person { // ✅ 외부에서 접근 가능
public String name; // ✅ 외부에서 접근 가능
private String secret; // ❌ 외부에서 접근 불가
public Person() {} // ✅ 외부에서 접근 가능
public void methodA() {} // ✅ 외부에서 접근 가능
private void methodB() {} // ❌ 외부에서 접근 불가
}
Person person = new Person(); // ✅ 접근가능 생성자가 public
person.name; // ✅ 접근가능 변수가 public
person.secret; // ❌ 접근불가능 변수가 private
person.methodA(); // ✅ 접근가능 메서드가 public
person.methodB(); // ❌ 접근불가능 메서드가 private
데이터 접근
private 로 보호하고 있습니다.게터 활용법
public class Person {
private String secret;
public String getSecret() {
return this.secret; // ✅ 객체의 secret 속성 반환
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
p1.secret; // ❌ 직접 접근 불가능
String newSecret = p1.getSecret(); // ✅ 게터를 활용해 접근가능
}
}
세터 활용법
public class Person {
private String secret;
public void setSecret(String secret) {
this.secret = secret; // ✅ secret 속성 설정 및 변경
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
p1.secret = "password"; // ❌ 직접접근, 변경 불가능
p1.setSecret("newPassword"); // ✅ 세터를 활용해 접근, 변경가능
}
}
무분별한 세터를 주의
객체의 일관성을 유지하기 힘들어지고 어디서 어떤 상태가 변경되는지 파악하기 힘들어진다. 즉, 코드의 가독성이 떨어질 수 있다.
extends 키워드를 사용해서 상속관계를 구현합니다.재사용성
public class Parent {
public String familyName = "스파르탄";
public int honor = 10;
public void introduceFamily() {
System.out.println("우리 " + this.familyName + " 가문은 대대로 명성을 이
}
}
class Child extends Parent { // ✅ extends 키워드 활용
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.honor); // ✅ 부모의 속성을 물려받아 사용
System.out.println(child.familyName); // ✅ 부모의 속성을 물려받아 사용
child.introduceFamily(); // ✅ 부모의 메서드를 물려받아 사용
}
}
super
public class Child extends Parent {
private String familyName = "gim"
public void superExample() {
System.out.println("우리 " + this.familyName + " 가문은 ...");
System.out.println("원래 가문의 이름은 " + super.familyName);
}
}
생성 순서
super() 는 항상 생성자의 첫 줄에 위치해야 합니다.public class Child extends Parent {
...
public Child() {
super(); // ✅ (1)부모클래스 생성자를 먼저 호출
// 추가로직은 여기에 작성
}
}
public class Parent {
public Parent() {} // ✅ 부모 생성자
}
확장
public class Child extends Parent {
...
// ✅ 부모에는 없지만 자식에만 있는 기능
public void showSocialMedia() {
System.out.println("우리 가문은 이제 SNS도 합니다. 팔로우 부탁드려요!");
}
}
public class Main {
public static void main(String[] args) {
...
child.showSocialMedia(); // ✅ 부모에는 없지만 자식에만 있는 기능
}
}
재정의
@Override 키워드를 붙이는 것을 권장합니다.public class Parent {
// 기존 기능
public void introduceFamily() {
System.out.println("우리 " + familyName + " 가문은 대대로 명성을 이어온 집안입니다.");
}
}
class Child extends Parent {
...
@Override
void introduceFamily() { // ✅ 자식클래스에서 재정의
System.out.println("오버라이드");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.introduceFamily(); // ✅ 출력 "오버라이드"
}
}
추상클래스
abstract 키워드로 클래스를 선언하면 추상클래스입니다.abstract 키워드로 메서드를 선언하면 자식클래스에서 강제로 구현해야합니다.abstract class Animal {
private String name; // ✅ 변수선언가능
abstract void eat(); // ⚠️ 추상메서드: 상속 받은 자식은 강제 구현해야합니다.
public void sleep() { // ✅ 자식클래스에서 재사용가능합니다.
System.out.println("쿨쿨");
}
}
public class Cat extends Animal {
@Override
void eat() {
System.out.println("냠냠"); // ⚠️ 자식클래스에서 강제 구현해야합니다.
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal(); // ❌ 추상클래스는 구현할 수 없습니다.
Cat cat = new Cat();
cat.eat(); // ⚠️ 강제 구현한 메서드 사용
cat.sleep(); // ✅ 부모클래스의 매서드 사용
}
}
추상클래스와 인터페이스 차이점
인터페이스 상속을 활용한 추상 계층 표현
public interface LifeForm {
void exist(); // ✅ 공통: 모든 생명체는 존재한다.
}
public interface Animal extends LifeForm {
void makeSound(); //✅ 공통: 모든 동물은 소리를 냅니다.
}
public class Cat implements Animal {
@Override
public void exist() {
System.out.println("고양이가 존재합니다.");
}
@Override
public void makeSound() {
System.out.println("야옹");
}
public void scratch() {
System.out.println("스크래치");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.exist();
cat.makeSound();
cat.scratch();
}
}
클래스 상속을 활용한 추상 계층 표현
public class LifeForm {
public void exist() {
System.out.println("존재합니다2"); // ✅ 공통: 모든 객체는 존재한다.
}
}
public class Animal extends LifeForm {
public void makeSound() {
System.out.println("소리를 냅니다2"); // ✅ 공통: 모든 생명체는 성장한다.
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹2");
}
public void scratch() {
System.out.println("스크래치!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.exist();
cat.makeSound();
cat.scratch();
}
}
인터페이스를 활용한 다형성 구현
public interface LifeForm {
void exist();
}
public interface Animal extends LifeForm {
void makeSound();
}
public class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("야옹");
}
@Override
public void exist() {
System.out.println("고양이가 존재합니다.");
}
public void scratch() {
System.out.println("스크래치!");
}
}
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("멍멍");
}
@Override
public void exist() {
System.out.println("강아지가 존재합니다.");
}
public void wag() {
System.out.println("꼬리 흔들");
}
}
public class Main {
public static void main(String[] args) {
// 다형성 활용
Animal animal = new Cat();
animal.exist(); // ✅
animal.makeSound(); // ✅
}
}
형변환
업캐스팅
public class Main {
public static void main(String[] args) {
// 다형성 활용
Animal animal = new Cat(); // ✅ 자동 형 변환
animal.exist(); // ✅
animal.makeSound(); // ✅
}
}
업캐스팅 주의사항
public class Main {
public static void main(String[] args) {
// 다형성 활용
Animal animal = new Cat(); // ✅ 자동 형 변환
animal.exist();
animal.makeSound();
animal.scratch(); // ❌ 사용 불가
}
}
다운캐스팅
public class Main {
public static void main(String[] args) {
// 다형성 활용
Animal animal = new Cat();
animal.exist();
animal.makeSound();
Cat cat = (Cat) animal; // ✅ 다운캐스팅(부모Animal -> 자식Cat)
cat.scratch(); // ✅ 자식 클래스의 기능 활용 가능
}
}
다운캐스팅 주의사항
public class Main {
public static void main(String[] args) {
// 다운 캐스팅
Animal dog = new Dog();
// 문법적으로 잘못된건 아니라서 에러가 발생하지 않습니다.
Cat cat1 = (Cat) dog; // ⚠️
cat1.scratch(); // ❌ 해당 라인이 실행할때만 에러 여부를 확인할 수 있습니다
}
}
그래서 다운캐스팅을 사용할 때 instanceof 를 활용해야합니다.
instanceof 는 객체가 특정 클래스나 인터페이스의 인스턴스인지 확인해주는 역할을 합니다.public class Main {
public static void main(String[] args) {
Animal animal2 = new Dog();
// ✅ 안전한 다운캐스팅(animal2 가 Cat 의 인스턴스 유형인지 확인합니다.)
if (animal2 instanceof Cat) {
Cat cat = (Cat) animal2;
cat.scratch();
} else {
System.out.println("객체가 고양이가 아닙니다.");
}
}
}
한 가지 타입으로 유연한 프로그래밍 처리
public class Main {
public static void main(String[] args) {
Animal[] animals = {new Cat(), new Dog()};
for (Animal animal : animals) {
animal.makeSound();
}
}
}
참조 타입을 Animal[] 배열로 선언하고 배열안에 Cat, Dog의 객체를 모두 담았습니다. animal[0]부터 순차실행합니다.
인터페이스
turnOn() → 기기를 켜는 기능turnOff() → 기기를 끄는 기능package com.example.interfacetest;
interface ElectronicDevices {
void turnOn();
void turnOff();
}
class Computer implements ElectronicDevices {
@Override
public void turnOn() {
System.out.println("Computer turned on");
}
@Override
public void turnOff() {
System.out.println("Computer turned off");
}
void calculation() {
System.out.println("Computer calculation");
}
}
class Television implements ElectronicDevices {
@Override
public void turnOn() {
System.out.println("Television turned on");
}
@Override
public void turnOff() {
System.out.println("Television turned off");
}
void watchingTv() {
System.out.println("Watching tv");
}
}
class AirConditioner implements ElectronicDevices {
@Override
public void turnOn() {
System.out.println("Air conditioner turned on");
}
@Override
public void turnOff(){
System.out.println("Air conditioner turned off");
}
void keppCool() {
System.out.println("Air conditioner kepp cool");
}
}
public class Interfacetest {
public static void main(String[] args) {
// 객체 선언
Computer computer = new Computer();
Television television = new Television();
AirConditioner airConditioner = new AirConditioner();
// 기능 출력
computer.turnOn();
computer.turnOff();
computer.calculation();
television.turnOn();
television.turnOff();
television.watchingTv();
airConditioner.turnOn();
airConditioner.turnOff();
airConditioner.keppCool();
}
}
위는 내가 실습한 코드이다. 코드는 이상없이 작동하지만 main클래스의 작성이 효율적이지 못하다. 이를 해결하기 위해 상위 형으로 객체를 선언하여 인터페이스 메소드를 작성하고, if문에 instnceof 를 사용하여 형 확인 후 새 변수에 할당, 고유의 메서드를 호출하면 아래와 같은 코드가 완성된다.
public class Interfacetest {
public static void main(String[] args) {
// 배열 선언
ElectronicDevices[] devices = {
new Computer(),
new Television(),
new AirConditioner()
};
// 공통 기능
for (ElectronicDevices device : devices) {
device.turnOn();
device.turnOff();
System.out.println();
}
// 고유 기능
for (ElectronicDevices device : devices) {
if (device instanceof Computer computer) {
computer.calculation();
} else if (device instanceof Television television) {
television.watchingTv();
} else if (device instanceof AirConditioner airConditioner) {
airConditioner.keppCool();
}
}
}
}
객체지향
package com.example.abstraction;
interface Book {
void storyTelling();
}
interface Novel extends Book {
void onlyText();
}
class HarryPotter implements Novel {
@Override
public void storyTelling() {
System.out.println("HarryPotter is story telling");
}
@Override
public void onlyText() {
System.out.println("HarryPotter is only text");
}
public void popular() {
System.out.println("HarryPotter is popular");
}
}
class Pilgrim implements Novel {
@Override
public void storyTelling() {
System.out.println("Pilgrim is story telling");
}
@Override
public void onlyText() {
System.out.println("Pilgrim is only text");
}
public void outOfDate() {
System.out.println("Pilgrim is out of date");
}
}
class Game {
public void fun() {
System.out.println("Game is fun");
}
}
class OnlineGame extends Game {
public void multiPlaying() {
System.out.println("OnlineGame is multi playing");
}
}
class LeagueOfLeagues extends OnlineGame {
@Override
public void multiPlaying() {
System.out.println("LeagueOfLeagues is multi playing");
}
public void rank() {
System.out.println("Silver");
}
}
public class abstraction {
public static void main(String[] args) {
Novel[] novels = {new HarryPotter(), new Pilgrim()};
LeagueOfLeagues lol = new LeagueOfLeagues();
lol.fun();
lol.multiPlaying();
lol.rank();
for(Novel novel : novels) {
novel.storyTelling();
novel.onlyText();
}
for(Novel novel : novels) {
if(novel instanceof HarryPotter) {
((HarryPotter) novel).popular();
} else if(novel instanceof Pilgrim) {
((Pilgrim) novel).outOfDate();
}
}
}
}
instanceof 를 사용추상화 실습을 진행하다 보니 다형성을 만족하는 코드를 완성하여 따로 실습을 진행하지 않음.
인터페이스
public 으로 선언하였지만 구현화 메서드에서는 default 로 선언하여 접근권한의 범위가 좁아졌습니다.public 으로 선언하여 해결하였습니다.예외란
예외의 구조와 종류
RuntimeException 을 상속받는 모든 예외를 UncheckedException 이라고 합니다.Exception 클래스를 직접 상속받는 모든 예외를 CheckedException 이라고합니다. RuntimeException과 RuntimeException 을 상속받은 예외는 제외합니다.예외처리
public class ExceptionPractice {
public void callCheckedException() {
// ✅ try-catch 로 예외 처리
try {
if (true) {
System.out.println("체크예외 발생");
throw new Exception();
}
} catch (Exception e) {
System.out.println("예외 처리");
}
}
}
public class ExceptionPractice {
public void callCheckedException() throws Exception { // ✅ throws 예외를 상위로 전파
if (true) {
System.out.println("체크예외 발생");
throw new Exception();
}
}
}
Optional이란
null 을 안전하게 다루게 해주는 객체입니다.Optional 사용 이유
null 을 반환할 수 있는 메서드의 값이 채워지지 않아 null 을 반환하면 NPE(NullPointerException) 가 발생합니다.NPE(NullPointerException) 은 런타임 예외이고 컴파일러가 잡아주지 못합니다.Optional 활용
null 이 반환될 가능성을 전달할 수 있습니다.isPresent() 같은 Optional API로 안전한 처리를 할 수 있습니다.isPresent 활용
Optional 내부의 값이 존재하면 true 아니면 false 를 반환합니다.import java.util.Optional;
public class Camp {
// 속성
private Student student;
// 생성자
// 기능
// ✅ null 이 반환될 수 있음을 명확하게 표시
public Optional<Student> getStudent() {
return Optional.ofNullable(student);
}
public void setStudent(Student student) {
this.student = student;
}
}
public class Main {
public static void main(String[] args) {
Camp camp = new Camp();
// isPresent() 활용시 true 를 반환하고 싶을때 활용
// Student newStudent = new Student();
// camp.setStudent(newStudent);
// Optional 객체 반환받음
Optional<Student> studentOptional = camp.getStudent();
// Optional 객체의 기능 활용
boolean flag = studentOptional.isPresent(); // false 반환
if (flag) {
// 존재할 경우
Student student = studentOptional.get(); // ✅ 안전하게 Student 객체 가져오기
String studentName = student.getName();
System.out.println("studentName = " + studentName);
} else {
// null 일 경우
System.out.println("학생이 없습니다.");
}
}
}
orElseGet 활용
import java.util.Optional;
public class Camp {
// 속성
private Student student;
// 생성자
// 기능
// ✅ null 이 반환될 수 있음을 명확하게 표시
public Optional<Student> getStudent() {
return Optional.ofNullable(student);
}
}
import java.util.Optional;
public class Main {
public static void main(String[] args) {
Camp camp = new Camp();
// ✅ Optional 객체의 기능 활용 (orElseGet 사용)
Student student = camp.getStudent()
.orElseGet(() -> new Student("미등록 학생"));
System.out.println("studentName = " + student.getName());
}
}