Java学习---第七周总结

1. Thread 类(续), 线程池(重点)

1.1 如何解决多线程安全问题?------(同步机制)

1.1.1 检验多线程安全问题的标准:
	1)是否存在多线程环境
	2)是否有共享数据
	3)是否存在多条语句对共享数据进行操作   更改这个标准
1.1.2 方法
 1: 同步代码块:
 	使用synchronized(锁对象){		-----Lock  接口
        多条语句对共享数据进行操作
    }
 2:   
 	volatile关键字:也可以解决线程安全
 3: 等待唤醒机制:
 	wait()+notify():等待唤醒机制:也属于同步机制一种
    对共享数据进行操作,使用锁操作,别的线程没有方法访问,除非当前锁被释放了,然后才能互相抢占CPU执行权!
 4: 同步方法

1.2 什么是同步方法,锁对象是谁?

    跟所谓成员方法定义格式一样,就是在当前成员方法中,如果方法体的内容是一个同步代码块,可以进行优化,
    将synchronized定义方法声明上---同步方法,------锁对象:就是this

    静态的同步方法跟类相关,锁对象--->当前类名.class属性---->Class 字节码文件对象(class  包名.类名)

1.3 wait()和notify()为什么定义在Object类中,不定义在Thread类中

wait()和notify():
	代表线程处于等待或者线程唤醒,他们的调用是要通过同步机制中的锁对象来访问的,(锁对象可以是任意的Java类对象)	当前某个线程持有锁的时候,调用wait(),处于等待过程,但是立即释放锁,然后可以通过锁对象.notify()唤醒对方线程,解决死锁问题; 

等待唤醒机制:
	生产者线程不断的产生数据,没有数据了,等待先产生数据,有数据了之后,需要通知消费者线程来使用数据;
	消费者线程不断的使用数据,有数据了,先等待使用数据,当数据使用完毕,没有数据了,需要通知生产者线程产生数据;

1.4 sleep()和wait()方法的区别 (面试题)

1)来源不同
	sleep()来自于Thread类中,表示线程睡眠 sleep(long time):时间毫秒值  
	wait()来自于Object类中,表示线程等待,需要被锁对象来访问
2)是否会释放锁
	 sleep(long time):属于一个普通方法,调用该方法,不会去释放锁,只是导致线处于睡眠(线程阻塞),当线程的睡眠时间到了,就继续执行线程;
	 wait():属于锁方法,被锁对象访问之后,会立即释放锁,才能够使用同步等待唤醒机制解决死锁问题,通过锁对象调用notify(),唤醒对方线程;
3)这两个方法都会抛出异常(中断异常),都是属于本地方法,底层非Java语言实现
    当线程睡眠过程中,睡眠还没到,导致的睡眠状态被打断,就会出现InterruptedException
    当线程处于等待状态,被中断,就抛出这个异常!
		 public static native void sleep(long millis) throws InterruptedException
		 public final native void wait(long timeout) throws InterruptedException;	   

1.5 Thread类中:

public final ThreadGroup getThreadGroup():获取当前线程所属的线程组
       ThreadGroup
       public final String getName():获取线程组名称
         
ThreadGroun的构造方法:
       public ThreadGroup(String name)

示例:

 MyRunnable implents Runnable(){}
 ..
 // 创建runnable对象
 MyRunnable myRunnable = new MyRunnable() ;
 //创建线程
 Thread t1 = new Thread(myRunnable) ;
 //获得该线程所爱的线程池
 ThreadGroup tg1 = t1.getThreadGroup();

1.6 第三种方式实现线程的创建:使用线程池 (重点)

工厂类: 里面提供了一个静态方法: 固定线程池
	public static ExecutorService newFixedThreadPool(int nThreads);创建一个固定的可重复的线程数的线程池
	
 ExecutorService:接口 它可以进行多个异步任务的执行
            <T> Future<T> submit(Callable<T> task) :提交异步任务, 这些异步任务就需要执行Callable中的call方法:
                     V call() throws Exception :

     A: 如果仅仅是要实现多个线程并发执行,那么调用sumbit的方式,返回值可以不写
     B: 如果是要计算具体的结果,那么就需要将Future返回,它表示异步计算的结果值是什么
1.6.1 A 示例: 不计算结果,仅仅展示:多个线程并发执行
// CALLABLE 子实现类
public class MyCallable implements Callable {
    //不计算结果,仅仅展示:多个线程并发执行
    @Override
    public Object call() throws Exception {

        for(int x = 0 ; x < 100 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
        return null;
    }
}
//测试类
public class ThreadPoolTest {
    public static void main(String[] args) {

        //public static ExecutorService newFixedThreadPool(int nThreads)
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        //<T> Future<T> submit(Callable<T> task)
        //接口多态
        Callable callable = new MyCallable() ;
        //两个线程分别执行异步任务, 没有返回值, 只是连个线程分别执行
        threadPool.submit(callable) ;
        threadPool.submit(callable) ;

        //使用完毕,关闭线程池
        //线程需要归还到线程池中
        //void shutdown()
        threadPool.shutdown() ;
    }
}
1.6.2 B : 两个线程分别进行求和!----计算结果 使用线程池完成
/*<T> Future<T> submit(Callable<T> task)
 *          Future:计算的结果 接口
 *                  V get()获取结果
 */
 // callable子实现类
 public class MyCallable implements Callable<Integer> {
    private int num ;
    public MyCallable(int num){
        this.num = num ;
    }

    @Override
    public Integer call() throws Exception {
        //定义最终结果变量
        int sum = 0 ;
        for(int x = 1 ; x <= num ; x ++){
            sum += x ;
        }
        return sum;
    }
}
//测试类
public class ThreadTest {
    public static void main(String[] args) throws
            ExecutionException, InterruptedException {

        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(2);// 线程数2 的固定线程池

        //提交异步任务进计算
        Future<Integer> f1 = threadPool.submit(new MyCallable(100));
        Future<Integer> f2 = threadPool.submit(new MyCallable(200));
		// 有返回值, 两个线程分别进行计算
        Integer i1 = f1.get();// 计算0-100之和
        Integer i2 = f2.get();// 计算0-200之和
        System.out.println(i1) ;
        System.out.println(i2) ;

        //关闭线程池
        threadPool.shutdown() ;
    }
}

1.7 线程池的7大参数(重点)

线程池的的优化:
	同过参数进行优化.
7大参数:
	private volatile int corePoolSize: 核心线程数量
	
	private volatile int maximumPooSize: 允许最大线程池数: 
			当 corePoolSize 数量已经最大,且WorkQueue已经塞满,继续创建线程
				
	private volatile long keepAliveTime:临时线程存活时间
			如果当前线程数量达到核心线程数量,开启临时线程.
	
	private final BlockQueue<Runnable>workQueue: 工作队列, 先进先出
    		当已达到最大核心线程数量,所有线程都会进入工作队列, 当前工作队列中所有线程塞满了, 处于阻塞状			态, 等待被使用; 本质的底层原理: collection
	
	private volatile ThreadFactory threadFactory: 线程工厂. 
			创建线程实例的子实现类
	
	private volatile RejectedExecutionHandler handler: 线程池的拒绝策略.
			当线程池中线程数量达到了最大线程数量, 就会调用此方法,启用拒绝策略,保证当前线程池中所有线程可			重复用.
			
	TimeUnit unit: unit 是 keepAliveTime的计量单位.

1.8 多线程安全问题:

1.8.1 安全问题的校验标准:
    1)检查你的程序是否为多线程环境      是
    2)是否存在共享数据:                 存在
    3)是否有多条语句对共享数据进行操作  也存在
1.8.2 解决方法
    现在环境就需要使用多线程实现  --> 1)不能更改
    必须存在共享数据,因为三个线程共享一个内存区域  -->  2)标准不能被更改
    Java提供了:使用同步代码块 将 多条对共享数据的操作包裹起来,需要 ---> 更改3)
