【Java_week05总结回顾】

5 篇文章 0 订阅
4 篇文章 0 订阅

目录

1.TreeSet(比较器排序)

2.Map集合的功能以及遍历方式,v>

2.1 引入Map集合,v>

2.2 Map集合和Collection集合的区别?(面试题)

2.3 Map集合的基本功能

2.4 Mpa集合的两种遍历方式

2.5 HashMap

2.6 Collections:针对单例集合的工具类

3 线程

3.1 进程和线程的概念

3.2 jvm是多线程的吗?(面试题)

3.3 Java能创建线程吗?(面试题)

3.4 创建线程的两种方式

3.5throw和throws的区别(面试题)

3.6 Java中异常的处理

3.7 异常的使用的注意事项

3.8 Thread线程提供的一些方法

3.9 静态代理(重点) 

3.10 使用两种方式分别去模拟电影卖票案例---多线程的安全问题(重点)

 3.11 多线程安全问题解决--->效率低--->死锁 (生产者和消费者必须使用同一个资源)(重点)

3.12 什么是同步方法?(非静态) 以及静态同步方法他们说锁对象是谁?

3.13 wait()/notify()并非Thread类中为什么Object的?

3.14 什么死锁?如何解决死锁----"生成者消费者模式思想"

3.15 jdk5提供了根据的锁操作---Lock(接口)和synchronized同义

3.16 创建线程的另一种方式---->"线程池"

4.单例模式

4.1 I/O流

4.2 字节输出流

4.3 字节输入流

4.5 字节缓冲流

4.6 字符输入输出流

4.7 字符缓冲流

4.7 URL-->统一资源定位符号

4.8 Properties

5. 网络编程


1.TreeSet(比较器排序)

按照TreeSet的构造方法,分为两种排序方式,1.自然排序,2.比较器排序

public TreeSet(Comparator<E> comparator)创建TreeSet集合使用比较器进行排序,需要存储的类型实现Comparator接口中的int compare(T o1,T o2),T--Type类型,需要Compartor中的泛型一致!

//创建TreeSet集合对象
        //public TreeSet(Comparator<E> comparator)
        //方案1:自定义一个类 实现Comparator接口
        //接口多态/创建具体的子实现类对象
       // MyComparator my = new MyComparator() ;
        //TreeSet<Student> ts = new TreeSet<>(my) ;
//创建一个类去重写 Comparator接口里面的compare方法
public class MyComparator  implements Comparator<Student> {
    //重写接口中的compare方法
    @Override
    public int compare(Student s1, Student s2) {
        //主要条件:
        // 按照学生的年龄从小到大排序
        //s1--->相当于自然排序里面 this
        //s2--->相当于自然排序里面s
        int num = s1.getAge() - s2.getAge() ;
        //次要条件:年龄相同的人,按照姓名进行比较:(字典顺序比较)
        int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
        return num2;
    }
}

2.Map<K,V>集合的功能以及遍历方式

2.1 引入Map<K,V>集合

需求:根据学生的编号Id,获取指定的学生的姓名
 * 之前Collection<E>/List<E>,E--存储对应的类型,学生类中将id和姓名设置为属性!想要通过id获取指定的学生姓名,通过单列集合
 * 没有办法直接操作,Java提供了一个键值对Map集合
 引入Map<K,V>集合
 * Map<K,V> :一个键对应一个值(键值映射),Map集合针对键必须唯一,值是可以重复的!
            K-->Integer     V ---String
                    1             张三
                    2             王五
                    3             高圆圆
                    4             文章
                    1              赵又廷     --键重复,后面的值将前面的值覆盖

2.2 Map集合和Collection集合的区别?(面试题)

 

 1)存储类型
            Collection<E>,只能存储一种引用类型---属于"单列集合"---->理解为"光棍"
            Map<K,V>,可以存储多种类型的引用类型--->属于"双列集合"----理解为"夫妻对"
        2)关系区别
            Collection和Map没有直接关系,间接关系
                Collection具体的子接口Set--->HashSet/Treeset 直接的去依赖于Map接口的HashMap和TreeMap

2.3 Map集合的基本功能

 V put(K key,V value) :添加方法---返回值什么意思?
                        通过返回值是否为null,判断键是否重复,如果不重复,返回值永远是null;
                        如果键重复,将第一次存储键对应的值返回,将后面键对应的值进行存储
        void clear():清空Map集合的所有键值对
        V remove(Object key):删除Map集合的指定键,然后返回被删除的值
         boolean containsKey(Object key):是否包含指定的键
         boolean containsValue(Object value):是否包含指定的值
            boolean isEmpty():判断Map集合是否为空

2.4 Mpa集合的两种遍历方式

第一种:
 *  1)Set<K> keySet()获取Map集合的所有的键的集合
 *          V get(Object key):通过键获取值
 */
 Set<String> keySet  = map.keySet();
        for(String key:keySet){
            String value = map.get(key);
            System.out.println(key+"="+value);
        }
//遍历大集合
        //获取所有的键
        Set<String> keySet = hm.keySet();
        //遍历所有键
        for(String key:keySet){
            System.out.println(key+"\t") ;
            //通过键获取值---ArrayList<String>
            ArrayList<String> keyValue = hm.get(key);
            //遍历ArrayList
            for(String value:keyValue){
                System.out.println("\t"+value);
            }
        }
/**
 * 第二种方式遍历
 * Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象
 *      K getKey()获取键
 *      V getValue()
 */
 //获取所有的键值对对象
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        for(Map.Entry<String,String> en:entrySet){
            //获取键,获取值
            String key = en.getKey();
            String value = en.getValue();
            System.out.println(key+"="+value) ;
        }

2.5 HashMap

/**
 * HashMap<Student,String>
 *      Key:Student类型  ---学生信息
 *      Value:String类型 ---朝代
 *  使用HashMap集合保证key唯一!
 *
 *  HashMap--->底层哈希表实现,put方法依赖hashCode和equals方法,
 *  Map针对键有效,要针对键的类型自定类型,当前类必须重写equals和hashCode(),保证键唯一(重点)
 */

2.6 Collections:针对单例集合的工具类

/**
 * java.util.Collections:针对集合操作工具类
 *      提供一些常用的方法:
 *    public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key):
 *      二分搜索在List中查询元素 (不管集合还是Arrays---->binaraySearch方法,(集合或者数组必须有序))
 *    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) :获取Colection中的最大值
 *    public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) :获取Colection中的最小值
 *    public static <T extends Comparable<? super T>> void sort(List<T> list):针对List集合进行自然排序
 *    public static <T> void sort(List<T> list,Comparator<? super T> c):针对List集合比较器排序
 *    public static void shuffle(List<?> list):针对List集合随机置换
 *
 *    public static <T> List<T> synchronizedList(List<T> list):将线程不安全ArrayList,变成线程安全的列表
 */

3 线程

3.1 进程和线程的概念

线程是依赖于进程的,没有进程就没有线程!
进程
    在计算机的任务管理查看到此电脑上所有的客户端的进行!
    概念:
        调用"系统资源"的独立单位!
        开启一个进程----创建 "系统资源"
所有的计算机,包括我们"jvm",都支持多进程,多进程的意义?
    一句话:提高CPU的使用率!
    在打游戏的同时,听音乐,---同时开启了两个进程,是同时进行的吗?
    并不是同时的,CPU(时间片),一点点时间片可以高效在两个进制来回切换!

 线程:
    是进程中的最小执行单元!(理解为,进程中的某个任务)
    举例:
            360进程开启之后,可以杀毒/清理/电脑瘦身等等
    多线程:在一个程序的过程中,其执行路径有多条,线程的执行具有"随机性"!
    多线程的意义?
            多个线程"共享同一个资源",互相抢占CPU执行权,从而达到线程的执行速度!

            举例
                1 v 3 打篮球,3个人抢占的篮球的几率大,可能这1个人也能抢占大篮球!

