# learnJava
**Repository Path**: hb-haoren/learn-java
## Basic Information
- **Project Name**: learnJava
- **Description**: Java基础入门
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-01-05
- **Last Updated**: 2026-01-09
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 接口和抽象类的区别
| | 抽象类 | 接口 |
|------|-------------|----------------------------|
| 属性 | 与普通类一致 | 只能声明常量 |
| 构造器 | 与普通类一致 | 不能声明构造器 |
| 方法 | 与普通类一致,抽象方法 | 抽象方法,私有方法,default实例方法,静态方法 |
| 实例化 | 不能实例化 | 不能实例化 |
## 思考以下问题
- 为什么有了抽象类还有接口?
- 抽象类能不能继承普通类
- 普通类能不能作为抽象类的父类
- 抽象类是否可以继承接口
- 接口能不能继承接口
- 接口是否有单继承的限制
- 接口能不能继承抽象类
# 内存基础
- 栈内存(虚拟机栈 / 方法栈):Java 程序运行时,每调用一个方法,JVM 就会为该方法在虚拟机栈中创建一个对应的栈帧(而非简单的 “方法栈”)。栈帧中主要存放方法的局部变量(包括基本数据类型的值、引用数据类型的引用地址)、方法的操作数栈、动态链接等信息。当方法执行完毕(正常返回或异常终止)时,对应的栈帧会被立即出栈销毁,栈帧内的局部变量也随之释放,内存空间被回收。栈内存的分配和回收由 JVM 自动完成,效率极高。
- 堆内存:通过new关键字(或反射、克隆等方式)创建的对象(包括实例对象、数组),其本体数据都存储在堆内存中。堆内存是 JVM 中最大的内存区域,所有线程共享。堆中对象的生命周期不由方法执行周期决定,而是由垃圾回收器(GC) 统一管理:当一个对象不再被任何可达的引用变量指向(即 “不可达”),且经过 GC 的可达性分析、标记 - 清除等流程确认后,会被判定为垃圾对象;在 GC 触发时,这些垃圾对象占用的内存会被回收。程序员无法通过代码主动销毁堆中的对象或触发 GC(System.gc()仅为建议,不保证立即执行)。
# 作业
1. 完成上述的思考问题
2. 将课程中的抽象类和接口的案例进行手动复现
# 核心类
## Object
示例代码
```java
Point p1=new Point(1,1);
Point p2=new Point(1,1);
```
内存图

- Object类是所有类的父类,Object类中定义了Object类所有对象所共有的方法。
- **toString()**:以字符串的形式将对象的内容返回
```java
@Override
public String toString() {
return "Point[x="+this.x+",y="+this.y+"]";
}
```
- **equals**:判断两个对象是否"相等"
```java
@Override
public boolean equals(Object obj) {
//1 非空判断
if(obj == null) return false;
//2 判断地址值是否一致
if(obj== this) return true;
//3 判断对象的内容
// 判断obj的实际运行时类型是否属于Point或者Point的子类
if(obj instanceof Point){//判断是否属于统一类型
Point p=(Point)obj;
//定义内容的判断逻辑
return this.x == p.x && this.y == p.y;
}
return false;
}
```
- **hashCode()**:返回对象的哈希值.java中重写了equals方法必须重写hashCode方法。确保equals方法为true的两个对象的hashCode方法返回值也相等(参与equals比较的属性参与到hashCode的计算中)。无需确保两个不相等的对象的hashCode方法返回值不相等
```java
@Override
public int hashCode() {
return this.x>>this.y;
}
```
练习
根据下面的类完成类的定义

# String 字符串
Java中使用双引号表示字符串的常量,String类是final类,不能被继承。**String一旦初始化之后值不可变**。
思考:String为什么要使用final修饰?
```java
String s1=new String("abc");
```
上述创建了两个对象:
1. new关键字在堆内存中创建的String对象
2. 在常量池中创建的abc (常量池是运行时常量池,JVM启动时创建,存放字符串常量(字面量),实现复用)
```java
String s1="abc";
String s2="abc";
System.out.println(s1==s2);//true
```
思考:上述代码创建了几个对象?
## String中常用的方法
- **char charAt(int index)**:返回指定索引处的char值
- **int length()**:返回字符串的长度
- **String substring(int beginIndex,int endIndex)**:返回从beginIndex到endIndex的字符串
- **String toLowerCase()**:将字符串中的大写字母转换成小写字母
- **String toUpperCase()**:将字符串中的小写字母转换成大写字母
- **boolean equals(Object obj)**:判断两个字符串是否相等
- **boolean equalsIgnoreCase(String str)**:忽略大小写判断两个字符串是否相等
- **String replace(char oldChar,char newChar)**:将字符串中的oldChar替换成newChar
- **String trim()**:删除字符串两端的空格
- **String[] split(String regex)**:按照给定的正则表达式,将字符串进行切割
- **int indexOf(String str)**:返回字符串中str第一次出现的索引
- **int indexOf(String str,int fromIndex)**:返回字符串中str第一次出现的索引,从fromIndex开始查找
- **int lastIndexOf(String str)**:返回字符串中str最后一次出现的索引
- **int lastIndexOf(String str,int fromIndex)**:返回字符串中str最后一次出现的索引,从fromIndex开始查找
- **boolean startsWith(String prefix)**:判断字符串是否以prefix开头
- **boolean endsWith(String suffix)**:判断字符串是否以suffix结尾
- **boolean contains(String str)**:判断字符串中是否包含str
- **boolean isEmpty()**:判断字符串是否为空
## 字符编码
字符编码涉及到中文乱码的问题。
1. **ASCII**:ASCII码表,英文字母、数字、符号的编码,占一个字节(键盘)。
2. **GBK (GB2312)**:中文编码,中文字符的编码,占两个字节(汉字)。
3. **UTF-8**:万国码,任意字符的编码,占一个或多个字节。一个中文三个字节
4. **UTF-16**:万国码,任意字符的编码,占两个字节。一个中文两个字节。不利于在网络中传输,因为网络传输存在丢包的问题。
5. **iso-8859-1**: iso标准,英文字符的编码,占一个字节,不支持中文。
6. 编码特性对比
| 编码标准 | 汉字字节数 | 兼容性 | 网络适用性 |
|---------|-----------|--------|-----------|
| GBK | 2字节 | 中文优化 | 一般 |
| UTF-8 | 3字节 | 全球通用 | 优秀 |
| UTF-16 | 2/4字节 | 一般 | 较差 |
案例
```java
String str="abc计算机";
//指定字符编码进行转码
byte[] gbkBytes=str.getBytes("gbk");
byte[] utf8Bytes=str.getBytes("utf-8");
byte[] utf16Bytes=str.getBytes("utf-16");
//[97, 98, 99, -68, -58, -53, -29, -69, -6]
System.out.println(Arrays.toString(gbkBytes));
//[97, 98, 99, -24, -82, -95, -25, -82, -105, -26, -100, -70]
System.out.println(Arrays.toString(utf8Bytes));
//[-2, -1, 0, 97, 0, 98, 0, 99, -117, -95, 123, -105, 103, 58]
System.out.println(Arrays.toString(utf16Bytes));
//利用String(byte[] bytes, String charsetName)构造器实现字符串解码
String gbk = new String(gbkBytes, "gbk");
System.out.println(gbk);
//如果编码和解码的字符集不一致,出现乱码
String utf8 = new String(utf8Bytes, "gbk");
System.out.println(utf8);
```
### 编码选择建议
1. **Web开发**:统一使用UTF-8编码
2. **数据库**:设置与应用程序一致的字符集
3. **文件处理**:明确指定读写文件的字符编码
4. **网络传输**:在协议头中声明使用的字符集
### 问题排查技巧
1. 检查系统默认编码设置
2. 验证数据传输各环节的字符集一致性
3. 使用编码检测工具分析乱码原因
4. 在代码中显式指定字符编码
### 扩展学习建议
- UTF-16在网络传输中的具体问题分析
- 不同编程语言中的字符编码处理机制
- 字符编码的历史演变和发展趋势
## 正则表达式
正则表达式是一种描述字符串规则的方式。
正则表达式的语法:
- 描述一个字符:[]
* [abc] 表示该字符为a、b、c中的一个
* [^abc] 表示该字符不为a、b、c中的一个
* [a-z] 描述字符a到z之间的任意字符
* [a-zA-Z] 描述字符a到z、A到Z之间的任意字符
* [0-9] 描述字符0到9之间的任意字符
* [a-zA-Z0-9] 描述字符a到z、A到Z、0到9之间的任意字符
* . 描述任意字符
* 邮编:规则6个数字[0-9][0-9][0-9][0-9][0-9][0-9]
- 预定义字符
* \d 描述数字[0-9]
* \D 描述非数字[^0-9]
* \w 描述单词字符[a-zA-Z_0-9]
* \s 描述空白字符[ \t\n\x0B\f\r]
- 描述次数:{ }
* {n}: 表示该字符重复n次
* {n,m}: 描述该字符重复n到m次
* {n,}: 描述该字符重复n次以上
* {,m}: 描述该字符重复0到m次
* 邮编:规则6个数字[0-9]{6}
* \* 表示该字符重复0次或者多次
* \+ 描述该字符重复1次或者多次
* ? 描述该字符重复0次或者1次
* 案例:匹配零个或者多个空格 \s*
- 边界符
* ^ 描述字符串的开始
* $ 描述字符串的结束
练习:
1.匹配一个手机号码国内手机号码的规则:
- 1开头,共11位数字组成
- 第2位为3、4、5、7、8、9中的一个
- 第2位以后为0-9之间的任意数字
正则表达式:1[3-9]\d{9}
2. 匹配身份证号码
- 共18位
- 前17位为0-9之间的任意数字
- 最后一位为x、X、0-9之间的任意字符
正则大表达式:\d{17}[xX0-9]
课程代码
```java
//正则表达式:定义字符串格式的规则
//手机号码,身份证号码
//String mailCodeRegex="[0-9][0-9][0-9][0-9][0-9][0-9]";
//String mailCodeRegex="[0-9]{6}";
String mailCodeRegex="^\\d{6}$";
System.out.println("123456".matches(mailCodeRegex));//true
System.out.println("123a56".matches(mailCodeRegex));//false
String scopeRegex="^\\s*$";
System.out.println(" ".matches(scopeRegex));//true
System.out.println("123456".matches(scopeRegex));//false
System.out.println("".matches(scopeRegex));//true
```
## 作业
- 提供一个字符串,获得该字符串所有的子串
基本的思路

```java
public String[] childString(String data){
String [] arr=new String[0];
//格式校验
if(data.matches("^\\s*$")){
throw new RuntimeException("字符串不能为空");
}
//len表示子串的长度
for(int len=data.length();len>=1;len--){
for(int start=0;start<=data.length()-len;start++){
String sub=data.substring(start,start+len);
arr=Arrays.copyOf(arr,arr.length+1);
arr[arr.length-1]=sub;
}
}
return arr;
}
```
- 获得两个字符串中最长的公共子串
# StringBuilder
StringBuilder类是一个可变字符串类,用于创建和操作字符串。StringBuilder类提供了一些方法,用于在字符串的末尾添加新的字符或字符串,并返回新的字符串。StringBuilder类与String类一样,也是不可变字符串类,也就是说,一旦创建了一个StringBuilder对象,就不能修改它的内容。但是,StringBuilder类提供了一些方法,可以修改字符串的内容,并返回新的字符串。
```java
StringBuilder sb=new StringBuilder("01234abcd");
sb.append("ABC");
System.out.println(sb);//01234abcdABC
sb.reverse();
System.out.println(sb);//CBAdcba43210
```
开发中如果存在大量字符串的拼接推荐使用StringBuilder#append方法,效率更高。
# 日期类型
可以使用long类型描述时间,是底层常用的时间API
```java
//获得当前距离1970.1.1 00:00:00的毫秒数
long l = System.currentTimeMillis();
//1767771342732
System.out.println(l);
```
如果需要描述生日的日期,商品的生产日期,这些不推荐使用long类型进行描述。
## JDK8.0之前的日期类型
java.util.Date:描述日期和时间.在JDK8.0之前推荐描述日期概念的对象。比如:注册时间,生产日期等等
Date的底层实现存在缺陷,很多API已经过时不推荐使用。Date不能进行格式转换,不能进行日期的计算。
```java
Date d=new Date();
System.out.println(d);//Wed Jan 07 15:41:18 CST 2026
//126 获得的year是当前年份减去1900 (千年虫问题)
System.out.println(d.getYear());
// 0-11 表示1到12月
System.out.println(d.getMonth());//0
```
## LocalDateTime
- java.time.LocalDate:描述日期,JDK8.0之后推荐使用LocalDate进行描述。
- java.time.LocalDateTime:描述日期和时间,JDK8.0之后推荐使用LocalDateTime进行描述。LocalDateTime类提供了很多方法,用于获取日期和时间的属性,比如:年、月、日、时、分、秒、纳秒等等。
### 初始化
```java
//获得当前的日期和时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now);//2026-01-07T15:46:05.612392900
//指定一个时间
LocalDateTime t1=LocalDateTime.of(2025,12,12,15,0,0);
System.out.println(t1);//2025-12-12T15:00
```
### 常用的API
```java
//当前时间 2026-01-07 154826
LocalDateTime now = LocalDateTime.now();
System.out.println(now.getYear());//2026
System.out.println(now.getDayOfMonth());//7
System.out.println(now.getDayOfYear());//7
System.out.println(now.getDayOfWeek());//WEDNESDAY
//对日期进行计算
//3天前
LocalDateTime t1 = now.minusDays(3);
System.out.println(t1);//2026-01-04T15:49:57.439230500
//1个月后
LocalDateTime t2 = now.plusMonths(1);
System.out.println(t2);//2026-02-07T15:50:38.296868
//1小时后
LocalDateTime t3 = now.plus(1, ChronoUnit.HOURS);
//2026-01-07T16:53:08.048997500
System.out.println(t3);
//with方法
LocalDateTime t4 = now.withDayOfMonth(20);
//2026-01-20T16:20:50.287718400
System.out.println(t4);
//LocalDateTime t5 = now.with(ChronoField.DAY_OF_WEEK, 5);
LocalDateTime t5 = now.with(ChronoField.DAY_OF_WEEK, DayOfWeek.FRIDAY.getValue());
System.out.println(t5);
```
### 时间的格式化
在进行数据交互的过程中,存在字符串转时间和时间转字符串的场景。
- java.time.format.DateTimeFormatter:用于格式化LocalDateTime对象
- java中时间的预定义格式
* yyyy:表示4位年
* MM:表示2位月,如01
* M: 表示1-12月,如1
* dd:表示2位日,如01
* HH:表示2位时,24小时制,如01
* hh:表示2位时,12小时制,如01
* mm:表示2位分,如01
* ss:表示2位秒,如01
```java
LocalDateTime now = LocalDateTime.now();
//实例化一个DateTimeFormatter对象,指定日期的格式
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//2026-01-07 16:10:17
String str = now.format(formatter);
System.out.println(str);
formatter=formatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
//2026年01月07日 16时12分13秒
String str1 = now.format(formatter);
System.out.println(str1);
//字符串转日期
DateTimeFormatter datematter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime t1 = LocalDateTime.parse("2026-01-07 16:10:17",datematter);
System.out.println(t1);
```
### 作业
1. 定义一个方法parseIdCard,解析身份证上的出生日期获得当前用户的年龄。
```java
public int parseIdCard(String idCard){
//1. 对idCard进行格式校验
String idCardRegex="^\\d{17}[0-9Xx]$";
if(!idCard.matches(idCardRegex)){
throw new RuntimeException("身份证号码格式错误!");
}
//2. 格式校验通过之后获得出生日期的信息xxxxxx19xx1201xxxx
String data = idCard.substring(6, 14);
//将字符串转成日期类型LocalDate,日期格式yyyyMMdd
DateTimeFormatter timeFormatter=DateTimeFormatter.ofPattern("yyyyMMdd");
//出生的时间信息
LocalDate newDate = LocalDate.parse(data, timeFormatter);
//当前的时间
LocalDate nowDate = LocalDate.now();
int age= nowDate.getYear()-newDate.getYear();
//获得今年的生日的日期
LocalDate birthday = newDate.withYear(nowDate.getYear());
if(birthday.isAfter(nowDate)){
age--;
}
//3. 计算年龄
return age;
}
```
# 包装类
Java中使用对象的概念对八种基本数据类型提供的封装。
| 基本数据类型 | 包装类 |
|---------|-----------|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| boolean | Boolean |
| char | Character |
## Integer
Integer是int类型的包装类,为整型int提供了面向对象的封装
- 属性
* MAX_VALUE
* MIN_VALUE
- 方法
* parseInt(String s):将字符串转换成int类型
* toBinaryString(int i): 将整型转成二进制
* toHexString(int i): 将整型转成十六进制
* toOctalString(int i): 将整型转成八进制
## 装箱和拆箱
- 装箱:将基本数据类型转换成对应的包装类对象
```java
Integer i = Integer.valueOf(100);//new Integer(100);
//将基本数据类型200直接赋值给Integer类型
Integer i1= 200;//编译时,自动调用valueOf方法
```
关于Integer.valueOf的实现
```java
@IntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
```
缓存机制:IntegerCache.low和IntegerCache.high为-128到127之间,IntegerCache.cache为-128到127的缓存对象。
- 拆箱:将包装类对象转换成对应的基本数据类型
```java
//Integer.valueOf的结构为Integer类型,但是可以直接赋值对应的基本数据类型
int i=Integer.valueOf(100);
```
# 泛型
泛型是JDK5引入的语法现象,在编译期间对数据类型进行约束,保证数据安全。
```java
public class MyList {
//存储数据长度
private int size;
//储存数据的数组
private T[] cache;
public MyList(){
this.size=0;
this.cache=(T[])new Object[5];
}
//添加数据
public void add(T obj){
//判断数组是否已满
if(size==cache.length) {
//扩容
T[] newCache=(T[])new Object[cache.length*2];
//将cache中的数据复制到newCache中
System.arraycopy(cache,0,newCache,0,cache.length);
//将newCache赋给cache
cache=newCache;
}
//将数据添加到数组中
cache[size]=obj;
//数据长度加1
size++;
}
@Override
public String toString() {
return Arrays.toString(cache) ;
}
}
```
实例化的方法
```java
MyList list=new MyList();
list.add(100);
list.add(Integer.valueOf(200));
//list.add("123");
//list.add(1.2);
//list.add(false);
System.out.println(list);
```
## 类型擦除
泛型在JDK5之前,编译期间不会进行类型检查,在JDK5之后,泛型在编译期间进行对类型进行检测,编译完成之后,将泛型的类型擦除,只保留基本的数据类型。
```java
MyList list=new MyList();
//编译之后:只有MyList的类型,不存在MyList的类型
System.out.println(list instanceof MyList);
System.out.println(list.getClass());
```
## 菱形语法
在实例化对象是,编译时类型中的泛型类型必须和运行时泛型的类型一致。可以将运行时的泛型的类型省略,使用菱形语法。
```java
MyList list1=new MyList<>();
```
# 集合框架
java提供集合框架对数据进行处理。基于底层的数据结构对数据集合进行管理。数组在实际的数据处理上存在问题。最核心的长度不可变。
数组:存储相同数据类型的顺序结构,一旦初始化之后长度不可变。
## Collection
定义了集合框架的基本API,java种提供了java.util.Colletion定义集合框架的基本功能。
> The root interface in the collection hierarchy. A collection represents a group of objects, known as its elements. Some collections allow duplicate elements and others do not. Some are ordered, and others are unordered. Collections that have a defined encounter order are generally subtypes of the SequencedCollection interface. The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List. This interface is typically used to pass collections around and manipulate them where maximum generality is desired.
Collection中常见的子接口:
* List: 基于线性表的实现
* Set: 基于哈希表的实现
### Collection中的抽象方法

