JAVA黑马刘意学习笔记

目录

JAVAEE 进阶

一、快捷键

二、逻辑运算符&,|与短路逻辑运算符&&,||

三、修饰符

3.1状态修饰符

3.2权限修饰符

四、多态

五、接口

5.1成员特点:

5.2接口组成更新:

六、内部类

七、常用API

八、类型转换

九、异常处理

十、集合

10.1单列集合 Collection

10.2双列集合Map

十一、泛型:类型化参数

泛型类:

泛型方法:

泛型接口:

可变参数:

十二、Collections:

Collections常用方法:

案例:ArraysList存储学生对象并排序

案例:模拟斗地主

案例:模拟斗地主(排序版)

十三、IO流

13.1File

13.2字节流

13.3字符流

13.4复制文件的异常处理

13.5特殊操作流

十四、多线程

14.1实现多线程

14.2线程同步

14.3生产者消费者

十五、编程入门

15.1网络编程入门

15.2UDP通信程序

15.3TCP通信程序

十六、Lambda表达式

十七、方法引用

17.1方法引用符

17.2引用类方法

17.3引用对象的实例方法

17.4引用类的实例方法

17.5引用构造器


JAVAEE 进阶

JVM:为了使JAVA跨平台运行,使用JVM(JAVA虚拟机)

JRE:JAVA程序运行环境

JDK:JAVA程序开发包

一、快捷键

快速生成main方法:psvm

快速生成输出语句:sout

代码规范化:Ctrl+Alt+L

查看方法:Ctr+Alt+B

创建对象补足语句:Ctrl+Alt+V

二、逻辑运算符&,|与短路逻辑运算符&&,||

a&b——算式a,b都要进行计算

a&&b——只计算算式a。若算式a为假,算式b不执行,整体为假

a||b——判断原理如上。若算式a为真,算式b不执行,整体为真

三、修饰符

3.1状态修饰符

3.1.1final(最终态)

1.修饰方法:表明方法是最终方法,不能被重写

2.修饰变量:表明该变量是常量,不能再次被赋值。再次赋值,不能通过编译,直接报错

3.修饰类:表明该类是最终类,不能被继承,不能被子类重写,但是可以被重载。能够通过子类对象

4.修饰局部变量:变量是基本类型:final修饰的是指基本类型的数据值不能发生改变

变量是引用类型:final修饰的是引用类型的地址值不能发生改变,但地址内容可以发生改变

3.1.2static(静态)

static修饰特点:被类的所有对象共有

非静态成员方法可以访问静态和非静态的成员变量和方法,但静态成员方法只能访问静态的成员变量和成员方法

3.2权限修饰符

同一个类中 同包中子类无关类 不同包子类 不同类无关类
private
默认
protect
public

四、多态

 public class Animal....
 public class Cat extends Animal...
   
 //向上转型
 Animal a= new Cat();
 //Cat向上转型,Animal通过子类Cat创建对象
 a.eat();
 //编译看左边,若Animal类中没有eat成员方法,则报错
 //执行看右边,使用Cat类中的eat成员方法
//向下转型
 Cat c=(Cat) a;
 //a为Animal对象,强制向下转型,转型后c可以使用子类中独有的方法

五、接口

5.1成员特点:

成员变量:只能是常量,默认修饰符public static final

构造方法:接口无构造方法,接口主要是对行为抽象,没有具体存在

成员方法:只能是抽象方法,默认修饰符public abstract

5.2接口组成更新:

默认方法:修饰符 public default,默认方法不是抽象方法,所以不强制被重写,如需重写,重写时去掉default关键字。

静态方法:修饰符 public static,静态方法只能通过接口名调用,不能通过实现类名或者对象调用。

私有方法:Java 9新增了带方法体的私有方法,当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然烤炉将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人用的,因此用私有隐藏起来

注意:

默认方法可以调用私有静态和私有非静态

静态方法只能调用私有的静态方法

六、内部类

访问特点

1.内部类可以直接访问外部类的成员,包括私有

2.外部类要访问内部类的成员,必须创建对象

成员内部类:(公有内部类)Outer.Inner oi = new Outer().new Inner();

局部内部类:在方法中定义的类,在方法内部创建对象并使用,可以访问外部类成员和方法内的局部变量

匿名内部类:本质是一个继承该类或实现了该接口的子类匿名对象

 new Inner(){    //类名或者接口名
 public void show(){ }//重写方法
 };
 ​
 //创建对象,调用方法,通过多态方式上转
 Inner in = new Inner(){
 public void show(){ }
 };
 i.show();

七、常用API

Math类,Arrays类,System类,Object类

工具类设计思想:

1.构造方法用private修饰,防止外界创建对象

2.成员用public static修饰,使用类名访问成员方法

八、类型转换

 int---->String
   int number=100;
 String s=String.valueOf(number);
 String---->int
   //方式一
   String s="100";
 Integer i=Integer.valueOf(s);
 int x=i.intValue(i);//String-->Integer-->int

 //装箱:将基本数据类型转换为包装类类型
 Integer i =Integer.valueOf(100);//通过调用方法将100转换为对应的包装类类型
 Integer ii=100;//自动装箱,上面语句的简写方式。
 ​
 //拆箱:将包装类类型转换为基本数据类型
 ii =ii.intValue()+200;//ii.intValue()将ii转换为int类型,然后再自动装箱
 ii+=200//自动拆箱,装箱

九、异常处理

RunTimeException:运行时异常,编译不报错。除RunTimeException和其子类外,其他的都属于编译异常,必须立即处理,修改程序。

1.try...catch:捕捉异常,程序继续运行

2.throws:抛异常,不立即处理异常,延迟处理,由调用者处理

throws与throw的区别:

throws:用在方法声明后面,跟的是异常类名。表示抛出异常,由方法调用者处理。表示出现异常可能性,并不一定发生。

throw:用在方法体内,跟的是异常对象名。表示抛出异常,由方法体内的语句处理。执行throw一定抛出了某种异常。

十、集合

10.1单列集合 Collection

遍历使用Iterator迭代器

Iterator iterator():返回此集合中的元素的迭代器,通过集合的iterator()方法得到

Iterator it=c.iterator 通过集合对象c获得迭代器对象(多态)

10.1.1可重复:List

1.ArrayList

2.LinkedList 。。。

特点:输入、输出有序;元素可重复

并发修改异常:ConcurrentModificationException

产生原因:在迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素时预期修改值和实际修改值不一致(通过迭代器获取元素,iterator.next(),会进行判断预期修改集合次数与实际修改集合次数是否相等,不同则抛出并发修改异常)

