[자바] java 조건문
01. Object 클래스 - 모든 클래스의 최상위 클래스
java.lang 패키지
프로그래밍시 import 하지 않아도 자동으로 imort됨
import.java.lang.*;
많이 사용하는 기본 클래스들이 속한 패키지
String, Integer, System…
모든 클래스는 Object 클래스를 상속 받는다
java.lang.Object 클래스
모든 클래스의 최상위 클래스는
모든 클래스는 Object에서 상속받고, Object 클래스의 메서드 중 일부는 재정의해서 사용할 수 있음
컴파일러가 extends Object를 추가함
class Student => class Student extends Object
toString() 메서드
객체의 정보를 String으로 바꾸어서 사용할 때 쓰임
String이나 Integer 클래스는 이미 재정의 되어 있음
toString()메서드 재정의 예
class Book{
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String toString() {
return title + "," + author;
}
}
public class BookTest {
public static void main(String[] args) {
Book book = new Book("데미안", "헤르만 헤세");
System.out.println(book);
}
}
이 부분에서 toString()을 주석처리하고 실행하면 앞은 클래스 풀네임(패키지 이름까지) 뒤는 메모리 위치(인스턴스 메모리 위치에 다한 가상메모리값). jvm 값이고 실제 메모리 값은 아. 16진수.
근데 이렇게 String 값을 하나 만들고 출력해보면 test가 제대로 나오는데 그 이유는
안에 toString이 저장되어 있어서 바로 호출되기 떄. 그래서 우리도 바로 저 타이틀 저자를 바로 출력하고 싶으면 toString() 부분을 오버라이딩 해서 바꾸면 된다.
이렇게 객체에 대한 정보 출력 할 떄 toString() 메서드를 많이 재정의 해서 사용하게 된다.
02. Object 클래스의 메서드 활용
인스턴스를 힙 메모리에 만들게 되면 학생에 대한 메모리가 힙에 잡힘. 학생을 또 만들면 얘도 잡히게 됨(새로 만들게 됨.) 두개 인스턴스가 같다는 건 인스턴스가 같다는 거.(힙메모리가 같다 == 메모리 위치가 같다.) 변수의 주소값 찍었을 때 똑같으면 같은거 (==) 로 같다는 걸 사용
근데 물리적으로 다르지만 논리적으로 같을 경우가 있다. 학생 인스턴스 대해 여러 메모리 생길 수 있지만 같은 학생이라 봐야하는 경우가 있다. 그럼 그게 같다라고 볼수 있는경우를 예로 학번이 같거나 사원번호가 같으면 같은 사람인 거.
equals()는 원형은 주소값을 비교함. 근데 재정의 해서 논리적으로 true/false를 반환하게 된다.
만약 학번이 100 번인 학생이 두명이면 물리적으론 다르지만 논리적으로는 같은 학생으로 봄. (==)는 주소값만 비교하지만 equals로 오버라이딩을 통해 논리 비교를 가능하다. 그리고 저 주소값을 해쉬코드라 한다. 해시가 들어가 있는 이유는 자바에서 힙 메모리를 관리하는 방식이 해쉬방식임.
키값이 있고 그 키값에 대해 오브젝트를 어디에 저장할건지 지정하는 방식.
키값을 알면 인덱스를 지정할 수 있다. 키값에 인덱스 저장하고 (해쉬펑션에 키를넣고) 그 키값을 찾아가는게 해쉬 펑션. 키는 유일함.(중복 불가) 자바에서 JVM이 메모리 관리하는게 해쉬방식. 그 안의 값을 해쉬값이라 한다. 논리적으로 같다고 하면 그 해쉬코드 값이 같아야 한다. 근데 물리적으로 같냐고 하면 또 그건 아니다! 물리적인 해쉬코드 값 꺼내보는 함수는 따로 있다.
그래서 아래 두 equals와 hashcode는 페어라 볼 수 있다.
스트링도 equlas가 오버라이딩 되어있어서 같은 문자열이면 true반환하는데 그떄 해쉬코드 반환해보면 같다. 스트링 클래스 내부적으로 해쉬코드와 이퀄스가 오버라이드 되어있고 스트링에 있는 문자열 조합해 숫자값 나타낸다. 해쉬값이 integer인데 integer 반환한다.
equals() 메서드
두 인스턴스의 주소 값을 비교하여 true/false를 반환
재정의 하여 두 인스턴스가 논리적으로 동일함의 여부를 구현함
인스턴스가 다르더라도 논리적으로 동일한 경우 true를 반환하도록 재정의 할 수 있음
(같은 학번, 같은 사번, 같은 아이디의 회원…)
hashCode() 메서드
hashCode()는 인스턴스의 저장 주소를 반환함
힙메모리에 인스턴스가 저장되는 방식이 hash 방식
hash : 정보를 저장, 검색하는 자료구조
자료의 특정 값(키 값)에 대한 저장 위치를 반환해주는 해시 함수를 사용
두 인스턴스가 같다는 것은?
두 인스턴스에 대한 equals()의 반환 값이 true 동일한 hashCode() 값을 반환
논리적으로 동일함을 위해 equals() 메서드를 재정의 하였다면 hashCode()메서드도 재정의 하여 동일한 hashCode 값이 반환되도록 한다
Student.java
public class Student {
private int studentId;
private String studentName;
public Student(int studentId, String studentName)
{
this.studentId = studentId;
this.studentName = studentName;
}
public boolean equals(Object obj) {
<!-- 넘어온 obj가 student인지 확인 아니면 비교할 필요도 없어서 false.
맞으면 student로 바꿔준다. -->
if( obj instanceof Student) {
Student std = (Student)obj;
if(this.studentId == std.studentId )
//
return true;
else return false;
}
return false;
}
@Override
public int hashCode() {
return studentId;
}
}
여기서 해쉬코드와 eqals 를 오버라이딩 해서 재정의.
if( obj instanceof Student) { 넘어온 obj가 student인지 확인 아니면 비교할 필요도 없어서 false. 맞으면 student로 바꿔준다.
EqualTest.java
package ch02;
public class EqualTest {
public static void main(String[] args) throws CloneNotSupportedException {
Student Lee = new Student(100, "Lee");
Student Lee2 = Lee;
Student Shun = new Student(100, "Lee");
System.out.println(Lee == Shun);
System.out.println(Lee.equals(Shun));
System.out.println(Lee.hashCode());
System.out.println(Shun.hashCode());
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
System.out.println(i1.equals(i2));
System.out.println(i1.hashCode());
System.out.println(i2.hashCode());
System.out.println(System.identityHashCode(i1));
System.out.println(System.identityHashCode(i2));
Student Lee3 = (Student)Lee.clone();
System.out.println(System.identityHashCode(Lee));
System.out.println(System.identityHashCode(Lee3));
Lee.setStudentName("kim");
Student copyStudent = (Student)Lee.clone();
//생성을 하는게 아니라 인스턴스 그대로 복제해서 만드는 함수
//하위클래스도 그대로 복제할 떄 사용 가능.
System.out.println(copyStudent);
}
}
맨 위는 값은 이렇게 나오고 equals는 다르게 나오고 해쉬값은 메모리가 다르기 때문에 다르게 나온다하다. 근데 해쉬코드를 찍으면 같은 값이 나와야 한다. (100, 100으로 동일)
진짜 해쉬코드 값은 어떻게 아나? identityHashCode를 써서 진짜 해쉬코드 값을 출력해준다(스태틱함수)
clone() 메서드
객체를 new 했을 때 생성자 호출되고 그 생성자 호출에 의해 일련의 과정 거쳐 객체 생성되는데 clone을 쓰면 멤버변수가 같은 스테이터스가 똑같은 애를 생성.
오브젝트 상태를 그대로 복제.
하나의 프로토 타입을 붕어빵마 찍어냄. 생성자와 다른게 생성자는 초기화 해서 처음값을 가지고 생성하지만 클론은 그대로 복제. 객체지향에서 정보은닉이나 보호에서 위배될수 있음(private까지 복사하므로) 그래서 명시적으로 선언해서 구현한다.
implements Cloneable{
반드시 이 부분을 선언해서 사용해야 한다.(복제 가능하다고 선언한거와 마찬가지.)
물론 클론도 재정의가 가능하다.
객체의 원본을 복제하는데 사용하는 메서드
생성과정의 복잡한 과정을 반복하지 않고 복제 할 수 있음
clone()메서드를 사용하면 객체의 정보(멤버 변수 값등…)가 동일한 또 다른 인스턴스가 생성되는 것이므로, 객체 지향 프로그램에서의 정보 은닉, 객체 보호의 관점에서 위배될 수 있음
해당 클래스의 clone() 메서드의 사용을 허용한다는 의미로 cloneable 인터페이스를 명시해 줌
Student.java
public class Student implements Cloneable{
.......
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
EqualTest.java
Student Lee3 = (Student)Lee.clone();
System.out.println(System.identityHashCode(Lee));
System.out.println(System.identityHashCode(Lee3));
클론의 리턴타입은 오브젝트인데 위에서 student로 바꿨다.
이걸 그냥 저런식으로 짜면 에러나는데 위에서 말했듯 public class Student implements Cloneable{ 부분을 선언해서 실행하면 된다.
03. String, StringBuilder, StringBuffer 클래스, text block
String 클래스
자바 13 부터 스트링 블럭 지원 html, json 만드는데 유용
스트링 블럭만드는데 2가지 방법, new 또는 assing해서
- String 선언하기
String str1 = new String("abc"); String str2 = "abc";
힙 메모리에 인스턴스로 생성되는 경우와 상수 풀(constant pool)에 있는 주소를 참조하는 두 가지 방법
- 힙 메모리는 생성될때마다 다른 주소 값을 가지지만, 상수 풀의 문자열은 모두 같은 주소 값을 가짐
public class StringTest {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);
String str3 = "abc";
String str4 = "abc";
System.out.println(str3 == str4);
}
}
이거 실행해보면
한번 생성된 String은 불변(immutable)
String을 연결하면 기존의 String에 연결되는 것이 아닌 새로운 문자열이 생성됨 ( 메모리 낭비가 발생할 수도 )
public class StringTest2 {
public static void main(String[] args) {
String java = new String("java");
String android = new String("android");
System.out.println(System.identityHashCode(java));
java = java.concat(android);
System.out.println(java);
System.out.println(System.identityHashCode(java));
}
}
이 해쉬 코드 연결된 자바값이랑 비교하면 두개의 어드레스는 다르다. 연결된 아웃풋은다르다( 자바 따로, 안드로이드 따로 ) 즉 연결한 스트링은 다른 어드레스가 된다.
StringBuilder, StringBuffer 활용하기
가변적(스트링은 파이널인데 얘들은 파이널이 아니라 바뀔 수 있음). 캐릭터 버퍼가 변경해서 유용하게 사용 가능. 마지막에 toString()으로 사용. 멀티스레드에서 동기화를 제공해준다. 단일스레드에선 스트링 빌더 멀티스레드에선 스트링 버퍼 사용.
내부적으로 가변적인 char[]를 멤버 변수로 가짐
문자열을 여러번 연결하거나 변경할 때 사용하면 유용함
새로운 인스턴스를 생성하지 않고 char[] 를 변경함
StringBuffer는 멀티 쓰레드 프로그래밍에서 동기화(synchronization)을 보장
단인 쓰레드 프로그램에서는 StringBuilder 사용을 권장
toString() 메서드로 String반환
public class StringBuilderTest {
public static void main(String[] args) {
String java = new String("java");
String android = new String("android");
StringBuilder buffer = new StringBuilder(java);
System.out.println(System.identityHashCode(buffer));
buffer.append("android");
System.out.println(System.identityHashCode(buffer));
java = buffer.toString();
}
}
찍어보면 동일. 아무리 연결해도 가변하기 때문에 버퍼는 변환 스트링 인자로 쓸일 많을땐 tostring사용.
text block 사용하기 (java 13부터 지원)
문자열을 “”” “”” 사이에 이어서 만들 수 있음
html, json 문자열을 만드는데 유용하게 사용할 수 있음(벡틱) 제이슨은(키값으로) 태그나 뉴라인 이런거 해서 넣으면 지저분하고 복잡한데 벡틱처럼 선언해서 바로 사용이 가능하다.(8에서는 에러남)
이거 선택하고 실행
public class StringTextBlock {
public static void main(String[] args) {
String strBlock = """
This
is
text
block
test.""";
System.out.println(strBlock);
System.out.println(getBlockOfHtml());
}
public static String getBlockOfHtml() {
return """
<html>
<body>
<span>example text</span>
</body>
</html>""";
}
}
04. Class 클래스 사용하기
Class 클래스
클래스 이름이 클래스. 컴파일을 하면 자바의 클래스나 인터페이스를 클래스 하면 .class파일이 생기고 그 안의 정보들이 다 들어있는데 그 정보를 가져오거나 인스턴스를 생성하거나 하는게 Class클래스임.
자바의 모든 클래스와 인터페이스는 컴파일 후 class 파일이 생성됨
Class 클래스는 컴파일 된 class 파일을 로드하여 객체를 동적 로드하고, 정보를 가져오는 메서드가 제공됨
Class.forName(“클래스 이름”) 메서드로 클래스를 동적으로 로드 함
#####레퍼런스 보면 클래스들이 많은데 꼭 기억해야 할 게 forName
해당 스트링이 들어가서 클래스 네임이 따옴표 안에 들어가게 되는데 스트링 네임에 해당되는 클래스나 인터페이스에 대해 매핑(동적 바인딩)을 해줌.
Class c = Class.forName("java.lang.String");
얘는 스트링 클래스에 대한 풀네임 썼는데 자바 스트링 클래스에 대한 풀네임 가져옴. 가져와서 동적로딩 해서 런타임시 실행시 필요한 클래스를 로딩시켜 사용함.(다른 언어에도 동적로딩해서 사용하는 방식이 있다)
- 클래스 이름으로 직접 Class 클래스 가져오기
Class c = String.class;
- 생성된 인스턴스에서 Class 클래스 가져오기
String s = new String(); Class c = s.getClass(); //Object 메서드
동적 로딩
컴파일 시에 데이터 타입이 binding 되는 것이 아닌, 실행(runtime) 중에 데이터 타입을 binding 하는 방법
프로그래밍 시에는 문자열 변수로 처리했다가 런타임시에 원하는 클래스를 로딩하여 binding 할 수 있다는 장점
컴파일 시에 타입이 정해지지 않으므로 동적 로딩시 오류가 발생하면 프로그램의 심각한 장애가 발생가능
컴파일 시 뭐 로드할지 모르지만 런타임 시 로드하는 경우도 있음 예로 jdbc드라이버가 어디 데이터 읽기 전에 라이브러리가 뭐가 될지 모름. 그럼 변수 갖고있다 오라클이면 오라클 메인 넣어줌. 그래서 위에선 변수그냥 써주고 런타임시 각 맞게 로딩 됨.
스트링 값이라 어떤 값이든 들어가기도 가능하다.
단점은 컴파일시 그 데이터가 없거나(로컬에) 스펠링 틀리거나 없거나 해서 클래스가 없으면 에러나거나 셧다운 될 수 있다. 시스템이 돌아가다 부르는거라서 그렇다.
Class의 newInstance()메서드로 인스턴스 생성
- new 키워드를 사용하지 않고 클래스 정보를 활용하여 인스턴스를 생성할 수 있음
우리가 객체 생성할 떄 new 로 생성자 선언해서 사용했는데 new가 아니라 뉴 인스턴스 메서드를 사용해서 선언했다.
클래스 정보 알아보기
reflection 프로그래밍 : Class 클래스를 사용하여 클래스의 정보(생성자, 변수, 메서드)등을 알 수 있고 인스턴스를 생성하고, 메서드를 호출하는 방식의 프로그래밍
로컬 메모리에 객체 없는 경우, 원격 프로그래밍, 객체의 타입을 알 수 없는 경우에 사용
java.lang.reflect 패키지에 있는 클래스를 활용하여 프로그래밍
일반적으로 자료형을 알고 있는 경우엔 사용하지 않음
StringTest.java
public class StringTest {
public static void main(String[] args) throws ClassNotFoundException {
Class c3 = Class.forName("java.lang.String");
Constructor<String>[] cons = c3.getConstructors();
for(Constructor con: cons) {
System.out.println(con);
}
System.out.println();
Method[] methods = c3.getMethods();
for(Method method : methods) {
System.out.println(method);
}
}
}
위는 동적로딩으로 가져와서 스트링 정보 보여주는 것. Class c3 = Class.forName(“java.lang.String”); 얘가 없으면 클래스 낫파운드 익셉션이 떨어진다.
Person.java
public class Person {
private String name;
private int age;
public Person() {};
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return name;
}
}
ClassTest.java
package ch04;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ClassTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
ClassNotFoundException, NoSuchMethodException, SecurityException {
Person person = new Person("James");
System.out.println(person);
Class c1 = Class.forName("ch04.Person");
Person person1 = (Person)c1.newInstance();
System.out.println(person1);
Class[] parameterTypes = {String.class};
Constructor cons = c1.getConstructor(parameterTypes);
Object[] initargs = {"김유신"};
Person personLee = (Person)cons.newInstance(initargs);
System.out.println(personLee);
//이 위에서 한게 이 한줄과 동일
Person kim2 = new Person("kim");
}
}
newInstance() 했을 떄 c2에 getConstructor 해서 파라미터 타입 뭔지 써줘야 한다. 그 위에 클래스 어레이 선언.
Class[] parameterTypes = {String.class};]
그럼 저런 생성자를 찾고 반환해준다.
Constructor cons = c1.getConstructor(parameterTypes);
argument타입은 array.
이 위에서 한게 이 한줄과 동일하다. Person kim2 = new Person(“kim”);
이런 코딩은 로컬에 Person이 없을 때 사용 로컬에 저런 타입 이 없는데 생성하고 그 생성자 정보를 가져와서 사용해야될 떄 저렇게 사용한다.
로컬은 같은 프로세스(메모리) 를 의미하는데 서로 다른 프로세스는 전혀 다름. 이런 경우에도 이렇게 사용 가능.(같은 메모리 안에서도)