java Code로 상속관계를 표현 할때는 extends 키워드를 사용합니다.
자바는 단일 상속만 가능 → 한개의 클래스만 extends 가능하다.
다중 상속대신에 자바는 인터페이스를 사용하여 여러 메소드를 구현 할 수 있다.
public class B (object 클래스가 생략 되어 있다){ }
public class B extends A { }
this.getClass().getName())
->클래스 이름을 가지고 온다.
Parent x = new Child(); //가능 -> 이렇게 선언한 경우 child이 있긴 있지만 Parent 타입만 사용한다.
Child y = new Parent(); //불가능 -> child 으로 선언한 경우 parent와 , child가 모두 필요하지만 Parent만 존재하기 때문에 사용X
default,pivate은 하위 클래스가 상속 받아도 접근 할수 없습니다 → 접근하기 위해서는 protected나 public 클래스를 사용 해야한다.
protected는 상속 관계 이거나 동일 패키지에서 접근 가능하다.
this는 자기자신
super는 부모 객체를 의미
부모의 멤버 변수가 private으로 되어 있다면 super에 public getter를 이용하여 접근 가능하다.
super.getName;
부모클래스에서 제공하는 생성자가 있는 경우 자식의 클래스에서 부모 클래스에 있는 생성자를 호출 가능하다
→ super(name,level) ;
자식 class의 기본 생성자는 부모 class의 생성자를 별도로 호출하지 않으면 부모 class의 기본 생성자를 호출합니다.
그런데 부모클래스의 기본 생성자가 없으면 오류가 발생합니다!
super호출자의 경우는 무조건 자식 생성자 이전에 만들어 져야한다
한 클래스 파일에 여러 클래스 등장 할 수 있지만 public이라고 수식어가 붙어 있는 클래스는 하나 밖에 없다.
→여러개의 클래스가 있을때 파일의 이름이 클래스 이름과 같은 클래스 앞에 접근지정자가 존재한다.
object Class -toString()
public static void main (string[] args) {
Virsus virus = new Virus("UNJNOWN",5);
System.out.println(virus);//이렇게 사용하게 되면 toString을 선언 해줬다면 선언해준것이
//나오고 아니면 클래스와 주소가 나온다
}
Type 과 Method를 로 크게 나누어 생각 할 수 있다. (Overridng과 객체 reference 타입으로 다형성을 만들 수 있다)
Method Overloding과 Overriding으로 다형성 구현
Overloading
name이 같아도 Parameter가 다르면 별개의 메소드로 간주 → 생성자() {} , 생성자 (String name){} 두개 다른것
Overriding ****중요 → 재정의
상속관계에서 부모 Class의 method를 자식 Class에서 재정의 할 수 있음
상속관계에서 부모의 Class method를 파라미터를 다르게 만들면 오버로딩 이다.
부모 type으로 자식 type의 객체를 reference 할 수 있습니다
즉 한개의 type으로 여러 하위 type의 객체를 할당 받을 수 있다.
Object←Phone ← FolderblePhone
Object o = new Object;
Object o = new Phone;
Object o = new FoldeblePhone;
Virus corona = new Corona("Covid19", 8 ,20); //Virus <- corona //Virus 호출 범위
coroan.toString();//호출 가능
corona.showInfo(); //호출 불가
Virus 호출 범위 ,new Corona -호출 대상
하위 클래스(corona)에서 재정의 한 메소드만 사용 가능하다 .
→ 재정의 (overriding) 된 Method만 부모 type으로 선언한 변수로 호출하면 호출이 된다.
public class OverridingABCDE {
public static void main(String[] args) {
//AA x = new CC(); x.a();
//CC x = new EE(); x.b(3);
//DD x = new BB(); x.b();
//BB x = new DD(); x.a();
}
}
class AA{
void a(int i) { System.out.println("AA:a(int i)"); }
}
class BB extends AA{
void b() { System.out.println("BB:b()"); }
}
class CC extends BB{
void a() { System.out.println("CC:a()"); }
void b(int i) { System.out.println("CC:b(int i)"); }
}
class DD extends CC{
void a(int i) { System.out.println("DD:a(int i)"); }
void b() { System.out.println("DD:b()");}
}
class EE extends DD{
void a() { System.out.println("EE:a()"); }
void b() { System.out.println("EE:b()"); }
}
복습 할때 예상 해볼것! 결과 \
재정의 할 경우 계속 계속 밑에를 타고 내려간다고 생각
@Override //Annotation
void getName() {
// TODO Auto-generated method stub
System.out.println("name : Tomson");
}
public interface Folder{
public void fold();
public void open();
}
A , B 가 있을때 A가 m()이라는 메소드가 필요할떄 인터페이스 타입으로 I를 선언하고 M() 을 선언한다
그런 후에 B라는 클래스에서 인터페이스 I의 m()을 구현을 한뒤에 A()가 m()의 메소드가 필요할때 I =new B로 만들어서 B의 다른 메소드를 사용하지 않고 m()만 사용 할 수 있다.
상속은 부모클래스를 전부 이식하는반면 인터페이스는 타 클래스에서 지정한 기능만을 불러오는 역할
인터페이스는 연결해서 기능을 불러오는 역할을 합니다.
java - 8버전에는 body를 가진 method를 interface에 추가 되었다
package com.inter;
class Tom{
int age = 50;
}
interface Hillary{
int age = 46; //인터페이스 안의 필드는 static, final이 default이다.
}
class Bill{}
public class LittleTom extends Tom implements Hillary {
int age = 20;
public void test() {
System.out.println(age); //littleTom
System.out.println(this.age); //littleTom
System.out.println(super.age); //tom
System.out.println(Hillary.age); //Hillary
}
public abstract void go(String m);//메소드
public abstract class myAbstract{
public abstract void go(String m); //클래스
}
추상 class는 class이나 추상 method를 가져 , 스스로 객체를 만들지 못합니다. → new 사용 불가
다른 클래스에서 추상클래스를 상송해서 미완성의 메소드를 구현해 줘야함
추상 method 앞에 abstract keyword를 붙인다.
class는 추상 class의 method를 구현할 의무를 가진다
public class Complete extends Myabstract{
public void go (String m) {} // 이정도만 해도 완성 된것!
}
추상클래스를 상속받아도 상속과 똑같은 취급 → circle은 shape 타입
추상 클래스를 이용한 type 상속! 가능
Shape[] s = new Shape[3]; //3칸짜리 배열이 만들어진것 -> shape가 만들어 진것이 아님 -> shape 타입의 데이터만 들어감
s[0] = new Circle(10);
s[1] = new Rect(3 ,5);
s[2] = new Circle(15);
folder f = new folderble Phone()일때
상속이거나 implement 인것을 나타내는 연산자
Folder f = new FolderblePhone();
if( f instanceof Folder ){ //F= reference ,Folder =클래스 이름
System.out.println("instanceof Folder");
}
if( f instanceof Object ){
System.out.println("instanceof Object");
}
if( f instanceof Phone ){
System.out.println("instanceof Phone");
}
⇒ f라는 reference가 가리키는 대상이 Folder 인가요 라고 묻는것
아래와 같은 결과 발생
instanceof Folder
instanceof Object
instanceof Phone
IOC ioc = new IOC();
if( ioc instanceof IOA ) {
System.out.println("IOA");
}else if( ioc instanceof IOB ) {
System.out.println("IOB");
}else if( ioc instanceof IOC ) {
System.out.println("IOC");
}
if( ioc instanceof IOC ) {
System.out.println("IOC");
}else if( ioc instanceof IOB ) {
System.out.println("IOB");
}else if( ioc instanceof IOA ) {
System.out.println("IOA");
}
}
}
instanceof 연산자를 사용할때는 제일 하위클래스 부터 올라와야한다.
클래스 안에서 다시 정의 되는 class를 말한다.
원래 클래스 안에 다른 클래스를 선언 할수 없다 → top level class는 불가능
→ 이너 클래스는 toplevel 클래스 안에 들어 있는것 이기 때문에 가능하다.
anonymous Class
class안에서 이름이 없이 만들어지는 Inner Class, 이름이 없으므로 재사용되지 않고 한번 사용되고 ,한번 사용한다는건 ,객체를 생성하는 코드에 바로 class의 내용을 전달.
new Folder 다음에 정의된 내용은 인터페이스 객체가 아니라 인터페이스를 구현한 어떤 익명 클래스 객체
package com.inner;
abstract class Movie{
abstract void play();
}
interface Game{
void play(); //public abstract
}
class MyMovie extends Movie{
@Override
void play() {
// TODO Auto-generated method stub
}
}
public class MP3Player {
public void start(Movie m) {
m.play();
}
public void start(Game g) {
g.play();
}
public static void main(String[] args) {
MP3Player player = new MP3Player();
//무명클래스 : 클래스 선언과 생성을 동시에 수행하는 클래스 . 1회용
player.start(new Movie() {
@Override
void play() {
// TODO Auto-generated method stub
System.out.println("harry potter");
}// movie클래스를 상속 받는 자식을 선언 후 생성까지 함
});
player.start(new Movie() {
@Override
void play() {
// TODO Auto-generated method stub
System.out.println("kim");
}
});
player.start {
public void play() {
System.out.println("king kong");
}
});
}
}
member Class
member Class : 바깥쪽 객체가 먼저 생성된 후에 사용 가능 하다.
자동차가 만들어 져 있지 않으면 엔진 혼자 생성 불가능 으로 쉽게 이해해보자.
package com.inner;
public class Car {
int num = 1234;
//member inner class
public class Engine{
public void go() {
System.out.println(num);
}
}
public static void main(String[] args) {
Car c = new Car();
System.out.println(c.num);
**Engine e = c.new Engine();**
e.go();
System.out.println(e.getClass().getName());
}
}
local Class
static Class
Static Class : 객체 생성을 하지 않고 사용 할 수 있는 이너 클래스
package com.inner;
//inner Class
public class Outer {// top - level class
static int count = 99;
//static inner class
static class Inner{
public void go() {
System.out.println(count);
}
}
public static void main(String[] args) {
System.out.println(Outer.count);
Outer.Inner i = new Inner();
i.go();
System.out.println(i.getClass().getName());
}
}
밑줄친 부분이 anonymous를 사용하는 방법
객체를 생성하지 않고 사용하기 위해 선언하는 클래스
→ Math.PI
더이상 자식 class를 만들지 않을려면 final keyword를 사용
//order 4번가지 하기
pay 메소드에서 trash를 사용하지 않으려고 money와 watch를 그룹을 묶어주는게 필요할때
valuable로 implements해서 pay 메소드에 valuable로 파라미터를 받게 되면 trash를 거를 수 있다.
만약에 다른 사용자가 서비스를 제공받고자 할때는 인터페이스만 주어서 사용할 메소드를 알게 해주면된다.
인터페이스 안에는 abc만 들어 있을때 다른 클래스에서는 go(){} 메소드를 알지도 못하고 사용도 못한다 .
사용할때는 싱글톤 패턴으로 인터페이스를 선언하고 인스턴스를 불러온다 static 은 어디서든 접근 가능하다.
→
IBookManager bookManager = IBookManagerImpl.getInstance();
<> GENERIC
한 컨테이너에 여러 타입을 삽일 할 수 있다
대부분의 소스들은 컴파일에서 타입이 다 정해지지만
Generic 타입은 컴파일이 끝나도 generic의 타입이 정해져 있지 않고 실행될때 type이 정해진다.
public static void main(String[] args) {
GenericTest<String> t1 = new GenericTest<>(); //이떄 타입이 정해진다.
GenericTest<Integer> t2 = new GenericTest<>(); // integer 은 기본형이였던 INT를 참조형으로 변환시킨것
GenericTest<Circle> t3 = new GenericTest<>(); //추상클래스 Circle 타입
}
public class GenericContainer<T> {
private T obj;
public GenericContainer(){}
public T getObj() { return obj; }
public void setObj(T t) { obj = t; }
}
장점 Iterator를 사용하면 삭제를 할때 전체적인 index나 null을 신경 쓸 필요가 없어진다
→반복 도중 List에 변화에 동적으로 대응
boolean hasNext() - 다음 인덱스에 존재하는지 묻는 메소드
next() 있는것을 호출하는 메소드
순서가 유지되며
일정이상의 데이터가 들어와도 알아서 배열을 늘려 준다.
ArrayList<Rect> shape =new ArrayList<>();
List<Rect> shape = new ArrayList<>();
//참조 객체를 유연하게 바꾸기 위해서 인터페이스 객체로 선언한다.
중복이 불가능한 배열
Set<Patient> patientList = new HashSet<Patient>();
patientList.add(p1);
patientList.add(p2);
Patient p3 = new Patient("환자3", 33, "010-3333-3333", "고열", "001", false);
univHospital.addPatient(p3);
univHospital.addPatient(p3);
//p3는 여러번 등록하더라도 한번만 등록됨
HashSet은 두 객체의 중복 여부를 체크할떄 List계열보다 엄격.
Object class의 hashCode()를 이용하여 그 값이 같은 경우에 같은 객체로 인식한다.
객체가 다르면 다른 hashCode() 리턴하도록 overriding 합니다.
객체를 정렬할때 그 객체는 comparable Interface를 구현해야한다.
Comparable Interface는 compareTo()라는 우선운위 비교 Method가 있다.
Corona c1 = new Corona("Corona5", 5, 5);
Corona c2 = new Corona("Corona7", 5, 7);
Corona c3 = new Corona("Corona3", 5, 3);
Corona c4 = new Corona("Corona9", 5, 9);
Corona c5 = new Corona("Corona1", 5, 2);
Corona c6 = new Corona("Corona1", 5, 6);
Corona c7 = new Corona("Corona1", 5, 5);
Corona c8 = new Corona("Corona1", 5, 8);
Corona c9 = new Corona("Corona1", 5, 10);
PriorityQueue<Corona> pq = new PriorityQueue<>();
pq.add(c1);
pq.add(c2);
pq.add(c3);
pq.add(c4);
pq.add(c5);
pq.add(c6);
pq.add(c7);
pq.add(c8);
while(!pq.isEmpty()) {
System.out.println(pq.poll());
//하나씩 꺼내 쓰는것
}
comparator : sort의 두번째 인자에서 사용되는 Interface
comparable : compareTo()를 이용하기 위한 Interface /compare : 인자 2개의 order를 비교하는 함수명"로 정리해보았습니다!
anonymous 표현
Collections.sort(list, new Comparator<Virus>() {
@Override
public int compare(Virus o1, Virus o2) {//앞쪽에서 뒤에쪽을 빼면 내림차순
return o1.getLevel() - o2.getLevel();
}
});
lamba 식 표현
Collections.sort(list, ( o1, o2 ) -> {
return o1.getLevel() - o2.getLevel();
});
프로그램 실행 중 발생하는 문제 상황
→ 데이터베이스 연결시, 파일 입/출력시, 배열의 잘못된 인덱스 접근
Error
복구가 불가능한 문제 상황
stackOverFlowError, NoSuchMethodError
Exception
복구 가능한 문제 상황
NullPointerException, IOException ,
RuntimeException -실행 도중에 발생하는 예외
Non-RuntimeException (예시 : InterruptedException) 실행이 안되기 때문에 무조건 예외 처리를 해줘야한다.
RuntimeException
런타임시 발생되므로 예측할 수 없음
nullPointerExcetption
버그로서 반드시 해결
try- catch-finally
예외가 발생한 곳에서 직접 처리하는 방식
try는 예외가 발생 할 수 있는 부분을 블럭으로 만들고
Catch는 그 예외상황을 처리할 방법을 블럭으로 만든다
finally는 예외 발생 여부와 관계 없이 무조건 수행되어야 하는 코드를 블럭으로 만든다/.
FileInputStream fis = null;
try {
fis = new FileInputStream("hello.txt");
}catch(FileNotFoundException e) {
System.out.println("Handing Exception : " + e.getMessage());
e.printStackTrace();
}finally {
try {
fis.close();
}catch(Exception e) {
e.printStackTrace();
}
}
throws
직접 처리하지 않고 호출한 쪽(호출자로 ) 예외처리를 위임 넘긴다.
public static void main(String[] args) throws FileNotFoundException {
// FileInputStream fins = new FileInputStream("hello.txt");
try catch throw
예외처리를 직접 처리하되 새로운 예외 발생시 호출한 쪽으로 넘긴다.
#3 throw e
FileInputStream fis = null;
try {
fis = new FileInputStream("hello.txt");
}catch(FileNotFoundException e) {
System.out.println("Handing Exception : " + e.getMessage());
e.printStackTrace();
throw new IOException(); // throw IOException 객체 to main()
}finally {
try {
fis.close();
}catch(Exception e) {
e.printStackTrace();
}
}
IO 관련 class는 IO관련 예외가 자주 발생 →try-catch 구문을 자주 사용한다.
AutoCloseable Interface가 추가 되어 try() 괄호 안의 자원은 finally에서 반납할 필요 없다. (자동반납)
AutoCloseable Interface
try( FileInputStream fis = new FileInputStream("hello.txt"); ) {
// fis code
}catch(IOException e) {
System.out.println("Handing Exception : " + e.getMessage());
e.printStackTrace();
}
// Not Runtime Exception
FileInputStream fis = null;
try { fis = new FileInputStream("hello.txt"); }
catch(Exception e) {
System.out.println("Handing Exception : " + e.getMessage()); //
}
catch(IOException e) { System.out.println("Handing Exception : " +
e.getMessage()); }
catch(FileNotFoundException e) { //
System.out.println("Handing Exception : " + e.getMessage()); // }
Exception > IOException > FileNotFoundException 상속관계가 존재 할떄는 구체적인 case부터 catch해야한다.
method를 재정의 할 떄 , 부모 calss의 method가 throws하는 예외의 범위를 넘어 설 수 없다.
(접근 지정자는 부모의 범위보다 넓은 경우만 사용 가능)
예외는 작게, 접근은 넓게 //**???
만약 상속 받은 메소드에 throw를 추가 하게 된다면 부모클래스도 같이 고쳐줘야한다.
데이터의 시작과 끝이 있고 양쪽 사이에 데이터가 이동하는데 이때 시자과 끝을 node 나머지를 stream이라고 한다.
Stream
Stream은 두가지로 나누어 생각하는데 문자Character와 비문자binary로 나누어 생각
InputStream - 입력용 outputStream - 출력용
입출력 단위 charater Stream (한글쓸때 charater Stream 사용), Byte Stream
데이터입출력방법
//?? Object는 무엇을 나타내나나
FIle.Separator는 os에 독립적으로 사용여 코딩 할 수 있다.
String dirName = "c:"+File.separator+"SSAFY"+File.separator+"mydir";
boolean success = file1.mkdir(); //폴더 만들기
if( success ) {
System.out.println("Folder Created!" );
}
}
File file2 = new File(dirName, "test2.txt");
//file2.createNewFile(); //파일 만들기
file2.delete();// 파일 삭제
}
IO를 할때 Buffered를 사용하게 되면 1000배 정도의 속도 차이가 난다.!
public class BufferPerformanceTest {
public static void main(String[] args) {
File src = new File("c:/Windows/explorer.exe");
File target = new File("c:/Temp/copied_explorer.exe");
try (FileInputStream fi = new FileInputStream(src); //autocloseable
FileOutputStream fo = new FileOutputStream(target);
BufferedInputStream bi = new BufferedInputStream(fi);
BufferedOutputStream bo = new BufferedOutputStream(fo);) {
copy("노드", fi, fo);
copy("보조", bi, bo);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void copy(String type, InputStream input, OutputStream output) throws IOException {
long start = System.nanoTime();
byte[] cart = new byte[1024];
int read = -1;
while ((read = input.read(cart)) > 0) {
output.write(cart, 0, read);
}
long end = System.nanoTime();
System.out.println(type + ", 소요 시간: " + (end - start) + "ns");
}
}
말그대로 객체를 직렬화하여 전송 가능한 형태로 만드는 것을 의미한다. 객체들의 데이터를 연속적인 데이터로 변형하여 Stream을 통해 데이터를 읽도록 해준다.
바이트 단위로 변환되어 전송되는 작업이다.
이것은 주로 객체들을 통째로 파일로 저장하거나 전송하고 싶을 때 주로 사용된다.
그럼 역직렬화(Deserialization)는?
직렬화된 파일 등을 역으로 직렬화하여 다시 객체의 형태로 만드는 것을 의미한다. 저장된 파일을 읽거나 전송된 스트림 데이터를 읽어 원래 객체의 형태로 복원한다.
전제 조건 !
직렬화를 위한 전제조건이 있다. 바로 직렬화가 가능한 클래스를 먼저 만드는 것이다.
Serializable 인터페이스를 implements 해서 직렬화가 가능한 클래스로 만든다.
숨기고 싶은 정보는
transient를 이용하여 직렬화 대상에서 제외하기
import java.io.Serializable;
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String ssn; //숨기고 싶은 정보를 transient 추가한다.
public Person(String name, int age, String ssn, String userId, String userPass) {
this.name = name;
this.age = age;
this.ssn = ssn;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", ssn=" + ssn + "]";
}
}
public class ObjectStreamTest {
public static void main(String[] args) {
File target = new File("c:" + File.separator + "SSAFY" + File.separator + "objPerson.dat");
Person person = new Person("홍길동", 20, "111111-2222222", "hong", "1234");
try {
//객체 저장
// ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(target));
// oos.writeObject(person);
// oos.close();
// 객체 로딩
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(target));
Object readed = ois.readObject();
if (readed != null && readed instanceof Person) {
Person casted = (Person) readed;
System.out.println(casted);
}
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
객체 직렬화를 위해 Serializable interfcae를 implement해야한다.
S
package com.io;
public class StringTest {
public static void main(String[] args) {
String s = "mylimeorange"; //스트링의 경우 두가지 모두 가능
String t = new String("mylimeorange");//1
String x = "mylimeorange"; //2
if(s==t) //주소값 비교
System.out.println("s == t"); //false
if(s.equals(t)) //내용 비교
System.out.println("s.equals(t)"); //true
if(s==x)//주소값 비교
System.out.println("s == x"); //true
System.out.println(s);
String b = s.concat("hello"); //concat된 s는 따로 다른공간에 저장되어 있고 s는 변화 x
System.out.println(s);
System.out.println(b);
s = s + "gogogo"; // 이렇게 사용하게 되면 원래 저장되어있던 s gogog가 추가로 들어 오는것이 아니라
//새로생긴 s로 가르키게 된다.
System.out.println(s);
StringBuilder sb = new StringBuilder("mylimeorange"); //StringBuilder는 원본이 수정 가능하다.
sb.append("hello");
sb.insert(2, 'q');
System.out.println(sb);
}
}