1. Getter 와 Setter

🚀  객체의 필드(데이터)를 외부에서 마음대로 읽고 변경할 경우 객체의 무결성이 깨질 수 있다.

       ex. 자동차의 속력은 음수가 될 수 없는데, 외부에서 음수로 변경하면 객체의 무결성이 깨짐

🚀  이러한 문제점 때문에 OOP에서는 직접적인 외부에서의 필드 접근을 막고 대신 메소드를 통해 필드에 접근하는 것을 선호

       ➡️  메소드는 데이터를 검증해서 유효한 값만 필드에 저장할 수 있기 때문

       ➡️  이러한 역할을 하는 메소드가 Setter

🚀  필드값이 객체 외부에서 사용하기에 부적절한 경우, 메소드로 적절한 값으로 변환해서 리턴할 수 있다.

       ➡️  이러한 역할을 하는 메소드가 Getter

private 타입 fieldName;

// Getter
public 타입 getFieldName() {
  return fieldName;
}

// Setter
public void setFieldName(타입 fieldName) {
  this.fieldName = fieldName;
}

 

  • field 타입이 boolean일 경우에는 Getter는 get으로 시작하지 않고 is로 시작하는 것이 관례
public class Car {
	// 필드 선언
	private int speed;
	private boolean stop;
	
	// speed 필드의 Getter/Setter 선언
	public int getSpeed() {
		return speed;
	}
    
	public void setSpeed(int speed) {
		if(speed < 0) {
			this.speed = 0;
			return;
		} else {
			this.speed = speed;
		}
	}
    
	// stop 필드의 Getter/Setter 선언
	public boolean isStop() {
		return stop;
	}
    
	public void setStop(boolean stop) {
		this.stop = stop;
		if(stop == true) this.speed = 0;
	}
}
public class CarExample {
	public static void main(String[] args) {
		//객체 생성
		Car myCar = new Car();

		//잘못된 속도 변경
		myCar.setSpeed(-50);
		System.out.println("현재 속도: " + myCar.getSpeed());

		//올바른 속도 변경
		myCar.setSpeed(60);
		System.out.println("현재 속도: " + myCar.getSpeed());
		
		//멈춤
		if(!myCar.isStop()) {
			myCar.setStop(true);
		}
		System.out.println("현재 속도: " + myCar.getSpeed());
	}
}

 

 


2.  싱글톤 패턴  Singleton

🚀  애플리케이션 전체에서 단 한 개의 객체만 생성해서 사용하고 싶다면 싱글톤 패턴을 적용

🚀  핵심은 생성자를 private 접근 제한해서 외부에서 new 연산자로 생성자를 호출할 수 없도록 막는 것

🚀  대신 정적 메소드를 통해 간접적으로 객체를 얻을 수 있다.

public class 클래스 {
  // private 접근 권한을 갖는 정적 필드 선언과 초기화
  private static 클래스 singleton = new 클래스();
  
  // private 접근 권한을 갖는 생성자 선언
  private 클래스() {}
  
  // public 접근 권한을 갖는 정적 메소드 선언
  public static 클래스 getInstance() {
    return singleton;
  }
}
  • 외부에서 객체를 얻는 유일한 방법은 getInstance() 메소드를 호출하는 것.
  • getInstance() 메소드가 리턴하는 객체는 정적 필드가 참조하는 싱글톤 객체이다.
  • 따라서 아래 코드에서 변수1과 변수2가 참조하는 객체는 동일한 객체가 된다.
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();

public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton() {}

    static Singleton getInstance() {
        return singleton;
    }
}
public class SingletonExample {
	public static void main(String[] args) {
		/*
 		Singleton obj1 = new Singleton(); //컴파일 에러
 		Singleton obj2 = new Singleton(); //컴파일 에러
		 */
		
		//정적 메소드를 호출해서 싱글톤 객체 얻음
		Singleton obj1 = Singleton.getInstance();
		Singleton obj2 = Singleton.getInstance();

		//동일한 객체를 참조하는지 확인
		if(obj1 == obj2) {
			System.out.println("같은 Singleton 객체입니다.");
		} else {
			System.out.println("다른 Singleton 객체입니다.");
		}
	}
}

 

 

 

*  내용 참고 - 책 '이것이 자바다'

 


1.  final 필드와 상수

✏️  값을 변경하는 것을 막고 읽기만 허용할 때 사용

 

1)  final 필드 선언

final 타입 필드 [ =초기값];

 

  ⚒️  final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어 실행 도중 값을 수정 X

  ⚒️  final 필드에 초기값을 줄 수 있는 방법은 2가지

  • 필드 선언 시에 초기값 대입
  • 생성자에서 초기값 대입

  ⚒️ 고정된 값이라면 필드 선언 시에 주는 것이 간단하지만 복잡한 초기화 코드가 필요하거나 객체 생성 시에 외부에서 전달된 값으로 초기화한다면 생성자에서 해야 한다.

        ⚡️  초기값을 주지 않고 final 필드를 그대로 남겨두면 컴파일 에러가 생김

public class Korean {
	//인스턴스 final 필드 선언
	final String nation = "대한민국";
	final String ssn;
	
	//인스턴스 필드 선언
	String name;

	//생성자 선언
	public Korean(String ssn, String name) {
		this.ssn = ssn;
		this.name = name;
	}
}
public class KoreanExample {
	public static void main(String[] args) {
		//객체 생성 시 주민등록번호와 이름 전달
		Korean k1 = new Korean("123456-1234567", "감자바");
		
		//필드값 읽기
		System.out.println(k1.nation);
		System.out.println(k1.ssn);
		System.out.println(k1.name);

		//Final 필드는 값을 변경할 수 없음
		//k1.nation = "USA";
		//k1.ssn = "123-12-1234";

		//비 final 필드는 값 변경 가능
		k1.name = "김자바";
	}
}

 


2)  상수 선언

static final 타입 상수 [ =초기값];

 

  ⚒️  불변의 값을 저장하는 필드를 자바에서는 상수 constant 라고 부름

         ex. 원주율 파이나 지구의 무게 및 둘레 등

  ⚒️  상수는 객체마다 저장할 필요가 없고, 여러 개의 값을 가져도 안되기 때문에 static이면서 final인 특성을 가져야 함.

  ⚒️  상수 이름은 모두 대문자로 작성하는 것이 관례. 서로 다른 단어가 혼합된 이름이라면 언더바(_)로 단어들을 연결함

static final double PI = 3.14159;
static final double EARTH_SURFACE_AREA = 5.147185403641517E8;

 

  ⚒️  상수는 정적 필드 이므로 클래스로 접근

클래스명.상수

 

public class Earth {
	//상수 선언 및 초기화
	static final double EARTH_RADIUS = 6400;

	//상수 선언
	static final double EARTH_SURFACE_AREA;
	
