블로그 이름 뭐하지
[JAVA] 클래스 본문
객체를 생성하기 위한 설계도.
필드, 생성자, 메서드로 구성된다.
클래스 생성 방식
1. 클래스 선언
2. 객체 속성(필드) 정의하여 데이터 저장
3. 객체를 생성하는 방식(생성자) 선언.
생성자는 반환타입이 없고 이름은 클래스와 동일함
4. 객체 행위(메서드) 정의
public class Car{ //1. 클래스 선언
//2. 필드 정의
String company;
String model;
String color;
double price;
double speed;
char gear;
//3. 생성자 정의
//()안에 매개변수가 없으니 기본 생성자.
public Car(){}
//4. 메서드 정의
//반환타입이 char인 changeGear(char type)의 메서드 선언
//매개변수 type을 통해 메서드가 호출되면 gear 필드에 해당값을 저장하고 리턴
char changeGear(char type){
gear = type;
return gear;
}
}
객체 생성
객체 생성 연산자 new를 사용하면 클래스로 객체를 생성할 수 있다.
new 연산자 뒤에는 해당 클래스의 생성자 호출코드를 작성한다.
Car car1 = new Car();
//new 뒤에 생성자 호출코드 작성
//Car클래스의 생성자(public Car(){})이므로 Car()로 호출
//Car 클래스의 객체인 car1 인스턴스 생성
//new 연산자로 객체가 생성되면 해당 인스턴스의 주소 반환
>>car1이라는 참조형 변수를 통해 받음
객체 배열
객체는 참조형 변수와 동일하게 취급되므로 배열, 컬렉션에도 저장될 수 있다.
public class Main {
public static void main(String[] args) {
Car[] carArray = new Car[3];
Car car1 = new Car();
car1.changeGear('P');
carArray[0] = car1;
Car car2 = new Car();
car2.changeGear('N');
carArray[1] = car2;
Car car3 = new Car();
car3.changeGear('D');
carArray[2] = car3;
for (Car car : carArray) {
System.out.println("car.gear = " + car.gear);
}
}
}
// 출력
//car.gear = P
//car.gear = N
//car.gear = D
객체 속성: 필드
객체의 데이터를 저장한다.
필드 초기화
필드에 초기값을 제공하지 않을 경우(ex. String gear;)
객체 생성시 자동으로 기본값으로 초기화된다.
데이터 타입 | 기본값 |
byte, short, int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | \n0000(공백) |
boolean | false |
배열, 클래스, 인터페이스 | null |
필드 사용
클래스에 필드를 선언하고 바로 사용할 수 있는 것이 아니다.
클래스는 설계도일 뿐이고, 필드의 데이터를 가지고 있는 것은 객체이므로,
객체를 생성한 후에 필드를 사용할 수 있다.
public class Car{
String color;
double speed;
public Car(){};
//내부접근
double breakPedal(){
speed = 0;
return speed;
}
}
public class Main{
Car car = new Car();
//외부접근
car.color = "blue";
car.breakPedal();
}
외부 접근
다른 클래스에서 객체를 생성(Car car = new Car())하면 참조변수(car)를 이용해 필드에 접근할 수 있다.
이때는 도트연산자(.)를 이용한다(car.필드명)
내부 접근
객체 내부 메서드에서도 내부 필드에 접근할 수 있다.
메서드 내부(breakPedal())에서 필드를 바로 호출(speed = 0)하는 것이다.
객체 행위: 메서드
객체 간의 협력을 위해 사용된다.
메서드 선언
리턴타입 메서드명(매개변수, ...){
실행할 코드;
}
▷ 리턴 타입
메서드가 실행된 후 호출한 곳으로 값을 반환할 때, 해당 값의 타입을 의미한다.
return 타입을 선언하여 반환할 값이 있다면 반드시 return문을 작성하여 반환값을 지정해야한다.
반환값이 없을 때는 void를 작성한다.
메서드 실행시에는 return문을 만나면 메서드를 종료하므로 return을 이용해 원하는 지점에서 메서드를 종료할 수 있다.
char changeGear(char type){
gear = type;
return gear;
}
//리턴타입 char.
//char타입의 변수 gear를 return문에 담아 리턴함
void horn(){
System.out.println("빵빵");
}
//리턴타입 없음(void)
//리턴타입이 없으니 return문도 필요없음
▷ 매개 변수
메서드를 호출할 때 메서드로 전달하려는 값을 받기 위해 사용되는 변수
전달하려는 값이 없으면 생략이 가능하다(ex. void horn())
가변 길이의 매개변수도 선언할 수 있다. (...)을 사용해 매개값을 개수 상관없이 전달한다.
double gasPedal(double kmh, char type){
speed = kmh;
return speed;
}
매개변수: double타입의 kmh, char타입의 type
메서드 호출시에는 각 타입에 맞는 값을 넣어 호출한다
ex. gasPedal(100, 'D');
void carSpeeds(double ... speeds){
for(double v : speeds){
System.out.println("v: "+v);
}
}
가변길이의 매개변수
double값을 개수 상관없이 전달함
ex. carSpeeds(100, 90); carSpeeds(110, 120, 150);
메서드 호출
필드와 마찬가지로 객체 생성 후에 메서드를 사용할 수 있다.
public class Car{
char gear;
double speed;
public Car(){};
char changeGear(char type){
gear = type;
return gear;
}
//내부접근
double gasPedal(double kmh, char type){
changeGear(type);
speed = kmh;
return speed;
}
}
public class Main{
Car car = new Car();
//외부접근
car.gasPedal(100, 'D');
//반환값 저장
car.speed = car.gasPedal(200, 'D');
}
외부 접근
다른 클래스에서 객체를 생성(Car car = new Car())하면 참조변수(car)를 이용해 메서드에 접근할 수 있다.
이때는 도트연산자(.)를 이용한다(car.메서드명(매개값))
내부 접근
객체 내부 메서드에서도 내부 메서드에 접근할 수 있다.
메서드 내부(gasPedal(double kmh, char type))에서 메서드를 바로 호출(changeGear(type))하는 것이다.
반환값 저장
리턴타입으로 반환할 값이 있다면 변수를 이용해 받아 줄 수 있다.
반드시 변수와 리턴타입의 타입이 동일하거나, 자동형변환이 가능해야한다.
메서드 오버로딩
함수가 하나의 기능만을 구현하지 않고 하나의 메서드명으로 여러 기능을 구현하도록 한다.
오버로딩 조건
매개변수의 차이로만 구현가능
메서드의 이름이 같고 매개변수의 개수, 타입, 순서가 다를 것.
응답값, 접근제어자만 다르면 오버로딩 불가.
오버로딩 장점
1. 메서드 이름 하나로 상황에 따른 동작을 정의할 수 있다.
2. 메서드의 이름을 절약할 수 있다.
ex. println() >> String, int, boolean 어떤 값을 넣어도 출력 가능
기본형 참조형 매개변수
기본형 매개변수
메서드 호출 시 전달할 매개값으로 지정한 값을 메서드의 매개변수에 복사하여 전달
값 자체가 복사되므로 매개값으로 지정된 변수의 원본값은 변경되지 않는다.
참조형 매개변수
메서드 호출 시 전달할 매개값으로 지정한 값의 주소를 메서드의 매개변수에 복사하여 전달
저장된 곳의 원본 주소를 전달하므로 값을 읽고, 변경할 수도 있다.
매개변수 뿐 아니라 반환타입도 참조형이 될 수 있다.
인스턴스 멤버와 클래스 멤버
멤버 = 필드 + 메서드
인스턴스 멤버 = 인스턴스 필드 + 인스턴스 메서드
클래스 멤버 = 클래스 필드 + 클래스 메서드
선언하는 방법에 따라 인스턴스 멤버와 클래스 멤버로 구분할 수 있다.
인스턴스 멤버
객체를 생성해야 사용할 수 있다.
객체의 인스턴스 필드는 각각의 인스턴스마다 고유한 값을 가진다.
객체가 인스턴스화 할 때마다 매번 메서드가 생성되지 않고, 메서드 영역에 있는 메서드를 모든 인스턴스들이 공유하여 사용하는 구조이다.
클래스 멤버
메서드 영역의 클래스와 같은 위치에 고정적으로 위치하는 멤버.
따라서 객체의 생성없이 바로 사용이 가능하다.
클래스 멤버는 static 키워드를 사용한다.
공용적인 데이터를 저장하는 필드, 인스턴스 필드를 사용하지 않고 실행되는 메서드는
클래스 메서드로 선언하는 것이 좋다.
클래스 멤버로 선언된 메서드는 인스턴스 멤버를 사용할 수 없다.(객체 생성이 필요하므로)
인스턴스 멤버로 선언된 메서드는 클래스 멤버를 사용할 수 있다.
클래스 멤버를 사용할 때는 클래스의 이름과 함께 도트연산자(.)를 사용한다.
//클래스 멤버
static String company = "GENESIS"
static String setCompany(String companyName){
company = companyName;
return company;
}
//클래스 멤버 사용시에는 인스턴스를 생성하지 않고, 클래스이름.으로 사용한다.
Car.company = "Audi"
String companyName = Car.setCompany("Benz");
지역변수
메서드 내부에서 선언한 변수
메서드가 실행될 때마다 독립적인 값을 저장하고 관리한다.
메서드 내부에서 정의될 때 생성되어 메서드가 종료될 때까지 유지된다.
public class Main {
public static void main(String[] args) {
Main main = new Main();
// 메서드 호출 : main.getClass()
System.out.println("main.getClass() = " + main.getNumber());
System.out.println("main.getClass() = " + main.getNumber());
System.out.println("main.getClass() = " + main.getNumber());
}
public int getNumber() {
int number = 1; // 지역 변수
number += 1;
return number; // 메서드 종료되면 지역변수 제거됨
}
}
// 출력
//main.getNumber() = 2
//main.getNumber() = 2
//main.getNumber() = 2
final 필드와 상수
final 필드
초기값이 저장되면 해당 값을 프로그램 실행 중에는 수정할 수 없다.
또한 final 필드 정의시에는 반드시 초기값을 지정해야한다.
사용은 인스턴스 필드와 동일하다.
final String company = "GENESIS";
//정의와 동시에 초기값 지정
//final String company;
//company = "GENESIS"; 불가.
Car car = new Car();
//car.company = "Benz" 처럼 초기값 지정 후 수정 불가.
상수
반드시 값이 한 개이며 불변의 값을 의미한다.
인스턴스마다 상수를 저장할 필요없다.
static final을 사용해 상수를 지정한다.
사용은 클래스 필드와 동일하다.
일반적으로 상수는 대문자로 작성한다.
static final String company = "GENESIS";
//Car.company = "Benz" 처럼 초기값 지정 후 수정 불가.
생성자
객체가 생성될 때 호출되며 객체를 초기화한다.
선언과 호출
생성자는 반환타입이 없고 클래스의 이름과 동일하다.
public Car() {} //선언
Car car = new Car(); //호출
기본 생성자
선언시 괄호()안에 아무것도 넣지 않는 생성자.
모든 클래스는 반드시 생성자가 하나 이상 존재하며,
생성자를 선언하지 않았을 경우 컴파일러가 자동으로 기본 생성자를 추가한다.
하나라도 생성자가 선언되어 있다면 컴파일러는 기본 생성자를 추가하지 않는다.
컴파일러에 의해 생성되는 기본 생성자는 클래스의 접근제어자를 따른다.
필드 초기화
생성자는 객체를 초기화하는 역할이다.
객체를 만들 때 인스턴스마다 다른 값을 가져야 한다면 생성자를 통해 필드를 초기화 할 수 있다.
반대로 인스턴스마다 동일한 데이터의 필드를 가져야 한다면 클래스상의 필드에 초기값을 대입한다.
// 인스턴스가 각각 다른 데이터의 필드를 가질 경우
// 생성자 이용
class Car{
String model;
String color;
public Car(String modelName, String colorName) {
model = modelName;
color = colorName;
};
}
class Main{
Car car01 = new Car("GENESIS", "black");
Car car02 = new Car("Benz", "white");
//Car car03 = new Car(); >> 오류!
//하나 이상의 생성자가 존재하므로 기본생성자가 존재하지 않음
}
// 모든 인스턴스가 공통적인 데이터의 필드를 가질 경우
// 필드 초기값 지정
class Car{
String model = "GENESIS";
String color = "red";
}
class Main{
Car car01 = new Car();
Car car02 = new Car();
//생성자를 만들지 않았으므로 기본 생성자가 자동 추가됨
}
생성자 오버로딩
메서드처럼 생성자도 오버로딩이 가능하다.
동일하게 매개변수의 개수, 타입, 순서를 다르게 하여 적용한다.
public class Car{
String model;
String color;
double price;
//생성자 오버로딩
public Car(String modelName){
model = modelName;
}
public Car(String modelName, String colorName){
model = modelName;
color = colorName;
}
public Car(String modelName, String colorName, double priceValue){
model = modelName;
color = colorName;
price = priceValue;
}
//이 경우에는 오류가 남!
//매개변수 colorName과 modelName의 순서를 바꿨지만,
//String String double 이므로 개수, 타입, 순서가 동일함
public Car(String colorName, String modelName, double priceValue){
model = modelName;
color = colorName;
price = priceValue;
}
}
this와 this()
this
객체, 즉 인스턴스 자신을 표현하는 키워드.
객체 내부 생성자 및 메서드에서 객체 내부 멤버에 접근하기 위해 사용한다.
생성자에서는 주로 매개변수의 이름과 필드명이 같을 때 사용한다.
또한 객체의 메서드에서 리턴타입이 인스턴스 자신의 클래스 타입일 경우 this를 사용해 인스턴스의 주소를 반환할 수도 있다.
public class Car{
String model;
String color;
double price;
//매개변수명과 필드명이 동일하므로 this사용하여 구별
public Car(String model, String color, double price){
this.model = model;
this.color = color;
this.price = price;
}
}
//자신의 주소를 반환
Car returnInstance(){
return this;
}
this()
객체, 즉 인스턴스 자신의 생성자를 호출하는 키워드.
객체 내부 생성자 및 메서드에서 해당 객체의 생성자를 호출하기 위해 사용
코드의 중복을 줄여줄 수 있다.
public Car(String model) {
this.model = model;
this.color = "Blue";
this.price = 50000000;
}
public Car(String model, String color) {
this.model = model;
this.color = color;
this.price = 50000000;
}
>> this() 키워드 사용
public Car(String model) {
this(model, "Blue", 50000000);
}
public Car(String model, String color) {
System.out.println("이러면 오류남")
//this() 키워드 사용시
//생성자 호출 이전에 다른 코드가 존재하면 오류 발생함
this(model, color, 100000000);
}
접근 제어자
클래스, 변수. 메서드의 선언부에 사용되어 의미를 부여한다.
외부의 접근을 제어하여 데이터를 보호하는 역할을 수행한다.
생성자에 접근 제어자를 사용할 경우 인스턴스의 생성을 제한한다.
접근 제어자(public, protected, default, private), 그외 제어자(static, final, abstract)
하나의 대상에 여러 제어자를 사용할 수 있으나 접근 제어자는 개중 하나만 사용할 수 있다.
종류
클래스, 멤버변수, 메서드, 생성자에 사용되고, 따로 지정되어 있지 않으면 default로 자동 지정된다.
public : 접근 제한 없음(= 어디에서든 접근가능)
protected : 같은 패키지 내, 다른 패키지의 자손 패키지 내에서 접근 가능
default : 같은 패키지 내에서만 접근 가능
private : 같은 클래스 내에서만 접근 가능
사용가능한 접근 제어자
클래스 : public, default
메서드, 멤버변수 : 전부
지역변수 : 없음
사용가능한 그외 제어자
클래스: final, abstract
메서드: 전부
멤버변수: final, static
지역변수: final
제어자 조합 주의사항
1) 메서드에 static + abstract 함께 사용금지
2) 클래스에 final + abstract동시 사용금지
3) abstract 메서드의 접근제어자에 private 사용금지
4) 메서드에 private + final 사용하지 않음
Getter 와 Setter
객체의 무결성(변경이 없는 상태)를 유지하기 위해 사용한다.
private로 접근제어자가 걸린 필드를 읽거나 저장하기 위해 사용한다.
Getter
객체의 private한 필드를 읽을 때 사용한다.
get + 필드이름으로 지정
Setter
객체의 private한 필드를 저장, 수정할 때 사용한다.
set + 필드이름으로 지정
public class Car{
private double speed;
//getter
public double getSpeed(){
return speed;
}
//setter
public void setSpeed(double speed){
this.speed = speed;
}
}
Package와 Import의 이해
Package
클래스를 식별해주는 용도이다.
상위패키지와 하위패키지는 도트(.)로 구분한다.
package 상위패키지.하위패키지
Import
다른 패키지의 클래스를 사용하기 위해 명시하는 키워드이다.
클래스 이름을 생략하고 *로 지정하면 패키지 아래의 모든 클래스를 사용할 수 있다.
package oop.main;
import oop.pk1.Car;
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.horn(); // pk1 빵빵
oop.pk2.Car car2 = new oop.pk2.Car();
car2.horn(); // pk2 빵빵
}
}
'JAVA' 카테고리의 다른 글
[JAVA] 추상 클래스와 인터페이스 (0) | 2024.09.23 |
---|---|
[JAVA] 상속 (0) | 2024.09.23 |
[JAVA] 객체 지향 프로그래밍의 이해 (1) | 2024.09.20 |
[JAVA] 컬렉션 (0) | 2024.09.20 |
[JAVA] 배열 (0) | 2024.09.20 |