기본 콘텐츠로 건너뛰기

Chapter12~

 






제네릭<>

제네릭 타입(class<T>, interface<T>)

    public class 클래스명<T> {...}
    public interface 인터페이스명<T> {...}

제네릭의 장점 - 컴파일시 강한 타입 체크로 실헹 시 에러를 사전에 방지. 타입변환(casting)을 제거

public class Box {
	private Object object;
	public void set(Object object) {
		this.object = object;
	}
	public Object get() {
		return object;
	}
}
public class BoxExample {

	public static void main(String[] args) {
		Box box = new Box();
		box.set("홍길동");
		String name = (String) box.get();
		System.out.println(name);
		box.set(555);
		int number = (int) box.get();
		System.out.println(number);
	}
}

타입 변환을 사용해야함

public class Box<T> {
	private T t;
	public void set(T t) {
		this.t = t;
	}
	public T get() {
		return t;
	}
}
public class BoxExample {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Box<String> box1 = new Box<String>();
		box1.set("홍길동");
		String name = box1.get();
		System.out.println(name);
		Box<Integer> box2 = new Box<Integer>();
		box2.set(555);
		int number = box2.get();
		System.out.println(number);
	}
}

타입 변환 없이 사용

멀티 타입 파라미터(class<K, V, ....>, interface<K, V, ....>)

public class Product<T, M> {
	private T kind;
	private M model;
	
	public T getKind() {
		return kind;
	}
	public void setKind(T kind) {
		this.kind = kind;
	}
	public M getModel() {
		return model;
	}
	public void setModel(M model) {
		this.model = model;
	}
}
public class ProductExample {
	public static void main(String[] args) {
		Product<Tv, String> product1 = new Product<Tv, String>();
		//자바 7부터
		Product<Tv, String> product2 = new Product<>();
		product1.setKind(new Tv());
		product1.setModel("스마트Tv");
		Tv tv = product1.getKind();
		String tvModel = product1.getModel();
	}
}

자바 7부터는 축약 사용 가능

제네릭 메소드(<T,R> R method(T t)) - 매개 변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드, 리턴 타입 앞에 <> 기호를 추가하고 타입 파라미터를 기술한 다음, 리턴 타입과 매개 타입으로 타입 파라미터를 사용

public <타입파라미터, ...> 리턴타입 메소드명(매개변수, ...){ ...}
public <T> Box<T> boxing(T t) {...}

제네릭 메소드 호출 방법

리턴타입 변수 = <구체적타입> 메소드명(매개값);     //명시적으로 구체적 타입을 지정
리턴타입 변수 = 메소드명(매개값);               //매개값을 보고 구체적 타입을 추정
Box<Integer> box = <Integer>boxing(100);//타입 파라미터를 명시적으로 Integer로 지정
Box<Integer> box = boxing(100);         //타입 파라미터를 Integer로 추정


public class Util2 {
	public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
		boolean keyCompare = p1.getKey().equals(p2.getKey());
		boolean valueCompare = p1.getValue().equals(p2.getValue());
		return keyCompare && valueCompare;
	}
}
public class Pair<K, V> {
	private K key;
	private V value;
	
	public Pair(K key, V value) {
		this.key = key;
		this.value = value;
	}
	
	public void setKey(K key) {
		this.key = key;
	}
	public void setValue(V value) {
		this.value = value;
	}
	
	public K getKey() {
		return key;
	}
	public V getValue() {
		return value;
	}
}
public class CompareMethodExample {

	public static void main(String[] args) {
		Pair<Integer, String> p1 = new Pair<Integer, String>(1, "사과");
		Pair<Integer, String> p2 = new Pair<Integer, String>(1, "사과");
		//구체적인 타입을 명시적으로 지
		boolean result1 = Util2.<Integer, String>compare(p1, p2);
		if(result1) {
			System.out.println("논리적으로 동등한 객체입니다.");
		}else {
			System.out.println("논리적으로 동등하지 않는 객체입니다.");
		}
		
		Pair<String, String> p3 = new Pair<>("user1", "홍길동");
		Pair<String, String> p4 = new Pair<>("user2", "홍길동");
		//구체적인 타입을 추정
		boolean result2 = Util2.compare(p3, p4);
		if(result2) {
			System.out.println("논리적으로 동등한 객체입니다.");
		}else {
			System.out.println("논리적으로 동등하지 않는 객체입니다.");
		}
	}
}

