출처 | 인프런 즐거운 자바
System.out.println("Hello");
컴퓨터 프로그래밍의 패러다임 중 하나이다. 객체지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, "객체"들의 모임으로 파악하고자 하는 것이다.
Book b = new Book();
#생성자 부분 new "Book"
# b는 레퍼런스 타입이다.
1. Heap 메모리에서 book이라는 인스턴스가 하나 생성된다.
2. Book를 가리키는 참조 변수가 b다.
인스턴스를 특별한 이름으로 불러주고 싶다면?
-> 참조형 변수를 선언한다.
참조되지 않는 인스턴스는 "쓰레기"
클래스는 필드와 메서드로 구성된다. 클래스를 사용하기 위해선 인스턴스를 선언해야한다. 위의 코드가 선언문장이다.
public class VendingMachine {
public static void main(String[] args)
{
vendingMachine v1 = new vendingMachine();
//vendingMachine이라는 걸 v1이 참조한다.
vendingMachine v2 = new vendingMachine();
}
}
static이 붙은 메소드는 클래스 메소드라고 부른다. 클래스 메소드는 인스턴스를 생성하지 않아도 사용할 수 있다. [사용 가능이라는 말은 메모리에 올라가 있다.] 인스턴스를 만들지 않아도 메모리에 올라가있다.
메소드 안에서 사용하는 것들은 다 의존하는 것이다. VendingMachine 클래스가 있어야만 main 메소드는 컴파일 되고 실행될 수 있다. 그렇기 때문에 의존하는 관계다.
VendingMachineMain은 VendingMachine에 의존한다.
훌룡하고 성장 가능한 시스템을 만들기 위한 핵심은 모듈 내부의 속성과 행동이 어떤가보다 모듈이 어떻게 커뮤니케이션하는가에 달려있다.
-> 객체 지향 프로그래밍을 한다는 것은 메소드가 언제 호출되고 어떻게 호출될까
-> 메소드의 이름은 어떻게 지어야 할까? 어떻게 호출해야 할까?를 고민해야 한다.
main메소드를 생각하면서 메소드 선언 방법을 살펴봐야한다.
메소드 이름은 소문자로 작성
클래스 이름은 대문자
VendingMachine.printVesion();
ex2)
클래스.메소드()로 사용가능하다.
JVM은 PERM 메모리에 올라가 있는 VendingMachineMain 클래스에서 main 메소드를 찾는다.
main 메소드를 찾고 해당 메소드 정보를 Java Stack에 넣어준다.
Java Stack에 저장된 메소드 실행 정보 하나를 스택 엔트리라고 한다. main 메소드 안에 선언된 변수들을 스택 엔트리에 저장한다. 이러한 변수를 로컬변수라고 한다.
main 메소드의 아규먼트인 String[] args인데 Main메소드를 실행하게 되면 Heap메모리에 String배열 인스턴스가 만들어지고 이 인스턴스를 args 변수가 참조하게 된다.
VendingMachine.printVersion(); JVM은 위의 코드를 만나면 printVersion()메소드는 Static 메소드이기 떄문에 실행 가능하다고 판단하고 실행한다.
printVersion()메소드가 실행되면 java Stack에 스택 엔트리가 하나 더 추가 된다.
printVersion() 메소드 안에서 선언된 변수들은 스택 엔트리에 생성된다.
printVersion() 메소드가 실행 후에 종료가 되면 해당 메소드의 실행 정보를 담고 있는 스택엔트리는 자바 스택에서 제거된다.
VendingMachine vm1 = new VendingMachine();
위의 코드를 실행하면서 인스턴스가 생성된다.
vendingMachine 인스턴스를 스택 엔트리의 vm1 변수가 참조를 하게 된다.
VendingMachine vm2 = new VendingMachine();
2번째 VendingMachine 인스턴스도 Heap 메모리에 생성되고 스택 엔트리의 vm2 변수가 참조하게 된다.
pushProductButton() 메소드가 호출되면서 자바 스택엔 스택 엔트리가 생성되고 pushProudctButton() 메소드에 선언된 변수 menuId가 스택 엔트리에 생성된다.
메소드가 종료되면 자바 스택에서 제거된다.
public class MymathTest{
public[private] Mymath(){} #기본생성자 -> 생성자가 없을 경우 자동으로 생성된다.
public static int abs(int x){
if(x<0)
return x * -1;
else
return x;
}
}
if) 생성자를 private라고 설정한다면 인스턴스를 못 만든다.
Math.메소드() 형식으로 쉽게 호출하도록 만들었다. 인스턴스를 생성하지 못하게 하려고 private한 생성자를 생성했다.
[접근제한자][static][final]타입 필드명 [=초기값];
대괄호 안에 있는 내용은 생략가능하다.
접근제한자는 public, protected 아무것도 없는 경우 (default), private이 올 수 있다.
필드명은 식별자 규칙을 따른다. 다만 필드는 첫번째 글자는 소문자로 시작하는 것이 프로그래머 관례다.
타입(type)은 기본형(boolean, byte, char, short, int, long, float, double)과 참조타입(class, 인터페이스, 배열) 등이 나올 수 있다.
초기값이 없을 경우에는 참조형일 경우 null로 boolean형일 경우는 false로 나머지 기본형은 모두 0 값으로 초기화된다.
필드선언예제
String name;
String address = "경기도 고양시";
public int age = 50;
protected boolean flag; #기본값으로 false가 저장된다.
public Class Person{
String name;
String address;
boolean isVip;
}
Person 클래스는 name, address, isVip라는 3개의 필드를 선언하고 있다.
public class Person {
String name;
String address;
boolean isVip;
}
public class PersonTest {
public static void main(String[] args)
{
Person p1 = new Person(); //p1 == null이다.
Person p2 = new Person();
p1.name = "홍길동";
p2.name = "조조";
System.out.println(p1.name);
System.out.println(p1.address);
System.out.println(p1.isVip);
System.out.println(p2.name);
System.out.println(p2.address);
System.out.println(p2.isVip);
}
}
public class PersonTest {
public static void main(String[] args)
{
Person p1 = new Person(); //p1 == null이다.
Person p2 = new Person();
p1.name = "홍길동";
p2.name = "조조";
p1.address = "일산";
p1.isVip = true;
p2.address = "서울";
System.out.println(p1.name);
System.out.println(p1.name.length());
System.out.println(p1.address);
System.out.println(p1.isVip);
System.out.println('------------------');
System.out.println(p2.name);
System.out.println(p2.name.length());
System.out.println(p2.address);
System.out.println(p2.isVip);
}
}
public class Person {
String name;
String address;
boolean isVip;
static int count = 0;
}
Person 인스턴스가 생성될 때, count 변수가 생성될까? Person클래스를 사용하는 PersonTest2 클래스를 작성하겠다.
Public class PersonTest2{
public static void main(String[] args)
{
Person p1 = new Person();
person p2 = new Person();
p1.name = "홍길동";
p2.name = "조조";
System.out.println(p1.name);
System.out.println(p2.name);
System.out.println(p1.count);
System.out.println(p2.count);
p1.count++;
System.out.println(p1.count);
System.out.println(p2.count);
p2.count++;
System.out.println(p1.count);
System.out.println(p2.count);
}
}
--결과값--
홍길동
조조
0
1
1
2
2
>> p1만 증가 시켰는데 p2도 같이 증가 됐다.
java 명령으로 클래스를 실행하는데, 이 java 명령이 JVM이라고 했다. JVM은 CLASSPATH에서 클래스를 찾아 실행한다. CLASSPATH는 모두 대문자로 작성한다.
우리는 클래스 정보 자체는 정적이라고 말한다. 클래스 정보 자체는 실행되는 것이 아니기 때문에, SSD나 하드디스크에 저장되어 있던 클래스를 읽어 들여서 자바가 사용할 수 있는 메모리 영역에 올리게 된다.
클래스 정보를 메모리에 올리게 되는데 이때 클래스에 Static 필드가 있는지 살펴본다.
Person p1 = new Person(); 위의 줄이 실행되려면 Person 클래스가 필요하다. JVM은 CLASSPATH에서 Person 클래스를 찾고 Person 클래스 정보를 메모리에 올린다.
public class Person {
String name; //인스턴스 필드
String address;
boolean isVip;
static int count = 0; // 클래스 필드
public void printName() //인스턴스 메소드
{
System.out.println("내 이름은 " + name);
}
public static void printCount() // 클래스 메소드
{
System.out.println("Count : " + count);
}
}
public class personTest3
{
public static void main(String[] args)
{
Person p1 = new Person();
p1.name = "홍길동";
p1.printName();
Person.printCount();
Person.count++;
p1.printCount();
}
}
static메소드(클래스 메소드)는 클래스명.메소드명() 형태로 실행하는게 좋다. Person.printCount();로 변경해도 결과가 같게 나온다.
printName() 메소드는 인스턴스 메소드이다. 인스턴스 메소드는 메소드를 가지고 있는 클래스가 인스턴스가 돼야지 실행가능하다.
public class Person{
public static void main(String[] args)
{
System.out.println(Person.count);
Person.printCount();
System.out.println(Person.name);
Person.printName();
}
}
여기서 Person.printName은 실행이 안 된다. 그 이유는 Person.name, Person.printName()은 인스턴스 필드, 인스턴스 메소드이기 때문에 인스턴스를 생성하지 않고 사용하면 컴파일이 발생한다.
-> JVM은 Person 클래스 정보를 읽어 들일 때, Person이 가지고 있는 클래스 필드와 클래스 메소드를 사용가능하도록 메모리에 올리게 된다.
-> 인스턴스 필드와 메소드는 그 인스턴스를 참조하는 참조 변수를 이용해서 사용해야 된다.
Person p2 = new Person();
p2.name = "길동차";
클래스 메소드 printCount() 메소드 안에서 인스턴스 필드 name을 사용하려고 하면 컴파일 오류가 발생한다.
public class Person {
String name; //인스턴스 필드
String address;
boolean isVip;
static int count = 0; // 클래스 필드
public void printName() //인스턴스 메소드
{
System.out.println("내 이름은 " + name);
}
public static void printCount() // 클래스 메소드
{
System.out.println("Count : " + count);
Sysyem.out.println(name) -> 실행 안됨
}
}
그러면 printCount 메소드 안에서 name, address 사용이 가능한가?
-> 클래스 메소드 printCount() 메소드 안에서 인스턴스 필드 name을 사용하려고 하면 컴파일 오류가 발생한다.
-> static한 메소드에선 인스턴스 필드나, 인스턴스 메소드를 사용할 수 없다.
-> 메모리에 생성되는 시점이 다르기 때문에 클래스 메소드는 인스턴스가 없어도 사용 가능하지만 인스턴스 필드는 인스턴스가 있어야만 사용하기 때문이다.
-> 클래스 메소드가 실행되는 시점에서는 인스턴스 필드가 메모리에 없으니깐 사용할 수 없다고 컴파일 오류가 발생한다.
클래스 메소드 안에서는 클래스 필드만 사용 가능하다. 제일 중요한 부분
public class Person {
String name; //인스턴스 필드
String address;
boolean isVip;
static int count = 0; // 클래스 필드
static{
count = 100; } //count = 100으로 초기화 할 수 있다.
public void printName() //인스턴스 메소드
{
System.out.println("내 이름은 " + name);
}
public static void printCount() // 클래스 메소드
{
System.out.println("Count : " + count);
Sysyem.out.println(name) -> 실행 안됨
}
}
public class Hello2 {
static int i;
static{
i = 500;
System.out.println("static block"); //원래는 static field를 초기화한다.
}
public static void main(String[] args)
{
System.out.println("hello");
}
}
// javac Hello2.java로 컴파일
// java Hello2 >>CLASSPATH에서 Hello2 클래스를 찾는다. Hello2클래스를 찾고 이 클래스를 읽어 들여서 그 정보를 메모리에 올리게 된다.
// 클래스 필드나 클래스 메소드는 실행 가능한 상태가 된다. -> JVM은 클래스 메소드 중에서 String[] 을 받아들이는 main 메소드를 찾고 실행한다.
// main 보다 더 먼저 작성한다.
소스코드, 클래스 파일 자체는 정적이다.
동적인 것들은 실행이되면서 생성되는 것들을 말한다. 클래스 정보 자체는 정적이다.
Person p1 = new Person();
Person p2 = new Person();
JVM이 실행할 때, Person을 처음 만나게 되면 이 클래스를 읽어 들여서 그 정보를 메모리에 올린 후 이를 이용해 인스턴스를 생성한다. p1 = new Person 부분이다.
그럼 밑에 p2를 실행할 땐, 또 생성하는게 아니다.
이것만 기억하자 !
클래스는 설계도라고 하면 인스턴스가 되어 메모리에 올라갔을 때 이것을 객체라고한다.
응집도가 높다? -> 객체는 책임을 가지고 있다. 관련된 기능을 잘 가지고 있다. 응집도가 높다.
결합도가 낮다? -> 부가적인 장비 즉, 의존도를 의미한다. 결합도가 낮을 수록 좋다.
다형성은 프로그래밍 언어의 자료형 체계의 성질을 나타내는 것이다. 프로그램 언어의 각 요소들(상수,변수,식,오브젝트,함수,메소드)이 다양한 자료형에 속하는 것이 허가 되는 성질을 말한다. 반댓말은 단형성으로 프로그램 언어의 각 요소가 한가지 형태만 가지는 성질을 의미하낟.
System.out.println(...)
public class StandardOutput {
public void println(boolean b)
{
System.out.println(b);
}
public void println(int i )
{
System.out.println(i);
}
public void println(double d )
{
System.out.println(d);
}
public void println(String s )
{
System.out.println(s);
}
public static void main(String[] args)
{
StandardOutput outpur = new StandardOutput();
output.println(100);
output.println("Hello");
output.println(10.5);
output.println(false);
}
}
인스턴스 메소드를 사용하려면 해당 메소드를 가지고 있는 클래스의 인스턴스를 만들어야한다. 그리고 프로그램이 동작하려면 main()메소드가 필요하다.
JVM CLASSPATH에서 StandardOutput 클래스를 찾아서 메모리에 올린다.
클래스 메소드는 사용가능하도록 메모리에 올라가고 인스턴스 메소드는 올라가지 않는다.
package 패키지명;
com.example.util이란 package에 Calculator 클래스를 작성한다.
해당 클래스는 int plus(int,int), int minus(int,int) 메소드를 가진다.
com.example.main이란 package에 CalculatorTest 클래스를 작성한다.
해당 클래스는 Calculator 클래스 인스턴스를 생성한 후, plus, minus메소드를 호출한 결과를 출력한다.
Tree명령(혹은 파일 탐색기)으로 src 폴더 아래를 보면 com 폴더 아래 example 폴더가 생성되어 있고, example 폴더 아래 util폴더가 생성된 것을 알 수 있다.
(.)점은 하위 폴더를 나타낸다.
package com.example.util;
public class Calculator {
//폴더에는 같은 이름의 파일이 여러 개 있을 수 없다.
// 거꾸로 적은 도메인 + 프로젝트 이름(모듈이름) 형태로 하면 충돌할 확률이 적다.
public int plus(int x, int y)
{
return x+y;
}
public int minus(int x, int y)
{
return x-y;
}
}
컴파일 하고 실행할 때, 컴파일 하는 방법이 있다.
실행할때는 반드시 java com.example.util.Calculator라고 실행한다.
package com.example.main;
import com.example.util.Calculator;
public class CalculatorMain {
public static void main(String[] args)
{
Calculator cal = new Calculator();
int value = cal.plus(50,100);
System.out.println(value);
}
}
부모가 가지고 있는 것을 그대로 물려주지만, 일반화 시킨다. 일반화란 자식클래스들을 부모클래스로 부를 수 있는 것을 말한다.
상속 = 일반화 + 확장
상속이란 일반화와 확장이라는 개념을 합한 것. 부모클래스는 상속받는다는 것은 부모가 가지고 있는 것을 자식이 물려받아 사용할 수 있다는 것이다.
(자동차 + 삽 = 포크레인)
상속은 결합도가 높다. 그리고 가장 강한 결합이니 잘못 상속받으면 타격이 너무 크다.
[접근제한자][abstract|final] class 클래스명 extends 부모클래스명
{
}
부모 타입으로 자손 타입을 참조할 수 있다.
Car car = new Bus();
Bus라는 인스턴스가 생겼고 자동차가 생겼다.
버스를 가리키면서 자동차다 라고 말하는건 말이 가능하다. 컴파일 오류가 발생하지 않으려면 Bus는 Car의 자식이나 자손이어야한다.
Bus bus = new Bus(); 이렇게 인스턴스를 사용해도 되는데 왜 참조 타입을 부모타입으로 사용할까?
그림으로 설명하면 Car라는 클래스가 있다.
Bus 인스턴스를 만드는데 참조 타입이 Bus일 경우에는 참조 변수를 이용해 달리다(), 안내방송() 2개의 메소드를 모두 사용할 수 있다.
b1.달리다();
b1.안내방송(); 도 사용가능하다.
new Bus();라고 했는데 자동차의 기능만 이용하고 싶은 경우에는 어떻게 해야 될까?
Car c1 = new Bus();
운전만 할거다 라고 했을때, 위 코드에서 실제로 만들어진건 버스인데 자동차의 기본 기능만 사용할거야 라는 것이다.
bus 메소드는 사용하지 못한다. c1.달리다만 사용가능하다.
ex) 주차요원[발렛파킹] 주차요원이 지붕을 열었다 닫았다 하면서 하면 안 되니깐
뒷부분은 안 보고 앞 부분 타입만 보고 Car의 메소드만 사용한다.
Car car = new Bus();
car.run();
superCar라는 클래스가 있다. 부모가 가지고 있는것을 자식이 동일하게 가지고 있다. 자식 클래스가 메소드를 오버라이딩 하면, 이제 자식에서 선언된 메소드가 사용된다.
Car가 가지고 있는 void run()메소드는 "전륜 구동으로 달린다." 라는 내용을 출력하도록 구현되어 있다.
Car를 상속 받고 void run() 메소드를 오버라이딩 하지 않은 클래스들은 인스턴스를 만들고 run() 메소드를 호출하면 "전륜 구동으로 달린다" 라고 출력한다.
도로를 달리는 자동차를 보면, 자동차 별로 동작 원리를 모른다면 전륜구동으로 달리고 있는지, 후륜구동으로 달리고 있는지 사람들은 모른다. 그냥 달린다.
Bus를 만든 사람들은 이걸 후륜구동으로 가게 하고 싶다.
superCar를 "사륜구동으로 달린다"로 정의
//bus는 저동차의 한 종류다.
public class Bus extends Car {
public void run() //Bus에 Car가 가지고 있는 run()메소드와 똑같은 형태의 메소드를 선언한다.
//메소드 시그니처가 같다고 표현한다.[타입이나 개수가 같아야 한다.]
//run(int x) --> x
//
{
System.out.println("후륜구동으로 달린다.");
}
//버스를 일반화 시키면 자동차라고 말 할 수 있고 자동차를 확장하면 버스가 된다.
public void 안내방송(){
System.out.println("안내방송");
}
}
public class Car {
public void run()
{
System.out.println("달리다.");
}
}
}
public class CarExam01 {
public static void main(String[] args)
{
Bus b1 = new Bus();
b1.run(); //부모가 가지고 있는 run
// b1.안내방송(); // 자식이 가지고 있는 안내방송
Car c1 = new Bus();//버스는 자동차라는 문법이 가능하기 때문에
//둘 다 b1,c1이나 둘 다 버스라는 인스턴스가 생겼다.
//Bus는 run()메소드를 "후륜구동으로 달리다"라고 오버라이딩해서 구현함
c1.run();
//그(C1) 자동차는 달린다.
//우리 눈 앞에 버스가 있다.
//버스를 가리키면서 자동차다! 라고 말했다.
//이어서 그 자동차는 달린다 라고 말했다.
//그 자동차는? 버스다.
//버스는 후륜구동으로 달리도록 구현해 놨기 때문에 버스를 자동차라고 불러도 버스는 후륜구동으로 달린다.
Bus b2 = (Bus)c1;
//c1이 참조하는 Bus 인스턴스를 원래의 Bus 형태로 참조해서 사용하겠다라는 의미
//
b2.안내방송();
c1.안내방송(); //이건 실행이 안 된다.
// Car라는 참조 변수의 타입으로 Car를 사용하면 Car가 가지고 있는 메소드만 사용 가능하다.
Car c2 = new SuperCar();
c2.run(); //C2 호출하면 "사륜구동으로 달린다"가 출력되는 것을 알 수 있다.
}
}
//둘다 "후륜구돟으로 달린다" 라고 출력된다.
하나의 인스턴스를 b2,c1이 참조하게 됩니다. 이 코드가 가능한 이유는 c1이 참조하는 인스턴스가 실제로는 Bus 인스턴스 이기 때문이다.
c1 이 참조하는 Bus 인스턴스를 Bus타입으로 변환해서 b2가 참조하게 하라는 의미다.
public class Parent {
int i = 5;
public void printI(){
System.out.println("parent : printIn() : " + i);
}
}
public class Child extends Parent{
public int i = 15;
public void printI()
{
System.out.println("Child : printI() : " + i);
}
}
public class Exam01 {
public static void main(String[] args)
{
Parent p1 = new Parent();
System.out.println(p1.i);
p1.printI(); //5가 나옴
System.out.println("------------------");
Child c1 = new Child();
System.out.println(c1.i);
c1.printI(); // 15가 나옴
System.out.println("------------------");
Parent p2 = new Child(); // Child는 Parnet의 후손이다. 혹은 자식이다.
p2.printI();
// 결과
// 5
// Child : PrintI() : 15
// 메소드는 오버라이딩 되면 자식메소드가 실행된다.
// 둘다 printI를 가지고 있음 그렇게 되면 자식이 가지고 있는 printI를 실행한다.
// i의 값은 5가 나온다. 왜? 필드값이 Parent를 따라갔기 떄문이다.
// 필드는 오버라이딩 되도 부모 타입을 따라간다.
}
}
public class Book {
private int price; // field price이다.
public int getPrice()
{
return this.price; //인스턴스 변수
}
public void getPrice(int price) //메서드 안에 선언된건 local price
{
this.price = price;
}
}
public class Book {
private int price;
private String title;
public int getPrice()
{
return this.price;
}
public void setPrice(int price)
{
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Book 클래스는 몇 개의 프로퍼티를 가지고 있냐 라고 물어본다면, title, price 2개를 가지고 있다. 2개의 프로퍼티를 가지고 있다고 대답하면 된다.
public class Book {
private int price;
private String title;
public int getPrice()
{
return this.price;
}
public void setPrice(int price)
{
this.price = price;
}
public String getName() {
return title;
}
public void setName(String title) {
this.title = title;
}
}
Object 01 = new Car();
Object 01 = new Bus();
Object 01 = new 이층버스();
System.out.println(O1.toString()); ==
System.out.println(O1)
car 부분이 생성자다.
ex)
public Car()
{
System.out.println("자동차가 한대 생성됩니다.);
}
ex2) 이름을 가지고 태어나게 하고 싶다.
이름을 가지고 하고 싶다면, 필드에도 선언되어 있어야한다.
메소드 하나 추가해서 printName()
main 함수에서 출력하면
결과
main
public class UserEaxm {
public static void main(String[] args)
{
User user = new User("김성박", "uasduasu@nave.com"); // 생성자를 처음으로 만들게 되면 여기서 에러 발생
// System.out.println(user.getName());
// System.out.println(user.getEmail());
System.out.println(user);
User user2 = new User("홍길동", "www.anmer.cner", "1234");
System.out.println(user2.getName());
System.out.println(user2.getEmail());
System.out.println(user2.getPassword());
System.out.println(user2);
}
}
user
public class User {
private String email;
private String password;
private String name;
public String getEmail() {
return email;
} //리턴 값만 가지고 있는것이 불변객체라고 한다.
public String getPassword() {
return password;
}//리턴 값만 가지고 있는것이 불변객체라고 한다.
public String getName() {
return name;
}//리턴 값만 가지고 있는것이 불변객체라고 한다.
public void setEmail(String email) {
this.email = email;
}//리턴 값만 가지고 있는것이 불변객체라고 한다.
public void setPassword(String password) {
this.password = password;
}//리턴 값만 가지고 있는것이 불변객체라고 한다.
public void setName(String name) {
this.name = name;
}//리턴 값만 가지고 있는것이 불변객체라고 한다.
public User(String name, String email) {
this.email = email;
this.name = name;
}
public User(String name, String email, String password) {
this.email = email;
this.password = password;
this.name = name;
//this(name,email,null);
}//생성자 오버로딩하면 된다. 여기서 보면 this로 중복되어 있는 부분이 많다. 이 부분을 개선하기 위해서 this를 이용해서 요약하는게 좋다.
// this를 사용하는 기준점은 파라미터값을 많이 갖고 있는 생성자를 기준으로 this를 사용하는게 좋다.
@Override //USER가 가지고 있는 필드들을 오버라이딩한다.
public String toString() {
return "User{" +
"email='" + email + '\'' +
", name='" + name + '\'' +
'}';
}
}
//생성자를 하나라도 만들게 되면 기본생성자는 자동으로 안 만들어진다.
super는 인스턴스 부모를 참조할 때 사용하는 키워드다.
super() 생성자는 부모 생성자를 의미한다.
super() 생성자는 생성자 안에서만 사용가능하다.
super() 생성자는 생성자 안에서 첫번째 줄에만 올 수 있다.
생성자는 무조건 super() 생성자를 호출해야 한다. 사용자가 super() 생성자를 호출하는 코드를 작성하지 않았다면 자동으로 부모의 기본 생성자가 호출된다.
부모 클래스가 기본 생성자를 가지고 있지 않다면 사용자는 반드시 직접 super() 생성자를 호출하는 코들르 작성해야한다.
ex1)
public class Car2Exam {
public static void main(String[] args)
{
Car2 c1 = new Car2("jihan");
Bus2 b1 = new Bus2();
}
}
ex2)
public class Car2 {
public Car2(String name)
{
System.out.println("Car2() 생성자 호출");
}
}
ex3)
public class Bus2 extends Car2{
public Bus2()
{
super("Bus!!"); // 부모의 기본 생성자를 호출한다.
// 부모가 기본 생성자가 없으면 자식 생성자에선 super에 변수 값 작성하여 해야한다.
System.out.println("Bus2 기본생성자.");
}
}
정보 감사합니다.