2022.07.19 화요일/자바 정리/클래스사용법

Jimin·2022년 7월 19일
0

비트캠프

목록 보기
4/60

레퍼런스 배열

class Score{
	String name;
    int kor,
    int eng;
    int math;
    int sum;
    float aver;
}
Score[] arr = new Score[3];
arrinstance(new로 선언된 곳, 이 공간또한 레퍼런스들이다.)
주소(200)200-> null(arr[0]) / null(arr[1]) / null(arr[2])
  • 레퍼런스는 자동으로 null값으로 초기화된다.
  • 로컬 변수는 초기화 되지 않는다.
  • null? 문자열이 아니라 주소 변수가 0으로 설정되었음을 뜻한다. ==> 한마디로 주소가 없음을 나타낸다.
  • 아직 레퍼런스 배열의 각 항목에 인스턴스 주소가 없는 상태이기에 레퍼런스 배열의 각 항목에 인스턴스를 저장한 후 사용해야 한다.
arr[0] = new Score();
arr[1] = new Score();
arr[2] = new Score();
  • new Score()는 Score 클래스에 선언된 변수를 Heap 메모리 영역에 순서대로 만든다.

    arr[0] -->namekorengmathsumaver
    arr[1] -->namekorengmathsumaver
    arr[2] -->namekorengmathsumaver

클래스 레벨



- inner class (내부 클래스)

class Outer {
    변수;
    메소드;

    public class Inner {

    }
}
  • 객체 생성
Outer 객체1 = new Outer();
Outer.Inner 객체2 = 객체1.new Inner();
  • inner class 예시
public class Outer {
  int a = 10;
  private int b = 20;
  static int c = 30;

  // Inner class
  class Inner {
    public void print() {
      System.out.println(a + " " + b + " " + c);
    }
  }

  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
    inner.print();
  }
}

- static nested class(정적 중첩 클래스)

class Outer {
    변수;
    메소드;

    public static class Inner {

    }
}
  • 객체 생성
Outer.Inner 객체 = new Outer.Inner();

  • 특정 패키지에 소속된 여러 클래스를 사용한다면 다음과 같이 패키지 명 다음에 wildcard(*)을 지정하면 편리하다.
import 패키지명.*;
  • 이 경우, 사용하는 클래스마다 import를 따로 지정할 필요가 없다.
  • 하지만 서브 패키지는 해당되지 않으므로 주의하자.
  • 하지만 어떤 클래스가 어떤 패키지에 들어있는지 바로 확인할 수 없기 때문에, 가능한 wildcard(*)대신, 패키지 명과 클래스명을 정확하게 구체적으로 명시한다.
  • import문은 컴파일할 때 코드 줄에 포함되지 않는다.
  • java.lang 패키지만 생력 가능하다.
    -> 하지만 java.lang 패키지의 하위패키지는 생략 불가능하다.

클래스 문법의 활용 예:

Score s1 = new Score();

- name변수를 부르는 여러 방법들

  • s1레퍼런스에 저장된 주소를 찾아가서 해당 인스턴스의 name 변수
  • s1레퍼런스가 가르키는 인스턴스의 name 변수
  • s1인스턴스의 name 변수
  • s1 객체의 name 변수(필드)
  • refactor를 해서 rename을 해야 내부 클래스 명까지 한꺼번에 바뀐다.

  • 여러 메서드에서 공유하려면 클래스 멤버로 만들어야 한다.
  • Optimizing: 최적화, 속도 향상 <-> 코드 난해(유지 보수 하락)
  • Refactoring: 코드 개선, 유지보수 용의성 향상 <-> 속도 감소
  • 자바는 Refactoring에 더 초점을 맞춘다.

Store

- 낱개 변수 사용

public class ExamTest1 {
  public static void main(String[] args) {
    class Store {
      String name;
      int kor;
      int eng;
      int math;
      int sum;
      float aver;
    }
    Store s1 = new Store();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    s1.sum = s1.kor + s1.eng + s1.math;
    s1.aver = (float) s1.sum / 3;

    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s1.name, s1.kor, s1.eng, s1.math, s1.sum, s1.aver);

    Store s2 = new Store();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    s2.sum = s2.kor + s2.eng + s2.math;
    s2.aver = (float) s2.sum / 3;

    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s2.name, s2.kor, s2.eng, s2.math, s2.sum, s2.aver);

    Store s3 = new Store();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    s3.sum = s3.kor + s3.eng + s3.math;
    s3.aver = (float) s3.sum / 3;

    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s3.name, s3.kor, s3.eng, s3.math, s3.sum, s3.aver);
  }
}

- 성적 데이터를 저장할 사용자 정의 데이터 타입을 만든다.

  • Score는 static이지만, Score 클래스의 내부 변수들은 non-static 변수들이기 때문에 Score.name으로 바로 접근이 안되고, new명령어를 통해 만들어진, Score의 레퍼런스 변수인 s1을 통해서만 s1.name과 같이 접근할 수 있다.
