클래스는 필드와 메서드로 구성되며, 생성자는 객체를 초기화하는 역할을 담당한다. 객체를 생성할 때 필드에 값을 할당하거나 초기 작업을 수행하기 위해 생성자가 사용된다.
생성자, 메서드, 필드는 하나도 없거나 일부만 있어도 되지만, 특정 기능을 위해 존재하는 것이 좋다.
클래스 이름은 식별자 규칙에 따라 작성해야 한다.
Car, SportsCar.3Car(X).$와 _만 사용할 수 있다. 예: $Car, _Car (O), @Car, #Car(X).int, for(X).public class Car {
// 기본 생성자
public Car() {
System.out.println("생성자 호출 확인");
}
public static void main(String[] args) {
Car car1 = new Car(); // Car 객체 생성, 생성자 호출
}
}
이 코드에서 Car 클래스에는 기본 생성자가 선언되어 있으며, 객체가 생성될 때 Car() 생성자가 호출된다. car1 객체를 생성하면 자동으로 생성자가 호출되어 “생성자 호출 확인"이라는 메시지가 출력된다.
클래스를 정의하면 해당 클래스로부터 여러 객체를 생성할 수 있다.
public class Car {
public static void main(String[] args) {
Car sonata = new Car(); // sonata 객체 생성
Car avante = new Car(); // avante 객체 생성
Car ferrari = new Car(); // ferrari 객체 생성
}
}
이 예제에서는 Car 클래스로부터 세 개의 객체(sonata, avante, ferrari)가 생성된다.
각 객체는 new Car() 구문을 통해 개별적으로 생성되며, 각각의 객체는 서로 독립적인 인스턴스이다.
생성자는 매개변수를 가질 수 있으며, 이를 통해 객체 생성 시 필드를 초기화할 수 있다.
public class Car {
String model;
// 매개변수를 받는 생성자
public Car(String model) {
this.model = model; // 필드를 초기화
}
public static void main(String[] args) {
Car sonata = new Car("Sonata"); // model을 "Sonata"로 초기화
Car avante = new Car("Avante"); // model을 "Avante"로 초기화
System.out.println(sonata.model); // "Sonata" 출력
System.out.println(avante.model); // "Avante" 출력
}
}
이 코드는 Car 클래스에 매개변수를 받는 생성자를 정의하여 객체 생성 시 필드를 초기화하는 방법을 보여준다. sonata 객체와 avante 객체는 서로 다른 model 값을 가지며, 각각의 생성자 호출에서 전달된 값으로 초기화된다.
필드는 객체의 고유 데이터를 저장하는 곳이다. 변수와 비슷하게 클래스 전역에서 사용될 수 있으며, 객체가 소멸되지 않는 한 객체와 함께 존재한다. 클래스와 함께 선언된 변수를 멤버 변수 혹은 필드라고 부른다.
public class Car {
String company; // Car 객체에 대한 생명 주기를 함께함, 멤버 변수 또는 필드라고 한다.
public static void main(String[] args) {
Car car = new Car(); // 객체 생성
}
}
보통 멤버 변수를 만들면, 생성자에서 멤버 변수를 초기화해준다.
입력인수를 받는 생성자도 있고, 입력인수를 받지 않는 생성자도 있다. 생성자 앞에는 접근제어자 public 또는 private이 올 수 있다. 생성자는 객체가 생성될 때 호출되며, new 클래스명(입력인수, ...)처럼 new 키워드를 사용할 때 호출된다.
public class Car {
String company;
// 디폴트 생성자: 입력받는 매개변수가 없는 생성자
Car() {
company = "현대자동차";
}
public static void main(String[] args) {
Car car1 = new Car(); // 디폴트 생성자를 이용해 객체 생성
}
}
생성자 오버로딩은 클래스 내에서 같은 이름의 생성자를 여러 개 선언하는 것을 말한다. 이는 하나의 생성자 이름으로 여러 기능을 수행하게끔 할 수 있다. 오버로딩은 생성자뿐만 아니라 메서드에서도 사용할 수 있다.
public class Car {
String company;
String model;
int maxSpeed;
// 1번 생성자: 회사명만 초기화
Car(String company) {
this.company = company;
}
// 2번 생성자: 회사명과 모델명 초기화
Car(String company, String model) {
this.company = company;
this.model = model;
}
// 3번 생성자: 회사명, 모델명, 최고속도 초기화
Car(String company, String model, int maxSpeed) {
this.company = company;
this.model = model;
this.maxSpeed = maxSpeed;
}
}
public class CarExample {
public static void main(String[] args) {
Car sonata = new Car("현대"); // 1번 생성자 이용
Car sportage = new Car("기아", "sportage"); // 2번 생성자 이용
Car gv80 = new Car("제네시스", "gv80", 300); // 3번 생성자 이용
}
}
위의 예시에서 볼 수 있듯, 객체를 생성할 때 다양한 매개변수를 전달해 그에 맞는 생성자를 호출할 수 있다. 이는 객체 생성 시 유연하게 초기화 작업을 할 수 있게 해준다
메서드는 클래스 내에 정의된 함수이다. 메서드는 호출하면 특정 기능을 수행하며, 입력값(인풋)을 받아 결과값(아웃풋)을 반환할 수 있다. 입력값이 없을 경우 빈 괄호를 사용하고, 출력값이 없을 경우에는 void를 사용한다.
// 인풋이 없는 메서드
String printField() {
return "~~~";
}
// 아웃풋이 없는 메서드
void method2(String input) {
// 실행문
}
개발자는 필요한 기능을 메서드로 작성하고, 외부에서 해당 메서드를 호출하여 기능을 실행한다. 인풋을 받아 결과를 출력하거나, 인풋을 받아 기능만 수행하거나, 인풋 없이 결과만 출력하는 방식도 가능하다.
인풋과 아웃풋이 모두 없는 메서드는 다음과 같이 정의할 수 있다.
void method() {
// 실행문
}
메서드는 선언부와 실행블록으로 구성된다. 메서드 선언부에는 리턴타입과 메서드 이름이 포함되며, 필요 시 매개변수를 받는다.
리턴타입 메서드이름([매개변수 선언, ...]) { // 선언부
// 실행블록
}
return문은 메서드가 실행된 후 결과를 호출한 곳으로 돌려주는 역할을 한다. 메서드에 리턴값이 있을 수도 없을 수도 있는데, 리턴값이 있는 경우에는 return문을 사용하여 그 값을 반환해야 한다. 리턴문은 메서드 내에서 마지막에 위치해야 하며, 중간에 리턴을 사용하면 그 이후의 코드는 실행되지 않는다.
java코드 복사
void powerOn() {
System.out.println("전원을 켭니다"); // void 리턴타입: 리턴값이 없을 경우
}
double divide(int x, int y) {
return (double) x / y; // double로 자동 형 변환 가능
}
double divide(int x, int y) {
return (double) x / y;
System.out.println("출력"); // 리턴문 뒤에 실행문이 있어서 에러 발생
}
특정 조건이 만족되었을 때만 리턴할 수도 있으며, 그 외의 경우에도 반드시 리턴문을 제공해야 한다. 그렇지 않으면 에러가 발생할 수 있다. else 구문을 사용하여 조건에 맞지 않을 때의 리턴값을 지정할 수 있다.
// 1. 조건에 해당하지 않으면 56.7을 리턴
double divide(int x, int y) {
if ((double) x / y == 4.5) {
return (double) x / y;
}
return 56.7;
}
// 2. else 구문으로 리턴 처리
double divide(int x, int y) {
if ((double) x / y == 4.5) {
return (double) x / y;
} else {
return 56.7;
}
}
else 문을 사용하는 것은 컨벤션 차이로, 의미를 명확하게 하고 싶다면 else 문에 리턴을 포함할 수 있지만, 밖으로 빼더라도 동작에는 문제가 없다.
매개변수와 인수
매개변수는 메서드에 전달된 입력값을 저장하는 변수이며, 인수는 메서드를 호출할 때 전달하는 값이다. 메서드 선언 시에는 매개변수라고 부르며, 메서드를 호출할 때는 인수라고 한다.
void setMaxSpeed(int maxSpeed) { // 매개변수
this.maxSpeed = maxSpeed;
}
car1.setMaxSpeed(200); // 인수
메서드 내에서 선언된 변수는 해당 메서드 내에서만 유효하다. 예를 들어, 메서드 내부에서 값을 변경하더라도 메서드가 종료되면 변경된 값은 사라진다.
public class Calculator {
int postfixOperator(int a) {
a++;
return a;
}
public static void main(String[] args) {
int a = 1;
Calculator calculator = new Calculator();
a = calculator.postfixOperator(a);
System.out.println(a); // 출력: 2
}
}
변수 a는 메서드가 실행되는 동안에만 값이 증가하며, 이후 호출한 곳에 반환된다.
객체를 매개변수로 전달할 때의 동작을 이해하려면, 자바에서의 값 전달(call by value) 개념을 이해해야 한다. 자바는 기본적으로 값을 복사해서 전달하는 방식을 사용한다. 그러나 객체는 참조 타입이기 때문에, 참조하는 주소값이 매개변수로 전달된다. 즉, 참조된 객체의 속성값을 변경하면 원본 객체에도 영향을 미친다.
public class Calculator {
int a; // 인스턴스 변수
// 기본 생성자
Calculator() {
}
// 매개변수를 받는 생성자
Calculator(int a) {
this.a = a; // this는 인스턴스 변수 a를 가리킴
}
// 기본 타입(프리미티브) 변수를 매개변수로 받는 메서드
int postfixOperator(int a) {
a++; // 매개변수로 받은 값 증가, 원본 객체에는 영향 없음
return a;
}
// 객체를 매개변수로 받는 메서드
void postfixOperator(Calculator cal) {
cal.a++; // 전달된 객체의 a 값을 증가시킴
}
public static void main(String[] args) {
// 1. 기본 타입 매개변수 전달
int a = 1;
Calculator calculator = new Calculator();
a = calculator.postfixOperator(a); // 메서드 실행 후 반환값을 a에 다시 할당
System.out.println(a); // 출력: 2
// 2. 객체 매개변수 전달
Calculator cal1 = new Calculator(1); // a 값을 1로 초기화한 객체 생성
cal1.postfixOperator(cal1); // 객체 자체를 매개변수로 전달
System.out.println(cal1.a); // 출력: 2
}
}
프리미티브 타입 변수 a는 값이 복사되어 postfixOperator 메서드에 전달된다. 이 복사된 값은 메서드 안에서 증가되지만, 원본 변수 a에는 영향이 없기 때문에 리턴된 값을 다시 할당해야 한다.
int a = 1;
a = calculator.postfixOperator(a); // 2가 리턴되고 다시 a에 할당됨
System.out.println(a); // 출력: 2
객체 cal1을 메서드에 전달할 때는 주소값이 복사되어 전달된다. 즉, cal1과 매개변수 cal은 같은 객체를 참조하고 있으며, 하나에서 변경된 값은 다른 곳에서도 동일하게 반영된다.
Calculator cal1 = new Calculator(1);
cal1.postfixOperator(cal1); // 같은 주소를 참조하고 있어 a 값이 증가됨
System.out.println(cal1.a); // 출력: 2
cal1의 주소값이 cal에 복사되었으므로, 메서드 내에서 cal의 a 값을 변경해도 cal1도 같은 값을 가리키므로 변경된 값이 반영된다. 메서드가 종료되어 cal 변수가 소멸되더라도, cal1은 여전히 동일한 주소를 참조하므로 변경된 값을 유지한다.자바에서 객체를 매개변수로 전달할 때는, 같은 객체를 직접 수정하는 방식은 선호되지 않는다. 대신 새로운 변수를 만들어 그 변수로 작업을 처리하거나, 메서드에서 리턴값을 반환하는 방식이 더 좋다.
java코드 복사
int postfixOperator(int a) {
a++;
return a;
}
Calculator incrementA(Calculator cal) {
cal.a++;
return cal;
}
this는 현재 객체를 가리킨다. 동일한 이름의 매개변수와 인스턴스 변수가 있을 때, this를 사용하여 인스턴스 변수를 명확히 지칭할 수 있다.
Calculator(int a) {
this.a = a; // this는 현재 객체의 a를 가리킴
}
public class DefultConstructor {
String field;
// 매개변수를 받는 생성자
DefultConstructor(String field) {
this.field = field;
}
public static void main(String[] args) {
// DefultConstructor c2 = new DefultConstructor(); // 오류 발생: 디폴트 생성자가 없음
}
}
컴파일러는 개발자가 생성자를 정의한 경우, 디폴트 생성자를 추가하지 않으므로 명시적으로 디폴트 생성자를 추가해야 한다.
public class DefultConstructor {
String field;
// 매개변수 1개를 가진 생성자
DefultConstructor(String field) {
field = "필드 값 초기화";
}
public static void main(String[] args) {
// 매개변수가 있는 생성자 호출
DefultConstructor constructorTest = new DefultConstructor("특정값");
// 주석처리 후 생성자를 삭제하면 디폴트 생성자가 문제 없이 생성된다.
// DefultConstructor c2 = new DefultConstructor();
}
}
DefultConstructor() {
field = "특정 값으로 초기화";
}
this.a = a; // this.a는 객체의 필드, 오른쪽 a는 매개변수
final int MAX_SPEED = 120; // final 필드
public class Car {
final int maxSpeed;
Car(int maxSpeed) {
this.maxSpeed = maxSpeed; // 생성자에서 final 필드 초기화
}
}
public class MathConstants {
public static final double PI = 3.14159; // 상수 선언
}
접근 제어자는 변수나 메서드에 대한 접근 권한을 설정할 수 있다.

