总结-第一阶段-第五周

TreeSet比较器排序

//自定义类实现Comparator接口,重写compare方法
	public class MyComparator  implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
 //主要条件:
// 按照学生的年龄从小到大排序
        int num = s1.getAge() - s2.getAge() ;
 //次要条件:年龄相同的人,按照姓名进行比较:(字典顺序比较)  //自定义类使用该compareTo方法需重写equals、hashCode方法
        int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
        return num2;
    }
}


//使用比较器排序完成
//public TreeSet(Comparator<E> comparator)创建TreeSet集合使用比较器进行排序
//需要存储的类型实现Comparator接口中的int compare(T o1,T o2),T--Type类型,需要Compartor中的泛型一致
		public class TreeSetDemo {
    	public static void main(String[] args) {
//创建TreeSet集合对象

方案1:自定义一个类 实现Comparator接口
//接口多态/创建具体的子实现类对象
        MyComparator my = new MyComparator() ;
        TreeSet<Student> ts = new TreeSet<>(my) ;


方案2:参数就是接口匿名内部类
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s1.getAge() - s2.getAge() ;
                //次要条件:年龄相同的人,按照姓名进行比较:(字典顺序比较)
                int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
                return num2;
            }
        }) ;


//创建学生对象,测试
        Student s1 = new Student("gaoyuanyuan",44) ;
        Student s2 = new Student("zhangjianing",44) ;
        Student s3 = new Student("wenzhang",35) ;
        ts.add(s1) ;
        ts.add(s2) ;
        ts.add(s3) ;
        //遍历
        for(Student s:ts){
            System.out.println(s) ;
        }
    }
}


//学生类
    public class Student {
        private String name ;
        private  int    age ;
// ......
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Student student = (Student) o;

            if (age != student.age) return false;
            return name.equals(student.name);
        }
        @Override
        public int hashCode() {
            int result = name.hashCode();
            result = 31 * result + age;
            return result;
        }
    }

Map集合

Map<K,V> :一个键对应一个值(键值映射),Map集合针对键必须唯一,值是可以重复的
  K             Value
  1             张三
  2             王五
  
  
Map集合的基本功能:
//创建Map实现子类 
 	Map<String,String> map = new HashMap<>() ;
1、添加:V put(K key,V value) :添加方法,通过返回值是否为null,判断键是否重复,如果不重复,返回值永远是null;如果键重复,将第一次存储键对应的值返回,将后面键对应的值进行存储
	map.put("邓超","孙俪") ;
	map.put("文章","马伊琍") ;//被替换
	map.put("文章","姚笛") ;//返回“马伊琍”,将“姚笛”加入
2、 void clear():清空Map集合的所有键值对
	map.clear();
3、 V remove(Object key):删除Map集合的指定键,然后返回被删除的值
	map.remove("文章");//删除该键,返回“yaod”
4、 boolean containsKey(Object key):是否包含指定的键
	map.containsKey("邓超"); //true,存在
5、boolean containsValue(Object value):是否包含指定的值
	map.containsValue("孙俪"); //true,存在
6、 boolean isEmpty():判断Map集合是否为空
	map.isEmpty();// true

Map集合遍历方式

 	Map<String,String> map  = new HashMap<>();
 	 map.put("杨过","小龙女") ;
 	 map.put("郭靖","黄蓉") ;
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 + ":" +get(value);
    }
    
2、 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) ;
        }
 

面试题:Map集合和Collection集合的区别?

1)集合类型的区别
	Collection单例集合,只能存储一种引用类型数据
	Map,双列集合,可以存储键值对Key,Value,两种引用类型数据
2)遍历方式的区别
	Collection--->使用自己的专有遍历方式:迭代器---增强for来代替迭代器
	Map:1、Set<K> keySet()--->增强for遍历所有的键---->V get(Object  key)
		2、Set<Map.entry<K,V>> etnrySet()--->遍历键值对对象--->K getKey(), V getValue()
3)间接有一定关系
	Collection里面Set接口的两个子实现类,HashSet和TreeSet都依赖于Map接口中两个子实现类
	HashMap和TreeMap---put方法有关系

HashMap嵌套ArrayList,及嵌套遍历

 HashMap<String, ArrayList<String>> hm = new HashMap<>() ;
//第一个子集合
        ArrayList<String> array1 = new ArrayList<>() ;
        array1.add("宋江") ;
        array1.add("晁盖") ;
//将第一个ArrayList集合作为值添加到HashMap集合中
        hm.put("水浒传",array1) ;

//第二个子集合
        ArrayList<String> array2 = new ArrayList<>() ;
        array2.add("刘备") ;
        array2.add("曹操") ;
        hm.put("三国演义",array2) ;

//遍历大集合
//获取所有的键
        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);
            }
        }

HashMap

 hashMap--->底层哈希表实现,put方法依赖hashCode和equals方法,
 Map针对键有效,要针对键的类型自定类型,自定义类必须重写equals和hashCode(),保证键唯一
    HashMap<Student,String> hm = new HashMap<>() ;
  	 Student s1 = new Student("唐伯虎",35) ;
  	 hm.put(s1,"明朝") ;
  	 //遍历
        Set<Student> keySet = hm.keySet();
        for(Student s:keySet){
            String value = hm.get(s);
            System.out.println(s.getName()+s.getAge()+value);
        }

TreeMap

TreeMap<K,V><存储键值对元素,保证键唯一的同时,还有排序
使用哪种排序,取决于使用构造方法:

