클래스 멤버

 

인스턴스 상태인 변수의 값이 인스턴스마다 다른값을 가질 수 있다는 점은

한개의 클래스를 여러개의 인스턴스로 만들어서 사용할 수 있다는점에서 좋은기능이다.

 

그런데 경우에 따라 인스턴스가 모두 같은 값을 공유하게 하고싶을때는?

아래코드는 각 인스턴스마다 원주율 값을 가질 필요가 없어서 원주율 PI 를 클래스의 소속인 멤버로 만들었다

class Calculator {
    static double PI = 3.14;
    int left, right;
 
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void sum() {
        System.out.println(this.left + this.right);
    }
 
    public void avg() {
        System.out.println((this.left + this.right) / 2);
    }
}
 
public class CalculatorDemo1 {
 
    public static void main(String[] args) {
 
        Calculator c1 = new Calculator();
        System.out.println(c1.PI);
 
        Calculator c2 = new Calculator();
        System.out.println(c2.PI);
 
        System.out.println(Calculator.PI);
 
    }
 
}

static을 멤버(변수, 메서드)앞에 붙이면 클래스의 멤버가 된다.

클래스 소속의 변수

static double PI = 3.14;

 

 

- 클래스 변수에 접근하는 방법 2가지

1. 인스턴스를 통해서 PI 접근

System.out.println(c1.PI);

2. 클래스를 통해서 PI 접근

이 경우는 인스턴스를 생성할 필요 없이 Calculator클래스에 직접 접근

System.out.println(Calculator.PI);

 

정리해서 클래스 변수의 용도는

  • 인스턴스에 따라서 변하지 않은 값이 필요한 경우
  • 인스턴스를 생성할 필요가 없는 값을 클래스에 저장하고싶은 경우
  • 값의 변경사항을 모든 인스턴스가 공유해야하는 경우
반응형
LIST

 

자바는 다중 상속이 불가능하지만 인터페이스는 다중 상속 가능하다

 

추상 클래스와 메소드

자식 클래스에 반드시 오버라이딩을 해야 사용할 수 있는 메소드이다

추상메소드를 선언하여 자식 클래스에서 상속받아 필요한 부분을 재정의하여 사용한다

abstract class 클래스이름{
      abstract 반환타입 메소드이름();
}

 

 

선언부만 존재하는 추상메소드를 일단 선언

abstract class Time{
	abstract void Am(); //추상메소드의 선언부 (구현부가 없는 메서드이다.)
}

 

자식 클래스에 추상메소드를 오버라이딩 하고 구현부를 작성함으로써 재정의를 한다.

class Room extends Time{ //Time 추상 클래스로부터 직접적으로 상속받음
	public void Am() { //추상메소드 오버라이딩해야 인스턴스 생성가능
		System.out.println("오전 10시 청소 시작"); //재정의 (추상메소드의 구현부)
	}
	public void vacuum() {
		System.out.println("그릇 설거지");
	}
}
public class Polymorphism2 {

	public static void main(String[] args) {
		//추상 클래스는 인스턴스 생성불가
		Room room = new Room(); //오버라이딩 후 인스턴스 생성가능
		room.Am();
		room.vacuum();
	}
}
오전 10시 청소 시작
그릇 설거지

 

추상 클래스의 특징

  • 추상 클래스는 하나 이상의 추상 메소드를 포함하며
  • 추상메소드, 일반메소드 모두 포함시킬 수 있다.
  • 대신 추상 메소드는 구현부가 없는 메소드이며 일반 메소드는 구현부가 있는 메소드이다. 
  • 인터페이스와의 차이점은 미리 구현된 메소드를 가지고 있느냐의 차이
  • 인터페이스와의 공통점은 직접 객체를 생성할 수 없다(new연산자 사용X) 
  • 왜냐면 아직 정의되지 않은 추상메소드가 존재하기 때문이다.

 

 

 

다중상속

자바는 하나의 자식클래스가 여러 부모 클래스를 동시에 상속받는 다중 상속을 지원하지 않는다.

클래스는 단일상속만!!

하지만 다중상속이 불가능하나 아래처럼 다중 상속을 대체할 유연한 상속 구조를 구성할 수 있다.

 

자식 Room 클래스는 Time 추상메소드를 직접적으로 상속 받고있어서 

ParentB 클래스의 메소드를 상속을 못받으니 ParentB 인스턴스를 생성하여 해당 메소드의 기능을 사용한다

package com.example.demo;

//추상클래스와 추상메소드 (다중상속 불가능)
abstract class Time{
	abstract void Am(); //추상메소드의 선언부 (구현부가 없는 메서드이다.)
}

class Room extends Time{ //Time 추상 클래스로부터 직접적으로 상속받음
	public void Am() { //추상메소드 오버라이딩해야 인스턴스 생성가능
		System.out.println("오전 10시 청소 시작"); //재정의 (추상메소드의 구현부)
	}
	public void vacuum() {
		System.out.println("그릇 설거지");
	}
}

class ParentB{
	void methodParentB() {
		System.out.println("메소드 B");
	}
}

public class Polymorphism2 {

	public static void main(String[] args) {
		//추상 클래스는 인스턴스 생성불가
		Room room = new Room();
		room.Am();
		room.vacuum();
		System.out.println();
		
		// 자바는 이미 상속을 받고 있는데 다른 클래스 상속을 받을 수 없다 (다중상속이 불가능)
		// Room 클래스는 이미 Time 추상 클래스를 이미 상속받고 있지만 다른 클래스의 메소드를 사용하고 싶을때  
		ParentB pB = new ParentB(); // 인스턴스 생성하여 메소드 호출하기
		pB.methodParentB();
	}
}
오전 10시 청소 시작
그릇 설거지
메소드 B

 

 

 

 

인터페이스

-인터페이스는 다중상속이 가능하다

클래스를 통한 다중 상속은 메소드 출처의 모호성 등의 여러가지 문제때문에 지원하지 않지만 다중상속의 이점이 크기 때문에 인터페이스를 통해 다중 상속을 지원한다

 

