jichen学习JavaSE笔记

190 阅读9分钟

一、常用类

1.1 Date类

1.1.1 java.util.date

Date类用于获取当前日期,具体用法如下面代码所示:
Date date = new Date();
System.out.println(date);
System.out.println(date.toString());
// 获取格林尼治时间
System.out.println(date.toGMTString());
// 获取本地时间
System.out.println(date.toLocaleString());
// 注意:这里是获取从 1900年到现在 之间的年份数
System.out.println(date.getYear());
// 获取月份,注意,月份是从 0开始的
System.out.println(date.getMonth());
// 获取从1970.1.1 00:00:00到现在的毫秒数
System.out.println(date.getTime());
// 获取从1970.1.1 00:00:00到现在的毫秒数
System.out.println(System.currentTimeMillis());
/**
 * 思考。date.getTime 和 System.currentTimeMillis()哪个更好?
 * 答案:System.currentTimeMillis() 更好
 * 原因:
 * 1. date.getTime需要去new一个Date对象,但是 System.currentTimeMillis() 不需要
 * 2. public static native long currentTimeMillis();
 * 这个 native说明这是一个本地方法,是Java通过调用其他的方法生成的
 * 3. 应用场景如下:记录一个算法的耗时
 */
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    System.out.println(i);
}
long end = System.currentTimeMillis();
System.out.println("这段算法的耗时是:" + (end - start));

1.1.2 java.sql.date

java.util.date 和 java.sql.date的区别:
java.util.date 返回的是年月日 时分秒
java.util.date 返回的是年月日
java.util.date 和 java.sql.date的联系:
java.sql.date extends java.util.date
java.util.date 和 java.sql.date的转换->代码如下:
// java.util.date -> java.sql.date
java.util.Date date1 = new java.util.Date();
java.sql.Date date2 = (java.sql.Date) date1;
// 这样代码会报错,Exception in thread "main" java.lang.ClassCastException: java.util.Date cannot be cast to java.sql.Date
//原因就是 date1并没有 指定父类指向的是 sql.Date
// 正确的写法应该是

// java.util.date -> java.sql.date
java.util.Date date = new java.util.Date();
java.util.Date date1 = new java.sql.Date(date.getTime());
java.sql.Date date2 = (java.sql.Date) date1;
// java.sql.date -> java.util.date
java.util.Date date1 = new java.sql.Date(15678778789L);;
System.out.println(date1);
// 字符串转为sql.Date
java.sql.Date date = Date.valueOf("2023-5-3");
System.out.println(date);

1.1.3 SimpleDateFormate

/**
引入:String -> java.util.Date
思路:String -> java.sql.Date -> java.util.Date
*/
java.sql.Date date=Date.valueOf("2023-5-3");
java.util.Date date2 = date;
/**
这样是可以转成功的,但是 Date.valueOf的参数变成 2023/5/3 就会报错:
java.lang.IllegalArgumentException
为了解决这样的日期格式问题,java中引入了SimpleDateFormate
*/
// 基本用法
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
    // String -> Date
    Date parse = df.parse("2023-5-3 13:23:45");
    System.out.println(parse);
} catch (ParseException e) {
    e.printStackTrace();
}
// Date -> String
String format = df.format(new Date());
System.out.println(format);
// 前言 报出的异常:java.lang.IllegalArgumentException
// 用此方案就可以解决,这也是SimpleDateFormate解决问题的场景
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
try {
    // String -> Date
    Date parse = df.parse("2023/5/3");
    System.out.println(parse);
} catch (ParseException e) {
    e.printStackTrace();
}

1.1.4 Calendar

Date中的很多方法都是过时的,Calendar有这些功能,具体用法为
// 实例化Calendar对象
// 方式一
Calendar cal = Calendar.getInstance();
// 方式二
Calendar cal2 = new GregorianCalendar();

// get方法
System.out.println(cal.get(Calendar.YEAR));
// 月份从0开始计算
System.out.println(cal.get(Calendar.MONTH));
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
System.out.println(cal.get(Calendar.WEEK_OF_MONTH));
// 默认以星期天作为一周的开始
System.out.println(cal.get(Calendar.DAY_OF_WEEK));
// 设置以星期一为一周的开始
cal.setFirstDayOfWeek(Calendar.MONDAY);

System.out.println(cal.getActualMaximum(Calendar.DATE)); // 获取本月最大的天数
System.out.println(cal.getActualMinimum(Calendar.DATE)); // 获取本月最小的天数

