day01
今日内容 Object类(toString()和equals()方法) 日期相关类 Date类 SimpleDateFormat类 Calendar类 System类 StringBuilder类 包装类
Object类(toString()和equals()方法) 什么是Object类 Object是所有类的超(基)类,直接或者间接父类 java.lang包下的类,使用的时候不需要导包
* public String toString():将对象转为字符串 如果一个类没有重写Object类中的toString()方法,就会使用Object类中的继承下来的toString()方法 com.itheima.demo01.Object.Person@16e8e0a *com.itheima.demo01.Object.Person 全类名(包名+类名) *@ 分隔符 *16e8e0a 地址值(哈希值)十六进制表现形式 底层源代码: public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
继承自Object类中的toString方法获取的是地址值,对我们来说意义不大,所以我们一般都会重写toString()方法 我们以后自定义的类会重写toString()方法,包括JDK提供的大部分类(ArrayList、String)都重写了toString()方法。 重写的原则是:获取对象中的内容(成员变量的值),以后重写不需要手动重写,只需要使用快捷即可 alt+insert 选择toString * public boolean equals(Object obj):比较两个对象是否相等 // 如果一个方法的参数是类类型,那么传递的实际参数是该类的对象或者子类对象 public boolean equals(Object obj) {// 多态 Object obj = new Person(); return (this == obj); } 形参:形式参数,在方法定义的时候,小括号中的参数,由数据类型和变量名组成 实参:实际参数,在调用方法的时候,小括号中传递的参数,一般直接传递值 equals方法如果没有重写使用的也是Object类中继承下来的,该方法底层使用的是==去比较是否相等,意义也不太大, 既然底层使用的是==,为什么我不直接使用==呢,所以我们一般也会重写该方法。 重写的原则:是比较对象中的内容(成员变量)是否相等。
面试题: ==和equals的区别 ==既可以比较基本数据类型数据,也可以比较引用数据类型的数据,比较基本数据类型比较的是值是否相等,比较引用数据 类型比较的是地址值是否相等。 equals方法只能比较引用数据类型,不能比较基本数据类型,如果使用继承自Object类的equals方法比较引用数据类型比较的 是地址值,底层其实使用的是==,如果对equals重写之后,比较的是对象中的内容(String,ArrayList) 重写之后的equals方法解析 @Override public boolean equals(Object o) { // 提供判断的效率 if (this == o) return true; // 由于equals方法的参数是Object类型,它可以接收任意类型的对象 // 这是在转换的时候就必须进行一个健壮性判断,防止下面的类型转换出现异常 if (o == null || getClass() != o.getClass()) return false; // 向下转型,因为多态之后不能直接使用子类的特有成员(成员变量和成员方法) // 必须向下转换为具体的类型 Person person = (Person) o; // 如果年龄相同,并且姓名相同就认为是两个对象相同 // 年龄和姓名其实就是对象中的内容(成员变量) return age == person.age && Objects.equals(name, person.name); }
需求: 编写一个标准的Person类,其中有两个成员变量name和age 使用快捷键重写toString方法和equals方法,并理解为什么要重写这两个方法, 以及重写之后的代码的意思
日期相关类 Date类 毫秒值 1秒 = 1000 毫秒 一个日期的毫秒值是一个相对值,相对的是1970年1月1日 0时0分0秒以来所经历的时间
获取一个日期(当前系统时间)的毫秒值 long System.currentTimeMillis() 365 * 24 * 60 * 60 * 1000 Date类位于java.util包下,使用的时候必须导包 Date类是日期类,代表的是特定的瞬间(精确到毫秒值),可以代表过去,现在和将来 两个构造方法 Date() :构造的当前系统时间的Date对象 Date(long date) :构造指定毫秒值所对应的时间的Date对象 两个成员方法 long getTime() void setTime(long time) 两类功能 毫秒值转日期对象 Date(long date) void setTime(long time) 日期对象转毫秒值 long getTime() SimpleDateFormat类 DateFormat是一个抽象类,一般不直接使用它,而使用它的子类SimpleDateFormat 其中主要有两个功能:日期格式化和解析 格式化:将Date对象转为字符串 String format(Date date) 格式化得到的字符串和我们指定的模式(模板)一致 yyyy-MM-dd yyyy年MM月dd日 2018-10-20 2018年10月20日 解析:将日期字符串转为Date对象 Date parse(String dateStr) "2018年10月20日 14:23:40" "2018年10月17日 10:23:40" 注意: (1)解析传递的字符串不能是任意字符串,必须是日期类型的字符串,否则会发生解析异常(错误) (2)给定的日期字符串必须匹配指定的模式,否则不能进行解析,也会发生解析异常
两个构造方法 SimpleDateFormat() SimpleDateFormat(String pattern) 两个方法 String format(Date date) Date parse(String dateStr) 需求: (1)将当前系统日期按照指定的格式打印,格式为:2018-10-20 14:47:40 (2)将指定的日期字符串("1980年10月2日 12:20:59")转为Date对象,并获取毫秒值 Calendar类 位于java.util包下,使用的时候需要导包,它是一个抽象类 日历类,可以操作单独的日期字段(年、月、日、时、分、秒) 获取Calendar的对象的方式: Calendar c = Calendar.getInstance(); 操作: 获取 int get(int field) 设置 void set(int field, int value) 将给定的日历字段设置为给定值。 void set(int year, int month, int date) 设置日历字段 YEAR、MONTH 和 DAY_OF_MONTH 的值。 void set(int year, int month, int date, int hourOfDay, int minute) 设置日历字段 YEAR、MONTH、DAY_OF_MONTH、HOUR_OF_DAY 和 MINUTE 的值。 void set(int year, int month, int date, int hourOfDay, int minute, int second) 设置字段 YEAR、MONTH、DAY_OF_MONTH、HOUR、MINUTE 和 SECOND 的值。 增和减 void add(int field, int amount) amount: 正数 增 负数 减 和Date对象互转 Date getTime() void setTime(Date date) 注意: 月:Calendar.MONTH,0~11 0代表1月 1代表2月 依次类推 星期:Calendar.DAY_OF_WEEK 1~7 1代表星期天 2代表星期一 依次类推 7代表星期六
System类 位于java.lang包下,使用的时候不需要导包
两个方法 static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。 static long currentTimeMillis()
StringBuilder类 字符串,但是不要把它和String等价
String是不可变的字符串 String str = "hello"; System.out.println(str);// hello str += "world"; System.out.println(str);// helloworld StringBuilder是可以变的字符串 构造方法 StringBuilder() StringBuilder(String str) 常用方法 StringBuilder append(任意类型):向StringBuilder中添加元素,往后添加 StringBuilder insert(int offset, 任意类型) StringBuilder reverse() String toString() 功能总结: 添加 append和insert 反转 reverse 和String的互转 StringBuilder ->String * toString String ->StringBuilder * new StringBuilder(String str) * append(String str)
包装类 什么是包装类,其实就是8种基本数据类型对应的引用数据类型,它们各自都有一个对应的类,这些类就是包装类 变成了包装类之后里面就可以封装对基本数据类型更方便的操作(有方法和变量常量)
定义一个int类型的变量并赋值,判断该变量记录的值是否是int的范围 8种基本数据类型对应的包装类 基本数据类型 包装类 byte Byte short Short int Integer long Long float Float double Double boolean Boolean char Character 包装类基本上都是基本数据类型首字母小写变大写,但是有两个比较特别,int对应Integer,char对应Character 装箱和拆箱 装箱:将基本数据类型转为包装类型 * Integer(int value) * static Integer valueOf(int i) // 装箱 int ->Integer /*int i = 10; Integer in = new Integer(i);// 构造方法过时 System.out.println(in); // static Integer valueOf(int i) Integer in2 = Integer.valueOf(i); System.out.println(in2);*/ 拆箱:将包装类型转为基本数据类型 * int intValue() // 拆箱 Integer ->int Integer in = new Integer(10); int i = in.intValue(); System.out.println(i); Integer类 构造方法 Integer(int value):接收一个基本类型的数据转为Integer对象 Integer(String s) 成员方法 int intValue() static int parseInt(String s) String toString() static String toString(int i) static Integer valueOf(int i) static Integer valueOf(String s) 自动拆装箱 JDK1.5的新特性,从JDK1.5之后基本数据类型和引用数据类型之后的转换不需要我们手动调用方法来转换,可以自动转换了 // 自动装箱 int ->Integer int i = 10; // 底层进行如下操作:Integer in = Integer.valueOf(i); Integer in = i;// 底层其实调用的是valueOf(int i)方法 // 自动拆箱 Integer ->int Integer in = new Integer(20); // 底层进行如下操作:int i = in.intValue(); int i = in;// 底层其实调用的是intValue()方法 int和String的互转 int ->String + "" String -> int Integer.parseInt("10")
上午的学习目标 能够说出Object类的特点 能够重写Object类的toString方法 能够重写Object类的equals方法 能够使用日期类输出当前日期
下午的学习目标 能够使用将日期格式化为字符串的方法 能够使用将字符串转换成日期的方法 能够使用System类的数组复制方法 能够使用System类获取当前毫秒时刻值
day02
今日内容 Collection 迭代器 增强for 泛型(泛型类、泛型接口、泛型方法、泛型通配符) 斗地主案例
集合的分类 单列集合(collection)
双列集合(Map)
集合的由来 之前我们学习过很多的容器了,比如:变量,数组,StringBuilder等,为什么还需要一个新的容器(集合)呢 原因是之前的容器都有一个弊端,变量一次只能存储一个元素,如果有多个元素,那么变量就需要定义多个,如果元素 过多,变量数量太庞大,不适用;数组长度固定,如果存储的数据是变化的,那么数组也不太好用;StringBuilder存储 元素的时候所有的元素都会变成字符串,也不好用,所以出现的了新的容器(集合)。
动态初始化:指定数组的长度,由系统赋予默认值 数据类型[] 数组名 = new 数据类型[长度]; 静态初始化:指定数组中存储的元素,由系统计算长度 数据类型[] 数组名 = new 数据类型[]{元素1,元素2,....}; 数据类型[] 数组名 = {元素1,元素2,....};
数组的特点 (1)数组是一个容器,可以存储同一种类型的多个元素 (2)数组有整数索引,范围在0~数组名.length-1 (3)数组的长度固定 (4)数组既可以存储基本数据类型的数据,也可以存储引用数据类型的数据 int[] arr = {1,2,3}; Student[] stus = new Student[5];
集合的特点 (1)集合是一个容器,可以存储多个元素,但是存储的类型是否是同一种要看是否使用了泛型 如果没有使用泛型,默认是Object,此时可以存储不同类型 如果使用了泛型,只能存储泛型所规定的那一种类型 (2)集合有些有索引,有些没有索引 比如:Set体系的集合就没有索引 List体系的集合有索引,范围在0~长度-1 (3)集合的长度是可变的 (4)集合只能存储引用数据类型,如果存储基本数据类型也不会报错,因为会先自动装箱成为引用数据类型再存储
面试题: 数组和集合的区别 (1)数组的长度固定,集合的长度可变 (2)数组既可以存储基本数据类型数据,也可以存储引用数据类型 集合只能存储引用数据类型
学习集合不是学习某一个类,集合中包含了很多的类,而这些类不是没有关系的单独的类,它们之间其实都有联系(继承或者实现关系)这些集合的关系形成了集合的体系。
Collection集合的体系 Collection接口 |-List接口:有序(存储和取出的顺序一致),有索引,可以存储相同的元素 |-ArrayList类 |-LinkedList类 |-Vector类 |-Set接口:无序,无索引,不可以存储相同的元素 |-HashSet类 |-LinkedHashSet类 |-TreeSet类
Collection 是一个接口,是单列集合的顶层
Collection是单列集合的顶层接口,所以其中定义了最多的共性方法,所以学习了这些方法,所有集合的这些都学会了 然后再单独学习不同的集合特有的内容,这样就能把所有的常用集合掌握 常用方法 boolean add(E e) :添加指定元素 void clear() :清空集合,删除集合中的所有元素 boolean remove(Object o) :删除集合中指定的元素 boolean contains(Object o) :判断集合中是否包含指定的元素 boolean isEmpty() :判断集合中的是否有元素 int size() :获取集合中元素的个数 Object[] toArray() :将集合转为数组 选择题: 以下是Collection中定义的共性的方法有(AD) A:remove(Object o) B: get(int index) C: set(int index, E e) D: clear() 集合遍历的方式: 方式一:集合转数组(toArray()) 步骤: (1)创建一个集合 (2)向集合中添加一些元素(add) (3)调用集合中的toArray()方法将集合转为数组 (4)遍历数组 // (1)创建一个集合 Collection<String> coll = new ArrayList<String>(); // (2)向集合中添加一些元素(add) coll.add("hello"); coll.add("world"); coll.add("java"); coll.add("php"); // (3)调用集合中的toArray()方法将集合转为数组 Object[] objs = coll.toArray(); // (4)遍历数组 for(int i=0; i<objs.length; i++){ System.out.println(objs[i]); } 方式二:迭代器(Iterator) 步骤: (1)创建一个集合 (2)向集合中添加一些元素(add) (3)获取到迭代器,使用集合中的iterator() (4)使用while循环,在while的判断条件中使用迭代器的hasNext()方法判断是否还有元素 (5)在while循环的内部使用迭代器的next()方法获取指定的元素 // (1)创建一个集合 Collection<String> coll = new ArrayList<String>(); // (2)向集合中添加一些元素(add) coll.add("hello"); coll.add("world"); coll.add("java"); coll.add("php"); // (3)获取到迭代器,使用集合中的iterator() Iterator<String> it = coll.iterator(); // (4)使用while循环,在while的判断条件中使用迭代器的hasNext()方法判断是否还有元素 while(it.hasNext()){ // (5)在while循环的内部使用迭代器的next()方法获取指定的元素 String str = it.next(); System.out.println(str); } 需求: 创建一个Collection集合,泛型规定为String,向其中添加4个元素, 分别是:"hello","world","java","php"。使用迭代器的方式遍历集合 方式三:增强for // (1)创建一个集合 Collection<String> coll = new ArrayList<String>(); // (2)向集合中添加一些元素(add) coll.add("hello"); coll.add("world"); coll.add("java"); coll.add("php"); for(String str:coll){ System.out.println(str); } 需求: 创建一个Collection集合,泛型规定为String,向其中添加4个元素, 分别是:"hello","world","java","php"。使用增强for的方式遍历集合
迭代器Iterator 是一个取出集合中元素的工具
迭代器相当于抓娃娃的抓手,集合就相当于装娃娃的箱子,娃娃就相当于其中的元素 Iterator的方法 E next() :获取元素 boolean hasNext() :判断是否有元素 void remove() :删除元素,光标指到谁就删除谁
增强for JDK1.5的新特性(泛型、自动拆装箱)
格式: for(数据类型 变量名:Collection或数组){ // 变量名中记录的就是集合或者数组中的元素 System.out.println(变量名); }
泛型 广泛的类型,可以表示任意的类型,想让它是什么类型就可以是什么类型,是未知的类型,传递了什么类型就是什么类型。
泛型的好处: (1)避免了强制类型转换的麻烦 (2)将运行时可能出现的异常提前到了编译时期排除 泛型类的定义和使用 泛型类的定义,是将泛型(<泛型>)定义在类的后面 泛型类的泛型的确定是在创建对象的时候确定 比如: class ArrayList<E> class Student<E> 泛型方法的定义和使用 泛型方法的定义,是将泛型(<泛型>)定义在方法的声明上,修饰符和返回值类型之间 泛型方法的泛型的确定是在调用方法的时候确定 比如: public <T> T[] toArray(T[] a) { } public <T> int getSum(T t){ } 泛型接口的定义和使用 泛型接口的定义,是将泛型(<泛型>)定义在接口的后面 泛型接口的使用 方式一:编写一个类去实现接口,在实现接口的时候确定泛型 public class GenericInterfaceImpl1 implements GenericInterface<String>{ } 方式二:编写一个类去实现接口,实现类的泛型和接口的泛型一致,实现的时候不确定,创建实现类对象的时候确定 public class GenericInterfaceImpl2<I> implements GenericInterface<I> { } 方式二:编写一个类去实现接口,实现类的泛型和接口的泛型一致,实现的时候不确定,创建实现类对象的时候确定 public class GenericInterfaceImpl2<I> extends GenericInterface<I> { } 比如: interface List<E> interface GenericInterface<String> 泛型的通配符 ? <?>只能用在方法的参数中 泛型的上限:? extends E ,规定了上限为E,所以泛型只能是E或者E的子类 泛型的下限:? super E ,规定了下限为E,所以泛型只能是E或者E的父类
上午的学习目标 能够说出集合与数组的区别 说出Collection集合的常用功能 能够使用迭代器对集合进行取元素
下午和晚上的学习目标 能够使用集合存储自定义类型 能够使用foreach循环遍历集合 能够使用泛型定义集合对象 能够理解泛型上下限 能够阐述泛型通配符的作用
晚上的学习目标 能够说出使用StringBuilder类可以解决的问题 能够使用StringBuilder进行字符串拼接操作 能够说出8种基本类型对应的包装类名称 能够说出自动装箱、自动拆箱的概念 能够将字符串转换为对应的基本类型 能够将基本类型转换为对应的字符串
day03今日内容
数据结构 List子体系 Set子体系 难点:理解Set保证元素唯一性的原理 可变参数 Collections工具类 难点:比较器排序和自然排序
Collection集合的体系 Collection接口 |-List接口:有序(存储和取出的顺序一致),有索引,可以存储相同的元素 |-ArrayList类:底层是数组结构 |-LinkedList类:底层是双向链表结构 |-Vector类:底层是数组结构 |-Set接口:无序,无索引,不可以存储相同的元素 |-HashSet类 |-LinkedHashSet类:底层是双向链表结构(有序) |-TreeSet类:底层是红黑树结构
数据结构 栈 特点:先进后出 举例:子弹的弹夹 队列 特点:先进先出 举例:排列,火车过隧道 数组 特点:查询快,增删慢 链表 特点:查询慢,增删快 红黑树 特点:查询快,增删慢
List接口 List体系的特点 (1)有序(存储和取出的顺序一致) (2)有索引,有可以可根据索引进行操作的方法 (3)可以存储重复元素
List的特有方法 这些特有方法都是和索引有关的 增删改查 void add(int index,E e):在指定的索引处添加元素 E remove(int index):删除指定索引处的元素,并返回该被删除的元素 E set(int index,E e):修饰指定索引处的元素为新的值,并返回被修改的元素值 E get(int index):获取指定索引处的元素 List的遍历方式 Collection可以使用的三种方式,List也可以使用,因为它是子接口 (1)toArray() (2)迭代器iterator() (3)增强for (4)普通for,结合get(int index)和size()方法 【List子体系特有的遍历方式】 步骤: a:创建一个List集合 b:向集合中添加元素 c:遍历 // (1)创建一个集合 List<String> list = new ArrayList<String>(); // (2)向集合中添加一些元素(add) list.add("hello"); list.add("world"); list.add("java"); list.add("php"); // c:遍历 for(int i=0;i<list.size();i++){ String str = list.get(i); System.out.println(str); } LinkedList LinkedList implments List extends Collection 有操作首尾元素的特有方法 特有功能: 增 push(E e) addFirst(E e) addLast(E e)
今日内容 Map集合
Map集合体系 Map接口是双列集合的顶层接口,它和Collection接口没有继承关系,双列集合和单列集合是独立的两个体系, 它们的功能(方法)不太一样 Map集合的体系和Set集合的体系非常相似,因为Set集合底层就使用了Map集合,比如HashSet底层调用了HashMap
Set |-HashSet |-LinkedHashSet |-TreeSet Map |-HashMap:底层是哈希表=数组+单向链表/红黑树 |-LinkedHashMap:底层哈希表+双向链表 |-TreeMap Set集合的底层使用了Map集合,只用到了Key,所以Map集合的Key的特点和Set集合一致
Map接口的特点 (1)双列集合,存储的值有两列,第一列叫键(Key),第二列叫值(Value) (2)键和值是一一对应的,键只能对应唯一的值 (3)键不能重复,值可以重复 如果键重复,只会保留一个不会报错,值进行覆盖
add("hello"); add("world"); add("java"); // put("张三","西安"); put("张三","南京"); put("李四","北京"); put("王五","北京");
Map接口的常用方法 public V put(K key, V value) public V remove(Object key) public V get(Object key) boolean containsKey(Object key)
Set<K> keySet()
Map集合的遍历 方式一:通过键找值的方式,使用keySet()和get(Object k)方法 步骤: (1)首先通过keySet()方法获取到Map集合中所有的键,返回一个Set集合 (2)遍历(迭代器和增强for)Set集合,获取每一个键 (3)使用get(Object k)通过键获取值
方式二:通过键值对找键和值 步骤: (1)首先通过entrySet()方法获取到Map集合中所有的键值对对象(Entry),返回一个Set集合 (2)遍历(迭代器和增强for)Set集合,获取每一个键值对对象(Entry) (3)使用Entry中的方法getKey()和getValue()分别获取到Entry中的键和值 Set<Map.Entry<String, Integer>> 由于Entry是Map中的内部接口,所以在其他类中要使用Entry,没法直接看到,需要使用Map.Entry 由于Entry表示的是一个键值对,所以它的泛型就规定了键值对的键和值的类型,它的类型其实和Map集合一致
HashMap Map集合的数据结构都是针对键有效,特点也是针对键有效的 不能存储相同元素,无索引,无序
LinkedHashMap LinkedHashMap是HashMap的子类,它的操作和功能(方法)以及特点基本上和父类一致 除了它是有序的,而不是无序的
面试题: ArrayList和Vector StringBuilder和StringBuffer HashMap和Hashtable的区别 (1)HashMap线程不安全,线程不同步,没有加同步锁,效率高 Hashtable线程安全,线程同步,加了同步锁,效率低 (2)HashMap可以存储null键和null值,可以将null作为键或者值 Hashtable不可以存储null键和null值,不可以将null作为键或者值,否则会报空指针异常(NullPointerException)
上午的学习目标 能够使用集合工具类Collections addAll/shuffle/sort 能够使用Comparator比较器进行排序 能够说出Map集合特点 使用Map集合添加方法保存数据
下午的学习目标 使用”键找值”的方式遍历Map集合 使用”键值对”的方式遍历Map集合 能够使用HashMap存储自定义键值对的数据 能够使用HashMap编写斗地主洗牌发牌案例
删 pop() removeFirst() removeLast() 查 getFirst() getLast() Vector Vector是JDK1.0出现的,集合体系(框架)是JDK1.2出现的。 (1)演示Vector的特有方法 void addElement(E obj) :添加元素,相当于add(E e) E elementAt(int index) : 获取指定索引处的元素,相当于get(int index) Enumeration<E> elements() :获取枚举(迭代器) (2)Vector和ArrayList的区别 a:Vector原来的方法的名称比较长,ArrayList的方法比较短,ArrayList替代它 b:Vector是线程安全的(同步),加了同步锁,效率低 ArrayList是线程不安全的(不同步),没有加锁,效率高 (3)Enumeration和Iterator的区别 a:迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。 b:方法名称得到了改进。 Iterator hasNext() next() remove() Enumeration hasMoreElements() nextElement()
Set接口 Set接口中的方法和Collection接口中的方法一模一样,它没有特有方法,所以不用单独学习其中的方法。 Set集合的特点 (1)无序 (2)不能存储重复元素 (3)无索引,没有一个是和索引有关的方法
Set集合的使用和遍历 步骤: (1)创建集合对象 (2)添加元素,add (3)遍历,和Collection中的遍历方式一模一样 需求: 使用Set集合添加5个元素,分别是"hello","world","php","hello","java" 然后使用三种方式对其遍历,观察遍历之后的元素的特点(无序,不能重复) 哈希值 是一个十进制的整数,可以通过hashCode()方法获取到 如果hashCode()方法没有重写,hashCode()计算的哈希值可以反映内存地址值 但是如果hashCode()方法被重写,hashCode()计算的哈希值就不能反映内存地址值 Person p1 = new Person("张三",19); Person p2 = new Person("李四",30); System.out.println(p1 == p2); hashCode()也一般会被重写,重写的原则是根据对象中的成员变量,让不同的对象的哈希值尽量不同,就是为了提高效率。 相同的对象哈希值一定相同,不同的对象哈希值可能相同 如果哈希值不同绝对不是同一个对象 不同的对象如果哈希值相同,称为哈希碰撞或者哈希冲突 哈希表 哈希表 = 数组 + 链表/红黑树
HashSet保证元素唯一(元素不重复)的原理 使用add方法添加的时候,add方法的底层依赖了hashCode()和equals()方法 当通过add添加元素的时候,会先通过hashCode()方法计算该元素的哈希值,将哈希值和集合中的其他元素的哈希值进行 比较,如果没有哈希值相同的,直接添加,此时不会调用equals()方法。如果有相同的,此时会调用equals()方法来比较 其中的内容,如果为true,说明是同一个元素,则不添加,如果为false,说明不是同一个元素,则添加。 这里其实是两层过滤,hashCode()是第一层过滤(粗过滤),equals()是第二次过滤(完全过滤) 此时如果我们尽量让hashCode()确定元素是否相同,这样就可以减少equals()方法调用,就可以提高效率 如果我们要保证元素的唯一性就应该重写hashCode()和equals()方法 需求:使用HashSet集合存储4个学生对象,分别是["张三",10],["李四",20],["张三",10],["张三",18] 我们认为同姓名和同年龄的学生为同一个学生,只能存储一次。 单列集合的使用和操作基本上都是一样,因为它们有同一个体系,很多基本的方法都一样,不同的是各自集合的特点不一样, 我们必须抓住集合的特点,以便我们选择正确的集合进行使用。 ArrayList list = new ArrayList(); list.add(""); // 遍历 // toArray() // 迭代器 // 增强for
HashSet set = new HashSet() set.add(""); // 遍历 // toArray() // 迭代器 // 增强for LinkedHashSet 它的父类是HashSet,它的底层是使用哈希表(数组+链表/红黑树)+双向链表,比父类多了一个双向链表,可以保证有序 它的功能(方法)和特点基本上与父类一致,只有一点,它可以保证有序
总结: 存储相同元素,从List体系下选 如果增删多选LinkedList,否则选择ArrayList,如果不知道选什么就选ArrayList 保证没有相同元素,从Set体系下选 如果有保证有序选择LinkedHashSet,否则选择HashSet
Collections工具类 面试题:Collections和Collection的区别 Collections是集合操作的工具类 Collection是单列集合的顶层接口
工具类的特点: (1)其中的方法都是静态修饰的,为了方便调用 (2)构造方法隐藏(private) public static <T> boolean addAll(Collection<? super T> c,T... elements):往指定的集合对象中添加多个元素 public static void shuffle(List<?> list) public static <T extends Comparable<? super T>> void sort(List<T> list):排序,自然顺序升序排列 public static <T> void sort(List<T> list, Comparator<? super T> c) 注意: 使用该方法只能对List集合进行排序,并且List集合中存储的元素所在的类必须实现Comparable接口,重写compareTo(T o)方法 对JDK提供的类(String/Integer/Double)的对象进行排序不需要再实现该接口了,因为人家底层已经实现类,所以可以直接进行排序 如果使用sort方法对自定义的对象进行排序就必须实现接口重写方法。
排序方式 方式一:自然排序 参与排序的对象所在类实现Comparable接口,重写compareTo(T o)方法 compareTo(T o)方法中涉及两个东西:this和参数o 升序 this在前 o在后 降序 o在前 this在后
原集合 list03.add(new Person("王五",15)); list03.add(new Person("李四",20)); list03.add(new Person("张三",18)); 排序之后 list03.add(new Person("李四",20)); list03.add(new Person("张三",18)); list03.add(new Person("王五",15)); return o.getAge() - this.getAge(); 15 - 20 负数 20 - 18 正数 15 - 18 负数
如何排序和compareTo方法的返回值有关,是一个int类型的数据 正数 : 认为大,不交换顺序,往往右边(下边)放 0 : 认为相同,不交换顺序,在集合中是什么顺序就保持什么循序 负数 : 认为小,交换顺序,元素往左边(上边)放 方式二:比较器排序 实现Comparator接口,重写int compare(T o1,T o2)方法,然后创建比较器的对象传递到sort方法中
o1 相当于自然排序中的this o2 相当于自然排序中的o 升序 o1 - o2 降序 o2 - o1
这两种排序方式可以共存,如果两种都存在的时候,优先使用比较器排序。 (1)如果是对JDK提供的类的对象进行排序,默认有自然排序,已经定义了规则,但是如果定义的规则不是你想要的,此时只能 再去定义比较器排序,定义新的规则,将自然排序规则覆盖掉。 (2)如果是对自定义类的对象进行排序,可以选择自然排序和比较器排序,建议使用比较器排序
上午的学习目标 能够说出List集合特点 能够说出常见的数据结构 能够说出数组结构特点 能够说出栈结构特点 能够说出队列结构特点 能够说出单向链表结构特点
今日内容 异常 多线程入门
异常 什么是异常 在程序的编译和运行过程中出现的错误
异常的体系 Throwable类 |-Error:错误,非常严重的问题,比如:数据库崩溃,服务器宕机,内存溢出,不应该试图通过异常处理语句去进行处理 |-Exception:异常,非严重的问题,比如:空指针异常,索引越界异常,可以通过异常处理语句进行处理 |-编译时异常:Exception下除了RuntimeException及其子类之外的所有异常,都是编译时异常 在编译时期就必须处理,否则编译不通过 |-运行时异常:RuntimeException及其子类,都是运行时异常 在编译时期可以不处理,在运行时期才可能报出
Error:病 Exception:疾 电脑坏了 摔成两半 电脑蓝屏 Java是面向对象的语言,所有的问题在Java中都会封装成对象,对象也一定会有对应的类,异常也是这样的,研究每一个异常都会 有一个对应的类,其实我们是在研究异常类。
JVM默认处理异常的方式 (1)将异常的信息(异常的类型,异常出现的原因和位置)输出在控制台,以红色字体显示 (2)在异常出现的位置处终止程序的运行 throw关键字 只能在方法内部使用,throw不是一种处理异常的方式,我们可以将它看成一种制造异常的方式 只能抛出(throw)异常对象,而且一次只能抛出一个,抛出的异常可以是编译时异常对象也可以是运行时异常对象 异常处理的方式 方式一:声明抛出,throws 使用在方法的声明上,抛出的是异常类名,可以抛出多个,多个之间用逗号隔开
方式二:捕获处理,try...catch...finally 格式一: try{ // 可能出现异常的代码 }catch(异常类型 变量名){ // 异常处理逻辑 }catch(异常类型 变量名){ // 异常处理逻辑 } ... 格式二: try{ // 可能出现异常的代码 }catch(异常类型 变量名){ // 异常处理逻辑 }catch(异常类型 变量名){ // 异常处理逻辑 }finally { // 不论异常是否发生,最终一定会执行的代码,一般由于释放资源,做一些收尾工作 } 格式三: try{ // 可能出现异常的代码 }finally { // 不论异常是否发生,最终一定会执行的代码,一般由于释放资源,做一些收尾工作 } 格式的注意事项: (1)catch块可以出现多个 (2)格式只有上面三种,单独的try,catch或者finally不能使用,并且catch和finally两者不能单独组合一起使用 编译时异常可以throws也可以try...catch,当你不知道如何处理,或者你懒得处理,可以throws,想自己处理使用try...catch 运行时异常,在编译时期可以不处理,如何要处理一般选择try...catch,不选择throws Throwable中的常见方法 String getMessage():获取异常出现的原因 String toString():获取异常出现的类型和原因 void printStackTrace():打印异常出现的详细信息(异常出现的类型、原因和位置) JVM底层处理异常打印异常信息,调用的就是printStackTrace()方法 子类重写父类方法,遵循的原则 一大两同两小 一大:子类方法的权限必须大于等于父类方法的权限 两同: 方法名相同 参数列表相同 两小: 返回值类型:子类方法的返回值类型必须和父类方法的返回值类型一致或者比它小(只针对引用数据类型) 抛出的异常:子类方法抛出的异常必须小于等于父类方法抛出的异常 如果父类方法没有抛出异常,子类方法绝对不能抛出,只能try...catch 针对的是编译时异常 自定义异常 (1)自定义一个类继承Exception或者RuntimeException 如果是自定义的编译时异常,就继承Exception 如果是自定义的运行时异常,就继承RuntimeException (2)生成两个构造方法 一个空参 一个带字符串参数的构造,调用父类构造传递字符串参数
上午的学习目标 说出异常的分类 说出虚拟机处理异常的方式 列举出常见的三个运行期异常 能够使用try...catch关键字处理异常 能够使用throws关键字处理异常
今日内容 多线程 一堆概念 了解 线程的两种实现方式 重点 多线程安全问题出现和解决 重点
一堆概念 并发和并行 并发:同一时间段有多个线程同时在执行,但是在同一个时刻只会有一个线程执行(单核单线程的CPU) 10分 扫地 20分 洗衣服
30分这个时间段内 做了两件事情 ,但是我是一个人 并行:同一时间段有多个线程同时在执行,但是同一时刻也会有多个线程执行(多核多线程CPU) 10分 扫地 20分 洗衣服 这时间段内 做了两件事情 ,但是有两个人 进程和线程 进程:正在执行的程序,加载到内存,占用内存资源和CPU资源 线程:进程中的执行路径(单元),一个进程中至少会有一条线程,如果一条都没有,进程也会随之结束 多线程的好处: 效率高 线程之间互不干扰 线程的调度 抢夺式调度 谁抢夺到了CPU的执行权就执行,如果优先级越高抢夺到的几率越大 如果优先级相同抢夺到的几率相同 4个任务 2(a b)个人去抢
线程的两种实现方式 重点 方式一:继承Thread类 步骤: (1)自定义一个类,继承Thread类 (2)在自定义类中重写run方法 重写之后run方法的声明不能做任何改变, 必须和父类方法声明一致 如果run方法中有异常,不能抛,只能try...catch public void run() { // 多线程执行的主体(核心代码) }
(3)在测试类中,创建自定义类的对象 (4)使用自定义类对象调用start()方法开启新线程
面试题:开启线程是使用start()方法还是run()方法,它们有什么区别 开启线程调用start()方法 区别: start()两个作用,第一个开启新线程,第二调用run()方法执行线程的核心代码 run()只能执行线程的核心代码,但是不能开启新线程 方式二:实现Runnable接口 步骤: (1)自定义一个类实现Runnable接口 (2)在实现类中重写run方法 public void run() { } (3)在测试类中创建自定义类的对象 (4)创建Thread类的对象,将自定义类对象当做构造方法的参数进行传递 (5)使用Thread类的对象调用start()方法
Thread类 常用构造方法 Thread() Thread(String name) Thread(Runnable run) Thread(Runnable run,String name)
常用成员方法 void start():开启新线程,底层会去调用run方法 void run():线程执行的核心代码,通过start去调用 String getName():获取线程的名称 static Thread currentThread():获取当前正在执行该的线程 void setName(String name):给线程设置名称 static void sleep(long millis):使线程休眠(暂停)指定的毫秒数,时间到了会自动醒来(继续执行) 此方法会抛出一个编译时异常,编译时期必须处理
获取线程名称的两种方式 (1)直接调用getName() (2)先获取当前正在执行的线程对象,然后调用getName() Thread.currentThread().getName() 线程的名称如果没有设置,会有默认名称 普通线程:Thread-n ,n从0开始依次递增 主线程:main 这些线程都可以修改清楚 修改线程名称的两种方式: (1)调用setName(String name)方法 (2)生成一个带一个字符串参数的构造方法,通过构造方法传递名称
两种实现线程方式的区别 继承Thread类 缺点: (1)Java中的类只支持单一继承,如果一个类已经有了父类,就不能使用这种方式;或者说如果使用了这种方式 以后就再也不能继承其他类 (2)扩展性稍差,线程任何和开启新线程耦合在一起 优点: 继承了Thread类,就可以直接使用Thread类中的方法,使用稍方便
实现Runnable接口 缺点: 没有继承Thread类,不能直接使用Thread类中的方法,使用之前必须先获取当前正在执行的线程对象 Thread.currentThread() 优点: (1)打破了单一继承的局限性,即使有了父类,也可以使用这种方式 (2)扩展性强,线程任何和开启新线程进行了分离
使用匿名内部类实现多线程 回顾匿名内部类 格式: new 父类/接口(){ // 重写或者实现方法 }
相当于在创建父类的子类对象,或者是接口的实现类对象 需求: 使用匿名内部类开启两个线程,一个线程使用继承Thread的方式,另一个线程使用实现Runnable接口的方式 这两个线程执行的任务各不相同,继承Thread类的方式开启的线程,打印100次hello在控制台,另一个打印 100次world在控制台
线程安全问题的解决 方式一:同步代码块 格式: synchronized(锁对象){ // 可能出现线程安全问题的代码 // 其实就是操作了共享数据的代码 }
锁对象可以是任意对象,但是必须保证多个线程使用的锁对象是同一个 锁对象同一时刻只能被一个线程所拥有,其他线程只有等该线程释放了锁,获取了锁才能执行同步代码块中的代码 判断锁->获取锁->释放锁 方式二:同步方法 非静态的同步方法 默认锁:this 静态的同步方法 默认锁:当前类的字节码对象:当前类名.class
上午和下午的学习目标 说出进程的概念 说出线程的概念 能够开启新线程 能够理解并发与并行的区别 能够使用继承类的方式创建多线程 能够使用实现接口的方式创建多线程 能够说出实现接口方式的好处
今日内容 线程状态 线程通信 Lambda表达式
线程状态 在Thread的内部枚举中定义了6种状态,Thread.State 6种状态 NEW:新建,在创建线程对象的时候 RUNNABLE:可运行,线程抢夺到了CPU的执行权 BLOCKED:阻塞,线程没有抢夺到CPU的执行权,有执行资格 WAITING:无限等待,相当于调用wait()方法,必须调用notify或者notifyAll唤醒,否则一直等待 TIMED_WAITING:计时等待,相当于调用了sleep(时间)或者wait(时间) TERMINATED:死亡,线程执行结束,run方法执行完,或者调用了stop方法,或者出现异常终止
线程通信 等待唤醒机制,生成和消费者案例
notify():随机唤醒一个正在等待的线程 notifyAll():唤醒所有正在等待的线程 面试题:sleep和wait的区别 (1)sleep是Thread类中的方法,而且是静态方法,直接可以通过类名调用 wait方法是Object类中的方法,不是静态的,通过锁对象调用 (2)sleep的方法都必须传递了时间参数,如果传递是计时等待 wait有传递参数,也可以不传递参数,如果传递了时间参数,是计时等待,如果没有传递参数,是无限等待 (3)如果sleep和wait在同步中使用,sleep不会释放锁,wait会释放锁
需求: 使用线程池完成两个线程任务,任务各不相同,一个输出100次hello,另一个输出100次world
Lambda表达式的标准格式: (参数) -> {} :简化了匿名内部类的代码,它也是在创建接口的实现类对象
匿名内部类 new 接口名(){ // 实现抽象方法 修饰符 返回值类型 方法名(参数) { // 方法体 } } Lambda表达式 // 实现抽象方法 (参数) -> { // 方法体 }
创建接口(函数式接口,接口中只有一个抽象方法)的实现类对象
今日内容 File类 递归(难点) 文件(名称)过滤器 过滤器的原理(难点)
File类 File类的概述 File表示的是文件和文件夹(目录) E:\2018双元Java\02_JavaEE基础进阶\day08\avi
File的静态变量 static String pathSeparator :文件路径分隔符,Windows系统是分号(";"),Linux是冒号(":") static char pathSeparatorChar :Windows系统是分号(';'),Linux是冒号(':') static String separator:文件名称分割符,Windows系统是反斜杠("\"),Linux是正斜杠("/") static char separatorChar :Windows系统是反斜杠('\'),Linux是正斜杠('/') 绝对路径和相对路径 绝对路径 以盘符开头的完整路径 相对路径 不以盘符开头的路径,省略写法 对于文件路径来说,在idea中相对的是当前项目的根目录,而不是模块 day08\\day08课堂笔记.txt aa/Hello.java 相对路径 D:\\hehe\\World.java 绝对路径 File类的构造方法 File(String path) File(String parent,String child) File(File parent,String child) "c:\\a.txt" "c:\\" "a.txt" File类的常用方法 获取功能 String getAbsolutePath():返回绝对路径 String getPath():返回路径,返回的是构造方法中传递的路径,是相对就返回相对路径,是绝对就返回绝对路径 和toString()作用等价,toString()的底层调用的就是getPath()方法 String getName() :返回文件或者文件夹名称 long length():返回文件大小(长度,字节个数) 不能获取文件夹的大小 判断功能 boolean exists():判断文件或者文件夹是否存在 boolean isFile():判断是否是文件 boolean isDirectory():判断是否是文件夹(目录)
创建和删除功能 boolean createNewFile():创建文件,不能创建文件夹 boolean mkdir():创建单级空文件夹 boolean mkdirs():创建单级和多级文件夹 boolean delete():删除文件和空文件夹 删除不走回收站,不要拿重要的进行删除演示 需求: 在当前模块下创建一个a\b\HelloWorld.java文件,但是a文件夹也是不存在的 遍历功能 String[] list():获取指定文件夹下所有的文件和子文件夹名称,返回的是一个字符串数组(String[]) File[] listFiles():获取指定文件夹下所有的文件和子文件夹,返回的是文件对象数组(File[]) 注意: 如果遍历的是文件,或者不存在的目录,返回null,这时如果进行遍历,会抛出空指针异常 需求: 遍历获取当前模块下的所有文件和文件夹
递归 方法自身调用自身
直接调用 间接调用 递归使用的注意事项 1、递归一定要有条件限制,否则会报内存溢出错误 2、递归有条件限制,同时次数也不能太多,否则会报内存溢出错误 3、构造方法不能递归 使用递归计算1-n之间的积
文件(名称)过滤器 public File[] listFiles(FileFilter filter) {// filter就是文件过滤器 // 1、获取指定文件夹下所有的文件和子文件夹 // 这里的this代表的就是File file = new File("c:\abc"); // {a,b,abc.java,abc.txt} String ss[] = this.list(); // this所代表的是一个文件路径,或者是一个不存在的文件夹路径 if (ss == null) return null;
// 创建一个集合,用于存储满足条件的File对象 ArrayList<File> files = new ArrayList<>(); // 2、遍历数组,或者其中的每一个文件或者文件夹的名称 for (String s : ss) { // 3、 // s = a -> c:\\abc\\a -> File f = new File("c:\\abc\\a"); File f = new File(s, this);// File(String child, File parent) // 如果传递了过滤器,过滤与否就和filter.accept(f)的返回值有关 // 如果返回值为false的时候就过滤,为true就留下 if ((filter == null) || filter.accept(f)) files.add(f); } // 集合转数组 return files.toArray(new File[files.size()]); }
今日学习目标 能够说出File对象的创建方式 能够说出File类获取名称的方法名称 能够说出File类获取绝对路径的方法名称 能够说出File类获取文件大小的方法名称 能够说出File类判断是否是文件的方法名称 能够说出File类判断是否是文件夹的方法名称 能够辨别相对路径和绝对路径 能够遍历文件夹 能够解释递归的含义 能够使用递归的方式计算5的阶乘
今日内容 字节流 字符流 Properties集合
IO流的概述 什么是IO流 I是input的意思,输入 O是output的意思,输出 IO流就是输入输出流
IO流的作用 IO流用于处理设备(内存、硬盘、控制台、键盘、打印机)之间的数据传输 实现应用中可以进行文件的复制、文件上传、文件下载 如果只是IO流没有结合网络编程,只能处理一台计算机上的数据传输 如果IO流和网络编程技术结合,就可以再不同的计算机上进行数据传输 输入和输出的概念 固定是以内存为参数物 所以从内存到其他设备,都叫输出(写出);从其他设备到内存,都叫输入(读取) IO流的分类 流向 输入流 输出流 操作的数据的类型 字节流 字符流
字节流和字符流的顶层抽象父类 字节输入流:InputStream 字节输出流:OutputStream
字符输入流:Reader 字符输出流:Writer 字节、字符、字符串的区别 字节:byte,一个字节等于8个二进制位 0000 0001 -> 1 字符:char,'你' 字符串:由一串字符组成的一串字符数据,String,"你好" 字符是由字节组成,有些字符是一个字节组成,比如ASSIC码表中的数字(0,1,2...)和字母('A','a',...) 有些字符有多个字节组成,比如中文,'你' ,在GBK码表占两个字节,在UTF-8码表占三个字节 'A' 65 0100 0001 字节 ----解析(解码)----> 字符 ----解析(编码)----> 字节 IO流使用的时候三点注意事项 (1)IO流相关的类,都在java.io包下 (2)IO流相关的操作都有异常,需要处理异常(throws) (3)IO流操作完都需要进行关流,释放资源
字节输出流 OutputStream(抽象类) |-FileOutputStream(具体子类)
共性方法 void close() 关闭此输出流并释放与此流有关的所有系统资源。 void flush() 刷新此输出流并强制写出所有缓冲的输出字节。 void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。 void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 void write(int b) 将指定的字节写入此输出流。 构造方法 FileOutputStream(File file) FileOutputStream(String name) 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流
追加写(续写)和换行 续写 FileOutputStream(File file, boolean append) FileOutputStream(String name, boolean append) append为true时可以续写,如果为false不能续写 append为false时,如果文件不存在就创建,如果文件存在就会清空文件(不会创建新文件)再次写入新的数据 换行 fos.write("\r\n".getBytes());
字节输入流 InputStream(抽象类) |-FileInputStream(具体的子类)
共性方法 void close() 关闭此输入流并释放与该流关联的所有系统资源。 int read():返回读取到的字节,读取了数据之后光标会让后移动一位,读取到文件末尾返回-1 int read(byte[] b):将多个字节读取到指定的字节数组中,并返回读取的有效字节个数,读取到文件的末尾返回-1 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流 FileInputStream的构造方法 FileInputStream(File file) FileInputStream(String name):关联的读取的文件一定要存在,如果不存在就会抛FileNotFoundException异常
需求: 在当前的模块下创建(手动)一个b.txt文件,在文件中有abcde这5个字节, 使用一次读取多个字节的方式将内容读取出来,并转换为字符串打印在控制台。 c:\\a.txt -读取-> 内存 -写出-> d:\\a.txt
字符输入流 Reader(抽象类) |-InputStreamReader |-FileReader(具体的子类)
共性方法 void close() 关闭此输入流并释放与该流关联的所有系统资源。 int read():返回读取到的字符,读取了数据之后光标会让后移动一位,读取到文件末尾返回-1 int read(char[] chs):将多个字符读取到指定的字符数组中,并返回读取的有效字符个数,读取到文件的末尾返回-1 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流 FileReader的构造方法 FileReader(File file) FileReader(String name):关联的读取的文件一定要存在,如果不存在就会抛FileNotFoundException异常
字符流输出流 Writer(抽象类) |-OutputStreamWriter |-FileWriter(具体子类)
共性方法 void close() 关闭此输出流并释放与此流有关的所有系统资源。 void flush() 刷新此输出流并强制写出所有缓冲的输出字节。 void write(char[] chs) 将 b.length 个字符从指定的 char 数组写入此输出流。 void write(char[] chs, int off, int len) 将指定 char 数组中从偏移量 off 开始的 len 个字节写入此输出流。 void write(int b) 将指定的字符写入此输出流。 void write(String str):写出整个字符串 void write(String str, int off, int len):写出字符串的一部分 构造方法 FileWriter(File file) FileWriter(String name) 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流
追加写(续写)和换行 续写 FileWriter(File file, boolean append) FileWriter(String name, boolean append) append为true时可以续写,如果为false不能续写 append为false时,如果文件不存在就创建,如果文件存在就会清空文件(不会创建新文件)再次写入新的数据 换行 fos.write("\r\n");
close()和flush()的区别 (1)flush刷新缓冲区 close先刷新缓冲区,再释放资源 (2)flush调用完之后,流对象可以继续使用 close调用完之后,流对象不能继续使用,否则会报IOException,提示流已关闭
Properties 是一个双列集合,键和值存储的都是字符串 它的父类是Hashtable,所以Map接口中定义的共性方法Properties都可以使用,但是我们推荐使用特有方法 put ->setProperty(String key,String value) get ->getProperty(String key)
和IO流结合的方法 store load
今日学习目标 能够说出IO流的分类和功能 能够使用字节输出流写出数据到文件 能够使用字节输入流读取数据到程序 能够使用字节流完成文件的复制 能够使用FileWirter写数据到文件 能够说出FileWriter中关闭和刷新方法的区别 能够使用FileReader读数据 能够使用FileWriter写数据实现换行和追加写 能够使用Properties的load方法加载文件中配置信息
今日内容 字节流 字符流 Properties集合
IO流的概述 什么是IO流 I是input的意思,输入 O是output的意思,输出 IO流就是输入输出流
IO流的作用 IO流用于处理设备(内存、硬盘、控制台、键盘、打印机)之间的数据传输 实现应用中可以进行文件的复制、文件上传、文件下载 如果只是IO流没有结合网络编程,只能处理一台计算机上的数据传输 如果IO流和网络编程技术结合,就可以再不同的计算机上进行数据传输 输入和输出的概念 固定是以内存为参数物 所以从内存到其他设备,都叫输出(写出);从其他设备到内存,都叫输入(读取) IO流的分类 流向 输入流 输出流 操作的数据的类型 字节流 字符流
字节流和字符流的顶层抽象父类 字节输入流:InputStream 字节输出流:OutputStream
字符输入流:Reader 字符输出流:Writer 字节、字符、字符串的区别 字节:byte,一个字节等于8个二进制位 0000 0001 -> 1 字符:char,'你' 字符串:由一串字符组成的一串字符数据,String,"你好" 字符是由字节组成,有些字符是一个字节组成,比如ASSIC码表中的数字(0,1,2...)和字母('A','a',...) 有些字符有多个字节组成,比如中文,'你' ,在GBK码表占两个字节,在UTF-8码表占三个字节 'A' 65 0100 0001 字节 ----解析(解码)----> 字符 ----解析(编码)----> 字节 IO流使用的时候三点注意事项 (1)IO流相关的类,都在java.io包下 (2)IO流相关的操作都有异常,需要处理异常(throws) (3)IO流操作完都需要进行关流,释放资源
字节输出流 OutputStream(抽象类) |-FileOutputStream(具体子类)
共性方法 void close() 关闭此输出流并释放与此流有关的所有系统资源。 void flush() 刷新此输出流并强制写出所有缓冲的输出字节。 void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。 void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 void write(int b) 将指定的字节写入此输出流。 构造方法 FileOutputStream(File file) FileOutputStream(String name) 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流
追加写(续写)和换行 续写 FileOutputStream(File file, boolean append) FileOutputStream(String name, boolean append) append为true时可以续写,如果为false不能续写 append为false时,如果文件不存在就创建,如果文件存在就会清空文件(不会创建新文件)再次写入新的数据 换行 fos.write("\r\n".getBytes());
字节输入流 InputStream(抽象类) |-FileInputStream(具体的子类)
共性方法 void close() 关闭此输入流并释放与该流关联的所有系统资源。 int read():返回读取到的字节,读取了数据之后光标会让后移动一位,读取到文件末尾返回-1 int read(byte[] b):将多个字节读取到指定的字节数组中,并返回读取的有效字节个数,读取到文件的末尾返回-1 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流 FileInputStream的构造方法 FileInputStream(File file) FileInputStream(String name):关联的读取的文件一定要存在,如果不存在就会抛FileNotFoundException异常
需求: 在当前的模块下创建(手动)一个b.txt文件,在文件中有abcde这5个字节, 使用一次读取多个字节的方式将内容读取出来,并转换为字符串打印在控制台。 c:\\a.txt -读取-> 内存 -写出-> d:\\a.txt
字符输入流 Reader(抽象类) |-InputStreamReader |-FileReader(具体的子类)
共性方法 void close() 关闭此输入流并释放与该流关联的所有系统资源。 int read():返回读取到的字符,读取了数据之后光标会让后移动一位,读取到文件末尾返回-1 int read(char[] chs):将多个字符读取到指定的字符数组中,并返回读取的有效字符个数,读取到文件的末尾返回-1 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流 FileReader的构造方法 FileReader(File file) FileReader(String name):关联的读取的文件一定要存在,如果不存在就会抛FileNotFoundException异常
字符流输出流 Writer(抽象类) |-OutputStreamWriter |-FileWriter(具体子类)
共性方法 void close() 关闭此输出流并释放与此流有关的所有系统资源。 void flush() 刷新此输出流并强制写出所有缓冲的输出字节。 void write(char[] chs) 将 b.length 个字符从指定的 char 数组写入此输出流。 void write(char[] chs, int off, int len) 将指定 char 数组中从偏移量 off 开始的 len 个字节写入此输出流。 void write(int b) 将指定的字符写入此输出流。 void write(String str):写出整个字符串 void write(String str, int off, int len):写出字符串的一部分 构造方法 FileWriter(File file) FileWriter(String name) 三个步骤 (1)创建流对象,关流文件 (2)流操作(读或者写) (3)关流
追加写(续写)和换行 续写 FileWriter(File file, boolean append) FileWriter(String name, boolean append) append为true时可以续写,如果为false不能续写 append为false时,如果文件不存在就创建,如果文件存在就会清空文件(不会创建新文件)再次写入新的数据 换行 fos.write("\r\n");
close()和flush()的区别 (1)flush刷新缓冲区 close先刷新缓冲区,再释放资源 (2)flush调用完之后,流对象可以继续使用 close调用完之后,流对象不能继续使用,否则会报IOException,提示流已关闭
Properties 是一个双列集合,键和值存储的都是字符串 它的父类是Hashtable,所以Map接口中定义的共性方法Properties都可以使用,但是我们推荐使用特有方法 put ->setProperty(String key,String value) get ->getProperty(String key)
和IO流结合的方法 store load
今日学习目标 能够说出IO流的分类和功能 能够使用字节输出流写出数据到文件 能够使用字节输入流读取数据到程序 能够使用字节流完成文件的复制 能够使用FileWirter写数据到文件 能够说出FileWriter中关闭和刷新方法的区别 能够使用FileReader读数据 能够使用FileWriter写数据实现换行和追加写 能够使用Properties的load方法加载文件中配置信息
今日内容
网络编程
软件架构
C/S:Client/Server 客户端服务器,比如:QQ、IDEA
B/S:Browser/Server 浏览器服务器,比如:OA系统
网络通信协议
什么网络通信协议
定义了在网络中数据传输的规则
分类
UDP:面向无连接,数据不安全,可能丢失数据,数据有大小限制(64K)
速度快,效率高,区分发送端和接收端
TCP:面向连接,三次握手,数据安全,不会丢失数据,数据大小没有限制
速度稍慢,效率稍低,区分客户端和服务端
网络编程的三要素
协议
IP
是计算机在网络中的唯一标识
两个版本:
IPv4
IPv6
两个特殊的IP地址(代表本机)
127.0.0.1/localhost
端口
是程序(软件)在计算机中的唯一标识
端口号不能重复,重复的话就会报端口被占用的异常
端口号的取值范围:0~65535,推荐使用1024以上的,因为1024以下给系统预留的
TCP通信
Socket:客户端Socket
* InputStream getInputStream():获取到输入流,可以接收到服务器端的数据
* OutputStream getOutputStream():获取输出流,可以给服务器端发送数据
ServerSocket:服务端Socket
* Socket accept():获取到客户端Socket对象
* InputStream getInputStream():获取到输入流,可以接收到客户端的数据
* OutputStream getOutputStream():获取输出流,可以给客户端发送数据