	//정적 블록에서 상수 초기화
	static {
		EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
	}
}
public class EarthExample {
	public static void main(String[] args) {
		//상수 읽기
		System.out.println("지구의 반지름: " + Earth.EARTH_RADIUS + "km");
		System.out.println("지구의 표면적: " + Earth.EARTH_SURFACE_AREA + "km^2");
	}
}

 

 


2.  접근 제한자  Access Modifier

 

🚀  중요한 필드와 메소드가 외부로 노출되지 않도록 해 객체의 무결성(결점이 없는 성질)을 유지

🚀  default는 접근 제한자가 아니라 접근 제한자가 붙지 않은 상태!

접근 제한자 제한 대상 제한 범위
public 클래스, 필드, 생성자, 메소드 없음
protected 필드, 생성자, 메소드 같은 패키지이거나, 자식 객체에만 사용 가능
(default) 클래스, 필드, 생성자, 메소드 같은 패키지
private 필드, 생성자, 메소드 객체 내부

 

 

1)  클래스의 접근 제한

  ⚒️  클래스는 public과 default 접근 제한을 가질 수 있다.

[ public ] class 클래스 { ... }
  • 클래스 선언할 때 public 접근 제한자를 생략했다면 클래스는 default 접근 제한을 가진다.
  • 이 경우 클래스는 같은 패키지에서는 아무런 제한 없이 사용할 수 있지만 다른 패키지에서는 사용할 수 없게 된다.
  • public을 붙였다면 다른 패키지에서도 사용 가능

 

 


2)  생성자의 접근 제한

  ⚒️  생성자는 public, default, private 접근 제한을 가질 수 있다.

public class ClassName {
    // 생성자 선언
    [ public | private ] ClassName(...) {...}
}

 

접근 제한자 생성자 설명
public 클래스(...) 모든 패키지에서 생성자를 호출할 수 있다.
= 모든 패키지에서 객체를 생성할 수 있다.
default 클래스(...) 같은 패키지에서만 생성자를 호출할 수 있다.
= 같은 패키지에서만 객체를 생성할 수 있다.
private 클래스(...) 클래스 내부에서만 생성자를 호출할 수 있다.
= 클래스 내부에서만 객체를 생성할 수 있다.

 

package ch06.sec13.exam02.package1;

public class A {
	//필드 선언
	A a1 = new A(true);
	A a2 = new A(1);
	A a3 = new A("문자열");

	//public 접근 제한 생성자 선언
	public A(boolean b) {
	}
	
	//default 접근 제한 생성자 선언
	A(int b) {
	}

	//private 접근 제한 생성자 선언
	private A(String s) {
	}
}
package ch06.sec13.exam02.package1;

public class B {
	// 필드 선언
	A a1 = new A(true); 	//o
	A a2 = new A(1); 	    //o
	//A a3 = new A("문자열");	//x
}

 


3)  필드와 메소드의 접근 제한

  ⚒️  필드와 메소드는 public, default, private 접근 제한을 가질 수 있다.

// 필드 선언
[ public | private ] 타입 필드;

// 메소드 선언
[ public | private ] 리턴타입 메소드(...) { ... }

 

접근 제한자 생성자 설명
public 필드
메소드(...)
모든 패키지에서 필드를 읽고 변경할 수 있다.
모든 패키지에서 메소드를 호출할 수 있다.
default 필드
메소드(...)
같은 패키지에서만 필드를 읽고 변경할 수 있다.
같은 패키지에서만 메소드를 호출할 수 있다.
private 필드
메소드(...)
클래스 내부에서만 필드를 읽고 변경할 수 있다.
클래스 내부에서만 메소드를 호출할 수 있다.

 

public class A {
	//public 접근 제한을 갖는 필드 선언
	public int field1;
	//default 접근 제한을 갖는 필드 선언
	int field2;
	//private 접근 제한을 갖는 필드 선언
	private int field3;

	//생성자 선언
	public A() {
		field1 = 1; 		//o
		field2 = 1; 		//o
		field3 = 1; 		//o

		method1(); 			//o
		method2(); 			//o
		method3(); 			//o
	}

	//public 접근 제한을 갖는 메소드 선언
	public void method1() {
	}
	
	//default 접근 제한을 갖는 메소드 선언
	void method2() {
	}
	
	//private 접근 제한을 갖는 메소드 선언
	private void method3() {
	}
}

 

같은 패키지
package ch06.sec13.exam03.package1;

public class B {
	public void method() {
		//객체 생성
		A a = new A();

		//필드값 변경
		a.field1 = 1; 		// o
		a.field2 = 1; 		// o
		//a.field3 = 1;		// x

		//메소드 호출
		a.method1(); 		// o
		a.method2(); 		// o
		//a.method3(); 		// x
	}
}

 

다른 패키지
package ch06.sec13.exam03.package2;

import ch06.sec13.exam03.package1.*;

public class C {
	public C() {
		//객체 생성 
		A a = new A();
		
		//필드값 변경 
		a.field1 = 1; 		// (o)
		//a.field2 = 1; 	// (x)
		//a.field3 = 1; 	// (x)

		//메소드 호출 
		a.method1(); 		// (o)
		//a.method2(); 		// (x)
		//a.method3(); 		// (x)
	}
}

 

 

 

 

* 내용 참고 - 책 '이것이 자바다'


구분 설명
인스턴스(instance) 멤버 객체에 소속된 멤버
(객체를 생성해야만 사용할 수 있는 멤버)
정적(static) 멤버 클래스에 고정된 멤버
(객체 없이도 사용할 수 있는 멤버)

 

1.  인스턴스 멤버

🚀  객체에 소속된 멤버를 말한다. 

public class Car {
    // 인스턴스 필드 선언
    int gas;
    
    // 인스턴스 메소드 선언
    void setSpeed(int speed) { ... }
}

 

  • gas 필드와 setSpeed() 메소드는 인스턴스 멤버이기 때문에 외부 클래스에서 사용하기 위해서는 Car 객체를 먼저 생성하고 참조 변수로 접근해서 사용해야 한다.
Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);

Car yourCar = new Car();
yourCar.gas = 20;
yourCar.setSpeed(80);

 

 

👾  메소드는 코드의 덩어리이므로 객체마다 저장한다면 중복 저장으로 인해 메모리 효율이 떨어짐

👾  메소드 코드는 메소드 영역에 두되 공유해서 사용하고, 이때 객체 없이는 사용하지 못하도록 제한을 걸어둔 것

 

 

 

 

 


 

2.  정적 멤버

🚀  자바는 클래스 로더를 이용해서 클래스를 메소드 영역에 저장하고 사용한다.

🚀  정적 멤버란 메소드 영역의 클래스에 고정적으로 위치하는 멤버를 말한다.

       ➡️  객체를 생성할 필요 없이 클래스를 통해 바로 사용이 가능

 

1)  정적 멤버 선언

  ⚒️  필드와 메소드는 모두 'static 키워드'를 추가하면 정적 멤버가 될 수 있다.