3.2 jvm是多线程的吗?(面试题)

 是多线程;至少有两条件线程
 *          1)main---"用户线程"
 *          2)垃圾回收线程,回收没有更多的引用对象,从而释放空间;如果没有
 *      垃圾回收线程,Java中不断的去new,不断的堆内存空间---->导致OOM(Out Of Memory:内存溢出)

3.3 Java能创建线程吗?(面试题)

 创建线程--->创建进程---->创建系统资源---->Java不能够直接创建资源的,间接的提供了一个Thread

3.4 创建线程的两种方式

 *  创建线程的方式1:
 *          1)自定义一个类 继承Thread类
 *          2)重写Thread类的run方法
 *          3)在main()用户线程中,创建当前线程了对象
 *          4)启动线程--->start()不是run方法,run方法是一个普通方法,
 *
 *      Thread类提供方法:
 *              public final String getName()获取线程的名称
 *              public final void setName(String name):设置线程名称
 */
/**
 * 线程的创建方式2:
 *      1)自定义一个类,实现Runnable接口
 *      2)重写Runnable接口的run方法
 *      3)在用户线程main中去创建当前类对象--->"资源类"
 *      4)创建Thread类对象,将3)资源类作为Thread类对象的参数进行传递
 */
 //Thread类静态方法
            //public static Thread currentThread():获取正在执行的线程
            System.out.println(Thread.currentThread().getName()+":"+x) ;

3.5throw和throws的区别(面试题)

异常处理
 *  throws:抛出
 *  throw:抛出
 *
 *  区别:
 *          1)书写位置的区别
 *              throws:用在方法声明上
 *              throw:用在方法体中
 *          2)后面跟的类型不一样的
 *              throws后面跟的异常类名,可以多个,中间逗号隔开
 *              throw:后面只能一个异常对象,new XXException()
 *          3)是否出现异常的时机
 *                  throws:可能出现问题,可能性
 *                  throw:执行某个逻辑代码,一定会出现这个异常!(肯定性)
 *          4)处理异常:
 *                  throws:抛出给调用者,谁调用这个方法(带有throws的方法),谁必须对这个已处理(throws/try...catch...)
 *                  throw:异常的处理:交给方法体中逻辑判断,if(xxx){throw  new XXException(...);}

3.6 Java中异常的处理

/**
 *      程序中会出现异常,开发人员必须解决异常!
 * 异常的父类:Throwable
 *          -->提供一些方法:将异常的信息直接打印在控制上:具体(跟踪堆栈-->底层原码)
 *          public void printStackTrace()
 *
 *      Error(严重问题)     Exception
 *
 *  严重问题:内存溢出(借助于其他硬件解决这个问题,加内存条)
 *  Exception:
 *              编译时期异常:只要不是RuntimeException的子类,都属于编译时期
 *                          必须让开发人员必须处理,不处理,代码编译通过不了!
 *              运行时期异常:RuntimeException
 *                          大部分逻辑不够严谨,导致出现的问题
 *                          NullPointerEception:在获取到某个类的对象的时候,并没有非空判断,
 *                                  可能对象是null,使用这个对象调用它里面的方法,出现空指针了
 *   在Java中处理异常方案:
 *          throws:抛出
 *          //标准格式
 *          try{
 *              可能出现问题的代码
 *          }catch(异常类名 对象名){
 *              处理异常
 *          }finally{
 *              //释放资源
 *          }
 *
 *          变形格式
 *          try{
 *              可能出现问题的代码
 *          }catch(异常类名 对象名){
 *              处理异常
 *          }
 *
 *
 *          try{
 *                可能出现问题的代码
 *            }catch(异常类名 对象名){
 *                处理异常
 *            }catch(异常类名2 对象名2){
 *              处理异常...
 *  }
 */
//处理方案:
        //try...catch...
        //try代码如果出现问题,---执行catch语句---->处理异常---->内存中,jvm会创建异常的对象,new XXException,
        //执行的catch语句代码
简单来说就是两种处理方案:
1.是抛出  throws:抛出,抛给调用者,谁调用谁处理
2.是捕获 try,,,catch,,,

3.7 异常的使用的注意事项

/**
 * 异常的使用的注意事项:
 *      1)子类继承父类的时候,如果子类重写父类的方法,父类的方法如果本身没有throws抛出异常
 *      子类的方法中出现异常,子类只能自己处理,只能try...catch...
 *
 *      2)子类继承父类,子类重写父类方法的时候,如果父类的方法抛出异常,
 *          那么子类的该方法最起码要根父类的方法异常保持一致(要么子类该放的异常是父类方法的异常中的子类)
 */

3.8 Thread线程提供的一些方法

底层源码中看到方法被native修饰,表示本地方法,表示该方法的实现由非java语言实现,比如C

join()-----等待线程终止
/**
 * 等待该线程中终止
 *public final void join() throws InterruptedException
 * 因为线程的执行具有随机性,哪个线程启动之后,调用join(),正常理想情况,等待该线程执行完毕,执行其他线程!
 * 需求
 *      实现三个线程依次先后执行完毕, t1,t2,t3每一个线程启动之后,都可以调用join()
 */

/**
 * public static void yield():线程礼让(暂停当前正在执行的线程,执行其他线程)
 */
/**
 * public final void setPriority(int newPriority):设置优先级
 * public final int getPriority():获取优先级
 *
 *  Thread提供三个自定义常量:
 *      public static final int MAX_PRIORITY    10    最大优先级
 *      public static final int MIN_PRIORITY    1    最小优先级
 *      public static final int NORM_PRIORITY   5    默认优先级
 *  创建线程,没有设置优先级,线程的默认优先级就是5(抢占CPU执行权的概率一样大)
 *
 *
 */
 yield() ;//每一个线程在执行这个方法的时候,都要暂停当前自己的线程,执行其他线程

线程的生命周期

3.9 静态代理(重点) 

/**
 * 设计模式一种思想(前辈总结出来的),并非一种技术
 *       代理设计模式属于结构型设计模式一种;
 *              代理设计模式: 核心思想:让代理角色帮助真实角色完成一件事情!
 *                      静态代理
 *                      动态代理:
 *                              jdk动态代理:前提必须有接口
 *                              cglib动态代理:第三方jar包 ---基于子类实现
 *
 *  静态代理:
 *          最大特点:代理角色和真实角色必须实现同一个接口
 *          代理角色:帮助真实角色完成一些事情
 *          真实角色:专注于自己的事情
 *

举例:

 *  举例:
 *          结婚这件事情
 */



//测试类
public class StaticProxyDemo {
    public static void main(String[] args) {
            //测试真实角色
            //接口多态/创建具体的子类
            You you = new You() ;
            you.mary();
            System.out.println("---------------------------------") ;
            //创建WeddingCompany类对象
            You you2 = new You() ;//---->类似  资源类对象 MyRunnable implement Runnble
            WeddingCompany weddingCompany = new WeddingCompany(you2) ; //类似 ---Thread t1 = new Thread(MyRunnable对象,线程名称);
            weddingCompany.mary() ;                                     //类似----t1.start() ; jvm调用run
    }


}
//结婚的接口
interface  Mary{
    void mary() ;
}