제한된 타입 파라미터(<T extends 최상위타입>) - 구체적인 타입을 제한할 때 ex) 숫자 연산 메소드는 매개값으로 Number 또는 하위 클래스 타입(Byte, Short, Integer, Long, Double)의 인스턴스만 가져야 할때 extends 키워드를 붙이고 상위 타입을 명시하고 인터페이스도 가능한데 implements를 안쓰고 extends를 사용

public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) { ...}

메소드의 중괄호 {} 안에서 타입 파라미터 변수로 사용한 것은 상위 타입의 멤버(필드, 메소드)로 제한

public class Util3 {
	public static <T extends Number> int compare(T t1, T t2) {
		double v1 = t1.doubleValue();	//Number의 doubleValue() 메소드 사
		double v2 = t2.doubleValue();
		return Double.compare(v1, v2);
	}
}
public class BoundedTypeParameterExample {
	public static void main(String[] args) {
		//String str = Util3.compare("a", "b");Number 타입이 아님
		//int -> Integer
		int result1 = Util3.compare(10, 20);
		System.out.println("result1 : " + result1);
		//double -> Double
		int result2 = Util3.compare(4.5, 3);
		System.out.println("result2 : " + result2);
	}
}

와일드카드 타입(<?>, <? extends ...>, <? super ...>)  

            - 제네릭타입<?> : 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있음(제한 없음)

            - 제네릭타입<? extends 상위타입> : 타입 파라미터를 대치하는 구체적인 타입으로 상위타입이나 하위 타입만 올 수 있음(상위 클래스 제한)

            - 제네릭타입<? super 하위타입> : 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입이 옴(하위 클래스 제한)



public class Course<T> {
	
	private String name;
	private T[] students;
	
	public Course(String name, int capacity) {
		this.name = name;
		students = (T[]) (new Object[capacity]);
		System.out.println(Arrays.toString(students)+"끄억"+capacity);
	}
	
	public String getName() {
		return name;
	}
	public T[] getStudents() {
		return students;
	}
	//배열에 비어있는 부분을 찾아서 수강생을 추가
	public void add(T t) {
		for(int i=0; i<students.length; i++) {
			students[i] = t;
			break;
 		}
	}
}
public class WildCardExample {
	public static void main(String[] args) {
		Course<Person> personCourse = new Course<Person>("일반인과정", 5);
		personCourse.add(new Person("일반인"));
		personCourse.add(new Worker("직장인"));
		personCourse.add(new Student("학생"));
		personCourse.add(new HighStudent("고등학생"));
		
		Course<Worker> workerCourse = new Course<Worker>("직장인과정", 5);
		workerCourse.add(new Worker("직장인"));
		Course<Student> studentCourse = new Course<Student>("학생과정", 5);
		studentCourse.add(new Student("학생"));
		studentCourse.add(new HighStudent("고등학생"));
		Course<HighStudent> highStudentCourse = new Course<HighStudent>("고등학생과정", 5);
		highStudentCourse.add(new HighStudent("고등학생"));
		//모든 과정 등록 가
		registerCourse(personCourse);
		registerCourse(workerCourse);
		registerCourse(studentCourse);
		registerCourse(highStudentCourse);
		
		//registerCourseStudent(personCourse); 학생이 아니므로
		//registerCourseStudent(workerCourse);
		registerCourseStudent(studentCourse);
		registerCourseStudent(highStudentCourse);
		
		registerCourseWorker(personCourse);
		registerCourseWorker(workerCourse);
		//registerCourseWorker(studentCourse; 학생 이므로
		//registerCourseWorker(highStudentCourse);
		
		
	}
	//모든 과정
	public static void registerCourse(Course<?> course) {
		System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
	}
	//학생 과정
	public static void registerCourseStudent(Course<? extends Student> course) {
		System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
	}
	//직장인과 일반인 과정
	public static void registerCourseWorker(Course<? super Worker> course) {
		System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
	}
}

제네릭 타입의 상속과 인터페이스 구현도 가능하다 


람다식

람다식 기본 문법 - (타입 매개변수, ...) -> { 실행문; ...}

