第五周地狱总结

1.List和和Set集合的区别?

1)是否有序
	List存储和取出一致,有序的
	Set存储和取出不一致,不能保证迭代次序---->默认使用HashSet--->HashMap集合
2)是否重复
	List可以允许元素重复
	Set集合中存储的元素唯一
3)具体实现类的数据结构不同
	List集合--->ArrayList/Vector:底层数据结构:数组
			   LinkedList底层数据结构:链表
	Set集合---->HashSet基于HashMap实例(底层哈希表)
				TreeSet基于TreeMap的实例(底层是一种红黑树(自平衡的二叉树)结构)

2.List集合的遍历方式

//遍历方式
Object[] toArray()
Iterator iterator() 
size()和get(int index)结合普通for循环
ListIterator listiterator() //列表迭代器
//增强for循环    
for(存储的数据类型 遍历名: 集合对象){
		//...
}

3.TreeSet集合存储自定义类型如何实现自然排序?

//TreeSet集合要实现自然排序,使用无参构造方法,而且需要保证TreeSet存储的类型必须实现Compareable接口中的compareTo方法(T t)
TreeSet<Student> ts = new TreeSet<>() ;
//创建一些学生对象....
//添加到集合中


//大部分常用了String,Character,Integer..等等都会实现Compareable接口,完成自然升序排序

class Student implements Compareable<Student>{
    
       //属性私有化
    //setXXX(xx)/getXXX()
    
    public int compareTo(Student s){
        
        //需要按照主要条件排序
        //自己分析次要条件
        return 结果;
    }
}

4.HashSet集合存储自定义类型,如何保证元素唯一

HashSet集合保证自定义类型的元素唯一:
		add方法--->依赖于HashMap的put方法---->putVal()方法
		--->间接依赖于Object的hashCode()和equals方法;
		当前自定义类型必须重写Object的equals和hashCode,保证元素唯一!

5.java.util.Date和String日期文本之间的相互转换

class DateUtils{
    private DateUtils(){
        
    }
    
    public static  String date2String(Date date,String patttern){
        return new SimpleDateFormat(pattern).format(date) ;
    }
    //String--->Date
    public static Date string2Date(String sourceDate,String pattern)throws ParseException{
        return new SimpleDateFormat(pattern).parse(sourceDate) ;
    }
    
}

Map集合 <K, V>两个子实现类

Map<K,V> :一个键对应一个值(键值映射),Map集合针对键必须唯一,值是可以重复的!

将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值
    Map接口和Collection接口的不同
    Map是双列的,Collection是单列的
    Map的键唯一,Collection的子体系Set是唯一的
    Map集合的数据结构值针对键有效,跟值无关
    Collection集合的数据结构是针对元素有效

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集合是否为空

Map集合遍历方式:1)

方式1:根据键找值
     获取所有键的集合
     遍历键的集合,获取到每一个键
     根据键找值
     Set<K> keySet()获取Map集合的所有的键的集合
     V get(Object key):通过键获取值

例子1

package MapBianLi;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) {
        Map<String,String>MP=new HashMap<>();
        MP.put("路飞","橡胶果实");
        MP.put("黑胡子","黑暗果实");
        MP.put("白胡子","震动果实");
        MP.put("艾斯","烧烧果实");
        Set<String> str = MP.keySet();//获取Map集合的所有的键的集合
        //Set<K> keySet()获取Map集合的所有的键的集合
        //  V get(Object key):通过键获取值
        for(String s:str){
            String s2 = MP.get(s);
            System.out.println(s+s2);
        }
    }
}

例子2

package MapBianLi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class Test3 {
    public static void main(String[] args) {
        HashMap<String, ArrayList<String>>hs=new HashMap<>();
        ArrayList<String>as=new ArrayList<>();
        as.add("蒙奇.D.路飞");
        as.add("罗罗诺亚.索隆");
        as.add("文斯莫克.山治");
        hs.put("海贼王",as);

        ArrayList<String>as2=new ArrayList<>();
        as2.add("漩涡鸣人");
        as2.add("宇智波佐助");
        as2.add("春野樱");
        hs.put("火影忍者",as2);

        ArrayList<String>as3=new ArrayList<>();
        as3.add("黑崎一护");
        as3.add("井上织姬");
        as3.add("茶渡泰虎");
        hs.put("死神",as3);

        Set<String> str = hs.keySet();
        for(String s:str){
            System.out.println(s+"\t");
            ArrayList<String> s2 = hs.get(s);
            System.out.println("\t"+s2);
        }
    }
}

Map集合遍历方式:2)

方式2:
     根据键值对对象找键和值
     获取所有键值对对象的集合
     遍历键值对对象的集合,获取到每一个键值对对象
     根据键值对对象找键和值
     Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象
     K getKey()获取键
     V getValue()

例子1

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

        //创建Map集合
        Map<String,String> map  = new HashMap<>() ;

        //添加
        map.put("杨过","小龙女") ;
        map.put("郭靖","黄蓉") ;
        map.put("陈玄风","梅超风") ;
        map.put("令狐冲","任盈盈") ;

        //获取所有的键值对对象
        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

package MapBianLi;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test4 {
    public static void main(String[] args) {
        Map<String,String>ms=new HashMap<>();
        ms.put("蒙奇.D.路飞","橡胶果实");
        ms.put("罗罗诺亚.索隆","剑豪");
        ms.put("文斯莫克.山治","改造人");
        Set<Map.Entry<String, String>> entries = ms.entrySet();
        for(Map.Entry<String, String> mss:entries){
            String key = mss.getKey();
            String value = mss.getValue();
            System.out.println(key+"\t "+value);
        }
    }
}

Map集合和Collection集合的区别?

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

HashMap<K,V>

Key:Student类型  ---学生信息
Value:String类型 ---朝代
    使用HashMap集合保证key(键
    )唯一!
HahMap--->底层哈希表实现,put方法依赖hashCode和equals方法,

Map针对键有效,要针对键的类型自定类型,当前类必须重写equals和hashCode(),保证键唯一(重点)

例子

package HashMapTest;

import java.util.HashMap;
import java.util.Set;

class Student{
    private String name;
    private  int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}
public class Test7 {

        public static void main(String[] args) {
            //创建HashMap集合
            HashMap<Student,String> hm = new HashMap<>() ;
            Student s1 = new Student("唐伯虎",35) ;
            Student s2 = new Student("唐伯虎",35) ;
            Student s3 = new Student("祝枝山",30) ;
            Student s4 = new Student("宋江",54) ;
            Student s5 = new Student("宋江",54) ;
            Student s6 = new Student("高圆圆",44) ;
            Student s7 = new Student("高圆圆",44) ;
            Student s8 = new Student("秋香",32) ;

            //添加元素
            hm.put(s1,"明朝") ;
            hm.put(s2,"明朝") ;
            hm.put(s3,"宋朝") ;
            hm.put(s4,"清朝") ;
            hm.put(s5,"清朝") ;
            hm.put(s6,"现代") ;
            hm.put(s7,"现代") ;
            hm.put(s8,"明朝") ;
            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<K,V>

    存储键值对元素,保证键唯一的同时,还有排序!
使用哪种排序,取决于使用构造方法:
    public TreeMap():自然排序,当前键的类型必须实现Compareable接口
    public TreeMap(Comparator<? super K> comparator):使用比较器排序 (推荐) 直接可以使用接口匿名内部类
需求:

TreeMap<Student,String>

key:Student类型

value:String,代表学生住址信息

主要条件:

按照学生姓名的长度,从小到排序

分析次要条件:长度相同的,不一定姓名内容相同

"gaoyuanyuan"

"zhangsanfen"

例子

package TreeMapDTest;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;

class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

}
public class Test5 {
    public static void main(String[] args) {
        TreeMap<Student,String>ts=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;
            }
        });
        Student n1=new Student("蔡徐坤",43);
        Student n2=new Student("陈立",33);
        Student n3=new Student("陈立",13);
        Student n4=new Student("何超",23);
        Student n5=new Student("何超",33);
        ts.put(n1,"美国");
        ts.put(n2,"中国");
        ts.put(n3,"法国");
        ts.put(n4,"英国");
        ts.put(n5,"日本国");
        Set<Student> stu = ts.keySet();
        for(Student s:stu){
            String s2 = ts.get(s);
            System.out.println(s.getName()+"---"+s.getAge()+"---"+s2);
        }
    }
}

