즐거운 자바(1)

지환·2023년 7월 31일
1

JAVA

목록 보기
1/39
post-thumbnail

출처 | 인프런 즐거운 자바

필기 1일차

Hello.java의 3가지 중요한 부분

System.out.println("Hello");
  1. System.out은 System이 가지고 있는 out이라는 의미이다.
  2. out.println은 out이 가지고 있는 println이라는 의미다.
  3. println뒤에 괄호가 붙어 있는데, 이때 println메소드라고 말한다.
  4. out은 괄호가 붙지 않았는데, 이 부분을 out 필드라고 한다.
  5. out이 가지고 있는 println메소드의 역할은 괄호안의 내용을 화면에 출력한다. 즉 "Hello"가 출력되게 된다.

필기 2일차

객체지향프로그래밍은?

컴퓨터 프로그래밍의 패러다임 중 하나이다. 객체지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, "객체"들의 모임으로 파악하고자 하는 것이다.

Book b = new Book();
#생성자 부분 new "Book"
# b는 레퍼런스 타입이다. 

1. Heap 메모리에서 book이라는 인스턴스가 하나 생성된다.
2. Book를 가리키는 참조 변수가 b다.
  1. 인스턴스를 특별한 이름으로 불러주고 싶다면?
    -> 참조형 변수를 선언한다.

  2. 참조되지 않는 인스턴스는 "쓰레기"

  3. 클래스는 필드와 메서드로 구성된다. 클래스를 사용하기 위해선 인스턴스를 선언해야한다. 위의 코드가 선언문장이다.

클래스

  • 클래스는 필드와 메소드를 가진다.
  • 필드는 클래스의 속성이라고 말할 수 있다.
  • 메소드는 클래스의 기능이라고 말할 수 있다.
  • 클래스 앞에 나오는 이 키워드들은 public, protected, private, 접근 제한자라고 한다.

자판기를 그려보자

public class VendingMachine {

    public static void main(String[] args)
    {
        vendingMachine v1 = new vendingMachine();
//vendingMachine이라는 걸 v1이 참조한다.
        vendingMachine v2 = new vendingMachine();

    }
}
  1. static이 붙은 메소드는 클래스 메소드라고 부른다. 클래스 메소드는 인스턴스를 생성하지 않아도 사용할 수 있다. [사용 가능이라는 말은 메모리에 올라가 있다.] 인스턴스를 만들지 않아도 메모리에 올라가있다.

  2. 메소드 안에서 사용하는 것들은 다 의존하는 것이다. VendingMachine 클래스가 있어야만 main 메소드는 컴파일 되고 실행될 수 있다. 그렇기 때문에 의존하는 관계다.

  3. VendingMachineMain은 VendingMachine에 의존한다.

객체지향의 핵심은 메시징이다.

훌룡하고 성장 가능한 시스템을 만들기 위한 핵심은 모듈 내부의 속성과 행동이 어떤가보다 모듈이 어떻게 커뮤니케이션하는가에 달려있다.

-> 객체 지향 프로그래밍을 한다는 것은 메소드가 언제 호출되고 어떻게 호출될까
-> 메소드의 이름은 어떻게 지어야 할까? 어떻게 호출해야 할까?를 고민해야 한다.

메소드

  • main메소드를 생각하면서 메소드 선언 방법을 살펴봐야한다.

  • 메소드 이름은 소문자로 작성

  • 클래스 이름은 대문자

매개변수:O 반환값 : O

  • 들어가는 값은 여러개가 될 수 있다.

매개변수 : X 반환값 : X

매개변수 : O 반환값 : X

매개변수 : X 반환값 : O

매개변수 2개 반환값 X

예제 MathBean 클래스 만들기

Static한 메소드는 인스턴스를 생성하지 않아도 호출할 수 있다.

VendingMachine.printVesion();


ex2) 

클래스.메소드()로 사용가능하다. 


3일차

  • Intellij는 소스를 컴파일 하고 out/production/프로젝트폴더 아래에 class 파일을 생성하게 된다. 해당폴더를(OUT) Intellij는 CLASSPATH로 인식하게 한다.

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이라는 키워드가 함께 사용되는 필드는 클래스 필드, 함께 사용되지 않은 필드를
    인스턴스 필드라고 한다.

필드 선언 방법


[접근제한자][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가 저장된다.

User Class 작성하기

public Class Person{
	String name;
    String address;
    boolean isVip;
}

Person 클래스는 name, address, isVip라는 3개의 필드를 선언하고 있다.

Person

public class Person {
    String name;
    String address;
    boolean isVip;
}

PersonTest

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);
        





    }
}

  • p1.name = "홍길동"; 문자열은 new를 사용하지 않고 인스턴스를 사용할 수 있다. 되도록 new를 사용하지 말자(String 사용할 땐)
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);
        

    }
}