解决方案:通过集合使用for循环方式遍历,get方法获取元素,add方法会对实际修改集合次数进行++,但在get方法中不会对预期修改和实际修改进行判断

 //Iterator迭代器遍历
 List list = new ArrayList();
 Iterator iterator = list.Iterator();
 while(iterator.hasNext()){
 String s = iterator.next();//ConcurrentModificationException
   //源码中next方法会判断预期修改集合次数与实际修改集合次数是否相等
 if(s.equals("world")){
 list.add("javaEE");
     //源码中调用add方法后会对实际修改集合次数进行++,导致预期修改值和实际修改值不一致
 }
 }
 ​
 //解决方案
 List list1 = new ArrayList();
 Iterator iterator1 = list.Iterator();
 for(int i=0,i 
  

ListIterator:列表迭代器

1.通过List集合的ListIterator()方法得到,是list集合特有的迭代器

2.允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的位置

增强for循环:简化数组和Collection集合的遍历方式

1.实现Iterator接口的类允许其对象成为增强型for语句目标

2.内部原理为Iterator迭代器

 List list = new ArrayList();
 Iterator iterator = list.Iterator();
 //增强for循环遍历,最方便的遍历方式
 for(String s:list){
 System.out.println(s); 
 }
 //Iterator迭代器遍历,集合特有方式
 while(iterator.hasNext()){
 String s = iterator.next();
 System.out.println(s); 
 }
 //for循环遍历,索引方式遍历
 for(int i=0,i 
  

数据结构

常见数据结构:

栈:先进后出的模型

队列:先进先出的模型

数组:查询数据通过索引定位,查询任意数组耗时相同;删除数据时,被删除数据后每个数据前移,效率低。添加数据时,被添加数据位置后每个数据后移,效率低。

链表:一个结点,有数据域和地址域,地址域指向下一个结点的地址域。增删快,查询慢,每次查询都从头结点开始

10.1.2不可重复:set

集合特点:

1.不包含重复元素;

2.没有带索引的方法,不能使用普通for循环遍历!

根据元素的哈希值进行判断,保证元素唯一性,没有重复元素

10.1.2.1HashSet:

特点:

1.底层数据结构是哈希表(元素为链表的数组);

2.不保证存储、取出元素顺序一致,对集合迭代顺序不做保证;

3.没有索引,不能使用普通for循环遍历;

4.没有重复元素。

HashSet集合存储元素,要保证元素的唯一性,需要重写hashCode()和equals()方法

哈希值:根据对象的地址或者字符串或数字算出来的int类型数组;Object类中有一个方法hashCode()可以获取哈希值

哈希表:将哈希值作为数组下标,把下标对应的位置作为键值对的存储位置,通过该方法建立的数组叫做哈希表

对象哈希值特点:1.同一对象多次调用hashCode()方法返回哈希值相同;2.默认情况下,不同对象哈希值不同。通过方法重写,可以让不同对象哈希值相同

HashMap保证元素唯一性:

调用对象的hashCode()方法获取对象的哈希值,根据对象的哈希值计算对象的存储位置,如果该位置已经有元素存在,先遍历该位置所有元素,和新存入的元素比较哈希值是否相同,若不相同,则存储元素到该位置,若相同则调用equals()方法比较内容是否相同,若相同则元素重复,不存储。若不同存储元素到该位置

10.1.2.2LinkedHashSet

特点:1.底层数据结构是哈希表和链表;2.具有可预测的迭代顺序,由链表保证元素有序,说明元素存入,取出顺序一致;3.由哈希表保证元素唯一,无重复元素。

10.1.2.3TreeSet

特点:1.元素有序,按照一定规则排序,具体排序方法看构造方法:无参构造,TreeSet(),元素按照自然排序进行排序;有参构造,TreeSet(Comparator comparator),指定比较器排序。2.没有索引,不能使用普通for循环遍历;3.没有重复元素。

自然排序Comparable的使用

 //Comparator排序问题理解
 TreeSet treeset = new TreeSet(new Comparator(){
   public int compara(Student s1,Student s2){//s1为当前需要存入的元素,s2为之前存入的存入的元素
   int num=s1.getAge()-s2.getAge(); 
     //升序,若s1大于s2,num为正,则将s1顺序存入,不颠倒顺序;若s2大于s1,,num为负,则颠倒s1和s2的顺序。
     int num2 =s2.getAge()-s1.getAge();
     //降序,若s2大于s1,num2为正,将s1顺序存入,不颠倒顺序;若s1大于s2,num2为负,则颠倒s1和s2的顺序。
     return num;//升序
     //return num2;降序
  }
 });

10.2双列集合Map

  1. interface Map

  2. 将键映射到值的对象,不包含重复的键

  3. 每个键最多可以映射到一个值

  4. 创建Map对象,多态,具体实现类HashMap

10.2.1Map常用方法:

put,remove,containsKey,containsValue,isEmpty,size.........

 public class Map_Demo {
     public static void main(String[] args) {
         Map map =new HashMap<>();
 ​
         map.put("001","linqingxia");
         map.put("002","zhangmanyu");
         map.put("003","wangzuxian");
 //       map.put("003","liuyan");//有重复的键,用值覆盖
 ​
         System.out.println(map.remove("001"));//返回被删除键对应的值
 ​
 //       map.clear();
 ​
         System.out.println(map.containsKey("002"));//true
         System.out.println(map.containsKey("004"));//false
         System.out.println(map.containsValue("zhangmanyu"));//true
 ​
         System.out.println(map.isEmpty());
 ​
         System.out.println(map.size());
         System.out.println(map);
    }
 }

获取功能:

  1. V get (Object key) 根据键获取值

  2. Set keySet() 获取所有键的集合

  3. Collection values() 获取所有值的集合

  4. Set> entrySet() 获取所有键值对对象的集合

两种遍历方式

 qpublic class MapBianLi_Demo1 {
     public static void main(String[] args) {
         Map map =new HashMap<>();
 ​
         map.put("001","linqingxia");
         map.put("002","zhangmanyu");
         map.put("003","wangzuxian");
         //遍历方式一:Set  keySet()获取所有键的集合
         /*
         获取所有键的集合,用keySet()方法实现
         遍历键的集合,获取到每一个键。用增强for实现
         根据键去找值,用get(Object key)方法实现
         */
         Set keySet = map.keySet();
         for (String s:keySet){
             String value = map.get(s);
             System.out.println(s+","+value);
        }
         //遍历方式二:Set> entrySet()获取所有键值对对象的集合
         /*
         Set> entrySet()获取所有键值对对象的集合
         遍历键值对对象的集合,获取到每一个键值对对象。用增强for实现,得到每一个Map.Entry
         根据键值对对象获取键和值
         getKey()得到键
         getValue()得到值
         */
         Set> entrySet = map.entrySet();
         for (Map.Entry me :entrySet){
             System.out.println(me.getKey()+","+me.getValue());
        }
    }
 }

10.2.2HashMap

特点:底层数据结构为数组+链表,数组为HashMap的主体,链表则主要是为了解决哈希冲突而存在的

 public class HashMap_Demo2 {
     public static void main(String[] args) {
         HashMap hashMap = new HashMap<>();
         Student s1 =new Student("林青霞",30);
         Student s2 =new Student("张曼玉",35);
         Student s3 =new Student("王祖贤",33);
         Student s4 =new Student("王祖贤",33);
         hashMap.put(s1,"成都");
         hashMap.put(s2,"河北");
         hashMap.put(s3,"陕西");
         hashMap.put(s4,"北京");
 ​
         Set keySet = hashMap.keySet();
         for (Student key:keySet){
             String value = hashMap.get(key);
             System.out.println(key.getName()+","+key.getAge()+","+value);
        }
         System.out.println("==========================");
         Set> entrySet = hashMap.entrySet();
         for (Map.Entry me:entrySet){
             Student student=me.getKey();
             String value=me.getValue();
             System.out.println(student.getName()+","+student.getAge()+","+value);
 ​
        }
    }
 }

集合嵌套

案例:ArraysList嵌套HashMap

  • 需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历

  • 思路:

    • 1.创建一个ArrayList集合;

    • 2.创建HashMap集合,并添加键值对;

    • 3.将HashMap作为元素添加到ArrayList集合

    • 4.遍历ArrayList集合

 public class ArraysList_HashMap {
     /*
     * 需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历
     * 思路:1.创建一个ArrayList集合;
     *     2.创建HashMap集合,并添加键值对;
     *     3.将HashMap作为元素添加到ArrayList集合
     *     4.遍历ArrayList集合
     * */
 ​
     public static void main(String[] args) {
         ArrayList> arrays =new ArrayList<>();
 ​
         HashMap hm1=new HashMap<>();
         hm1.put("孙策","大乔");
         hm1.put("周瑜","小乔");
         arrays.add(hm1);
         HashMap hm2=new HashMap<>();
         hm2.put("郭靖","黄蓉");
         hm2.put("杨过","小龙女");
         arrays.add(hm2);
         HashMap hm3=new HashMap<>();
         hm3.put("令狐冲","任盈盈");
         hm3.put("林平之","岳灵珊");
         arrays.add(hm3);
 ​
         for (HashMap hm:arrays){
             Set keySet = hm.keySet();
             for (String key : keySet){
                 String value=hm.get(key);
                 System.out.println(key+","+value);
            }
        }
    }
 }

案例:HashMap嵌套ArrayList

  • 需求:创建一个HashMap集合,存储三个键值对元素,每一个键值对元素的键都是String,值是ArrayList

  • 思路:

    • 1.创建一个HashMap集合;

    • 2.创建ArrayList集合,并添加元素;

    • 3.将ArrayList作为元素添加到HashMap集合

    • 4.遍历HashMap集合

 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 ​
 public class HashMap_ArrayList {
     /*
      * 需求:创建一个HashMap集合,存储三个键值对元素,每一个键值对元素的键都是String,值是ArrayList
      * 思路:1.创建一个HashMap集合;
      *     2.创建ArrayList集合,并添加元素;
      *     3.将ArrayList作为元素添加到HashMap集合
      *     4.遍历HashMap集合
      * */
     public static void main(String[] args) {
         HashMap> hashMap=new HashMap<>();
 ​
         ArrayList arrayList1=new ArrayList<>();
         arrayList1.add("诸葛亮");
         arrayList1.add("赵云");
         hashMap.put("三国演义",arrayList1);
 ​
         ArrayList arrayList2=new ArrayList<>();
         arrayList2.add("唐僧");
         arrayList2.add("孙悟空");
         hashMap.put("西游记",arrayList2);
 ​
         ArrayList arrayList3=new ArrayList<>();
         arrayList3.add("武松");
         arrayList3.add("鲁智深");
         hashMap.put("水浒传",arrayList3);
 ​
         Set>> entrySet = hashMap.entrySet();
         for (Map.Entry> m:entrySet){
             String key= m.getKey();
             ArrayList arr =m.getValue();
             System.out.println(key+":");
             for (String s:arr){
                 System.out.println("\t"+s);
            }
        }
         System.out.println("=========================");
         Set set = hashMap.keySet();
         for (String s1:set){
             ArrayList arr=hashMap.get(s1);
             System.out.println(s1+":");
             for (String s2:arr){
                 System.out.println("\t"+s2);
            }
        }
    }
 }

案例:统计字符串中每个字符出现的次数

  • 需求:键盘录入一个字符串,要求统计字符串中每个字符的出现次数

    • 输入:hello

    • 输出:h(1)e(1)l(2)o(1)

  • 思路:

    • 1.键盘录入一个字符串

    • 2.创建HashMap集合,键是Character,值是Integer

    • 3.遍历字符串,得到每一个字符

    • 4.拿到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值

 import java.util.Scanner;
 import java.util.Set;
 import java.util.TreeMap;
 /*
 *   需求:键盘录入一个字符串,要求统计字符串中每个字符的出现次数
 *       输入:hello
 *       输出:h(1)e(1)l(2)o(1)
 *   思路:
 *       1.键盘录入一个字符串
 *       2.创建HashMap集合,键是Character,值是Integer
 *       3.遍历字符串,得到每一个字符
 *       4.拿到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值
 * 
 * */
 public class CountWord_Demo {
     public static void main(String[] args) {
         Scanner sc =new Scanner(System.in);
         System.out.println("请输入一串字符");
         String s=sc.next();
 ​
 //       HashMap hm =new HashMap<>();//字符输出无序
         TreeMap hm =new TreeMap<>();//字符输出有序
         for (int i=0;ichar
             System.out.println(key);
             Integer value = hm.get(key);
             if (value==null){
                 hm.put(key,1);
            }else{
                 value++;
                 hm.put(key,value);
            }
        }
         
 //       StringBuilder sb =new StringBuilder();
 //       Set keySet = treeMap.keySet();
 //       for (Character key:keySet){
 //           Integer value=treeMap.get(key);
 //           sb.append(key).append("(").append(value).append(")");
 //
 //       }
 //       String result =sb.toString();
 //       System.out.println(result);
         
         Set keySet = hm.keySet();
         for (Character key:keySet){
             Integer value=hm.get(key);
             System.out.print(key+"("+value+")");
        }
    }
 }

十一、泛型:类型化参数

将类型由原来的具体的类型参数化,在使用和调用的时候传入具体类型

泛型类:

public class A{ }

泛型方法:

public void B(T t){ }

泛型接口:

public interface C{ }

public class Cimple implement C{ }

可变参数:

public static int sum(int...a){ }

修饰符 返回类型 方法名(数据类型...变量名){ }

注意:其中a为数组,如果参数有多个参数包含可变参数,可变参数应放在最后

十二、Collections:

针对集合操作的工具类;

其中所有方法均为静态;

Collections常用方法:

public static > void sort(List list) :将指定的列表按升序排序

public static void reverse(List list) :反转指定列表中元素的顺序

public static void shuffle(List list) :使用默认的随机源随机排序指定的列表

 import java.util.*;
 ​
 public class Collections_Demo {
     public static void main(String[] args) {
         List list = new ArrayList<>();
         list.add(20);
         list.add(50);
         list.add(30);
         list.add(70);
         list.add(60);
         list.add(40);
 //       Collections.sort(list);
 //       System.out.println(list);
         System.out.println("================");
 //       Collections.reverse(list);
 //       System.out.println(list);
         System.out.println("================");
         Collections.shuffle(list);
         System.out.println(list);
    }
 }

案例:ArraysList存储学生对象并排序

  • 需求:

    • ArraysList存储学生对象,使用Collections对ArrayList排序

    • 要求:按照年龄从小到大排序,年龄相同时,按照名字首字母排序

  • 思路:

    • 1.定义学生类

    • 2.创建ArrayList集合对象

    • 3.创建学生对象

    • 4.把学生添加到集合

    • 5.使用Collections对集合排序

    • 6.遍历集合

 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 ​
 /*
 *   需求:
 *       ArraysList存储学生对象,使用Collections对ArrayList排序
 *       要求:按照年龄从小到大排序,年龄相同时,按照名字首字母排序
 *   思路:
 *       1.定义学生类
 *       2.创建ArrayList集合对象
 *       3.创建学生对象
 *       4.把学生添加到集合
 *       5.使用Collections对集合排序
 *       6.遍历集合
 * */
 public class ArrayList_Student {
     public static void main(String[] args) {
         ArrayList arrayList=new ArrayList<>();
 ​
         Student s1 =new Student("linqingxia",30);
         Student s2 =new Student("zhangmanyu",35);
         Student s3 =new Student("wangzuxian",33);
         Student s4 =new Student("liuyan",33);
         arrayList.add(s1);
         arrayList.add(s2);
         arrayList.add(s3);
         arrayList.add(s4);
 ​
         Collections.sort(arrayList, new Comparator() {
             @Override
             public int compare(Student o1, Student o2) {
                 int num1 = o1.getAge()-o2.getAge();
                 int num2=num1==0?o1.getName().compareTo(o2.getName()):num1;
                 return num2;
            }
        });
 ​
         for (Student student:arrayList){
             System.out.println(student.getName()+","+student.getAge());
        }
    }
 }
 ​

案例:模拟斗地主

  • 需求:通过程序实现斗地主过程中的洗牌,发牌和看牌

  • 思路:

    • 1.创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现

    • 2.往牌盒里装牌

    • 3.洗牌,把牌打散,用Collections的shuffle方法实现

    • 4.发牌,遍历集合,给三个玩家发牌

    • 5.看牌,三个玩家分别遍历自己的牌

/*
*   需求:通过程序实现斗地主过程中的洗牌,发牌和看牌
*   思路:
*       1.创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现
*       2.往牌盒里装牌
*       3.洗牌,把牌打散,用Collections的shuffle方法实现
*       4.发牌,遍历集合,给三个玩家发牌
*       5.看牌,三个玩家分别遍历自己的牌
*
* */
public class DouDiZhu_Demo {
    public static void main(String[] args) {
        ArrayList array=new ArrayList<>();
        /*
        * ♦2,3...♦K,♦A
        * ♣2,3...♣K,♣A
        * ♥2,3...♥K,♥A
        * ♠2,3...♠K,♠A
        * 大王,小王
        * */
        //定义花色数组
        String[] colors={"♦","♣","♥","♠"};
        String[] numbers={"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
        for (String color:colors){
            for (String number:numbers){
                array.add(color+number);
            }
        }
        array.add("小王");
        array.add("大王");

        Collections.shuffle(array);

        ArrayList player1=new ArrayList<>();
        ArrayList player2=new ArrayList<>();
        ArrayList player3=new ArrayList<>();
        ArrayList dpArray=new ArrayList<>();

        for (int i=0;i< array.size();i++){
            String poker = array.get(i);
            if (i>=array.size()-3){
                dpArray.add(poker);
            }else if(i%3==0){
                player1.add(poker);
            }else if(i%3==1){
                player2.add(poker);
            }else if(i%3==2) {
                player3.add(poker);
            }
        }
        lookPoker("玩家一",player1);
        lookPoker("玩家二",player2);
        lookPoker("玩家三",player3);
        lookPoker("底牌",dpArray);

    }
//    看牌的方法
    public static void lookPoker(String name,ArrayList arrayList){
        System.out.println(name+"的牌"+":");
        for (String poker:arrayList){
            System.out.print(poker+" ,");
        }
        System.out.println();
    }
}

案例:模拟斗地主(排序版)

  • 需求:通过程序实现斗地主过程中的洗牌,发牌和看牌,要求对牌进行排序

  • 思路:

    • 1.创建HashMap集合,键是编号,值是牌

    • 2.创建ArrayList集合,存储编号

    • 3.创建花色数组和点数数组

    • 4.从0开始往HashMap中存储编号,并存储对应的值,同时往ArrayList里面存储编号

    • 5.洗牌(洗的是编号),用Collections的shffle方法实现

    • 6.发牌(发的是编号),为了保证编号有序,使用TreeSet集合接收

    • 7.定义方法看牌,遍历TreeSet集合,获取编号,到HashMap中找对应的值

    • 8.调用看牌方法

/*
 *   需求:通过程序实现斗地主过程中的洗牌,发牌和看牌,要求对牌进行排序
 *   思路:
 *       1.创建HashMap集合,键是编号,值是牌
 *       2.创建ArrayList集合,存储编号
 *       3.创建花色数组和点数数组
 *       4.从0开始往HashMap中存储编号,并存储对应的值,同时网ArrayList里面存储编号
 *       5.洗牌(洗的是编号),用Collections的shffle方法实现
 *       6.发牌(发的是编号),为了保证编号有序,使用TreeSet集合接收
 *       7.定义方法看牌,遍历TreeSet集合,获取编号,到HashMap中找对应的值
 *       8.调用看牌方法
 * */
public class DouDiZhu_SuperDemo {
    public static void main(String[] args) {
        HashMap hm = new HashMap<>();
        ArrayList array = new ArrayList<>();
        /*
         * ♦2,3...♦K,♦A
         * ♣2,3...♣K,♣A
         * ♥2,3...♥K,♥A
         * ♠2,3...♠K,♠A
         * 大王,小王
         * */
        //定义花色数组
        String[] colors = {"♦", "♣", "♥", "♠"};
        String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
        int index = 0;
        for (String number : numbers) {
            for (String color : colors) {
                hm.put(index, color + number);
                array.add(index);
                index++;
            }
        }
        hm.put(index, "小王");
        array.add(index);
        index++;
        hm.put(index, "大王");
        array.add(index);

        Collections.shuffle(array);
        TreeSet player1 = new TreeSet<>();
        TreeSet player2 = new TreeSet<>();
        TreeSet player3 = new TreeSet<>();
        TreeSet dpTree = new TreeSet<>();

        for (int i = 0; i < array.size(); i++) {
            int pokerNum = array.get(i);
            if (i >= array.size() - 3) {
                dpTree.add(pokerNum);
            } else if (i % 3 == 0) {
                player1.add(pokerNum);
            } else if (i % 3 == 1) {
                player2.add(pokerNum);
            } else if (i % 3 == 2) {
                player3.add(pokerNum);
            }
        }
        lookPoker("玩家一", player1, hm);
        lookPoker("玩家二", player2, hm);
        lookPoker("玩家三", player3, hm);
        lookPoker("底牌", dpTree, hm);

    }

    //    看牌的方法
    public static void lookPoker(String name, TreeSet treeSet, HashMap hm) {
        System.out.println(name + "的牌" + ":");
        for (Integer pokerNum : treeSet) {
            String poker = hm.get(pokerNum);
            System.out.print(poker + " ");
        }
        System.out.println();
    }
}

十三、IO流

13.1File

文件和目录路径名的抽象表示

  1. 文件和目录是可以 通过File封装的

  2. 对与File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已,它是可以存在的,也可以是不存在的,将来是要通过具体的操作把这个路径的内容转换为具体存在。

    public class File_Demo {
        public static void main(String[] args) {
            
            File f1=new File("D:\\data1\\java.txt");
            System.out.println(f1);
            
            File f2=new File("D:\\data1","java.txt");
            System.out.println(f2);
            
            File f3=new File("D:\\data1");
            File f4=new File(f3,"java.txt");
            System.out.println(f4);
        }
    }

13.1.1创建功能:

  1. pulic boolean createNewFile():当具有该名称的文件不存在时,创建一个由该抽象路径命名的新空文件。文件已存在,返回false。

  2. public boolean mkdir():创建由此抽象路径名命名的目录

  3. public boolean mkdirs():创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录

13.1.2判断获取功能

  1. public boolean isDirectory():测试此抽象路径名表示的File是否为目录

  2. public boolean isFile():测试此抽象路径名表示的File是否为文件

  3. public boolean exists():测试此抽象路径名表示的File是否存在

  4. public String getAbsolutePath0():返回此抽象路径名的绝对路径名字符串

  5. public String getPath():将此抽象路径名转换为路径名字符串

  6. public String getName() :返回由此抽象路径名表示的文件或目录的名称

  7. public String[] list():返回此抽象路径名表示的目录中的文件和目录的名称字符串数组

  8. public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组

13.1.3递归

递归解决问题思路:

把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

递归策略只需要少量的程序就可描述出解题过程所需要的多次重复计算

递归解决问题要找到两个内容:

  • 递归出口:否则会出现内存溢出

  • 递归规则:与原问题相似的规模较小的问题

public class DiGui_Demo {
    public static void main(String[] args) {
        //每个月的兔子对数,1,1,2,3,5,8,13.。。。。
        //首先定义一个方法发f(n),表示第n个月的兔子对数
        //那么第n—1个月,f(n-1)
        int[] arr = new int[20];
        arr[0]=1;
        arr[1]=1;
        for (int i=2;i< arr.length;i++){
            arr[i]=arr[i-1]+arr[i-2];
        }
        System.out.println(arr[19]);
        System.out.println(f(20));
    }
    public static int f(int n){
        if (n==1||n==2){
            return 1;
        } else {
            return f(n-1)+f(n-2);
        }
    }
}
public class DiGui_Demo {
        public static void main(String[] args) {
            //求5的阶乘
            int n=5;
            int result=jc(5);
            System.out.println("阶乘是:"+result);
        }
        public static int jc(int n){
            if(n==1){
                return 1;
            }else{
                return n*jc(n-1);
            }
        }
}

13.1.4遍历目录

需求:给定一个路径,请通过递归遍历该目录下的所有内容,并把所有文件的绝对路径输出在控制台

思路:

  1. 根据给定的路径创建一个File对象

  2. 定义一个方法,用于获取给定目录下的所有内容,参数为第一步创建的File对象

  3. 获取给定的File目录下所有文件或目录的File数组

  4. 遍历该File数组,得到每一个File对象

  5. 判断该File对象是否是目录

    是:递归调用

    否:获取绝对路径输出在控制台

  6. 调用方法

public class MuLu_Demo {
    public static void main(String[] args) {
        File srcfile=new File("E:\\aixuexi");
        getAllFilePath(srcfile);

    }
    public static void getAllFilePath(File srcfile){
        File[] fileArray=srcfile.listFiles();
        if (fileArray!=null){
            for (File file:fileArray){
                if (file.isDirectory()){
                    getAllFilePath(file);
                }else {
                    System.out.println(file.getAbsoluteFile());
                }
            }
        }
    }
}

13.2字节流

IO:输入/输出

流:抽象概念,对数据传输的总称,流的本质是数据传输

IO流根据数据流向分:输入流:读数据; 输出流:写数据

IO流根据数据类型分:字节流:字节输入流,字节输出流 字符流:字符输入流,字符输出流

(由于字节流操作中文不是特别方便,所以java提供字符流)

字符流=字节流+编码表

一个汉字存储
 * GBK编码:占用两个字节      中国——>[-42,-48,-71,-6]
  	 * UTF-8编码:占用3个字节        中国——>[-28,-72,-83,-27,-101,-67]
    	* 汉字在存储的时候,第一个字符都是负数
FileOutputStream fos=new FileOutputStream("idea_test\\src\\fos.txt",true);
//追加写入数据,在文件末尾写入内容
public class FileOutPutStream_Demo01 {
    public static void main(String[] args) throws IOException {
        //创建输出流对象
        FileOutputStream fos=new FileOutputStream("idea_test\\src\\fos.txt");
//      做了三件事:A:调用系统功能创建文件  B:创建了字节流输出对象    C:让字节流输出对象指向创建好的文件
        fos.write(97);  //a
//        fos.write(57);  //9字符
//        fos.write(55);  //7字符
        //最后都要释放资源!
        fos.close();
//      做了两件事:A:关闭此文件输出流 B:并释放与此流相关的任何系统资源
    }
}

13.2.1字节流写数据加异常处理

finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源。

try{
//可能出现异常的代码
}catch(异常类名 变量名){
//异常处理
}finally{
//执行所有的清除操作
}

13.2.2案例:复制文本文件

需求:把”E:\IDEA\idea_test\src\fos.txt"复制到模块目录下的“qyp.txt”

思路:

  1. 根据数据源创建字节输入流对象

  2. 根据目的地创建字节输出流对象

  3. 读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)

  4. 释放资源

public class Copy_Demo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("idea_test\\src\\fos.txt");
        FileOutputStream fos=new FileOutputStream("idea_test\\src\\qyp.txt");
        
        int by;
        while ((by=fis.read())!=-1){
            fos.write(by);
        }
        fis.close();
        fos.close();
    }
}