//真实角色
class You implements  Mary{

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

//婚庆公司:代理角色
class WeddingCompany implements  Mary{
    //声明Mary接口变量
    private  Mary mary ; //---类似Thread类的private Runnable  target;
    public WeddingCompany(Mary mary){//类似 Thread(Runnable target,String name)
        this.mary = mary ;
    }
    @Override
    public void mary() {
        if(mary!=null){
            System.out.println("婚庆公司,要布置婚礼现场!");
            //真实角色完成它自己的事情
            mary.mary();
            System.out.println("婚庆公司接收尾款!");
        }


    }
}

3.10 使用两种方式分别去模拟电影卖票案例---多线程的安全问题(重点)

/方式一
package com.qf.thread_06;

/**
 * 自定义一个类继承Thread类
 */
public class SellTicket  extends Thread{

    //同时出售100张门票
    //定义一个成员变量 -被共享
    private  static int tickets = 100 ;

    @Override
    public void run() {
        //为了模拟一直有票
        while(true){
            if(tickets>0){

                //模拟网络延迟,线程睡眠100毫秒
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
            }
            //++或者-- 原子性操作(对变量本身最简单最直接的操作)
            //记录之前的值,tickets,然后在进行自增或者自减
        }
    }
}
package com.qf.thread_06;

/**
 * 需求:
 *     使用线程创建的方式1,来实现,电影院有三个窗口,同时出售100张门票
 *
 *
 *模拟真实的网络延迟,让线程的睡眠
 */
public class SellTicketTest {
    public static void main(String[] args) {

        //创建三个线程,分别代表三个窗口
        SellTicket st1 = new SellTicket() ;
        SellTicket st2 = new SellTicket() ;
        SellTicket st3 = new SellTicket() ;

        st1.setName("窗口1") ;
        st2.setName("窗口2") ;
        st3.setName("窗口3") ;

        //分别启动线程
        st1.start() ;
        st2.start() ;
        st3.start() ;
    }
}
//方式二:

package com.qf.thread_07;

/**
 * 自定义一个类实现Runnable接口
 */
public class SellTicket  implements Runnable{

    //同时出售100张门票
    //定义一个成员变量 -被共享
    private  static int tickets = 100 ;

