java.lang.Object
类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。
常用 API:
public String toString()
: 返回该对象的字符串表示
public boolean equals(Object obj)
: 与其他对象比较是否与当前对象“相等”
Object
类的 equals
方法容易抛出空指针异常,在 Objects
类中提供了 equals
方法优化这个问题。
public static boolean equals(Object a, Object b)
:判断两个对象是否相等。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
public Date()
:分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。public Date(long date)
:分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。public long getTime()
把日期对象转换成对应的时间毫秒值。java.text.DateFormat
是日期/时间格式化子类的抽象类,通过这个类完成日期和文本之间的转换。
标识字母(区分大小写) | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
Demo:
/*从出生到现在经历多少天*/
private static void daysFormBirthday() throws ParseException {
System.out.println("输入生日:(格式 yyyy-MM-dd)");
Scanner scanner = new Scanner(System.in);
String birthday = scanner.next();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date birthdayDate = simpleDateFormat.parse(birthday);
long birthdayDateTime = birthdayDate.getTime();
long nowTime = new Date().getTime();
long interval = nowTime - birthdayDateTime;
long days = interval / 1000 / 60 / 60 / 24;
System.out.println("从出生到现在已过" + days + "天!");
}
日历类。
Calendar类中提供很多成员常量,代表给定的日历字段:
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月(从0开始,可以+1使用) |
DAY_OF_MONTH | 月中的天(几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(周几,周日为1,可以-1使用) |
public static long currentTimeMillis()
:返回以毫秒为单位的当前时间。public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将数组中指定的数据拷贝到另一个数组中。数组的拷贝动作是系统级的,性能很高。System.arraycopy方法具有5个参数,含义分别为:
参数序号 | 参数名称 | 参数类型 | 参数含义 |
---|---|---|---|
1 | src | Object | 源数组 |
2 | srcPos | int | 源数组索引起始位置 |
3 | dest | Object | 目标数组 |
4 | destPos | int | 目标数组索引起始位置 |
5 | length | int | 复制元素个数 |
数组的长度是固定的,集合的长度是可变的
数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象,而且对象的类型可以不一致,在开发中一般当对象多的时候,使用集合存储。
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(E e)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中。Iterator接口的常用方法如下:
public E next()
:返回迭代的下一个元素。public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
受限泛型
泛型的上限:
类型名称 extends 类 > 对象名称
只能接收该类型及其子类
泛型的下限:
类型名称 super 类 > 对象名称
只能接收该类型及其父类型
java.util.ArrayList
集合数据存储的结构是数组结构,元素增删满,查找快。日常开发常用的功能是查询数据和遍历数据,所以ArrayList
是最常用的集合。
java.until.LinkedList
集合数据存储的结构是链表结构,方便元素添加和删除的集合,但是查询慢。
java.until.Set
接口和 java.util.List
接口一样,同样继承于 Collection
接口,它与 Collection
接口中的方法基本一致。Set
接口中的元素无序,并且都会以某种规则保证存入的元素不会出现重复。
HashSet
能保证存储的元素唯一,但是无序。想要即唯一,又有顺序就需要用 java.util.LinkedHashSet
java.utils.Collections
是集合工具类,用来对集合进行操作。部分方法:
public static boolean addAll(Collection c, T... elements)
:往集合中添加一些元素。public static void shuffle(List> list) 打乱顺序
:打乱集合顺序。public static void sort(List list)
:将集合中元素按照默认规则排序。public static void sort(List list,Comparator super T> )
:将集合中元素按照指定规则排序。Comparator
接口代表一个比较器,其中:
public int compare(String o1, String o2);
比较其两个参数的顺序。
若按照升序排序,则 o1 < o2 (负数)
若按照降序排序,则 o1 > o1 (正数)
Demo:
private static void sortDemo() {
ArrayList arrayList = new ArrayList();
arrayList.add(new Student("邱学伟",18));
arrayList.add(new Student("梁朝伟",30));
arrayList.add(new Student("周星驰",24));
arrayList.add(new Student("刘德华",28));
arrayList.add(new Student("a",22));
arrayList.add(new Student("b",22));
// 按类内定义排序
// Collections.sort(arrayList);
// 年龄降序
// arrayList.sort(new Comparator() {
// @Override
// public int compare(Student o1, Student o2) {
// return o2.getAge() - o1.getAge();//年龄降序
// }
// });
// 年龄升序,相同年龄 按首字母
arrayList.sort(new Comparator() {
@Override
public int compare(Student o1, Student o2) {
int result = o1.getAge() - o2.getAge();
if (result == 0) {
result = o1.getName().charAt(0) - o2.getName().charAt(0);
}
return result;
}
});
for (Student student : arrayList) {
System.out.println(student);
}
}
Map 中的集合,元素是成对存在的,每个元素由键和值两部分组成,通过键可以找到所对应的值;
Collection 中的集合称为单列集合,Map 中的集合称为双列集合;
Map 中的集合不能包含重复的键,值可以重复。每个键对应一个值。
Map接口中定义了很多方法,常用的如下:
public V put(K key, V value)
: 把指定的键与指定的值添加到Map集合中。public V remove(Object key)
: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。public V get(Object key)
根据指定的键,在Map集合中获取对应的值。boolean containsKey(Object key)
判断集合中是否包含指定的键。public Set keySet()
: 获取Map集合中所有的键,存储到Set集合中。public Set> entrySet()
: 获取到Map集合中所有的键值对对象的集合(Set集合)。Entry
将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历 Map 集合时,就可以从每一个键值对(Entry
)对象中获取对应的键与其对应的值。
public K getKey()
:获取Entry对象中的键。public V getValue()
:获取Entry对象中的值。在Map集合中也提供了获取所有Entry对象的方法:
public Set> entrySet()
: 获取到Map集合中所有的键值对对象的集合(Set集合)。当给 HashMap 中存放自定义对象时,如果自定义对象作为 Key 存在,这时要保证对象的唯一,则必须重写对象的 hashCode
和 equals
方法。
如果要保证 map 中存在的 key 和取出的顺序一致,可以使用 java.util.LinkedHashMap
集合存放。
并发:指两个或多个事件在同一时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:线程是进程中的一个执行单元,负责当前进程中的程序的执行,一个进程中至少有一个线程。一个进程是可以有多个线程的,这个应用程序也可称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程可以包含多个线程。
线程的调度方式:
同步代码块:
synchronized(同步锁)
{
/// 需要同步执行的代码
}
其中同步锁:
wait/notify 就是线程间的一种协作机制。
调用wait和notify方法需要注意的细节
一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多的资源。
面向对象的思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情;
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果不重视过程。
(参数类型 参数名称) -> { 代码语句 }
小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
-> 是新引入的语法,代表指向动作
大括号内的语法与传统方法体要求基本一致
// Lambda , 年龄升序,相同年龄,首字母降序
arrayList.sort((Student s1, Student s2)->{
int result = s1.getAge() - s2.getAge();
if (result == 0) {
result = s2.getName().charAt(0) - s1.getName().charAt(0);
}
return result;
});
省略规则:
小括号内参数的类型可以省略;
如果小括号内有且仅有一个参数,则小括号可以省略
如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return 关键字及语句分号。
使用 Lambda 注意:
Java 中的 Lambda 可以被当做是匿名内部类的替代品。
如果函数的参数是一个函数式接口类型,就可以使用 Lambda 表达式进行替代,使用 Lambda 表达式作为方法参数,其实就是使用函数式接口作为方法参数。
类似的,如果一个方法的返回值是一个函数式接口,那么也可以直接返回一个 Lambda 表达式。
java.util.function.Supplier
接口仅包含一个无参方法 T get()
。用以获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的 Lambda 表达式需要“对外提供”一个符合泛型类型的对象数据。
private static void demo2() {
String s1 = "极客学伟";
String s2 = "科技有限公司";
String s3 = getString(()-> s1 + s2 );
System.out.print(s3);
}
private static String getString(Supplier<String> function) {
return function.get();
}
java.util.function.Consumer
接口与 Supplier 相反,它不是产生一个数据,而是消费一个数据,其数据类型由泛型决定。
private static void demo3() {
consumerString((num)-> System.out.println("打印:" + num));
}
private static void consumerString(Consumer<Integer> function) {
function.accept(1024);
}
消费数据时,首先做一个操作,然后再做一个操作,实现组合。
private static void demo4() {
consumerAndThenString(s -> System.out.println(s.toLowerCase()) ,s -> System.out.println(s.toUpperCase()));
}
private static void consumerAndThenString(Consumer<String> f1, Consumer<String> f2) {
f1.andThen(f2).accept("Hello World!");
}
private static void demo5() {
ArrayList students = new ArrayList(Arrays.asList(new Student("极客",18) , new Student("学伟",28)));
userInfoLog(students,student -> System.out.print("姓名:" + student.getName()) , student -> System.out.print(" 年龄:" + student.getAge() + ";\n"));
}
private static void userInfoLog(ArrayList s, Consumer s1, Consumer s2) {
for (Student student : s) {
s1.andThen(s2).accept(student);
}
}
对某种类型的数据进行判断,从而得到一个 boolean 值的结果,可以使用 java.util.function.Predicate
接口。
用于条件判断的场景:
private static void demo6() {
Student s = new Student("极客学伟",27);
adjustMethod(student -> student.getName().equals("极客学伟") , s);
}
private static void adjustMethod(Predicate predicate, Student s) {
boolean isMe = predicate.test(s);
System.out.println("名字叫" + s.getName() + "的" + (isMe ? "很帅" : "一点点丑"));
}
逻辑判断中将两个 Predicate
条件使用“与”逻辑连接起来实现 “并且”的效果
逻辑判断中将两个 Predicate
条件使用“或”逻辑连接起来实现 “或者”的效果
逻辑判断中将两个 Predicate
条件使用“非”逻辑连接起来实现 “取反”的效果
Demo:
private static void demo7() {
String[] girls = {"邓紫棋,26", "韩红,46", "韩雪,36", "杨紫,29", "慧慧,28", "古力娜扎,32", "AngelaBaby,28"};
/// 筛选 二十来岁名字是两个字的女神
ArrayList result = predicateDemo(girls, g -> Integer.parseInt(g.split(",")[1]) < 30, g -> g.split(",")[0].length() < 3);
result.forEach(System.out::println);
}
// 筛选满足两个条件的数组
private static ArrayList predicateDemo(String[] girls, Predicate p1, Predicate p2) {
ArrayList list = new ArrayList<>(girls.length);
for (String girl : girls) {
if (p1.and(p2).test(girl)) {
list.add(girl);
}
}
return list;
}
java.util.function.Function
接口用来根据一个类型的数据得到另一个类型的数据,前者成为前置条件,后者称为后置条件。
R apply(T t)
根据类型 T 的参数获取类型 R 的结果。
Java 中的流思想类似于工厂车间的“生产流水线”。
Stream(流)是一个来自数据源的元素队列
基础特性:
Iterator
或者增强 for
的方式,显式的在集合外部进行迭代,这叫外部迭代。Stream 提供内部迭代的方式,流可以直接调用遍历方法。使用步骤:
获取一个数据源 -> 数据转换 -> 执行操作获取想要的结果。每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。
demo:
/// 筛选 二十来岁名字是两个字的女神
private static void streamDemo1() {
String[] girls = {"邓紫棋,26", "韩红,46", "韩雪,36", "杨紫,29", "慧慧,28", "古力娜扎,32", "AngelaBaby,28"};
Arrays.stream(girls).filter(s -> parseInt(s.split(",")[1]) < 30).filter(s -> s.split(",")[0].length() < 3).forEach(s -> System.out.println(s));
}
java.util.Collection
接口中加入了 default 方法 stream
用来获取流,所以其所有实现类均可直接获取流。
java.util.Map
接口不是 Collection
的子接口,且其 K-V 数据结构不符合流元素的单一特征,所以获取对应的流需要 Key、Value 或 entry 等情况。
数组对象不可能添加默认方法,Stream
接口中提供了静态方法 of
,用于生成数组的 stream
private static void arrayStreamDemo() {
String[] array = {"张三", "李四", "王五", "赵六"};
Stream<String> arrayStream = Stream.of(array);
arrayStream.forEach(System.out::println);
}
接收一个 Consumer
接口函数,会将每一个流元素交给该函数处理。
将一个流转换成另一个子集流
将流中的元素映射到另一个流中
获取流中的元素个数
对流进行截取,只取前 n 个
跳过流中的前 n 个元素
将两个流合并成为一个流