public class 클래스 {
    // 정적 필드 선언
    static 타입 필드 [= 초기값];
    
    // 정적 메소드
    static 리턴타입 메소드( 매개변수, ... ) { ... }
}

 

  • 객체마다 가지고 있을 필요성이 없는 공용적인 필드는 정적 필드로 선언하는 것이 좋다.
  • 예를 들어 Calculator 클래스에서 원의 넓이나 둘레를 구할 때 필요한 파이(π)는 객체마다 가지고 있을 필요가 없기 때문에 정적필드로 선언
public class Calculator {
    String color;                // 계산기 별로 색깔이 다를 수 있다.
    static double pi = 3.14159;  // 계산기에서 사용하는 파이 값은 동일
}

 

  • 인스턴스 필드를 이용하지 않는 메소드는 정적 메소드로 선언하는 것이 좋다. 
  • 예를 들어 Calculator의 plus() 메소드는 외부에서 주어진 매개값들을 가지고 처리하므로 정적 메소드로 선언하는 것이 좋다.
public class Calculator {
    String color;  // 인스턴스 필드
    void setColor(String color) { this.color = color; } // 인스턴스 메소드
    static int plus(int x, int y) { return x + y; }     // 정적 메소드
    static int minus(int x, int y) { return x - y; }    // 정적 메소드
}

 


2)  정적 멤버 사용

  ⚒️  클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있는데, 클래스 이름과 함께 도트(.) 연산자로 접근하면 된다.

  ⚒️  객체 참조 변수로도 접근이 가능

public class Calculator {
    static double pi = 3.14159;
    static int plus(int x, int y) { ... }
    static int minus(int x, int y) { ... }
}
double result1 = 10 * 10 * Calculator.pi;
int result2 = Calculator.plus(10,5);
int result3 = Calculator.minus(10,5);
Calculator myCalcu = new Calculator();
double result1 = 10 * 10 * myCalcu.pi;
int result2 = myCalcu.plus(10, 5);
int result3 = myCalcu.minus(10, 5);

 


3)  정적 블록

  ⚒️  정적 필드는 필드 선언과 동시에 초기값을 주는 것이 일반적

  ⚒️  but, 복잡한 초기화 작업이 필요하다면 정적 블록을 이용

static {
  ...
}

 

  • 정적 블록은 클래스가 메모리로 로딩될 때 자동으로 실행된다.
  • 정적 블록이 클래스 내부에 여러 개가 선언되어 있을 경우에는 선언된 순서대로 실행된다.
public class Television {
    static String company = "MyCompany";
    static String model = "LCD";
    static String info;
    
    static {
        info = company + "-" + model;
    }
}

public class TelevisionExample {
    public static void main(String[] args) {
        System.out.println(Television.info);
    }
}

 


4)  인스턴스 멤버 사용 불가

  ⚒️  정적 메소드와 정적 블록은 객체가 없어도 실행된다는 특징 때문에 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다.

  ⚒️  객체 자신의 참조인 this 도 사용 X

  ⚒️  main() 메소드도 정적 메소드이므로 동일한 규칙이 적용된다. 

         ➡️  객체 생성 없이 인스턴스 필드와 인스턴스 메소드를 main() 메소드에 바로 사용할 수 없다.

public class Car {
    // 인스턴스 필드 선언
    int speed;
    
    // 인스턴스 메소드 선언
    void run() { ... }
    
    // 메인 메소드 선언
    public static void main(String[] args) {
        speed = 60;  // 컴파일 에러
        run();       // 컴파일 에러
    }
}

 

public static void main(String[] args) {
    // 객체 생성
    Car myCar = new Car();
    // 인스턴스 멤버 사용
    myCar.speed = 60;
    myCar.run();
}

 

 


사용 예제
class Cars {

    static int wheel = 4; // 정적 멤버 -> 객체 생성 안하더라도 메모리에 올라가 있음
    int speed;            // 인스턴스 멤버
    
}

public class Test {

    // 인스턴스 변수는 각각의 인스턴스마다 고유의 저장 공간을 가지고 있기 때문에 독립적으로 고유값을 저장 가능.
    public static void main(String[] args) {
    
        System.out.println(Cars.wheel); // 클래스 이름으로 접근 가능
        System.out.println(Cars.speed); // 에러발생! 
        // 인스턴스 변수는 클래스 이름으로 접근 불가 -> 메모리에 올라와있지 않기 때문.

        Cars myCar1 = new Cars();
        System.out.println(Cars.wheel); // 4
        System.out.println(myCar1.speed); // 0

        Cars myCar2 = new Cars();

        System.out.println("<변경 전>");
        System.out.println("myCar1.speed: " + myCar1.speed); // 0
        System.out.println("myCar2.speed: " + myCar2.speed); // 0
        System.out.println("myCar1.wheel: " + Cars.wheel); // 4
        System.out.println("myCar2.wheel: " + Cars.wheel); // 4
    
        myCar2.speed = 100;
        myCar2.wheel = 5;
        
        System.out.println("<변경 후>");
        System.out.println("myCar1.speed: " + myCar1.speed); // 0
        System.out.println("myCar2.speed: " + myCar2.speed); // 100
        System.out.println("myCar1.wheel: " + Cars.wheel);   // 5
        System.out.println("myCar2.wheel: " + Cars.wheel);   // 5
        // 인스턴스는 클래스 변수를 공유하기 때문에 같은 값이 됨.
    }
}

 

 

정적 메소드와 인스턴스 메소드
class Area {

    static void manual() {  // 클래스 메서드
        System.out.println("현재 사용 가능한 함수 목록");
        System.out.println("triangle : 삼각형 넓이");
        System.out.println("rectangle : 사각형 넓이");
        System.out.println("입니다.");
    }

    double triangle(int a, int b) { // 인스턴스 메서드
        return (double) a * b / 2;
    }

    int rectangle(int a, int b) { // 인스턴스 메서드
        return a * b;
    }
}

public class Test {
    public static void main(String[] args) {

        Area.manual(); // 클래스 메서드 접근 가능
        // Area.triangle(3,5); 에러 발생. 
        // Area.rectangle(3,5); 에러발생.

        Area cal = new Area();
        cal.manual();  // 가급적 클래스 단위로 메서드 호출하는 것이 좋음.
        System.out.println(cal.triangle(3, 5));
        System.out.println(cal.rectangle(3,4));
    }
}

 

 

 

* 내용 참고 - 책 '이것이 자바다'


 

1.  생성자 선언과 호출

📄  '생성자 constructor' 객체가 생성될 때 객체를 초기화하는 특수한 메서드. ( = 인스턴스 초기화 메서드 )

 

