JavaSE模块三笔记
第一阶段——模块三
一、lang包
1. Object类
1.1 常用方法
1.1.1 equals方法
默认比较两个对象的地址,通过重写equals方法可以自定义比较规则。
当调用对象不为空,而参数对象为空时,比较结果为空。
// Object类中equals方法的实现
public boolean equals(Object obj) {
return (this == obj);
}
// 重写equals方法
public boolean equals(Object obj) {
// 当调用对象和参数对象指向同一个对象时,则内容一定相同。
if (this == obj) {
return true;
}
// 当调用对象不为空,而参数对象为空时,比较结果为空。
if (obj == null) {
return false;
}
if (obj instanceof Student) {
Student s = (Student) obj;
return this.getId() == s.getId();
// 如果是比较引用类型,需要使用String;类中重写的equals方法,用来比较字符串是否相同。
// return this.getName().equals(s.getName());
}
return false;
}
1.1.2 hashcode方法
在重写equals方法的同时通常也需要同时重写hashcode方法,因为当两个对象根据equals方法相等时,hashcode的值也必须相同。
1.1.3 toString方法
-
用户获取该对象的字符串形式,该方法默认返回的字符串为:包名.类名@哈希码的十六进制。比如:
pers.kyle.java.mylang.Student@15db9742
-
使用print或者println打印引用或者字符串拼接引用时都会自动调用toString方法
// s1为Student对象 System.out.println(s1); System.out.println("hello" + s1);
所以通常情况下也是需要重写toString方法
2. 包装类
2.1 Integer包装类
2.1.1 装箱和拆箱的笔试考点
Integer i1 = 127;
Integer i5 = 128;
Integer i2 = 127;
Integer i6 = 128;
Integer i3 = new Integer(127);
Integer i7 = new Integer(128);
Integer i4 = new Integer(127);
Integer i8 = new Integer(128);
System.out.println(i1 == i2); // true
System.out.println(i5 == i6); // false
System.out.println(i7.equals(i8)); // true
System.out.println(i3 == i4); // false
注意:在Integer类的内部提供了自动装箱池技术,将-128到127之间的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率。
2.2 包装类的使用总结
-
基本数据类型转换为对应包装类的方式:调用包装类的构造方法或静态方法(推荐)。
Integer v = Integer.valueOf(123); Integer v = Integer.valueOf("123");
-
获取包装类对象中基本数据类型变量数值的方式:调用包装类中的xxxValue方法。
Double d = 3.14; double dv = d.doubleValue();
-
字符串转换为基本数据类型的方式:调用包装类的parseXxx方法。
int i = Integer.parseInt("123");
3. 数字处理类
3.1 BigDecimal类
3.1.1 基本概念
由于float和double类型在运算时可能会有误差,若希望实现精确运算则借助java.lang.BigDecimal类型加以描述。
3.1.2 常用方法
方法声明 | 功能介绍 |
---|---|
BigDecimal(String s) | 根据参数指定的字符串来构造对象 |
BigDecimal add(BigDecimal b) | 用于实现加法运算 |
BigDecimal subtract(BigDecimal b) | 用于实现减法运算 |
BigDecimal multiply(BigDecimal b) | 用于实现乘法运算 |
BigDecimal divide(BigDecimal b) | 用于实现除法运算 |
3.1.3 注意事项
BigDecimal b1 = new BigDecimal("2");
BigDecimal b2 = new BigDecimal("0.3");
System.out.println(b1.devide(b2)); // 会报错,因为该结果无法得到精确值,是一个无限循环小数。
// 非要得到结果的话,需要指定舍弃规则
System.out.println(b1.devide(b2, RoundingMode.HALF_UP));
4. String类(重点)
4.1 常量池的概念
由于String类型描述的字符串内容是常量不可改变,因此Java虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,从而提高性能。
// 到目前为止,只有String这个特殊的类处理new的方式外还可以直接字符串赋值
String s1 = "123";
String s2 = "123";
System.out.println(s1 == s2); // true
4.2 面试考点
String s1 = "hello"; // 这句代码只会创建一个对象,存放在常量池中
String s2 = new String("hello"); // 会创建2个对象,一个存放在常量池中,1个存放在堆区中。
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2)); // String重写了equals方法,结果是true
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); // true
System.out.println(s2 == s4); // false
System.out.println(s2.equals(s4)); //true
// 常量有优化机制,变量没有
String s1 = "hello";
String s2 = "hel" + "lo";
System.out.println(s1 == s2); // true,常量优化机制
String s3 = "hel";
String s4 = s3 + "lo";
System.out.println(s1 == s4); // false
4.3 String和数组之间的转换
String s1 = "hello";
byte[] b = s1.getBytes(); // 转换为字节数组
char[] c = s1.toCharArray(); // 转换为字符数组
4.4 将String转化为int类型
笔试考点
// 方式一:调用Integer类中的parseInt方法
String s1 = new String("12345");
int i = Integer.parseInt(s1);
// 方式二:使用ASCII码来实现
int res = 0;
for (i = 0; i < s1.length(); i++) {
res = res * 10 + s1.charAt(i) - '0';
}
System.out.println(res);
4.4 案例题目
判断字符串"上海自来水来自海上"是否是回文
String s1 = new String("上海自来水来自海上");
// 循环到(args.length) / 2 就可以了
for (int i = 0; i < (args.length) / 2; i++) {
if (s1.charAt(1) != s1.charAt(s1.length() - i - 1)) {
System.out.println(s1 + " 不是回文");
return;
}
}
System.out.println(s1 + " 是回文");
5. 可变字符串类
5.1 StringBuffer
属于线程安全的类,因此效率比较低。
5.2 StringBuilder(重点)
属于非线程安全的类,效率比较高。
5.2.1 常用构造方法
方法声明 | 功能介绍 |
---|---|
StringBuilder() | 使用无参方式构造对象,容量为16 |
StringBuilder(int capcity) | 根据参数指定的容量来构造对象,容量为指定大小 |
StringBuilder(String str) | 根据参数指定的容量来构造对象,容量为 16 + 字符串长度 |
示例代码
// 无参构造
StringBuilder sb1 = new StringBuilder();
System.out.println("sb1 = " + sb1);
System.out.println("容量是:" + sb1.capacity());
System.out.println("长度是:" + sb1.length());
// 有参构造,指定容量大小
StringBuilder sb2 = new StringBuilder(5);
System.out.println("------------------------");
System.out.println("sb2 = " + sb2);
System.out.println("容量是:" + sb2.capacity());
System.out.println("长度是:" + sb2.length());
// 有参构造,指定初始字符串
StringBuilder sb3 = new StringBuilder("abc");
System.out.println("------------------------");
System.out.println("sb3 = " + sb3);
System.out.println("容量是:" + sb3.capacity());
System.out.println("长度是:" + sb3.length());
5.2.2 常用成员方法
方法声明 | 功能介绍 |
---|---|
int capacity() | 用于返回调用对象的容量 |
int length() | 用于返回字符串的长度,也就是字符的个数 |
StringBulider insert(int offset, String str) | 插入字符串并返回调用对象的引用,就是自己。 |
StringBulider append(String str) | 追加字符串 |
StringBulider deleteCharAt(int index) | 将当前字符串中下标为index位置的单个字符删除 |
StringBulider delete(int start, int end) | 删除字符串 |
StringBulider delete(int start, int end, String str) | 替换字符串 |
StringBulider reverse() | 字符串反转 |
5.2.3 自动扩容
当字符串长度超过了字符串对象的初始容量时,该字符串对象会自动扩容,默认的扩容算法是:原始容量*2 + 2。
5.2.4 返回值的设计
StringBulider 的很多方法的返回值均是StringBulider类型。这些方法的返回语句均是:return this
由此可见,这些方法在对StringBulider 所封装的字符串序列进行改变后又返回了该对象的引用,基于这样设计的目的在于可以连续调用。
6. Date、SimpleDateFormat和Calendar类
三个类结合使用,示例代码如下:
// Date d1 = new Date(2008-1900, 8-1, 8, 20, 8, 8 ); // 过时
Calendar calendar = Calendar.getInstance();
calendar.set(2008, 8, 8-1, 20, 8, 8);
Date d2 = calendar.getTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// String format = sdf.format(d1);
String format = sdf.format(d2);
System.out.println("获取到的时间是:" + format);
7. Java8中的日期时间类
示例代码如下:
LocalDateTime dateTime = LocalDateTime.now();
System.out.println("获取到的当前日期时间是: " + dateTime);
// 自定义年月日时分秒
LocalDateTime of = dateTime.of(2008, 8, 8, 20, 8, 8);
System.out.println("指定的日期时间是: " + of);
System.out.println("获取到的年份是:" + of.getYear());
System.out.println("获取到的月份是:" + of.getMonthValue());
System.out.println("获取到的日是:" + of.getDayOfMonth());
System.out.println("获取到的小时是:" + of.getHour());
System.out.println("获取到的分钟是:" + of.getMinute());
System.out.println("获取到的秒是:" + of.getSecond());
注意:使用设置特征(withXxx, plusXxx, minusXxx)的方法返回的是LocalDateTime类型,但是是一个新的LocalDateTime对象,而原来的LocalDateTime对象并没有发生改变,与String一样。
8. 集合类库(重点)
8.1 Collection集合
8.1.1 基本概念
- java.util.Collection接口是List接口、Queue接口以及Set接口的父接口
8.1.2 常用方法
方法声明 | 功能介绍 |
---|---|
boolean add(E e) | 向集合中添加元素 |
boolean addAll(Collection<? extends E> c) | 用于将参数指定的集合中的所有元素添加到当前集合中 |
boolean contains(Object o) | 判断是否包含指定对象 |
boolean retainAll(Collection<? > c) | 保留当前集合中存在且参数指定的集合中存在的元素 |
boolean remove(Object o) | 删除参数指定的元素 |
boolean removeAll(Collection<? > c) | 删除参数指定的集合中的所有元素 |
void clear() | 清空集合 |
int size() | 集合元素个数 |
Object[] toArray() | 将集合转换为数组 |
Iterator |
获取当前集合迭代器 |
需要注意的方法
- contains方法
Collection c = new ArrayList();
System.out.println("集合中的元素有:" + c);
boolean b1 = c.add("one");//自动装箱
boolean b1 = c.add(new String("one"));
boolean b2 = c.add(2);
boolean b3 = c.add(new Person("张三", 18));
System.out.println("集合中的元素有:" + c);//集合中的元素有:[one, 2, Person [name=张三, age=18]]
boolean b4 = c.contains(new Person("张三", 18));
System.out.println(b4);// false
boolean b5 = c.contains(2);
System.out.println(b5);// true
boolean b6 = c.contains(new String("one"));
System.out.println(b6);// true
// 总结,最后还是比较的地址,java封装好的类是重写了equals方法,所以比较结果为true,而自己定义的类如果没有重写equals方法,则比较结果为false。
- remove方法
在删除元素的时候也是使用equals方法来判断是否有该元素,有则删除,返回true;否则返回false。
案例:用iterator模拟toString的打印效果
public class CollectionPrintTest {
public static void main(String[] args) {
// 使用迭代器来模拟toString方法的打印效果
Collection c = new ArrayList();
System.out.println("集合中的元素有:" + c);
boolean b1 = c.add("one");
boolean b2 = c.add(2);
boolean b3 = c.add(new Person("张三", 18));
System.out.println("集合中的元素有:" + c);
Iterator iterator = c.iterator();
StringBuilder sb = new StringBuilder();
sb.append("[");
while (iterator.hasNext()) {
Object next = iterator.next();
if (!iterator.hasNext()) {
sb.append(next).append("]");
}else {
sb.append(next).append(",").append(" ");
}
}
System.out.println(sb.toString());
}
}
8.2 List集合(重中之重)
-
该集合允许有重复的元素,并且有先后放入次序。
-
该集合的主要实现类有:ArrayList类、LinkedList类、Stack类、Vector类
-
遍历List集合的方式:Iterator、for each、toString
8.2.1 ArrayList类
List list = new ArrayList();
list.add(2);
System.out.println(list);
- ArrayList源码解析
// 第一行代码
List list = new ArrayList();
// 进入源码
public ArrayList() {
// elementData = Object[]
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = Object[] = {}
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 由源码可知,new ArrayList();这句代码相当于只是创建了一个长度为0的Object[],并没有为数组申请内存空间
// 第二行代码
list.add(2);
// 进入源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 再进入ensureCapacityInternal(size + 1);这行代码
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 再进入calculateCapacity方法内
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 条件为true
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULT_CAPACITY的默认值是10,minCapacity为1,所以最后返回的是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
// 跳出之后再进入ensureExplicitCapacity方法内
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 这里传入的minCapacity为10,所以条件成立
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 进入grow方法内
private void grow(int minCapacity) {
// overflow-conscious code
// oldCapacity = 0
int oldCapacity = elementData.length;
// newCapacity = 0
// 核心代码,每次扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 进入这里,newCapacity = 10
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 这里核心,重新申请一个长度为newCapacity的数组,即长度为10的数组。
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 到这里,核心的源码就走完了,然后开始执行下面一行代码
elementData[size++] = e; // 将新数组的第一个元素赋值为2
以上源码中有两个核心点
第一个:数组扩容算法:newCapacity = oldCapacity + (oldCapacity >> 1)
第二个:更新数组:elementData = Arrays.copyOf(elementData, newCapacity)
8.2.2 LinkedList类
源码解析
List list = new LinkedList();
list.add(2);
System.out.println(list);
// 源码分析
List list = new LinkedList();// 这一行代码实际上就是创建了一个空的list
// 接下来这一行代码
list.add(2);
// 进入源码内部
public boolean add(E e) {
linkLast(e);
return true;
}
// 进入linkLast方法内部
// Links e as last element.
void linkLast(E e) {
final Node<E> l = last; // last == null
// 调用构造方法生成一个新节点,该节点为最后一个节点,所以它的下一个节点为null,
// 上一个节点为last
final Node<E> newNode = new Node<>(l, e, null);
last = newNode; // 前后节点都为空
if (l == null) // 如果前一个节点也是null
first = newNode; // 那么该节点既是第一个节点,也是最后一个节点。
else
l.next = newNode;
size++;
modCount++;
}
// Node是一个静态内部类,表示一个节点,源码如下
private static class Node<E> {
E item; // 当前节点的值
Node<E> next; // 当前节点的下一个节点
Node<E> prev; // 当前节点的下一个节点
// 构造方法
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
8.2.3 List常用方法
方法声明 | 功能介绍 |
---|---|
void add(int index, E element) | 向集合中指定位置添加元素 |
boolean addAll(int index, Collection<? extends E> c) | 向集合中添加所有元素 |
E get(int index) | 从集合中获取指定位置元素 |
int indexOf(Object o) | 查找参数指定的对象 |
int lastIndexOf(Object o) | 反向查找参数指定的对象 |
E set(int index, E element) | 修改指定位置的元素 |
E remove(int index) | 删除指定位置的元素 |
List subList(int fromIndex, int toIndex) | 用于获取子List |
需要注意的是:subList方法返回的List和原来的List共有同一片内存空间
List l2 = new LinkedList();
l2.add(2);
l2.add("one");
l2.add("two");
System.out.println(l2);
List subl2 = l2.subList(0,2);
System.out.println(subl2);
System.out.println("-------------");
subl2.remove(1);
System.out.println(subl2);
System.out.println(l2);
// 代码的执行结果如下
//[2, one, two]
//[2, one]
//-------------
//[2]
//[2, two]
// 体现出了内存空间的公用
8.4 Stack类
8.3 Queue集合(重点)
- 该集合的主要实现类是LinkedList
8.3.1 常用方法
方法声明 | 功能介绍 |
---|---|
boolean offer(E e) | 将一个对象添加至队尾,若添加成功则返回true |
E poll() | 从队首删除并返回一个元素 |
E peek() | 返回队首的元素(但并不删除) |
8.4 泛型(熟悉)
- 泛型只是在编译时期有效,运行时是没有泛型的。
- 泛型只能用于引用数据类型不能是基本数据类型
8.4.1 自定义泛型类
public class Person<T> {
private String name;
private int age;
private T gender;
...
}
public class PersonTest {
public static void main(String[] args) {
// 如果没有指定泛型,那么默认是按照Object类型来处理
Person p1 = new Person("zhangsan", 20, "男");
System.out.println(p1);
p1.setGender("nan");
// 指定泛型
Person<String> p2 = new Person<>();
System.out.println(p2);
}
}
继承泛型类的方式
//public class Student extends Person{ 不保留泛型并且没有指定类型,此时Person类中的T默认为Object类型
//public class Student extends Person<String>{不保留泛型但是指定了泛型的类型,此时Person类中的T被指定为String类型
//public class Student<T> extends Person<T>{// 保留父类的泛型
public class Student<T,T1> extends Person<T>{// 保留父类的泛型,并且子类额外增加了泛型
}
8.4.2 自定义泛型方法(重点)
泛型方法的语法格式
[访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]) { 方法体; }
public <T2> void printArray(T2[] arr){
方法体
}
// 参数是泛型参数,返回值也是泛型
public <T2> T2[] toArray(T2[] arr) {
方法体
}
8.4.3 泛型通配符(熟悉)
- 无限制通配符:表示我们可以传入任意类型的参数。
- 表示类型的上界是E,只能是E或者是E的子类。
- 表示类型的下界是E,只能是E或者是E的父类。
// 无限制通配符,就是说可以是任何数据类型,不能添加元素
List<?> l1 = new LinkedList<>();
//l1.add(new Animal()); Error,万一泛型是Dog类型呢,那肯定不能放Animal类型咯
//l1.add(new Dog());Error
//l1.add(new Object());Error
System.out.println("----------------------");
// 不能添加元素
List<? extends Animal> l2 = new LinkedList<>();
//l2.add(new Animal()); Error,万一泛型是Dog类型呢,那肯定不能放Animal类型咯
//l2.add(new Dog()); Error,万一还有个cat继承Animal类呢,如果泛型是cat,那肯定也是不能放Dog的撒
System.out.println("----------------------");
// 可以添加元素
List<? super Animal> l3 = new LinkedList<>();
l3.add(new Animal());
l3.add(new Dog());
//l3.add(new Object()); Error
8.5 Set集合(熟悉)
8.5.1 HashSet类
元素放入HashSet集合的原理
-
使用元素调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算出该元素在数组中的索引位置。
-
若该位置没有元素,则将该元素直接放入即可。
-
若该位置有元素,则使用新元素与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放 入。
-
若新元素与已有元素的哈希值相同,则使用新元素调用equals方法与已有元素依次比较。
-
若相等则添加元素失败,否则将元素直接放入即可。
思考:为什么要求重写equals方法后要重写hashCode方法呢?
当两个元素调用equals方法相等时证明这两个元素相同,重写hashCode方法后保证这两个元素得到的哈希码值相同,由同一个哈希算法生成的索引位置相同,此时只需要与该索引位置已有元素比较即可,从而提高效率并避免重复元素的出现。
8.5.2 TreeSet类
由于TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
既然需要比较大小,那么就需要设置比较规则,比较元素大小的规则有两种方式:
- 使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口;
- 使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;
8.5.3 LinkedHashSet类
8.6 Map集合(重点)
8.6.1 主要实现类
- HashMap:底层采用哈希表
- TreeMap:底层采用红黑树
- LinkedHashMap:底层采用双向列表
8.6.2 主要方法
方法声明 | 功能介绍 |
---|---|
V put(K key, V value) | 将Key-Value对存入Map,若集合中已经包含该Key,则替换该Key所对应的Value,返回值为该Key原来所对应的Value,若没有则返回null |
V get(Object key) | 返回与参数Key所对应的Value对象,如果不存在则返回null |
boolean containsKey(Object key); | 判断集合中是否包含指定的Key |
boolean containsValue (Object value); | 判断集合中是否包含指定的Value |
V remove(Object key) | 根据参数指定的key进行删除 |
Set keySet() | 返回此映射中包含的键的Set视图 |
Collection values() | 返回此映射中包含的值的Set视图 |
Set<Map.Entry<K,V>> entrySet() | 返回此映射中包含的映射的Set视图 |
8.6.3 元素放入HashMap集合的原理
- 使用元素的key调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算在数组中的索引 位置。
- 若该位置没有元素,则将该键值对直接放入即可。
- 若该位置有元素,则使用key与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
- 若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较。 若相等则将对应的value修改,否则将键值对直接放入即可。
9.Collections类
9.1 常用方法
- max
- min
- copy,第一个参数是目的集合,第二个参数是原始集合。
- sort
- shuffle
- reverse