13.2.3案例:复制图片

  • 需求:把”E:\qyp.JPG“复制到模块目录下的”mn.jpg“

  • 思路:

    • 1.根据数据源创建字节输入流

    • 2.根据目的地创建字节输出流

    • 3.读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)

    • 4.释放资源

public class Copy_Demo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("E:\\qyp.jpg");
        FileOutputStream fos=new FileOutputStream("idea_test\\src\\mn.jpg");
        
		//一次读取一个字节数组,一次写入一个字节数组
        byte[] bys=new byte[1024];
        int len;
        while ((len=fis.read(bys))!=-1){
            fos.write(bys);
        }
        fis.close();
        fos.close();


    }
}

13.2.4字节缓冲流

BufferedOutputStream:该类实现缓冲输出流。通过设置 这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用

BufferedInputStream:创建BufferedInputStream将创建一 个内部缓冲区数组。当从流中读取或跳过字节时,内部缓 冲区将根据需要从所包含的输入流中重新填充,一次很多字节

/*
* 需求:把”E:\47.avi“复制到模块目录下
* 思路:
 *   1.根据数据源创建字节输入流
 *   2.根据目的地创建字节输出流
 *   3.读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组
 *   4.释放资源
 * 四种实现方式复制视频,并记录每种方式复制视频的时间
 *  1.基本字节流一次读写一个字节     共耗时:11343毫秒
 *  2.基本字节流一次读取一个字节数组       共耗时:20毫秒
 *  3.字节缓冲流一次读取一个字节      共耗时:61毫秒
 *  4,字节缓冲流一次读取一个字节数组       共耗时:7毫秒
*/
public class Copy_Demo {
    public static void main(String[] args) throws IOException {
        long startTime=System.currentTimeMillis();
//        method1();//共耗时:11343毫秒
//        method2();//共耗时:20毫秒
//        method3();//共耗时:61毫秒
//        method4();//共耗时:7毫秒
        long stopTime=System.currentTimeMillis();
        System.out.println("共耗时:"+(stopTime-startTime)+"毫秒");
    }
    public static void method1() throws IOException {
        FileInputStream fis=new FileInputStream("E:\\47.avi");
        FileOutputStream fos=new FileOutputStream("idea_test\\src\\47.avi");

        int by;
        while ((by=fis.read())!=-1){
            fos.write(by);
        }
        fos.close();
        fis.close();
    }

