5_异常_多线程_设计模式_IO流_网络编程_反射

JavaSE_第五周

异常

异常的概念

什么是异常
概念
概念:程序在运行过程中出现的特殊情况

异常-----通过Jvm将异常的信息打印在控制台---告诉开发者(当前程序在某个环节出现了哪些问题!)
异常处理的必要性
异常处理的必要性:任何程序都可能存在大量的未知问题、错误,如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失。

异常的分类

Throwable
Throwable: 可抛出的,一切错误或异常的父类,位于java.lang包中
Error
Error:	JVM,硬件,执行逻辑错误,不能手动处理

例:Error:StackOverflowError 堆栈溢出错误
Exception
Exception: 程序在运行和配置中产生的问题,可处理
RuntimeException
RuntimeException:程序在执行过程中产生的异常,可处理,可不处理
CheckedException
CheckedException:受查异常,也称编译时期异常,Java语法原因,导致出现的问题,必须处理

异常的产生

自动抛出异常
自动抛出异常:当程序在运行时遇到不规范的代码和结果时,会产生异常
手动抛出异常
手动抛出异常:语法:throw new 异常类型(实际参数);
产生异常结果
产生异常结果:相当于遇到return语句,导致程序因异常而终止

异常的传递

异常的传递
异常的传递:按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)
受查异常
编译时期异常:
	语法通过不了,或者使用jdk提供的一些本身就带有异常的方法,不处理不行

	需要开发者要显示处理,否则报错!

受查异常:throws 声明异常,修饰在方法参数列表后端
运行时异常
运行时期异常:
	开发者可以显示处理,也可以不显示处理,无需声明异常,可以通过逻辑代码判断...
    public static void method2() {

        //显示处理
       /* try{
            int a = 20 ;
            int b = 0 ;



            System.out.println(a/b);
        }catch(Exception e){
            System.out.println("除数不能为0");
        }
        */


        int a = 20 ;
        int b = 0 ;

        //代码逻辑判断

        if(b!=0){
            System.out.println(a/b);
        }else{
            System.out.println("除数为0!");
        }
    }

常见的的运行时期异常

public class TestRuntimeException {

	public static void main(String[] args) {

		m6();
	}
	//java.lang.NullPointerException
	public static void m1() {
		Object o = null;
		o.hashCode();
	}
	//java.lang.ArrayIndexOutOfBoundsException
	public static void m2() {
		int[] nums = new int[4];
		System.out.println(nums[4]);
	}
	//java.lang.StringIndexOutOfBoundsException
	public static void m3() {
		String str = "abc";
		System.out.println(str.charAt(3));
	}
	//java.lang.ArithmeticException
	public static void m4() {
		System.out.println(3/0);
	}
	//java.lang.ClassCastException
	public static void m5() {
		Object o = new Integer(688);
		String s = (String)o;
	}
	//java.lang.NumberFormatException
	public static void m6() {
		new Integer("10A");
	}
}

JVM的默认处理方案

把异常的名称,错误原因及异常出现的位置等信息输出在了控制台 ,程序停止执行

异常的处理

捕获异常
try{
	可能出现异常的代码
}catch(Exception e){
	异常处理的相关代码,如:getMessage(); printStackTrace()
}finally{
	无论是否出现异常,都需执行的代码结构,常用于释放资源
}

try{
	int result = num1 / num2;//throw new ArithmeticException("/by zero")
	System.out.println(result);
}catch(Exception e) {// new ArithmeticException();
//	System.out.println("除数不能为零!");//处理方式1(自定义处理)
//	e.printStackTrace();//处理方式2(打印堆栈跟踪信息)
	System.out.println(e.getMessage());//处理方式3(打印throwable中详细消息字符串)
}

如果try不存在问题,最终try里面的代码执行完毕,执行finally代码;
finally中代码一定会执行,除非 jvm退出了!

finally语句中代码:
	释放系统资源
		 数据库连接对象.close();
         IO流中的流对象.close() ;
         jdbc执行对象Statement.close()
         获取数据结果集对象.close()...
public class ExceptionDemo4 {

    public static void main(String[] args) {

        try{
            //给定一个日期文本格式
            String source = "2021-2-23" ;

            //创建SimpleDateFormat对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd "); //存在问题:模式不匹配
            //解析
            Date date = sdf.parse(source) ;  //jvm执行:内存中:创建ParseException实例
            System.out.println(date);
        }catch (ParseException e){
            System.out.println("文本解析出现问题了...");

            //jvm退出
            System.exit(0) ;//0:正常终止jvm
        }finally{
            System.out.println("这里面的代码一定会执行...");
        }
        System.out.println("程序over...");
    }
}

抛出异常
throws :抛出,消极处理(告知了调用者,此方法可能会产生异常)
throws和throw的区别
throws和throw的区别
 1)throws: 位置在方法声明上
    throw:位置是在方法体中
 
 2)throws的后面跟的是异常类名,后面可以跟多个异常类名,中间使用逗号隔开
  throw的后面跟的是异常对象,一般情况 new XXXException() ; 后面跟的是某个具体的异常对象

 3)throws表示的是抛出异常的一种可能性!(不一定)
    throw:表示抛出异常的一种肯定性,执行这段代码,一会产生这个异常!

 4)throws抛出异常,调用者必须对当前这个方法中的异常进行处理
	throw抛出异常,方法体中通过逻辑代码控制!
public class ExceptionDemo2 {

    public static void main(String[] args) {

        System.out.println("程序开始了...");

        //捕获处理
        try {
            method1() ;
        } catch (ParseException e) {
            e.printStackTrace();
        }

        System.out.println("程序结束了...");

//        method2() ;
    }

    //throw
    private static void method2() {
//        定义两个变量
        int a = 10 ;
        int b = 0 ;

        if(b!=0){
            System.out.println(a/b);
        }else{
            //代码执行此处,抛出异常
            throw new ArithmeticException() ; //创建异常实例(匿名对象)
        }
    }


    //解析日期文本格式
    private static void method1() throws ParseException { //抛出异常
        String s = "2022-6-30" ;
        //String--->Date格式
        SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd") ;
        //解析
        Date date = sdf.parse(s) ; //日期格式

        System.out.println("date格式为:"+date);
    }
}

Throwable中的常用的功能

关于Throwable中的一些常用的功能
 	public String getMessage():获取异常的详细消息字符串
    public String toString():获取异常的简单描述
      		异常类名: 详细的消息字符串getMessage()
	public void printStackTrace():追踪错误的输出流(包含异常的toString()) (推荐)
public class ExceptionDemo3 {

    public static void main(String[] args) {
        try{
            //给定一个日期文本格式
            String source = "2021-2-23" ;

            //创建SimpleDateFormat对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //存在问题:模式不匹配
            //解析
            Date date = sdf.parse(source) ;  //jvm执行:内存中:创建ParseException实例
            System.out.println(date);
        }catch (ParseException e){
            //使用throwable 的功能
            //执行catch语句
           // String msgStr = e.getMessage() ;
          //  System.out.println(msgStr);
            // public String toString()
            //String msgStr = e.toString() ;
            //System.out.println(msgStr);
            //public void printStackTrace():
            e.printStackTrace();
        }
        System.out.println("程序over...");



    }
}

自定义异常

概念
自定义异常:需继承自Exception或Exception的子类,常用RuntimeException
构造方法
无参构造方法
有String message参数的构造方法
public class TestDefinedException {

	public static void main(String[] args) {

		Student s = new Student();
		try {
			s.setAge(333);
		} catch (AgeException e) {
			e.printStackTrace();//打印堆栈跟踪信息
		}
		
		try {
			s.setSex("666");
		}catch(SexMismatchException e){
			System.err.println("性别输入有误," + e.getMessage());
		}
	}

}

//受查异常
class AgeException extends Exception{

	public AgeException() {
		super();
	}

	public AgeException(String message) {
		super(message);
	}

	
}
//运行时异常
class SexMismatchException extends RuntimeException{

	public SexMismatchException() {
		super();
	}

	public SexMismatchException(String message) {
		super(message);
	}
	
}

class Student{
	private int age;
	private String sex;
	public int getAge() {
		return age;
	}
	public void setAge(int age) throws AgeException {
	
		if(age > 0 && age <= 253) {
			this.age = age;
		}else {
			//抛出异常
			throw new AgeException("年龄的正确取值区间:1~253");
		}
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		if(sex.equals("男") || sex.equals("女")) {
			this.sex = sex;
		}else {
			//抛出异常
			throw new SexMismatchException("性别的正确取值:'男'  或 '女'");
		}
	}
	
}

方法重写

带有异常声明的方法覆盖:
		方法名、参数列表、返回值类型必须与父类相同
		子类的访问修饰符和父类相同,或比父类更宽泛
		子类中的方法,不能抛出比父类或接口 更宽泛的异常	
public class TestOverrideExceptionMethod {

	public static void main(String[] args) {

		Super s = new Sub();
		try {
			s.method();
		} catch (ClassCastException e) {
			e.printStackTrace();
		}
		
		Printable p = new MyClass();
		try {
			p.print();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		MyClass mc = new MyClass();
		mc.print();
		
	}

}

class Super{
	public void method() throws ClassCastException {
		System.out.println("Super---method()");
	}
}

class Sub extends Super{
	public void method() {
		System.out.println("Sub---method()");
	}
}

interface Printable{
	public void print() throws IOException;
}

class MyClass implements Printable{

	@Override
	public void print(){
		System.out.println("MyClass -- print()");
	}
	
}

面试题

//1.打印输出结果
public class TestTryCatchFinally {

	public static void main(String[] args) {

		System.out.println(method(4));
	}
	
	public static int method(int n) {
		try {
			if(n % 2 == 0) {
				throw new RuntimeException("不能为偶数");
			}
			return 10;
		} catch (Exception e) {
			System.out.println("捕获异常...");
			return 20;
		}finally {
			System.out.println("方法结束");//必须执行
		}
		
	}

}

运行结果:
    	捕获异常...
		方法结束
		20

2.打印输出结果
public class FinallyTest {
    public static void main(String[] args) {

        System.out.println(getNum(20));//结果?
    }

    private static int getNum(int a) {

        System.out.println(a);//20
        try{
            a = 10 ;
            System.out.println(a/0);
        }catch (ArithmeticException e){ //执行catch
            a = 30 ;   // 30
            return a ;  //形式返回路径了: return 30
        }finally{
            a = 40 ;

        }
        return a ;

    }
}

运行结果:30
    
	如果处理异常的时候,try...catch...finally格式,catch语句中有return语句, finally中代	码还会执行吗?如果会执行,是在catch之前还是之后?
	finally会执行,具体在finally之前(中间),return 变量:已经形成返回路径!
	finally代码一定会执行,除非是在执行finally语句之前,jvm退出了!
        
3. finally,final,finalize的区别
 finally:捕获异常的finally语句, try...catch...finally
	一般用途就是释放相关的资源(连接对象资源,流资源,执行(jdbc)资源)
		
    finally中的代码一定会执行,在执行finally之前,如果jvm退出了,finally语句是不会执行的!
fiinal,状态修饰符
		修饰类       :该类不能继承
		修饰成员变量 :此时是一个常量
		修饰成员方法  :该方法不能重写
finalize:Object类中的方法,开启垃圾回收器:System.gc()---->实际调用的finalize()
		回收没有更多引用的对象!

4.处理异常的方式有几种
捕获异常	
	try...catch...finally
	try...catch...catch...
抛出异常
	throws:抛出在方法上
	throw:在方法体中抛出

5.throwthrows的区别
		throws:		
			1)在方法声明上使用
			2)throws后面跟的异常类名,中间使用逗号隔开可以跟多个异常类名 (为了使用方便)
			3)在当前方法上如果使用throws,调用者必须对这个方法处理!
			4)throws表示出现异常的可能性!
			
		throw:
			1)方法语句体中使用
			2)后面跟的异常对象,一般匿名对象(具体异常具体抛出) 
					throw new xxxException() ;
			3)它对异常的处理,是通过逻辑代码进行判断
			4)throw表示出现异常的肯定性!

多线程

什么是线程

进程
进程:正在运行的应用程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间 和系统资源。
程序是静止的,只有真正运行时的程序才能被称为进程

单核CPU在任何时间点上,只能运行一个进程,宏观并行,微观串行

多进程---为了提高CPU的使用率!同时玩游戏,听音乐,并不是同时进行,而是CPU的一点点时间片在
多个进程之前进行高效的切换!(和CPU有关系)
线程
线程(Thread) 一个轻量级的进程(是最小的基本单元)  -----进程的某个任务
程序的一个顺序控制流程,是CPU的基本调度单位。
多线程
多个线程组成,彼此间完后不同的工作,交替执行,成为多线程

多个线程在互相抢占CPU的执行权,执行具有随机性
Java程序运行原理
java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主 线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
JVM是多线程吗
Jvm,是一个假想计算机,是一个进程,是一个多线程环境,默认包含主线程(main)
通过代码创建多个独立的线程,与main并发执行
  不断的创建对象------ 需要使用垃圾回收器回收,------垃圾回收线程

至少存在两条线程: main和垃圾回收线程(开启垃圾回收器)
进程与线程之间的区别
进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位
一个程序运行后至少有一个进程
一个进程可以包含多个线程,但是至少需要一个线程
进程之间不能共享数据段地址,但同进程的线程之间可以

线程的组成

任何一个线程都具有基本的组成部分:
	CPU时间片:操作系统(os)会为每个线程分配执行时间
	
	运行数据:
		堆空间:存储线程需使用的对象,多个线程可以共享堆中的空间
		栈空间;存储线程需使用的局部变量,每个线程都拥有独立的栈
	
	线程的逻辑代码

多线程的实现

使用Java程序如何实现多线程?
  
  线程----->依赖于进程---->需要有一个进程----->创建进程------需要创建系统资源
  
  Java语言不能创建系统资源----创建系统资源---->使用C/C++
  
  提供了一个类-----> java.lang.Thread:线程是程序中的执行线程!
  jvm运行多个线程并发的执行!
多线程环境的实现方式
继承Thread类
继承关系
		(1)自定义一个类,继承自Thread类
		(2)重写Thread类中的run方法
		(3)在main线程中,创建该类对象
		(4)启动线程(start())
实现Runnable接口
实现Runnable接口的方式
		(1)自定义类实现Runnable接口
		(2)重写Runnable接口中的run 方法
		(3)创建该类对象-----资源类(可以被多个线程共用)
			创建Thread类对象将资源类作为参数进行传递
			Thread(Runnable target,String name)
		(4)启动线程

接口实现关系----(面向接口编程---- 多个线程对象在操作同一个资源:共享的概念)  里面用到代理模式之静态代理

代理模式
		静态代理
		动态代理
 			1)jdk动态代理---基于接口实现(InvocationHandler)
 			2)cglib动态代理----基于子类实现
线程池
线程池
	1)实现Runnable接口
		自定义类实现Runnable接口
		重写Runnable接口中的run方法
		使用工厂类(Executors)创建线程池对象(newFixedThreadPool)
		提交异步任务(Future<?> submit(Runnable task))
		关闭线程池(shutdown())
	2)实现Callable接口
		自定义类实现Callable接口
		重写Calllable接口中的call方法
		使用工厂类(Executors)创建线程池对象(newFixedThreadPool)
		提交异步任务(<T> Future<T> submit(Callable<T> task))
		获取结果(V get())
		关闭线程池(shutdown())
Java能够开启多线程吗?
Java是不能够开启的,借助于底层语言开启多线程环境(根据系统资源)
 start()
 private native void start0(); 非Java语言实现(native:本地方法)
使用匿名内部类的方式实现
public class ThreadDemo {

    public static void main(String[] args) {

        //方式1
        new Thread(){
            @Override
            public void run() {
                for(int x = 0 ; x < 100 ; x ++){
                    System.out.println(getName()+":"+x);
                }
            }
        }.start();

        //方式2:
        //自定义类一个---实现Runnable接口----> 资源类
        new Thread(new Runnable(){

            @Override
            public void run() {
                for(int x = 0 ; x < 100 ; x ++){
                    System.out.println(Thread.currentThread().getName()+":"+x);
                }
            }
        }).start();

        System.out.println("-----------------------------");

        //内部类(有名字类)----->匿名内部类----> 前提:如果一个接口中只有一个抽象方法,这个接口@FunctionalInterface:函数式接口
        //jdk8以后---拉姆达表达式
        //企业中:jdk8新特性使用到./jdk7/jdk5
        //要求:在开发中使用枚举类/拉姆达


    }
}

Thread类(java.lang.Thread)

构造方法
public Thread():无参构造

public Thread(Runnable target):有参构造:里面传递Runnable接口 (间接使用就 静态代理)
成员方法
线程名称
public final void setName(String name):设置线程名称

public final String getName():获取线程名称
线程调度
线程有两种调度模型: 
分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 

抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级	高的线程获 取的 CPU 时间片相对多一些。 

Java使用的是抢占式调度模型。
线程优先级
public final int getPriority():获取线程的优先级

默认的优先级:5
public final void setPriority(int newPriority):更改优先级

