본문 바로가기

programming/Java [notion 정리본 업로드]

Class

A. Class

한번 생각해보자!

우리가 성적 처리 프로그램을 만드려고 해.

학생 이름, 학생들이 각각 시험 본 과목별 점수들.. 이것들을 한 번에 묶어 저장하기 위해 만들어진 객체라는 데이터 타입을 만들었다..!

  • 관련이 있는 기본타입이나 다른 타입들을 포함하고 있는 새로운 데이터 타입, 객체
  • 객체를 생성하기 위한 속성들을 정의해놓은 CLASS
    package com.itwill.class01;
    
    public class AppMain01 {
    	public static void main(String[] args) {
    // 클래스 : 데이터 타입
    // (1) 
    		Subject subject = new Subject(); // (2)
    		// Error! : System.out.println(subject);
    		System.out.println(subject.java);
    		System.out.println(subject.js);
    		System.out.println(subject.sql);
    	}
    }
    
    • 객체가 담겨있는 subject 변수는 바로 print가 불가능하다.
    1. 생성자 : constructor - 클래스에 선언된 변수(필드)들을 초기화
    2. 생성자 호출, 객체 생성, 인스턴스
  • package com.itwill.class01; public class Subject { // 변수 선언: [타입] [이름]; // 필드(field) : 멤버 변수 int java; int sql; int js; }

생성자 (constructor)

  • 객체를 생성하고 객체의 필드들을 초기화하는 역할 담당
  • 생성자의 이름은 반드시 클래스의 이름과 같아야 함
  • 메서드를 작성하는 방법과 비슷. 리턴 타입이 없음!
    • return 타입을 작성하면 생성자가 아니라 메서드 취급합!
    • package com.itwill.class01; public class Subject { // 변수 선언: [타입] [이름]; // 필드(field) : 멤버 변수 int java; int sql; int js; // 생성자 (constructor) **public Subject(int java, int sql, int js) { this.java = java; this.sql = sql; this.js = js; }** }
  • 기본 생성자(default constructor)
    • 클래스를 작성할 때 생성자를 하나도 정의하지 않으면 자바 컴파일러가 기본 생성자를 자동으로 만들어줌
    • 아규먼트를 갖지 않는 생성자
    • 일반적으로 필드 타입의 기본값으로 필드들을 초기화
      • 정수 기본값 : 0
      • 실수 기본값 : 0.0
      • boolean 기본값 : false
      • 나머지 참조 타입 : null
    • 클래스를 작성할 때 생성자를 직접 작성하는 경우, 자바 컴파일러는 기본 생성자를 자동으로 만들지 않음.
      • 만약 기본 생성자 사용하고 싶다면, 아무 동작도 하지 않는 기본 생성자를 명시해주면 된다.
  • argument를 갖는 생성자
    • 아규먼트의 값들로 필드들을 초기화
  • 생성자 오버 로딩 (Overloading)
    • 생성자를 파라미터가 다른 여러 개 정의하는 것
  • 한 클래스로 생성된 객체들의 멤버 변수 값, 즉 인스턴스 필드의 값은 독립적으로 존재한다.
  • overloading된 생성자들 중에서 기본 생성자 호출
    • 실제로는 반대로 호출하는 경우가 더 많음.
    public SuperClass() {
    		this(0);
    }
    
    public SuperClass(int a) {
    		this.a = a;
    		// this의 의미 : SuperClass 타입으로 생성된 객체 (주소)
    }
    
  • public SuperClass(int a) { this(); this.a = a; // this의 의미 : SuperClass 타입으로 생성된 객체 (주소) }

클래스 : 데이터 (필드) + 초기화(생성자) + 기능 (메서드) ⇒ 데이터 타입

package com.itwill.class04;

public class Score {
	// field
	int korean;
	int english;
	int math;

//	생성자 : 기본 생성자, 아규먼트를 갖는 생성자
	public Score() {
	}

	public Score(int korean, int english, int math) {
		this.korean = korean;
		this.english = english;
		this.math = math;
	}

	// method : getTotal, getMean
	public int getTotal () {
		return korean + english + math;
	}
	
	public double getMean() {
		return (double) getTotal() / 3;
	}

	@Override
	public String toString() {
		return "국어 성적 : " + korean + "\\n영어 성적 : " + english + "\\n수학 성적 : " + math;
	}	
	
}
package com.itwill.class04;

public class Student {
//	필드
	int studentNo; // 학번
	String studentName; // 이름
	Score score; // 수강 과목 점수

//	생성자 : 기본 생성자와 아규먼트를 갖는 생성자
	public Student() {
	}

	public Student(int studentNo, String studentName, Score score) {
		this.studentNo = studentNo;
		this.studentName = studentName;
		this.score = score;
	}

	@Override
	public String toString() {
		return studentNo + ". " + studentName;
	}

	// 학생의 정보를 출력하는 메서드 : 학번 이름 국어점수 영어점수 수학점수 총점 평균
	public void getStudentInfo() {
//		System.out.println("1. 학번 : " + studentNo);
//		System.out.println("2. 이름 : " + studentName);
		if (this.studentName == null || this.score == null) {
			System.out.println("아직 정보가 입력되지 않았습니다.");
			return;
		}
		System.out.println(this);
		
		System.out.println();

//		System.out.println("국어 점수 : " + this.score.korean);
//		System.out.println("영어 점수 : " + this.score.english);
//		System.out.println("수학 점수 : " + this.score.math);

		System.out.println(this.score);
		
		System.out.println();
		System.out.println("총점 : " + score.getTotal());
		System.out.println("평균 : " + score.getMean());

	}
}

Student 생성시

  • A) Score 인스턴스를 생성한 후, Student 인스턴스 생성 시 해당 아규먼트로 사용하는 방법
  • Score score1 = new Score(100, 100, 90); Student stu1 = new Student (101, "채원", score1);
  • B) Student 인스턴스 생성 시 Score 생성자를 바로 해당 아규먼트로 사용하는 방법
  • Student stu1 = new Student (101, "채원", new Score(100, 100, 90));
  • 위와 같은 방법들이 있음 (더 있지만,,, 나는 이게 조와)
    • 위 두 방법 중 어느것을 더 많이 사용하나라는 궁금증이 있었는데, 강사님이 개인취향이라고 하셨음.
    • 나는 new Score()을 바로 Student를 생성하는 생성자로 사용하는 것이 더 좋음.

B. 메서드

  • 클래스 안에서 작성하는 함수(function, 기능)
    • 메서드는 함수에 포함된다.
    • 자바에서 함수는 클래스 안에 메서드로서만 존재할 수 있다.
    • 프로그램에서 반복적으로 사용되는 기능을 코드 블럭으로 작성

<aside> 💡 주의🚫 자바는 메서드 안에서 다른 메서드를 정의하거나 선언할 수 없음! 항상 클래스의 메서드로만 선언할 수 있다.

</aside>

  • argument : 인수라고 번역 하기도 함.
    • 메서드를 호출하는 곳에서 메서드에게 전달하는 값
  • parameter : 매개변수. 인자.
    • 아규먼트를 저장하기 위해서 메서드를 선언(정의)하는 곳에서 선언하는 값
  • return value : 반환 값.
    • 메서드가 기능을 모두 수행한 후에 메서드를 호출한 곳으로 반환하는 값
    • 메서드에 따라서 반환값이 있을수도, 없을 수도 있음
  • 메서드 정의(선언)하는 법메소드 선언부는 메서드 시그니쳐라고 하며, 다음 요소를 포함한다.
    • 리턴 타입 : 메서드가 리턴하는 결과의 타입을 표시
    • 메서드 이름 : 메서드의 기능이 드러나도록 식별자 규칙에 따라 이름을 지어줌
    • 매개 변수 선언 : 메서드를 실행할 때 필요한 데이터를 받기 위한 변수를 선언
    • 메서드 실행 블록 : 실행할 코드 작성
  • [수식어] 리턴타입 메서드이름([파라미터선언, 파라미터선언,... ]) {... ...}

B-1. 메서드 선언하기

  • 선언부(리턴 타입, 메서드 이름, 매개 변수 선언)와 실행 블록으로 구분
  1. 리턴 타입 : 리턴 값의 타입
    • void : 실행하지만 반환 값이 없는 경우
    • 데이터 타입 : 메서드의 반환 타입과 동일한 타입의 변수에 대입하여 사용
      • 만약 사용하지 않고 실행시키는 목적만 있을 경우, 꼭 변수에 할당하지 않아도 됨.
    • 숫자로 시작하면 안되고, $와 _를 제외한 특수 문자 사용이 불가능
    • 관례적으로 메서드 이름은 소문자로 작성
    • CamelCase로 작성메서드 이름 : 자바 식별자 규칙에 맞게 작성
  2. 매개 변수 선언
    • 메서드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용
    • 매개 변수가 없는 경우, 소괄호()는 작성하고, 내부를 비워놓으면 된다.

💡 메서드 이름은 어떤 기능을 수행하는지 쉽게 알 수 있는 기능 이름으로 지어주는 것이 좋음. 메서드 이름의 길이는 프로그램 실행과는 무관, 너무 짧게 주지 않도록!

매개 변수의 개수를 모를 경우

가변 길이 인수 (variable-length argument)

  • 메서드 호출 시 아규먼트의 개수가 정해져 있지 않을 경우
  • 이에 대한 해결책으로 (1)매개 변수 타입을 배열로 선언하거나 (2)매개 변수를 …으로 선언하기의 방법이 있음
  1. 매개 변수를 배열 타입으로 선언하기
    • 매개 변수를 int 배열로 받고 있으며 호출시 다음과 같이 사용할 수 있음
      • argument로 배열 넣을 때 {1,2,3}과 같이 배열 생성과 메서드 호출을 같이 할 수 없는 것을 주의하자!
    • int[] values = {1, 2, 3}; int result = sum1 (values); int result = sum1 (new int[] {1,2,3,4,5});
  2. int sum1(int[] values) {... ...};
  3. 매개 변수를 … 사용하여 받기
    • 매개변수를 배열 타입을 선언하면 메서드를 호출하기 전 배열을 생성해야 하는 불편함이 있음.
    • 배열을 생성하지 않고 값의 목록만 받을 수 있도록 한 방법
      • 가변인수를 갖는 메서드를 사용할 때는 전달하는 아규먼트 개수에 제한이 없음
        • 전달하지 않아도 괜춚!
    • 값의 목록만 넘겨주면, values라는 지역 변수에 값의 목록으로 생성된 배열이 할당되어 있음. 사용 방법은 다음과 같음
    • int result = sum (1,2,3,4);
    • 실제 sum2 메서드 내에서 values는 다음과 같음
    • values = {1, 2, 3, 4};
    • 첫번째 인자가 아닌 마지막 인자로 사용할 수 있고, 그 예시로 printf 메서드가 있음.
    <aside> 💡 ⛔주의
    1. 가변 인수를 저장하는 파라미터는 반드시 가장 마지막 파라미터여야 함
    2. 가변인수를 저장하는 파라미터는 2개 이상 있을 수 없음 </aside>
    [예제] 가변인수 메서드 만들기
  4. /** * 아규먼트로 전달받은 모든 정수들을 다 더해서 리턴 아규먼트가 없는 경우, 0을 리턴 * * @param args 더해줄 정수들. 개수 제한 없음. * @return 0 또는 args 들의 합. */ public static int addAll(int... args) { int result = 0; for (int x : args) { result += x; } return result; } public static void main(String[] args) { int sum = addAll(); // argument 0개 System.out.println(sum); int sum1 = addAll(1, 2, 3, 4); // argument 0개 System.out.println(sum1); int sum2 =addAll(new int[] {1,2,3,4}); System.out.println(sum2); }
  5. int sum2 (int ... values) {}
  • 가변 길이 parameter에 배열로 argument를 전달해도 상관 없다!
    • int… args으로 메서드의 파라미터를 작성했을 때는 args라는 int 배열 타입의 지역 변수가 생성되고, 들어온 (몇 개인지 모르는) 여러 인수들을 모아

리턴문 [리턴 값이 있는 경우에만 사용]

  • 메서드 선언에 리턴 타입이 있는 메서드는 반드시 리턴 문을 사용해서 리턴값 지정해야 함.
  • 만약 리턴 문이 없다면 컴파일 에러가 발생
  • promotioning 할 수 있는 타입으로는 리턴이 가능
    • 리턴 타입이 int인데 실제 리턴 타입은 byte인 예

문서화 주석 사용하기

package com.itwill.method02;

public class MethodMain02 {
	public static void main(String[] args) {
// 리턴 타입과 아규먼트를 갖는 메서드 작성, 호출 연습
		MethodMain02.add(0, 0);
		
	}
/**
 * 숫자 2개를 전달받아, 두 숫자의 덧셈 결과를 반환
 * 
 * @param x(double)
 * @param y(double)
 * @return 
 * */
	public static double add(double x, double y) {
		return x + y;
	}
}
  • 문서화 주석을 활용하여 만들어진 메서드에 관한 설명문을 작성할 수 있음.

B-2. 메서드 호출하기

  • 메서드를 호출할 때, 해당 메서드가 속한 클래스 내부에서의 호출인지 외부에서의 호출인지에 따라 호출 방법의 차이가 있음

메서드의 반환값 이용하기

  • 메서드에서 받은 반환값을 이용하고 싶은 경우, 메서드의 return 타입과 동일한 타입의 변수를 선언해서 저장할 수 있음.
  • 만약 반환값이 없거나, 있어도 서비스 내에서 사용하지 않을 경우에는 새로운 변수를 선언하여 결과를 할당할 필요 없이 호출만 하면 됨.
  • 리턴 값을 넣는 변수의 타입은 자동 형 변환이 되는 타입이나 (return 값이 int인데 담는 변수는 double형인 경우) 동일한 타입이어야 함.

객체 내부에서 호출할 때

package com.itwill.method02;

public class MethodMain02 {
	public static void main(String[] args) {
// 리턴 타입과 아규먼트를 갖는 메서드 작성, 호출 연습
		*MethodMain02.add(0, 0);*
	}
/**
 * 숫자 2개를 전달받아, 두 숫자의 덧셈 결과를 반환
 * 
 * @param x(double)
 * @param y(double)
 * @return 
 * */
	public static double add(double x, double y) {
		return x + y;
	}
}
  • 물론 위와 같이 [클래스 이름].[메서드명](argument)와 같이 사용할 수는 있으나, 같은 클래스 내의 다른 메서드에서 호출될 때는, add(0,0)과 같이 클래스 명은 생략한 채로 호출하는 것이 기본

객체 외부에서 호출할 때

B-3. 메서드 오버로딩

  • 메서드의 파라미터가 다르면 같은 이름으로 메서드를 여러개 선언(정의)할 수 있음</aside>
  • <aside> 💡 주의🚫 : 파라미터는 동일하고 리턴 타입만 다르게는 오버로딩할 수 없음 → 🚫🚫컴파일 에러!!🚫🚫
  • System.out.println을 보면 parameter 타입이 다른 동명의 여러 메서드들을 가지고 있음
  • 자바 런타임은 해당 argument의 타입을 체크하여 어떤 메서드를 사용해야 하는지 구분한다.
  • return 타입의 차이가 아니라 parameter 타입의 차이라는 것을 명심 또 명심!

[예제] - countdown

package com.itwill.method03;

public class MethodMain03 {
	
	public static void main(String[] args) {
		
		countdown(5);
	}
	public static void countdown(int start) {

		while (start >= 0) {
			System.out.println(start--);
		}
		
		System.out.print("!!!!!");
	}
	
}
  • count down에 음수가 온다면 호출은 되지만, 실제 개발자가 의도한 실행은 불가능
    • 그런 의미에서 좋은 코드는 아님
    package com.itwill.method03;
    
    public class MethodMain03 {
    	
    	public static void main(String[] args) {
    		
    		countdown(-333);
    	}
    	public static void countdown(int start) {
    		if (start < 0) {
    			System.out.println("음수는 카운트다운 할 수 없습니다!");
    		}
    		while (start >= 0) {
    			System.out.println(start--);
    		}
    		
    		System.out.print("!!!!!");
    	}
    	
    }
    
    • 위의 코드와 같이 start로 받는 argument가 음수일 경우, 에러를 출력하도록 함.
    • void 메서드 이용 시 의도적 프로세스 종료
    • package com.itwill.method03; public class MethodMain03 { public static void main(String[] args) { countdown(-333); } public static void countdown(int start) { if (start < 0) { System.out.println("음수는 카운트다운 할 수 없습니다!"); return; } while (start >= 0) { System.out.println(start--); } System.out.print("!!!!!"); } }
    • 위와 같이 return 문장을 추가하여 프로그램 실행을 멈추는 패턴을 많이 이용.
      1. 메서드 종료
      2. (return 문장에 값이 있으면), 값을 메서드 호출한 곳에 반환

C. 한정자

C-1. Access modifier [접근 한정자] (1)클래스 멤버

  • 클래스 멤버(필드, 생성자, 메서드)의 접근(가시성) 수식어
  • 클래스 멤버들을 보여주는 범위를 설정
  • 가시성의 범위 : private < default (package) < protected < public
    1. private
      • 선언된 클래스 내부에서만 보이는(접근 가능한) 멤버
    2. default (package)
      • 수식어가 없음
      • 같은 패키지에 있는 클래스에서 보이는(접근 가능한) 멤버
    3. protected
      • 같은 패키지에 있거나 상속하는 클래스에서 보이는 (접근 가능한) 멤버
    4. public
      • 어떤 곳에서나 보이는 (접근 가능한) 멤버
  • 한정자 예제
    package com.itwill.modifier01;
    
    public class ModifierMain01 {
    	public static void main(String[] args) {
    		AccessTest test = new AccessTest(1, 2, 3, 4);
    //		System.out.println(test.a); => 컴파일 에러 : 버이지 안하...
    		System.out.println(test.b);
    
    		test.info(); //출력 : a=1, b=2, c=3, d=4
    	}
    }
    
    • 2)에서 직접 1)의 a 필드에 접근할 수 없다.
    • 하지만 2)에서 test.info() 코드를 보면, a의 값이 출력되고 있다.
    • 즉, private 멤버에 대하여 다른 클래스에서 직접 접근은 불가능하나, 1)의 메서드를 경유하여 값을 조회하거나, 변경할 수 있다.
  • package com.itwill.modifier01; public class AccessTest { private int a; int b; protected int c; public int d; public AccessTest(int a, int b, int c, int d) { this.a = a; this.b = b; this.c = c; this.d = d; } public void info() { System.out.printf("a=%d, b=%d, c=%d, d=%d", a, b, c, d); } }
  • 다른 패키지의 클래스를 사용하기 위해서는 import 문이 필요하다.
    • 다른 패키지에서 선언된 클래스 이름으로 변수를 선언하거나 사용하려고 할 때
      1. import 문장을 작성하고, 클래스 이름만 사용
      2. import 문장을 작성하지 않고, 패키지 이름을 포함한 클래스 전체 이름 사용
        • com.itwill.modifier01.AccessTest = new com.itwill.modifier01.AccessTest ();
    • public 한정자를 가진 d 이외 필드의 값은 수정, 조회가 불가능하다.
  • package com.itwill.modifier02; **import com.itwill.modifier01.AccessTest;** public class ModifierMain02 { public static void main(String[] args) { // AccessTest 타입 객체 생성 AccessTest test = new AccessTest(1, 2, 3, 4); // 1) // System.out.println(test.c); => 컴파일 에러(not visible) test.d = 100; // 다른 패키지에 있는 클래스에서는 public으로 공개된 멤버만 사용가능 test.info(); com.itwill.modifier01.AccessTest test2 = new com.itwill.modifier01.AccessTest(1, 2, 3, 4); // (2) } }
  • 만약 다른 패키지 내에 동명의 클래스가 있고 현재 패키지에서 둘 다 사용해야할 경우, 하나는 1)방식처럼 import 문으로 작성하고, 나머지는 2) 방식으로 사용해야 한다.
    • 파이썬의 경우, 식별을 위한 별칭을 줄 수 있으나 자바는 그런 문법이 없음.
    • >> import math as m >> m.pi 3.141592653589793 >> m.sqrt(3.0) 1.7320508075688772
  • 만약 다른 패키지 내 모든 클래스를 불러오고 싶다면 다음과 *을 이용하여 import 해준다.
  • import com.itwill.a.*;
  • java.lang 패키지 내 포함된 클래스들은 import 문장 없이 클래스 이름을 사용할 수 있음.
    • java.lang.String, java.lang.System, java.lang.Math, …
    • 많이 사용하는 클래스들 ㅠ 0
  • 클래스는 public과 package (접근) 한정자를 사용할 수 있다.