一、自然排序,无参构造方法:public TreeMap():当前键的类型必须实现Compareable接口,重写comeable方法

二、选择器排序:有参构造方法,传入比较器
//使用比较器排序实现
      TreeMap<Student,String> tm  = new TreeMap<>(new Comparator<Student>() {  //匿名内部类
          @Override
          public int compare(Student s1, Student s2) {
// 按照学生姓名的长度,从小到排序
            int num = s1.getName().length() - s2.getName().length() ;
//次要条件:长度相同的,不一定姓名内容相同
            int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
              return num2 ;
            }
        }) ;

Collections:针对集合操作工具类

java.util.Collections:针对集合操作工具类。提供一些常用的方法:
	List<Integer> list = new ArrayList<>();
        list.add(15);
        list.add(10);
        list.add(3);

1、binarySearch  二分搜索在List中查询元素 (不管集合还是Arrays---->binaraySearch方法,(集合或者数组必须有序))
 public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key):
 		 int index2 = Collections.binarySearch(list, 15);
 
2、max 获取Colection中的最大值   public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 
		 Integer max = Collections.max(list);  
		 
3、min 获取Colection中的最小值   public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) 
		 Integer max = Collections.min(list);

4、sort 针对List集合进行自然排序   public static <T extends Comparable<? super T>> void sort(List<T> list):针对List集合进行自然排序
		Collections.sort(list);

5、 public static <T> void sort(List<T> list,Comparator<? super T> c):针对List集合比较器排序。
6、public static void shuffle(List<?> list):针对List集合随机置换
7、 public static <T> List<T> synchronizedList(List<T> list):将线程不安全ArrayList,变成线程安全的列表

集合应用:使用集合"模拟斗地主"

在这里插入图片描述

使用集合模拟斗地主,创建牌盒,洗牌,发牌,看牌,保证每个人手上的牌有序

public class PokerTest {
    public static void main(String[] args) {
//1)创建牌盒HashMap<Integer,String>
        HashMap<Integer,String> hm = new HashMap<>() ;
 //创建ArrayList集合单独存储的牌的编号
        ArrayList<Integer> array = new ArrayList<>() ;
//2)装牌
//点数数组
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"} ;
 //花色数组
        String[] colors = {"♥","♠","♣","♦"} ;
 //定义一个索引值,记录牌的编号
        int index = 0 ;
        for(String number:numbers){
            for(String color:colors){
                String poker = color.concat(number);
 //将编号和牌添加大集合中
                hm.put(index,poker) ;
//将index添加ArrayList集合中
                array.add(index) ;
                index++ ;
            }
        }
 //添加小王,大王
        hm.put(index,"小王");
        array.add(index) ;
        index++ ;
//添加大王
        hm.put(index,"大王") ;
        array.add(index) ;
//洗牌:洗的是ArrayList那个编号
        Collections.shuffle(array) ;
//发牌:发的也是编号,为了保证每个人上手的牌有序,使用TreeSet
        TreeSet<Integer> player1 = new TreeSet<>() ;
        TreeSet<Integer> player2 = new TreeSet<>() ;
        TreeSet<Integer> player3 = new TreeSet<>() ;
        TreeSet<Integer> diPai = new TreeSet<>() ;
//循环:获取所有的牌的编号
        for(int x = 0 ; x < array.size(); x++){
            if(x >= array.size()-3){
                //底牌
                diPai.add(array.get(x)) ;
            }else if(x % 3 == 0){
                //玩家1的
                player1.add(array.get(x)) ;
            }else if(x % 3 == 1){
                //玩家2
                player2.add(array.get(x)) ;
            }else if(x % 3 == 2){
                //玩家3
                player3.add(array.get(x)) ;
            }
        }

//调用看牌的功能
        lookPoker("李国栋",player1,hm) ;
        lookPoker("刘一帆",player2,hm) ;
        lookPoker("梁飞虎",player3,hm) ;
        lookPoker("底牌",diPai,hm) ;
    }
//定义看牌的功能   
    public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String > hm){
        System.out.print(name+"的牌是:\t") ;
        //遍历TreeSet集合:获取到每一个玩家的编号
        for(Integer key:ts){
            //在hm大集合中去通过键获取值
            String value = hm.get(key);
            System.out.print(value+" ");
        }
        System.out.println();
    }


线程

进程和线程的概念

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

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

面试题:jvm是多线程的吗、Java能创建线程吗

1、jvm是多线程的吗?
    是多线程;至少有两条件线程
         1)main---"用户线程"
         2)垃圾回收线程,回收没有更多的引用对象,从而释放空间;如果没有
         垃圾回收线程,Java中不断的去new,不断的堆内存空间---->导致OOM(Out Of Memory:内存溢出)
2、Java能创建线程吗?
 	Java不能够直接创建资源的,间接的提供了一个Thread

创建线程的方式

创建线程的方式1:
    1)自定义一个类 继承Thread类
    2)重写Thread类的run方法
    3)在main()用户线程中,创建当前线程了对象
    4)启动线程--->start()不是run方法,run方法是一个普通方法