    @Override
    public void run() {
        //为了模拟一直有票
        while(true){
            if(tickets>0){

                //模拟网络延迟,线程睡眠100毫秒
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
            }

        }
    }
}

package com.qf.thread_07;

/**
 * 需求:
 *     使用线程创建的方式2,来实现,电影院有三个窗口,同时出售100张门票
 *
 *
 *模拟真实的网络延迟,让线程的睡眠
 */
public class SellTicketTest {
    public static void main(String[] args) {


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

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

 3.11 多线程安全问题解决--->效率低--->死锁 (生产者和消费者必须使用同一个资源)(重点)

/**
             * synchronized(锁对象){ //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
             *                多条语句对共享数据的操作
             *
             *  }
             */
            //t1,t2,t3
//            synchronized (new Object()){//必须同一把锁
            //t1线程抢占到了,t2,t3进不来的,t1将这个同步代码块内容执行,出售第100张
            //--->t1,t2,t3在继续互相抢占---t3进来了,t2和t1进不来的

 /**
             * 负票的出现:(睡眠动作以及线程执行随机性)
             *      tickets-- :记录以前的值,在进行--动作(原子性操作)
             *  窗口1正在出售1张票,tickets=1 ,记录以前的1的值,然后在进行--动作
             *  在准备--,第二个线程或者第三个线程互相抢,窗口2先抢占到了,先睡眠了醒来了,窗口2正在出售第0张票
             *  在准备--的时候,t1线程抢占到了,睡醒来,窗口1正在出售第-1张票
             *
             */
/**
 * 需求:
 *     使用线程创建的方式2,来实现,电影院有三个窗口,同时出售100张门票
 *
 *
 *模拟真实的网络延迟,让线程的睡眠
 * 虽然使用方式2(静态代理)进行实现,也依然出现了问题
 *      1)同一张票被多次  --->线程的执行随机性
 *      2)负票的出现     --->加入睡眠以及原子性操作(--或者--)
 *
 *  上面2种情况,都是多线程的安全问题
 *
 *  校验多线程安全问题的标准 (解决方案)
 *      1)检查程序是否是多线程环境  :是多线程卖票
 *      2)是否存在共享数据 :     也存在的而且存在100张票
 *      3)是否存在多条语句对共享数据的操作:也存在,判断tickets以及完成--
 *
 *      Java提供了同步代码块,解决上面3),使用同步代码块将多条语句对象共享数据的包起来
 *          Java的同步机制
 *          synchronized(锁对象){ //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
 *              多条语句对共享数据的操作
 *          }
 *

3.12 什么是同步方法?(非静态) 以及静态同步方法他们说锁对象是谁?

/**
 * 什么是同步方法?
 *  如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上
 *  默认的锁对象this(非静态)
 *
 *  如果是静态的同步方法,锁对象是 "当前类名.class"--跟"反射"有关系
 *              反射:Java代码经历三个阶段
 *                      "SOURCE"  ,"CLASS"(反射中的核心)  "RUNTIME"
 *
 *
 */
package com.qf.thread_02;

/**
 * 资源类
 */
public class SellTicket  implements Runnable{
    private static int tickets = 100 ;
    //创建一把锁
    private Object obj = new Object() ;
    //定义一个统计变量
    int  x = 0 ;
    @Override
    public void run() {
        //模拟一直有票
        while(true){

                if(x% 2==0){
//                    synchronized (obj) {
                    //synchronized (this) {//当前类对象的地址值引用
                    synchronized (SellTicket.class) {//当前类名.class:获取到当前类的字节码文件对象(静态同步方法的锁)
                        if (tickets > 0) {
                            //模拟网络延迟
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
                        }
                    }
                }else {
                    //x变量不能被2整除
                    sell() ; //同步方法(非静态)
                }
                x++ ;
        }

    }
    //同步方法:如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上
    //同步方法的锁对象:是this---当前类对象的地址值引用!
//    public synchronized void sell(){
    public  static synchronized void sell(){//静态是随着类的加载而加载 ,静态的同步方法的锁对象是"类名.class"
        /*synchronized (obj){
            if (tickets > 0) {
                //模拟网络延迟
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
            }
        }*/
        if (tickets > 0) {
            //模拟网络延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
        }
    }
}

3.13 wait()/notify()并非Thread类中为什么Object的?

	wait()线程等待--->底层域wait(long time),Java的本地方法(底层语言操作),使用monitor(监视器锁)来调用wait方法(本质就是synchronized([ˈsɪŋkrənaɪzd])同步锁)
	notify()/notifyAll():唤醒单个线程以及唤醒所有线程,都是锁对象的方法,
	而锁对象可以是任意Java类对象,不会定义在Thread类中,而是在Object类中定义的

3.14 什么死锁?如何解决死锁----"生成者消费者模式思想"

/**
 * 死锁:
 *      两个或者两个以上的线程,在执行的过程中,互相持有对方的锁,需要等待对方释放锁,导致出现了互相等待情况!
 *
 *              举例:
 *                      中国人和美国人吃饭
 *                      一双筷子 / 一把刀/一把叉
 *
 *                      中国人:
 *                              一根筷子/一把刀
 *                       美国人:
 *                              一跟筷子/一把叉
 *      解决死锁:多个线程必须使用统一资源对象!  (引入生产者和消费者模式思想)
 *
 */






/**
 * 资源类
 */
public class DieLock implements Runnable {
    private  boolean flag ;
    public DieLock(boolean flag){
        this.flag = flag ;
    }

    @Override
    public void run() {
        if(flag){
            synchronized (MyLock.objA){
                    System.out.println("if ObjA...");
                synchronized (MyLock.objB){
                    System.out.println("if ObjB...");
                }
            }
        }else {
            synchronized (MyLock.objB){
                System.out.println("else ObjB...");
                synchronized (MyLock.objA){
                    System.out.println("else objA...");
                }
            }
        }

    }
}

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

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

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

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

/**
 * 第一种情况:
 * t1线程先进来了,t1线程d1资源类对象--->执行true的结果,先持有了ObjA这个锁(if ObjA),需要执行"if ObjB",
 * 等待t2线程去将objB这锁释放;t2线程再执行执行"else ObjB",--->需要等待t1线程释放ObjA这个锁"else ObjA"
 * if ObjA...
 * else ObjB...
 *
 * 第二种情况:
 *  else ObjB...  t2线程先抢占到---等待t1线程释放锁
 * if ObjA..    t1线程等待t2线程是否ObjB这个锁
 *
 *
 * 第三种情况:理想状态:
 *  else ObjB...            t2线程抢占到了, else ObjB---在执行下面else ObjeA,t1线程已经释放了ObjA这个锁
 * else objA...
 * if ObjA...
 * if ObjB...
 */

3.15 jdk5提供了根据的锁操作---Lock(接口)和synchronized同义

/**
 * jdk5以后提供了具体的"锁定操作"
 * java.util.concurrent.locks.Lock接口---->可重入的互斥锁ReentrantLock([ri:'entrənt] )
 *  提供一些方法,可以获取锁,可以去释放锁
 *          void lock() 获取锁
 *          void unlock()释放锁
 *
 *   电影院三个窗口卖票---使用Lock解决多线程安全问题----和synchronized是同样的语义
 */
 //之前:锁对象可以是任意Java类对象,现在jdk提供Lock具体的锁----ReetrantLock子实现类
    private Lock lock = new ReentrantLock() ;
 try{
                //获取锁
                // void lock() 获取锁
                // synchronized (自定义一个锁){}
                lock.lock();
                if(tickets>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                }
            }finally {
                //finally除非一种特例:在执行finally语句之前,jvm退出 System.exit(0) ; ,任何情况下,finally语句一定会执行!
                //使用完,释放锁
                lock.unlock();
            }


3.16 创建线程的另一种方式---->"线程池"

1.线程池:属于”池化技术“
	经常用到:”创建固定的可重用的线程数的一个线程池“
	可以重复利用的   Executors([ɪgˈzɛkjətərz])
/**
 *  线程池:
 *          可以通过Executors工厂类(当前类构造方法私有化,对外提供静态方法--->简单工厂模式)提供一些创建线程池的功能
 * 可以创建固定的可重用的线程数的线程池
 *          public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
 *          上面的返回值ExecutorService接口
 *                      提供一些方法
 *                              Future<?> submit(Runnable task)提交异步任务
 *                              <T> Future<T> submit(Callable<T> task):提交异步任务
 *                              void shutdown():关闭线程池
 *
 *
 *

//创建线程池---创建固定的可重用的线程数的线程池
//        public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
        ExecutorService threadPool = Executors.newFixedThreadPool(2);//创建两个线程池

 //提交异步任务:展示两个线程并发执行的,不做计算,submit不需要返回值结果
        // submit(Runnable task)提交异步任务
       // MyRunnable my = new MyRunnable() ;
       // threadPool.submit(my) ;
       // threadPool.submit(my) ;

        // <T> Future<T> submit(Callable<T> task):提交异步任务 ,不做异步任务的计算,不需要返回结果
        MyCallable myCallable = new MyCallable() ;
        threadPool.submit(myCallable) ;
        threadPool.submit(myCallable) ;

        // void shutdown():关闭线程池
        threadPool.shutdown();
-------------------------------------------------------------------------
异步提交任务计算求和
import com.qf.executors_06.MyCallable;

import java.util.concurrent.Callable;

/**
 * 异步任务
 */
public class MySelfCallable  implements Callable<Integer> {
    private int number ;
    public MySelfCallable(int number){ //100或者200
        this.number = number ;
    }

    @Override
    public Integer call() throws Exception {
        //定义一个最终结果变量
        int sum = 0 ;
        for(int x = 1; x <= number ;x++){
            sum += x ;
        }

        return sum;
    }
}

/**
 * 需求:使用线程池的方式完成多个线程的异步计算:
 *        1个线程求1-100的和
 *       另一个线程求1-200的和
 *
 *       1)创建线程池:固定的可重用的线程数的线程池
 *       2)提交异步任务<T> Future<T> submit(Callable<T> task):提交异步任务
 *              返回值:Future接口:代表异步计算的结果
 *                  V get():获取计算的结果
 */
public class Test {
    public static void main(String[] args) throws
            ExecutionException, InterruptedException {

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

        //2)提交异步任务
        //<T> Future<T> submit(Callable<T> task):
        Future<Integer> f1 = threadPool.submit(new MySelfCallable(100));
        Future<Integer> f2 = threadPool.submit(new MySelfCallable(200));
        System.out.println(f1.get());
        System.out.println(f2.get());

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

3.17 Callable和Runnable的区别

1、最大的区别,runnable没有返回值,而实现callable接口的任务线程能返回执行结果
2、callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出

4.单例模式

1.单例设计模式----属于创建型设计模式(创建对象)
概念:
	始终在内存中有且仅有一个当前类的实例(有一个对象)
	
 * 单例设计模式:
 *          1)饿汉式
 *
 *          2)懒汉式
 *
 *      饿汉式:不会出现安全问题的单例设计模式
 *                  1)当前类是具体类
 *                  2)类一加载就创建当前类实例
 *                  3)构造私有化,对外隐藏,不能new实例
 *                  4)对外提供静态方法,返回值当前类本身
 *
 *                  Java中的类Runtime类:标准的单例(饿汉式),和计算机的运行环境有关系
 */
	单例模式之懒汉式
		1)当前类是个具体类
		2)当前类构造方法私有化
		3)当前类的成员位置:声明当前类的一个静态变量
		4)对外提供静态方法,返回值是当前类本身:需要判断当前变量是否为null
	现实可发业务中:
		存在懒加载(按需加载)
		一个用户有多个账户:一对多

4.1 I/O流

/**
 * java.io.File--->表示文件或者文件夹(目录)一种抽象路径形式
 * 构造方法:
 *  File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。
 *  File(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
 *  File(String parent, String child)  父路径名字符串和子路径名字符串创建新的 File实例
 */
public class FileDemo {
    public static void main(String[] args) throws IOException {
        //描述:D盘下的demo文件夹中的a.txt文件
        //File(String parent, String child)
        File file = new File("D:\\demo","a.txt") ;
        System.out.println(file);
        System.out.println("----------------------------------------") ;
        //File(File parent, String child)
        File file2 = new File("D:\\demo") ;
        File file3 = new File(file2,"a.txt") ;
        System.out.println(file3);
        System.out.println("----------------------------------------") ;

        //File(String pathname)(推荐)
        File file4 = new File("D:\\EE_2302\\day26\\code\\a.txt") ;
        System.out.println(file4.createNewFile()) ;//创建文件
        System.out.println(file4);

    }
}
/**
 * File的基本功能以及特有功能:
 *      public boolean createNewFile() throws IOException:创建文件
 *      public boolean mkdir():创建文件夹
 *      public boolean mkdirs():创建文件夹,当父目录不存在,自动创建
 *      public boolean delete():删除文件夹或则文件路径,如果是文件夹,必须是空目录
 *      public boolean exists():判断此路径名表示的文件或者目录是否存在
 *      public boolean isDirectory():此抽象路径名表示的文件是否是目录
 *      public boolean isFile():是否是文件
 *      public boolean isHidden():是否隐藏
 *
 *
 *      特有功能:高级功能
 *      public String[] list():获取指定抽象路径名表示的文件或者目录的名称(返回字符串数组)
 *      public File[] listFiles():获取指定路径名表示的文件或者目录的File数组
 */
