JavaSE学习小结二

JavaSE学习小结二

记录学习----------JavaSE模块三&四 其中大部分是一些工具类,会将常用方法及其功能描述整理成表格方便日后查阅


文章目录

  • JavaSE学习小结二
  • 一、Object类
    • 1. 基本概念
    • 2. 常用方法
    • 3. 示例:
    • 4. 小结
  • 二、包装类的装箱与拆箱
    • 1. 基本数据类型 与 对应的包装类
    • 1.Integer
      • 1.1 常用变量(表示一些常用的数值,比如最大值、最小值……)
      • 1.2 常用方法
      • 1.3 装箱与拆箱
    • 2.Double
      • 2.1 常用常量
      • 2.2 常用方法
    • 3.Boolean
      • 3.1 常用常量
    • 3.2 常用方法
    • 4.Character
      • 4.1 常用常量
      • 4.2常用方法
    • 5.包装类小结
  • 三、数学工具类
    • 1. Math类
    • 2. BigDecimal类(实现精确运算,因为float类型和double类型在运算时可能会有误差)
    • 3.BigInteger类(表示比long类型范围还大的整数数据)
  • 四、String类
    • 1.基础知识
    • 2.常量池
    • 3. 常用构造方法(如同上文所说直接赋值也可以)
    • 4.常用成员方法
  • 四(补)可变长字符串StringBuffer & StringBuilder
    • StringBuilder类
      • 1. 构造方法
      • 2. 常用方法(前两个注意区分一下)
  • 五、正则表达式
    • 1. 基本知识
    • 2.正则表达式的规则
    • 3. 正则表达式的常用方法
  • 六、日期类(就是一个工具类)
    • 1. System类中的关于时间的方法
    • 2.Date类 & SimpleDateFormat类
      • 2.1 Date类
      • 2.2 SimpleDateFormat类
      • 2.3 Calendar类
      • 小结:多态的使用场合
    • 3. Java8的日期类新特性
      • 3.1 LocalDate类的概述
      • 3.2 LocalTime类的概述
      • 3.3 LocalDateTime类的概述
      • 3.4 Instant类
      • 3.5 DateTimeFormatter类
  • 七、集合(灰常的重要,即便算法不是那么精通,平时也会经常用到)
    • 1.集合的框架
    • 2.Collection集合
    • 3.Iterator接口 --- 迭代器
    • 4.List集合
      • 4.1 基本概念
      • 4.2 常用方法
      • 4.3 Stack 栈
      • 4.4 Queue集合
    • 5. Set集合
      • 5.1 基本概念:
      • 5.2 常用方法
      • 5.3 元素放入HashSet集合的原理(数据结构哈希表的概念)
      • 5.4 TreeSet集合的概念
    • 6. Map集合(※※)
      • 6.1 基本概念
      • 6.2 常用方法
      • 6. 3元素放入HashMap集合的原理
      • 6.4 相关的常量
    • 7.Collections类 --- 提供对集合操作或者返回集合的静态方法(工具类一个)
  • 八、泛型
    • 1. 基本概念
    • 2. 泛型类、泛型方法 & 泛型接口
      • 2.1 泛型类
      • 2.2 泛型方法
      • 2.3 泛型接口
      • 2.4 泛型的继承
      • 2.5 泛型通配符(※)
  • 九、异常(※※)-- Java采用的异常处理机制是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护
    • 1.基本知识
    • 2. 异常的捕获
    • 3. 异常的抛出
    • 4. 异常类的示例:
  • 十、文件File类(※)--- 本质上还是一个工具类
  • 十一、IO流
    • 1.体系结构 & 框架
    • 2.FileWriter & FileReader --- 主要用于对文本文件的读写
      • 2.1FileWriter 的常用方法:
      • 2.2 FileReader 的常用方法:
      • 2.3代码示例
    • 3. FileOutputStream & FileInputStream -- 主要用于对图像数据之类的原始字节流的读写
      • 3.1 FileOutputStream 的常用方法
      • 3.2 FileInputStream 的常用方法
      • 3.3 代码示例 --- 拷贝一张图片
    • 4.缓冲流 之 BufferedOutputStream & BufferedInputStream
      • 4.1 BufferedOutputStream 的常用方法
      • 4.2 BufferedInputStream的常用方法
      • 4.3 代码示例 --- 拷贝视频
      • 4.4BufferedXXXStream 源码解析
    • 5.缓冲流 之 BufferedWriter& BufferedReader
      • 5.1 BufferedWriter 常用方法
      • 5.2 BufferedReader 常用方法
    • 6.PrintStream & PrintWriter
      • 6.1 PrintStream 常用方法
      • 6.2 PrintWriter 常用方法
    • 7. 转换流
      • 7.1 OutputStreamWriter
      • 7.2 InputStreamReader
    • 8.ObjectOutputStream & ObjectInputStream
      • 8.1 ObjectOutputStream
      • 8.2 ObjectInputStream
      • 8.3 代码示例
      • 8.4 其他知识点
  • 十二、多线程
    • 1. 线程的创建(三种方式)
    • 2. 线程同步机制(※)
      • 2.1 synchronized关键字
      • 2.2 静态方法的锁定
      • 2.4 线程安全类和不安全类
      • 2.5 使用Lock(锁)实现线程同步
      • 2.6 与synchronized方式的比较
    • 3.Object类常用的方法
    • 4. 线程池
      • 4.1 Executors类
      • 4.2 ExecutorService接口
    • 4.3 线程池的使用 --- 这里顺便说一下线程的第三种创建方法
  • 十三、网络编程
    • 1. 编程模型
    • 2. ServerSocket & Socket
  • 十四、反射
    • 1. 基本概念
    • 2. Class类
    • 3. Constructor类
    • 4. Field类
    • 5.Method类
  • 总结


一、Object类

1. 基本概念

  • java.lang.Object类是Java语言中类层次结构的根类,也就是说任何一个类都是该类的直接或者间接子类。
  • 如果定义一个Java类时没有使用extends关键字声明其父类,则其父类为 java.lang.Object 类。
  • Object类定义了“对象”的基本行为, 被子类默认继承

2. 常用方法

方法名 功能
Object() 使用无参方式构造对象
boolean equals(Objectobj) 用于判断调用对象是否与参数对象相等。该方法默认比较两个对象的地址是否相等,与 == 运算符的结果一致若希望比较两个对象的内容,则需要重写该方法。若该方法被重写后,则应该重写hashCode方法来保证结果的一致性
int hashCode() 用于获取调用对象的哈希码值(内存地址的编号)。若两个对象调用equals方法相等,则各自调用该方法的结果必须相同。若两个调用对象equals方法不相等,则各自调用该方法的结果应该不相同。为了使得该方法与equals方法保持一致,需要重写该方法
String toString() 用于获取调用对象的字符串形式。该方法默认返回的字符串为:包名.类名@哈希码值的十六进制。为了返回更有意义的数据,需要重写该方法。使用print或println打印引用或字符串拼接引用都会自动调用该方法
Class getClass() 用于返回调用对象执行时的Class实例,反射机制使用

3. 示例:

要求:

  • 编程实现Student类的封装,特征:学号(id)和姓名,要求提供打印所有特征的方法。
  • 编程实现StudentTest类,在main方法中使用有参方式构造两个Student类型的对象并打印特征。
import java.util.Objects;

public class Student extends Object {
    private int id; // 用于描述学号的成员变量
    private String name; // 用于描述姓名的成员变量

    public Student() {
    }