1.8.2.1 解决方法1: 同步代码块
            synchronized(锁对象){     
               多条语句对共享数据进行操作
              }
锁对象:
	可以是任意的Java类对象(jdk提供的任意类或者自定义类型)
	多个线程使用的必须是  同一个锁对象!   理解为:"火车上上厕所的门开和关!"
1.8.2.2 解决方法1: 同步方法
如果一个方法,一进来就是同步代码块,我们就可以把synchronized,提到方法声明上, 将这个方法变为同步方法.
            权限修饰符 synchronized 返回值类型 方法名(形式参数列表){
                               ...
                           }
同步方法:
		都指定的非静态的同步方法
隐藏的锁对象:
			this:代表当前对象的地址值引用
一个静态方法,可以定义静态的同步方法:
锁对象:
			跟类的字节码文件有关系:类名.class

1.9 死锁现象:

原因:多个线程在互相抢占资源数据的时候,出现了线程互相等待的情况!
解决办法:
	必须保证多个线程 "共享同一个资源对象"------>生成者消费者模式思想操作	

1.10 生产者和消费者模式

生产者和消费者共同使用同一个资源对象.
生产者: 
	 产生数据,等待消费者使用. wait()---> 产生数据了,通知消费者线程来消费数据--->notify()
消费者:
	 消费数据,等待生产者生产. wait()---> 消费完数据了,通知生产者线程生产数据--->notify()

1.11 示例: 模拟生产者和消费者模式

(生产者消费者模式 + 等待唤醒机制)
    ThreadDemo类:main线程:开启两个线程
    Student类: 学生数据  (姓名和年龄)
    SetThread: 生产者资源类:产生学生数据
    GetThread:消费者资源类: 使用学生数据
分析:
 	1) 需要使用同一个资源对象(student)
 	2) 同步代码块 解决数据紊乱问题(姓名和年龄不符!)	
 	3) 等待唤醒机制,信号灯法,解决线程抢占问题(消费者使用数据:一次性打印一大片,想出现依次打印
           高圆圆 42
           李云迪 39)

示例:

//学生类
public class Student {
    String name ; //姓名
    int age ;//年龄
    boolean flag ; //默认没有数据,通过这标记信号:表示是否存在数据

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
// 生产者资源类
public class SetThread implements  Runnable {

    private Student s ; //声明学生变量s
    public SetThread(Student s){
        this.s = s ;
    }

    //统计变量
    private int x = 0 ;

    @Override
    public void run() {
        while(true){
            //t1
            synchronized (s){
                //判断: 如果当前生产者没有数据,先等待产生数据       
                if(s.flag){
                    try {
       //wait()方法为什么定义Object类中? 因为锁对象可以是任意的Java类对象 (包括Object)
        //wait()一旦被调用,会立即释放锁
                        s.wait();                                   
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                   	 }
                }

                if(x % 2 ==0){
                    s.name ="高圆圆" ;
                    s.age = 42 ;
                }else {
                    s.name = "李云迪";
                    s.age = 39;
                }
                //改变标记:有数据了
                //改变标记
                s.flag = true ;
                //调用通知对方:唤醒对方线程
                s.notify() ;
            }
            x++ ;//原子性操作
        }
    }
}
// 消费者资源类
public class GetThread implements Runnable {

    //声明学生变量s
    private Student s ;
	// 成员变量赋值给局部变量
    public GetThread(Student s) {
        this.s =  s ;
    }

    //Student s= new Student() ;
    @Override
    public void run() {

        //使用学生数据
        //System.out.println(s.name+"-"+s.age) ;
        //不断的去使用数据
        while(true){
            synchronized (s){
                //如果当前消费者存在数据
                if(!s.flag){
                    //等待将产生的数据消费掉
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(s.name+"---"+s.age);
                //改变标记
                s.flag= false ;
                //通知生成者线程,产生数据
                s.notify() ;
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {

        //创建一个学生对象
        Student s = new Student() ;

        //创建生产者资源类对象
        SetThread st = new SetThread(s) ;
        //创建消费者资源类对象s
        GetThread gt = new GetThread(s) ;

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

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

2. 代理–静态/ 动态代理代理

2.1 什么代理?

  就是代理角色帮助真实角色完成一些事情!
  举例:
   	过年回家,让朋友代买火车票

2.2 静态代理:

     通过代理角色帮助真实角色完成一些事情,真实角色只只专注于自己的事情!
     前提条件:代理角色和真实角色必须是同一个接口!
  举例:
  	 结婚这件事情
        真实角色:You
        代理角色:WeddingCompany:婚庆公司

2.3 jdk动态代理

jdk动态代理----必须存在接口 ----通过反射方式完成:
需要将业务功能和系统监控的功能,分离起来!

UserDao接口
	void add() ;
	void updte() ;
	void delete() ;
	void find() ;

java.lang.reflect.Proxy

	public static Object  newProxyInstance(
	ClalssLoader loader,
	Class[] interfaces,
	InvocationHandler handler) ; //接口 基于代理的处理程序,需要对业务功能代码增强
	自定义类实现InvocationHandler ,重写invoke方法,完成方法增强!

3. 等待唤醒机制–信号灯法:

3.1 将等待唤醒机制:解决死锁问题代码进行优化 (“信号灯法”)

      1)将数据:Student类的成员加入私有化
      2)在Student类中提供set方法,对学生数据进行赋值,加入同步方法
      3)在生产者资源类中,只需要调用当前set方法进行赋值
      4)在Student类中提供get方法,获取学生数据,加入同步方法
      5)在GetThread:消费者资源类中,调用get方法即可!

示例:

//学生类:
public class Student {
    private String name ; //姓名
    private int age ;//年龄
    private boolean flag ; //默认没有数据,通过这标记信号:表示是否存在数据