优先级的常量表
public static final int MAX_PRIORITY 10         最高优先级
public static final int MIN_PRIORITY 1          最低优先级
public static final int NORM_PRIORITY 5         默认优先级

优先级越大的----当前这个线程抢占到的CPU执行权越大
守护线程
public final void setDaemon(boolean on)
 该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
 这个方法调用必须在启动之前调用! (注意事项)
 
 public static Thread currentThread()返回正在执行的线程对象的引用
public class ThreadDaemonDemo {

    public static void main(String[] args) {
        //创建线程类对象
        ThreadDeamon td1 = new ThreadDeamon() ;
        ThreadDeamon td2 = new ThreadDeamon() ;

        //设置线程名称
        td1.setName("关羽");
        td2.setName("张飞");

        //启动线程之前,将td1,td2标记为守护线程 ,jvm会自动退出
        td1.setDaemon(true);
        td2.setDaemon(true);


        //执行两个子线程
        td1.start();
        td2.start();

        Thread.currentThread().setName("刘备");
        for(int x = 0 ; x <10 ; x ++){
            //public static Thread currentThread()
           //获取当前正在执行的线程main
            System.out.println(Thread.currentThread().getName()+":"+x);
        }


    }


}
public class ThreadDeamon extends  Thread {

    //td1,td2子线程
    @Override
    public void run() {
        for(int x = 0 ; x < 100; x ++){
            System.out.println(getName()+":"+x);
        }
    }
}

线程休眠
public static void sleep(long millis) throws InterruptedException:
		线程休眠 ,参数为毫秒值
		线程休眠----时间休眠期到了,又继续去执行线程! (属于阻塞式方法)
线程放弃
public static void yield() :暂停当前正在执行的线程对象,并执行其他线程

当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
线程结合
public final void join()
		throws InterruptedException等待该线程终止。
		允许其他线程加入到当前线程中,并优先执行

底层依赖于 wait(long mills)非Java语言 (线程阻塞)
线程中断
public final void stop()让线程停止,过时了,但是还可以使用。
public void interrupt()中断线程。 把线程的状态终止,并抛出一个InterruptedException。

线程的状态

线程的状态有六种:
NEW---初始状态
RUNNABLE---运行状态
BLOCKED---阻塞状态
WAITING---无限期等待
TIMED_WAITING---限期等待
TERMINATED---终止状态

注:JDK5之前为7种状态,JDK5之后就绪(Ready)、运行(Running)统称为Runnable

线程安全

线程不安全
线程不安全:
	当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
	
	临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
	
	原子操作:不可分割的多步操作,被视为一个整体,其顺序和步骤不可打乱或缺省
校验多线程安全问题的标准
校验多线程安全问题的标准
	1)当前是否是一个多线程环境     
	2)是否存在共享数据       
	3)是否有多条语句对共享数据操作  
解决多线程安全问题
基本思想:让程序没有安全问题的环境。
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可

使用synchronized(锁对象){        //多个线程必须使用的是同一把锁(钥匙)
		多条语句对共享数据操作
	}
线程同步
同步机制
	synchronized基于jvm,------->多个线程持有"锁标志",通过同步代码块控制访问的字段,每一个持有的锁必须是同一个,
	当某个线程如果执行了并且进入到同步代码块中,其他线程在当前线程执行期间,不能够持有该锁,
当前这个线程执行完毕,会释放"锁标志";其他线程如果进入到同步代码块中,持有该同一个"锁"
	synchronized和Lock都属于可重入锁,Lock锁更灵活: lock()/unlock(),通用方式:synchronized
	synchronized(锁对象){
		语句体(多条语句多共享数据的操作)
	}
同步方式1
同步方式:
	同步代码块:
		synchronized(临界资源对象){//对临界资源对象加锁
			//代码(原子操作)
		}
	注:
		每一个对象都有一个互斥锁标记,用来分配给线程的
		只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的的同步代码块
		线程退出同步代码块时,会释放相应的互斥锁标记
同步方式2
同步方法:
	synchronized 返回值类型 方法名称 (形参列表){ // 对当前对象(this)加锁
 		//代码(原子操作)
	}
	注:
		只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
		线程退出同步方法时,会释放相应的互斥锁标记
同步的规则
同步规则:
	注意:
		只有在调用包含同步代码块的方法或同步方法时,才需要对象的锁标记
		如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用
同步方法的锁对象
同步方法 ---->这里面的锁对象是谁?  
	this:代表当前类对象的地址值引用 //锁对象可以是任意的java类对象
静态的同步方法?它的锁对象是谁?  
	锁对象:当前类的字节码文件对象---"反射思想"
            类名.class
已知线程安全的类
已知JDK中线程安全的类
	StringBuffer
	Vector
	Hashtable
	以上类中的公开方法,均为synchronized修饰的同步方法
线程的同步和异步
线程的同步:
	形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续

线程的异步:
	形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,二者竞争时间片,并发执行

LOCK接口

JDK5以后提供了一比Synchronized更广泛的锁定操作.也可以实现锁对象控制多个线程对共享资源的访问!
java.util.concurrent.locks.Lock:接口
与synchronized比较
lock与synchronized比较,显示定义,结构更灵活
提供更多实用性方法,功能更强大,性能更优越
常用方法
void lock()	//获取锁,如锁被占用,则等待
boolean tryLock()	//尝试获取锁(成功返回true,失败返回false,不阻塞) 
void unlock()	//释放锁
子实现类
ReentrantLock
重入锁:
	ReentrantLock: Lock接口的实现类,与synchronized一样具有互斥锁功能
//银行取款案例
public class TestReentrantLock {

	public static void main(String[] args) throws InterruptedException {

		
		Account acc = new Account("1001","123456",2000D);
		
		Husband h = new Husband(acc);
		Wife w = new Wife(acc);
		
		Thread t1 = new Thread(h);
		Thread t2 = new Thread(w);
		
		t1.start();
		t2.start();
	}

}

class Husband implements Runnable{
	Account acc;
	
	public Husband(Account acc) {
		this.acc = acc;
	}
	@Override
	public void run() {
			this.acc.withdrawal("1001", "123456", 1200D);
	}
}

class Wife implements Runnable{
	Account acc;
	 public Wife(Account acc) {
		 this.acc = acc;
	 }
	@Override
	public void run() {
			this.acc.withdrawal("1001", "123456", 1200D);
	}
}

class Account{
	Lock locker = new ReentrantLock();
	
	String cardNo;
	String password;
	double balance;
	public Account(String cardNo, String password, double balance) {
		super();
		this.cardNo = cardNo;
		this.password = password;
		this.balance = balance;
	}
	
	public void withdrawal(String no,String pwd,double money) {
			//开启锁 synchronized(this){
			locker.lock();
			try {
				System.out.println("请稍后。。。");
				if(this.cardNo.equals(no) && this.password.equals(pwd)) {
					System.out.println("验证成功,请稍后。。。");
					if(money < balance) {
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						balance -= money;
						System.out.println("取款成功,当前余额为:" + balance);
					}else {
						System.out.println("卡内余额不足!");
					}
				}else {
					System.out.println("卡号或密码错误!");
				}
			}finally {
				//释放锁 }
				locker.unlock();
			}
	}
}
ReentrantReadWriteLock
读写锁:
	ReentrantReadWriteLock:
    	一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁
		支持多次分配读锁,使多个读操作可以并发执行
		
	互斥规则:
			写——写:互斥,阻塞
			读——写:互斥,读阻写塞,写阻读塞
			读——读:不互斥,不阻塞
			在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率
public class TestReentrantReadWriteLock {

	public static void main(String[] args) {
		final Student s = new Student();
		
		Callable<Object> writeTask = new Callable<Object>() {

			@Override
		
			public Object call() throws Exception {
				s.setValue(111);
				return null;
			}
		};
		
		Callable<Object> readTask = new Callable<Object>() {

			@Override
			public Object call() throws Exception {
				s.getValue();
				return null;
			}
		};
		 
		ExecutorService es = Executors.newFixedThreadPool(20);
		
		long start = System.currentTimeMillis();
		
		for(int i = 0; i < 2; i++) {
			es.submit(writeTask);
		}
		
		for(int i = 0; i < 18; i++) {
			es.submit(readTask);
		}
		
		//停止线程池(不再接受新任务,将现有任务全部执行完完毕)
		es.shutdown();
		while(true) {
			System.out.println("结束了吗?");
			if(es.isTerminated()) {
				System.out.println("终于结束了");
				break;
			}
		}
		
		//....
		System.out.println(System.currentTimeMillis() - start);
	}

}

class Student{
//	ReentrantLock rLock = new ReentrantLock();
	ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
	ReadLock rl = rwl.readLock();
	WriteLock wl = rwl.writeLock();
	
	int value;

	//读
	public int getValue() {
		rl.lock();
		try {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return this.value;
		}finally {
			rl.unlock();
		}
	}

	//写
	public void setValue(int value) {
		wl.lock();
		try {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.value = value;
		}finally {
			wl.unlock();
		}
	}
	
	
}

线程间的通信

线程的通信:
 	等待:
		public final void wait()
		public final void wait(long timeout)
		必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有的锁标记,
		同时此线程因obj处在无限期等待的状态中,释放锁,进入等待队列。
	
	通知:
		public final void notify()
		public final void notifyAll()
		必须在对obj加锁的同步代码块中,从obj的waiting中释放一个或全部线程,对自身没有任何影响
等待唤醒机制
多个线程在使用资源的时候,如果当前资源并非同一个资源,可能会出现锁;
引入生产者消费者模式思想: (操作必须同一个资源对象!)
	生产者线程----不断的产生数据----如果当前没有数据了,等待生产者线程产生数据
					当前这个数据已经有了,需要通知(notify())消费者线程,使用这个数据
	消费者线程----不断的使用数据----如果当前有数据了,等待使用完毕
					如果没有数据了,通知(notify())生产者线程---产生数据....
sleep()和wait()的区别
1)是否会释放锁
	sleep()的调用,不会释放锁---->通过Thread类的访问的
	wait()的调用,会立即释放持有的"锁标志"---->通过锁对象访问的
2)来源不同
	sleep()来源于Thread类
	wait()----Object
3)都可能会有异常中断异常
		sleep() throws InterruptedException{}
		wait() throws InterruptedException{}
为什么wait(),notify(),notifyAll()等方法都定义在Object类中
1,这些方法存在与同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

静态代理

Thread类本身底层使用的就是静态代理

什么是静态代理
  通过代理角色帮助真实角色完成业务,对真实角色业务进行增强!
  开发中,打印日志/事务管理/权限校验....

 真实角色(目标角色):专注于自己的事情
 代理角色:帮助真实角色进行方法增强!
 	特点:
		真实角色和代理角色都需要实现同一个接口!

动态代理
	jdk动态代理:通过反射方式---基于接口
	第三方jar包:cglib动态代理----基于子类实现
/**
 * Thread类本身底层使用的就是静态代理
 *
 * 什么是静态代理
 *  通过代理角色帮助真实角色完成业务,对真实角色业务进行增强!
 *  开发中,打印日志/事务管理/权限校验....
 *
 *  真实角色(目标角色):专注于自己的事情
 *  代理角色:帮助真实角色进行方法增强!
 *  特点:
 *      真实角色和代理角色都需要实现同一个接口!
 *
 *
 *      举例:
 *              结婚这件事情
 *
 *              1)自己完成结婚这件事情(真实角色)
 *              2)加入一个代理角色:婚庆公司,帮助我们完成结婚这件事情!  WeddingCompany
 *
 *
 *
 *               //Thread:就是代理角色
 *         //MyRunnable:真实角色---->run()---> 业务操作....
 *
 *
 *
 *
 *         反射---------->动态代理
 *                       jdk动态代理:通过反射方式---基于接口
 *                       第三方jar包:cglib动态代理----基于子类实现
 *
 */

//测试类
public class ThreadDemo {

    public static void main(String[] args) {

        //测试
        //接口多态的方式
//        Marry marry = new You() ;
        //marray.happyMarry();
        //具体类
        You you = new You() ;
        you.happyMarry();

        System.out.println("--------------------------------------------------");

        //创建婚庆公司类对象:代理角色
        //创建真实角色对象
        You you2 = new You() ;
        WeddingCompany wc = new WeddingCompany(you2) ;
        wc.happyMarry(); //对真实角色You类型中happyMarry()进行了增强



        //对比Thread类
        //创建线程的实现方式2: 自定义类
        //class MyRunnable implement Runnable{
        //      public void run(){
        //
        //              ...
        //      }
        // }
        /**
         *
         * 源码:
         * public class Thread implements Runnable{
         *
         * private Runnable target; //Runnable接口类型
         *
         *
         *      public Thread(Runnable target,String name) {
         *
         *          ....
         *      }
         *      @Override
         *     public void run() {
         *         if (target != null) { //如果当前Runnable接口对象(通过子实现类实例化)
         *             target.run();
         *         }
         *     }
         * }
         */



    }
}

//定义接口:Marry:结婚
interface  Marry{
    //结婚
    void happyMarry() ;
}

//定义类:You----- 真实角色
class You implements  Marry{

    @Override
    public void happyMarry() {
        System.out.println("很开心,要结婚了...");
    }
}

//定义一个代理角色:目的就是happyMarry(),进行增强
class WeddingCompany implements  Marry{//代理角色也实现该接口

    //声明一个真实角色类型:You
    private You you ;
    //构造方法                          //  Thread t1 = new Thread(自定义的类实现Runnbale接口的对象) ;//代理
    public WeddingCompany(You you){
        this.you = you ;
    }
    @Override
    public void happyMarry() {
        //结婚之前
        before() ;
        //真实角色:专注于自己的事情:结婚
        you.happyMarry(); //结婚
        //结婚之后
        after() ;
    }

    public void before() {
        System.out.println("结婚之前,布置婚礼现场...");
    }
    public void after() {
        System.out.println("结婚之后,男方付尾款...");
    }

}

经典问题

死锁
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁

一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁

死锁现象:当前多个线程操作的资源对象,不是同一个对象,而且加入同步代码块之后,出现了一种互相等待的情况
public class ThreadDemo {
    public static void main(String[] args) {

        //创建资源类对象DieLock
        DieLock d1 = new DieLock(true) ;
        DieLock d2 = new DieLock(false) ;

        //创建线程类对象
        Thread t1 = new Thread(d1) ;
        Thread t2 = new Thread(d2) ;

        //分别启动线程
        t1.start();
        t2.start();
        /**
         * 情况1:
         *  if objA    t1
         * else objB    t2
         *
         * 情况2:
         * else objB   t2先抢占到
         * if objA
         *
         *
         * 理想状态:
         *  else objB       t2抢占到
         * else objA
         * if objA          t1执行
         * if objB
         */
    }
}
//自定义类中,创建两把锁对象
public class MyDieLock {

    public static final Object objA = new Object() ;
    public static final Object objB = new Object() ;
}

//资源类
public class DieLock  implements  Runnable{

    //声明一个boolean类型的变量
    private boolean flag ;
    public DieLock(boolean flag){
        this.flag = flag ;
    }

    @Override
    public void run() {

        //加入判断
        if(flag){
            //true
            synchronized (MyDieLock.objA){ //objA锁
                System.out.println("if objA"); //if ObjA
                synchronized (MyDieLock.objB){
                    System.out.println("if objB");
                }
            }
        }else{
            //false
            synchronized (MyDieLock.objB){    //t2
                System.out.println("else objB"); //else objB
                synchronized(MyDieLock.objA){
                    System.out.println("else objA");
                }
            }
        }


    }
}

生产者与消费者
生产者与消费者问题是多线程同步的一个经典问题。生产者和消费者同时使用一块缓冲区,生产者生产商品放入缓冲区,消费者从缓冲区中取出商品。我们需要保证的是,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可取出商品。
public class TestPAC {

	public static void main(String[] args) {

		final MyStack ms = new MyStack();
//		ms.push("A");
//		ms.push("B");
//		ms.push("C");
//		ms.push("D");
//		ms.push("E");
//		ms.push("E");
//		
//		ms.pop();
//		ms.pop();
//		ms.pop();
//		ms.pop();
//		ms.pop();
//		ms.pop();
		
		Thread t1 = new Thread() {
			public void run() {
				for(char ch = 'A'; ch <= 'Z'; ch++) {
					ms.push(ch + "");
				}
			}
		};
		Thread t2 = new Thread() {

			
			public void run() {
				for(int i = 0; i < 26; i++) {
					ms.pop();
				}
			}
		};
		
		t1.start();
		t2.start();
	}

}

class MyStack{
	
	private String[] values = new String[] {"","","","",""};
	int size = 0;
	
	public synchronized void push(String str) {
		
		this.notify();//唤醒对方
		while (values.length == size) {
			System.out.println("满了");
			try { this.wait();} catch (Exception e) {}//结束 ---> 暂停(等待)
		}
		
		System.out.println(str + "入栈");
		values[size] = str;
		size++;
	}
	
	public synchronized void pop() {
		
		this.notify();//唤醒对方
		while (size == 0) {
			System.out.println("空了");
			try {this.wait();} catch (Exception e) {}//结束 ---> 暂停(等待)
		}
		
		System.out.println(values[size-1] + "出栈");
		values[size-1] = "";
		size--;
	}
}
import java.util.ArrayList;
import java.util.List;