// set方法
cal.set(Calendar.YEAR,2019);
cal.set(Calendar.MONTH,11);
cal.set(Calendar.DATE,23);
System.out.println(cal);

/**
 * String -> Calendar
 * 思路:
 * 1. String -> java.sql.Date
 * 2. java.sql.Date -> Calendar
 */
java.sql.Date date = java.sql.Date.valueOf("2020-04-12");
cal.setTime(date);
System.out.println(cal);
/**
 * 案例:输入年月日,打印这个月的日历
 */
System.out.println("请输入类似 yyyy-MM-dd的日期:(打印本月日历)");
String inputWords = new Scanner(System.in).nextLine();
System.out.println("日\t一\t二\t三\t四\t五\t六");
// 1.字符串转date
Date date = Date.valueOf(inputWords);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// 获取一个月有多少天
int maxDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
// 获取输入日期记录的天数
int inputDay = cal.get(Calendar.DATE);
// 设置日期从1号开始
cal.set(Calendar.DATE,1);
// 获取当月第一天是周几
int firstDayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
int count = 0; // 计数器
// 前面先打印空格
for (int i = 0; i < firstDayOfWeek - 1; i++) {
    System.out.print("\t");
    count += 1;
}
// 遍历这个月的天数
for (int i = 1; i <= maxDay; i++) {
    if (i == inputDay){
        System.out.print(i+"*\t");
    }else {
        System.out.print(i+"\t");
    }
    count += 1;
    if (count % 7 == 0){
        System.out.println();
    }
}

1.1.5 jdk1.8之后新增的日期类

出现的原因

/**java
 * jdk1.0使用java.util.Date作为日期类 -> 第一代日期api
 * jdk1.1使用calendar作为日期类 -> 第二代日期api
 * jdk1.8又提供了新的日期类api
 * 为啥有了1.01.1的日期api,还出现了1.8的api:
 * 1. 可变性:像日期和时间这样的类应该是不可变的
 * 2. Date中的年份是从1900年开始,而月份是从0开始
 * 3. 格式化只对Date有用,对Calendar没用
 */

1.1.5.1 localDate/localTime/localDateTime 实例化方法

// 实例化方式1
// now方法,获取当前日期
LocalDate localDate = LocalDate.now(); // 2023-05-04
LocalTime localTime = LocalTime.now(); // 23:28:35.990 时:分:秒:毫秒
LocalDateTime localDateTime = LocalDateTime.now(); // 2023-05-04T23:28:35.990

// 实例化方法2
// of方法,可以指定日期
LocalDate localDate1 = LocalDate.of(2020, 9, 16);
LocalTime localTime1 = LocalTime.of(17, 5, 34);
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 9, 15, 4, 3, 35);

1.1.5.2 get和with方法

// get方法
// LocalDate和LocalTime都没有LocalDateTime使用的频率高
// 之后就以LocalDateTime演示为例
// 可以看出LocalDateTime获取的时间没有偏移差
System.out.println(localDateTime.getYear()); // 2023
System.out.println(localDateTime.getMonth()); // MAY
System.out.println(localDateTime.getMonthValue()); // 5
System.out.println(localDateTime.getDayOfMonth()); // 4
System.out.println(localDateTime.getDayOfWeek()); // THURSDAY
System.out.println(localDateTime.getHour()); // 23
System.out.println(localDateTime.getMinute()); // 35
System.out.println(localDateTime.getSecond()); // 0

// with方法,由于localDateTime是不可变的,因此改变localDateTime的值,会生成一个新的对象
// 改变其他的日期值也是类似的
LocalDateTime newLocalDateTime = localDateTime.withYear(2022);
System.out.println(localDateTime); // 2023-05-04T23:39:27.624
System.out.println(newLocalDateTime); // 2022-05-04T23:39:27.624

1.1.5.3 localDateTime加减法

// 由于localDateTime是不可变的,因此对日期进行
// 加减法的时候,也会生成一个新的对象
// 日期的其他 月/日/时/分/秒的方法与下面的类比即可
LocalDateTime localDateTime = LocalDateTime.now(); // 2023-05-04T23:42:51.507
// 加法
LocalDateTime localDateTime1 = localDateTime.plusYears(2); // 2025-05-04T23:42:51.507
// 减法
LocalDateTime localDateTime2 = localDateTime.minusYears(1); // 2022-05-04T23:42:51.507