    public Student(int id, String name) {
        setId(id);
        setName(name);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        if (id > 0) {
            this.id = id;
        } else {
            System.out.println("学号不合理哦!!!");
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
/*
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (id != student.id) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

 */
    /**
     * 为了比较两个对象的内容,也就是学号信息需要重写该方法
     */
    // Student this = s1;
    // Object obj = s2;
    /*
    @Override
    public boolean equals(Object obj) {
        // 当调用对象和参数对象指向同一个对象时,则内容一定相同
        if (this == obj) return true;
        // 当调用对象为不为空而参数对象为空时,则内容一定不相同
        if (null == obj) return false;
        // 判断obj指向的对象是否为Student类型的对象,若是则条件成立,否则条件不成立
        if (obj instanceof Student) {
            Student ts = (Student) obj;
            // 以学号作为基准判断两个对象是否相等  int是基本数据类型,内存空间中放的就是数据本身,使用 == 可以判断数据是否相同
            //return this.getId() == ts.getId();
            // 以姓名作为基准判断两个对象是否相等  String是引用数据类型,内存空间中放的是地址,使用 == 判断地址是否相同
            // 也就是判断两个对象中姓名字符串的地址是否相同,不够完美
            //return this.getName() == ts.getName();
            return this.getName().equals(ts.getName()); // 比较姓名字符串的内容是否相同
        }
        // 否则类型不一致没有可比性,则内容一定不相同
        return false;
    }
    */
    /**
     * 为了使得该方法的结果与equals方法的结果保持一致,从而满足Java官方的常规协定,需要重写该方法
     */
    /*
    @Override
    public int hashCode() {
        //return getId(); // 不再代表内存地址的编号了
        final int type = 12;
        //return type*31 + getId();
        return type*31 + getName().hashCode();
    }
    */
    /**
     * 为了返回更有意义的字符串数据,则需要重写该方法
     */
    /*
    @Override
    public String toString() {
        return "Student[id = " + getId() + ", name = " + getName() + "]";
    }
     */
}


public class StudentTest {

    public static void main(String[] args) {
		// 1.使用有参方式构造Student类型的两个对象并判断是否相等
        Student s1 = new Student(1001, "zhangfei");
        //Student s2 = new Student(1002, "guanyu");
        Student s2 = new Student(1001, "zhangfei");
        //Student s2 = s1;  // 表示s2和s1都指向了同一个对象,地址相同了
        // 下面调用从Object类中继承下来的equals方法,该方法默认比较两个对象的地址,可以查看源码验证
        // 当Student类中重写equals方法后,则调用重写以后的版本,比较内容
        //boolean b1 = s1.equals(s2);
        //Student s3 = null;
        //boolean b1 = s1.equals(s3);
        //Student s3 = s1;
        boolean b1 = s1.equals(s2);
        System.out.println("b1 = " + b1); // false true
        System.out.println(s1 == s2); // 比较地址  false

        System.out.println("----------------------------------------------------------");
        // 下面调用从Object类中继承下来的hashCode方法,获取调用对象的哈希码值(内存地址的编号)
        // 当Student类中重写hashCode方法后,则调用重写以后的版本
        int ia = s1.hashCode();
        int ib = s2.hashCode();
        System.out.println("ia = " + ia);
        System.out.println("ib = " + ib);

        System.out.println("----------------------------------------------------------");
        // 下面调用从Object类中继承下来的toString方法,获取调用对象的字符串形式:包名.类名@哈希码值的十六进制
        // 当Student类中重写toString方法后,则调用重写以后的版本:Student[id = 1001, name = zhangfei]
        String str1 = s1.toString();
        System.out.println("str1 = " + str1); // com.lagou.task11.Student@55d
        System.out.println(s1); // 当打印一个引用变量时会自动调用toString方法
        String str2 = "hello" + s1;
        System.out.println("str2 = " + str2);
    }
}

4. 小结

Object类主要需要知道一下知识点:

  1. equals() 方法 默认比较两个对象的地址是否相等,这一点与直接使用运算符 “ == ” 是一样的,以上例为例,如果我们希望使用equals()方法比较两个实例的内容是否相同就需要重写
  2. hashCode() 必须和equals() 保持一致,这是因为如果两个对象相同,那么获取到的哈希值也是一样的,所以在重写equals() 的时候也要记得重写 hashCode()
  3. 一般情况下我们可以直接使用快捷键生成 equals() hashCode() toString() 的代码

二、包装类的装箱与拆箱

1. 基本数据类型 与 对应的包装类

包装类 基本数据类型
java.lang.Byte byte
java.lang.Short short
java.lang.Integer int
java.lang.Long long
java.lang.Float float
java.lang.Double double
java.lang.Boolean boolean
java.lang.Character char

1.Integer

1.1 常用变量(表示一些常用的数值,比如最大值、最小值……)

常量名 功能
public static final int MAX_VALUE 表示 int 类型的最大值 : 2^31 - 1
public static final int MIN_VALUE 表示 int 类型的最小值 : - 2^31
public static final int SIZE 表示 int 类型采用二进制补码形式的位数
public static final int BYTES 表示 int 类型所占的字节个数
public static final Class TYPE 表示int类型的Class实例(反射机制中会用到)

1.2 常用方法

方法名 功能
Integer(int value) 根据参数指定的整数来构造对象(已过时)
Integer(String s) 根据参数指定的字符串来构造对象 (已过时)
int intValue() 获取调用对象中的整数值并返回 (拆箱, Java5已实现自动拆箱)
static Integer valueOf(int i) 根据参数指定整数值得到Integer类型对象 (装箱, Java5已实现自动装箱
boolean equals(Object obj) 比较调用对象与参数指定的对象是否相等
String toString() 返回描述调用对象数值的字符串形式
static int parseInt(String s) 将字符串类型转换为int类型并返回
static String toString(int i) 获取参数指定整数的十进制字符串形式
static String toBinaryString(int i) 获取参数指定整数的二进制字符串形式
static String toHexString(int i) 获取参数指定整数的十六进制字符串形式
static String toOctalString(int i) 获取参数指定整数的八进制字符串形式

1.3 装箱与拆箱

  1. 自从Java5发布之后,我们不再需要手动进行繁琐的 拆箱 装箱 操作,JVM会帮我们完成在运算时所需要的拆箱 与 装箱 操作。Integer te = 100; //自动拆箱 & int ib = te; //自动装箱
  2. 在Integer类的内部提供了自动装箱池技术,将-128到127之间的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率。

2.Double

2.1 常用常量

常量名 功能
public static final int SIZE 表示double类型的二进制位数
public static final int BYTES 表示double类型的字节个数
public static final Class TYPE 表示double类型的Class实例(反射机制中会用到)

2.2 常用方法

方法名 功能
Double(double value) 根据参数指定的浮点数据来构造对象(已过时)
Double(String s) 根据参数指定的字符串来构造对象 (已过时)
double doubleValue() 获取调用对象中的浮点数据并返回 (拆箱, Java5已实现自动拆箱)
static Double valueOf(double d) 根据参数指定浮点数据得到Double类型对象 (装箱, Java5已实现自动装箱
boolean equals(Object obj) 比较调用对象与参数指定的对象是否相等
String toString() 返回描述调用对象数值的字符串形式
static double parseDouble(String s) 将字符串类型转换为double类型并返回
boolean isNaN() 判断调用对象的数值是否为非数字

3.Boolean

3.1 常用常量

常量名 功能
public static final Boolean FALSE 对应基值为false的对象
public static final Boolean TRUE 对应基值为true的对象
public static final Class TYPE 表示boolean类型的Class实例

3.2 常用方法

方法名 功能
Boolean(boolean value) 根据参数指定的布尔数值来构造对象(已过时)
Boolean(String s) 根据参数指定的字符串来构造对象 (已过时)
boolean booleanValue() 获取调用对象中的布尔数值并返回 (拆箱, Java5已实现自动拆箱)
static Boolean valueOf(boolean b) 根据参数指定布尔数值得到Boolean类型对象(装箱, Java5已实现自动装箱
boolean equals(Object obj) 比较调用对象与参数指定的对象是否相等
String toString() 返回描述调用对象数值的字符串形式
static boolean parseBoolean(String s) 将字符串类型转换为boolean类型并返回

4.Character

4.1 常用常量

常量类型和名称 功能介绍
public static final int SIZE 表示char类型的二进制位数
public static final int BYTES 表示char类型的字节个数
public static final Class TYPE 表示char类型的Class实例

4.2常用方法

方法声明 功能介绍
Character(char value) 根据参数指定的字符数据来构造对象(已过时)
char charValue() 获取调用对象中的字符数据并返回
static Character valueOf(char c) 根据参数指定字符数据得到Character类型对象
boolean equals(Object obj) 比较调用对象与参数指定的对象是否相等
String toString() 返回描述调用对象数值的字符串形式
static boolean isUpperCase(char ch) 判断参数指定字符是否为大写字符
static boolean isLowerCase(char ch) 判断参数指定字符是否为小写字符
static boolean isDigit(char ch) 判断参数指定字符是否为数字字符
static char toUpperCase(char ch) 将参数指定的字符转换为大写字符
static char toLowerCase(char ch) 将参数指定的字符转换为小写字符

5.包装类小结

  • 基本数据类型转换为对应包装类的方式
    调用包装类的构造方法或静态方法即可
  • 获取包装类对象中基本数据类型变量数值的方式
    调用包装类中的xxxValue方法即可
  • 字符串转换为基本数据类型的方式
    调用包装类中的parseXxx方法即可

三、数学工具类

1. Math类

方法声明 功能介绍
static int max(int a, int b) 返回两个参数中的最大值
static int min(int a, int b) 返回两个参数中的最小值
static double pow(double a, double b) 返回第一个参数的幂
static int abs(int a) 返回参数指定数值的绝对值
static long round(double a) 返回参数四舍五入的结果
static double sqrt(double a) 返回参数的平方根
\static double random() 返回0.0到1.0的随机数

2. BigDecimal类(实现精确运算,因为float类型和double类型在运算时可能会有误差)

方法声明 功能介绍
BigDecimal(String val) 根据参数指定的字符串来构造对象
BigDecimal add(BigDecimal augend) 用于实现加法运算
BigDecimal subtract(BigDecimal subtrahend) 用于实现减法运算
BigDecimal multiply(BigDecimal multiplicand) 用于实现乘法运算
BigDecimal divide(BigDecimal divisor) 用于实现除法运算
public class BigDecimalTest {

    public static void main(String[] args) {

        // 1.构造BigDecimal类型的两个对象
        BigDecimal bd1 = new BigDecimal("5.2");
        BigDecimal bd2 = new BigDecimal("1.3");
        // 2.使用构造完毕的对象实现加减乘除运算
        System.out.println("实现加法运算的结果是:" + bd1.add(bd2)); // 6.5
        System.out.println("实现减法运算的结果是:" + bd1.subtract(bd2)); // 3.9
        System.out.println("实现乘法运算的结果是:" + bd1.multiply(bd2)); // 6.76
        System.out.println("实现除法运算的结果是:" + bd1.divide(bd2)); // 4

        System.out.println("---------------------------------------------------------------");
        // 3.实现精确运算
        System.out.println(0.1 + 0.2); // 0.30000000000000004
        BigDecimal bd3 = new BigDecimal("0.1");
        BigDecimal bd4 = new BigDecimal("0.2");
        System.out.println("精确计算的结果是:" + bd3.add(bd4)); // 0.3

        System.out.println("---------------------------------------------------------------");
        // 4.注意事项
        BigDecimal bd5 = new BigDecimal("2");
        BigDecimal bd6 = new BigDecimal("0.3");
        System.out.println("除法运算的结果是:" + bd5.divide(bd6, RoundingMode.HALF_UP)); // 7
    }
}

3.BigInteger类(表示比long类型范围还大的整数数据)

方法声明 功能介绍
BigInteger(String val) 根据参数指定的字符串来构造对象
BigInteger add(BigInteger val) 用于实现加法运算
BigInteger subtract(BigInteger val) 用于实现减法运算
BigInteger multiply(BigInteger val) 用于实现乘法运算
BigInteger divide(BigInteger val) 用于实现除法运算
BigInteger remainder(BigInteger val) 用于实现取余运算
BigInteger[] divideAndRemainder(BigInteger val) 用于实现取商和余数的运算
public class BigIntegerTest {

    public static void main(String[] args) {

        // 1.构造两个BigInteger类型的对象并指定初始值
        BigInteger bi1 = new BigInteger("20");
        BigInteger bi2 = new BigInteger("8");
        // 2.实现加减乘除取余操作并打印
        System.out.println("实现加法运算的结果是:" + bi1.add(bi2)); // 28
        System.out.println("实现减法运算的结果是:" + bi1.subtract(bi2)); // 12
        System.out.println("实现乘法运算的结果是:" + bi1.multiply(bi2)); // 160
        System.out.println("实现除法运算的结果是:" + bi1.divide(bi2)); // 2
        System.out.println("实现取余运算的结果是:" + bi1.remainder(bi2)); // 4

        System.out.println("-----------------------------------------------------");
        // 3.一次性得到商和余数
        BigInteger[] arr = bi1.divideAndRemainder(bi2);
        for (int i = 0; i < arr.length; i++) {
            System.out.println("下标为" + i + "的元素是:" + arr[i]); // 2 4
        }
    }
}

四、String类

1.基础知识

  • Java程序中所有的字符串字面值都可以使用该类的对象加以描述
  • 该类由final关键字修饰(该类不能被继承
  • 从jdk1.9开始该类的底层使用 byte[]加上编码标记来存储数据,从而节约了一些空间。
  • 该类描述的字符串内容是个常量不可更改,因此可以被共享使用,如:String str1 = “abc”; 其中"abc"这个字符串是个常量不可改变。 str1 = “123”; 将“123”字符串的地址赋值给变量str1。 改变的是str1的指向而不是改变指向的内容,换句话来说 str1 原本指向的是存储 “ abc ” 的空间的地址,当运行 str1 = “123”;后 str1 指向的是存储 “ 123 ” 的空间的地址,原本存储 “ abc ” 的空间如果不需要就会被JVM的垃圾回收机制自动回收。

2.常量池

我们在之前提到过,字符串内容是个常量不可更改。因此Java虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,从而提高了性能。如果我们想验证常量池的存在可以这样做:

public class StringPool {

    public static void main(String[] args) {
    	//String 除了可以使用new String(......)的方式外,还可以直接进行字符串的赋值,
        String str1 = "abc";
        String str2 = "abc";
        System.out.println(str1 == str2); // 我们在之前说过 == 比较的是:地址,所以如果运行结果是true就说明常量池存在 
    }
}

3. 常用构造方法(如同上文所说直接赋值也可以)

方法声明 功能介绍
String() 使用无参方式构造对象得到空字符序列
String(byte[] bytes, int offset, int length) 使用bytes数组中下标从offset位置开始的length个字节来构造对象
String(byte[] bytes) 使用bytes数组中的所有内容构造对象
String(char[] value, int offset, int count) 使用value数组中下标从offset位置开始的count个字符来构造对象
String(char[] value) 使用value数组中的所有内容构造对象
String(String original) 根据参数指定的字符串内容来构造对象,新创建对象为参数对象的副本

4.常用成员方法

方法声明 功能介绍
String toString() 返回字符串本身
byte[] getBytes() 将当前字符串内容转换为byte数组并返回
char[] toCharArray() 用于将当前字符串内容转换为char数组并返回
char charAt(int index) 方法charAt用于返回字符串指定位置的字符。
int length() 返回字符串字符序列的长度
boolean isEmpty() 判断字符串是否为空

判断一个字符串是否是回文(一道非常基础的题目了, 各种地方都能看见)

public class test {
/*
	简单说一下思路,从两头向中间比较,如果不同则说明不是回文
	使用到的方法也自然就是: charAt() & length()
*/
    public static void main(String[] args) {
        String str = new String("柳庭风静人眠昼,昼眠人静风庭柳"); 
        System.out.println(str);

        int len = str.length();
        for(int i = 0; i < len/2; i++) {
            if(str.charAt(i) != str.charAt(len - 1 - i)) {
                System.out.println("字符串不是回文");
                return; // 返回数据并结束方法
            }
        }

        System.out.println("字符串是回文");
    }
}
  • compareTo() & compareToIgnoreCase() 方法用于实现字符串之间的比较
方法声明 功能介绍
int compareTo(String anotherString) 用于比较调用对象和参数对象的大小关系
int compareToIgnoreCase(String str) 不考虑大小写,也就是’a’和’A’是相等的关系
public class test {
/*
	字符串比较的规则(compareTo()方法的比较方式) :
		按照字典序进行比较。我们可以看compareTo()的源码:
	public int compareTo(String anotherString) {
        byte v1[] = value;
        byte v2[] = anotherString.value;
        if (coder() == anotherString.coder()) {
            return isLatin1() ? StringLatin1.compareTo(v1, v2)
                              : StringUTF16.compareTo(v1, v2);
        }
        return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
                          : StringUTF16.compareToLatin1(v1, v2);
     }
     
	再看一下StringLatin1.compareTo(v1, v2)的源码:
	public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
        int lim = Math.min(len1, len2);
        for (int k = 0; k < lim; k++) {
            if (value[k] != other[k]) {
                return getChar(value, k) - getChar(other, k);
            }
        }
        return len1 - len2;
    }
	
	不难看出根本的方法就是,对两个字符串从前向后依次遍历每一个字符,令两个字符相同位置的字符进行减法运算(ASCII码),如果相同则计算下一个位置的两个字符,如果不同那么就得出了比较结果。
*/
    public static void main(String[] args) {
        // 1.构造String类型的对象并打印
        String str1 = new String("hello");
        System.out.println("str1 = " + str1); // hello

        // 2.使用构造好的对象与其它字符串对象之间比较大小并打印
        System.out.println(str1.compareTo("world"));  // 'h' - 'w' => 104 - 119 => -15
        System.out.println(str1.compareTo("haha"));   // 'e' - 'a' => 101 - 97  => 4
        System.out.println(str1.compareTo("hehe"));   // 'l' - 'h' => 108 - 104 => 4
        System.out.println(str1.compareTo("heihei")); // 'l' - 'i' => 108 - 105 => 3
        System.out.println(str1.compareTo("helloworld")); // 长度: 5 - 10 => -5
        System.out.println(str1.compareToIgnoreCase("HELLO")); // 0
    }
}
  • 其他常用方法(String类封装好的方法还是很多的)
  1. 字符串拼接常用方法
方法声明 功能介绍
String concat(String str) 用于实现字符串的拼接
boolean contains(CharSequence s) 用于判断当前字符串是否包含参数指定的内容
String toLowerCase() 返回字符串的小写形式
String toUpperCase() 返回字符串的大写形式
String trim() 返回去掉前导和后继空白的字符串
boolean startsWith(String prefix) 判断字符串是否以参数字符串开头
boolean startsWith(String prefix, int toffset) 从指定位置开始是否以参数字符串开头
boolean endsWith(String suffix) 判断字符串是否以参数字符串结尾
  1. 字符串比较常用方法
方法声明 功能介绍
boolean equals(Object anObject) 用于比较字符串内容是否相等并返回
int hashCode() 获取调用对象的哈希码值
boolean equalsIgnoreCase(String anotherString) 用于比较字符串内容是否相等并返回,不考虑大小写,如:'A’和’a’是相等
  1. 字符串获取下标常用方法
方法声明 功能介绍
int indexOf(int ch) 用于返回当前字符串中参数ch指定的字符第一次出现的下标
int indexOf(int ch, int fromIndex) 用于从fromIndex位置开始查找ch指定的字符
int indexOf(String str) 在字符串中检索str返回其第一次出现的位置,若找不到返回-1
int indexOf(String str, int fromIndex) 表示从字符串的fromIndex位置开始检索str第一次出现的位置
int lastIndexOf(int ch) 用于返回参数ch指定的字符最后一次出现的下标
int lastIndexOf(int ch, int fromIndex) 用于从fromIndex位置开始查找ch指定字符出现的下标
int lastIndexOf(String str) 返回str指定字符串最后一次出现的下标
int lastIndexOf(String str, int fromIndex) 用于从fromIndex位置开始反向搜索的第一次出现的下标。
  1. 字符串获取字串常用方法
方法声明 功能介绍
String substring(int beginIndex, int endIndex) 返回字符串中从下标beginIndex(包括)开始到 endIndex(不包括)结束的子字符串
String substring(int beginIndex) 返回字符串中从下标beginIndex(包括)开始到字符串结尾的子字符串

四(补)可变长字符串StringBuffer & StringBuilder

- 关于StringBuffer & StringBuilder:

  • StringBuffer类是从jdk1.0开始存在,属于线程安全的类,因此效率比较低。
  • StringBuilder类是从jdk1.5开始存在,属于非线程安全的类,效率比较高。(不考虑锁了,当然会更快)

StringBuilder类

1. 构造方法

方法声明 功能介绍
StringBuilder() 使用无参方式构造对象,容量为16
StringBuilder(int capacity) 根据参数指定的容量来构造对象,容量为参数指定大小
StringBuilder(String str) 根据参数指定的字符串来构造对象,容量为:16+字符串长度

2. 常用方法(前两个注意区分一下)

方法声明 功能介绍
int capacity() 用于返回调用对象的容量
int length() 用于返回字符串的长度,也就是字符的个数
StringBuilder insert(int offset, String str) 在下标为 offset 的位置插入字符串并返回调用对象的引用,就是自己。
StringBuilder append(String str) 在字符串末尾追加字符串
StringBuilder deleteCharAt(int index) 将当前字符串中下标为index位置的单个字符删除
StringBuilder delete(int start,int end) 删除区间在 [start, end) 的字符串,其实就是左闭右开
StringBuilder replace(int start,int end,String str) 替换字符串,和上面一样也是左闭右开
StringBuilder reverse() 字符串反转
  • StringBuilder的很多方法的返回值均为StringBuilder类型。这些方法的返回语句均为:return this。由此可见,这些方法在对StringBuilder所封装的字符序列进行改变后又返回了该对象的引用。基于这样设计的目的在于可以连续调用
  • 要注意insert()方法在插入时,参数index是字符串str的第一个字符出现的下标。如str = “xxxxxx” 则str.insert(3, “abc”)的结果为:xxxabcxxx

五、正则表达式

1. 基本知识

  • 正则表达式本质就是一个“规则字符串”,可以用于对字符串数据的格式进行验证,以及匹配、查找、替换等操作。该字符串通常使用^运算符作为开头标志,使用$运算符作为结尾标志,当然也可以省略。
  • 简单而言:正则表达式是由一些具有特殊含义的字符组成的字符串,多用于查找、替换符合规则的字符串。在表单验证、Url映射等处都会经常用到。

2.正则表达式的规则

正则表达式 说明
[abc] 可以出现a、b、c中任意一个字符
[^abc] 可以出现任何字符,除了a、b、c的任意字符
[a-z] 可以出现a、b、c、……、z中的任意一个字符
[a-zA-Z0-9] 可以出现az、AZ、0~9中任意一个字符
正则表达式 说明
. 任意一个字符(通常不包含换行符)
\d 任意一个数字字符,相当于[0-9]
\D 任意一个非数字字符
\s 空白字符,相当于[\t\n\x0B\f\r]
\S 非空白字符
\w 任意一个单词字符,相当于[a-zA-Z_0-9]
\W 任意一个非单词字符
正则表达式 说明
X? 表示X可以出现一次或一次也没有,也就是0 ~ 1次
X* 表示X可以出现零次或多次,也就是0 ~ n次
X+ 表示X可以出现一次或多次,也就是1 ~ n次
X{n} 表示X可以出现恰好 n 次
X{n,} 表示X可以出现至少 n 次,也就是>=n次
X{n,m} 表示X可以出现至少 n 次,但是不超过 m 次,也就是>=n并且<=m次
  • 简单的示例: 表示6位数字 String reg = "^[0-9]{6}$"; 当然就像一开始说到的我们可以省略 ^ 和 $ 写成这个样子String reg = "[0-9]{6}"; 之后我们还能进一步简化,用到第二个表格中的语法:String reg = "\\d{6}";
  • 稍微难一些:描述身份证号码的规则:总共18位,6位数字代表地区,4位数字代表年,2位数字代表月,2位数字代表日期, 3位数字代表个人,最后一位可能数字也可能是X。
    String reg = "(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9|X])";

3. 正则表达式的常用方法

正则表达式 说明
boolean matches(String regex) 判断当前正在调用的字符串是否匹配参数指定的正则表达式规则
String[] split(String regex) 参数regex为正则表达式,以regex所表示的字符串为分隔符,将字符串拆分成字符串数组
String replace(char oldChar, char newChar) 使用参数newChar替换此字符串中出现的所有参数 oldChar
String replaceFirst(String regex, String replacement) 替换此字符串匹配给定的正则表达式的第一个子字符串
String replaceAll(String regex, String replacement) 将字符串中匹配正则表达式regex的字符串替换成 replacement

六、日期类(就是一个工具类)

1. System类中的关于时间的方法

方法声明 功能介绍
static longcurrentTimeMillis() 返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差
public class SystemTest {

    public static void main(String[] args) {

        // 1.获取当前系统时间距离1970年1月1日0时0分0秒的毫秒数
        long msec = System.currentTimeMillis();
        System.out.println("当前系统时间距离1970年1月1日0时0分0秒已经过去" + msec + "毫秒了!");

        // 通常用于测试某一段代码的执行效率
    }
}

2.Date类 & SimpleDateFormat类

2.1 Date类

  • java.util.Date类主要用于描述特定的瞬间,也就是年月日时分秒,可以精确到毫秒
  • 常用方法如下:
方法声明 功能介绍
Date() 使用无参的方式构造对象,也就是当前系统时间
Date(long date) 根据参数指定毫秒数构造对象, 参数为距离1970年1月1日0时0分0秒的毫秒数
long getTime() 获取调用对象距离1970年1月1日0时0分0秒的毫秒数
void setTime(long time) 设置调用对象为距离基准时间time毫秒的时间点
  • 简单演示一下:
public class DateTest {

    public static void main(String[] args) {

        // 1.使用无参方式构造Date对象并打印
        Date d1 = new Date();
        System.out.println("d1 = " + d1); // 获取当前系统时间

        System.out.println("------------------------------------");
        // 2.使用参数指定的毫秒数来构造Date对象并打印  1秒 = 1000毫秒  东八区
        Date d2 = new Date(1000);
        System.out.println("d2 = " + d2); // 1970 1 1 8 0 1

        System.out.println("------------------------------------");
        // 3.获取调用对象距离1970年1月1日0时0分0秒的毫秒数
        long msec = d2.getTime();
        System.out.println("获取到的毫秒数是:" + msec); // 1000

        // 4.设置调用对象所表示的时间点为参数指定的毫秒数
        d2.setTime(2000);
        System.out.println("修改后的时间是:" + d2); // 1970 1 1 8 0 2
    }
}

2.2 SimpleDateFormat类

  • 我们可以用 SimpleDateFormat类 实现日期和文本之间的转换。
  • 常用方法如下:
方法声明 功能介绍
SimpleDateFormat() 使用无参方式构造对象
SimpleDateFormat(String pattern) 根据参数指定的模式来构造对象,模式主要有: y-年 M-月 d-日 H-时 m-分 s-秒
final String format(Date date) 用于将日期类型转换为文本类型
Date parse(String source) 用于将文本类型转换为日期类型
  • 通过代码看一下:
public class SimpleDateFormatTest {

    public static void main(String[] args) throws Exception {

        // 1.获取当前系统时间并打印
        Date d1 = new Date();
        System.out.println("d1 = " + d1);

        // 2.构造SimpleDateFormat类型的对象并指定格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        // 3.实现日期类型向文本类型的转换并打印
        String format = sdf.format(d1);
        System.out.println("转换后的日期为:" + format);
        
        // 4.实现文本类型到日期类型的转换并打印
        Date parse = sdf.parse(format);
        System.out.println("转回日期格式的结果为:" + parse);
    }
}

2.3 Calendar类

  • java.util.Calender类主要用于描述特定的瞬间,取代Date类中的过时方法实现全球化。
  • 该类是个抽象类,因此不能实例化对象,其具体子类针对不同国家的日历系统,其中应用最广泛的是GregorianCalendar(格里高利历),对应世界上绝大多数国家/地区使用的标准日历系统。
  • 常用方法如下:
方法声明 功能介绍
static Calendar getInstance() 用于获取Calendar类型的引用
void set(int year, int month, int date, int hourOfDay, int minute, int second) 用于设置年月日时分秒信息
Date getTime() 用于将Calendar类型转换为Date类型
void set(int field, int value) 设置指定字段的数值
void add(int field, int amount) 向指定字段增加数值

我们要注意一个问题那就是Calendar类是一个抽象类,那为什么可以可以通过 getInstance() 方法用于获取Calendar类型的引用?
这里实际上是使用了多态,也就是常说的多态的第三种使用方式。我们由源码可知,返回的并不是Calendar类型的对象,而是Calendar类的子类GregorianCalendar等对象,形成了多态

  • 代码如下:
		// 2.1 获取Calendar类型的引用	
        Calendar instance = Calendar.get Instance();
        // 2.2 设置指定的年月日时分秒信息
        instance.set(2008, 8-1, 8, 20, 8, 8);
        // 2.3 转换为Date类型的对象
        Date d2 = instance.getTime();
        String format1 = sdf.format(d2);
        System.out.println("获取到的时间是:" + format1); // 2008 8 8 20 8 8

小结:多态的使用场合

  • 通过方法的参数传递形成多态;
public static void draw(Shape s){
	s.show();
}

draw(new Rect(1, 2, 3, 4));
  • 在方法体中直接使用多态的语法格式
Account acc = new FixedAccount();
  • 通过方法的返回值类型形成多态
Calender getInstance(){
	return new GregorianCalendar(zone, aLocale);
 }

3. Java8的日期类新特性

  • Java 8通过发布新的Date-Time API来进一步加强对 日期与时间的处理。
  • java.time包:该包日期/时间API的基础包。
  • java.time.chrono包:该包提供对不同日历系统的访问。
  • java.time.format包:该包能够格式化和解析日期时间对象。
  • java.time.temporal包:该包包含底层框架和扩展特性。
  • java.time.zone包:该包支持不同时区以及相关规则的类。

3.1 LocalDate类的概述

  • java.time.LocalDate类主要用于描述年-月-日格式的日期信息,该类不表示时间和时区信息
  • 常用方法如下,使用起来也很简单,LocalDate now = LocalDate.now();之后就可以直接输出了,比如:System.out.println("获取到的当前日期是:" + now);
方法声明 功能介绍
static LocalDate now() 在默认时区中从系统时钟获取当前日期

3.2 LocalTime类的概述

  • java.time.LocalTime 类主要用于描述时间信息,可以描述时分秒以及纳秒。
  • 常用方法如下,使用的话和上面的LocalDate类是一样的
方法声明 功能介绍
static LocalTime now() 从默认时区的系统时间中获取当前时间
static LocalTime now(ZoneId zone) 获取指定时区的当前时间

3.3 LocalDateTime类的概述

  • java.time.LocalDateTime类主要用于描述ISO-8601日历系统中没有时区的日期时间,如2007-12-03T10:15:30。
  • 常用方法如下:
方法声明 功能介绍
static LocalDateTime now() 从默认时区的系统时间中获取当前日期时间
static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second) 根据参数指定的年月日时分秒信息来设置日期时间
int getYear() 获取年份字段的数值
int getMonthValue() 获取1到12之间的月份字段
int getDayOfMonth() 获取日期字段
int getHour() 获取小时数
int getMinute() 获取分钟数
int getSecond() 获取秒数
LocalDateTime withYear(int year) 设置为参数指定的年
LocalDateTime withMonth(int month) 设置为参数指定的月
LocalDateTime withDayOfMonth(int dayOfMonth) 设置为参数指定的日
LocalDateTime withHour(int hour) 设置为参数指定的时
LocalDateTime withMinute(int minute) 设置为参数指定的分
LocalDateTime withSecond(int second) 设置为参数指定的秒
LocalDateTime plusYears(long years) 加上参数指定的年
LocalDateTime plusMonths(long months) 加上参数指定的月
LocalDateTime plusDays(long days) 加上参数指定的日
LocalDateTime plusHours(long hours) 加上参数指定的时
LocalDateTime plusMinutes(long minutes) 加上参数指定的分
LocalDateTime plusSeconds(long seconds) 加上参数指定的秒
LocalDateTime minusYears(long years) 减去参数指定的年
LocalDateTime minusMonths(long months) 减去参数指定的月
LocalDateTime minusDays(long days) 减去参数指定的日
LocalDateTime minusHours(long hours) 减去参数指定的时
LocalDateTime minusMinutes(long minutes) 减去参数指定的分
LocalDateTime minusSeconds(long seconds) 减去参数指定的秒
  • LocalDateTime类的使用是比较简单的,代码示例如下:
public class LocalDateTimeTest {
/*
简单来说两步走:1. 调用 now() 或 of() 获取LocalDateTime对象 --- 2. 调用常用方法,想干啥干啥,怎么舒服怎么来~~~
*/
    public static void main(String[] args) {

        // 1.获取当前日期信息并打印
        LocalDate now = LocalDate.now();
        System.out.println("获取到的当前日期是:" + now);
        // 2.获取当前时间信息并打印
        LocalTime now1 = LocalTime.now();
        System.out.println("获取到的当前时间是:" + now1);
        // 3.获取当前日期时间信息并打印,使用最多
        LocalDateTime now2 = LocalDateTime.now();
        System.out.println("获取到的当前日期时间是:" + now2);

        System.out.println("-------------------------------------------------------");
        // 4.使用参数指定的年月日时分秒信息来获取对象并打印
        // 使用ctrl+F12来查找指定的方法
        LocalDateTime of = LocalDateTime.of(2008, 8, 8, 20, 8, 8);
        System.out.println("指定的日期时间是:" + of); // 自动调用toString方法
        System.out.println("获取到的年是:" + of.getYear()); // 2008
        System.out.println("获取到的月是:" + of.getMonthValue()); // 8
        System.out.println("获取到的日是:" + of.getDayOfMonth()); // 8
        System.out.println("获取到的时是:" + of.getHour()); // 20
        System.out.println("获取到的分是:" + of.getMinute()); // 8
        System.out.println("获取到的秒是:" + of.getSecond()); // 8

        System.out.println("-------------------------------------------------------");
        // 5.实现特征的设置并打印
        // 与String类型相似,调用对象本身的数据内容不会改变,返回值相当于创建了一个新的对象,由此证明了不可变性
        LocalDateTime localDateTime = of.withYear(2012);
        System.out.println("localDateTime = " + localDateTime); // 2012-08-08T20:08:08
        System.out.println("of = " + of); // 2008-08-08T20:08:08
        LocalDateTime localDateTime1 = localDateTime.withMonth(12);
        System.out.println("localDateTime1 = " + localDateTime1); // 2012 12 8 20 8 8

        System.out.println("-------------------------------------------------------");
        // 6.实现特征的增加并打印
        LocalDateTime localDateTime2 = localDateTime1.plusDays(2);
        System.out.println("localDateTime2 = " + localDateTime2); // 2012 12 10 20 8 8
        System.out.println("localDateTime1 = " + localDateTime1); // 2012 12 8 20 8 8
        LocalDateTime localDateTime3 = localDateTime2.plusHours(3);
        System.out.println("localDateTime3 = " + localDateTime3); // 2012 12 10 23 8 8

        System.out.println("-------------------------------------------------------");
        // 7.实现特征的减少并打印
        LocalDateTime localDateTime4 = localDateTime3.minusMinutes(1);
        System.out.println("localDateTime4 = " + localDateTime4); // 2012 12 10 23 7 8
        System.out.println("localDateTime3 = " + localDateTime3); // 2012 12 10 23 8 8
        LocalDateTime localDateTime5 = localDateTime4.minusSeconds(3);
        System.out.println("localDateTime5 = " + localDateTime5); // 2012 12 10 23 7 5

    }
}

3.4 Instant类

  • 用于描述瞬间的时间点信息
  • 常用方法:
方法声明 功能介绍
static Instant now() 从系统时钟上获取当前时间
OffsetDateTime atOffset(ZoneOffset offset) 将此瞬间与偏移量组合以创建偏移日期时间
static Instant ofEpochMilli(long epochMilli) 根据参数指定的毫秒数来构造对象,参数为距离1970年1月1 日0时0分0秒的毫秒数
long toEpochMilli() 获取距离1970年1月1日0时0分0秒的毫秒数
  • 代码示例:
public class InstantTest {

    public static void main(String[] args) {

        // 1.使用Instant类来获取当前系统时间  并不是当前系统的默认时区  本初子午线   差8小时  东八区
        Instant now = Instant.now();
        System.out.println("获取到的当前时间为:" + now);

        // 2.加上时区所差的8个小时
        OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
        System.out.println("偏移后的日期时间为:" + offsetDateTime);

        System.out.println("--------------------------------------------------------");
        // 3.获取当前调用对象距离标准基准时间的毫秒数
        long g1 = now.toEpochMilli();
        System.out.println("获取到的毫秒差为:" + g1);

        // 4.根据参数指定的毫秒数来构造对象
        Instant instant = Instant.ofEpochMilli(g1);
        System.out.println("根据参数指定的毫秒数构造出来的对象为:" + instant);
    }
}

3.5 DateTimeFormatter类

  • 用于格式化和解析日期。
  • 常用方法:
方法声明 功能介绍
static DateTimeFormatter ofPattern(String pattern) 根据参数指定的模式来获取对象
String format(TemporalAccessor temporal) 将参数指定日期时间转换为字符串
TemporalAccessor parse(CharSequence text) 将参数指定字符串转换为日期时间
  • 代码示例:
public class DateTimeFormatterTest {

    public static void main(String[] args) {

        // 1.获取当前系统的日期时间并打印
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = " + now);

        // 2.按照指定的格式准备一个DateTimeFormatter类型的对象
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 3.实现日期时间向字符串类型的转换并打印
        String str = dateTimeFormatter.format(now);
        System.out.println("调整格式后的结果是:" + str);
        // 4.实现字符串类型到日期时间类型的转换并打印
        TemporalAccessor parse = dateTimeFormatter.parse(str);
        System.out.println("转回去的结果是:" + parse);
    }
}


七、集合(灰常的重要,即便算法不是那么精通,平时也会经常用到)

1.集合的框架

JavaSE学习小结二_第1张图片

  • 首先来说Java集合框架的两个顶层的框架: java.util.Collection 集合 & java.util.Map 集合
  • java.util.Collection 集合存取元素的基本单位是:单个元素
  • java.util.Map 集合存取元素的基本单位是:单个键值对

2.Collection集合

  • 从上图我们可以很清晰地看到 java.util.Collection 接口是 List 接口、Queue 接口以及 Set 接口的父接口,因此该接口里定义的方法既可用于操作 List 集合,也可用于操作 Queue 集合和 Set 集合。
  • 这里也再说一句:如果想创建Collection对象,依旧是使用多态,比如Collection c1 = new ArrayList();
  • 常用方法(能记多少记多少,忘了直接查API或者看这这里~):
方法声明 功能介绍
boolean add(E e) 向集合中添加对象
boolean addAll(Collection c) 用于将参数指定集合c中的所有元素添加到当前集合中
boolean contains(Object o) 判断是否包含指定对象
boolean containsAll(Collection c) 判断是否包含参数指定的所有对象
boolean retainAll(Collection c) 保留当前集合中存在且参数集合中存在的所有对象
boolean remove(Object o) 从集合中删除对象
boolean removeAll(Collection c) 从集合中删除参数指定的所有对象
void clear() 清空集合
int size() 返回包含对象的个数
boolean isEmpty() 判断是否为空
boolean equals(Object o) 判断是否相等
int hashCode() 获取当前集合的哈希码值
Object[] toArray() 将集合转换为数组
Iterator iterator() 获取当前集合的迭代器
  • 代买示例:
public class test {
    public static void main(String[] args) {
        // 1.准备一个Collection集合并打印
        // 接口类型的引用指向实现类的对象,形成了多态
        Collection c1 = new ArrayList();
        // 自动调用toString方法
        System.out.println("集合中的元素有:" + c1); // []

        System.out.println("--------------------------------------------------------");
        // 2.向集合中添加单个元素并打印
        c1.add(new String("one"));
        System.out.println("集合中的元素有:" + c1); // [one]

        c1.add(Integer.valueOf(2));
        System.out.println("集合中的元素有:" + c1); // [one, 2]

        c1.add(new Person("zhangfei", 30));
    
        System.out.println("集合中的元素有:" + c1); // [one, 2, Person{name='zhangfei', age=30}]
        
        System.out.println("--------------------------------------------------------");
        //这里有一个小的知识点,是关于add() & addAll()的
        Collection c2 = new ArrayList();
        c2.add("two");  
        c2.add(4);        
        System.out.println("c2 = " + c2);

		Collection c3 = new ArrayList();
        c3.add("three");  
        c3.add(6);        
        System.out.println("c3 = " + c3);
        
        // 将c2中的所有元素全部添加到集合c1中,也就是将集合c2中的元素一个一个依次添加到集合c1中
        c1.addAll(c2);
        // 表示将集合c3整体看做一个元素添加到集合c1中
        c1.add(c3);
        
        System.out.println("c1 = " + c1); //c1 = [one, 2, Person{name='zhangfei', age=30}, two, 4, [three, 6]]

		System.out.println("--------------------------------------------------------");
		//关于contains方法的知识点
		/*contains方法的工作原理是:Objects.equals(o, e),其中o代表contains方法的形式参数,e代表集合中的每个元素
          也就是contains的工作原理就是 拿着参数对象与集合中已有的元素依次进行比较,比较的方式调用Objects中的equals方法
          而该方法equals的工作原理如下:
        
          public static boolean equals(Object a, Object b) {    其中a代表Person对象,b代表集合中已有的对象
            return (a == b) || (a != null && a.equals(b));
            元素包含的第一种方式就是:Person对象与集合中已有对象的地址相同
                     第二种方式就是:Person对象不为空,则Person对象调用equals方法与集合中已有元素相等
          }
        
         当Person类中没有重写equals方法时,则调用从Object类中继承下来的equals方法,比较两个对象的地址  false
         当Person类中重写equals方法后,则调用重写以后的版本,比较两个对象的内容  true


		 此外,contains() 和 containsAll()的关系和add() & addAll()是一样的
        */
        
        System.out.println("--------------------------------------------------------");
        //现集合和数组类型之间的转换   通常认为:集合是用于取代数组的结构
        // 实现集合向数组类型的转换
        Object[] objects = c5.toArray();
        // 打印数组中的所有元素
        System.out.println("数组中的元素有:" + Arrays.toString(objects)); 
        // 实现数组类型到集合类型的转换
        Collection objects1 = Arrays.asList(objects);
        System.out.println("集合中的元素有:" + objects1); 
    }
}

3.Iterator接口 — 迭代器

  • 迭代器,就是用于遍历集合的。而且呢,java.util.Collection 接口继承 Iterator接 口,因此所有实现 Collection接 口的实现类都可以使用该迭代器对象。
  • 常用方法也就三个:
方法声明 功能介绍
boolean hasNext() 判断集合中是否有可以迭代/访问的元素
E next() 用于取出一个元素并指向下一个元素
void remove() 用于删除访问到的最后一个元素
  • 我们就可以使用这个迭代器,来模拟一下toString方法:
public class test {

    public static void main(String[] args) {

        // 1.准备一个Collection集合并放入元素后打印
        Collection c1 = new ArrayList();
        c1.add("one");
        c1.add(2);
        c1.add(new Person("zhangfei", 30));

        System.out.println("------------------------------------------------"); 
        //  获取当前集合中的迭代器对象
        Iterator iterator1 = c1.iterator();
       
        //使用迭代器来模拟toString方法的打印效果
        StringBuilder sb1 = new StringBuilder();
        sb1.append("[");
        while (iterator1.hasNext()) {
            Object obj = iterator1.next();
            // 当获取的元素是最后一个元素时,则拼接元素加中括号
            if (!iterator1.hasNext()) {
                sb1.append(obj).append("]");
            } else {
                // 否则拼接元素加逗号加空格
                sb1.append(obj).append(",").append(" ");
            }
        }
        // [one, 2, Person{name='zhangfei', age=30}]
        System.out.println("c1 = " + sb1);

        System.out.println("------------------------------------------------");
        //不断地去获取集合中的元素并判断,当元素值为"one"时则删除该元素
        iterator1 = c1.iterator();
        while (iterator1.hasNext()) {
            Object obj = iterator1.next();
            if("one".equals(obj)) {
                iterator1.remove();  //使用迭代器的remove方法删除元素没问题
                //c1.remove(obj); // 使用集合的remove方法编译ok,运行发生ConcurrentModificationException并发修改异常
            }
        }
        System.out.println("删除后集合中的元素有:" + c1); // [2, Person{name='zhangfei', age=30}]
    }
}
  • 注意事项 :迭代的过程中,要用迭代器的remove()而不是集合的remove()

4.List集合

4.1 基本概念

  • java.util.List集合是Collection集合的子集合,该集合中允许有重复的元素并且有先后放入次序。
  • 该集合的主要实现类有:ArrayList类、LinkedList类、Stack类、Vector类。
  • 其中ArrayList类的底层是采用动态数组进行数据管理的,支持下标访问,增删元素不方便。
  • 其中LinkedList类的底层是采用双向链表进行数据管理的,访问不方便,增删元素方便。
  • 可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList 更适合于随机访问而LinkedList更适合于插入和删除;在性能要求不是特别苛刻的情形下可以忽略这个差别。
  • 其中Stack类的底层是采用动态数组进行数据管理的,该类主要用于描述一种具有后进先出特征的 数据结构,叫做栈(last in first out LIFO)。
  • 其中Vector类的底层是采用动态数组进行数据管理的,该类与ArrayList类相比属于线程安全的
    类,效率比较低,以后开发中基本不用。

4.2 常用方法

方法声明 功能介绍
void add(int index, E element) 向集合中指定位置添加元素
boolean addAll(int index, Collection c) 向集合中添加所有元素
E get(int index) 从集合中获取指定位置元素
int indexOf(Object o) 查找参数指定的对象
int lastIndexOf(Object o) 反向查找参数指定的对象
E set(int index, E element) 修改指定位置的元素
E remove(int index) 删除指定位置的元素
List subList(int fromIndex, int toIndex) 用于获取子List

4.3 Stack 栈

常用方法如下:

方法声明 功能介绍
boolean empty() 测试堆栈是否为空。
Object peek( ) 查看堆栈顶部的对象,但不从堆栈中移除它。
Object pop( ) 移除堆栈顶部的对象,并作为此函数的值返回该对象。
Object push(Object element) 把项压入堆栈顶部。
int search(Object element) 返回对象在堆栈中的位置,以 1 为基数。

示例:准备一个Stack集合,将数据11、22、33、44、55依次入栈并打印,然后查看栈顶元素并打印,然后将栈中所有数据依次出栈并打印。再准备一个Stack对象,将数据从第一个栈中取出来放入第二个栈中,然后再从第二个栈中取出并打印。

public class test {
    public static void main(String[] args) {
       Stack s1 = new Stack();
       Stack s2 = new Stack();

       for(int i = 1; i<= 5; i++) {
           s1.push(i*11);
       }

        System.out.println("s1 : " + s1);

       int len = s1.size();
       for(int i = 1; i <= len; i++){
           Object pop = s1.pop();
           s2.push(pop);
       }
       System.out.println("s2 : " + s2);

        len = s2.size();
        for (int i = 1; i <= len; i++) {
            Object to = s2.pop();
            System.out.println("出栈的元素是:" + to); // 11 22 33 44 55
        }
    }
}
  • 运行结果:
    JavaSE学习小结二_第2张图片

4.4 Queue集合

  • java.util.Queue集合是Collection集合的子集合,与List集合属于平级关系。
  • 该集合的主要用于描述具有先进先出特征的数据结构,叫做队列(first in first out FIFO)。
  • 该集合的主要实现类是LinkedList类,因为该类在增删方面比较有优势
  • 常用方法如下:
方法声明 功能介绍
boolean offer(E e) 将一个对象添加至队尾,若添加成功则返回true
E poll() 从队首删除并返回一个元素
E peek() 返回队首的元素(但并不删除)

示例:准备一个Queue集合,将数据11、22、33、44、55依次入队并打印,然后查看队首元素并打印,然后将队列中所有数据依次出队并打印

public class test {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();

        for(int i = 1; i <= 5; i++)
            queue.offer(i * 11);

        System.out.println(queue);

        int len = queue.size();
        for(int i = 1; i <= len; i++) {
            Integer poll = queue.poll();
            System.out.println("出队的元素是:" + poll);
        }
    }
}
  • 运行结果:

JavaSE学习小结二_第3张图片

5. Set集合

5.1 基本概念:

  • 该集合中元素没有先后放入次序,且不允许重复。
  • 该集合的主要实现类是:HashSet类 和 TreeSet类以及LinkedHashSet类。
  • 其中HashSet类的底层是采用哈希表进行数据管理的。
  • 其中TreeSet类的底层是采用红黑树进行数据管理的。
  • 其中LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。

5.2 常用方法

  • 参照Collection集合中的方法

5.3 元素放入HashSet集合的原理(数据结构哈希表的概念)

  • 使用元素调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算出该元素在数组中的索引位置。若该位置没有元素,则将该元素直接放入即可。若该位置有元素,则使用新元素与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。若新元素与已有元素的哈希值相同,则使用新元素调用equals方法与已有元素依次比较。若相等则添加元素失败,否则将元素直接放入即可。
  • 这也就是为什么要求重写equals方法后要重写hashCode方法。 当两个元素调用equals方法相等时证明这两个元素相同,重写hashCode方法后保证这两个元素得到的哈希码值相同,由同一个哈希算法生成的索引位置相同,此时只需要与该索引位置已有元素比较即可,从而提高效率并避免重复元素的出现。

5.4 TreeSet集合的概念

  • TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
  • 自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序**(个人倾向于优先使用比较器)**

比较元素大小的规则有两种方式:
使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口;
使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;

//使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口
public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        //return 0;   // 调用对象和参数对象相等,调用对象就是新增加的对象
        //return -1;  // 调用对象小于参数对象
        //return 1;     // 调用对象大于参数对象
        //return this.getName().compareTo(o.getName());  // 比较姓名 getName()返回的是字符串,而String类中已经实现compareTO()
        //return this.getAge() - o.getAge(); // 比较年龄
        int ia = this.getName().compareTo(o.getName());
        if (ia == 0) { //如果同名,比年龄
            return this.getAge() - o.getAge();
        }
        return ia;

//        int ia = this.getName().compareTo(o.getName());
//        return ia != 0 ? ia : this.getAge() - o.getAge();
    }
}
public class TreeSetTest {
    public static void main(String[] args) {
    /*
        Set s1 = new TreeSet<>();
        s1.add("aa");
        System.out.println(s1);
        s1.add("cc");
        s1.add("bb");
        //TreeSet集合底层由红黑树实现,故元素由大小次序,默认从小到大打印 ------> String类型已经实现自然排序规则
        System.out.println(s1);
    */
        System.out.println("*****************");
        //设置一个比较器作为构造方法的参数 ----> 使用匿名内部类实现
        //使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;
        Comparator<Student> comparator = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) { // o1表示新增加的对象(add)  o2表示集合中已有的对象
                int ia = o1.getName().compareTo(o2.getName());
                if(ia == 0) {
                    return o1.getAge() - o2.getAge();
                }
                return ia;
            }
        };

        // 从Java8开始支持Lambda表达式: (参数列表) -> { 方法体 }
        //Comparator comparator = (Student o1, Student o2) -> { return o1.getAge() - o2.getAge(); };

        Set<Student> s2 = new TreeSet<>(comparator);
        s2.add(new Student("zhangfei", 35));
        s2.add(new Student("zhangfei", 30));
        s2.add(new Student("guanyu", 35));
        s2.add(new Student("liubei", 40));
        System.out.println("s2 = " + s2);

    }
}