    public static void method2() throws IOException {
        FileInputStream fis=new FileInputStream("E:\\47.avi");
        FileOutputStream fos=new FileOutputStream("idea_test\\src\\47.avi");

        byte[] by=new byte[1024];
        int len;
        while ((len=fis.read(by))!=-1){
            fos.write(by,0,len);
        }
        fos.close();
        fis.close();
    }

    public static void method3() throws IOException {
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("E:\\47.avi"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("idea_test\\src\\47.avi"));

        int by;
        while ((by=bis.read())!=-1){
            bos.write(by);
        }
        bos.close();
        bis.close();
    }

    public static void method4() throws IOException {
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("E:\\47.avi"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("idea_test\\src\\47.avi"));

        byte[] bys=new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1){
            bos.write(bys,0,len);
        }
        bos.close();
        bis.close();
    }
}

13.2.5字符流中的编码解码问题

字符流抽象基类:

  1. Reader:字符输出流的抽象类

  2. Writer:字符输出流的抽象类

字符流中和编码解码问题相关的两个类

  1. InputSreamReader

  2. OutputStreamWriter

flush():刷新流,还可以继续写数据

close():关闭流,释放资源,但是在关闭之前会先刷新流,关闭后不能再写数据

13.2.6案例:复制Java文件

需求:把”E:\IDEA\idea_test\Copy_Demo.java"复制到模块目录下的“copy.java”

