데이터 엔지니어링 과정/java
[20일차] 메소드
오리는짹짹
2023. 1. 18. 19:49
목차
1. 메소드
2. 인스턴스 멤버와 정적 멤버
3. 패키지와 접근 제한자
1. 메소드
1. 메소드 선언
(1) 리턴 타입
- 리턴 타입 : 리턴값의 타입
- 리턴값 : 메소드를 실행한 후의 결과값
- 리턴값이 있는 메소드
➡ void로 리턴 타입 기술 - 리턴값이 없는 메소드
➡ 리턴값의 타입 기술
(2) 메소드 이름
- 숫자로 시작하면 안됨
- 관례적으로 메소드 이름은 소문자로 작성
- 서로 다른 단어가 혼합된 이름이라면 뒤이어 오는 단어의 첫 글자는 대문자로 작성
(3) 매개 변수 선언
메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용된다.
- 💻 메소드 선언
package sec04.exam01;
public class Calculator {
//메소드
void powerOn() {
System.out.println("전원을 켭니다.");
}
int plus (int x, int y) {
int result = x + y;
return result;
}
double divide (int x, int y) {
double result = (double)x / (double)y;
return result;
}
void powerOff() {
System.out.println("전원을 끕니다.");
}
}
- 💻 메소드 호출
package sec04.exam01;
public class CalculatorExample {
public static void main(String[] args) {
Calculator myCalc = new Calculator();
myCalc.powerOn();
int result1 = myCalc.plus(5, 6);
System.out.println("result1: "+result1);
byte x =10;
byte y = 4;
double result2 = myCalc.divide(x, y);
System.out.println("result2: "+result2);
myCalc.powerOff();
}
}
>>> 전원을 켭니다.
>>> result1: 11
>>> result2: 2.5
>>> 전원을 끕니다.
(4) 매개 변수의 개수를 모를 경우
매개 변수를 배열 타입으로 선언한다.
그런데, 메소드를 호출하기 전에 배열을 생성해야 하는 불편한 점이 있다.
다른 방법으로는 배열을 생성하지 않고 값의 목록만 넘겨주는 방봅이 있다.
- 💻 매개 변수의 개수를 모르는 경우
package sec04.exam02;
public class Computer {
int sum1(int[] values) { //매개변수를 배열 타입으로 선언
int sum = 0;
for (int i=0; i<values.length; i++) {
sum += values[i];
}
return sum;
}
int sum2 (int ... values) { //배열 생성하지 않고 값의 목록만 넘겨주기
int sum = 0;
for (int i=0; i<values.length; i++) {
sum+=values[i];
}
return sum;
}
}
- 💻 매개 변수의 개수를 모를 경우
package sec04.exam02;
public class ComputerExample {
public static void main(String[] args) {
Computer myCom = new Computer();
int[] values1= {1,2,3};
int result1 = myCom.sum1(values1);
System.out.println("result1: "+result1);
int result2 = myCom.sum1(new int[] {1,2,3,4,5});
System.out.println("result2: "+result2);
int result3 = myCom.sum2(1,2,3);
System.out.println("result3: "+result3);
int result4 = myCom.sum2(1,2,3,4,5);
System.out.println("result4: "+result4);
}
}
>>> result1: 6
>>> result2: 15
>>> result3: 6
>>> result4: 15
2. 리턴(return)문
- 리턴값이 있는 메소드
- 메소드 선언에 리턴 타입이 있는 메소드는 반드시 리턴 문을 사용해서 리턴값을 지정해야 함
- return문이 실행되면 메소드는 즉시 종료
❓만약 return문이 없다면❓➡ 컴파일 에러가 발생
return문의 리턴값은 리턴 타입이거나 리턴 타입으로 변환될 수 있어야 함
- 리턴값이 없는 메소드 : void
- 리턴값이 없는 메소드는 리턴 타입으로 void 사용
하지만 void로 선언된 메소드에서도 return이 사용이 가능
👀 이 때의 역할은 리턴값을 정하는 게 아니라 메소드 실행을 강제 종료하는 역할
- 리턴값이 없는 메소드는 리턴 타입으로 void 사용
- 💻 return문
package sec04.exam03;
public class Car {
//필드
int gas;
//생성자
//메소드
void setGas(int gas) {
this.gas = gas;
}
boolean isLeftGas() {
if (gas == 0) {
System.out.println("gas가 없습니다.");
return false;
}
System.out.println("gas가 있습니다.");
return true;
}
void run() {
while (true) {
if (gas > 0) {
System.out.println("달립니다.(gas 잔량: "+gas+")");
gas-=1;
} else {
System.out.println("멈춥니다.(gas 잔량: "+gas+")");
return; //메소드 강제 종료
}
}
}
}
- 💻 return문
package sec04.exam03;
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car();
myCar.setGas(5);
boolean gasState = myCar.isLeftGas();
if (gasState) {
System.out.println("출발합니다.");
myCar.run();
}
if (myCar.isLeftGas()) {
System.out.println("gas를 주입할 필요가 없습니다.");
} else {
System.out.println("gas를 주입하세요.");
}
}
}
>>> gas가 있습니다.
>>> 출발합니다.
>>> 달립니다.(gas 잔량: 5)
>>> 달립니다.(gas 잔량: 4)
>>> 달립니다.(gas 잔량: 3)
>>> 달립니다.(gas 잔량: 2)
>>> 달립니다.(gas 잔량: 1)
>>> 멈춥니다.(gas 잔량: 0)
>>> gas가 없습니다.
>>> gas를 주입하세요.
3. 메소드 호출
(1) 객체 내부에서 호출
- 메소드가 매개 변수를 가지고 있을 때에는 매개 변수의 타입과 수에 맞게 매개값 제공
- 메소드가 리턴 값이 없거나, 있어도 받고 싶지 않을 경우에도 호출 가능
이 때, 변수 타입은 리턴 타입과 동일하거나, 자동 타입 변환이 될 수 있어야 한다는 점에 주의해야 함
- 클래스 내부에서 메소드 호출
package sec04.exam04;
public class Calculator {
//필드
//생성자
//메소드
int plus(int x, int y) {
int result = x + y;
return result;
}
double avg(int x, int y) {
double sum = plus(x,y);
double result = sum/2;
return result;
}
void execute() {
double result = avg(7,10);
println("실행결과: "+ result);
}
void println(String message) {
System.out.println(message);
}
}
- Calculator의 execute() 실행
package sec04.exam04;
public class CalculatorExample {
public static void main(String[] args) {
Calculator myCalc = new Calculator();
myCalc.execute();
}
}
>>> 실행결과: 8.5
(2) 객체 외부에서 호출
- 클래스로부터 객체 생성 ➡ 참조 변수와 함꼐 도트(.) 연산자를 사용해서 메소드 호출
- 💻 클래스 외부에서 메소드 호출
package sec04.exam05;
public class Car {
//필드
int speed;
//생성자
//메소드
int getSpeed() {
return speed;
}
void keyTurnOn() {
System.out.println("키를 돌립니다.");
}
void run() {
for (int i=10; i<=50; i+=10) {
speed = i;
System.out.println("달립니다.(시속: "+speed+"km/h)");
}
}
}
- 💻 클래스 외부에서 메소드 호출
package sec04.exam05;
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car();
myCar.keyTurnOn();
myCar.run();
int speed = myCar.getSpeed();
System.out.println("현재 속도: "+speed+"km/h");
}
}
>>> 키를 돌립니다.
>>> 달립니다.(시속: 10km/h)
>>> 달립니다.(시속: 20km/h)
>>> 달립니다.(시속: 30km/h)
>>> 달립니다.(시속: 40km/h)
>>> 달립니다.(시속: 50km/h)
>>> 현재 속도: 50km/h
4. 메소드 오버로딩
- 정의 : 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것
- 조건 : 매개 변수의 타입, 개수, 순서 중 하나가 달라야 함
- 필요한 이유 : 매개값을 다양하게 받아 처리할 수 있도록 하기 위해서
- ❗주의할 점❗
- 매개 변수의 타입, 개수, 순서가 똑같을 경우, 매개 변수 이름이 다르더라도 메소드 오버로딩 아님!
- 리턴 타입만 다르고 매개 변수가 동일하면, 오버로딩 아님
- 💻 메소드 오버로딩
package sec04.exam06;
public class Calculator {
//정사각형의 넓이
double areaRectangle(double width) {
return width * width;
}
//직사각형의 넓이
double areaRectangel(double width, double height) {
return width*height;
}
}
- 💻 메소드 오버로딩
package sec04.exam06;
public class CalculatorExample {
public static void main(String[] args) {
Calculator myCalcu = new Calculator();
//정사각형의 넓이 구하기
double result1 = myCalcu.areaRectangle(10);
//직사각형의 넓이 구하기
double result2 = myCalcu.areaRectangel(10, 20);
//결과 출력
System.out.println("정사각형 넓이= "+result1);
System.out.println("직사각형 넓이= "+result2);
}
}
>>> 정사각형 넓이= 100.0
>>> 직사각형 넓이= 200.0
2. 인스턴스 멤버와 정적 멤버
1. 인스턴스 멤버와 this
- 인스턴스 멤버
: 객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드 - 인스턴스 멤버 선언
- 인스턴스 메소드는 객체에 소속된 멤버인데, 왜 객체 내부에 존재하지 않고 메소드 영역에 저장되고 공유될까❓
➡ 메소드는 코드 블록이므로, 객체마다 동일한 코드 블록을 가질 필요가 없기 때문! - 인스턴스라는 말은 왜 붙었지❓
➡ 메모리 블록 내부에 인스턴스 필드 등이 사용되는 경우가 있기 때문!
- 인스턴스 메소드는 객체에 소속된 멤버인데, 왜 객체 내부에 존재하지 않고 메소드 영역에 저장되고 공유될까❓
- this
: 객체 내부에서 인스턴스 멤버에 접근하기 위해서 this를 사용
- 💻 인스턴스 멤버와 this
package sec05.exam01;
public class Car {
//필드
String model;
int speed;
//생성자
Car(String model) {
this.model = model;
}
//메소드
void setSpeed(int speed) {
this.speed = speed;
}
void run() {
for (int i = 10; i<=50; i+=10) {
this.setSpeed(i);
System.out.println(this.model+"가 달립니다. (시속: "+this.speed + "km/h)");
}
}
}
- 💻 인스턴스 멤버와 this
package sec05.exam01;
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car("포르쉐");
Car yourCar = new Car("벤츠");
myCar.run();
yourCar.run();
}
}
>>> 포르쉐가 달립니다. (시속: 10km/h)
>>> 포르쉐가 달립니다. (시속: 20km/h)
>>> 포르쉐가 달립니다. (시속: 30km/h)
>>> 포르쉐가 달립니다. (시속: 40km/h)
>>> 포르쉐가 달립니다. (시속: 50km/h)
>>> 벤츠가 달립니다. (시속: 10km/h)
>>> 벤츠가 달립니다. (시속: 20km/h)
>>> 벤츠가 달립니다. (시속: 30km/h)
>>> 벤츠가 달립니다. (시속: 40km/h)
>>> 벤츠가 달립니다. (시속: 50km/h)
2. 정적 멤버와 static
- 정적 멤버
: 클래스에 고정된 멤버로서 객체를 생성하지 않고, 사용할 수 있는 필드와 메소드 - 정적 멤버 선언
- 방법 : static 키워드를 추가적으로 붙임
- 판단 기준
- 객체마다 가지고 있어야 할 데이터라면 인스턴스로 선언
- 객체마다 가지고 있을 필요가 없는 공용 데이터라면 정적으로 선언
- 판단 기준
- 방법 : static 키워드를 추가적으로 붙임
- 정적 멤버 사용
➡ 클래스 이름과 함께 도트(.) 연산자로 접근
- 💻 정적 멤버 사용
package sec05.exam02;
public class Calculator {
static double pi = 3.1459;
static int plus(int x, int y) {
return x + y;
}
static int minus(int x, int y) {
return x - y;
}
}
- 💻 정적 멤버 사용
package sec05.exam02;
public class CalculatorExample {
public static void main(String[] args) {
double result1 = 10 * 10 * Calculator.pi;
int result2 = Calculator.plus(10,5);
int result3 = Calculator.minus(10,5);
System.out.println("result1 : " + result1);
System.out.println("result2 : " + result2);
System.out.println("result3 : " + result3);
}
}
>>> result1 : 314.59000000000003
>>> result2 : 15
>>> result3 : 5
- 정적 메소드 선언 시 주의할 점
- 이들 내부에 인스턴스 필드나 인스턴스 메소드를 사용 불가
- 객체 자신의 참조인 this 키워드 사용 불가
- 정적 메소드 선언 시 주의할 점
package sec05.exam03;
public class Car {
int speed;
void run() {
System.out.println(speed + "으로 달립니다.");
}
public static void main(String[] args) {
Car myCar = new Car();
myCar.speed = 60;
myCar.run();
}
}
>>> 60으로 달립니다.
3. 싱글톤
- 정의 : 단 하나의 객체만 만들도록 보장하는, 단 하나만 생성되는 객체
- 만드는 방법
① 생성자를 외부에서 호출할 수 없도록, 생성자 앞에 private 접근 제한자를 붙여줌
② 외부에서 호출 가능한 정적 메소드인 getInstance()를 선언하고 정적 필드에서 참조하고 있는 자신의 객체를 리턴
- 💻 싱글톤
package sec05.exam04;
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
static Singleton getInstance() {
return singleton;
}
}
- 💻 싱글톤
package sec05.exam04;
public class SingletionExample {
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 객체입니다.");
}
}
}
>> 같은 Sington 객체입니다.
4. final 필드와 상수
- final 상수
- 정의 : 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중 수정 불가
- 초기값 주는 방법
① 필드 선언시
: 단순 값이라면 간단
② 생성자에서 받기
: 복잡한 초기화 코드가 필요하거나 객체 생성 시에 외부 데이터로 초기화
- 💻 final 필드 선언과 초기화
package sec05.exam05;
public class Person {
final String nation = "Korea";
final String ssn;
String name;
public Person(String ssn, String name) {
this.ssn = ssn;
this.name = name;
}
}
- 💻 final 필드 테스트
package sec05.exam05;
public class PersonExample {
public static void main(String[] args) {
Person p1 = new Person("123456-1234567","홍길동");
System.out.println(p1.nation);
System.out.println(p1.ssn);
System.out.println(p1.name);
//p1.nation="usa";
//p1.ssn="654321-7654321";
p1.name = "고길동";
}
}
- 상수
- static이면서 final
- 객체마다 존재하지 않고 클래스에만 존재
- 한 번 초기값이 저장되면 변경 불가
- 상수 이름은 모두 대문자로 작성
만약 서로 다른 단어가 혼합된 이름이라면 언더바(_)로 단어들을 연결
- 💻 상수 선언
package sec05.exam06;
public class Earth {
static final double EARTH_RADIUS = 6400;
static final double EARTH_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
}
- 💻 상수 사용
package sec05.exam06;
public class EarthExample {
public static void main(String[] args) {
System.out.println("지구의 반지름: " + Earth.EARTH_RADIUS + " km");
System.out.println("지구의 지름: " + Earth.EARTH_AREA + "km^2");
}
}
>>> 지구의 반지름: 6400.0 km
>>> 지구의 지름: 5.147185403641517E8km^2
3. 패키지와 접근 제한자
0. 시작하기 전에
- 패키지
- 물리적인 형태 : 파일 시스템 폴더
- 클래스의 일부분으로, 클래스를 유일하게 만들어주는 식별자 역할
- 클래스 이름이 동일하더라도 패키지가 다르면 다른 클래스로 인식
- 패키지가 상·하위로 구분되어 있다면 도트(.) 사용하여
상위패키지.하위패키지.클래스
로 표현
1. 패키지 선언
- 클래스를 작성할 때 해당 클래스가 어떤 퍀키지에 속할 것인지를 선언하는 것
- 패키지 이름 규칙
- 숫자로 시작 x
- _ $를 제외한 특수 문자 사용 불가
- java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용 불가
- 모두 소문자로 작성하는 것이 관례
- import문
- 사용하고자 하는 클래스 또는 인터페이스가 다른 패키지에 소속되어 있다면,
import문으로 해당 패키지의 클래스 또는 인터페이스를 가져와 사용할 것임을 컴파일러에게 알려줌 - 작성 방법
import 상위패키지.하위패키지.클래스이름;
import 상위패키지.하위패키지.* - 패키지 선언과 클래스 선언 사이에 작성
- 만약 사용하고자 하는 클래스들이 동일한 패키지 소속이라면,
*를 이용해서 해당 패키지에 소속된 클래스들을 사용할 것임을 알려주는 것이 좋음
- 사용하고자 하는 클래스 또는 인터페이스가 다른 패키지에 소속되어 있다면,
- 💻 import문
package sec06.exam02.hankook;
public class SnowTire {
}
package sec06.exam02.hankook;
public class Tire {
}
package sec06.exam02.kumho;
public class BigWidthTire {
}
package sec06.exam02.kumho;
public class Tire {
}
package sec06.exam02.hyundai;
public class Engine {
}
package sec06.exam02.mycompany;
import sec06.exam02.hankook.*;
import sec06.exam02.hyundai.Engine;
import sec06.exam02.kumho.*;
public class Car {
//필드
Engine engine = new Engine();
SnowTire tire1 = new SnowTire();
BigWidthTire tire2 = new BigWidthTire();
sec06.exam02.hankook.Tire tire3 = new sec06.exam02.hankook.Tire();
sec06.exam02.kumho.Tire tire4 = new sec06.exam02.kumho.Tire();
}
2. 접근 제한자
- 접근을 제한하기 위해 사용
- 접근 ➡ 클래스 및 인터페이스 그리고 이들이 가지고 있는 멤버의 접근
- 종류
- public 접근 제한자
: 단어 뜻 그대로 외부 클래스가 자유롭게 사용 가능 - protected 접근 제한자
: 같은 패키지 또는 자식 클래스에서 사용 가능 - private 접근 제한자
: 개인적인 것이라 외부에서 사용 불가 - default 접근 제한
: 같은 패키지에 소속된 클래스만 사용 가능
+ 위의 세가지 접근 제한자가 적용되지 않았을 때 적용
- public 접근 제한자
3. 클래스의 접근 제한
- default 접근 제한
- 클래스 선언 생략 시 적용
- 같은 패키지에서 아무런 제한 없이 사용 가능
- 다른 패키지에서는 사용 불가
- public 접근 제한
- 같은 패키지 + 다른 패키지 모두 사용 가능
- 다른 개발자가 사용할 수 있도록 라이브러리 클래스로 개발한다면 반드시 public 접근 제한을 갖도록 해야 함
- 인터넷으로 배포되는 라이브러리 클래스도 모두 public 접근 제한을 갖고 있음
- 💻 클래스의 접근 제한
package sec06.exam03.package1;
public class A {}
package sec06.exam03.package1;
public class B {
A a;
}
package sec06.exam03.package2;
import sec06.exam03.package1.*;
public class C {
A a;
B b;
}
4. 생성자의 접근 제한
- 자동으로 생성되는 기본 생성자의 접근 제한은 클래스의 접근 제한과 동일
- 💻 생성자의 접근 제한
package sec06.exam04.package1;
public class A {
//필드
A a1 = new A(true);
A a2 = new A(1);
A a3 = new A("문자열");
//생성자
public A (boolean b) {}
A(int b) {}
private A(String s) {}
}
package sec06.exam04.package1;
public class B {
//필드
A a1 = new A(true);
A a2 = new A(1);
//A a3 = new A("문자열");
}
package sec06.exam04.package2;
import sec06.exam04.package1.*;
public class C {
//필드
A a1 = new A(true);
//A a2 = new A(1);
//A a3 = new A("문자열");
}
5. 필드와 메소드의 접근 제한
package sec06.exam05.package1;
public class A {
//필드
public int field1;
int field2;
private int field3;
//생성자
public A() {
field1 = 1;
field2 = 1;
field3 = 1;
method1();
method2();
method3();
}
//메소드
public void method1() {}
void method2() {}
private void method3() {}
}
package sec06.exam05.package1;
public class B {
public B() {
A a =new A();
a.field1 = 1;
a.field2 = 1;
//a.field3 = 1;
a.method1();
a.method2();
//a.method3();
}
}
package sec06.exam05.package2;
import sec06.exam05.package1.*;
public class C {
public C () {
A a = new A ();
a.field1 = 1;
//a.field2 = 1;
//a.field3 = 1;
a.method1();
//a.method2();
//a.method3();
}
}
6. Getter와 Setter 메소드
- 클래스를 선언할 때 가능하면
필드는 private를 선언해서 외부로부터 보호하고,
필드에 대한 Setter 과 Getter 메소드를 작성해서 필드값을 안전하게 변경/사용하는 것이 좋음 - 객체의 필드를 객체 외부에서 직접적으로 접근하는 것을 막음
➡ 외부에서 마음대로 변경할 경우, 객체의 무결성이 깨질 수 있기 때문! - Getter
- 외부에서 객체의 데이터를 읽을 때 사용
➡ 필드값을 직접 사용하면 부적절한 경우가 있기 때문 - 메소드로 필드값을 가공한 후 외부로 전달
- 외부에서 객체의 데이터를 읽을 때 사용
- Setter
- 메소드를 통해서 필드 변경
- 필드는 외부에서 접근할 수 없도록 막고, 메소드는 공개해서 외부에서 메소드를 통해 필드에 접근하도록 유도
➡ 메소드는 매개값을 검증해서 유효한 값만 객체의 필드로 저장할 수 있기 때문!
package sec06.exam06;
public class Car {
//필드
private int speed;
private boolean stop;
//생성자
//메소드
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
if (speed < 0) {
this.speed = 0;
return;
} else {
this.speed = speed;
}
}
public boolean isStop() {
return stop;
}
public void setStop(boolean stop) {
this.stop = stop;
this.speed = 0;
}
}
package sec06.exam06;
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car();
//잘못된 속도 변경
myCar.setSpeed(-50);
System.out.println("현재 속도: " + myCar.getSpeed());
//올바른 속도 변경
myCar.setSpeed(60);
//멈춤
if (!myCar.isStop()) {
myCar.setStop(true);
}
System.out.println("현재 속도: " + myCar.getSpeed());
}
}
>>> 현재 속도: 0
>>> 현재 속도: 0