    /**
     * 给学生数据进行赋值
     * @param name  姓名
     * @param age 年龄
     */
    public synchronized  void set(String name,int age){
        //如果方法一进来就是一个同步代码块,需要将synchronized定义在方法声明上,锁对象this
            //判断:
            //如果当前生产者没有数据,先等待产生数据
            if (this.flag) {
                try {
                    this.wait(); //wait()方法为什么定义Object类中? 因为锁对象可以是任意的Java类对象 (包括Object)
                    //     //wait()一旦被调用,会立即释放锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
           }
            //赋值
           this.name = name ;
           this.age = age ;

        //改变标记:有数据了
        //改变标记
        this.flag = true ;
        //调用通知对方:唤醒对方线程
        this.notify() ;
    }

    /**
     * 获取学生数据
     */
    public synchronized void get(){

            //如果当前消费者存在数据
            if (!this.flag) {
                //等待将产生的数据消费掉
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(this.name + "---" + this.age);

        //改变标记
        this.flag= false ;
        //通知生成者线程,产生数据
        this.notify() ;
    }
}

// 生产者资源类
public class SetThread implements  Runnable {
    //Student s = new Student() ;

    private Student s ; //声明学生变量s
    public SetThread(Student s){
        this.s = s ;
    }

    //统计变量
    private int x = 0 ;
    @Override
    public void run() {
        while(true){
            if(x % 2 ==0){
                s.set("高圆圆",42);
            }else {
                s.set("赵又廷",45);
            }
            x++ ;//原子性操作
        }
    }
}
 //消费者资源类
public class GetThread implements Runnable {

    //声明学生变量s
    private Student s ;
    public GetThread(Student s) {
        this.s =  s ;
    }

    //Student s= new Student() ;
    @Override
    public void run() {
        //使用学生数据
        //System.out.println(s.name+"-"+s.age) ;
        //不断的去使用数据
        while(true){
            s.get();
        }
    }
}

//测试类
public class ThreadDemo {
    public static void main(String[] args) {

        //创建一个学生对象
        Student s = new Student() ;

        //创建生产者资源类对象
        SetThread st = new SetThread(s) ;
        //创建消费者资源类对象s
        GetThread gt = new GetThread(s) ;

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

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

4. LOCK 锁

jdk5以后提供了一个juc包下(java.util.concurrent.locks )
 	Lock接口  比	synchronized具有扩展性的功能
  		子实现类ReentrantLock:可重入的互斥锁
           public void lock()获取锁
           public void unlock()释放锁

API举例
             Lock l = ...;
             l.lock();
             try {
                // access the resource protected by this lock
                } finally {
                l.unlock();
                }
        处理异常:          try...catch...finally(释放资源:代码一定会执行/除非执行之前jvm退出了)         捕获异常的标准格式
                         try...catch...catch...
                         try...finally
                 throws  :抛出异常:抛出在方法声明上

示例 : 使用这个Lock:模拟电影院三个窗口同时卖票100张

// runable 子实现类
public class SellTicket implements Runnable {
    //票数
    private  static int tickets = 100 ;

    //创建一个锁:Lock ---子实现类:可重入的互斥锁
    private ReentrantLock lock = new ReentrantLock() ;

    @Override
    public void run() {
        //模拟一直有票
        while(true){

            //获取锁
            lock.lock() ;
            try{
                if(tickets > 0){
                    //睡眠100毫秒  (模拟网络延迟睡眠100毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                }
            }finally {
                //释放资源代码块: 结合捕获异常 try...catch...finally一块使用:变形格式 try...finally
                lock.unlock();
            }
        }
    }
}
// 测试类
public class LockDemo {
    public static void main(String[] args) {

        //创建资源类对象
        SellTicket st = new SellTicket() ;// st: 共享资源类

        //创建三个线程
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;

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

5. TIMER 定时器

java.util.Timer :JavaSE的定时器  :处理一次或者重复执行的任务( TimerTask:抽象类 )
                  JavaEE的定时任务框架Quartz----后面可以"Spring"整合

5.1 构造方法:

	public Timer():创建一个新的定时器,不是守护线程!

5.2 成员方法:

       public void cancel():取消定时器
       public void schedule(TimerTask task,long delay):经过多少毫秒后执行一次定时任务
         
       public void schedule(TimerTask task,long delay,long period)
       经过delay毫秒后指向定时任务,每经过period重复执行定时任务!
         
       public void schedule(TimerTask task,Date time) :在指定时间的时候(精确到毫秒)执行定时任务

5.3 示例:

public class TimerDemo {
    public static void main(String[] args) {

        //创建一个定时器
        //空参构造
        Timer timer = new Timer( );

        //开启定时任务
     /*   timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("bom...");
            }
        }, 3000);*/

     // public void schedule(TimerTask task,long delay,long period)
        timer.schedule(new MyTask(timer),2000,3000);
    }
}

//定时任务
class MyTask extends TimerTask{

    //声明定时器变量
    private Timer t ;
    public MyTask(Timer t){
        this.t = t ;
    }
    @Override
    public void run() {
        System.out.println("bom...");
        t.cancel() ;
    }
}

6. 设计模式和原则

6.1 简单工厂模式

简单工厂模式:
      也称为"静态工厂方法模式",主要作用负责某些类的实例的创建过程!
优点:
    	通过工厂类完成各个类的对象的创建
弊端:
    	当有新的类型增加,需要不断的修改工厂类,维护性难度大!

6.2 工厂模式

工厂方法模式:
       工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
优点:
	具体的工厂类负责创建当前具体的实例,结构层次很清晰,有具体类/工厂类/抽象工厂(接口)
弊端:
	有新的类型增加,需要编写外的代码,所有代码增加,导致工作量增加了

6.3 单例模式

 单例:
    一个类的实例在内存中有且仅有一个!
    在程序内存中,始终只有一个对象!
6.3.1 饿汉式:
 饿汉式:当类一加载,就创建了当前类对象,而且始终只有一个
           1)自定义类:具体类
           2)无参构造方法私有化,目的:外界不能创建对象了
           3)在当前类的成员位置:创建自己本身对象
           4)在当前类中提供一个对外的公共的静态的功能,返回值是它本身

示例:

public class SingleObjectPattern {
    public static void main(String[] args) {

       /* Student s1 = new Student() ;
        Student s2 = new Student() ;
        //多例了;对象不同
        System.out.println(s1==s2);
        System.out.println(s1.getClass()==s2.getClass());*/ //获取字节码文件对象*/

        //Student.s = null ;//没有意义:直接赋值空对象,不安全

        Student s1 = Student.getInstance();
        Student s2 = Student.getInstance();
        Student s3 = Student.getInstance() ;
        System.out.println(s1==s2) ; // true: 都是同一个student对象
        System.out.println(s2==s3) ; // true
    }
}
// 学生类
public class Student {//具体类

    //成员位置:创建当前类的实例
    private static Student s = new Student() ;
    //无参构造私有化
    private Student(){}

    //在当前类中提供一个对外的公共的静态的功能,返回值是它本身
    public static Student getInstance(){
        return s ;//静态只能访问静态
    }
}
6.3.2 懒汉式( 重点):
懒汉式:
   1)自定义类:具体类
   2)无参构造方法私有化,外界不能创建当前类对象
   3)需要在成员位置,声明当前类型的变量
   4)提供一个对外公共的并且静态的访问方法,返回值就是当前类本身

可能出现安全问题的一种单例模式
           1)懒加载 (延迟加载)  Mybatis框架:延迟加载(用户表和账户表)
           2)可能存在一种多线程安全问题
  
     面试官都会问:"什么懒汉式",如果保证懒汉式安全的?
     	同步方法,解决安全问题
public class SingleObjectPattern2 {
    public static void main(String[] args) {

        //测试
        Teacher t1 = Teacher.getInstance() ;
        Teacher t2 = Teacher.getInstance() ;
        System.out.println(t1== t2) ; // true : 初始值都是null,所以new的同一个teacher
    }
}
// 老师类
public class Teacher  {//具体类

    private  static Teacher t ; //声明  默认值null
    //无参构造方法私有化
    private Teacher(){

    }

    //对外提供一个静态的公共访问方法,返回值是当前类本身
    //每一个用户都需要请求getInstance():每一个用户相当于线程
    //t1,t2,t3  都执行这个代码
  
    public static synchronized  Teacher getInstance(){ //静态的同步方法
        //先判断
        //如果当前t变量为null,说明并没有创建当前类实例 (在使用的时候才创建)
        if(t == null){
            t = new Teacher() ; //创建对象
        }
        return  t ;
    }
}

6.4 比较重要的设计原则

开闭原则: 
	对代码的修改关闭, 对扩展开放;
单一职责原则:
	"低耦合,高内聚", 所有设计必须遵循.
接口分离:
	接口和接口之间必须互相独立, 代表的是某个"模块".
依赖注入:
	面向接口或者抽象类进行编程.
迪米特原则:
	降低耦合性----反射.

7. Runtime 类

 Runtime类:
 	每个Java程序都有自己的运行环境,通过Runtime创建的实例表示:当前系统的运行环境

7.1 示例:

public class RunTimeDemo {
    public static void main(String[] args) throws IOException {
        //创建当前运行环境的实例
        Runtime runtime = Runtime.getRuntime() ;

        //获取计算机cpu的处理器数量
        //public int availableProcessors()
        System.out.println(runtime.availableProcessors());

        //操作指令
//        public Process exec(String command) throws IOException
       // System.out.println(runtime.exec("calc")); //打开计算器
       // System.out.println(runtime.exec("mspaint")); //打开计算器
       // runtime.exec("notepad") ;
     //   runtime.exec("qq") ;

        //关机指令
     //   runtime.exec("shutdown -s -t 300") ; //300秒后关机
        runtime.exec("shutdown -a") ; //取消关机
    }
}

8. 递归

8.1 什么是递归

 	方法调用方法本身的一种现象! 而不是方法嵌套方法

8.2 递归的思想

    1)必须定义一个方法(函数)
    2)有一定的规律
    3)必须有出口条件(结束条件)   ,如果没有结束条件就是死递归!
    构造方法不存在递归;