public class ExamTest2 {
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    static void calc(Score s) {
      s.sum = s.kor + s.eng + s.math;
      s.aver = (float) s.sum / 3;
    }
  }
  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    Score.calc(s1);
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    Score.calc(s2);
    printScore(s2);


    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    Score.calc(s3);
    printScore(s3);

  }

  static void printScore(Score s) {
    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

}

- 리팩토링: 메서드 추출(extract method), static nested class

public class ExamTest3 {
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
  }
  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    printScore(s2);

    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    printScore(s3);
  }

  static void printScore(Score s) {
    s.sum = s.kor + s.eng + s.math;
    s.aver = (float) s.sum / 3;

    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

- 리팩토링: 메서드 추출(extract method) = 한 개의 메서드는 한 개의 기능을 수행해야 한다.

public class ExamTest3 {
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
  }
  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    printScore(s2);

    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    printScore(s3);
  }

  static void printScore(Score s) {
    s.sum = s.kor + s.eng + s.math;
    s.aver = (float) s.sum / 3;

    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

- GRASP(General Responsibility Assignment Software Patterns) 패턴

  • Information Expert: 데이터를 다룰 때는 그 데이터를 갖고 있는 객체에게 묻는다.
    리팩토링: 메서드 이동(Move Method)
  • 메서드를 관련된 클래스로 이동시킨다. => 코드의 이해가 쉽다.
public class ExamTest4 {
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
  }
  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    printScore(s2);

    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    printScore(s3);
  }

  static void printScore(Score s) {
    s.sum = s.kor + s.eng + s.math;
    s.aver = (float) s.sum / 3;

    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }
}

- 인스턴스 메서드: 인스턴스 주소를 받는 더 쉬운 문법

public class ExamTest5 {
  static class Score {
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;

    void calc() {
      this.sum = this.kor + this.eng + this.math;
      this.aver = (float) this.sum / 3;
    }
  }
  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    s1.calc();
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    s2.calc();
    printScore(s2);


    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    s3.calc();
    printScore(s3);

  }

  static void printScore(Score s) {
    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

}

- 패키지 멤버 클래스:

  • 여러 곳에서 사용할 클래스라면 다른 클래스에 안에 두지 말고
    패키지의 멤버 클래스로 둬라!
  • 외부 class는 이미 static이라 바로 사용할 수 있다.
  • Score의 class (굳이 public으로 선언될 필요 없다.)
class Score{
  String name;
  int kor;
  int eng;
  int math;
  int sum;
  float aver;

  void calc() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = (float) this.sum / 3;
  }
}
  • ExamTest6.class
public class ExamTest6 {

  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    s1.calc();
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    s2.calc();
    printScore(s2);


    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    s3.calc();
    printScore(s3);

  }

  static void printScore(Score s) {
    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

}

- 클래스를 역할에 따라 패키지로 분류

  • 클래스가 많을 경우 유지보수하기 쉽도록 적절한 패키지로 분산 배치한다.

  • 데이터 타입의 역할을 하는 클래스의 경우
    보통 domain, vo(value object), dto(data transfer object) 라는 이름을 가진
    패키지에 분류한다.

  • 패키지가 다르면 modifier 옵션에 따라 접근 범위가 달라진다.
    멤버의 접근 범위 설정

  • public: 모두 공개

  • protected: 서브 클래스와 같은 패키지의 멤버는 접근 가능

  • (default): 같은 패키지의 멤버는 접근 가능

  • private: 접근 불가! 그 멤버가 속한 클래스의 내부에서만 접근 가능

  • 새로 만든 패키지, domain에 Score 클래스 생성
    (ExamTest7과 다른 패키지에 생성되었기에 접근을 위해 모든 변수가 public으로 선언되어야 한다.)

public class Score{
  public String name;
  public int kor;
  public int eng;
  public int math;
  public int sum;
  public float aver;

  public void calc() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = (float) this.sum / 3;
  }
}
  • ExamTest7
import com.eomcs.oop.ex02.test2.domain.Score;
public class ExamTest7 {
  public static void main(String[] args) {
    Score s1 = new Score();
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 85;
    s1.calc();
    printScore(s1);

    Score s2 = new Score();
    s2.name = "임꺽정";
    s2.kor = 90;
    s2.eng = 80;
    s2.math = 75;
    s2.calc();
    printScore(s2);


    Score s3 = new Score();
    s3.name = "유관순";
    s3.kor = 80;
    s3.eng = 70;
    s3.math = 65;
    s3.calc();
    printScore(s3);

  }

  static void printScore(Score s) {
    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

}

- 생성자 도입: 인스턴스를 생성할 때 값을 초기화시키는 특별한 메서드

  • 생성자로 호출할 때부터 변수 초기화 해주기

- this