-인터페이스 다른 클래스를 작성할때 기본이 되는 틀을 제공하면서 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미

 

추상클래스 - 추상메소드, 생성자, 필드, 일반 메소드 포함

인터페이스 - 추상메소드, 상수만 포함

 

 

인터페이스 선언 (공통적으로 적용)

접근제어자 interface 인터페이스이름 {
   public static final 타입 상수이름 = 값;
   
   public abstract void 메소드이름();
}

인터페이스의 모든 필드는  public static final

인터페이스의 모든 메소드는 추상 메소드 public abstract 

* 접근 제어자는(public) 자바 컴파일러가 자동으로 추가해줘서 생략해도 기본적으로 적용이 된다.

 

 

이렇게 모든 메소드는 선언만 됐을뿐 어떤 기능을 할지 정의되지 않은 상태이기 때문에 '추상 메소드' 라고 부른다.

그래서 abstract 를 적지 않아도 모두 추상 메소드로 간주된다.

그렇기 때문에 인터페이스는 추상 클래스보다 한단계 더 추상화 된 클래스라고 생각하면된다.

 

 

인터페이스는 직접 인스턴스 생성(new 연산자 사용X)은 못하고 인터페이스에 포함하고 있는 추상 메소드(abstract)를 구현해줄 클래스를 작성해야한다.

class 클래스이름 implements 인터페이스 이름 { }

 

▼ 인터페이스의 예시

//인터페이스
interface Cleaning{
	public abstract void wipe();
}

//인터페이스 상속받는 클래스
class Room implements Cleaning{
	public void wipe(){
    	 System.out.println("바닥 닦기");
    }
}

class Kitchen implements Cleaning{
    public void wipe(){
     System.out.println("냉장고 닦기");
    }
}

public class Poltmorphism03{
	public static void main(String[] args){
    	Room r = new Room();
        Kitchen k = new Kitchen();
        
        r.wipe();
        k.wipe();
    }
}

 

 

 

다중 상속 = 여러 인터페이스를 상속받을 수 있다

class 클래스이름 implements 인터페이스 이름1, 인터페이스 이름2 { }

 

 

 

먼저 클래스를 이용한 인터페이스의 다중 상속의 문제점을 예시를 살펴보자

 

Kitchen, LivingRoom 두개의 클래스가 Time()  클래스를 상속을 받고 Am()메소드를 오버라이딩을 했다

class Time{
	public void Am() {
		System.out.println("오전 청소");
	}
}

class Kitchen extends Time{
	public void Am() {
		System.out.println("오전 10시 청소 시작");
	}
	public void dishWash() {
		System.out.println("그릇 설거지");
	}
}

class LivingRoom extends Time{
	public void Am() {
		System.out.println("오전 8시 시작");
	}
	public void floorClean() {
		System.out.println("빗자루 쓸기");
	}
}

 

 

Person 클래스가 Kitchen,LivingRoom 클래스를 상속을 받을때! 문제가 되는데

class Person extends Kitchen,LivingRoom{}
public class School{
	
	public static void main(String[] args) {
		Person person = new Person();
		person.Am();
        //Kitchen,LivingRoom 둘 중 어떤 Am() 메소드인지 구분못함 = 이런 이유로 자바에서는 다중 상속을 지원하지 않는다.
	}
}

 

 

▼ 전체코드

//자바가 다중 상속을 지원하지 않는 이유
class Time{
	public void Am() {
		System.out.println("오전 청소");
	}
}

class Kitchen extends Time{
	public void Am() {
		System.out.println("오전 10시 청소 시작");
	}
	public void dishWash() {
		System.out.println("그릇 설거지");
	}
}

class LivingRoom extends Time{
	public void Am() {
		System.out.println("오전 8시 시작");
	}
	public void floorClean() {
		System.out.println("빗자루 쓸기");
	}
}

// 두개의 클래스를 동시에 상속받는다면?
class Person extends Kitchen,LivingRoom{}

public class School{
	
	public static void main(String[] args) {
		Person person = new Person();
		person.Am();//Kitchen,LivingRoom 둘 중 어떤 Am() 메소드인지 구분못함 = 이런 이유로 자바에서는 다중 상속을 지원하지 않는다.
	}
}

 

Person person = new Person();

person.Am() 

 

Am() 메소드를 호출했을때 Am() 메소드는 어떤 클래스(Kitchen, LivingRoom)에서 상속받은 메소드인지 구분을 못하는 모호성을 가지게 돼서 클래스를 이용한 다중상속을 지원하지 않는다.

이럴때 인터페이스를 사용하여 다중상속을 한다.

 

 

 

▼ 인터페이스의 다중 상속 예시

 

Person, Person2 클래스는 Kitchen, LivingRoom 두개의 인터페이스를 다중 상속받아 구현하고 있는 코드

 

Time 인터페이스를 상속하는 Kitchen, LivingRoom 두개의 인터페이스를

class Person implements Kitchen,LivingRoom {}

class Person2 implements Kitchen,LivingRoom {}

Person, Person2 클래스가 상속받아 오버라이딩(재정의)를 해준다

package com.example.demo;

//인터페이스 (다중상속)
interface Time{
	public abstract void Am();
}

interface Kitchen extends Time{
	public abstract void Am();
	public abstract void dishWash();
}

interface LivingRoom extends Time{
	public abstract void Am();
	public abstract void floorClean();
}

//두개의 인터페이스(Kitchen,LivingRoom) 다중 상속
class Person implements Kitchen,LivingRoom{  //class 클래스 이름 implements 인스턴스 이름
	public void Am() {			// 오버라이딩
		System.out.println("오전 10시 시작");
	}
	public void dishWash() {	// 오버라이딩
		System.out.println("그릇 설거지");
	}
	
	public void floorClean() {	// 오버라이딩
		System.out.println("거실 청소기 돌리기");
	}
}