(int a) -> {System.out.println(a);}
//매개 변수 타입은 런타임 시에 대입되는 값에 따라 자동으로 인식되기에 타입을 제거
(a) -> {System.out.println(a);}
//하나의 매개 변수만 있으면 괄호()를 생략, 하나의 실행문만 있다면 중괄호{}도 생략
a -> System.out.println(a)
//매개 변수가 없다면 람다식에서 매개 변수 자리가 없어지므로 ()를 반드시 사용
() -> {실행문; ...}
//중괄호{}를 실행하고 결과값을 리턴해야 할때는
(x,y) -> {return x+y;};
//중괄호{}에 return문만 있을 경우, return문 생략
(x,y) -> x+y

함수적 인터페이스(@FunctionalInterface) - 두 개 이상의 추상 메소드가 선언된 인퍼테이스가 아닌 하나의 추상 메소드가 선언된 인터페이스, @FunctionalInterface어노테이션을 붙이면 두 개 이상의 추상 메소드가 선언되면 오류가뜸

클래스 멤버와 로컬 변수 사용 - 클래스의 멤버(필드와 메소드)는 제약 사항없이 사용 가능하지만, 로컬 변수는 제약 사항이 따름

        클래스의 멤버 사용 - 익명 객체 내부에서 this는 익명 객체의 참조지만, 람다식에서 this는 람다식을 실행한 객체의 참조

        로컬 변수 사용 - 바깥 클래스의 필드나 메소드는 제한 없이 사용 가능하지만, 메소드의 매개 변수 또는 로컬 변수를 사용하면 이 두 변수는 final 특성을 가져야 함, 따라서 매개 변수 또는 로컬 변수를 람다식에서 읽는 것은 허용 되지만, 람다식 내부 또는 외부에서 변경할 수 없음

표준 API의 함수적 인터페이스 - 함수적 인터페이스는 java.util.function표준 API 패키지로 제공함

            Consumer 함수적 인터페이스 - 리턴값이 없는 accept() 메소드를 가짐
            Supplier 함수적 인터페이스 - 매개 변수가 없고 리턴값이 있는 getXXX() 메소드를 가짐

            Function 함수적 인터페이스 - 매개값과 리턴값이 있는 applyXXX() 메소드를 가짐

            Operator 함수적 인터페이스 - Function과 동일하게 매개 변수와 리턴값이 있는 applyXXX()메소드를 가지지만, 이 메소드들은 매개값을 이용해서 연산을 수행 후 동일한 타입으로 리턴값을 제공

            Predicate 함수적 인터페이스 - 매개 변수와 boolean 리턴값이 있는 testXXX() 메소드를 가짐

    

andThen()과 compose() 디폴트 메소드 - Consumer, Function, Operator 종류의 함수적 인터페이스는 andThen()과 compose() 디폴트 메소드가 있음, 첫 번째 처리 결과를 두 번째 매개값으로 제공해서 최종 결과값을 얻을 때 사용
            andThen() - 인터페이스A.andThen(인터페이스B) : 인터페이스A부터 처리하고 결과를 인터페이스B의 매개값으로 제공해서 인터페이B는 최종 결과를 리턴
            compose() - 인터페이스A.compose(인터페이스B) : andThen()과 순서가 반대