//1、自定义一个类 继承Thread类
	public class MyThread  extends  Thread{
    @Override
    public void run() {//重写run方法
        for(int x = 0 ; x < 200 ; x ++){
            //public final String getName()获取线程的名称
            System.out.println(getName()+":"+x) ;
        }}} 
        
 //2、创建当前线程了对象、启动线程
	 public class ThreadDemo {
   		 public static void main(String[] args) {
//在main()用户线程中,创建当前线程了对象
        MyThread my = new MyThread() ;//第一个线程对象
        MyThread my2 = new MyThread() ;//第二个线程对象
        my.setName("高圆圆") ;//setName()方法,设置线程名称
        my2.setName("李纪奔");
//启动线程
//my.run();用线程对象将普通run方法,并不会出现线程的执行随机性
        my.start();
        //my.start() ;//IllegalThreadStateException:非法线程状态异常,一个线程只能启动一次
        my2.start();
    }}   




线程的创建方式2:
1)自定义一个类,实现Runnable接口
 2)重写Runnable接口的run方法
 3)在用户线程main中去创建当前类对象--->"资源类"
 4)创建Thread类对象,将3)资源类作为Thread类对象的参数进行传递

public class ThreadDemo {
    public static void main(String[] args) {
        //创建MyRunnable类对象
        MyRunnable my = new MyRunnable() ;
        //创建线程Thread类对象,将资源类作为参数传递
        //public Thread(Runnable target,String name)
        Thread t1 = new Thread(my,"t1") ;
        Thread t2 = new Thread(my,"t2") ;
        //启动线程
        t1.start() ;
        t2.start() ;
    }}
//自定义一个类,实现Runnable接口
public class MyRunnable  implements Runnable{
    @Override
    public void run() {
        //完成耗时的操作
        //两个线程都要进来
        for(int x = 0 ; x < 200 ; x ++){
            //Thread类静态方法
            //public static Thread currentThread():获取正在执行的线程
            System.out.println(Thread.currentThread().getName()+":"+x) ;
        }}}
       

在这里插入图片描述

线程案例:龟兔赛跑

乌龟和兔子使用同一个赛道---"体现数据共享"
public class Race implements Runnable{
    //定义一个静态的String类型变量:代表胜利者
    private static String winner ;
    @Override
    public void run() {
        //for循环0-199---x:代表步数
        for(int x = 0 ; x < 100 ; x++){
            //如果当前线程名称是"兔子",并且每10步睡眠一下  让他睡眠
            if("兔子".equals(Thread.currentThread().getName()) && (x%10==0)){
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"跑了---->"+x+"步");

            boolean flag =  isGameOver(x) ;//x就是的步数
            if(flag){
                //true:表示比赛结束
                break;
            }
        }
    }
 //定义一个方法:比赛是否结束的方法
    public boolean isGameOver(int step){//上面的步数
        if(winner!=null){
            //已经存在胜利者
            return  true ;
        }//局部代码块
        {
            if(step >=99){
                //跑完了
               winner = Thread.currentThread().getName() ;
                System.out.println("winner is"+winner) ;
                return  true ;
            }
        }
        return  false ;
    }
    public static void main(String[] args) {
        //创建赛道---资源类对象
        Race race = new Race() ;
        //创建两个线程
        Thread t1 = new Thread(race,"兔子") ;
        Thread t2 = new Thread(race,"乌龟") ;
        //启动线程
        t1.start() ;
        t2.start() ;
    }

}

线程礼让yield、线程 插队join()

public static void yield():线程礼让(暂停当前正在执行的线程,执行其他线程)
案例:
	public class YieldThread  extends Thread{
   	 @Override
    public void run() {
        for(int x = 0 ; x < 100 ;x++){
            System.out.println(getName()+":"+x);
            yield() ;//每一个线程在执行这个方法的时候,都要暂停当前自己的线程,执行其他线程
        }
    }
}

public final void join() throws InterruptedException
因为线程的执行具有随机性,哪个线程启动之后,调用join(),正常理想情况,等待该线程执行完毕,执行其他线程
案例:
		jt1.start();
        try {
            jt1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jt2.start();
        try {
            jt2.join();//等待该线程终止
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jt3.start();
        

join()和yield()的区别

join()
	当前线程终止(线程调用join()方法会让当前线程执行完毕)
yield()	
	线程礼让,暂停正在执行的线程,并执行其他线程

解决多线程安全问题-synchronized-买票案例

校验多线程安全问题的标准 (解决方案)
1)检查程序是否是多线程环境  :是多线程卖票
2)是否存在共享数据 :     也存在的而且存在100张票
3)是否存在多条语句对共享数据的操作:也存在,判断tickets以及完成--
  Java提供了同步代码块,解决上面3),使用同步代码块将多条语句对象共享数据的包起来
    Java的同步机制
	synchronized(锁对象){ //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
	 多条语句对共享数据的操作
       }
 
什么是同步方法?
 如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上
  默认的锁对象this
  如果是静态的同步方法,锁对象是 "当前类名.class"--跟"反射"有关系

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---当前类对象的地址值引用
     //如果是静态的同步方法,锁对象是 "当前类名.class"
//    public synchronized void sell(){
    public  static synchronized void sell(){//静态是随着类的加载而加载 ,静态的同步方法的锁对象是"类名.class"
        if (tickets > 0) {
            //模拟网络延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
        }
    }
}

死锁

 public class MyLock {
    // 锁对象:可以是任意Java类对象
    public static Object objA = new Object() ;
    public static Object objB = new Object() ;
}
 
  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...");
                } } }}

生产者-消费者模式-解决线程死锁问题

生产者不断产生数据,当他没有数据了,等待产生数据之后,自己等待wait,notify通知(唤醒)消费者所在的线程来使用数据
消费者不断使用数据,当他有数据了,等待先使用数据,使用完毕没有数据了,自己等待wait,notify通知(唤醒)生产者所在的线程来产生数据