public class FileDemo2 {
    public static void main(String[] args) throws IOException {
        //在创建D:\EE_2302\day26\code\demo文件夹
        //表示它的路径
        File file = new File("D:\\EE_2302\\day26\\code\\demo") ;
        //public boolean mkdir():创建文件夹
        System.out.println(file.mkdir());
        System.out.println("--------------------------------") ;
        //public boolean mkdirs():创建文件夹,当父目录不存在,自动创建
        //创建aaa\bbb\ccc 多级目录(直接指定具体文件夹或者文件,相对路径创建)
        File file2 = new File("aaa\\bbb\\ccc") ;
        System.out.println(file2.mkdirs());
        System.out.println("---------------------------------") ;
        File file3 = new File("a.txt") ;
        //创建文件
        System.out.println(file3.createNewFile());
        System.out.println("--------------------------------");
        System.out.println(file3.exists());
        System.out.println(file3.isDirectory());
        System.out.println(file3.isFile());
        System.out.println(file3.isHidden());
        System.out.println(file3.canRead());
        System.out.println(file3.canWrite());


        System.out.println("-----------------------------------");
        //public String[] list():获取指定抽象路径名表示的文件或者目录的名称(返回字符串数组)
        //public File[] listFiles():获取指定路径名表示的文件或者目录的File数组

        //描述D盘---所有的文件夹以及文件的名称 获取到
        File myFile = new File("d:\\") ;
        String[] strs = myFile.list() ;
        if(strs!=null){
            for (String str : strs) {
                System.out.println(str);
            }
            //System.out.println(Arrays.toString(strs));
        }

    }
}

/**
 * 获取D盘下的所有的以".jpg"结尾的文件
 *      "需要将文件名打印到控制台上"
 *
 *      分析:
 *          1)描述D盘---使用File对象
 *          2)通过 public File[] listFiles() :获取指定路径下所有的文件以及文件夹的File数组
 *          3)非空判断
 *                  3.1) 遍历File数组,获取到每一个File对象
 *                  3.2)如果当前File对象所表示文件路径的形式
 *                      3.2.1)是文件,判断文件是".jpg"结尾
 *                      3.2.2)输出这个文件的名称 public String getName()
 */
public class FileTest {
    public static void main(String[] args) {

        //1)描述D盘---使用File对象
        File file  = new File("D:\\") ;

        //2)通过 public File[] listFiles() :获取指定路径下所有的文件以及文件夹的File数组
        File[] files = file.listFiles();
        if(files!=null){
            for (File f : files) {
                //f:代表File对象
                //判断是否是文件
                if(f.isFile()){
                    //满足是文件
                    //获取文件名称,以".jpg"结尾
                    if(f.getName().endsWith(".jpg")){
                        //输出文件名称
                        System.out.println(f.getName());
                    }
                }
            }
        }
    }
}
/**
 * 获取D盘下的所有的以".jpg"结尾的文件
 *      "需要将文件名打印到控制台上"
 *
 *      之前的写法:
 *          1)先获取当前指定路径形式下的所有的文件以及文件夹的File数组或者String[]数组
 *          2)然后在遍历
 * 另一种方式:直接获取的同时就将满足条件的内容遍历
 * 参数都是接口---FilenameFilter:文件名称过滤器
 *          public boolean accept(File dir,String name)
 *              将指定的文件存储在文件列表中dir,返回值true(存储在文件列表中),否则false,不存储
 * public File[] listFiles(FilenameFilter filter)
 * public String[] list(FilenameFilter filter)
 *
 *
 */
public class FileTest2 {
    public static void main(String[] args) {
        //1)描述D盘:使用File对象
        File file = new File("D:\\") ;
        //2)public String[] list(FilenameFilter filter) 直接获取满足条件的文件,存储在String数组中
        String[] strs = file.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                //同时满足条件:
                //创建File对象--描述指定目录的文件列表
                File f = new File(dir, name);
                //分步完成
                //f是表示文件形式
                //boolean flag = f.isFile();
                //f是文件,文件名称是以".jpg"结尾
                //boolean flag2 = f.getName().endsWith(".jpg");

                //return flag && flag2;

                //一步走
                return f.isFile() && f.getName().endsWith(".jpg") ;
            }
        });
        if(strs!=null){
            for (String str : strs) {
                //满足文件列表的内容输出
                System.out.println(str);
            }
        }

    }
}

4.2 字节输出流

/**
 * 字节输出流:
 *  OutputStream---抽象类--不能new,具体的子类:针对文件操作:FileOutputStream
 *
 *  操作步骤:
 *      1)创建字节输出流对象
 *      2)写数据
 *      3)释放资源
 */
//public FileOutputStream(String name)throws FileNotFoundException
        //OutputStream out = new FileOutputStream("fos.txt") ;//多态
        FileOutputStream fos = new FileOutputStream("fos.txt") ;//具体类new  --->这个过程:需要创建系统资源(C语言)
//写数据
        //void write(byte[] b):写字节数组
        //void write(byte[] b, int off, int len):写字节数组一部分
        //abstract void write(int b) 写一个字节
        //fos.write(97) ;//写一个字节

        byte[] bytes = {98,99,100,101,102,103,104} ;
        fos.write(bytes) ;

        fos.write(bytes,0,3);

        //释放资源:将流对象指向的文件地址的系统资源进行释放
        fos.close() ;

        //fos.write(108); 当流对象关闭,释放系统资源,不能在写数据的!否则,IOException: Stream Closed
/**
 * 字节输出流:
 *  FileOutputStream实现文件的字节数追加
 *  public FileOutputStream(String name,
 *                         boolean append)
 *                  throws FileNotFoundException
 *                  第二个参数是true:自动追加内容
 *
 *     IO流写数据,实现换行效果 widows系统 "\r\n"
 */