思路:

  • 根据数据源创建字符输入流对象

  • 根据目的地创建字符输出流对象

  • 读写数据,复制文件

  • 释放资源

public class Copy_Demo {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr=new InputStreamReader(new FileInputStream("idea_test\\Copy_Demo.java"));
        //解决编码问题使用 InputStreamReader和 OutputStreamReader
        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("idea_test\\copy.java"));

//         int ch;
//         while ((ch=isr.read())!=-1){
//             osw.write(ch);
//         }

         char[] chs=new char[1024];
         int len;
         while ((len=isr.read(chs))!=-1){
             osw.write(chs,0,len);
         }
        osw.close();
        isr.close();
    }
}

//改进版,使用InputStreamReader和 OutputStreamWriter的子类FileReader和FileWriter
public class Copy_Demo {
    public static void main(String[] args) throws IOException {
		FileReader fr=new FileReader("idea_test\\Copy_Demo.java");
        FileWriter fw=new FileWriter("idea_test\\abc.java");

//        int ch;
//        while ((ch=fr.read())!=-1){
//            fw.write((char)ch);
//        }

        char[] chs =new char[1024];
        int len;
        while ((len=fr.read(chs))!=-1){
            fw.write(chs,0,len);
        }
        fw.close();
        fr.close();
    }
}

13.2.7字符缓冲流

BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符、数组和字符的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大。

BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符、数组和行的高效读取,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大。

BufferedReader br=new BufferedReader(new FileReader("idea_test\\bw.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("idea_test\\bw.txt"));

需求:把”E:\IDEA\idea_test\Copy_Demo.java"复制到模块目录下的“Buffer.java”

思路:

  • 根据数据源创建字符缓冲输入流对象

  • 根据目的地创建字符缓冲输出流对象

  • 读写数据,复制文件

  • 释放资源

public class Copy_Demo {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("idea_test\\Copy_Demo.java"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("idea_test\\abc.java"));

        String line;
        while ((line=br.readLine())!=null){		//readLine读数据不读换行符
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        bw.close();
        br.close();
    }
}

13.2.8集合_文件

1.集合到文件

需求:把ArrayList集合中的字符串数据写入到文本文件,要求:每个字符串元作为文件中的一行数据

思路:

  • 创建ArrayList集合

  • 往集合里存储字符串元素

  • 创建字符缓冲输出流对象

  • 遍历集合,得到每一个字符串数据

  • 调用字符缓冲输出流的方法写数据

public class ArrayList_wenjian {
    public static void main(String[] args) throws IOException {
        ArrayList arrayList=new ArrayList<>();
        arrayList.add("hello");
        arrayList.add("world");
        arrayList.add("java");
        BufferedWriter  bw=new BufferedWriter(new FileWriter("idea_test\\array.txt"));
        for (String s:arrayList){
            bw.write(s);
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

2.文件到集合

需求:把文本文件中的数据读取到集合中,并遍历集合,要求:文件的每一行数据是一个集合元素

思路:

  • 创建字符缓冲输入流对象

  • 创建ArrayList集合

  • 调用字符缓冲输入流的方法读数据

  • 把读取到的字符串元素数据存储到集合中

  • 释放资源

  • 遍历集合

public class ArrayList_wenjian {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("idea_test\\array.txt"));
        ArrayList arrayList=new ArrayList();
        String line;
        while ((line=br.readLine())!=null){
            arrayList.add(line);
        }
        br.close();
        for (String s:arrayList){
            System.out.println(s);
        }
    }
}

3.集合到文件(改进版)

需求:把ArrayList集合中的字符串数据写入到文本文件,要求:每个字符串元作为文件中的一行数据

格式:学号,姓名,年龄,居住地

思路:

  • 定义学生类

  • 创建ArrayList集合

  • 创建学生对象

  • 把学生对象存储到集合中去

  • 创建字符缓冲输出流对象

  • 遍历集合,得到每一个学生对象

  • 把学生对象的数据拼接成指定格式的字符串

  • 调用字符缓冲输出流对象的方法写数据

  • 释放资源

ArrayList arrayList=new ArrayList<>();
        Student s1=new Student("001","邱艳萍",20,"成都");
        Student s2=new Student("002","徐千千",21,"河北");
        Student s3=new Student("003","武旭琴",20,"山西");
        Student s4=new Student("004","唐小茹",21,"成都");
        Student s5=new Student("005","李丝萌",19,"河北");
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        arrayList.add(s4);
        arrayList.add(s5);

        BufferedWriter bw=new BufferedWriter(new FileWriter("idea_test\\array.txt"));
        for (Student student:arrayList){
            System.out.println(student.getNumber()+","+student.getName()+","+student.getAge()+","+student.getAddress());
            StringBuilder sb=new StringBuilder();
            sb.append(student.getNumber()).append(",").append(student.getName()).append(",").append(student.getAge())
                    .append(",").append(student.getAddress());
//            bw.write(student.getNumber()+","+ student.getName()+","+student.getAge()+","+student.getAddress());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        bw.close();

4.文件到集合(改进版)

需求:把文本文件中的数据读取到集合中,要求:文件的每一行数据是一个学生对象的成员变量值 格式:学号,姓名,年龄,居住地 思路:

  • 定义学生类

  • 创建ArrayList集合

  • 创建字符缓冲输入流对象

  • 调用字符缓冲输入流对象的方法读数据

  • 把读取到的数据按”,“分隔,得到字符串数组

  • 创建学生对象

  • 把字符串数组中的每一个元素取出来赋值给学生对象的成员变量值

  • 把学生对象添加到集合

  • 释放资源

  • 遍历集合

public class ArrayList_wenjian {
    public static void main(String[] args) throws IOException {
需求:把文本文件中的数据读取到集合中,要求:文件的每一行数据是一个学生对象的成员变量值
         格式:学号,姓名,年龄,居住地
思路:
- 定义学生类
- 创建ArrayList集合
- 创建字符缓冲输入流对象
- 调用字符缓冲输入流对象的方法读数据
- 把读取到的数据按”,“分隔,得到字符串数组
- 创建学生对象
- 把字符串数组中的每一个元素取出来赋值给学生对象的成员变量值
- 把学生对象添加到集合
- 释放资源
- 遍历集合
*/
        ArrayList arrayList = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader("idea_test\\array.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            String[] arr = line.split(",");
            Student s = new Student();
            s.setNumber(arr[0]);
            s.setName(arr[1]);
            s.setAge(Integer.parseInt(arr[2]));
            s.setAddress(arr[3]);
            arrayList.add(s);
        }
        br.close();
        for (Student student : arrayList) {
            System.out.println(student.getNumber() + "," + student.getName() + "," + student.getAge() + "," + student.getAddress());
        }

    }
}

5.集合到文件(排序版)

需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),要求:按照成绩总分从高到低写入文本文件

格式:姓名,语文成绩,数学成绩,英语成绩

思路:

  • 定义学生类

  • 创建TreeSet集合

  • 键盘录入学生数据

  • 创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量

  • 把学生对象添加到TreeSet集合

  • 创建字符缓冲输出流对象

  • 遍历集合,得到每一个学生对象

  • 把学生对象的数据拼接成指定格式的字符串

  • 调用字符缓冲输出流对象的方法写数据

  • 释放资源

public class JiHe_wenjian {
    public static void main(String[] args) throws IOException {
/*需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),要求:按照成绩总分从高到低写入文本文件
格式:姓名,语文成绩,数学成绩,英语成绩
思路:
- 定义学生类
        - 创建TreeSet集合
        - 键盘录入学生数据
        - 创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量
        - 把学生对象添加到TreeSet集合
        - 创建字符缓冲输出流对象
        - 遍历集合,得到每一个学生对象
        - 把学生对象的数据拼接成指定格式的字符串
        - 调用字符缓冲输出流对象的方法写数据
        - 释放资源
*/
        TreeSet treeSet=new TreeSet<>(new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                int num = o2.getSum() - o1.getSum();
                int num2=num==0?o1.getChinese()- o2.getChinese():num;
                int num3=num2==0?o1.getMath()-o2.getMath():num2;
                int num4=num3==0?o1.getName().compareTo(o2.getName()):num3;
                return num4;
            }
        });
        for (int i=0;i<5;i++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入第"+i+"学生信息");
            System.out.println("姓名:");
            String name = sc.next();
            System.out.println("语文成绩:");
            int chinese = sc.nextInt();
            System.out.println("数学成绩:");
            int math = sc.nextInt();
            System.out.println("英语成绩:");
            int english = sc.nextInt();
            Person person=new Person(name,chinese,math,english);
            treeSet.add(person);
        }
        BufferedWriter bw=new BufferedWriter(new FileWriter("idea_test\\array.txt"));
        for (Person person:treeSet){
            StringBuilder sb=new StringBuilder();
            sb.append(person.getName()).append(",").append(person.getChinese()).append(",")
                    .append(person.getMath()).append(",").append(person.getEnglish()).append(",").append(person.getSum());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
            System.out.println(person.getName()+","+person.getChinese()+","+person.getMath()
            +","+person.getEnglish()+","+person.getSum());
        }
        bw.close();
    }
}

13.2.9案例:点名器

需求:我有一个文件里面存储了班级同学的姓名,每个姓名占一行,要求通过程序实现随机点名器

思路:

  • 创建字符缓冲输入流

  • 创建ArrayList集合对象

  • 调用字符缓冲输入流对象的方法读数据

  • 读到的字符串数据存储到集合中

  • 释放资源

  • 使用Random产生一个随机数,随机数的范围在:[0,集合的长度)

  • 把第六步产生的随机数作为索引到ArrayList集合中获取值

public class DianMIng_Demo {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("idea_test\\DianMingCe.txt"));
        ArrayList arrayList=new ArrayList<>();
        String line;
        while ((line=br.readLine())!=null){
            arrayList.add(line);
        }
        br.close();

        Random r=new Random();
        int index = r.nextInt(arrayList.size());
        String name= arrayList.get(index);
        System.out.println("幸运者是:"+name);

    }
}