针对集合操作工具类

提供一些常用的方法:
1)public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key):

2)二分搜索在List中查询元素 (不管集合还是Arrays---->binaraySearch方法,(集合或者数组必须有序))

3)public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) :获取Colection中的最大值

4)public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) :获取Colection中的最小值

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

6)public static <T> void sort(List<T> list,Comparator<? super T> c):针对List集合比较器排序

7)public static void shuffle(List<?> list):针对List集合随机置换


7)public static <T> List<T> synchronizedList(List<T> list):将线程不安全ArrayList,变成线程安全的列表

例子

package CollectionTool;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test8 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(23);
        list.add(14);
        list.add(8);
        list.add(46);
        list.add(89);
        list.add(12);
        System.out.println(list);//[23, 14, 8, 46, 89, 12]
        System.out.println("-----------------");
        Integer max = Collections.max(list);
        System.out.println("list集合最大值是: "+max);//list集合最大值是: 89
        System.out.println("-----------------");
        //Integer本身实现的接口就是一个自然排序Compareable
        Collections.sort(list);
        System.out.println(list);//[8, 12, 14, 23, 46, 89]
        System.out.println("-----------------");
        int i = Collections.binarySearch(list, 99);//寻找list集合中是否有99这个元素
        int i1 = Collections.binarySearch(list, 12);//寻找list集合中是否有12这个元素
        System.out.println(i);//-7
        System.out.println(i1);//1
        System.out.println("-----------------");
        //针对List集合随机置换
        Collections.shuffle(list);
        System.out.println(list);


    }
}

斗地主老师代码

package com.qf.collections_07;

import java.util.ArrayList;
import java.util.Collections;

/**
 * 使用集合"模拟斗地主"洗牌,发牌---可以完成看牌的功能!
 *
 *
 *     斗地主---->3个人玩, 54张牌,底牌3张
 *1)创建牌盒   ---没有明确说明什么集合,默认就使用ArrayList---->array
 *2)装牌
 *        点数数组 String[] numbers = {"A","2","3","4",...."J","Q","K"} ;
 *        花色数组 String[] colors = {"♥","♠","♣","♦"} ;
 *2.1)将花色和点数遍历,拼接成对一个 String poker
 *2.2)将poker牌 添加牌盒中,添加"小王","大王"
 *
 *3)洗牌----->Collections工具类提供的:public static void shuffle(List<?> list):针对List集合随机置换
 *4)发牌:
 *      三个玩家----每一个玩家都看成是ArrayList
 *              player1
 *              player2
 *              player3
 *              diPai集合---存3张牌
 *      4.1) 规律:遍历牌盒中的所有牌
 *              for(int x = 0 ; x <array.size() ; x++ ){
 *                  判断如果当前
 *                  if(x >= array.size()-3){
 *                      //diPai.add(array.get(角标x)) ;
 *                  }else if(x % 3 == 0){
 *                      //玩家1的牌
 *                  }else if(x % 3 == 1){
 *                      //玩家2的牌
 *                  }else if(x % 3 == 2){
 *                      //玩家3的牌
 *                  }
 *              }
 *
 *5)定义一个静态方法----看牌的功能
 *      public static void lookPoker(String name,ArrayList<String> array){
 *
 *              //name的牌是:
 *              //遍历这个牌盒
 *              //将牌获取到,输出....
 *      }
 *
 */
public class ArrayListTest {
    public static void main(String[] args) {

        //1)使用ArrayList模拟一个牌盒
        ArrayList<String> array = new ArrayList<>() ;

        //2)装牌
        //定义一个点数数组
        String[] numbers = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"} ;
        //定义一个花色数组
        String[] colors = {"♥","♠","♣","♦"} ;

        //循环遍历
        for(String color:colors){
            //获取花色
            for(String number:numbers){
                String poker = number.concat(color);
                //将poker添加牌盒中
                array.add(poker) ;
            }
        }
        //添加小王和大王
        array.add("小王") ;
        array.add("大王") ;
        //System.out.println(array) ;

        //3)洗牌
        //Collections工具类提供的:public static void shuffle(List<?> list):针对List集合随机置换
        Collections.shuffle(array) ;
        //System.out.println(array) ;

        //4)发牌----3个玩家 ,还创建一个集合:代表底牌
        //每一个玩家---默认ArrayList存储
        ArrayList<String> player1 = new ArrayList<>() ;
        ArrayList<String> player2 = new ArrayList<>() ;
        ArrayList<String> player3 = new ArrayList<>() ;
        ArrayList<String> diPai = new ArrayList<>() ;

        //规律:遍历牌盒中的所有牌
        //判断:如果当前角标>= 牌盒中所有的元素 -3 ---底牌
        for(int x = 0 ; x < array.size() ; x++){
            if(x >= array.size()-3){
                //底牌
                diPai.add(array.get(x)) ;
            }else if(x % 3 == 0){ //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) ;
        lookPoker("李国栋",player2) ;
        lookPoker("赵又廷",player3) ;
        lookPoker("底牌",diPai) ;
    }

    //将看牌定义一个功能
    public static void lookPoker(String name,ArrayList<String> array){
        System.out.print(name+"的牌是:\t") ;
        //遍历每一个人的牌
        for(String poker:array){
            System.out.print(poker+" ");
        }
        System.out.println();

    }
}

线程和进程

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

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

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

创建线程的方式1: Thread类提供方法:

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方法是一个普通方法,

     Thread类提供方法:
           public final String getName()获取线程的名称
            public final void setName(String name):设置线程名称

例子

/**
 * 标记当前类是一个线程类
 */
public class MyThread  extends  Thread{

    //重写run方法


    @Override
    public void run() {
        //两个线程都要进来
        //完成耗时的操作
        //循环


        for(int x = 0 ; x < 200 ; x ++){
            //public final String getName()获取线程的名称
            System.out.println(getName()+":"+x) ;
        }
    }
}

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

        /*
        for(int x = 0 ; x < 1000;x++){
            System.out.println(new Object()+":"+x) ;
        }*/
//        在main()用户线程中,创建当前线程了对象
        MyThread my = new MyThread() ;
        MyThread my2 = new MyThread() ;//第二个线程对象
        my.setName("高圆圆") ;
        my2.setName("李纪奔");
        //启动线程
     /*   my.run() ;
        my.run();*/
        //上面代码:使用线程对象将普通run方法调用两次,并不会出现线程的执行随机性!
        my.start();
        //my.start() ;//IllegalThreadStateException:非法线程状态异常,一个线程只能启动一次
        my2.start();

    }
}

Thread类的sleep方法以及join()方法/setPriority()设置线程优先级的方法,以及线程的创建方式2Runnable接口

   安全     	     不安全     

