文章目录
- Java基础重点知识总结
- 一、Java基础内置对象
- 1.Object
- 2.String
- 3.StringBuffer/StringBuilder
- 4.BigInteger/BigDecimal
- 5.数组工具类Arrays
- 6.系统工具类System
- 7.时间
- 8.instanceof
- 9.Comparable - compareTo
- 二、高级功能
- 1.反射
- 获取Class对象的四种方式
- ClassLoader和Class.forname的区别
- 类加载器
- 反射的应用
- 2.断言
- 3.异常
- 4.日志
- log4j
- logback
- 5.泛型
- 类型变量的上限
- 泛型擦除
- 泛型方法
- 类型通配符
- 6.内部类
- 三、集合
- Collection
- List
- Set
- Map
- Iterator
- ListIterator
- Collections工具类
- 四、Jdk1.8新特性
- 4.1 Lambda表达式与函数式接口
- 4.2 Stream流库
- Stream 的操作三个步骤
- 创建Steam的四种方式
- 中间操作
- 终止操作
- 案例演示
- 4.3 Optional
- 万事如意,阖家安康
Java基础重点知识总结
一、Java基础内置对象
1.Object
可以使用该类型的变量引用任何类型的对象,其中有如下几个方法
- toString()
- getClass():获取对象的“运行时”类型
- finalize():垃圾回收
- hashCode():返回一个对象的hashCode值
- 如果一个对象的参与计算hashCode值的成员变量没有修改,那么在程序运行期间,每次获取的hashCode值不变
- 如果两个对象hashCode不同,那么这两个一定不“相等”
- 如果两个的hashCode值相同,那么这两个对象不一定“相等”
- 如果两个相等,那么他们的hashCode值一定要相同
- equals()
用于判断当前对象与指定对象是否相等,默认的实现,等价于“==”,比较对象的内存地址 - clone()
一个类实现了Cloneable接口,以向Object.clone()方法指示该方法制作该类实例的字段对字段副本是合法的
2.String
它有如下特点
- String类是final修饰的,不能被继承
- String类的底层使用数组存储
- String类的对象不可变(
没有提供修改字符串中某个字符的方法
)
- 字符串常量池中存储字符串常量,可以共享
- 每次修改都会产生新对象,频繁修改的话效率不高
常用API如下
序号 | 方法签名 | 方法功能简介 |
1 | String() | 创建空字符串 |
2 | String(String original) | 根据original创建一个新字符串 |
3 | static String valueOf(xx value) | 根据value内容创建一个字符串 |
4 | String intern() | 将字符串的内容存入常量池 |
5 | String concat() | 字符串拼接 |
6 | boolean equals(Object obj) | 判断当前字符串与指定字符串内容是否已在,严格区分大小写 |
7 | boolean equalsIgnoreCase(String obj) | 判断当前字符串与指定字符串内容是否已在,不区分大小写 |
8 | int compareTo(String str) | 比较当前字符串与指定字符串的大小,严格区分大小写 |
9 | int compareToIgnoreCase(String str) | 比较当前字符串与指定字符串的大小,不区分大小写 |
10 | boolean isEmpty() | 判断当前字符串是否为空 |
11 | int length() | 返回当前字符串的长度 |
12 | String toLowerCase() | 将当前字符串转为小写 |
13 | String toUpperCase() | 将当前字符串转为大写 |
14 | String trim() | 去掉当前字符串前后空白符 |
15 | boolean contains(xx) | 判断当前字符串中是否包含xx |
16 | int indexOf(xx) | 在当前字符串中查找xx第一次出现的下标 |
17 | int lastIndexOf(xx) | 在当前字符串中查找xx最后一次出现的下标 |
18 | String substring(int beginIndex) | 从当前字符串的[beginIndex, 最后]截取一个子串 |
19 | String substring(int beginIndex, int endIndex) | 从当前字符串的[beginIndex, endIndex)截取一个子串 |
20 | char charAt(index) | 返回当前字符串[index]位置字符 |
21 | char[] toCharArray() | 将当前字符串的内容用一个字符数组返回 |
22 | String(char[] value) | 用value字符数组的元素构建一个新字符串 |
23 | String(char[] value,int offset, int count) | 用value字符数组的[offset]开始的count个字符构建一个新字符串 |
24 | static String copyValueOf(char[] data) | 用data字符数组的元素构建一个新字符串 |
25 | static String copyValueOf(char[] data, int offset, int count) | 用data字符数组的[offset]开始的count个字符构建一个新字符串 |
26 | static String valueOf(char[] data) | 用data字符数组的元素构建一个新字符串 |
27 | static String valueOf(char[] data, int offset, int count) | 用data字符数组的[offset]开始的count个字符构建一个新字符串 |
28 | byte[] getBytes() | 将当前字符串按照平台默认字符编码方式编码为字节序列 |
29 | byte[] getBytes(字符编码方式) | 将当前字符串按照指定字符编码方式编码为字节序列 |
30 | String(byte[] bytes) | 将bytes字节序列按照平台默认字符编码方式解码为字符串 |
31 | String(byte[] bytes,String charsetName) | 将bytes字节序列按照指定字符编码方式解码为字符串 |
32 | boolean startsWith(xx) | 判断当前字符串是否以xx开头 |
33 | boolean endsWith(xx) | 判断当前字符串是否以xx结尾 |
34 | boolean matchs(xx) | 判断当前字符串是否满足xx正则 |
35 | String replace(xx,yy) | 将当前字符串中所有xx替换为yy |
36 | String replaceFirst(xx,value) | 将当前字符串中第一个满足xx正则的字符替换为value |
37 | String repalceAll(xx, value) | 将当前字符串中所有满足xx正则的字符替换为value |
38 | String[] split(xx) | 将当前字符串按照xx正则拆分为多个字符串 |
39 | void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) | 将当前字符串的[srtBegin,srcEnd)部分字符复制到dst字符数组中,dst数组从[dstBegin]开始存储 |
3.StringBuffer/StringBuilder
两者的区别
- String类的对象是不可变字符序列,StringBuffer和StringBuilder的对象是可变字符序列
当前String类创建对象完毕之后,该对象的内容(字符序列)是不能改变的
一旦改变就会得到(返回)一个全新的String
- StringBuffer:老点,线程安全的,因为它的方法有synchronized修饰
- StringBuilder:JDK1.5之后引入的,线程不安全,单线程情况下推荐使用
常用API如下
序号 | 方法签名 | 方法区功能简介 |
1 | StringBuffer() | 创建一个空的可变字符序列,默认长度16 |
2 | StringBuffer(String str) | 用字符串str内容创建一个可变字符序列 |
3 | StringBuffer append(数据类型 b) | 在当前字符序列后面追加b |
4 | StringBufferinsert(int index, 数据类型 s) | 在当前字符序列[index]插入s |
5 | StringBuffer delete(int start, int end) | 删除当前字符序列[start,end)部分字符 |
6 | StringBuffer deleteCharAt(int index) | 删除当前字符序列[index]位置字符 |
7 | void setLength(int newLength) | 修改当前字符序列的长度为newLength |
8 | void setCharAt(int index, char ch) | 替换当前字符序列[index]位置字符为ch |
9 | StringBuffer reverse() | 将当前字符序列内容反转 |
10 | StringBuffer replace(int start, int end, String str) | 替换当前字符序列[start,end)部分字符为str |
11 | int indexOf(String str) | 在当前字符序列中开始查找str第一次出现的下标 |
12 | int indexOf(String str, int fromIndex) | 在当前字符序列[fromIndex]开始查找str第一次出现的下标 |
13 | int lastIndexOf(String str) | 在当前字符序列中开始查找str最后一次出现的下标 |
14 | int lastIndexOf(String str, int fromIndex) | 在当前字符序列[fromIndex]开始查找str最后一次出现的下标 |
15 | String substring(int start) | 截取当前字符序列[start,最后]部分构成一个字符串 |
16 | String substring(int start, int end) | 截取当前字符序列[start,end)部分构成一个字符串 |
17 | String toString() | 将当前可变字符序列的内容用String字符串形式表示 |
18 | void trimToSize() | 如果缓冲区大于保存当前字符序列所需的存储空间,则将重新调整其大小,以便更好地利用存储空间。 |
19 | int length() | 返回当前字符序列的长度 |
20 | char charAt(int index) | 返回当前字符序列[index]位置字符 |
21 | void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) | 将当前字符串的[srtBegin,srcEnd)部分字符复制到dst字符数组中,dst数组从[dstBegin]开始存储 |
4.BigInteger/BigDecimal
大数,为了解决基本的整数or浮点数精度不能够满足需求
- BigInteger:实现任意精度的整数运算
- BigDecimal:实现任意精度的浮点数运算
5.数组工具类Arrays
java.util.Arrays
此类包含用于操作数组的各种方法(例如排序和搜索)
序号 | 方法签名 | 方法功能简介 |
1 | static int binarySearch(数据类型[] a, 数据类型 key) | 使用二分查找法在a数组中查找key的下标 |
2 | static 数据类型[] copyOf(数据类型[] original, int newLength) | 根据original复制一个新数组长度为newLength |
3 | static 数据类型[] copyOfRange(数据类型[] original, int from, int to) | 根据original的[from,to)部分复制一个新数组 |
4 | static boolean equals(数据类型[] a1, 数据类型[] a2) | 比较两个数组内容是否一致 |
5 | static void fill(数据类型[] a, 数据类型val) | 用val的值填充a数组 |
6 | static void sort(数据类型[] a) | 将a数组按照自然排序规则实现升序排列 |
7 | static void sort(数据类型[] a, Comparator c) | 将a数组按照c指定的定制比较规则实现升序排列 |
8 | static String toString(数据类型[] a) | 将数组的元素拼接为一个字符串返回 |
6.系统工具类System
- static long currentTimeMillis():获取系统时间的毫秒值
- static void exit(x):退出JVM
- static void arraycopy(原数组, 原数组的起始下标, 目标数组, 目标数组的起始下标,一共复制几个元素)
- static void gc():通知垃圾回收器工作
- static String getProperty(系统属性名)
7.时间
java.util.Date
- new Date():获取系统日期时间
- new Date(long 毫秒):根据毫秒值来获取日期时间
- long getTime():获取该日期时间对应的毫秒值,距离1970-1-1 0:0:0
java.util.Calendar
日历
如何创建/获取Calendar的对象?
- 创建子类对象:GregorianCalendar
Calendar calendar = new GregorianCalendar();
- 获取指定时区的日历对象
- getInstance()
- getInstance(TimeZone 时区) 或 getInstance(Locale 语言环境)
- getInstance(TimeZone, Locale)
- get(字段名)
例如:int year = get(Calendar.YEAR);
java.text.DateFormat
日期时间格式化
// 使用它的子类:SimpleDateFormat
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 把日期转字符串
String str = sf.format(Date的日期对象);
// 把字符串解析为日期
Date d = sf.parse(字符串);
/**
y:年
M:月
d:日
D:一年中的第几天
H:24小时制的时
h:12小时制的时
m:分
s:秒
S:毫秒
E:星期
**/
java.time
LocalDate/LocalTime/LocalDateTime
- now():获取当前的日期或时间
- of(x,x,x):获取指定的日期或时间
- withXxx(), plusXxx(), minusXxx():修改日期和时间,修改后要重新接收新对象
- isLeapYear():判断是否是闰年
- between(x,y):间隔两个日期或时间
- …
8.instanceof
instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例
9.Comparable - compareTo
Comparable接口中只有一个compareTo方法,它用于与指定的对象进行比较以进行排序。 当此对象小于、等于或大于指定对象时,返回一个负整数、零或正整数0
public class DemoApplication {
public static void main(String[] args) {
Integer a = 3;
Integer b = 4;
int compareTo = a.compareTo(b);
System.out.println(compareTo);
}
}
此时返回结果为-1
二、高级功能
1.反射
用几个用处
- 运行时获取类信息、分析类能力
- 运行时检查实例对象,通过java.lang.reflect中的三个类Field、Method、Constructor
- 实现泛型
关于类加载的机制,看JVM篇
java.lang.Class类可以访问Java运行时系统为所有对象维护的一个运行时的类型标识,我觉得也可以叫他是字节码对象
获取Class对象的四种方式
- 类型名.class
- 对象.getClass()
- Class.forName(类型全名称)
- ClassLoader的类加载器对象.loadClass(类型全名称)
Student stu = new Student();
// ***********************************************************************
// 获取Class对象的四种方式
// 方法一:Class.forName("com.test02.Persion");
Class clStu1 = Class.forName("com.test0621.Student");
System.out.println("Class clStu1 = " + "\"" + clStu1 + "\"");
// 方法二:Persion.class;
Class clStu2 = Student.class;
System.out.println("Class clStu2 = " + "\"" + clStu2 + "\"");
// 方法三:对象.getClass();
Class clStu3 = stu.getClass();
System.out.println("Class clStu3 = " + "\"" + clStu3 + "\"");
// 方法四:ClassLoader的类加载器对象.loadClass(类型全名称)
// 可以用系统类加载对象或自定义加载器对象加载指定路径下的类型
ClassLoader clsLoder = clStu1.getClassLoader();
Class clStu4 = clsLoder.loadClass("com.test0621.Student");
System.out.println("Class clStu4 = " + "\"" + clStu4 + "\"");
// ***********************************************************************
ClassLoader和Class.forname的区别
- 相同点
都是针对类加载过程 - 不同点
- Class.forName()
除了将类的class字节码文件加载到JVM之外,还会对类进行解释,执行类中的静态块。(注意这里的静态块
指的是在类初始化时的一些数据,但是ClassLoader
却没有)
而实际上,Class.forName()中也有调用了ClassLoader来实现,其内部实际调用的方法为
Class.forName(className, true, classloader)
- className:加载的类名
- true:指Class被加载后是不是必须被初始化, 不初始化就是不执行static的代码
- ClassLoader.getClassLoader(caller):类加载器
- ClassLoader
遵循双亲委派模型最终调用启动类加载器的类加载器,内部实际调用的方法是 ClassLoader.loadClass(className, false)。
- 总结
class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块,当然还可以指定是否执行静态块。
classLoader将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
类加载器
大致分为如下几种类型
- 引导类加载器(Bootstrap Classloader)又称为根类加载器
它负责加载jre/rt.jar核心库,它本身不是Java代码实现的,也不是ClassLoader的子类,获取它的对象时往往返回null
- 扩展类加载器(Extension ClassLoader)
它负责加载jre/lib/ext扩展库,它是ClassLoader的子类
- 应用程序类加载器(Application Classloader)
它负责加载项目的classpath路径下的类,它是ClassLoader的子类
- 自定义类加载器
当你的程序需要加载“特定”目录下的类,可以自定义类加载器;当你的程序的字节码文件需要加密时,那么往往会提供一个自定义类加载器对其进行解码,常见的自定义类加载器案例:Tomcat
反射的应用
由于方法太多,这里列举核心的几个玩法
- Field
功能
- 用于获取当前对象的成员变量的类型
- 用于对成员变量重新设值
获取Field对象的方法
- Class.getFields():获取类中public类型的属性,返回一个包含 Field 对象的数组;
该数组包含此 Class 对象所表示的类或接口的所有可访问公共字段。 - getDeclaredFields():获取类中所有的属性(public、protected、default、private);
但不包括继承的属性,返回Field对象的一个数组。 - getField(String name):获取类特定的方法,name参数指定了属性的名称;
不能是方法名,而且必须public属性。 - getDeclaredField(String name): 获取类特定的方法,name参数指定了属性的名称。
常用方法
- 获取变量的类型
- Field.getType():返回这个变量的类型
- Field.getGenericType():如果当前属性有签名属性类型就返回
否则就返回Field.getType() - isEnumConstant() : 判断这个属性是否是枚举类
- 获取成员变量的修饰符
- Field.getModifiers():以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符
- 获取和修改成员变量的值
- getName() : 获取属性的名字
- get(Object obj): 返回指定对象obj上此 Field 表示的字段的值
- set(Object obj, Object value) :将指定对象变量上此 Field 对象表示的字段设置为指定的新值
- Class
classOne.getName(); // 获取类类对象代表的类的完整类名
classOne.getSimpleName(); // 获取类类对象代表的类的类名,不包含包名。
classOne.getPackage(); // 获取类类对象代表的类的所在包的对象
classOne.getFields(); // 获取所有public修饰的属性的数组,不仅限于本类,包含父类及父类的父类。
classOne.getDeclaredFields(); // 获取本类所有的修饰符修饰的属性的数组,不包含父类
classOne.getField(""); // 根据参数获取单个的属性,必须public修饰
classOne.getDeclaredField(""); // 根据参数获取单个的属性,任意修饰符
classOne.getMethods(); // 获取所有的public修饰的方法的数组,不仅限于本类,包含父类及父类的父类
classOne.getDeclaredMethods(); // 获取本类所有的修饰符修饰的方法的数组,不包含父类
classOne.getMethod(""); // 利用方法名及参数组获取单个的方法,必须public修饰
classOne.getDeclaredMethod(""); // 利用方法名及参数组获取单个的方法,任意修饰符
classOne.getConstructors(); // 获取所有的构造方法,必须public修饰
classOne.getDeclaredConstructors(); // 获取所有的构造方法,数组,任意修饰符
classOne.getConstructor(); // 根据参数组获取单个的构造方法,必须public修饰
classOne.getDeclaredConstructor(); // 根据参数组获取单个的构造方法,任意修饰符
使用反射的方式获取类类对象代表的类的对象,及调用属性和方法
Class classOne = Student.class;
// Object obj = classOne.newInstance();// 调用newInstance方法是调用无参构造方法,要求类中必须有无参构造方法
// 获取到类类对象代表的类的有参构造方法
Constructor constructor = classOne.getDeclaredConstructor(String.class,int.class,String.class);
Object obj = constructor.newInstance("Tom",20,"男");// 调用有参构造方法创建对象
Field field = classOne.getDeclaredField("stuName");
field.setAccessible(true);// 暴力破解
field.set(obj,"Tom");
System.out.println(field.get(obj));
Method method = classOne.getDeclaredMethod("methodOne",String.class,int.class);
method.invoke(obj,"hello",100);
总结以下上面的应用,以后再对反射专门写一篇文章
- 获取类型的详细信息
- 创建任意引用类型的对象
- 操作任意类型的属性
- 调用任意类型的方法
- 获取泛型父类信息
- 读取注解信息
2.断言
也就是所谓的Assertion,是jdk1.4后加入的新功能,它主要使用在代码开发和测试时期,用于对某些关键数据的判断
,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出
语法
- assert expression;
expression代表一个布尔类型的表达式,如果为真,就继续正常运行,如果为假,程序退出 - assert expression1 : expression2;
expression1是一个布尔表达式,expression2是一个基本类型或者Object类型,如果expression1为真,则程序忽略expression2继续运行;如果expression1为假,则运行expression2,然后退出程序。
举例说明
int i = 3;
assert i==6;
System.out.println("如果断言正常,我就被打印");
断言功能用于软件的开发和测试,是禁止用于生产环境中的代码
3.异常
在我的另外一篇文章有统一异常返回的处理类
4.日志
相信平时大家在调试bug的时候,无非就是两个玩法:Debug+Sout输出,如果采用输出的方式,回头还要把代码删掉,特别的麻烦,所以日志的API就是为了解决这个问题而存在。
常见的日志框架有如下
log4j
下面做一个log4j简单的演示,先了解基本的操作流程
- 依赖引入
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 编写配置文件放入资源根目录下
log4j.properties
#############
# 输出到控制台
#############
# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日志级别 CONSOLE:输出位置自己定义的一个名字 logfile:输出位置自己定义的一个名字
log4j.rootLogger=DEBUG,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 配置CONSOLE日志的输出格式 [frame] 2019-08-22 22:52:12,000 %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
################
# 输出到日志文件中
################
# 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 保存编码格式
log4j.appender.logfile.Encoding=UTF-8
# 输出文件位置此为项目根目录下的logs文件夹中
log4j.appender.logfile.File=logs/root.log
# 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
log4j.appender.logfile.MaxFileSize=10MB
# 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
log4j.appender.logfile.MaxBackupIndex=3
# 配置logfile为自定义布局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
##########################
# 对不同的类输出不同的日志文件
##########################
# club.bagedate包下的日志单独输出
#log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
#log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
logback
既然log4j演示了,那这个顺便
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
配置logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT"/>
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="mapper.EmpMapper" level="DEBUG"/>
</configuration>
5.泛型
当我们在声明类或接口时,类或接口中定义某个成员时,该成员有些类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型
语法格式:
【修饰符】 class 类名<类型变量列表>{
}
【修饰符】 interface 接口名<类型变量列表>{
}
注意:
- <类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:、<K,V>等。
- <类型变量列表>中的类型变量不能用于静态成员上。
在使用这种参数化的类与接口时,我们需要指定泛型变量的实际类型参数
- 实际类型参数必须是引用数据类型,不能是基本数据类型
- 在创建类的对象时指定类型变量对应的实际类型参数
类型变量的上限
当在声明类型变量时,如果不希望这个类型变量代表任意引用数据类型,而是某个系列的引用数据类型,那么可以设定类型变量的上限
<类型变量 extends 上限1 & 上限2>
如果多个上限中有类有接口,那么只能有一个类,而且必须写在最左边。接口的话,可以多个
如果在声明<类型变量>时没有指定任何上限,默认上限是java.lang.Object
泛型擦除
当使用参数化类型的类或接口时,如果没有指定泛型,那么会怎么样呢?
会发生泛型擦除,自动按照最左边的第一个上限处理。如果没有指定上限,上限即为Object
泛型方法
语法格式:
【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
//...
}
- <类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:、<K,V>等。
- <类型变量>同样也可以指定上限
public class MyArrays{
public static <T extends Comparable<T>> void sort(T[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length-i; j++) {
if(arr[j].compareTo(arr[j+1])>0){
T temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
类型通配符
当我们声明一个方法时,某个形参的类型是一个参数化的泛型类或泛型接口类型,但是在声明方法时,又不确定该泛型实际类型,我们可以考虑使用类型通配符
- <?>任意类型
- <? extends 上限>
- <? super 下限>
会发生的问题
< ? >:不可变,因为< ?>类型不确定,编译时,任意类型都是错
6.内部类
当在描述
一个比较复杂的事物
时,发现它的内部需要另一个完整的结构来辅助
它的描述。这个内部的结构只为外部类服务
,并且需要直接访问外部类的私有的数据等的时候,这样的情况就可以把这个内部的结构声明为内部类。
内部类大概有这么几种分类
- 静态修饰
【修饰符】 class 外部类 【extends 父类】 【implements 接口们】{
【其他修饰符】 static class 静态内部类 【extends 父类】 【implements 接口们】{
}
}
- 非静态修饰
【修饰符】 class 外部类 【extends 父类】 【implements 接口们】{
【其他修饰符】 class 非静态内部类 【extends 父类】 【implements 接口们】{
}
}
- 匿名的方法体内部类
- 非匿名的方法体内部类
【修饰符】 class 外部类 【extends 父类】 【implements 接口们】{
【修饰符】 返回值类型 方法名(【形参列表】){
【abstract或final或没有】 class 局部内部类 【extends 父类】 【implements 接口们】{
}
}
}
实际案例先搁置。
三、集合
首先看看菜鸟教程的集合框架图
我觉得涉及到的知识点大概如下
- 有哪些集合
- 集合之间的差别
- 线程安全/效率
- 算法
Collection
是一个根接口,没有直接实现
归类 | 方法签名 | 方法描述 |
添加 | add(E e) | 添加一个元素 |
添加 | addAll(Collection c) | 添加多个元素,把c集合的所有元素都添加到当前集合中,this = this ∪ c; |
删除 | clear() | 清空集合 |
删除 | remove(Object obj) | 删除一个元素,根据元素的equals()来判断是否是要被删除的元素,如果元素的类型没有重写equals方法,那么等价于==,如果重写了equals,那么就按照equals的规则来比较,一般比较内容。 |
删除 | removeAll(Collection c) | 删除多个元素,把当前集合中和c共同的元素删除,即this = this - this ∩ c; |
删除 | retainAll(Collection c) | 删除多个元素,在当前集合中保留和c的共同的元素,即this = this ∩ c; |
查 | int size() | 获取元素的个数 |
查 | boolean contains(Object obj) | 是否包含某个元素。根据元素的equals()来判断是否是要被删除的元素,如果元素的类型没有重写equals方法,那么等价于==,如果重写了equals,那么就按照equals的规则来比较,一般比较内容。 |
查 | boolean containsAll(Collection c) | 是否包含多个元素。判断当前集合中是否包含c集合的所有元素,即c是否是this的子集。 |
查 | boolean isEmpty() | 集合是否为空 |
遍历 | Object[] toArray() | 将集合中的元素用数组返回 |
遍历 | Iterator iterator() | 返回一个迭代器对象,专门用于遍历集合 |
List
动态数组,存储了一组可重复的数据,是一个有序的Collection,通过下标去访问逐个对象
有两个出名的实现类
- ArrayList
- LinkedList
什么情况下用哪个呢?
- 以下情况使用
ArrayList
:
频繁访问列表中的某一个元素。
只需要在列表末尾进行添加和删除元素操作。 - 以下情况使用
LinkedList
:
你需要通过循环迭代来访问列表中的某些元素。
需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
Set
不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败
常用的实现类
- HashSet
- TreeSet
- LinkedHashSet
Map
就,键值对集合,有 key 和 value 。不能包含重复的键,值可以重复
常用的实现类
- HashMap
- LinkedHashMap
- TreeMap
- Hashtable(线程安全)
Iterator
迭代器
接口方法 | |
boolean hasNext() | 判断下一跳是否有值 |
E next() | 指针往下一跳挪动 |
void remove() | 删除元素 |
default void forEachRemaining(Consumer<? super E> action) | 实现了这个接口才能使用foreach遍历,Map系列就没有实现 |
ListIterator
List系列的集合内部有提供listIterator()方法可以获取ListIterator对象
接口方法 | |
boolean hasNext() | 判断下一跳是否有值 |
E next() | 指针往下一跳挪动 |
void remove() | 删除刚迭代的元素 |
boolean hasPrevious() | 如果以逆向遍历列表,往前是否还有元素 |
E previous() | 返回列表中的前一个元素 |
void add(E e) | 添加元素到对应集合 |
void set(E e) | 替换正迭代的元素 |
int nextIndex() | 返回列表中的下一个元素的索引 |
int previousIndex() | 返回列表中的前一个元素的索引 |
Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类
工具类方法 | |
boolean addAll(Collection<? super T> c,T… elements) | 将所有指定元素添加到指定 collection 中 |
int binarySearch(List<? extends Comparable<? super T>> list,T key) | 在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定 |
int binarySearch(List<? extends T> list,T key,Comparator<? super T> c) | 在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且集合也事先必须是按照c比较器规则进行排序过的,否则结果不确定 |
<T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) | 在coll集合中找出最大的元素,集合中的对象必须是T或T的子类对象,而且支持自然排序 |
T max(Collection<? extends T> coll,Comparator<? super T> comp) | 在coll集合中找出最大的元素,集合中的对象必须是T或T的子类对象,按照比较器comp找出最大者 |
void reverse(List<?> list) | 反转指定列表List中元素的顺序 |
void shuffle(List<?> list) | List 集合元素进行随机排序,类似洗牌 |
<T extends Comparable<? super T>> void sort(List list) | 根据元素的自然顺序对指定 List 集合元素按升序排序 |
void sort(List list,Comparator<? super T> c) | 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 |
void swap(List<?> list,int i,int j) | 将指定 list 集合中的 i 处元素和 j 处元素进行交换 |
int frequency(Collection<?> c,Object o) | 返回指定集合中指定元素的出现次数 |
void copy(List<? super T> dest,List<? extends T> src) | 将src中的内容复制到dest中 |
boolean replaceAll(List list,T oldVal,T newVal) | 使用新值替换 List 对象的所有旧值 |
许多synchronizedXxx() 方法方法 | 该方法可使将指定集合包装成线程同步的集合 |
许多unmodifiableXxx()方法 | 该方法返回指定 Xxx的不可修改的视图 |
四、Jdk1.8新特性
它学与不学,不会影响后面你的编码工作,但是它会让你的code变得更加简洁
4.1 Lambda表达式与函数式接口
lambda表达式其实就是实现SAM接口的语法糖,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法
1、消费型接口
这类接口的抽象方法特点:有形参,但是返回值类型是void
接口名 | 抽象方法 | 描述 |
Consumer | void accept(T t) | 接收一个对象用于完成功能 |
BiConsumer<T,U> | void accept(T t, U u) | 接收两个对象用于完成功能 |
DoubleConsumer | void accept(double value) | 接收一个double值 |
IntConsumer | void accept(int value) | 接收一个int值 |
LongConsumer | void accept(long value) | 接收一个long值 |
ObjDoubleConsumer | void accept(T t, double value) | 接收一个对象和一个double值 |
ObjIntConsumer | void accept(T t, int value) | 接收一个对象和一个int值 |
ObjLongConsumer | void accept(T t, long value) | 接收一个对象和一个long值 |
2、供给型接口
这类接口的抽象方法特点:无参,但是无返回值
接口名 | 抽象方法 | 描述 |
Supplier | T get() | 返回一个对象 |
BooleanSupplier | boolean getAsBoolean() | 返回一个boolean值 |
DoubleSupplier | double getAsDouble() | 返回一个double值 |
IntSupplier | int getAsInt() | 返回一个int值 |
LongSupplier | long getAsLong() | 返回一个long值 |
3、判断型接口
这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。
接口名 | 抽象方法 | 描述 |
Predicate | boolean test(T t) | 接收一个对象 |
BiPredicate<T,U> | boolean test(T t, U u) | 接收两个对象 |
DoublePredicate | boolean test(double value) | 接收一个double值 |
IntPredicate | boolean test(int value) | 接收一个int值 |
LongPredicate | boolean test(long value) | 接收一个long值 |
4、功能型接口
这类接口的抽象方法特点:既有参数又有返回值
接口名 | 抽象方法 | 描述 |
Function<T,R> | R apply(T t) | 接收一个T类型对象,返回一个R类型对象结果 |
UnaryOperator | T apply(T t) | 接收一个T类型对象,返回一个T类型对象结果 |
DoubleFunction | R apply(double value) | 接收一个double值,返回一个R类型对象 |
IntFunction | R apply(int value) | 接收一个int值,返回一个R类型对象 |
LongFunction | R apply(long value) | 接收一个long值,返回一个R类型对象 |
ToDoubleFunction | double applyAsDouble(T value) | 接收一个T类型对象,返回一个double |
ToIntFunction | int applyAsInt(T value) | 接收一个T类型对象,返回一个int |
ToLongFunction | long applyAsLong(T value) | 接收一个T类型对象,返回一个long |
DoubleToIntFunction | int applyAsInt(double value) | 接收一个double值,返回一个int结果 |
DoubleToLongFunction | long applyAsLong(double value) | 接收一个double值,返回一个long结果 |
IntToDoubleFunction | double applyAsDouble(int value) | 接收一个int值,返回一个double结果 |
IntToLongFunction | long applyAsLong(int value) | 接收一个int值,返回一个long结果 |
LongToDoubleFunction | double applyAsDouble(long value) | 接收一个long值,返回一个double结果 |
LongToIntFunction | int applyAsInt(long value) | 接收一个long值,返回一个int结果 |
DoubleUnaryOperator | double applyAsDouble(double operand) | 接收一个double值,返回一个double |
IntUnaryOperator | int applyAsInt(int operand) | 接收一个int值,返回一个int结果 |
LongUnaryOperator | long applyAsLong(long operand) | 接收一个long值,返回一个long结果 |
BiFunction<T,U,R> | R apply(T t, U u) | 接收一个T类型和一个U类型对象,返回一个R类型对象结果 |
BinaryOperator | T apply(T t, T u) | 接收两个T类型对象,返回一个T类型对象结果 |
ToDoubleBiFunction<T,U> | double applyAsDouble(T t, U u) | 接收一个T类型和一个U类型对象,返回一个double |
ToIntBiFunction<T,U> | int applyAsInt(T t, U u) | 接收一个T类型和一个U类型对象,返回一个int |
ToLongBiFunction<T,U> | long applyAsLong(T t, U u) | 接收一个T类型和一个U类型对象,返回一个long |
DoubleBinaryOperator | double applyAsDouble(double left, double right) | 接收两个double值,返回一个double结果 |
IntBinaryOperator | int applyAsInt(int left, int right) | 接收两个int值,返回一个int结果 |
LongBinaryOperator | long applyAsLong(long left, long right) | 接收两个long值,返回一个long结果 |
5、表达式语法
(形参列表) -> {Lambda体}
6、示范案例
public class DemoApplication {
public static void main(String[] args) {
// 1 消费型接口示例
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("s=" + s);
}
};
consumer.accept("消费型接口示例");
// 2 供给型接口示例
Supplier<String> stringSupplier = new Supplier<String>() {
@Override
public String get() {
return "供给型接口示例";
}
};
System.out.println(stringSupplier.get());
}
}
4.2 Stream流库
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream 的操作三个步骤
- 创建 Stream:通过一个数据源(如:集合、数组),获取一个流
- 中间操作:中间操作是个操作链,对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行
- 终止操作:一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream
注意
- Stream 自己不会存储元素
- Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。即所有的中间操作需要解释新的Stream,除非连写
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
创建Steam的四种方式
- 通过集合:Java8 中的 Collection 接口被扩展,提供了两个获取流的方法
- public default Stream stream():返回一个顺序流
- public default Stream parallelStream():返回一个并行流
- 通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流
- public static Stream stream(T[] array)
或通过重载形式获取处理对应的基本类型的数组
- public static IntStream stream(int[] array):返回一个整型数据流
- public static LongStream stream(long[] array):返回一个长整型数据流
- public static DoubleStream stream(double[] array):返回一个浮点型数据流
- 通过Steam静态方法
- public static Stream of(T… values):返回一个顺序流
- public static Stream iterate(final T seed, final UnaryOperator f):返回无限流
- public static Stream generate(Supplier s):返回无限流
中间操作
API方法说明如下
方 法 | 描 述 |
Stream filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 |
Stream distinct() | 筛选,通过流所生成元素的equals() 去除重复元素 |
Stream limit(long maxSize) | 截断流,使其元素不超过给定数量 |
Stream skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
Stream peek(Consumer action) | 接收Lambda,对流中的每个数据执行Lambda体操作 |
Stream sorted() | 产生一个新流,其中按自然顺序排序 |
Stream sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
Stream map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
Stream mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
Stream mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
Stream mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
Stream flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
终止操作
API方法说明如下
方法 | 描述 |
boolean allMatch(Predicate p) | 检查是否匹配所有元素 |
boolean anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
boolean noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
Optional findFirst() | 返回第一个元素 |
Optional findAny() | 返回当前流中的任意元素 |
long count() | 返回流中元素总数 |
Optional max(Comparator c) | 返回流中最大值 |
Optional min(Comparator c) | 返回流中最小值 |
void forEach(Consumer c) | 迭代 |
T reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
U reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
R collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
案例演示
public class DemoApplication {
public static void main(String[] args) {
/**
* 这里做了三步操作
* 1.使用静态方法创建了一个流
* 2.条件过滤
* 3.终止操作执行迭代
*/
Stream.of(1, 2, 3, 4).filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
if (integer >= 3) {
return false;
}
return true;
}
}).sorted().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("输出元素=" + integer);
}
});
}
}
4.3 Optional
用于检查空指针防止代码污染,让我们不用去显式的进行空指针的检查,而实际上Optional也是一个容器,先上代码演示
public class DemoApplication {
public static void main(String[] args) {
ArrayList<Integer> arrayList = null;
ArrayList<Integer> arrayListOther = new ArrayList<>();
arrayListOther.add(11);
arrayListOther.add(12);
/**
* 通过静态方法创建一个容器 执行非空判断
* 如果不为空则返回第一个集合
* 如果为空则返回第二个
* 注意:这里的空是指内存地址为空 而不是集合内不存在元素
*/
ArrayList<Integer> integerArrayList = Optional.ofNullable(arrayList).orElse(arrayListOther);
for (Integer integer : integerArrayList) {
System.out.println(integer);
}
}
}
接下来解释一下常用的API
1、创建Optional对象
类方法 | 描述 |
static Optional empty() | 用来创建一个空的Optional |
static Optional of(T value) | 用来创建一个非空的Optional |
static Optional ofNullable(T value) | **(常用)**用来创建一个可能是空,也可能非空的Optional |
2、获取Optional容器中取出所包装的对象
类方法 | 描述 |
T get() | 要求Optional容器必须非空,T get()与of(T value)使用是安全的 |
T orElse(T other | **(常用)**如果Optional容器中非空,就返回所包装值;如果为空,就用orElse(T other)other指定的默认值(other)代替,一般orElse(T other) 与ofNullable(T value)配合使用 |
T orElseGet(Supplier<? extends T> other) | 如果Optional容器中非空,就返回所包装值;如果为空,就用Supplier接口的Lambda表达式提供的值代替 |
T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果Optional容器中非空,就返回所包装值;如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException |
3、其他方法
类方法 | 描述 |
boolean isPresent() | 判断Optional容器中的值是否存在 |
void ifPresent(Consumer<? super T> consumer) | 判断Optional容器中的值是否存在,如果存在,就对它进行Consumer指定的操作,如果不存在就不做 |
Optiona map(Function<? super T,? extends U> mapper) | 判断Optional容器中的值是否存在,如果存在,就对它进行Function接口指定的操作,如果不存在就不做 |
你学废了吗