Java包管理器
包(package)的导入
Java体系非常庞大,为了管理更多的代码互不侵犯,采用了一个叫“包管理”的机制来管理代码,简单来说就是把不同的Java代码放在不同的文件夹里,这个文件夹就是“包”。对于使用不同包的代码,得需要先导入这个包。
String、int、double、long这些属于基础类型,所以不需要导入包。在Java当中导入包的语句就是import 包名+类名;,包名+类名组成了完整的包路径。
LocalDate 这个类的完整包路径是 java.time.LocalDate
,在计算机磁盘里实际上就有这样的文件夹和文件 java/time/LocalDate.java
所有上面的代码应该是这样的:
import java.time.LocalDate;
public class DateTest {
public static void main(String[] args) {
// 得到当前的完整时间
LocalDate now = LocalDate.now();
// 打印出时间
System.out.println(now.toString());
}
}
我们也可以试一下没有 import 包的时候出现什么问题了:
public class DateTest2 {
public static void main(String[] args) {
// 得到当前的日期
LocalDate now = LocalDate.now();
// 打印出日期
System.out.println(now.toString());
}
}
Compilation error: Line 7 - 找不到符号
符号: 变量 LocalDate
位置: 类 DateTest2
相信你应该看到类似这样的错误了,当我们之后看见这样的错误,就有两种可能性:
- 类型写错了
- 类型没有 import(导入)包
我们可以继续深入一下这个 LocalDate 日期类
日期时间和字符串的转化
要利用这个格式来输出字符串,还要借助一个日期格式化类来帮助我们做格式化,这个类就是 DateTimeFormatter,
它的完整包路径是 java.time.format.DateTimeFormatter
,所以还是需要先 import(导入)一下,多个包的导入用换行就可以了,每一行是一个 import 语句。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateTest5 {
public static void main(String[] args) {
LocalDate time = LocalDate.now();
// 打印默认的时间数据
System.out.println(time.toString());
// 创建一个格式化方式
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd");
// 执行时间的格式化处理,得到期望格式的时间字符串
String timeStr = df.format(time);
// 打印时间
System.out.println(timeStr);
}
}
前面说过这个时间格式的字符串是可以更换的,比如想要的时间是类似这样的2024/06/01
,可以这样写:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateTest6 {
public static void main(String[] args) {
LocalDate time = LocalDate.now();
// 创建一个格式化方式
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd");
// 执行时间的格式化处理,得到期望格式的时间字符串
String timeStr = df.format(time);
// 打印时间
System.out.println(timeStr);
}
}
获取日期时间具体的值
如果要从一个日期里得到年份、月份、日、星期几等数据:
import java.time.LocalDate;
public class DateTest7 {
public static void main(String[] args) {
LocalDate time = LocalDate.now();
// 得到当前时间所在年
int year = time.getYear();
System.out.println("当前年份 " + year);
// 得到当前时间所在月
int month = time.getMonth().getValue();
System.out.println("当前月份 " + month);
// 得到当前时间在这个月中的天数
int day = time.getDayOfMonth();
System.out.println("当前日 " + day);
// 得到当前时间所在星期数
int dayOfWeek = time.getDayOfWeek().getValue();
System.out.println("当前星期 " + dayOfWeek);
}
}
这里有些用 getValue(),有些没有,因为getMonth() 和 getDayOfWeek() 方法的返回值不是具体的数字,而是一个对象(Java 官方的设计),所以必须用 getValue() 得到具体的数字。
时间日期类的运用
字符串转化为日期时间
之前通过 time.toString()
这个方法的执行把 time 的数据变成了字符串,所以就会有一个方向的需求把字符串变成 LocalDate 类型,这种需求也是非常常见的:
import java.time.LocalDate;
public class DateTest8 {
public static void main(String[] args) {
// 定义一个时间字符串,日期是2019年1月1日
String date = "2019-01-01";
// 把字符串转化位 LocalDate 对象,并得到字符串匹配的日期
LocalDate date2 = LocalDate.parse(date);
// 打印出日期
System.out.println(date2.toString());
}
}
如上代码,LocalDate.parse(date)
这个 parse 方法可以把日期字符串转化为日期类型,这个字符串格式是 年-月-日 时:分:秒(英文格式是:yyyy-MM-dd HH:mm:ss)其中年是 4 位数字(yyyy),月是 2 位数字(MM),日是 2 位数字(DD),这个格式不能错哦,比如 2019 年 2 月 1 号就应该是2019-02-01
而不是2019-2-1。
如果日期字符串的格式不是 yyyy-MM-dd
,那么就要借助 DateTimeFormatter
,比如:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateTest81 {
public static void main(String[] args) {
// 定义一个时间字符串,日期是2019年1月1日
String date = "2019/01/01";
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd");
// 把字符串转化位 LocalDate 对象,并得到字符串匹配的日期
LocalDate date2 = LocalDate.parse(date,df);
// 打印出日期
System.out.println(date2.toString());
}
}
时间日期的计算
如果预定酒店,2019 年 5 月 1 日入住,3 天后离店。只要我们对日期的天数进行+3 的计算就可以完成了。LocalDate 类有一个 plusDays(天数)
方法用于执行天数的相加:
import java.time.LocalDate;
public class DateTest9 {
public static void main(String[] args) {
LocalDate leaveTime = getLeaveTime("2019-05-01", 3);
System.out.println(leaveTime.toString());
}
/**
* 根据入住时间和入住天数得到离店时间
* @param checkInTime
* @param days
* @return
*/
public static LocalDate getLeaveTime(String checkInTime, int days) {
// 把字符串转化为 LocalDate 类型
LocalDate time = LocalDate.parse(checkInTime);
// 使用 plusDays 添加天数,得到新的时间
LocalDate leaveTime = time.plusDays(days);
return leaveTime;
}
}
除了处理天数相加外,还可以处理其他的日期时间计算:
import java.time.LocalDate;
public class DateTest10 {
public static void main(String[] args) {
LocalDate now = LocalDate.now();
System.out.println("当前:" + now.toString());
System.out.println("加法运算");
System.out.println("加1天:" + now.plusDays(1));
System.out.println("加1周:" + now.plusWeeks(1));
System.out.println("加1月:" + now.plusMonths(1));
System.out.println("加1年:" + now.plusYears(1));
System.out.println("减法运算");
System.out.println("减1天:" + now.minusDays(1));
System.out.println("减1周:" + now.minusWeeks(1));
System.out.println("减1月:" + now.minusMonths(1));
System.out.println("减1年:" + now.minusYears(1));
}
}
两个日期时间的判断
在日常工作中经常会对两个时间进行判断,比如说判断两个时间是否是同一天、一个日期是否在另外一个日期的前面或者后面:
import java.time.LocalDate;
public class DateTest11 {
public static void main(String[] args) {
LocalDate now = LocalDate.now();
// 可以对两个 LocalDate 进行比较,
// 可以判断一个日期是否在另一个日期之前或之后,
// 或者判断两个日期是否是同年同月同日。
boolean isBefore = now.minusDays(1).isBefore(LocalDate.now());
System.out.println("是否在当天之前:" + isBefore);
boolean isAfter = now.plusDays(1).isAfter(LocalDate.now());
System.out.println("是否在当天之后:" + isAfter);
// 判断是否是当天
boolean sameDate = now.isEqual(LocalDate.now());
System.out.println("是否在当天:" + sameDate);
}
}
实例:小明在日历上添加了一个提醒,2024-10-01 和小红一起去上海迪士尼
需求 1:创建一个 notice 方法,这个方法用来执行提醒任务并接受一个字符串(存储的是日期时间)
- 如果传入的日期早于 10 月 1 号,则打印:活动还未开始
- 如果传入的日期是 10 月 1 号,则打印:活动开始啦
- 如果传入的时间超过 10 月 1 号,则打印:活动已经结束啦
需求 2:在 main 方法里执行 3 次 notice 方法,每次调用的参数值都不同,分别是:
2024-09-01
2024-10-01
2024-10-02
import java.time.LocalDate;
public class Test803 {
public static void main(String[] args) {
notice("2024-09-01");
notice("2024-10-01");
notice("2024-10-02");
}
public static void notice(String time) {
// 把字符串转化为 LocalDate 类型
LocalDate dateTime = LocalDate.parse(time);
LocalDate noticeTime = LocalDate.parse("2024-10-01");
if (dateTime.isBefore(noticeTime)) {
System.out.println("活动还未开始");
} else if (dateTime.isAfter(noticeTime)) {
System.out.println("活动已经结束啦");
} else if (dateTime.equals(noticeTime)) {
System.out.println("活动开始啦");
}
}
}
面向对象-抽象
Java 中对象如一个房子的户型图一样,把房子设计成一个户型图就是抽象的过程。
在 Java 面向对象中把上述的抽象过程视为面向对象编程,在 Java 中一切皆对象,任何事物都可以抽象成一个 Java 对象,就像可以把房子抽象成户型图一样。
创建 House.java
Java 源码是由多个后缀名为 java
的文本文件组成,现在开始尝试一下自己创建 Java 类。
创建 Java 类文件有四个规范需要遵守:
- 文件名由
类名
+.java
组成,比如说 HelloWord.java - 类名遵循大驼峰命名法,就是首字母大写,另外类名里不能有中文和特殊字符哦(比如:&、#、@、* 等),只能是字母和数字。比如: HelloWord、Test、Test40
- Java 类内容遵守固定的格式
- Java 类存放在 Java 包中,默认情况下 Java 工程中的代码是存放在
src/main/java
目录中,从这个目录开始才是包名开始的地方,这个是 Java 工程化推荐的目录结构。
public class 类名称 {
}
比如说 Java 文件是 House.java,那么文件内容是
public class House {
}
面向对象-包
由于 Java 天生是面向企业级的编程语言,所以基于工程化的考虑,一般不会把所有的 Java 类都存放在默认的包下,这样文件会非常难于管理,一般我们开发的时候都会使用package
来管理代码。
创建包和包下面的类
自定义包其实就是创建文件夹,包路径就是文件夹相对路径,比如 com.csdn.test
是一个包路径(注意包路径和子包路径直接使用.
来做分隔),对应的文件路径就是 src/main/java/com/csdn/test
,如果工程没有这个文件夹,就需要自己创建一下,和创建文件一样,只是要多创建几个子文件夹。
包名的规则:一般来说包名的开始是一个公司或者一个组织的域名的反写,如果是个人也可以遵守类似的规则,比如csdn的域名是 csdn.com,
那么包名的起始就是com.csdn
,就需要创建com/csdn
这样的两级目录。Java 的规范建议是用包管理代码。
如果我们在某个包下创建 Java 文件,那么 Java 类的内容就需要多个一个声明包路径的语法
比如com.csdn.test.Hello.java
这样的 Java 类,我们可以分析一下,它的包名是
com.csdn.test
类名称是Hello
,创建好的类代码是这样的
package com.csdn.test;
public class Hello {
}
这里多了一个 package包路径;
这样的语法,这个就是包的声明,包路径是该 Java 所在的包目录。一个文件只有一个 package 语句,并且一般是在文件第一行。
如何引入自定义的类
import
语句用于申明导入不同包的类对象,在自己创建的类对象一样也有这个问题。
比如 House.java
,如果把这个类创建成
com.youkeda.model.House
上面的代码中House 对象的是存放在 com.youkeda.model
包里,可以通过下面的办法import:
package com.csdn.test;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
//引入 House 类对象
import com.csdn.model.House;//import 语句得在 package 之后
public class Hello {
}
面向对象-属性/方法
设计师通过户型图展示了房子的特征和行为,这就可以表示一个房子信息了,我们可以再分析一下房子的情况:
- 特征:颜色,卧室
- 行为:打开水龙头,打开电灯
特征称为对象属性,而行为称为对象方法,完整的抽象过程分别是抽象对象名称、对象属性、对象方法。
对象属性
首先把房子的特征抽象成对象属性:
public class House {
// 房子的颜色是绿色
public String color = "green";
// 房子的卧室有2间
public int bedRooms = 2;
}
上面的代码中把在类中直接定义的变量称为对象属性,或者类属性。
public String color = "green";
这个和方法的变量不同点就是多了个 public
关键字,这个代表的是公共变量的意思,除了 public,还有 protected、private等等
对象属性在定义的时候可以直接赋值,比如上面的color = "green"
,默认房子的颜色设置为绿色。对象属性也可以不在定义的时候直接赋值,看功能的需要。
对象方法
把房子的行为抽象成 Java 类的方法:
public class House {
// 房子的颜色是绿色
public String color = "green";
// 房子的卧室有2间
public int bedRooms = 2;
/**
* 打开水龙头
*/
public void runWater(){
}
/**
* 打开电灯
*/
public void turnLightsOn(){
}
}
这里的方法没有 static
关键字,有 static 的方法我们称为静态方法,没有 sttic 的方法叫对象方法,比如:
/**
* 打开电灯
*/
public void turnLightsOn(){
}
总结:对象属性和对象方法是面向对象的基础知识,把对象名称、对象属性、对象方法都抽象出来,就完成了面向对象的第一步:抽象。
实例化对象
1个户型图是可以建造很多个房子,这个建造房子的过程在 Java 当中就叫做对象实例化
对于房子来说,得知道谁是谁的房子。想要进入房子,就必须要先找到房子,找到房子才可以进入。
实例化
以 House
为例:
//我的房子
House myHouse = new House();
//你的房子
House yourHouse = new House();
//她的房子
House herHouse = new House();
在上面的代码中,根据 House
这个对象实例化了3个房子,分别是 myHouse、yourHouse、herHouse。对象的属性和方法也只有实例化后才能被调用,就像只有找到房子才能对房子进行操作,比如打开灯。
有了实例以后,如果想打开你房子
的电灯,那就可以执行:
youHouse.turnLightsOn();
如果想把她的房子颜色变成红色:
herHouse.color = "red";
房子是看得见、摸得着的物体,如果建造一个房子,那么就会土地盖楼。在 Java 中实例化对象也是一样,会在 Java 内存中开辟一个存储空间来存储对象实例,这样才能确保每个实例是独一无二的、互不侵犯的,就如房子一样。
实例化就像开发商可以通过户型图来盖房子;而装修设计师也可以根据户型图来设计装修房间;一个户型图可以创建N个房子,重复利用的价值也非常大,对于 Java 对象来说,这个好处依然存在,一个类对象可以被实例化成多个实例,每个实例都可以被同样的方法、属性来操作,这样就具备了大规模系统工程化的能力。
构造函数
实例化的语法是:
new 构造函数();
构造函数是其实一个比较特别的对象方法,具体表现在
-
它的名称是和类名一模一样
比如 House.java 这个类的构造函数名称就是
House
,绝大多数情况下构造函数都是由 Java 系统自动创建的 -
构造函数这个方法是没有返回值
public class House{ //这就是默认的构造函数 public House(){ } }
-
构造函数一般配合实例化一起使用的,可以把构造函数当作类对象的初始化方法
注意:构造函数是对象方法,所以也具备所有方法的能力,比如有参数、可以在方法里写代码逻辑。
构造函数
带参数构造函数
House myHouse = new House();
myHouse.color = "red";
如果创建多个 House 的实例,那么
myHouse.color = "red";
就会显的冗余,那么这段代码可以借助构造函数来实现代码的简化,实例化的时候就可以指定 color:
public class House {
// 房子的颜色是绿色
public String color = "green";
// 自定义一个带 color 参数的构造函数
public House(String color){
this.color = color;
}
}
上面的代码中多了一个 this
,实际这段构造函数代码的作用等同于之前的
myHouse.color = "red";
this
和 myHouse
同等,也就是说 this 代表的是实例化后的对象,有了这个构造函数,就可以通过如下代码来实例化:
House myHouse = new House("red");
显示指定默认构造函数
注意:如果在Java 类中定义了新的构造函数,那么默认的无参数构造函数就失效了。
会出现如下错误:
./src/main/java/com/youkeda/action/HouseAction.java:8: 错误: 无法将类 House中的构造器 House应用到给定类型;
House hourse = new House();
^
需要: String
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
这就是默认的构造函数失效了,如果想让默认的构造函数生效,可以再添加一个默认的
public class House {
// 房子的颜色是绿色
public String color = "green";
// 房子的卧室有2间
public int bedRooms = 2;
public House() {
}
// 自定义一个带 color 参数的构造函数
public House(String color) {
this.color = color;
}
}
举例:加强一下 House 类,新增一个新的构造函数,新的构造函数需要支持颜色和房间数的初始化
public House(String color,int bedRooms){
this.color = color;
this.bedRooms = bedRooms;
}
更改 HouseAction 类的实例化方式,改成新增的构造函数。我们希望的数据是
颜色 | 房间数 |
---|---|
red | 3 |
white | 2 |
green | 2 |
public static void main(String[] args) {
House hourse = new House("red",3);
System.out.println(hourse.color+":"+hourse.bedRooms);
hourse = new House("white",2);
System.out.println(hourse.color+":"+hourse.bedRooms);
hourse = new House("green",2);
System.out.println(hourse.color+":"+hourse.bedRooms);
}
Java 内部也有很多面向对象的类,比如 java.io.File
File(文件)是操作系统中的基本元素,Java File 也非常强大,它也是抽象电脑上的文件而来的。
首先我们还是要实例化 File:
// 实例化一个文件实例,指向到 d:/img/a.png 文件
File file = new File("d:/img/a.png");
除了上面的实例化方式,File 还有两个常用的构造函数:
//根据parent路径名字符串和child路径名字符串创建一个新 File 实例
File(String parent, String child)
//通过给定的父文件对象和子路径名字符串创建一个新的File实例
File(File parent, String child)
实例化成功后,File 类会找到对应的那个文件,从而可以获取和操作文件
下面是获取文件大小的方法,实例化以后可以调用这个方法得到文件的大小
// 返回字节数
public long length()
通过这个我们可以知道方法名称、方法参数、返回类型,看一下例子
//得到文件大小的字节数
long size = file.length();
Java 运行的时候,是从工程目录开始读取文件的,所以如果不是文件的绝对路径,那么就会在工程目录下找文件(相对路径)
File 对象的其他方法
- public String getName():返回由此抽象路径名表示的文件或目录的名称。
- public String getParent():返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null。
- public File getParentFile():返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null。
- public String getPath():将此抽象路径名转换为一个路径名字符串。
- public boolean isAbsolute():测试此抽象路径名是否为绝对路径名。
- public String getAbsolutePath():返回抽象路径名的绝对路径名字符串。
- public boolean canRead():测试应用程序是否可以读取此抽象路径名表示的文件。
- public boolean canWrite():测试应用程序是否可以修改此抽象路径名表示的文件。
- public boolean exists():测试此抽象路径名表示的文件或目录是否存在。
- public boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。
- public boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
- public long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
- public long length():返回由此抽象路径名表示的文件的长度。
- public boolean createNewFile() throws IOException:当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。
- public boolean delete():删除此抽象路径名表示的文件或目录。
- public void deleteOnExit():在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
- public String[] list():返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。
- public String[] list(FilenameFilter filter):返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的。
- public File[] listFiles():返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。
- public File[] listFiles(FileFilter filter):返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。
- public boolean mkdir():创建此抽象路径名指定的目录。
- public boolean mkdirs():创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
- public boolean renameTo(File dest):重新命名此抽象路径名表示的文件。
- public boolean setLastModified(long time):设置由此抽象路径名所指定的文件或目录的最后一次修改时间。
- public boolean setReadOnly():标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。
- public static File createTempFile(String prefix, String suffix, File directory) throws IOException:在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
- public static File createTempFile(String prefix, String suffix) throws IOException:在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。
- public int compareTo(File pathname):按字母顺序比较两个抽象路径名。
- public int compareTo(Object o):按字母顺序比较抽象路径名与给定对象。
- public boolean equals(Object obj):测试此抽象路径名与给定对象是否相等。
- public String toString():返回此抽象路径名的路径名字符串。
ArrayList
在Java 的世界里一切皆对象,只不过有些对象是Java 自己创建的,其他的就要自己来创建。比如 String、int、boolean 这些也都是由 Java 创建的对象,但是int、boolean (小写)是编程语言的原生对象。
实际上 Java 也有类似 String 这样的对象类型(也叫做包装类):
- int 的 Java 对象就是 Integer;
- boolean 的 Java 对象就是 Boolean;
- double 的 Java 对象是 Double。
原生对象和包装类的主要区别在于,包装类的默认值是 null。比如 int 的默认值是 0,但是 Integer 的默认值是 null;boolean 的默认值是 false,但是 Boolean 的默认值是 null。
这两种写法是完全互通的,某些情况下系统可以自动处理原生对象与包装对象之间的转换。在有些需要用对象的地方用对象,否则就是用原生的。
所以对象除了可以单独实例化,执行实例化的方法外,其实还可以作为方法的入参或者返回参数,因为从规范上来说,只要是对象都是允许的。假定文件夹 root 下有 N 个文件,现在我们遍历这些文件,并打印出文件名称。
由于 N 个文件的个数不确定,所以我们无法用数组来存储多个文件(因为数组的长度是事先确定的),这个时候就需要一个动态数组的方案来存储多个文件,这个动态数组在 Java 当中就是 java.util.ArrayList
对象
ArrayList 语法
ArrayList 本质上就是一个动态数组对象,可以方便的存储集合对象,一般会把同类型的数据存储在 ArrayList 里。如果要使用 ArrayList 就必须先实例化,实例化的语法都是一样的,使用 new 关键字,并且需要指定对象类型。
// 这里的 Java 对象类型可以是任意的对象类型
// 比如 String、Integer、House 等
// 这里的 <> 是 Java 泛型的规范,记住这个语法就行了
ArrayList<Java 对象类型> list = new ArrayList<>();
注意:ArrayList 这个对象是存放在 java.util
包下的,如果要使用它就需要先进行 import, 完整的引入是import java.util.ArrayList;
add 方法
完成 ArrayList 实例化之后,就可以使用 ArrayList 的 add
方法进行数据的添加。
import java.util.ArrayList;
public class ArrayListTest1{
public static void main(String[] args){
// 创建一个 ArrayList 存储字符串集合
ArrayList<String> strs = new ArrayList<>();
// 添加数据到 ArrayList 实例里
strs.add("张三");
strs.add("李四");
}
}
get/size 方法
ArrayList 既然是动态数组那也就具备数组的特点:
- 可以获取长度
size()
- 可以根据索引获取具体的值 get(索引),ArrayList 的索引是从 0 开始的
一般我们对索引的单词使用是 index
import java.util.ArrayList;
public class ArrayListTest1{
public static void main(String[] args){
// 创建一个 ArrayList 存储字符串集合
ArrayList<String> strs = new ArrayList<>();
// 添加数据到 ArrayList 实例里
strs.add("张三");
strs.add("李四");
// 获取集合的长度
int size = strs.size();
// 使用 for 循环迭代每一条记录
for(int i=0;i<size;i++){
// 根据索引获取值,值的类型是 String
String str = strs.get(i);
System.out.println(str);
}
}
}
遍历集合:for 语句
实际上,如果在循环迭代集合(遍历集合)的时候,还有另外一种写法:
import java.util.ArrayList;
public class ArrayListTest1 {
public static void main(String[] args){
// 创建一个 ArrayList 存储字符串集合
ArrayList<String> strs = new ArrayList<>();
// 添加数据到 ArrayList 实例里
strs.add("张三");
strs.add("李四");
// 获取集合的长度
int size = strs.size();
// 使用 for 循环迭代每一条记录
for(String str : strs){
System.out.println(str);
}
}
}
在上面的例子中,集合是字符串集合,所以使用的是
for(String str : strs){
}
和之前所学的 for ... i
的区别在于,遍历集合的时候能否得到 i
值
for(int i=0;i<size;i++){
// 根据索引获取值,值的类型是 String
String str = strs.get(i);
System.out.println(str);
}
List 和 ArrayList
List 是接口,ArrayList是其中一个常用的实现类。当然还有其它一些不常用的实现类。
ArrayList<String> strs = new ArrayList<>();
写作下面的形式会更好,更符合面向对象的编程原则。
import java.util.List;
List<String> strs = new ArrayList<>();
Map
Java 中常用的可以存储对象的集合容器,除了按顺序存储对象的 ArrayList,还有一种是 Map 。
Map 不强调存储的顺序,而是通过键值对来存储和读取。
键值对在生活中很常见:门牌号就是Key,指代一个建筑;姓名也是Key,指带一个具体的人;姓名可能会重复,有时也会使用学号做Key,也是为了指代一个具体的人。
如上图,Map(映射)是遵循 key:value 这样的形式的集合。key、value 的类型可以是任何的 Java 对象。
大部分的情况下,都会使用 HashMap 这个 Map 的实现类。所以如果想得到一个 Map 的实例的话可以使用 HashMap:
import java.util.Map;
import java.util.HashMap;
// key value 得是 Java 类型
Map<key, value> map = new HashMap<>();
Map是接口,HashMap 是常用的实现。此外 Map 接口还有其它很多不常用的实现类。
存入数据
举个例子,我们把 1、2、3、4、5、6、7 这个数字映射为英文的星期,利用 Map 如何完成呢?
key | value |
---|---|
1 | Monday |
2 | Tuesday |
3 | Wednesday |
4 | Thursday |
5 | Friday |
6 | Saturday |
7 | Sunday |
首先可以使用 map.put(key,value)
这个方法完成数据的存储
由于 Map 的键和值都是 Java 对象类型,所以在实际创建的时候也需要指定对应的类型,上面的案例中 key 是数字类型:Integer,value 是字符串类型:String,所以可以用 Map<Integer, String>
来表示,这也是关于泛型的用法
// 实例化Map对象
Map<Integer,String> map = new HashMap<>();
map.put(1, "Monday");
map.put(2, "Tuesday");
map.put(3, "Wednesday");
map.put(4, "Thursday");
map.put(5, "Friday");
map.put(6, "Saturday");
map.put(7, "Sunday");
现在这个 map 实例已经包含了 7 条记录,也就完成了集合的存储。
读取数据
如果我们想知道数字 3 对应的值,那么就可以使用 map.get(key)
这个方法来获取啦
String weekText = map.get(3);
System.out.println(weekText);
由于 Map 也是一种集合数据,所以也有集合大小的方法:size()
和遍历能力for
比如上面的代码想要打印出集合大小
int size = map.size();
System.out.println(size);
遍历所有数据
Map 的遍历相对麻烦一点,因为是 key:value 格式,所以一般先得到这个数据格式的集合(entrySet),完整的例子如下:
for (Map.Entry<Integer,String> entry : map.entrySet()){
System.out.println("Key = " + entry.getKey() +
", Value = " + entry.getValue());
}
Key的类型
在 Map 中,Key 的类型不局限于整数,用字符串做 Key 也很常见
// 实例化Map对象
Map<String, String> map = new HashMap<>();
map.put("一", "Monday");
map.put("二", "Tuesday");
获取是,需要给 get 方法传入正确的 Key
String weekText = map.get("二");
动态数组 和 映射集合 的区别
ArrayList
可以参考下图理解动态数组:
动态数组好比一个队伍,教练完全不认识队员们。
- 教练想 找到 某个人,只能通过索引叫某一个出列:
strs.get(i)
- 教练想让某个人 入队 ,通过
add
方法strs.add("张三")
- 教练想让 每个人 报数,使用到循环迭代
for
语法
映射集合
可以参考下图理解Map:
只需要通过姓名牌(Key)就可以找到某个人(Value),这时候不用管座次位置。
老师上课点名其实也是一样的:姓名其实就是 Key,老师叫到 “张三” ,张三同学就回答“到”,老师不关心张三同学坐在第几排,只需要叫姓名就可以了。