ArrayList LinkedList
Vector StringBuilder
StringBuffer HashMap
TreeMap HashSet
String TreeSet

1.Collection和Map集合的区别

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

2.HashMap集合的键存储自定义类型,如何保证键唯一

保证键唯一
		需要重写HashCode和equals方法,HashMap底层依赖put方法最终直接关联到Object的hashCode和equals方法;

3.List集s合和Set集合的区别?

1)是否有序
	List是有序,存储和取出一致
	Set是无序的,默认使用HashSet--->HashMap,(存储和取出不一致)
2)元素是否可以重复
	List:元素可以重复
	Set:能够保证元素唯一
3)两个接口对应实现类的底层数据结构的区别
	List
		ArrayList:底层数组结构,查询快,增删慢,线程不安全,单线程中效率高
		Vector   :底层数组结构,查询快,增删慢,线程安全的类,单线程中效率低,多线程中使用居多
		LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全的类,单线程中效率高
	Set:
    	HashSet--->依赖HashMap---底层哈希表结构,保证元素唯一
    	TreeSet--->依赖TreeMap--->自平衡的二叉树结构,保证唯一,而且根据存储的类型,是自然排序/还是比较器排序!(取决于使用的构造函数)

5.什么是进程和线程?

进程:
	是调用"系统资源"一个独立单位!(每一个客户端应用程序,对应一个进程!)
	一个进程:0-n个线程组成
	多进程:是为了提高cpu的使用率!
线程:
	依赖于进程,是程序中最小执行单元(CPU调度的一种最小单位!) 多个线程去同一个资源(程序计数器:pc寄存器)
	多线程:为了让多个线程互相抢占CPU执行,提高线程的利用率!

6.创建线程的方式1的步骤

 //1)自定义一个类,让这个类继承自Thread类  :标记当前是一个线程类
class MyThread extends Thread{
    
    //2)重写Thread类中的run方法
    public void run(){
        //线程中的耗时操作...
        
        for(int x = 0 ; x < 200 ; x++){
            System.out.println(getName()+":"+x) ;
        }
    }
}

class ThreadDemo{
    public static void main(String[] args){
        //3)在main:用户线程中(主线程中) 创建当前这个线程类对象
        MyThread my1 = new MyThread() ;
        MyThread my2 = new MyThread() ;
        
        //4)设置线程名称
        my1.setName("t1线程") ;
        my2.setName("t2线程") ; 
        
        //5)启动线程
        my1.start() ;
        my2.start() ;
    }
}

2.Java中异常的处理 (两种处理方案)

throws和try...catch,实际开发中,在业务层代中service层,使用try...catch捕获异常;

在dao层代码中(数据库访问:jdbc/mybatis框架)----全部使用throws

4.线程的创建第二种 (重点)

	另一种方法来创建一个线程是声明实现类"Runnable接口"。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动;   
	如何使用:上面给的步骤
	
	第二种方式优点?
			能够体现数据共享/用到了设计模式"静态代理"
			Java中23种设计模式(面试中:创建型/结构型)
				创建型:单例/简单工厂/工厂方法...
				结构型:代理设计模式
							静态代理
							动态代理--->跟"反射"有关系
				行为型

异常概念

 程序中会出现异常,开发人员必须解决异常!
  异常的父类: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…

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

        //处理方案:
        //try...catch...
        //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();
        }

        System.out.println("over");

    }

例子throws,throw

public class ExceptionDemo2 {
    public static void main(String[] args) throws IOException,ArrayIndexOutOfBoundsException, Exception { //throws 在方法声明上

        //将可能出现代码包起来---try...catch--- (ctrl+alt+t---->选择第6个)
       /* try {
            int[] arr = {11,22,33} ;

            System.out.println(arr[0]);
            System.out.println(arr[1]);
            System.out.println(arr[3]);//ArrayIndexOutOfBoundsException数组角标越界
        } catch (Exception e) {
            e.printStackTrace();
        }*/

        int[] arr = {11,22,33} ;

        System.out.println(arr[0]);
        System.out.println(arr[1]);
        //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException数组角标越界

        System.out.println("----------------------------------------------");
        Date date = string2Date();
        System.out.println(date);
        System.out.println("-------------------------------");
        divide();
    }

    public static Date string2Date() throws ParseException {
        String source = "2023-03-21" ;
        //将source--->java.util.Date
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
        //当解析的时候,模式的格式和String文本格式不一致会出现问题
        Date date = sdf.parse(source);//编译时期异常 --谁调用这个方法,就必须处理这个方法
        return  date ;
    }
    public static void divide() throws Exception {
        int i = 10 ;
        int b = 0 ;
        if(b==0){
            //如果执行到这个代码中,一定会出异常!
            throw  new Exception("除数不能为0") ;
        }else{
            System.out.println(i/b) ;
        }

    }
}

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

异常的使用的注意事项:

1)子类继承父类的时候,如果子类重写父类的方法,父类的方法如果本身没有throws抛出异常

子类的方法中出现异常,子类只能自己处理,只能try...catch...

2)子类继承父类,子类重写父类方法的时候,如果父类的方法抛出异常,

那么子类的该方法最起码要根父类的方法异常保存一致(要么子类该放的异常是父类方法的异常中的子类)

例子

class Father{
    public void method()  {
        System.out.println("method Father");
    }
    public void show() throws ArrayIndexOutOfBoundsException{
        System.out.println("show father");
    }
}
class Son extends  Father{
    @Override
    public void show() throws ArrayIndexOutOfBoundsException {
        System.out.println("show Son");
    }

    @Override
    public void method()  {
        try {
            //将日期文本---解析java.util.Date
            String dateStr = "2023-05-01" ;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
            Date date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
public class ExceptionDemo {
    public static void main(String[] args) {

    }
}

异常的使用的注意事项:

1)子类继承父类的时候,如果子类重写父类的方法,父类的方法如果本身没有throws抛出异常
     子类的方法中出现异常,子类只能自己处理,只能try...catch...

     2)子类继承父类,子类重写父类方法的时候,如果父类的方法抛出异常,
         那么子类的该方法最起码要根父类的方法异常保存一致(要么子类该放的异常是父类方法的异常中的子类)

例子

class Father{
    public void method()  {
        System.out.println("method Father");
    }
    public void show() throws ArrayIndexOutOfBoundsException{
        System.out.println("show father");
    }
}
class Son extends  Father{
    @Override
    public void show() throws ArrayIndexOutOfBoundsException {
        System.out.println("show Son");
    }

