기타 등등 (계속 추가)
당연히 알고있다 생각하지만 흐릿하거나 모르고 지나칠 수 있는 상식들을 적어 두었다.
000001. static
인스턴스를 생성하면 서로 다른 값을 유지하기 때문에 경우에 따라서 각 인스턴스들이 공통적으로 같은 값이 유지되어야 하는 경우 static을 붙인다.
static이 붙은 멤버변수(클래스 변수)는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문에 인스턴스를 생성하지 않아도 사용할 수 있다.
static이 붙은 메서드에서는 인스턴스 변수를 사용할 수 없다. 반대는 가능하다.
메서드의 작업 중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없지만 만약 필요로 하지 않는다면 static을 붙여서 메서드 호출시간을 짧게해줄 수가 있다.
static 영역은 GC가 관리하지 않고 클래스들이 모여있고 new 를 통해서 객체가 생성되어 모여져있는 heap영역에서는 GC가 관리한다.
바깥의 class는 static class가 될 수 없는 것이 아니라 붙일 이유가 없을 뿐이다. 메모리에 바로 올라가기 때문이다.
static class가 nonStatic class를 상속받을 수 없다.
000002. 접근제어자
- public : 모든 클래스들이 접근 가능하다.
- protected : 같은 패키지 내에 있거나 또는 상속받은 경우에 접근 가능하다.
- default (package-private) : 같은 패키지 내에 있을 때에 접근 가능하다.
- private : 해당 클래스 내에서만 접근 가능하다.
- public > protected > default > private
- public으로 선언된 클래스가 파일 내에 있으면 해당 소스코드의 파일 이름은 이것과 동일해야 한다.
public class Test{
public static void main(String[] args){}
}
public class Test2{} //오류 발생. public 제거 해줘야 한다.
- 부모 클래스의 메서드를 자식 클래스에서 오버라이딩 할 때에 접근 제어자를 변경할 수는 있지만 접근권한이 확장되는 경우만 가능하다. ex) 부모 protected면 자식에서 protected, public 만 가능하다.
000003. BigDecimal
BigDecimal은 문자열이다. 따라서 계산하려면 따로 메서드를 사용해야 한다.
BigDecimal bigNumber1 = new BigDecimal("100000.12345");
BigDecimal bigNumber2 = new BigDecimal("10000");
System.out.println("덧셈(+) :" +bigNumber1.add(bigNumber2));
System.out.println("뺄셈(-) :" +bigNumber1.subtract(bigNumber2));
System.out.println("곱셈(*) :" +bigNumber1.multiply(bigNumber2));
System.out.println("나눗셈(/) :" +bigNumber1.divide(bigNumber2));
System.out.println("나머지(%) :" +bigNumber1.remainder(bigNumber2));
비교 또한 compareTo
를 이용해서 비교한다.
000004. this 와 super
public class Parent {
public void say() {
System.out.println("parent said");
}
public void charge(){
say();
}
}
public class Child extends Parent {
@Override
public void say() {
System.out.println("child said");
}
@Override
public void charge() {
super.charge();
}
}
public class test {
public static void main(String[] args) {
Child child = new Child();
child.charge();
}
}
// child said 출력
Child의 charge 메서드를 호출하면 해당 메서드를 탐색하기위해 self 참조가 가리키는 클래스부터 탐색을 하게된다. self 란 자바에서 말하는 this를 뜻한다. self 참조가 Child를 가리키고 있고 상속계층의 역방향 순서로 탐색을 하게되는데 charge 라는 시그니처가 Child에 존재하기 때문에 해당 메서드를 실행하게 된다.
super.charge()를 호출하는데 super는 흔히 부모 클래스라고 생각하기 쉽지만 이건 정확히 부모 클래스부터 메세지 탐색을 하라는 의미이다. 즉 만약 Parent가 GrandParent라는 클래스를 상속받고 있고 super.grandCharge() 라는 GrandParent 클래스에 존재하는 메서드일 경우 super는 Parent에서부터 탐색을 시작하게 된다. Parent에서 찾지 못하였으니 GrandParent로 이동해서 해당 메세지를 찾는 것이다.
다시 본론으로 돌아와서 그렇게 Parent의 charge 메서드를 실행하게 되는데 say() 메서드를 또 다시 호출한다. 이 메서드는 Parent 안에 say 메서드가 있으니 해당 메서드를 호출하는 것이 아니라 self 참조한테 해당 메세지를 전달하게 된다. 이것을 self 전송이라고 부른다.
그렇게 self 참조에서 부터 다시 say를 찾게 돼서 Child의 say 메서드가 호출이 되는 것이다.
000005 Java split의 2번째 인수
String str = ",,a,b,,c, ,d,e,,";
String[] limitDefault = str.split(",");
String[] limitZero = str.split(",", 0);
String[] limitMinusOne = str.split(",", -1);
String[] limitTwo = str.split(",", 2);
String[] limitFive = str.split(",", 5);
Arrays.stream(limitDefault).forEach(System.out::println);
System.out.println("=============");
Arrays.stream(limitZero).forEach(System.out::println);
System.out.println("=============");
Arrays.stream(limitMinusOne).forEach(System.out::println);
System.out.println("=============");
Arrays.stream(limitTwo).forEach(System.out::println);
System.out.println("=============");
Arrays.stream(limitFive).forEach(System.out::println);
System.out.println("=============");
흔히 spilt을 자주사용한다. StringTokenizer와는 다르게 split은 공백의 데이터도 나타내준다. 뒤의 인수는 파라미터 int limit의 자리이다.
기본 Default 자체가 0이다. 해당 limit 만큼의 토큰만 잘라주고 나머지는 그대로 붙여서 나온다.
limitDefault와 limitZero의 결과값은 {"", "", "a","b","","c","","d","e"} 이다. 0일 경우 뒤의 공백들은 모두 제외하고 앞에서부터 차례대로 토큰에 넣어준다. 뒤의 공백까지 다 넣고 싶다면 -1을 넣어주면 된다. 밑의 결과는 limitMinusOne은 limitZero의 결과에서 뒤에 공백 데이터가 2개가 더 들어간 형식이다.
그렇다면 limitTwo와 Five는?? {"", ",a,b,,c, ,d,e,,"}, {"", "", "a", "b", ",c, ,d,e,,"} 가 된다.
000006 Stream.forEach VS for-loop
메서드 체이닝을 이용하는 과정에서의 forEach
가 아닌 그저 n번 반복을 위해 forEach
를 사용할 때가 있다. Stream.forEach
은 for-loop
의 경우보다 오버헤드가 심각하게 발생해서 CPU 싸이클 낭비가 무시하지 못할 수준이 될 수가 있다.
ArrayList, for-loop : 6.55 ms
ArrayList, seq. stream: 8.33 ms
int-array, for-loop : 0.36 ms
int-array, seq. stream: 5.35 ms
원시 데이터일 경우에는 특히 더 for-loop
를 사용해야 한다는 것을 벤치마크 결과로 알 수가 있다. 특히 Collection보다 배열일 경우 더욱이 말이다.
List<Integer> list = Arrays.asList(1, 2, 3);
// Old school
for (Integer i : list)
for (int j = 0; j < i; j++)
System.out.println(i / j);
// "Modern"
list.forEach(i -> {
IntStream.range(0, i).forEach(j -> {
System.out.println(i / j);
});
});
/* Old school
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:13)
Modern
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.lambda$1(Test.java:18)
at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
at Test.lambda$0(Test.java:17)
at java.util.Arrays$ArrayList.forEach(Arrays.java:3880)
at Test.main(Test.java:16)
내부 반복을 사용하면 내부적으로 JVM과 라이브러리가 할 일이 많아져 복잡한 호출구조를 띄게 되어 오류 발생 시 트레이스 위와 같이 이해하기 힘들게 나오게 된다.
000007 e.printStackTrace() 대신 logger.error를 사용해야 하는 이유
e.printStackTrace()
를 사용해도 자동으로 로그파일에 저장되는 것이 아니다. 따라서 logger.error()
를 통해 에러를 출력해주어야 한다. 그리고 loggingframework를 이용하면 로그를 다른 장소에 동시에 남길 수 있으며, 중요도에 따라 로그를 필터링 할 수도 있으며 코드 변환없이 로그 포맷에 영향을 줄 수 있다.
000008 Objects.requireNonNull()
Objects.requireNonNull()은 다음과 같이 3개의 오버로딩이 되어있다.
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier == null ?
null : messageSupplier.get());
return obj;
}
기본적으로 null이 아니라면 해당 타입을 그대로 반환해주지만, null 일 경우 아무 메세지도 안날리거나, String으로 설정을 해주던가 또는 Supplier를 선언해줄 수 있다.
static class Fruit {
String name;
public Fruit (String name) {
this.name = name;
}
}
public static void main(String[] args) {
Fruit apple = new Fruit("apple");
System.out.println(Objects.requireNonNull(apple));
apple = null;
System.out.println(Objects.requireNonNull(apple, "널이랍니다.")); //널이랍니다. 메세지 출력
System.out.println(Objects.requireNonNull(apple, () -> "널이랍니다."));//상동
}
000009 this()를 통한 생성자의 호출은 오직 생성자에서만 가능하다
public class Chair implements Furniture{
String name = "chair";
@Override
public Furniture changeFurniture() {
return this(); //컴파일 오류
return new Chair(); //통과
}
}
생성자 외의 메서드에서 this()
를 통한 생성자의 호출은 불가능하다.
public class Chair implements Furniture{
String name;
public Chair() {
this("chair");
}
public Chair(String name) {
this.name = name;
}
@Override
public Furniture changeFurniture() {
return new Chair();
}
}
생성자 내에서의 this()
를 통한 생성자의 호출은 가능하다.
000010 map.put과 map.replace의 차이점
map.put(key, value)
는 해당 key 값에 value를 추가한다. 이미 존재하는 key 라면 value를 덮어씌운다.map.replace(key, value)
는 해당 key 값을 value로 갈아치운다.
그러면 그냥 put
을 사용하면 되는데 왜 굳이 replace
라는 메서드가 존재하는 것일까 ??
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
replace
의 세부내용은 다음과 같다. null이 아닐 때만 put
을 실행을 하고 있다.
Map<String, Integer> map = new HashMap<>(Map.of("bepoz", 100));
map.replace("giraffe", 150);
System.out.println(map);
map.put("giraffe", 150);
System.out.println(map);
/*
{bepoz=100}
{bepoz=100, giraffe=150}
put
은 해당 key 값이 없으면 그대로 추가해버리지만, replace
는 해당 key 값이 존재할 때만 바꿔주므로 조금 더 안정성이 있다는 것을 확인할 수가 있다.
000011 프레임워크와 라이브러리
라이브러리는 도구다. 사용자가 라이브러리를 호출하고 그것의 결과출력이나 수행과정을 사용자가 코드를 통해 제어할 수 있다.
프레임워크는 제어관계가 역전된다. 관심사 또한 분리된다. 라이브러리와 달리 프로그래밍을 할 규칙이 정해져 있다. 메뉴얼 등이 정해져있는 틀이라고 생각하면 쉬울 것 같다.
000012 restassured의 response.body().as(객체)
response.jsonPath.get( keyValue );
가 아닌 response.body().as( Object );
형식으로 객체를 받아올 수 있다.
static 변수에 @Value 사용하는 방법
@Component
public class EncryptionUtil {
public static String key;
@Value("${enc.key}"})
public void setKey(String value) {
key = value;
}
}
static 변수 위에 바로 @Value
를 사용하는 것은 불가능하다. 아마 jvm과 스프링 컨텍스트 구동 시점이 달라서 그런 것이라고 본다. 이 때에는 따로 setter를 이용해서 주입해줄 수가 있다.
'공부 기록들' 카테고리의 다른 글
알고리즘 모아보기 (0) | 2021.09.05 |
---|---|
개인적인 알고리즘 문제 풀이 팁 기록 (0) | 2020.11.12 |
웹 캐싱 기법 (0) | 2020.10.25 |
디스크 관리 (0) | 2020.10.25 |
가상 메모리 (0) | 2020.10.25 |