public class TestProducerAndConsumer {

	public static void main(String[] args) {
		MyQueue mq = new MyQueue();
		
//		mq.offer("A");
//		mq.offer("B");
//		mq.offer("C");
//		mq.offer("D");
//		mq.offer("E");
//		
//		System.out.println(mq.poll());
//		System.out.println("以下是遍历内容:");
//		
//		mq.show();
		
		Produce1 p1 = new Produce1(mq);
		Produce2 p2 = new Produce2(mq);
		Consumer c1 = new Consumer(mq);
		
		p1.start();
		p2.start();
		c1.start();
		
		System.out.println("main end");
	}
}

class Consumer extends Thread{
	MyQueue mq;
	public Consumer(MyQueue mq) {
		this.mq = mq;
	}
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.println(mq.poll() + "被移除");
		}
	}
}

class Produce1 extends Thread{
	MyQueue mq;
	public Produce1(MyQueue mq) {
		this.mq = mq;
	}
	
	public void run() {
		System.out.println("Produce1启动");
		for (char ch = 'A'; ch <= 'E'; ch++) {
			mq.offer(ch);
		}
		System.out.println("Produce1结束");
	}
}
class Produce2 extends Thread{
	MyQueue mq;
	public Produce2(MyQueue mq) {
		this.mq = mq;
	}
	
	public void run() {
		System.out.println("Produce2启动");
		for (char ch = 'F'; ch <= 'J'; ch++) {
			mq.offer(ch);
		}
		System.out.println("Produce2结束");
	}
}
//我的队列
class MyQueue{
	private List values = new ArrayList();
	private int max = 4;
	
	//存入队列
	public synchronized void offer(Object o) {
		if(values.size() == max) {
			//进来线程,停下
			try {
				this.wait();
				//唤醒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notifyAll();
		System.out.println(Thread.currentThread().getName() + "存入第" + (values.size() + 1) + "个元素");
		values.add(o);
	}
	
	//从队列取出
	public synchronized Object poll() {
		if(values.size() == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notifyAll();//唤醒因mq对象而进入无限期等待的线程对象(一个)
		return values.remove(0);
	}
	
	public void show() {
		for (Object obj : values) {
			System.out.println(obj);
		}
	}
}

龟兔赛跑问题

/**
 * //需要又有个赛道:Race-----乌龟/兔子---共用赛道
 */
public class Race implements Runnable {
    //定义变量 :胜利者
    public static String winner ; //默认值:null

    @Override
    public void run() {
        //定义一个for循环;模拟 1-100之间:步数
        for(int x = 1 ; x<=100; x ++){
            //需要让兔子模拟 它睡觉
            //如果线程对象所在的名称是兔子 并且 是 每10步
            if(Thread.currentThread().getName().equals("兔子")&& (x % 10==0)){
                //睡眠50毫秒
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //判断比赛是否结束
            boolean flag = gameOver(x) ;//参数步数
            if(flag){//true
                break ;
            }

            System.out.println(Thread.currentThread().getName()+"跑了---->"+x+"步"); //比赛结束之后打印出来每一个线程跑了多少不
        }

    }

    //定义一个比赛是否结束的方法
    private boolean gameOver(int steps) {//步数

        //情况1:如果当前已经存在胜利者---比赛结束
        if(winner!=null){
            return true ;
        }{
            //情况2
            //如果steps>=100 :说明已经跑完了

            if(steps>=100){
                // //记录下一胜利者 :
                winner = Thread.currentThread().getName() ;//胜利者
                System.out.println("winner is--->"+winner);
                return true ;
            }
        }
        return false ;



    }


    //用户线程
    public static void main(String[] args) {

        //将赛道被共用
        Race race = new Race() ;

        //创建两条线程
        Thread t1 = new Thread(race,"兔子") ;
        Thread t2 = new Thread(race,"乌龟") ;

        //启动线程
        t1.start();
        t2.start();
    }
}

线程池

线程池概念
现有问题:
	线程是宝贵的资源,单个线程约占1MB空间,过多分配易造成内存溢出。
	频繁的创建及销毁线程会增加虚拟机的回收频率,资源开销,造成程序性能下降
线程池:
	线程容器,可设定线程分配的数量上限
	将预先创建的线程对象存入池中,并重用线程池中的线程对象
	避免频繁的创建和销毁
线程池原理
将任务提交给线程池,由线程池分配线程,运行任务,并在当前任务结束后复用线程
获取线程池
常用的线程池接口和类(所在包java.util.concurrent);
 	Executor:	线程池顶级接口
 	ExecutorService:	线程池接口,可通过submit(Runnable task) 提交任务代码
 	Executors工厂类:	通过此类可以获得一个线程池

	通过newFixedThreadPool(int nThreads) 获取固定数量的线程池,
		参数: 指定线程池中线程的数量

	通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,没有上限
     
    void shutdown():关闭线程池中以前提交的异步任务!
public class TestThreadPool {

	public static void main(String[] args) {

		//线程池(引用) ---》Executors工具类(工厂类)
		ExecutorService es = Executors.newFixedThreadPool(3);
		Runnable task = new MyTask();
		
		es.submit(task);
		es.submit(task);
		es.submit(task);
		es.submit(task);
		
	}

}

class MyTask implements Runnable{

	@Override
	public void run() {
		for(int i = 1; i <= 1000; i++) {
			System.out.println(Thread.currentThread().getName() + " MyTask:" + i);
		}
	}
	
}
Callable接口
Callable接口:
	public interface Callable<V>{
		public V call() throws Exception;
	}
	
	JDK5.0加入,与Runnable接口类似,实现之后代表一个线程任务
	Callable具有泛型返回值,可以声明异常
Future接口
Future接口:
 	概念:异步接受ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值
 		
	方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)
public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //使用工厂类创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //提交异步任务
      /*  pool.submit(new MyRunnable()) ;
        pool.submit(new MyRunnable()) ;*/

        Future<Integer> f1 = pool.submit(new MyCallable(100));
        Future<Integer> f2 = pool.submit(new MyCallable(200));

        //获取结果
        Integer result1 = f1.get();
        Integer result2 = f2.get();
        System.out.println(result1);
        System.out.println(result2);

        //关闭线程池
        pool.shutdown();
    }
}
import java.util.concurrent.Callable;

//两个线程分别进行计算结果:求和
public class MyCallable implements Callable<Integer> {

    //声明变量
    private int number ;
    public MyCallable(int number){
        this.number = number ;
    }


    @Override
    public Integer call() throws Exception { //call方法的返回值和Callbe<类型>保持一致
        int sum = 0 ; //结果变量
        for(int x = 1 ; x<= number ; x ++){
            sum += x ;
        }
        return sum;
    }
}

线程安全的集合

CopyOnWriteArrayList:
 	线程安全的ArrayList,加强版读写分离
	写有锁,读无锁,读写之间不阻塞,优于读写锁
	写入时,先copy一个容器副本,再添加新元素,最后替换引用
 	使用方式与ArrayList无异
	
CopyOnWriteArraySet:
	线程安全的Set,底层使用CopyOnWriteArrayList实现
	唯一不同在于,使用addIfAbsent()添加元素,会遍历数组
	如存在元素,则不添加(扔掉副本)

ConcurrentHashMap:
	初始容量默认为16段(segment) , 使用分段锁设计
	不对整个Map加锁,而是为每个segment加锁
	当多个对象存入同一个segment时,才需要互斥
	最理想的状态为16个对象分别存入16个segment,并行数量为16
	使用方式与HashMap无异
public class TestCopyOnWriteArrayList {

	public static void main(String[] args) {

		List<String> list = new CopyOnWriteArrayList<String>();//接口引用指向实现类对象,更容易更换实现
		list.add("A");
		
		Set<String> set = new CopyOnWriteArraySet<String>();
		set.add("B");
		
		Map<String,String> map =  new ConcurrentHashMap<String,String>();
		map.put("","");
	}

}
Collections中的工具方法
Collections中的工具方法:
	Collections工具类中提供了多个可以获得线程安全的方法
	public static <T> Collection<T> synchronizedCollection(Collection<T> c)
	public static <T> List<T> synchronizedList(List<T> list)
	public static <T> Set<T> synchronizedSet(Set<T> s)
	public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
	public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
	public static <K,V> SortedMap(K,V) synchronizedSortedMap(SortedMap<K,V> m) 

	JDK1.2提供,接口统一,维护性高,但性能没有提升,均以synchronized实现
public class TestCollectionFonSyn {

	public static void main(String[] args) {
		
		List<String> list = new ArrayList<String>();
		
		List<String> safeList = Collections.synchronizedList(list);
		
		safeList.add("A");
		safeList.add("A");
		safeList.add("A");
		safeList.add("A");
	}

}

class SafeCollection<E>{
	private Collection c = null;
	final Object o = new Object();
	
	public SafeCollection(Collection c) {
		this.c = c;
	}
	public void add(E e) {
		synchronized(o) {
			c.add(e);
		}
	}
	
}

Queue接口(队列)

Queue接口(队列): Collection的子接口,表示队列FIFO(First In First Out)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBY3x7Xk-1615108342170)(Collection体系集合.png)]

常用方法

抛出异常:
	boolean add(E e) //顺序添加一个元素(到达上限后再添加,则会抛出异常)
	E remove() // 获得第一个元素并移除(如果队列没有元素时,则会抛出异常)
	E element() // 获得第一个元素但不移除(如果队列没有元素,则会抛出异常)
返回特殊值:推荐
	boolean offer(E e) // 顺序添加一个元素(到达上限后再添加,则会返回false)
	E poll() // 获得第一个元素并移除(如果队列没有元素,则会返回null)
	E peek() // 获得第一个元素但不移除(如果队列没有元素时,则会返回null)

ConcurrentLinkedQueue

ConcurrentLinkedQueue:
		线程安全,可高效读写的队列,高并发下性能最好的队列
		无锁,CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
		V:要更新的变量;E: 预期值;N:新值
		只有当V = E时,V = N; 否则表示已被更新过,则取消当前操作
public class TestQueue {

	public static void main(String[] args) {

//		Queue q = new LinkedList();
		Queue q = new ConcurrentLinkedQueue();
		
		q.offer("A");
		q.offer("B");
		q.offer("C");
		
		System.out.println(q.poll());
		System.out.println(q.poll());
		System.out.println(q.poll());
	}

}

BlockingQueue(阻塞队列)

BlockingQueue接口(阻塞队列)
	Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法
			
	方法:
		void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待
		E take() // 获取并移除此队列头部元素,如果没有可用元素,则等待

 		可用于解决生产者和消费者的问题
ArrayBlockingQueue
ArrayBlockingQueue:
		数组结构实现,有界队列。(手工固定上限)
		BlockingQueue<String> abq = new ArrayBlockingQueue<String>(10)
LinkedBlockingQueue
LinkedBlockingQueue:
		链表结构实现,无界队列,(默认上限:Integer.MAX_VALUE)
		BlockingQueue<String> lbq = new LinkedBlockingQueue<String>()

Timer定时器

java.util.Timer:定时器 (定时工具)
可安排任务执行一次,或者定期重复执行
构造方法
public Timer():创建一个定时器
成员方法
public void cancel()终止此计时器,丢弃所有当前已安排的任务

public void schedule(TimerTask task,Date time):在指定的日期时间时完成定时任务
            参数1:为定时任务(定时业务...)
             参数2:规定的日期时间(String:"2021-2-25 18:00:00")

public void schedule(TimerTask task,long delay,long period):
		固定延迟的时间间隔来完成这个任务(重复执行的)

public void schedule(TimerTask task,long delay):在指定延迟时间后执行这个任务

TimerTask定时任务

定时任务:TimerTask :抽象类  ------> implements  Runnable

public abstract void run() :指定计时器任务操作
public class ThreadDemo2 {

    public static void main(String[] args) {
        //创建一个定时器
        Timer timer = new Timer() ;


        //启用定时任务
        //public void schedule(TimerTask task,long delay):在指定延迟时间后执行这个任务
       // timer.schedule(new MyTask(timer),3000); //3秒后这个任务

        //public void schedule(TimerTask task,long delay,long period):固定延迟的时间间隔来完成这个任务(重复执行的)
        timer.schedule(new MyTask(),2000,3000); //2秒后执行这个定时任务,然后每经过3秒重复执行定时任务


    }
}
//自定一个类 继承TimerTask
class MyTask extends TimerTask{
    private Timer t ;
    public MyTask(){

    }
    public MyTask(Timer t){
        this.t = t ;
    }

    @Override
    public void run() {
        //for(int x = 0 ; x< 5 ;x ++){
//            System.out.println("bom...");
//            t.cancel(); //终止了
        //}
        System.out.println("bom");

    }
}

题目

需求:"2021-2-25 18:00:00"---->删除 D盘下:
	JavaEE_2011
		day_2_25  中所有后缀为.java文件
                        描述这个文件----->java.io.File类

        
public void schedule(TimerTask task,Date time)
        
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test05 {

    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();

        String str = "2021-2-26 08:36:00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse(str);

        timer.schedule(new MyTask(),date);

    }
}

class MyTask extends TimerTask {
    private Timer t;
    public MyTask(){}

    public MyTask(Timer t){
        this.t = t;
    }
    @Override
    public void run() {
        File file = new File("K://source//");
        File[] files = file.listFiles();
        if(files != null){
            for(File f : files){
                if(f.isFile()){
                    if(f.getName().endsWith(".java")){
                        System.out.println(f.delete());
                    }
                }
            }
        }
    }
}

设计模式

面向对象设计原则

面向对象思想设计原则

在实际的开发中,我们要想更深入的了解面向对象思想,就必须熟悉前人总结过的面向对象的思想的设计原则

单一职责原则
其实就是开发人员经常说的”高内聚,低耦合” 
也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则 
开闭原则
核心思想是:一个对象对扩展开放,对修改关闭。 
其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。 也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到 这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则 是可以改变和扩展的。
里氏替换原则
核心思想:在任何父类出现的地方都可以用它的子类来替代。
其实就是说:同一个继承体系中的对象应该有共同的行为特征。
依赖注入原则
核心思想:要依赖于抽象,不要依赖于具体实现。 
其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他 类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
接口分离原则
核心思想:不应该强迫程序依赖它们不需要使用的方法。 
其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一 个接口中。
迪米特原则
核心思想:一个对象应当对其他对象尽可能少的了解 
其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部 工作原理,它可以使各个模块耦合度降到最低,促进软件的复用

常见见设计模式

设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

好处:使用设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 

设计模式不是一种方法和技术,而是一种思想 设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设 计的程序可复用 学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成
设计模式的分类
创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)

结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)\

行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备
忘录模式、迭代器模式、解释器模式。(10个)
简单工厂模式
简单工厂----(静态工厂方法模式)
	提供一个工厂类:
		提供静态的功能 (使用多态:提高扩展性):负责创建一些类的实例!

优点:不需要让具体类创建对象了
弊端:一旦有新的类型增加,需要修改工厂类代码,代码量增加了...
public class PatternDemo {
    public static void main(String[] args) {

        //没有使用设计模式之前:
        //对象的创建
        Dog d = new Dog() ;
        d.eat();
        d.sleep();
        Cat c = new Cat() ;
        c.eat();
        c.sleep();

        System.out.println("--------------------------");

        //简单工厂
        //需要通过一个工厂类:创建这些具体动物实例!
        /*Cat cat = AnimalFactory.createCat();
        cat.eat();
        cat.sleep();
        Dog dog = AnimalFactory.createDog();
        dog.eat();
        dog.sleep();
        Pig pig = AnimalFactory.createPig();
        pig.eat();
        pig.sleep();*/
        Animal animal = AnimalFactory.createAnimal("dog"); //new Dog() ;
        animal.eat();
        animal.sleep();

        animal = AnimalFactory.createAnimal("cat"); //new Cat() ;
        animal.eat();
        animal.sleep();

        animal = AnimalFactory.createAnimal("pig"); //new Pig() ;
        animal.eat();
        animal.sleep();

        animal = AnimalFactory.createAnimal("monkey") ;
        if(animal!=null){
            animal.sleep();
            animal.eat();
        }else{
            System.out.println("工厂类中没有提供该动物实例的创建...");
        }

    }
}
/**
 * 工程类---创建具体动物实例
 */
public class AnimalFactory {

    //里面一个功能静态的功能
    //创建猫类
   /* public static Cat createCat(){
        return new Cat() ;
    }

    //创建狗类
    public static Dog createDog(){
        return new Dog() ;
    }

    //创建猪类对象
    public static Pig createPig(){
        return new Pig() ;
    }*/


   //返回值使用父类型
   public static Animal createAnimal(String type){//参数:为当前类型
       if("dog".equals(type)){
           return new Dog() ;  //Animal 对象名 = new Dog() ;//多态
       }else if("cat".equals(type)){
           return new Cat() ;
       }else if("pig".equals(type)){
           return new Pig() ;
       }else{
           return null ;
       }

   }
}
public class Animal {

    public void eat(){
        System.out.println("动物需要吃饭...");
    }
    public void sleep(){
        System.out.println("动物都需要休息...");
    }
}
//具体的狗类
public class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }

    @Override
    public void sleep() {
        System.out.println("狗躺着睡觉...");
    }
}
public class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }

    @Override
    public void sleep() {
        System.out.println("猫趴着睡觉...");
    }
}
public class Pig extends Animal {

    @Override
    public void eat() {
        System.out.println("猪吃白菜...");
    }

    @Override
    public void sleep() {
        System.out.println("猪卧着睡觉...");
    }
}

工厂方法模式
工厂方法模式
     1)有一个抽象类:Animal----具体的子类:(Dog/Cat)
     2)工厂接口----->创建具体类的实例 (抽象功能)
     3)每一个具体的类----都对应一个该类的工厂(DogFactory,CatFactory)

     优点:每一个具体的类都使用具体的工厂类创建该类实例
     弊端: 代码量增加!!
public class PatternDemo {
    public static void main(String[] args) {

        Factory factory = new CatFactory() ; //接口多态
        Animal animal = factory.createAnimal(); //new Cat() ;
        animal.eat();
        animal.sleep();
        System.out.println("-----------------------");
        factory = new DogFactory() ;
        animal = factory.createAnimal() ;
        animal.eat();
        animal.sleep();
    }
}
public abstract class Animal { //动物类

    public abstract void eat() ;
    public abstract void sleep() ;
}
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }

    @Override
    public void sleep() {
        System.out.println("狗趴着睡觉...");
    }
}
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    @Override
    public void sleep() {
        System.out.println("猫侧着睡觉");
    }
}
//工厂接口:创建具体的动物实例
public interface Factory {

    //提供扩展性---使用多态
    public Animal createAnimal() ;
}
//狗的工厂类
public class DogFactory implements Factory {
    @Override
    public Animal createAnimal() {
        return new Dog() ;
    }
}
//猫的工厂类
public class CatFactory implements Factory {
    @Override
    public Animal createAnimal() { //抽象类多态
        return new Cat();
    }
}

单例设计模式
概述
单例模式就是要确保类在内存中只有一个对象,该实例必须自动创建,并且对外提供。

优点:在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系 统的性能。 
缺点:没有抽象层,因此扩展很难。 职责过重,在一定程序上违背了单一职责
分类
饿汉式
饿汉式:(不会出现问题的单例!)
	特点:在类加载的时候就创建该类实例!
		1)在某个类中,成员变量位置:创建该类的静态实例
		2)构造方法私有化
		3)需要提供静态的功能,返回值是它本身 ---创建静态实例
		
例子:Runntime类就是单例,是饿汉式
public class Student {

    //为了保证数据的安全性:加入私有修饰
    private static Student s = new Student() ;


    private Student(){}//构造方法私有化

    //提供对外的公共访问---静态的
    public static Student getStudent(){
        return s ;
    }

}
public class SinglePattern {

    public static void main(String[] args) throws IOException {


        //使用Student类
        //外界:依然可以通过无参构造方法创建对象
        /*Student s1 = new Student() ;
        Student s2 = new Student() ;
        System.out.println(s1==s2);*/

        Student s1 = Student.getStudent();
        Student s2 = Student.getStudent();
        System.out.println(s1==s2);
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1.getClass() == s2.getClass());
        //getClass()---->获取正在运行的Java类对象:Class字节码文件对象
        //Class ----class com.qf.pattern_06.Student

        System.out.println("---------------------------");
        Runtime runtime = Runtime.getRuntime();
        runtime.exec("calc") ;//里面执行一些指令
        runtime.exec("shutdown -s -t 3600") ;//里面执行一些指令
        runtime.exec("shutdown -a") ;//取消关机

        int count = runtime.availableProcessors();
        System.out.println(count);//cpu核数

    }
}

懒汉式
懒汉式
 	1)某个类中,构造方法私有化---目的:外界不能够直接通过构造方法创建对象
 	2)成员变量位置:声明一个静态变量: 该类型
	3)提供对外的公共访问方法: 静态
 		需要判断:
	 		如果当前变量为null,这个时候,创建一个对象!

懒汉式:可能出现问题的单例模式!
	延迟加载
	懒加载
	都可能造成多线程安全问题
	
	将当前这个静态的公共的方法---加入同步代码块---解决线程安全问题!
public class Teacher {

    private static Teacher t ;  //类型:Teacher

    //构造方法私有化
    private Teacher(){

    }

    //提供静态的对外的公共访方法
    //t1,t2,t3 ----线程
   /* public static Teacher getTeacher(){

        //如果当前对象存在,一直使用;如果为null,就创建一个对象!
        //判断
        //每一个线程对象---都在访问资源(访问冲突)
        synchronized (Teacher.class){

            if(t == null){
                t = new Teacher() ;
            }
            return t ;
        }

    }*/

   //将synchronized定义在方法声明上
   public synchronized static Teacher getTeacher(){ //锁对象:Teacher.class

       //如果当前对象存在,一直使用;如果为null,就创建一个对象!
       //判断
       //每一个线程对象---都在访问资源(访问冲突)
           if(t == null){
               t = new Teacher() ;
           }
           return t ;

   }
}

IO框架

File类

概念
java.io.File:文件和目录(文件夹)路径名的抽象表示形式。
构造方法
构造方法
	 public File(String pathname):直接传递字符串路径 (推荐....)
	 public File(File parent,String child):
     	参数1:File对象:某个目录(文件夹)File对象
        参数2:当前指定的具体路径
     public File(String parent,String child):分别指定父目录的路径以及子文件的路径
功能
创建功能
创建文件:
	public boolean createNewFile() throws IOException 
		如果不存在,创建空文件,返回true;否则,false
		必须存在目录,目录都不存在(系统找不到指定的路径。)
创建文件夹(目录)
	public boolean mkdir():创建目录(文件夹)

	public boolean mkdirs():创建此抽象路径名指定的目录,包括所有必需但不存在的父目录时,创建!
            
如果操作File所示的路径:没有带盘符----->文件/目录创建到哪里了?
将文件或者文件夹创建在当前项目路径下!
删除功能
删除功能:
	public boolean delete():可以删除文件或者文件夹(目录),如果要删除目录,前提必须是空目录!
重命名功能
重命名功能:
 public boolean renameTo(File dest)
         重新命名此抽象路径名表示的文件
 
 将D盘下这个文件进行重命名
       情况1:
               重名的时候:源文件/目标文件都是相同地址 :d盘下----只是重命名
 
       情况2:
               源文件/目标文件不再同一个路径下: 剪切+重命名  
判断功能
判断功能:
      public boolean isAbsolute() :是否为绝对路径
      public boolean isDirectory():File所表示的路径是否指定的是一个目录
      public boolean isFile():是否是文件
      public boolean isHidden():是否是一个隐藏文件
      public boolean canRead():是否可读
      public boolean canWrite():是否可写
      public boolean exists():是否存在
获取功能
获取功能
	public String getAbsolutePath():获取绝对路径
	public String getPath():获取路径
	long lastModified():获取文件最后一个被修改的时间
	public String getName():获取文件/目录的名称
	public long length():获取文件的长度(单位是字节...)
	public String[] list() :获取当前路径下所表示的文件或者文件夹的字符串数组
	public File[] listFiles():获取某个路径的下所表示的文件或者文件夹的File数组
高级获取功能
高级获取功能
	 通过文件名称过滤器:获取某个路径下所有 的文件以及文件夹的String[]/File[]数组
	 public File[] listFiles(FilenameFilter filter) :
	 public String[] list(FilenameFilter filter) :
递归
递归:
	方法调用方法本身的一种现象,并非方法嵌套方法!
	Math.max(Math.max(10,20),35) ; 这个方法嵌套
递归的条件
递归的条件:
  	1)必须定义一个方法:通过方法调用方法本身的一种现象
  	2)必须有出口条件(结束条件),否则死递归
	3)存在一定的规律...
	
注意:
  构造方法:没有递归的!
阶乘
public class DiGuiDemo {

   /* public DiGuiDemo(){ //构造方法
//        DiGuiDemo() ;
    }*/

    public static void main(String[] args) {
        //循环
        //阶乘思想:最终结果变量
        int jc = 1 ;
        for(int x= 1; x <=5 ; x ++){
            jc *= x ;
        }
        System.out.println("5的阶乘是:"+jc);

        System.out.println("---------------------------------");

        //递归的思想
        System.out.println("5的阶乘是:"+getNum(5));
    }

    //定义一个方法
    private static int getNum(int n) {//5//4 //3
        //结束条件
        if(n==1){
            return 1 ;
        }else{
            return n*getNum(n-1) ;
        }
    }
}

不死神兔
/**
 * 需求:[不死神兔],有一对兔子,三个月后每一个月产生一对兔子,小兔子长到第三个月又产生一对兔子
 * 假设兔子都不死,第20个月,总共有多少对兔子!
 *
 *
 * 分析:
 *      第一个月:1
 *      第二个月:1
 *      第三个月:2
 *      第四个月:3
 *      第五个月:5
 *      第六个月:8
 *      .....
 *
 *    规律:
 *          已知两个数据:第一个月/第二个月:1
 *         从第三个月开始:每个月兔子的对数:等于前两个月之和!
 *
 *     结束条件:第一个月和第二个都是1
 *
 *   解决方案:
 *      1)数组方式
 *      2)递归方式
 */
public class DiGuiTest {
    public static void main(String[] args) {

        //方式1:定义一个数组:动态初始化
        int[] arr = new int[20] ;//角标:0开始,, 长度20

        //第一个月和第二个月都是1
        arr[0] = 1 ;
        arr[1] = 1 ;
        // 从第三个月开始:每个月兔子的对数:等于前两个月之和!
        for(int x = 2 ;x < arr.length ; x++){
            arr[x] = arr[x-1] + arr[x-2] ;
        }
        System.out.println("第二十个月兔子的对数有:"+arr[19]);//6765

        System.out.println("----------------------------------------------");

        //定义一个方法:getRabbitNum(20):  参数代表:第几个月
        System.out.println("第二十个月兔子的对数有:"+getRabbitNum(20));
    }

    //n:表示的月份
    private static int getRabbitNum(int n) {

        //结束条件:第一个月/第二个月:1
        if(n==1 || n== 2){
            return 1 ;
        }else{
            //从第三个月开始:中间的月份:
//            每个月兔子的对数:等于前两个月之和!
            //3             (3-1)                           3-2
            return getRabbitNum(n-1) + getRabbitNum(n-2) ;//6765
        }

    }
}


递归删除指定的目录
/**
 * 需求:递归删除指定的目录
 *
 * 删除:当前 项目下的demo文件夹
 * 分析:
 *
 *  1)封装文件夹(目录):File对象
 *  2)定义一个删除文件夹方法
 *      deleteSrc(file对象)
 *
 *  3)
 *      获取File对象所表示路径下的所有文件夹以及文件的File数组
 *  4)判断数组不为空,遍历
 *      获取到每一个File对象
 *      判断:如果这个file对象代表的是文件夹isDirectory()
 *      继续回到2)
 *      如果不是文件夹,
 *         直接删除文件,(查看删除了谁,获取文件名称)
 *
 *   5)直接将demo文件删除掉
 *
 *
 *
 *
 */
public class DiGuiTest2 {
    public static void main(String[] args) {
        //创建File对象:表示抽象路径
        File srcFile = new File("demo") ;

        //定义一个删除文件夹的方法
        deleteSrc(srcFile) ;
    }

    //递归删除文件夹的方法
    public static void deleteSrc(File srcFile) {
        //获取这个srcFile下面的所有文件夹以及文件的file数组
        File[] fileArray = srcFile.listFiles();
        //防止空指针
        if(fileArray!=null){
            //遍历File数组
            for(File file:fileArray){
                //判断如果file对象是文件夹
                if(file.isDirectory()){
                    //继续调用 deleteSrc方法来删除
                    deleteSrc(file);
                }else{
                    //是文件
                    System.out.println(file.getName()+"---"+file.delete());
                }
            }
            //删除demo文件
            System.out.println(srcFile.getName()+"---"+srcFile.delete());
        }
    }
}

题目
/**
 * 需求:
 *      获取指定盘符下的文件并且该文件是以.
 *      jpg结尾的文件
 *
 *  分析:
 *      1)使用File对象描述一个盘符:D盘
 *      2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组
 *      3)如果File[]不为空,然后遍历----获取到每一个File对象
 *          3.1) 如果当前File对象所表示的是一个文件:boolean isFile()
 *              3.2)如果是文件,要判断当前文件是以.jpg结尾的文件
 *                      String类:endsWith(String xx)
 *                  获取文件名称...
 *
 *
 */
 
 public class FileTest {

    public static void main(String[] args) {

        //1)使用File对象描述一个盘符:D盘
        File srcFile = new File("D://") ;

        //2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组
        File[] fileArray = srcFile.listFiles();
        //非空判断
        if(fileArray!=null){
            //遍历---获取到每一个File对象
            for(File file: fileArray){
//                3.1) 如果当前File对象所表示的是一个文件:boolean isFile()
                if(file.isFile()){
                    //是文件
                    //如果是文件,要判断当前文件是以.jpg结尾的文件
                    if(file.getName().endsWith(".jpg")){
                        //获取文件
                        System.out.println(file.getName());
                    }
                }
            }
        }
    }
}

/**
 * 需求:
 *      获取指定盘符下的文件并且该文件是以.
 *      jpg结尾的文件
 *
 *  分析:
 *      1)使用File对象描述一个盘符:D盘
 *      2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组
 *      3)如果File[]不为空,然后遍历----获取到每一个File对象
 *          3.1) 如果当前File对象所表示的是一个文件:boolean isFile()
 *              3.2)如果是文件,要判断当前文件是以.jpg结尾的文件
 *                      String类:endsWith(String xx)
 *                  获取文件名称...
 *
 *
 *       上面这种可以,麻烦---- 获取到了所有的文件以及文件夹的File数组---->加入一系列判断!
 *
 *       在File对象(D盘符)获取的时候整个盘符下是文件以及文件夹File数组,就已经能够拿到每一个file对象?
 *       文件名称过滤器:FilenameFilter
 *
 *        通过文件名称过滤器:获取某个路径下所有 的文件以及文件夹的String[]/File[]数组
 *      public File[] listFiles(FilenameFilter filter) :
 *              形式参数:是一个接口类型:文件名称过滤器 -----匿名内部类/自定义一个子实现类
 *              boolean accept(File dir,String name)测试指定文件是否应该包含在某一文件列表中。
 *                      参数1:dir:指定的文件所在的目录
 *                      参数2:当前文件的名称
 *
 *                   返回值:true/false取决于 当前是否能够将当前dir所在的目录中指定的文件名称添加在文件列表中
 *                   true:添加到文件列表中
 *                   false:不会添加到文件列表中
 */
public class FileTest2 {

    public static void main(String[] args) {
        //表示D盘
        File srcFile = new File("D://") ;

        //直接获取到文件列表
        // public File[] listFiles(FilenameFilter filter) :
        // public String[] listFiles(FilenameFilter filter) :
        String[] strArray = srcFile.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
//                return false;
                //加入自己的逻辑
                //返回值为true:将文件名称所在的目录添加到列表中
                //dir:目录  name:文件名称
                File file = new File(dir,name) ;
               // System.out.println(file);
               // return true ;

                //需要的是文件/以及以.jpg结尾的文件
                /*boolean flag1 = file.isFile() ;//判断是文件
                boolean flag2 = file.getName().endsWith(".jpg");
                return flag1 && flag2 ;*/

                return file.isFile() && file.getName().endsWith(".jpg") ;

//
            }
        });

        for(String s : strArray){
            System.out.println(s);
        }


    }
}

IO流

流的概念

概念:内存与存储设备之间传输数据的通道

流的用途

IO流:-----> 底层创建系统资源,在不同的设备之间进行数据传输的!

I: 输入---->磁盘上有一个文件,让通过流的方式将文件读出来!

O:输出 ---->在内存中—通过输出流,在某个磁盘下输出文件并且写入内容

流的分类

IO流的分为:
	按流的方向
				输入流:将存储设备中的内容读取到内存中
				输出流:将内存中的内容写入到存储设备中
	按流的类型:
				字节流:以字节为单位,可以读取所有数据
				字符流:以字符为单位,只能读写文本数据

字节流

字节流的抽象基类: 
InputStream ,OutputStream。

xxxxInputStream
XXXXOutputStream
都是子类
字节输入流
InputStream : 字节输入流						            
	public int read(){}
	public int read(byte[] b){}
	public int read(byte[] b, int off, int len){}
字节文件输入流
FileInputStream
	读取 fos3.txt 文件
	使用步骤:
		1)创建具体的子类对象:FileInputStream
            public FileInputStream(String name)
		2)读取数据:一次读取一个字节/一次读取一个字节数组
			public abstract int read()throws IOException
			public abstract int read(byte[] bytes)throws IOException
        3)释放资源