클래스 변수 = new 클래스();

 

  • new 연산자는 객체를 생성한 후 생성자를 호출해서 객체를 초기화하는 역할을 한다
  • 생성자가 성공적으로 실행이 끝나면 new 연산자는 객체의 주소를 리턴
  • 리턴된 주소는 클래스 변수에 대입되어 객체의 필드나 메소드에 접근 할 때 이용

1)  기본 생성자

  👾  모든 클래스는 생성자가 존재하며 하나 이상을 가질 수 있다.

  👾  클래스에 생성자 선언이 없으면 컴파일러는 다음과 같은 기본 생성자를 바이트코드 파일에 자동으로 추가시킨다.

         ➡️  그렇기 때문에 new 연산자 뒤에 기본 생성자를 호출할 수있다.

         ➡️  개발자가 명시적으로 선언한 생성자가 있다면 컴파일러는 기본 생성자를 추가하지 않는다.

[public] 클래스() { }
  • 클래스가 public class로 선언되면 기본 생성자도 public이 붙지만, 클래스가 public 없이 class로만 선언되면 기본 생성자에도 public이 붙지 않는다.


2)  생성자 선언

클래스(매개변수,...) {
  // 객체의 초기화 코드
}

 

  👾  생성자는 메소드와 비슷한 모양을 가지고 있으나, 리턴 타입이 없고 클래스 이름과 동일

  👾  매개변수는 new 연산자로 생성자를 호출할 때 매개값을 생성자 블록 내부로 전달하는 역할

public class Car {
    // 생성자 선언
    Car(String model, String color, int maxSpeed) { }
}
public class CarExample {
  public static void main(String[] args) {
    Car myCar = new Car("그랜저", "검정", 250);
    // Car myCar = new Car(); // 기본 생성자 호출 못함
  }
}

 

필드 초기화

 

  📍  객체마다 동일한 값을 갖고 있다면 필드 선언 시 초기값을 대입

  📍  객체마다 다른 값을 가져야 한다면 생성자에서 필드를 초기화

class Pizza {
    int size;
    String type;

    public Pizza() { // 매개변수가 없는 생성자
        size = 12;
        type = "슈퍼슈프림";
    }
    public Pizza(int s, String t) { // 매개변수가 있는 생성자
        size = s;
        type = t;
    }
}
public class PizzaTest {
    public static void main(String[] args) {
        Pizza pizza1 = new Pizza();
        System.out.println("(" + pizza1.type + ", " + pizza1.size + ")"); // (슈퍼슈프림, 12)

        Pizza pizza2 = new Pizza(24,"포테이토");
        System.out.println("(" + pizza2.type + ", " + pizza2.size + ")"); // (포테이토, 24)
    }
}

 

 

this 키워드

 

  📍 매개변수명이 필드명과 동일한 경우 필드임을 구분하기 위해 ths 키워드를 필드명 앞에 붙여줌

  📍 this는 현재 객체를 말하며, this.name은 현재 객체의 데이터(필드)로서의 name을 뜻함

public class Korean {
    // 필드 선언
    String nation = "대한민국";
    String name;
    String ssn;
    
    // 생성자 선언
    public Korean(String name, String ssn) {
        this.name = name;
        this.ssn = ssn;
    }    
}

 


3)  생성자 오버로딩 Overloading

  👾  매개값으로 객체의 필드를 다양하게 초기화하려면 생성자 오버로딩이 필요

  👾  생성자 오버로딩이란 매개변수를 달리하는 생성자를 여러 개 선언하는 것을 말함

public class Car {
    Car() { ... }
    Car(String model) { ... }
    Car(String model, String color) { ... }
    Car(String model, String color, int maxSpeed) { ... }
}

 

  • 매개변수의 타입, 개수, 순서가 다르게 여러 개의 생성자 선언
  • new 연산자로 생성자를 호출할 때 제공되는 매개값의 타입과 수에 따라 실행될 생성자가 결정됨.
// 오버로딩 아닌 경우
Car(String model, String color) { ... }
Car(String color, String model) { ... }

 

public class Car {
	//필드 선언
	String company = "현대";
	String model;
	String color;
	int maxSpeed;
	
	//생성자 선언
	Car() {}
	
	Car(String model) { 
		this.model = model; 
	}
	
	Car(String model, String color) {
		this.model = model;
		this.color = color;
	}
	
	Car(String model, String color, int maxSpeed) {
		this.model = model;
		this.color = color;
		this.maxSpeed = maxSpeed;
	}
}
public class CarExample {
	public static void main(String[] args) {
		Car car1 = new Car();
		System.out.println("car1.company : " + car1.company);
		System.out.println();

		Car car2 = new Car("자가용");
		System.out.println("car2.company : " + car2.company);
		System.out.println("car2.model : " + car2.model);
		System.out.println();
		
		Car car3 = new Car("자가용", "빨강");
		System.out.println("car3.company : " + car3.company);
		System.out.println("car3.model : " + car3.model);
		System.out.println("car3.color : " + car3.color);
		System.out.println();
		
		Car car4 = new Car("택시", "검정", 200);
		System.out.println("car4.company : " + car4.company);
		System.out.println("car4.model : " + car4.model);
		System.out.println("car4.color : " + car4.color);
		System.out.println("car4.maxSpeed : " + car4.maxSpeed);
	}
}

 

다른 생성자 호출

 

  📍 생성자 오버로딩이 많아질 경우 생성자 간의 중복된 코드가 발생할 수 있다.

  📍 이 경우 공통 코드를 한 생성자에만 집중적으로 작성하고, 나머지 생성자는 this(...)를 사용하여 공통 코드를 가지고 있는 생성자를 호출하는 방법으로 개선

Car(String model) { 
	this.model = model; 
	this.color = "은색";
	this.maxSpeed = 250;
}
	
Car(String model, String color) {
	this.model = model;
	this.color = color;
	this.maxSpeed = 250;
}
	
Car(String model, String color, int maxSpeed) {
	this.model = model;
	this.color = color;
	this.maxSpeed = maxSpeed;
}

 

Car(String model) {
    this(model, "은색", 250);
}

Car(String model, String color) {
    this(model, color, 250);
}

Car(String model, String color, int maxSpeed) {
    // 공통 초기화 코드
    this.model = model;
    this.color = color;
    this.maxSpeed = maxSpeed;
}

 

  • this( 매개값, ... )는 생성자의 첫 줄에 작성되며 다른 생성자를 호출하는 역할을 한다.
  • 호출하고 싶은 생성자의 매개변수에 맞게 매개값을 제공하면 됨.
  • this() 다음에는 추가적인 실행문을 작성할 수 있는데, 호출되는 생성자의 실행이 끝나면 원래 생성자로 돌아와서 다음 실행문을 실행한다. 

 

응용 문제
  1. 학생을 나타내는 클래스 Student를 만든다.
  2. 학생은 이름(name)과 학번(rollNo), 나이(age)를 가진다.
  3. 메서드는 생성자만 가진다.
  4. 생성자를 실행하면 아래의 결과값이 출력된다.
   학생의 이름 : Kim
   학생의 학번 : 0001
   학생의 나이 : 20
   Student 객체가 생성되었습니다.