class Person2 implements Kitchen,LivingRoom{
	public void Am() {				// 오버라이딩
		System.out.println("오전 9시 시작");
	}
	public void dishWash() {		// 오버라이딩
		System.out.println("설거지 물기 닦기");
	}
	
	public void floorClean() {		// 오버라이딩
		System.out.println("방 청소기 돌리기");
	}
}

public class Inferface {

	public static void main(String[] args) {
		Person p1 = new Person();
		Person2 p2 = new Person2();
		
		p1.Am();
		p1.dishWash();
		p1.floorClean();
		
		System.out.println();
		
		p2.Am();
		p2.dishWash();
		p2.floorClean();
	}
}
오전 10시 시작
그릇 설거지
거실 청소기 돌리기

오전 9시 시작
설거지 물기 닦기
방 청소기 돌리기

 

따라서 인터페이스는 구현된 메소드가 없기 때문에 다중상속이 가능하다

반대로 추상클래스는 이미 구현된 메소드가 있기때문에 다중상속이 불가능하

 

 


  • 이렇게 인터페이스와 추상클래스를 미리 선언해두면 개발시 기능 구현에만 집중할 수 있어 개발자가 비즈니스 로직에 집중할 수 있다는 장점이있다.
  • 공통의 인터페이스와 추상클래스를 선언해두면 선언과 구현을 구분할 수 있다.
  • 인터페이스를 선언할 때 공통적인 기능을 미리 구현해두면 좋다
  • 미리 구현해두면 좋은 기능은 일반 메서드를 생성하는데 추상클래스를 이용하고
  • 그렇지 않은 메서드는 추상메서드에 남겨둔다.

 

 

 

 

 

 extends , implements 의 차이

 

둘 다 부모의 상속을 받지만

  • extends는 오버라이딩(재정의)를 하지않아도 부모에 구현된 메소드나 변수까지 사용가능
    • interface가 interface 상속을 받을때
    • class가 class 상속을 받을때 
    • extends는 단일 상속만 가능
  • implemets 는 부모의 메소드를 오버라이딩(재정의) 해야한다
    • class가 interface 상속을 받을때
    •  다중 상속도 가능 > implemts 인터페이스 이름1, 인터페이스 이름2 {}
반응형
LIST

'공부' 카테고리의 다른 글

메이븐 프로젝트 설정, JPA 설정  (0) 2023.07.31
클래스 멤버  (0) 2023.07.20
다형성  (0) 2023.06.22
접근 제한자 | 지역변수, 전역변수와 static  (0) 2023.06.22
생성자  (0) 2023.06.20

다형성은 객체지향에서 아주 중요한 개념이다

 

부모-자식 상속 관계인 클래스에서 상위 클래스가 하위 클래스들을 동일한 메세지로 다르게 동작시키는

객체지향 원리이다

 

 

 

Man , Woman 클래스는 People클래스를 상속받아 resultPerson() 함수를 호출할 수 있다.

package com.example.demo;

//다형성
class People{
	public void resultPerson() {
		System.out.println("나는 사람이다.");
	}
}

//Person 클래스 상속받음
class Man extends People{} 
class Woman extends People{}

public class Polymorphism {
	
	public static void main(String[] args) {
		
		Man man = new Man();
		Woman woman = new Woman();
		
		man.resultPerson();
		woman.resultPerson();
		
	}
}

 

결과

나는 사람이다.
나는 사람이다.

 

 

 

  • people은 > man, woman 이다 (X)
  • man, woman은 > people이다 (O)

man, woman은 people이기 때문에 people 자료형을 받을 있다. 아래처럼 (이것이 다형성의 개념)

People p = new Man();
p.resultPerson();

p = new Woman();
p.resultPerson();

 

p라는 참조변수는 Man,Woman 객체를 가르키는 변수이며 객체의 주소값을 저장한다. 

헷갈리는게 객체를 저장하는 변수가 아니다!

 

첫번째 줄인 People p = new Man(); 의 p는 Man객체를 가르키고

두번째인 p = new Woman(); 의 p는 이제 Woman객체를 가르킨다 (p 참조변수는 한개의 객체 주소만 가질 수 있음)

그리고 new 연산자는 힙 메모리에 로드하기 때문에 힙 메모리에는 두 객체의 주소값이 저장된다.

 

따라서 메모리상에서는 Man, Woman 객체가 저장되며 p변수는 마지막에 p = new Woman(); 로 인해 Woman 객체를 가르키는 주소값을 가지고있다.

 

 

 

그리고 man, woman 은 resultPerson()을 물려받았기에 오버라이딩이 가능하다.

man, woman 각 클래스에 맞게 오버라이딩을 해서 다르게 동작하도록 해보자

//Person 클래스 상속받음
class Man extends People{ 
	public void resultPerson() { //resultPerson() 오버라이딩해서 상위 클래스 동작을 다르게 변경하기 위해  
		super.resultPerson();  //상위클래스(People)의 함수실행 
		System.out.println("그리고 난 남자"); //추가 동작
	}
} 
class Woman extends People{
	public void resultPerson() {
		super.resultPerson();
		System.out.println("그리고 난 여자");
	}
}
나는 사람이다.
그리고 난 남자

나는 사람이다.
그리고 난 여자

 

전체 코드

package com.example.demo;

//다형성
class People{
	public void resultPerson() {
		System.out.println("나는 사람이다.");
	}
}

//Person 클래스 상속받음
class Man extends People{ 
	public void resultPerson() { //resultPerson() 오버라이딩해서 상위 클래스 동작을 다르게 변경하기 위해  
		super.resultPerson();  //상위클래스(People)의 함수실행 
		System.out.println("그리고 난 남자"); //추가 동작
	}
} 
class Woman extends People{
	public void resultPerson() {
		super.resultPerson();
		System.out.println("그리고 난 여자");
	}
}

public class Polymorphism {
	