//测试类
    public class ThreadDemo {
    public static void main(String[] args) {
        //创建学生对象,锁对象
        Student s = new Student() ;
        //创建资源类
        SetThread st = new SetThread(s) ;
        GetThread gt = new GetThread(s) ;
        Thread t1 = new Thread(st) ;
        Thread t2 = new Thread(gt) ;
        t1.start() ;
        t2.start() ;
    }
//学生类(锁对象)
 public class Student {
    String name ;  //姓名
    int age     ;  //年龄
    boolean flag ; //false,没有数据,true,有数据!
}

//生产者所在的资源类
	public class SetThread  implements Runnable{
    private Student s ;
    public SetThread(Student s){
        this.s = s ;
    }
    private  int x = 0 ;//统计变量
    @Override
    public void run() {
        while(true){
            synchronized (s){ //锁对象,任意Java类对象
                if(s.flag){
                    //没有数据,先产生数据,等待消费使用数据
                    //线程等待--Object类---->wait()
                    try {
                        s.wait();  //wait方法调用,立即释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(x % 2==0){
                    s.name = "高圆圆" ;
                    s.age = 44 ;
                }else {
                    s.name = "李国栋";
                    s.age = 23 ;
                }
                //如果有数据了,通知(唤醒)消费者资源来,赶紧来消费(改变信号)
                s.flag = true ;
                s.notify() ;
            }
            x ++ ;
}}}

//消费者资源类
	public class GetThread  implements Runnable{
    //声明学生变量
    private Student s ;
    public GetThread(Student s){
        this.s = s ;
    }
    @Override
    public void run() {
        while(true){
            synchronized (s){
                if(!s.flag){
                    //有数据了,等待先使用这个数据
                    try {
                        s.wait();//wait方法调用,立即释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(s.name.concat(""+s.age));
                //当flag是一个false,没有数据了,通知(唤醒)生产者所在的线程,来产生数据!
                s.flag = false ;
                s.notify();
            }}}}

面试题: sleep和wait方法区别?

1)使用区别:
	sleep()单独使用,里面传入超时时间,
	wait()不能单独用,必须在同步代码块/同步方法中使用,否则,就会出现			java.lang.IllegalMonitorStateException
2)来源不同
	sleep(long time)线程睡眠,来源于Thread类中,wait()来源于Object类中
3)导致线程状态不同
	sleep(long time)参数出入的超时时间,时间到达,线程睡眠接收,线程会继续执行,在休眠过程中,
   	线程处于的状态是TIMED_WAITTING(超时等待)
   	wait() 
   	线程进入等待状态,如果没有锁对象调用notify()或则notifyAll(),线程状态就进入到WAITTING(死死等待)
4)是否主动唤醒
	sleep(long time):时间到达之后,自动唤醒(主动唤醒),而wait(),被动唤醒,需要使用notify()或者notifyAll进行唤醒
5)是否会释放锁
	sleep()方法不会去释放锁,wait()方法调用会立即释放锁

线程池

线程的好处:
	1)提高了线程使用率
	2)大大减少资源开销
	4)可以通过一些参数(7大参数)进行线程池的调优,到达一些效果
弊端:
	维护成本大
	可以通过线程池---->
	Executors工厂类提供很多静态方法
public static ExecutorService newFixedThreadPool(int nThreads):创建固定的可重用的线程数的线程池
	ExecutorService子实现类=--- TreadPoolExecutor完成线程池初始化
如果涉及到定期让线程进行活动(public static ScheduledExecutorService newScheduledThreadPool)

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

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

Lock锁

jdk5以后提供了具体的"锁定操作"
java.util.concurrent.locks.Lock接口---->可重入的互斥锁ReentrantLock
提供一些方法,可以获取锁,可以去释放锁
      void lock() 获取锁       void unlock()释放锁

//使用案例
//对象可以是任意Java类对象,现在jdk提供Lock具体的锁----ReetrantLock子实现类
    private Lock lock = new ReentrantLock() ;
    @Override
    public void run() {
        //模拟一直有票
        while(true){
            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();
            }}}}

设计模式-代理模式-静态代理

静态代理:
	 最大特点:代理角色和真实角色必须实现同一个接口
 	 代理角色:帮助真实角色完成一些事情
     真实角色:专注于自己的事情

public class StaticProxyDemo {
    public static void main(String[] args) {
            //测试真实角色
            //接口多态/创建具体的子类
            //创建WeddingCompany类对象
            You you2 = new You() ;
            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("婚庆公司接收尾款!");
        }
    }
}

线程的生命周期

在这里插入图片描述

Thread类中有内部枚举类:
		enum State{
            NEW,新建状态
            RUNNABLE,运行状态
            BLOCKED,线程阻塞状态
            WAITTING,死死等待,
            TIMED_WAITTING,超时等待
            TERMINATED,线程终止/线程死亡
		}

线程池

在这里插入图片描述

 线程池:
可以通过Executors工厂类(当前类构造方法私有化,对外提供静态方法--->简单工厂模式)提供一些创建线程池的功能
 可以创建固定的可重用的线程数的线程池
 public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
 上面的返回值ExecutorService接口,提供一些方法
  Future<?> submit(Runnable task)提交异步任务
 <T> Future<T> submit(Callable<T> task):提交异步任务
  void shutdown():关闭线程池
 
public class ExecutorsDemo {
    public static void main(String[] args) {
//创建线程池---创建固定的可重用的线程数的线程池
// 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();
    }
}


 自定义类实现Callable:完成异步计算