and(), or(), negate() 디폴트 메소드 - Predicate 종류의 함수적 인터페이스는 and(), or(), negate() 디폴트 메소드를 가짐(&&, ||, !와 같음)
isEqual() 정적 메소드 - test() 매개값인 sourceObject와 isEqual()의 매개값인 targetObject를 java.util.Objects 클래스의 equals()의 매개값으로 제공하고, Objects.equals(sourceObject, targetObject)의 리턴값을 얻어 새로운 Predicate<T>를 생성함
Predicate<Object> predicate = Predicate.isEqual(targetObject);
boolean result = predicate.test(sourceObject);
minBy(), maxBy() 정적 메소드 - BinaryOperator<T> 함수적 인터페이스는 매개값으로 제공되는 Comparator를 이용해서 최대 T와 최소 T를 얻는 BinaryOperator<T>를 리턴
메소드 참조 - 메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어, 람다식에서 불필요한 매개 변수를 제거하는 것이 목적
(left, right) -> Math.max(left, right);
//메소드 참조(Math :: max)
IntBinaryOperator operator = Math :: max;
            정적 메소드 참조 - 정적 메소드 참조시 클래스 이름 뒤에 :: 기호를 붙이고 정적 메소드 이름을 기술
                        클래스 :: 메소드
            인스턴스 메소드 참조 - 인스턴스 메소드일 경우 먼저 객체를 생성한 다음 참조 변수 뒤에 :: 기호를 붙이고 인스턴스 메소드 이름을 기술
                        참조변수 :: 메소드
            매개 변수의 메소드 참조 - 람다식에서 제공되는 a 매개 변수의 메소드를 호출해서 b 매개 변수를 매개값으로 사용 할 때 a의 클래스 이름 뒤에 :: 기호를 붙이고 메소드 이름을 기술
                        (a, b) -> { a.instanceMethod(b); }
                        클래스 :: instanceMethod

            생성자 참조 - 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치, 클래스 이름 뒤에 :: 기호를 붙이고 new 연산자를 기술
                        (a, b) -> { return new 클래스(a, b); }
                        클래스 :: new

컬렉션 프레임워크

Collection            List            순서를 유지하고 저장            ArrayList, Vector, LinkedList
                                             중복 저장 가능
                            Set            순서를 유지하지 않고 저장      HashSet, TreeSet
                                             중복 저장 안 됨
Map                                      키와 값의 쌍으로 저장            HashMap, Hashtable, TreeMap, Properties
                                             키는 중복 저장 안 됨
List 컬렉션 - 객체를 인덱스로 관리(저장 순서를 유지)하고 null도 저장 가능
list.add("홍길동");          //맨끝에 객체 추가
list.add(1, "신용권");       //지정된 인덱스에 객체 삽입
String str = list.get(1);    //인덱스로 객체 찾기
list.remove(0);              //인덱스로 객체 삭제
list.remove("신용권");       //객체 삭제
            
            ArrayList - 배열과 다르게 객체를 저장할 용량이 증가하면 자동으로 증가, 인덱스의 객체를 제거하면 바루 뒤 인덱스부터 마지막 인덱스까지 1씩 당겨지고 객체를 삽입하면 반대로 해닥 인덱스부터 마지막 인덱스까지 1씩 밀림, 빈번한 객체 삭제와 삽입은 LinkedList가 인덱스 검색이나, 맨 마지막에 객체를 추가하는 경우에는 ArrayList가 더 좋음
            고정된 객체 들로 구성된 List를 생성할 때는 Arrays.asList(T...a)가 더 간편
            List<T> list = Arrays.asList(T... a);
            List<String> list1 = Arrays.asList("홍길동", "신용권", "감자바");
            
            Vector - Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 이 메소드들을 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행(스레드가 안전)하므로 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제 함
            
            LinkedList - LinkedList는 인접 참조를 링크해서 체인처럼 관리하므로 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않음

Set 컬렉션 - List는 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않고, 객체를 중복해서 저장할 수 없고, 하나의 null만 저장
            Iterator 인터페이스 - 인덱스를 사용하지 않기에 반복자(Iterator)를 제공함
            Set<String> set = ...;
            Iterator<String> iterator = set.iterator();
            while(iterator.hasNext()){
              String str = iterator.next();
              //삭제
              if(str.equals("홍길동")){
                iterator.remove();
              }
            }           
            HashSet - 객체를 저장하기 전에 해시코드를 만들어서 저장되어 있는 객체들의 해시코드를 비교(equals())후 같지 않을 때 저장

Map 컬렉션 - 키와 값으로 구성된 Entry 객체를 저장, 키는 중복 저장될 수 없지만 값은 중복 저장 가능
map.put("홍길동", 30);            //객체 추가
int score = map.get("홍길동");    //객체 찾기
map.remove("홍길동");             //객체 삭제

저장된 객체를 대상으로 하나씩 얻고 싶을 경우 두가지 방법을 사용
//keySet() 메소드로 모든 키를 Set 컬렉션으로 얻은 다음, 반복자를 통해 키를 하나씩 얻고 get() 메소드를 통한 방법
Set<K> keySet = map.keySet();
Iterator<K> keyIterator = keySet.iterator();
while(keyIterator.hasNext()){
  K key = keyIterator.next();
  V value = map.get(key);
}