	public static void main(String[] args) {
		
		People p = new Man();
		p.resultPerson(); //부모 클래스 함수 물려받음 = 오버라이딩
		
        System.out.println();
        
		p = new Woman();
		p.resultPerson();
	}
}

 

 

 

만약 man, woman 클래스에서 단독으로 메소드를 추가해보자

class Man extends People{ 
	public void resultPerson() { //resultPerson() 오버라이딩해서 상위 클래스 동작을 다르게 변경하기 위해  
		super.resultPerson();  //상위클래스(People)의 함수실행 
		System.out.println("그리고 난 남자"); //추가 동작
	}
	public void army() {
		System.out.println("군대를 간다.");
	}
} 
class Woman extends People{
	public void resultPerson() {
		super.resultPerson();
		System.out.println("그리고 난 여자");
	}
	public void makeup() {
		System.out.println("화장을 한다.");
	}
}

 

army() , makeup() 함수는 People 클래스에서 없는 메소드이며 상속받은 함수가 아니기때문에

p.army() 할 수가 없다 그래서 amy() 메소드가 있는 객체로 캐스팅(형변환)해서 메소드를 사용한다.

 

따라서 형변환을 Man으로 해서 Man 클래스의 메소드를 사용할 수 있다.

((Man)p).army();

 

People p = new Man(); 을 했을때 new가 동적 메모리에 할당하는 역할을 한다

Man은 heap 메모리에 로드가 되고 p는 형변환을 Man으로 가능하다

public class Polymorphism {
	
	public static void main(String[] args) {
		
		//남자는 사람이다(o)
		People p = new Man();
		p.resultPerson(); //부모 클래스 함수 물려받음 = 오버라이딩
		((Man)p).army();
		
		System.out.println();
		
		p = new Woman();
		p.resultPerson();
		((Woman)p).makeup();
	}
}
나는 사람이다.
그리고 난 남자
군대를 간다.

나는 사람이다.
그리고 난 여자
화장을 한다.

 

 

다형성은 대표적으로 메소드의 매개변수에 상속하는 클래스를 받아서 사용한다

func메소드의 매개변수 people은 People클래스의 객체를 전달할 수 있다.

따라서 func 메소드는 People 클래스를 직접 상속한 Man, Woman 객체를 전달한다 이것이 다형성의 특징이다.

 

즉 Man, Woman 은 하위 클래스로 func 메소드의 매개변수로 전달하며 동일한 메소드를 이용해 다양한 객체를 다룰 수 있고 객체의 실제 타입에 따라 적절한 동작을 수행 할 수 있다는게 다형성의 특징!

 

public class Polymorphism {
	public static void func(People people) {
		people.resultPerson();
	}
	
	public static void main(String[] args) {
		
		Man man = new Man();
		Woman woman = new Woman();
		func(man);
		System.out.println();
		func(woman);
    }
}

 

결과

나는 사람이다.
그리고 난 남자

나는 사람이다.
그리고 난 여자

 

전체코드

package com.example.demo;

//다형성
class People{
	public void resultPerson() {
		System.out.println("나는 사람이다.");
	}
}

//Person 클래스 상속받음
class Man extends People{ 
	public void resultPerson() { //resultPerson() 오버라이딩해서 상위 클래스 동작을 다르게 변경하기 위해  
		super.resultPerson();  //상위클래스(People)의 함수실행 
		System.out.println("그리고 난 남자"); //추가 동작
	}
	public void army() {
		System.out.println("군대를 간다.");
	}
} 
class Woman extends People{
	public void resultPerson() {
		super.resultPerson();
		System.out.println("그리고 난 여자");
	}
	public void makeup() {
		System.out.println("화장을 한다.");
	}
}



public class Polymorphism {
	public static void func(People people) { //people객체를 받아서 이 객체를 상속받는 모든 클래스의 객체도 전달할 수 있다.
		people.resultPerson();
	}
	
	public static void main(String[] args) {
		
		Man man = new Man();
		Woman woman = new Woman();
//		//People 클래스를 상속한 하위 클래스를 매개변수로 전달 
		func(man); 
		System.out.println();
		func(woman);
		
	}
}

 

 

man, woman 클래스에서 단독으로 추가한 메소드는 어떻게 출력을 해야할까

people 변수를 Man 타입으로 형변환을 하면 people변수가 Man 타입으로 인식이 되어 army() 메소드를 사용 할 수 있다 

((Man)people).army();
public class Polymorphism {
	public static void func(People people) { //people객체를 받아서 이 객체를 상속받는 모든 클래스의 객체도 전달할 수 있다.
		people.resultPerson();
		((Man)people).army();
	}
	
	public static void main(String[] args) {
		
		Man man = new Man();
		//Woman woman = new Woman();

		func(man); 
		System.out.println();
		//func(woman);

	}
}
나는 사람이다.
그리고 난 남자
군대를 간다.

 

 

대신 Woman 객체로도 형변환을 해야하기 때문에 insanceof 연산자를 이용하여 캐스팅(형변환)을 해보자 

참조변수 instanceof 클래스
> people instanceof Man  // people 변수가 Man  클래스의 객체인지 확인하는 연산자

people 변수가 Man 클래스의 객체를 참조한다 = people 변수가 Man 클래스의 인스턴스이다

people 변수가 Man 클래스로 생성된 객체인지를 검사?

 

people instanceof Man = true! 

왜?

func(man); 에서 man 변수는 func()메소드의 매개변수로 전달받아 people 매개변수는 man 변수가 가리키고 있는 Man 클래스의 객체를 참조한다.

정리하자면 man변수는 People 타입으로 자동형변환을 하고 func()메소드의 people 개변수로 전달. 그럼 people매개변수는  'man의 변수가 가르키고 있는' Man 클래스의 객체를 참조하게 된다 

그럼 people 변수는 Man 클래스의 속성과 메소드에 접근가능

 

** 헷갈리는거!

참조한다는 것은 변수가 해당 객체를 가르키고 있다는 뜻