public class Secret {
private String name;
private String getName() {
return this.name; // 클래스 내부에서만 접근 가능
}
}
package house; // 패키지가 동일할 경우
public class HouseKim {
String lastname = "Kim"; // default 접근 제어자
}
package house; // 같은 패키지
public class HousePark {
public static void main(String[] args) {
HouseKim kim = new HouseKim();
System.out.println(kim.lastname); // default 접근 제어자이므로 접근 가능
}
}
lastname 필드는 default 접근 제어자로 설정되어 있어 같은 패키지 내에서 접근할 수 있다.extends 키워드를 사용하여 선언된다.class Sonata extends Car {
// Sonata 클래스가 Car 클래스를 상속받음
}
package chap06.car;
public class Car {
protected String company = "르노삼성"; // protected 변수
private String model = "모델"; // private 변수 (상속 불가)
}
package car.example; // 서로 다른 패키지
import chap06.car.Car;
public class Sonata extends Car { // Car 클래스를 상속받음
public static void main(String[] args) {
Sonata sonata = new Sonata();
System.out.println(sonata.company); // 상속받은 protected 변수에 접근 가능
}
}
company 필드를 default로 선언했다면, 패키지가 달라 컴파일 오류가 발생한다.public class Sonata {
private int speed; // private 필드 (캡슐화)
// speed 값을 설정하는 Setter 메서드
public void setSpeed(int speed) {
if (speed < 0) {
this.speed = 0; // 유효성 검사: 음수일 경우 0으로 설정
} else {
this.speed = speed;
}
}
}
public class Sonata {
private int speed; // private 필드
// speed 값을 반환하는 Getter 메서드
public int getSpeed() {
return speed;
}
}
public class Sonata {
private int speed;
private boolean stop;
// Setter 메서드
public void setSpeed(int speed) {
if (speed < 0) {
this.speed = 0;
} else {
this.speed = speed;
}
}
// Getter 메서드
public int getSpeed() {
return speed;
}
// boolean 타입 Getter 메서드: 관례상 is로 시작
public boolean isStop() {
return stop;
}
// Setter 메서드
public void setStop(boolean stop) {
this.stop = stop;
}
public static void main(String[] args) {
Sonata sonata = new Sonata();
// 잘못된 속도 변경 시도
sonata.setSpeed(-50);
System.out.println("현재 속도: " + sonata.getSpeed()); // 0 출력
// 올바른 속도 설정
sonata.setSpeed(60);
System.out.println("변경 후 속도: " + sonata.getSpeed()); // 60 출력
// 멈추기
if (!sonata.isStop()) {
sonata.setStop(true);
sonata.setSpeed(0); // 멈춘 후 속도 0 설정
}
System.out.println("멈춘 후 속도: " + sonata.getSpeed()); // 0 출력
}
}
private으로 선언하고, Setter와 Getter를 통해 필드에 접근하는 것이 권장된다.@Getter
@Setter
public class Sonata {
private int speed;
private boolean stop;
}