class Student{
    String name;
    String rollNo;
    int age;

    public Student(String name, String rollNo, int age){
        this.name = name;
        this.age = age;
        this.rollNo = rollNo;

        System.out.println("학생의 이름 : " + name); // 학생의 이름 : Kim
        System.out.println("학생의 학번 : " + rollNo); // 학생의 학번 : 0001
        System.out.println("학생의 나이 : " + age); // 학생의 나이 : 20

        System.out.println("Student 객체가 생성되었습니다.");
    }
}

public class Test {
    public static void main(String[] args) {
        Student student = new Student("Kim", "0001", 20);
    }
}

 

 

 

* 내용 참고 - 책 '이것이 자바다'

 


1.  JVM 메모리 구조

1)  메서드 영역 (method area)

  • 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스파일을 분석하여 이곳에 저장한다.

2)  힙(heap)

  • 인스턴스가 생성되는 공간.

3)  호출스택(call stack 또는 execution stack)

  • 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면, 호출된 메서드를 위한 메모리가 할당되며 이 메모리는 메서드가작업을 수행하는 동안 지역변수들과 연산의 중간결과 등을 저장하는데 사용된다.
  • 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워진다.
  • 각 메서드를 위한 메모리상의 작업공간은 서로 구별되며, 첫 번째로 호출된 메서드를 위한 작업공간이 호출스택의 맨 밑에 마련되고, 첫 번째 메서드 수행 중에 다른 메서드를 호출하면 첫 번째 메서드의 바로 위에 두 번째로 호출된 메서드를 위한 공간이 마련된다.
💡  후입선출 구조 (LIFO : Last-in, First-out)
     -  제일 상위에 위치하는 메서드가 현재 실행중인 메서드 
     -  이 메서드가 끝나야 먼저 호출된 메서드가 다시 실행하게 됨
     -  반환타입이 있는 메서드는 종료되면서 결과값을 자신을 호출한 메서드(caller)에게 반환한다
     -  caller는 넘겨받은 반환값으로 수행을 계속 진행하게 된다

public class MyMethod_08 {

	// main() -> firstMethod() -> secondMethod()
	// 메모리 구조상 secondMethod가 가장 위에 쌓이고, main이 가장 아래에 깔린다고 생각하면 됨.

	public static void main(String[] args) {
	     System.out.println("main(String[] args)이 시작되었음."); //1
	     firstMethod(); // 이 메서드를 호출함. 리턴 값이 있든 없든 이 메서드가 끝나야지 다음 줄로 넘어감.
	     System.out.println("main(String[] args)이 끝났음.");//6
	}
	
    static void firstMethod() {
    	System.out.println("firstMethod()이 시작되었음."); //2
        secondMethod(); // 이 메서드가 끝나야지 다음 줄로 넘어감.
        System.out.println("firstMethod()이 끝났음.");//5
    }
    
    static void secondMethod() {
    	System.out.println("secondMethod()이 시작되었음."); //3
    	System.out.println("secondMethod()이 끝났음."); //4
    }
}

 

 

​2.  오버로딩(overloading)

🚀  한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 말함

🚀  오버로딩의 조건  1) 메서드 이름이 같아야 한다. 2) 매개변수의 개수 또는 타입이 달라야 한다.

      ⚡️  매개변수에 의해서만 구별될 수 있으므로 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.

장점
  • 메서드의 이름만 보고도 같은 기능을 하겠다는 예측을 쉽게 할 수 있다.
  • 메서드의 이름을 절약할 수 있음   ➡️  하나의 이름으로 여러개의 메서드를 정의할 수 있어 이름 짓는데 고민이 줄고, 동시에 사용되었어야 할 메서드 이름을 다른 메서드의 이름으로 사용할 수 있다.
public class MyMethod_06 {

	public static void main(String[] args) {
		
		printGreet(); // 안녕하세요
		printGreet("안녕~!"); // 안녕~!
		printGreet("한수", "잘 지내셨나요?"); // 한수님! 잘 지내셨나요?
		printGreet(3); // 안녕하세요. 3번째 보는거네요.
	}
	
	public static void printGreet() {
		System.out.println("printGreet() 메서드가 실행됩니다.");
		System.out.println("안녕하세요.");
	}
	
	public static void printGreet(String greeting) {
		// 같은 이름의 메서드가 있으나 매개변수가 없음
		System.out.println("printGreet(String greeting) 메서드가 실행됩니다.");
	    System.out.println(greeting);
	}
	
	public static void printGreet(String name, String greeting) {
		// 매개변수가 문자열인 메서드가 있으나 갯수가 다름
		System.out.println("printGreet(String name, String greeting) 메서드가 실행됩니다.");
	    System.out.println(name + "님! "+ greeting);
	}
	
    public static void printGreet(int cont) {
    	// 매개변수가 하나인 메서드가 있으나 데이터 타입이 다름
        System.out.println("printGreet(int cont) 메서드가 실행됩니다.");
        System.out.println("안녕하세요. " + cont + "번째 보는거네요.");
    }
}
 


3.  응용 문제

1)  암산 트레이닝

public class Ex_02_01 {
    static Scanner scanner= new Scanner(System.in);
   
    static boolean confirmRetry() {
    	int numb;
    	do {
    		System.out.print("다시 한 번? <Yes-1/No-0> : ");
    		numb = scanner.nextInt();
    	} while(numb != 0 && numb != 1);
    	return numb == 1;
    }

	public static void main(String[] args) {
		Random random = new Random();
		
		System.out.println("암산 트레이닝!!");
	    do {
	    	int x = random.nextInt(90)+10;
	    	int y = random.nextInt(90)+10;
	    	int z = random.nextInt(90)+10;
	    	
	    	while (true) {
	    		System.out.print(x + " + " + y + " + " + z + " = ");
	    		int k = scanner.nextInt();
	    		if (k == x + y + z) { // 정답
	    			break;
	    		}	
	    		else if ( k > x + y + z ) {
	    			System.out.println("down");
	    		}
	    		else {
	    			System.out.println("up");
	    		}
	    		System.out.println("틀렸습니다!!");
	    	}
	    } while(confirmRetry());
	}
}

 2)  학생 조사

public class Ex_02_02 {

	static Scanner scanner = new Scanner(System.in);
	static boolean run = true; 
    static int studentNum = 0; 
    static int[] scores = null;
	
	public static void main(String[] args) {
		
        while (run) {
            System.out.println("-----------------------------------------------------");
            System.out.println("1.학생수 | 2.점수입력 | 3.점수리스트 | 4.분석 | 5.종료");
            System.out.println("-----------------------------------------------------");
            System.out.print("선택> ");
            int selectNo = scanner.nextInt();
          
            if (selectNo == 1) {
            	inputStudentNum();
            } else if (selectNo == 2) {
            	inputScores();
            } else if (selectNo == 3) {
                printScores();
            } else if (selectNo == 4) {
                printAnalysis();
            } else if (selectNo == 5) {
                setRun();
            }
        }
        System.out.println("프로그램 종료");
	}
	