  • instance에서만 이용할 수 있다.
  • static으로 선언이 안되어 있는데, 같은 클래스 내부이고, 객체 생성 없이 바로 이용하고 싶을 때 this를 사용한다. -> 같은 클래스 내부에 있는 instance 객체를 이용하고 싶을 때 사용.
  • 변수나 메소드나 둘다 this로 호출 가능
  • Score2 선언
public class Score2 {
  public String name;
  public int kor;
  public int eng;
  public int math;
  public int sum;
  public float aver;

  public Score2(String name, int kor, int eng, int math) {
    this.name= name;
    this.kor=kor;
    this.eng=eng;
    this.math=math;

    this.calc();
  }

  public void calc() {
    this.sum = this.kor + this.eng + this.math;
    this.aver = (float) this.sum / 3;
  }
}
  • ExamTest8
import com.eomcs.oop.ex02.test2.domain.Score2;
public class ExamTest8 {
  public static void main(String[] args) {
    Score2 s1 = new Score2("홍길동", 100, 90, 85);

    printScore(s1);

    Score2 s2 = new Score2("임꺽정", 90, 80, 75);
    printScore(s2);


    Score2 s3 = new Score2("홍길동", 80, 70, 65);
    printScore(s3);

  }

  static void printScore(Score2 s) {
    System.out.printf("%s: %d, %d, %d, %d, %.1f\n", s.name, s.kor, s.eng, s.math, s.sum, s.aver);
  }

}

문법 정리

- static

  • main 메소드 안에서는 static 선언 불가
  • static method는 class이름.method명();으로 접근이 가능하다.
  • 바깥에 있는 class는 이미 static이 붙여진 애라
  • 멤버 클래스는 static으로 선언하는 것이 좋다.
  • class가 static이어도 class 내부의 변수나 메소드가 non-static(instance)로 선언된 애면, static이어도 객체를 선언해서 접근해야한다.
public class ExamTest {
  static class Store{
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float aver;
  }
  public static void main(String[] args) {
    Store s1 = new Store();
    s1.name = "홍길동";
    Store.name = "홍길동"; // 오류, name이 non-static이기 때문.
    }
 }
  1. 클래스를 설계할 때, 멤버변수모든 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다. 
  • 인스턴스를생성하면, 각 인스턴스들은 서로 독립적이기 때문에 서로 다른 값을 유지한다. 
  • 경우에따라서는 각 인스턴스들이 공통적으로 같은 값유지되어야 하는 경우 static을 붙인다.
  1. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
  • static이 붙은 멤버변수(클래스변수)는 클래스가 메모리에 올라갈때 이미 자동적으로 생성되기
    때문이다.
  1. static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.
  • static이 메서드는 인스턴스 생성 없이 호출가능한 반면, 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문에... static이 붙은 메서드(클래스메서드)를 호출할 때 인스턴스가 생성되어있을수도 그렇지 않을 수도 있어서 static이 붙은 메서드에서 인스턴스변수의 사용을 허용하지 않는다. 
    (반대로, 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다.
    인스턴스변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.)
  1. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다. 
  • 메서드의 작업내용중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없다. 
  • 반대로 인스턴스변수를 필요로 하지 않는다면, 가능하면 static을 붙이는 것이 좋다. 
  • 메서드 호출시간이 짧아지기 때문에 효율이 높아진다
  • (static을 안붙인 메서드는 실행시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.)
  1. 클래스 설계시 static의 사용지침 
  • 먼저 클래스의 멤버변수중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지 살펴보고 있으면, static을 붙여준다. 

  • 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을 붙일 것을 고려한다. 일반적으로 인스턴스변수와 관련된 작업을 하는 메서드는 인스턴스메서드(static이 안붙은 메서드)이고 static변수(클래스변수)와 관련된 작업을 하는 메서드는 클래스메서드static이 붙은 메서드)라고 보면 된다.

  • instance는 독립적이기 때문에 자주 변하는 값, 독립적이어야 하는 값에 이용하면 좋다고 생각한다.
    - instance 더 알아보기

면접관 : static키워드에 대해서 설명해보세요.
응시자 : static키워드를 쓰면, 객체를 생성하지 않고도 변수나 함수를 사용할 수 있습니다. 
면접관 : 왜 static키워드를 쓰나요?
응시자 : 객체를 생성하지 않아도 되니까 편리하고 속도도 빠릅니다. 
면접관 : 그렇다면 모든 변수와 함수에 static을 붙이는 것이 좋겠네요?
응시자 : 가능한한 static을 붙이는 것이 좋다고 생각합니다. 
면접관 : 어떤 경우에 static을 붙일 수 있고, 어떤 경우에 static을 붙일 수 없습니까?
응시자 : ...  
면접관 : 만일 당신이 새로운 클래스를 작성한다고 할 때, 어떤 경우에 static키워드를 사용해야한다고 생각합니까?
출처: https://vaert.tistory.com/101 [Vaert Street:티스토리]

profile
https://github.com/Dingadung

0개의 댓글