//entrySet() 메소드로 모든 Map.Entry를 Set 컬렉션으로 얻은 다음, 반복자를 통해 Map.Entry를 하나씩 얻고 getKey()와 getValue() 메소드를 이용해 키와 값을 얻음
Set<Map.Entry<K, V>> entrySet = map.entrySet();
Iterator<Map.Entry<K, V>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
  Map.Entry<K, V> entry = entryIterator.next();
  K key = entry.getKey();
  V value = entry.getValue();
}
            HashMap - 키로 사용할 객체는 hashCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야함(hashCode()의 리턴값이 같고, equals() 메소드가 true를 리턴), 키와 값의 타입은 기본 타입(byte, short, int, float, double, boolean, char)을 사용할 수 없고 클래스 및 인터페이스 타입만 가능
            Hashtable - Hashtable도 키로 사용할 객체는 hashCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야함, 차이점은 Hashtable은 동기화된 메소드로 구성되어 있기 떄문에 멀티 스레드가 동시에 이 메소드들을 실행할 수는 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행(스레드가 안전), 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있음
            Properties - Hashtable의 하위 클래스이므로 모든 특징을 그대로 가지고 있음, 키와 값을 String 타입으로 제한한 컬렉션, 한글은 유니코드로 저장됨

검색 기능을 강화시킨 컬렉션 - 이진 트리를 이용해서 계층적 구조를 가지면서 객체를 저장하는 TreeSet과 TreeMap을 제공
            이진 트리 구조 
            TreeSet - 
검색 관련 메소드
정렬과 관련된 메소드
//내림차순 정렬
NavigableSet<E> descendingSet = treeSet.descendingSet();
//오름차순 정렬 descendingSet() 메소드를 두번 호출
NavigableSet<E> ascendingSet = descendingSet.descendingSet();
//내림차순 정렬
NavigableSet<E> descendingSet = treeSet.descendingSet();
//오름차순 정렬 descendingSet() 메소드를 두번 호출
NavigableSet<E> ascendingSet = descendingSet.descendingSet();
범위 검색 메소드
//c <= 검색단어 <= f
NavigableSet<String> rangeSet = treeSet.subSet("c", true, "f", true);

            TreeMap - 키와 값이 저장된 Map.Entry를 저장하고, 부모 키값과 비교해서 키 값이 낮은 것은 왼쪽 자식 노드에, 키 값이 높은 것은 오른쪽 자식 노드에 Map.Entry 객체를 저장
검색 관련 메소드
정렬 관련 메소드
//내림차순 정렬
NavigableSet<E> descendingMap = treeMap.descendingMap();
//오름차순 정렬 descendingMap() 메소드를 두번 호출
NavigableSet<E> ascendingMap = descendingMap.descendingMap();
범위 검색 메소드
//c~f 사이의 단어 검색
NavigableMap<String, Integer> rangeMap = treeMap.subMap("c", true, "f", true);
    
            Comparable과 Comparator - TreeSet의 객체와 TreeMap의 키는 오름차순으로 정렬되는데, 정렬을 위해 java.lang.Comparable을 구현한 객체를 사용하고 Comparable에서는 compareTo() 메소드가 정의되어 있으므로 사용자 정의 클래스에서는 이 메소드를 오버라이딩하여 리턴값을 만들어야 함
                                                       - TreeSet 또는 TreeMap 생성자의 매개값으로 정렬자(Comparator)의 compare() 메소드를 정의하면 Comparable 비구현 객체도 정렬 가능함
//오름차순 정렬
TreeSet<E> treeSet = new TreeSet<E>(new AscendingComparator());
//내림차순 정렬
TreeMap<K, V> treeMap = new TreeMap<K, V>(new DescendingComparator());

LIFO와 FIFO 컬렉션 - 후입선출(Last In First Out), 선입선출(First In First Out) 
            Stack - LIFO 자료구조를 구현한 클래스
            Queue - FIFO 자료구조를 구현한 인터페이스이고 대표적인 클래스는 LinkedList임