public class FileOutputStreamDemo2 {
    public static void main(String[] args) throws IOException {

                //创建FileOutputStream流对象
        FileOutputStream fos2 = new FileOutputStream("fos2.txt",true) ;
        //fos2.write("world".getBytes()) ;
        for(int x = 0 ; x < 10 ; x ++){
            fos2.write(("hello"+x).getBytes());
            //写入换行符号
            fos2.write("\r\n".getBytes());
        }


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


4.3 字节输入流

/**

 * InputStream:字节输入流--->抽象类--->具体的子类FileInputStream:文件字节输入流

 * 1)创建文件字节输入流对象FileInputStream(String name) :读取指定的文件 name:文件地址

 * 2)读数据

 *    继承它父类的方法:public abstract int read()throws IOException:一次读取一个字节

 *    public int read(byte[] b) throws IOException:一次读取一个字节数组

 *

 *

 *    需求:

 *          使用字节输入流将当前项目下的"fis.txt"文件内容读取出来打印在控制台上

 *

 *    一次读取一个字节的方式:

 *          针对中文---会出现乱码 (char)字节数,平台默认编码格式utf-8:一个中文对应三个字节

 *          英文字母和中文-拼接一块,一个英文字母对应一个字节,这个时候拼接不上,导致文乱码---Java提供"字符流",解决乱码

 */

/**
 * 使用字节输入流一次读取一个字节数组的方式:
 *  public int read(byte[] b) throws IOException:一次读取一个字节数组
 */
public class FileInputStreamDemo2 {

    public static void main(String[] args) throws IOException {
        //创建一个字节输入流对象
       // FileInputStream fis2 = new FileInputStream("fis2.txt") ;
        //读取当前项目下的FileOutputStreamDemo3.java
        FileInputStream fis2 = new FileInputStream("FileOutputStreamDemo3.java") ;

        //第一次读取
       /* byte[] bytes = new byte[5] ;
        // public int read(byte[] b)--->返回:读取的字节数组的长度
        int len = fis2.read(bytes) ;
        System.out.println(len);
        //将bytes字节数组---->String(byte[] bytes,int offset,int len)
        //每一次都是0开始读取实际字节数
        System.out.println(new String(bytes,0,len));

        System.out.println("--------------------------------------") ;
        //第二次读取
        len = fis2.read(bytes) ;
        System.out.println(len) ;
        System.out.println(new String(bytes,0,len));
        System.out.println("------------------------------------------");
        //第三次读取
        len = fis2.read(bytes) ;
        System.out.println(len) ;
        System.out.println(new String(bytes,0,len));
        System.out.println("------------------------------------------");
        //第四次读取
        len = fis2.read(bytes) ;
        System.out.println(len) ;
        System.out.println(new String(bytes,0,len));
        System.out.println("------------------------------------------");
        //第五次读取
        len = fis2.read(bytes) ;
        System.out.println(len) ;*/
        //System.out.println(new String(bytes,0,len));


        //最终代码:字节流一次读取一个字节数组
        //创建一个字节缓冲区:长度:1024或者1024整数倍
        byte[] bytes = new byte[1024] ;
        //实际字节数从0开始
        int len = 0 ;
        while((len=fis2.read(bytes))!=-1){//赋值,判断   -- read(byte[] bytes):阻塞式方法,没有到-1,就一直读
            String str = new String(bytes,0,len) ;
            System.out.println(str);

        }


    }
}

 

/**

 * 需求:

 *  将当前项目下的myavi.mp4文件复制到D:\EE_2302\day26\code\copy.mp4

 *

 *  分析:

 *          当前项目下的myavi.mp4---->源文件地址---->使用字节输入FileInputStream读取

 *          D:\EE_2302\day26\code\copy.mp4---目的地文件---->使用字节输出流FileOutputStream写

 */

public class CopyMp4 {
    public static void main(String[] args) {
        long start = System.currentTimeMillis() ;
        //共耗时:222564毫秒
        //copyMp4("myavi.mp4","D:\\EE_2302\\day26\\code\\copy.mp4");

        //共耗时:356毫秒
        copyMp4_2("myavi.mp4","D:\\EE_2302\\day26\\code\\copy.mp4");
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时:"+(end-start)+"毫秒");
    }

    /**
     * 使用字节输入流一次读取一个字节数组
     * @param srcFile 源文件
     * @param destFile 目标文件
     */
    public static void copyMp4_2(String srcFile,String destFile){
        FileInputStream fis = null ;
        FileOutputStream fos = null ;

        try {
            fis = new FileInputStream(srcFile) ;
            fos = new FileOutputStream(destFile) ;

            //一次读取一个字节数组
            byte[] bytes = new byte[1024] ;
            //实际字节数
            int len = 0 ;
            while((len=fis.read(bytes))!=-1){
                //一次读取一个字节数组,写字节数组(每次0开始,写入实际字节数)
                fos.write(bytes,0,len);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
 /**
     * 使用字节输入流一读取一个字节的方式,进行文件复制
     * @param srcFile  源文件地址
     * @param destFile  目标文件地址
     */
    public static void copyMp4(String srcFile, String destFile) {
        FileInputStream fis = null ;
        FileOutputStream fos = null ;
        try {
            //创建字节输入流操作srcFile
             fis = new FileInputStream(srcFile) ;
            //创建字节输出流对象操作destFile
             fos = new FileOutputStream(destFile) ;

            //一次读取一个字节
            int by = 0 ;
            while((by=fis.read())!=-1){
                //使用字节输入流一次一个字节,使用fos输出流对象写一个字节
                fos.write(by) ; //不需要强转,要的字节
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4.4 递归

方法递归:

 *    方法本身调用方法的一种现象

 *    前提条件:

 *      1)定义一个方法

 *      2)这个方法的逻辑是规律的

 *      3)这个方法是有结束条件,否则---成了"死递归"

 *

 *     注意:

 *      构造没有递归

 *

 *    需求:

 *      求5的阶乘

 *      1)可以使用循环解决---求阶乘思想

 *              5!= 5*4!

 *                  --->

 *                      --->

 *

 *      2)使用递归:

 *              1)定义一个方法

 *              2)有规律:

 *                      分解法:

 *                       将问题1

 *                                 拆分问题11

 *                         5!=5*4!

 *                               4! = 4 * 3!

 *                                          3! = 3 * 2!

 *                                                   2! = 2 * 1!

 *                                                            1!永远是1

public class DiGuiDemo {
    public static void main(String[] args) {
        //求5的阶乘
        //定义最终结果变量
        int jc = 1 ;
        for(int x = 1 ; x <= 5 ; x ++){
            jc *= x ;
        }
        System.out.println("5的阶乘是:"+jc);
        System.out.println("----------------------------------------------") ;
        System.out.println("5的阶乘是:"+getJc(5));
    }
    public static int getJc(int n){ //5
        if(n==1){
            return  1 ;
        }else {
                //5*getJc(4)
                //4*getJc(3)
               //3*getJc(2)
            //2*getJc(1)
            return  n * getJc(n-1) ;
        }
    }

4.5 字节缓冲流

/**

 * Java之IO流提供了字节缓冲流:让读取的速度更快一些,提供对应的类

 * 字节缓冲输入流/字节缓冲输出流  (高效的字节流)---仅仅是内部提供缓冲区字节数组长度:8192长度,

 *      文件的读写复制还的依赖于基本字节流(InputStream/OutputStream)

 * BufferedInputStream(InputStream in):字节缓冲输入流

 * BufferedOutputStream(OutputStream out):字节缓冲输出流

 *

 * 使用字节缓冲流来完成读写复制---看他们执行效率

 *

 * 使用基本字节流一次读取一个字节:共耗时:222564毫秒

 * 使用基本字节流一次读取一个字节数组:共耗时:356毫秒

 *

 * 推荐"使用字节缓冲流"完成图片文件/视频文件/音频文件的读取!

 *

 * 使用字节缓冲流:一次读取一个字节:    共耗时:1250毫秒

 * 使用字节缓冲流:一次读取一个字节数组:  共耗时:110毫秒

 * 需求:
 *      将当前项目下的myavi.mp4--->复制到 D:\EE_2302\day27\code\copy.mp4
 *
 *
 */
public class CopyFileTest {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
//        copyFile("myavi.mp4","D:\\EE_2302\\day27\\code\\copy.mp4") ;
        copyFile2("myavi.mp4","D:\\EE_2302\\day27\\code\\copy.mp4") ;
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时:"+(end-start)+"毫秒");
    }

    /**
     * 使用字节缓冲输入流一次读取一个字节数组
     * @param srcFile 源文件地址
     * @param destFile 目标文件地址
     */
    public static void copyFile2(String srcFile, String destFile) throws IOException {
        //BufferedInputStream(InputStream in):字节缓冲输入流
        //创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
        // BufferedOutputStream(OutputStream out):字节缓冲输出流
        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;//实际字节数
        while((len=bis.read(bytes))!=-1){
            //每次从0开始写入实际字节数
            bos.write(bytes,0,len);
            //使用字节缓冲输出流的时候,防止一些字节数没有交给OutputStream字节输出流
            //强制刷新缓冲输出流(尤其图片,图片文件带有缓冲数据:可能字节数没有写入到底层输出流中,导致图片文件缺失)
            bos.flush() ;
        }
        //释放资源
        bos.close();
        bis.close();
    }

    /**
     * 使用字节缓冲输入流一次读取一个字节
     * @param srcFile 源文件地址
     * @param destFile 目标文件地址
     */
    public static void copyFile(String srcFile, String destFile) throws IOException {
        //BufferedInputStream(InputStream in):字节缓冲输入流
        //创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
        // BufferedOutputStream(OutputStream out):字节缓冲输出流
        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;

        //一次读取一个字节
        int by = 0 ;
        while((by=bis.read())!=-1){
            //读取一个字节,写出一个字节
            bos.write(by) ;
        }
        //释放资源
        bos.close();
        bis.close();

    }
}

4.6 字符输入输出流

/**
 * 字符输入流---->Reader(抽象类)--->
 *  具体的子类:字符转换输入流InputStreamReader --- 将字节输入流---->转换--->字符输入流
 *  public InputStreamReader(InputStream in):使用平台默认字符集进行解码---读取数据
 *  public InputStreamReader(InputStream in,String charsetName)
 *              使用指定字符集解码---读取数据
 *
 *   读数据:
 *      int read() :一次读取一个字符  --->返回结果:实际的字符数
 *      int read(char[] cbuf) :一次读取一个字符数组
 *      int read(char[] cbuf,int off, int len):读取字符数组的一部分
 *
 */
public class InputStreamReaderDemo {
    public static void main(String[] args) throws Exception {
        //创建字符转换输入流对象

        //解码:gbk(中国编码表): 一个中文对应两个字节
      /*  InputStreamReader isr = new InputStreamReader(
                new FileInputStream("osw.txt"),"gbk") ;*/
        //public InputStreamReader(InputStream in)
        InputStreamReader isr = new InputStreamReader(
                new FileInputStream("osw.txt")) ;
        //读数据
        //一次读取一个字符
        int by = 0 ;//实际字符数
        while((by=isr.read())!=-1){
            //展示控制台上  读取的时候将文件内容的里面--->int类型----->转换成字符
            System.out.print((char)by);
        }

    }
}
/**
 * 字符流:
 *      字符输出流:Writer(抽象类)--->
 *          具体的子类 OutputStreamWriter:字符转换输出流,"可以将字节输出流---转换---字符输出流"
 *          构造方法:
 *                  public OutputStreamWriter(OutputStream out):使用平台默认字符集进行编码--输出数据
 *                  public OutputStreamWriter(OutputStream out,String charsetName):
 *                                  使用指定的字符集进行编码---输出数据
 *          写数据:
 *                void write(char[] cbuf) :写入字符数组
 *                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 Exception {

        //创建一个字符转换输出流对象
        //utf-8:一个中文对应三个字节
       /* OutputStreamWriter osw = new OutputStreamWriter(
                new FileOutputStream("osw.txt"),"utf-8") ;*/

        OutputStreamWriter osw = new OutputStreamWriter(
                new FileOutputStream("osw.txt"));

        //写字符串
        osw.write("helloworld");
        osw.write("高圆圆");
        osw.write(97) ;//写入字符,传入参数int--->ASCII码表对应的字符值

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


4.7 字符缓冲流

/**
 * BufferedReader:字符缓冲输入流---可以读取一行内容
 * 构造方法:
 *  public BufferedReader(Reader in):提供默认缓冲区大小的字符缓冲输入流 (8192个长度)
 *              这个流如果直接操作文件--->参数里面使用FileReader
 *              public String readLine()throws IOException
 *
 *      特有功能:
 *          public String readLine()throws IOException:一次读取一行
 *                      返回值是读取到这一行的内容,当流已经到达末尾,则返回null
 *
 *  键盘录入
 *          1)Scanner(InputStream in)---->Scanner sc = new Scanner(System.in) ;
 *          2)new BufferedReader(new InputStreamReader(System.in))
 *                                  --->InputStreamReader(InputStream in)
 */
public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        //创建一个字符缓冲输入流对象
        //BufferedReader br = new BufferedReader(new FileReader("br.txt")) ;
        BufferedReader br = new BufferedReader(new FileReader("copy.java")) ;

        //public String readLine()throws IOException:一次读取一行
        // 返回值是读取到这一行的内容,当流已经到达末尾,则返回null

        /*//第一次读取
        String line = br.readLine();
        System.out.println(line);

        //第二次读取
        line = br.readLine() ;
        System.out.println(line);

        //第三次读取
        line = br.readLine() ;
        System.out.println(line);

        //第四次读取
        line = br.readLine() ;
        System.out.println(line);

        //第五次读取
        line = br.readLine() ;
        System.out.println(line);*/

        //最总版本:循环改进
        String line = null ;
        while((line=br.readLine())!=null){ //赋值,判断 readLine() --->阻塞式方法
            System.out.println(line);
        }



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

    }

}
/**
 * 字符缓冲输出流:提供字符数组/字符/字符串的一种高效写入
 *  BufferedWriter构造方法
 *      public BufferedWriter(Writer out):提供默认缓冲区大小的字符缓冲输出流,默认缓冲区足够大 8192长度
 *  写的功能:
 *          write(字符/字符数组/字符串...)
 *        特有功能:
 *              不需要在使用"\r\n"换行符号, public void newLine()写入行的分隔符
 */
