수업/└Java

[Ch08]자바 제어자 : final

onlyun 2022. 2. 2. 02:58

 

 

클래스 접근 제어자 : final, abstract

클래스 멤버 접근 제어자 : public, protected, default, private

(멤버에 static 붙으면 클래스 소속)

 

1. final

완성된 것이니까 바꾸지 말란 뜻.

 

(1) final 필드, 지역변수

처음에 지정된 값을 바꿀 수 없음. 상수.

값이 한 번 대입되면 나중에 초기화할 수 없다. 다른 값으로 변경 불가능.

상수 영역에 데이터 저장

 

(2) final 메소드

오버라이딩 불가

-

(3) final 클래스

상속 자체 불가

 

_예시)

package ee;
public class A {
	int a = 10;
	final int b = 5;	
	//public final 가능. 순서 바꿔도 됨.
	public void func() {
		System.out.println("final method");
	}
}

final class B extends A {
	int c = 5;
	public void func() { // remove final modifier
                         // final 함수라 오버라이딩 불가.
                         // func()의 final 지우면 괜찮음. 
	}
}

class C extends B{ //final class B는 상속 불가.
}

 


2. abstract

(1) 사용 이유

부모 타입으로 cry()를 호출하기 위해.

여러 사람이 협업할 때, 메소드 매개변수 입력 종류나 순서 등의 통일성을 갖추기 위해 사용.

 

(1) abstract 메소드 

미완성 메소드. 기능없이 이름만 정의된 것. 구현 x

구현 여부 : 중괄호({ }) - 중괄호 없으면 구현되지 않았다.

리턴 타입 앞에 입력. 기능을 정의하는 중괄호({ }) 생략

abstract void cry();
//추상 메서드. 구현 안 된 것.

void cry(){
  //구현된 메소드. 아무 것도 실행할 게 없어서 그렇지.
}

 

(2) abstract 클래스

내부에 추상 메소드(미완성 메소드)를 하나 이상 포함하거나 클래스 자체가 추상 클래스인 것.

객체 생성 불가(서브 클래스(자식 클래스)에서 객체 생성.)

 

_예시)

abstract class A {
    abstract void abc(); //추상 메소드
    void bcd(){...}  //일반 메소드
}

_ex)

package ff;
public abstract class Animal {
	public abstract void cry(String str); // 0.
	public abstract void print();

	public Animal() {	} //default
}

class Dog extends Animal{ // 2.
      //Add unimplemented method
	@Override
	public void cry(String str) {	}

	@Override
	public void print() {	} 
}

class Cat extends Animal{

	@Override
	public void cry(String str) {	}
	//Add unimplemented method

	@Override
	public void print() {	}
}

└0. abstract 메소드가 하나라도 있으면 클래스에서 abstract 추가해야 함.

└2. 부모클래스가 abstract면 무조건 구현해줘야 한다.

package ff;
public class Test01 {
	public static void main(String[] args) {
		//Animal animal = new Animal(); //객체 생성 불가. Animal class가 abstract라서 
	}
}

 

(3) 추상 클래스 구현

-오버라이딩 : 부모 클래스 메소드를 자식 클래스에서 재정의

-구현하기(implements) : 부모 클래스의 미완성메소드를 자식클래스에서 재정의

class Animal{
    void cry() {    }
}

class Bird extends Animal{
    void cry(){ System.out.println("짹짹"); }
}

class Cat extends Animal{
    void cry(){ System.out.println("야옹"); }
}

class Dog extends Animal{
    void cry(){ System.out.println("멍멍"); }
}

//추상클래스 및 메소드

추상 클래스의 추상 메소드를 상속받은 자식 클래스에서 반드시 재정의해줘야 함. 해줄 때까지 오류 뜸.

package aa;
public abstract class Animal {//추상 클래스
	abstract void cry(); //추상 메소드
	void func() {
		System.out.println("구현 메소드");
	}
}

class Dog extends Animal{
	//빨간 줄 쳐진 Dog에 커서 갖다대면 Add ~ 나오는데 그거 누르면 자동 완성해줌.
	@Override
	void cry() {
		System.out.println("멍멍");		
	}
}

class Cat extends Animal{
	@Override
	void cry() {
		System.out.println("멍멍");
	}
}

 

(4) 객체 생성하는 법

추상 클래스 자체로는 객체 생성 불가.

-1. 상속받은 자식클래스에서 객체 생성

객체를 많이 만들 때 유용. 정의해놓고 필요할 때마다 불러쓸 수 있어서.