13.2.10案例:复制单级文件夹

需求:把“E:\IDEA_learn"这个文件夹复制到模块目录下 思路:

  • 创建数据源,路径是”E:\IDEA_learn“

  • 获取数据源目录File对象的名称(itcast)

  • 创建目的地目录File对象,路径名是模块名+itcast组成(idea_test\itcast)

  • 判断目的地目录对应的File是否存在,如果不存在,就创建

  • 获取数据源目录下所有文件的File数组

  • 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件

  • 获取数据源文件File对象的名称

  • 创建目的地文件File对象,路径名是目的地目录+mn.jpg组成(idea_test\itcast\qyp.jpg)

  • 复制文件,由于文件不仅仅是文本文件,还有图片、视频等文件,所以采用字节流复制文件

public class Copy_Folder {
    public static void main(String[] args) throws IOException {
        File srcFolder=new File("E:\\IDEA_learn");
        String srcFolderName = srcFolder.getName();
        File destFolder=new File("idea_test",srcFolderName);
        if (!destFolder.exists()){
            destFolder.mkdir();
        }
        File[] srcFolders = srcFolder.listFiles();
        for (File srcfile:srcFolders){
            File destFile=new File(destFolder,srcfile.getName());
            copyFile(srcfile,destFile);
        }
    }
    private static void copyFile(File srcFolder,File destFolder) throws IOException{
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFolder));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFolder));
        byte[] bys=new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1){
            bos.write(bys,0,len);
        }
        bos.close();
        bis.close();
    }
}

13.2.11案例:复制多级文件夹

需求:把把“E:\IDEA_learn"这个文件夹复制到F盘下 思路:

  • 创建数据源File对象,路径是E:\IDEA_learn

  • 创建目的地Fileduix,路径是F:\

  • 写方法实现文件夹的复制,参数为数据源File对象和目的地File对象

  • 判断数据源File是否是目录

      • 在目的地创建和数据源File名称一样的目录

      • 获取数据源File下所有文件或者目录的File数组

      • 遍历该File数组,得到每一个File对象

      • 把该File作为数据源File对象,递归调用复制文件夹的方法

    • 不是

      • 说明是文件,直接复制,用字节流

public class Copy_Folders {
    public static void main(String[] args) throws IOException {
        File srcFolder =new File("E:\\IDEA_learn");
        File destFolder=new File("F:\\");
        copyFolder(srcFolder,destFolder);
    }

    private static void copyFolder(File srcFile, File destFile) throws IOException {
        if (srcFile.isDirectory()){
            String srcFileName=srcFile.getName();
            File newFolder=new File(destFile,srcFileName);
            if (!newFolder.exists()){
                newFolder.mkdir();
            }
            File[] arr=srcFile.listFiles();
            for (File file:arr){
                copyFolder(file,newFolder);
            }
        }else {
            File newFile=new File(destFile,srcFile.getName());
            copyFiles(srcFile,newFile);
        }
    }

    private static void copyFiles(File srcFolder,File destFolder)throws IOException{
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFolder));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFolder));
        byte[] bys=new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1){
            bos.write(bys,0,len);
        }
        bos.close();
        bis.close();
    }
}

13.3字符流

13.4复制文件的异常处理

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

    }

    //JDK9改进方案
    private static void method4() throws IOException {
        FileReader fr = new FileReader("fr.txt");
        FileWriter fw = new FileWriter("fw.txt");
        try (fr; fw) {
            char[] chs = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chs, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
//自动释放资源
    }

    //JDK7改进方案
    private static void method3() {
        try (FileReader fr = new FileReader("fr.txt");
             FileWriter fw = new FileWriter("fw.txt");) {

            char[] chs = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chs, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
//        fw.close();
//        fr.close();
//        自动释放资源
    }

    //    try catch finally处理异常
    private static void method2() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("fr.txt");
            fw = new FileWriter("fw.txt");
            char[] chs = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chs, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (fr != null) {
                    try {
                        fr.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    //    抛出处理
    private static void method1() throws IOException {
        FileReader fr = new FileReader("fr.txt");
        FileWriter fw = new FileWriter("fw.txt");
        char[] chs = new char[1024];
        int len;
        while ((len = fr.read()) != -1) {
            fw.write(chs, 0, len);
        }
        fw.close();
        fr.close();
    }
}

13.5特殊操作流

13.5.1标准输入输出流

System类中有两个静态的成员变量:

  • public static final InputStream in:标准输入流,通常该流对应键盘输入或由主机环境或用户指定的另一个输入源

    • BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
      System.out.println("请输入一个字符串:");
      String line =br.readLine();
      System.out.println("请输入一个整数:");
      int i=Integer.parseInt(br.readLine());
    • 等价于

      Scanner sc=new Scanner(System.in);
  • public static final PrintStream out:标准输出流,通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标

    • PrintStream ps=System.out;
    • 等价于System.out.println(); 本质是一个字节输出流

      System.out.println(100);

13.5.2打印流

字节打印流:PrintStream

java.lang.Object 继承者 java.io.OutputStream 继承者 java.io.FilterOutputStream 继承者 java.io.PrintStream

  • PrintStream(String fileName)使用指定文件名创建新的打印流

  • 使用继承父类的方法写数据write,查看时会转码;使用自己的特有方法写数据print、println,查看数据原样输出

字符打印流:PrintWriter

java.lang.Object 继承者 java.io.Writer 继承者 java.io.PrintWriter

  • PrintWriter(String fileName) 使用指定文件名创建新的打印流,而不需要自动执行刷新

打印流特点:

  • 只负责输出数据,不负责读取数据

  • 有自己的特有方法

13.5.3对象序列化流

对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象

这种机制就是使用一个字节序列表示一个对象,该字节包括:对象的类型、对象的数据和对象中存储的属性等信息,字节序列写到文件之后,相当于文件中持久保存了一个对象的信息

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化

对象序列化流——ObjectOutputStream

对象反序列化流——ObjectInputStream

13.5.3.1对象序列化流

对象序列化流——ObjectOutputStream

  • 将Java对象的原始数据类型和图形写入OutputStream,可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。

构造方法

  • ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream

序列化对象的方法

  • void writeObject(Object obj) :将指定的对象写入 ObjectOutputStream。

注意:

  • 一个对象要想被序列化,该对象所属的类必须实现Serializable接口

  • Serializable是一个标记接口,实现该接口,不需要重写任何方法

public class ObjectOutPutStream_Demo {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("idea_test\\src\\System_InOut\\oos.txt"));
        Student student=new Student("林青霞",30);
        oos.writeObject(student);
        oos.close();
    }
}

13.5.3.2对象反序列化流

对象反序列化流——ObjectOutputStream

  • ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

构造方法

  • ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream

序列化对象的方法

  • Object readObject() 从 ObjectInputStream 读取对象

public class ObjectInputStream_Demo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("idea_test\\src\\System_InOut\\oos.txt"));
        Object obj=ois.readObject();
        Student student=(Student) obj;//向下转型
        System.out.println(student.getName()+","+student.getAge());
        ois.close();
    }
}

13.5.3.3问题