public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {
        //创建字符缓冲输出流对象

//        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ;
        //FileWriter(Stirng pathname,boolean append):第二个参数true,自动追加(字符追加到末尾)
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt",true)) ;

        //写数据
        for(int x =   1 ; x <=10 ;x ++){
            bw.write("hello"+x);
            //换行
            bw.newLine();
            bw.flush();
        }
        //释放资源
        bw.close();




    }
}

4.7 URL-->统一资源定位符号

/**
 * 需求:
 *  需要将网络图片下载到本地
 * Java提供了类:URL-->统一资源定位符号
 *          1)创建URL对象public URL(String spec)
 *          2)使用URL建立http请求---建立连接
 *                  public URLConnection openConnection()throws IOException
 *                              URLConnnection:和http建立的通信协议(不能new)
 *
 *                                  HttpURLConnection--子类提供一些方法:
 *                                  public InputStream getInputStream()throws IOException 获取到字节输入流
 *         3)创建基本字节输出流FileOutputStream
 *         4)一次读取一个字节数组的方式,将图片复制到本地磁盘上
 *
 */
public class Test2 {
    public static void main(String[] args) {

        HttpURLConnection httpURLConnection = null ;
        InputStream inputStream = null ;
        FileOutputStream fos = null ;
        try {
            //1)创建URL地址对象
            URL url = new URL("http://rs0k1dy20.hd-bkt.clouddn.com/bug.jpg");
            //2)建立连接通信
            //public URLConnection openConnection()throws IOException
            // URLConnnection:和http建立的通信协议(不能new)
            httpURLConnection = (HttpURLConnection) url.openConnection();
            //3)获取输入流对象-读取网络地址的内容
            inputStream = httpURLConnection.getInputStream();
            //4)创建字节输出流对象
            fos = new FileOutputStream("gaoyuanyuan.jpg")  ;
            //一次读取一个字节数组
            byte[] bytes  = new byte[1024] ;
            int len = 0 ;
            while((len=inputStream.read(bytes))!=-1){
                //写
                fos.write(bytes,0,len) ;

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(httpURLConnection!=null){
                //让连接失效public abstract void disconnect()
                httpURLConnection.disconnect();
            }

        }
    }
}

4.8 Properties

/**
 * java.util.Properties:属性集合列表---extends Hashtabl<K,V>-->
 *
 *          本质是Map<K,V>集合
 *          添加功能:put(K key,V value)
 *          遍历方式:Set<K> keySet()
 *                  遍历键-->通过键获取值 V get(K key)
 *
 * 自己的特有功能:
 *      java.util.Properties:没有泛型,键和值都只能String---->
 *                  作用:用来读取配置文件xx .properties配置文件(src下面)
 *      构造方法:
 *              public Properties()
 *      添加属性列表的键和值:
 *              public Object setProperty(String key,String value)
 *       特有的遍历方式:
 *              public Set<String> stringPropertyNames():获取属性列表中的所有的键(属性名称)
 *              public String getProperty(String key):通过属性列表中的属性名称获取对应的值
public class PropertiesDemo {
    public static void main(String[] args) {

        //创建属性集合列表
        Properties prop = new Properties() ;
        System.out.println(prop);
        //public Object setProperty(String key,String value)
        prop.setProperty("张三","30") ;
        prop.setProperty("李四","25") ;
        prop.setProperty("王五","35") ;
        System.out.println(prop);

        //遍历
        // public Set<String> stringPropertyNames():获取属性列表中的所有的键(属性名称)
        //  public String getProperty(String key):通过属性列表中的属性名称获取对应的值
        Set<String> keySet = prop.stringPropertyNames();
        for(String key:keySet){
            String value = prop.getProperty(key);
            System.out.println(key+"---"+value);
        }

    }
}
/**
 * java.util.Properties可以保存到流中或从流中加载
 *
 *  将文件的内容加载到属性集合列表中:
 *  public void load(InputStream inStream/Reader)
 *
 *  将属性列表中的内容保存到指定文件中
 *  第二个参数:描述属性列表
 *  public void store(OutputStream out/Writer,String comments)
 *
 * 举例:
 *      游戏的关卡的加载--->读文件
 *      游戏津达的保存----->写文件
public class PropertiesDemo2 {
    public static void main(String[] args) throws IOException {

//        myLoad();
        myStore() ;

    }

    //写
    private static void myStore() throws IOException {
        //将属性列表中的内容---保存指定文件中
        Properties prop = new Properties() ;
        //存储用户名和密码
        prop.setProperty("文章","123") ;
        prop.setProperty("高圆圆","123456") ;
        prop.setProperty("admin","admin") ;
        System.out.println(prop) ;
        //public void store(OutputStream out/Writer,String comments)
        prop.store(new FileWriter("D:\\EE_2302\\day27\\code\\day27\\src\\my.properties"),"name'list") ;


    }

    private static void myLoad() throws IOException {
        //创建属性集合列表
        Properties prop = new Properties() ;
        System.out.println(prop);

        //如何读取src下面的xx.properties文件
        //1)获取当前类的字节码文件对象--->类名.class 属性
     //   Class c = PropertiesDemo2.class ;
        //2)通过获取到的字节码文件对象获取 当前类的加载器
        // public ClassLoader getClassLoader()
       // ClassLoader classLoader = c.getClassLoader();

        //3)ClassLoader---类加载器--->读取到src下面配置文件的内容--->存储到字节输入流中
        //public InputStream getResourceAsStream(String name) 返回用于读取指定资源的输入流
        //InputStream inputStream = classLoader.getResourceAsStream("name.properties");

        //一步走
        InputStream inputStream = PropertiesDemo2.class.getClassLoader().
                getResourceAsStream("name.properties");
        //public void load(InputStream inStream/Reader)
        prop.load(inputStream);
        System.out.println(prop);
    }
}

5. 网络编程

1.网络三要素
ip地址:
端口号
协议

ip地址:A/B/C
A类---->前面第一个号段:网络号段   后面的号段:主机号段(国家部门---机房)
B类----->前面两个号段:网络号段     后面的号段:主机号段(大学校园/公司内网等)
C类----->属于私人地址:前面三个号段:网络号段    后面的号段:主机号段(私人)
点分十进制法--->10.35.165.17

端口号:0-65535   这里面0-1024(属于保留端口号)
自己指定端口号----->1025---65535(不要和系统端口号冲突----Adress  already  in   use:BindException:地址被占用"  )
mysql端口号:3306
tomcat:web服务器:8080
协议:
TCP/UDP协议(底层网络协议)
UDP协议:
1)属于不可靠连接,不需要建立连接通道(不安全)----以“数据报包”的方式进行数据传输的
2)不同步的,执行效率相对于TCP协议,效率高
3)发送数据大小由限制的!
TCP协议:
1)属于可靠连接,需要建立连接通道!----以“流”的方式发送给数据
2)同步的,执行效率低,安全性相对UDP协议(安全性能高)
3)发送数据大小无限制!

网络编程--->“Socket”编程
发送端/客户端
接收端/服务器端
两端都必须有Socket对象!("套接字")
/**
 * java.net.InetAddress:代表互联网ip地址对象!
 * 获取ip地址对象---->获取ip地址字符串形式/获取主机名称
 *  参数:要么传入主机名称/要么ip地址字符串
 * public static InetAddress getByName(String host)throws UnknownHostException :获取互联ip地址对象
 *
 *  InetAddress的成员方法
 *  public String getHostAddress():获取ip地址字符串形式
 *  public String getHostName():获取ip地址的主机名

/**
 * 使用UDP协议发送数据
 *  1)创建发送端的socket对象
 *  2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
 *  3)使用发送端的Socket对象发送"数据报包"
 *  4)释放资源
 */
//1)创建发送端的socket对象 ---java.net.DatagramSocket:此类表示用于发送和接收数据报数据包的套接字
        //public DatagramSocket()throws SocketException
        DatagramSocket ds = new DatagramSocket() ;

