유효범위 (scope)
public class ScopeDemo{
static void a(){
int i = 0;
}
public static void main(String args[]){
for(int i = 0; i < 5; i++){
a();
System.out.println(i);
}
}
}
/* 실행 결과
0
1
2
3
4
*/
만약 유효범위 (scope)가 없다면 위 코드의 for문은 무한루프에 빠지게 된다.
아무리 for문에서 i++를 해줘도 a() 메소드로 인해 다시 0으로 초기화되기 때문이다.
하지만 위 코드가 정상적인 실행 결과를 보여주는 이유는
충돌이 발생하는 것을 해결하기 위해 고안된 유효범위 덕분이다.
비교를 위해 위 코드와 같은 동작을 하지만 충돌이 발생하는 코드는 아래와 같다.
public class ScopeDemo2{
static int i;
static void a(){
i = 0;
}
public static void main(String[] args){
for(i = 0; i < 5; i++){
a();
System.out.println(i);
}
}
}
두 코드를 비교해보면
ScopeDemo 코드는 a() 메소드 안과 for문 안에서 각각 새롭게 변수 i를 생성해주고 있으며
ScopeDemo2 코드는 제일 위에 변수 i를 생성하고 이를 a() 메소드와 for문에서 가져다 사용하고 있다.
즉, ScopeDemo 코드는 유효범위를 활용하여 지역변수로,
ScopeDemo2 코드는 유효범위를 활용하지 않고 전역변수로 변수를 생성하여 사용하고 있기 때문에 두 코드가 다른 실행 결과를 보이는 것이다.
유효범위는 중괄호 {} 를 기준으로 나뉜다.
유효범위 안에서만 값이 유효하며 범위를 벗어난 곳에서는 값을 사용하지 못한다.
지역변수가 전역변수보다 우선 순위가 높다.
class C {
int v = 10;
void m() {
int v = 20;
System.out.println(v); // 20
System.out.println(this.v); // 10
}
}
public class ScopeDemo7 {
public static void main(String[] args) {
C c1 = new C();
c1.m();
}
}
인스턴스의 유효범위도 클래스와 거의 동일하지만 결정적인 차이점은 this에 있다.
this는 생성된 인스턴스를 가리킨다.
그래서 this.v는 인스턴스의 v 즉, 10이 출력되는 것이다.
전역변수에 의존한다는 것은 이 메소드가 다른 완제품의 부품으로서 사용될 수 없다는 의미이다.
가급적이면 전역변수의 사용을 자제하는 것이 좋고
단일 객체가 너무 커지지 않도록 적절히 규모를 쪼개는 것이 중요하다.
초기화와 생성자
초기화는 어떤 일을 시작하기 전에 준비 작업을 의미한다.
객체 지향 프로그래밍에서의 초기화 작업은 생성자를 통해 진행된다.
class Calculator {
int left, right;
// 생성자
public Calculator(int left, int right) {
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
public void avg() {
System.out.println((this.left + this.right) / 2);
}
}
public class CalculatorDemo1 {
public static void main(String[] args) {
Calculator c1 = new Calculator(10, 20);
c1.sum();
c1.avg();
Calculator c2 = new Calculator(20, 40);
c2.sum();
c2.avg();
}
}
위 코드와 같이 인스턴스를 생성할 때 필요한 값도 같이 입력하도록 강제성을 주면
절차를 줄임과 동시에 오류 발생 확률도 줄이는 효과를 얻는다.
생성자의 특징
1. 값을 반환하지 않는다.
생성자는 인스턴스를 생성해주는 역할을 하는 특수 메소드이기 때문
2. 생성자의 이름은 클래스의 이름과 동일하다. [관례]
상속
아래의 경우에 속한다면 객체에 메소드를 추가하기 어렵다.
- 객체를 자신이 만들지 않아 소스를 변경할 수 없을 경우
- 객체가 다양한 곳에서 사용되고 있는 경우 불필요한 기능이 추가되는 것일 수 있다.
기존의 객체를 수정하지 않으면서 새로운 객체를 기존 객체 기반으로 만들기 위해 등장한 것이 상속이다.
기존의 객체는 기능을 물려준다는 의미에서 부모 객체라 부르며
새로운 객체는 기존 객체의 기능을 물려받는다는 의미에서 자식 객체라 부른다.
부모 객체 = 부모 클래스 = 상위 클래스 = super 클래스 = 기초 클래스
자식 객체 = 자식 클래스 = 하위 클래스 = sub 클래스 = 유도 클래스
부모가 만든 코드를 자식이 또 만들지 않는다는 점에서 코드중복제거, 재사용성 증가 효과를 얻으며
부모의 코드가 변경되면 자식 또한 자동으로 변경되어 유지보수가 용이하고 가독성이 증가한다.
ex) 상속을 적용한 계산기 예제
class Calculator {
int left, right;
public void setOprands(int left, int right) {
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
public void avg() {
System.out.println((this.left + this.right) / 2);
}
}
// 자식클래스 extends 부모클래스
class SubstractionableCalculator extends Calculator {
public void substract() {
System.out.println(this.left - this.right);
}
}
public class CalculatorDemo1 {
public static void main(String[] args) {
SubstractionableCalculator c1 = new SubstractionableCalculator();
c1.setOprands(10, 20);
c1.sum(); // 30
c1.avg(); // 15
c1.substract(); // -10
}
}
부모 클래스는 여러 자식 클래스를 만들 수 있으며
자식 클래스 또한 부모 클래스가 되어 자식 클래스를 만들 수 있다.
상속과 생성자
public class ConstructorDemo {
public static void main(String[] args) {
ConstructorDemo c = new ConstructorDemo();
}
}
자바는 객체를 생성할 때 자동으로 기본 생성자를 만들어주기 때문에 위 코드는 에러를 발생시키지 않는다.
- 기본 생성자 : 인자가 없는 생성자
public class ConstructorDemo {
public ConstructorDemo(int param1) {}
public static void main(String[] args) {
ConstructorDemo c = new ConstructorDemo();
}
}
하지만 이처럼 매개변수가 있는 생성자가 있을 때는 자동으로 기본 생성자를 만들어주지 않기 때문에 에러가 발생한다.
public class ConstructorDemo {
public ConstructorDemo(){}
public ConstructorDemo(int param1) {}
public static void main(String[] args) {
ConstructorDemo c = new ConstructorDemo();
}
}
위 코드와 같이 기본 생성자를 추가로 작성해줘야 에러가 발생하지 않는다.
앞서 상속에서 본 계산기 코드는 생성자를 이용하지 않은 코드이다.
생성자를 이용하여 계산기 코드를 리팩토링하면 다음과 같다.
class Calculator {
int left, right;
// 생성자
public Calculator(int left, int right){
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
public void avg() {
System.out.println((this.left + this.right) / 2);
}
}
class SubstractionableCalculator extends Calculator {
public SubstractionableCalculator(int left, int right) {
this.left = left;
this.right = right;
}
public void substract() {
System.out.println(this.left - this.right);
}
}
public class CalculatorConstructorDemo5 {
public static void main(String[] args) {
SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
c1.sum();
c1.avg();
c1.substract();
}
하지만 이 코드는 에러를 발생시킨다.
SubstractionableCalculator는 Calculator를 상속받는 자식 클래스이기 때문에
호출이 될 때 자동으로 부모 클래스의 기본 생성자를 호출한다.
Calculator 기본 생성자가 없기 때문에 에러가 발생한다.
이 문제를 해결하기 위해서는 아래와 같이 부모 클래스에 기본 생성자를 추가하면 된다.
class Calculator {
int left, right;
// 기본 생성자
public Calculator(){
}
public Calculator(int left, int right){
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
public void avg() {
System.out.println((this.left + this.right) / 2);
}
}
class SubstractionableCalculator extends Calculator {
public SubstractionableCalculator(int left, int right) {
this.left = left;
this.right = right;
}
public void substract() {
System.out.println(this.left - this.right);
}
}
public class CalculatorConstructorDemo5 {
public static void main(String[] args) {
SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
c1.sum();
c1.avg();
c1.substract();
}
}
하지만 이 코드에는 중복이 생긴다.
부모 클래스도 left, right를 인자로 받아 객체를 생성하며
자식 클래스인 SubstractionableCalculator도 left, right를 인자로 받아 생성하기 때문이다.
이 문제를 해결하기 위해 등장한 것이 super이다.
class Calculator {
int left, right;
public Calculator(int left, int right){
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
public void avg() {
System.out.println((this.left + this.right) / 2);
}
}
class SubstractionableCalculator extends Calculator {
public SubstractionableCalculator(int left, int right) {
// 변경된 부분
super(left, right);
//
}
public void substract() {
System.out.println(this.left - this.right);
}
}
public class CalculatorConstructorDemo5 {
public static void main(String[] args) {
SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
c1.sum();
c1.avg();
c1.substract();
}
}
super 키워드는 부모 클래스를, super()는 부모 클래스의 생성자를 의미한다.
그렇기 때문에 기본 생성자가 없어져도 에러가 나타나지 않는다.
'JAVA > [생활코딩] 자바' 카테고리의 다른 글
[생활코딩] 예외 처리, Object 클래스, 상수와 enum (0) | 2023.04.30 |
---|---|
[생활코딩] 접근 제어자, abstract, final, 인터페이스, 다형성 (1) | 2023.04.17 |
[생활코딩] overriding, overloading, 클래스 패스, 패키지, API와 API 문서 보는 법 (0) | 2023.04.09 |
[생활코딩] 자바 객체지향프로그래밍, 클래스와 인스턴스 그리고 객체, 클래스 멤버와 인스턴스 멤버 (0) | 2023.03.31 |
[생활코딩] 자바 숫자와 문자, 변수, 주석과 세미콜론, 데이터 타입, 상수의 데이터 타입, 형변환, 연산자, 비교와 Boolean (0) | 2023.03.21 |