일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바의 정석 기초편 ch14
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch1
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch13
- 스프링 db2 - 데이터 접근 기술
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch9
- 자바의 정석 기초편 ch11
- 게시글 목록 api
- 자바의 정석 기초편 ch8
- @Aspect
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch7
- 스프링 고급 - 스프링 aop
- 스프링 mvc1 - 서블릿
- 코드로 시작하는 자바 첫걸음
- jpa 활용2 - api 개발 고급
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch5
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch3
- 스프링 mvc2 - 검증
- 스프링 입문(무료)
- 자바의 정석 기초편 ch6
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch4
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch12
- 타임리프 - 기본기능
- Today
- Total
나구리의 개발공부기록
생성자, 생성자 - 필요한 이유, this, 생성자 - 도입, 기본 생성자, 생성자 - 오버로딩과 this(), 문제와 풀이 본문
생성자, 생성자 - 필요한 이유, this, 생성자 - 도입, 기본 생성자, 생성자 - 오버로딩과 this(), 문제와 풀이
소소한나구리 2024. 12. 28. 11:44출처 : 인프런 - 김영한의 실전 자바 - 기본편 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 생성자 - 필요한 이유
1) 생성자
(1) 필요한 이유
- 객체를 생성하는 시점에 어떤 작업을 하고 싶다면 생성자(Constructor)를 이용하면 됨
(2) MemberInit
package construct;
public class MemberInit {
String name;
int age;
int grade;
}
(3) MethodInitMain1
- 회원 객체를 사용하기 제대로 사용하기 위해서는 회원 객체를 생성하고 나면 name, age, grade같은 변수에 초기값을 설정해야 하는데 코드를 보면 회원의 초기값을 설정하는 부분이 계속 반복되고 있음
package construct;
public class MethodInitMain1 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
member1.name = "user1";
member1.age = 15;
member1.grade = 90;
MemberInit member2 = new MemberInit();
member2.name = "user2";
member2.age = 16;
member2.grade = 80;
MemberInit[] members = {member1, member2};
for (MemberInit member : members) {
System.out.println("이름:" + member.name + " 나이:" + member.age + " 성적:" + member.grade);
}
}
}
/* 출력 결과
이름:user1 나이:15 성적:90
이름:user2 나이:16 성적:80
*/
(3) MethodInitMain2
- initMember(...) 메서드를 사용하여 반복을 제거했지만 이 메서드는 대부분 MemberInit 객체의 멤버 변수를 사용하고 있음
- 이런 경우에는 속성과 기능을 한 곳에 두는것이 객체 지향 관점에서 더 나은 방법임
package construct;
public class MethodInitMain2 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
initMember(member1, "user1", 15, 90);
MemberInit member2 = new MemberInit();
initMember(member2, "user2", 16, 80);
MemberInit[] members = {member1, member2};
for (MemberInit member : members) {
System.out.println("이름:" + member.name + " 나이:" + member.age + " 성적:" + member.grade);
}
}
static void initMember(MemberInit member, String name, int age, int grade) {
member.name = name;
member.age = age;
member.grade = grade;
}
}
2. this
1) this
(1) MemberInit - initMember() 추가
- void initMember() 메서드를 추가
- this로 내 멤버변수로 접근할 수 있음
package construct;
public class MemberInit {
String name;
int age;
int grade;
void initMember(String name, int age, int grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}
(2) MethodInitMain3
- initMember()는 Member에 초기값 설정 기능을 제공하는 메서드로 member1.initMember("user1", 15, 90)과 같이 호출하면 객체의 멤버 변수에 인자로 넘어온 값을 채움
package construct;
public class MethodInitMain3 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
member1.initMember("user1", 15, 90);
MemberInit member2 = new MemberInit();
member2.initMember("user2", 16, 80);
MemberInit[] members = {member1, member2};
for (MemberInit member : members) {
System.out.println("이름:" + member.name + " 나이:" + member.age + " 성적:" + member.grade);
}
}
}
/* 출력결과
이름:user1 나이:15 성적:90
이름:user2 나이:16 성적:80
*/
(3-1) this
- initMember(String name, ...)의 코드를 보면 메서드의 매개변수에 정의한 String name과 Member의 멤버 변수의 이름이 String name 으로 똑같고 나머지 변수도 모두 똑같음
- 멤버 변수와 메서드의 매개변수의 이름이 같으면 멤버 변수보다 매개변수가 코드 블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가지게 됨
- 따라서 initMember(String name, ...) 메서드 안에서 name이라고 적으면 매개변수에 접근하게 되는데, 멤버 변수에 접근하려면 앞에 this. 이라고 해주면 되며 여기서 this는 인스턴스 자신의 참조값을 가리킴
(3-2) 진행과정
- this.name = name; -> 1. 오른쪽의 name은 매개변수에 접근
- this.name = "user"; -> 2. name 매개변수의 값 사용
- 참조값.name = "user"; -> 3. this.은 인스턴스 자신의 참조값을 뜻함, 따라서 인스턴스의 멤버 변수에 접근
(3-3) this제거
- 만약 이 예제에서 this를 제거하게 되면 name = name 모두 매개변수를 뜻하게 되어 멤버변수의 값이 변경되지 않음
(3-4) 정리
- 매개변수의 이름과 멤버 변수의 이름이 같은 경우 this를 사용하여 둘을 명확하게 구분해야 함
- this는 인스턴스 자신을 가리킴
(4) this의 생략 - MemberThis
- this는 생략할 수 있는데 이 경우에는 변수를 찾을 때 가까운 지역변수(매개변수도 지역변수임)를 먼저 찾고 없으면 그 다음으로 멤버 변수를 찾고 멤버 변수도 없으면 오류가 발생함
- 아래의 예제는 필드 이름과 매개변수의 이름이 서로 다른 상황인데 nameField는 앞에 this가 없어도 멤버 변수에 접근함
- nameField는 먼저 지역변수에서 같은 이름이 있는지 찾는데 지역변수에는 nameField가 없으므로 멤버 변수에서 찾음
- nameParameter도 먼저 지역변수에서 같은 이름이 있는지 찾는데 지역변수에 nameParameter가 있으므로 매개변수를 사용함
package construct;
public class MemberThis {
String nameField;
void initMember(String nameParameter) {
nameField = nameParameter;
}
}
(5) this와 코딩 스타일
public class MemberThis {
String nameField;
void initMember(String nameParameter) {
this.nameField = nameParameter;
}
}
- 지금 처럼 멤버 변수에 접근하는 경우 항상 this를 사용하는 코딩 스타일도 있음
- this.nameField를 보면 this를 생략할 수 있지만 생략하지 않고 사용해도되며 이렇게 this를 사용하면 이 코드가 멤버 변수를 사용한다는 것을 눈으로 쉽게 확인할 수 있음
- 과거에는 이런 스타일을 많이 사용하여 지역 변수와 멤버 변수를 눈에 잘 보이도록 코드를 작성하였는데 최근에는 IDE가 발전하면서 멤버 변수와 지역 변수를 색상으로 구분을 해줌
- 그렇기 때문에 최근에는 이런 경우에는 this를 사용하지 않는 코딩 스타일이 더욱 많이 쓰이기 때문에 지역변수와 멤버변수의 이름이 같을때에만 구분을 위하여 this를 사용하는 것을 권장함
** 참고
- 관습을 따를 때에는 왜? 라는 질문을 하고 따르는 것을 권장하는데 관습을 따르는 이유를 생각했을 때 굳이 쓸 이유가 없다면 그 관습은 굳이 따르지 않아도 된다고 생각한다고 함
- this의 경우도 멤버변수와 지역변수를 명확히 구분하기위해 this를 항상 붙이는 코딩 스타일을 당연히 써야했지만 지금은 IDE가 색깔로 구분을 해주기 때문에 굳이 명확히 구분되는 상황에서는 사용할 필요가 없다고 생각한다고 함
3. 생성자 - 도입
1) 생성자 도입
(1) 초기값 할당
- 프로그래밍을 하다보면 객체를 생성하고 이후에 바로 초기값을 할당해야하는 경우가 많아 앞서 만든 initMember()와 같은 메서드를 매번 만들어야 할 수 있음
- 그래서 대부분의 객체 지향 언어는 객체를 생성하자마자 즉시 필요한 기능을 좀 더 편리하게 수행할 수 있도록 생성자라는 기능을 제공하며 생성자를 사용하면 객체를 생성하는 시점에 즉시 필요한 기능을 수행할 수 있음
(2) MemberConstruct
- Public MemberConstruct(...) 부분이 바로 생성자인데 메서드와 비슷하지만 차이가 있음
- 생성자의 이름은 클래스 이름과 같아야 하므로 첫 글자도 대문자로 시작함
- 생성자는 반환 타입이 없어서 비워두어야 함
- 나머지는 메서드와 같음
package construct;
public class MemberConstruct {
String name;
int age;
int grade;
public MemberConstruct(String name, int age, int grade) {
System.out.println("생성자 호출 name= " + name + ", age= " + age + ", grade= " + grade);
this.name = name;
this.age = age;
this.grade = grade;
}
}
(2) ConstructMain1
- 객체를 생성할 때 new 키워드 다음에 입력했던 것이 바로 생성자였음
- 실행 해보면 생성자에 입력한 출력문과 반복문으로 출력한 출력문이 함께 출력됨
package construct;
public class ConstructMain1 {
public static void main(String[] args) {
MemberConstruct member1 = new MemberConstruct("user1", 15, 90);
MemberConstruct member2 = new MemberConstruct("user2", 16, 80);
MemberConstruct[] members = {member1, member2};
for (MemberConstruct member : members) {
System.out.println("이름: " + member.name + " 나이: " + member.age + " 성적: " + member.grade);
}
}
}
/* 실행 결과
생성자 호출 name= user1, age= 15, grade= 90
생성자 호출 name= user2, age= 16, grade= 80
이름: user1 나이: 15 성적: 90
이름: user2 나이: 16 성적: 80
*/
(3) 생성자 호출
- 생성자는 인스턴스를 생성하고 나서 즉시 호출되며 생성자를 호출하는 방법은 new 명령어 다음에 생성자 이름과 매개변수에 맞추어 인수를 전달하면됨
- 생성자 이름이 클래스이름이기 때문에 new 생성자이름(), new 클래스 이름() 모두 맞는 표현임
- new MemberConstruct("user1", 15, 90)으로 생성자를 호출하면 인스턴스를 생성하고 즉시 해당 생성자를 호출하는데 인스턴스를 생성할 때 메모리게 값을 할당하고 생성자를 호출할 때 값을 할당하는 순서로 동작하게 됨
- new 키워드를 사용해서 객체를 생성할 때 마지막에 괄호()도 포함해야 하는 이유가 바로 생성자 때문이며 객체를 생성하면서 동시에 생성자를 호출한다는 의미를 포함함
2) 생성자 장점
(1) 중복 호출 제거
- 생성자가 없던 시절에는 생성 직후에 어떤 작업을 수행하기 위해 메서드를 직접 한번 더 호출해야 했음
- 그러나 생성자 덕분에 객체를 생성하면서 동시에 생성 직후에 필요한 작업을 한번에 처리할 수 있게 되었음
//생성자 등장 전
MemberInit member = new MemberInit();
member.initMember("user1", 15, 90);
//생성자 등장 후
MemberConstruct member = new MemberConstruct("user1", 15, 90);
(2) 제약 - 생성자 호출 필수
- 위에서 작성한 생성자 등장 전 코드에서 initMember(...)를 실수로 호출하지 않게되면 프로그램은 작동하지만 회원의 이름과 나이 성적 데이터가 없는 상태로 프로그램이 동작하게됨
- 만약 이 값들을 필수로 반드시 입력해야 한다면 시스템에 큰 문제가 발생할 수 있으며 아무 정보가 없는 유령 회원의 시스템 내부에 등장하게 됨
- 생성자의 진짜 장점은 객체를 생성할 때 직접 정의한 생성자가 있다면 직접 정의한 생성자를 반드시 호출해야한다는 점임
- 예제로 작성한 MemberConstruct 클래스의 경우 생성자를 직접 정의했기 때문에 직접 정의한 생성자를 반드시 호출해야 함
- 참고로 생성자를 메서드 오버로딩 처럼 여러개 정의할 수 있는데 지금과 같은 경우에는 하나만 호출하면 됨
- 만약 직접 정의한 생성자를 호출하지 않으면 컴파일 오류가 발생하며 개발자는 객체를 생성할 때 직접 정의한 생성자를 필수로 호출해야 한다는 것을 바로 알 수 있어 실수를 방지하고 빠르게 문제를 수정할 수 있게 됨
- 즉 생성자 덕분에 회원의 이름, 나이, 성적은 항상 필수로 입력하게 되며 아무 정보가 없는 유령 회원이 시스템 내부에 등장할 가능성을 원천 차단할 수 있게 됨
- 생성자를 사용하면 필수값 입력을 보장할 수 있음
** 참고
- 좋은 프로그램은 무한한 자유도가 주어지는 프로그램이 아니라 적절한 제약이 있는 프로그램임
4. 기본 생성자
1) 기본 생성자
(1) 설명
- 과거 예제를 보면 생성자를 만들지 않았는데 생성자를 호출했었음
- new MemberInit() 이 부분은 분명히 매개변수가 없는 생성자가 필요할텐데 그냥 호출하여 객체를 생성할 수 있었음
MemberInit member1 = new MemberInit();
- 매개변수가 없는 생성자를 기본 생성자라고 하는데 클래스에 생성자가 하나도 없으면 자바 컴파일러는 매개변수가 없고 작동하는 코드가 없는 기본 생성자를 자동으로 만들어줌
- 생성자가 하나라도 있으면 자바는 기본 생성자를 만들지 않음
- 즉 MemberInit 클래스의 경우 생성자를 직접 만들지 않았으므로 자바가 자동으로 기본 생성자를 만들어준 것임
(2) MemberDefault
- 생성자를 직접 정의하지않은 MemberDefault 클래스
package construct;
public class MemberDefault {
String name;
}
(3) MemberDefaultMain
- MemberDefault 클래스에는 생성자가 하나도 없기 때문에 자바는 기본 생성자를 만들어주며 우리눈에는 보이지 않음
- 그래서 new MemberDefault()를 호출하여 객체를 생성할 수 있음
package construct;
public class MemberDefaultMain {
public static void main(String[] args) {
MemberDefault memberDefault = new MemberDefault();
}
}
(4) MemberDefault - 기본 생성자
- 예제와 같은 코드처럼 자바가 기본 생성자를 만들어 준다고 보면됨
- 참고로 자바가 자동으로 생성해주는 기본 생성자는 클래스와 같은 접근 제어자를 가지는데 접근제어자에 대한 내용은 뒤에서 자세히 설명함
package construct;
public class MemberDefault {
String name;
public MemberDefault() {
}
}
(5) MemberDefault - 기본 생성자 직접 정의
- 이렇게 직접 기본 생성자를 정의할 수 있으며 MemberDefaultMain의 코드를 실행해보면 해당 출력문이 출력되는 것을 확인할 수 있음
package construct;
public class MemberDefault {
String name;
public MemberDefault() {
System.out.println("생성자 호출");
}
}
(6) 기본 생성자를 자동으로 만들어지는 이유
- 만약 자바에서 기본 생성자를 만들어주지 않으면 생성자 기능이 필요하지 않은 경우에도 모든 클래스에 개발자가 직접 기본 생성자를 정의해야함
- 생성자 기능을 사용하지 않는 경우도 많기 때문에 이런 편의 기능을 제공하는 것임
(7) 정리
- 생성자는 반드시 호출되어야 함
- 생성자가 없으면 기본 생성자가 제공됨
- 생성자가 하나라도 있으면 기본 생성자가 제공되지 않으며 이 경우 개발자가 정의한 생성자를 직접 호출해야 함
5. 생성자 - 오버로딩과 this()
1) 생성자 오버로딩
(1) MemberConstruct - 생성자 추가
- 기존 Memberconstruct에 매개변수 개수가 다른 생성자를 하나 추가하여 생성자가 2개가 되었으며 새로 추가한 생성자는 grade는 받지 않지만 grade가 50점으로 고정됨
package construct;
public class MemberConstruct {
String name;
int age;
int grade;
public MemberConstruct(String name, int age) {
this.name = name;
this.age = age;
this.grade = 50;
}
public MemberConstruct(String name, int age, int grade) {
System.out.println("생성자 호출 name= " + name + ", age= " + age + ", grade= " + grade);
this.name = name;
this.age = age;
this.grade = grade;
}
}
(2) ConstructMain2
- 생성자를 오버로딩한 덕분에 성정 입력이 꼭 필요한 경우에는 grade가 있는 생성자를 호출하면 되고 그렇지 않은 경우에는 grade가 없는 생성자를 호출하면 됨
- grade가 없는 생성자를 호출하면 성적은 50점으로 고정됨
package construct;
public class ConstructMain2 {
public static void main(String[] args) {
MemberConstruct member1 = new MemberConstruct("user1", 15, 90);
MemberConstruct member2 = new MemberConstruct("user2", 16);
MemberConstruct[] members = {member1, member2};
for (MemberConstruct member : members) {
System.out.println("이름: " + member.name + " 나이: " + member.age + " 성적: " + member.grade);
}
}
}
/* 실행 결과
생성자 호출 name= user1, age= 15, grade= 90
이름: user1 나이: 15 성적: 90
이름: user2 나이: 16 성적: 50
*/
2) this()
(1) MemberConstruct - this() 사용
- 위에서 생성한 두 생성자를 비교해보면 코드가 중복되는 부분이 있는데 이때 this()라는 기능을 사용하면 생성자 내부에서 자신의 생성자를 호출할 수 있음
- this는 인스턴스 자신의 참조값을 가리키므로 자신의 생성자를 호출한다고 생각하면 됨
- 첫번째 생성자 코드를 보면 this()를 활용하여 생성자 내부에서 두번째 생성자를 호출하도록 작성할 수 있으며 이 부분을 잘 활용하면 코드 중복을 제거할 수 있게됨
- 코드의 동작은 위에서 작성한 코드와 똑같이 동작함
package construct;
public class MemberConstruct {
String name;
int age;
int grade;
public MemberConstruct(String name, int age) {
this(name, age, 50);
}
public MemberConstruct(String name, int age, int grade) {
System.out.println("생성자 호출 name= " + name + ", age= " + age + ", grade= " + grade);
this.name = name;
this.age = age;
this.grade = grade;
}
}
(2) this() 규칙
- this()는 생성자 코드의 첫줄에만 작성할 수 있으며 아래처럼 작성하게 되면 규칙 위반이 되어 컴파일 오류가 발생함
public MemberConstruct(String name, int age) {
System.out.println("go");
this(name, age, 50);
}
6. 문제와 풀이
1) Book과 생성자
(1) 요구사항
- BookMain 코드가 작동하도록 Book 클래스를완성
- 특히Book 클래스의 생성자 코드에 중복이 없도록 주의
(2) 문제
package construct.ex;
public class Book {
String title;
String author;
int page;
// TODO 코드를 완성하세요.
}
package construct.ex;
public class BookMain {
public static void main(String[] args) {
// 기본 생성자 사용
Book book1 = new Book();
book1.displayInfo();
// title과 author만을 매개변수로 받는 생성자
Book book2 = new Book("Hello Java", "Seo");
book2.displayInfo();
// 모든 필드를 매개변수로 받는 생성자
Book book3 = new Book("JPA 프로그래밍", "kim", 700);
book3.displayInfo();
}
}
제목: , 저자: , 페이지: 0
제목: Hello Java, 저자: Seo, 페이지: 0
제목: JPA 프로그래밍, 저자: kim, 페이지: 700
(3) 정답
package construct.ex;
public class Book {
String title;
String author;
int page;
public void displayInfo() {
System.out.println("제목: " + title + " , 저자: " + author + " , 페이지: " + page);
}
public Book() {
this("", "", 0);
}
public Book(String title, String author) {
this(title, author, 0);
}
public Book(String title, String author, int page) {
this.title = title;
this.author = author;
this.page = page;
}
}
package construct.ex;
public class BookMain {
public static void main(String[] args) {
// 기본 생성자 사용
Book book1 = new Book();
book1.displayInfo();
Book book2 = new Book("Hello Java", "Seo");
book2.displayInfo();
Book book3 = new Book("JPA 프로그래밍", "kim", 700);
book3.displayInfo();
}
}
제목: , 저자: , 페이지: 0
제목: Hello Java , 저자: Seo , 페이지: 0
제목: JPA 프로그래밍 , 저자: kim , 페이지: 700
'인프런 - 실전 자바 로드맵 > 실전 자바 - 기본편' 카테고리의 다른 글
접근제어자, 접근 제어자 이해, 접근 제어자 종류, 접근 제어자 사용(필드,메서드,클래스레벨) 캡슐화, 문제와 풀이 (0) | 2025.01.04 |
---|---|
패키지, 패키지(시작 / import) 패키지 규칙, 패키지 활용 (0) | 2024.12.28 |
객체 지향 프로그래밍, 절차 지향 프로그래밍, 클래스와 메서드, 문제와 풀이 (0) | 2024.12.25 |
기본형과 참조형, 기본형 vs 참조형, 참조형과 메서드 호출, 변수와 초기화, null, NullPointerException, 문제와 풀이 (1) | 2024.12.21 |
클래스와 데이터, 프로젝트 환경 구성, 클래스가 필요한 이유, 클래스 도입, 객체 사용, 클래스, 객체, 인스턴스 정리, 배열 도입, 문제와 풀이 (2) | 2024.12.20 |