* add(E a):向集合中添加一个数据
* empty() :判断集合是否为空
* size() :集合中的数据量
* contains(Object o) :判断集合中是否存在对象o,依赖该对象的equals方法
## List
List是Collection的子接口。
> An ordered collection, where the user has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.Unlike sets, lists typically allow duplicate elements.
List集合的特点
* 记录数据的顺序
* 支持下标的操作
* 允许有重复元素
### List的扩展的API
提供了支持下标的操作。
* add(int index, E element)
* get(int index)
* remove(int index)
### ArrayList
> Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.)
ArrayList的特点
* 基于可变长数组的List的实现(基于顺序表的实现)
* 支持null的元素
* ArrayList线程不安全的,效率高
案例1:
```java
//1. 示例化一个ArrayList
List l1=new ArrayList<>();
l1.add(1);
l1.add(100);
System.out.println(l1.size());
```
案例2:
```java
Apple a1=new Apple("red",160,"陕西");
Apple a2=new Apple("green",170,"陕西");
Apple a3=new Apple("red",160,"陕西");
//将苹果添加到集合中
List lists=new ArrayList<>();
lists.add(a1);
lists.add(a2);
//判断集合中是否存在a1相同的对象
//Apple类中必须重写equals方法,否则contains总是返回false
System.out.println(lists.contains(a3));
lists.add(a3);
System.out.println(lists.size());
```
### LinkedList
LinkedList是基于双向链表实现。
Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).
LinkedList源码中关于节点Node的定义
```java
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
```
注意:LinkedList同时是Deque和Queue接口的实现
- Deque 是双向队列的定义
- Queue 是单向队列的定义
## Set
官方API的说明
> A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.
Set基本的特点
* 不包含重复的数据(能够自动去重复)
* 去重复数据的功能依赖数据的equals和hashCode方法
* 不支持下标操作
如果向Set集合中添加的数据没有重写equals和hashCode方法,那么不能实现去重的效果。
set通过add添加数据的流程图