예를들어 Man man = new Man();

Man 클래스 객체생성하면 그 객체를 man 변수에 할당한다 man변수는 객체를 참조하고 있어 man변수를 사용해  Man클래스의 속성과 메소드에 접근할 수 있다.

 

 

people 변수는가 Man, Woman 클래스의 메소드에 접근하려면 타입이 다르니까 형변환을 해줘야함

if(people instanceof Man) { 
    ((Man)people).army();
}else {
    ((Woman)people).makeup();
}

 

public class Polymorphism {
	public static void func(People people) { 
		people.resultPerson();
        
		//Man,Woman클래스의 단독으로 추가한 메서드 출력하기
		if(people instanceof Man) { 
			((Man)people).army();
		}else {
			((Woman)people).makeup();
		}
	}
    
    public static void main(String[] args) {
		
		Man man = new Man();
		Woman woman = new Woman();
		//man이라는 변수를 func()메소드의 people 매개변수에 전달하면 people 매개변수는 man의 변수가 가르키는 Man클래스의 객체를 참조한다.
		func(man); 
		
		System.out.println();
		func(woman);
		
	}
}

 

 

 

 

 이해는 어느정도 했지만 아직까지 헷갈리는 다형성..

 

반응형
LIST

'공부' 카테고리의 다른 글

클래스 멤버  (0) 2023.07.20
추상 메소드와 인터페이스 | 다중상속  (0) 2023.06.27
접근 제한자 | 지역변수, 전역변수와 static  (0) 2023.06.22
생성자  (0) 2023.06.20
메소드화 | 객체화 | 인스턴스  (0) 2023.06.16

접근 제한자

클래스는 변수와 함수, 셍성자로 이루어져 있는데 이 세가지 속성을 접근하기 위해 접근제한자가 필요하다

접근제한자는 함수나 변수에 접근하는 방식에 대한 것을 설정하는 것.

 

  • public
  • protected
  • default
  • private

접근 허용 범위 크기는 위쪽부터 아래순

 

접근제한자는 객체 지향의 특징 중 '캡슐화' 와 관련있다

캡슐화란? 클래스 안에 기능과 속성을 묶어서 데이터 외부로부터 보호하는 뜻

 

 

public , private

public은 외부에서 접근가능하여 접근 제한이 없다 같은 패키지/다른 패키지에서도 접근가능하다

private은 캡슐화를 위해 자기 자신의 클래스 내에서만 접근이 가능하다 

class A {
	public String one = "public";
	private String two = "private";
}

public class Student{
	public static void main(String[] args) {
		A a = new A();
		System.out.println("public 테스트 : "+ a.one); // public 테스트 : public
		System.out.println("private 테스트 : "+ a.two); // 컴파일 에러
    }
}

 

 

public 다른 패키지여도 접근가능

package model;

public class School{
	public static String region = "울산";
}
package demo;

public class Student{
	public static void main(String[] args) {
    	School school = new School();
		System.out.println(school.region);
    }
}

 

 

default

변수 앞에 아무것도 안 적으면 default 적용이 되어 같은 패키지 내에서만 접근가능

하지만 자바에서 비명시적 (default)를 권장하지 않으니 되도록 명시적 private, public, protected를 설정해준다.

class A {
	String one = "default";
}

public class Student{
	public static void main(String[] args) {
		A a = new A();
		System.out.println("default 테스트 : "+ a.one); // default 테스트 : default
    }
}

 

 

 

protected

 

같은 패키지 내에서 접근이 가능하나 다른 패키지에 접근 가능하다

다른 패키지에 접근이 가능하게 하려면 파생 클래스에서만 멤버를 접근할 수 있다.

 

파생클래스란? 

부모 클래스의 상속을 통해 새롭게 작성된 클래스

class 자식 클래스 extend 부모 클래스

 

다른 패키지에 접근 가능하기 위해 School 클래스를 상속받고  School 클래스의 멤버에 접근한다 (단 static 변수)

public class Student extends School

 

package model;

public class School{
	protected static String region = "울산";
   	protected String name;
}
package demo;

public class Student extends School{
	
	public static void main(String[] args) {
        	System.out.println(region); // 방법1. static 멤버면 바로 접근가능
        	System.out.println(School.region);  // 방법2. 클래스명.static변수명

        	School school = new School(); // 방법3. 객체 생성해서도 static 변수 접근가능
		System.out.println(school.region); 
        
     	 	System.out.println(school.name); // 변수에 static 을 사용하지 않고(=인스턴스 변수) 객체를 생성하여 호출
    }
    
}

 

  • static변수에 접근하는 방법
방법1. static 멤버면 바로 접근가능 
System.out.println(region);

방법2. 클래스이름.static변수이름
System.out.println(School.name);

방법3. 객체생성 후 static변수 접근하지만 static변수가 아닌 인스턴스 변수일때도 접근가능
School school = new School();
System.out.println(school.name);

 

기본적으로 new 연산자를 이용해 객체를 생성(인스턴스 생성)하면 메모리에 할당을 하고 사용하는데

이때 멤버와 메소드에 static 을 써줘서 같은 시점에 메모리에 올라가기 때문에

객체를 생성하지 않아도 model 패키지의 클래스 변수(static)에 접근가능하다. 

하지만 static변수는 메모리에 올라가면 계속 상주해 있기때문에 되도록이면 안 쓰는게 좋다

 

 

만약 멤버변수에 static을 안써줬다면? 메모리에 로드되는 시점이 안맞아서 에러가 뜬다

The static field School.region should be accessed in a static way 

public class School{
	protected String region = "울산";
}

 

 

 

 

 

잠시 용어 정리!!

  • 참조변수 

참조변

 

여기서 mc 가 참조변수(변수) 이며 생성된 객체의 참조를 가진다.

생성 된 MyClass 객체를 mc가 참조를 하고  > mc.

MyClass 클래스 내에 있는 메소드에 인자값을 넣어 전달 > mc.setAge(27)