    @Override
    public void method()  {
        try {
            //将日期文本---解析java.util.Date
            String dateStr = "2023-05-01" ;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
            Date date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
public class ExceptionDemo {
    public static void main(String[] args) {

    }
}

线程的Join方法

等待该线程中终止
     public final void join() throws InterruptedException

因为线程的执行具有随机性,哪个线程启动之后,调用join(),正常理想情况,等待该线程执行完毕,执行其他线程!

需求

实现三个线程依次先后执行完毕, t1,t2,t3每一个线程启动之后,都可以调用join()

例子

package Test2;
/*现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,
		T3在T2执行完后执行 (线程里面的join使用)
		T1--T2--T3
		*/
class Demo extends Thread{
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}
public class Test2 {
    public static void main(String[] args) {
        Demo T1=new Demo();
        Demo T2=new Demo();
        Demo T3=new Demo();
        T1.setName("T1");
        T2.setName("T2");
        T3.setName("T3");

        T1.start();
        try {
            T1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        T2.start();
        try {
            T2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        T3.start();
        try {
            T3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

线程的yield方法

public static void yield():线程礼让(暂停当前正在执行的线程,执行其他线程)

package Yield;
class YieldThread extends Thread{
    @Override
    public void run() {
        for(int x=0;x<20;x++){
            System.out.println(getName()+": "+x);
            yield();
        }
    }
}
public class Test5 {
    public static void main(String[] args) {
        YieldThread yt=new YieldThread();
        YieldThread yt2=new YieldThread();
        YieldThread yt3=new YieldThread();

        yt.setName("张三");
        yt2.setName("李四");
        yt3.setName("王五");

        yt.start();
        yt2.start();
        yt3.start();

    }
}

优先级

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执行权的概率一样大)

例子

package PriorityThread;
class Priority extends Thread{
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x++){
            System.out.println(getName()+":"+x);
        }
    }
}

public class PriorityThread {
    public static void main(String[] args) {
        Priority p1=new Priority();
        Priority p2=new Priority();
        Priority p3=new Priority();


        p1.setName("王拉拉");
        p2.setName("王喜喜");
        p3.setName("王露露");

        //设置优先级
        p1.setPriority(10);
        p3.setPriority(1);

        p1.start();
        p2.start();
        p3.start();
    }
}

线程创建方式1

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

     Thread类提供方法:
           public final String getName()获取线程的名称
            public final void setName(String name):设置线程名称

例子

   /**
     * 标记当前类是一个线程类
     */
    public class MyThread  extends  Thread{
    
        //重写run方法
    
    
        @Override
        public void run() {
            //两个线程都要进来
            //完成耗时的操作
            //循环
    
    
            for(int x = 0 ; x < 200 ; x ++){
                //public final String getName()获取线程的名称
                System.out.println(getName()+":"+x) ;
            }
        }
    }
    
    public class ThreadDemo {
        public static void main(String[] args) {
    
            /*
            for(int x = 0 ; x < 1000;x++){
                System.out.println(new Object()+":"+x) ;
            }*/
    //        在main()用户线程中,创建当前线程了对象
            MyThread my = new MyThread() ;
            MyThread my2 = new MyThread() ;//第二个线程对象
            my.setName("高圆圆") ;
            my2.setName("李纪奔");
            //启动线程
         /*   my.run() ;
            my.run();*/
            //上面代码:使用线程对象将普通run方法调用两次,并不会出现线程的执行随机性!
            my.start();
            //my.start() ;//IllegalThreadStateException:非法线程状态异常,一个线程只能启动一次
            my2.start();
    
        }
    }

线程创建方式2

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

例子

package ThreadCreat2;
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 Test8 {
    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() ;

    }
}

方式1和方式2的优劣

方式1的弊端:
     1)继承关系说。继承关系有局限性。继承Thread类,重写了run方法,但是其他的方法也会继承过来,
     2)内存角度看,多个线程导致多个栈指向多个堆
方式2的好处
     1)面向接口编程,直接实现Runnable
   这种方式:静态代理,可以体现数据共享!创建对象的时候都指向同一个资源对象!实现Runnable接口的 类对象

java有23种模式

创建型:单例/简单工厂/工厂方法...
				结构型:代理设计模式
							静态代理
							动态代理--->跟"反射"有关系
				行为型

线程的6种状态

在Thread类中提供了内部枚举:enum State{}

NEW=                 new新建

RUNNABLE=            runnable  运行
  
BLOCKED=             blocked  阻塞

WAITING=             waiting死死等待 

TIMED_WAITING=       timed_waiting 超时等待

TERMINATED=          terminated线程终止/死亡

静态代理

设计模式一种思想(前辈总结出来的),并非一种技术
       代理设计模式属于结构型设计模式一种;
              代理设计模式: 核心思想:让代理角色帮助真实角色完成一件事情!
                     静态代理
                    动态代理:
                           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("婚庆公司接收尾款!");
        }


    }
}

关键字锁(synchronized)

Java的同步机制
       synchronized(锁对象){ //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
           多条语句对共享数据的操作

老师例子

package Synchronized;

class Demo{}
class SellTicket implements Runnable{
private static int ticket=100;
private Object obj=new Object();
private  Demo d=new Demo();
// synchronized(锁对象){ 
    //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
   // 多条语句对共享数据的操作}
    @Override
    public void run() {
        while (true){
            synchronized (obj){//由于锁里的可以是任意java类 所以随便定义一个类就可,里面写上面的 d 也可以
                if(ticket>0){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");

                }
            }
        }
    }
}
public class Test10 {
    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() ;

    }
}

1.如何解决多线程的安全问题

使用Java同步锁---同步代码块
格式:
		//多个线程必须使用同一个锁对象!(任意Java类对象,jdk提供,或者自定义的类)
		synchronized(锁对象){
            将多条语句对共享数据的操作包裹起来;
		}
//Java中线程安全的类Vector集合/StringBuffer还有Collections里面有一些方法都是同步锁		

2.join()和yield()的区别?

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

3.线程的状态有哪些?

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

4.什么是静态代理?

静态代理:
	特点:代理角色和真实角色必须实现同一个接口!
	始终代理的核心思想:让代理角色帮助真实角色完成功能的增强!
	
举例:
	这个角色---CEO (添加权限/删除权限/查询权限/修改权限)
	这个用户是否是CEO---->在进行添加/删除/修改/查询---"权限校验"
				如果有权限--->完成了查询/修改/添加/删除的动作
				对当前哪一天这个人操作的这些事情---->"产生日志"
	
	弊端:
		代理角色对业务功能增强的时候,和"系统监控的代码"不能有效分离!

5.线程创建的方式2的实现方式

//创建线程的方式2使用的就是一种静态代理:
class MyRunnable implements Runnable{
    
    public void run(){
        //线程执行的业务
        
    }
}

class ThreadTest{
    public static void main(String[] args){
        MyRunnable my = new MyRunnable() ;//真实角色
        
        //Thread类  implements Runnable{ //代理角色
        		private Runnable target;
        		private String name ;
      //  public Thread(Runnable target,String name ){
            //init初始化方法---->通过局部变量成员变量赋值
        //}
        //public void run(){
          //  if(target!=null){
                target.run() ;
            //}
        //}	
        
  //  }
        //创建线程类对象
        Thread t1 = new Thread(my,"t1线程") ;
        Thread t2 = new Thread(my,"t2线程") ;
        //启动线程
        t1.start() ;
        t2.start() ;
    }
}

1.解决死锁—>同步中使用 "生产者-消费者"思想

出现死锁原因:
		多个线程加入同步锁之后,线程互相之间持有对方的锁,每一个线程必须让对方释放锁之后,才能去使用同步中的内容,导致线程之间出现互相等待!(访问的资源不是同一个资源)
		  
		  
		  
wait()+notify():这个两个必须在synchronzied使用(同步代码块或者同步方法)  使用"信号灯法"来解决死锁问题

2.面试题: 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()方法调用会立即释放锁

3.线程池

线程的好处:
	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类中定义的

同步方法

如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上

    默认的锁对象this

如果是静态的同步方法,锁对象是 "当前类名.class"--跟"反射"有关系

反射:Java代码经历三个阶段

"SORUCE"  ,"CLASS"(反射中的核心)  "RUNTIME"
    //同步方法:如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上
    //同步方法的锁对象:是this---当前类对象的地址值引用!

例子

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


public class Test_thecher {
    public static void main(String[] args) {
        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() ;
    }
}

