Загружаем каталог…
Загружаем каталог…
1. 중첩 클래스 : 클래스 안에 클래스를 중첩해서 정의하는 것 이는 클래스를 정의하는 위치에 따라 크게 두 가지로 분류가 가능하고 그 안에서 세부적으로 나눌 수 있다. 내부 클래스(non-static) 는 바깥 클래스를 구성하는 요소이므로 바깥 클래스의 인스턴스에 소속되지만 정적 중첩 클래스(static) 는 바깥 클래스의 인스턴스에 소속되지 않는다. 내부 클래스의 종류 내부 클래스: 바깥 클래스 인스턴스의 멤버에 접근 지역 클래스: 내부 클래스의 특정 + 지역 변수에 접근 익명 클래스: 지역 클래스의 특징 + 클래스의 이름이 없는 특별한 클래스 중첩 클래스를 사용하는 이유 논리적 그룹화 : 특정 클래스에서만 사용하는 클래스를 그 클래스 안에 넣어 관련 있는 코드끼리 묶는 것 캡슐화 : 중첩 클래스는 바깥 클래스의 private 멤버에 접근이 가능하다. 따라서 불필요한 public 메서드를 제거할 수 있다. 1) 정적 중첩 클래스 public class NestedOuter { private static int outClassValue = 3; private int outInstanceValue = 2; static class Nested { private int nestedInstanceValue = 1; public void print() { // 자신의 멤버에 접근 System.out.println(nestedInstanceValue); // 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다. // System.out.println(outInstanceValue); // 바깥 클래스의 클래스 멤버에는 접근할 수 있다. private 역시 System.out.println(NestedOuter.outClassValue); } } } private 접근 제어자는 같은 클래스 안에 있을 때만 접근이 가능하다. 중첩 클래스도 바깥 클래스와 같은 클래스 안에 있기 때문에 바깥 클래스의 private에 접근 가능하다. public class NestedOuterMain { public static void main(String[] args) { NestedOuter outer = new NestedOuter(); NestedOuter.Nested nested = new NestedOuter.Nested(); nested.print(); System.out.println("nestedClass = " + nested.getClass()); } } 정적 중첩 클래스는 new 바깥클래스.중첩클래스()로 생성할 수 있다. -> 중첩 클래스(+ 내부 클래스)는 용도가 자신이 소속된 바깥 클래스에서 사용되도록 하는 것이다. 따라서 외부에서 생성하여 사용하는 것이 가능하지만, 이미 중첩 클래스의 용도에 맞지 않을 수 있기 때문에 이때는 중첩 클래스를 밖으로 빼는 것을 고려하자. 인스턴스가 생성된 상태 바깥 클래스의 멤버에 접근 정적 중첩 클래스는 바깥 클래스의 static 필드에는 접근할 수 있따. 하지만 바깥 클래스가 만든 인스턴스 필드에는 바깥 인스턴스의 참조가 없기 때문에 접근할 수 없다. -> 서로 다른 클래스 두개를 그저 중첩해 놓은 것일 뿐 아무런 관계가 없다. (private 접근 가능 정도만 예외) 2) 내부 클래스 : 정적 중첩 클래스는 바깥 클래스와 서로 관계가 없다. 하지만 내부 클래스는 바깥 클래스의 인스턴스를 이루는 요소가 된다. 다시말해 내부 클래스는 바깥 클래스의 인스턴스에 소속된다. public class InnerOuter { private static int outClassValue = 3; private int outInstanceValue = 2; class Inner { private int innerInsanceValue = 1; public void print() { // 자신의 멤버에 접근 System.out.println(innerInsanceValue); // 외부 클래스의 인스턴스 멤버에 접근 가능, private 포함 System.out.println(outInstanceValue); // 외부 클래스의 클래스 멤버에는 접근 가능, private 포함 System.out.println(InnerOuter.outClassValue); } } } 내부 클래스의 앞에는 static이 붙지 않아 인스턴스 멤버가 된다. private 접근 제어자는 같은 클래스 안에 있을 때만 접근이 허용된다. 내부 클래스 역시 바깥 클래스와 같은 클래스 안에 있으므로 바깥 클래스의 private 접근 제어자에 접근할 수 있다. public class InnerOuterMain { public static void main(String[] args) { InnerOuter outer = new InnerOuter(); InnerOuter.Inner inner = outer.new Inner(); inner.print(); System.out.println("innerClass = " + inner.getClass()); } } 내부 클래스는 바깥 클래스의 인스턴스에 소속된다. 따라서 바깥 클래스의 인스턴스 정보를 알아야 생성할 수 있다. 내부 클래스는 바깥 클래스의 인스턴스 참조.new 내부클래스() 로 생성할 수 있다. outer.new Inner() 로 생성한 내부 클래스는 개념상 바깥 클래스의 인스턴스 내부에 생성된다. -> 바깥 클래스의 인스턴스를 먼저 생성해야 내부 클래스의 인스턴스를 생성할 수 있다. 개념상 내부 클래스의 생성은 다음과 같다. 같은 이름의 바깥 변수 접근 public class ShadowingMain { public int value = 1; class Inner { public int value = 2; void go() { int value = 3; System.out.println("value = " + value); System.out.println("thi.value = " + this.value); System.out.println("ShadowingMain.value = " + ShadowingMain.this.value); } } public static void main(String[] args) { ShadowingMain main = new ShadowingMain(); Inner inner = main.new Inner(); inner.go(); } } 변수의 이름이 같기 때문에 어떤 변수를 먼저 사용할지 우선순위가 필요하다. 프로그래밍에서 우선순위는 더 가깝고 더 구체적인 것이 갖는다. 메서드 go()에서는 지역변수은 value = 3이 제일 가깝기 때문에 우선순위가 제일 높다. -> 다른 변수들은 가려서 보이지 않음( 섀도잉 ) 3) 내부클래스 - 지역클래스 public class LocalOuterV1 { private int outInstanceVar = 3; public void process(int paramVar) { int localVar = 1; class LocalPrinter { int value = 0; public void printData() { System.out.println("value = " + value); System.out.println("localVar = " + localVar); System.out.println("paramVar = " + paramVar); System.out.println("outInstanceVar = " + outInstanceVar); } } LocalPrinter printer = new LocalPrinter(); printer.printData(); } public static void main(String[] args) { LocalOuterV1 localOuter = new LocalOuterV1(); localOuter.process(2); } } 지역클래스는 자신이 속한 코드 블럭의 매게변수에 접근할 수 있다.(매개변수도 지역변수임) 지역 클래스는 접근제어자를 사용할 수 없다. (1) 지역 변수와 변수 생명주기 변수의 생명주기 클래스 변수: 프로그램 종료까지(메서드 영역) 인스턴스 변수: 인스턴스의 생존기간, 소속된 인스턴스의 GC 전(힙 영역) 지역 변수: 메서드 호출 종료까지(스택 영역) public class LocalOuterV3 { private int outInstanceVar = 3; public Printer process(int paramVar) { int localVar = 1; class LocalPrinter implements Printer { int value = 0; @Override public void print() { System.out.println("value = " + value); // 인스턴스는 지역 변수보다 더 오래 살아남는다. System.out.println("localVar = " + localVar); System.out.println("paramVar = " + paramVar); System.out.println("outInstanceVar = " + outInstanceVar); } } Printer printer = new LocalPrinter(); // printer.print()를 여기서 실행하지 않고 Printer 인스턴스만 반환한다. return printer; } public static void main(String[] args) { LocalOuterV3 localOuter = new LocalOuterV3(); Printer printer = localOuter.process(2); // printer.print()를 나중에 실행한다. process()의 스택 프레임이 사라진 이후에 실행 printer.print(); } } 여기서 LocalPrinter 인스턴스는 process() 메서드에서 생성된다. 또한 process()에서 main()으로 생성한 LocalPrinter 인스턴스를 반환하고 printer 변수에 참조를 보관했으므로 LocalPrinter 인스턴스는 main이 종료될 때까지 살아있는다. paramVar, localVar와 같은 지역변수는 process() 메서드가 실행되는 동안만 스택에서 생존한다. LocalPrinter 인스턴스는 print() 메서드를 통해 힙 영역 인스턴스 변수의 outInstanceVar에 접근했다. 또한 print() 메서드를 통해 스택 영역에 존재하는 지역 변수도 접근하는 것처럼 보이지만 그렇지 않다. 지역 변수의 생명주기는 매우 짧은 반면 인스턴스는 GC 전까지 생존이 가능하다. process() 메서드가 종료되면 process()의 스택 프레임이 제거되며 지역변수도 함께 제거되지만 LocalPrinter 인스턴스는 계속 생존할 수 있다. 따라서 print()로 paramVar과 localVar에 접근할 수 없다. 하지만 위 코드를 실행해보면 지역 변수의 값이 문제없이 출력되는 것을 알 수 있다. (2) 지역 변수 캡쳐 : 지역 클래스의 인스턴스를 생성하는 시점에 필요한 지역 변수를 복사해서 생성한 인스턴스에 함께 넣어두는 과정 LocalPrinter 인스턴스 생성이 시도됨과 동시에 지역 클래스가 접근하는 지역 변수를 확인하여 paramVar과 localVar 지역 변수에 접근하여 값을 복사한다. 복사한 지역 변수를 인스턴스에 포함시켜 인스턴스 생성을 마친다. (3) 지역 변수 값변경 지역 클래스가 접근하는 지역 변수는 절대 중간에 값이 변하면 안된다. LocalPrinter를 생성하는 시점에 지역 변수인 localVar과 paramVar를 캡쳐하기 때문에 캡쳐한 값을 바꾸면 스택 영역에 있는 값과 인스턴스에 캡쳐된 값이 일치하지 않는 동기화 문제 가 발생하게 된다. 4) 익명 클래스 : 지역 클래스의 일종으로 클래스의 이름이 없다. 익명 클래스를 사용하면 기존 클래스의 선언과 생성을 따로 하던 것에 반해 이 둘을 한꺼번에 처리할 수 있게 된다. import nested.local.Printer; public class AnonymousOuter { private int outInstanceVar = 3; public void process(int paramVar) { int localVar = 1; Printer printer = new Printer() { int value = 0; @Override public void print() { System.out.println("value = " + value); System.out.println("localVar = " + localVar); System.out.println("paramVar = " + paramVar); System.out.println("outInstanceVar = " + outInstanceVar); } }; printer.print(); System.out.println("printer.class = " + printer.getClass()); } public static void main(String[] args) { AnonymousOuter main = new AnonymousOuter(); main.process(2); } } new Printer() {body} 익명 클래스는 클래스의 본문을 정의함과 동시에 생성한다. 익명 클래스를 만들 때는 부모 클래스를 상속받거나 인터페이스를 구현해야 한다. 또한 익명 클래스는 이름을 가지지 않으므로 생성자를 가질 수 없어 기본 생성자만 사용된다. 익명 클래스는 클래스를 별도로 정의하지 않고도 인터페이스를 구현할 수 있어 코드가 간결해진다는 장점이 있다. 익명 클래스를 사용할 수 없는 경우 익명 클래스는 단 한번만 인스턴스를 생성할 수 있다. 따라서 여러번 인스턴스를 생성해야 한다면 익명 클래스를 사용할 수 없다. (지역 클래스 선언)
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
멋쟁이사자처럼 5번째 벨로그. 1. 중첩 클래스 : 클래스 안에 클래스를 중첩해서 정의하는 것 이는 클래스를 정의하는 위치에 따라 크게 두 가지로 분류가 가능하고 그 안에서 세부적으로 나눌 수 있다. 내부 클래스(non-static) 는 바깥 클래스를 구성하는 요소이므로 바깥 클래스의 인스턴스에 소속되지만 정적 중첩 클래스(static) 는 바깥 클래스의 인스턴스에 소속되지 않는다. 내부 클래스의 종류 내부 클래스: 바깥 클래스 인스턴스의 멤버에 접근 지역 클래스: 내부 클래스의 특정 + 지역 변수에 접근 익명 클래스: 지역 클래스의 특징 + 클래스의 이름이 없는 특별한 클래스 중첩 클래스를 사용하는 이유 논리적 그룹화 : 특정 클래스에서만 사용하는 클래스를 그 클래스 안에 넣어 관련 있는 코드끼리 묶는…
Открыть источник