메소드를 호출하여 멤버 변수에 데이터를 저장. > int age;

 

 

객체 내에 존재하는 멤버 변수에 접근 > mc.age = 10;
객체 내에 존재하는 멤버 메소드에 접근 > mc.setAge(27);

 

 

 

 

 

 

 

지역변수, 전역변수 , static 

 

 

 

지역변수

함수내에 선언이 됐으면 그 안에서만 사용가능하고 밖에서 사용불가

public static void main(String[] args) {

    String b = "지역변수"; // 지역변수에 접근제한자 사용x
    System.out.println(b);

    run();
}

public static void run() {
    System.out.println(b);  //에러. 지역변수는 main함수 안에서만 사용가능
}

 

 

전역변수

전역변수에는 인스턴스 변수와 클래스 변수(static)이 있다.

 

1. 인스턴스 변수

다른 파일에서도 접근가능

클래스 영역에 static 없이 변수를 생성 후 객체를 생성해야 호출가능

public class Variable {

	//static을 사용하지 않고 호출하는법 객체를 생성하여 호출 (static을 많이 쓰면 프로그램 실행시 느려짐)
	public String a = "전역변수"; 
	
	public static void main(String[] args) {
		Variable v = new Variable(); //객체 생성 후
		System.out.println(v.a); // 인스턴스 변수 호출가능

		//에러. static 함수라서 클래식 변수(static)이여야함
        System.out.println(a);  
	}
    
    public void run() { // 전역변수. 클래식 전체에 사용가능
    	System.out.println(a);  
    }
 }

 

2. 클래스 변수 (static) = 정적변수

다른 파일에서 접근불가 해당 파일에서만 static 쓴 것끼리 접근가능

객체를 생성하지 않아도 사용가능하다 대신 static 은 메모리에 계속 상주해 있어서 프로그램이 종료할때까지 계속 있다

static을 많이 쓰면 느려지거나 시스템의 퍼포먼스에 악영향을 끼칠 수 있다..

package com.example.demo;

public class Variable {

	//클래스 변수(static)
	public static String a = "전역변수"; 
	
	public static void main(String[] args) {
    	//객체 생성할 필요없이 클래식 변수는 접근가능
		System.out.println(a); 			//static 전역 변수 접근방법
		System.out.println(Variable.a); //static 전역 변수 접근방법
        
		run();
	}
	
	public static void run() {
		System.out.println(a); 	 // static 변수(전역변수)는 클래스 전체에 사용가능
	}
}

 

 

 

정적변수(static)는 data영역에 저장되어 메모리상에 계속 할당되어 프로그램이 종료될까지 있다

모든 객체가 공유하는 메모리의 장점이 있으나 

위에서 말했듯 static을 남발하게 되면 시스템상으로 악영향을 주기에 많이 쓰면 안된다

(Garbage Collector 가 관리 안해줌)

 

전역변수는 new 연산을 통해 객체를 생성하여 사용하는데 이떄 생성된 객체는 Heap 영역에 저장되어 Garbage Collector 가 알아서 데이터를 관리해준다는 장점이 있다.

반응형
LIST

'공부' 카테고리의 다른 글

추상 메소드와 인터페이스 | 다중상속  (0) 2023.06.27
다형성  (0) 2023.06.22
생성자  (0) 2023.06.20
메소드화 | 객체화 | 인스턴스  (0) 2023.06.16
메소드  (1) 2023.06.16

생성자

모든 클래스에는 생성자가 반드시 존재해야하며 하나 이상 가질 수 있다.

 

Cafe cafe = new Cafe();

new 연산자에 의해 생성자 실행한다. 생성자를 실행시키지 않고는 객체 생성은 불가능

생성자는 클래스로부터 객체를 생성할때 '객체의 초기화' 를 해준다.

 

* 객체 초기화란 변수를 초기화하거나 메서드를 호출하여 객체를 사용할 준비를 하는 것

 

 

- 기본생성자

클래스 내부에 기본 생성자 생성

객체를 새로 생성할때 가장 먼저 호출되는 메서드이며 반환값을 안 적어야 생성자로 인식한다.

 

 

 

 

클래스 내부에 기본 생성자 선언을 생략했다면 컴파일러는 {} 안에 내용이 비어있는 기본 생성자를 바이트 코드에 자동추가 시킨다 (선언한 생성자가 한개라도 존재하면 컴파일러는 기본생성자를 추가하지 않음)

또한 클래스의 접근제한자가 public없이 클래스가 선언됐다면 기본 생성자도 같이 public이 붙지 않는다.

 

[자바 소스 파일 Cafe.java]

public class Cafe() {
}

 

[바이트 코드 파일 Cafe.class ]

public class Cafe() {
    //기본생성자
    public Cafe() {
        System.out.println("생성자.");
     }
}

 

따라서 클래스 내부에 생성자 선언하지 않아도 new 연산자 뒤에 기본 생성자를 호출하여 객체를 생성한다.

Cafe cafe = new Cafe(); //Cafe() 기본생성자

 

 

생성자 선언 (생성자 명시)

Cafe 클래스에서 기본 생성자가 아닌 생성자를 명시적으로 선언하면? 아래의 형태로 

근데 왜 public 를 안붙일까

public class Cafe() {
    //생성자 명시
    Cafe( 매개변수 선언,...) {   // 생성자 블록
      ...
     }
}

 

이렇게 클래스 안에 생성자가 명시적으로 선언을 했다면 선언된 생성자를 호출(new 연산자로 생성자 호출)하여 객체를 생성한다

Cafe cafe = new Cafe(); 기본생성자는 호출 불가

Cafe cafe = new Cafe("컴포즈", "espresso", "orangeJuice");

세개의 매개값은  생성자 블록내부에 전달

public class Cafe() {
	// 생성자
	Cafe(String name, String caffeine, String decaffeine) { //생성자 블록
            this.name = name;
            this.caffeine = caffeine;
            this.decaffeine = decaffeine;
        }
 }

 

 

 