public class MyCallable  implements Callable<Object> {
    @Override
    public Object call() throws Exception {
        for(int x = 0 ; x < 200 ; x++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
        return null;
    }
}

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

使用线程池的方式完成多个线程的异步计算

需求:使用线程池的方式完成多个线程的异步计算:
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();
    }
}


异步任务
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;
    }
}

线程池七大参数

ThreadPoolExecutor它的构造方法涉及的相关参数

线程池相关参数
1.corePoolSize   
	核心线程数:是指线程池中长期存活的线程数
2.maximumPoolSize
	最大线程数:线程池允许创建的最大线程数量,当线程池的任务队列满了之后,可以创建的最大线程数。
3.keepAliveTime
	空闲线程存活时间,当线程池中没有任务时,会销毁一些线程,销毁的线程数=maximumPoolSize(最大线程数)-corePoolSize(核心线程数)
4.TimeUnit
	时间单位:空闲线程存活时间的描述单位
5.BlockingQueue
	阻塞队列:线程池存放任务的队列,用来存储线程池的所有待执行任务。
它可以设置以下几个值:
    ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
    SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
    PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
    DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
    LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
6.ThreadFactory
	线程工厂:线程池创建线程时调用的工厂方法,通过此方法可以设置线程的优先级、线程命名规则以及线程类型(用户线程还是守护线程)等。
7.RejectedExecutionHandler
	拒绝策略:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略.
默认的拒绝策略有以下 4 种:
    AbortPolicy:拒绝并抛出异常。
    CallerRunsPolicy:使用当前调用的线程来执行此任务。
    DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
    DiscardPolicy:忽略并抛弃当前任务。
    线程池的默认策略是 AbortPolicy 拒绝并抛出异常

异常

异常概念

 程序中会出现异常,开发人员必须解决异常
异常的父类:Throwable
 -->提供一些方法:将异常的信息直接打印在控制上:具体(跟踪堆栈-->底层原码)
        public void printStackTrace()

1、 Error(严重问题)     Exception
 严重问题:内存溢出(借助于其他硬件解决这个问题,加内存条)

2、 Exception:
	1)编译时期异常:只要不是RuntimeException的子类,都属于编译时期异常。必须让开发人员必须处理,不处理,代码编译通过不了
    2)运行时期异常:RuntimeException。大部分逻辑不够严谨,导致出现的问题
 	例如:NullPointerEception:在获取到某个类的对象的时候,并没有非空判断,可能对象是null,使用这个对象调用它里面的方法,出现空指针了

处理异常方案

一、throws:向方法调用者,(向父类抛出异常,最终会抛出给jvm)
二、捕获异常 try...catch
	1、标准格式try...catch...finally
	 try{
           可能出现问题的代码
        }catch(异常类名 对象名){
              处理异常
         }finally{
              //释放资源
          }
    2、变形格式try...catch...
          try{
              可能出现问题的代码
          }catch(异常类名 对象名){
              处理异常
          }
     3、变形格式try...catch...catch...
      try{             
      		可能出现问题的代码
           }catch(异常类名 对象名){
             处理异常
           }catch(异常类名2 对象名2){
            处理异常...
            }


案例:
 //try代码如果出现问题,---执行catch语句---->处理异常---->内存中,jvm会创建异常的对象,new XXException,
        //执行的catch语句代码
        try{
            //可能出现的代码
            int i = 10 ;
            System.out.println(i/0) ;
        }catch(Exception e){ //捕获的异常Exception
            //自己处理
            //System.out.println("除数不能为0");
            e.printStackTrace();
        }

自定义异常

自定义异常: 自定义一个类,继承自Exception/RuntimeException类

//自定义一个类
	public class MyException  extends Exception{
   	 public MyException(){
    }
    //有参构造:message: 异常信息提示
    public  MyException(String message){
        super(message);
    }}


	public class Teacher {
    	public void getSocre(int socre) throws MyException{//向调用者抛出异常
        if(socre <0 || socre>100){
            throw  new MyException("成绩非法!") ;  //产生异常
        }else {
            System.out.println("当前学生成绩是:"+socre) ;
        }}}

	public class ExceptionTest {
方式一://向上抛出异常
	public static void main(String[] args) throws MyException {//向上抛出异常
   		Scanner sc = new Scanner(System.in) ;
        System.out.println("请您输入学生成绩:") ;
        int socre = sc.nextInt() ;
        Teacher t = new Teacher() ;
        t.getSocre(socre) ;
方式二://捕获异常
	 public static void main(String[] args)  {
	try {
            Scanner sc = new Scanner(System.in) ;
            System.out.println("请您输入学生成绩:") ;
            int socre = sc.nextInt() ;
            Teacher t = new Teacher() ;
            t.getSocre(socre) ;
        } catch (MyException e) {
            e.printStackTrace();
        } 

异常的使用的注意事项

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

面试题:throw和throws的区别

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

单例设计模式

单例设计模式---属于创建型设计模式(创建对象)
    概念:始终在内存中有且仅有一个当期类的实例(有一个对象)
单例设计模式:分为饿汉式、懒汉式 
1)饿汉式
构造器私有化,对外不能new
静态实例:Student一加载,就创建当前类实例
对外提供静态的方法,返回值是当前类本身
 	public class Student {
    	private static Student s = new Student() ;
   		private Student(){}
   		 public static Student getStudent(){
        return  s;
        
2)懒汉式
声明静态变量
构造器私有化,对外不能new
对外提供静态方法,返回值是当期类本身
	public class Teacher {
    //声明静态变量,t的类型Teacher
    private  static Teacher t ;
    private Teacher(){}
    //多个线程同时去访问这个方法,
    //线程1,线程2,线程3--导致出现线程安全问题
    /*public static Teacher getInstance(){
        synchronized (Teacher.class){ //静态方法的锁对象:跟类有关系 类名.class
            if(t ==null){
                t = new Teacher() ;
            }
            return  t;
        }
    }*/
    public static synchronized Teacher getInstance(){//变成了静态的同步方法 :锁对象:类名.class
            if(t ==null){
                t = new Teacher() ;
            }
            return  t;

饿汉式:不会出现安全问题的单例设计模式
    1)当前类是具体类
    2)类一加载就创建当前类实例
    3)构造私有化,对外隐藏,不能new实例
    4)对外提供静态方法,返回值当前类本身
懒汉式(可能存在安全问题的一种单例模式)
    1)当前类是个具体类
    2)当前类构造方法私有化
    3)当前类的成员位置:声明当前类的一个静态变量
    4)对外提供静态方法,返回值是当前类本身:需要判断当前变量是否为null

