集合
-
分类
- 单列集合:一次添加一个元素,都是Collection接口的实现
- List接口:可以存储重复元素;有索引;存取有序
- ArrayList
- LinkedList
- Set接口:不能存储重复元素;没有索引;存取无序
- TreeList
- HashList
- LinkedHashList
- List接口:可以存储重复元素;有索引;存取有序
- 多列集合,都是实现了Map接口
- 单列集合:一次添加一个元素,都是Collection接口的实现
-
Collection接口
- 方法:
- public boolean add(E e): 把给定元素添加到当前集合中(List可以新增重复元素永远返回true,Set不能新增重复元素可能返回false)
- public void clear(): 清空集合所有元素。
- public boolean isEmpty(): 判断集合是否为空
- public boolean contains(Object j): 判断当前集合中是否包含给定的对象,依赖于对象的equals方法
- public boolean isEmpty(): 判断当前集合是否为空。依赖对象的equals方法
- public int size(): 返回集合中元素的个数/集合的长度。
- 遍历方法
迭代器interator- hasNext(); 判断是否有元素可以迭代
- next(); 获取元素,指针++
Iterator<String> it = 集合对象.interator(); while(it.hasNext()) { String s = it.next() sout(s) }- 增强for循环:可以便利集合、数组等
- eg: for(String str : col)
- foreach
- default void foreach(Consumer<T> action)
- eg: col.foreach(item -> {sout(item)})
- 方法:
-
方法引用
- JDK8开始出现,是对Lamda的进一步简化
- 方法引用使用一对冒号
::,可推导即可省略原则 - list.foreach(MethodReference::change);
-
List接口
- 遍历方式
- 集合的方法:
foreach增强for循环iterator - 普通for循环
- ListIterator: 是iterator的实现,拓展了更多方法(
previoushasPrevious)
- 集合的方法:
- 遍历方式
-
并发修改异常(ConcurrentModificationException)
- 使用
迭代器遍历集合的过程中,调用了集合的添加、删除操作就会出现此异常 - 解决方法:不允许使用集合的添加或删除方法,使用
迭代器自身的添加和删除方法 Iterator没有add方法,有remove方法,ListIterator方法有add和remove功能。
List<Student> list = new List<>(); list.add(new student()); ListIterator<Student> it = list.listIterator(); while(it.hasNext()) { Student stu = it.next() if("张三".equals(stu.getName())) { list.remove(stu) // 此处会报并发修改错误,在使用迭代器的同时调用了集合的删除功能 it.remove() // 正确方法使用迭代器的remove方法,不需要参数。 } } - 使用
数据结构
- 栈:先进后出
- 队列:先进先出
- 数组:内存是连续的空间;查询效率高;增删效率低
- 链表:
- 特点:
- 节点存储数据和下一个节点的地址;
- 链表中的结点是独立的对象,在内存中不是连续的
- 查询慢,无论查询那个数据都要从头开始找,首尾操作极快
- 增删相对数组快
- 分类
- 单向列表
- 双向列表: 结点存储数据、上一个结点地址、下一个结点地址(LinkedList)
- 特点:
ArrayList源码
- 使用空参构造函数创建时,会生成一个长度为
0的数组 - 在添加第一个元素时,底层会创建一个新的长度为
10的数组 - 存满时,会扩容1.5倍。
LinkedList
- 基于双链表实现,查询元素慢,增删首尾元素非常快
- 特有方法:
- public void addFirst(E e): 在该列表开头插入指定的元素
- public void addLast(E e): 在该列表结尾插入指定元素
- public E getFirst(): 获取列表中第一个元素
- public E getLast(): 获取列表中最后一个元素
- public E removeFirst(): 删除第一个元素
- public E removeLast(): 删除最后一个元素
- get() 方法:根据索引获取元素,实际上是根据索引值从头或从尾部开始遍历集合寻找。
泛型
- 好处:
- 统一数据类型
- 将运行期的错误提升到了编译器
- 注意事项:
- 泛型只能传入
引用类型,基本类型需要传入类(Integer)。 - 如果没有指定具体类型,默认为
Object类型
- 泛型只能传入
- 常见标识符:
- E: Element 元素
- T:Type 类型
- K:Key 键
- V: Value 值
- 泛型方法
- 非静态方法:根据类的泛型去匹配。
- 静态方法:需要声明自己独立的泛型。调用的时候,传入实际参数,在调用时确定具体的数据类型
public class ArrayList<E> { // 非静态方法 public boolean add(E e) {}; // 静态方法,静态方法优先于类存在,需要声明自己独立的泛型。 public static<T> void printArray(T[] arr) {}; } - 泛型接口
- 实现类时,直接给具体的类型。
- 保持接口的泛型,等创建对象的时候再确定。
Interface Inter<E> { void show(E e); } class InterImpl1 implements Inter<String> { @override public void show(String str) {}; } class InterImpl2<E> implements Inter<E> { @override public void show(E e) {}; } - 泛型通配符
?: 用?替代类型,代表所有类型? extends: 只能接收E或者是E的子类? super: 只能接收E或E的父类
Set集合
- 特点:存取无序;无索引;不可以存储重复数据
- 3个实现类
- TreeSet:有序
- HashSet:元素唯一
- LinkedHashSet: 元素唯一,存取有序
- 增删改查方法:同Collection接口(add, remove)
- 遍历方式:iterator;for增强;foreach;
TreeSet:
- 二叉树:
- 特点:
- 任意结点开始,左边的结点都比当前节点小,右边的都比当前结点大
- 每一次添加结点,都从根节点开始比大小,小的往左走,大的往右走,一样的不存。
- 分类:
- 二叉查找树
- 平衡二叉树
- 红黑树,可以自平衡的二叉树,增删改查性能都很好
- 特点:
- TreeSet是一种红黑树结构。
- TreeSet的两种排序方法
- 自然排序
- 类需要继承Comparable接口
- 重写compareTo方法,根据方法的返回值来排序(返回0表示数据相等,只保留一个)
- 负数:往左边存
- 正数:往右边存
- 0:不存
- 第一个元素为树根,不管返回什么都会存储
- 取元素顺序:左中右
- 正序排列(this - o); 倒叙排列(o - this)
- 比较器排序
- 如果同时具有自然排序和比较器排序,优先比较器排序
- 在TreeSet的构造方法中,传入Compartor接口的实现类对象。
- 重写Compare方法
- 根据方法的返回值排序
- 自然排序
HashSet
- 当添加HashSet对象时,先调用
hashCode方法计算出索引位置,当两个对象的索引位置相同时,使用equals方法进行比较,如果相同则不存储,不同则通过链表/红黑树存储在同一个索引位置。 - hasCode
- 是Object的方法,会生成一个随机数(地址值)
- 自定义类可以重写hasCode,IDE可以自动生成。
- 哈希表
- JDK8以前由
数组+链表组成,JDK8以后由数组+链表+红黑树组成 - 哈希表存储数据的流程:
- 创建一个默认长度为
16,加载因子为0.75的数组,数组名为table - 根据元素的
哈希值跟数组的长度计算出应存入的位置 - 判断当前位置是否为
null,如果为null直接存入。否则调用equals方法比较,如果一样则不存。不一样JDK8以前采用头插法插入链表,JDK8以后采用尾插法插入链表。 - 当
元素存到16*0.75=12时,就会自动扩容,扩容为原来的2倍。 - JDK8以后,当链表的长度大于
8,并且数组长度大于64的时候会将链表转换为红黑树。
- 创建一个默认长度为
- JDK8以前由
LinkedHashSet
- 数据唯一性且存取顺序一致。
- LinkedHashSet依然是基于哈希表(
数组、链表、红黑树),但是他的每个元素都额外多了一个双向链表,记录前后元素的位置。
可变参数(...)
- 可变参数本质是一个数组,可以遍历。
- 用法:
- 用在形参中可以接收多个数据
- 格式:数据类型... 参数名(int... nums)
- 可以不传参数,也可以传1个或多个,也可以传一个数组
- 注意事项:
- 参数中只能有一个可变参数
- 如果有其他参数,可变参数必须放到最后,eg: fun(int a, int... nums)
Collections 集合工具类
- public static boolean addAll(Collection<? super T>, T... elements): 给集合对象批量添加元素。eg: Collections.addAll(list, 1, 2, 3);
- public static void shuffle(List<?> list): 洗牌,打乱
List集合元素的顺序。Collections.shuffle(list); - public static void max/min(Collection coll): 根据默认的自然排序获取最大/最小值
- public static void sort(List list): 将
List集合中元素按照默认规则排序(自然排序) - public static void sort(List list, Comparator<? super T> c): 也可传入排序方法。eg:Collections.sort(list, (o1, o2) -> o2 - o1);
Map集合
- Map是双列集合,每个元素包含两个数据:
key、value,key不能重复,value可以重复。 - 实现类:
- HashMap:元素按照键是无序,不重复,无索引
- LinkedHashMap: 元素按照键是有序,不重复,无索引
- TreeMap: 元素按照键排序,不重复,无索引
- Map常见API
- V put(K key, V value): 添加/修改元素,如果key值存在,则修改value。新增返回null,修改返回被替换的值,
- V remove(K key): 根据键删除元素,返回删除键对应的值
- void clear(): 清空Map集合
- boolean containsKey(Object key): 是否包含指定键
- boolean containsValue(Object value): 是否包含指定值
- boolean isEmpty(): 集合是否为空
- int size(): 集合的长度
- V get(Object key): 根据键查找对应的值。
- Map的遍历
- 通过键找值
- Set keySet(): 获取Map集合中所有的键
Map<String, Integer> map = new HashMap<>(); map.put("张胜", 1); map.put("李胜", 2); map.put("刘胜", 3); Set<String> keySet = map.keySet(); // {"张胜", "李胜", "刘胜"} for(String key: keySet) {sout(key + "---" + map.get(key))} - 通过键值对对象获取建和值
- Set<Map.Entry<K, V>> entrySet(): 获取集合中所有的键值对对象
- Entry类的方法
- getKey(): 获取键
- getValue():获取值
Map<String, Integer> map = new TreeMap<>(); map.put("张胜", 1); map.put("李胜", 2); map.put("刘胜", 3); Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); for(Map.Entry<String, Integer> entry: entrySet) {sout(entry.getKey() + "---" + entry.getValue())};- 通过foreach方法
- default void forEach(BiConsumer<? super K, ? spuer V> action): 遍历Map集合,获取键和值
map.forEach((k, v) -> sout(k + "----" + v)) - 通过键找值
Stream流
- 获取Stream流对象
- 集合:使用Collection接口中的默认方法:default Stream<E> stream()
- 单列集合直接调用
- 多列集合转换为单列集合(keySet、values、entrySet)后调用
- 数组:使用
Arrays数组工具类中的静态方法:static<T> Stream<T> stream(T[] array) - 零散数据:使用
Stream类中的静态方法:static<T> Stream<T> of(T... values)
- 集合:使用Collection接口中的默认方法:default Stream<E> stream()
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "hh", "ww");
list1.stream().forEach(System.out::println);
Map<String, Integer> map = new HashMap<>();
map.put("qq", 1);
map.put("ww", 2);
map.entrySet().stream().forEach(System.out::println);
int[] arr = {1, 2, 3};
Arrays.stream(arr).forEach(System.out::println);
Stream.of(1, 2, 3).forEach(System.out::println);
- Stream流的中间操作方法: 流中操作不会修改数据源。
- Stream<T> filter(Predicate<? super T> predicate): 对流中的数据进行过滤;返回值为Stream流
- Stream<T> limit(long maxSize): 获取前几个元素(截取前几个元素)
- Stream<T> skip(long n): 跳过前几个元素
- Stream<T> distinct(): 去除流中重复的元素(依赖hashCode和equals方法)
- staic<T> Stream<T> concat(Stream a, Stream b): 合并a,b流
- Stream<R> map(Function<? super T, ? extends R> mapper): 对流中每一个元素进行转换
- Stream流的终结方法:调用该方法后流不能再使用
- public void forEach(Comsumer action): 对流的每个元素遍历
- public long count(): 返回流中的元素数
- Stream收集操作
- 把Stream流操作后的结果数据转换为集合
- R collect(Collector collector): 收集stream流,指定收集器(collector)
- Collectors工具类提供了具体的收集方法
- public static<T> Collector toList(): 把元素收集到List集合中
- public static<T> Collector toSet(): 把元素收集到Set集合中
- public static Collector toMap(Function keyMapper, Function valueMapper): 把元素收集到Map集合中
ArrayList<String> list1 = new ArrayList<>(); Collections.addAll(list1, "hh-1", "ww-2"); List<String> res1 = list1.stream().collect(Collectors.toList()); Set<String> res2 = list1.stream().collect(Collectors.toSet()); Map<String, String> res3 = list1.stream().collect(Collectors.toMap(str -> str.split("-")[0], str -> str.split("-")[1])) // hh 1;ww 2 - 把Stream流操作后的结果数据转换为集合
异常
- 异常(Throwable)的结构
- Error:严重级别问题,通常跟系统有关
- Exception:异常类,程序常见的错误
- 编译时异常: 没有继承RuntimeException的异常,编译阶段就会出错。
- 运行时异常:继承自RuntimeException的异常或子类。
- 异常的默认处理流程
- 虚拟机会在出现异常代码位置自动创建一个异常对象:xxxException
- 异常会从方法中出现的位置抛给上级调用者,调用者层层上抛,最终抛给JVM虚拟机
- 虚拟机接收到异常对象后,先在控制台直接输出异常信息数据
- 终止java程序
- 异常处理方式
- try{...} catch(Exception e){...}:捕获异常,出现问题程序可以继续执行
- 异常类的方法
- getMessage(): 获取异常的原因
- printStackTrace(): 获取异常的调用代码及异常原因
- 异常类的方法
- throws: 抛出异常,程序会在错误点停止,不会继续执行。
- 用在方法名后,声明这个方法中的异常抛出(throw)
- 在继承关系中,子类不能抛出父类不存在的异常,或比父类异常更大的异常。
- throw: throw new Exception("xxx"), 真正抛出异常
- 如果throw的异常对象是运行时异常,就不需要编写throws,反之必须写。
- 使用区别:问题是否需要暴露 ? 抛出 : try catch
- try{...} catch(Exception e){...}:捕获异常,出现问题程序可以继续执行
- 自定义异常类
- 自定义编译时异常:继承Exception,重写构造器
- 自定义运行时异常:继承RuntimeException,重写构造器
File类
- File类构造函数
- public File(String pathname)
- public File(String parent, String child)
- public File(File parent, String child)
- File封装的对象仅仅是一个路径名,这个路径可以存在,也可以不存在。
- 路径:
- 相对路径:相对于当前项目的路径
- 绝对路径:从磁盘根目录开始
- File类的方法:
判断方法
- public boolean isDirectory(): 是否为文件夹
- public boolean isFile(): 是否为文件
- public boolean exists(): 判断File是否存在
常用方法
- public long length(): 返回文件的大小(字节数量),如果是文件夹对象调用,返回结果是错误的
- public String getAbsolutePath(): 返回文件的绝对路径
- public String getPath(): 返回定义文件时使用的路径
- public String getName(): 返回文件名称,带后缀
- public long lastModified(): 返回文件的最后修改时间(时间毫秒值)
创建和删除方法
- public boolean createNewFile(): 创建一个空对象,创建失败返回false
- public boolean mkdir(): 创建一级文件夹
- public boolean mkdirs(): 可以创建多级文件夹
- public boolean delete(): 删除此路径名表示的文件或空文件夹,只能删除空文件夹,且不走回收站。
遍历方法
- public File[] listFiles(): 获取当前目录下所有的
一级文件对象,返回File数组。- 当调用对象的路径不存在时,返回null
- 当调用对象的路径是文件时,返回null
- 当调用对象的路径是空文件夹时,返回一个长度为0的数组。
- 当调用对象的路径需要权限才能访问时,返回null
Math类
- public static int abs(int a): 获取绝对值
- public static double ceil(double a): 向上取整
- public static double floor(double a): 向下取整
- public static int round(float a): 四舍五入
- public static max(int a, int b): 获取两个int值中较大值
- public static pow(double a, double b): 返回a的b次幂的值
- public static random(): 返回值为double的随机值,范围[0.0, 1.0);Random对象功能类似。
System
- public static void exit(int status): 终止当前运行的Java虚拟机,非零表示异常终止
- public static long currentTimeMillis(): 返回当前系统的时间毫秒值,1970年1月1日到现在的毫秒值
- public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length): 数组拷贝:1-数据源数组 2-起始索引 3-目的地数组 4-拷贝个数
包装类
- 将基本数据类型,包装成类
- 8种基本类型都有包装类:byte(
Byte), short(Short), int(Integer), long(Long), chat(Character), float(Float), double(Double), boolean(Boolean) - 如何包装:
- 手动装箱:手动调用Integer.valueOf()方法,将基本数据类型手动包装为类。
- 手动拆箱:手动调用Integer中intValue()方法,将包装类对象,转换为基本数据类型。
- 自动拆装箱:基本数据类型和对应的包装类,可以直接运算,操作非常便捷。
BigDecimal类使用
- 创建对象:用于解决小数运算,不精确问题
- public BigDecimal(double val): 无法保证小数运算精度,不推荐使用
- public BigDecimal(String val)
- public static BigDecimal valueOf(double val):推荐使用
- 常用方法:
- public BigDecimal add(BigDecimal b): 加法
- public BigDecimal subtract(BigDecimal b): 减法
- public BigDecimal multiply(BigDecimal b): 乘法
- public BigDecimal divide(BigDecimal b): 除法,除不尽会报错
- public BigDecimal divide(BigDecimal b, 精确位数, 舍入模式):除法的重载方法
- RoundingMode.HALF_UP: 四舍五入
- RoundingMode.UP:进一法
- RoundingMode.DOWM:去尾法
- public double doubleValue(): 转换为double类型
Arrays类
- 数组操作工具类
- public static String toString(类型[] a): 将数组元素拼接为带有格式的字符串
- public static boolean equals(类型[] a, 类型[] b): 比较两个数组内容是否相同
- public static int binarySearch(int[] a, int key): 查找元素在数组中的索引(二分查找法:数组必须有序,否则结果错误),返回负数表示不存在。
- public static void sort(类型[] a): 对数组进行默认升序排列
- public static void sort(T[] a, Comparator c): 传入比较器,指定排序规则
Date类(JDK8之前)
- 构造器
- public Date(): 将当前时间封装为Date日期对象
- public Date(long time): 把毫秒值转换成Date日期对象
- 常见方法:
- public long getTime(): 获取当前对象距离1970年1月1日 0时0分0秒的毫秒数
- public void setTime(long time): 将当前对象设置为距离1970年time毫秒的时间。
SimpleDateFormat类 日期格式化(JDK8之前)
- 构造器:
- public SimpleDateFormat(String pattern): 使用指定格式构造一个类
- 格式化方法:
- public final String format(Date date): 将日期格式化成日期/时间字符串
- public final Date parse(String source): 将字符串解析为日期格式
JDK8以后的时间类
-
优点:
- 日期时间类
- LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
- LocalDate: 代表本地日期(年、月、日、星期)
- LocalTime: 代表本地时间(时、分、秒、纳秒)
- 获取对象:
- public static Xxx now(): 获取系统当前时间对应的对象
- LocalDate ld = LocalDate.now()
- LocalTime lt = LocalTime.now()
- LocalDateTime ldt = LocalDateTime.now()
- public static Xxx of(...): 获取指定时间的对象
- LocalDate ld = LocalDate.of(yyyy, MM, dd);
- LocalTime lt = LocalTime.of(HH, mm, ss);
- LocalDateTime ldt = LocalDateTime.of(yyyy, MM, dd, HH, mm, ss, ns);
- public static Xxx now(): 获取系统当前时间对应的对象
- LocalDateTime可以转换为LocalDate(toLocalDate()),LocalTime(toLocalTime())
- 获取年月日时分秒的方法
- int getYear(): 获取年
- Month getMonth(): 获取月的枚举,使用getValue()获取int值
- int getMonthValue(): 获取1到12之间的月份字段
- int getDayOfMonth() : 获取日期字段
- DayOfWeek getDayOfWeek(): 获取星期几的枚举DayOfWeek,使用getValue()获取int值
- int getHour()
- int getMinute()
- int getSecond()
- 修改时间:都是返回一个新的时间对象,不会修改调用对象
- 修改(width):widthHour(H),widthMinute(M), widthSecond(s), widthNano(ns)
- 加(plus): plusHour(H)...
- 减(minus): minusHour(H)...
- equals、isBefore、isAfter: d1.equals(d2)
- DateTimeFormatter类:时间的格式化和解析
- static DateTimeFormatter ofPattern(格式): 获取格式对象
- String format(时间对象): 按照指定方式格式化
- 计算时间间隔的工具
- ChronoUnit: 用于计算两个日期间隔(年月日时分秒)
- ChronoUnit.YEARS.between(d1, d2): d2 - d1
- Period:用于计算两个日期间隔(年月日)
- Duration:用于计算两个时间间隔(时分秒)
- ChronoUnit: 用于计算两个日期间隔(年月日时分秒)
IO流
- IO流体系结构
- 字节流(万能流)
- InputStream(抽象类): FileInputStream(实现类)
- OutputStream(抽象类):FileOutputStream(实现类)
- 字符流(纯文本)
- Reader: FileReader
- Writer: FileWriter
- 字节流(万能流)
- FileOutputStream 字节输出流
- 构造函数
- FileOutputStream(String name) : 输出流关联文件,文件路径以字符串形式给出。
- 如果是空文件会创建新的文件
- 如果是存在的文件,会清空然后写入,如果想追加添加append参数。
- FileOutputStream(String name, boolean append):追加写入开关
- FileOutputStream(File file)
- FileOutputStream(File file, boolean append)
- FileOutputStream(String name) : 输出流关联文件,文件路径以字符串形式给出。
- 成员方法
- void write(int b): 在文件中写出单个字节(a-97, b-98)
- void write(byte[] b): 写出一个字节数组{97, 98, 99}
- void write(byte[] b, int off, int len): 写字节数组的一部分
- write(b, 0, 2) // 写出b数组的从0位置开始,长度为2
- 如果想写入字符串“你好”,可以使用 "你好".getBytes(); 获取byte数组
- 注意
- 流对象使用完使用close()方法关闭,不然会一直占用资源
- 关闭后不能进行write等操作
- 在try的小括号中创建文件流对象,try中代码执行完会自动调用close方法。try小括号中的对象必须实现了autoCloseable接口
try(FileOutStream fos = new FileOutStream("D:\\a.txt")) { fos.write("abc".getBytes()); } catch(IOException e) { e.printStackTrace; } - 构造函数
- FileInputStream 字节输入流
- 构造方法
- FileInputStream(String name): 输入流关联文件,文件路径以字符串形式
- FileInputStream(File file)
- 如果文件或路径表示的文件不存在,会直接报错
- 成员方法
- int read(): 读取一个字节并返回,如果到达文件结尾则返回 -1
- int read(byte[] b): 将读取的字节,放入传入的数组中,返回读取到的有效字节数,到达文件结尾返回 -1
- String 构造方法
- public String(byte[] b, int start, int len): 创建一个字符串对象,内容为byte数组,start开始索引,长度为len
FileInputStream fis = new FileInputStream("D:\\A.text"); byte[] arr = new byte[2]; int len; while((len = fis.read(arr)) != -1) { String s = new String(arr, 0, len); sout(s); // 打印A.txt中的所有字符 } - 构造方法
- FileReader 字符输入流(处理纯文本文档,解决乱码问题)
- 构造方法:
- FileReader(String path): 传入路径
- FileReader(File file): 传入一个文件
- 成员方法:
- public int read(): 读入一个字符,文件结尾返回-1
- public int read(char[] cArr): 将读入的数据装入char数组中。返回读取成功的个数
- 构造方法:
- FileWriter 字符输出流
- 构造方法:
- FileWriter(String path)
- FileWriter(String path, boolean append)
- FileWriter(File file)
- FileWriter(File file, boolean append)
- 成员方法:
- public void write(int c): 写出单个字符(ASC码值或者直接传入单个字符'a'会自动转换为int类型)
- public void write(char[] cubf): 写出字符数组
- public write(char[] cubf, int off, int len): 写出字符数组的一部分
- public void write(String str): 写出字符串
- public void write(String str, int off, int len): 写出字符串的一部分
- 字符输出流写出数据,需要调用flush或者close方法,数据才会写出,**flush后可以继续写出,close不行。**底层代码中有一个stringBuffer数组容量为1024,装满后会写出,不装满除非调用flush或者close,否则不会写出。
- 构造方法:
- Properties 提供了IO方法
- 定义 Properties表示一组持久的属性,可以保存到流中或从流中加载,属性列表中每个键及其对应的值都是一个字符串。底层是一个HASH集合。
- IO相关方法
- void load(InputStream inStream): 从输入字节流读取属性列表(键值对)
- void load(Reader reader): 从输入字符流读取属性列表(键值对)
- void store(OutputStream out, String comments): 将属性列表写入Properties表中
- void store(Writer writer, String comments): 将属性列表写入Properties表中
- HuTool工具类
- IOUtil常用方法
- copy(InputStream in, OutputStream out, int bufferSize)
- copy(Reader reader, Writer writer)
- readLines(Reader reader, Collection<String> collection): 按行读取到集合中
- close(Closeable closeable): 安全关闭流
- FileUtil常用方法
- toach(String filePath): 创建文件(自动创建父目录)
- mkdir(String filePath): 创建文件夹(支持多级目录)
- copy(String srcPath, String destPath, boolean isOverride): 复制文件或文件夹
- move(String srcPath, String destPath, boolean isOverride):剪切文件或文件夹
- IOUtil常用方法
进程和线程
- 进程:程序的执行过程
- 独立性: 每个进程都有自己的空间,没有经过进程本身的允许,一个进程不能直接访问其他的进程空间
- 动态性:进程是动态产生,动态消亡的
- 并发性:任何进程都可以同其他进程一起并发执行。(在cpu中交替执行)
- 并行:在不同cpu中同时执行
- 多进程同时工作:对于一个cpu而言,它是在多个进程间轮换执行的。
- 线程:进程中的任务,多线程就是多个任务
- 提高效率
- 可以同时处理多个任务。
- Java开启线程的方法
- 继承Thread类:定义类继承Thread;重写run方法;创建新类对象;调用start方法;
public static void main() { MyThread thread = new MyThread(); thread.start(); } class MyThread extends Thread { @Override public void run() {...}; }- 实现Runnable接口:
- 定义类实现接口Runnable; 重写run方法;
- 创建新类对象(线程任务对象);
- 创建一个线程对象(Thread),将新类对象作为参数传入;
- 调用线程对象的start方法
class MyRunnable implements Runnable { @Override public void run() {...} } MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(mr); t1.start();- 实现Callable接口(带返回值):
- 编写一个类实现Callable接口;重写call方法(带返回值);
- 创建新类对象(线程资源对象);
- 创建线程任务对象(FutureTask-Runnable的实现类),并传入线程资源对象;
- 创建线程对象,传入线程任务;调用线程对象start方法;
- 调用线程任务对象get方法获取call方法返回值;
class MyCallable implements Callable<Integer> { @Override public Integer call() { int sum = 0; for(int i = 0; i <= 100; i++) { sum += i; } return sum; } } MyCallable mc = new MyCallable(); FutureTask<Integer> ft = new FutureTask<>(mc); Thread t = new Thread(ft); t.start(); Integer res = ft.get(); // 此方法只能在start方法执行后调用才有效 - 线程的方法
- String getName(): 返回此线程的名称
- void setName(String name): 设置线程的名字(构造方法也可以设置名字)
- static Thread currentThread(): 获取当前线程的对象
- static void sleep(long time): 让线程休眠指定时间(单位:毫秒)
- setPriority(int newPriority): 设置线程优先级
- 优先级为1-10 默认为5
- 优先级越高,抢占到cpu的概率越高
- final int getPriority(): 获取线程优先级
- final void setDaemon(boolean on): 设置为线程守护,当其他线程结束后此线程也会结束。
- 线程安全的问题(线程同步)
- 定义:将多条语句操作共享数据的代码锁起来,让任意时刻只能由一个线程可以执行
- 同步代码块:
- 锁对象可以是任意对象,但需要保证多条线程的锁对象是同一把锁。
- 同步可以解决多线程的数据安全问题,但是也会降低程序的运行效率。
- synchronized(锁对象) {...}: 锁对象建议使用对象的字节码 使用类名.class方法调用类的字节码:synchronized(TicketTask.class);
- 同步方法
- 将需要上锁的部分提取为方法,方法的返回值类型前添加synchronized关键字
- 方法为静态方法时,锁对象时字节码对象;非静态方法,锁对象为this
- Lock锁
- public ReentrantLock(): 创建一个ReentrantLock实例
- void lock(): 加锁
- void unlock(): 释放锁。如果有循环break之前需要释放锁。
- 自定义线程池
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- 核心线程数量(正式工): 不能小于0
- 最大线程数量(正式工+临时工): 任务 > 核心线程数量 + 任务队列时,调用临时线程
- 空闲时间
- 时间单位:TimeUnit枚举值
- 任务队列(指定排队人数)
- 有界队列:new ArrayBlockQueue<>(capacity) - 一般创建这个
- 无界队列: new LinkBlockQueue() - 最多Integer.MAX_VALUE个
- 线程对象工厂: Excutors.defaultThreadFactory()
- 拒绝策略: 当 任务 > 最大线程数量 + 任务队列 时触发
- new ThreadPoolExecutor.abortPolicy(): 默认,丢弃任务并抛出异常(RejectedExecutionException)
- new ThreadPoolExecutor.DiscardPolicy(): 丢弃任务不抛出异常,不推荐
- new ThreadPoolExecutor.DiscardOldestPolicy(): 抛弃队列中等待最久的任务,把新任务加入队列中
- new ThreaPoolExecutor.CallerRunsPolicy(): 调用任务的run方法,绕过线程直接执行。
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
网络通信三要素
- IP地址
- 作用:设备在网络中的地址,是唯一的标识。
- IPV4:主流方案,最多只有2^32次方个ip,目前已经用完
- 地址分类:公网地址(互联网使用)和私有地址(局域网使用,由路由器分配地址)
- 192.168. 开头的是私有地址,范围为192.168.0.0 ~ 192.168.255.255
- 127.0.0.1,也可以是localhost,回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。
- IPV6:为了解决IPV4不够用,最有有2^128次方个ip
- 常见CMD命令:
- ipconfig 查看本机IP地址
- ping 查看网络是否通畅
- 获取IP地址的方法InetAddress
- static InetAddress getByName(String host) 确定主机名称的IP地址(host可以是机器名称/ip地址)
- String getHostName() 获取IP地址的主机名
- String getHostAddress() 返回文本显示中的IP地址字符串
- 端口号:应用程序在设备中唯一的标识
- 有两个字节表示的整数,取值范围:0~65535
- 其中0~1023直接的端口号用于一些知名的网络服务或应用
- 我们自己使用1024以上的端口号
- 一个端口号只能被一个应用程序使用
- 传输协议
- UDP协议(用户数据报协议)
- 面向无连接的协议(不管是否连接成功都可以发送):速度快,有大小限制,一次最多64K,数据不安全,已丢失数据。
- 在线视频
- TCP协议(传输控制协议)
- 面向连接的通信协议:速度慢,没有大小限制,数据安全。
- 发送邮件
- UDP协议(用户数据报协议)
UDP协议收发数据
- 创建DatagramSock对象用于收发数据
- 创建DatagramPacket对象用于打包数据
- 调用send和recive方法发送接收数据
TCP协议收发数据
- 三次握手:
- 客户端向服务器发出连接请求,等待服务器确认
- 服务器向客户返回一个响应,告诉客户端收到了请求
- 客户端向服务器再次发出确认信息,连接建立
- 四次挥手
- 客户端向服务器发送取消链接请求
- 服务器返回响应,表示收到取消请求,并将最后数据处理完毕
- 服务器向客户端发出确认取消信息
- 客户端再次发送确认消息,连接取消
- 实现过程
- 客户端创建Socket对象,指定IP和端口号
- 服务端创建ServerSocket对象,指定端口号,调用accept详情请求,得到Socket对象
- 两端通过socke对象获取输入输出流,收发数据。
枚举
- 枚举是Java的
- 枚举定义格式: 修饰符 enum 枚举类名 {枚举项1, 枚举项2}
- 特点:
- 每个枚举项其实是该枚举的一个对象
- 通过枚举类名去访问指定的枚举项
- 所有枚举类都是Enum的子类
- 枚举也是类,可以定义成员变量
- 枚举类的第一行必须是枚举项
- 枚举类可以有构造器,但必须是private的,默认也是private
- 枚举类也可以有抽象方法,但是枚举项必须重写该方法
类加载器
- 将类的字节码载入到方法区中。
- 加载时机:用到就加载。
- 加载过程:
- 加载:
- 通过包名+类名,获取这个类,准备用流进行传输
- 将类加载到内存中
- 加载完毕创建一个class对象
- 链接
- 验证:验证类是否符合JVM规范,安全性检查,防止代码被插件篡改
- 准备:为static变量分配空间,设置默认值。
- 解析:将常量池中的符号引用解析为直接引用(分配真实地址)
- 初始化:初始化类变量和其他资源(为类的静态成员赋值)
- 加载:
- 类加载器的分类
- Bootstrap class loader: 启动类加载器,虚拟机的内置类加载器,由c++实现,只能获取为null
- Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
- JDK9 之前:Extension class loader扩展类加载器
- Application class loader: 应用程序类加载器,负责加载自己写的类。
- 自定义类加载器:上级为Application
- 加载器存在逻辑父子关系(不是extends关系):Bootstrap > Platform > Application
- 双亲委派模式
- 首先到最下级AppClassLoader, AppClassLoader判断是否加载过,加载过-不再加载;没有加载过会向上委派给PlatformClassLoader
- PlatformClassLoader判断是否加载过,加载过-不再加载;没有加载过会向上委派给BootstrapClassLoader
- BootstrapClassLoader判断是否加载过,加载过-不再加载;没有加载过会判断是否该自己加载?是-加载;否-向下传递给PlatformClassLoader
- PlatformClassLoader判断是否该自己加载,是-加载;否-向下传递给AppClassLoader
- AppClassLoader判断是否该自己加载,是-加载;否-报错(ClassNotFoundException)
- 好处:避免类的重复加载
反射
- 介绍
- 框架技术的灵魂
- 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意属性和方法。
- 这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
- 反射操作的是字节码对象
- 获取字节码对象的3种方法
- Class.forName(全类名)
- 类名.class
- 对象名.getClass();
- 反射类中的构造方法
- Constructor<?>[] getConstructors(): 返回所有public构造方法的数组
- Constructor<?>[] getDeclaredConstructors(): 返回所有构造方法的数组
- Constructor<T> getConstructor(Class<?>... parameterTypes): 返回单个public构造方法对象,参数为构造函数的参数的字节码(int.class)
- Constructor<T> getDeclaredConstructors(Calss<?>... parameterTypes): 返回单个构造方法对象
- Constructor类用于创建对象的方法
- T newInstance(Object...initargs): 根据指定的构造方法创建对象
- setAccessible(boolean flag): 设置为true,表示取消访问检查。使用getDeclaredXxx获取的private的构造方法,可以通过设置为true,取消访问检查,创建对象。
- 反射类中的成员变量
- Field[] getFields(): 返回所有public成员变量对象的数组
- Field[] getDeclaredFileds(): 返回所有成员变量对象的数组
- Field getField(String name): 返回单个public成员变量对象,参数为成员变量名
- Field getDeclaredField(String name): 返回单个成员变量对象,参数为成员变量名
- Field类的设置和获取
- void set(Object obj, Object value): 赋值,参数为赋值对象,值
- Object get(Object obj): 获取值,获取obj的field值
- 如果是暴力获取的private字段,需要setAccessible(true)
- 反射类中的成员方法
- Method[] getMethods(): 返回所有public成员方法的数组,包括继承的
- Method[] getDeclaredMethods(): 返回所有成员方法对象数组,不包括继承的
- Method getMethod(): 返回单个public成员方法
- Method getDeclaredMethod(): 返回单个成员方法
- Method类用于执行方法的方法
- Object invoke(Object obj, Object... args): 运行方法
- boolean isAnnotationPresent(注解类: annotationClass): 判断方法是否包含传入的注解类
注解
- 对程序进行标注。注释是给人看的,注解是给虚拟机看的。
- JDK中常见的注解
- @Override:表示方法的重写
- @Deprecated: 表示修饰的方法已过时
- @SuppressWarnings("all"): 压制警告
- 元注解: 用在注解上的注解
- @Target 注解在哪里使用,如果没有指定则任意位置都可使用
- 可以使用的值定义在
ElementType枚举中,常用值: TYPE类接口FIELD成员变量METHOD成员方法PARAMETER方法参数CONSTRUCTOR构造方法LOCAL_VARIABLE局部变量
- 可以使用的值定义在
- @RetentionPolicy 用来表示注解的生命周期(有效范围)
- 可以使用的值定义在
RetentionPolicy枚举中,常用值如下 SOURCE: 注解只作用在源码阶段,字节码文件中不存在CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在。默认值。RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段。
- 可以使用的值定义在
- @Target 注解在哪里使用,如果没有指定则任意位置都可使用
- 自定义注解
- 注解格式:
- public @interface 注解名称 { public 属性类型 属性名() default 默认值 };
- eg: public @interface Anno{String show() defualt "A"};
- 属性类型:
基本数据类型StringClass注解枚举- 以上数据类型的
一维数组
- 使用注解时,如果没有给默认值,需要手动给出
- 如果数组只有一个属性值,在使用时{}可以省略
- 如果只有一个属性名字为
value没有赋值,可以直接给出值,不需要写属性名。
- 注解格式:
动态代理
- 定义:动态代理是创建对象的一种方式,可以在不修改源代码的情况下,对原有功能进行增强
- 作用:在运行时动态生成代理类,无需手动为每个目标类编写代理类
- java.lang.reflect.Proxy类:提供了使用动态代理方式创建对象的API
- static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h): loader-指定用那个类加载器,去加载生成的实现类;interfaces-指定接口数组;h-指定生成的实现类中不同方法的实现方案。