변수(필드) 초기화

 

필드를 선언할 때 초기값을 주게 되면 Cafe()클래스로부터 생성되는 객체는 모두 같은 데이터를 갖는다.

객체 생성 시점에는 같은 필드값이고 객체생성 후 변경가능 

public class Cafe() {
	String name = "컴포즈";
	String caffeine;
	String decaffeine;
}
Cafe cafe = new Cafe(); // name 필드값 = "컴포즈"

 

 

객체 생성 시점에서 외부에서 제공되는 값들로 초기화 해야한다면 생성자를 초기화 해준다

외부에서 제공되는 값 = 매개값으로 다양한 값들을 받아서 초기화 해주기

public class Cafe() {
	String name = "컴포즈";
	String caffeine;
	String decaffeine;
    
    public Cafe(String caf, String decaf){
        this.caffeine = caf;
        this.decaffeine = decaf;
    }
}
Cafe cafe = new Cafe("Americano","blueberryJuice");

매개변수 cad , decaf을 통해 caffeine, decaffeine 값을 받는다.

 

 

원래는 필드(caffeine)와 매개변수(caf)가 동일한 이름을 사용한다 

위처럼 하면 내부에서 필드에 접근 할 수 없어서 'this.필드명' 으로 하면 필드에 접근이 가능하다

접근을 못하는 이유 > 매개변수가 사용 우선순위가 높기 때문.

public class Cafe() {
	public String name = "컴포즈";
	public String caffeine;
	public String decaffeine;
    
    public Cafe(String caffeine, String decaffeine){
        this.caffeine = caffeine;  // this.caffeine (필드), caffeine (매개변수)
        this.decaffeine = decaffeine; // this.decaffeine (필드) , decaffeine (매개변수)
    }
}

 

this 객체 자신을 참조한다는 의미 

 

 

 

이렇듯 중요한 몇개의 필드만 매개변수에 의해 초기화가 되고 나머지 필드는 필드 선언시 초기화하기도 한다.

 

 

 

 

생성자 오버로딩

 

오버로딩이란? 매개변수를 달리하는 생성자를 여러개 선언이 가능하다

 

자바는 생성자 오버로딩을 제공함으로써 객체생성을 다양한 방법으로 할 수 있다 

외부에서 제공되는 다양한 데이터들을 이용해 객체를 초기화해야해서 생성자도 다양화 할 필요가 있다.

 

-아래는 생성자 오버로딩

public class Cafe {
	
	//생성자 선언
	Cafe() {
   	 }
    
   	Cafe(String name, String caffeine) {
		this.name = name;
		this.caffeine = caffeine;
	}
    
	Cafe(String name, String caffeine, String decaffeine) {
		this.name = name;
		this.caffeine = caffeine;
		this.decaffeine = decaffeine;
	}

 

-다양한 방법으로 객체 생성

Cafe cafe = new Cafe();  // 기본생성자로 객체 생성
Cafe cafe = new Cafe("컴포즈", "espresso");  // Cafe(String name, String caffeine) 생성자 호출
Cafe cafe = new Cafe("컴포즈", "espresso", "orangeJuice"); // Cafe(String name, String caffeine, String decaffeine) 생성자 호출

 

new 연산자로 생성자를 호출할 때 제공되는 매개변수의 타입개수에 의해 호출될 생성자가 결정된다.

 

 

 

다시 정리

아래 예시는 다양한 Coffee 객체가 생성하고 생성자가 오버로딩 되어 있는 경우 new 연산자로 생성자를 호출 할때 매개변수의 타입과 개수에 의해 생성자 선택

public class Coffee {

	String name = "Americano";
	String bean;
	int shot;
	
	public Coffee() {
	}
	
	public Coffee(String bean) {
		this.bean = bean;
	}
	
	public Coffee(String bean,int shot) {
		this.bean = bean;
		this.shot = shot;
	}	
	
	public static void main(String[] args) {
    	//다양한 Coffee 객체가 생성
		Coffee coffee1 = new Coffee();
		System.out.println("coffee1 : " + coffee1.name);
		
		Coffee coffee2 = new Coffee("아라비카");		
		System.out.println("coffee2 : " + coffee2.name + ", " + coffee2.bean);
		
		Coffee coffee3 = new Coffee("케냐",2);
		System.out.println("coffee2 : " + coffee3.name + ", " + coffee3.bean + ", " + coffee3.shot);
	}
}

 

※ 주의 할 점

매개변수 순서가 바뀌면 안된다

public Coffee(int shot, String bean)
Coffee coffee3 = new Coffee("케냐",2);

 

 

 

 

package com.example.demo;

public class Cafe {

	//멤버 변수
	public String name;
	public String caffeine;
	public String decaffeine;
	
	
	//생성자 선언
//	public Cafe(String name, String caffeine, String decaffeine) {
//		this.name = name;
//		this.caffeine = caffeine;
//		this.decaffeine = decaffeine;
//	}

	//기본 생성자
	//생성자를 가장 먼저 호출되어 출력된다, 반환값을 안적으면 생성자로 인식
	public Cafe() {
		System.out.println("생성자.");
	}
	
	public void buy() {
		System.out.println("메서드.");
	}
	