用对象序列化流序列化了一个对象,假如修改了对象所属的类文件,读取数据会不会出现问题?

  • java.io.InvalidClassException ​ System_InOut.Student; local class incompatible: ​ stream classdesc serialVersionUID = -5552501312078000042, ​ local class serialVersionUID = -239222344963212228 ​ 当 Serialization 运行时检测到某个类具有以下问题之一时,抛出此异常。 ​ 该类的序列版本号与从流中读取的类描述符的版本号不匹配 ​ 该类包含未知数据类型 ​ 该类没有可访问的无参数构造方法

如果出现问题,如何解决?

  • 强烈建议 所有可序列化类都显式声明 serialVersionUID 值, 原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性, 根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。 因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。 还强烈建议使用 private 修饰符显示声明 serialVersionUID ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果一个对象中的某个成员变量的值不想被序列化,该如何实现?

  • 使用transient关键字修饰成员变量

13.5.4Properties

java.lang.Object 继承者 java.util.Dictionary 继承者 java.util.Hashtable 继承者 java.util.Properties

13.5.4.1Properties是一个Map体系的集合类

public class Properties_Demo {
    public static void main(String[] args) {
        public class Propertiesextends Hashtable
//        Properties prop=new Properties();
//        prop.put(001,"qyp");
//        prop.put(002,"txr");
//        prop.put(003,"lsm");
//        Set keySet= prop.keySet();
//        for (Object key:keySet){
//            Object value=prop.get(key);
//            System.out.println(key+","+value);
//        }

        Properties prop=new Properties();
//        Object setProperty(String key, String value)调用 Hashtable 的方法 put。
        prop.setProperty("001","qyp");
        prop.setProperty("002","txr");
        prop.setProperty("003","lsm");
        System.out.println(prop.getProperty("001"));
        System.out.println(prop);
        System.out.println("=========================");
        Set propertyNames = prop.stringPropertyNames();
        System.out.println(propertyNames);
        for (String key:propertyNames){
            String value=prop.getProperty(key);
            System.out.println(key+","+value);
        }
    }
} 
  

13.5.4.2Properties可以保存到流中或从流中加载

  • public void load(Reader reader) throws IOException按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

  • public void store(Writer writer,String comments) throws IOException以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

  • public class Properties_Demo {
        public static void main(String[] args) throws IOException {
            public class Propertiesextends Hashtable
            myStore();
            myLoad();
    
        }
    
        public static void myLoad() throws IOException{
            Properties prop=new Properties();
            FileReader fr=new FileReader("idea_test\\src\\Properties_learn\\fw.txt");
            prop.load(fr);
            fr.close();
            System.out.println(prop);
        }
        public static  void myStore() throws IOException {
            Properties prop=new Properties();
            prop.setProperty("001","qyp");
            prop.setProperty("002","txr");
            prop.setProperty("003","lsm");
            FileWriter fw=new FileWriter("idea_test\\src\\Properties_learn\\fw.txt");
            prop.store(fw,null);
            fw.close();
        }
    }

13.5.4.3案例:游戏次数

需求:请写程序实现猜字小游戏,只能试玩三次,如果还想玩,提示:游戏试玩已结束,想玩请充值(www.itcast.cn)

思路:

  • 写一个游戏类,里面有一个猜字的小游戏

  • 写一个测试类,测试类中有main()方法,main()方法中按照下面步骤完成

    • 从文件中读取数据到Properties集合,用Load()方法实现

      • 文件已存在:game.txt

      • 里面有一个数据值:count=0

    • 通过Properties集合获取到玩游戏的次数

    • 判断次数是否到了三次

      • 如果到了,提示:游戏试玩已结束,想玩请充值(www.itcast.cn)

      • 如果不到,玩游戏,次数加一,重新写回文件,用Properties的store()方法实现

public class GuessNumber {
//    private GuessNumber() {
//
//    }
    public static void start(){
        Random r=new Random();
        int number=r.nextInt(100)+1;
        while(true){
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入你要猜的数字:");
            int num = sc.nextInt();
            if (num==number){
                System.out.println("恭喜你猜中了");
                break;
            }else if (num>number){
                System.out.println("你猜的数字大了");
            }else{
                System.out.println("你猜的数字小了");
            }
        }
    }
}


public class Properties_test {
    public static void main(String[] args) throws IOException {
        Properties prop=new Properties();
        FileReader fr=new FileReader("idea_test\\src\\Properties_learn\\game.txt");
        prop.load(fr);
        fr.close();
//        System.out.println(prop.getProperty("count"));
        String count= prop.getProperty("count");
        int number = Integer.parseInt(count);
        if (number>=3){
            System.out.println("游戏试玩已结束,想玩请充值(www.itcast.cn)");
        }else{
            GuessNumber.start();
            number++;
            prop.setProperty("count",String.valueOf(number));
            FileWriter fw=new FileWriter("idea_test\\src\\Properties_learn\\game.txt");
            prop.store(fw,null);
            fw.close();
        }
    }
}


//game.txt
//count=0    

十四、多线程

14.1实现多线程

14.1.1进程

进程:正在运行的程序

  • 是系统进行资源分配和调用的独立单位

  • 每个进程都有它自己的内存空间和系统资源

14.1.2线程

线程:是进程中的单个顺序控制流,是一条执行路径

  • 单线程:一个进程如果只有一条执行路径,则称为单线程程序

  • 多线程:一个进程如果有多条执行路径,则称为多线程程序

14.1.3多线程的实现方式

方式一:继承Thread类

  • 定义一个类MyThread继承Thread类

  • 在MyThread类中重写run()方法

  • 创建MyThread类的对象

  • 启动线程

方式二:实现Runnable接口

  • 定义一个类MyRunnable实现Runnable接口

  • 在MyRunnable类中重写run()方法

  • 创建MyRunnable类的对象

  • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数

  • 启动线程

相比继承Thread类,实现Runnable接口的好处

—避免了Java单继承的局限性

—适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面对对象的设计思想

14.1.4设置和获取线程名称

Thread类中设置和获取线程名称的方法

  • void setName(String name) 改变线程名称为参数 name 。

  • String getName() 返回该线程的名称

获取main()方法所在的线程名称:

  • static Thread currentThread() 返回对当前正在执行的线程对象的引用。

  • System.out.println(Thread.currentThread().getName());

14.1.5线程调度

两种调度模型:

  • 分时调度模型:所有线程轮流使用CPU的使用股权,平均分配每个线程占用CPU的时间片

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

Java使用的是抢占式调度模型,多线程程序的执行是有随机性的,因为谁抢到CPU的使用权是不一定的

Thread类中设置和获取线程优先级的方法

  • int getPriority() 返回线程的优先级。

  • void setPriority(int newPriority) 更改线程的优先级。

14.1.6线程控制

  • static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

  • void join() 等待该线程终止。

  • void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 当运行的线程都是守护线程时,Java虚拟机将退出

14.1.7线程生命周期

见课程《计算机操作系统》

14.2线程同步

14.2.1案例:买票

需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

思路:

  • 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets=100;

  • 在SellTicket类中重写run()方法实现卖票,代码步骤如下

    • 判断票数大于0,就卖票,并告知是哪一个窗口卖的

    • 卖了票之后,总票数减一

    • 通过sleep()方法来模拟出票时间

    • 票没有了,也可能有人来问,所以这里要用死循环让卖票的动作一直执行

  • 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下

    • 创建SellTicket类的对象

    • 创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应窗口名称

    • 启动线程

public class SellTicket implements Runnable{
    private int tickets=100;
    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");

                tickets--;

            }
        }
    }
}

public class SellTicket_Demo {
    public static void main(String[] args) {
        SellTicket sellTicket=new SellTicket();
        Thread thread1=new Thread(sellTicket,"窗口一:");
        Thread thread2=new Thread(sellTicket,"窗口二:");
        Thread thread3=new Thread(sellTicket,"窗口三:");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

程序出现问题:

  • 相同的票出现多次

  • 出现了复数的票

14.2.2数据安全

判断多线程程序是否会有数据安全问题的标准:

  • 是否是多线程环境

  • 是否有共享数据

  • 是否有多条语句操作共享数据

解决多线程安全问题:

  • 基本思想:让程序没有安全问题的环境

  • 把多条语句操作共享数据的代码锁起来,让任意时刻只能有一个线程执行即可

  • Java提供了同步代码块来解决这个问题

14.2.2.1同步代码块

锁住多条语句操作共享语句

synchronized(任意对象){ //相当于给代码加锁了,任意对象可以看成是一把锁

多条语句操作共享语句

}

好处和弊端:

  • 好处:解决了多线程数据安全问题

  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源,无形中会降低程序的运行效率

public class SellTicket_Demo {
    public static void main(String[] args) {
        SellTicket sellTicket=new SellTicket();
        Thread thread1=new Thread(sellTicket,"窗口一:");
        Thread thread2=new Thread(sellTicket,"窗口二:");
        Thread thread3=new Thread(sellTicket,"窗口三:");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

public class SellTicket implements Runnable{
    private int tickets=100;
    Object obj=new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj){
            //thread1进来后,将这段代码进行加锁
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--;
                }
            }
            //thread1出来后,这段代码的锁就被解开了
        }
    }
}