	public static void inputStudentNum() {
		System.out.print("학생수> ");
        studentNum = scanner.nextInt();
        scores = new int[studentNum];
        System.out.println(Arrays.toString(scores));
	}
	
	public static void inputScores() {
		for(int i = 0; i < scores.length; i++) {
        	System.out.print("scores["+i+"] : ");
        	scores[i] = scanner.nextInt();
		}
	}
	
	public static void printScores() {
		for(int i = 0; i < scores.length; i++) {
            System.out.println("score["+i+"] : " + scores[i]);
    	}
	}
	
	public static void printAnalysis() {
		int max = 0;
    	int sum = 0;
    	double avg = 0;
    	
    	for(int i = 0; i < scores.length; i++) {
    		max = (max < scores[i]) ? scores[i] : max;
    		sum += scores[i];
			}
		    avg = (double) sum / studentNum;
		    System.out.println("최고 점수: " + max);
    		System.out.println("평균 점수: " + avg);
	}
	
	public static void setRun() {
		run = false;
	}
}
 

 3)  login & logout

  • login() 메서드와 logout() 메서드를 선언
  • login() 메서드를 호출할 때는 매개값으로 id와 passwd를 제공하고, logout() 메서드는 id만 매개값으로 제공
  • login() 메서드는 매개값 id가 "admin", 매개값 password가 "1234"일 경우에만 true로 리턴하고 그외의 값일 경우에는 false를 리턴 
  • logout() 메서드는 "admin 아이디가 로그아웃 되었습니다."가 출력
public class Ex_02_04 {
	
	static Scanner scanner = new Scanner(System.in);
	
	public static boolean login(String id, String password) {
		System.out.print("아이디를 입력해 주세요. >> ");
        String inputId = scanner.nextLine();
        
        
        System.out.print("비밀번호를 입력해 주세요. >> ");
        String inputPw = scanner.nextLine();
        
        return inputId.equals("admin") && inputPw.equals("1234");
	}
	
	public static void logout(String id) {
		System.out.println(id + "가 로그아웃 되었습니다.");
	}

	public static void main(String[] args) {

        boolean result = login("",""); // true대신 login()호출;
         if (result) {
             System.out.println("로그인 되었습니다.");
             logout("admin");
             //logout() 호출;
         }
         else {
             System.out.println("id 또는 password가 올바르지 않습니다.");
         }
         
	}
}
 

 

*  내용 참고 : 학원강의 및 자바의정석 3rd

 


Method 함수(function) 

👩🏻‍💻  특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것

👩🏻‍💻  기본적으로 수학의 함수와 유사하며, 어떤 값을 입력하면 이 값으로 작업을 수행해서 결과를 반환

👩🏻‍💻  main 메서드는 '규칙'. 이 메서드 안에 실행되기를 기다리는 로직을 위치시켜야 한다. 

1)  메서드의 장점과 작성지침

  · 반복적인 코드를 줄이고 코드의 관리가 용이.

  · 반복적으로 수행되는 여러 문장을 메서드로 작성.

  · 하나의 기능을 위해 작성한 여러 코드를 하나의 단어로 표현할 수 있음. (추상화)

  · 하나의 메서드는 한 가지 기능만 수행하도록 작성하는 것이 좋음.

  · 메서드는 특정 동작을 하는 코드이기 때문에 이름을 동사로 시작   ex. getAge ()


2)  메서드 용어

  · 인수 (argument) : 메서드에 전달할 입력(input)을 의미

  · 매개변수 (parameter) : 인수를 받아서 저장하는 변수를 의미

  · 반환값 (return) : 메서드의 출력(output)을 의미. 

      ⚡️  return의 기능 1) 값을 반환  2) 함수를 종료 (반복문의 break와 비슷) 

  · 메서드 호출 : 메서드를 실제로 사용하는 것을 의미

 

사용 예제

 

   📍  3명의 신장, 체중, 나이의 최대값을 구해서 표시
         ✔️  메서드를 만들면 1) 소스가 줄어들어서 main 메서드의 기본 로직을 읽기 쉽고 2) 재사용을 할 수 있다

public class MyMethod_01 {

    static Scanner scanner = new Scanner(System.in);
    static int getMax(int a, int b, int c) { // a,b,c의 최대값을 반환
    	int max = a;
    	if(b > max) max = b;
    	if(c > max) max = c;
    	return max;
    }
    
    static void inputData(int[] height, int[] weight, int[] age) {
    	  for (int i = 0; i < 3; i++) {
  	    	System.out.print("[" + (i+1) + "] ");
  	    	System.out.print("신장: ");  height[i] = scanner.nextInt();
  	    	System.out.print("   체중: "); weight[i] = scanner.nextInt();
  	        System.out.print("   나이: ");  age[i]  = scanner.nextInt();
  	    }
    }

	public static void main(String[] args) {
		
		// 3명의 신장, 체중, 나이의 최대값을 구해서 표시.
		int[] height = new int[3];  // 신장
		int[] weight = new int[3];  // 체중
		int[] age = new int[3];     // 나이
		
		inputData(height, weight, age);
	    
/*	    // 신장의 최대값을 구한다
	    int maxHeight = height[0];
	    if (height[1] > maxHeight) maxHeight = height[1];
	    if (height[2] > maxHeight) maxHeight = height[2];
	    
	    // 체중의 최대값을 구한다
	    int maxWeight = weight[0];
	    if (weight[1] > maxWeight) maxWeight = weight[1];
	    if (weight[2] > maxWeight) maxWeight = weight[2];
	    
	    // 나이의 최대값을 구한다
	    int maxAge = age[0];
	    if (age[1] > maxAge) maxAge = age[1];
	    if (age[2] > maxAge) maxAge = age[2]; */
	    
	    int maxHeight = getMax(height[0], height[1], height[2]);  // 신장의 최대값
	    int maxWeight = getMax(weight[0], weight[1], weight[2]);  // 체중의 최대값
	    int maxAge = getMax(age[0], age[1], age[2]);  // 나이의 최대
	    
	    System.out.println("신장의 최대값은 " + maxHeight + "입니다." );
	    System.out.println("체중의 최대값은 " + maxWeight + "입니다." );
	    System.out.println("나이의 최대값은 " + maxAge + "입니다." );		

	}
}
 
사용 예제 2
public class MyMethod_03 {
	