Static한 field 추가하기

Person

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);

    }
}

PersonTest3

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) -> 실행 안됨

    }
}

static에 관련된 활용 static block

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를 실행할 땐, 또 생성하는게 아니다.

이것만 기억하자 !

  1. new 연산자를 사용할 때마다 메모리에 인스턴스가 생성된다.
  2. 인스턴스는 더 이상 참조되는 것이 없을 때, 나중에 가비지컬렉션이 된다.
  3. static한 필드는 클래스가 로딩될 때 딱 한번 메모리에 올라가고 초기화된다.
  4. 인스턴스 메소드는 인스턴스를 생성하고나서 레퍼런스 변수를 이용해 사용할 수 있다.
  5. 클래스 메소드는 클래스명.메소드명()으로 사용가능하다.
  6. 메소드 안에 선언된 변수들은 메소드가 실행될 때 메모리에 생성되었다가 메소드를 종료될 때 사라진다.

추상화

  1. 비지니스 영역(도메인 영역을 고려하여 작성한다.)
  2. 중요한 것만 남기고 불필요한것은 제거한다.

캡슐화

  1. 관련된 것을 잘 모아서 것을 캡슐화라고 말한다. 관련된 것을 잘 모아서 가지고 있을수록 응집도가 높다

좋은 객체 vs 나쁜 객체

좋은 객체는 응집도는 높고 결합도는 낮다.

클래스는 설계도라고 하면 인스턴스가 되어 메모리에 올라갔을 때 이것을 객체라고한다.

응집도가 높다? -> 객체는 책임을 가지고 있다. 관련된 기능을 잘 가지고 있다. 응집도가 높다.

결합도가 낮다? -> 부가적인 장비 즉, 의존도를 의미한다. 결합도가 낮을 수록 좋다.

다형성과 오버로딩

다형성은 프로그래밍 언어의 자료형 체계의 성질을 나타내는 것이다. 프로그램 언어의 각 요소들(상수,변수,식,오브젝트,함수,메소드)이 다양한 자료형에 속하는 것이 허가 되는 성질을 말한다. 반댓말은 단형성으로 프로그램 언어의 각 요소가 한가지 형태만 가지는 성질을 의미하낟.

System.out.println(...)
  • println은 "인자를 출력하고 줄바꿈한다."라는 기능이다.
  • 여기에 인자는 int, float, double, String 등이 될 수 있다.
  • 중요한건 메소드 이름이 같다는 것이다. 왜 메소드 이름이 같다는게 중요할까?
    동일한 이름의 메소드지만 다양한 타입을 받아들인다. 이름은 같지만 받아들인 타입이 다른 것을 타형성 - 메소드 오버로딩이라고 한다.

다형성-메소드 오버로딩

  • 메서드의 이름은 같고 매개변수의 갯수나 타입이 다른 함수를 정의하는 것을 의미한다.
  • 리턴값만을 다르게 갖는 오버롣이은 작성 할 수 없다.
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 클래스를 찾아서 메모리에 올린다.

클래스 메소드는 사용가능하도록 메모리에 올라가고 인스턴스 메소드는 올라가지 않는다.

필기4일차

패키지

  • 클래스는 패키지를 이용하여 관련된 클래스들 관리한다. 자바에서 패키지는 폴더와 같은 기능이다.

패키지 이름 규칙

  • 패키지 이름은 도메인 이름을 거꾸로 적은 후에 프로젝트 이름 등을 붙여서 만든다.

패키지 선언 방법

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;
    }


}

컴파일 하고 실행할 때, 컴파일 하는 방법이 있다.

  • javac -d 경로명 *.java
  • -d옵션을 사용한다. 옵션을 주는 이유는 패키지에 해당하는 폴더가 생성되고 그 폴더 아래에 클래스가 생성된다

실행할때는 반드시 java com.example.util.Calculator라고 실행한다.

import

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의 메소드만 사용한다.

다형성-메소드 오버라이딩(Overriding)

  • over + ride = 올라 타다
  • 상위 클래스의 메서드를 하위 클래스가 재정의 하는 것이다.
  • 메서드의 이름은 물론 파라메터의 갯수나 타입도 동일해야 하며, 주로 상위 클래스의 동작을 상속 받은 하위 클래스에서 변경하기 위해 사용한다.

[중요] 메소드가 오버라이딩 되면 무조건 자식의 메소드가 실행된다.