6. Map集合(※※)

6.1 基本概念

  • java.util.Map集合中存取元素的基本单位是:单对元素,其中类型参数如下:
    K - 此映射所维护的键(Key)的类型,相当于目录。
    V - 映射值(Value)的类型,相当于内容。
  • 该集合中key是不允许重复的,而且一个key只能对应一个value。
  • 该集合的主要实现类有:HashMap类、TreeMap类、LinkedHashMap类、Hashtable类、Properties类。
  • 其中HashMap类的底层是采用哈希表进行数据管理的。
  • 其中TreeMap类的底层是采用红黑树进行数据管理的。
  • 其中LinkedHashMap类与HashMap类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
  • 其中Hashtable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作 为key或者value的数值。
  • 其中Properties类是Hashtable类的子类,该对象用于处理属性文件,key和value都是String类型的。
  • Map集合是面向查询优化的数据结构, 在大数据量情况下有着优良的查询性能。经常用于根据key检索value的业务场景。

6.2 常用方法

方法声明 功能介绍
V put(K key, V value) 将Key-Value对存入Map,若集合中已经包含该Key,则替换该Key所对应的Value,返回值为该Key原来所对应的Value,若没有则返回null
V get(Object key) 返回与参数Key所对应的Value对象,如果不存在则返回null
boolean containsKey(Object key) 判断集合中是否包含指定的Key
boolean containsValue (Object value) 判断集合中是否包含指定的Value
V remove(Object key) 根据参数指定的key进行删除
Set keySet() 返回此映射中包含的键的Set视图
Collection values() 返回此映射中包含的值的Set视图
Set> entrySet() 返回此映射中包含的映射的Set视图