package aa;
public abstract class Animal {//추상 클래스
	abstract void cry(); //추상 메소드
	void func() {
		System.out.println("구현 메소드");
	}
}

class Dog extends Animal{ //1
	//빨간 줄 쳐진 Dog에 커서 갖다대면 Add ~ 나오는데 그거 누르면 자동 완성해줌.
	@Override
	void cry() {
		System.out.println("멍멍");		
	}
}

class Cat extends Animal{
	@Override
	void cry() {
		System.out.println("멍멍");
	}
}

-2. 익명이너클래스 사용

일회용. 잠깐 쓰고 말 거라면.

package aa;
public class AnimalTest {
	public static void main(String[] args) {
		Animal a = new Dog();
		a.cry();
		a.func();
		
		Animal b = new Cat();
		b.cry();
		b.func();
		
		Animal c = new Animal(){ //추상 클래스 - 익명이너클래스
			@Override
			void cry() {
				System.out.println("왈왈");
			} 
		};
		
		Animal d = new Animal(){ //추상 클래스 - 익명이너클래스
			@Override
			void cry() {
				System.out.println("컹컹");
			} 
		};
		c.cry();
		d.cry();

추상 클래스 이름을 사용해 객체 만드는 것.

 

-1번과 2번의 장단점

1. 추상클래스 상속한 자식 클래스 : B로 객체를 여러 개 생성할 때 유용. 객체를 많이 만들 때 정의해놓고 불러쓸 수 있어서 편리.

2. 익명이너클래스 : 일회용. 잠깐 쓰고 말거면. 

package aa;
public class AnimalTest02 {
	public static void main(String[] args) {
        //1번(상속받은 자식클래스에서 생성)것은 객체 생성해서 호출하면 됨.
        Animal dog = new Dog();
		Animal cat = new Cat(); 
		
		//2번. 익명이너클래스를 여기서 쓰려면 구현부터 해줘야 한다.
		Animal c = new Animal(){ //추상 클래스 - 익명이너클래스
			@Override
			void cry() {
				System.out.println("왈왈");
			} 
		};
	}//main
}

 

 

추상클래스 :  객체는 생성하지 않지만 공통 특성을 모아두는 곳. ex) 자료형, 메소드 등등

└일반 클래스 : 객체 생성 O

└추상 클래스 : 클래스에 앱스트랙트 붙거나 앱스트랙트 메소드가 하나라도 포함된 클래스.


3. 인터페이스(interface)

*java에선 감을 못 잡고 스프링 가서 감 잡는 경우 많음.

*왜 쓰는가, 언제 쓰는가 이해.

*인터페이스 : ()나 추상 메소드로만 이루어진 것.

└협업할 때 틀을 잡아놓을 때. 어떤 자료형, 어떤 순서, 어떤 리턴 타입으로 쓸지 미리 정해놓는 것.

 

자체적으로 객체 생성 불가.

-모든 필드 : public static final

-모든 메소드 : public abstract

 (*디폴트 메소드 : public)

 

인터페이스엔 일반 필드 못 들어감. 그냥 적어도 public static final이 자동 붙음.

interface A {
    int a = 3;
    void abc();
    //생략시 자동 추가 ↓
    public static final int a = 3; //어디서든 접근. 객체 생성없이 사용. 상수
                                   //인터페이스 A로 바로 접근할 수 있는 상수
    public abstract void abc();
}

 

-인터페이스 기본

package bb;
public interface bb {
	//field
	public static final int a = 3;
	
	//method
	public abstract void abc();
}
package bb;
public class InterfaceTest {
	public static void main(String[] args) {
		//Interface 객체 생성 불가
//		A a = new A();
//		B b = new B();
		
		//static이라서 A이름으로 바로 접근 가능.
		System.out.println(A.a);
		System.out.println(A.a);
		//final이라 값 변경 불가.
//		A. a= 4;
	}
}

 

(2)인터페이스 상속
인터페이스는 구현 불가 → 클래스로 구현

 

-상속 시 implements 사용. 다중상속 가능(*클래스(일반/추상)는 다중상속 불가)

package bb;

public interface A {
	//field
	public static final int a = 3;
	
	//method
	public abstract void abc();
}

interface B{
	int b = 3;  //생략됐지만 public static final 붙어 있음.
	void bcd();
}

class C_class implements A{
	//A가 추상메소드를 포함하고 있기 때문에 메소드 구현할 때까지 오류
	@Override
	public void abc() {
		System.out.println("C_class");
	}
}

class A_B_Class implements A, B {
	//A가 추상메소드를 포함하고 있기 때문에 메소드 구현할 때까지 오류
	@Override
	public void bcd() {
		System.out.println("A Interface method");	
	}

