[java] 생성자, 오버라이딩, 하이딩, 멤버변수, 객체비교
생성자 = 매서드
new를 사용하는 순간 메모리를 할당하는 공간이 생기는 것.
실행하면 메모리에 내용이 담기는 것.
생성자는 특수한 매서드이다.
일반적인 매서드와 다르게 명령이 없이도,
new라고 생성자 만들어주면 알아서 동작을 하는 것.
클래스 명이랑 같은 것이 생성자
class Animal{
Animal(){ //생성자(: 매서드이지만 클래스 이름과 같음)
}
생성자일 땐,
부모의 형태로 자식을 낳거나
자식의 형태로 자식을 낳거나
일단 부모부터 타는건 같다
매서드와의 차이 분명히.
매서드일 땐 해당하는 매서드 하나만 수행한다.
P클래스에 get()가 있고, C클래스에 get() 이 있고,
P a2 = new C() -> a2.get() 이 호출된다면,
오버라이딩에 의해 자식에 있는 get() 만 수행된다.
부모에 있는 get()으로 가는게 아니라!(생성자일 때만! 해당된다.)
오버라이딩 하면
자식에 있는 매서드를 사용하게 된다.
그리고 부모에 있는 매서드만 사용할 수 있다.
하이딩 개념도 익히기.
멤버변수는 나와 가까운 것.
멤버변수는 부모가 나를 생성했든 내가 나를 생성했든
새로 생긴 생성자에 부모, 내가 가진 멤버변수를 메모리상에 모두 갖는다.
부모가 가진 a라는 공간, 내가 가진 a라는 공간 모두 생긴다.
매서드가 가까운 곳을 찾아간다.
생성자
//생성자
//'클래스 명'이랑 같은 것 : 생성자
//자식 클래스에서 super로 명시적으로 호출하지 않는다면
//부모의 default 생성자를 호출한다. P() 형태
//만약 P()형태가 없고, P(인자) 형태만 있다면 아무것도 태우지 않는다.
//만약 인자 있는 P(인자)를 태우고 싶다면, 자식 생성자에 super(인자)가 있어야한다.
class P{
P(){System.out.printIn("A");} //인자 없는 생성자
P(int a){System.out.printIn("B");} //인자 있는 생성자
}
class C extends P{
C(){System.out.printIn("C");}
C(int a){System.out.printIn("D");}
C(int a, int b){
super(a);
System.out.printIn("E");
}
}
P p1 = new C(); //C() 자식 매서드 호출하는데, 부모가 낳아줬으니(상속관계) 부모를 찾아간다.
//부모를 찾아갔는데, super라고 부모의 생성자를 명시적으로 호출하지 않는다.
//부모의 생성자 두개 중 인자가 없는 default 생성자를 묵시적으로 출력하게 된다.
P p2 = new C(1);//인자 있는 매서드 호출하는데, 부모를 먼저 찾아간다.
//super이라고 부모클래스를 명백히 호출하는 구간 없다.
//그래서 인자없는 P()를 호출하게 된다.
P p3 = new C(1,2);//super라고 명시적으로 호출하면서 super(a)라고 인자 있는 생성자를 호출한다.
//출력
//A
//C
//A
//D
//B
//E
//상속 관계에서는 아버지부터 찾아간다.
오버라이딩
//오버라이딩
class P{
public void func1(){System.out.printIn("A");}
public void func2(){System.out.printIn("B");}
}
class C extends P{
public void func1(){System.out.printIn("C");}
public void func3(){System.out.printIn("D");}
}
P p1 = new C() // 아버지(P class)가 자식클래스(C class) 낳은 형태
p1.func1(); // 부모, 자식 클래스 모두 func1() 가지고 있음. 오버라이딩. 재정의된 자식클래스만 실행
p1.func2(); // func2()는 부모 클래스에만 있음. 부모클래스만 실행
p1.func3(); //아버지가 나를 낳았으면 기본적으로 자식도 가지고 있어야하는데,
//가지고 있지 않은걸 호출하므로 실행x오류
//출력.
//C
//B
//Error
하이딩
//하이딩
//특징 static이 들어가 있다.
//static이 있을 땐 오버라이딩이 아니라 하이딩! 이고,
//func1()을 각각 가지고 있는 형태가 된다.
class P{
public static void func1(){System.out.printIn("A");}
}
class C extends P{
public static void func1(){System.out.printIn("C");}
}
P p1 = new C(); //아버지가 날 낳은 형태.
p1.func1(); //static이 있으므로(하이딩 형태기 때문에) 부모가 가진것 출력한다. //A출력
//static없었으면(오버라이딩이면) C출력했을 것
C p2 = new C(); //내가 나를 낳은 형태
p2.func1(); //자식이 가진 func1()인 C를 출력하게 된다.
멤버변수
//멤버변수
class P{
public int a = 10;
public void func1(){System.out.printIn(a);}
public void func2(){System.out.printIn(a);}
}
class C extends P{
public int a = 10;
public void func1(){System.out.printIn(a);}
}
P p1 = new C(); //아버지가 날 낳은 형태. 멤버변수로 P , C 클 멤버변수 int a = 10을 둘 다 갖는다.
p1.a = 20 //부모의 형태를 가지고 있으므로 부모가 가진 a에다가 20을 집어 넣게 된다.
p1.func1(); //오버라이딩 이기 때문에, 자식이 가진 func1을 호출한다.
p1.func2();
//부모가 자식을 생성했기 때문에 두개의 멤버변수가 메모리 상에 같이 생긴다.
//생성자p1는 부모의 a, 자식의 a 모두 갖는다.
//이럴 경우 나와 가까운 클래스를 고른다. P p1 형태니까 앞에 P인 부모클래스에 가깝다.
//어쨌든 부모의 형태로 나를 생성했기 때문에.
//p1.a=20 --> P클래스의 a가 20이 된다.
//p1.func1() --> 매서드 오버라이딩에 의해 자식이 가진 func1()이 호출된다.
//부모도 자식도 다 a를 갖지만, 호출된 매서드가 자식이 가진 a가 가까우니까 자식이 가진 a가 출력됨.
//p1.func2() --> func2()는 아버지 클래스만 가지고 있다. 아버지 클래스에서 가까운 a 출력.
//출력
//10
//20
//1) 생성자 : 자식 인스턴스의 인자가 대입 안되는 경우
class Parent{
int age = 43;
String name = "이흥직";
public Parent(){ //클래스 명이랑 같은 것 : 생성자(인자없다)
System.out.printIn("부모 디폴트 생성자");
}
public Parent(int age, String name){ //클래스 명이랑 같은 것 : 생성자(인자있다)
this.age = age;
this.name = name;
System.out.printIn("부모 인자 있는 생성자");
}
public void print(){
System.out.printIn(name+","+age);
}
}
class Child extends Parent{ //Parent클래스 안에 있는 모든 속성, 매서드를 물려받는다(private한 것 빼고)
//Parent클래스의 age, name 멤버변수, print()매서드 상속 받음.
public Child(int age, String name){ //인자 있는 생성자 (디폴트 생성자는 없는 상태)
// 인스턴스 생성에 의해 13, 창훈이 들어가게됨
System.out.printIn("자식 생성자");//내꺼 호출 전에 부모부터 찾아간다.
} //super 없다. 부모가 가진 default 생성자 호출한다.
public static void main(String[] args){
Child a = new Child(13, "이창훈"); //자식 형태로 인스턴스 생성->자식형태의 생성자를 태움
//a 인스턴스는 age, name, print()상속받음
a.print(); //부모가 가진 print() 출력
//내가 가진 age, name 변수에 대입한 게 하나도 없음
//출력
//부모 디폴트 생성자
//자식 생성자
//이흥직, 43
}
//2) 생성자 : 자식 인스턴스의 인자가 대입되는 경우
class Parent{
int age = 43; //13으로 바뀐다
String name = "이흥직"; //"이창훈" 으로 바뀐다.
public Parent(){ //클래스 명이랑 같은 것 : 생성자(인자없다)
System.out.printIn("부모 디폴트 생성자");
}
public Parent(int age, String name){ //클래스 명이랑 같은 것 : 생성자(인자있다)
//4)age, name 에 각각 13, 이창훈이 들어간다.
this.age = age; //5)멤버 변수 age를 바꿔준다.
this.name = name; //6)멤버 변수 name을 바꿔준다.
//this.멤버변수 : 지금 인자로 받은 값.
System.out.printIn("부모 인자 있는 생성자"); 7)출력
}
public void print(){ //9)
System.out.printIn(name+","+age); //10)출력
}
}
class Child extends Parent{ //Parent클래스 안에 있는 모든 속성, 매서드를 물려받는다(private한 것 빼고)
//Parent클래스의 age, name 멤버변수, print()매서드 상속 받음.
public Child(int age, String name){ //인자 있는 생성자 (디폴트 생성자는 없는 상태)
//2) 인스턴스 생성에 의해 현재 13, 창훈이 들어가게됨
super(age, name); //super 있다 -> 인자 있는 부모생성자 호출.
// 3)age와 name을 집어넣어라.
System.out.printIn("자식 생성자");//내꺼 호출 전에 부모부터 찾아간다. 8)출력
}
public static void main(String[] args){
Child a = new Child(13, "이창훈"); //1)자식 형태로 인스턴스 생성->자식형태의 생성자를 태움
//a 인스턴스는 age, name, print()상속받음
a.print(); //부모가 가진 print() 출력
//내가 가진 age, name 변수에 대입한 게 하나도 없음
//출력
//부모 인자 있는 생성자
//자식 생성자
//이창훈, 13
}
//3) 부모클래스에 "default 생성자"가 없는 경우
//자식 클래스에서 super로 명시적으로 호출하지 않는다면
//부모의 default 생성자를 호출한다. P() 형태
//만약 P()형태가 없고, P(인자) 형태만 있다면 아무것도 태우지 않는다.
//만약 인자 있는 P(인자)를 태우고 싶다면, 자식 생성자에 super(인자)가 있어야한다.
//1) 생성자 : 자식 인스턴스의 인자가 대입 안되는 경우
class Parent{
int age = 43;
String name = "이흥직";
public Parent(int age, String name){ //클래스 명이랑 같은 것 : 생성자(인자있다)
this.age = age;
this.name = name;
System.out.printIn("부모 인자 있는 생성자");
}
public void print(){
System.out.printIn(name+","+age);
}
}
class Child extends Parent{ //Parent클래스 안에 있는 모든 속성, 매서드를 물려받는다(private한 것 빼고)
//Parent클래스의 age, name 멤버변수, print()매서드 상속 받음.
public Child(int age, String name){ //인자 있는 생성자 (디폴트 생성자는 없는 상태)
// 인스턴스 생성에 의해 13, 창훈이 들어가게됨
System.out.printIn("자식 생성자");//내꺼 호출 전에 부모부터 찾아간다.
} //super 없다. 부모가 가진 default 생성자 호출한다.
//부모 클래스에 default 생성자 없다 -> 아무일 일어나지않음.
//출력 : 자식생성자
public static void main(String[] args){
Child a = new Child(13, "이창훈"); //자식 형태로 인스턴스 생성->자식형태의 생성자를 태움
//a 인스턴스는 age, name, print()상속받음
a.print(); //부모가 가진 print() 출력
//내가 가진 age, name 변수에 대입한 게 하나도 없음
//출력: 이흥직, 43
//출력
//자식 생성자
//이흥직, 43
}
객체비교
//자바 객체비교
//일반변수
//객체변수
// == : 메모리 주소값 비교
//.equals : 내용 비교
String str1 = "hello" //일반변수 int a = 10 과 같이 값을 대입한 형태
String str2 = "hello" //일반변수(new 없으면 일반변수)
String str3 = new String("hello"); //객체변수(new썼으니까 객체변수라고 생각)
//셋다 'hello'를 갖는다.
if(str1==str2){ //만약 str1, str2가 일반변수면 대입 되어 있는 값을 비교하면 된다.("hello"="hello")
System.out.printIn("True");
}else{
System.out.printIn("False");
}
if(str1.equals(str2)){ //둘의 내용이 같으냐 ("hello"=="hello")
System.out.printIn("True");
}else{
System.out.printIn("False");
}
if(str1==str3){ //str1은 일반변수, str3은 객체변수 -> 서로 주소가 달라 서로 비교가 안됨.
System.out.printIn("True");
}else{
System.out.printIn("False");
}
if(str1.equals(str3)){ //둘의 내용이 같으냐 ("hello"=="hello")
System.out.printIn("True");
}else{
System.out.printIn("False");
}
//True
//True
//False
//True
JAVA 객체 비교 종류
1. A==B : 객체 A,B의 메모리 주소값 비교 . 즉 값을 비교
2. A.equals(B) : 두개의 '내용'을 비교하게 된다.
2_1. 객체.equals(객체) : 객체는 내용 비교 한다.
2_2. arr1.equals(arr2) : 배열은 내용비교 안한다 (암기)
2_3. Arrays.equals(arr1, arr2) : Arrays.equals를 사용하면 내용비교 한다
//자바 객체 비교
//배열은 안에 있는 내용을 비교하지 않는다.
//같은값 arr1.equals(같은값 arr2)는 내용비교 하지 않는다 False(암기)
//Arrays.equals(arr1, arr2) 는 내용비교 한다.
inst[] arr1 = {1,2,3}; //1차원 배열 arr1[0] 100번지 주소
inst[] arr2 = {1,2,3}; //1차원 배열 arr2[0] 200번지 주소
if(arr1 == arr2){
System.out.printIn("True");
}else{
System.out.printIn("False");
}
if(arr1.equals(arr2)){ //배열이라 안에 있는 내용을 비교하지 않는다.(암기)
System.out.printIn("True");
}else{
System.out.printIn("False");
}
if(Arrays.equals(arr1, arr2)){ //Arrays.equals(arr1, arr2) 는 내용비교 한다.(암기)
System.out.printIn("True");
}else{
System.out.printIn("False");
}
//False
//False
//True
자바에서 객체의 메모리 주소값 비교는
객체들이 동일한 메모리 위치를 참조하는지 또는 서로 다른 메모리에 존재하는지에 따라 달라진다.
이를 확인하려면 두 객체가 같은 객체인지, 즉 동일한 인스턴스를 가리키는지 봐야한다.
객체의 메모리 주소 = 참조값.
//두 객체의 메모리 주소값이 같은 경우(같은 객체 참조)
//하나의 객체를 여러 변수가 공유하고 있는 경우.
String a = "hello";
String b = a; // b도 a와 같은 객체를 참조
if (a == b) {
System.out.println("같은 객체를 참조하고 있습니다.");
}
// a, b는 같은 메모리 주소값을 가지며, 같은 객체를 가리킨다.
1)
//두 객체의 메모리 주소값이 같은 경우(같은 객체 참조)
//하나의 객체를 여러 변수가 공유하고 있는 경우.
String a = "hello";
String b = a; // b도 a와 같은 객체를 참조
if (a == b) {
System.out.println("같은 객체를 참조하고 있습니다.");
}
// a, b는 같은 메모리 주소값을 가지며, 같은 객체를 가리킨다.
//2) 객체변수의 == 비교 & equals 비교
//메모리 주소값이 다르지만 -> == : False
//내용은 같은 경우 -> equals : True
//new 키워드를 사용하여 객체를 생성할 때마다,
//새로운 메모리 공간이 할당되며 객체들이 각각 다른 참조값을 가지게 된다
//두 객체는 별도로 생성된 독립적인 인스턴스
String a = new String("hello");
String b = new String("hello");
if (a != b) {
System.out.println("다른 객체를 참조하고 있습니다.");
}
//a ,b 모두 같은 문자열 값을 가지지만, new 키워드로 서로 다른 독립적 객체로 생성되었음.
//서로 다른 메모리 주소를 참조한다.
String a = new String("hello");
String b = new String("hello");
if (a.equals(b)) {
System.out.println("내용이 동일합니다.");
}
//여기서는 a와 b의 참조값은 다르지만, equals()는 두 문자열의 값이 같다는 것을 확인해줍니다.
//생성자.
//부모가 태어난 상태
class Animal{
Animal(){
System.out.printIn("A")
}
Animal(String name){
System.out.printIn("B")
}
class Dog extends Animal{
Dog(){
System.out.printIn("C")
}
Dog(String name){
System.out.printIn("B")
}
Dog(String name){
super("name")
System.out.printIn("E")
}
Animal a2 = new Animal("Tiger) //아버지가 스스로 태어난 상태.
//아버지가 스스로 생성된 구조. -> 자식 클래스 볼 필요 없다.
//'A' : 문자 A
//"A" : 문자열 A
//"Tiger" 인자값이 있으므로
// 부모 클래스에서 문자열을 인자로 받는 생성자를 호출한다.
//출력
//B
//생성자
//자식이 태어난 상태
//인자가 없는 생성자
class Animal{
Animal(){
System.out.printIn("A")
}
Animal(String name){
System.out.printIn("B")
}
class Dog extends Animal{
Dog(){
System.out.printIn("C")
}
Dog(String name){
System.out.printIn("B")
}
Dog(String name){
super("name")
System.out.printIn("E")
}
Dog a2 = new Dog(); //자식이 태어난 상태.
//생성자
//자식이 태어난 상태
//인자가 있는 생성자
class Animal{
Animal(){
System.out.printIn("A")
}
Animal(String name){
System.out.printIn("B")
}
class Dog extends Animal{
Dog(){
System.out.printIn("C")
}
Dog(String name){ //부모를 명시적으로 호출하지 않는다 : super 없다 -> 부모의 default생성자 호출
System.out.printIn("B")
}
Dog(String name){
super("name")
System.out.printIn("E")
}
Dog a2 = new Dog("Dodam"); //자식이 태어난 상태.
//출력
//A
//D
//생성자
//부모가 자식을 낳은 상태
//super 가 있을 때,
class Animal{
Animal(){
System.out.printIn("A")
}
Animal(String name){
System.out.printIn("B")
}
class Dog extends Animal{
Dog(){
System.out.printIn("C")
}
Dog(String name){
System.out.printIn("B")
}
Dog(String name){
super("name") //인자 하나있는 부모 생성자 호출을 명시해줬다.
System.out.printIn("E")
}
Animal a2 = new Dog("Dodam", 10); //부모가 자식을 낳은 상태.
//출력
//A
//E
super.i : 부모 클래스가 가진 가진 i 값을 참조한다
super.a : 부모 클래가 가진 ()a라는 매서드를 참조한다.
super(~) : 부모 클래스가 가진 생성자를 호출한다.
//생성자
//부모가 자식을 낳은 형태
★//this()의 의미.
class Animal{
Animal(){ //4) default 생성자를 호출
System.out.printIn("A") // 5)출력
}
Animal(String name){
System.out.printIn("B")
}
class Dog extends Animal{
Dog(){ // 3)나의 인자없는 this()에 의해 인자없는 나의 생성자로 보내진다. 부모 호출 권한도 위임, super없다.
System.out.printIn("C") // 6)출력
}
Dog(String name){
this(); // this는 나의 생성자. // 2)인자 없는 this생성자이기 때문에
System.out.printIn("B") 8)출력
}
Dog(String name){
super("name")
System.out.printIn("E")
}
Animal a2 = new Dog("Dodam"); //1)부모가 나를 태어난 상태. 인자있는 나.
//출력
//A
//C
//B
this() -> 내 클래스에서 내가 가진 생성자를 호출하는데,
호출할 때, 부모 호출 권한도 함께 위임한다.