자바는 왜 이런 문법을 제공하는 것일까?

데이터 캡슐화!

  • Class의 생성자를 private으로 하는 경우도 있다.
    • Math Class도 생성자로 객체를 생성하려고 할 때, The constructor Math() is not visible 에러 발생
    • 즉, Math의 생성자는 private으로 감춰진 것
    • 실제 Math.class 파일을 확인해보니, 예상과 같았다.
    • 라이브러리로 이용되는 클래스들의 객체 생성이 막혀있다.

C-2. Access modifier [접근 한정자] (2)클래스

  • 클래스 멤버의 접근(가시성) 수식어 : private < (package) < protected < public
  • 클래스 접근(가시성) 수식어
    • Access / Visibility modifier
      1. public : 어디서나 공개된 클래스, import 문장을 사용할 수 있음
      2. package : 수식어가 없는 경우. 같은 패키지의 클래스에서만 사용할 수 있는 클래스

<aside> 💡 🚧 public class의 이름은 자바 파일의 이름과 항상 같아야 함! 🚧

  • 만약 default 한정자로 사용하는 경우에는 자바 파일과 class명이 달라도 상관 없음
  • 하나의 자바 파일 안에서 클래스를 여러개 선언하는 것은 가능하지만, public class는 파일에서 오직 하나만 선언됨
    • 만약 하나의 파일에 클래스를 여러 개를 선언했을 때, 컴파일 후에는 각각의 클래스 파일(바이트 코드)이 생성됨
    • 클래스 파일은 클래스 단위 (not java file 단위)