同步锁//同步代码块//同步方法

(以下是扩展内容)
    同步代码块
synchronized(obj)
{
    //需要被同步的代码块
}

    同步方法
    同步方法是当方法声明上加synchronized,表示此时只有一个线程进入同步方法。锁住的该类实例化的对象。

死锁

     两个或者两个以上的线程,在执行的过程中,互相持有对方的锁,需要等待对方释放锁,导致出现了互相等待情况

例子

//创建两把锁
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...");
                }
            }
        }

    }
}


//测试类
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();
    }
}

生产者和消费者模式思想

 使用生产者和消费者模式思想,来解决线程死锁问题!
     组成:
              Student类:姓名,年龄两个学数据
             SetTread类:生产资源类,里面产生学生数据
            GetThred类:消费者资源类,里面使用学生数据
            ThreadDemo类:用户线程,等会开启两个生产者所在的线程/消费者所在的线程

常见的问题 和解决方案

   问题1:
         输出null--0,因为生产资源类中和消费资源类中并没有使用同一个资源!
    优化:
         需要不断的在生产资源类中产生学生数据,在消费者资源类中不断地使用学生数据!
   问题2:
         不断的在生产者资源类中产生数据,消费者资源不断使用使用数据,出现数据紊乱!
               姓名和年龄不符! ---多线程环境不安全
         ---需要多线程安全问题
        校验多线程安全问题的标准:
                 1)是否是多线程环境
                 2)是否存在共享数据
                 3)是否有多条语句对共享数据操作
                  使用同步代码块---将3) 包起来

   问题3: 加入同步代码块,解决了线程安全问题,数据不会紊乱,但是数据一打印一大片
     原因:线程的执行,通过cpu一点点时间片,足够线程执行很多次!

例子老师

package Test2;
class Student {
    String name ;  //姓名
    int age     ;  //年龄
    boolean flag ; //false,没有数据,true,有数据!
}

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类对象

                //创建学生对象
                // Student s = new Student() ;
                if(x % 2==0){//%2是因为这样子的话打印出来就是依次两个的。
                    s.name = "高圆圆" ;
                    s.age = 44 ;
                }else {
                    s.name = "李国栋";
                    s.age = 23 ;
                }
            }
            x ++ ;
        }
    }
}
class GetThread  implements Runnable{
    //声明学生变量
    private Student s ;
    public GetThread(Student s){
        this.s = s ;
    }

    @Override
    public void run() {
        while(true){
            synchronized (s){
                //使用学生学生
                //Student s = new Student() ;
                System.out.println(s.name.concat(""+s.age));
            }

        }

    }
}
public class TestMiNi {
    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() ;
    }
}

信号灯机制和等待唤醒机制

 //使用生产者和消费者模式思想,来解决线程死锁问题!
     组成:
             Student类:姓名,年龄两个学数据
             SetTread类:生产资源类,里面产生学生数据
             GetThred类:消费者资源类,里面使用学生数据
            ThreadDemo类:用户线程,等会开启两个生产者所在的线程/消费者所在的线程

     Object notify() 方法用于唤醒一个在此对象监视器上等待的线程
     wait()+notify() 也算Java同步机制--->必须使用锁对象调用方法

例子老师-改进版

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

                //创建学生对象
                // Student s = new Student() ;
                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();
                    }
                }

                //使用学生学生
                //Student s = new Student() ;
                System.out.println(s.name.concat(""+s.age));

                //当flag是一个false,没有数据了,通知(唤醒)生产者所在的线程,来产生数据!
                s.flag = false ;
                s.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() ;
    }
}

Lock–>锁定操作

  jdk5以后提供了具体的"锁定操作"
       java.u til.concurrent.l cks.Lock接口---->可重入的互斥锁            ReentrantLock    
  提供一些方法,可以获取锁,可以去释放锁
          void lock() 获取锁
          void unlock()释放锁
    finally除是一种特例:在执行finally语句之前,jvm退出      System.exit(0) ; ,任何情况下,finally语句一定会执行!//使用完,释放锁

例子

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 资源类
 */
public class SellTicket implements Runnable {
    //100张票
    private  static int tickets = 100 ;

    //之前:锁对象可以是任意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 {
              
                lock.unlock();
            }


        }
    }
}
 *   电影院三个窗口卖票---使用Lock解决多线程安全问题----和synchronized是同样的语义
 */
public class LockDemo {
    public static void main(String[] args) {
        //创建资源类
        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() ;
    }
}

线程池

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

例子老师

import java.util.concurrent.Callable;

/**
 * 自定义类实现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) ;
        }
    }
}
public class ExecutorsDemo {
    public static void main(String[] args) {
        //创建线程池---创建固定的可重用的线程数的线程池
//        public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

   
        MyCallable myCallable = new MyCallable() ;
        threadPool.submit(myCallable) ;
        threadPool.submit(myCallable) ;


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

}

两条线程求1-100和1-200的和

/**
 * 需求:使用线程池的方式完成多个线程的异步计算:
 *        1个线程求1-100的和
 *       另一个线程求1-200的和
 *
 *       1)创建线程池:固定的可重用的线程数的线程池
 *       2)提交异步任务<T> Future<T> submit(Callable<T> task):提交异步任务
 *              返回值:Future接口:代表异步计算的结果
 *                  V get():获取计算的结果
 */
package com.qf.executor_07;

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

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


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

面试题必问–> 线程池5个创建方式以及7大参数

线程池

    线程是稀有资源,系统频繁创建会很大程度上影响服务器的使用效率,如果不加以限制,很容易就会把服务器资源耗尽。
    所以,我们可以通过创建线程池来管理这些线程,提升对线程的使用率
简而言之,线程池就是管理线程的一个容器,有任务需要处理时,会相继判断核心线程数是否还有空闲、线程池中的任务队列是否已满、是否超过线程池大小,然后调用或创建线程或者排队,线程执行完任务后并不会立即被销毁,而是仍然在线程池中等待下一个任务,如果超过存活时间还没有新的任务就会被销毁,通过这样复用线程从而降低开销。

线程池5个创建方式

1.newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的
ThreadFactory 创建新线程。
特征:
(1)线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
(2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟)
(3)当线程池中,没有可用线程,会重新创建一个线程
通过: Executors.newCachedThreadPool();创建线程池

2.newFixedThreadPool

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于
处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。
如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个
线程被显式地关闭之前,池中的线程将一直存在
特征:
(1)线程池中的线程处于一定的量,可以很好的控制线程的并发量
(2)线程可以重复被使用,在显示关闭之前,都将一直存在
(3)超出一定量的线程被提交时候需在队列中等待
创建方式
(1)Executors.newFixedThreadPool(int nThreads);//nThreads为线程的数量
//nThreads为线程的数量,threadFactory创建线程的工厂方式
(2)Executors.newFixedThreadPool(int nThreads,ThreadFactory threadFactory);

3.newSingleThreadExecutor

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现
失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在
任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool不同,可保证无需重新配置此方法所返回
的执行程序即可使用其他的线程。
特征:
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
创建方式:
(1)Executors.newSingleThreadExecutor() ;
// threadFactory创建线程的工厂方式
(2)Executors.newSingleThreadExecutor(ThreadFactory threadFactory);

4.newScheduleThreadPool

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行
特征:
(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动
创建方式:
(1)Executors.newScheduledThreadPool(int corePoolSize);// corePoolSize线程的个数
(2)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);// corePoolSize线程的个数,
threadFactory创建线程的工厂

5.newSingleThreadScheduledExecutor