Io流

1)按流的方向划分
	输入流/输出流
2)按流的类型划分:
	字节流
		字节输入流:InputStream
			具体的子类:FileInputStream 
			高效字节输入流:BufferedIntputStream
			System.out----->PrintStream:字节打印流
			提供功能println(换行任何类型的内容)
			print(任何类型的内容)
		字节输出流:OutputStream
			具体的子类:FileOutputStream 
			高效字节输入流:BufferedOutputStream
	字符流
		字符输入流:Reader
			具体的子类:InputStreamReader:字符转换入流
			便捷类:FileReader 
			高效字符输入流:BufferedReader
		字符输出流:Writer
			具体的子类:OutputStreamWriter:字符转换输出流
			便捷类:FileWriter
			高效的字符输出流:BufferedWriter

File

 java.io.File--->表示文件或者文件夹(目录)一种抽象路径形式

Fiel构造方法:
1、File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。
	 File file2 = new File("D:\\demo") ;
     File file3 = new File(file2,"a.txt") ;   
2、 File(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
	 File file4 = new File("D:\\EE_2302\\day26\\code\\a.txt") ;
3、File(String parent, String child)  父路径名字符串和子路径名字符串创建新的 File实例
  	 File file = new File("D:\\demo","a.txt") ;

File的基本功能以及特有功能
1、 public boolean createNewFile() throws IOException:创建文件
		 File file3 = new File("a.txt") ;
		 file3.createNewFile();
2、public boolean mkdir():创建文件夹
		File file = new File("D:\\EE_2302\\day26\\code\\demo") ;
		file.mkdir();
3、public boolean mkdirs():创建文件夹,当父目录不存在,自动创建
创建aaa\bbb\ccc 多级目录(直接指定具体文件夹或者文件,相对路径创建)
		 File file2 = new File("aaa\\bbb\\ccc") ;
         file2.mkdirs();
4、public boolean delete():删除文件夹或则文件路径,如果是文件夹,必须是空目录
5、public boolean exists():判断此路径名表示的文件或者目录是否存在
6、public boolean isDirectory():此抽象路径名表示的文件是否是目录
7、public boolean isFile():是否是文件
8、public boolean isHidden():是否隐藏
9、public boolean canRead():是否可读
10、public boolean canWrite():是否可写
11、public String getName():获取当前File对象所表示的文件夹以及文件的名称
特有功能:高级功能
12、public String[] list():获取指定抽象路径名表示的文件或者目录的名称(返回字符串数组)
//描述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));
        }
13、public File[] listFiles():获取指定路径名表示的文件或者目录的File数组
 	File[] files = myFile.listFiles();
        //防止空指针
        if(files!=null){
            for (File f : files) {
                //获取当前File对象所表示的文件夹以及文件的名称 --public String getName()
                System.out.println(f.getName());
            }
        }

获取以磁盘下以"xxx"结尾的文件

获取D盘下的所有的以".jpg"结尾的文件
  "需要将文件名打印到控制台上"
方式一:
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());
                    }}}}}}

方式二:文件名称过滤器
参数都是接口---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);
            }}}}

字节输入输出

字节输出流

字节输出流
OutputStream---抽象类--不能new,具体的子类:针对文件操作:FileOutputStream
操作步骤:
       1)创建字节输出流对象
       2)写数据
       3)释放资源
 
public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //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());  //str.grtBytes获得字符串的字节数组
        }
        //关闭资源
        fos2.close();
    }
}

