Java awt setVisible(boolean b)
2023. 2. 1. 18:45ㆍJava
void java.awt.Window.setVisible(boolean b)
setVisible(boolean b) 메서드는 파라메터 b 값에 따라서 이 Window를 보여주거나 숨긴다.
프로젝트 내 src 내 javabasic 패키지 내 Ex7ButtonArray.java
package javabasic;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.border.LineBorder;
public class Ex7ButtonArray extends JFrame {
JButton[] btn = new JButton[7];
JLabel lblColor;
String[] btnTitle = { "Red", "Blue", "Green", "Magenta", "Yellow", "Orange", "Pink" };
Color[] color = { Color.red, Color.blue, Color.green, Color.magenta, Color.yellow, Color.orange, Color.pink };
public Ex7ButtonArray(String title) {
// TODO Auto-generated constructor stub
super(title);
this.setBounds(700, 100, 400, 400);// 시작위치x,y,크기 w,h
// super로 해도 되고 this로 해도 됨 super는 조상
// this로 해도 상속을 받아서 괜찮음
// this.getContentPane().setBackground(Color.orange);//프레임위에 있는 패널의 색상 변경
this.getContentPane().setBackground(new Color(211, 225, 208));// 프레임위에 있는 패널의 색상 변경
this.setDesign();// 디자인 코드
this.setVisible(true);// 보이게 하기
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 프로그램을 종료해주는 메서드
}
public void setDesign() {
int yPos = 20;
// 레이아웃 무효화
this.setLayout(null);
// 7개의 버튼 생성 및 이벤트
for (int i = 0; i < btn.length; i = i + 1) {
// 버튼 생성
btn[i] = new JButton(btnTitle[i]);
// 위치,크기 지정
btn[i].setBounds(20, yPos, 100, 30);
// 글자색을 해당 색상으로 변경
btn[i].setForeground(color[i]);
// 프레임에 추가
this.add(btn[i]);
yPos = yPos + 40;
final int idx = i;
// 바로 밑줄의 int idx = i; 에서
// idx 변수는 그냥 int 형만이 아니고
// effectively final 로 유사 final 이고
// final 변수 없이도 오류가 안뜨고 정상적으로 프로그램이 돈다.
// int idx = i;
/*effectively final(유사 final) 변수 ef 하나 추가해서
익명클래스에서 변화 시도해보기*/
int ef = i;
// 이벤트 추가-익명 내부 클래스 형태
// Ex7ButtonArray 클래스 내에 있는 메서드 setDesign() 안에서
// 선언은 없이 생성만 된 익명 내부 클래스 형태로써 이 익명 내부 클래스는
// 또한 내부 로컬 클래스 이기도 하다. 내부 로컬 클래스란 Ex7ButtonArray
// 클래스처럼 클래스가 있고 그 속에 메서드가 있을 때, 고 메서드 내부에서
// 선언된 클래스를 말한다.
// Inner Local class
// >>> 메서드 내부에서 지역 변수 처럼 선언된 클래스를 말한다.
// 클래스 이름에서 알 수 있듯이 마치 지역 변수에서 변수 대신 클래스를 바꾸어
// 쓴 것이다. 지역 변수. 지역 클래스 이면서 동시에
// 내부 클래스란 말 자체가 클래스 안에 또 다른 클래스를 구성하는 것이므로
// 지역 클래스 이면서 동시에 내부 클래스라서
// 내부 지역 클래스 라고 말한다.
// 클래스 이름에서 알 수 있듯이 메서드 안에서 선언된 클래스의 속성은
// 메서드 밖에서 사용할 수 없다. 생각해보면 이뿐만 아니라
// 메서드 안에서 선언된 지역 변수는 메서드 밖에서도 사용할 수 없는 것이랑
// 일맥상통한다고 볼 수 있겠다. 지역 변수란 특정한 구역 {지역변수 선언 ...}
// 안에서 생성되어 그 지역 에만 사용할 수 있는 변수다.
// 지역변수는 void method(){...}
// 안에 생성되며 void method(){...}를 벗어나면 void method(){...}
// 라는 메서드가 끝나는 시점에 바로 삭제된다.
// Named Local class(이름 있는 지역 클래스) 와
// Anonymous class(익명 클래스)
// 즉, 익명 내부 클래스이면서 내부 지역 클래스라서
// 길게 말하면
// 익명 내부 지역 클래스(익명 Inner Local Class) 이다.
// Inner Local class 는 메서드 블록 안에 구성된 내부 클래스로
// 지역 변수와 비슷한 특징을 갖는다.
// 메서드 안에서 생성된 Inner class 는 지역 변수 처럼 그 안에서만
// 사용할 수 있다. 따라서 main(String[] args) 에서
// void actionPerformed(ActionEvent e) 를 직접적으로
// 호출할 수 없다.
// Anonymous class (익명 클래스)
// 익명 클래스란 다른 내부 클래스와는 달리 이름을 가지지 않는 클래스다.
// 익명 클래스는 클래스의 선언과 동시에 객체를 생성하므로,
// 단 하나의 객체만을 생성하는 일회용 클래스이다.
// 따라서 생성자를 선언할 수도 없으며,
// 오로지 단 하나의 클래스나 단 하나의 인터페이스를 상속받거나
// 구현할 수 있다.
// 내부 클래스를 사용하게 되면 비슷한 형태의 클래스들을 묶을 수 있어서
// 코드의 캡슐화를 증가시킨다. 또한, 내부 클래스에서 외부 클래스의
// 멤버에 손쉽게 접근할 수 있게 된다.
btn[i].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
lblColor.setBackground(color[idx]);
// idx = 1000; // 대입안됨.오류.int idx = i; 문을 쓸 때
// 변경시도해보나 변경이 안되니 int idx = i;
// 에서 idx 는 effectively final 이다.
// int ef = i; 라고 final 없이 선언했기에
// final 이 붙지 않은 변수라 값을 변경할 수 있어야 하지만
// 변경이 안된다. 그럴 때 그 변수를 effectively final
// 이라고 한다. 아랫줄처럼 ef 에 새 값을 대입했으나 final
// 키워드가 없음에도 불구하고 변경이 안된다.
// ef = 1000; // 오류발생한다.
// ef++; // 오류발생한다.
// ef = ef + 1; // 오류발생한다.
// System.out.println(ef); // 정상 작동한다.
// yPos 변수 또한 외부 블락에 있는 외부변수라서 접근이 가능하다고 생각하였다.
// 하지만 오류가 뜬다. 왜냐하면 외부에 있는 변수이지만 yPos 은 지역변수이기도 하다.
// 그래서 final 이나 effectively final 이어야만 접근이 가능하다.
// yPos 변수는 외부에 있는 변수이면서 지역 변수 (외부 지역 변수)
// System.out.println(yPos); // 오류:Local variable yPos defined in an enclosing scope must be final or effectively final
//
// 밑에 두줄에서는 btnTitle 외부변수가 전역변수(객체변수)라서
// 정상작동하는 것이다.
// System.out.println(btnTitle[0]); // 정상작동 오류안난다.
// System.out.println(btnTitle); // 정상작동 오류안난다.
// btnTitle[0] = "빨강색";
// btn[0].setText(btnTitle[0]);
// 밑줄과 같이 쓰면 오류가 난다.
// lblColor.setBackground(color[i]);
// 바로 윗줄에서 오류가 나는 내용은 다음과 같다.
// Local variable i defined in an enclosing scope must be final or effectively final
// 요 actionPerformed(ActionEvent e) 메서드를
// 감싸고 있는 익명클래스를 setDesign() 메서드의 반복문(for)문
// 안에서 생성되게 하지 말고
// setDesign() 메서드 바로 내부에서 생성되게 한다고 생각
// 해보자. 그러면 Inner Local class 가 되서 지역변수처럼
// 쓸 수 있다.
// 그런데 setDesign() 내부에 for 문을 작성하여 계속해서
// 변하는 변수 i 를 for 문 내부에서만 쓸 수 있는 변수로 선언
// 했다고 하자. 그 때 익명클래스 대신 작성한 setDesign() 메서드
// 바로 내부에서 생성한 Inner Local class 의
// actionPerformed(ActionEvent e) 메서드 내부에서
// for 문 안의 지역변수를 가져다 쓸 수가 없기 때문에
// 상수는 아니지만 오로지 단 한 번만 초기화 할 수 있는 final
// 키워드를 붙여서 for 문 밖에서도 쓸 수 있도록 한 듯 하다..
// 그리고 i 값이 변하는 값이라서 final int idx = i;
// 라는 문을 만날 때 마다 새롭게 다시 idx 변수를 재정의 하는
// 것인 듯 하다.. for 문 바깥에다 final int idx = i;
// 를 하면 단 한번만 초기화를 할 수 있으므로 고정되기 때문이다.
// 익명 클래스는 이름이 없다는 점을 빼면 지역 클래스와 유사하다.
// 지역 클래스를 한 번만 사용하면 되는 경우라면
// 익명 클래스를 사용하자.
// 익명 클래스는 자신을 포함하는 외부 클래스(enclosing class)
// 의 멤버에 접근할 수 있다.
// 외부 클래스의 멤버에 접근할 수 있지만
// 만약 그 외부에 있는 변수가 지역 변수일 경우 final 혹은 effectively final 인
// 경우에만 접근이 가능하다.
// (final 또는 유사 final(effectively final) 로 선언된 변수에만 접근할 수 있다.)
// effectively final 이란
// 초기화 이후에 그 값이 절대로 변화되지 않는 변수인데,
// 파이널이 아닌(non-final) 지역 변수의 값이나
// 메서드의 파라메터 값이
// 초기화 이후에 절대로 변화되지 않는다.
// effectively final 은 Java 8 에 추가된
// syntactic sugar(구문 설탕, 문법적 설탕) 의 일종이다.
// 초기화된 이후 값이 한 번도 변경되지 않았다면 effectively final 이다.
// effectively final 변수는 final 키워드가 없지만 final 키워드를
// 붙인 것과 동일하게 컴파일러에서 처리한다. 의미상 final 하다고
// 이해해도 괜찮을 것 같다.
// effectively final 은 익명 클래스나 람다식에서 코드를 좀 더 간결케
// 만든다.
// Java 7 에서는 익명 클래스가
// 외부지역변수가 final 인 경우에만 접근이 가능했기에 항상 final 키워드를
// 추가해야만 했었다.
// Java 8 에서는 effectively final 인 경우에도
// 접근이 가능하게 바뀌어서
// 조건을 만족한다면 final 키워드를 생략할 수 있다.
// 람다에서 사용되는 지역 변수는 왜 final 혹은
// effectively final 인가?
// Capturing lambda 키워드
// Capturing lambda 와 Non-Capturing lambda
// 람다식에는 2가지 타입이 있다.
// 첫번째 Capturing lambda 는
// 외부 변수를 이용하는 람다식이다.
// 외부 변수는 지역변수, 인스턴스 변수, 클래스 변수 모두를 포함한다.
// 두번째는 Non-Capturing lambda
// 외부 변수를 이용하지 않는 람다식이다.
// Capturing lambda 는 다시
// local capturing lambda 와
// non-local capturing lambda 로 구분한다.
// local 과 non-local 로 구분하는 이유는
// 지역 변수가 가지는 특징 때문에 내부 동작 방식이 다르기 때문
// 이라고 한다.
// Local Capturing lambda
// 외부 변수로 지역 변수를 이용하는 람다식을 의미한다.
// 특징으로는
// 1. 람다식에서 사용되는 외부 지역 변수는 복사본이다.
// 2. final 혹은 effectively final 인 지역 변수만
// 람다식에서 사용할 수 있다.
// 3. 복사된 지역 변수 값은 람다식 내부에서도 변경할 수 없다.
// 즉, final 변수로 다뤄야 한다.
// 1. 람다식에서 사용되는 외부 지역변수는 복사본이다.
// 람다식에서는 외부 지역변수를 그대로 사용하지 못하고
// 복사본을 사용하는 이유는
// -> 지역 변수는 스택 영역에 생성된다. 따라서 지역 변수가
// 선언된 block 이 끝나면 스택에서 제거된다.
// -> 메서드 내 지역 변수를 참조하는 람다식을 리턴하는
// 메서드가 있을 경우, 메서드 block 이 끝나면
// 지역 변수가 스택에서 제거 되므로 추후에 람다식이
// 수행될 때 참조할 수 없다고 한다. 이해가 안되서
// 시간이 걸렸다. 람다식을 리턴한다는 예를 본적이
// 없다.
// -> 지역 변수를 관리하는 Thread 와 람다식이 실행되는
// Thread 가 다를 수 있다고 한다.
// -> 스택은 각 Thread 의 고유의 공간이고, Thread
// 끼리 공유되지 않기 때문에 마찬가지로 람다식이
// 수행될 때 값을 참조할 수 없다고 한다.
// 요런 이유 때문에 람다식에서는 외부 지역 변수를 직접 참조하지
// 않고 복사본을 전달받아 사용하게 된다.
// 2. final 혹은 effectively final 인 지역 변수만
// 람다식에서 사용할 수 있다.
// 만약 참조하고자 하는 지역 변수가 final 혹은
// effectively final 이 아닐 경우,
// 즉, 변경이 가능할 경우 문제가 발생할 수 있다.
// 예시
// public void executelocalVariableInMultiThread() {
// boolean shouldRun = true;
// executor.execute(() -> {
// while (shouldRun) {
// // 작업 하기
// }
// });
//
// shouldRun = false;
// }
// 람다식이 어떤 thread 에서 수행될지는 미리 알 수 없다. 이 말은
// 곧 외부 지역 변수를 다루는 thread 와 람다식이 수행되는 thread
// 가 다를 수 있다는 의미이다.
// 지역 변숫 값(shouldRun) 을 제어하는 thread A,
// 람다식을 수행하는 thread B 가 있다고 가정하자.
// 문제가 생긴다.
// thread B 의 shouldRun 값이 가장 최신 값으로 복사되어
// 전달 됐는지 확신할 수 없다는 것이다.
// 왜냐하면 shouldRun 은 변경이 가능한 지역 변수이고,
// 지역 변수를 thread 간에 sync(동기화) 해주는 것은
// 불가능 하기 때문이다.
// (지역 변수는 thread A 의 스택 영역에 존재하기 때문에
// 다른 thread 에서 접근이 불가능하다. volatile 과
// 같은 키워드가 로컬(지역)변수에서 사용될 수 없는 이유도
// 이와 같다.)
// 값이 보장되지 않는다면 매번 다른 결과가 도출 될 수 있다.
// thread 는 시간 단위로 지나면서 각각의 thread 가
// 타이밍을 맞추는(동기화하는 것) 것이 어렵기 때문인 듯 하다..
// 그래서 똑같은 값이 보장이 안되면 여기서는 a값이라서 멈추고
// 저기서는 a가 아닌 b값이라서 계속 실행되어 출력을 더 많이
// 하고 어디서는 그 타이밍이 달라서 출력양이 더 적고 멈추는 등
// 의 문제가 생긴다.
// 예측할 수 없는 코드가 의미가 있을까? 이러한 이유로 인해
// 외부 지역 변수는 전달되는 복사본이 변경 되지 않은
// 최신 값 임을 보장하기 위해 final 혹은
// effectively final 이어야 한다.
// 3. 복사된 지역 변수 값은 람다식 내부에서도 변경할 수 없다.
// 즉, final 변수로 다뤄야 한다.
// 처음에는 이미 복사가 된 값이므로 변경해도 문제가
// 없는 것 아닌가? 라고 생각한다. 하지만 그렇지 않다.
// 복사 될 값의 변조를 막아 최신 값임을 보장하기 위해
// final 제약을 걸었는데 람다식 내부에서 변경이 가능할 경우
// 다시 제자리로 돌아오게 된 격이라고 한다.
// 또한 컴파일 된 람다식은 static 메서드 형태로 변경이
// 된다고 한다. 그런데 이 때 복사된 값이 파라메터로 전달되므로
// 마찬가지로 스택영역에 존재하기 때문에 sync 를 해주는 것도
// 불가능하다. 따라서 람다식 내부에서도 값이 변경되어서는 안된다.
// 컴파일러 레벨에서 앞, 뒤로 final 제약을 걸어줌으로써
// 멀티 스레드 환경에서 대응하기 어려운 문제를 미연에 방지
// 한다고 한다.
// Non-local capturing lambda
// 외부 변수로 인스턴스 변수 혹은 클래스 변수를 이용하는 람다식이다.
// local capturing lambda 와 다르게 final 제약이
// 없고, 외부 변수 값도 복사하지 않는다.
// 그 이유는 인스턴스 변수나 클래스 변수를 저장하고 있는
// 메모리 영역은 공통 영역이고
// 값이 메모리에서 바로 회수되지 않기 때문에
// 람다식에서 바로 참조가 가능하다고 한다.
// 따라서 복사 과정이 불필요하고 참조 시 최신 값 임을 보장할 수 있다.
// 다만 멀티 스레드 환경일 경우
// volatile, synchronized 등을 이용하여
// sync 를 맞춰주는 작업을 잊어서는 안된다고 한다.
// volatile 키워드는 Java 변수를 메인메모리에 저장하겠다는
// 명시를 하는 것이다. 매번 변수의 값을 읽을 때마다
// CPU cache에 저장된 값이 아닌 메인 메모리에서 읽는 것이다.
// 또한 변수 값을 쓸때마다 메인메모리에까지 작성하는 것이다.
// volatile 변수를 사용하고 있지 않은 멀티스레드 어플리케이션
// 에서는 task를 수행하는 동안 성능 향상을 위해 메인메모리에서
// 읽은 변수 값을 CPU cache에 저장하게 된다.
// 만약에 멀티스레드 환경에서 thread가 변수 값을 읽어올때
// 각각의 CPU cache에 저장된 값이 다르기 때문에 변수 값
// 불일치 문제가 발생하게 된다.
// 예를 들어
// public class SharedObject {
// public int counter = 0;
// }
// SharedObject를 공유하는 두 개의 thread 가 있다.
// Thread-1 은 counter 값을 더하고 읽는 연산을 한다(읽기와쓰기)
// Thread-2 는 counter 값을 읽기만 한다.(Only Read)
// Thread-1 은 counter 값을 증가시키고 있지만 CPU cache에만
// 반영되어있고 실제로 메인메모리에는 반영이 되지 않았다. 그렇기
// 때문에 Thread-2 는 counter 값을 계속 읽어오지만
// 0 을 가져오는 문제가 발생한다.
// 어떻게 해결할까요?
// volatile 키워드를 추가하게 되면 메인메모리에 저장하고
// 읽어오기 때문에 변수 값 불일치 문제를 해결 할 수 있다.
// public class SharedObject {
// public volatile int counter = 0;
// }
// 언제 volatile 이 적합할까요?
// 멀티 스레드 환경에서 하나의 thread 만 읽기와쓰기를 하고
// 나머지 thread 가 오직 읽기만 하는 상황에서 가장 최신의
// 값을 보장한다.
// 람다식에서 외부 지역 변수를 이용할 경우
// final 혹은 effectively final 이어야 하는 이유는
// 지역 변수가 스택에 저장되기 때문에
// 람다식에서 값을 바로 참조하는 것에 제약이 있어 복사된 값을
// 이용하게 되는데,
// 이때 멀티 스레드 환경에서 복사될/복사된 값이 변경 가능 할
// 경우 이로 인한 동시성 문제를 대응할 수 없기 때문이라고 한다.
}
});
}
// 라벨 추가
lblColor = new JLabel();
lblColor.setBounds(150, 20, 210, 270);
lblColor.setBorder(new LineBorder(Color.DARK_GRAY));
lblColor.setOpaque(true);
this.add(lblColor);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Ex7ButtonArray("버튼배열");
}
}
익명 내부 클래스에서 외부 지역 변수를 쓸 때 final 이나 effectively final 변수를 써야 한다.
메모리 저장 내용과 다른 내용이 무척 많아서 찾는데 시간이 걸렸다.
[자바] 람다(lambda)가 이끌어 갈 모던 JAVA(2)
아래 포스트는 학습용도로 네이버 개발자 센터 기술 포스팅에서 가져온 내용입니다. 원본자료는 참고링크(https://d2.naver.com/helloworld/4911107)를 따라가셔서 확인 바랍니다. 그 외에 람다 대수(lambda c
12bme.tistory.com
https://vagabond95.me/posts/lambda-with-final/
[Java] lambda 와 effectively final - 기록은 기억을 지배한다
자바 8에서 추가된 람다식에는 다음과 같은 규칙이 존재한다. 람다식은 외부 block 에 있는 변수에 접근할 수 있다. 외부에 있는 변수가 지역 변수 일 경우 final 혹은 effectively final 인 경우에만 접
vagabond95.me
'Java' 카테고리의 다른 글
Java printStackTrace() (1) | 2023.02.01 |
---|---|
Java LineBorder(Color color) (0) | 2023.02.01 |
Java setBounds(int x, int y, int width, int height) (0) | 2023.02.01 |
Java Icon (0) | 2023.02.01 |
Java javax.swing.JButton (0) | 2023.02.01 |