public class FileInputStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建文件输入流对象
        FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java") ;
        //单独将带中文的文件---输出在控制台上,由于强制类型转换---一个中文(UTF-8)对应三个字节,跟前面的英文字符对应的字节拼接不上,所以造成乱码

        //使用循环实现----不知道循环多少次,使用while
        //结束条件:当前字节数为-1,流已经到末尾了
      //一次读取一个字节
        //将赋值,判断,以及获取写在一块
        int by = 0 ; //字节数开始为0,没有开始读
        while((by=fis.read())!=-1){
            //将获取的都字节数---转换字符
            System.out.print((char)by);
        }

        //释放资源
        fis.close();
    }
}


public class FileInputStreamDemo2 {
    public static void main(String[] args) throws IOException {

        //创建一个文件输入流对象
        FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java") ;
        // public abstract int read(byte[] bytes)throws IOException :一次读取一个字节数组
       //重复性代码:使用循环实现:while循环
        //结束条件:如果流已经读取到末尾了,返回-1
        //一次读取一个字节数组:模板代码
        //一般情况:字节数组的长度:1024或者是1024的整数倍
        //在使用new String(bytes)获取实际的内容,带上len的使用:实际的内容
        //String(bytes[] bytes,0,len):每一次读取的时候,都是从角标0开始,读取实际长度!
        //赋值,判断,获取都写在while循环中
        byte[] bytes = new byte[1024] ; //构造字节缓冲区
        int len = 0 ;//开始没有读取
        while((len=fis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

        //资源释放
        fis.close();
    }
}

字节缓冲输入流
字节缓冲输入流对象: (高效的字节输入流)
BufferedInputStream(InputStream in) :默认大小的缓冲区(创建字节缓冲输入流)
public int read()
public int read(byte[] bytes)
public class BufferedInputStreamDemo  {

    public static void main(String[] args) throws IOException {
        //创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("bos.txt")) ;

        //读取数据
        //一次读取一个字节
     /*   int by = 0 ;
        while((by=bis.read())!=-1){
            System.out.print((char)by);
        }*/
        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }


//        资源关闭
        bis.close();
    }
}

字节输出流
OutputStream: 字节输出流						
	public void write(int n){}
	public void write(byte[] b){}
	public void write(byte[] b, int off, int len){}
字节文件输出流
FileOutputStream:
	输出一个文本文件,并且写内容
	使用步骤:
           1)创建输出流对象
                   public FileOutputStream(String name)
                   throws FileNotFoundException
           2)写入内容
                 写入的字节....
           3)关闭资源
写入数据的功能
 public void write(int b) throws IOException :写入一个字节
 public void write(byte[] b) throws IOException:写入一个字节数组
 
 public void write(byte[] b,int off,int len):写入当前字节数组的一部分
构造方法
public FileOutputStream(File file,boolean append) throws FileNotFoundException
		参数1:File对象所表示的文件
		参数2:是否进行末尾追加(true,文件末尾处追加) 在之前操作记录之上,继续追加!
		
如果输出文件写入内容需要进行换行操作:
换行符号
     windows操作系统:
                 "\r\n"

     Linux操作系统中:
              	"\n"
 
     Mac系统:
             	"\r"
public class FileOutputStreamDemo3 {

    public static void main(String[] args) throws IOException {

        //File表示文件
        File file  = new File("fos3.txt") ;

        //创建文件输出流对象
        //public FileOutputStream(File file,boolean append) throws FileNotFoundException
        FileOutputStream fos = new FileOutputStream(file,true) ;

        //写入内容
        for(int x = 0 ; x < 10 ; x ++){
            fos.write(("hello"+x).getBytes());
            //写入这个换行符号
            fos.write("\r\n".getBytes());
        }


        //释放资源
        fos.close();

    }
}

异常处理
使用文件输出流的时候,加入异常处理:try...catch...finally
 
实际开发中:IO流/JDBC ---->加入异常处理,try...catch...finally:统一处理
public class FileOutputStreamDemo4 {

    public static void main(String[] args) {



        //方式1: 跟流对象相关的一一处理
       // method1() ;

        //方式2:统一处理
        method2();


    }

    public static void method2() {
        FileOutputStream fos = null ;
        try {
            fos = new FileOutputStream("fos4.txt") ;
            //写入内容
            fos.write("hello,FileOutputStream".getBytes());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void method1() {

        //创建文件输出流对象
        FileOutputStream fos = null ;
        try{
            fos = new FileOutputStream("fos4.txt") ;
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }

        //写入内容
        try {
            fos.write("hello,FileOutputStream".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        //释放资源
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字节缓冲输出流
BufferedOutputStream:字节缓冲输出流(高效的字节流):是OutputStream的子类
作用:内部提供一个(默认大小)缓冲区
构造方法
public BufferedOutputStream(OutputStream out):创建一个默认大小的字节缓冲输出流对象,
                  形式参数:抽象类---实际参数--需要该抽象类的子类对象
                  
BufferedInputStream/OutputStream:只是提供一个缓冲区,针对文件读/写的操作,还是
 需要使用基本字节流:InputStream/OutputStream
 
源码
/*public BufferedOutputStream(OutputStream out) {
      this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
      super(out);
      if (size <= 0) {
         throw new IllegalArgumentException("Buffer size <= 0");
      }
      buf = new byte[size];
}

byte[] buf  = new byte[8192] ;长度*/

编码表

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就
将各个国家的文字用数字来表示,并一一对应,形成一张表。

ASCII:美国标准信息交换码。用一个字节的7位可以表示。

ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。

GB2312:中国的中文编码表。

GBK:中国的中文编码表升级,融合了更多的中文文字符号。

GB18030:GBK的取代版本BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。

Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicodeUTF-8:最多用三个字节来表示一个字符。

UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:它将Unicode编码为00000000-0000007F的字符,用单个字节来表示,它将Unicode编码为00000080-000007FF的字符用两个字节表示,它将Unicode编码为00000800-0000FFFF的字符用3字节表示
public class TestEncoding {

	public static void main(String[] args) throws UnsupportedEncodingException {

		String s1 = "你好世界123abc喆";
		
		byte[] bs = s1.getBytes("GBK");//GBK文本"编码"为二进制
		
		
		String s2 = new String(bs,"BIG5");//二进制"解码"为GBK文本
		System.out.println(s2);
		
		byte[] bs2 = s2.getBytes("BIG5");

		String s3 = new String(bs2,"GBK");
		System.out.println(s3);
	}

}

字符流

桥转换流
字符转换输出流
字符输出流的使用
      OutputStreamWriter 是字符流通向字节流的桥梁
构造方法
构造方法
 OutputStreamWriter(OutputStream out)
      使用平台默认的编码集(UTF-8)--->对写入的内容进行编码 (字符转换输出流)

 OutputStreamWriter(OutputStream out,String charsetName):执行编码操作
成员方法
 void write(char[] cbuf)
            写入字符数组。
abstract  void write(char[] cbuf, int off, int len)
          写入字符数组的某一部分。
 void write(int c)
           写入单个字符。
 void write(String str)
          写入字符串。
 void write(String str, int off, int len)
        写入字符串的某一部分。
public class OutputStreamWriterDemo {
    public static void main(String[] args) throws IOException {

        //创建字符转换输出流对象:
        //字符流中指向字节流
        OutputStreamWriter osw = new OutputStreamWriter(
                new FileOutputStream("osw.txt")) ;//UTF-8格式编码

        //写入数据
//        char[] chs = {'a','b','c','d','e'} ;
        //void write(char[] cbuf)
//        osw.write(chs);
        //abstract  void write(char[] cbuf, int off, int len)
//        osw.write(chs,1,2);

        //void write(int c)
//        osw.write('a');

        osw.write("我爱中国");

        //刷新流
        osw.flush();

        //释放资源
        osw.close();
    }
}

字符转换输入流
字符转换输入流:InputStreamReader
		是字节流通向字符流的桥梁: (解码:将流中的内容使用指定的字符集解码成字符!)
构造方法
public InputStreamReader(InputStream in):平台默认字符集
public InputStreamReader(InputStream in,String charsetName):指定的字符集
成员方法
int read()
	读取单个字符。
int read(char[] cbuf)
 	将字符读入数组。
abstract  int read(char[] cbuf, int off, int len)
	将部分字符读入数组
public class InputStreamReaderDemo {

    public static void main(String[] args)  throws IOException {

        //读取当前项目osw.txt
        //指定字符集:GBK
//        InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"GBK") ;
        InputStreamReader isr = new InputStreamReader(
                new FileInputStream("osw.txt")) ;//默认:UTF-8

        //读取内容
        //int read(char[] cbuf)
        //一次读取一个字符数组
        char[] chs = new char[1024] ;
        int len = 0 ;//字符数
        while((len=isr.read(chs))!=-1){
            System.out.println(new String(chs,0,len));
        }

        //关闭资源
        isr.close();


    }
}

字符文件流
字符流操作文件两种方式

1)使用字符转换流操作:读写操作
2)使用FileReader/FileWriter:读写操作
构造方法
public FileReader(String fileName)

public FileWriter(String fileName)
public class CopyTest {

    public static void main(String[] args) throws IOException {


        //封装源文件
        FileReader fr = new FileReader("d://FileOutputStreamDemo.java") ;

        //封装目的地文件
        FileWriter fw = new FileWriter("MySelf.java") ;

        //读写操作
        //一次读取字符数据
        char[] chs = new char[1024] ;
        int len = 0 ;
        while((len=fr.read(chs))!=-1){
            //读一个字符数组,写入字符数组
            fw.write(new String(chs,0,len));
            //刷新
            fw.flush();
        }

        //释放资源
        fw.close();
        fr.close();
    }
}

字符缓冲流
字符缓冲输出流
字符缓冲输出流:BufferedWriter
针对文本来进行高效的写入(高效的字符流)
只是提供缓冲区:针对文件的操作还需要使用基本流
构造方法
public BufferedWriter(Writer out):创建一个默认大小的字符缓冲输出流对象(默认值足够大了)
成员方法
void newLine()
		写入一个行分隔符。

void write(char[] chs)
void write(char[] cbuf, int off, int len)
         写入字符数组的某一部分。
void write(int c)
	   写入单个字符。
void write(String str)
void write(String s, int off, int len)
public class BuffferedWriterDemo {

    public static void main(String[] args) throws IOException {
        //创建一个字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ; //默认大小:defaultBufferSize:8192
       /* BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream("bw.txt"))) ;*/

       //直接写入字符串内容
        bw.write("Hello");
        //之前的换行:通过系统写入"\r\n"
       // public void newLine()throws IOException
        bw.newLine();

        bw.write("JavaEE");
        bw.newLine();
        bw.write("World");
        bw.newLine();

        //刷新流
        bw.flush();

        //释放资源
        bw.close();


    }
}
字符缓冲输入流
字符缓冲输入流:BufferedReader
针对文本高效读取(高效的字符输入流)
构造方法
BufferedReader(Reader read):构造一个默认大小的缓冲输入流对象(默认值足够大)
成员方法
public String readLine()throws IOException :一次读取一行内容,返回值如果为null,表示当前读完了
public int read(int ch):读取一个字符
public int read(char[] chs):读取一个字符
public void read(String str/)
public class BufferedReaderDemo {

    public static void main(String[] args) throws IOException{
        //创建一个字符缓冲输入流对象
//        BufferedReader br = new BufferedReader(new FileReader("bw.txt")) ;
        BufferedReader br = new BufferedReader(new FileReader("BuffferedWriterDemo.java")) ;

        //读取:一次读取一个字符/一次读取一个字符数组
     /*   char[] chs = new char[1024] ;
        int len = 0 ;//实际字符数
        while((len=br.read(chs))!=-1){
            System.out.println(new String(chs,0,len));
        }*/

     //public String readLine()throws IOException :一次读取一行内容,返回值如果为null,表示当前读完了

     //使用循环,while循环
     String line = null ;
     while((line=br.readLine())!=null){
         System.out.println(line);
     }

        //释放资源
      br.close();


    }

}
/**
 * 需求:当前项目下的:BufferedWriterDemo.java文件
 *
 * 复制到D://copy.java文件中
 *
 * 源文件:
 *      BufferedReader:读取 BufferedWriterDemo.java文件  (推荐)
 *  目标文件:
 *      BufferedWriter:写入D://copy.java文件
 */
public class CopyFile {
    public static void main(String[] args) throws IOException {
        //封装源文件/目标文件
        BufferedReader br = new BufferedReader(new FileReader("BuffferedWriterDemo.java")) ;
        BufferedWriter bw = new BufferedWriter(new FileWriter("D://copy.java")) ;


        //读写复制操作:使用字符缓冲流的特有功能
        String line = null ;
        while((line=br.readLine())!=null){
            //读一行,写一行
            bw.write(line);
            bw.newLine();
            bw.flush(); //刷新
        }

        //关闭资源
        bw.close();
        br.close();
    }
}


其他流

打印流
字节打印流
字节打印流:PrintStream
System.out---->标准"输出流"
   public static final PrintStream out ;

打印各种数据表示形式(输出字节,如果是字符----转换成字节)
print(xxx);
 println(xxx);
字符打印流
字符打印流:PrintWriter
	只能操作目标文件
 	
	这个流:他可以启用自动刷新功能
 public PrintWriter(Writer out,boolean autoFlush) :第二个参数为true:启用自动刷新
 public void println():写入行的分隔符号,终止当前行
public class PinrtStreamDemo {

    public static void main(String[] args) throws IOException {
        PrintStream out = System.out;
        out.println("hello");
        out.println("-------------------------------------");
        out.println(true) ;
        char[] chs = {'a','b','c'} ;
        out.println(chs) ;

        out.println("-------------------------------------");

        //创建一个字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("BuffferedWriterDemo.java")) ;
        //创建字符打印流       public PrintWriter(Writer out,boolean autoFlush)
        PrintWriter ps = new PrintWriter(new FileWriter("demo.java"),true) ;



        //读写复制操作
        //一次读取一行
        String line = null ;
        while((line=br.readLine())!=null){
            ps.println(line); //给当前ps指向这个demo.java文件中打印内容
            //不需要刷新:自动刷新
        }

        //释放资源
        ps.close();
        br.close();
    }
}

字节输入流的逻辑串联
操作两个文件
构造方法
  SequenceInputStream(InputStream s1,InputStream s2)
  操作两个源文件...
/**
 * SequenceInputStream:字节输入流的逻辑串联
 *
 * 构造方法
 *  SequenceInputStream(InputStream s1,InputStream s2)
 *  操作两个源文件...
 *
 *  举例
 *          将当前项目下的BuffferedWriterDemo.java+PinrtStreamDemo.java复制到
 *   当前项目下的:copy.java文件中
 *
 */
public class SequenceInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //封装源文件
        SequenceInputStream sis = new SequenceInputStream(new FileInputStream("BuffferedWriterDemo.java"),
                new FileInputStream("PinrtStreamDemo.java")) ;
        //封装目标文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.java")) ;

        //读写操作
        byte[] bytes = new byte[1024] ;
        int len = 0 ;//字节数
        while((len=sis.read(bytes))!=-1){
            bos.write(bytes,0,len);
            bos.flush(); //字节流中刷新:强制刷新缓冲区中字节数
        }

        bos.close();
        sis.close();
    }
}

操作两个文件以上
构造方法:
    public SequenceInputStream(Enumeration<? extends InputStream> e):
    针对两个以上的文件进行复制
/**
 * public SequenceInputStream(Enumeration<? extends InputStream> e):
 * 针对两个以上的文件进行复制
 *
 * 举例
 *
 *    D://a.txt + c://b.txt + e://c.txt
 *
 *    ----->当前项目demo.txt文件中
 */
public class SequenceInputStreamDemo2 {

    public static void main(String[] args) throws IOException {

        //BuffferedWriterDemo.java+PinrtStreamDemo.java+SequenceInputStreamDemo.java
        //封装源文件
        //创建Vector集合对象
        Vector<InputStream> v = new Vector<InputStream>() ;
        InputStream s1 = new FileInputStream("BuffferedWriterDemo.java") ;
        InputStream s2 = new FileInputStream("PinrtStreamDemo.java") ;
        InputStream s3 = new FileInputStream("SequenceInputStreamDemo.java") ;
        //添加元素
        v.add(s1)  ;
        v.add(s2)  ;
        v.add(s3)  ;

        //Vector------>获取特有的迭代器(Enumeration)
        Enumeration<InputStream> en = v.elements(); //获取向量的枚举组件-------- iterator() ---->Iterator
        SequenceInputStream sis = new SequenceInputStream(en) ;

        //d://demo.java:目标文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D://demo.java")) ;

        //读写复制操作
        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len=sis.read(bytes))!=-1){
            bos.write(bytes,0,len);
            bos.flush();
        }

        //释放资源
        bos.close();
        sis.close();


    }
}

序列化和反序列化

