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가 불가능하다.
- 생성자 : constructor - 클래스에 선언된 변수(필드)들을 초기화
- 생성자 호출, 객체 생성, 인스턴스
- 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. 메서드 선언하기
- 선언부(리턴 타입, 메서드 이름, 매개 변수 선언)와 실행 블록으로 구분
- 리턴 타입 : 리턴 값의 타입
- void : 실행하지만 반환 값이 없는 경우
- 데이터 타입 : 메서드의 반환 타입과 동일한 타입의 변수에 대입하여 사용
- 만약 사용하지 않고 실행시키는 목적만 있을 경우, 꼭 변수에 할당하지 않아도 됨.
-
- 숫자로 시작하면 안되고, $와 _를 제외한 특수 문자 사용이 불가능
- 관례적으로 메서드 이름은 소문자로 작성
- CamelCase로 작성메서드 이름 : 자바 식별자 규칙에 맞게 작성
- 매개 변수 선언
- 메서드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용
- 매개 변수가 없는 경우, 소괄호()는 작성하고, 내부를 비워놓으면 된다.
💡 메서드 이름은 어떤 기능을 수행하는지 쉽게 알 수 있는 기능 이름으로 지어주는 것이 좋음. 메서드 이름의 길이는 프로그램 실행과는 무관, 너무 짧게 주지 않도록!
매개 변수의 개수를 모를 경우
가변 길이 인수 (variable-length argument)
- 메서드 호출 시 아규먼트의 개수가 정해져 있지 않을 경우
- 이에 대한 해결책으로 (1)매개 변수 타입을 배열로 선언하거나 (2)매개 변수를 …으로 선언하기의 방법이 있음
- 매개 변수를 배열 타입으로 선언하기
- 매개 변수를 int 배열로 받고 있으며 호출시 다음과 같이 사용할 수 있음
- argument로 배열 넣을 때 {1,2,3}과 같이 배열 생성과 메서드 호출을 같이 할 수 없는 것을 주의하자!
- int[] values = {1, 2, 3}; int result = sum1 (values); int result = sum1 (new int[] {1,2,3,4,5});
- 매개 변수를 int 배열로 받고 있으며 호출시 다음과 같이 사용할 수 있음
- int sum1(int[] values) {... ...};
- 매개 변수를 … 사용하여 받기
- 매개변수를 배열 타입을 선언하면 메서드를 호출하기 전 배열을 생성해야 하는 불편함이 있음.
- 배열을 생성하지 않고 값의 목록만 받을 수 있도록 한 방법
- 가변인수를 갖는 메서드를 사용할 때는 전달하는 아규먼트 개수에 제한이 없음
- 전달하지 않아도 괜춚!
- 가변인수를 갖는 메서드를 사용할 때는 전달하는 아규먼트 개수에 제한이 없음
- 값의 목록만 넘겨주면, values라는 지역 변수에 값의 목록으로 생성된 배열이 할당되어 있음. 사용 방법은 다음과 같음
- int result = sum (1,2,3,4);
- 실제 sum2 메서드 내에서 values는 다음과 같음
- values = {1, 2, 3, 4};
- 첫번째 인자가 아닌 마지막 인자로 사용할 수 있고, 그 예시로 printf 메서드가 있음.
- 가변 인수를 저장하는 파라미터는 반드시 가장 마지막 파라미터여야 함
- 가변인수를 저장하는 파라미터는 2개 이상 있을 수 없음 </aside>
- /** * 아규먼트로 전달받은 모든 정수들을 다 더해서 리턴 아규먼트가 없는 경우, 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); }
- 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 문장을 추가하여 프로그램 실행을 멈추는 패턴을 많이 이용.
- 메서드 종료
- (return 문장에 값이 있으면), 값을 메서드 호출한 곳에 반환
C. 한정자
C-1. Access modifier [접근 한정자] (1)클래스 멤버
- 클래스 멤버(필드, 생성자, 메서드)의 접근(가시성) 수식어
- 클래스 멤버들을 보여주는 범위를 설정
- 가시성의 범위 : private < default (package) < protected < public
- private
- 선언된 클래스 내부에서만 보이는(접근 가능한) 멤버
- default (package)
- 수식어가 없음
- 같은 패키지에 있는 클래스에서 보이는(접근 가능한) 멤버
- protected
- 같은 패키지에 있거나 상속하는 클래스에서 보이는 (접근 가능한) 멤버
- public
- 어떤 곳에서나 보이는 (접근 가능한) 멤버
- private
- 한정자 예제
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 문이 필요하다.
- 다른 패키지에서 선언된 클래스 이름으로 변수를 선언하거나 사용하려고 할 때
- import 문장을 작성하고, 클래스 이름만 사용
- 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
- public : 어디서나 공개된 클래스, import 문장을 사용할 수 있음
- package : 수식어가 없는 경우. 같은 패키지의 클래스에서만 사용할 수 있는 클래스
- Access / Visibility modifier
<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 필드 지역 변수는 초기화된 값을 변경할 수 없는 변수, 즉 상수
- 변경할 수 없는, 즉 상수를 만들 때 사용
- 컴파일 에러. 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 필드 ⁉️
- 선언과 동시에 초기화하거나
- 반드시 아규먼트를 갖는 생성자를 선언해서 명시적으로 초기화해야 함
- 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 멤버
- 인스턴스 멤버 (필드, 메서드) :
- static 수식어가 사용되지 않은 멤버
- 객체를 생성한 후에 참조변수를 이용해서 사용하는 멤버
- JRE(Java Runtime Environment)이 사용하는 메모리 공간 중에서 힙(heap)에 생성되는 변수
- static (정적) 멤버 → 필드, 메서드
- static 수식어가 사용된 멤버
- 객체를 생성하지 않아도 사용할 수 있는 멤버들
- ex) Integer.MAX_VALUE, Math.PI, Math.sqrt(), …
- 클래스 이름을 이용해서 사용하는 멤버
- JRE가 사용하는 메모리 공간 중에서 메서드 영역에 생성되는 변수
- 프로그램의 메인 메서드가 호출되기 전에 프로그램 로딩 시점에 메모리에 생성된다.
- 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
- 기능 : 저장, 삭제, 변경, 검색
- 데이터 : 이름, 전화 번호, 프로필 사진, 메모, 생일, 주소, 이메일, …
- 연락처 클래스 : 이름, 전하번호, 메일
- 연락처 클래스 배열
- UI (User Interface) - 콘솔 입출력
- 1. 전체 목록 | 2. 저장 123132| 3. 검색 | 4. 삭제 | 5. 수정 | 6. 종료
- 1 선택 → 배열 내용 출력 → 1) UI 보여줌
- 2 선택 → 저장할 내용 입력 & 저장 → 1) UI
- 3 선택 → 인덱스 검색 → 1) UI
- 4 선택 → 변경할 인덱스 입력 & 수정
- 5 선택 → 미뤄둘게….*
- 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 |