创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
特征:
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
(2)可定时或者延迟执行线程活动
创建方式:
(1)Executors.newSingleThreadScheduledExecutor() ;
//threadFactory创建线程的工厂
(2)Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

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

1.corePoolSize

核心线程数:
        是指线程池中长期存活的线程数. 

这就好比古代大户人家,会长期雇佣一些“长工”来给他们干活,这些人一般比较稳定,无论这一年的活多活少,这些人都不会被辞退,都是长期生活在大户人家的

2.maximumPoolSize

最大线程数:
    线程池允许创建的最大线程数量,当线程池的任务队列满了之后,可以创建的最大线程数
这是古代大户人家最多可以雇佣的人数,比如某个节日或大户人家有人过寿时,因为活太多,仅靠“长工”是完不成任务,这时就会再招聘一些“短工”一起来干活,这个最大线程数就是“长工”+“短工”的总人数,也就是招聘的人数不能超过 maximumPoolSize。
     最大线程数 maximumPoolSize 的值不能小于核心线程数 corePoolSize,否则在程序运行时会报
IllegalArgumentException 非法参数异常

3.keepAliveTime

空闲线程存活时间,当线程池中没有任务时,会销毁一些线程,
    销毁的线程数=maximumPoolSize(最大线程数)-     corePoolSize(核心线程数
当大户人家比较忙的时候就会雇佣一些“短工”来干活,但等干完活之后,不忙了,就会将这些“短工”辞退掉,而 keepAliveTime 就是用来描述没活之后,短工可以在大户人家待的(最长)时间

4.TimeUnit

时间单位:空闲线程存活时间的描述单位

5.BlockingQueue

阻塞队列:线程池存放任务的队列,用来存储线程池的所有待执行任务。
它可以设置以下几个值:
    ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
    SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
    PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
    DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
    LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

6.ThreadFactory

线程工厂:线程池创建线程时调用的工厂方法,通过此方法可以设置线程的优先级、线程命名规则以及线程类型(用户线
程还是守护线程)等。

7.RejectedExecutionHandler

拒绝策略:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略.
默认的拒绝策略有以下 4 种:
    AbortPolicy:拒绝并抛出异常。
    CallerRunsPolicy:使用当前调用的线程来执行此任务。
    DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
    DiscardPolicy:忽略并抛弃当前任务。
线程池的默认策略是 AbortPolicy 拒绝并抛出异常

解决死锁—>同步中使用 "生产者-消费者"思想

出现死锁原因:
		多个线程加入同步锁之后,线程互相之间持有对方的锁,每一个线程必须让对方释放锁之后,才能去使用同步中的内容,导致线程之间出现互相等待!(访问的资源不是同一个资源)
		  
		  
		  
wait()+notify():这个两个必须在synchronzied使用(同步代码块或者同步方法)  使用"信号灯法"来解决死锁问题

2.面试题: 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()方法调用会立即释放锁

3.线程池

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

单例

单例设计模式---属于创建型设计模式(创建对象)
概念:
     始终在内存中有且仅有一个当前类的实例!(有一个对象)

饿汉式

不会出现安全问题的单例设计模式
        1)当前类是具体类
        2)类一加载就创建当前类实例
        3)构造私有化,对外隐藏,不能new实例
        4)对外提供静态方法,返回值当前类本身

        Java中的类Runtime类:标准的单例(饿汉式),和计算机的运行环境有关系

例子

package SinglePattern;

import java.io.IOException;
//饿汉式
class Student{
    //静态实例:Student一加载,就创建当前类实例
    private static Student s=new Student();
    //对外不能new
    private Student(){}
    //对外提供静态的方法,返回值是当前类本身
    public static Student getStudent(){
        return s;
    }
}
public class Test1 {
    public static void main(String[] args) throws IOException {
        Student s1=Student.getStudent();
        Student s2=Student.getStudent();

        System.out.println(s1==s2);//true

        //创建Runtime实例:获取运行环境
        Runtime runtime = Runtime.getRuntime();
        //availableProcessors()获取cpu处理器的数量
        System.out.println(runtime.availableProcessors());
        //exec(String dos指令)
        System.out.println(runtime.exec("calc"));//打开计算器指令
    }
}

懒汉式

可能存在安全问题的一种单例模式
        1)当前类是个具体类
        2)当前类构造方法私有化
        3)当前类的成员位置:声明当前类的一个静态变量
        4)对外提供静态方法,返回值是当前类本身:需要判断当前变量是否为null
  现实开发业务中:
          存在懒加载(按需加载)
           一个用户有多个账户: 一对多 (从用户维度去看账户) ,查询用户的时候,开启懒加载方式(针对用户用哪个账户查询哪个账户)
           账户的维度去看用户:一个账户从属于某个用户的  一对一

例子

package SinglePattern;
//懒汉式

class  Teacher{
        //声明静态变量,t的类型Teacher
    private static Teacher t;
        //构造方法私有化:
    private Teacher(){}

    public static synchronized Teacher getInstance(){
        if(t==null){
            t=new Teacher();
        }
        return t;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Teacher t1 = Teacher.getInstance();
        Teacher t2 = Teacher.getInstance();

        System.out.println(t2==t1);//tre

    }
}

File

java.io.File--->
     表示文件或者文件夹(目录)一种抽象路径形式
构造方法:
    File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。
    File(String pathname)通过将给定的路径名字符串转换
    File(String parent, String child)  父路径名字符串和子路径名字符串创建新的 File实例

例子

import java.io.File;
import java.io.IOException;

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.prin
            
            tln(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数组

例子老师

import java.io.File;
import java.io.IOException;
import java.util.Arrays;


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));
        }
        System.out.println("----------------------------------------------") ;
        File[] files = myFile.listFiles();
        //防止空指针
        if(files!=null){
            for (File f : files) {
                //获取当前File对象所表示的文件夹以及文件的名称 --public String getName()
                System.out.println(f.getName());
            }
        }

    }
}

经典案例

获取".jpg"结尾的文件

方案1

import java.io.File;
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());
                    }
                }
            }
        }
    }
}

方案2

package IO_File;
import java.io.File;
public class FilenameFilter {
    public static void main(String[] args) {
        File file = new File("D:\\个人东西");
        String[] list = file.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File file1 = new File(dir, name);
                boolean file2 = file1.isFile();
                boolean file3 = file1.getName().endsWith("jpg");
                return file2&&file3;
            }
        });
        if(list!=null){
            for(String list1:list){
                System.out.println(list1);
            }
        }
    }
}

方案3(方案2改良版)

package IO_File;
import java.io.File;
public class FilenameFilter {
    public static void main(String[] args) {
        File file = new File("D:\\个人东西");
        String[] list = file.list(new java.io.FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File file1 = new File(dir, name);
                return file1.isFile() && file1.getName().endsWith(".jpg");
            }
        });
        if(list!=null){
            for(String list1:list){
                System.out.println(list1);
            }
        }
    }
}

IO流

I:Input

O : Output

流的定义:流是指一连串流动的字符。以先进先出方式发送信息的通道。

字节流是 8 位通用字节流,字符流是16位Unicode字符流 

字节流

java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流

字符流

java.io.Writer 字符输出流
java.io.Reader 字符输入流

出写入读

都是抽象类。

1.**文件**(File)

java.io.FileInputStream 

java.io.FileOutputStream 

java.io.FileReader

java.io.FileWriter

2.**转换流**:(将字节流转换成字符流)

java.io.InputStreamReader
java.io.OutputStreamWriter

3.**缓冲流**