	public static void main(String[] args) {
//		Cafe cafe = new Cafe("컴포즈", "espresso", "orangeJuice");
		
		//객체 생성
		Cafe cafe = new Cafe();
		cafe.name = "Compose";
		cafe.caffeine = "espresso";
		cafe.decaffeine = "orangeJuice";
		
		//객체 호출
		System.out.println("카페 이름 : " + cafe.name);
		System.out.println("카페인 음료 : " + cafe.caffeine);
		System.out.println("비카페인 음료 : " + cafe.decaffeine);
		cafe.buy();
	}
}

 

= 출력된 console 창

생성자.
카페 이름 : Compose
카페인 음료 : espresso
디카페인 음료 : orangeJuiced
메서드.

 

클래스의 객체가 생성되면 생성자가 호출

다음으로 (객체 호출)멤버 변수 값들을 출력

System.out.println("카페 이름 : " + cafe.name);
System.out.println("카페인 음료 : " + cafe.caffeine);
System.out.println("디카페인 음료 : " + cafe.decaffeine);

 

마지막으로 메서드 호출하면서 cafe.buy(); 프린트출력

 

 

반응형
LIST

'공부' 카테고리의 다른 글

다형성  (0) 2023.06.22
접근 제한자 | 지역변수, 전역변수와 static  (0) 2023.06.22
메소드화 | 객체화 | 인스턴스  (0) 2023.06.16
메소드  (1) 2023.06.16
[백준] 4단계 | 10807, 10871, 10818, 2562, 5597, 3052, 1546  (0) 2023.03.04

메소드화

 

두개의 값을 더하는 중복이 있다. (수많은 로직이 있으면 복잡한 로직이 돼 버림)

public class DemoApplication {

	public static void main(String[] args) {
		System.out.println(10+20);
		System.out.println(20+30);
	}
}

 

 

두개의 값을 더하는 중복을 제거해보자

그러면 sum 메소드를 만들어서 코드양을 줄이고 문제가 생길때 원인을 찾기도 쉽다

public class DemoApplication {
	public static void sum(int a, int b) {
		System.out.println(a+b);
	}

	public static void main(String[] args) {
		sum(10,20);
	}
}

 

 

이때 평균도 구해야한다면?

avg 메소드도 생성하고 입력값을 변수화 시켜서 더 코드양을 줄일 수 있다.

public class DemoApplication {

	public static void sum(int a, int b) {
		System.out.println(a+b);
	}
	
	public static void avg(int a, int b) {
		System.out.println((a+b)/2);
	}
	
	public static void main(String[] args) {
		int a = 10;
		int b = 10;
		sum(a, b);
		avg(a, b);
		
		a = 20;
		b = 30;
		sum(a, b);
		avg(a, b);
	}
}

 

 

 

객체화

객체 지향은 연관되어있는 변수와 메소드를 하나의 그룹으로 묶어서 그룹화한 것

> 연관된 변수와 메소드

int a = 10;
int b = 10;
sum(a, b);
avg(a, b);

 

 

 

Calculator 클래스를 생성하고 안에 로직들은 변수 a,b / 메소드 sum, avg 기 연관된 로직이다.

Calculator 라는 이름으로 그룹핑을 한 것! 

클래스는 연관되어있는 변수와 메소드의 집합

class Calculator {
	int a, b;
	
	public void setOprands(int left, int right) {
		this.a = a;
		this.b = b;
	}
	
	public void sum() {
		System.out.println(this.a + this.b);
	}
	public void avg() {
		System.out.println((this.a + this.b)/2);
	}
}

public class DemoApplication {

	public static void main(String[] args) {
   	 // 인스턴스 1
        Calculator ca = new Calculator();
        ca.setOprands(10, 20);
        ca.sum();
        ca.avg();
        
        // 인스턴스 2
    	Calculator ca2 = new Calculator();
        ca2.setOprands(20, 30);
        ca2.sum();
        ca2.avg();
	}
}

System.out.println(a + b); 랑 같은 결과값이 나오는데 왜 this를 쓰는지 모르겠다

 

 

 

인스턴스

 

- 클래스 : 설계도
- 인스턴스 : 제품

 

설계도(클래스)로 구체적인 제품(객체)을 만들어야한다 

클래스에는 객체를 생성할 변수와 메서드가 정의되어 있다

그렇게 만들어진 객체를 '인스턴스'라고 하며 클래스로부터 객체를 만드는 과정은 '인스턴스화' 라고 한다

하나의 클래스로부터 여러개의 인스턴스(객체)를 만들 수 있다. (여러 제품들)

 

 

어떻게?

new 연산자를 이용해 클래스로부터 객체들을 생성한다 

class Student{
	
}

public class StudentExe {
	public static void main(String[] args) {
    		// 두 개의 인스턴스 생성
		Student s1 = new Student();
		Student s2 = new Student();
	}
}

 

 

Student s1 = new Student();
Student s2 = new Student();

new Student(); 한 만큼 객체가 메모리에 생성된다. 이러한 객체들을 Student클래스의 '인스턴스'들이다

 

StudentExe.class 가 실행되면 메모리에 변수와 객체가 생성된다.

이렇게 생성된 객체는 메모리 힙 영역에 생성되며 각 다른 객체이다.

 

 

메모리 내에서 객체의 위치를 알 수 있도록 new 연산자는 힙 영역에 객체를 생성하고

객체의 주소를 반환하여 변수에 저장하면 해당 변수가 객체를 참조하게 된다.

 

변수에 인스턴스를 담은 이유는 인스턴스를 제어하기 위해서이다

* 참조란? s1. 하면 클래스안에 있는 메소드들을 불러올 수 있다(객체 호출)

 

ex) s1.avg  가 객체호출

 

이렇게 한 클래스가 여러 인스턴스를 생성한다는 점에서 서로 다른 행동과 값을 하게 된다

이것이 객체 지향이 제공하는 가장 기본적인 재활용성이라고 할 수 있다.

 

 

 

이때 궁금한 점 ca 변수의 데이터 타입은 뭘까?? 사용자 정의 데이터 타입 이라고 하는데 데이터 타입은 그 클래스가 된다.

 

 

 

 

 

 

반응형
LIST

'공부' 카테고리의 다른 글

접근 제한자 | 지역변수, 전역변수와 static  (0) 2023.06.22
생성자  (0) 2023.06.20
메소드  (1) 2023.06.16
[백준] 4단계 | 10807, 10871, 10818, 2562, 5597, 3052, 1546  (0) 2023.03.04
[백준] 3단계 | 반복문 8~12  (0) 2023.02.17

+ Recent posts