	static int getMax(int a, int b) { // a,b의 최대값을 반환
		// return이 여러번 나와도 된다.
	    System.out.println("함수 시작");
	    if(a>b) 
	    	return a;
	    else
	    	return b;
	    // System.out.println("함수 종료") -> return 때문에 함수가 종료되기 때문에 출력되지 않음.
	}

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		
		System.out.print("정수 a : ");
		int a = scanner.nextInt();
		System.out.print("정수 b : ");
		int b = scanner.nextInt();
		
		System.out.println("최대값은 " + getMax(a,b) + "입니다.");
	    scanner.close();
	}
}

 


 3)  메서드의 선언과 구현

  🚀  메서드는 크게 두 부분 '선언부(header, 머리)'와 '구현부(body, 몸통)'로 이루어져 있다.

반환타입 메서드이름 (타입 변수명, 타입 변수명, ...)
{
  // 메서드 호출시 수행될 코드
}
int add (int a, int b) {
    int result = a + b ;
    return result; // 호출한 메서드로 결과를 반환한다.
}

 

메서드 선언부
(method declaration,
method header)
- '메서드의 이름'과 '매개변수 선언' 그리고 '반환타입'으로 구성되어 있다.
- 메서드가 작업을 수행하기 위해 어떤 값들을 필요로 하고 작업의 결과로 어떤 타입의 값을 반환하는지에 대한 정보를 제공
매개변수 선언
(prameter declaration)
- 매개변수는 메서드가 작업을 수행하는데 필요한 값들(입력)을 제공받기 위한 것이며, 필요한 값의 개수만큼 변수를 선언.
- 각 변수 간의 구분은 쉼표 ',' 를 사용. 두 변수의 타입이 같아도 변수의 타입을 생략하는 것 불가.
  ex. int add ( int x, y) {...}  // 에러. 매개변수 y의 타입이 없다.
메서드의 이름
(method name)
- 특정 작업을 수행하므로 메서드의 이름은 'add' 처럼 동사인 경우가 많다.
반환타입
(return type)
- 메서드의 작업수행 결과(출력)인 '반환값(return value)'의 타입을 적는다.
- 반환값이 없는 경우 'void'
메서드의 구현부
(method body)
- 메서드의 선언부 다음에 오는 괄호{}를 '메서드의 구현부'라고 .
- 메서드를 호출했을 때 수행될 문장들을 넣는다.
return 문 - 메서드의 반환타입이 'void'가 아닌 경우, 구현부{} 안에 'return 반환값;'이 반드시 포함되어 있어야 한다.
- 이 문장은 반환값을 호출한 메서드로 전달.
- 이 값의 타입은 반환타입과 일치하거나 적어도 자동 형변환이 가능한 것이어야 한다.
- 단 하나의 값만 반환할 수 있음.
지역변수
(local variable)
- 메서드 내에 선언된 변수들은 그 메서드 내에서만 사용할 수 있으므로 서로 다른 메서드라면 같은 이름의 변수를 선언해도 된다.
- 이처럼 메서드 내에 선언된 변수를 '지역변수'라고 한다.
 

public class ExMethod_02 {

	public int add(int a, int b) {
		int c = a + b;
		return c;
		// return 다음에 위치하는 데이터는 반드시 메서드의 리턴 타입과 일치해야 함.
		// return 3.5; // 리턴 타입과 다른 데이터형을 반환하면 오류가 발생. (자동 형변환은 가능)
	}

	public static void main(String[] args) {
	     int numb1 = 10;
	     int numb2 = 20;
	     int result = 0;
	     
	     ExMethod_02 method = new ExMethod_02();
	     result = method.add(numb1, numb2);
	     System.out.println("두수의 합은 " + result); // 메서드의 반환 값을 저장해서 출력.
	     System.out.println("두수의 합은 " + method.add(numb1, numb2)); // 메서드의 반환 값을 바로 출력
		
	}
}

 


4)  메서드의 호출

인자(argument)와 매개변수(parameter)
int result = add(1,2,3);    // 에러. 메서드에 선언된 매개변수의 개수가 다름 
int result = add(1.5, 2.3); // 에러. 메서드에 선언된 매개변수의 타입이 다름

 

    - 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.

    - 인자는 메서드가 호출되면서 매개변수에 대입되므로, 인자의 타입은 매개변수의 타입과 일치하거나 자동형변환이 가능한 것이어야 함.

 

반환 타입

    - 반환타입이 void가 아닌경우, 메서드가 작업을 수행하고 반환한 값을 대입연산자로 변수에 저장하는 것이 보통이지만, 저장하지 않아도 문제가 되지 않는다.

 

메서드의 실행흐름
  • main 메서드에서 메서드 getAge를 호출
  • 호출시 지정한 인자들이 메서드 getAge의 매개변수 a,b에 각각 복사(대입) 
  • 메서드 getAge의 괄호 {} 안에 있는 문장들이 순서대로 수행
  • 메서드 getAge의 모든 문장이 실행되거나 return문을 만나면, 호출한 메서드(main메서드)로 되돌아와서 이후의 문장들을 실행

 

⚒️   메서드 호출방식

  A.  값에 의한 호출

  • 값에 의한 메서드 호출 방식은 메서드 호출 시에 값이 복사되어 전달
  • 메서드의 매개변수 타입이 '기본형 데이터'일 때 값에 의한 호출을 함.
💡  값 형식 (Value Type)

     -  변수가 값을 담은 데이터 형식, 스택 메모리 영역과 관련.

     -  코드 블록 안에서 생성된 모든 값 형식의 변수들은 프로그램 실행이 중괄호 "}"를 만나면 메모리에서 제거

public class CallByValue {

    static public int increase(int n) {
    	++n;
    	return n;
    }

	public static void main(String[] args) {
		int var1 = 100;
		int result = increase(var1);
		System.out.println("var1: " + var1 + ", result: " + result);		
	}
}

 

  • 메서드 increase() 호출 시에 n이라는 매개변수가 메모리에 생성되어 var1의 값인 100 이 복사되어 처리
  • 메서드 실행이 종료되면 매개변수 n은 메모리에서 소멸
  • 이 처럼 값에 의한 호출은 호출시 매개변수로 전달되는 값이 복사되어 전달. 따라서 var1의 값은 변하지 않음

 

B.  참조에 의한 호출

  • 매개변수의 타입이 참조형 타입일 때 사용, 참조에 의한 호출은 메서드 호출 시 참조 데이터의 위치가 매개변수에 전달
  • 값에 의한 호출은 메모리에 동일한 값을 복사 후 사용하지만 참조에 의한 호출은 메모리의 주소를 넘기기 때문에 값을 복사하지 않음
💡  참조 형식 Reference Type

       - 변수가 값 대신 값이 있는 곳의 위치(참조)를 담는 데이터 형식, 힙 메모리 영역과 관련

       - 힙 영역에 데이터를 저장하고, 스택 영역에는 데이터가 저장된 힙 메모리의 주소를 저장

       - 가비지 컬렉터가 프로그램 뒤에 숨어 동작하면서 힙에 더 이상 사용하지 않는 객체가 있으면 그 객체를 쓰레기로 간주하고 수거하는 기능을 함

          ex. class, interface, delegate, object, string