8.3 示例1: 求5的阶乘!

/* 分析: a)必须有一个方法
 *  	b)有规律
 *  	c)有结束条件(出口条件) */
public class DiGuiTest {
    public static void main(String[] args) {
        System.out.println("5的阶乘是:"+getResult(5)) ;
    }

    //定义方法
    private static int getResult(int n) {//5
        //判断n的值
        if(n==1){
            return  1 ; //出口条件(结束条件)
        }else{
            return n*getResult(n-1) ;//5*getResult(4) ;
        }
    }
}

示例2: 需求:

 *           有一对兔子,从出生后第三个月起每个月产生一对兔子,小兔子长到第三个月后每个月又产生一对兔子;如果兔子都不死,
 *           第二十个月兔子的对数是多少?     (不死神兔)
 *           *   已知就是前两个月的兔子的对数都是1
public class DiGuiTest2 {

    public static void main(String[] args) {

        System.out.println("第二十个月的兔子对数是:"+getRabbit(20)) ;
    }

    //定义一个方法
    public static int getRabbit(int n){//第n个月
        //出口条件
        //n==1或者n==2: 兔子的对数1
        if(n==1|| n==2){
            return 1 ;
        }else{
           return getRabbit(n-1) + getRabbit(n-2) ;
        }
    }
}

9. IO流的File类

描述的一个文件/目录的抽象路径名的形式
	File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
    File(String pathname)  :直接描述地址路径
    File(String parent, String child)

9.1 成员方法:

创建文件/创建文件夹相关的方法
    public boolean createNewFile() throws IOException:创建文件,如果文件不存在,就会自动创建,创建了,返回true
    public boolean mkdir():创建文件夹(目录),如果文件夹不存在,则自动创建;创建成功,返回true,否则false;
    public boolean mkdirs():如果父目录不存在,会自动创建:针对带多级目录创建
    public boolean delete():删除文件或者目录,如果File表示的目录,删除必须为空目录

如果没有带盘符呢,创建的文件或者文件夹去哪了?
默认就是在当前项目下

9.2 需求:

9.1.1 使用File表示E盘下的demo文件夹的a.txt文件
//方式1:File(String pathname)
	File f1 = new File("E:\\demo\\a.txt") ;

//方式2:File(File parent, String child)
    File f2  = new File("E:\\demo") ;
    File f3 = new File(f2,"a.txt") ;

//方式3:File(String parent, String child)
	File f4 = new File("E:\\demo","a.txt") ;

9.1.2 成员方法

public boolean createNewFile() throws IOException:创建文件,如果文件不存在,就会自动创建,创建了,返回true
public boolean mkdir():创建文件夹(目录),如果文件夹不存在,则自动创建;创建成功,返回true,否则false;
public boolean mkdirs():如果父目录不存在,会自动创建:针对带多级目录创建
public boolean delete():删除文件或者目录,如果File表示的目录,删除必须为空目录

如果没有带盘符呢,创建的文件或者文件夹去哪了?
默认就是在当前项目下

示例: 要删除当前D盘下所有的以.jpg结尾的文件

public class FileTest {

    public static void main(String[] args) {

        //   1)表示d盘
        File file = new File("d:\\") ;
        //2)需要将d盘下的里面的文件以及文件夹的名称都获取到
        File[] fileArray = file.listFiles() ;
        //3)先判断fileArray是否为空,防止空指针
        if(fileArray!=null){
            //遍历
            for(File f :fileArray){
                //f就是获取到的每一个file对象: 文件还是文件夹?  判断
                //1)判断当前File对象所表示的抽象路径名是否是一个文件
                if(f.isFile()){
                    //是文件
                    //需要满足,它的文件名称必须以.jpg结尾
                    if(f.getName().endsWith(".jpg")){
                        //就满足,删除文件
                        System.out.println(f.getName()+"----"+f.delete());
                    }
                }
            }
        }
    }
}

10. IO流

10.1 分类

IO流按流的方向: 
		输入 / 输出
按类型
    字节:先有               
        字节输入 :InputStream
        字节输出 :OutputStream
    字符:后有
        字符输入 :Reader
        字符输出 :Writer
    抽象类: 				它的子类
    InputStream			 XXXInputStream
    OutputStream		 XXXOutputStream
    Reader				 XXXReader
    Writer				 XXXWriter

10.2 字节输入流

10.2.1 字节输入流读数据的方式
构造方法:
	FileInputStream
	public FileInputStream(String name) throws FileNotFoundException:创建文件字节输入流对象
成员方法:
	 read这些方法:都是阻塞式方法:只要文件没有读完,一直等待要读取!
     abstract int read():一次读取一个字节,返回的读取的字节数
     int read(byte[] b)  :一次读取一个字节数组
A: 一次读取一个字节
public class FileInputStreamDemo3 {
    public static void main(String[] args) throws IOException {

        //创建文件字节输入流对象
        //FileInputStream fis = new FileInputStream("fis.txt") ;//当前项目下的fis.txt
        //读取的是当前项目下的java文件FileOutputStreamDemo2.java
        FileInputStream fis = new FileInputStream("FileOutputStreamDemo2.java") ;
        int by = 0 ;
        while((by=fis.read())!=-1){
            System.out.print((char)by);
        }
        //3)释放资源
        fis.close() ;
    }
}
B: 一次读取一个字节数组
public class FileInputStreamDemo4 {
    public static void main(String[] args) throws IOException {

        //读取是当前项目下的fis2.txt文件
        //创建文件字节输入流对象
        FileInputStream fis = new FileInputStream("fis2.txt") ;
        byte[] bytes = new byte[1024] ;//提供字节缓冲区
        //实际的长度:根据内容判断 :获取实际字节数
        int len = 0 ;
        //判断和获取一块使用
        while((len=fis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len)); //每次从0开始获取实际字节数---转换成字符串
        }
        //释放资源
        fis.close() ;
    }
}

10.3 字节输出流

字节输出流--->如何在写入文件的时候,进行换行操作呢?
	windows操作系统中:写入数据的时候,换行符号 "\r\n"

FileOutputStream的构造方法
	public FileOutputStream(String pathname,boolean append):如果第二个参数为true,则自动后面追加
10.3.1 throws ,throw和 try…catch…finally:
1)使用位置不同
    throws:跟在方法上,而且可以跟多个异常类名,中间逗号隔开
    throw:方法体的语句中
2)处理方式不同
    throws:它的处理交给调用者处理,谁调用这个带throws的方法,谁就需要进行处理
    throw:它的处理交给方法体中逻辑语句处理:
    			if(xx){
			
			}else{
			
					throw new XXXException() ;
				}
3)是否出现的可能性
     throws:可能出现问题
     throw:表示执行某段代码一定会出现这个异常
4)后面是否跟的是对象
     throws:后面跟的异常类名
     throw:后面的异常对象  new XXXExeption() ;

try...catch...finally:
        捕获----通过业务逻辑代码(业务层代码:service层)
                   业务层数据service----->来源于dao层代码(数据访问对象: JDBC)
                          dao层代码抛出,业务service代码调用dao层代码,需要捕获异常!
         try{

                    可能出现问题的代码;---一旦这有问题:Jvm 就会创建异常对象

                }   catch   (异常类名 e)  { //如果和当前异常类名匹配了
                       //处理异常
                       //System.out.println("数组角标越界了") ;
                       e.printStackTrace() ;//可以看到你自己写的代码哪块出错了以及底层原码
                }finally{
                   //释放资源
                    }

                try...catch
                try...catch...catch...catch
                try...finally

示例:

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {//自己使用可以去抛异常

        //创建文件字节输出流对象
       // FileOutputStream fos = new FileOutputStream("fos.txt") ;
        //public FileOutputStream(String pathname,boolean append)
        FileOutputStream fos = new FileOutputStream("fos.txt",true) ;
        //写数据
        //写一个字节数组
        for(int x = 0 ; x < 10 ; x++){
            fos.write(("hello"+x).getBytes()) ;
            //换行符号 "\r\n"
           fos.write("\r\n".getBytes());
        }
        //释放资源
        fos.close() ;
    }
}