java.io.BufferedReader

java.io.BufferedWriter

java.io.BufferedInputStream

java.io.BufferedOutputStream

4.**数据流**

java.io.BufferedOutputStream

java.io.DataOutputStream

5.**标准输出流**

java.io.PrintWriter

java.io.PrintStream

6.**对象专属流**

java.io.ObjectInputStream 

java.io.ObjectOutputStream 

7.**File文件类**

java.io.File 

字节输出流

OutputStream--抽象的 ---
    具体的子类针对文件操作:FileOutputStream
步骤
    1)创建字节输出流对象
    2)写数据
    3)释放资源
    
    基本功能
    //void write(byte[] b):写字节数组
    //void write(byte[] b, int off, int len):写字节数组一部分
    //abstract void write(int b) 写一个字节
    //释放资源:将流对象指向的文件地址的系统资源进行释放
        fos.close() ;

例子

package FileOutputStreamDemo;
import java.io.FileOutputStream;
import java.io.IOException;

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

        FileOutputStream fos = new FileOutputStream("fos.txt");
        //写数据
        //abstract void write(int b) 写一个字节
        fos.write(100);
        //void write(byte[] b):写字节数组
        byte[] bytes = {98,99,100,101,102,103,104} ;
        fos.write(bytes);
        //void write(byte[] b, int off, int len):写字节数组一部分
        fos.write(bytes, 0, 3);

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

字节数追加

字节输出流:
    FileOutputStream实现文件的字节数追加
    public FileOutputStream(String name,
                          boolean append)
                  throws FileNotFoundException
                第二个参数是true:自动追加内容

     IO流写数据,实现换行效果 widows系统 "\r\n"

例子

package FileOutputStreamDemo;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test5 {
    public static void main(String[] args) throws IOException {
        FileOutputStream ttx = new FileOutputStream("ttx.txt");
        ttx.write("hello".getBytes());
        System.out.println(ttx);
        for(int x=0;x<10;x++){
            ttx.write(("world"+x).getBytes());
            ttx.write("\r\n".getBytes());
        }
        ttx.close();
    }
}
效果:
helloworld0
world1
world2
world3
world4
world5
world6
world7
world8
world9

字节输入流

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提供"字符流",解决乱码

老师例子

package com.qf.io_04_fileinputStream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 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:一次读取一个字节
            //返回值:读取的实际字节数
            //第一次读取
            /*int by = fis.read() ;
            System.out.println(by);
            System.out.println((char)by);
            System.out.println("---------------------------------------------") ;
            //第二次读取
            by = fis.read() ;
            System.out.println(by) ;
            System.out.println((char)by);
            System.out.println("---------------------------------------------") ;
            //第三次读取
            by = fis.read();
            System.out.println(by) ;
            System.out.println((char)by);
            System.out.println("---------------------------------------------") ;
            //第四次读取
            by = fis.read();
            System.out.println(by) ;
            System.out.println((char)by);
            System.out.println("---------------------------------------------") ;
            //第五次读取
            by = fis.read();
            System.out.println(by) ; //-1:流对象的内容已经到达末尾
            System.out.println((char)by);*/

            //文件内容未知---使用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();
                }
            }
        }

    }
}

例子1(老办法)

package FileInputStreamDemo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;


public class Test6 {
    public static void main(String[] args) {
        FileInputStream fis=null;
        try {
       fis = new FileInputStream("fos.txt");
            //第一次读取
            int by=fis.read();
            System.out.println(by);
            System.out.println((char) by);
            //第2次读取
            by=fis.read();
            System.out.println(by);
            System.out.println((char) by);
            //第3次读取
            by=fis.read();
            System.out.println(by);
            System.out.println((char) by);
            //第4次读取
            by=fis.read();
            System.out.println(by);
            System.out.println((char) by);
            //第5次读取
            by=fis.read();
            System.out.println(by);
            System.out.println((char) by);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }
结果:
100
d
98
b
99
c
100
d
101
e
fos.txt:dbcdefghbcd(内容是这个  读取这个txt文档)

例子2(改良的)一次输入一个字节

package FileInputStreamDemo;

import java.io.FileInputStream;
import java.io.IOException;


public class Test7 {
    public static void main(String[] args) {
        FileInputStream fis = null ;
        try {
            fis = new FileInputStream("Test6.java") ;
            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();
                }
            }
        }
    }
}

使用字节输入流一次读取一个字节数组的方式:

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

例子(老版本)

package FileInputStreamDemo;

import java.io.FileInputStream;
import java.io.IOException;
//字节流一次读取一个字节数组
public class Test8 {
    public static void main(String[] args) throws IOException {
        FileInputStream fia = new FileInputStream("fia.txt");

        byte[] bytes=new byte[5];
        // public int read(byte[] b)--->返回:读取的字节数组的长度
        int len = fia.read(bytes);
        System.out.println(len);//5
        System.out.println("------");
        //byte数组转成String
        //每一次是从0开始读取实际字节数
        System.out.println
                (new String(bytes,0,len));//hello
        System.out.println("------");
        len=fia.read(bytes) ;
        System.out.println(len);
        System.out.println
                //windows系统换行符号\r\n
                (new String(bytes,0,len));//Wor  "\r\n"占了两个字符
        System.out.println("------");
        len=fia.read(bytes) ;
        System.out.println(len);
        System.out.println     (new String(bytes,0,len));//ld--//J
        System.out.println("------");
        len=fia.read(bytes) ;
        System.out.println(len);
        System.out.println     (new String(bytes,0,len));


    }
}

fia:
Hello
World
Java
输出结果
5
------
Hello
------
5

Wor
------
5
ld
J
------
5
ava

例子(最终效果)

package FileInputStreamDemo;

import java.io.FileInputStream;
import java.io.IOException;


//创建一个字节缓冲区(长度是1024或者1024的整数倍)
public class Test9 {
    public static void main(String[] args) throws IOException {
        FileInputStream fia = new FileInputStream("fia.txt");
        byte[] bytes=new byte[1024];
        //实际字节数从0开始
        int len=0;
        while((len=fia.read(bytes)) !=-1){//如果没有字节内容可读。到达末尾,返回值为-1
            String s = new String(bytes, 0, len);
            System.out.println(s);
            /*Hello
              World
              Java
*/
        }
    }
}
结果:
    Hello
    World
    Java

例子(最终效果)2

package FileInputStreamDemo;

import java.io.FileInputStream;
import java.io.IOException;


//创建一个字节缓冲区(长度是1024或者1024的整数倍)
public class Test9 {
    public static void main(String[] args) throws IOException {
        FileInputStream fia = new FileInputStream("Test6.java");
        byte[] bytes=new byte[1024];
        //实际字节数从0开始
        int len=0;
        while((len=fia.read(bytes)) !=-1){//如果没有字节内容可读。到达末尾,返回值为-1
            String s = new String(bytes, 0, len);
            System.out.println(s);
            /*Hello
              World
              Java
*/
        }
    }
}
Test6内容是一段代码  读取这段代码输出的中文不是乱码
为什么呢 因为这次没有强转char而是读取的实际字节  字符串

Copy例子

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

一次读取一个字节的方式 --代码体现

//读写复制操作
//D:\\a.txt文件---复制到 当前项目下的E:\\b.txt文件中
//创建字节输入流对象--FileInputStream
FileInputStream fis = new FileInputStream("d:\\a.txt") ;
//字节输出流对象
FileOutputStream fos = new FileOutputStream("e:\\b.txt") ;
int by = 0 ;
while((by=fis.read())!=-1){
    //读一个字节,通过fos流对象写出到b.txt文件中
    fos.write(by) ;
}
//释放资源
fos.close() ;
fis.close() ;