### HashSet
> This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits the null element.
HasSet的特点
* 基于哈希表的Set的实现
* 不记录数据的顺序
案例
```java
Set set=new HashSet<>();
set.add("abc");
set.add("123");
set.add("012");
set.add("abc");
System.out.println(set.size());//
System.out.println(set);//[012, 123, abc]
//set的遍历 :for-each
for(String str:set){
System.out.println(str);
}
```
### LinkedHashSet
> Hash table and linked list implementation of the Set interface, with well-defined encounter order. This implementation differs from HashSet in that it maintains a doubly-linked list running through all of its entries. This linked list defines the encounter order (iteration order), which is the order in which elements were inserted into the set (insertion-order).
LinkedHashSet的特点
* 基于Hash表和链表的实现
* 能够记录数据的顺序
* 去重复
## 作业
1. 统计苹果集合中苹果的总重量,平均重量,最大重量,最小重量
```java
// 1. 定义一个类封装统计数据
public class Stats{
//总重量
private int sum;
//平均重量
private double avg;
//最大重量
private int max;
//最小重量
private int min;
}
//2 定义一个方法对苹果的集合进行统计
public Stats statsAppleDatas(List datas){
return null;
}
```
2. 统计苹果集合中有几种产地
```java
public Set statsAppleLocation(List datas){
return null;
}
```
## 扩展学习
1. ArrayList和LinkedList如何选择(性能问题)
2. ArrayList线程不安全为什么还要推荐使用?
## 预习
1. Map