10.4 面试题: 什么是阻塞式流:

阻塞式IO:BIO
	IO即input/output,阻塞式IO指的是“一旦输入/输出工作没有完成,则程序阻塞,直到输入/输出工作完成”。

非阻塞式IO:
	非阻塞式IO其实也并非完全非阻塞,通常都是通过设置超时来读取数据的。未超时之前,程序阻塞在读写函数上;超	时后,结束本次读取,将已读到的数据返回。

10.5 字节高效流

10.5…1 字节缓冲输入流:BufferedInputStream
构造方法:
 		BufferedInputStream(InputStream in) 
成员方法:
		使用InputStream的read的功能:
        一次读取一个字节/一次读取一个字节数组
10.5.2 字节缓冲输出流:BufferedOutputStream
这个流仅仅是在内部提供了一个缓冲区(字节数组),针对文件的输出并且同时写入数据使用的还是底层流OutputStream
构造方法:
		public BufferedOutputStream(OutputStream out):构造一个默认的缓冲区大小,通过底层流进行输出(写入数据)
       一般情况:默认的缓冲区大小足够大了

示例:

public class BufferedOutputSteamDemo {
    public static void main(String[] args) throws IOException {
        //write();
        //读
        read() ;
    }

    //读取 当前项目下的bos.txt的内容
    private static void read() throws IOException {
        //创建字节缓冲输入流对象
        //BufferedInputStream(InputStream in)
        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() ;
    }

    private static void write() throws IOException {
        //创建字节缓冲输出流对象
        //public BufferedOutputStream(OutputStream out):形式参数是一个抽象类,需要子类对象

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;

        //写数据:使用的底层流的OutputStream的方法
        bos.write("hello,BufferedStream".getBytes());

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

10.6 合并流: SequenceInpuStream

是InputStream的子类:它这个类只能操作源文件. 可以实现a.txt/b.txt/c.txt----->复制到一个文件中
构造函数:
       public SequenceInputStream(Enumeration<? extends InputStream> e)
       public SequenceInputStream(InputStream s1,InputStream s2):将两个字节输入流对象指向的文件进行合并

示例:

需求:
        将当前项目下的FileOutputStreamDemo.java/FileOutputStreamDemo2.java
        ----复制到D:\EE_2110\day26\code\\Copy.java

        当两个以上的文件进行复制,
        public SequenceInputStream(Enumeration<? extends InputStream> e)
public class SequenceInpuStreamTest {
    public static void main(String[] args) throws IOException {
        //method1();
        method2() ;//多个文件进行复制(两个以上的文件)
    }
	//方法二: 
    private static void method2() throws IOException {
        //当前项目下的FileOutputStreamDemo.java/FileOutputStreamDemo2.java/InputAndOutputStreamCopy.java
        //复制到D:\EE_2110\day26\code\\MyCopy.java

        // public SequenceInputStream(Enumeration<? extends InputStream> e)
        //创建Vector集合<InputStream>
        Vector<InputStream> v  = new Vector<>() ;
        v.add(new FileInputStream("FileOutputStreamDemo.java")) ;
        v.add(new FileInputStream("FileOutputStreamDemo2.java")) ;
        v.add(new FileInputStream("InputAndOutputStreamCopy.java")) ;

        //类似于迭代器:获取Enumeration枚举组件接口对象
        Enumeration<InputStream> enumeration = v.elements();
        //直接创建合并流对象
        SequenceInputStream sis = new SequenceInputStream(enumeration) ;
        //封装目的地文件
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("D:\\EE_2110\\day26\\code\\MyCopy.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();
    }
	// 方法一: 合并流
    private static void method1() throws IOException {
        //创建两个字节文件输入流对象:指向两个文件
        InputStream in = new FileInputStream("FileOutputStreamDemo.java") ;
        InputStream in2 = new FileInputStream("FileOutputStreamDemo2.java") ;
        //封装到合并流中
        SequenceInputStream sis = new SequenceInputStream(in,in2) ;

        //目的地文件D:\EE_2110\day26\code\Copy.java
        //BufferedOutputStream流
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("D:\\EE_2110\\day26\\code\\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();
    }
}

10.7 字符流

 字符流出现的原因:
 	在字节流基础上,它会提供字符集,解决乱码问题!
 如果使用记事本打开一个文件,(文本文件):
 		优先采用的就是字符流!
 图片/音频/视频:
 		只能使用字节流
10.7.1 字符输出流 Writer:
 Writer:抽象类----->子类 :
 				字符转换输出流 OutputStreamWriter:里面包装的是字节流
 构造方法:
        OutputStreamWriter(OutputStream out) :使用平台默认的编码字符集进行编码--->写入数据
        OutputStreamWriter(OutputStream out,String charsetName )使用指定的编码字符集进行编码--->写入数据
 写的功能:
        write(int ch):写一个字符
        write(char[] chs):写字符数组
        write(char[] chs,int index,int len):写字符数组的一部分
        writer(String str):写字符串
        writer(String str,int index,int len):写字符串的一部分

示例:

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

        //创建字符输出流对象
        //OutputStreamWriter(OutputStream out)
       // Writer out = new OutputStreamWriter(new FileOutputStream("out.txt")) ;
        /*OutputStreamWriter oos = new OutputStreamWriter(
                    new FileOutputStream("oos.txt")) ;*///默认平台 utf-8 编码
        OutputStreamWriter oos = new OutputStreamWriter(
                new FileOutputStream("oos.txt"),"GBK") ;// 指定GBK 编码

        oos.write("hello,字符流");
        //释放资源
        oos.close() ;
    }
}
10.7.2 字符输入流:Reader:
Reader:抽象类--->子类:
		InputStreamReader:字符缓冲输入流:通向字节输入流的桥梁
构造方法:
      InputStreamReader(InputStream in) :使用平台默认的字符集进行解码,里面包装的字节流
      InputStreamReader(InputStream in,String charsetName):使用指定的字符集进行解码 
读取:
       读一个字符read()
       读一个字符数组read(char[] chs)

示例:

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

        //需要读取oos.txt
        //创建字符缓冲输入流对象
        //InputStreamReader(InputStream in)
        InputStreamReader isr = new InputStreamReader(
                new FileInputStream("oos.txt"),"GBK") ;

        //一次读取一个字符
      /*  int ch = 0 ;//字符数 ()
        while((ch=isr.read())!=-1){
            System.out.print((char)ch);
        }*/
      //一次读取一个字符数组
        char[] chs = new char[1024] ;
        int len = 0 ;
        while((len=isr.read(chs))!=-1){
            System.out.print((new String(chs,0,len))) ;
        }
        //释放资源
        isr.close() ;
    }
}
10.7.3 FileReader/FileWriter
使用Reader和Writer的子类进行操作:字符转换流(InputStreamReader/OutputStreamWriter)
复制文件,太麻烦了!
所以提供了便捷类:  FileReader/FileWriter 可以直接操作文件
        FileReader(String pathname):使用平台默认字符集:进行解码
        FileWriter(String pathname)使用平台默认字符集:进行编码
示例: 复制文本文件
public class CopyFileTest {