序列化
序列化:    ObjectOutputStream
将Java对象(任意Java类的对象) 对象 在网络中进行数据传输-----变成一种"流"数据,这个过程称为序列化
反序列化
反序列化     ObjectInputStream
又需要将流数据-----还原成对象
public class ObjectStreamDemo {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化:
//        myWrite();
        //反序列化
        myRead() ;
    }

    public  static void myRead() throws IOException, ClassNotFoundException {

        //ObjectInputStream
        //构造方法
        //public ObjectInputStream(InputStream in)throws IOException
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;

        //public final Object readObject()throws IOException,ClassNotFoundException
        Object object = ois.readObject();


        System.out.println(object); //Person    //Person{name='高圆圆', age=41}

        //释放资源
        ois.close();
    }

    public static void myWrite() throws  IOException{
        //对象 在网络中进行数据传输-----变成一种"流"数据
//        public ObjectOutputStream(OutputStream out)throws IOException
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("oos.txt")) ;

        //public final void writeObject(Object obj)
        //                       throws IOException 将对象写入到流中
        Person p = new Person("高圆圆",41) ;
        oos.writeObject(p);   //写入内容:编码集:GBK
        //java.io.NotSerializableException: com.qf.objectstream_05.Person

        //释放资源
        oos.close();


    }
}

import java.io.Serializable;

//只能将支持 java.io.Serializable 接口的对象写入流
//要能够启用序列化功能:自定义对象所在的类必须实现:Serializable
//类通过实现 java.io.Serializable 接口以启用其序列化功能

/**
 * 实现了序列化接口(标记接口)的类----在内存中会产生一个序列化的版本Id: SerialVersionUID:唯一标识符
 * 类----对应类的签名:就是一个唯一的id值
 *
 * 序列化---反序列化----如果现在手动更改成员信息----如果直接在进行反序列化 :
 *
 *  java.io.InvalidClassException: com.qf.objectstream_05.Person; local class incompatible: stream classdesc serialVersionUID = 3971989068296701088,
 *          local class serialVersionUID = 5645013692103785236
 *
 *     该类的序列版本号与从流中读取的类描述符的版本号不匹配 :
 *
 *      序列化的时候:com.qf.objectstream.Person:会产生一个serialVersionUID
 *      然后反序列化的时候:会读取当前Person在内存中的序列化版本ID(一致),获取Java对象
 *      如果更改了成员信息,直接进行反序列化,序列化版本Id不一致,出现这个异常!
 *      产生一个固定的序列化版本Id号(常量)或者针对某个成员加入这个关键字: transient:针对某个类的成员不会参与序列化/反序列化!
 *
 *
  */
public class Person  implements Serializable {


   private transient   String name ;
    int age ;


    public Person() {
    }

    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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

属性集合类

属性集合类:Properties extends Hashtable<K,V>
Properties 类表示了一个持久的属性集。
            可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
这个属性集合类:应该具备Map集合的功能
public class PerpertiesDemo {
    public static void main(String[] args) {

        //创建属性集合类对象
        //使用Map接口的功能
        Properties prop = new Properties() ;
        //添加元素:直接使用put方法
        prop.put("1","张三") ;
        prop.put("2","高圆圆") ;
        prop.put("3","赵又廷") ;
        prop.put("4","文章") ;

        //遍历Map集合的遍历几种方式:
        //通用:keySet()
        Set<Object> set = prop.keySet();
        for(Object key :set){
            Object value = prop.get(key);
            System.out.println(key+"---"+value);
        }
    }
}

构造方法
public Properties():无参构造
特有功能
添加功能
添加元素:
	  public Object setProperty(String key,String value)
获取功能
获取:
   public Set<String> stringPropertyNames():获取属性列表中所有的键集
   public String getProperty(String key):通过属性列表中的属性名称-->获取属性值
public class PropertiesDemo2 {
    public static void main(String[] args) {

        //创建属性列表对象
        Properties prop = new Properties() ;
        System.out.println(prop);

        //添加元素
        prop.setProperty("高圆圆","赵又廷") ;
        prop.setProperty("文章","马伊琍") ;
        prop.setProperty("孙悟空","紫霞仙子") ;
        System.out.println(prop);

        //遍历public Set<String> stringPropertyNames():获取属性列表中所有的键集
        Set<String> set = prop.stringPropertyNames();
        for(String key :set){
            String value = prop.getProperty(key);
            System.out.println(key+"---"+value);
        }
    }
}

加载功能
加载功能:
   public void load(Reader reader) throws IOException:
   public void load(InputStream in)throws IOException
		将某个文件的内容加载到当前属性列表中
保存功能
保存:
  public void store(Writer writer,String comments)

  将属性列表中的内容保存到指定的文件中...
public class PropertiesDemo3 {

    public static void main(String[] args) throws IOException {
//        myStore() ;
        myLoad() ;
    }

    //将文件中的内容加载属性集合类中
    private static void myLoad() throws IOException {


        //创建属性集合列表对象
        Properties prop = new Properties() ;
        System.out.println(prop);

        System.out.println("---------------------");
        //从文本文件中获取信息加载属性集合类中
        prop.load(new FileReader("user.txt"));

        System.out.println(prop);
    }

    //将属性列表中的内容:保存到某个文件中
    private static void myStore() throws IOException {
        //创建一个属性列表集合对象
        Properties prop = new Properties() ;

        //添加内容
        prop.setProperty("张三","30") ;
        prop.setProperty("高圆圆","41") ;
        prop.setProperty("李四","40") ;
        prop.setProperty("文章","35") ;

        //  public void store(Writer writer,String comments)
        //参数1:字符输出流
        //参数2:给当前属性列表中添加描述信息
        prop.store(new FileWriter("username.txt"),"student's list");



    }
}

网络编程

计算机网络

网络
网络:由点和线构成,表示诸多对象间的相互联系
计算机网络
计算机网络:
	为实现资源共享和信息传递,通过通信线路连接起来的若干主机(host)
	
	互联网:Internet	点与点相连
	万维网:WWW - World Wide Web	端与端相连
	物联网:IoT - Internet of things	物与物相连

网络编程
网络编程:让计算机与计算机之间建立连接,进行通信

网络模型

网络模型
网络模型:
	OSI:(Open System Interconnection)	开放式系统互联

	开放系统A			开放系统B
			 应用层协议
	应用层	<------------>	应用层		
	第七层:应用层负责文件访问和管理,可靠运输服务,远程操作服务(HTTP,FTP,SMTP)
			 表示层协议
	表示层	<------------>	表示层		
	第六层:表示层负责定义转换数据格式及加密,允许选择以二进制或ASCII格式传输
			会话层协议
	会话层	<------------>	会话层		
	第五层:会话层负责使应用建立和维持会话。使通信在失效时继续恢复通信(断点续传)
			传输层协议
	传输层	<------------>	传输层		
	第四层:传输层负责是否选择差错恢复协议,数据流重用,错误顺序重排(TCP,UDP)
	网络层					网络层		
	第三层:网络层负责定义了能够标识所有网络节点的逻辑地址(IP地址)
	数据链路层					数据链路层		
	第二层:链路层在物理层上,通过规程或协议(差错控制)来控制传输数据的正确性(MAC)
 
	物理层					物理层		
	第一层:物理层为设备之间的数据通信提供传输信号和物理介质(双绞线,光导纤维)
《---------通信介质(物理媒体)----------
TCP/IP模型
TCP/IP模型:
	一组用于实现网络互连的通信协议,将协议分为四个层次
	
	TCP/IP模型:
	
	应用层			第四层:应用层负责传送各种最终形态的数据,是直接与用户打交道的层,典型的协议是HTTP,FTP等
			
	传输层			第三层:传输层负责传输文本数据,主要协议是TCP协议,UDP协议

	网络层			第二层:网络层负责分配地址和传送二进制数据,主要协议是IP协议

	网络接口层		第一层:接口层负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网,ADSL等等

TCP/UDP

TCP
TCP协议:Transmission Control Protocol 传输控制协议
	是一种面向连接的,可靠的,基于字节流的传输层通信协议,数据大小无限制,建立连接过程需要三次握手,断开连接过程需要四次挥手
UDP
UDP协议:User Datagram Protocol 用户数据报协议
	是一种无连接的传输协议,提供面向事物的简单不可靠信息传送服务,每个包的大小是64KB

IP

IP协议
IP协议:Internet Protocol Address 互联网协议地址/网际协议地址
		分配给互联网设备的数字标签(唯一标识)
IP地址
IPV4
IP地址分为两种:
	IPV4:4字节32位整数,并分为4段8位二进制数,每8位之间用圆点隔开,每8位整数可以转换为一个0~255的十进制整数
	格式:D.D.D.D		例如:255.255.255.255
IPV4的应用分类
A类:政府机构:1.0.0.1~126.255.255.254
B类:中型企业:128.0.0.1~191.255.255.254
C类:个人用户:192.0.0.1~223.255.255.254
D类:用于组播:224.0.0.1~239.255.255.254
E类:用于实验:240.0.0.1~255.255.255.254

回环地址:127.0.0.1	指本机,一般用于测试使用
查看命令:ipconfig
测试IP命令:ping D.D.D.D
IPV6
	IPV6:16字节128位整数,并分为8段十六进制数,每16位之间用圆点隔开,每16位整数可以转换为一个0~65535的十进制数
	格式:X.X.X.X.X.X.X.X 	例如:FFFF.FFFF.FFFF.FFFF.FFFF.FFFF.FFFF.FFFF

端口(Port)

端口号
端口号:在通信实体上进行网络通讯的程序的唯一标识
端口分类
端口分类:
	公认端口:0~1023
	注册端口:1024~49151
	动态或私有端口:49152~65535
常用端口
常用端口:
	MySql:	3306
	Oracle: 1521
	Tomcat:	8080
	SMTP:	25
	web服务器:80
	FTP服务器:21

InetAddress类

概念
表示互联网协议(IP)地址对象,封装了与该IP地址相关的所有信息,并提供获取信息的常用方法
方法
public static InetAddress getLocalHost()获得本地主机地址对象
public static InetAddress getByName(String host) 根据主机名称获得Ip地址对象
	参数:要么是知道机器名称的情况/要么就是ip地址字符串形式
public static InetAddress[] getAllByName(String host)获得所有相关地址对象
public String getHostName():获取IP地址主机名称
public String getHostAddress():获取ip地址(字符串形式)
import java.net.InetAddress;
import java.net.UnknownHostException;

public class TestInetAddress {

	public static void main(String[] args) throws UnknownHostException {

		//获得本机IP地址对象
		InetAddress localhost = InetAddress.getLocalHost();
		
		//获得IP地址字符串
		System.out.println(localhost.getHostAddress());
		
		//获得IP地址主机名
		System.out.println(localhost.getHostName());
		
		System.out.println(localhost);
		
		//获得任意主机的IP地址对象(IP、主机名、域名)
		InetAddress baidu = InetAddress.getByName("www.baidu.com");
//		System.out.println(baidu.getHostAddress());
//		System.out.println(baidu.getHostName());
		
		//获得任意域名所绑定的所有IP地址对象
		InetAddress[] addrs = InetAddress.getAllByName("www.baidu.com");
		for(InetAddress addr : addrs) {
			System.out.println(addr.getHostAddress());
			System.out.println(addr.getHostName());
		}
		
	}

}

基于UDP的网络编程

UDP发送端的使用步骤
UDP发送端的使用步骤:
	1)创建发送端的Socket对象
		DatagramSocket:此类表示用来发送和接收数据报包的套接字。
		构造方法
			public DatagramSocket() throws SocketException:
			将任何端口号绑定当前这个数据包套接字上
	2)创建数据报包对象:DatagramPacket此类表示数据报包。
		构造方法
			public DatagramPacket(byte[] buf,        包数据
            					int length,           包长度
             					InetAddress address, 目标地址
                 				int port)             端口号
	3)发送
 		public void send(DatagramPacket p) throws IOException

	4)释放资源
       关闭 public void close()
public class UDP_Send {
    public static void main(String[] args) throws IOException {
    // 创建发送端的Socket对象
    DatagramSocket ds = new DatagramSocket() ;

    //2)创建数据报包对象:DatagramPacket此类表示数据报包
        /**
         * public DatagramPacket(byte[] buf,包数据
         *                         int length,           包长度
         *                         InetAddress address, 目标地址
         *                       int port)             端口号
          */

       byte[] bytes = "hello,udp,我来了".getBytes() ;
        /*int length  = bytes.length ;

        InetAddress inetAddress = InetAddress.getByName("10.12.156.196");
        int port = 8888 ;*/

        //DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,InetAddress.getByName("10.12.156.196"),8888);

        //3)发送
        ds.send(dp);

        //4)释放资源
        ds.close();

    }
}
UDP接收端的使用步骤
 UDP接收端的使用步骤
   1)创建接收端的Socket
       public DatagramSocket(int port) throws SocketException
   2)创建一个接收容器:数据报包 (并非真实数据)
       public DatagramPacket(byte[] buf,int length)
       参数1:缓冲区大小:长度1024/1024的倍数
       参数2:长度
 
   3)使用接收容器:接收数据
           public void receive(DatagramPacket p) throws IOException
   4)从缓冲区中获取真实数据(解析)
               public byte[] getData():实际字节数组
               public int getLength():实际长度
 
   5)展示...哪一个ip发送过来的数据 new String(实际字节数组,0,实际长度)
 
   6)接收端---->关闭
 
 
 接收端一般一直开启,不关闭并且-先启动接收端,在启动发送端!
public class UDP_Receive {
    public static void main(String[] args) throws IOException {

        //1)创建接收端的Socket
        //public DatagramSocket(int port) throws SocketException
        DatagramSocket ds = new DatagramSocket(8888) ;
        
        //2)创建一个接收容器:数据报包 (并非真实数据)
        byte[] bytes = new byte[1024] ;
        int length = bytes.length ;
        DatagramPacket dp  = new DatagramPacket(bytes,length) ;
        
        //3)接收数据
        ds.receive(dp);
        
        //4)解析容器中的数据
        //public byte[] getData():实际字节数组
        //public int getLength():实际长度
        byte[] buffer = dp.getData();
        int buffeLength = dp.getLength();
        //获取传递过来的数据
        String dataStr = new String(buffer,0,buffeLength) ;
        //获取ip地址字符串形式
        //public InetAddress getAddress()
        //public String gethostAddress()
        String ip = dp.getAddress().getHostAddress() ;
        System.out.println("data from --"+ip+",data is-->"+dataStr);

        //关闭
        ds.close();
    }
}

优化
优化:
	需要发送端,键盘录入数据(BufferedReader的readLine())不断录入数据,并且自定义结束标记"886",
 	接数收端不关闭,不断的接收据

	udp一个窗口进行聊天,ChatRoom类 (简易版)
       开启两条线程
          发送端线程
          接收端的线程
public class SendTest {
    public static void main(String[] args) {

        //发送端的Socket
        DatagramSocket ds = null ;
        try {
           ds =  new DatagramSocket() ;
           //数据报包:把数据放在包中
            //利用BufferedReader的readLine
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;

            String line = null ;
            while((line=br.readLine())!=null){
                //结束标记
                if("886".equals(line)){
                    break ;
                }

                //创建DatagramPacket
                InetAddress inetAddress = InetAddress.getByName("10.12.156.196");
                DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,
                        inetAddress,10086) ;

                //发送
                ds.send(dp);

            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ds!=null){
                ds.close();
            }
        }
    }
}

//接收端不关闭(真实场景)
public class ReceiveTest {

    public static void main(String[] args) {
        //创建接收端的Socket对象
        try {
            DatagramSocket ds  = new DatagramSocket(10086) ;
            while(true){

                //创建接收容器
                byte[] bytes = new byte[1024] ;
                int length = bytes.length ;
                DatagramPacket dp = new DatagramPacket(bytes,length) ;

                //接收数据
                ds.receive(dp);

                //解析数据
                String dataStr  = new String(dp.getData(),0,dp.getLength()) ;
                //获取ip地址
                String ip = dp.getAddress().getHostAddress() ;
                System.out.println("data from --->"+ip+",data is--->"+dataStr);

            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //            //不关闭...
        }
    }
}

基于TCP的网络编程

开发步骤
开发步骤:
	建立通信连接(会话):
		创建ServerSocket,指定端口号
		调用accept等待客户端接入
	客户端请求服务器:
		创建Socket,指定服务器IP+端口号
		使用输出流,发送请求数据给服务器
		使用输入流,接受响应数据到客户端(等待)
	服务器响应客户端
		使用输入流,接受请求数据到服务器(等待)
		使用输出流,发送响应数据给客户端
TCP客户端的使用步骤
TCP客户端(需要建立连接通道) 使用步骤
	1)创建客户端所在的Socket对象
	public Socket(String host,int port)throws UnknownHostException,IOException
          	参数1:主机名称/ip地址字符串表现形式
			参数2:端口号
	2)写数据到服务器端
      public OutputStream getOutputStream() throws IOException:获取连接通道内的输出流对象
      写数据write:字节
  	3)释放资源
       关闭socket对象所在的系统资源
public class ClientDemo {
    public static void main(String[] args) throws IOException {

        //1)创建客户端所在的Socket对象
        Socket socket = new Socket("10.12.156.196",6666) ;


        //2)获取连接通道内的输出流对象
        // public OutputStream getOutputStream() throws IOException:
        OutputStream out = socket.getOutputStream();
        //写数据到流中
        out.write("hello TCP,我来了".getBytes());


        //3)释放资源
        socket.close();
    }
}