1.1.5.4 针对localDate/localTime/localDateTime格式化

1.1.5.4.1 预定义格式化

有三个预定义的值:

DateTimeFormatter.ISO_DATE --> 针对localDate的格式化

DateTimeFormatter.ISO_TIME --> 针对localTime的格式化

DateTimeFormatter.ISO_DATE_TIME --> 针对localDateTime的格式化

下面的案例以 DateTimeFormatter.ISO_DATE_TIME 为例

DateTimeFormatter df1 = DateTimeFormatter.ISO_DATE_TIME;  
LocalDateTime now = LocalDateTime.now();  
// LocalDateTime --> String  
String str = df1.format(now); // 2023-05-12T22:42:16.303  
// String --> LocalDateTime  
TemporalAccessor parse = df1.parse("2023-05-12T22:42:16.303"); // {},ISO resolved to 2023-05-12T22:42:16.303
1.1.5.4.2 本地化格式

如 ofLocalizedDateTime

参数:FormatStyle.LONG、FormatStyle.SHORT、FormatStyle.MEDIUM

DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);  
LocalDateTime now = LocalDateTime.now();  
// localDateTime --> String  
String format = df.format(now);  
// String --> localDateTime  
TemporalAccessor parse = df.parse("2023年5月17日 下午10时32分50秒");  
System.out.println(parse);

FormatStyle.LONG: 2023年5月17日 下午10时32分50秒

FormatStyle.MEDIUM: 2023-5-17 22:35:40

FormatStyle.SHORT: 23-5-17 下午10:36

1.1.5.4.3 自定义格式化(用的最多)

ofPattern()

LocalDateTime now = LocalDateTime.now();  
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");  
// localDatetime --> String  
String format = df.format(now); // 2023-05-17 10:40:07  
// String --> localDateTime  
TemporalAccessor parse = df.parse("2023-05-17 10:40:07");

1.2 Math类

常用方法和属性

// 常用属性  
System.out.println(Math.PI);  
// 常用方法  
System.out.println(Math.random()); // 随机数,取值范围为 [0,1)  
System.out.println(Math.abs(-100)); // 绝对值,100  
System.out.println(Math.ceil(3.1)); // 向上取值 4.0  
System.out.println(Math.floor(3.9)); // 向下取值 3.0  
System.out.println(Math.round(3.5)); // 四舍五入  
System.out.println(Math.max(1, 2)); // 取最大值  
System.out.println(Math.min(1, 2)); // 取最小值

静态导入 可以导入Math中的所有的静态属性和方法

import static java.lang.Math.*;  
  
public class Main {  
    public static void main(String[] args) {  
    // 常用属性  
    System.out.println(PI);  
    // 常用方法  
    System.out.println(random()); // 随机数,取值范围为 [0,1)  
    System.out.println(abs(-100)); // 绝对值,100  
    System.out.println(ceil(3.1)); // 向上取值 4.0  
    System.out.println(floor(3.9)); // 向下取值 3.0  
    System.out.println(round(3.5)); // 四舍五入  
    System.out.println(max(1, 2)); // 取最大值  
    System.out.println(min(1, 2)); // 取最小值  
    }  

    public static int random(){  
    return 100;  
    }  
}

注意此时的random函数走的是我们自己定义的这个

1.3 String类

1.3.1 String类的本质

String类是不可变的

String类是被final修饰的,不可以被继承

String底层是一个char类型的数组

1.3.2 构造器

String s1 = new String();  
String s2 = new String("abc");  
String s3 = new String(new char[]{'a', 'b', 'c'});

**补充**:数组定义的三种方式

int[] arr1 = new int[10];

int[] arr2 = new int[]{1,2};

int[] arr3 = {};

1.3.3 常用方法

方法名作用本质
length()返回字符串的长度return value.length()
isEmpty()返回字符串是否为空return value.length() == 0
charAt(int index)返回字符串索引为index的字符return value[index]
subString(int index)返回从index开始的截取的字符串
subString(int v1,int v2)返回下标在[v1,v2)范围截取的字符串
concat(String s)返回字符串和S拼接的结果"1".concat("2") // "12"
replace(char c1,char c2)把字符串中的c1改成c2
replace(CharSequence c1,CharSequence c2)把字符串中的c1改成c2String implements CharSequence
replaceFirst(String s1,String s2)把字符串的第一个s1替换成s2