6. 3元素放入HashMap集合的原理

  • 使用元素的key调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算在数组中的索引位置。
  • 若该位置没有元素,则将该键值对直接放入即可。
  • 若该位置有元素,则使用key与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
  • 若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较。
  • 若相等则将对应的value修改,否则将键值对直接放入即可。

6.4 相关的常量

  • DEFAULT_INITIAL_CAPACITY : HashMap的默认容量是16。
  • DEFAULT_LOAD_FACTOR:HashMap的默认加载因子是0.75。
  • threshold:扩容的临界值,该数值为:容量*填充因子,也就是12。
  • TREEIFY_THRESHOLD:若Bucket中链表长度大于该默认值则转化为红黑树存储,该数值是8。
  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量,该数值是64。

7.Collections类 — 提供对集合操作或者返回集合的静态方法(工具类一个)

方法声明 功能介绍
static > T max(Collection coll) 根据元素的自然顺序返回给定集 合的最大元素
static T max(Collection coll, Comparator comp) 根据指定比较器引发的顺序返回给定集合的最大元素
static > T min(Collection coll) 根据元素的自然顺序返回给定集合的最小元素
static T min(Collection coll, Comparator comp) 根据指定比较器引发的顺序返回给定集合的最小元素
static void copy(List dest, List src) 将一个列表中的所有元素复制到另一个列表中
方法声明 功能介绍
static void reverse(List list) 反转指定列表中元素的顺序
static void shuffle(List list) 使用默认的随机源随机置换指定的列表
static > void sort(List list) 根据其元素的自然顺序将指定列表按升序排序
static void sort(List list, Comparator c) 根据指定比较器指定的顺序对指定列表进行排序
static void swap(List list, int i, int j) 交换指定列表中指定位置的元素
public class Test {

