赞
踩
代码演示
import java.util.ArrayList; import java.util.Collections; import java.util.List; /** 目标:初步体验Stream流的方便与快捷 */ public class StreamTest { public static void main(String[] args) { List<String> names = new ArrayList<>(); Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强"); System.out.println(names); // // // 1、从集合中找出姓张的放到新集合 // List<String> zhangList = new ArrayList<>(); // for (String name : names) { // if(name.startsWith("张")){ // zhangList.add(name); // } // } // System.out.println(zhangList); // // // 2、找名称长度是3的姓名 // List<String> zhangThreeList = new ArrayList<>(); // for (String name : zhangList) { // if(name.length() == 3){ // zhangThreeList.add(name); // } // } // System.out.println(zhangThreeList); // 3、使用Stream实现的 names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s)); } }
输出结果
[张三丰, 张无忌, 周芷若, 赵敏, 张强]
张三丰
张无忌
Stream流式思想的核心
Stream操作集合或者数组的第一步是先得到Stream流,然后才能使用流的功能。
集合获取Stream流的方式
可以使用Collection接口中的默认方法stream()生成流
名称 | 说明 |
---|---|
default Stream stream() | 获取当前集合对象的Stream流 |
数组获取Stream流的方式
名称 | 说明 |
---|---|
public static Stream stream(T[] array) | 获取当前数组的Stream流 |
public static Stream of(T… values) | 获取当前数组/可变数据的Stream流 |
代码演示
import java.util.*; import java.util.stream.Stream; /** 目标:Stream流的获取 Stream流式思想的核心: 是先得到集合或者数组的Stream流(就是一根传送带) 然后就用这个Stream流操作集合或者数组的元素。 然后用Stream流简化替代集合操作的API. 集合获取流的API: (1) default Stream<E> stream(); 小结: 集合获取Stream流用: stream(); 数组:Arrays.stream(数组) / Stream.of(数组); */ public class StreamDemo02 { public static void main(String[] args) { /** --------------------Collection集合获取流------------------------------- */ Collection<String> list = new ArrayList<>(); Stream<String> s = list.stream(); /** --------------------Map集合获取流------------------------------- */ Map<String, Integer> maps = new HashMap<>(); // 键流 Stream<String> keyStream = maps.keySet().stream(); // 值流 Stream<Integer> valueStream = maps.values().stream(); // 键值对流(拿整体) Stream<Map.Entry<String,Integer>> keyAndValueStream = maps.entrySet().stream(); /** ---------------------数组获取流------------------------------ */ String[] names = {"赵敏","小昭","灭绝","周芷若"}; Stream<String> nameStream = Arrays.stream(names); Stream<String> nameStream2 = Stream.of(names); } }
Stream流的常用API(中间操作方法)
名称 | 说明 |
---|---|
Stream filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤。 |
Stream limit(long maxSize) | 获取前几个元素 |
Stream skip(long n) | 跳过前几个元素 |
Stream distinct() | 去除流中重复的元素。依赖(hashCode和equals方法) |
static Stream concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
注意:
Stream流的常见终结操作方法
名称 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
long count() | 返回此流中的元素数 |
注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。
代码演示
学生类
public class Student { private String name; public Student() { } public Student(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } }
测试类
import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; /** 目标:Stream流的常用API forEach : 逐一处理(遍历) count:统计个数 -- long count(); filter : 过滤元素 -- Stream<T> filter(Predicate<? super T> predicate) limit : 取前几个元素 skip : 跳过前几个 map : 加工方法 concat : 合并流。 */ public class StreamDemo03 { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰"); list.add("张三丰"); // Stream<T> filter(Predicate<? super T> predicate) list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s)); long size = list.stream().filter(s -> s.length() == 3).count(); System.out.println(size); // list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s)); list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println); list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println); // map加工方法: 第一个参数原材料 -> 第二个参数是加工后的结果。 // 给集合元素的前面都加上一个:黑马的: list.stream().map(s -> "黑马的:" + s).forEach(a -> System.out.println(a)); // 需求:把所有的名称 都加工成一个学生对象。 list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s)); // list.stream().map(Student::new).forEach(System.out::println); // 构造器引用 方法引用 // 合并流。 Stream<String> s1 = list.stream().filter(s -> s.startsWith("张")); Stream<String> s2 = Stream.of("java1", "java2"); // public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) Stream<String> s3 = Stream.concat(s1 , s2); s3.distinct().forEach(s -> System.out.println(s)); }
输出结果
张无忌 张强 张三丰 张三丰 4 张无忌 张强 张三丰 张三丰 黑马的:张无忌 黑马的:周芷若 黑马的:赵敏 黑马的:张强 黑马的:张三丰 黑马的:张三丰 Student{name='张无忌'} Student{name='周芷若'} Student{name='赵敏'} Student{name='张强'} Student{name='张三丰'} Student{name='张三丰'} 张无忌 张强 张三丰 java1 java2
需求:
分析:
代码演示
员工类
public class Employee { private String name; private char sex; private double salary; private double bonus; private String punish; // 处罚信息 public Employee(){ } public Employee(String name, char sex, double salary, double bonus, String punish) { this.name = name; this.sex = sex; this.salary = salary; this.bonus = bonus; this.punish = punish; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public String getPunish() { return punish; } public void setPunish(String punish) { this.punish = punish; } public double getTotalSalay(){ return salary * 12 + bonus; } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", sex=" + sex + ", salary=" + salary + ", bonus=" + bonus + ", punish='" + punish + '\'' + '}'+"\n"; } }
Topperformer 类
public class Topperformer { private String name; private double money; // 月薪 public Topperformer() { } public Topperformer(String name, double money) { this.name = name; this.money = money; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Topperformer{" + "name='" + name + '\'' + ", money=" + money + '}'; } }
实现类
import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class StreamDemo04 { public static double allMoney ; public static double allMoney2 ; // 2个部门去掉最高工资,最低工资的总和 public static void main(String[] args) { List<Employee> one = new ArrayList<>(); one.add(new Employee("猪八戒",'男',30000 , 25000, null)); one.add(new Employee("孙悟空",'男',25000 , 1000, "顶撞上司")); one.add(new Employee("沙僧",'男',20000 , 20000, null)); one.add(new Employee("小白龙",'男',20000 , 25000, null)); List<Employee> two = new ArrayList<>(); two.add(new Employee("武松",'男',15000 , 9000, null)); two.add(new Employee("李逵",'男',20000 , 10000, null)); two.add(new Employee("西门庆",'男',50000 , 100000, "被打")); two.add(new Employee("潘金莲",'女',3500 , 1000, "被打")); two.add(new Employee("武大郎",'女',20000 , 0, "下毒")); // 1、开发一部的最高工资的员工。(API) // 指定大小规则了 // Employee e = one.stream().max((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(), e2.getSalary() + e2.getBonus())) // .get(); // System.out.println(e); Topperformer t = one.stream().max((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(), e2.getSalary() + e2.getBonus())) .map(e -> new Topperformer(e.getName(), e.getSalary() + e.getBonus())).get(); System.out.println(t); // 2、统计平均工资,去掉最高工资和最低工资 one.stream().sorted((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(), e2.getSalary() + e2.getBonus())) .skip(1).limit(one.size() - 2).forEach(e -> { // 求出总和:剩余员工的工资总和 allMoney += (e.getSalary() + e.getBonus()); }); System.out.println("开发一部的平均工资是:" + allMoney / (one.size() - 2)); // 3、合并2个集合流,再统计 Stream<Employee> s1 = one.stream(); Stream<Employee> s2 = two.stream(); Stream<Employee> s3 = Stream.concat(s1 , s2); s3.sorted((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(), e2.getSalary() + e2.getBonus())) .skip(1).limit(one.size() + two.size() - 2).forEach(e -> { // 求出总和:剩余员工的工资总和 allMoney2 += (e.getSalary() + e.getBonus()); }); // BigDecimal BigDecimal a = BigDecimal.valueOf(allMoney2); BigDecimal b = BigDecimal.valueOf(one.size() + two.size() - 2); System.out.println("开发部的平均工资是:" + a.divide(b,2, RoundingMode.HALF_UP)); } }
输出结果
Topperformer{name='猪八戒', money=55000.0}
开发一部的平均工资是:42500.0
开发部的平均工资是:34285.71
Stream流的收集操作
Stream流的收集方法
名称 | 说明 |
---|---|
R collect(Collector collector) | 开始收集Stream流,指定收集器 |
Collectors工具类提供了具体的收集方式
名称 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
代码演示
import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** 目标:收集Stream流的数据到 集合或者数组中去。 */ public class StreamDemo05 { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰"); list.add("张三丰"); Stream<String> s1 = list.stream().filter(s -> s.startsWith("张")); List<String> zhangList = s1.collect(Collectors.toList()); // 可变集合 zhangList.add("java1"); System.out.println(zhangList); // List<String> list1 = s1.toList(); // 得到不可变集合 // list1.add("java"); // System.out.println(list1); // 注意注意注意:“流只能使用一次” Stream<String> s2 = list.stream().filter(s -> s.startsWith("张")); Set<String> zhangSet = s2.collect(Collectors.toSet()); System.out.println(zhangSet); Stream<String> s3 = list.stream().filter(s -> s.startsWith("张")); // Object[] arrs = s3.toArray(); String[] arrs = s3.toArray(String[]::new); // 可以不管,拓展一下思维!! System.out.println("Arrays数组内容:" + Arrays.toString(arrs)); } }
输出结果
[张无忌, 张强, 张三丰, 张三丰, java1]
[张强, 张三丰, 张无忌]
Arrays数组内容:[张无忌, 张强, 张三丰, 张三丰]
补充
jdk16 开始可以直接调用 toList() 方法得到一个不可变集合
什么是异常?
为什么要学习异常?
异常体系
编译时异常和运行时异常
运行时异常
直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
运行时异常示例
运行时异常:一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误,自己的水平有问题!
代码演示
/** 拓展: 常见的运行时异常。(面试题) 运行时异常的概念: 继承自RuntimeException的异常或者其子类, 编译阶段是不会出错的,它是在运行时阶段可能出现的错误, 运行时异常编译阶段可以处理也可以不处理,代码编译都能通过!! 1.数组索引越界异常: ArrayIndexOutOfBoundsException。 2.空指针异常 : NullPointerException。 直接输出没有问题。但是调用空指针的变量的功能就会报错!! 3.类型转换异常:ClassCastException。 4.迭代器遍历没有此元素异常:NoSuchElementException。 5.数学操作异常:ArithmeticException。 6.数字转换异常: NumberFormatException。 小结: 运行时异常继承了RuntimeException ,编译阶段不报错,运行时才可能会出现错误! */ public class ExceptionDemo { public static void main(String[] args) { System.out.println("程序开始。。。。。。"); /** 1.数组索引越界异常: ArrayIndexOutOfBoundsException。*/ int[] arr = {1, 2, 3}; System.out.println(arr[2]); // System.out.println(arr[3]); // 运行出错,程序终止 /** 2.空指针异常 : NullPointerException。直接输出没有问题。但是调用空指针的变量的功能就会报错!! */ String name = null; System.out.println(name); // null // System.out.println(name.length()); // 运行出错,程序终止 /** 3.类型转换异常:ClassCastException。 */ Object o = 23; // String s = (String) o; // 运行出错,程序终止 /** 5.数学操作异常:ArithmeticException。 */ //int c = 10 / 0; /** 6.数字转换异常: NumberFormatException。 */ //String number = "23"; String number = "23aabbc"; Integer it = Integer.valueOf(number); // 运行出错,程序终止 System.out.println(it + 1); System.out.println("程序结束。。。。。"); } }
编译时异常
不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
编译时异常的作用
代码演示
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** 目标:常见的编译时异常认识。 编译时异常:继承自Exception的异常或者其子类,没有继承RuntimeException "编译时异常是编译阶段就会报错", 必须程序员编译阶段就处理的。否则代码编译就报错!! 编译时异常的作用是什么: 是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒! 提醒程序员这里很可能出错,请检查并注意不要出bug。 编译时异常是可遇不可求。遇到了就遇到了呗。 了解: */ public class ExceptionDemo { public static void main(String[] args) throws ParseException { String date = "2015-01-12 10:23:21"; // 创建一个简单日期格式化类: SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); // 解析字符串时间成为日期对象 Date d = sdf.parse(date); // System.out.println(d); } }
输出结果
Exception in thread "main" java.text.ParseException: Unparseable date: "2015-01-12 10:23:21"
代码演示
/** 目标:异常的产生默认的处理过程解析。(自动处理的过程!) (1)默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。 (2)异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。 (3)虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。 (4)直接从当前执行的异常点干掉当前程序。 (5)后续代码没有机会执行了,因为程序已经死亡。 小结: 异常一旦出现,会自动创建异常对象,最终抛出给虚拟机,虚拟机 只要收到异常,就直接输出异常信息,干掉程序!! 默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡! */ public class ExceptionDemo { public static void main(String[] args) { System.out.println("程序开始。。。。。。。。。。"); chu(10, 0); System.out.println("程序结束。。。。。。。。。。"); } public static void chu(int a , int b){ System.out.println(a); System.out.println(b); int c = a / b; System.out.println(c); } }
输出结果
程序开始。。。。。。。。。。
10
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
代码演示
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** 目标:编译时异常的处理方式一。 编译时异常:编译阶段就会报错,一定需要程序员处理的,否则代码无法通过!! 抛出异常格式: 方法 throws 异常1 , 异常2 , ..{ } 建议抛出异常的方式:代表可以抛出一切异常, 方法 throws Exception{ } 方式一: 在出现编译时异常的地方层层把异常抛出去给调用者,调用者最终抛出给JVM虚拟机。 JVM虚拟机输出异常信息,直接干掉程序,这种方式与默认方式是一样的。 虽然可以解决代码编译时的错误,但是一旦运行时真的出现异常,程序还是会立即死亡! 这种方式并不好! 小结: 方式一出现异常层层跑出给虚拟机,最终程序如果真的出现异常,程序还是立即死亡!这种方式不好! */ public class ExceptionDemo01 { // public static void main(String[] args) throws ParseException, FileNotFoundException { // System.out.println("程序开始。。。。。"); // parseTime("2011-11-11 11:11:11"); // System.out.println("程序结束。。。。。"); // } // // public static void parseTime(String date) throws ParseException, FileNotFoundException { // SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); // Date d = sdf.parse(date); // System.out.println(d); // // InputStream is = new FileInputStream("E:/meinv.jpg"); // } public static void main(String[] args) throws Exception { System.out.println("程序开始。。。。。"); parseTime("2011-11-11 11:11:11"); System.out.println("程序结束。。。。。"); } public static void parseTime(String date) throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse(date); System.out.println(d); InputStream is = new FileInputStream("E:/meinv.jpg"); } }
输出结果
程序开始。。。。。
Fri Nov 11 11:11:11 CST 2011
Exception in thread "main" java.io.FileNotFoundException: E:\meinv.jpg (系统找不到指定的路径。)
代码演示
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** 目标:编译时异常的处理方式二。 方式二:在出现异常的地方自己处理,谁出现谁处理。 自己捕获异常和处理异常的格式:捕获处理 try{ // 监视可能出现异常的代码! }catch(异常类型1 变量){ // 处理异常 }catch(异常类型2 变量){ // 处理异常 }... 监视捕获处理异常企业级写法: try{ // 可能出现异常的代码! }catch (Exception e){ e.printStackTrace(); // 直接打印异常栈信息 } Exception可以捕获处理一切异常类型! 小结: 第二种方式,可以处理异常,并且出现异常后代码也不会死亡。 这种方案还是可以的。 但是从理论上来说,这种方式不是最好的,上层调用者不能直接知道底层的执行情况! */ public class ExceptionDemo02 { public static void main(String[] args) { System.out.println("程序开始。。。。"); parseTime("2011-11-11 11:11:11"); System.out.println("程序结束。。。。"); } public static void parseTime(String date) { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); Date d = sdf.parse(date); System.out.println(d); InputStream is = new FileInputStream("E:/meinv.jpg"); } catch (Exception e) { e.printStackTrace(); // 打印异常栈信息 } } // public static void parseTime(String date) { // try { // SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); // Date d = sdf.parse(date); // System.out.println(d); // // InputStream is = new FileInputStream("E:/meinv.jpg"); // } catch (FileNotFoundException|ParseException e) { // e.printStackTrace(); // 打印异常栈信息 // } // } // public static void parseTime(String date) { // try { // SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); // Date d = sdf.parse(date); // System.out.println(d); // // InputStream is = new FileInputStream("E:/meinv.jpg"); // } catch (FileNotFoundException e) { // e.printStackTrace(); // 打印异常栈信息 // } catch (ParseException e) { // e.printStackTrace(); // } // } // public static void parseTime(String date) { // try { // SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); // Date d = sdf.parse(date); // System.out.println(d); // } catch (ParseException e) { // // 解析出现问题 // System.out.println("出现了解析时间异常哦,走点心!!"); // } // // try { // InputStream is = new FileInputStream("E:/meinv.jpg"); // } catch (FileNotFoundException e) { // System.out.println("您的文件根本就没有啊,不要骗我哦!!"); // } // } }
输出结果
程序开始。。。。
java.text.ParseException: Unparseable date: "2011-11-11 11:11:11"
程序结束。。。。
代码演示
import java.io.FileInputStream; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** 目标:编译时异常的处理方式三。 方式三: 在出现异常的地方把异常一层一层的抛出给最外层调用者, 最外层调用者集中捕获处理!!(规范做法) 小结: 编译时异常的处理方式三:底层出现的异常抛出给最外层调用者集中捕获处理。 这种方案最外层调用者可以知道底层执行的情况,同时程序在出现异常后也不会立即死亡,这是 理论上最好的方案。 虽然异常有三种处理方式,但是开发中只要能解决你的问题,每种方式都又可能用到!! */ public class ExceptionDemo03 { public static void main(String[] args) { System.out.println("程序开始。。。。"); try { parseTime("2011-11-11 11:11:11"); System.out.println("功能操作成功~~~"); } catch (Exception e) { e.printStackTrace(); System.out.println("功能操作失败~~~"); } System.out.println("程序结束。。。。"); } public static void parseTime(String date) throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy、MM-dd HH:mm:ss"); Date d = sdf.parse(date); System.out.println(d); InputStream is = new FileInputStream("D:/meinv.jpg"); } }
输出结果
程序开始。。。。
java.text.ParseException: Unparseable date: "2011-11-11 11:11:11"
功能操作失败~~~
程序结束。。。。
代码演示
/** 目标:运行时异常的处理机制。 可以不处理,编译阶段又不报错。 按照理论规则:建议还是处理,只需要在最外层捕获处理即可 */ public class Test { public static void main(String[] args) { System.out.println("程序开始。。。。。。。。。。"); try { chu(10, 0); } catch (Exception e) { e.printStackTrace(); } System.out.println("程序结束。。。。。。。。。。"); } public static void chu(int a , int b) { // throws RuntimeException{ System.out.println(a); System.out.println(b); int c = a / b; System.out.println(c); } }
输出结果
程序开始。。。。。。。。。。
10
0
程序结束。。。。。。。。。。
java.lang.ArithmeticException: / by zero
**需求:**键盘录入一个合理的价格为止(必须是数值,值必须大于0)。
**分析:**定义一个死循环,让用户不断的输入价格。
代码演示
import java.util.Scanner; /** 需求:需要输入一个合法的价格为止 要求价格大于 0 */ public class Test2 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (true) { try { System.out.println("请您输入合法的价格:"); String priceStr = sc.nextLine(); // 转换成double类型的价格 double price = Double.valueOf(priceStr); // 判断价格是否大于 0 if(price > 0) { System.out.println("定价:" + price); break; }else { System.out.println("价格必须是正数~~~"); } } catch (Exception e) { System.out.println("用户输入的数据有毛病,请您输入合法的数值,建议为正数~~"); } } } }
自定义异常的必要
自定义异常的好处
自定义异常的分类
1、自定义编译时异常
2、自定义运行时异常
代码演示
自定义编译时异常
/**
自定义的编译时异常
1、继承Exception
2、重写构造器
*/
public class ItheimaAgeIlleagalException extends Exception{
public ItheimaAgeIlleagalException() {
}
public ItheimaAgeIlleagalException(String message) {
super(message);
}
}
自定义运行时异常
/**
自定义的运行时异常
1、继承RuntimeException
2、重写构造器
*/
public class ItheimaAgeIlleagalRuntimeException extends RuntimeException{
public ItheimaAgeIlleagalRuntimeException() {
}
public ItheimaAgeIlleagalRuntimeException(String message) {
super(message);
}
}
测试类
/** 目标:自定义异常(了解) 引入:Java已经为开发中可能出现的异常都设计了一个类来代表. 但是实际开发中,异常可能有无数种情况,Java无法为 这个世界上所有的异常都定义一个代表类。 假如一个企业如果想为自己认为的某种业务问题定义成一个异常 就需要自己来自定义异常类. 需求:认为年龄小于0岁,大于200岁就是一个异常。 自定义异常: 自定义编译时异常. a.定义一个异常类继承Exception. b.重写构造器。 c.在出现异常的地方用throw new 自定义对象抛出! 编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!! 自定义运行时异常. a.定义一个异常类继承RuntimeException. b.重写构造器。 c.在出现异常的地方用throw new 自定义对象抛出! 提醒不强烈,编译阶段不报错!!运行时才可能出现!! */ public class ExceptionDemo { public static void main(String[] args) { // try { // checkAge(-34); // } catch (ItheimaAgeIlleagalException e) { // e.printStackTrace(); // } try { checkAge2(-23); } catch (Exception e) { e.printStackTrace(); } } public static void checkAge2(int age) { if(age < 0 || age > 200){ // 抛出去一个异常对象给调用者 // throw :在方法内部直接创建一个异常对象,并从此点抛出 // throws : 用在方法申明上的,抛出方法内部的异常 throw new ItheimaAgeIlleagalRuntimeException(age + " is illeagal!"); }else { System.out.println("年龄合法:推荐商品给其购买~~"); } } public static void checkAge(int age) throws ItheimaAgeIlleagalException { if(age < 0 || age > 200){ // 抛出去一个异常对象给调用者 // throw :在方法内部直接创建一个异常对象,并从此点抛出 // throws : 用在方法申明上的,抛出方法内部的异常 throw new ItheimaAgeIlleagalException(age + " is illeagal!"); }else { System.out.println("年龄合法:推荐商品给其购买~~"); } } }
输出结果
com.itheima.d9_exception_custom.ItheimaAgeIlleagalRuntimeException: -23 is illeagal!
注意
想清楚的知道一个系统运行的过程和详情怎么办?
日志
输出语句记录日志的弊端
日志技术具备的优势
输出语句 | 日志技术 | |
---|---|---|
输出位置 | 只能是控制台 | 可以将日志信息写入到文件或者数据库中 |
取消日志 | 需要修改代码,灵活性比较差 | 不需要修改代码,灵活性比较好 |
多线程 | 性能较差 | 性能较好 |
Logback日志框架
Logback主要分为三个技术模块
导入Logback日志技术到项目中,用于纪录系统的日志信息
在项目下新建文件夹lib,导入Logback的相关jar包到该文件夹下,并添加到项目依赖库中去。
将Logback的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)。
在代码中获取日志的对象
public static final Logger LOGGER = LoggerFactory.getLogger("Test.class");
使用日志对象LOGGER调用其方法输出日志信息
LOGGER.error("无法解析该时间格式");
Logback日志系统的特性都是通过核心配置文件logback.xml控制的。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- CONSOLE :表示当前的日志信息是可以输出到控制台的。 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--输出流对象 默认 System.out 改为 System.err--> <target>System.out</target> <encoder> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern> </encoder> </appender> <!-- File是输出的方向通向文件的 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <charset>utf-8</charset> </encoder> <!--日志输出路径--> <file>D:/日志记录/data.log</file> <!--指定日志文件拆分和压缩规则--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!--通过指定压缩文件名称,来确定分割文件方式--> <fileNamePattern>D:/日志记录/data-%d{yyyy-MM-dd}.log%i.gz</fileNamePattern> <!--文件拆分大小--> <maxFileSize>1MB</maxFileSize> </rollingPolicy> </appender> <!-- level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR | ALL 和 OFF , 默认debug <root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。 --> <root level="ALL"> <!-- 注意:如果这里不配置关联打印位置,该位置将不会记录日志--> <appender-ref ref="FILE" /> </root> </configuration>
Logback日志输出位置、格式设置
输出到控制台的配置标志
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
输出到系统文件的配置标志
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
如果系统上线后只想记录一些错误的日志信息或者不想记录日志了,怎么办?
可以通过设置日志的输出级别来控制哪些日志信息输出或者不输出。
日志级别
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
完场电影购票系统
集成日志框架、用于后期记录日志信息。
xml 文件配置
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- CONSOLE :表示当前的日志信息是可以输出到控制台的。 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--输出流对象 默认 System.out 改为 System.err--> <target>System.out</target> <encoder> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern> </encoder> </appender> <!-- File是输出的方向通向文件的 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <charset>utf-8</charset> </encoder> <!--日志输出路径--> <file>D:/Product/data.log</file> <!--指定日志文件拆分和压缩规则--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!--通过指定压缩文件名称,来确定分割文件方式--> <fileNamePattern>D:/Product/data-%d{yyyy-MM-dd}.log%i.gz</fileNamePattern> <!--文件拆分大小--> <maxFileSize>1MB</maxFileSize> </rollingPolicy> </appender> <!-- level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR | ALL 和 OFF , 默认debug <root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。 --> <root level="ALL"> <!-- 注意:如果这里不配置关联打印位置,该位置将不会记录日志--> <appender-ref ref="FILE" /> </root> </configuration>
创建日志对象
public static final Logger LOGGER = LoggerFactory.getLogger("MovieSystem.class");
定义Movie类
import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Date; import java.util.List; public class Movie { private String name; private String actor; private double time; private double price; private int number; // 余票 private Date startTime; // 放映时间 public Movie() { } public Movie(String name, String actor, double time, double price, int number, Date startTime) { this.name = name; this.actor = actor; this.time = time; this.price = price; this.number = number; this.startTime = startTime; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getActor() { return actor; } public void setActor(String actor) { this.actor = actor; } public double getScore() { List<Double> scores = MovieSystem.MOVIES_SCORE.get(name); if(scores!=null && scores.size() > 0){ double sum = 0; for (Double score : scores) { sum += score; } return BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(scores.size()), 2 , RoundingMode.UP).doubleValue(); }else { return 0; } } public double getTime() { return time; } public void setTime(double time) { this.time = time; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public Date getStartTime() { return startTime; } public void setStartTime(Date startTime) { this.startTime = startTime; } }
定义User类
/** 用户类(客户和商家的父类 ) */ public class User { private String loginName; // 假名 不能重复 private String userName; // 真名 private String passWord; private char sex; private String phone; private double money; public User(){ } public User(String loginName, String userName, String passWord, char sex, String phone, double money) { this.loginName = loginName; this.userName = userName; this.passWord = passWord; this.sex = sex; this.phone = phone; this.money = money; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } }
定义Business类
public class Business extends User{ // 店铺名称 private String shopName; // 店铺地址 private String address; public String getShopName() { return shopName; } public void setShopName(String shopName) { this.shopName = shopName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
定义Customer类
import java.util.HashMap; import java.util.Map; /** 客户角色 */ public class Customer extends User{ // 定义一个属性存储购买记录。 private Map<String, Boolean> buyMovies = new HashMap<>(); public Map<String, Boolean> getBuyMovies() { return buyMovies; } public void setBuyMovies(Map<String, Boolean> buyMovies) { this.buyMovies = buyMovies; } }
定义集合List用户存放系统注册的用户对象信息
/**
定义系统的数据容器用户存储数据
1、存储很多用户(客户对象,商家对象)
*/
public static final List<User> ALL_USERS = new ArrayList<>();
定义集合Map<Business, List>存放商家和其排片信息
/**
2、存储系统全部商家和其排片信息 。
商家1 = [p1,p2,p3,...]
商家2 = [p2,p3,...]
...
*/
public static final Map<Business, List<Movie>> ALL_MOVIES = new HashMap<>();
准备一些测试数据
/** 3、准备一些测试数据 */ static { Customer c = new Customer(); c.setLoginName("zyf888"); c.setPassWord("123456"); c.setUserName("黑马刘德华"); c.setSex('男'); c.setMoney(10000); c.setPhone("110110"); ALL_USERS.add(c); Customer c1 = new Customer(); c1.setLoginName("gzl888"); c1.setPassWord("123456"); c1.setUserName("黑马关之琳"); c1.setSex('女'); c1.setMoney(2000); c1.setPhone("111111"); ALL_USERS.add(c1); Business b = new Business(); b.setLoginName("baozugong888"); b.setPassWord("123456"); b.setUserName("黑马包租公"); b.setMoney(0); b.setSex('男'); b.setPhone("110110"); b.setAddress("火星6号2B二层"); b.setShopName("甜甜圈国际影城"); ALL_USERS.add(b); // 注意,商家一定需要加入到店铺排片信息中去 List<Movie> movies = new ArrayList<>(); ALL_MOVIES.put(b , movies); // b = [] Business b2 = new Business(); b2.setLoginName("baozupo888"); b2.setPassWord("123456"); b2.setUserName("黑马包租婆"); b2.setMoney(0); b2.setSex('女'); b2.setPhone("110110"); b2.setAddress("火星8号8B八层"); b2.setShopName("巧克力国际影城"); ALL_USERS.add(b2); // 注意,商家一定需要加入到店铺排片信息中去 List<Movie> movies3 = new ArrayList<>(); ALL_MOVIES.put(b2 , movies3); // b2 = [] }
定义扫描器
public static final Scanner SYS_SC = new Scanner(System.in);
首页展示功能
/** 首页展示 */ private static void showMain() { while (true) { System.out.println("===============黑马电影首页================="); System.out.println("1、登录"); System.out.println("2、用户注册"); System.out.println("3、商家注册"); System.out.println("请输入操作命令:"); String command = SYS_SC.nextLine(); switch (command) { case "1": // 登录了 login(); break; case "2": // break; case "3": break; default: System.out.println("命令有误,请确认!"); } } }
定义一个静态的User类
实现商家和客户可以共用一个登录功能。
// 定义一个静态的User类型的变量记住当前登录成功的用户对象
public static User loginUser;
登录功能
/** 登录功能 */ private static void login() { while (true) { System.out.println("请您输入登录名称:"); String loginName = SYS_SC.nextLine(); System.out.println("请您输入登录密码:"); String passWord = SYS_SC.nextLine(); // 1、根据登录名称查询用户对象。 User u = getUserByLoginName(loginName); // 2、判断用户对象是否存在,存在说明登录名称正确了 if(u != null){ // 3、比对密码是否正确 if(u.getPassWord().equals(passWord)){ // 登录成功了:... loginUser = u; // 记住登录成功的用户 LOGGER.info(u.getUserName() +"登录了系统~~~"); // 判断是用户登录的,还是商家登录的。 if(u instanceof Customer) { // 当前登录的是普通用户 showCustomerMain(); }else { // 当前登录的肯定是商家用户 showBusinessMain(); } return; }else { System.out.println("密码有毛病~~"); } }else { System.out.println("登录名称错误,请确认"); } } }
根据登录名称查询用户对象
public static User getUserByLoginName(String loginName){
for (User user : ALL_USERS) {
// 判断这个用户的登录名称是否是我们想要的
if(user.getLoginName().equals(loginName)){
return user;
}
}
return null; // 查询此用户登录名称
}
判断登录成功的用户的真实类型,根据用户类型完成对应的操作界面设计。
客户操作界面
/** 客户操作界面 */ private static void showCustomerMain() { while (true) { System.out.println("============黑马电影客户界面==================="); System.out.println(loginUser.getUserName() + (loginUser.getSex()=='男'? "先生":"女士" + "欢迎您进入系统" + "\t余额:" + loginUser.getMoney())); System.out.println("请您选择要操作的功能:"); System.out.println("1、展示全部影片信息功能:"); System.out.println("2、根据电影名称查询电影信息:"); System.out.println("3、评分功能:"); System.out.println("4、购票功能:"); System.out.println("5、退出系统:"); System.out.println("请输入您要操作的命令:"); String command = SYS_SC.nextLine(); switch (command){ case "1": // 展示全部排片信息 showAllMovies(); break; case "2": break; case "3": // 评分功能 scoreMovie(); showAllMovies(); break; case "4": // 购票功能 buyMovie(); break; case "5": return; // 干掉方法 default: System.out.println("不存在该命令!!"); break; } } }
商家的后台操作界面
/** 商家的后台操作界面 */ private static void showBusinessMain() { while (true) { System.out.println("============黑马电影商家界面==================="); System.out.println(loginUser.getUserName() + (loginUser.getSex()=='男'? "先生":"女士" + "欢迎您进入系统")); System.out.println("1、展示详情:"); System.out.println("2、上架电影:"); System.out.println("3、下架电影:"); System.out.println("4、修改电影:"); System.out.println("5、退出:"); System.out.println("请输入您要操作的命令:"); String command = SYS_SC.nextLine(); switch (command){ case "1": // 展示全部排片信息 showBusinessInfos(); break; case "2": // 上架电影信息 addMovie(); break; case "3": // 下架电影信息 deleteMovie(); break; case "4": // 修改电影信息 updateMovie(); break; case "5": System.out.println(loginUser.getUserName() +"请您下次再来啊~~~"); return; // 干掉方法 default: System.out.println("不存在该命令!!"); break; } } }
定义日期格式
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
展示当前商家的信息
/** 展示商家的详细:展示当前商家的信息。 */ private static void showBusinessInfos() { System.out.println("================商家详情界面================="); LOGGER.info(loginUser.getUserName() +"商家,正在看自己的详情~~~"); // 根据商家对象(就是登录的用户loginUser),作为Map集合的键 提取对应的值就是其排片信息 :Map<Business , List<Movie>> ALL_MOVIES Business business = (Business) loginUser; System.out.println(business.getShopName() + "\t\t电话:" + business.getPhone() + "\t\t地址:" + business.getAddress() + "\t\t余额:" + business.getMoney()); List<Movie> movies = ALL_MOVIES.get(business); if(movies.size() > 0) { System.out.println("片名\t\t\t主演\t\t时长\t\t评分\t\t票价\t\t余票数量\t\t放映时间"); for (Movie movie : movies) { System.out.println(movie.getName()+"\t\t\t" + movie.getActor()+ "\t\t" + movie.getTime() + "\t\t" + movie.getScore() + "\t\t" + movie.getPrice() + "\t\t" + movie.getNumber() + "\t\t" + sdf.format(movie.getStartTime())); } }else { System.out.println("您的店铺当前无片在放映~~~~"); } }
提供影片上架功能
就是创建一个影片对象,存入到商家的集合中去。
/** 商家进行电影上架 Map<Business , List<Movie>> ALL_MOVIES u1 = [p1,p2,p3] u2 = [p1,p2,p3] */ private static void addMovie() { System.out.println("================上架电影===================="); // 根据商家对象(就是登录的用户loginUser),作为Map集合的键 提取对应的值就是其排片信息 :Map<Business , List<Movie>> ALL_MOVIES Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); System.out.println("请您输入新片名:"); String name = SYS_SC.nextLine(); System.out.println("请您输入主演:"); String actor = SYS_SC.nextLine(); System.out.println("请您输入时长:"); String time = SYS_SC.nextLine(); System.out.println("请您输入票价:"); String price = SYS_SC.nextLine(); System.out.println("请您输入票数:"); String totalNumber = SYS_SC.nextLine(); // 200\n while (true) { try { System.out.println("请您输入影片放映时间:"); String stime = SYS_SC.nextLine(); // public Movie(String name, String actor, double time, double price, int number, Date startTime) // 封装成电影对象 ,加入集合movices中去 Movie movie = new Movie(name, actor ,Double.valueOf(time) , Double.valueOf(price) , Integer.valueOf(totalNumber) , sdf.parse(stime)); movies.add(movie); System.out.println("您已经成功上架了:《" + movie.getName() + "》"); return; // 直接退出去 } catch (ParseException e) { e.printStackTrace(); LOGGER.error("时间解析出了毛病"); } } }
退出功能
case "5":
System.out.println(loginUser.getUserName() +"请您下次再来啊~~~");
return; // 干掉方法
影片下架功能
/** 影片下架功能 */ private static void deleteMovie() { System.out.println("================下架电影===================="); Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); if(movies.size() == 0) { System.out.println("当期无片可以下架~~"); return; } // 2、让用户选择需要下架的电影名称 while (true) { System.out.println("请您输入需要下架的电影名称:"); String movieName = SYS_SC.nextLine(); // 3、去查询有没有这个影片对象。 Movie movie = getMovieByName(movieName); if(movie != null){ // 下架它 movies.remove(movie); System.out.println("您当前店铺已经成功下架了:" + movie.getName()); showBusinessInfos(); return; }else { System.out.println("您的店铺没有上架该影片!"); System.out.println("请问继续下架吗?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } }
查询当前商家下的排片
/**
去查询当前商家下的排片
*/
public static Movie getMovieByName(String movieName){
Business business = (Business) loginUser;
List<Movie> movies = ALL_MOVIES.get(business);
for (Movie movie : movies) {
if(movie.getName().contains(movieName)) {
return movie;
}
}
return null;
}
影片修改功能
/** 影片修改功能 */ private static void updateMovie() { System.out.println("================修改电影===================="); Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); if(movies.size() == 0) { System.out.println("当期无片可以修改~~"); return; } // 2、让用户选择需要下架的电影名称 while (true) { System.out.println("请您输入需要修改的电影名称:"); String movieName = SYS_SC.nextLine(); // 3、去查询有没有这个影片对象。 Movie movie = getMovieByName(movieName); if(movie != null){ // 修改它 System.out.println("请您输入修改后的片名:"); String name = SYS_SC.nextLine(); System.out.println("请您输入修改后主演:"); String actor = SYS_SC.nextLine(); System.out.println("请您输入修改后时长:"); String time = SYS_SC.nextLine(); System.out.println("请您输入修改后票价:"); String price = SYS_SC.nextLine(); System.out.println("请您输入修改后票数:"); String totalNumber = SYS_SC.nextLine(); // 200\n while (true) { try { System.out.println("请您输入修改后的影片放映时间:"); String stime = SYS_SC.nextLine(); movie.setName(name); movie.setActor(actor); movie.setPrice(Double.valueOf(price)); movie.setTime(Double.valueOf(time)); movie.setNumber(Integer.valueOf(totalNumber)); movie.setStartTime(sdf.parse(stime)); System.out.println("恭喜您,您成功修改了该影片了!!!"); showBusinessInfos(); return; // 直接退出去 } catch (Exception e) { e.printStackTrace(); LOGGER.error("时间解析出了毛病"); } } }else { System.out.println("您的店铺没有上架该影片!"); System.out.println("请问继续修改吗?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } }
展示全部商家和其排片信息
/**
用户功能:展示全部商家和其排片信息
*/
private static void showAllMovies() {
System.out.println("=============展示全部商家排片信息=================");
ALL_MOVIES.forEach((business, movies) -> {
System.out.println(business.getShopName() + "\t\t电话:" + business.getPhone() + "\t\t地址:" + business.getAddress());
System.out.println("\t\t\t片名\t\t\t主演\t\t时长\t\t评分\t\t票价\t\t余票数量\t\t放映时间");
for (Movie movie : movies) {
System.out.println("\t\t\t" + movie.getName()+"\t\t\t" + movie.getActor()+ "\t\t" + movie.getTime()
+ "\t\t" + movie.getScore() + "\t\t" + movie.getPrice() + "\t\t" + movie.getNumber() + "\t\t"
+ sdf.format(movie.getStartTime()));
}
});
}
用户购票功能
/** 用户购票功能 ALL_MOVIES = {b1=[p1,p2,p3,..] , b2=[p2,p3,...]} */ private static void buyMovie() { showAllMovies(); System.out.println("=============用户购票功能================="); while (true) { System.out.println("请您输入需要买票的门店:"); String shopName = SYS_SC.nextLine(); // 1、查询是否存在该商家。 Business business = getBusinessByShopName(shopName); if(business == null){ System.out.println("对不起,没有该店铺!请确认"); }else { // 2、此商家全部的排片 List<Movie> movies = ALL_MOVIES.get(business); // 3、判断是否存在上映的电影 if(movies.size() > 0) { // 4、开始进行选片购买 while (true) { System.out.println("请您输入需要购买电影名称:"); String movieName = SYS_SC.nextLine(); // 去当前商家下,查询该电影对象。 Movie movie = getMovieByShopAndName(business, movieName); if(movie != null){ // 开始购买 while (true) { System.out.println("请您输入要购买的电影票数:"); String number = SYS_SC.nextLine(); int buyNumber = Integer.valueOf(number); // 判断电影是否购票 if(movie.getNumber() >= buyNumber){ // 可以购买了 // 当前需要花费的金额 double money = BigDecimal.valueOf(movie.getPrice()).multiply(BigDecimal.valueOf(buyNumber)) .doubleValue(); if(loginUser.getMoney() >= money){ // 终于可以买票了 System.out.println("您成功购买了"+ movie.getName() + buyNumber + "张票!总金额是:" + money); // 更新自己的金额 更新商家的金额 loginUser.setMoney(loginUser.getMoney() - money); business.setMoney(business.getMoney() + money); movie.setNumber(movie.getNumber() - buyNumber); Customer c = (Customer) loginUser; // 记录购买电影的信息 // 第一个参数是购买的电影,第二个参数是没有评价的标记! c.getBuyMovies().put(movie.getName(), false); return;// 结束方法 }else { // 钱不够! System.out.println("是否继续~~"); System.out.println("是否继续买票?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } }else { // 票数不够 System.out.println("您当前最多可以购买:" + movie.getNumber()); System.out.println("是否继续买票?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } }else { System.out.println("电影名称有毛病~~"); } } }else { System.out.println("该电影院关门了~~~"); System.out.println("是否继续买票?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } } }
根据商家店铺名称查询商家对象
/**
根据商家店铺名称查询商家对象
* @return
*/
public static Business getBusinessByShopName(String shopName){
Set<Business> businesses = ALL_MOVIES.keySet();
for (Business business : businesses) {
if(business.getShopName().equals(shopName)){
return business;
}
}
return null;
}
根据商家和电影名称查询电影对象
public static Movie getMovieByShopAndName(Business business , String name){
List<Movie> movies = ALL_MOVIES.get(business);
for (Movie movie : movies) {
if(movie.getName().contains(name)){
return movie;
}
}
return null;
}
Movie 类
public class Business extends User{ // 店铺名称 private String shopName; // 店铺地址 private String address; public String getShopName() { return shopName; } public void setShopName(String shopName) { this.shopName = shopName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
User 类
/** 用户类(客户和商家的父类 ) */ public class User { private String loginName; // 假名 不能重复 private String userName; // 真名 private String passWord; private char sex; private String phone; private double money; public User(){ } public User(String loginName, String userName, String passWord, char sex, String phone, double money) { this.loginName = loginName; this.userName = userName; this.passWord = passWord; this.sex = sex; this.phone = phone; this.money = money; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } }
Business 类
public class Business extends User{ // 店铺名称 private String shopName; // 店铺地址 private String address; public String getShopName() { return shopName; } public void setShopName(String shopName) { this.shopName = shopName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Customer 类
import java.util.HashMap; import java.util.Map; /** 客户角色 */ public class Customer extends User{ // 定义一个属性存储购买记录。 private Map<String, Boolean> buyMovies = new HashMap<>(); public Map<String, Boolean> getBuyMovies() { return buyMovies; } public void setBuyMovies(Map<String, Boolean> buyMovies) { this.buyMovies = buyMovies; } }
功能实现类
import com.itheima.bean.Business; import com.itheima.bean.Customer; import com.itheima.bean.Movie; import com.itheima.bean.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; public class MovieSystem { /** 定义系统的数据容器用户存储数据 1、存储很多用户(客户对象,商家对象) */ public static final List<User> ALL_USERS = new ArrayList<>(); /** 2、存储系统全部商家和其排片信息 。 商家1 = [p1,p2,p3,...] 商家2 = [p2,p3,...] ... */ public static final Map<Business, List<Movie>> ALL_MOVIES = new HashMap<>(); public static final Scanner SYS_SC = new Scanner(System.in); // 定义一个静态的User类型的变量记住当前登录成功的用户对象 public static User loginUser; public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); public static final Logger LOGGER = LoggerFactory.getLogger("MovieSystem.class"); /** 3、准备一些测试数据 */ static { Customer c = new Customer(); c.setLoginName("zyf888"); c.setPassWord("123456"); c.setUserName("黑马刘德华"); c.setSex('男'); c.setMoney(10000); c.setPhone("110110"); ALL_USERS.add(c); Customer c1 = new Customer(); c1.setLoginName("gzl888"); c1.setPassWord("123456"); c1.setUserName("黑马关之琳"); c1.setSex('女'); c1.setMoney(2000); c1.setPhone("111111"); ALL_USERS.add(c1); Business b = new Business(); b.setLoginName("baozugong888"); b.setPassWord("123456"); b.setUserName("黑马包租公"); b.setMoney(0); b.setSex('男'); b.setPhone("110110"); b.setAddress("火星6号2B二层"); b.setShopName("甜甜圈国际影城"); ALL_USERS.add(b); // 注意,商家一定需要加入到店铺排片信息中去 List<Movie> movies = new ArrayList<>(); ALL_MOVIES.put(b , movies); // b = [] Business b2 = new Business(); b2.setLoginName("baozupo888"); b2.setPassWord("123456"); b2.setUserName("黑马包租婆"); b2.setMoney(0); b2.setSex('女'); b2.setPhone("110110"); b2.setAddress("火星8号8B八层"); b2.setShopName("巧克力国际影城"); ALL_USERS.add(b2); // 注意,商家一定需要加入到店铺排片信息中去 List<Movie> movies3 = new ArrayList<>(); ALL_MOVIES.put(b2 , movies3); // b2 = [] } public static void main(String[] args) { showMain(); } /** 首页展示 */ private static void showMain() { while (true) { System.out.println("===============黑马电影首页================="); System.out.println("1、登录"); System.out.println("2、用户注册"); System.out.println("3、商家注册"); System.out.println("请输入操作命令:"); String command = SYS_SC.nextLine(); switch (command) { case "1": // 登录了 login(); break; case "2": // break; case "3": break; default: System.out.println("命令有误,请确认!"); } } } /** 登录功能 */ private static void login() { while (true) { System.out.println("请您输入登录名称:"); String loginName = SYS_SC.nextLine(); System.out.println("请您输入登录密码:"); String passWord = SYS_SC.nextLine(); // 1、根据登录名称查询用户对象。 User u = getUserByLoginName(loginName); // 2、判断用户对象是否存在,存在说明登录名称正确了 if(u != null){ // 3、比对密码是否正确 if(u.getPassWord().equals(passWord)){ // 登录成功了:... loginUser = u; // 记住登录成功的用户 LOGGER.info(u.getUserName() +"登录了系统~~~"); // 判断是用户登录的,还是商家登录的。 if(u instanceof Customer) { // 当前登录的是普通用户 showCustomerMain(); }else { // 当前登录的肯定是商家用户 showBusinessMain(); } return; }else { System.out.println("密码有毛病~~"); } }else { System.out.println("登录名称错误,请确认"); } } } /** 商家的后台操作界面 */ private static void showBusinessMain() { while (true) { System.out.println("============黑马电影商家界面==================="); System.out.println(loginUser.getUserName() + (loginUser.getSex()=='男'? "先生":"女士" + "欢迎您进入系统")); System.out.println("1、展示详情:"); System.out.println("2、上架电影:"); System.out.println("3、下架电影:"); System.out.println("4、修改电影:"); System.out.println("5、退出:"); System.out.println("请输入您要操作的命令:"); String command = SYS_SC.nextLine(); switch (command){ case "1": // 展示全部排片信息 showBusinessInfos(); break; case "2": // 上架电影信息 addMovie(); break; case "3": // 下架电影信息 deleteMovie(); break; case "4": // 修改电影信息 updateMovie(); break; case "5": System.out.println(loginUser.getUserName() +"请您下次再来啊~~~"); return; // 干掉方法 default: System.out.println("不存在该命令!!"); break; } } } /** 影片修改功能 */ private static void updateMovie() { System.out.println("================修改电影===================="); Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); if(movies.size() == 0) { System.out.println("当期无片可以修改~~"); return; } // 2、让用户选择需要下架的电影名称 while (true) { System.out.println("请您输入需要修改的电影名称:"); String movieName = SYS_SC.nextLine(); // 3、去查询有没有这个影片对象。 Movie movie = getMovieByName(movieName); if(movie != null){ // 修改它 System.out.println("请您输入修改后的片名:"); String name = SYS_SC.nextLine(); System.out.println("请您输入修改后主演:"); String actor = SYS_SC.nextLine(); System.out.println("请您输入修改后时长:"); String time = SYS_SC.nextLine(); System.out.println("请您输入修改后票价:"); String price = SYS_SC.nextLine(); System.out.println("请您输入修改后票数:"); String totalNumber = SYS_SC.nextLine(); // 200\n while (true) { try { System.out.println("请您输入修改后的影片放映时间:"); String stime = SYS_SC.nextLine(); movie.setName(name); movie.setActor(actor); movie.setPrice(Double.valueOf(price)); movie.setTime(Double.valueOf(time)); movie.setNumber(Integer.valueOf(totalNumber)); movie.setStartTime(sdf.parse(stime)); System.out.println("恭喜您,您成功修改了该影片了!!!"); showBusinessInfos(); return; // 直接退出去 } catch (Exception e) { e.printStackTrace(); LOGGER.error("时间解析出了毛病"); } } }else { System.out.println("您的店铺没有上架该影片!"); System.out.println("请问继续修改吗?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } } /** 影片下架功能 */ private static void deleteMovie() { System.out.println("================下架电影===================="); Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); if(movies.size() == 0) { System.out.println("当期无片可以下架~~"); return; } // 2、让用户选择需要下架的电影名称 while (true) { System.out.println("请您输入需要下架的电影名称:"); String movieName = SYS_SC.nextLine(); // 3、去查询有没有这个影片对象。 Movie movie = getMovieByName(movieName); if(movie != null){ // 下架它 movies.remove(movie); System.out.println("您当前店铺已经成功下架了:" + movie.getName()); showBusinessInfos(); return; }else { System.out.println("您的店铺没有上架该影片!"); System.out.println("请问继续下架吗?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } } /** 去查询当前商家下的排片 */ public static Movie getMovieByName(String movieName){ Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); for (Movie movie : movies) { if(movie.getName().contains(movieName)) { return movie; } } return null; } /** 商家进行电影上架 Map<Business , List<Movie>> ALL_MOVIES u1 = [p1,p2,p3] u2 = [p1,p2,p3] */ private static void addMovie() { System.out.println("================上架电影===================="); // 根据商家对象(就是登录的用户loginUser),作为Map集合的键 提取对应的值就是其排片信息 :Map<Business , List<Movie>> ALL_MOVIES Business business = (Business) loginUser; List<Movie> movies = ALL_MOVIES.get(business); System.out.println("请您输入新片名:"); String name = SYS_SC.nextLine(); System.out.println("请您输入主演:"); String actor = SYS_SC.nextLine(); System.out.println("请您输入时长:"); String time = SYS_SC.nextLine(); System.out.println("请您输入票价:"); String price = SYS_SC.nextLine(); System.out.println("请您输入票数:"); String totalNumber = SYS_SC.nextLine(); // 200\n while (true) { try { System.out.println("请您输入影片放映时间:"); String stime = SYS_SC.nextLine(); // public Movie(String name, String actor, double time, double price, int number, Date startTime) // 封装成电影对象 ,加入集合movices中去 Movie movie = new Movie(name, actor ,Double.valueOf(time) , Double.valueOf(price) , Integer.valueOf(totalNumber) , sdf.parse(stime)); movies.add(movie); System.out.println("您已经成功上架了:《" + movie.getName() + "》"); return; // 直接退出去 } catch (ParseException e) { e.printStackTrace(); LOGGER.error("时间解析出了毛病"); } } } /** 定义一个静态的Map集合存储电影的评分 */ public static final Map<String , List<Double>> MOVIES_SCORE = new HashMap<>(); /** 展示商家的详细:展示当前商家的信息。 */ private static void showBusinessInfos() { System.out.println("================商家详情界面================="); LOGGER.info(loginUser.getUserName() +"商家,正在看自己的详情~~~"); // 根据商家对象(就是登录的用户loginUser),作为Map集合的键 提取对应的值就是其排片信息 :Map<Business , List<Movie>> ALL_MOVIES Business business = (Business) loginUser; System.out.println(business.getShopName() + "\t\t电话:" + business.getPhone() + "\t\t地址:" + business.getAddress() + "\t\t余额:" + business.getMoney()); List<Movie> movies = ALL_MOVIES.get(business); if(movies.size() > 0) { System.out.println("片名\t\t\t主演\t\t时长\t\t评分\t\t票价\t\t余票数量\t\t放映时间"); for (Movie movie : movies) { System.out.println(movie.getName()+"\t\t\t" + movie.getActor()+ "\t\t" + movie.getTime() + "\t\t" + movie.getScore() + "\t\t" + movie.getPrice() + "\t\t" + movie.getNumber() + "\t\t" + sdf.format(movie.getStartTime())); } }else { System.out.println("您的店铺当前无片在放映~~~~"); } } /** 客户操作界面 */ private static void showCustomerMain() { while (true) { System.out.println("============黑马电影客户界面==================="); System.out.println(loginUser.getUserName() + (loginUser.getSex()=='男'? "先生":"女士" + "欢迎您进入系统" + "\t余额:" + loginUser.getMoney())); System.out.println("请您选择要操作的功能:"); System.out.println("1、展示全部影片信息功能:"); System.out.println("2、根据电影名称查询电影信息:"); System.out.println("3、评分功能:"); System.out.println("4、购票功能:"); System.out.println("5、退出系统:"); System.out.println("请输入您要操作的命令:"); String command = SYS_SC.nextLine(); switch (command){ case "1": // 展示全部排片信息 showAllMovies(); break; case "2": break; case "3": // 评分功能 scoreMovie(); showAllMovies(); break; case "4": // 购票功能 buyMovie(); break; case "5": return; // 干掉方法 default: System.out.println("不存在该命令!!"); break; } } } private static void scoreMovie() { // 1、查询当前登录成功的用户历史购买记录,看哪些电影是它可以评分的。 Customer c = (Customer) loginUser; Map<String, Boolean> movies = c.getBuyMovies(); if(movies.size() == 0 ){ System.out.println("当前您没有看过电影,不能评价!"); return; } // 买过了 ,看哪些电影是它可以评分的。 movies.forEach((name, flag) -> { if(flag){ System.out.println(name +"此电影已评价"); }else { System.out.println("请您对:" + name +"进行打分(0-10):"); double score = Double.valueOf(SYS_SC.nextLine()); // 先根据电影名称拿到评分数据 List<Double> scores = MOVIES_SCORE.get(name); // MOVIES_SCORE = [ 名称=[10] , ... ] if(scores == null){ // 说明此电影是第一次评价 scores = new ArrayList<>(); scores.add(score); MOVIES_SCORE.put(name , scores); }else { scores.add(score); } movies.put(name, true); } }); } /** 用户购票功能 ALL_MOVIES = {b1=[p1,p2,p3,..] , b2=[p2,p3,...]} */ private static void buyMovie() { showAllMovies(); System.out.println("=============用户购票功能================="); while (true) { System.out.println("请您输入需要买票的门店:"); String shopName = SYS_SC.nextLine(); // 1、查询是否存在该商家。 Business business = getBusinessByShopName(shopName); if(business == null){ System.out.println("对不起,没有该店铺!请确认"); }else { // 2、此商家全部的排片 List<Movie> movies = ALL_MOVIES.get(business); // 3、判断是否存在上映的电影 if(movies.size() > 0) { // 4、开始进行选片购买 while (true) { System.out.println("请您输入需要购买电影名称:"); String movieName = SYS_SC.nextLine(); // 去当前商家下,查询该电影对象。 Movie movie = getMovieByShopAndName(business, movieName); if(movie != null){ // 开始购买 while (true) { System.out.println("请您输入要购买的电影票数:"); String number = SYS_SC.nextLine(); int buyNumber = Integer.valueOf(number); // 判断电影是否购票 if(movie.getNumber() >= buyNumber){ // 可以购买了 // 当前需要花费的金额 double money = BigDecimal.valueOf(movie.getPrice()).multiply(BigDecimal.valueOf(buyNumber)) .doubleValue(); if(loginUser.getMoney() >= money){ // 终于可以买票了 System.out.println("您成功购买了"+ movie.getName() + buyNumber + "张票!总金额是:" + money); // 更新自己的金额 更新商家的金额 loginUser.setMoney(loginUser.getMoney() - money); business.setMoney(business.getMoney() + money); movie.setNumber(movie.getNumber() - buyNumber); Customer c = (Customer) loginUser; // 记录购买电影的信息 // 第一个参数是购买的电影,第二个参数是没有评价的标记! c.getBuyMovies().put(movie.getName(), false); return;// 结束方法 }else { // 钱不够! System.out.println("是否继续~~"); System.out.println("是否继续买票?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } }else { // 票数不够 System.out.println("您当前最多可以购买:" + movie.getNumber()); System.out.println("是否继续买票?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } }else { System.out.println("电影名称有毛病~~"); } } }else { System.out.println("该电影院关门了~~~"); System.out.println("是否继续买票?y/n"); String command = SYS_SC.nextLine(); switch (command) { case "y": break; default: System.out.println("好的!"); return; } } } } } public static Movie getMovieByShopAndName(Business business , String name){ List<Movie> movies = ALL_MOVIES.get(business); for (Movie movie : movies) { if(movie.getName().contains(name)){ return movie; } } return null; } /** 根据商家店铺名称查询商家对象 * @return */ public static Business getBusinessByShopName(String shopName){ Set<Business> businesses = ALL_MOVIES.keySet(); for (Business business : businesses) { if(business.getShopName().equals(shopName)){ return business; } } return null; } /** 用户功能:展示全部商家和其排片信息 */ private static void showAllMovies() { System.out.println("=============展示全部商家排片信息================="); ALL_MOVIES.forEach((business, movies) -> { System.out.println(business.getShopName() + "\t\t电话:" + business.getPhone() + "\t\t地址:" + business.getAddress()); System.out.println("\t\t\t片名\t\t\t主演\t\t时长\t\t评分\t\t票价\t\t余票数量\t\t放映时间"); for (Movie movie : movies) { System.out.println("\t\t\t" + movie.getName()+"\t\t\t" + movie.getActor()+ "\t\t" + movie.getTime() + "\t\t" + movie.getScore() + "\t\t" + movie.getPrice() + "\t\t" + movie.getNumber() + "\t\t" + sdf.format(movie.getStartTime())); } }); } public static User getUserByLoginName(String loginName){ for (User user : ALL_USERS) { // 判断这个用户的登录名称是否是我们想要的 if(user.getLoginName().equals(loginName)){ return user; } } return null; // 查询此用户登录名称 } }
File类创建对象
方法名称 | 说明 |
---|---|
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 从父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 |
绝对路径和相对路径
绝对路径:从盘符开始
File file1 = new File(“D:\\itheima\\a.txt”);
相对路径:不带盘符,默认直接到当前工程下的目录寻找文件。
File file3 = new File(“模块名\\a.txt”);
代码演示
import java.io.File; /** 目标:学会创建File对象定位操作系统的文件(文件 文件夹的) */ public class FileDemo { public static void main(String[] args) { // 1、创建File对象(指定了文件的路径) // 路径写法: D:\resources\xueshan.jpeg // D:/resources/xueshan.jpeg // File.separator // File f = new File("D:\\resources\\xueshan.jpeg"); // File f = new File("D:/resources/xueshan.jpeg"); File f = new File("D:" + File.separator+"resources"+ File.separator +"xueshan.jpeg"); long size = f.length(); // 是文件的字节大小 System.out.println(size); // 2、File创建对象,支持绝对路径 支持相对路径(重点) File f1 = new File("D:\\resources\\beauty.jpeg"); // 绝对路径 System.out.println(f1.length()); // 相对路径:一般定位模块中的文件的。 相对到工程下!! File f2 = new File("file-io-app/src/com/itheima/data.txt"); System.out.println(f2.length()); // 3、File创建对象 ,可以是文件也可以是文件夹 File f3 = new File("D:\\resources"); System.out.println(f3.exists()); // 判断这个路径是否存在,这个文件夹存在否 } }
方法名称 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为文件夹 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或文件夹的名称 |
public long lastModified() | 返回文件最后修改的时间毫秒值 |
代码演示
import java.io.File; import java.text.SimpleDateFormat; /** 目标:File类的获取功能的API - public String getAbsolutePath() :返回此File的绝对路径名字符串。 - public String getPath() : 获取创建文件对象的时候用的路径 - public String getName() : 返回由此File表示的文件或目录的名称。 - public long length() : 返回由此File表示的文件的长度。 */ public class FileDemo02 { public static void main(String[] args) { // 1.绝对路径创建一个文件对象 File f1 = new File("D:/resources/xueshan.jpeg"); // a.获取它的绝对路径。 System.out.println(f1.getAbsolutePath()); // b.获取文件定义的时候使用的路径。 System.out.println(f1.getPath()); // c.获取文件的名称:带后缀。 System.out.println(f1.getName()); // d.获取文件的大小:字节个数。 System.out.println(f1.length()); // 字节大小 // e.获取文件的最后修改时间 long time = f1.lastModified(); System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time)); // f、判断文件是文件还是文件夹 System.out.println(f1.isFile()); // true System.out.println(f1.isDirectory()); // false System.out.println("-------------------------"); File f2 = new File("file-io-app\\src\\data.txt"); // a.获取它的绝对路径。 System.out.println(f2.getAbsolutePath()); // b.获取文件定义的时候使用的路径。 System.out.println(f2.getPath()); // c.获取文件的名称:带后缀。 System.out.println(f2.getName()); // d.获取文件的大小:字节个数。 System.out.println(f2.length()); // 字节大小 // e.获取文件的最后修改时间 long time1 = f2.lastModified(); System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time1)); // f、判断文件是文件还是文件夹 System.out.println(f2.isFile()); // true System.out.println(f2.isDirectory()); // false System.out.println(f2.exists()); // true File file = new File("D:/"); System.out.println(file.isFile()); // false System.out.println(file.isDirectory()); // true System.out.println(file.exists()); // true File file1 = new File("D:/aaa"); System.out.println(file1.isFile()); // false System.out.println(file1.isDirectory()); // false System.out.println(file1.exists()); // false } }
File类创建文件的功能
方法名称 | 说明 |
---|---|
public boolean createNewFile() | 创建一个新的空的文件 |
public boolean mkdir() | 只能创建一级文件夹 |
public boolean mkdirs() | 可以创建多级文件夹 |
File类删除文件的功能
方法名称 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或空文件夹 |
代码演示
import java.io.File; import java.io.IOException; /** 目标:File类的创建和删除的方法 - public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时, 创建一个新的空文件。 (几乎不用的,因为以后文件都是自动创建的!) - public boolean delete() :删除由此File表示的文件或目录。 (只能删除空目录) - public boolean mkdir() :创建由此File表示的目录。(只能创建一级目录) - public boolean mkdirs() :可以创建多级目录(建议使用的) */ public class FileDemo03 { public static void main(String[] args) throws IOException { File f = new File("file-io-app\\src\\com\\itheima\\data.txt"); // a.创建新文件,创建成功返回true ,反之 ,不需要这个,以后文件写出去的时候都会自动创建 System.out.println(f.createNewFile()); File f1 = new File("file-io-app\\src\\com\\itheima\\data.txt"); System.out.println(f1.createNewFile()); // (几乎不用的,因为以后文件都是自动创建的!) // b.mkdir创建一级目录 File f2 = new File("D:/resources/aaa"); System.out.println(f2.mkdir()); // c.mkdirs创建多级目录(重点) File f3 = new File("D:/resources/ccc/ddd/eee/ffff"); // System.out.println(f3.mkdir()); System.out.println(f3.mkdirs()); // 支持多级创建 // d.删除文件或者空文件夹 System.out.println(f1.delete()); File f4 = new File("D:/resources/xueshan.jpeg"); System.out.println(f4.delete()); // 占用一样可以删除 // 只能删除空文件夹,不能删除非空文件夹. File f5 = new File("D:/resources/aaa"); System.out.println(f5.delete()); } }
方法名称 | 说明 |
---|---|
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 |
public File[] listFiles()(常用) | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) |
listFiles方法注意事项
代码演示
import java.io.File; import java.util.Arrays; /** 目标:File针对目录的遍历 - public String[] list(): 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 - public File[] listFiles()(常用): 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) */ public class FileDemo04 { public static void main(String[] args) { // 1、定位一个目录 File f1 = new File("D:/resources"); String[] names = f1.list(); for (String name : names) { System.out.println(name); } // 2.一级文件对象 // 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) File[] files = f1.listFiles(); for (File f : files) { System.out.println(f.getAbsolutePath()); } } }
什么是方法递归?
递归的形式
方法递归存在的问题?
代码演示
/** 递归的形式 */ public class RecursionDemo01 { public static void main(String[] args) { test2(); } public static void test(){ System.out.println("=======test被执行========"); test(); // 方法递归 直接递归形式 } public static void test2(){ System.out.println("=======test2被执行========"); test3(); // 方法递归 间接递归 } private static void test3() { System.out.println("=======test3被执行========"); test2(); } }
递归案例-计算1-n的阶乘
代码演示
测试代码
/** 目标:递归的算法和执行流程 */ public class RecursionDemo02 { public static void main(String[] args) { System.out.println(f(5)); } public static int f(int n){ if(n == 1){ return 1; }else { return f(n - 1) * n; } } }
输出结果
120
递归解决问题的思路
递归算法三要素大体
递归的公式: f(n) = f(n-1) * n
递归的终结点:f(1)
递归的方向必须走向终结点:
f(5) = f(4) * 5
f(4) = f(3) * 4
f(3) = f(2) * 3
f(2) = f(1) * 2
f(1) = 1
递归求阶乘的执行流程
代码演示
测试代码
/** 目标:1 - n求和 */ public class RecursionDemo03 { public static void main(String[] args) { System.out.println(f(5)); } public static int f(int n){ if(n == 1){ return 1; }else { return f(n - 1) + n; } } }
输出结果
15
分析:
整体来看,每一天都是做同一个事件,典型的规律化问题,考虑递归三要素:
递归公式:
f(x) - f(x)/2 - 1 = f(x+1)
2f(x) - f(x) - 2 = 2f(x + 1)
f(x) = 2f(x + 1) + 2
递归终结点:f(10) = 1
递归方向:略
代码演示
测试代码
/** 目标 猴子吃桃。 公式(合理的): f(x) - f(x)/2 - 1 = f(x+1) 2f(x) - f(x) - 2 = 2f(x + 1) f(x) = 2f(x + 1) + 2 求f(1) = ? 终结点: f(10) = 1 递归的方向:合理的 */ public class RecursionDemo04 { public static void main(String[] args) { System.out.println(f(1)); System.out.println(f(2)); System.out.println(f(3)); } public static int f(int n){ if(n == 10){ return 1; }else { return 2 * f(n + 1) + 2; } } }
输出结果
1534
766
382
需求:
分析:
代码演示
测试代码
import java.io.File; import java.io.IOException; /** 目标:去D判断搜索 idea64.exe文件 */ public class RecursionDemo05 { public static void main(String[] args) { // 2、传入目录 和 文件名称 searchFile(new File("D:/") , "Typora.exe"); } /** * 1、搜索某个目录下的全部文件,找到我们想要的文件。 * @param dir 被搜索的源目录 * @param fileName 被搜索的文件名称 */ public static void searchFile(File dir,String fileName){ // 3、判断dir是否是目录 if(dir != null && dir.isDirectory()){ // 可以找了 // 4、提取当前目录下的一级文件对象 File[] files = dir.listFiles(); // null [] // 5、判断是否存在一级文件对象,存在才可以遍历 if(files != null && files.length > 0) { for (File file : files) { // 6、判断当前遍历的一级文件对象是文件 还是 目录 if(file.isFile()){ // 7、是不是咱们要找的,是把其路径输出即可 if(file.getName().contains(fileName)){ System.out.println("找到了:" + file.getAbsolutePath()); // 启动它。 try { Runtime r = Runtime.getRuntime(); r.exec(file.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); } } }else { // 8、是文件夹,需要继续递归寻找 searchFile(file, fileName); } } } }else { System.out.println("对不起,当前搜索的位置不是文件夹!"); } } }
输出结果
找到了:D:\DevelopSoftware\Typora\Typora.exe
需求:
/** 目标:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶, 请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。 答案:15瓶 3盖子 1瓶子 */ public class RecursionDemo06 { // 定义一个静态的成员变量用于存储可以买的酒数量 public static int totalNumber; // 总数量 public static int lastBottleNumber; // 记录每次剩余的瓶子个数 public static int lastCoverNumber; // 记录每次剩余的盖子个数 public static void main(String[] args) { // 1、拿钱买酒 buy(10); System.out.println("总数:" + totalNumber); System.out.println("剩余盖子数:" + lastCoverNumber); System.out.println("剩余瓶子数:" + lastBottleNumber); } public static void buy(int money){ // 2、看可以立马买多少瓶 int buyNumber = money / 2; // 5 totalNumber += buyNumber; // 3、把盖子 和瓶子换算成钱 // 统计本轮总的盖子数 和 瓶子数 int coverNumber = lastCoverNumber + buyNumber; int bottleNumber = lastBottleNumber + buyNumber; // 统计可以换算的钱 int allMoney = 0; if(coverNumber >= 4){ allMoney += (coverNumber / 4) * 2; } lastCoverNumber = coverNumber % 4; if(bottleNumber >= 2){ allMoney += (bottleNumber / 2) * 2; } lastBottleNumber = bottleNumber % 2; if(allMoney >= 2){ buy(allMoney); } } }
需求:
分析:
测试代码
import java.io.File; /** 目标:删除非空文件夹 */ public class RecursionDemo07 { public static void main(String[] args) { deleteDir(new File("D:/new")); } /** 删除文件夹,无所谓里面是否有内容,都可以删除 * @param dir */ public static void deleteDir(File dir){ // 1、判断dir存在且是文件夹 if(dir != null && dir.exists() && dir.isDirectory()){ // 2、提取一级文件对象。 File[] files = dir.listFiles(); // 3、判断是否存在一级文件对象,存在则遍历全部的一级文件对象去删除 if(files != null && files.length > 0){ // 里面有内容 for (File file : files) { // 4、判断file是文件还是文件夹,文件直接删除 if(file.isFile()){ file.delete(); }else { // 递归删除 deleteDir(file); } } } // 删除自己 dir.delete(); } } }
字符集基础知识
计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1)
二进制是可以转换成十进制的
11 = 1*2^1 + 1*2^0 = 2 + 1 = 3
10 = 1*2^1 + 0*2^0 = 2 + 0 = 2
01 = 0*2^1 + 1*2^0 = 0 + 1 = 1
00 = 0*2^1 + 0*2^0 = 0 + 0 = 0
01100001 = 97
01100010 = 98
计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。
ASCII 字符集
01100001 = 97 => a
01100010 = 98 => b
GBK
window系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。
GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
Unicode码表
注意事项
汉字存储和展示过程解析
String编码
方法名称 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
String解码
构造器 | 说明 |
---|---|
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的 String |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来构造新的 String |
测试代码
import java.util.Arrays; /** 目标:学会自己进行文字的编码和解码,为以后可能用到的场景做准备。 */ public class Test { public static void main(String[] args) throws Exception { // 1、编码:把文字转换成字节(使用指定的编码) String name = "abc我爱你中国"; // byte[] bytes = name.getBytes(); // 以当前代码默认字符集进行编码 (UTF-8) byte[] bytes = name.getBytes("GBK"); // 指定编码 System.out.println(bytes.length); System.out.println(Arrays.toString(bytes)); // 2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码 ) // String rs = new String(bytes); // 默认的UTF-8 String rs = new String(bytes, "GBK"); // 指定GBK解码 System.out.println(rs); } }
IO流也称为输入、输出流,就是用来读写数据的。
IO 流的分类
流的四大类
字节输入流:FileInputStream
构造器 | 说明 |
---|---|
public FileInputStream(File file) | 创建字节输入流管道与源文件对象接通 |
public FileInputStream(String pathname) | 创建字节输入流管道与源文件路径接通 |
方法名称 | 说明 |
---|---|
public int read() | 每次读取一个字节返回,如果字节已经没有可读的返回-1 |
public int read(byte[] buffer) | 每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
代码演示
InputStream is = new FileInputStream("D:\Code\data.txt");
int b1 = is.read();
System.out.println((char)b1);
int b2 = is.read();
System.out.println((char)b2);
//使用循环改进
int b;
while (( b = is.read() ) != -1){
System.out.print((char) b);
}
}
每次读取一个字节存在什么问题?
方法名称 | 说明 |
---|---|
public int read(byte[] buffer) | 每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
代码演示
InputStream is = new FileInputStream("D:\Code\data.txt"); byte[] buffer = new byte[3]; // 3B int len = is.read(buffer); System.out.println("读取了几个字节:" + len); //可能出现后面读取的字节不足现象 String rs = new String(buffer); System.out.println(rs); int len2 = is.read(buffer); System.out.println("读取了几个字节:" + len2); //读取多少倒出多少 String rs2 = new String(buffer, 0 ,len2); System.out.println(rs2); //改进使用循环,每次读取一个字节数组 byte[] buffer = new byte[3]; int len; // 记录每次读取的字节数。 while ((len = is.read(buffer)) != -1) { // 读取多少倒出多少 System.out.print(new String(buffer, 0 , len)); }
每次读取一个字节数组存在什么问题?
如何使用字节输入流读取中文内容输出不乱码呢?
直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
方式一
方法名称 | 说明 |
---|---|
public int read(byte[] buffer) | 每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
File f = new File("D:\Code\data.txt");
InputStream is = new FileInputStream(f);
//定义一个字节数组与文件的大小刚刚一样大。
byte[] buffer = new byte[(int) f.length()];
int len = is.read(buffer);
System.out.println("读取了多少个字节:" + len);
System.out.println("文件大小:" + f.length());
System.out.println(new String(buffer));
方式二
方法名称 | 说明 |
---|---|
public byte[] readAllBytes() throws IOException | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
注意:此方法jdk9之后才支持
//读取全部字节数组
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
字节输出流:FileOutputStream
构造器 | 说明 |
---|---|
public FileOutputStream(File file) | 创建字节输出流管道与源文件对象接通 |
public FileOutputStream(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileOutputStream(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileOutputStream(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
字节输出流(FileOutputStream)写数据出去的API
方法名称 | 说明 |
---|---|
public void write(int a) | 写一个字节出去 |
public void write(byte[] buffer) | 写一个字节数组出去 |
public void write(byte[] buffer , int pos , int len) | 写一个字节数组的一部分出去。 |
流的关闭与刷新
方法 | 说明 |
---|---|
flush() | 刷新流,还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
代码演示
//追加数据管道 OutputStream os = new FileOutputStream("D:\Code\data.txt" , true); //先清空之前的数据,写新数据进入 OutputStream os = new FileOutputStream("D:\Code\data.txt"); os.write('a'); os.write(98); os.write('李'); //中文占三个字节 os.write("\r\n".getBytes()); //换行 byte[] buffer = {'a' , 97, 98, 99}; os.write(buffer); os.write("\r\n".getBytes()); // 换行 //写数据必须,刷新数据 可以继续使用流 os.flush(); //释放资源,包含了刷新的!关闭后流不可以使用了 os.close();
需求:
思路:
代码演示
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; /** * 目标:学会使用字节流完成文件的复制(支持一切文件类型的复制) */ public class Copy { public static void main(String[] args) { try { // 1、创建一个字节输入流管道与原视频接通 InputStream is = new FileInputStream("D:\Avi\old.avi"); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream("D:\Avi\new.avi"); // 3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; int len; // 记录每次读取的字节数。 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0 , len); } System.out.println("复制完成了!"); // 4、关闭流。 os.close(); is.close(); } catch (Exception e){ e.printStackTrace(); } } }
字节流适合做一切文件数据的拷贝吗?
任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式、编码一致没有任何问题。
try-catch-finally 格式
try{
...
} catch (Exception e){
e.printStackTrace();
} finally{
...
}
代码演示
InputStream is = null; OutputStream os = null; try { //1、创建一个字节输入流管道与原视频接通 is = new FileInputStream("D:\Avi\old.avi"); //2、创建一个字节输出流管道与目标文件接通 os = new FileOutputStream("D:\Avi\new.avi"); //3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; //记录每次读取的字节数 int len; while ((len = is.read(buffer)) != -1){ os.write(buffer, 0 , len); } System.out.println("复制完成了!"); } catch (Exception e){ e.printStackTrace(); } finally { try { //4、关闭流。 if(os!=null)os.close(); } catch (IOException e) { e.printStackTrace(); } try { if(is != null) is.close(); } catch (IOException e) { e.printStackTrace(); } }
注意事项
finally 代码块不要要加 retrun
public static int test(int a , int b){
try {
int c = a / b;
return c;
}catch (Exception e){
e.printStackTrace();
return -111111; // 计算出现bug.
}finally {
System.out.println("--finally--");
// 哪怕上面有return语句执行,也必须先执行完这里才可以!
// 开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
return 100;
}
}
jdk7 改进方案
try ( // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作) // 1、创建一个字节输入流管道与原视频接通 InputStream is = new FileInputStream("D:\Avi\old.avi"); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream("D:\Avi\new.avi"); ) { // 3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; int len; // 记录每次读取的字节数。 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0 , len); } System.out.println("复制完成了!"); } catch (Exception e){ e.printStackTrace(); }
jdk9 改进方案(了解即可)
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作) // 1、创建一个字节输入流管道与原视频接通 InputStream is = new FileInputStream("D:\Avi\old.avi"); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream("D:\Avi\new.avi"); try ( is ; os ) { // 3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; int len; // 记录每次读取的字节数。 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0 , len); } System.out.println("复制完成了!"); } catch (Exception e){ e.printStackTrace(); }
注意事项
public abstract class InputStream implements Closeable {}
public abstract class OutputStream implements Closeable, Flushable{}
代码演示
try (
MyConnection connection = new MyConnection(); // 最终会自动调用资源的close方法
) {
}catch (Exception e){
e.printStackTrace();
}
class MyConnection implements AutoCloseable{
@Override
public void close() throws IOException {
System.out.println("连接资源被成功释放了!");
}
}
import java.io.*; /** 目标:拷贝文件夹 */ public class CopyFile { public static void main(String[] args) { copy(new File("D:\\old") , new File("D:\\new")); } public static void copy(File src , File dest){ // 1、判断源目录是否存在 if(src!= null && src.exists() && src.isDirectory()){ // 2、目标目录需要创建一下 D:\new\resources File destOne = new File(dest , src.getName()); destOne.mkdirs(); // 3、提取原目录下的全部一级文件对象 File[] files = src.listFiles(); // 4、判断是否存在一级文件对象 if(files != null && files.length > 0) { // 5、遍历一级文件对象 for (File file : files) { // 6、判断是文件还是文件夹,是文件直接复制过去 if(file.isFile()){ copyFile(file, new File(destOne , file.getName())); }else { // 7、当前遍历的是文件夹,递归复制 copy(file, destOne); } } } } } public static void copyFile(File srcFile, File destFile){ try ( // 1、创建一个字节输入流管道与原视频接通 InputStream is = new FileInputStream(srcFile); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream(destFile); ) { // 3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; int len; // 记录每次读取的字节数。 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0 , len); } } catch (Exception e){ e.printStackTrace(); } } }
字节流读取中文输出会存在什么问题?
会乱码。或者内存溢出。
读取中文输出,哪个流更合适,为什么?
字符流更合适,最小单位是按照单个字符读取的。
文件字符输入流:Reader
构造器 | 说明 |
---|---|
public FileReader(File file) | 创建字符输入流管道与源文件对象接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件路径接通 |
方法名称 | 说明 |
---|---|
public int read() | 每次读取一个字符返回,如果字符已经没有可读的返回-1 |
public int read(char[] buffer) | 每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1 |
代码演示
Reader fr = new FileReader("D:\Code\data.txt");
// 读取一个字符返回,没有可读的字符了返回-1
int code = fr.read();
System.out.print((char)code);
int code1 = fr.read();
System.out.print((char)code1);
// 使用循环读取字符
int code;
while ((code = fr.read()) != -1){
System.out.print((char) code);
}
字符流的好处
每次读取一个字符存在什么问题?
方法名称 | 说明 |
---|---|
public int read(char[] buffer) | 每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1 |
代码演示
// 1、创建一个文件字符输入流与源文件接通
Reader fr = new FileReader("D:\Code\data.txt");
// 2、用循环,每次读取一个字符数组的数据。 1024 + 1024 + 8
char[] buffer = new char[1024]; // 1K字符
int len;
while ((len = fr.read(buffer)) != -1) {
String rs = new String(buffer, 0, len);
System.out.print(rs);
}
每次读取一个字符数组的优势?
文件字符输出流:FileWriter
构造器 | 说明 |
---|---|
public FileWriter(File file) | 创建字符输出流管道与源文件对象接通 |
public FileWriter(File file,boolean append) | 创建字符输出流管道与源文件对象接通,可追加数据 |
public FileWriter(String filepath) | 创建字符输出流管道与源文件路径接通 |
public FileWriter(String filepath,boolean append) | 创建字符输出流管道与源文件路径接通,可追加数据 |
文件字符输出流(FileWriter)写数据出去的API
方法名称 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
void write(int c) | 写一个字符 |
流的关闭与刷新
方法 | 说明 |
---|---|
flush() | 刷新流,还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
代码演示
// 覆盖管道,每次启动都会清空文件之前的数据 Writer fw = new FileWriter("D:\Code\data.txt"); // 追加数据管道,每次启动不会清空文件之前的数据 Writer fw = new FileWriter("D:\Code\data.txt", true); fw.write(98); fw.write('a'); fw.write('徐'); fw.write("\r\n"); // 换行 fw.write("abc我是中国人"); fw.write("\r\n"); // 换行 char[] chars = "abc我是中国人".toCharArray(); fw.write(chars); fw.write("\r\n"); // 换行
字节缓冲流性能优化原理
构造器
构造器 | 说明 |
---|---|
public BufferedInputStream(InputStream is) | 可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能 |
public BufferedOutputStream(OutputStream os) | 可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
代码演示
// 创建一个字节输入流管道
InputStream is = new FileInputStream("D:\Avi\old.avi");
// 把原始的字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(is);
// 创建一个字节输出流管道与
OutputStream os = new FileOutputStream("D:\Avi\new.avi");
// 把字节输出流管道包装成高级的缓冲字节输出流管道
OutputStream bos = new BufferedOutputStream(os);
分别使用不同的方式复制大视频观察性能情况
需求:
分析:
代码演示
import java.io.*; /** 目标:利用字节流的复制统计各种写法形式下缓冲流的性能执行情况。 复制流: (1)使用低级的字节流按照一个一个字节的形式复制文件。 (2)使用低级的字节流按照一个一个字节数组的形式复制文件。 (3)使用高级的缓冲字节流按照一个一个字节的形式复制文件。 (4)使用高级的缓冲字节流按照一个一个字节数组的形式复制文件。 源文件:C:\course\3-视频\18、IO流-文件字节输出流FileOutputStream写字节数据出去.avi 目标文件:C:\course\ 小结: 使用高级的缓冲字节流按照一个一个字节数组的形式复制文件,性能好,建议开发使用! */ public class ByteBufferTimeDemo { private static final String SRC_FILE = "D:\\course\\基础加强\\day08-日志框架、阶段项目\\视频\\14、用户购票功能.avi"; private static final String DEST_FILE = "D:\\course\\"; public static void main(String[] args) { // copy01(); // 使用低级的字节流按照一个一个字节的形式复制文件:慢的让人简直无法忍受。直接被淘汰。 copy02(); // 使用低级的字节流按照一个一个字节数组的形式复制文件: 比较慢,但是还是可以忍受的! 6.29s // copy03(); // 缓冲流一个一个字节复制:很慢,不建议使用。 37.071s copy04(); // 缓冲流一个一个字节数组复制:飞快,简直太完美了(推荐使用) 1.399s } private static void copy04() { long startTime = System.currentTimeMillis(); try ( // 1、创建低级的字节输入流与源文件接通 InputStream is = new FileInputStream(SRC_FILE); // a.把原始的字节输入流包装成高级的缓冲字节输入流 InputStream bis = new BufferedInputStream(is); // 2、创建低级的字节输出流与目标文件接通 OutputStream os = new FileOutputStream(DEST_FILE + "video4.avi"); // b.把字节输出流管道包装成高级的缓冲字节输出流管道 OutputStream bos = new BufferedOutputStream(os); ) { // 3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; int len; // 记录每次读取的字节数。 while ((len = bis.read(buffer)) != -1){ bos.write(buffer, 0 , len); } } catch (Exception e){ e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("使用缓冲的字节流按照一个一个字节数组的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s"); } private static void copy03() { long startTime = System.currentTimeMillis(); try ( // 1、创建低级的字节输入流与源文件接通 InputStream is = new FileInputStream(SRC_FILE); // a.把原始的字节输入流包装成高级的缓冲字节输入流 InputStream bis = new BufferedInputStream(is); // 2、创建低级的字节输出流与目标文件接通 OutputStream os = new FileOutputStream(DEST_FILE + "video3.avi"); // b.把字节输出流管道包装成高级的缓冲字节输出流管道 OutputStream bos = new BufferedOutputStream(os); ){ // 3、定义一个变量记录每次读取的字节(一个一个字节的复制) int b; while ((b = bis.read()) != -1){ bos.write(b); } }catch (Exception e){ e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("使用缓冲的字节流按照一个一个字节的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s"); } private static void copy02() { long startTime = System.currentTimeMillis(); try ( // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作) // 1、创建一个字节输入流管道与原视频接通 InputStream is = new FileInputStream(SRC_FILE); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream(DEST_FILE + "video2.avi") ) { // 3、定义一个字节数组转移数据 byte[] buffer = new byte[1024]; int len; // 记录每次读取的字节数。 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0 , len); } } catch (Exception e){ e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("使用低级的字节流按照一个一个字节数组的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s"); } /** 使用低级的字节流按照一个一个字节的形式复制文件 */ private static void copy01() { long startTime = System.currentTimeMillis(); try ( // 1、创建低级的字节输入流与源文件接通 InputStream is = new FileInputStream(SRC_FILE); // 2、创建低级的字节输出流与目标文件接通 OutputStream os = new FileOutputStream(DEST_FILE + "video1.avi") ){ // 3、定义一个变量记录每次读取的字节(一个一个字节的复制) int b; while ((b = is.read()) != -1){ os.write(b); } }catch (Exception e){ e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("使用低级的字节流按照一个一个字节的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s"); } }
总结
字符缓冲流自带8K缓冲区
字符缓冲输入流
构造器 | 说明 |
---|---|
public BufferedWriter(Writer w) | 可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
字符缓冲输出流新增功能
方法 | 说明 |
---|---|
public void newLine() | 换行操作 |
字符缓冲输出流
构造器 | 说明 |
---|---|
public BufferedWriter(Writer w) | 可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
字符缓冲输出流新增功能
方法 | 说明 |
---|---|
public void newLine() | 换行操作 |
// 创建一个字符输入流 Reader fr = new FileReader("D:\Code\data.txt"); // 把低级的字符输入流包装成高级的缓冲字符输入流。 BufferedReader br = new BufferedReader(fr); // 创建一个字符输出流 Writer fw = new FileWriter("D:\Code\out.txt"); // 覆盖管道,每次启动都会清空文件之前的数据 Writer fw = new FileWriter("D:\Code\out.txt", true); // 追加数据 // 把低级的字符输出流包装成高级的缓冲字符输出流。 BufferedWriter bw = new BufferedWriter(fw); String line; while ((line = br.readLine()) != null){ System.out.println(line); } bw.write("两个黄鹂鸣翠柳"); bw.newLine(); // 换行 bw.write("一行白鹭上青天")
注意事项
Writer fw = new FileWriter("D:\Code\out.txt", true);
拷贝出师表
需求:
把《出师表》的文章顺序进行恢复到一个新文件中。
分析:
代码演示
import java.io.*; import java.util.*; /** 目标:完成出师表顺序的恢复,并存入到另一个新文件中去。 */ public class BufferedCharTest3 { public static void main(String[] args) { try( // 1、创建缓冲字符输入流管道与源文件接通 BufferedReader br = new BufferedReader(new FileReader("D:\\重装系统前\\、代码笔记\\黑马程序员\\Java基础\\配套笔记\\day20、IO流二\\代码\\io-app2\\src\\csb.txt")); // 5、定义缓冲字符输出管道与目标文件接通 BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\重装系统前\\、代码笔记\\黑马程序员\\Java基础\\配套笔记\\day20、IO流二\\代码\\io-app2\\src\\new.txt")); ) { // 2、定义一个List集合存储每行内容 List<String> data = new ArrayList<>(); // 3、定义循环,按照行读取文章 String line; while ((line = br.readLine()) != null){ data.add(line); } System.out.println(data); // 4、排序 // 自定义排序规则 List<String> sizes = new ArrayList<>(); Collections.addAll(sizes, "一","二","三","四","五","陆","柒","八","九","十","十一"); Collections.sort(data, new Comparator<String>() { @Override public int compare(String o1, String o2) { // o1 八...... // o2 柒...... return sizes.indexOf(o1.substring(0, o1.indexOf("."))) - sizes.indexOf(o2.substring(0, o2.indexOf("."))); } }); System.out.println(data); // 6、遍历集合中的每行文章写出去,且要换行 for (String datum : data) { bw.write(datum); bw.newLine(); // 换行 } } catch (Exception e) { e.printStackTrace(); } } }
问题引出
1、之前我们使用字符流读取中文是否有乱码?
2、如果代码编码和文件编码不一致,使用字符流直接读取还能不乱码吗?
构造器
字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流。
构造器 | 说明 |
---|---|
public InputStreamReader(InputStream is) | 可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样。 |
public InputStreamReader(InputStream is ,String charset) | 可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不乱码了(重点) |
代码演示
InputStream is = new FileInputStream("D:\\Code\\java-basics\\file\\test-ansi.txt");
Reader isr = new InputStreamReader(is , "GBK");
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
问题引入
1、如果需要控制写出去的字符使用的编码,怎么办?
构造器
字符输入转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流。
构造器 | 说明 |
---|---|
public OutputStreamWriter(OutputStream os) | 可以把原始的字节输出流按照代码默认编码转换成字符输出流。几乎不用。 |
public OutputStreamWriter(OutputStream os,String charset) | 可以把原始的字节输出流按照指定编码转换成字符输出流(重点) |
代码演示
OutputStream os = new FileOutputStream("D:\\Code\\java-basics\\file\\test-ansi.txt",true);
Writer osw = new OutputStreamWriter(os , "GBK");
BufferedWriter bw = new BufferedWriter(osw);
bw.newLine();
bw.write("苹果雪梨水蜜桃");
bw.close();
对象字节输出流
构造器 | 说明 |
---|---|
public ObjectOutputStream(OutputStream out) | 把低级字节输出流包装成高级的对象字节输出流 |
方法名称 | 说明 |
---|---|
public final void writeObject(Object obj) | 把对象写出去到对象序列化流的文件中去 |
学生对象
import java.io.Serializable; /** 对象如果要序列化,必须实现Serializable序列化接口。 */ public class Student implements Serializable { // 申明序列化的版本号码 // 序列化的版本号与反序列化的版本号必须一致才不会出错! private static final long serialVersionUID = 1; private String name; private String loginName; // transient修饰的成员变量不参与序列化了 private transient String passWord; private int age ; public Student(){ } public Student(String name, String loginName, String passWord, int age) { this.name = name; this.loginName = loginName; this.passWord = passWord; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", loginName='" + loginName + '\'' + ", passWord='" + passWord + '\'' + ", age=" + age + '}'; } }
注意事项
// 对象必须实现序列化接口
public class Student implements Serializable {}
// transient修饰的成员变量不参与序列化了
private transient String passWord;
// 序列化的版本号与反序列化的版本号必须一致才不会出错!
private static final long serialVersionUID = 1;
测试代码
import java.io.FileOutputStream; import java.io.ObjectOutputStream; /** 目标:学会对象序列化,使用 ObjectOutputStream 把内存中的对象存入到磁盘文件中。 transient修饰的成员变量不参与序列化了 对象如果要序列化,必须实现Serializable序列化接口。 申明序列化的版本号码 序列化的版本号与反序列化的版本号必须一致才不会出错! private static final long serialVersionUID = 1; */ public class ObjectOutputStreamDemo1 { public static void main(String[] args) throws Exception { // 1、创建学生对象 Student s = new Student("陈磊", "chenlei","1314520", 21); // 2、对象序列化:使用对象字节输出流包装字节输出流管道 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("io-app2/src/obj.txt")); // 3、直接调用序列化方法 oos.writeObject(s); // 4、释放资源 oos.close(); System.out.println("序列化完成了~~"); } }
对象字节输入流
构造器 | 说明 |
---|---|
public ObjectInputStream(InputStream out) | 把低级字节输如流包装成高级的对象字节输入流 |
方法名称 | 说明 |
---|---|
public Object readObject() | 把存储到磁盘文件中去的对象数据恢复成内存中的对象返回 |
代码演示
import java.io.FileInputStream; import java.io.ObjectInputStream; /** 目标:学会进行对象反序列化:使用对象字节输入流把文件中的对象数据恢复成内存中的Java对象。 */ public class ObjectInputStreamDemo2 { public static void main(String[] args) throws Exception { // 1、创建对象字节输入流管道包装低级的字节输入流管道 ObjectInputStream is = new ObjectInputStream(new FileInputStream("io-app2/src/obj.txt")); // 2、调用对象字节输入流的反序列化方法 Student s = (Student) is.readObject(); System.out.println(s); } }
PrintStream
构造器 | 说明 |
---|---|
public PrintStream(OutputStream os) | 打印流直接通向字节输出流管道 |
public PrintStream(File f) | 打印流直接通向文件对象 |
public PrintStream(String filepath) | 打印流直接通向文件路径 |
方法 | 说明 |
---|---|
public void print(Xxx xx) | 打印任意类型的数据出去 |
PrintWriter
构造器 | 说明 |
---|---|
public PrintWriter(OutputStream os) | 打印流直接通向字节输出流管道 |
public PrintWriter (Writer w) | 打印流直接通向字符输出流管道 |
public PrintWriter (File f) | 打印流直接通向文件对象 |
public PrintWriter (String filepath) | 打印流直接通向文件路径 |
方法 | 说明 |
---|---|
public void print(Xxx xx) | 打印任意类型的数据出去 |
PrintStream和PrintWriter的区别
代码演示
import java.io.PrintWriter; /** 目标:学会使用打印流 高效 方便写数据到文件。 */ public class PrintDemo1 { public static void main(String[] args) throws Exception { // 1、创建一个打印流对象 // PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt")); // PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt" , true)); // 追加数据,在低级管道后面加True // PrintStream ps = new PrintStream("io-app2/src/ps.txt" ); PrintWriter ps = new PrintWriter("io-app2/src/ps.txt"); // 打印功能上与PrintStream的使用没有区别 ps.println(97); ps.println('a'); ps.println(23.3); ps.println(true); ps.println("我是打印流输出的,我是啥就打印啥"); ps.close(); } }
输出语句的重定向
属于打印流的一种应用,可以把输出语句的打印位置改到文件。
import java.io.PrintStream; /** 目标:了解改变输出语句的位置到文件 */ public class PrintDemo2 { public static void main(String[] args) throws Exception { System.out.println("锦瑟无端五十弦"); System.out.println("一弦一柱思华年"); // 改变输出语句的位置(重定向) PrintStream ps = new PrintStream("io-app2/src/log.txt"); System.setOut(ps); // 把系统打印流改成我们自己的打印流 System.out.println("庄生晓梦迷蝴蝶"); System.out.println("望帝春心托杜鹃"); } }
Properties 属性集对象
其实就是一个Map集合,但是我们一般不会当集合使用,因为HashMap更好用。
Properties核心作用
Properties 的 API
Properties和IO流结合的方法:
方法 | 说明 |
---|---|
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
public Object setProperty(String key, String value) | 保存键值对(put) |
public String getProperty(String key) | 使用此属性列表中指定的键搜索属性值 (get) |
public Set stringPropertyNames() | 所有键的名称的集合 (keySet()) |
存储对象
import java.io.FileWriter; import java.util.Properties; /** 目标:Properties的概述和使用(框架底层使用,了解这个技术即可)(保存数据到属性文件) Properties: 属性集对象。 其实就是一个Map集合。也就是一个键值对集合,但是我们一般不会当集合使用, 因为有HashMap。 Properties核心作用: Properties代表的是一个属性文件,可以把键值对的数据存入到一个属性文件中去。 属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value。 大家在后期学的很多大型框架技术中,属性文件都是很重要的系统配置文件。 users.properties admin=123456 dlei=dlei 需求:使用Properties对象生成一个属性文件,里面存入用户名和密码信息。 Properties的方法: -- public Object setProperty(String key, String value) : 保存一对属性。 (put) -- public String getProperty(String key) : 使用此属性列表中指定的键搜索属性值 (get) -- public Set<String> stringPropertyNames() : 所有键的名称的集合 (keySet()) -- public void store(OutputStream out, String comments): 保存数据到属性文件中去 -- public void store(Writer fw, String comments): 保存数据到属性文件中去 小结: Properties可以保存键值对数据到属性文件 */ public class PropertiesDemo01 { public static void main(String[] args) throws Exception { // 需求:使用Properties把键值对信息存入到属性文件中去。 Properties properties = new Properties(); properties.setProperty("admin", "123456"); properties.setProperty("dlei", "003197"); properties.setProperty("heima", "itcast"); System.out.println(properties); /** 参数一:保存管道 字符输出流管道 参数二:保存心得 */ properties.store(new FileWriter("io-app2/src/users.properties") , "this is users!! i am very happy! give me 100!"); } }
保存的users.properties文件
#this is users!! i am very happy! give me 100!
#Sat Aug 14 16:21:04 CST 2021
dlei=003197
admin=123456
heima=itcast
获取对象
import java.io.FileReader; import java.util.Properties; /** 目标:Properties读取属性文件中的键值对信息。(读取) Properties的方法: -- public Object setProperty(String key, String value) : 保存一对属性。 -- public String getProperty(String key) :使用此属性列表中指定的键搜索属性值 -- public Set<String> stringPropertyNames() :所有键的名称的集合 -- public void store(OutputStream out, String comments):保存数据到属性文件中去 -- public synchronized void load(InputStream inStream):加载属性文件的数据到属性集对象中去 -- public synchronized void load(Reader fr):加载属性文件的数据到属性集对象中去 小结: 属性集对象可以加载读取属性文件中的数据!! */ public class PropertiesDemo02 { public static void main(String[] args) throws Exception { // 需求:Properties读取属性文件中的键值对信息。(读取) Properties properties = new Properties(); System.out.println(properties); // 加载属性文件中的键值对数据到属性对象properties中去 properties.load(new FileReader("io-app2/src/users.properties")); System.out.println(properties); String rs = properties.getProperty("dlei"); System.out.println(rs); String rs1 = properties.getProperty("admin"); System.out.println(rs1); } }
输出结果
{}
{dlei=003197, admin=123456, heima=itcast}
003197
123456
commons-io 概述
FileUtils 主要方法
方法名 | 说明 |
---|---|
String readFileToString(File file, String encoding) | 读取文件中的数据, 返回字符串 |
void copyFile(File srcFile, File destFile) | 复制文件。 |
void copyDirectoryToDirectory(File srcDir, File destDir) | 复制文件夹。 |
使用步骤
代码演示
import org.apache.commons.io.FileUtils; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; /** 目标:Commons-io包的使用介绍。 什么是Commons-io包? commons-io是apache开源基金组织提供的一组有关IO操作的类库, 可以挺提高IO功能开发的效率。commons-io工具包提供了很多有关io操作的类, 见下表: | 包 | 功能描述 | | ----------------------------------- | :------------------------------------------- | | org.apache.commons.io | 有关Streams、Readers、Writers、Files的工具类 | | org.apache.commons.io.input | 输入流相关的实现类,包含Reader和InputStream | | org.apache.commons.io.output | 输出流相关的实现类,包含Writer和OutputStream | | org.apache.commons.io.serialization | 序列化相关的类 步骤: 1. 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/ 2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中 3. 将commons-io-2.6.jar加入到classpath中 小结: IOUtils和FileUtils可以方便的复制文件和文件夹!! */ public class CommonsIODemo01 { public static void main(String[] args) throws Exception { // 1.完成文件复制! // IOUtils.copy(new FileInputStream("D:\\resources\\hushui.jpeg"), // new FileOutputStream("D:\\resources\\hushui2.jpeg")); // 2.完成文件复制到某个文件夹下! // FileUtils.copyFileToDirectory(new File("D:\\resources\\hushui.jpeg"), new File("D:/")); // 3.完成文件夹复制到某个文件夹下! // FileUtils.copyDirectoryToDirectory(new File("D:\\resources") , new File("D:\\new")); // FileUtils.deleteDirectory(new File("D:\\new")); // JDK1.7 自己也做了一些一行代码完成复制的操作:New IO的技术 // Files.copy(Path.of("D:\\resources\\hushui.jpeg"), Path.of("D:\\resources\\hushui3.jpeg")); FileUtils.deleteDirectory(new File("D:\\new")); } }
本文章参考B站 Java入门基础视频教程,java零基础自学首选黑马程序员Java入门教程(含Java项目和Java真题),仅供个人学习使用,部分内容为本人自己见解,与黑马程序员无关。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。