TCP服务端的使用步骤
  TCP服务端的使用步骤:
  	1)创建服务器端所在的Socket:此类实现服务器套接字
 		public ServerSocket(int port)  throws IOException:
 		创建服务器端socket对象同时监听某个端口
  
  	2)侦听客户端连接 (阻塞式方法:一旦客户端绑定的端口号和当前服务器端的端口不一致,永远等待..)
       public Socket accept() throws IOException :返回值就是被侦听到的客户端对象
 
  3)通过客户端获取通道内的输入流对象
       public InputStream getInputStream() throws IOException返回此套接字的输入流。
       要么一次读取一个字节/一次读取一个字节数组
 
  4)展示数据并且释放资源
 
 
 
  注意错误:
   启动一次,它就等待监听客户端;
   服务器端不要启动多次----BindException:绑定异常:   Address already in user:端口号被占用!
public class ServerDemo {
    public static void main(String[] args) throws IOException {

        //1)创建服务器端所在的Socket:此类实现服务器套接字
        ServerSocket ss = new ServerSocket(6666) ;

        //2)侦听客户端连接
        Socket socket = ss.accept();
        //一旦侦听到客户端--获取通道内的输入流对象

        InputStream inputStream = socket.getInputStream();
        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = inputStream.read(bytes); //获取字节数
        //展示接收到的数据
        String clientMsg = new String(bytes,0,len) ;
        //获取客户端ip地址
        //public InetAddress getInetAddress()
        InetAddress inetAddress = socket.getInetAddress();
        String ip = inetAddress.getHostAddress() ;
        System.out.println("data from--->"+ip+",data is-->"+clientMsg);

        //释放资源
        ss.close();
    }
}

加入响应
//客户端发送消息,服务器端接收消息之后展示,并且给客户端响应数据,客户端将响应的数据可以展示

public class ClientDemo {
    public static void main(String[] args) throws IOException {

        //创建一个客户端的Socket对象
        Socket socket = new Socket("10.12.156.196",12306) ;

        //获取通道内输出流,给服务器端写过去
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("ServerSocket,你好".getBytes());

        //获取服务器响应的数据
        //获取通道内的输入流对象
        InputStream inputStream = socket.getInputStream();
        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = inputStream.read(bytes) ;
        String responseMsg = new String(bytes,0,len) ;
        System.out.println("responseMsg:"+responseMsg);

        //关闭资源
        socket.close();
    }
}

public class ServerDemo {
    public static void main(String[] args) throws IOException {

        //创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(12306) ;

        //监听
        Socket socket = ss.accept();

        //获取到客户端的通道内输入流对象
        InputStream in = socket.getInputStream();
        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = in.read(bytes) ;
        String sendMsg = new String(bytes,0,len) ;
        System.out.println("data from--->"+socket.getInetAddress().getHostAddress()+",data is-->"+sendMsg);


        //服务器端响应给客户端数据
        //获取监听客户端所在的通道内输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("socket,你好,数据已经是收到".getBytes());

        //释放资源

        ss.close();
    }
}

UDP和TCP协议的区别
UDP是一种不可靠连接,不安全---执行效率高
UDP不需要建立连接通道,通过一种数据报包(DatagramPacket)的形式,发送数据/接收数据
UDP有文件大小限制
	
TCP是一种可靠连接,安全---执行效率低 
TCP是需要建立连接通道,通过一种最基本的OutputStream/InputStream
TCP相对UDP没有具体的限制
题目
键盘录入…
//TCP客户端:不断键盘录入数据,服务器将数据复制到当前项目下的Copy.java文件中
public class ClientTest {
    public static void main(String[] args) throws IOException {
        //1)客户端Socket对象
        Socket socket = new Socket("10.12.156.196",8888) ;

        //2)键盘录入数据:使用BufferedReader的readLine:读取一行
        BufferedReader br  = new BufferedReader(new InputStreamReader(System.in)) ;
        //3)获取当前客户端所在通道内的输出流:OutputStream  getOutputStream()
       // OutputStream out = socket.getOutputStream();
        //封装通道内的流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
        String line = null ;
        while((line=br.readLine())!=null){
            if("over".equals(line)){
                break ;
            }
            //录入一行数据,将数据写入到封装的输出流中
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //释放资源
        br.close();
        socket.close();
    }
}

public class ServerTest {
    public static void main(String[] args) throws IOException {

        //服务器端是Socket
        ServerSocket ss = new ServerSocket(8888) ;
        Socket socket = ss.accept();

        //封装通道内输入流
        BufferedReader br = new BufferedReader(
                new InputStreamReader(socket.getInputStream())) ;

        //创建字符缓冲输出流对象,输出指定文件copy.java
        BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt")) ;
        //一次读取一行数据
        String line = null ;
        while((line=br.readLine())!=null){
            //使用BufferedWriter写入到指定的文件中
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        bw.close();
        //服务器不关闭
    }
}

操作文本文件遇到阻塞问题
/**
 * TCP的客户端的操作ClientDemo.java文件
 *              需要使用BufferedReader进行读取,将文件内容写入到通道内的输出流中
 *
 *          服务器端将文件内容获取到并且复制到当前项目下Copy.java文件中
 *
 *      条件:
 *          文件复制完毕,服务器端给客户端进行反馈,客户端将反馈的消息打印出来!
 *
 *
 *    问题:服务器端和客户端都正在执行,程序就是没有结束!
 *
 *    原因:
 *
 *          读取某个文本文件
 *          readLine() 返回值为null,表示文件已经读取完毕  阻塞式方法
 *                         返回值已经null,但是null不能用来判断通道内的输出是否已经写入完毕
 *
 *
 *               文件已经读取完毕,服务器不知道!
 *
 *               针对服务器端:需要通过BufferedReader来进行读取复制文件内容,readLine():返回为null,文件复制完毕,
 *           等待服务器反馈!
 *
 *
 *     解决方案:
 *      1)在客户端写入完毕,自定义结束标记"886"/"over",服务器端读到这个标记的,结束!
 *     (推荐) 2)public void shutdownOutput() throws IOException:在客户端写入完毕的时候,调用这个功能,告诉服务器,没有内容需要写入到流中
 *
 *
 *
 *
 *
 */
public class ClientTest {
    public static void main(String[] args) throws IOException {
        //客户端Socket
        Socket socket = new Socket("10.12.156.196",6666) ; //一旦创建对象创建完毕,已经和服务器端连接
        //创建字符缓冲输入流,读取指定的文件
        BufferedReader br = new BufferedReader(new FileReader("ClientDemo.java")) ;
        //封装通道内 的输出流对象,将文件内容进行写入到流对象汇总
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;

        String line = null ;
        while((line=br.readLine())!=null){ //readLine() 返回值为null,表示文件已经读取完毕  阻塞式方法
                                //返回值已经null,但是null不能用来判断通道内的输出是否已经写入完毕
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //告诉服务器端,文件写入完毕
        //自定义标记
       /* bw.write("over");
        bw.newLine();
        bw.flush();*/


       //public void shutdownOutput() throws IOException
        socket.shutdownOutput();



        //接收服务器端的反馈
        //获取通道的内的输入流,读取
        //客户端也不知道服务器端是否将文件已经复制完毕
        BufferedReader br2  = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
        String serverMsg = br2.readLine();
        System.out.println("serverMsg:"+serverMsg);


        //释放资源
        br.close();
        socket.close();
    }
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerTest {
    public static void main(String[] args)  throws IOException {

        //服务器端是Socket
        ServerSocket ss = new ServerSocket(6666) ;
        Socket socket = ss.accept();//阻塞式方法: 如果一旦某个客户端端口号跟主机的端口号一致,可以被服务器监听

        //封装通道内输入流
        BufferedReader br = new BufferedReader(
                new InputStreamReader(socket.getInputStream())) ;

        //创建字符缓冲输出流对象,输出指定文件copy.java
        BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;
        //一次读取一行数据
        String line = null ;
        while((line=br.readLine())!=null){  //阻塞式方法
            //使用BufferedWriter写入到指定的文件中
            //读取标记:
            /*if("over".equals(line)){
                break ;
            }*/



            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //服务器端的响应动作
        //继续获取通道内的输出流,写给客户端
        BufferedWriter bw2 = new BufferedWriter(  //给客户端反馈
                new OutputStreamWriter(socket.getOutputStream())) ;
        bw2.write("服务器已经将数据复制完毕");
        bw2.newLine();
        bw2.flush();

        bw.close();
        //服务器不关闭
        ss.close();
    }
}

操作图片文件遇到图片缺失问题
/**
 * TCP客户端的有一个图片文件高圆圆.jpg,
 * 服务器端将图片内容进行复制---当前项目下:mv.jpg,并加入反馈操作(服务器端给客户端反馈!)
 *
 * 图片复制完毕,但是缺失,对于缓冲的字节数,通过字节流也可以刷新的
 * public void flush()
 *            throws IOException :强制刷新此缓冲区字节数!
 *
 */
public class UploadClient {
    public static void main(String[] args) throws IOException {
        //客户端
        Socket socket = new Socket("10.12.156.196",2222) ;

        //创建BufferedInputStream
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("高圆圆.jpg")) ;
        //封装通道内的输出流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len =  0 ;
        while((len=bis.read(bytes))!=-1){
            //将内容写入到通道内的输出流中
            bos.write(bytes,0,len);
            bos.flush();
        }

        //通知:服务器端:当前流数据没有可写内容了
        socket.shutdownOutput();


        //读取反馈
        //获取通过内的输入流
        InputStream in = socket.getInputStream();
        byte[] bytes2 = new byte[1024] ;
        int len2 = in.read(bytes2) ;
        String severMsg = new String(bytes2,0,len2) ;
        System.out.println("serverMsg:"+severMsg);

        //释放资源
        bis.close();
        socket.close();
    }

}

public class UploadServer {
    public static void main(String[] args) throws IOException {

        //服务器端的Socket
        ServerSocket ss = new ServerSocket(2222) ;
        Socket socket = ss.accept() ;

        //封装通道内输入流对象
        BufferedInputStream bis  = new BufferedInputStream(socket.getInputStream()) ;
        //创建输出流 -----mv.jpg
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mv.jpg")) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
            bos.flush();
        }

        //反馈
        //获取通道内输出流
        OutputStream out = socket.getOutputStream();
        out.write("图片已经复制完毕".getBytes());
        bos.flush();


        //释放资源
        bos.close();
        ss.close();
    }
}

反射

类的加载

类对象
类的对象:基于某个类new出来的对象,也称为实例对象

类对象:类加载的产物,封装了一个类的所有信息(类名,父类,接口,属性,方法,构造方法)
类加载
类的加载 
	当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。 
加载
	就是指将class文件读入内存,并为之创建一个Class对象。 
	任何类被使用时系统都会建立一个Class对象。 
连接
	验证是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据中的符号引用替换为直接引用
类初始化时机
	创建类的实例 
	访问类的静态变量,或者为静态变量赋值 
	调用类的静态方法 
	使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 
	初始化某个类的子类 
	直接使用java.exe命令来运行某个主类
类加载器
类加载器 
	负责将.class文件加载到内在中,并为之生成对应的Class对象。 
	
类加载器的组成 
	Bootstrap ClassLoader 根类加载器 
	也被称为引导类加载器,负责Java核心类的加载 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
	Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载。 在JDK中JRE的lib目录下ext目录
	Sysetm ClassLoader 系统类加载器
	负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

什么是反射

	在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调 用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java代码经历的三个阶段

1)SORUCE:源码阶段
2)CLASS:编译阶段        ----->Class:字节码文件对象
3)RUNNABLE:运行阶段

获取一个字节码文件对象的方式有几种

三种:
  Object类的getClass()---->Class<T>
    任意Java类型的class属性--->Class<T>

  Class类中静态功能:forName("类的全限定名称") ;
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {

        //创建一个Person类对象
        Person p1 = new Person() ;
        //第一种方式获取字节码文件对象
        System.out.println(p1.getClass());//class com.qf_reflect_08.Person
        System.out.println(p1.getClass().getName());//com.qf_reflect_08.Person
        System.out.println("--------------------------");

        //第二种方式获取
        Class<Person> c =  Person.class ;
        System.out.println(c);//class com.qf_reflect_08.Person
        System.out.println("--------------------------");
        //public static Class<?> forName(String className) throws ClassNotFoundException
        //注意事项:参数为字符串:全限定名从:包名.类名
        Class c2 =  Class.forName("com.qf_reflect_08.Person") ;
        System.out.println(c2);
        System.out.println(c2.getName());//com.qf_reflect_08.Person

    }
}

使用反射的方式创建某个类的实例

1)获取当前类的字节码文件对象:Class.forName("类的全限定名称")--->Class clazz
2)当前类的构造方法:无参/公共权限 :构造器对象所代表的的指定的公共的构造方法
		Constructor con = clazz.getConstructor() ;
		public 包名.类名类名()
		//如果是私有的构造方法/默认的/受保护的
		con.setAccessiable(true) ;
3)创建该类实例:  con.newInstance()--->Object obj :当前该类的实例!
   
   
    
public Constructor<?>[] getConstructors() throws SecurityException:获取公共的构造器对象
public Constructor<?>[] getDeclaredConstructors():获取的是所有的,包含私有,公共,受保护的,默认的.
public Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定的公共的构造器对象,
public T newInstance(Object... initargs)
    返回值就是当前类的实例 ,参数参数:需要给构造器中传递的实际参数,没有参数,空参
public class RefectTest {
    public static void main(String[] args) throws Exception {

        //1)获取当前Person类所在的字节码文件对象
        Class c = Class.forName("com.qf_reflect_08.Person") ;//class com.qf_reflect_08.Person

        //2)public Constructor<?>[] getConstructors() throws SecurityException:获取公共的构造器对象
        //public Constructor<?>[] getDeclaredConstructors():获取的是所有的,包含私有,公共,受保护的,默认的.
//        Constructor[] con = c.getConstructors();
       // Constructor[] con = c.getConstructors();
      //  Constructor[] con = c.getDeclaredConstructors() ;
        //遍历
     /*   for(Constructor constructor:con){
            System.out.println(constructor);*/
            /*
            公共的构造器对象
            * public com.qf_reflect_08.Person(java.lang.String)
              public com.qf_reflect_08.Person()

              所有的
              com.qf_reflect_08.Person(java.lang.String,int,java.lang.String)
                private com.qf_reflect_08.Person(java.lang.String,int)
                public com.qf_reflect_08.Person(java.lang.String)
                public com.qf_reflect_08.Person()
            * */
        //}

        //获取的单个的构造器对象: 公共的
        //public Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定的公共的构造器对象,
        //参数:形式参数类型 的字节码文件对象:类名.class
//        Constructor con = c.getConstructor(); //空参构造对象
        Constructor con = c.getConstructor(String.class);

        //System.out.println(con);//public com.qf_reflect_08.Person()

        //通过构造器对创建Person类的实例
        //public T newInstance(Object... initargs):
        //返回值就是当前类的实例 ,参数参数:需要给构造器中传递的实际参数,没有参数,空参!
//        Object obj = con.newInstance();
//        System.out.println(obj);
        Object obj = con.newInstance("高圆圆") ;
        System.out.println(obj);

        System.out.println("---------------------------");


        Person p = new Person("高圆圆") ;
        System.out.println(p);

    }
}

访问私有成员

IllegalAccessException:非法访问异常:这个类不能访问Person私有的成员

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类

public void setAccessible(boolean flag):参数为true:取消Java语言访问检查:暴力访问
/**
 * 通过私有的构造方法创建Person类的实例,并且给成员信息赋值
 */
public class ReflectTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Person类的字节码对象
        Class clazz = Class.forName("com.qf_reflect_08.Person");

        //2)获取指定的构造器对象所代表的构造方法
        //getDeclaredConstructor(Class<T>...parameterClass)
        //private Person(String name,int age):
//        Constructor con =  clazz.getDeclaredConstructor(String.class,int.class) ;
        Constructor con =  clazz.getDeclaredConstructor(String.class,int.class,String.class) ;
//        System.out.println(con);//private com.qf_reflect_08.Person(java.lang.String,int)

        con.setAccessible(true);
        //通过构造器对象所代表的指定的这个构造方法创建该类实例
        Object obj = con.newInstance("高圆圆", 30,"鄠邑区");
        //IllegalAccessException:非法访问异常:这个类不能访问Person私有的成员
        //AccessibleObject 类是 Field、Method 和 Constructor 对象的基类
        //public void setAccessible(boolean flag):参数为true:取消Java语言访问检查:暴力访问
        System.out.println(obj);//实例


        /**
         * Person p = new Person("高圆圆",30) ; //编译通过不了,私有的构造方法
         *
         */
    }
}

通过反射获取Field对象所代表的成员变量,并且为其赋值

public Field[] getFields():获取当前类/接口的公共字段
public Field[] getDeclaredFields():获取当前类/接口所声明的所有的字段
    

public Field getDeclaredField(String name):获取指定的字段
public Field getField(String name):获取单个的公共的字段
    形式参数:就是当前字段名称
 
public void set(Object obj,Object value):   ----类似于:p.name = "高圆圆";
        参数1:当前类的实例,参数2:当前这个赋的实际值
            