    public static void main(String[] args) {

        // 1.准备一个集合并初始化
        List<Integer> lt1 = Arrays.asList(10, 30, 20, 50, 45);
        // 2.实现集合中元素的各种操作
        System.out.println("集合中的最大值是:" + Collections.max(lt1)); // 50
        System.out.println("集合中的最小值是:" + Collections.min(lt1)); // 10

        // 实现集合中元素的反转
        Collections.reverse(lt1);
        System.out.println("lt1 = " + lt1); // [45, 50, 20, 30, 10]
        // 实现两个元素的交换
        Collections.swap(lt1, 0, 4);
        System.out.println("交换后:lt1 = " + lt1); // [10, 50, 20, 30, 45]
        // 实现元素的排序
        Collections.sort(lt1);
        System.out.println("排序后:lt1 = " + lt1); // [10, 20, 30, 45, 50]
        // 随机置换
        Collections.shuffle(lt1);
        System.out.println("随机置换后:lt1 = " + lt1); // [30, 10, 45, 20, 50] 随机
        // 实现集合间元素的拷贝
        //List lt2 = new ArrayList<>(20);
        List<Integer> lt2 = Arrays.asList(new Integer[10]);
        System.out.println("lt1的大小是:" + lt1.size());
        System.out.println("lt2的大小是:" + lt2.size());
        // 表示将lt1中的元素拷贝到lt2中
        Collections.copy(lt2, lt1);
        System.out.println("lt2 = " + lt2);
    }
}

八、泛型

1. 基本概念

  • 通常情况下集合中可以存放不同类型的对象,是因为将所有对象都看做Object类型放入的,因此从集合中取出元素时也是Object类型,为了表达该元素真实的数据类型,则需要强制类型转换,而强制类型转换可能会引发类型转换异常。
  • 为了避免上述错误的发生,从Java5开始增加泛型机制,也就是在集合名称的右侧使用<数据类型>的方式来明确要求该集合中可以存放的元素类型,若放入其它类型的元素则编译报错。
  • 泛型只在编译时期有效,在运行时期不区分是什么类型。
  • 说的更加通俗一些就是,我们将数据类型作为参数传递,其中E相当于形式参数负责占位。以 Queue 队列来举例,Queue queue = new LinkedList<>(); <>中的 Integer 就是一个实参,而 Queue< E > 类中的 E就是一个形参,当我们在具体编写代码时具体传入的是一个什么样的数据类型,这个集合就会存储什么数据类型。

2. 泛型类、泛型方法 & 泛型接口

2.1 泛型类

  • 泛型类,就是相比于普通类而言在类名之后添加了类型参数列表, 比如我们熟知的Stack< E >,ArrayList< E > ,Map等等,等到实例化时指定具体的数据类型(引用数据类型,比如Integer……)

2.2 泛型方法

  • 泛型方法就是我们输入参数的时候,输入的是泛型参数,而不是具体的参数。我们在调用这个泛型方法的时需要对泛型参数进行实例化。
  • 语法格式:[访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]) { 方法体; }
  • 此外还要注意一点:在静态方法中使用泛型参数的时候,需要我们把静态方法定义为泛型方法
public class Person<T> {
    private String name;
    private int age;
    private T gender;

	// geter,seter以及构造方法可自动生成

    // 不是泛型方法,该方法不能使用static关键字修饰,因为该方法中的T需要在new对象时才能明确类型
    public T getGender() {
        return gender;
    }

    public void setGender(T gender) {
        this.gender = gender;
    }

    // 自定义方法实现将参数指定数组中的所有元素打印出来
    public static <T1> void printArray(T1[] arr) {
        for (T1 tt: arr) {
            System.out.println("tt = " + tt);
        }
    }
}

2.3 泛型接口

  • 泛型接口的概念相比于之前的泛型类、泛型方法更加简单,仅仅是在普通接口的后面添加了类型参数列表
interface Test<T>{
	public void show(T t);
}

2.4 泛型的继承

  • 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
  • 子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
  • String是Object的子类,但是List< String >并不是List< Object >的子类(※)
//第一种 : 不保留泛型并且没有指定类型,此时Person类中的T默认为Object类型   擦除
public class SubPerson extends Person { 
	……
}
//第二种 : 不保留泛型但指定了泛型的类型,此时Person类中的T被指定为String类型
public class SubPerson extends Person<String> { 
	……
}
//第三种 : 保留父类的泛型  可以在构造对象时来指定T的类型
public class SubPerson<T> extends Person<T> { 
	……
}
//第四种 : 保留父类的泛型,同时在子类中增加新的泛型
public class SubPerson<T, T1> extends Person<T> { 
	……
}

2.5 泛型通配符(※)

  • 泛型通配符共有三种,分别是:
  • 无限制通配符:表示我们可以传入任意类型的参数。
  • 表示类型的上界是E,只能是E或者是E的子类。
  • 表示类型的下界是E,只能是E或者是E的父类。
/*
	自定义类:ClassA & ClassB(继承自ClassA)
*/
public class Test {
    public static void main(String[] args) {
        List<ClassA> list1 = new LinkedList<>();
        List<ClassB> list2 = new LinkedList<>();

        System.out.println("---------------------------------------------");
        // 使用通配符作为泛型类型的公共父类
        List<?> list3 = new LinkedList<>();
        list3 = list1; // 可以发生List类型到List类型的转换
        list3 = list2; // 可以发生List类型到List类型的转换

        // 向公共父类中添加元素和获取元素
        //lt3.add(new Animal()); Error: 不能存放ClassA类型的对象
        //lt3.add(new Dog());    Error: 不能存放ClassB类型的对象,不支持元素的添加操作,因为不知道到底想放什么类型

        Object o = list3.get(0); //支持获取,全部当作Object类型

        System.out.println("---------------------------------------------");
        // 3.使用有限制的通配符进行使用
        List<? extends ClassA> list4 = new LinkedList<>();
        // 不支持元素的添加操作,因为如果list要的是一个ClassB类型,传一个ClassA进来,就“大”了,如果list要的是一个ClassB类的子类,传一个ClassB进来,也还是“大”了,
        //lt4.add(new ClassA());
        //lt4.add(new ClassB());
        //lt4.add(new Object());
        // 但是我们在获取元素时,要用ClassA类型去接收,多态呗
        ClassA classA = list4.get(0);

        System.out.println("---------------------------------------------");
        List<? super ClassA> list5 = new LinkedList<>();
        //? super ClassA 表示ClassA以及ClassA的父类,我们要添加ClassA以及ClassA的子类时,都可以转换成ClassA,但是我们不能用Object原因和上面两个是一样的
        list5.add(new ClassA());
        list5.add(new ClassB());

        Object object = list5.get(0);
    }
}

九、异常(※※)-- Java采用的异常处理机制是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护