        //2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
        //java.net DatagramPacket
        //参数1:分组数据(字节数组)
        //参数2:包的长度
        //参数3:ip地址对象
        //参数4:端口号
        //public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
        byte[] bytes = "hello,udp我来了".getBytes() ;
        int length = bytes.length ;
        InetAddress inetAddress = InetAddress.getByName("10.35.165.17") ;
        int port = 6666 ;
        DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;

        //使用发送端的Socket对象发送"数据报包"
        //public void send(DatagramPacket p)throws IOException从此套接字发送数据报包
        ds.send(dp) ;

        //释放资源
        ds.close();
    }
}
/**
/**
 * Udp接收端
 * 1)创建接收端的Socket对象
 * 2)创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
 * 3)使用2)接收容器(缓冲区),接收发送端的数据
 * 4)解析接收容器中的实际内容
 * 5)展示数据即可
 * 6)释放接收端的资源
 *
 * 注意:
 *      接收端开启一次就可以;否则
 *          Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
 */
public class UdpReceive {
    public static void main(String[] args) throws Exception {
        //1)创建接收端的Socket对象
        //public DatagramSocket(int port)throws SocketException
        DatagramSocket ds = new DatagramSocket(6666) ;

        //2)创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
        //参数1:用于保存传入"数据报"的缓冲区。
        //参数2:要读取的字节数
        //public DatagramPacket(byte[] buf,int length)
        byte[] buffer = new byte[1024] ;
        DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;

        //3)使用2)接收容器(缓冲区),接收发送端的数据
        //public void receive(DatagramPacket p)throws IOException从此套接字接收数据报包
        ds.receive(dp) ;

        //4)解析接收容器中的实际内容
        //public byte[] getData() :解析数据报包中DatagramPacket缓冲的实际的字节数组
        byte[] bytes = dp.getData();
        //public int getLength():解析数据报包中DatagramPacket中的实际字节数组长度
        int length = dp.getLength();

        //展示数据
        String message = new String(bytes,0,length) ;
        //谁发送的数据---获取ip地址字符串形式
        //数据报包DatagramPacket--->public InetAddress getAddress()
        //InetAddress:ip地址--->String getHostAddress()
        String ip = dp.getAddress().getHostAddress() ;
        System.out.println("data from is--->"+ip+",data is--->"+message) ;

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

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值