Java基础语法学习笔记
准备工作
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版本。
配置环境变量
配置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正版专属激活码领取(永久更新):
项目结构
- 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 + "岁");
}
}
分支结构
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。
实体类是一种特殊形式的类,只负责数据存取(除了给对象存、取值的方法就没有提供其他方法了),而对数据的处理交给其他类来完成,以实现数据和数据业务处理相分离(应用场景)。
工具类
如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一样,所以把这样的类就叫做工具类。
工具类没有创建对象的需求,建议将工具类的构造器进行私有,这样别人就不能使用构造方法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类也会继承到一些方法
泛型
泛型限定:
- <?> 表示任意类型
- <? extends 数据类型> 表示指定类型或者指定类型的子类
- <? super 数据类型> 表示指定类型或者指定类型的父类
泛型擦除,即泛型只能编译阶段有效,一旦编译成字节码,是不包含泛型的。而且泛型只支持引用数据类型,不支持基本数据类型。
包装类
包装类就是把基本类型数据包装成对象。
基本数据类型 | 对应包装类(引用数据类型) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
字符串
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 < 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:ss");
// 对时间进行格式化
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 -> 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
实现类
- ArrayList
Set
接口:添加元素无序、不重复、无索引 - HashSet
实现类,子类LinkedHashSet 实现类是有序的 - TreeSet
实现类(默认按大小升序)
- HashSet
Map
- HashMap<K, V>实现类(无序、不重复、无索引),子类LinkedHashMap(有序、不重复、无索引)
- TreeMap<K, V>实现类
遍历集合
迭代器就是一种集合的通用遍历方式。
public class HelloWorld {
public static void main(String[] args) {
Collection<String> 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<String> 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 -> 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) -> System.out.println(k + "-->" + v));
}
}
Stream流
也叫Stream API,是从JDK 8以后的一个新特性,专业用于对集合或数组进行便捷操作。
获取Stream流:
public class HelloWorld {
public static void main(String[] args) {
// 1、获取List集合Stream流
List<String> names = new ArrayList<>();
Collections.addAll(names, "张三丰", "张无忌", "周芷若");
Stream<String> stream = names.stream();
System.out.println(stream);
// 2、获取Set集合Stream流
Set<String> set = new HashSet<>();
Collections.addAll(set, "刘德华", "张德玉", "蜘蛛精");
Stream<String> 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<String> keys = map.keySet();
Stream<String> ks = keys.stream();
System.out.println(ks);
Collection<Double> values = map.values();
Stream<Double> 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<String> s1 = Arrays.stream(names2);
Stream<String> 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),具体步骤如下:
- 在模块目录下,新建lib文件夹
- 把jar包复制粘贴到lib文件夹下
- 选择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<Student> {
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<Student>() {
// @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标签文本中有特殊字符,需要用一些占位符代替:
< 表示 <
> 表示 >
& 表示 &
' 表示 '
" 表示 "
<data> 3 < 2 && 5 > 4 </data>
如果标签文本中,出现大量的特殊字符,不想使用特殊字符,此时可以用CDATA区,格式如下:
<data>
<![CDATA[
3 < 2 && 5 > 4
]]>
</data>
读取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
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »