Java高级

0 阅读31分钟

集合

  1. 分类

    • 单列集合:一次添加一个元素,都是Collection接口的实现
      • List接口:可以存储重复元素;有索引;存取有序
        • ArrayList
        • LinkedList
      • Set接口:不能存储重复元素;没有索引;存取无序
        • TreeList
        • HashList
        • LinkedHashList
    • 多列集合,都是实现了Map接口
  2. 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)})
  3. 方法引用

    • JDK8开始出现,是对Lamda的进一步简化
    • 方法引用使用一对冒号::,可推导即可省略原则
    • list.foreach(MethodReference::change);
  4. List接口

    • 遍历方式
      • 集合的方法:foreach 增强for循环 iterator
      • 普通for循环
      • ListIterator: 是iterator的实现,拓展了更多方法(previous hasPrevious
  5. 并发修改异常(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方法,不需要参数。
        }
    }
    

数据结构

  1. 栈:先进后出
  2. 队列:先进先出
  3. 数组:内存是连续的空间;查询效率高;增删效率低
  4. 链表:
    • 特点:
      • 节点存储数据和下一个节点的地址;
      • 链表中的结点是独立的对象,在内存中不是连续的
      • 查询慢,无论查询那个数据都要从头开始找,首尾操作极快
      • 增删相对数组快
    • 分类
      • 单向列表
      • 双向列表: 结点存储数据、上一个结点地址、下一个结点地址(LinkedList)

ArrayList源码

  1. 使用空参构造函数创建时,会生成一个长度为0的数组
  2. 在添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍。

LinkedList

  1. 基于双链表实现,查询元素慢,增删首尾元素非常快
  2. 特有方法:
    • public void addFirst(E e): 在该列表开头插入指定的元素
    • public void addLast(E e): 在该列表结尾插入指定元素
    • public E getFirst(): 获取列表中第一个元素
    • public E getLast(): 获取列表中最后一个元素
    • public E removeFirst(): 删除第一个元素
    • public E removeLast(): 删除最后一个元素
  3. get() 方法:根据索引获取元素,实际上是根据索引值从头或从尾部开始遍历集合寻找。

泛型

  1. 好处:
    • 统一数据类型
    • 将运行期的错误提升到了编译器
  2. 注意事项:
    • 泛型只能传入引用类型,基本类型需要传入类(Integer)。
    • 如果没有指定具体类型,默认为Object类型
  3. 常见标识符:
    • E: Element 元素
    • T:Type 类型
    • K:Key 键
    • V: Value 值
  4. 泛型方法
    • 非静态方法:根据类的泛型去匹配。
    • 静态方法:需要声明自己独立的泛型。调用的时候,传入实际参数,在调用时确定具体的数据类型
    public class ArrayList<E> {
        // 非静态方法
        public boolean add(E e) {};
        // 静态方法,静态方法优先于类存在,需要声明自己独立的泛型。
        public static<T> void printArray(T[] arr) {};
    }
    
  5. 泛型接口
    • 实现类时,直接给具体的类型。
    • 保持接口的泛型,等创建对象的时候再确定。
    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) {};
    }
    
  6. 泛型通配符
    • ? : 用?替代类型,代表所有类型
    • ? extends : 只能接收E或者是E的子类
    • ? super : 只能接收E或E的父类

Set集合

  • 特点:存取无序;无索引;不可以存储重复数据
  • 3个实现类
    • TreeSet:有序
    • HashSet:元素唯一
    • LinkedHashSet: 元素唯一,存取有序
  • 增删改查方法:同Collection接口(add, remove)
  • 遍历方式:iterator;for增强;foreach;

TreeSet:

  1. 二叉树:
    • 特点:
      • 任意结点开始,左边的结点都比当前节点小,右边的都比当前结点大
      • 每一次添加结点,都从根节点开始比大小,小的往左走,大的往右走,一样的不存。
    • 分类:
      • 二叉查找树
      • 平衡二叉树
      • 红黑树,可以自平衡的二叉树,增删改查性能都很好
  2. TreeSet是一种红黑树结构。
  3. TreeSet的两种排序方法
    • 自然排序
      • 类需要继承Comparable接口
      • 重写compareTo方法,根据方法的返回值来排序(返回0表示数据相等,只保留一个)
        • 负数:往左边存
        • 正数:往右边存
        • 0:不存
      • 第一个元素为树根,不管返回什么都会存储
      • 取元素顺序:左中右
      • 正序排列(this - o); 倒叙排列(o - this)
    • 比较器排序
      • 如果同时具有自然排序和比较器排序,优先比较器排序
      • 在TreeSet的构造方法中,传入Compartor接口的实现类对象。
      • 重写Compare方法
      • 根据方法的返回值排序

HashSet

  1. 当添加HashSet对象时,先调用hashCode方法计算出索引位置,当两个对象的索引位置相同时,使用equals方法进行比较,如果相同则不存储,不同则通过链表/红黑树存储在同一个索引位置。
  2. hasCode
    • 是Object的方法,会生成一个随机数(地址值)
    • 自定义类可以重写hasCode,IDE可以自动生成。
  3. 哈希表
    • JDK8以前由数组+链表组成,JDK8以后由数组+链表+红黑树组成
    • 哈希表存储数据的流程:
      1. 创建一个默认长度16加载因子0.75数组,数组名为table
      2. 根据元素的哈希值数组的长度计算出应存入的位置
      3. 判断当前位置是否为null,如果为null直接存入。否则调用equals方法比较,如果一样则不存。不一样JDK8以前采用头插法插入链表,JDK8以后采用尾插法插入链表。
      4. 元素存到16*0.75=12时,就会自动扩容,扩容为原来的2倍
      5. JDK8以后,当链表的长度大于8,并且数组长度大于64的时候会将链表转换为红黑树

LinkedHashSet

  1. 数据唯一性且存取顺序一致。
  2. LinkedHashSet依然是基于哈希表数组链表红黑树),但是他的每个元素都额外多了一个双向链表记录前后元素的位置

可变参数(...)

  1. 可变参数本质是一个数组,可以遍历。
  2. 用法:
    • 用在形参中可以接收多个数据
    • 格式:数据类型... 参数名(int... nums)
    • 可以不传参数,也可以传1个或多个,也可以传一个数组
  3. 注意事项:
    • 参数中只能有一个可变参数
    • 如果有其他参数,可变参数必须放到最后,eg: fun(int a, int... nums)

Collections 集合工具类

  1. public static boolean addAll(Collection<? super T>, T... elements): 给集合对象批量添加元素。eg: Collections.addAll(list, 1, 2, 3);
  2. public static void shuffle(List<?> list): 洗牌,打乱List集合元素的顺序。Collections.shuffle(list);
  3. public static void max/min(Collection coll): 根据默认的自然排序获取最大/最小值
  4. public static void sort(List list): 将List集合中元素按照默认规则排序(自然排序)
  5. public static void sort(List list, Comparator<? super T> c): 也可传入排序方法。eg:Collections.sort(list, (o1, o2) -> o2 - o1);

Map集合

  1. Map是双列集合,每个元素包含两个数据:keyvaluekey不能重复,value可以重复。
  2. 实现类:
    • HashMap:元素按照键是无序不重复无索引
    • LinkedHashMap: 元素按照键是有序,不重复,无索引
    • TreeMap: 元素按照键排序,不重复,无索引
  3. 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): 根据键查找对应的值。
  4. 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流

  1. 获取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)
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);
  1. 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): 对流中每一个元素进行转换
  2. Stream流的终结方法:调用该方法后流不能再使用
    • public void forEach(Comsumer action): 对流的每个元素遍历
    • public long count(): 返回流中的元素数
  3. 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
    

异常

  1. 异常(Throwable)的结构
    • Error:严重级别问题,通常跟系统有关
    • Exception:异常类,程序常见的错误
      • 编译时异常: 没有继承RuntimeException的异常,编译阶段就会出错。
      • 运行时异常:继承自RuntimeException的异常或子类。
  2. 异常的默认处理流程
    • 虚拟机会在出现异常代码位置自动创建一个异常对象:xxxException
    • 异常会从方法中出现的位置抛给上级调用者,调用者层层上抛,最终抛给JVM虚拟机
    • 虚拟机接收到异常对象后,先在控制台直接输出异常信息数据
    • 终止java程序
  3. 异常处理方式
    • try{...} catch(Exception e){...}:捕获异常,出现问题程序可以继续执行
      • 异常类的方法
        • getMessage(): 获取异常的原因
        • printStackTrace(): 获取异常的调用代码及异常原因
    • throws: 抛出异常,程序会在错误点停止,不会继续执行。
      • 用在方法名后,声明这个方法中的异常抛出(throw)
      • 在继承关系中,子类不能抛出父类不存在的异常,或比父类异常更大的异常
      • throw: throw new Exception("xxx"), 真正抛出异常
      • 如果throw的异常对象是运行时异常,就不需要编写throws,反之必须写。
    • 使用区别:问题是否需要暴露 ? 抛出 : try catch
  4. 自定义异常类
    • 自定义编译时异常:继承Exception,重写构造器
    • 自定义运行时异常:继承RuntimeException,重写构造器

File类

  1. File类构造函数
    • public File(String pathname)
    • public File(String parent, String child)
    • public File(File parent, String child)
    • File封装的对象仅仅是一个路径名,这个路径可以存在,也可以不存在。
  2. 路径:
    • 相对路径:相对于当前项目的路径
    • 绝对路径:从磁盘根目录开始
  3. 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-拷贝个数

包装类

  1. 将基本数据类型,包装成类
  2. 8种基本类型都有包装类:byte(Byte), short(Short), int(Integer), long(Long), chat(Character), float(Float), double(Double), boolean(Boolean)
  3. 如何包装:
    • 手动装箱:手动调用Integer.valueOf()方法,将基本数据类型手动包装为类。
    • 手动拆箱:手动调用Integer中intValue()方法,将包装类对象,转换为基本数据类型。
    • 自动拆装箱:基本数据类型和对应的包装类,可以直接运算,操作非常便捷。

BigDecimal类使用

  1. 创建对象:用于解决小数运算,不精确问题
    • public BigDecimal(double val): 无法保证小数运算精度,不推荐使用
    • public BigDecimal(String val)
    • public static BigDecimal valueOf(double val):推荐使用
  2. 常用方法:
    • 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类

  1. 数组操作工具类
    • 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之前)

  1. 构造器
    • public Date(): 将当前时间封装为Date日期对象
    • public Date(long time): 把毫秒值转换成Date日期对象
  2. 常见方法:
    • public long getTime(): 获取当前对象距离1970年1月1日 0时0分0秒的毫秒数
    • public void setTime(long time): 将当前对象设置为距离1970年time毫秒的时间。

SimpleDateFormat类 日期格式化(JDK8之前)

  1. 构造器:
    • public SimpleDateFormat(String pattern): 使用指定格式构造一个类
  2. 格式化方法:
    • public final String format(Date date): 将日期格式化成日期/时间字符串
    • public final Date parse(String source): 将字符串解析为日期格式

JDK8以后的时间类

  1. 优点:

  2. 日期时间类
    • LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
    • LocalDate: 代表本地日期(年、月、日、星期)
    • LocalTime: 代表本地时间(时、分、秒、纳秒)
  3. 获取对象:
    • 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);
  4. LocalDateTime可以转换为LocalDate(toLocalDate()),LocalTime(toLocalTime())
  5. 获取年月日时分秒的方法
    • int getYear(): 获取年
    • Month getMonth(): 获取月的枚举,使用getValue()获取int值
    • int getMonthValue(): 获取1到12之间的月份字段
    • int getDayOfMonth() : 获取日期字段
    • DayOfWeek getDayOfWeek(): 获取星期几的枚举DayOfWeek,使用getValue()获取int值
    • int getHour()
    • int getMinute()
    • int getSecond()
  6. 修改时间:都是返回一个新的时间对象,不会修改调用对象
    • 修改(width):widthHour(H),widthMinute(M), widthSecond(s), widthNano(ns)
    • 加(plus): plusHour(H)...
    • 减(minus): minusHour(H)...
    • equals、isBefore、isAfter: d1.equals(d2)
  7. DateTimeFormatter类:时间的格式化和解析
    • static DateTimeFormatter ofPattern(格式): 获取格式对象
    • String format(时间对象): 按照指定方式格式化
  8. 计算时间间隔的工具
    • ChronoUnit: 用于计算两个日期间隔(年月日时分秒)
      • ChronoUnit.YEARS.between(d1, d2): d2 - d1
    • Period:用于计算两个日期间隔(年月日)
    • Duration:用于计算两个时间间隔(时分秒)

IO流

  1. IO流体系结构
    • 字节流(万能流)
      • InputStream(抽象类): FileInputStream(实现类)
      • OutputStream(抽象类):FileOutputStream(实现类)
    • 字符流(纯文本)
      • Reader: FileReader
      • Writer: FileWriter
  2. FileOutputStream 字节输出流
    • 构造函数
      • FileOutputStream(String name) : 输出流关联文件,文件路径以字符串形式给出。
        • 如果是空文件创建新的文件
        • 如果是存在的文件,会清空然后写入,如果想追加添加append参数。
      • FileOutputStream(String name, boolean append):追加写入开关
      • FileOutputStream(File file)
      • FileOutputStream(File file, boolean append)
    • 成员方法
      • 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;
    }
    
  3. 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中的所有字符
    }
    
  4. FileReader 字符输入流(处理纯文本文档,解决乱码问题)
    • 构造方法:
      • FileReader(String path): 传入路径
      • FileReader(File file): 传入一个文件
    • 成员方法:
      • public int read(): 读入一个字符,文件结尾返回-1
      • public int read(char[] cArr): 将读入的数据装入char数组中。返回读取成功的个数
  5. 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,否则不会写出。
  6. 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表中
  7. 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):剪切文件或文件夹

进程和线程

  1. 进程:程序的执行过程
    • 独立性: 每个进程都有自己的空间,没有经过进程本身的允许,一个进程不能直接访问其他的进程空间
    • 动态性:进程是动态产生,动态消亡的
    • 并发性:任何进程都可以同其他进程一起并发执行。(在cpu中交替执行)
      • 并行:在不同cpu中同时执行
    • 多进程同时工作:对于一个cpu而言,它是在多个进程间轮换执行的。
  2. 线程:进程中的任务,多线程就是多个任务
    • 提高效率
    • 可以同时处理多个任务。
  3. 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方法执行后调用才有效
    
  4. 线程的方法
    • 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): 设置为线程守护,当其他线程结束后此线程也会结束。
  5. 线程安全的问题(线程同步)
    • 定义:将多条语句操作共享数据的代码锁起来,让任意时刻只能由一个线程可以执行
    • 同步代码块:
      • 锁对象可以是任意对象,但需要保证多条线程的锁对象是同一把锁。
      • 同步可以解决多线程的数据安全问题,但是也会降低程序的运行效率。
      • synchronized(锁对象) {...}: 锁对象建议使用对象的字节码 使用类名.class方法调用类的字节码:synchronized(TicketTask.class);
    • 同步方法
      • 将需要上锁的部分提取为方法,方法的返回值类型前添加synchronized关键字
      • 方法为静态方法时,锁对象时字节码对象非静态方法,锁对象为this
    • Lock锁
      • public ReentrantLock(): 创建一个ReentrantLock实例
      • void lock(): 加锁
      • void unlock(): 释放锁。如果有循环break之前需要释放锁。
  6. 自定义线程池
    • 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方法,绕过线程直接执行。

网络通信三要素

  1. 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地址字符串
  2. 端口号:应用程序在设备中唯一的标识
    • 有两个字节表示的整数,取值范围:0~65535
    • 其中0~1023直接的端口号用于一些知名的网络服务或应用
    • 我们自己使用1024以上的端口号
    • 一个端口号只能被一个应用程序使用
  3. 传输协议
    • UDP协议(用户数据报协议)
      • 面向无连接的协议(不管是否连接成功都可以发送):速度快,有大小限制,一次最多64K,数据不安全,已丢失数据。
    • 在线视频
    • TCP协议(传输控制协议)
      • 面向连接的通信协议:速度慢,没有大小限制,数据安全。
      • 发送邮件

UDP协议收发数据

  1. 创建DatagramSock对象用于收发数据
  2. 创建DatagramPacket对象用于打包数据
  3. 调用send和recive方法发送接收数据

TCP协议收发数据

  1. 三次握手:
    • 客户端向服务器发出连接请求,等待服务器确认
    • 服务器向客户返回一个响应,告诉客户端收到了请求
    • 客户端向服务器再次发出确认信息,连接建立
  2. 四次挥手
    • 客户端向服务器发送取消链接请求
    • 服务器返回响应,表示收到取消请求,并将最后数据处理完毕
    • 服务器向客户端发出确认取消信息
    • 客户端再次发送确认消息,连接取消
  3. 实现过程
    • 客户端创建Socket对象,指定IP和端口号
    • 服务端创建ServerSocket对象,指定端口号,调用accept详情请求,得到Socket对象
    • 两端通过socke对象获取输入输出流,收发数据。

枚举

  1. 枚举是Java的
  2. 枚举定义格式: 修饰符 enum 枚举类名 {枚举项1, 枚举项2}
  3. 特点:
    • 每个枚举项其实是该枚举的一个对象
    • 通过枚举类名去访问指定的枚举项
    • 所有枚举类都是Enum的子类
    • 枚举也是类,可以定义成员变量
    • 枚举类的第一行必须是枚举项
    • 枚举类可以有构造器,但必须是private的,默认也是private
    • 枚举类也可以有抽象方法,但是枚举项必须重写该方法

类加载器

  1. 类的字节码载入到方法区中。
  2. 加载时机:用到就加载
  3. 加载过程:
    • 加载
      • 通过包名+类名,获取这个类,准备用流进行传输
      • 将类加载到内存中
      • 加载完毕创建一个class对象
    • 链接
      • 验证:验证类是否符合JVM规范,安全性检查,防止代码被插件篡改
      • 准备:为static变量分配空间,设置默认值。
      • 解析:将常量池中的符号引用解析为直接引用(分配真实地址)
    • 初始化:初始化类变量和其他资源(为类的静态成员赋值)
  4. 类加载器的分类
    • Bootstrap class loader: 启动类加载器,虚拟机的内置类加载器,由c++实现,只能获取为null
    • Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
      • JDK9 之前:Extension class loader扩展类加载器
    • Application class loader: 应用程序类加载器,负责加载自己写的类
    • 自定义类加载器:上级为Application
    • 加载器存在逻辑父子关系(不是extends关系):Bootstrap > Platform > Application
  5. 双亲委派模式
    • 首先到最下级AppClassLoader, AppClassLoader判断是否加载过,加载过-不再加载;没有加载过会向上委派给PlatformClassLoader
    • PlatformClassLoader判断是否加载过,加载过-不再加载;没有加载过会向上委派给BootstrapClassLoader
    • BootstrapClassLoader判断是否加载过,加载过-不再加载;没有加载过会判断是否该自己加载?是-加载;否-向下传递给PlatformClassLoader
    • PlatformClassLoader判断是否该自己加载,是-加载;否-向下传递给AppClassLoader
    • AppClassLoader判断是否该自己加载,是-加载;否-报错(ClassNotFoundException)
  6. 好处:避免类的重复加载

反射

  1. 介绍
    • 框架技术的灵魂
    • 运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意属性和方法。
    • 这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
    • 反射操作的是字节码对象
  2. 获取字节码对象的3种方法
    • Class.forName(全类名)
    • 类名.class
    • 对象名.getClass();
  3. 反射类中的构造方法
    • Constructor<?>[] getConstructors(): 返回所有public构造方法的数组
    • Constructor<?>[] getDeclaredConstructors(): 返回所有构造方法的数组
    • Constructor<T> getConstructor(Class<?>... parameterTypes): 返回单个public构造方法对象,参数为构造函数的参数的字节码int.class
    • Constructor<T> getDeclaredConstructors(Calss<?>... parameterTypes): 返回单个构造方法对象
  4. Constructor类用于创建对象的方法
    • T newInstance(Object...initargs): 根据指定的构造方法创建对象
    • setAccessible(boolean flag): 设置为true,表示取消访问检查。使用getDeclaredXxx获取的private的构造方法,可以通过设置为true,取消访问检查,创建对象。
  5. 反射类中的成员变量
    • Field[] getFields(): 返回所有public成员变量对象的数组
    • Field[] getDeclaredFileds(): 返回所有成员变量对象的数组
    • Field getField(String name): 返回单个public成员变量对象,参数为成员变量名
    • Field getDeclaredField(String name): 返回单个成员变量对象,参数为成员变量名
  6. Field类的设置和获取
    • void set(Object obj, Object value): 赋值,参数为赋值对象,值
    • Object get(Object obj): 获取值,获取obj的field值
    • 如果是暴力获取的private字段,需要setAccessible(true)
  7. 反射类中的成员方法
    • Method[] getMethods(): 返回所有public成员方法的数组,包括继承的
    • Method[] getDeclaredMethods(): 返回所有成员方法对象数组,不包括继承的
    • Method getMethod(): 返回单个public成员方法
    • Method getDeclaredMethod(): 返回单个成员方法
  8. Method类用于执行方法的方法
    • Object invoke(Object obj, Object... args): 运行方法
    • boolean isAnnotationPresent(注解类: annotationClass): 判断方法是否包含传入的注解类

注解

  1. 对程序进行标注。注释是给人看的,注解是给虚拟机看的。
  2. JDK中常见的注解
    • @Override:表示方法的重写
    • @Deprecated: 表示修饰的方法已过时
    • @SuppressWarnings("all"): 压制警告
  3. 元注解: 用在注解上的注解
    • @Target 注解在哪里使用,如果没有指定则任意位置都可使用
      • 可以使用的值定义在ElementType枚举中,常用值:
      • TYPE 类接口 FIELD 成员变量 METHOD 成员方法 PARAMETER 方法参数 CONSTRUCTOR 构造方法 LOCAL_VARIABLE 局部变量
    • @RetentionPolicy 用来表示注解的生命周期(有效范围)
      • 可以使用的值定义在RetentionPolicy枚举中,常用值如下
      • SOURCE: 注解只作用在源码阶段,字节码文件中不存在
      • CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在。默认值。
      • RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
  4. 自定义注解
    • 注解格式:
      • public @interface 注解名称 { public 属性类型 属性名() default 默认值 };
      • eg: public @interface Anno{String show() defualt "A"};
      • 属性类型
        • 基本数据类型
        • String
        • Class
        • 注解
        • 枚举
        • 以上数据类型的一维数组
    • 使用注解时,如果没有给默认值,需要手动给出
    • 如果数组只有一个属性值,在使用时{}可以省略
    • 如果只有一个属性名字为value没有赋值,可以直接给出值,不需要写属性名。

动态代理

  1. 定义:动态代理是创建对象的一种方式,可以在不修改源代码的情况下,对原有功能进行增强
  2. 作用:在运行时动态生成代理类,无需手动为每个目标类编写代理类
  3. java.lang.reflect.Proxy类:提供了使用动态代理方式创建对象的API
    • static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h): loader-指定用那个类加载器,去加载生成的实现类;interfaces-指定接口数组;h-指定生成的实现类中不同方法的实现方案。