동기화된 컬렉션 - 싱글 스레드 환경에서 사용하다가 멀티 스레드 환경으로 전환하기 위해서 synchronizedXXX() 메소드가 있음
List<T> list = Collections.synchronizedList(new ArrayList<T>());
Set<E> set = Collections.synshronizedSet(new HashSet<E>());
Map<K, V> map = Collections.synchronizedMap(new HashMap<K, V>());

병렬 처리를 위한 컬렉션 - java.util.concurrent패키지의 ConcurrentHashMap(부분 잠금)과 ConcurrentLinkedQueue(락-프리: 최소한 하나의 스레드가 안전하게 요소를 저장하거나 얻도록)는 멀티 스레드가 병렬적으로 처리하도록 기능을 제공
Map<K, V> map = new ConturrentHashMap<K, V>();
Queue<E> queue = new ConcurrentLinkedQueue<E>();





















































            
























댓글

이 블로그의 인기 게시물

mac 맥 맥북 Brew 완전 삭제

맥북에서 Brew 초기화 Brew를 써서 h2를 쓰려고 하는데 brew install h2가 안되서 이리 저리 알아보다가 완전 삭제 후 다시 설치 하니까 되서 그 방법을 남겨놈 1. 터미널에 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)" 입력후 y랑 뭐 비번.. 2. /usr/local 폴더에서 Homebrew 폴더 삭제 rm -rf Homebrew/ 권한설정으로 잘.....삭제하고 3. 다시 설치 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 좀 오래걸리니까 기다려야한다는걸 배움... 출처.... https://discourse.brew.sh/t/error-no-formulae-found-in-taps/8331/9

ORA-28000 계정이 잠금되었습니다 계정 잠길때

오라클 계정이 잠길때 해제방법 증상 t he account is locked 오류 발생 원인 Oracle 에서 t he account is locked  에러가 나는 원인은 ● 잘못된 패스워드로 설정횟수만큼 접속 시도시 Lock. ●  30일동안(Default) 해당 계정으로 로그인을 하지 않았을 경우 Lock. 등이 있다. 해결방법 command창에서 * 로컬일경우, sqlplus "/as sysdba"  또는  sqlplus /nolog  conn /as sysdba  * 로컬이 아닐 경우, sqlplus /nolog conn sys/password@<sid> 이름/패스워드@sid로 입력 로 접속 후 SELECT username, account_status, lock_date FROM dba_users; 으로 Lock이 된 사용자를 확인한 후 LOCKED<TIMED> 라고 되있으면, 패스워드 설정횟수 입력 오류로, 아래의 Unlock 명령만, EXPIRED & LOCKED 라고 되있으면, 패스워드 기간만료로, Unlock 후 비밀번호를 지정해줘야 한다. ALTER USER 사용자명 ACCOUNT UNLOCK; 로 Lock된 사용자를 Unl ock 시킨다 방금 말했다시피, 다시 Lock된 사용자 확인했는데,  Open되지 않고 EXPIRED되어 있다면, alter user 사용자명 identified by 바꿀패스워드;  로 패스워드를 변경하거나 또는 SQL*PLUS 를 재시작하여 Lock를 해제한 계정(사용자명/패스워드)로 로그인 하면 패스워드 변경 창이 뜬다. 추가로 패스워드 Lock 횟수 확인하는 방법은 SELECT U.USERNAME,P.PROFILE, P.RESOURCE_NAME, P.LIMIT  FROM D

메이븐으로 라이브러리 인식

 간혹 퍼블릭 jar가 아닌 파일이 있는데 그럴때 쓰면 될듯 <dependency> <groupId> SimpleCryptLib </groupId> <artifactId> SimpleCryptLib </artifactId> <version> 1.1.0 </version> <scope> system </scope> <systemPath> ${basedir}/src/main/webapp/WEB-INF/lib/SimpleCryptLib-1.1.0.jar </systemPath> </dependency> version, scope, systemPath는 꼭 작성해야 한다 groupId, artifactId, version은 암거나 해도 되는거 같음 최근(2021.05.04)스프링 부트    < dependency > < groupId > NiceID </ groupId > < artifactId > NiceID </ artifactId > < version > 1.0 </ version > < scope > system </ scope > < systemPath > ${basedir}/src/main/resources/lib/NiceID.jar </ systemPath > </ dependency > 이걸 추가해주는것도 필요할지도..?? < build > < plugins > < plugin > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-maven-plugi