字节输入流

 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 class FileInputStreamDemo {
    public static void main(String[] args)  {
           FileInputStream fis = null ;
        try {
            //创建文件字节输入流对象FileInputStream(String name)
             //fis = new FileInputStream("fis.txt") ;
            //读取当前项目下的FileOutputStreamDemo3.java文件,把它内容打印控制台上
             fis = new FileInputStream("FileOutputStreamDemo3.java") ;

            //public abstract int read()throws IOException:一次读取一个字节
            //返回值:读取的实际字节数值
            //文件内容未知---使用while循环
            //循环条件中---赋值,判断,一块使用
            //定义一个字节数:从0开始
            int by = 0 ;
            while((by=fis.read())!=-1){
                //将字节数by---强转char字符
                System.out.print((char)by) ;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 //读取当前项目下的FileOutputStreamDemo3.java
        FileInputStream fis2 = new FileInputStream("FileOutputStreamDemo3.java") ;

使用字节输入流一次读取一个字节数组的方式
 public int read(byte[] b) throws IOException:一次读取一个字节数组,返回:读取的字节数组的长度
 public class FileInputStreamDemo2 {
    public static void main(String[] args) throws IOException {
 	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

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

字节缓冲流

Java之IO流提供了字节缓冲流:让读取的速度更快一些,提供对应的类
字节缓冲输入流/字节缓冲输出流  (高效的字节流)---仅仅是内部提供缓冲区字节数组长度:8192长度,
文件的读写复制还的依赖于基本字节流(InputStream/OutputStream)
BufferedInputStream(InputStream in):字节缓冲输入流
BufferedOutputStream(OutputStream out):字节缓冲输出流

需求:
将当前项目下的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)+"毫秒");
    }


1、使用字节缓冲输入流一次读取一个字节数组
    /**
     * 使用字节缓冲输入流一次读取一个字节数组
     * @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();
    }
2、使用字节缓冲输入流一次读取一个字节
    /**
     * 使用字节缓冲输入流一次读取一个字节
     * @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();
    }
}

字符输入输出

字符输入流-转换流InputStreamReader

字符输入流---->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);
        }
    }
}

字符输出流-转换流OutputStreamWriter

 字符流:
 字符输出流:Writer(抽象类)--->
 具体的子类 OutputStreamWriter:字符转换输出流,"可以将字节输出流---转换---字符输出流"
构造方法:
 public OutputStreamWriter(OutputStream out):使用平台默认字符集进行编码--输出数据
 public OutputStreamWriter(OutputStream out,String charsetName):
使用指定的字符集进行编码---输出数据
写数据:
1、void write(char[] cbuf) :写入字符数组
2、void write(char[] cbuf,int off,int len ) :写入字符数组的一部分            3、void write(int c) :写一个字符
4、void write(String str) :字符串
5、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();
    }
}

字符输入输出流FileReader、FileWriter

public class CopyFileTest {
    public static void main(String[] args) throws IOException {
//创建字符输入流对象---public FileReader(String fileName)
        FileReader fr = new FileReader("D:\\EE_2302\\day27\\code\\DiGuiTest.java");//默认的字符集(解码)
 //创建字符输出流对象---FileWriter(String fileName):直接操作目标文件。
 //FileWriter(String fileName, boolean append)  :直接操作文件,实现追加写入
        FileWriter fw = new FileWriter("copy.java") ; //默认的字符集(编码)
  int by = 0 ;
        while((by=fr.read())!=-1){
            //写一个字符
            fw.write(by) ;
            fw.flush();
        }
        //释放资源
        fw.close();
        fr.close();
    }       

字符缓冲流

字符缓冲输入流
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("copy.java")) ;
     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 {
//创建字符缓冲输出流对象
//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();

字符缓冲输入+输出
将当前项目下的某个.java文件复制到某个磁盘上的路径中

public class Test {
    public static void main(String[] args) throws IOException {
        //封装源文件
        BufferedReader br = new BufferedReader(
                new FileReader("copy.java")) ;
        //封装目的地文件
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("D:\\EE_2302\\day27\\code\\my.java")) ;

        //一次读取一行
        String line = null ;
        while((line=br.readLine())!=null){
            //写一行
            bw.write(line) ;
            bw.newLine();
            bw.flush();
        }
        //释放资源
        bw.close();
        br.close();
    }
}

属性集合列表-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)
 

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


如何读取src下面的xx.properties文件
    private static void myLoad() throws IOException {
        //创建属性集合列表
        Properties prop = new 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);
    }
}

网络图片下载到本地

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

        }
    }
}

键盘录入的方式

键盘录入
 0)main方法里面:早期录入 String[] args
 1)Scanner(InputStream in)---->Scanner sc = new Scanner(System.in) ;
2)new BufferedReader(new InputStreamReader(System.in))
//InputStreamReader(InputStream in)

public class Demo {
    public static void main(String[] args) throws IOException {
//字符缓冲输入流需要--->包装字节输入流
// InputStream inputStream = System.in ;
//需要Reader字符流
//Reader reader = new InputStreamReader(inputStream);
//InputStreamReader(InputStream in)
//创建字符缓冲输入流对象
//  BufferedReader br = new BufferedReader(reader) ;
 //一步走
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请您输入一个数据:");
        String line = br.readLine();
        //数字字符串--->
        int num = Integer.parseInt(line);
        System.out.println("您输入的内容是:"+num);
    }
}

方法递归

方法递归:方法本身调用方法的一种现象
前提条件:
      1)定义一个方法
      2)这个方法的逻辑是规律的
      3)这个方法是有结束条件,否则---成了"死递归"
   注意:构造没有递归
 
需求:
求5的阶乘
              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) {
        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) ;
        }
    }
}

递归应用

 需求:删除某个磁盘上的带内容的目录
       "D:\EE_2302\day27\code\demo"

public class DiGuiTest {
    public static void main(String[] args) {
        //描述磁盘上的路径
        File srcFile = new File("D:\\EE_2302\\day27\\code\\demo") ;
        //定义递归删除带内容的目录的方法
        deleteDirectory(srcFile) ;
    }

    public static void deleteDirectory(File destFile) { //传入的的路径
            //获取目标文件夹 中所有的文件以及文件夹的File数组
        File[] files = destFile.listFiles();
        if(files!=null){
            for(File file:files){
                //获取每一个file对象
                //去判断
                if(file.isDirectory()){
                    //是文件夹
                    //调用递归删除的方法
                    deleteDirectory(file);
                }else {
                    //是文件,删除的同时获取它的父目录的名称
                    //public File getParentFile()
                    //System.out.println(file.getParentFile().getName()+"目录名称:"+"---->"+file.delete());
                    System.out.println(file.getName()+"目录名称:"+"---->"+file.delete());
                }
            }
            System.out.println(destFile.getName()+"----"+destFile.delete());
        }
    }
}

网络编程

网络编程三要素

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

2、端口号:
   范围 0~65535 这里面 0-1024(属于保留端口号)
   自己指定端口号--->1025-65535
  (不要和系统端口号冲突---->Address already in use:BindException:地址被占用)
              mysql端口号:3306
              tomcat:web服务器:8080

3、协议:
UDP/TCP协议区别  (底层网络协议)
UDP协议:
1)属于不可靠连接,不需要建立连接通道(不安全)  ----以"数据报包"的方式进行数据传输的
2)不同步的,执行效率相对TCP协议,效率高
3)发送数据大小有限制的


TCP协议:
1)属于可靠连接,需要建立连接通道   ----以 "流"的方式发送数据
2)同步的,执行效率低,安全性相对UDP协议(安全性能高)
3)发送数据大小无限制

InetAddress

 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地址的主机名

public class InetAddressDemo {
    public static void main(String[] args) throws UnknownHostException {
// public static InetAddress getByName(String host)throws UnknownHostException
        InetAddress inetAddress = InetAddress.getByName("DESKTOP-Q62EUJH");
		 InetAddress inetAddress = InetAddress.getByName("10.35.165.34");
//获取ip地址字符串形式
        String ip = inetAddress.getHostAddress() ;
//获取主机名称
        String name = inetAddress.getHostName();
    }
}

在这里插入图片描述

使用UDP协议发送、接收数据

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



使用UDP协议发送数据
 1)创建发送端的socket对象
 2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
 3)使用发送端的Socket对象发送"数据报包"
 4)释放资源

public class UdpSend {
    public static void main(String[] args) throws Exception {
        //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协议不断发送、接收数据

 UDP协议这种方式:发送端不断键盘录入数据,接收端不断的展示数据(接收不关闭,一直开启)
public class Send {
    public static void main(String[] args) {
//创建发送端的socket对象
        DatagramSocket ds = null ;
        try {
             ds = new DatagramSocket() ;
        //键盘录入数据--->BufferedReader
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(System.in)) ;
            System.out.println("请您输入数据 ") ;
            String line = null ;
            while((line=br.readLine())!=null){
                //数据报包:将数据发送接收端
                DatagramPacket dp = new DatagramPacket(
                        line.getBytes(),
                        line.getBytes().length,
                        InetAddress.getByName("10.35.165.17"),
                        12306) ;
                //发送数据
                ds.send(dp) ;
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ds!=null){
                ds.close();
            }
        }
    }
}



接收端不断的展示数据(接收不关闭,一直开启)
public class Receive {
    public static void main(String[] args) {
        //创建接收端的Socket
        try {
            DatagramSocket ds = new DatagramSocket(12306) ;
            //不断的展示数据
            while(true){
                //接收数据:接收容器
                byte[] buffer = new byte[1024] ;
                DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;
                //接收
                ds.receive(dp) ;

                //解析缓冲区(接收容器)的数据
                String message = new String(dp.getData(),0,dp.getLength()) ;
                //获取ip地址
                String ip = dp.getAddress().getHostAddress() ;
                System.out.println("data from --->"+ip+",content is--->"+message);

            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //接收端不关闭,模拟真实场景
        }
    }
}

线程方式使用UDP

//创建线程
public class ChatRoom {
    public static void main(String[] args) {
        try {
            //创建发送端Socket
            DatagramSocket sendDs = new DatagramSocket() ;
            //创建接收端的Socket
            DatagramSocket receiveDs = new DatagramSocket(10086) ;
            //创建发送端的线程所在的资源类对象
            SendTread st = new SendTread(sendDs) ;
            //创建接收端的线程所在资源类对象
            ReceiveThread rt  =  new ReceiveThread(receiveDs) ;

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

            //启动线程
            t1.start();
            t2.start();
         } catch (SocketException e) {
            e.printStackTrace();
        }
    }
}



接收端的资源类
public class ReceiveThread  implements Runnable{
    private DatagramSocket ds ;
    public ReceiveThread(DatagramSocket ds){
        this.ds = ds ;
    }
    @Override
    public void run() {
        try {
            //不断的展示数据
            while(true){
                //接收数据:接收容器
                byte[] buffer = new byte[1024] ;
                DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;
                //接收
                ds.receive(dp) ;
                //解析缓冲区(接收容器)的数据
                String message = new String(dp.getData(),0,dp.getLength()) ;
                //获取ip地址
                String ip = dp.getAddress().getHostAddress() ;
                System.out.println("data from --->"+ip+",content is--->"+message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //接收端不需要释放资源,一直开启状态
    }
}



发送端的资源类
public class SendTread  implements Runnable{

    private DatagramSocket ds ;
    public SendTread(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){
                //数据报包:将数据发送接收端
                DatagramPacket dp = new DatagramPacket(
                        line.getBytes(),
                        line.getBytes().length,
                        InetAddress.getByName("10.35.165.17"),
                        10086) ;
                //发送数据
                ds.send(dp) ;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ds.close();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值