	@Override
	public void abc() {
		System.out.println("B Interface method");	
	}
	
}
//-----------------------
class C{
	void func() {
		System.out.println("C의 메소드");
	}
}

//C를 상속받고 A, B를 구현하는?
class D extends C implements A, B{

	@Override
	public void bcd() {		
	}

	@Override
	public void abc() {		
	}
	
	//override
	void func() {
		super.func();
		System.out.println("D의 메소드");
	}	
}
package bb;

public class InterfaceTest {

	public static void main(String[] args) {
		//Interface 객체 생성 불가
//		A a = new A();
//		B b = new B();
		
		//static이라서 A이름으로 바로 접근 가능.
		System.out.println(A.a);
		System.out.println(A.a);
		//final이라 값 변경 불가.
//		A. a= 4;
		
		A c = new C_class();
		c.abc();
		
		B ab = new A_B_Class();
//		ab.abc();  //B일때 안 됨.
		ab.bcd();  //B일 때 안 됨
		
		D d = new D();
		d.abc();
		d.bcd();
		d.func();
	}
	
	//구현 불가. 구현하는 클래스 만들어 보기. A에서 //---- 이후
}

 

-인터페이스끼리 상속

클래스 extends 클래스 {    }
클래스 implements 인터페이스 {    }
인터페이스 extends 인터페이스 {    }

//불가
인터페이스 implements 클래스 {    }

 

-상속...

package bb;
public interface A {
	//field
	public static final int a = 3;
	
	//method
	public abstract void abc();
}

interface B extends A {
	int b = 3;  //생략됐지만 public static final 붙어 있음.
	void bcd();
}

//B를 구현한 클래스 생성
class BImpl implements B{
	//(B가 상속받은) A 메소드, B메소드 모두 구현하라.
	@Override
	public void abc() {
		System.out.println("method abc");		
	}

	@Override
	public void bcd() {
		System.out.println("method bcd");		
	}
	
}//BImple

 

-

interface ABExt extends A, B{
	void cde();
}

class ABExtImpl implements ABExt{

	@Override
	public void abc() {
		System.out.println("abc method");
		
	}

	@Override
	public void bcd() {
		System.out.println("bcd method");		
	}

	@Override
	public void cde() {
		System.out.println("cde method");		
	}
	
}

클래스는 2개 부모 불가. 인터페이스는 가능.

 

(4) 인터페이스 객체 생성 : 자체는 객체 생성 불가

-인터페이스를 일반 클래스로 상속 → 객체 생성

 

-익명 이너클래스 사용

컴퓨터 출력하기

-인터페이스 사용(cc)

필드 선언? 안 해도 되나?

package cc;

/*출력문
 My Desktop 컴퓨터입니다.
 cpu : i5, 16MB 메모리입니다
 
 My Notbook 컴퓨터입니다.
 SSD 256, cpu : i7, 메모리 : 16MB입니다.
 */
public interface Computer {
	void display(String name);
	void typing(int ssd, String cpu, int memory);
}

class DeskTop implements Computer{

	@Override
	public void display(String name) {
		System.out.println("My "+name+" 컴퓨터입니다.");
	}

	@Override
	public void typing(int ssd, String cpu, int memory) {
		System.out.println("모니터 크기 : 22인치");
		System.out.println("SSD : "+ssd+", CPU : "+cpu+", 메모리 :"+memory+"입니다.");
	}	
}//DeskTop

class Notbook implements Computer{

	@Override
	public void display(String name) {
		System.out.println("My "+name+" 컴퓨터입니다.");
	}

	@Override
	public void typing(int ssd, String cpu, int memory) {
		System.out.println("무게 : 300mg");
		System.out.println("SSD : "+ssd+", CPU : "+cpu+", 메모리 :"+memory+"입니다.");
	}
}

//
package cc;

public class ComputerTest {

	public static void main(String[] args) {
		Computer[] computers = new Computer[3];
		computers[0] = new DeskTop();
		computers[1] = new DeskTop();
		computers[2] = new Notbook();
		computers[0].display("DeskTop1");
		computers[0].typing(512, "i5", 16);		
	}
}

 

-추상 클래스 사용(dd)

필드 가질 수 있음.

package dd;

public abstract class Computer {
	int ssd;
	String cpu;
	int memory;
	String name;
	
	void display() { //method - 추상클래스에 구현된 메소드 가능.
		System.out.println("My "+name+" 입니다.");
	}
	