public class CallByRef {
	
	public static void increase(int[] array) {
		/* 배열을 매개변수로 받아서, 원래 저장된 값보다 1이 증가한 값으로 업데이트 */
		for(int i = 0; i < array.length; i++) {
			array[i]++;
		}
	}

	public static void main(String[] args) {
		int[] refArray = {100, 800, 1000}; // 배열 선언 및 초기화. 배열은 참조형 데이터
		
	    System.out.println("메서드 호출 전");
	    for(int i = 0; i < refArray.length; i++) {
	    	System.out.println("refArray[" + i + "]: " + refArray[i]);
	    }
	    
	    increase(refArray); // 리턴이 없음.
	    
	    System.out.println();
	    System.out.println("메서드 호출 후");
	    // 배열은 참조형이라 메서드 호출 후 값이 변경.
	    for(int i = 0; i < refArray.length; i++) {
	    	System.out.println("refArray[" + i + "]: " + refArray[i]);
	    }
	}
}

 

 

  C.  문자열에 의한 호출

  • 예외적으로 문자열을 전달하려면 참조형 데이터라도 값에 의한 호출을 함. 값에 의한 호출이기 때문에 값을 변경할려면 변경된 문자열을 리턴하고 호출한 쪽에서 값을 저장하면 됨
public class CallByString {

    public static String setAddress(String addr) {
    	addr = "경기도 수원시 장안구";
    	return addr;
    }
	
	public static void main(String[] args) {
	    String address = " 서울시 강남구 논현동";
	    
	    System.out.println("메서드 호출 전");
	    System.out.println("address: " + address); // address: 서울시 강남구 논현동
	    
	    address = setAddress(address);
	    
	    System.out.println("메서드 호출 후");
	    System.out.println("address: " + address); // address: 경기도 수원시 장안구
		
	}
}

 

 값 복사  VS  참조 복사
public class MyMethod_04 {

	public static void main(String[] args) {
		/* 깊은 복사. 값 복사. 기본형 변수
		 * deep copy
		 * 데이터 자체가 복사된다.
		 * 복사된 두 객체는 완전히 독립적인 메모리를 차지.
		 */
		int a = 1;
		int b = a;
		b++;
		
		System.out.println("a: " + a + ", b: " + b); // a: 1, b: 2
		
		/* 얕은 복사. 참조 복사. 참조형 변수
		 * shallow copy
		 * 값 자체를 복사하는 것이 아니라 주소값을 복사하여 같은 메모리를 가리키기 때문.
		 */
		int[] nums_01 = new int[1];
		nums_01[0] = 1;
		
		int[] nums_02 = nums_01;
		nums_02[0] = 2;
		
		System.out.println("nums_01[0]: " + nums_01[0] + ", nums_02[0]:" + nums_02[0]);
		// nums_01[0]: 2, nums_02[0]:2

	}
}
 
 

5)  return 문

  • 현재 실행중인 메서드를 종료하고 호출한 메서드로 되돌아간다.
  • 반환타입이 void 인 경우, return문 없어도 문제가 되지 않는다  ➡️  컴파일러가 메서드의 마지막에 자동적으로 추가해주기 때문
  • 결과로 사용되는 유형 : void, 기본자료형, 클래스형 등
public class ExMethod {

	public void print() {
	    System.out.println("메서드를 호출한다");
	    // 리턴 타입이 없더라도 return을 사용할 수 있음.
	    // 다만 return 다음에 값이 없어야 함.
	    // return의 의미는 이 부분에서 메서드 실행을 종료하고 복귀한다는 의미.
	    // return;
	}
	
	public static void main(String[] args) {
    
	    // 사용할 메서드가 있는 클래스의 인스턴스 선언
		ExMethod method = new ExMethod();
		method.print();
		method.print();
		method.print();

	}
}



 
반환값(return value)
int add(int x, int y) { 
    int result = x + y; 
    return x+y;
}

  -  반환값으로 주로 변수가 오긴 하지만 위의 코드처럼 'x+y' 수식이 올 수 있다. (수식의 결과가 반환됨)

  -  간단한 메서드의 경우 if문 대신 조건연산자(ex. 삼항연산자)를 사용하기도 함.

 

매개변수의 유효성 검사

 

  -  메서드의 구현부{}를 작성할 때, 제일 먼저 해야 하는 일이 매개변수의 값이 적절한 것인지 확인하는 것

       ➡️  타입만 맞으면 어떤 값도 매개변수를 통해 넘어올 수 있기 때문.

  -  적절하지 않은 값이 매개변수를 통해 넘어온다면 매개변수의 값을 보정하던가, 보정하는 것이 불가능하다면 return문을 사용해서 작업을 중단하고 호출한 메서드로 되돌아가야 한다.


예제 문제

 

  📍 밑변이 30, 높이가 10인 사각형의 넓이를 출력하세요
        * 단 메서드 calculator()를 호출해서 출력하세요

public class Ex_01_01 {
	public static void main(String[] args) {
		
		int width = 30;
		int height = 10;
		System.out.println("밑변이 "+ width + " 높이가 " + height + "인 사각형의 넓이는 " + calculator(width,height) + "입니다.");
	
    }
	
	static int calculator(int a, int b) {
    	System.out.println("계산을 시작합니다.");
        int area = a * b; // 밑변 * 높이 = 사각형의 높이
        return area;
	}
}

 

직각 삼각형 만들기 
 
public class Ex_01_02 {
	/* 
	 * 별이 출력되는 부분은 putStars() 메서드를 정의해서 구현하세요.
	 * 몇 단 : 4
	 * *
	 * **
	 * ***
	 * ****
	 */
     
	static void putStars(int limit) {
	    for( int i = 1; i <= limit; i++) {
	    	System.out.print("*");
	    }
	}
	
	public static void main(String[] args) {
	     Scanner scanner = new Scanner(System.in);
	    
	     System.out.println("좌하변이 직각인 직각삼각형을 표시합니다.");
	     System.out.print("몇 단 : ");
	     int n = scanner.nextInt();
	     
	     for(int i = 1; i <= n; i++) {
	    	 //putStars() 메서드 호출
	    	 putStars(i);
	    	 System.out.println();
	     }
	     scanner.close();
	}
}
 
 
 


6)  메서드 종류

매개변수, 반환 값 모두 있는 경우
int method(int a) { int b; return b; }

 

매개변수는 있는데, 반환 값이 없는 경우
void method(int a) { }

 

 

매개변수는 없는데, 반환 값이 있는 경우

 

int method() { int b; return b; }

 

 
매개변수, 반환 값 모두 없는 경우
void method() { }

 

 

 

 

 

 

* 내용 참고 : 학원강의 및 자바의정석 3rd

+ Recent posts