    public static void main(String[] args) {

        //捕获异常
        //创建字符输入流对象:操作源文件
        FileReader fr = null ;
        FileWriter fw = null ;
        try {
           fr = new FileReader("FileOutputStreamDemo.java") ;
           fw = new FileWriter("My.java") ;

            //读写操作
            //一次读取一个字符数组
            char[] chs = new char[1024] ;
            int len = 0 ;//实际字符数
            while((len=fr.read(chs))!=-1){
                //写
                fw.write(chs,0,len) ;
                fw.flush() ;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fw!=null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fr!=null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

10.8 字节缓冲流

10.8.1 BufferedWriter:字符缓冲输出流 (字符流的高效流)
仅仅提供缓冲区:
BufferedWriter(Writer out)  :提供默认缓冲区大小,默认值足够大:8192个长度(底层是一种字符数组)
特有功能:
       public void newLine() throws IOException :写入行的分隔符号

示例:

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

        //创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ;

        //写数据
        bw.write("hello");
        //调用 public void newLine() throws IOException :写入行的分隔符号
        bw.newLine() ;// 特有功能换行
        bw.write("world");
        bw.newLine() ;
        bw.write("java");
        bw.newLine() ;
        //字符流的使用:在关闭前最好刷新流
        //public void flush()
        bw.flush() ;

        //关闭
        bw.close();
    }
}
10.8.2 BufferedReader:字符缓冲输入流
提供默认的缓冲区大小 缓冲区足够大
	BufferedReader(Reader in)
特有功能:
	tring readLine() 读取一行内容

示例:

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

        //创建字符缓冲输入流对象:读取bw.txt
        BufferedReader br = new BufferedReader(new FileReader("bw.txt")) ;

        //bw.txt---拷贝D盘:字符缓冲输出流 my.txt
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\my.txt")) ;
         String line = null ;
        while((line=br.readLine())!=null){
            //System.out.println(line);
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //释放资源
        bw.close();
        br.close();
    }
}

11. 面试题: lock和synchronized区别

synchronized是一个关键字 :
		底层实现是一些指令:监视器锁(monitor),是一种悲观锁(独占锁),当某个线程持有这个锁的时候,其他线程访问不到;应用场景:少量同步代码使用synchronized;默认的锁的释放是需要等待执行完毕同步方法或者同步代码块
		当一个线程持有这把锁的时候,其他线程不能够修改当前共享数据的!
        执行效率就低了,而且可能如果资源类对象不是同一个,还可能会造成死锁!
Lock是接口:
		基于子实现类(ReentrantLock)进行实现:可重入的互斥锁,底层实现原理采用CAS(比较和交换)算法,本质属于乐观锁;应用场景:针对大量的同步代码使用Lock;本身就提供具体的功能,lock()/unlock()

12. 网络编程(重点)

12.1 网络编程三要素

12.1.1 ip地址:
ip地址使用的一种"点分十进制法": 10.12.159.xxx
A类: 国家部门
    前一个号段:网络号段
    后面三个号段:主机号段
B类: 大学/教育部门:
    前两个号段:网络号段
    后面两个号段:主机号段
C类:私人地址 (家庭/单位...)
    前三个号段:网络号段
    后面这个号段:主机号段
 windows系统:查看ipconfig
 Linux系统:查看ifconfig
12.1.2 端口(port)
 port:端口号:360软件---可以查看计算机中所有软件的端口!
  范围:
  		 0-65535
         0-1024属于保留端口
 常见端口:
 		 tomcat服务器  默认:8080        (极域软件:端口号8080)
         mysql:3306
         redis(NOSQL数据库)客户端端口:6575
         key:value
12.1.3 协议
12.1.3.1 UDP协议
     1)属于不可靠协议
     2)不需要建立连接通道,所以执行效率高,但是安全性低!
     3)发送内容的时候,有大写数据! 
     4)数据大小限制 64kb
12.1.3.2 TCP协议/IP协议
    1)属于可靠协议
    2)需要建立连接通道,服务器端监听客户端连接,如果客户端没有跟服务器端口进行绑定,服务器一直等待,执行效率低,但   是安全性高
    3)发送内容的时候,数据大小没有限制!	
    4)三次握手

12.2 InetAddress

静态方法:
	public static InetAddress getByName(String host)throws UnknownHostException通过主机名称获取ip地址对象.
InetAddress的其他成员方法:
    public String getHostName():获取ip地址对象所描述的主机的名称(计算机名称)
    public String getHostAddress():获取ip地址:以字符串形式展示的

12.3 UDP协议方式

12.3.1 UDP协议方式发送端
    1)创建UDP发送端的Socket对象
    2)创建数据报包
    3)发送
    4)释放资源
        socket对象.close()
12.3.2 UDP协议方式接收端
    1)创建接收端的Socket对象
    2)创建一个接收容器: 自定义一个缓冲区byte[]  (创建数据包报)
    3)接收
    4)解析真实数据
    5)展示ip和发送的内容

示例:

改进:UDP的发送端和接收端在一个窗口中进行聊天
UDP的发送端不断键盘录入数据,接收端不断的接收数据并且站数据数据:一个窗口中聊天呢
     多线程: 使用多线程实现方式2来实现
           分析
               1)SendThread:发送端的线程资源类  实现 Runnable接口  ,实现run方法
               2)ReveiveThread:接收端的线程资源类 实现Runnable接口 ,实现run方法汇总
               3)创建资源类对象
               4)创建Thread类对象,将上面的资源类对象作为参数传递
// 聊天室类
public class ChatRoomDemo {
    public static void main(String[] args) throws SocketException {

        //主线程中
        //分别此处创建发送端的Socket以及接收端的Socket
        //发送端的Socket
        DatagramSocket sendDs = new DatagramSocket() ;
        //接收端的Socket
        DatagramSocket receiveDs = new DatagramSocket(8888) ;

        //创建发送端的资源类对象
        SendThread st = new SendThread(sendDs) ;
        //创建接收端的资源类对象
        ReceiveThread rt = new ReceiveThread(receiveDs) ;

        //创建线程
        Thread t1 = new Thread(st) ;
        Thread t2 = new Thread(rt) ;
        t1.start() ;
        t2.start() ;
    }
}

//发送端线程
public class SendThread implements Runnable {
    private DatagramSocket ds ;
    public SendThread(DatagramSocket ds) {
        this.ds = ds ;
    }
    

    @Override
    public void run() {

        try {
            //创建一个BufferedReader流对象:键盘录入
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(System.in)) ;
            String line = null ;
            while((line=br.readLine())!=null){

                //自定义结束条件
                if("886".equals(line)) {
                    break;
                }
                DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,
                        InetAddress.getByName("10.12.159.190"),8888) ;
                //3)发送
                //public void send(DatagramPacket p)
                ds.send(dp) ;

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

//服务端线程
public class ReceiveThread implements Runnable {
    private  DatagramSocket ds ;
    public ReceiveThread(DatagramSocket ds) {
        this.ds = ds ;
    }

    @Override
    public void run() {
        try {
            while(true){
                //创建一个接收容器: 自定义一个缓冲区byte[]
                byte[] bytes = new byte[1024] ;
                int length = bytes.length ;
                //(创建数据包报)

                DatagramPacket dp = new DatagramPacket(bytes,length) ;

                //3)接收
    //        public void receive(DatagramPacket p)
                ds.receive(dp);
                //获取实际内容
                String str = new String(dp.getData(),0,dp.getLength()) ;
                //获取ip地址
    //        public InetAddress getAddress()
                String ip = dp.getAddress().getHostAddress() ;
                System.out.println("data from "+ip +",content is :"+str) ;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //释放资
//        ds.close() ;
    }
}

12.4 TCP/IP 协议

12.4.1 TCP/IP 协议客户端
    1)创建客户端的Socket对象
    2)获取客户端所在的通道内的字节输出流OutputStream
    3)给客户端通道内的流中写入数据
    4)释放客户端的资源对象
12.4.2 TCP/IP 协议服务端
    1)创建服务器端的Socket对象
    2)监听客户端的连接 ---一旦监听到了,就获取到那个客户端的Socket
    3)获取连接的客户端的通道内的输入流,读取数据
    4)展示数据
    5)释放资源
12.4.2.1 示例1: 复制本地图片到指定文路径
分析:
  	TCP客户端的图片,服务器端将文件复制,到指定文件中(当前项目下)
 	客户端将D盘下的高圆圆.jpg操作,服务器端将内容复制到当前项目下 mm.jpg
 	服务器端将内容复制到当前项目下 mm.jpg
 
