ㅁ 변수의 자료형이 boolean인 경우 getter 메서드에 get을 안쓰고 is를 쓴다.
ㅁ 오버로딩
- 한 클래스 내에서 같은 이름의 메소드를 여럿 정의하는 것.
ㅁ 오버라이딩
- 반환형도 같아야 함. (오버로딩은 반환형 상관없음)
- 오버라이딩시 자식 객체의 메소드가 우선되고 부모 객체의 메소드는 숨겨진다.
= 지금은 자바가 버전업되서, jdk버전업되면서 부모메소드 반환형의 자식타입까지는 가능.
버전이 안된다면 부모메소드의 반환형과 동일하게 해야한다. (거의 똑같이 할겁니다. 다르게 할 경우는 거의 없을것)
= 부모메소드의 접근제한자보다 범위가 같거나 커야한다.
- @Override 주석 붙이기 권장(jvm, 다른 개발자에게 오버라이딩된 메소드라느 ㄴ것을 알리기 위해)
(부모 메소드가 변경된 경우 - 자식클래스에 올 ㅠ발생 - 수정해야하는거알려줌
자식메소드로 작성시 실수로 잘못작성한 경우 - 오류 발생 - 수정해야하는ㄱ ㅓ알려줌)
@Override 어노테이션을 사용하면 자바 컴파일러가 해당 메소드가 실제로 부모 클래스나 인터페이스의 메소드를 오버라이딩하는지 확인할 수 있습니다.
ㅁ 지역변수에는 final만 붙일 수 있다.
ㅁ 여러개의 자식 클래스에서 추상화하여 부모 클래스를 설계할 때 만들어야 하느 ㄴ것들.
공통된 변수, 인포메이션, 게터세터, 매개변수없는생성자, 매개변수다있는생성자
ㅁ 자식클래스에서 해야하는 것들
- 없는 변수, 게터세터, 매개변수없는생성자
- 인포메이션은 자식클래스에서 오버라이딩.
+ 그리고 나중에 부모에서 가져온 변수4개를 합쳐서 총 5개의 매개변수를가진 생성자를 자식클래스에서만들고,
거기에 super.setXXX로 대입하지말고,
부모클래스의 모든 매개변수생성자를 호출하면 더 간단.
근데 이때 super.부모생성자() 형식이 아니고, super();로.
+ 자식클래스에서 information메소드를 재정의할때 하나하나 getXXX로 해도 되지만, 부모클래스의 super.information()에다가 또 +로 이어붙이면 더 간단.
ㅁ 생성자를 호출하는 문장은 반드시 가장 위에 있어야 한다.
ㅁ 상속관계
- 부모 자식, 조상 후손, 상위 하위, 슈퍼 서브,
ㅁ 클래스와 클래스간에 다중상속 불가. 부모가 여럿일 수 없음.
- 인터페이스는 클래스와 클래스 간의 다중 상속을 지원하지 않는 Java에서 다중 상속을 구현하는 주요 방법입니다. 다중 상속이란 한 클래스가 여러 개의 클래스로부터 상속을 받는 것을 말합니다. Java에서는 클래스가 하나의 직접적인 부모 클래스만 가질 수 있지만, 인터페이스는 여러 개를 구현할 수 있습니다.
ㅁ 부모 클래스와 자식 클래스에 동일한 이름의 정적 메서드가 있을 경우, Java에서는 메서드 오버라이딩이 아니라 "메서드 숨기기"라는 개념이 적용됩니다.
- 정적 메서드의 경우 메서드 오버라이딩 개념이 적용되지 않기 때문에, 변수 타입이 부모 클래스이건 자식 클래스이건 정적 메서드는 변수가 선언된 클래스의 타입을 기준으로 호출됩니다.
ㅁ 자식 생성자에서 부모생성자가 호출된다는 것은, 부모 객체가 생성된다는 것이다.
(내부적으로 부모객체가 먼저 생성됨)
- 자식 생성자 호출보다 부모 생성자 호출이 항상 먼저고, 자식 객체 생성보다 부모 객체 생성이 항상 먼저다.
ㅁ 기본생성자를 작성하는 습관을 들이라는 이유
- 부모 클래스의 기본생성자를 생략하고 매개변수생성자만 만들어놓은 경우에,
자식 생성자에서(기본생성자인지 매개변수있는생성자인지 상관없이) super(매개변수생성자)하지 않은 경우에는 자동적으로 super()를 호출해서 오류난다.
- 내가 지금 만드는 클래스가 언제 부모생성자가 될 지 모름.
- 자식 생성자는 항상 부모의 매개값없는 기본생성자를 호출하는 구문이 첫줄에 생략되어있다.
ㅁ
- # protected
- ~ default
- - private
- + public
- 기울이면 abstract다.
ㅁ 기본생성자에는 super()가 다 숨겨져있는데, 이 클래스가 상속받는게 없을경우 부모 객체를 생성하는데 문제되는거 아님?
- 아님. 모든 클래스는 Obect 클래스를 상속받기 때문.
extends Object가 숨겨져있따.
- 어떤 객체를 생성할 때마다 object 객체도 같이 생성된다. 필드는 없고 메서드만 있다.
ㅁ 이클립스 오른쪽 클릭 - source - generate constructor using fields 하면 매개변수 내가 골라서 생성자 자동 완성가능.
- 단축키 : 알트 쉬프트 s - o - 엔터
ㅁ 게터세터 빨리만들기
- 단축키 : 알트 쉬프트 s - r - 알트 a - 알트 r
ㅁ 그냥 println으로 객체를 호출하면, jvm이 자동으로 해당 레퍼런스.toString() 메소드를 반홯해서 그걸 출력함. 주소값.
- 레퍼런스(참조변수)를 출력할 때, JVM이 자동으로 레퍼런스.toString() 메소드 호출
ㅁ 상속을 배우기 전이었어서 information메소드로 별도로 만들었던 거고, 사실은 toString을 오버라이딩해서 그 객체의 멤버변수의 값들을 반환.
- 그래서 그냥 println 으로 객체 호출해서 함.
ㅁ 객체 두개를 println에서 ==와 equals로 하면, 둘다 false나옴.
- ==는 애초에 값가지고 비교하는건데 참조자료형들은 각자의 주소값이 다르기 때문이고.
- equals도 오버라이딩 안되어있으면 ==로 되어있음. String처럼 오버라이딩해야함.
- 오버라이딩전: object클래스의 equals메소드 실행 -> return 두 객체간의 주소값을 비교 ==;
- 오버라이딩후: book클래스의 equals메소드 실행 -> return 두 객체간의 모든 필드값들 비교;
(equals 메소드는 주소값을 비교해서 내용이 같아도 새로 생성되면 false를 반환하기 때문에 오버라이딩해서 필드값들 비교로 바꿈. 이때 String이면 equals메소드로비교)
ㅁ 자바에서 동일객체
public class Book {
private String title;
private String author;
private int price;
}
Book b3 = new Book("자바", "홍길동", 300);
Book b4 = new Book("자바", "홍길동", 300);
// 동일한 데이터의 Book객체 2개 생성
- 자바에서 동일객체란 equals의 결과가 true이고, 각 객체의 hashCode 결과값이 일치하는 경우이다.
@Override
public boolean equals(Object obj) {
if(obj instanceof Book) {
Book bbb = (Book) obj;
if(this.title.equals(bbb.title) && this.author.equals(bbb.author) && this.price==bbb.price) {
return true;
}
else {return false;}
}
else {
return false;
}
}
- 이렇게 equals를 오버라이딩 했어도, b3와 b4의 해시코드가 다르다. 고로 같은 객체는 아니다.
그래서 hashcode를 재정의해서 필드가 다 같으면 같은 숫자가 반환되게끔 할 것임.
- 오버라이딩전: object클래스의 hashcode메소드 실행 - return 주소값기반 10진수숫자;
- 오버라이딩후: book클래스의 hashcode메소드 실행 - return 멤버변수담긴값들 10진수;
= 항상 equals와 hashcode는 세트다. 항상 붙어다닌다.
[내가한거]
public int hashCode() {
if(title.equals("자바")&&author.equals("홍길동")&&price==300) {
return 100;
}
else
return 0;
}
[그런데 더 간단히]
return (title + author + price).hashCode(); 해도 됨.
같은 문자열인 경우 String에서 hashCode가 같도록 이미 정의되어 있어서 이렇게 해도 됨.
- 아까 Book 클래스 equals 오버라이딩할 때,
this.title.equals(other.title) && this.author.equals(other.author) && this.price==bbb.price
대신,
this.title==other.title && this.author==other.author && this.price==bbb.price
이렇게 해도
String은 같은 문자열은 동일 문자풀에 저장하니까
"자바"랑 "홍길동"도 같은 문자풀에 저장되어서
equals말고 == 해도 결과는 같은게 맞나요?
아뇨아뇨!
리터럴 형태로 문자열을 전달했다면 ==으로 해도 true 가 나오긴 할텐데
만약에
Book bk3 = new Book("자바", "홍길동", 300);
Book bk4 = new Book(new String("자바"), "홍길동", 300);
이런식으로 멤버변수에 값으로
"자바" 값이 아닌 new String("자바") 이런식으로
하게 되면 그때는 == 비교시 제대로 되지 않아요!
그래서 이런경우까지 고려를 한다면
== 보다 equals로 하는게 정확하죠!
- String str = new String("가나다");와 String str = "가나다";는 다른 동작을 합니다.
(1) 이 경우에는 문자열 리터럴 "가나다"가 문자열 풀(String Pool)에 저장됩니다.
(2) new String("가나다")는 새로운 객체를 생성하며, 이 때 "가나다" 문자열 리터럴이 문자열 풀에 이미 존재하더라도 새로운 객체를 생성합니다.
ㅁ자바에서 메인메소드에서 어떤 메소드를 실행시켰어. 근데 그 메소드에서 return;을 만나면 전체 프로그램이 종료돼 그 어떤 메소드만 종료돼?
- 자바에서 return; 문장은 메소드의 실행을 중단하고 해당 메소드를 호출한 곳으로 반환(return)합니다.
따라서 return;을 만나면 현재 실행 중인 메소드가 종료되며, 해당 메소드를 호출한 메소드로 제어가 돌아갑니다.
전체 프로그램이 종료되는 것이 아니라 단순히 그 메소드의 실행이 종료됩니다.
ㅁ 접근범위
(1) 부모 타입 레퍼런스로 부모 객체를 다루는 경우
Parent p1 = new Parent();
p1.printParent();
p1 레퍼런스로 Parent에만 접근 가능
(2) 자식 타입 레퍼런스로 자식 객체를 다루는 경우
Child c1 = new Child1();
c1.printParent();
c1.printChild1(); // 자동형변환. 원래는 (Parent)c1.printChild1();
c1 레퍼런스로 Parent, Child1에 둘 다 접근 가능
(3) 부모 타입 레퍼런스로 자식 객체를 다루는 경우
Parent p2 = new Child1();
p2.printParent(); 만 가능.
((Child1)p2).printChild1(); 이렇게 강제형변환하면 다시 사용 가능.
// 괄호 한번 더 안씌우면 .으로 메소드가 먼저 호출되서 안됨.
p2 레퍼런스로 Parent에 접근 가능하나, Child1에 접근하려면 DownCasting 진행해야 함.
- 부모 타입으로는 모든 자식 객체를 다 담을 수 있다.
- 자동형변환이 되고 있다. Parent p2 = (Parent) new Child1();
- 상속 구조일 때만, 클래스 간 형변환이 가능하다.
- Casting(형변환),
UpCasting(자식 -> 부모, 자동) 부모=자식객체; 자식.부모메소드();
DownCasting(부모 -> 자식, 강제) ((자식)부모).자식메소드();
ㅁ 접근 가능 범위 정리
- Parent는 부모클래스고 변수 x, y와 메소드 method1과 method2.
- Child는 자식클래스고 변수 z와 메소드 method3과 method4.
(1) Parent p = new Parent();인 경우
- p는 변수 x, y 가능
- p는 메소드 method1과 method2 가능.
(2) Child c = new Child();인 경우
- c는 변수 x, y, z 가능
- c는 메소드 method1, method2, method3, method4 가능
(3) Parent xx = new Child();인 경우
- xx는 변수 x, y 가능
- xx는 메소드 method1, method2 가능
- (참고1) 만약 Child에서 부모 클래스의 method2를 오버라이딩 한 경우에는, 특별히 부모의 method2가 아닌 자식의 method2가 호출됨.
- 이 때 xx를 통해 호출할 수 있는 메소드는 Parent 클래스에 정의된 메소드와 Child 클래스에서 오버라이딩한 메소드들입니다.
- (참고2) 강제 형변환을 통해 Child 클래스의 메소드 호출 // z도 접근 가능.
((Child)xx).method3();
((Child)xx).method4();
부모타입으로 선언했지만 자식객체의 메소드를 사용하고 싶으면 강제 형변환을 해야 한다.
※ 처음부터 Child yy = new Parent();로 선언하면?
- 불가능. new Parent()하는 순간 부모만 생성된거. 이걸 앞에 명시적으로 형변환 붙여줘도 안됨.
- 자식객체를 생성했다가, 부모 타입에 넣었다가, 다시 명시적 형변환으로 자식 타입에 넣는 것은 가능.
이건 왜 가능하냐면 처음에 new Child();를 했기 때문. 이러면 자식 + 부모를 다 가지고 있기 때문에.
ㅁ 부모타입 객체 배열에 자식타입 객체들을 저장하기.
- 자식 객체 2개, 또 자식 객체 2개가 있는 경우. 배열 2개에 각각 저장.
- 부모 배열 1개에 다 담을 수 있음. 이때 자동형변환이 됨.
- 그런데 부모 배열 변수[x].자식메소드();하고 싶으면 괄호 두개 씌워서 강제형변환 다시 해줘야 가능.
- 근데 Child2를 Parent로 했다가 Child1으로 하면, 컴파일시에는 오류(빨간줄)을 안띄우는데 실행할 때 오류를 띄움.
부적절한 형변환시 ClassCastException 발생.
- 그런데 몇 개 안될 경우에는 인덱스 번호에 따라 ((Child1)arr[0).printChild1();, ((Child2)arr[1).printChild2();를 하겠지만, 엄청 많을 경우에는 이것들을 다 일일이 if로 해줄 수 없다.
대신 if( 부모타입 instanceof 비교할자식타입)으로 한다.
ㅁ (이어서) instanceof 대신 오버라이딩을 활용하기.
- 부모 클래스 Parent print 메소드에 "부모" 출력.
- 자식 클래스 Child1 print 메소드에 "자식1" 출력.
- 자식 클래스 Child2 print 메소드에 "자식2" 출력.
- 부모객체배열 arr에 Child1, Child2 클래스가 2개씩 있다.
for(int i=0; i<arr.length; i++){
arr[i].print(); // 지금 arr[i]는 부모임.
}
// 향상된 for문으로는
for(Parent p : arr ) {
p.print();
}
- 아까는 오버라이딩된 메소드가 아니라 각각의 자식 클래스가 가진 메소드를 호출해서 매번 형변환을 해줘야 했다.
오버라이딩하면 형변환 과정 필요없이 호출 가능.
instatnceof로 비교할 필요도 없고.
※ 동적바인딩
- arr[i].print();여기서 print를 ctrl 클릭해보면 부모 메소드의 print()메소드로 감.
- 지금 가리키는건 부모 메서드를 가리키지만 실제 실행할 때는 오버라이딩된 메소드를 참조함.
- 소스코드 -(컴파일)-> byte코드 -(실행)-> 프로그램 실행
.java 컴파일과정 .class 런타임과정
(우리가 못봄)
- 이클립스에서 빨간줄 뜨는건 컴파일 에러임.
근데 컴파일 과정 중에는 문제가 없는데 실행할 때 오류가 생기는 경우가 있음. 그런걸 런타임 에러라고 함.
- 동적바인딩 : 프로그램 실행 전 "컴파일 시점"에는 정적바인딩에 의해서 부모 클래스의 메소드를 가리킴.
프로그램이 실행하는 순간 "런타임 시점"에는 실제로는 자식 클래스의 오버라이딩된 메소드가 실행됨.
(실질적으로 참조하고 있는 자식 클래스의 메소드를 찾아서 실행하는걸 동적바인딩이라고 함.)
ㅁ 상수필드는 선언과 동시에 값을 대입해둬야 한다.
- 상수필드에 대해서는 게터세터를 만들 필요가 없음.
static이라 그냥도 접근 가능.
ㅁ 부모 클래스에 추상메서드 추가할거면 그 메소드, 부모 클래스에 abstract 붙여줘야 함.
- Sport s = new Sports();
이렇게 못 씀. 인스턴스화할 수 없다. 추상클래스는.
- 대신 변수 선언은 가능하다.
Sports s;
어떻게 쓰냐. 부모 타입 변수에 자식 객체 할당하면 된다.
Sports s1 = new FootBall();
Sports s2 = new BasketBall();
Sports[ ] arr = new Sports[2];
arr[0] = new FootBall();
arr[1] = new BasketBall();
for(int i=0; i<arr.length; i++) {
arr[i].rule(); // 부모 rule을 가리키지만 실제 실행은 오버라이딩 된 자식 메소드가 됨.
}
ㅁ 추상 클래스
1. 미완성된 클래스
2. 객체 생성 불가능 .단, 레퍼런스 변수로 선언은 가능.
3. 추상 메소드가 없어도 추상클래스로 선언 가능함.
- 해당 클래스를 통해 객체 생성을 불가능하게끔 하고자 할 때.
- 일반 필드 + 일반 메소드 [ + 추상 메소드]
※ 추상메소드를 포함한 추상클래스를 선언하는 케이스
- 자식 클래스들에 실행시킬 내용은 다르지만, 동일한 형태의 메소드로 구현됐으면 할 때
- 강제로 해당 메소드가 오버라이딩 됨.
- 메소드의 통일성 확보 목적.
- 표준화된 틀을 제공할 목적.
ㅁ 자식 클래스에서 부모 메서드나 변수에 접근할 때,
중복되지 않는다면 super.setExp( super.getExp() + 20 ); 여기서 super. 을 생략할 수 있다.
setExp( getExp() + 20 ); 이렇게도 가능하다. 내가 가진걸 찾기 때문.
심지어 super. 대신 this.을 써도 된다. 내가 부모거를 갖고있기 때문.
ㅁ 예제
Person mom = new Mother("김엄마", 50, 70, "출산");
Person baby = new Baby("강아기", 3.5, 80);
// 다형성. 부모레퍼런스로 자식객체 받아주는거
// 하나의 타입으로 다양한 형태를 표현할 수 있기 때문에
System.out.println(mom);
System.out.println(baby);
// 컴파일시에는 동적바인딩으로 Person의 toString을 가리키지만, 실행시에는 오버라이딩된 자식객체의 toString을 가리킴
mom.eat();
baby.eat();
// 이것들도 동적바인딩.
ㅁ 인터페이스도 클래스의 일종이다
- 상수필드(static final)는 둘 수 있지만 일반 필드는 못 둔다. (상수면 선언과 동시에 초기화해야 한다.)
- 완성형태의 메소드를 가질 수 없다.
- 즉, 오직 상수필드와 추상메소드만 가질 수 있다.
- 인터페이스에서는 public static final을 생략해도 상수로 간주됨.
어차피 상수만 쓸 수 있기 때문에.
- 인터페이스에서는 public abstract를 생략해도 추상메소드로 간주됨.
어차피 추상메소드만 쓸 수 있기 때문에.
ㅁ
- 클래스에서 클래스를 상속받을 때 : 클래스 extends 클래스 (단일상속) [실선]
- 클래스에서 인터페이스를 구현할 때 : 클래스 implements 인터페이스, 인터페이스 (다중상속) [점선]
- 인터페이스에서 인터페이스를 상속받을 때 : 인터페이스 extends 인터페이스, 인터페이스 (다중상속) [실선]
ㅁ 인터페이스를 구현하는 추상 클래스는 인터페이스의 모든 추상 메서드를 반드시 구현할 필요는 없습니다.
'클라우드 활용 자바개발자 양성과정 > 01. JAVA 프로그래밍 언어 활용' 카테고리의 다른 글
11. 예외처리 (0) | 2024.07.09 |
---|---|
10. API (0) | 2024.07.05 |
07. 객체 배열 (0) | 2024.07.02 |
06. 객체 (0) | 2024.06.27 |
05. 배열 (0) | 2024.06.24 |