编辑
2026-04-01
undefined
00

目录

准备工作
安装JDK
配置环境变量
测试JDK
IDEA
项目结构
IDEA常用快捷键
基础语法
标识符
数据类型
获取键盘输入
分支结构
循环结构
生成随机数
内存分配
面向对象
权限修饰符
实体JavaBean
工具类
继承
多态
抽象类
接口
内部类
枚举类
泛型
包装类
字符串
String类常用方法
StringBuilder
StringJoiner
JDK 8日期类
时区
Instant类
日期格式化类
时间间隔
Arrays类
集合
遍历集合
List集合
Set集合
Map集合
Stream流
IO流
缓冲流
转换流
序列化流
IO框架
Lambda表达式
静态方法引用
异常
特殊文本文件
XML文件
日志技术

编程语言分类:

  • 面向过程、面向对象、面向函数
  • 静态类型、动态类型
  • 编译执行、解释执行
  • 有虚拟机、无虚拟机
  • 有GC、无GC

Java是一种面向对象、静态类型、编译执行有VM/GC和运行时、跨平台的高级语言(机器语言→汇编语言→高级语言)。

准备工作

JRE = JVM + 核心类库。JRE(Java运行环境),JVM(Java虚拟机,真正运行Java程序的地方),核心类库(Java已经写好的程序,供程序员调用)。

JDK = JRE + 开发工具。JDK(Java开发工具包),开发工具(java、javac等)。

安装JDK

官网下载:https://www.oracle.com/cn/java/technologies/downloads/,为了便于管理,建议采用Compressed Archive版本。

长期支持版 (Long-Term Support, LTS):

  • JDK 8 LTS 2014年3月
  • JDK 11 LTS 2018年9月
  • JDK 17 LTS 2021年9月
  • JDK 21 LTS 2023年9月

配置环境变量

配置Path环境变量:右键「此电脑」→「属性」→「高级系统设置」→「环境变量」→「系统变量」→「新建」,变量名:JAVA_HOME,变量值:填写JDK安装路径,如:C:\dev\java\jdk-21.0.4。

双击系统变量下的Path环境变量,新建%JAVA_HOME%\bin并上移到第一位,最后点击确定即可。

测试JDK

打开CMD窗口,输入:java -version,测试JDK是否安装成功。

业内大多数程序员都在遵守阿里巴巴的命名规则:《阿里巴巴Java开发手册(终极版)》。

IDEA

IDEA全称IntelliJ IDEA,是用于Java语言开发的集成环境,是业界公认的目前用于Java程序开发最好的工具。

IntelliJ IDEA官网:https://www.jetbrains.com/idea/download/

IntelliJ IDEA正版专属激活码领取(永久更新): JETBRA.IN CHECKER | IPFS:https://3.jetbra.in/

项目结构

  • project(项目、工程)
  • module(模块)
  • package(包)
  • class(类)

project中可以创建多个module,module中可以创建多个package,package中可以创建多个class。

IDEA常用快捷键

  • main:快速输入相关代码
  • Ctrl + D:复制当前行到下一行
  • Ctrl + X:删除所在行
  • Ctrl + Alt + L:格式化代码
  • Shift + Alt + ↑,↓:上下移动当前代码
  • Ctrl + /,Ctrl + Shift + /:代码注释
  • Alt + Enter:强制类型转换

基础语法

字面量:数据在程序中的书写格式。

标识符

就是给类,方法,变量等起的名字。

标识符命名规则,硬性要求:

  • 必须由数字、字母、下划线_、美元符号$组成
  • 数字不能开头
  • 不能是关键字
  • 区分大小写

软性建议:

  • 小驼峰命名法:适用于变量名和方法名
  • 大驼峰命名法:适用于类名

数据类型

基本数据类型,4大类8种:

  • 整型:byte 1 字节,short 2 字节,int默认 4 字节,long 8 字节
  • 浮点型:float,4字节;double默认,8字节
  • 字符型:char,2字节
  • 布尔型:boolean,1字节