Car car = new Bus();
car.run();
  • Bus의 run() 메소드가 실행된다.

  • superCar라는 클래스가 있다. 부모가 가지고 있는것을 자식이 동일하게 가지고 있다. 자식 클래스가 메소드를 오버라이딩 하면, 이제 자식에서 선언된 메소드가 사용된다.

  • Car가 가지고 있는 void run()메소드는 "전륜 구동으로 달린다." 라는 내용을 출력하도록 구현되어 있다.

  • Car를 상속 받고 void run() 메소드를 오버라이딩 하지 않은 클래스들은 인스턴스를 만들고 run() 메소드를 호출하면 "전륜 구동으로 달린다" 라고 출력한다.

  • 도로를 달리는 자동차를 보면, 자동차 별로 동작 원리를 모른다면 전륜구동으로 달리고 있는지, 후륜구동으로 달리고 있는지 사람들은 모른다. 그냥 달린다.

  • Bus를 만든 사람들은 이걸 후륜구동으로 가게 하고 싶다.

  • superCar를 "사륜구동으로 달린다"로 정의

BUS

//bus는 저동차의 한 종류다.
public class Bus extends Car {
    public void run() //Bus에 Car가 가지고 있는 run()메소드와 똑같은 형태의 메소드를 선언한다.
            //메소드 시그니처가 같다고 표현한다.[타입이나 개수가 같아야 한다.]
            //run(int x) --> x
            //
    {
        System.out.println("후륜구동으로 달린다.");


    }
    //버스를 일반화 시키면 자동차라고 말 할 수 있고 자동차를 확장하면 버스가 된다.
    public void 안내방송(){
        System.out.println("안내방송");

    }


}

Car

public class Car {
    public void run()
    {
        System.out.println("달리다.");
    }
}
}

CarExam01

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가 참조하게 하라는 의미다.

필드는 Type을 따라가고, 메소드는 오버라이딩된 자식의 메소드가 실행된다.

  • parent,child 예제

Parent

public class Parent {
    int i = 5;
    public void printI(){
        System.out.println("parent : printIn() :  " + i);
    }
}

Child

public class Child extends Parent{
    public int i = 15;
    public void printI()
    {
        System.out.println("Child : printI()  :  " + i);

    }
}

EXAM01예제

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를 따라갔기 떄문이다.
       // 필드는 오버라이딩 되도 부모 타입을 따라간다. 




   }
}

메소드 오버라이딩만 기억한다.

  • 정보 은닉은 객체지향의 중요한 기법이다. 중요한 필드는 은닉하고, 필드는 메소드를 통해서만 접근해서 사용하도록 한다.

Getter와 Setter

  • 쓰는 이유는 Private한 메서드 및 필드에 접근하기 어렵기 때문에[정보은닉] 접근하기 위해 사용한다.
  • 필드의 값을 수정하고 얻기 위한 메서드를 만든다.

예제

public class Book {
    private int price; // field price이다.

    public int getPrice()
    {
        return this.price; //인스턴스 변수 

    }
    public void getPrice(int price) //메서드 안에 선언된건 local price
    {
        this.price = price;
    }

}
  • static 클래스에서 this 사용 x
  • this.price는 인스턴스 변수 / this는 내 자신 인스턴스를 참조하는 예약어
  • setter 와 getter는 -> 프로터피(property) - 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;
    }
}
  • title로 대답하는게 아니라, Name, price 2개의 프로퍼티를 가지고 있다고 하면 된다.

Object가 오버라이딩 하라고 제공하는 메소드

  • toString()
  • equals & hashCode()
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("자동차가 한대 생성됩니다.);

}

  • 매개변수 0개인 생성자를 기본생성자라고 한다.
  • 생성자가 하나도 없으면 아무일도 안 하는 디폴트 생성자가 만들어진다.

ex2) 이름을 가지고 태어나게 하고 싶다.

이름을 가지고 하고 싶다면, 필드에도 선언되어 있어야한다.

메소드 하나 추가해서 printName()

main 함수에서 출력하면

결과

Alter + insert 단축키 꼭 암기하기 !

user예제

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 + '\'' +
                '}';
    }
}
//생성자를 하나라도 만들게 되면 기본생성자는 자동으로 안 만들어진다.

자신의 생성자를 호출 할 때는 this()를 사용한다.

  • this는 인스턴스 자기 자신을 참조할 대 사용하는 키워드다.
  • this() 생성자는 자기 자신의 생성자를 말한다.
  • this() 생성자는 생성자 안에서만 사용가능하다.
  • this() 생성자는 생성자 안에서 super() 생성자를 호출하는 코드 다음이나, 첫번째 줄에 위치해야한다.

부모의 생성자를 호출할 때는 super를 사용한다.

  • 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 기본생성자.");
    }


}
profile
아는만큼보인다.

2개의 댓글

comment-user-thumbnail
2023년 7월 31일

정보 감사합니다.

1개의 답글