🧑🏻‍💻byte code : 자바 가상 머신(JVM, Java Virtual Machine)이 실행할 수 있는 명령어 셋

</aside>

가시성 정리

공개적 제한적

가시성 한정자        
(클래스 멤버) public protected package private
가시성 한정자        
(클래스) public ←—————— ——————→ package

C-3. Final

  • 클래스, 클래스 멤버 (필드, 메서드), 지역 변수를 수식
    • access 수식어 (private, protected, public)는 지역 변수에서 사용할 수 업슴
  • final 필드 지역 변수는 초기화된 값을 변경할 수 없는 변수, 즉 상수
    • 변경할 수 없는, 즉 상수를 만들 때 사용
      1. 컴파일 에러. final 지역 변수는 새로운 값을 할당(저장)할 수 없다.
    • package com.itwill.modifier08; public final class ModifierMain08 { // final class private final String s = "hello"; public static void main(String[] args) { final int x = 1; // x = 2; (1) } }
  • ⁉️ final 필드 ⁉️
    1. 선언과 동시에 초기화하거나
    2. 반드시 아규먼트를 갖는 생성자를 선언해서 명시적으로 초기화해야 함
    3. package com.itwill.modifier08; public final class ModifierMain08 { // final class private final String s = "hello"; private final int n; public ModifierMain08(int n) { this.n = n; } public static void main(String[] args) { final int x = 1; } }

C-4. Static

  • 객체 (Object) : 프로그램에서 만들 대상. (비유) 붕어빵
  • 클래스 (Class) : 객체를 생성하기 위한 프로그램 코드, 설계도. 데이터 타입. (비유) 붕어빵 틀!
  • 인스턴스 (Instance) : 메모리에 생성된 객체. (비유) 만들어진 붕어빵
    • 실제로 생성자 호출해서 만들어진 그 객체
    • instantiate: 인스턴스화하다, 객체를 생성하다, 생성자 호출
  • 클래스 멤버(필드와 메서) : 인스턴스 멤버와 static 멤버
    1. 인스턴스 멤버 (필드, 메서드) :
      1. static 수식어가 사용되지 않은 멤버
      2. 객체를 생성한 후에 참조변수를 이용해서 사용하는 멤버
      3. JRE(Java Runtime Environment)이 사용하는 메모리 공간 중에서 힙(heap)에 생성되는 변수
    2. static (정적) 멤버 → 필드, 메서드
      1. static 수식어가 사용된 멤버
      2. 객체를 생성하지 않아도 사용할 수 있는 멤버들
        • ex) Integer.MAX_VALUE, Math.PI, Math.sqrt(), …
      3. 클래스 이름을 이용해서 사용하는 멤버
      4. JRE가 사용하는 메모리 공간 중에서 메서드 영역에 생성되는 변수
      5. 프로그램의 메인 메서드가 호출되기 전에 프로그램 로딩 시점에 메모리에 생성된다.
      6. static 메서드들은 static 필드와 메서드만 접근(사용) 가
  • static 멤버 사용 방법 : 클래스 이름을 접두사로 사용한다.
    • 객체 생성 전에도 사용이 가능하다.
    System.out.println(TEST.staticVar);
    TEST.staticVar = 1;
    System.out.println(TEST.staticVar);
    TEST.printVariables2();
    
  • 인스턴스 멤버 사용 방법 : 참조 변수를 이용, 객체를 생성한 후에 사용가능
  • TEST test = new TEST(); System.out.println(test.instanceVar); test.instanceVar = 100; test.printVariables();
  • 메서드에서 해당 클래스의 필드를 사용할 때, 다음과 같은 표현을 생략할 수 있다.
  • package com.itwill.modifier08; public class TEST { int instanceVar; // 인스턴스 필드 static int staticVar; // static 필드 public void printVariables() { System.out.println("========인스턴스 메서드========="); System.out.println("instanceVar = " + **this.**instanceVar); System.out.println("staticVar = " + **TEST.**staticVar); System.out.println("===================================="); } }

<aside> 💡 🚧주의🚧

  • static 멤버 변수는 Class와 함께 사용하는 것이 좋다.
    • CLASS_NAME.static_method_O_variable
    • 인스턴스에서 스태틱 값에 접근하지 마라
  • 인스턴스 값으로 오해할 수 있음 ⇒ 값 수정 시 전역 변수의 값 변경으로 문제가 발생할 수 있음.
    • 에러는 나지 않지만 경고가 발생
    • 👿 나쁜 코드 👿 : static 필드를 마치 인스턴스 변수처럼 사용
    • 👼🏻 좋은 코드 👼🏻 : Class를 통한 클래스 변수 접근 </aside>

예제 : 인스턴스를 통해 접근한 static 변수의 수정

package com.itwill.modifier08;

public class TEST {
	int instanceVar; // 인스턴스 필드
	static int staticVar; // static 필드

	// 인스턴스 메서드
	public void printVariables() {
		System.out.println("========인스턴스 메서드=========");
		System.out.println("instanceVar = " + instanceVar);
		System.out.println("staticVar = " + staticVar);
		System.out.println("====================================");
	}

	// static method
	public static void printVariables2() {
		System.out.println("========static 메서드=========");
//		System.out.println("instanceVar = " + instanceVar); // => static method는 인스턴스 멤버를 사용할 수 없다
		System.out.println("staticVar = " + staticVar);
		System.out.println("====================================");
	}
}
package com.itwill.modifier08;

public final class ModifierMain08 { // final class
	private final String s = "hello";
	private final int n;

	public ModifierMain08(int n) {
		this.n = n;
	}

	public static void main(String[] args) {
		final int x = 1;

		System.out.println(TEST.staticVar);
		TEST.staticVar = 1;
		System.out.println(TEST.staticVar);
		TEST.printVariables2();

		TEST test1 = new TEST();
		System.out.println(test1.instanceVar);
		test1.instanceVar = 100;
		test1.printVariables();

		TEST test2 = new TEST();
		test2.instanceVar = 200;
		test2.staticVar=12345;
		test2.printVariables();
		
		test1.printVariables();
		
	}

}
0
1
========static 메서드=========
staticVar = 1
====================================
0
========인스턴스 메서드=========
instanceVar = 100
staticVar = 1
====================================
========인스턴스 메서드=========
instanceVar = 200
staticVar = 12345
====================================
========인스턴스 메서드=========
instanceVar = 100
staticVar = 12345
====================================

D. 객체 지향 프로그래밍

Object-Oriented Programming

D-1. 객체 지향 프로그래밍

관련 필드와 메서드, 즉 데이터와 관련 기능들을 묶어서 코드 작

클래스 : 데이터(필드) +초기화 (생성자 ) + 기능(메서드 ) ⇒ 데이터 타입

클래스는 붕어빵 틀, 객체는 붕어빵.

클래스로 만들어진 객체는 인스턴스라고 부름

D-2. 데이터 캡슐화 (encapsulation)

  • 필드 (멤버 변수)들을 private으로 선언해서 클래스 외부에서는 보이지 않도록 감추고, 대신에 필요한 경우에 한해서 public으로 공개한 메서드를 제공하여 간접적으로 필드들의 값을 읽거나 수정하는 것을 허용하는 객체 지향 프로그래밍 방법
  • 데이터의 보안과 무결성을 유지하기 위해 캡슐화 사용

Getter와 Setter

  • Getter
    • private 필드의 값을 읽어오는 메서드. 필드의 값을 리턴하는 메서드
  • Setter
    • private 필드의 값을 변경하는 메서드. 필드의 값을 수정하는 메서드
    • setter는 특별한 경우가 아니면 void 타입으로 생성한다.
  • 예제 [getter/setter]
    package com.itwill.modifier03;
    
    public class ModifierMain03 {
    	public static void main(String[] args) {
    //	Person 타입의 객체 생성
    		Person person1 = new Person();
    		System.out.println(person1);
    
    		System.out.println("p1 이름 : " + person1.getName());
    		System.out.println("p1 나이 : " + person1.getAge());
    
    	}
    
  • package com.itwill.modifier03; public class Person { private String name; // 읽기 전용 데이터 private int age; // 읽기, 쓰기 가능 데이터 public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } /** * 메서드 * 인스턴스 name field를 리턴하는 메서드. - getter * @return name. */ public String getName() { return this.name; } /** * 메서드 * 인스턴스 age field를 리턴하는 메서드. - getter * @return age. */ public int getAge() { return this.age; } /** * method * 인스턴스 age field를 변경, 수정하는 메서드. - setter * @param age */ public void setAge(int age) { this.age = age; } // 특별한 경우가 아니면 setter는 void 타입으로 만들어진다. }
  • Spring Framework가 getter나 setter 메서드가 필요하다고 생각되면, 관습상의 CamelCase로 작성된 메서드명을 유추하여 호출함
    • 이러한 이유 때문에 변수 명이 길어 게터나 세터 메서드의 이름이 길어지더라도, getMemberPassword와 같이 관습상의 작명 그대로 붙여주는 것이 좋음

데이터 무결성

  • 만약 나이 데이터를 갖고 있으면, 나이는 0 이상 몇 이하의 양수여야 한다.
  • 이를 검증하고, 함부로 데이터 조작이 되지 않기 위해서 Setter를 이용한다,

E.mini project :연락처 프로그램 v0.1

brain storming

  • 기능 : 저장, 삭제, 변경, 검색
  • 데이터 : 이름, 전화 번호, 프로필 사진, 메모, 생일, 주소, 이메일, …
  • 연락처 클래스 : 이름, 전하번호, 메일
  • 연락처 클래스 배열
  1. UI (User Interface) - 콘솔 입출력
  2. 1. 전체 목록 | 2. 저장 123132| 3. 검색 | 4. 삭제 | 5. 수정 | 6. 종료
  3. 1 선택 → 배열 내용 출력 → 1) UI 보여줌
  4. 2 선택 → 저장할 내용 입력 & 저장 → 1) UI
  5. 3 선택 → 인덱스 검색 → 1) UI
  6. 4 선택 → 변경할 인덱스 입력 & 수정
  7. 5 선택 → 미뤄둘게….*
  8. 6 선택 ⇒ 프로그램 종
package com.itwill.ver01;

import java.util.Scanner;

public class ContactMain01 {
	public static final int MAX_LENGTH = 2; // 배열의 길이

	Scanner scanner = new Scanner(System.in);
	Contact[] contacts = new Contact[MAX_LENGTH];
	private int cnt = 0; // 배열 contacts에 저장된 연락처 개수
	// 배열에 새로운 연락처가 저장될 때마다 증가

	public static void main(String[] args) {
		System.out.println("******************* 연락처 프로그램 v0.1 *******************");

		ContactMain01 app = new ContactMain01();

		boolean run = true; // 프로그램 실행/ 종료 여부를 저장하는 변수

		while (run) {
			int selected = app.showMainMenu();

			switch (selected) {
			case 0:
				run = false;
				break;
			case 1: // 배열에 저장된 전체 목록 출력
				app.readAllContact();
				break;
			case 2: // 새 연락처 등록
				app.createContact();
				break;
			case 3:
				app.searchContact();
				break;
			case 4:
				app.modifyContact();
				break;
			case 5:
				app.deleteContact();
			default:
				System.out.println("메뉴를 다시 선택하세요..");
				break;
			}

		}
		System.out.println("******************* 프로그램 종료 *******************");
	} // end main

	/**
	 * 사용자에게 실행 메뉴를 보여주고, 선택한 값을 반환하는 메서드
	 * 
	 * @return 선택된 메뉴값
	 */
	private int showMainMenu() {
		System.out.println(" -----------------------------------------");
		System.out.println("| 0. 종료 | 1. 목록 | 2. 저장 | 3. 검색 | 4. 수정 |");
		System.out.println(" -----------------------------------------");
		System.out.print("선택 >>> ");

//		int menu = Integer.parseInt(scanner.nextLine());

		return Integer.parseInt(scanner.nextLine());
	}

	/**
	 * contacts 내에 저장되어있는 연락처 정보들을 콘솔로 보여주는 메서드
	 */
	private void readAllContact() {
		System.out.println("****************연락처 목록******************");
		if (cnt == 0) {
			System.out.println("저장된 연락처가 없습니다..");
		}
		for (int i = 0; i < cnt; i++) {
			System.out.printf("[%d] : %s\\n", i, contacts[i]);
		}
		System.out.println("*****************************************");
	}

	/**
	 * 사용자로부터 이름, 전화번호, 이메일을 받아 contacts 배열 내에 저장하는 메서드. exception : 만약 입력되지 않은 정보가
	 * 있다면, 저장 과정을 다시 수행함.
	 */
	private void createContact() {
		if (cnt >= MAX_LENGTH) {
			System.out.println("저장가능한 연락처 수를 초과했습니다..");
			return;
		}

		System.out.print("이름 입력 >>> ");
		String name = scanner.nextLine();
		System.out.print("전화번호 입력 (-포함) >>> ");
		String phone = scanner.nextLine();
		System.out.print("이메일 입력 >>> ");
		String email = scanner.nextLine();

		if (name.length() == 0 || phone.length() == 0 || email.length() == 0) {
			System.out.println("------------------------");
			System.out.println("정보 입력은 필수입니다...\\n다시 입력하세요.");
			System.out.println("------------------------");
			createContact();
			return;
		}

		contacts[cnt++] = new Contact(name, phone, email);
	}

	private void searchContact() {
		System.out.print("검색할 인덱스 입력 >>> ");

		int idx = Integer.parseInt(scanner.nextLine());
		if (contacts[idx] == null) {
			System.out.println("해당 인덱스에 저장된 번호가 없습니다..");
			return;
		}
		System.out.println(contacts[idx]);
	}

	/**
	 * 사용자로부터 contacts 배열 내의 idx를 받아, 해당 위치에 있는 회원정보를 수정하는 메서드
	 * 
	 */
	private void modifyContact() {
		System.out.println("몇 번째 연락처를 수정하시겠습니까?");
		System.out.print("인덱스 입력 >>> ");

		int idx = Integer.parseInt(scanner.nextLine());

		System.out.println(contacts[idx] + "를 수정하시겠습니까?");

		System.out.println("[0] 진행 | [이외 숫자] 중지");
		boolean 진행여부 = (Integer.parseInt(scanner.nextLine()) == 0);
		// -> 입력 받는 scanner 코드와 이를 활용하여 boolean 값을 초기화하는 코드가 한 줄에 있는데, 별로일듯,,

		if (!진행여부) {
			System.out.println("선택 화면으로 돌아갑니다.");
			return;
		}

		System.out.println("수정 진행");
		System.out.println("수정 사항 제외 Enter 입력");

		System.out.print("이름 입력 >>> ");
		String name = scanner.nextLine();
		System.out.print("전화번호 입력 (-포함) >>> ");
		String phone = scanner.nextLine();
		System.out.print("이메일 입력 >>> ");
		String email = scanner.nextLine();

		if (name.length() == 0 && phone.length() == 0 && email.length() == 0) {

			System.out.println("------------------------");
			System.out.println("수정할 정보는 필수입니다...\\n다시 진행하세요.");
			System.out.println("------------------------");

			modifyContact();
			return;
		}

		if (name.length() > 0)
			contacts[idx].setName(name);
		if (phone.length() > 0)
			contacts[idx].setPhone(phone);
		if (email.length() > 0)
			contacts[idx].setEmail(email);

	}

	private void deleteContact() {
		System.out.println("------------------------");
		System.out.println("몇 번째 연락처를 삭제할까요?");
		System.out.println("------------------------");

		int idx = Integer.parseInt(scanner.nextLine());
		if(idx>=MAX_LENGTH) {
			System.out.println("해당하는 연락처가 없습니다..");
			System.out.println("목록으로 돌아갑니다..");
			return;			
		} 

		Contact cont = contacts[idx];

		if(cont==null) {
			System.out.println("해당하는 연락처가 없습니다..");
			System.out.println("목록으로 돌아갑니다..");
			return;
		} 
		
		for (int i = idx; i < MAX_LENGTH - 1; i++) {
			contacts[i] = contacts[i + 1];
		}
		contacts[MAX_LENGTH - 1] = null;
		cnt--;

		System.out.println(cont+ " 연락처가 삭제되었습니다.");
	}

} // end class

'programming > Java [notion 정리본 업로드]' 카테고리의 다른 글

추상클래스와 인터페이스  (0) 2024.04.13
상속  (0) 2024.04.13
연산자  (0) 2024.04.13
조건문과 반복문  (0) 2024.04.13
변수 & 데이터 타입  (0) 2024.04.13