14.2.2.2同步方法

同步方法就是把synchronized关键字加到方法上

  • 格式:修饰符 synchronized 返回值类型 方法名(方法参数){ }

  • 同步方法的锁对象是this

  • 同步静态方法:修饰符 static synchronized 返回值类型 方法名(方法参数){ }

14.2.3线程安全的类

StringBuffer

  • 线程安全,可变的字符序列

  • JDK5后被StringBuilder替代,StringBulider更快,因为它不执行同步

Vector

  • 该类改进了List接口,是Java Collections Framework的成员。与新的集合实现不同,Vector被同步

HashTable

  • 该类实现了一个哈希表,任何非null对象都可以用作键或者值

  • 从Java 2 平台 v1.2起,此类就被改进以实现 Map 接口,使它成为 Java Collections Framework 中的一个成员。不像新的 collection 实现,Hashtable 是同步的

14.3生产者消费者

生产者消费者模式概述

生产者消费者模式是个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻所谓生产者消费者问题,实际上主要是包含了两类线程:

  • 一类是生产者线程用于生产数据

  • 一类是消费者线程用于消费数据

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库

  • 生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为

  • 消费者只需要从共享数据区中去获取数据, 并不需要关心生产者的行为

生产者——》共享数据区域《——消费者

为了体现生产和消费过程中的等待和唤醒,java就提供了几个方法供我们使用

  • void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

  • void notify() 唤醒在此对象监视器上等待的单个线程。

  • void notifyAll() 唤醒在此对象监视器上等待的所有线程。

14.3.1生产者消费者案例

生产者消费者案例中包含的类:

  • 奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作

  • 生产者类(Producer):实现Runnable接口, 重写run0方法,调用存储牛奶的操作

  • 消费者类(Customer): 实现Runnable接口,重写run0方法,调用获取牛奶的操作

  • 测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下

    • 创建奶箱对象,这是共享数据区域

    • 创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作

    • 创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作

    • 创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递

public class Box {
    private  int milk;
    //定义一个成员变量,表示奶箱状态
    private boolean state =false;

    public synchronized void put(int milk){
        //如果有牛奶等待消费
        if (state){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有牛奶,就生产牛奶
        this.milk=milk;
        System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱");

        //生产完毕后,修改奶箱状态
        state=true;
        //唤醒其他等待的线程
        notifyAll();
    }

    public synchronized void get(){
        //如果没有牛奶,就等待生产
        if (!state){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果有牛奶,就消费牛奶
        System.out.println("用户拿到第"+this.milk+"瓶奶");
        //消费完成后,修改奶箱状态
        state=false;
        notifyAll();
    }
}

public class Producer implements Runnable{
    private Box b;
    public Producer(Box b){
        this.b=b;
    }
    @Override
    public void run() {
        for(int i=1;i<=30;i++){
            b.put(i);
        }
    }
}

public class Customer implements Runnable{
    private Box b;
    public Customer(Box b){
        this.b=b;
    }
    @Override
    public void run() {
        while(true){
            b.get();
        }
    }
}

public class BoxDemo {
    public static void main(String[] args) {
        Box b=new Box();
        Producer producer=new Producer(b);
        Customer customer=new Customer(b);
        Thread thread1=new Thread(producer);
        Thread thread2=new Thread(customer);
        thread1.start();
        thread2.start();
    }
}

十五、编程入门

15.1网络编程入门

15.1.1网络编程三要素

IP地址(标识网络设备)、端口(标识网络设备上的应用程序)、通信协议

IP地址:

InetAddress:此类表示Internet协议(IP)地址

  • static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。

  • String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。

  • String getHostName() 获取此 IP 地址的主机名。

端口:

端口:设备上应用程序的唯一标识

端口号:用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通应用程序需要使用1024以上的端口号,如果端口号被另外一个服务或应用占用,会导致当前程序启动失败。

协议:

UDP协议:用户数据报协议,无连接通信协议,即在数据传输时。数据的发送端和接受端不建立逻辑连接。UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输。由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议

TCP协议:是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”

三次握手: TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠

  • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认

  • 第二次握手,服务器端向客户端回送个响应, 通知客户端收到了连接请求

  • 第三次握手,客户端再次向服务器端发送确认信息,确认连接

15.2UDP通信程序

发送数据的步骤

①创建发送端的Socket对象(DatagramSocket)

DatagramSocket()

②创建数据, 并把数据打包

DatagramPacket(byte[] buf, int length, InetAddress address, int port)构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。

③调用DatagramSocket对象的方法发送数据

void send(DatagramPacket p) 从此套接字发送数据报包。

④关闭发送端

void close()

接收数据的步骤

①创建接收端的Socket对象(DatagramSocket)

DatagramSocket(int port)

②创建一个数据包,用于接收数据

DatagramPacket(byte[] buf, int length)

③调用DatagramSocket对象的方法接收数据

void receive(DatagramPacketp)

④解析数据包, 并把数据在控制台显示

byte[] getData()

int getLength()

⑤关闭接收端

void close()

15.1.2案例:UDP通信程序练习

public class Send_Demo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket();
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        String line;
        while ((line=br.readLine())!=null){
            if ("886".equals(line)){
                break;
            }
            byte[] bys=line.getBytes();
            DatagramPacket dp=new DatagramPacket(bys,bys.length, InetAddress.getByName("192.168.107.1"),12345);
            ds.send(dp);
        }
        ds.close();
    }
}

public class Receive_Demo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket(12345);
        while (true) {
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);

            ds.receive(dp);

            System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
//            ds.close();
        }
    }
}

15.3TCP通信程序

发送数据的步骤

①创建客户端的Socket对象(Socket)

Socket(String host, int port)

②获取输出流,写数据

OutputStreamgetOutputStream()

③释放资源

void close()

接收数据的步骤

①创建服务器端的Socket对象(ServerSocket)

ServerSocket(int port)

②监听客户端连接, 返回个Socket对象

Socket accept()

③获取输入流, 读数据,并把数据显示在控制台

InputStream getInputStream()

④释放资源

void close()

15.1.2案例:TCP通信程序练习

public class Send_Demo {
    public static void main(String[] args) throws IOException {
        Socket soc=new Socket("192.168.107.1",10000);
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        //封装输出流对象
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(soc.getOutputStream()));
        String line;
        while ((line=br.readLine())!=null){
            if ("886".equals(line)){
                break;
            }

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

public class Receive_Demo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10000);
        Socket soc = ss.accept();//监听客户端的连接
        InputStream is = soc.getInputStream();
        //获取输入流
        BufferedReader br=new BufferedReader(new InputStreamReader(soc.getInputStream()));
        String line;
        while ((line=br.readLine())!=null){
            System.out.println(line);
            }

        ss.close();
    }
}

十六、Lambda表达式

Lambda表达式的三要素:形式参数,箭头,代码块

格式:(形式参数)->{代码块}

  • 形式参数:如果有多个参数,用逗号隔开;没有参数,留空即可

  • ->:由英文中画线和大于符号组成,固定写法,代表指向动作。

  • 代码块:是我们具体要做的事,也就是以前我们写的方法体内容

注意:参数类型可省略;如果参数有且仅有一个,小括号可以省略;如果代码块的语句只有一条,可以省略大括号和分号;如果有return,return也可省略

前提:15.1.2案例:UDP通信程序练习

  • 有一个接口

  • 接口中有且仅有一个抽象方法

十七、方法引用

17.1方法引用符

::该符号为引用运算符,而它所在的表达式被称为方法引用

Lambda表达式:

例如:usePrintable(s->System.out.println(s));

分析:拿到参数s后通过Lambda表达式,传递给System.out.println方法处理

方法引用:

例如:usePrintable(System.out::println)

分析:直接使用System.out中的println方法来取代Lambda,代码更加简洁

推导与省略:

  • 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导

  • 如果使用方法引用,同样是可以根据上下文进行推导

  • 方法引用是Lambda的孪生兄弟

17.2引用类方法

引用类方法,就是引用类的静态方法

格式:类名::静态方法

范例:Integer::parseInt Integer类的parseInt方法

Lambda表达式被类方法替代时,它的形式参数全部传递给静态方法作为参数

useConverter(s->Integer.parseInt(s));

改成:useConverter(Integer::parseInt)

17.3引用对象的实例方法

引用对象的实例方法,就是引用类中的成员方法

格式:对象::成员方法

范例:“HelloWorld”::toUpperCase String类中的方法,将所有字符转换为大写

Lambda表达式被对象的实例方法替代时,它的形式参数全部传递给静态方法作为参数

17.4引用类的实例方法

引用类的实例方法,就是引用类中的成员方法

格式:类名::成员方法

范例:String::subString String类中的方法,截取字符串

17.5引用构造器

引用构造器,就是引用构造方法

格式:类名::new

范例:Student::new

Lambda表达式被构造器替代时,它的形式参数全部传递给构造器作为参数


最后附上学习地址:Java_黑马刘意(风清扬)2019最新版_Java入门视频_Java入门_Java编程_Java入门教程_黑马教程_黑马程序员_idea版_哔哩哔哩_bilibili

你可能感兴趣的:(java)