1.3.4 equals源码

public boolean equals(Object anObject) {  
if (this == anObject) {  
    return true;  
}  
if (anObject instanceof String) {  
    String anotherString = (String)anObject;  
    int n = value.length;  
    if (n == anotherString.value.length) {  
        char v1[] = value;  
        char v2[] = anotherString.value;  
        int i = 0;  
        while (n-- != 0) {  
            if (v1[i] != v2[i])  
            return false;  
            i++;  
    }  
    return true;  
    }  
}  
return false;  
}

1.3.5 compareTo源码

public int compareTo(String anotherString) {  
    int len1 = value.length;  
    int len2 = anotherString.value.length;  
    int lim = Math.min(len1, len2);  
    char v1[] = value;  
    char v2[] = anotherString.value;  

    int k = 0;  
    while (k < lim) {  
        char c1 = v1[k];  
        char c2 = v2[k];  
        if (c1 != c2) {  
        return c1 - c2;  
    }  
        k++;  
    }  
    return len1 - len2;  
}

1.3.6 String内存分析

1、字符串拼接

public class Main {  
    public static void main(String[] args) {  
        String s1 = "abc";  
        String s2 = "a"+"bc";  
        String s3 = "ab"+"c";  
        String s4 = "abc"+"";  
    }  
}

编译之后如下图所示:原因为 进行了编译期优化。

public class Main {
  public static void main(String[] args) {
    String s1 = "abc";
    String s2 = "abc";
    String s3 = "abc";
    String s4 = "abc";
  }
}

内存分析如下

image.png

2、new String对象

new的对象会在堆中开辟出一块空间,这块空间的地址指向常量池中的“abc”,s5指向的是堆中的地址;s1,s2,s3以及s4指向的是常量池中的地址

image.png

3、字符串变量参与字符串拼接

public class Main {  
    public static void main(String[] args) {  
        String s1 = "abc";  
        String s2 = s1 + "def";  
        String s3 = "abcdef";  
        System.out.println(s2 == s3);  
    }  
}

这个时候编译器不能识别出s1,不会进行编译期优化;想看到这个字节码文件的解析过程,我们可以使用 反编译。使用IDEA进行反编译如下:

image.png

image.png

由此可见,这个s2和s3的内存分析层面完全不一致,因此s2 != s3

1.3.7 StringBuilder

StringBuilder sb = new StringBuilder();  
//表面上是在调用无参构造器,实际上是初始化了一个长度为16的字符数组
StringBuilder sb2 = new StringBuilder(4);  
//表面上是在调用有参构造器,实际上是初始化了一个长度为 传入参数长度的数组
StringBuilder sb = new StringBuilder("abc");  
// 1. 初始化长度为 3+16 的value字符数组  
// 2. 把"abc"对应的value字符数组 复制到 长度为19的value字符数组

String的不可变性是指,在地址值不改变的情况下,String的值不能发生改变。StringBuilder是可变的是指,在地址值不发生改变的情况下,它的值可以改变的。

StringBuilder和StringBuffer的区别

StringBuilder是jdk1.5 开始出现的,效率高,线程不安全

StringBuffer是jdk1.0 始的,效率低,线程安全

1.4 集合

1.4.1 算法和数据结构

算法

1、可以解决具体的问题

2、有设计解决问题的流程

3、有评价这个算法的具体指标(时间复杂度、空间复杂度)

数据结构:指计算机在硬盘、内存、缓存上面是如何组织管理数据的

1、逻辑结构-->思想上的结构-->线性表(数组、链表)、图、树、栈、队列

2、物理结构-->真实的结构-->紧密结构(顺序结构)和跳转结构(链式结构

1.4.2 简要集合结构图

image.png

1.4.3 Collection集合常用方法

方法名作用
add(E e)集合增加指定的元素
addAll(Collection<? extends E> c)将指定集合中的所有元素增加到此集合中
clear()删除集合中的所有元素
remove(Object o)删除集合中存在的指定元素
removeAll(Collecttion<?> c删除指定在集合中的元素
iterator()返回此集合的迭代器
size()返回集合中元素的个数
contains(Object o)如果集合中存在此元素,返回true
contains(Collecttion<?> c如果此集合包含指定集合中的所有元素,返回true
equals(Object o)如果此集合和指定集合中的元素相同,返回true
isEmpty()如果此集合中没有元素,返回true