1.基本知识

  • 异常的结构框架:
    JavaSE学习小结二_第4张图片
  • Error类主要用于描述Java虚拟机无法解决的严重错误,通常无法编码解决,如:JVM挂掉了
  • Exception类主要用于描述因编程错误或偶然外在因素导致的轻微错误,通常可以编码解决,如:0作为除数、空指针等等
  • 为避免异常,我们可以使用 if 判断来避免异常的发生,但也需要注意,如果 if 语句过多会导致代码的可读性差,冗长

2. 异常的捕获

  • 语法格式:
try {
 	编写可能发生异常的代码; }
catch(异常类型 引用变量名) {
 	编写针对该类异常的处理代码;
}
...
finally {
 	编写无论是否发生异常都要执行的代码;
}
  • 注意事项
    a. 当需要编写多个catch分支时,切记小类型应该放在大类型的前面;
    b. 懒人的写法: catch(Exception e) {}
    c. finally通常用于进行善后处理,如:关闭已经打开的文件等

3. 异常的抛出

  • 在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移给该方法的调用者,
    这种方法就叫异常的抛出。当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代
    码后续的代码就不再执行。
  • 语法格式
    访问权限 返回值类型 方法名称(形参列表) throws 异常类型1,异常类型2,…{ 方法体; }
    public void show() throws IOException{}
  • 方法重写的原则
    a. 要求方法名相同、参数列表相同以及返回值类型相同,从jdk1.5开始支持返回子类类型;
    b. 要求方法的访问权限不能变小,可以相同或者变大;
    c. 要求方法不能抛出更大的异常;
  • 注意:
    a. 子类重写的方法不能抛出更大的异常、不能抛出平级不一样的异常,但可以抛出一样的异常、更小的异常以及不抛出异常。
    b. 若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理
    c. 若一个方法内部又以递进方式分别调用了好几个其它方法,则建议这些方法内可以使用抛出的方法处理到最后一层进行捕获方式处理。

4. 异常类的示例:

编写一个员工类 —> 属性: 姓名,年龄
如果年龄小于18龄大于150抛出年龄异常
如果姓名为null或者""抛出空异常

public class AgeException extends Exception {
    public AgeException() {
    }

    public AgeException(String message) {
        super(message);
    }
}
public class NullException extends Exception{
    public NullException() {
    }

    public NullException(String message) {
        super(message);
    }
}
public class Worker {
    private String name;
    private int age;

    public Worker() {
    }