发现问题:
    	复制过来的图片缺失,文件大小不一致,图片文件本身就存在缓存数据,需要通过字节输出流,强制刷新流中缓存的字节数
   		 public void flush()
//服务端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
		// 服务端socket
        ServerSocket ss = new ServerSocket(12306) ;
        //监听客户端
        Socket socket = ss.accept();
        //读取通道内过来的数据:封装通道内的输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;
        //输出到当前项目mm.jpg
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mm.jpg")) ;
        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
            //强制刷新
            bos.flush() ;
        }
        //释放资源
        bos.close() ;
        ss.close() ;
    }
}

//客户端
public class ClientDemo {
    public static void main(String[] args) throws IOException {

        //创建socket
        Socket socket = new Socket("10.12.159.190",12306) ;
        //BuferedInputStream 读取d盘下的图片文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\高圆圆.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() ;
        }
     //释放资源
        bis.close();
        socket.close() ;
    }
}
12.4.2.2 示例2: 复制文本文件到指定路径
TCP客户端的文本文件,服务器端将文件复制到指定文件(当前项目下),加入服务器端反馈...
	
发现问题:
       文件是已经完毕了,但是客户端并没有收到服务器端的反馈,服务器也没关闭; 出现了互相阻塞了
       针对 文件的复制,如果是文本文件,我们通过BufferedReader的readLine的返回值是否为null,判断文件是否复制完毕
       null.表示文件复制完了,但是对于Server服务器端来说,他不知道通道内的流中是否还有数据需要在写过来;等待客户端通知
       我们的服务器端,文件已经复制完毕;客户端在等着服务器端的反馈的消息,所以就出现了互相等待;
 
解决方案:
      1)自定义结束条件,只要服务器端读取到这个自定义内容,就打破了阻塞,可以给客户端反馈
      如果当前这个文件:一开头"over",直接服务器端读取到这个就结束了,可能没复制完毕!
 
      2)直接使用客户端的功能完成:
      public void shutdownOutput():禁用客户端的输出流;不会写在流中写入数据了,服务器端就得到了通知,没有数据写过了

示例:

//客户端
public class ClientDemo {
    public static void main(String[] args) throws IOException {

        //创建socket
        Socket socket = new Socket("10.12.159.190",12306) ;
        //读取当前项目下的Copy.java文件, 使用BufferedReader 封装源文件
        BufferedReader br = new BufferedReader(new FileReader("Copy.java")) ;
        //封装通道内的字节输出流
        //分步走
        /*OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;*/
        //一步走
        BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(
                        socket.getOutputStream())) ;

        //一次读取一行进行读写复制,将文件中的内容写到封装通道 的流对象中 bw
                String line = null ;
                while((line=br.readLine())!=null){ //readLine()阻塞式方法
                    bw.write(line) ;
                    bw.newLine() ;   //换行
                    bw.flush() ;	 //刷新
        }

                //方式1
                //自定义结束条件
              /*  bw.write("over") ;
                bw.newLine() ;
                bw.flush() ;*/

             socket.shutdownOutput();

        //读取服务器端的的反馈
        //获取客户端所在的通道内的字节输入流
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024] ;
        int len = inputStream.read(bytes);
        String fkMsg = new String(bytes,0,len) ;
        System.out.println("接收的服务器端反馈的数据是:"+fkMsg);

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

//服务端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
		//服务端socket对象
        ServerSocket ss = new ServerSocket(12306) ;
        //监听客户端
        Socket socket = ss.accept();  //监听:阻塞式方法
        //获取当前客户端的Socket通道内的输入流InputStream
        //分步走
        /*InputStream inputStream = socket.getInputStream();
        //封装BufferedReader
        BufferedReader br = new BufferedReader(
                new InputStreamReader(inputStream)) */;
        //一步走
        //封装通道内的字节输入流
        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                        socket.getInputStream())) ;

        //将读取的内容,输出在当前项目下的My.java文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("Hello.java")) ;

        //一次读取一行
        String line = null ;
        while((line=br.readLine())!=null){ //阻塞式方法

          /*  if("over".equals(line)){
                break ;
            }*/
            bw.write(line) ;
            bw.newLine() ;
            bw.flush();
        }


        //服务器端需要进行反馈
        //获取通道内的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("文件已经复制完毕;".getBytes());

        //释放资源
        bw.close() ;
        ss.close() ;
    }
}
12.4.3 模拟网络聊天室

12.5 Object流

12.5.1 序列化
什么是序列化---->ObjectOutputStream:
        就是在将Java对象(实体类:User/Product/Order...),变成一种流数据,他们里面的数据存储流中,就可以在网络(服务器集群中:共享)中传输!
构造方法:
    public ObjectOutputStream(OutputStream out)
成员方法:
	public final void writeObject(Object obj) throws IOException:写入一个实体对象
12.5.2 反序列化
什么是反序列化---->ObjectInputStream
       就是将流数据(存储的一些相关的实体类数据)-----还原成Java对象!
构造方法:      
       public ObjectInputStream(InputStream in) throws IOException:将文件中的数据,进行读取
成员方法:       
       public final Object readObject()throws IOException,ClassNotFoundException
12.5.3 示例:
public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

//      write() ;
        read() ;
    }

    //反序列
    private static void read() throws IOException, ClassNotFoundException {
        //创建反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;

        // public final Object readObject()
        Object object = ois.readObject();

        System.out.println(object) ;
        //关闭资源
        ois.close() ;
    }

    //序列化
    private static void write() throws IOException {

        //将Person对象中里面成员信息永久存储到指定文件中:序列化,前提条件:Person类型必须实现标记接口序列化!
        //文件中乱码,不影响读取,通过反序列化---可以还原回来

        //创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("oos.txt")) ;

        //创建Person类对象
        Person p = new Person() ;
        p.name = "高圆圆" ;
        p.age = 42 ;

        //public final void writeObject(Object obj)
        oos.writeObject(p) ;

        //释放资源
        oos.close() ;
    }
}
12.5.4 异常: 未实现序列化接口的异常
java.io.NotSerializableException: com.qf.objectstream_01.Person:未实现序列化接口的异常
 * 只有支持java.io.Serializable接口的对象才能写入流中.需要在Person类实现接口(标记接口)
 * 刚才已经实现了反序列:将流中数据---读取出来,解析成对象了
 * 但是,当我更改了Person类的成员的时候,再次反序列化报错
 *
 *  java.io.InvalidClassException: com.qf.objectstream_01.Person;
 *  local class incompatible: stream classdesc serialVersionUID = 5810188958553179434,
 *  local class serialVersionUID = -45582393571180500
 *
 *  导致类的数据在文本文件中的前后的版本ID不一样  (序列化的时候---需要给类进行签名!)
 */
public class Person implements Serializable {

    //固定的序列化版本Id
    private static final long serialVersionUID = 6815664471916807752L;

    public transient String name ;//transient可以标记某个成员变量不参与序列化
    int age ;

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

12.6 Properties:属性集合类(属性列表)

12.6.1 Properties
是一个Map,但是没有泛型,键和值都是String.
特有功能:
       public Object setProperty(String key,String value):添加键和值
       public Set<String> stringPropertyNames():获取所有 键的集合
       public String getProperty(String key):获取键对应的值
12.6.1.1 功能使用, 示例:
public class PropertiesDemo {
    public static void main(String[] args) {

        //public PropertiesTest()
        //创建属性集合类对象
        Properties properties = new Properties() ;

        /*//put方法
        properties.put("张三","30") ;
        properties.put("高圆圆","42") ;
        properties.put("张佳宁","31") ;
        properties.put("张三丰","50") ;

        //遍历:keySet()---->获取所有的键
        Set<Object> set = properties.keySet();
        for(Object obj :set){
            //通过键获取值
            Object value = properties.get(obj);
            System.out.println(obj+"---"+value);
        }*/
        /**
         *  public Object setProperty(String key,String value):添加键和值
         *  public Set<String> stringPropertyNames():获取所有 键的集合
         *   public String getProperty(String key):获取键对应的值
         */
        //添加元素
        properties.setProperty("1","张三") ;
        properties.setProperty("2","李四") ;
        properties.setProperty("3","王五") ;
        properties.setProperty("4","赵六") ;

        Set<String> keySet = properties.stringPropertyNames();//获取所有 键的集合
        for(String key:keySet){
            String value = properties.getProperty(key);	//获取键对应的值
            System.out.println(key+"---"+value);
        }
    }
}
12.6.1.2 成员方法
public void store(Writer writer, String comments):将属性列表中的内容保存指定的文件中
								 参数2:给属性列表一个描述
public void load(Reader reader):将文件中的内容加载属性列表中

示例:

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