	public abstract void typing(); // 추상메소드
}

class Desktop extends Computer{
	int size;
	
	//생성자
	public Desktop(int ssd, String cpu, int memory, String name, int size) {
		this.ssd = ssd;
		this.cpu = cpu;
		this.memory = memory;
		this.name = name;
		this.size = size;
	}
	
	@Override
	public void typing() {
		System.out.println("Monitor size : "+size);
		System.out.println("SSD : "+ssd+", CPU : "+cpu+", MEMORY : "+memory+"입니다.");
	}
}

//Notbook
package dd;

public class ComputerTest {

	public static void main(String[] args) {
//		Computer computer = new Computer(); //abstract class라 객체 생성 불가
		Computer computer = new Desktop(512, "i7", 16, "DeskTop", 25);
		computer.display();
		computer.typing();
	}

}

둘 중 어느 것 사용? 인터페이스하는 함수를 모아두는 역할.

 

(5) 인터페이스 필요성

그래픽드라이버 매개변수 같은 것 하나 만들어 둚.

A그래픽드라이버, B드라이버 → 어플리케이션.

 

일을 나누어서 할 때, 틀을 만들어 두는 것.

 

 

(6) 디폴트 메소드

인터페이스를 상속하는 클래스. 이후에 인터페이스에 메소드 추가하면 인터페이스를 상속받는 클래스에서 구현해줘야 함. 해줄 때까지 오류 뜨게 되는데.

추가한 메소드를 디폴트 메소드라고 해주면 괜찮음.

그 디폴트 메소드는 다른 클래스에서 공통적으로 사용하는 것. 수정없이.

package ee;

public interface A {
	void abc();
	default void bcd() { //구현 안 해도 됨.
		System.out.println("method bcd");
	} //
}
class B implements A{

	@Override
	public void abc() {
		// TODO Auto-generated method stub
		System.out.println("B class의 abc");
	}
}

class C implements A{

	@Override
	public void abc() {
		// TODO Auto-generated method stub
		System.out.println("C class의 abc");
	}
}
package ee;
public class DefaultTest {
	public static void main(String[] args) {
		//인터페이스 A를 가지고 B, C 클래스의 객체 만들 수 있음.
		A b = new B();
		A c = new C();
		
		b.abc();
		b.bcd();
		c.abc();
		c.bcd();
	}
}

-

디폴트 메소드인데 굳이 구현해보겠다.

package ee;

public interface A {
	void abc();
	default void bcd() { //구현 안 해도 됨.
		System.out.println("method bcd");
	} 
}
class B implements A{

	@Override
	public void abc() {
		// TODO Auto-generated method stub
		System.out.println("B class의 abc");
	}
	
	//default method 굳이 구현하겠다.
	public void bcd() {
		//A의 default method랑 여기서 수정한 것 같이 출력.
		A.super.bcd();  //인터페이스는 부모가 여러 개일 수 있어서 인퍼테이스 지정해줘야 함.
		System.out.println("B 클래스에서 재정의한 bcd");
	}
}

class C implements A{

	@Override
	public void abc() {
		// TODO Auto-generated method stub
		System.out.println("C class의 abc");
	}
	public void bcd() {
		//A의 default method랑 여기서 수정한 것 같이 출력.
		A.super.bcd();  //인터페이스는 부모가 여러 개일 수 있어서 인퍼테이스 지정해줘야 함.
		System.out.println("C 클래스에서 재정의한 bcd");
		}
	
}
package ee;
public class DefaultTest {
	public static void main(String[] args) {
		//인터페이스 A를 가지고 B, C 클래스의 객체 만들 수 있음.
		A b = new B();
		A c = new C();
		
		b.abc();
		b.bcd();
		c.abc();
		c.bcd();
	}
}

 

(7) 정적 메소드 static

완성형으로 만들 수 있음.

package ee;

public interface A {
	void abc();
	default void bcd() { //구현 안 해도 됨.
		System.out.println("method bcd");
	}
	//-----------
	static void cde() {
		System.out.println("cde");
	}
}

A라는 인터페이스 소속 메소드.

접근할 때 바로 접근할 수 있음. 객체 생성 없이

package ee;
public class DefaultTest {
	public static void main(String[] args) {
		//인터페이스 A를 가지고 B, C 클래스의 객체 만들 수 있음.
		A b = new B();
		A c = new C();		
		b.abc();
		b.bcd();
		c.abc();
		c.bcd();		
		//------
		A.cde();  //static void cde()
	}
}