1. 접근 제어자
자유와 규제
너무 자유로우면 소통에 어려움을 겪게 되기에 적정선의 규제가 필요하다.
접근 제어자, 추상 클래스, final, 인터페이스 등이 규제에 해당한다.
문법
접근 제어자
객체 안에 멤버들(변수, 메소드)을 사용할 때의 권한
// public이 접근 제어자
public String x(){
return "public";
}
// private이 접근 제어자
private String y(){
return "private";
}
사용이유
보안이나 로직상의 이유로 사용자, 즉 외부에서 접근 규제하여 오작동 발생을 줄일 수 있기 때문이다.
세밀한 제어
public | protected | default | private | |
동일 패키지 동일 클래스 |
O | O | O | O |
동일 패키지 상속 관계 |
O | O | O | X |
동일 패키지 상속X |
O | O | O | X |
다른 패키지 상속 |
O | O | X | X |
다른 패키지 상속X |
O | X | X | X |
public과 private를 기본적으로 알고 추후 protected와 default를 활용해도 된다.
- 같은 클래스일 경우 접근 제어자와 무관하게 사용 가능
- 같은 패키지일 경우 private만 사용 불가
- 다른 패키지일 경우 public만 가능하지만 다른 패키지의 서브클래스를 상속한다면 protected도 가능
- public은 어디서든 접근 가능, private는 같은 클래스일 때만 사용 가능
클래스 접근 제어자
- default는 접근 제어자를 붙이지 않은 경우이며 같은 패키지에서만 사용 가능
- public은 다른 패키지의 클래스에서도 사용 가능
주의할 점은 public 클래스명과 파일명이 같아야 한다는 점이다.
PublicNameDemo.java
package org.opentutorials.javatutorials.accessmodifier.inner;
//public class PublicName {} <ㅡ 에러 발생
public class PublicNameDemo {}
하나의 소스 코드에는 하나의 public 클래스만 존재할 수 있기 때문이다.
2. abstract
문법
abstract는 한국어로 '추상'이라는 뜻입니다.
추상 클래스를 사용하기 위해서는 반드시 상속하도록 규제한다고 볼 수 있습니다.
abstract class A{
public abstract int b();
public void d(){
System.out.println("world");
}
}
// 추상 클래스 A를 오버라이딩
class B extends A{
public int b(){return 1;}
}
public class AbstractDemo {
public static void main(String[] args) {
// A obj = new A(); <ㅡ 에러 발생
B obj = new B();
System.out.println(obj.b());
}
}
- abstract 메소드는 본체, 즉 중괄호 안에 내용이 없어야 한다.
- 멤버 중 하나라도 abstract라면 해당 클래스는 abstract이다.
사용 이유
공통적인 부분은 추상 클래스를 활용하여 대략적으로 정의해놓고
상황에 따라 변경되는 유동적인 부분은 사용할 때마다 바꿔 사용할 수 있게 하기 위함이다.
// ex)
abstract class robot{
public void attack(){}
}
class punchRobot extends robot{
public void attack(){
System.out.print("punch");
}
}
class missileRobot entends robot{
public void attack(){
System.out.print("missile");
}
}
디자인 패턴
추상을 활용하여 템플릿을 정해놓고 하위 클래스에게 책임을 전가하는 패턴을 template method pattern이라고 한다.
이처럼 반복되는 패턴을 정리한 것이 디자인 패턴이다.
장점
- 좋은 설계를 단기간에 학습할 수 있다.
- 소통에 도움이 된다.
3. final
상속이나 변경을 금지하는 규제
첫 정의 후 변경이 불가능하다.
// final 필드: 값 변경 불가
static final double PI = 3.14;
// final 메소드: 상속 불가
class A{
final void a(){}
}
// final 클래스: 상속 불가
final class b{
final void b(){}
}
4. 인터페이스
문법과 개념
인터페이스를 사용하는 객체는 반드시 인터페이스의 메소드들을 구현해야 하는 규제
interface I{
public void z();
}
class A implements I{
public void z(){}
}
위 코드에서 클래스 A는 인터페이스 I를 "구현"하고 있다.
사용 이유
인터페이스는 클래스의 명세서 역할을 한다.
인터페이스를 사용하는 클래스는 명세서를 구체화해서 사용하면 된다.
// 인터페이스
public interface Calculatable {
public void setOprands(int first, int second, int third) ;
public int sum();
public int avg();
}
// 인터페이스를 구현한 클래스
class CalculatorDummy implements Calculatable{
public void setOprands(int first, int second, int third){
}
public int sum(){
return 60;
}
public int avg(){
return 20;
}
}
메인 클래스
public class CalculatorConsumer {
public static void main(String[] args) {
CalculatorDummy c = new CalculatorDummy();
c.setOprands(10, 20, 30);
System.out.println(c.sum()+c.avg());
}
}
규칙
하나의 클래스가 여러 개의 인터페이스를 구현할 수 있다.
interface I1{
public void x();
}
interface I2{
public void z();
}
class A implements I1, I2{
public void x(){}
public void z(){}
}
인터페이스도 상속이 된다.
interface I3{
public void x();
}
interface I4 extends I3{
public void z();
}
class B implements I4{
public void x(){}
public void z(){}
}
인터페이스의 멤버는 반드시 public이다.
public이어야 인터페이스를 사용할 다른 클래스에서 접근이 가능하다.
abstract VS interface
- 추상 클래스는 일반적인 클래스이기에 구체적인 로직이나 상태를 가질 수 있다.
- 인터페이스는 구체적인 로직이나 상태를 가질 수 없다.
5. 다형성
메소드 오버로딩과 다형성
다형성
하나의 메소드나 클래스가 있을 때 이것들이 다양한 방법으로 동작하는 것
ex) overloading
클래스와 다형성
class A{
public String x(){return "x";}
}
class B extends A{
public String y(){return "y";}
}
public class PolymorphismDemo1 {
public static void main(String[] args) {
A obj = new B();
obj.x();
obj.y(); // 에러 발생
}
}
위 코드는 obj 인스턴스의 타입인 A 클래스의 y메소드는 존재하지 않기 때문에 에러가 발생한다.
자식 클래스인 B 클래스에 x메소드를 오버로딩할 경우 에러가 발생하지 않는다.
- 부모 타입으로 자식 인스턴스를 생성했을 떼 부모 메소드를 자식 메소드가 오버로딩할 경우 자식 메소드의 우선 순위가 더 높다.
- 타입이 부모이기 때문에 자식 클래스에만 있는 메소드를 사용할 수 없다.
인터페이스와 다형성
public class CalculatorDemo {
public static void execute(Calculator cal){
System.out.println("실행결과");
cal.run();
}
public static void main(String[] args) {
Calculator c1 = new CalculatorDecoPlus();
c1.setOprands(10, 20);
Calculator c2 = new CalculatorDecoMinus();
c2.setOprands(10, 20);
execute(c1);
execute(c2);
}
}
- c1과 c2는 같은 데이터 타입을 가지고 있지만 인스턴스가 다르기에 다른 기능을 갖고 있다.
- 인스턴스와 동일한 데이터 타입으로 생성을 했다면 execute 메소드를 타입별로 만들어야 하므로 코드의 중복이 생기게 된다.
interface I2{
public String A();
}
interface I3{
public String B();
}
class D implements I2, I3{
public String A(){
return "A";
}
public String B(){
return "B";
}
}
public class PolymorphismDemo3 {
public static void main(String[] args) {
D obj = new D();
I2 objI2 = new D();
I3 objI3 = new D();
obj.A();
obj.B();
objI2.A();
//objI2.B();
//objI3.A();
objI3.B();
}
}
- 어떤 클래스가 어떤 인터페이스를 구현하고 있다면 해당 클래스의 타입은 구현하고 있는 인터페이스 타입이 될 수 있다.
- objI2의 타입은 인터페이스 I이기 때문에 I에게 존재하지 않는 메소드여서 에러가 발생한다. ojbI3.A() 의 에러 발생 이유 또한 이와 같다.
'JAVA > [생활코딩] 자바' 카테고리의 다른 글
[생활코딩] 참조, 제네릭, collections framework (0) | 2023.05.07 |
---|---|
[생활코딩] 예외 처리, Object 클래스, 상수와 enum (0) | 2023.04.30 |
[생활코딩] overriding, overloading, 클래스 패스, 패키지, API와 API 문서 보는 법 (0) | 2023.04.09 |
[생활코딩] 자바 유효범위, 초기화와 생성자, 상속, 상속과 생성자 (0) | 2023.03.31 |
[생활코딩] 자바 객체지향프로그래밍, 클래스와 인스턴스 그리고 객체, 클래스 멤버와 인스턴스 멤버 (0) | 2023.03.31 |