        //有一个属性列表
      Properties prop = new Properties() ;
        prop.setProperty("孙杰","30") ;
        prop.setProperty("臧永青","25") ;
        prop.setProperty("陈荣昌","28") ;
        prop.setProperty("陈琛","28") ;
        prop.setProperty("遆子林","25") ;
        prop.setProperty("于水利","26") ;

        //将属性列表中的内容保存指定文件中
        Writer w = new FileWriter("name.txt") ;
        prop.store(w,"name's list") ; 			 //store方法
        //释放资源
        w.close() ;
    }

    //将文件中加载进来,将属性列表中内容可以遍历
    private static void myLoad() throws IOException {
        Reader r = new FileReader("Lucky.txt") ;
        //创建空的列表
        Properties prop = new Properties() ;
        //System.out.println(prop);
        prop.load(r);							//load方法
        //System.out.println(prop);

        //获取所有的值
       /* Collection<Object> values = prop.values();
        for(Object obj :values){
            System.out.println(obj);
        }*/
        Set<String> keySet = prop.stringPropertyNames(); //获取键集合
        for(String key :keySet){
            String value = prop.getProperty(key);		//根据键获取对应的值
            System.out.println(key+"="+value) ;
        }
    }
}

12.6.2 如何读取src 文件夹路径下的properties文件

public class Test{
public static void main(String args[]){
	//调用方法,读取properties文件
	method();
}
//创建读取properties方法
private static void method() throws IOException {
        //文件在哪个了中要读:
        //1)获取当前了类的字节码文件对象
        /*Class c = Test.class ;
        //2)获取类加载器
        ClassLoader classLoader = c.getClassLoader();
        //3)通过类加载获取当前src目录下面的配置文件所在的输入流对象
        InputStream inputStream = classLoader.getResourceAsStream("name.properties");*/
    	
    	//链式编程,将文件读取到输入流中
        InputStream inputStream =
                Test.class.getClassLoader().getResourceAsStream("name.properties");

        //创建Properties
        Properties prop = new Properties() ;
    	// 将输入流中的文件加载进properties中
        prop.load(inputStream);

        System.out.println(prop) ;
    }
  }

12.7 面试题: UDP 和 TCP/IP 的区别

1)是否是一种可靠连接
		UDP,不可靠  TCP可靠连接
2)是否安全,执行效率是否高
		UDP不安全,不同步,所以执行效率高; TCP安全,同步,所以执行效率低
3)传输文件是否大小限制
        UDP是有大小限制,发送端数据报包(DatagramPacket),一般不超过64kb;
        而TCP发送文件数据没有限制,
        底层通道的流(最基本的字节流)的方式;
	TCP:采用三次握手(SYNC(信号)/ACK:确认字符) 

12.8 针对文本文件的读写复制操作有几种,列出流类型即可

FileInputStream/FileOutputStream:一次读取一个字节
FileInputStream/FileOutputStream:一次读取一个字节数组
BufferedInputStream/BufferedOutputStream:一次读取一个字节
BufferedInputStream/BufferedOutputStream:一次读取一个字节数组
InputStreamReader/OutputStreamWriter:解决乱码(编码和解码必须字符集统一:采用默认平台字符集)
									=
InputStreamReader/OutputStreamWriter:	一次读取一个字符数组
FileReader/FileWriter:一次读取一个字符
FileReader/FileWriter:一次读取一个字符数组
BufferedRreader/BufferedWriter:一次读取一个字符
BufferedRreader/BufferedWriter:一次读取一个字符数组不错v不那么,65特容易786546789087654321··123456980
0哦IP【】9redRreader/BufferedWriter:一次读取一行/每次写一行然后换行,,。、

12.9 IO流的分类有哪些(重点):

BIO
	BufferedInputStream/BufferedOutputStream 
	
	BufferedReader(Reader r)
			readLine():读取一行
				1)BufferedReader(new FileReader("xxx.java/.txt..")) ;//操作源文件
				2)BufferedReader(new InputStreamReader(System.in)) ; 键盘录入
	BufferedWriter      
	
	FileReader
	FileWriter
	
	ObjectInputStream/ObjectOutputStream
	DataInputStream/DataOutputStream :内存操作流 :底层一种将局部变量临时存储;
	

13. 数据库

13.1 数据库分类
存储数据的仓库,就是个文件夹
之前的存储:
        数组,StringBuffer,集合 ,IO流永久存储---->耗时
        一种关系型数据:mysql,oracel,SqlServer,Sqllite...
mysql:
	轻量级 :中小型企业使用居多 (免费的) 
oracle:
	大型企业使用的居多 (收费的)   买人家的数据库软件+买人家第三方服务+买服务器...银行类的企业Oralce
13.2 DDL(数据库定义)语句:
show databases; 			  -- 查询当前所有的数据库名称;
create database 库名;			-- 创建自己的数据库
create database if not exists 库名;-- 判断再去创建
drop database 库名			 -- 删除库
drop database if exists 库名;  -- 判断再去删除
show create database 库名;	-- 查询创建数据库的默认的字符集格式
show variables like '%character%';   									 -- 默认查询当前数据库中所有的带字符集的字符集格式
alter database myjavaee character set gbk ;
				 -- 修改数据库的默认的字符集
13.2.1 mysql字段类型:
 varchar(字符个数)  	---代表String 字符串
 int类型:整数类型
 int:默认的字符个数int(11):当前真实age年龄的长度 (所占的实际的字符数) 整数默认int(11)
                举例:	age int,
                			7
                
                int(指定字符个数):int(4)
                age int(4)
                	  0007
                	               	  
               date :日期 仅仅是日期
               datetime :日期+时间
               timestamp:时间戳  (即时时间)
               			当你在执行给这个字段插入某个指定的时候,当前的那个日期+时间
               			
               double(3,2) :这个字段值是3位数,小数后保留2位
               		举例:3.56
               double 
               			999.0
13.2.2 DDL之表的增删查改
use ee_2110;			-- 创建表的前提,先选择库,进入到库中
create table 表名(
			字段名称1 字段类型1,
			字段名称2 字段类型2,
			字段名称3 字段类型3
			... 
		) ;				-- 创建表的语法
		
desc 表名;			   -- 查询表的结构
alter table 表名  change 旧字段名称 新字段名称 数据类型;
						-- 修改表的字段名称
  (例: alter table student change gender sex varchar(5) ;)
alter table 表名 modify 字段名称 新的字段类型;
 						-- 修改字段类型
  (例:alter table student modify address varchar(100);)	 alter table 表名 add 新的字段名称 类型;	
 						-- 添加一个新的列(新的字段)
  (例:alter table student add socre double(3,1);)		alter table 表名 drop 字段名称;
  (例: alter table student drop email;)
create table 新的表名 like 以前的表名;
						-- 复制一张新的表跟以前的表结构相同
  (例: create table teacher like student ;)				alter table 以前的表名 rename to 新的表名;
  						-- 修改的表的名称
  (例: alter table teacher rename to tea;)				 drop table 表名 ;
drop table if exists 表名;		-- 删除表

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值