一次读取一个字节数组的方式–代码体现

//读写复制操作
//D:\\a.txt文件---复制到 当前项目下的E:\\b.txt文件中
//创建字节输入流对象--FileInputStream
FileInputStream fis = new FileInputStream("d:\\a.txt") ;
//字节输出流对象
FileOutputStream fos = new FileOutputStream("e:\\b.txt") ;
//字节缓冲区
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=fis.read(bytes))!=-1){
    //给fos流对象写入(每次从0开始)实际字节数
    fos.write(bytes,0,len) ;
}
//释放资源
fos.close();
fis.close() ;

同步方法的锁对象和静态同步方法的锁对象?

非静态的同步方法---锁对象---this
静态的同步方法锁对象---锁对象---类名.class

递归例子

package com.qf.digui_01;

import java.io.File;

/**
 * 需求:删除某个磁盘上的带内容的目录
 *      "D:\EE_2302\day27\code\demo"
 *
 *     分析:
 *         1) 描述磁盘上的路径
 *         2)定义一个方法:deleteDirectory(File f):递归删除文件夹的方法
 *         3)方法里面:获取目录中的所有的文件夹以及文件的File数组
 *                 File[] listFiles()
 *                 3.1)File数组不为null
 *                 3.2)遍历File数组,获取到File对象
 *                 3.3)File对象的路径形式是否是"文件夹"
 *                      满足条件---回到2)
 *                       否则,是文件
 *                              删除的同时获取,这个文件的父类名称是什么,然后直接删除--
 */
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());
        }
    }
}

字符流

字符流:
       字符输出流: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();
    }
}

字节缓冲流

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

例子

public class CopyFileTest {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        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();

    }
}

字符输入流

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

键盘录入的第二种方式

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

例子

package JianPanLuRu2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Test3 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请您输入一个数据:");
        String line = br.readLine();
        System.out.println("您输入的内容是:"+line);
        //数字字符串--->
      //  int num = Integer.parseInt(line);//输出数字字符串
      //  System.out.println("您输入的内容是:"+num);//输出数字字符串
    }
}

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)

例子

package BufferredReaderDemo;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Test1 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("新建 文本文档.txt"));
        String line=null;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }
}
新建 文本文档.txt:
dajiahao
woshi 
yisanbao

BufferedWriter字符缓冲输出流

BufferedWriter构造方法
      public BufferedWriter(Writer out):提供默认缓冲区大小的字符缓冲输出流,默认缓冲区足够大 8192长度
   写的功能:
         write(字符/字符数组/字符串...)
         特有功能:
             不需要在使用"\r\n"换行符号, public void newLine()写入行的分隔符

例子

package BufferredWriterDemo;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Test2 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("新建 文本文档.txt"));
              for(int x=0;x<=10;x++){
                  bw.write("哈哈"+x);
                  bw.newLine();//换行
                  bw.flush();//刷新
              }
              bw.close();
    }
}
新建 文本文档.txt:
哈哈0
哈哈1
哈哈2
哈哈3
哈哈4
哈哈5
哈哈6
哈哈7
哈哈8
哈哈9
哈哈10

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

文本文件—使用字符流去操作

字符转换流:不能直接操作文件地址,借助于底层流(字节流),字符转换流提供它的子类 "转换流的便捷类"

InputStreamReader读源文件 ----->FileReader(String pathName)

OutputStreamWriter写目标文件---->FileWriter(String pathName)

例子

public class CopyFileTest {
    public static void main(String[] args) throws IOException {
       
FileReader fr = new FileReader("D:\\EE_2302\\day27\\code\\DiGuiTest.java");
        
FileWriter fw = new FileWriter("copy.java") ;

        int by = 0 ;
        while((by=fr.read())!=-1){
          
            fw.write(by) ;
            fw.flush();
        }
        fw.close();
        fr.close();
    }
}

网络三要素:

1)IP地址:InetAddress 网络中设备的标识,不易记忆,可用主机名 

    A类->第一个号段是网络号段,后面的是主机号段(国家政府机关)
    B类->前面两个号段是网络号段,后面的是主机号段(校园网/公司内网等)
    C类->私人地址,前面的三个都是网络号段,后面是主机号段  "点分十进法"
    
2)端口号 用于标识进程的逻辑地址,不同进程的标识
    范围:0-65535  其中的0-1024是保留端口号,自己指定的端口号是1025-65535(不要和系统的端口号冲突否则报错->Address already in use :BindException:地址值被占用)
 
3)传输协议通讯的规则 常见协议:TCP,UDP 

TCP/UDP 底层网络协议
UDP:
    1)不可靠连接,不需要建立连接通道,以"数据报包"方式进行数据传输,不安全
    2)不同步,执行效率相对TCP来说高
    3)发送数据大小有限制!(字节流形式)
TCP:
    1)可靠连接,需要建立连接通道。
    2)同步的,执行效率低,但是安全性能较UDP高,
    3)发送数据大小无限制(字节流形式)

类InetAddress

对IP地址的获取和操作

获取任意主机:
    getByName() 

主机名:
    getHostName() 

主机Ip地址:
    getHostAddress()

例子

package AdressDemo;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class AdressDemo {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress address=InetAddress.getByName("凤梨罐头");
        String ip = address.getHostAddress();
        System.out.println(ip);//10.35.165.80
        String name = address.getHostName();
        System.out.println(name);//凤梨罐头
    }
}

网络编程(Socket编程)

发送端/客户端

接收端/服务器端

不管UDP还是TCP都要满足以下条件

    两端都必须要有Socket对象(”套接字“);

    发送端或者客户端必须绑定主机IP以及端口

    接收端或者服务器端必须指定端口号

1.使用UDP协议发送数据

    1)创建发送端的socket对象

    2)常见数据报包(DatagramSocket) 包括数据内容 ip  端口名

    3)使用发送端的socket发送数据报包
   
    4)释放资源close

接收端不能开多次 只能开一次 因为端口已经’'被占用了

UDP发送

package com.qf.udp_02;

import java.net.*;

/**
 * 使用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接受

package com.qf.udp_02;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * 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();

    }
}

2.键盘录入版本

UDP发送

package UDPDemo2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class TestSend {
    public static void main(String[] args) {
        DatagramSocket ds=null;
        try {
            ds= new DatagramSocket();
            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.80"), 1234);
                ds.send(dp);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ds!=null){
                ds.close();
            }
        }
    }
}

UDP接受

package UDPDemo2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class TestReceive {
    public static void main(String[] args) {
        try {
            DatagramSocket ds = new DatagramSocket(1234);
            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());
                String IP=dp.getAddress().getHostAddress();
                System.out.println("来自"+IP+"IP的信息:"+message);

            }
        } catch (SocketException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }finally {

        }

    }
}

线程版本写在同一个对话框的

发送端资源类

package com.qf.udp_04;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 发送端的资源类
 */
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();
        }
    }
}

接收端资源类

package com.qf.udp_04;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 接收端的资源类
 */
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();

        }
        //接收端不需要释放资源,一直开启状态
    }
}

测试类

package com.qf.udp_04;

import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * 需要优化:
 *      发送端不断键盘录入数据,接收端不断展示数据,在一个窗口中聊天!
 *  使用多线程的方式:开启两条线程,一条线程:发送端发送数据
 *                          另一条线程:接收端不断接收数据,展示数据
 */
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();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值