// 默认是int类型,字面量后面加 L/l 变成long类型 long lg = 100L; // 默认是double类型,字面量后面加 F/f 变成float类型 float f = 3.14F;引用数据类型,String、数组。

获取键盘输入

public class HelloWorld { public static void main(String[] args) { // 得到一个键盘扫描器对象 Scanner sc = new Scanner(System.in); // 调用sc,接收键盘输入数据 System.out.println("姓名:"); String name = sc.next(); System.out.println("年龄:"); int age = sc.nextInt(); System.out.println(name + age + "岁"); } }Scanner键盘录入字符串:

  • next():遇到空格或Tab键就不再录入,数据可能录入不完整
  • nextLine():以回车作为录入结束标记,之前调用过nextInt(), nextDouble(), nextFloat()等nextLine()就不干活了
  • 使用方案:如果键盘录入的数据,全都是字符串,直接nextLine()。如果除了字符串还有其他类型,使用next()

分支结构

switch 注意事项:

  • 表达式类型只能是byte、short、int、char。JDK 5开始支持枚举,JDK 7开始支持String,不支持long、float、double
  • case值不允许重复,且只能是字面量,不能是变量
  • 不要忘记写break,否则会出现穿透现象

循环结构

for循环中控制循环的变量只能在循环中使用,while循环中控制循环的变量在循环后还可以继续使用。

生成随机数

public class HelloWorld { public static void main(String[] args) { System.out.println(createCode(4)); }

public static String createCode(int n) { // 创建Random对象,用于生成随机数 Random r = new Random(); // 每位随机字符 String code = ""; for (int i = 1; i <= n; i++) { int type = r.nextInt(3); switch (type) { case 0: // 随机一个数字字符 0-9 code += r.nextInt(10); break; case 1: // 随机一个大写字符 A 65 Z 65+25 (0-25)+65 char ch1 = (char) (r.nextInt(26) + 65); code += ch1; break; case 2: // 随机一个小写字符 a 97 z 97+25 (0-25)+97 char ch2 = (char) (r.nextInt(26) + 97); code += ch2; break; } } return code; }

}

内存分配

Java为了便于虚拟机执行Java程序,将虚拟机内存划分为:

  • 方法区:字节码文件先加载到这里
  • 栈:方法运行时所进入的内存,变量也在这一块区域中
  • 堆:new出来的东西,会在这块内存中开辟空间并分配地址。静态成员变量
  • 本地方法栈
  • 程序计数器

面向对象

this关键字,就是一个变量,用在方法中,可以拿到当前类的对象,即方法调用者的地址值。

构造器其实是一种特殊的方法,但这个方法没有返回值类型,方法名必须和类名相同。在创建对象时,会调用构造器。也就是说new Student()就是在执行构造器,当构造器执行完了,也就意味着对象创建成功。

在设计一个类时,如果不写构造器,Java会自动生成一个无参数构造器。一旦定义了有参数构造器,Java就不再提供空参数构造器,此时建议加一个无参数构造器。

权限修饰符

封装设计规范用8个字总结,就是:合理隐藏、合理暴露,这就需要用到修饰符。 修饰符本类同一包下其他类任意包下子类任意包下任意类private√缺省√√protected√√√public√√√√ private < 缺省 < protected < public

实体JavaBean

一般称之为实体类(封装数据的类),可以使用ptg插件1秒生成标准JavaBean。

应用场景:实体类的对象只负责数据存取(除了给对象存、取值的方法就没有提供其他方法了),而对数据的业务处理交给其他类的对象来完成,以实现数据和数据业务处理相分离。

package com.abc.domain; public class Student { // 1.成员变量私有化 private String name; private int age; // 2.空参、带参构造方法 public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } // 3.对私有成员变量提供get和set方法 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; } }

工具类

如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一样,所以把这样的类就叫做工具类。

工具类没有创建对象的需求,建议将工具类的构造器进行私有,这样别人就不能使用构造方法new对象了。

继承

重写的方法上面,可以加一个注解@Override,用于标注这个方法是重写的父类方法。使用这个方法重写小技巧,可以指定java编译器,检查方法重写的格式是否正确,代码可读性也会更好。

super关键字:代表继承的父类。子类构造器,都会先调用父类构造器,再执行自己。如果不想使用默认的super()方式调用父类构造器,可以手动使用super(参数)调用父类有参数构造器。

this和super的用法:

访问本类成员: this.成员变量    //访问本类成员变量 this.成员方法    //调用本类成员方法 this()    //调用本类空参数构造器 this(参数)    //调用本类有参数构造器 访问父类成员: super.成员变量    //访问父类成员变量 super.成员方法    //调用父类成员方法 super()    //调用父类空参数构造器 super(参数)    //调用父类有参数构造器 注意:this和super访问构造方法,只能用到构造方法的第一句,否则会报错。

多态

多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。注意多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。

多态的表现形式:父类类型 对象名称 = 子类对象;,其实是自动类型转换

多态的前提:

  • 有继承/实现关系
  • 有父类引用指向子类对象
  • 有方法重写

多态的好处:

  • 在多态形式下,右边代码是解耦合的,更便于扩展和维护
  • 定义方法时,使用父类类型作为形参,可以接收一切子类对象,扩展性更强,更便利

多态的弊端:

  • 在多态形式下,不能直接调用子类特有方法,但是转型后是可以调用的。转型就是强制类型转换,即把父类变量转换为子类类型
  • 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)
  • 强转前,建议使用instanceof关键字,判断当前对象的真实类型

final关键字:

  • 修饰类:表明该类是最终类,不能被继承
  • 修饰方法:表明该方法是最终方法,不能被重写
  • 修饰变量:该变量只能被赋值一次

常量:被static final修饰的成员变量,通常用于记录系统配置信息。

抽象类

关键字abstract,是抽象的意思,可以修饰类也可以修饰方法:

  • 被abstract修饰的类,就是抽象类
  • 被abstract修饰的方法,就是抽象方法(不允许有方法体)

抽象类特点:

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
  • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类

接口

关键字interface,用来定义接口这种特殊结构。接口不能创建对象,是用来被类实现(implements)的,实现接口的类称为实现类。 public interface 接口名{ // 成员变量(常量) // 成员方法(抽象方法) }一个类可以实现多个接口(接口可理解成干爹),类实现接口必须重写所有接口的全部抽象方法,否则这个类也必须是抽象类。

接口和类的关系:

  • 类和类:继承关系,只能单继承,不能多继承,但可以多层继承
  • 类和接口:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
  • 接口和接口:继承关系,可以单继承,也可以多继承

内部类

内部类是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类内部,这个类就是内部类。

内部类有四种形式,分别是:

  • 成员内部类:类中的一个普通成员,类似于成员变量、成员方法。创建对象格式:Outer.Inner in = new Outer().new Inner();,外部类.内部类 变量名 = new 外部类().new 内部类()
  • 静态内部类:其实就是在成员内部类前面加static关键字,属于外部类自己持有。创建对象:Outer.Inner in = new Outer.Inner();
  • 局部内部类:定义在方法中的类,和局部变量一样,只能在方法中有效。所以局部内部类局限性很强,一般在开发中不使用
  • 匿名内部类:一种特殊局部内部类,格式如下: new 父类/接口(参数值){ @Override 重写父类/接口方法; }匿名内部类本质上是一个子类,并会立即创建出一个子类对象。

枚举类

枚举是一种特殊类,格式如下: public enum 枚举类名{ 枚举项1,枚举项2,枚举项3; }特点:

  • 枚举类第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象
  • 枚举类的构造器都是私有的,对外不能创建对象;枚举都是最终类,不能被继承
  • 枚举类中,从第二行开始,可以定义类的其他各种成员
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承java.lang.Enum类,从enum类也会继承到一些方法

泛型

泛型限定:

  • 表示任意类型
  • 表示指定类型或者指定类型的子类
  • 表示指定类型或者指定类型的父类

泛型擦除,即泛型只能编译阶段有效,一旦编译成字节码,是不包含泛型的。而且泛型只支持引用数据类型,不支持基本数据类型。

包装类

包装类就是把基本类型数据包装成对象。 基本数据类型对应包装类(引用数据类型)byteByteshortShortintIntegerlongLongcharCharacterfloatFloatdoubleDoublebooleanBoolean 当对一个原始值调用方法或属性时,解释器会临时将原始值包装为对应的对象,然后调用这个对象的属性或方法。

字符串

String创建对象的区别:

  • 只要是以"..."方式写出的字符串对象,会存储到字符串常量池,且相同内容的字符串只存储一份
  • 通过new方式创建字符串对象,每new一次都会产生一个新的对象放在堆内存中

字符串对象的内容比较,千万不要用==(默认比较地址),字符串对象的内容一样时地址不一定一样。判断字符串内容,建议用String提供的equals方法,只关心内容一样,就返回true,不关心地址。

String类常用方法

public class HelloWorld { public static void main(String[] args) { String s = "好abc"; // 字符串遍历 for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); System.out.println(ch); }

System.out.println("-------------------"); // 字符串转字符数组,再遍历 char[] chars = s.toCharArray(); for (char aChar : chars) { System.out.println(aChar); } System.out.println("-------------------"); System.out.println(createCode(4)); } public static String createCode(int n) { String code = ""; String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random r = new Random(); for (int i = 0; i &lt; n; i++) { int index = r.nextInt(data.length()); code += data.charAt(index); } return code; }

}

StringBuilder

StringBuilder可以看成是一个容器,创建之后里面的内容是可变的,作用是提高字符串的操作效率。

StringJoiner

使用StringBuilder拼接字符串时,代码写起来还是有一点麻烦,而StringJoiner号称是拼接神器,不仅效率高,而且代码简洁,JDK 8才出现这个类。 public class HelloWorld { public static void main(String[] args) { //参数1:间隔符,参数2:开头,参数3:结尾 StringJoiner s1 = new StringJoiner(",","[","]"); s1.add("java1"); s1.add("java2"); s1.add("java3"); System.out.println(s1); // [java1,java2,java3] } }

JDK 8日期类

表示日期、时间、日期时间的类:LocalDate、LocalTime、LocalDateTime类,这三个类的用法套路都是一样的。 public class HelloWorld { public static void main(String[] args) { // 获取本地日期对象(不可变对象) LocalDate ld = LocalDate.now(); System.out.println(ld); // 2024-09-10

// 获取日期对象中的信息 int year = ld.getYear(); // 年 int month = ld.getMonthValue(); // 月(1-12) int day = ld.getDayOfMonth(); // 日 int dayOfYear = ld.getDayOfYear(); // 一年中第几天 int dayOfWeek = ld.getDayOfWeek().getValue(); // 星期几(1-7) System.out.println(year); System.out.println(month); System.out.println(day); System.out.println(dayOfYear); System.out.println(dayOfWeek); // 直接修改某个信息: withYear、withMonth、withDayOfYear、withDayOfMonth LocalDate ld2 = ld.withYear(2099); LocalDate ld3 = ld.withMonth(12); System.out.println(ld2); // 2099-09-10 System.out.println(ld3); // 2024-12-10 System.out.println(ld); // 2024-09-10 // 把某个信息加多少: plusYears、plusMonths、plusDays、plusWeeks LocalDate ld4 = ld.plusYears(2); LocalDate ld5 = ld.plusMonths(2); // 把某个信息减多少:minusYears、minusMonths、minusDays、minusWeeks LocalDate ld6 = ld.minusYears(2); LocalDate ld7 = ld.minusMonths(2); // 获取指定日期的LocalDate对象 LocalDate ld8 = LocalDate.of(2099, 12, 12); LocalDate ld9 = LocalDate.of(2099, 12, 12); // 判断2个日期对象是否相等,在前还是在后:equals isBefore isAfter System.out.println(ld8.equals(ld9)); // true System.out.println(ld8.isAfter(ld)); // true System.out.println(ld8.isBefore(ld));// false // 获取本地日期和时间对象 LocalDateTime ldt = LocalDateTime.now(); System.out.println(ldt); // 2024-09-10T22:32:28.566246800 // 可以把LocalDateTime转换成LocalDate和LocalTime LocalDate d = ldt.toLocalDate(); LocalTime t = ldt.toLocalTime(); System.out.println(d); // 2024-09-10 System.out.println(t); // 22:33:39.302744400 LocalDateTime dt = LocalDateTime.of(d, t); System.out.println(dt); // 2024-09-10T22:33:39.302744400 }

}

时区

public class HelloWorld { public static void main(String[] args) { // ZoneId:时区id ZoneId zoneId = ZoneId.systemDefault(); System.out.println(zoneId.getId()); // Asia/Shanghai System.out.println(zoneId); // Asia/Shanghai

// 获取Java支持的全部时区id System.out.println(ZoneId.getAvailableZoneIds()); // 把某个时区id封装成ZoneId对象 ZoneId zoneId1 = ZoneId.of("America/New_York"); // ZonedDateTime:带时区的时间 // 获取某个时区的ZonedDateTime对象 ZonedDateTime now = ZonedDateTime.now(zoneId1); System.out.println(now); // 2024-09-10T11:00:28.352739400-04:00[America/New_York] // 世界标准时间 ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC()); System.out.println(now1); // 2024-09-10T15:00:28.352739400Z // 获取系统默认时区的ZonedDateTime对象 ZonedDateTime now2 = ZonedDateTime.now(); System.out.println(now2); // 2024-09-10T23:00:28.352739400+08:00[Asia/Shanghai] }

}

Instant类

通过获取Instant的对象可以拿到此刻的时间,由两部分组成:从1970-01-01 00:00:00开始到此刻的总秒数 + 纳秒数(1秒=1000毫秒 1毫秒=1000微秒 1微秒=1000纳秒)。 public class HelloWorld { public static void main(String[] args) { // 创建Instant对象,获取此刻时间信息,不可变对象 Instant now = Instant.now(); System.out.println(now); // 2024-09-10T15:42:27.397201Z

// 获取总秒数 long second = now.getEpochSecond(); System.out.println(second); // 1725982947 // 不够1秒的纳秒数 int nano = now.getNano(); System.out.println(nano); // 397201000 // Instant对象的作用:做代码的性能分析,或者记录用户的操作时间点 Instant now1 = Instant.now(); // 代码执行。。。。 Instant now2 = Instant.now(); }

}

日期格式化类

DateTimeFormater,对日期进行格式化和解析。 public class HelloWorld { public static void main(String[] args) { // 创建日期时间格式化器对象 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm

");

// 对时间进行格式化 LocalDateTime now = LocalDateTime.now(); System.out.println(now); String rs = formatter.format(now); // 正向格式化 System.out.println(rs); // 2024年09月10日 23:53:14 // 格式化时间另一种方案 String rs2 = now.format(formatter); // 反向格式化 System.out.println(rs2); // 2024年09月10日 23:53:14 // 解析时间一般使用LocalDateTime提供的解析方法来解析 String dateStr = "2029年12月12日 12:12:12"; LocalDateTime ldt = LocalDateTime.parse(dateStr, formatter); System.out.println(ldt); // 2029-12-12T12:12:12 }

}

时间间隔

Period、Duration这两个类用来对计算两个时间点的时间间隔。其中Period用来计算日期间隔(年、月、日),Duration用来计算时间间隔(时、分、秒、纳秒)。 public class HelloWorld { public static void main(String[] args) { LocalDate start = LocalDate.of(2029, 8, 10); LocalDate end = LocalDate.of(2029, 12, 15);

// 创建Period对象,封装两个日期对象 Period period = Period.between(start, end); // 通过Period对象获取两个日期对象相差的信息 System.out.println(period.getYears()); // 0 System.out.println(period.getMonths());// 4 System.out.println(period.getDays()); // 5 LocalDateTime start2 = LocalDateTime.of(2025, 11, 11, 11, 10, 10); LocalDateTime end2 = LocalDateTime.of(2025, 11, 11, 11, 11, 11); // 得到Duration对象 Duration duration = Duration.between(start2, end2); // 获取两个时间对象间隔的信息 System.out.println(duration.toDays()); // 0 间隔多少天 System.out.println(duration.toHours());// 0 间隔多少小时 System.out.println(duration.toMinutes()); // 1 间隔多少分 System.out.println(duration.toSeconds()); // 61 间隔多少秒 System.out.println(duration.toMillis());// 61000 间隔多少毫秒 System.out.println(duration.toNanos()); // 61000000000 间隔多少纳秒 }

}

Arrays类

public class HelloWorld { public static void main(String[] args) { int[] arr = {10, 20, 30, 40}; System.out.println(Arrays.toString(arr)); // [10, 20, 30, 40]

// 拷贝数组,包前不包后 int[] arr2 = Arrays.copyOfRange(arr, 1, 3); System.out.println(Arrays.toString(arr2)); // [20, 30] // 拷贝数组,指定新数组长度 int[] arr3 = Arrays.copyOf(arr, 5); System.out.println(Arrays.toString(arr3)); // [10, 20, 30, 40, 0] // 把数组中原数据改为新数据又存进去 double[] prices = {99.8, 128, 100}; // 把所有的价格都打八折,然后又存进去 Arrays.setAll(prices, new IntToDoubleFunction() { @Override public double applyAsDouble(int value) { // value = 0 1 2 return prices[value] * 0.8; } }); System.out.println(Arrays.toString(prices)); // [79.84, 102.4, 80.0] // 使用Lambda表达式简化格式 // Arrays.setAll(prices, value -&gt; prices[value] * 0.8 ); // 对数组进行排序(默认升序) Arrays.sort(prices); System.out.println(Arrays.toString(prices)); // [79.84, 80.0, 102.4] }

}

集合

集合包括:Collection单列集合和Map双列集合。

Collection接口:

  • List接口:添加元素有序、可重复、有索引

  • ArrayList实现类

  • LinkedList实现类

  • Set接口:添加元素无序、不重复、无索引

  • HashSet实现类,子类LinkedHashSet实现类是有序的

  • TreeSet实现类(默认按大小升序)

Map接口:

  • HashMap<K, V>实现类(无序、不重复、无索引),子类LinkedHashMap(有序、不重复、无索引)
  • TreeMap<K, V>实现类

遍历集合

迭代器就是一种集合的通用遍历方式。 public class HelloWorld { public static void main(String[] args) { Collection c = new ArrayList<>(); c.add("赵敏"); c.add("小昭"); c.add("灭绝"); System.out.println(c); // [赵敏, 小昭, 灭绝]

// 增强for循环(元素数据类型 变量名 : 数组或集合) for (String s : c) { System.out.println(s); } // 调用forEach方法 c.forEach(System.out::println); //[赵敏, 小昭, 灭绝] }

}

List集合

ArrayList适合:根据索引查询数据;不适合:数据量大的同时,又要频繁的进行增删操作。 public class HelloWorld { public static void main(String[] args) { // 创建ArrayList集合对象 ArrayList list = new ArrayList<>();

list.add("Python"); list.add("Java"); list.add("Python"); System.out.println(list); // [Python, Java, Python] // 往集合某个索引位置处添加数据 list.add(1, "MySQL"); System.out.println(list); // [Python, MySQL, Java, Python] // 根据索引获取集合中某个索引位置处值 String rs = list.get(1); System.out.println(rs); // MySQL // 获取集合大小,即存储的元素个数 System.out.println(list.size()); // 4 // 根据索引删除集合中的某个元素,返回被删除的元素 System.out.println(list.remove(1)); // MySQL // 直接删除某个元素,默认删除第一次出现的数据,删除成功返回true System.out.println(list.remove("Python")); // true System.out.println(list); // [Java, Python] // 修改某个索引位置处的数据,修改后会返回原来的值 System.out.println(list.set(1, "JS")); // Python System.out.println(list); // [Java, JS] list.removeIf(name -&gt; name.contains("J")); System.out.println(list); // [] }

}

Set集合

HashSet集合底层是基于哈希表实现的,哈希表根据JDK版本的不同有区别:

  • JDK 8以前:哈希表 = 数组 + 链表
  • JDK 8以后:哈希表 = 数组 + 链表 + 红黑树

HashSet集合存储元素时,底层调用了元素的两个方法:一个是hashCode方法获取元素的hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。

JDK 8开始后,当链表长度超过8,且数组长度>=64时,会把链表转成红黑树。

Map集合

Map集合遍历: public class HelloWorld { public static void main(String[] args) { Map<String, Double> map = new HashMap<>(); map.put("蜘蛛精", 169.8); map.put("孙悟空", 165.8); map.put("至尊宝", 169.5); System.out.println(map);

// 遍历map集合,传递Lambda表达式 map.forEach((k, v) -&gt; System.out.println(k + "--&gt;" + v)); }

}

Stream流

也叫Stream API,是从JDK 8以后的一个新特性,专业用于对集合或数组进行便捷操作。

获取Stream流:

public class HelloWorld { public static void main(String[] args) { // 1、获取List集合Stream流 List names = new ArrayList<>(); Collections.addAll(names, "张三丰", "张无忌", "周芷若"); Stream stream = names.stream(); System.out.println(stream); // 2、获取Set集合Stream流 Set set = new HashSet<>(); Collections.addAll(set, "刘德华", "张德玉", "蜘蛛精"); Stream stream1 = set.stream(); stream1.filter(s -> s.contains("德")).forEach(System.out::println); // 3、获取Map集合的Stream流 Map<String, Double> map = new HashMap<>(); map.put("古力娜扎", 172.3); map.put("迪丽热巴", 168.3); map.put("马尔扎巴", 166.3); Set keys = map.keySet(); Stream ks = keys.stream(); System.out.println(ks); Collection values = map.values(); Stream vs = values.stream(); System.out.println(vs); Set<Map.Entry<String, Double>> entries = map.entrySet(); Stream<Map.Entry<String, Double>> kvs = entries.stream(); kvs.filter(e -> e.getKey().contains("巴")) .forEach(e -> System.out.println(e.getKey() + "-->" + e.getValue())); // 4、获取数组Stream流 String[] names2 = {"张翠山", "东方不败", "唐大山"}; Stream s1 = Arrays.stream(names2); Stream s2 = Stream.of(names2); System.out.println(s1); System.out.println(s2); } }

Stream流中间方法可以继续调用,这样一来就可以支持链式编程(或者叫流式编程)。

Stream流终结方法,调用完之后,其结果就不再是Stream流了,所以不支持链式编程。

IO流

  • 输入流(读数据):磁盘/网络 --> 内存
  • 输出流(写数据):内存 --> 磁盘/网络

缓冲流

缓冲字节流(自带8KB缓冲池): public class HelloWorld { public static void main(String[] args) { try ( InputStream is = new FileInputStream("helloworld-app/src/abc.txt"); // 1、定义一个字节缓冲输入流包装原始字节输入流 InputStream bis = new BufferedInputStream(is);

OutputStream os = new FileOutputStream("helloworld-app/src/abc2.txt"); // 2、定义一个字节缓冲输出流包装原始字节输出流 OutputStream bos = new BufferedOutputStream(os); ){ byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1){ bos.write(buffer, 0, len); } System.out.println("复制完成!!"); } catch (Exception e) { e.printStackTrace(); } }

}

转换流

有两种转换流InputStreamReader,OutputStreamWriter,它们可以将字节流转换为字符流,并且可以指定编码方案。

序列化流

字节流是以字节为单位来读写数据、字符流是以字符为单位来读写数据、而对象流是以对象为单位来读写数据。也就是把对象当做一个整体,可以写一个对象到文件(序列化),也可以从文件中把对象读取出来(反序列化)。

IO框架

为了简化对IO操作,Apache开源基金组织提供了一组有关IO流小框架(名字叫Commons-IO),可以提高IO流的开发效率。

引入jar包(提供的工具类叫FileUtils),具体步骤如下:

  1. 在模块目录下,新建lib文件夹
  2. 把jar包复制粘贴到lib文件夹下
  3. 选择lib下的jar包,右键点击Add As Library,然后就可以用了

public class HelloWorld { public static void main(String[] args) throws IOException { // 复制文件 FileUtils.copyFile(new File("helloworld-app/src/abc.txt"), new File("helloworld-app/src/abc2.txt"));

// 复制文件夹 FileUtils.copyDirectory(new File("D:\\私人珍藏"), new File("D:\\私人珍藏2")); // 删除文件夹 FileUtils.deleteDirectory(new File("D:\\私人珍藏2")); // Java原生提供的一行代码搞定很多事情 Files.copy(Path.of("helloworld-app/src/abc.txt"), Path.of("helloworld-app/src/abc2.txt")); System.out.println(Files.readString(Path.of("helloworld-app/src/abc.txt"))); }

}

Lambda表达式

作用:用于简化匿名内部类的代码书写,JDK 8新语法形式。Lambda表达式只能简化函数式接口的匿名内部类。

函数式接口:

  • 有且仅有一个抽象方法的接口
  • 大部分函数式接口,上面都可能会有一个@FunctionalInterface注解,有该注解的接口必定是函数式接口

静态方法引用

用来进一步简化Lambda表达式,它简化的更加过分。 Student.java: package com.abc.hello;

public class Student implements Comparable { private String name; private double height; private int age;

public Student(String name, double height, int age) { this.name = name; this.height = height; this.age = age; } public int getAge() { return age; } @Override public int compareTo(Student o) { return this.age - o.age; // 按照年龄升序排列 // return o.age - this.age; // 按照年龄降序排列 } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", height=" + height + ", age=" + age + '}'; }

}

CompareByAge.java:package com.abc.hello; public class CompareByAge { public static int compareByAge(Student o1, Student o2) { return o1.getAge() - o2.getAge(); // 升序排序规则 } }

HelloWorld.java:

package com.abc.hello; import java.util.Arrays; public class HelloWorld { public static void main(String[] args) { Student[] students = new Student[3]; students[0] = new Student("蜘蛛精", 169.5, 23); students[1] = new Student("紫霞", 163.8, 26); students[2] = new Student("至尊宝", 167.5, 24); System.out.println(Arrays.toString(students)); // 原始写法:对数组中学生对象,按照年龄升序排序 //        Arrays.sort(students, new Comparator() { //            @Override //            public int compare(Student o1, Student o2) { //                return o1.getAge() - o2.getAge(); // 按照年龄升序排序 //            } //        }); // 使用Lambda简化后形式 //        Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge()); //        Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2)); //静态方法引用:类名::方法名。实际上是用类名调用方法,但把参数给省略了 Arrays.sort(students, CompareByAge::compareByAge); System.out.println(Arrays.toString(students)); } }

异常

异常体系:

  • Error:系统级别错误

  • Exception:异常

  • 运行时异常:RuntimeException

  • 编程时异常:Exception

特殊文本文件

两种特殊的文本文件,一种是properties文件,一种是xml文件。

XML文件

如果XML标签文本中有特殊字符,需要用一些占位符代替: <  表示 < >  表示 > & 表示 & ' 表示 ' " 表示 " 3 < 2 && 5 > 4 如果标签文本中,出现大量的特殊字符,不想使用特殊字符,此时可以用CDATA区,格式如下: 4 ]]> 读取XML文件中的数据,称为XML解析。好用的XML解析框架,最知名的是DOM4J(第三方开发)。

DOM4J也提供了往XML文件中写标签的方法,但是用起来比较麻烦,不建议使用。可以使用StringBuilder按照标签的格式拼接,然后再使用BufferedWriter写到XML文件中去就可以了。

XML约束有两种,一种是DTD约束(.dtd结尾文件)、一种是Schame约束(.xsd结尾文件)。Schame可以约束XML文件的编写和数据类型

日志技术

推荐使用Logback日志框架,是基于SLF4J日志规范实现的框架,分为3个模块:logback-core(必须有)、logback-classes(必须有)、logback-access(可选)。

要使用Logback日志框架,至少需要在项目中整合如下3个模块:

  • slf4j-api:日志接口
  • logback-core
  • logback-classes

本文作者:a

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!