    public Worker(String name, int age) throws AgeException, NullException {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) throws NullException{
        if(!"".equals(name) &&  name != null){
            this.name = name;
        } else {
            throw new NullException("姓名不可为空(null 或 '')");
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws AgeException {
        if(age >= 18 && age <= 150){
            this.age = age;
        } else {
            throw new AgeException("年龄超出合法范围");
        }
    }

    @Override
    public String toString() {
        return "Worker{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class WorkerTest {

    public static void main(String[] args) {
        try {
            Worker worker = new Worker(null, 34);
            String name = worker.getName();
            System.out.println(name);
        } catch (AgeException e) {
            e.printStackTrace();
        } catch (NullException e) {
            e.printStackTrace();
        }
    }
}

十、文件File类(※)— 本质上还是一个工具类

方法声明 功能介绍
File(String pathname) 根据参数指定的路径名来构造对象
File(String parent, String child) 根据参数指定的父路径和子路径信息构造对象
File(File parent, String child) 根据参数指定的父抽象路径和子路径信息构造对象
boolean exists() 测试此抽象路径名表示的文件或目录是否存在
String getName() 用于获取文件的名称
long length() 返回由此抽象路径名表示的文件的长度
long lastModified() 用于获取文件的最后一次修改时间
String getAbsolutePath() 用于获取绝对路径信息
boolean delete() 用于删除文件,当删除目录时要求是空目录
boolean createNewFile() 用于创建新的空文件
boolean mkdir() 用于创建目录
boolean mkdirs() 用于创建多级目录
File[] listFiles() 获取该目录下的所有内容
boolean isFile() 判断是否为文件
boolean isDirectory() 判断是否为目录
File[] listFiles(FileFilter filter) 获取目录下满足筛选器的所有内容
/*
	如果我们想查看一个目录下的所有文件(包括所有子文件)的话,我们可以使用递归的思想
*/
	public static void show(File file) {
        // 获取目录下的所有内容并记录到一维数组中
        File[] filesArray = file.listFiles();
        // 遍历数组
        for (File tf: filesArray) {
            String name = tf.getName();
            // 判断是否为文件,若是则直接打印文件名称
            if (tf.isFile()) {
                System.out.println(name);
            }
            // 若是目录,则使用[]将目录名称括起来,然后再调用show方法
            if (tf.isDirectory()) {
                System.out.println("[" + name + "]");
                show(tf);
            }
        }
    }
	//实现目录中所有内容获取的同时进行过滤
        // 匿名内部类的语法格式:接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
        /*FileFilter fileFilter = new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                // 若文件名是以.avi为结尾,则返回true表示保留   否则返回false就是表示丢弃
                return pathname.getName().endsWith(".avi");
            }
        };*/
        // Lambda表达式的格式:(参数列表) -> {方法体}
        FileFilter fileFilter = (File pathname) -> {return pathname.getName().endsWith(".avi");};
        File[] filesArray2 = f3.listFiles(fileFilter);
        for (File tf : filesArray2) {
            System.out.println(tf);
        }

十一、IO流

用一张图来理解输入输出:
JavaSE学习小结二_第5张图片
输入流主要指从文件中读取数据内容输入到程序中,也就是读文件 ------ Input
输出流主要指将程序中的数据内容输出到文件中,也就是写文件 ------ Output

1.体系结构 & 框架

  • 字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件
  • 字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件
  • 节点流主要指直接和输入输出源对接的流。
  • 处理流主要指需要建立在节点流的基础之上的流
    JavaSE学习小结二_第6张图片
    JavaSE学习小结二_第7张图片

2.FileWriter & FileReader — 主要用于对文本文件的读写

2.1FileWriter 的常用方法:

方法声明 功能介绍
FileWriter(String fileName) 根据参数指定的文件名构造对象
FileWriter(String fileName, boolean append) 以追加的方式根据参数指定的文件名来构造对象
void write(int c) 写入单个字符
void write(char[] cbuf, int off, int len) 将指定字符数组中从偏移量off开始的len个字符写入此文件输出流
void write(char[] cbuf) 将cbuf.length个字符从指定字符数组写入此文件输出流中
void flush() 刷新流
void close() 关闭流对象并释放有关的资源

2.2 FileReader 的常用方法:

方法声明 功能介绍
FileReader(String fileName) 根据参数指定的文件名构造对象
int read() 读取单个字符的数据并返回,返回-1表示读取到末尾
int read(char[] cbuf, int offset, int length) 从输入流中将最多len个字符的数据读入一个字符数组中,返回读取到的字符个数,返回-1表示读取到末尾
int read(char[] cbuf) 从此输入流中将最多 cbuf.length 个字符的数据读入字符数组中,返回读取到的字符个数,返回-1表示读取到末尾
void close() 关闭流对象并释放有关的资源

2.3代码示例

完成对一个文本文件的读写操作

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

        //1.创建文件输出流 --- 2.根据需要调用所需的write()方法 --- 3.flush()刷新 --- 4.close()关闭流

        FileWriter fw = null;
        try {
            fw = new FileWriter("d:/a.txt");
            char[] a = {'a', 'b', 'c', 'd', 'e', 'f'};
//            fw.write(a, 2, 4);
            fw.write(a);
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fw != null)
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}
public class FileReaderTest {
    public static void main(String[] args) {

        //1.创建文件输入流 --- 2.根据需要调用所需的read()方法 --- 3.flush()刷新 --- 4.close()关闭流

        FileReader fr = null;
        try {
            fr = new FileReader("d:/a.txt");

            //读出所有字符(存入数组)
            char[] ans = new char[5];
            int len = fr.read(ans);
            System.out.println(len);
            for (char ch : ans) {
                System.out.println(ch);
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

3. FileOutputStream & FileInputStream – 主要用于对图像数据之类的原始字节流的读写

3.1 FileOutputStream 的常用方法

方法声明 功能介绍
FileOutputStream(String name) 根据参数指定的文件名来构造对象
FileOutputStream(String name,boolean append) 以追加的方式根据参数指定的文件名来构造对象
void write(int b) 将指定字节写入此文件输出流
void write(byte[] b, int off, int len) 将指定字节数组中从偏移量off开始的len个字节写入此文件输出流
void write(byte[] b) 将 b.length 个字节从指定字节数组写入此文件输出流中
void flush() 刷新此输出流并强制写出任何缓冲的输出字节
void close() 关闭流对象并释放有关的资源

3.2 FileInputStream 的常用方法

方法声明 功能介绍
FileInputStream(String name) 根据参数指定的文件路径名来构造对象
int read() 从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾
int read(byte[] b, int off, int len) 从此输入流中将最多len个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾
int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾
void close() 关闭流对象并释放有关的资源
int available() 获取输入流所关联文件的大小

3.3 代码示例 — 拷贝一张图片

public class FileByteCopyTest {
    public static void main(String[] args) {
        //字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件

        //1.创建输入输出流 --- 2.先将数据读出并存入缓冲数组,再将数组内内容写入文件 --- 3.close()

        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream("d:/webserver.png");
            fos = new FileOutputStream("d:/newwebserver.png");
            // 方式一:以单个字节为单位进行拷贝,也就是每次读取一个字节后再写入一个字节
            // 缺点:文件稍大时,拷贝的效率很低
            /*int res = 0;
            while ((res = fis.read()) != -1) {
                fos.write(res);
            }*/
            // 方式二:准备一个和文件大小一样的缓冲区,一次性将文件中的所有内容取出到缓冲区然后一次性写入进去
            // 缺点:若文件过大时,无法申请和文件大小一样的缓冲区,真实物理内存不足
            /*int len = fis.available();
            System.out.println("获取到的文件大小是:" + len);
            byte[] bArr = new byte[len];
            int res = fis.read(bArr);
            System.out.println("实际读取到的文件大小是:" + res);
            fos.write(bArr);*/
            // 方式三:准备一个相对适当的缓冲区,分多次将文件拷贝完成
            byte[] buffer = new byte[1024];
            int res = 0;
            while((res = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, res);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null)
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            if(fos != null)
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }


    }
}

4.缓冲流 之 BufferedOutputStream & BufferedInputStream

4.1 BufferedOutputStream 的常用方法

方法声明 功能介绍
BufferedOutputStream(OutputStream out) 根据参数指定的引用来构造对象
BufferedOutputStream(OutputStream out, int size) 根据参数指定的引用和缓冲区大小来构造对象
void write(int b) 写入单个字节
void write(byte[] b, int off, int len) 写入字节数组中的一部分数据
void write(byte[] b) 写入参数指定的整个字节数组
void flush() 刷新流
void close() 关闭流对象并释放有关的资源

4.2 BufferedInputStream的常用方法

方法声明 功能介绍
BufferedInputStream(InputStream in) 根据参数指定的引用构造对象
BufferedInputStream(InputStream in, int size) 根据参数指定的引用和缓冲区大小构造对象
int read() 读取单个字节
int read(byte[] b, int off, int len) 读取len个字节
int read(byte[] b) 读取b.length个字节
void close() 关闭流对象并释放有关的资源

4.3 代码示例 — 拷贝视频

public class BufferedByteCopyTest {
    public static void main(String[] args) {
        //缓冲流是建立在基本输入输出流的基础之上,内部默认提供一个缓冲区 [8192]
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            bis = new BufferedInputStream(new FileInputStream("d:/Test/FateZero01.mp4")); //关联要复制的
            bos = new BufferedOutputStream(new FileOutputStream("d:/Test/copy01.mp4")); //关联复制到的

            byte[] buffer = new byte[1024];
            int res = 0;
            while((res = bis.read(buffer)) != -1){
                bos.write(buffer, 0, res);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bis != null)
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            if(bos != null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

    }
}

4.4BufferedXXXStream 源码解析

以 BufferedOutputStream 为例,我们看下面的源码:
JavaSE学习小结二_第8张图片

  • 源码的含义很简单,BufferedOutputStream在wirte时,会先判断要写的数据长度是否大于自己的缓冲区,**大于缓冲区则直接刷新缓冲区和数据,小于缓冲区则往缓冲区装。**直到装不下了再flush缓冲区。当然,最后的Write还是调用FileOutputStream的write方法来实现,只不过用了缓冲区后减少了IO次数。而正是因为减少了IO次数降低了访问磁盘的次数才使得效率得以提高

5.缓冲流 之 BufferedWriter& BufferedReader

5.1 BufferedWriter 常用方法

方法声明 功能介绍
BufferedWriter(Writer out) 根据参数指定的引用来构造对象
BufferedWriter(Writer out, int sz) 根据参数指定的引用和缓冲区大小来构造对象
void write(int c) 写入单个字符到输出流中
void write(char[] cbuf, int off, int len) 将字符数组cbuf中从下标off开始的len个字符写入输出流中
void write(char[] cbuf) 将字符串数组cbuf中所有内容写入输出流中
void write(String s, int off, int len) 将参数s中下标从off开始的len个字符写入输出流中
void write(String str) 将参数指定的字符串内容写入输出流中
void newLine() 用于写入行分隔符到输出流中
void flush() 刷新流
void close() 关闭流对象并释放有关的资源

5.2 BufferedReader 常用方法

方法声明 功能介绍
BufferedReader(Reader in) 根据参数指定的引用来构造对象
BufferedReader(Reader in, int sz) 根据参数指定的引用和缓冲区大小来构造对象
int read() 从输入流读取单个字符,读取到末尾则返回-1,否则返回实际读取到的字符内容
int read(char[] cbuf, int off, int len) 从输入流中读取len个字符放入数组cbuf中下标从off开始的位置上,若读取到末尾则返回-1,否则返回实际读取到的字符个数
int read(char[] cbuf) 从输入流中读满整个数组cbuf
String readLine() 读取一行字符串并返回,返回null表示读取到末尾
void close() 关闭流对象并释放有关的资源
public class BufferedCharCopyTest {

    public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            // 1.创建BufferedReader类型的对象与d:/a.txt文件关联
            br = new BufferedReader(new FileReader("d:/a.txt"));
            // 2.创建BufferedWriter类型的对象与d:/b.txt文件关联
            bw = new BufferedWriter(new FileWriter("d:/b.txt"));
            // 3.不断地从输入流中读取一行字符串并写入到输出流中
            System.out.println("正在玩命地拷贝...");
            String str = null;
            while ((str = br.readLine()) != null) {
                bw.write(str);
                bw.newLine(); // 当前系统中的行分隔符是:\r\n
            }
            System.out.println("拷贝文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭流对象并释放有关的资源
            if (null != bw) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.PrintStream & PrintWriter

6.1 PrintStream 常用方法

方法声明 功能介绍
PrintStream(OutputStream out) 根据参数指定的引用来构造对象
void print(String s) 用于将参数指定的字符串内容打印出来
void println(String x) 用于打印字符串后并终止该行
void flush() 刷新流
void close() 用于关闭输出流并释放有关的资源

6.2 PrintWriter 常用方法

方法声明 功能介绍
PrintWriter(Writer out) 根据参数指定的引用来构造对象
void print(String s) 将参数指定的字符串内容打印出来
void println(String x) 打印字符串后并终止该行
void flush() 刷新流
void close() 关闭流对象并释放有关的资源
public class PrintStreamChatTest {
    public static void main(String[] args) {

        // 由手册可知:构造方法需要的是Reader类型的引用,但Reader类是个抽象类,实参只能传递子类的对象  字符流
        // 由手册可知: System.in代表键盘输入, 而且是InputStream类型的 字节流
        // 要进行字节流与字符流之间的转换--OutputStreamWriter类主要用于实现从字符流到字节流的转换
        //                            --InputStreamReader类主要用于实现从字节流到字符流的转换
        BufferedReader br = null;
        PrintStream ps = null;

        try {
        	//BufferedReader()构造方法要的是Reader而System.in是PrintStream下的,所以要进行转换
            br = new BufferedReader(new InputStreamReader(System.in));
            ps = new PrintStream(new FileOutputStream("d:/Test/d.txt"));

            boolean flag = true;

            while(true) {
                System.out.print((flag ? "Tom" : "Alice") + " :");
                String str = br.readLine();

                if(str.equals("bye")) {
                    System.out.println("聊天结束");
                    break;
                }
                Date date = new Date();
                SimpleDateFormat  sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                ps.println("[" + sdf.format(date) + "] " + (flag ? " Tom: " : " Alice:") + str);

                flag = !flag;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ps != null)
                try {
                    ps.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            if (br != null)
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }



    }
}

7. 转换流

7.1 OutputStreamWriter

  • 主要用于实现从字符流到字节流的转换
方法声明 功能介绍
OutputStreamWriter(OutputStream out) 根据参数指定的引用来构造对象
OutputStreamWriter(OutputStream out, String charsetName) 根据参数指定的引用和编码构造对象
void write(String str) 将参数指定的字符串写入
void flush() 刷新流
void close() 用于关闭输出流并释放有关的资源

7.2 InputStreamReader

  • 主要用于实现从字节流到字符流的转换
方法声明 功能介绍
InputStreamReader(InputStream in) 根据参数指定的引用来构造对象
InputStreamReader(InputStream in, String charsetName) 根据参数指定的引用和编码来构造对象
int read(char[] cbuf) 读取字符数据到参数指定的数组
void close() 用于关闭输出流并释放有关的资源

8.ObjectOutputStream & ObjectInputStream

8.1 ObjectOutputStream

  • 基本概念
    java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中
    只能将支持 java.io.Serializable 接口的对象写入流中
    类通过实现 java.io.Serializable 接口以启用其序列化功能
    所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程。
  • 常用方法:
方法声明 功能介绍
ObjectOutputStream(OutputStream out) 根据参数指定的引用来构造对象
void writeObject(Object obj) 用于将参数指定的对象整体写入到输出流中
void close() 用于关闭输出流并释放有关的资源

8.2 ObjectInputStream

  • 基本概念
    java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来。
    所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。
  • 常用方法:
方法声明 功能介绍
ObjectInputStream(InputStream in) 根据参数指定的引用来构造对象
Object readObject() 主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾
void close() 用于关闭输入流并释放有关的资源

8.3 代码示例

//除了要实现Serializable接口还要指定序列化版本号
public class User implements Serializable {
    private static final long serialVersionUID = 8722581501774275558L;

    private String name;
    private String password;
    private String phoneNum;

    public User() {
    }

    public User(String name, String password, String phoneNum) {
        this.name = name;
        this.password = password;
        this.phoneNum = phoneNum;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", phoneNum='" + phoneNum + '\'' +
                '}';
    }
}
public class ObjectOutputStreamTest {
    public static void main(String[] args) {
        //只能将支持 java.io.Serializable 接口的对象写入流中,因此在User类实现Serializabele接口之前,调用writeObject会报错
        ObjectOutputStream oos = null;

        try {
            oos = new ObjectOutputStream(new FileOutputStream("d:/Test/d.txt"));
            User user1 = new User("Tom", "123456", "15638948561");
            oos.writeObject(user1);
            System.out.println("写入对象成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(oos != null)
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }


    }
}
public class ObjectInputSteamTest {
    public static void main(String[] args) {
        ObjectInputStream ois = null;

        try {
            ois = new ObjectInputStream(new FileInputStream("d:/Test/d.txt"));
            Object o = ois.readObject(); //从输入流中一次性将对象整体读取出来
            System.out.println(o);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois != null)
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }


    }
}

8.4 其他知识点

  • 序列化版本号
    序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,
    JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如
    果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常
    (InvalidCastException)。
  • transient关键字
    transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行
    化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进
    去的。
  • 经验的分享
    当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一
    个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,
    从而避免了通过返回值进行是否达到文件末尾的判断。

十二、多线程

1. 线程的创建(三种方式)

  • 自定义类继承Thread类并重写run方法,然后创建该类的对象调用start方法。

  • 自定义类实现Runnable接口并重写run方法,创建该类的对象作为实参来构造Thread类型的对
    象,然后使用Thread类型的对象调用start方法。

  • 实现java.util.concurrent.Callable接口 — 重写 V call() 方法,这个方法的返回值是泛型(代码示例参考线程池部分)

  • 相关方法

方法声明 功能介绍
Thread() 使用无参的方式构造对象
Thread(String name) 根据参数指定的名称来构造对象
Thread(Runnable target) 根据参数指定的引用来构造对象,其中Runnable是个接口类型
Thread(Runnable target, String name) 根据参数指定引用和名称来构造对象
void run() 若使用 Runnable 引用构造了线程对象,调用该方法时最终调用接口中的版本 若没有使用 Runnable 引用构造线程对象,调用该方法时则啥也不做
void start() 用于启动线程,Java虚拟机会自动调用该线程的run方法
  • 获取线程相关信息的方法
方法声明 功能介绍
long getId() 获取调用对象所表示线程的编号
String getName() 获取调用对象所表示线程的名称
void setName(String name) 设置/修改线程的名称为参数指定的数值
static Thread currentThread() 获取当前正在执行线程的引用

2. 线程同步机制(※)

  • 简单说就是OS中提及的线程同步问题,生产者-消费者问题、哲学家进餐问题……
  • 上锁,可以用synchronized关键字 或者 Lock锁

2.1 synchronized关键字

  • 多个需要同步的线程在访问同步块时,看到的应该是同一个锁对象引用
  • 使用同步代码块的方式实现部分代码的锁定,格式如下:
 synchronized(类类型的引用) {
 	编写所有需要锁定的代码;
 } 
  • 使用同步方法的方式实现所有代码的锁定。
    直接使用synchronized关键字来修饰整个方法即可,该方式等价于:
synchronized(this) { 整个方法体的代码 }

2.2 静态方法的锁定

  • 当我们对一个静态方法加锁,如: public synchronized static void xxx(){….} 那么该方法锁的对象是类对象。
  • 每个类都有唯一的一个类对象。获取类对象的方式:类名.class。
  • 静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的。
  • 原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象

2.4 线程安全类和不安全类

  • StringBuffer类是线程安全的类,但StringBuilder类不是线程安全的类。
  • Vector类和 Hashtable类是线程安全的类,但ArrayList类和HashMap类不是线程安全的类。
  • Collections.synchronizedList() 和 Collections.synchronizedMap()等方法实现安全。

2.5 使用Lock(锁)实现线程同步

  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
  • 该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁
  • 常用方法
方法声明 功能介绍
ReentrantLock() 使用无参方式构造对象
void lock() 获取锁
void unlock() 释放锁

2.6 与synchronized方式的比较

  • Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动释放。
  • Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁。
  • 使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好。
public class AccountRunnableTest implements Runnable {
    private int balance; // 用于描述账户的余额
    private Demo dm = new Demo();
    private ReentrantLock lock = new ReentrantLock();  // 准备了一把锁

    public AccountRunnableTest() {
    }

    public AccountRunnableTest(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    @Override
    public /*synchronized*/ void run() {
        // 开始加锁
        lock.lock();

        // 由源码可知:最终是account对象来调用run方法,因此当前正在调用的对象就是account,也就是说this就是account
        //synchronized (this) { // ok
        System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
        //synchronized (dm) { // ok
        //synchronized (new Demo()) { // 锁不住  要求必须是同一个对象
        // 1.模拟从后台查询账户余额的过程
        int temp = getBalance(); // temp = 1000  temp = 1000
        // 2.模拟取款200元的过程
        if (temp >= 200) {
            System.out.println("正在出钞,请稍后...");
            temp -= 200;  // temp = 800   temp = 800
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("请取走您的钞票!");
        } else {
            System.out.println("余额不足,请核对您的账户余额!");
        }
        // 3.模拟将最新的账户余额写入到后台
        setBalance(temp); // balance = 800  balance = 800
        //}
        lock.unlock(); // 实现解锁
    }

    public static void main(String[] args) {

        AccountRunnableTest account = new AccountRunnableTest(1000);
        //AccountRunnableTest account2 = new AccountRunnableTest(1000);
        Thread t1 = new Thread(account);
        Thread t2 = new Thread(account);
        //Thread t2 = new Thread(account2);
        t1.start();
        t2.start();

        System.out.println("主线程开始等待...");
        try {
            t1.join();
            //t2.start(); // 也就是等待线程一取款操作结束后再启动线程二
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("最终的账户余额为:" + account.getBalance()); // 600  800
    }
}

class Demo{}

3.Object类常用的方法

方法声明 功能介绍
void wait() 用于使得线程进入等待状态,直到其它线程调用notify()或notifyAll()方法
void wait(long timeout) 用于进入等待状态,直到其它线程调用方法或参数指定的毫秒数已经过去为止
void notify() 用于唤醒等待的单个线程
void notifyAll() 用于唤醒等待的所有线程

4. 线程池

  • 从Java5开始提供了线程池的相关类和接口:java.util.concurrent.Executors类java.util.concurrent.ExecutorService接口

4.1 Executors类

  • Executors是个工具类和线程池的工厂类,可以创建并返回不同类型的线程池,常用方法如下:
方法声明 功能介绍
static ExecutorService newCachedThreadPool() 创建一个可根据需要创建新线程的线程池
static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
static ExecutorService newSingleThreadExecutor() 创建一个只有一个线程的线程池

4.2 ExecutorService接口

  • ExecutorService接口是真正的线程池接口,主要实现类是ThreadPoolExecutor,常用方法如下:
方法声明 功能介绍
void execute(Runnable command) 执行任务和命令,通常用于执行Runnable
Future submit(Callable task) 执行任务和命令,通常用于执行Callable
void shutdown() 启动有序关闭

4.3 线程池的使用 — 这里顺便说一下线程的第三种创建方法

  • FutureTask类
    java.util.concurrent.FutureTask类用于描述可取消的异步计算,该类提供了Future接口的基本实
    现,包括启动和取消计算、查询计算是否完成以及检索计算结果的方法,也可以用于获取方法调用后的返回结果
  • FutureTask类的常用方法如下:
方法声明 功能介绍
FutureTask(Callable callable) 根据参数指定的引用来创建一个未来任务
V get() 获取call方法计算的结果
public class ThreadCallableTest implements Callable {

        @Override
        public Object call() throws Exception {
            // 计算1 ~ 10000之间的累加和并打印返回
            int sum = 0;
            for (int i = 1; i <= 10000; i++) {
                sum +=i;
            }
            System.out.println("计算的累加和是:" + sum); // 50005000
            return sum;
        }

    public static void main(String[] args) {

        ThreadCallableTest tct = new ThreadCallableTest();
        FutureTask ft = new FutureTask(tct);
        Thread t1 = new Thread(ft);
        t1.start();
        Object obj = null;
        try {
            obj = ft.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("线程处理方法的返回值是:" + obj); // 50005000
    }
}
public class ThreadPoolTest {

    public static void main(String[] args) {

        // 1.创建一个线程池 newFixedThreadPool()返回的是ThreadPoolExecutor
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 2.向线程池中布置任务
        executorService.submit(new ThreadCallableTest());
        // 3.关闭线程池
        executorService.shutdown();
    }
}

大致的一个思路就是  Executors类创建线程池 --- ExecutorService的实现类去运行

十三、网络编程

个人的理解就是 提供端口号与主机号,用于建立连接,之后就是一个大型的IO,具体的在JavaWeb中说,这里简单的说一点基于TCP的基础知识

1. 编程模型

  • 服务器:
    (1)创建ServerSocket类型的对象并提供端口号;
    (2)等待客户端的连接请求,调用accept()方法;
    (3)使用输入输出流进行通信;
    (4)关闭Socket;
  • 客户端:
    (1)创建Socket类型的对象并提供服务器的IP地址和端口号;
    (2)使用输入输出流进行通信;
    (3)关闭Socket;

2. ServerSocket & Socket

  • ServerSocket常用方法:
方法声明 功能介绍
ServerSocket(int port) 根据参数指定的端口号来构造对象
Socket accept() 侦听并接收到此套接字的连接请求
void close() 用于关闭套接字
  • Socket常用方法:
方法声明 功能介绍
Socket(String host, int port) 根据指定主机名和端口来构造对象
InputStream getInputStream() 用于获取当前套接字的输入流
OutputStream getOutputStream() 用于获取当前套接字的输出流
void close() 用于关闭套接字
public class ServerThread extends Thread {
    private Socket s;

    public ServerThread(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        PrintStream ps = null;

        try {
            // 3.使用输入输出流进行通信
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            ps = new PrintStream(s.getOutputStream());

            while(true) {
                // 实现对客户端发来字符串内容的接收并打印
                // 当没有数据发来时,下面的方法会形成阻塞
                String s1 = br.readLine();
                InetAddress inetAddress = s.getInetAddress();
                System.out.println("客户端" + inetAddress + "发来的字符串内容是:" + s1);
                // 当客户端发来的内容为"bye"时,则聊天结束
                if ("bye".equalsIgnoreCase(s1)) {
                    System.out.println("客户端" + inetAddress + "已下线!");
                    break;
                }
                // 实现服务器向客户端回发字符串内容"I received!"
                ps.println("I received!");
                System.out.println("服务器发送数据成功!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != ps) {
                ps.close();
            }
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != s) {
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
public class ServerStringTest {

    public static void main(String[] args) {
        ServerSocket ss = null;
        Socket s = null;

        try {
            // 1.创建ServerSocket类型的对象并提供端口号
            ss = new ServerSocket(8888);

            // 2.等待客户端的连接请求,调用accept方法
            while(true) {
                System.out.println("等待客户端的连接请求...");
                // 当没有客户端连接时,则服务器阻塞在accept方法的调用这里
                s = ss.accept();
                System.out.println("客户端" + s.getInetAddress() + "连接成功!");
                // 每当有一个客户端连接成功,则需要启动一个新的线程为之服务
                new ServerThread(s).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭Socket并释放有关的资源
            if (null != ss) {
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class ClientStringTest {

    public static void main(String[] args) {
        Socket s = null;
        PrintStream ps = null;
        Scanner sc = null;
        BufferedReader br = null;

        try {
            // 1.创建Socket类型的对象并提供服务器的主机名和端口号
            s = new Socket("127.0.0.1", 8888);
            System.out.println("连接服务器成功!");

            // 2.使用输入输出流进行通信
            sc = new Scanner(System.in);
            ps = new PrintStream(s.getOutputStream());
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));

            while(true) {
                //Thread.sleep(10000);
                // 实现客户端发送的内容由用户从键盘输入
                System.out.println("请输入要发送的数据内容:");
                String str1 = sc.next();
                // 实现客户端向服务器发送字符串内容"hello"
                //ps.println("hello");
                ps.println(str1);
                System.out.println("客户端发送数据内容成功!");
                // 当发送的数据内容为"bye"时,则聊天结束
                if ("bye".equalsIgnoreCase(str1)) {
                        System.out.println("聊天结束!");
                        break;
                }
                // 实现接收服务器发来的字符串内容并打印
                String str2 = br.readLine();
                System.out.println("服务器回发的消息是:" + str2);
            }

        } catch (IOException /*| InterruptedException*/ e) {
            e.printStackTrace();
        } finally {
            // 3.关闭Socket并释放有关的资源
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != ps) {
                ps.close();
            }
            if (null != sc) {
                sc.close();
            }
            if (null != s) {
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

十四、反射

1. 基本概念

  • 通常情况下编写代码都是固定的,无论运行多少次执行的结果也是固定的,在某些特殊场合中编写代码时不确定要创建什么类型的对象,也不确定要调用什么样的方法,这些都希望通过运行时传递的参数来决定,该机制叫做动态编程技术,也就是反射机制。
  • 通俗来说,反射机制就是用于动态创建对象并且动态调用方法的机制
  • 目前主流的框架底层都是采用反射机制实现的。

2. Class类

  • 获取Class对象的方式:
    1.使用数据类型.class的方式可以获取对应类型的Class对象。 ※
// 使用数据类型.class的方式可以获取对应类型的Class对象
        Class c1 = String.class;
        System.out.println("c1 = " + c1); // 自动调用toString方法  class java.lang.String
        c1 = int.class;
        System.out.println("c1 = " + c1); // int
        c1 = void.class;
        System.out.println("c1 = " + c1); // void

2.使用引用/对象.getClass()的方式可以获取对应类型的Class对象。
3.使用包装类.TYPE的方式可以获取对应基本数据类型的Class对象。
4.使用Class.forName()的方式来获取参数指定类型的Class对象。 ※

// 调用Class类中的forName方法来获取对应的Class对象
        //c1 = Class.forName("String"); // Error  要求写完整的名称:包名.类名,并且不能获取基本数据类型的Class对象
        c1 = Class.forName("java.lang.String");
        System.out.println("c1 = " + c1); // class java.lang.String

        c1 = Class.forName("java.util.Date");
        System.out.println("c1 = " + c1); // class java.util.Date

5.使用类加载器ClassLoader的方式获取指定类型的Class对象。

3. Constructor类

  • 用于描述获取到的构造方法信息
  • 我们通过调用Class类的方法,获取Constructor类
方法声明 功能介绍
Constructor getConstructor(Class… parameterTypes) 用于获取此Class对象所表示类型中参数指定的公共构造方法
Constructor[] getConstructors() 用于获取此Class对象所表示类型中所有的公共构造方法
  • Constructor类的常用方法
方法声明 功能介绍
T newInstance(Object… initargs) 使用此Constructor对象描述的构造方法来构造Class对象代表类型的新实例
int getModifiers() 获取方法的访问修饰符
String getName() 获取方法的名称
Class[] getParameterTypes() 获取方法所有参数的类型
  • 现在有一个类Person,如下所示,我们可以使用反射机制完成类的创建
public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

  • 先调用无参方式:
public class PersonConstructorTest {

    public static void main(String[] args) throws Exception {

        // 1.使用原始方式以无参形式构造Person类型的对象并打印
        Person p1 = new Person();
        System.out.println("无参方式创建的对象是:" + p1); // null 0

        System.out.println("---------------------------------------------------");
        // 2.使用反射机制以无参形式构造Person类型的对象并打印
        // 创建对象的类型可以从键盘输入
        System.out.println("请输入要创建对象的类型:"); //deom05.Person
        Scanner sc = new Scanner(System.in);
        String str1 = sc.next();
        Class c1 = Class.forName(str1);
   
        // 获取Class对象对应类中的无参构造方法,也就是Person类中的无参构造方法
        Constructor constructor = c1.getConstructor();
        // 使用获取到的无参构造方法来构造对应类型的对象,也就是Person类型的对象
        System.out.println("无参方式创建的对象是:" + constructor.newInstance());
        
        sc.close();
        
        System.out.println("---------------------------------------------------");
        // 创建对象的类型可以从配置文件中读取
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/a.txt")));
        String str1 = br.readLine();
        Class c1 = Class.forName(str1);
        Constructor constructor = c1.getConstructor();
        System.out.println("无参方式创建的对象是:" + constructor.newInstance());
        br.close();
     }
}
  • 再是有参构造(和无参构造大同小异):
public class PersonConstructorTest {

    public static void main(String[] args) throws Exception {
        // 1.使用原始方式以有参方式构造Person类型的对象并打印
        Person p2 = new Person("zhangfei", 30);
        System.out.println("有参方式构造的对象是:" + p2); // zhangfei 30

        System.out.println("---------------------------------------------------");
        // 2.使用反射机制以有参方式构造Person类型的对象并打印
        // 获取Class对象对应类中的有参构造方法,也就是Person类中的有参构造方法
        Constructor constructor1 = c1.getConstructor(String.class, int.class);
        // 使用获取到的有参构造方法来构造对应类型的对象,也就是Person类型的对象
        // newInstance方法中的实参是用于给有参构造方法的形参进行初始化的,也就是给name和age进行初始化的
        System.out.println("有参方式构造的对象是:" + constructor1.newInstance("zhangfei", 30)); // zhangfei 30
    }
}

4. Field类

  • 主要用于描述获取到的单个成员变量信息
  • 我们通过调用Class类的方法,获取Field类。意思就是获取到的Field类对象,就代表着一个变量,之后再调用 set() 或者 get() 方法都是对这个变量进行操作了。
方法声明 功能介绍
Field getDeclaredField(String name) 用于获取此Class对象所表示类中参数指定的单个成员变量信息
Field[] getDeclaredFields() 用于获取此Class对象所表示类中所有成员变量信息
  • Fieldr类的常用方法
方法声明 功能介绍
Object get(Object obj) 获取参数对象obj中此Field对象所表示成员变量的数值
void set(Object obj, Object value) 将参数对象obj中此Field对象表示成员变量的数值修改为参数 value的数值
void setAccessible(boolean flag) 当实参传递true时,则反射对象在使用时应该取消 Java 语言访问检查
int getModifiers() 获取成员变量的访问修饰符
Class getType() 获取成员变量的数据类型
String getName() 获取成员变量的名称
public class PersonFieldTest {

    public static void main(String[] args) throws Exception {

        // 1.使用原始方式来构造对象以及获取成员变量的数值并打印
        Person p1 = new Person("zhangfei", 30);
        //System.out.println("获取到的成员变量数值为:" + p1.name); // zhangfei

        System.out.println("-------------------------------------------------------");
        // 2.使用反射机制来构造对象以及获取成员变量的数值并打印
        // 2.1 获取Class对象
        Class c1 = Class.forName("com.lagou.task20.Person");
        // 2.2 根据Class对象获取对应的有参构造方法
        Constructor constructor = c1.getConstructor(String.class, int.class);
        // 2.3 使用有参构造方法来得到Person类型的对象
        Object object = constructor.newInstance("zhangfei", 30);
        // 2.4 根据Class对象获取对应的成员变量信息
        Field field = c1.getDeclaredField("name");
        // 设置Java语言访问检查的取消  暴力反射
        field.setAccessible(true);
        // 2.5 使用Person类型的对象来获取成员变量的数值并打印
        // 获取对象object中名字为field成员变量的数值,也就是成员变量name的数值
        System.out.println("获取到的成员变量数值为:" + field.get(object)); // zhangfei

        System.out.println("-------------------------------------------------------");
        // 3.使用原始方式修改指定对象中成员变量的数值后再次打印
        //p1.name = "guanyu";
        //System.out.println("修改后成员变量的数值为:" + p1.name); // guanyu

        System.out.println("-------------------------------------------------------");
        // 4.使用反射机制修改指定对象中成员变量的数值后再次打印
        // 表示修改对象object中名字为field成员变量的数值为guanyu,也就是成员变量name的数值为guanyu
        field.set(object, "guanyu");
        System.out.println("修改后成员变量的数值为:" + field.get(object)); // guanyu

        System.out.println("-------------------------------------------------------");
        // 5.获取Class对象对应类中所有的成员变量
        Field[] declaredFields = c1.getDeclaredFields();
        for (Field ft : declaredFields) {
            System.out.println("获取到的访问修饰符为:" + ft.getModifiers());
            System.out.println("获取到的数据类型为:" + ft.getType());
            System.out.println("获取到的成员变量名称是:" + ft.getName());
            System.out.println("---------------------------------------------");
        }
    }
}

5.Method类

  • 用于描述获取到的单个成员方法信息
  • 我们通过调用Class类的方法,获取Method类。
方法声明 功能介绍
Method getMethod(String name, Class… parameterTypes) 用于获取该Class对象表示类中名字为name参数为parameterTypes的指定公共成员方法
Method[] getMethods() 用于获取该Class对象表示类中所有公共成员方法
  • Method类的常用方法
方法声明 功能介绍
Object invoke(Object obj,Object… args) 使用对象obj来调用此Method对象所表示的成员方法,实参传递args
int getModifiers() 获取方法的访问修饰符
Class getReturnType() 获取方法的返回值类型
String getName() 获取方法的名称
Class[] getParameterTypes() 获取方法所有参数的类型
Class[] getExceptionTypes() 获取方法的异常信息
public class PersonMethodTest {

    public static void main(String[] args) throws Exception {

        // 1.使用原始方式构造对象并调用方法打印结果
        Person p1 = new Person("zhangfei", 30);
        System.out.println("调用方法的返回值是:" + p1.getName()); // zhangfei

        System.out.println("------------------------------------------------------");
        // 2.使用反射机制构造对象并调用方法打印结果
        // 2.1 获取Class对象
        Class c1 = Class.forName("com.lagou.task20.Person");
        // 2.2 根据Class对象来获取对应的有参构造方法
        Constructor constructor = c1.getConstructor(String.class, int.class);
        // 2.3 使用有参构造方法构造对象并记录
        Object object = constructor.newInstance("zhangfei", 30);
        // 2.4 根据Class对象来获取对应的成员方法
        Method method = c1.getMethod("getName");
        // 2.5 使用对象调用成员方法进行打印
        // 表示使用object对象调用method表示的方法,也就是调用getName方法来获取姓名
        System.out.println("调用方法的返回值是:" + method.invoke(object)); // zhangfei

        System.out.println("------------------------------------------------------");
        // 3.使用反射机制来获取类中的所有成员方法并打印
        Method[] methods = c1.getMethods();
        for (Method mt : methods) {
            System.out.println("成员方法的修饰符是:" + mt.getModifiers());
            System.out.println("成员方法的返回值类型是:" + mt.getReturnType());
            System.out.println("成员方法的名称是:" + mt.getName());
            System.out.println("成员方法形参列表的类型是:");
            Class<?>[] parameterTypes = mt.getParameterTypes();
            for (Class ct : parameterTypes) {
                System.out.print(ct + " ");
            }
            System.out.println();
            System.out.println("成员方法的异常类型列表是:");
            Class<?>[] exceptionTypes = mt.getExceptionTypes();
            for (Class ct: exceptionTypes) {
                System.out.print(ct + " ");
            }
            System.out.println();
            System.out.println("---------------------------------------------------");
        }
    }
}

总结

基本都是工具类,写这个文章的目的是用的时候看一眼方法,方便一些,顺便复习一下

你可能感兴趣的:(java,开发语言)