1)获取当前类的字节码文件对象:
		Class.forName("类的全限定名称")--->Class clazz
		//创建该类的实例
		clazz.newInstance()---->Object obj				---->Person p = new Person() ;
		
2)clazz.getField("公共的字段名称") ;----->Field f1
3)直接赋值: f1.set(obj,"实际参数") ;
public class ReflectTest3 {

    public static void main(String[] args) throws 
            ClassNotFoundException, NoSuchMethodException, IllegalAccessException, 
            InvocationTargetException, InstantiationException, NoSuchFieldException {
        //1)获取当前Person类字节码文件对象
        Class personClazz = Class.forName("com.qf_reflect_08.Person");

        //2)public Field[] getFields():获取当前类/接口的公共字段
        //public Field[] getDeclaredFields():获取当前类/接口所声明的所有的字段
//        Field[] fields = personClazz.getFields();
//        Field[] fields = personClazz.getDeclaredFields();
        //
       // for(Field field:fields){
         //   System.out.println(field);
            //public java.lang.String com.qf_reflect_08.Person.address

            /**
             * private java.lang.String com.qf_reflect_08.Person.name
             * int com.qf_reflect_08.Person.age
             * public java.lang.String com.qf_reflect_08.Person.address
             */
        //}

        //获取公共的构造器对象所代表的构造方法:无参/公共的
        Constructor con = personClazz.getConstructor();
        //通过构造器对象创建该类的实例
        Object obj = con.newInstance() ;//Person p = new Person() ;
        System.out.println(obj);

        //获取单个并且指定的构造方法:
        //public Field getDeclaredField(String name):获取指定的字段
        //public Field getField(String name):获取单个的公共的字段
        //形式参数:就是当前字段名称
        Field nameField = personClazz.getDeclaredField("name");
        //public void set(Object obj,Object value):   ----类似于:p.name = "高圆圆";
        //参数1:当前类的实例,参数2:当前这个赋的实际值
        nameField.setAccessible(true);//取消Java语言访问检查
        nameField.set(obj,"高圆圆"); //name私有的字段
        System.out.println(obj);

        System.out.println("-----------------------------");

        //获取指定的age字段
        Field ageField = personClazz.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj,30);
        System.out.println(obj);

        System.out.println("-----------------------------");

        //获取address字段:公共的
        Field addressField = personClazz.getField("address");
        addressField.set(obj,"鄠邑区");
        System.out.println(obj);

        System.out.println("---------------------------------------");
        //类对象的创建----Class.forName("全限定名名称")----> Constructor 创建该类实例
        //类对象的创建----Class.forName("全限定名名称")----> 直接创建该类实例
        //public T newInstance()
        Class clazz = Class.forName("com.qf_reflect_08.Person");
        System.out.println(clazz.newInstance()); //Person{name='null', age=0, address='null'}


    }
}

通过反射获取Method对象所代表的成员方法并调用这个方法

public Method[] getMethods():获取当前类/接口中的所有公共的成员方法,包括父类/父接口

public Method[] getDeclaredMethods():获取当前类/接口执行的成员方法:公共的,私有的,默认的,受保护的

    
获取单个并且指定的公共的构造方法并调用
public Method getMethod(String name,Class<?>... parameterTypes)
	参数1:方法名称
	参数2:当前这个方法的形式参数类型的class属性 举例:带一个String类型的参数:String.class
        
调用方法:底层的调用方式
	public Object invoke(Object obj,Object... args)
        参数1:当前类/接口的实例
        参数2:是对方法的形式参数赋值
        返回值:如果当前方法有返回结果,返回,没有的话,直接单独调用
 
1)获取当前类的字节码文件对象:
		Class.forName("类的全限定名称")--->Class clazz
		//创建该类的实例
		clazz.newInstance()---->Object obj						Person p = new Person() ;
2)获取指定的公共的Method对象所代表的的成员方法
		//clazz.getMethod("方法名",形式参数类型的class) 			---->p.show() ;
			clazz.getMethod("show",String.class) ;---->Method m
3)调用这个方法:invoke(当前类的实例,如果有形式参数--赋值实际参数) 
	m.invoke(obj,"hello,javaEE") ; :如果当前成员方法有返回值类型,这个时候调用的时候
	
		Object o = m.invoke(obj,"hello,javaEE") ;
public class ReflectTest4 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        //1)获取Person类的字节码文件对象
        Class personClazz = Class.forName("com.qf_reflect_08.Person") ;

        //2)public Method[] getMethods():获取当前类/接口中的所有公共的成员方法,包括父类/父接口
        //2)public Method[] getDeclaredMethods():获取当前类/接口执行的成员方法:公共的,私有的,默认的,受保护的
//        Method[] methods = personClazz.getMethods();
      /*  Method[] methods = personClazz.getDeclaredMethods();
        for(Method method:methods){
            System.out.println(method);
        }*/

        Constructor con  = personClazz.getConstructor() ;
        Object obj = con.newInstance() ;//Person p = new Person() ;


      //获取单个并且指定的公共的构造方法并调用
        //public Method getMethod(String name,Class<?>... parameterTypes)
        //参数1:方法名称
        //参数2:当前这个方法的形式参数类型的class属性 举例:带一个String类型的参数:String.class
        Method m1 = personClazz.getMethod("show");//p.show() ;
        //调用方法:底层的调用方式
        //public Object invoke(Object obj,Object... args)
        //参数1:当前类/接口的实例
        //参数2:是对方法的形式参数赋值
        //返回值:如果当前方法有返回结果,返回,没有的话,直接单独调用
        m1.invoke(obj) ;

        System.out.println("---------------------------------------------");

        //调用method():私有的
        Method m2 = personClazz.getDeclaredMethod("method", String.class);
//        System.out.println(m2);

        //取消Java语言访问检查
        m2.setAccessible(true);
        m2.invoke(obj,"Mysql..") ;

        System.out.println("-------------------------------------");

        //function()调用
        Method m3 = personClazz.getMethod("function");
        Object object = m3.invoke(obj);//返回的结果
        System.out.println(object);


    }
}

反射应用举例

1.通过配置文件(name.txt)运行类中的方法
1. 反射应用
   Student类中有love方法
      		Teacher类中有love方法

   	随着需求不断变化---现有一个.txt的配置文件:name.txt
   			className=包名.类名
   			methodName=love
   			
   	使用所学的东西将文件读取并获取文件中的键对应的值,然后使用反射创建该类实例,调用方法
   import java.io.*;
   import java.lang.reflect.Constructor;
   import java.lang.reflect.InvocationTargetException;
   import java.lang.reflect.Method;
   import java.util.Properties;
   
   public class Test02 {
       public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
   
           Properties prop = new Properties();
   
           prop.load(new FileReader("name.txt"));
   
           String classname = prop.getProperty("className");
           String methodname = prop.getProperty("methodName");
   
           Class clas = Class.forName(classname);
           Constructor con = clas.getConstructor();
           Object obj = con.newInstance();
   
           Method m = clas.getDeclaredMethod(methodname);
   
           m.invoke(obj);
   
   
       }
   }
  public class Student {

    public void love(){
        System.out.println("love Java...");
    }
}
public class Teacher {

    public void love(){
        System.out.println("love 高圆圆...");
    }
}
 
2,通过配置文件(name.properties)运行类中的方法
2.读取name.properties配置文件,并获取文件中的键对应的值,然后使用反射创建该类实例,调用方法

xxx.properties/xxx.xml/xxx.yml(springboot)
都放在src目录下:类路径下

 1)获取当前类的字节码文件对象
 2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器
 3)ClassLoader:类加载器
           public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象
 4)创建Properties属性集合列表对象
       load(InputStream in)
 5)获取key对应的value---反射创建实例,调用方法....


public class Test2 {

    public static void main(String[] args) throws Exception {

//        1)在哪个类下去读取这个配置文件:获取当前类的字节码文件对象
       Class clazz =  Test2.class ;
//        2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器
        ClassLoader classLoader = clazz.getClassLoader();
        //3)  public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象
        InputStream inputStream = classLoader.getResourceAsStream("name.properties");
        //4)创建Properties属性集合列表对象
        Properties prop = new Properties() ;
        //加载
        prop.load(inputStream);
//        System.out.println(prop);

        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        //反射创建该类实例
        Class c = Class.forName(className) ;
        Object obj = c.newInstance();

        //获取Method对象--调用
        Method method = c.getMethod(methodName);
        method.invoke(obj) ;
    }
}

3.ArrayList,有一些字符串元素,如何给ArrayList中添加Integer元素呢?
public class Test1 {
    public static void main(String[] args) throws Exception {
        //创建ArrayList集合对象
        ArrayList<String> array = new ArrayList<String>() ; //明确数据类型:防止运行时期异常
        array.add("hello") ;
        array.add("world") ;
        array.add("java") ;
        //array.add(100) ; //直接添加不了

        //通过反射的方式去完成
        //获取ArrayList集合的字节码文件对象
        Class clazz = array.getClass();
       // System.out.println(clazz);//class java.util.ArrayList

        //通过反射的方式:调用public boolean add(E e) {}
        //通过反射获取Method对象所代表的的成员方法
        //getMethod("方法名",形式参数类型的class属性)---->Method m
        Method method = clazz.getMethod("add", Object.class) ;
        //调用这个方法
        method.invoke(array,100) ;
        method.invoke(array,10) ;
        method.invoke(array,30) ;

        System.out.println(array);


    }
}

动态代理

代理模式
代理模式:
       静态代理----- Thread implements Runnable接口
               真实角色和代理角色:实现同一个接口
 
       动态代理----->
               在程序执行过程中:产生的代理对象
               代理角色---对当前真实角色的功能进行增强!
              
          	jdk动态代理:基于接口
            cglib动态代理:基于子类
jdk动态代理
	java.lang.reflect.Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。---->产生代理角色
	
	public static Object newProxyInstance(ClassLoader loader,
                                        Class<?>[] interfaces,
                                        InvocationHandler h)
 
                      参数1:代理类的加载器
                      参数2:代理类要实现的接口列表
                      参数3:代理实例的处理程序
 
                      返回结果:带有代理类的指定调用处理程序的代理实例
                       
 mybatis 里面动态代理---实质就是Proxy作为父类间接实现代理
public class Test {
    public static void main(String[] args) {

        //接口多态
        UserDao ud = new UserDaoImpl() ;  //目标角色
        ud.add();
        ud.delete();
        ud.update();
        ud.find();

        System.out.println("---------------------------------");

        //优化:需要使用jdk动态代理:在程序运行的时候,产生代理角色---UserDao的代理角色
        //获取基于代理处理程序---产生的代理类
     
        UserDao ud2 = new UserDaoImpl() ;

        //接口多态
        InvocationHandler handler = new MyInvocationHandler(ud2) ;
        UserDao udProxy = (UserDao) Proxy.newProxyInstance(ud2.getClass().getClassLoader(),
                ud2.getClass().getInterfaces(), handler);
        //udProxy----代理角色
        udProxy.add();
        udProxy.delete();
        udProxy.update();
        udProxy.find();
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//代理的处理程序
public class MyInvocationHandler implements InvocationHandler {

    //声明目标角色
    private Object target ; //UserDao
    public MyInvocationHandler(Object target){
        this.target = target ;
    }

    //对真实角色进行增强
    //method----基于代理的底层方法:add,delete,update,find...
    //args:参数--->这些方法的 对象数组
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验...");
        Object obj = method.invoke(target, args); //代理角色
        System.out.println("产生日志记录");
        return obj;
    }
}
//持久层接口---用户操作
public interface UserDao {

    void add() ;//添加
    void update(); //修改
    void delete() ;//删除
    void find() ;//查询

}
//接口的实现层
public class UserDaoImpl implements  UserDao {
    @Override
    public void add() {
        System.out.println("添加用户...");
    }

    @Override
    public void update() {
        System.out.println("修改用户...");
    }

    @Override
    public void delete() {
        System.out.println("删除用户...");
    }

    @Override
    public void find() {
        System.out.println("查询用户...");
    }
}

p.getProperty(“methodName”);

       Class clas = Class.forName(classname);
       Constructor con = clas.getConstructor();
       Object obj = con.newInstance();

       Method m = clas.getDeclaredMethod(methodname);

       m.invoke(obj);


   }

}
public class Student {

public void love(){
    System.out.println("love Java...");
}

}
public class Teacher {

public void love(){
    System.out.println("love 高圆圆...");
}

}


#### 2,**通过配置文件(name.properties)运行类中的方法**

```java
2.读取name.properties配置文件,并获取文件中的键对应的值,然后使用反射创建该类实例,调用方法

xxx.properties/xxx.xml/xxx.yml(springboot)
都放在src目录下:类路径下

 1)获取当前类的字节码文件对象
 2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器
 3)ClassLoader:类加载器
           public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象
 4)创建Properties属性集合列表对象
       load(InputStream in)
 5)获取key对应的value---反射创建实例,调用方法....


public class Test2 {

    public static void main(String[] args) throws Exception {

//        1)在哪个类下去读取这个配置文件:获取当前类的字节码文件对象
       Class clazz =  Test2.class ;
//        2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器
        ClassLoader classLoader = clazz.getClassLoader();
        //3)  public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象
        InputStream inputStream = classLoader.getResourceAsStream("name.properties");
        //4)创建Properties属性集合列表对象
        Properties prop = new Properties() ;
        //加载
        prop.load(inputStream);
//        System.out.println(prop);

        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        //反射创建该类实例
        Class c = Class.forName(className) ;
        Object obj = c.newInstance();

        //获取Method对象--调用
        Method method = c.getMethod(methodName);
        method.invoke(obj) ;
    }
}

3.ArrayList,有一些字符串元素,如何给ArrayList中添加Integer元素呢?
public class Test1 {
    public static void main(String[] args) throws Exception {
        //创建ArrayList集合对象
        ArrayList<String> array = new ArrayList<String>() ; //明确数据类型:防止运行时期异常
        array.add("hello") ;
        array.add("world") ;
        array.add("java") ;
        //array.add(100) ; //直接添加不了

        //通过反射的方式去完成
        //获取ArrayList集合的字节码文件对象
        Class clazz = array.getClass();
       // System.out.println(clazz);//class java.util.ArrayList

        //通过反射的方式:调用public boolean add(E e) {}
        //通过反射获取Method对象所代表的的成员方法
        //getMethod("方法名",形式参数类型的class属性)---->Method m
        Method method = clazz.getMethod("add", Object.class) ;
        //调用这个方法
        method.invoke(array,100) ;
        method.invoke(array,10) ;
        method.invoke(array,30) ;

        System.out.println(array);


    }
}

动态代理

代理模式
代理模式:
       静态代理----- Thread implements Runnable接口
               真实角色和代理角色:实现同一个接口
 
       动态代理----->
               在程序执行过程中:产生的代理对象
               代理角色---对当前真实角色的功能进行增强!
              
          	jdk动态代理:基于接口
            cglib动态代理:基于子类
jdk动态代理
	java.lang.reflect.Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。---->产生代理角色
	
	public static Object newProxyInstance(ClassLoader loader,
                                        Class<?>[] interfaces,
                                        InvocationHandler h)
 
                      参数1:代理类的加载器
                      参数2:代理类要实现的接口列表
                      参数3:代理实例的处理程序
 
                      返回结果:带有代理类的指定调用处理程序的代理实例
                       
 mybatis 里面动态代理---实质就是Proxy作为父类间接实现代理
public class Test {
    public static void main(String[] args) {

        //接口多态
        UserDao ud = new UserDaoImpl() ;  //目标角色
        ud.add();
        ud.delete();
        ud.update();
        ud.find();

        System.out.println("---------------------------------");

        //优化:需要使用jdk动态代理:在程序运行的时候,产生代理角色---UserDao的代理角色
        //获取基于代理处理程序---产生的代理类
     
        UserDao ud2 = new UserDaoImpl() ;

        //接口多态
        InvocationHandler handler = new MyInvocationHandler(ud2) ;
        UserDao udProxy = (UserDao) Proxy.newProxyInstance(ud2.getClass().getClassLoader(),
                ud2.getClass().getInterfaces(), handler);
        //udProxy----代理角色
        udProxy.add();
        udProxy.delete();
        udProxy.update();
        udProxy.find();
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//代理的处理程序
public class MyInvocationHandler implements InvocationHandler {

    //声明目标角色
    private Object target ; //UserDao
    public MyInvocationHandler(Object target){
        this.target = target ;
    }

    //对真实角色进行增强
    //method----基于代理的底层方法:add,delete,update,find...
    //args:参数--->这些方法的 对象数组
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验...");
        Object obj = method.invoke(target, args); //代理角色
        System.out.println("产生日志记录");
        return obj;
    }
}
//持久层接口---用户操作
public interface UserDao {

    void add() ;//添加
    void update(); //修改
    void delete() ;//删除
    void find() ;//查询

}
//接口的实现层
public class UserDaoImpl implements  UserDao {
    @Override
    public void add() {
        System.out.println("添加用户...");
    }

    @Override
    public void update() {
        System.out.println("修改用户...");
    }

    @Override
    public void delete() {
        System.out.println("删除用户...");
    }

    @Override
    public void find() {
        System.out.println("查询用户...");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值