文章目录

  • 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值
  1. 如果一个对象的参与计算hashCode值的成员变量没有修改,那么在程序运行期间,每次获取的hashCode值不变
  2. 如果两个对象hashCode不同,那么这两个一定不“相等”
  3. 如果两个的hashCode值相同,那么这两个对象不一定“相等”
  4. 如果两个相等,那么他们的hashCode值一定要相同
  • equals()
    用于判断当前对象与指定对象是否相等,默认的实现,等价于“==”,比较对象的内存地址
  • clone()
    一个类实现了Cloneable接口,以向Object.clone()方法指示该方法制作该类实例的字段对字段副本是合法的

2.String

它有如下特点

  • String类是final修饰的,不能被继承
  • String类的底层使用数组存储
  • String类的对象不可变(没有提供修改字符串中某个字符的方法
  1. 字符串常量池中存储字符串常量,可以共享
  2. 每次修改都会产生新对象,频繁修改的话效率不高

常用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:实现任意精度的浮点数运算

java的核心内容 java核心知识点总结_java

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的对象?

  1. 创建子类对象:GregorianCalendar
Calendar calendar = new GregorianCalendar();
  1. 获取指定时区的日历对象
  • getInstance()
  • getInstance(TimeZone 时区) 或 getInstance(Locale 语言环境)
  • getInstance(TimeZone, Locale)
  1. 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.反射

用几个用处

  1. 运行时获取类信息、分析类能力
  2. 运行时检查实例对象,通过java.lang.reflect中的三个类Field、Method、Constructor
  3. 实现泛型

关于类加载的机制,看JVM篇

java.lang.Class类可以访问Java运行时系统为所有对象维护的一个运行时的类型标识,我觉得也可以叫他是字节码对象

获取Class对象的四种方式
  1. 类型名.class
  2. 对象.getClass()
  3. Class.forName(类型全名称)
  4. 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的区别
  • 相同点
    都是针对类加载过程
  • 不同点
  1. Class.forName()
    除了将类的class字节码文件加载到JVM之外,还会对类进行解释,执行类中的静态块。(注意这里的静态块指的是在类初始化时的一些数据,但是ClassLoader却没有)
    而实际上,Class.forName()中也有调用了ClassLoader来实现,其内部实际调用的方法为
    Class.forName(className, true, classloader)
  • className:加载的类名
  • true:指Class被加载后是不是必须被初始化, 不初始化就是不执行static的代码
  • ClassLoader.getClassLoader(caller):类加载器
  1. ClassLoader
    遵循双亲委派模型最终调用启动类加载器的类加载器,内部实际调用的方法是 ClassLoader.loadClass(className, false)。
  • 总结
    class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块,当然还可以指定是否执行静态块。
    classLoader将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
类加载器

大致分为如下几种类型

  1. 引导类加载器(Bootstrap Classloader)又称为根类加载器

它负责加载jre/rt.jar核心库,它本身不是Java代码实现的,也不是ClassLoader的子类,获取它的对象时往往返回null

  1. 扩展类加载器(Extension ClassLoader)

它负责加载jre/lib/ext扩展库,它是ClassLoader的子类

  1. 应用程序类加载器(Application Classloader)

它负责加载项目的classpath路径下的类,它是ClassLoader的子类

  1. 自定义类加载器

当你的程序需要加载“特定”目录下的类,可以自定义类加载器;当你的程序的字节码文件需要加密时,那么往往会提供一个自定义类加载器对其进行解码,常见的自定义类加载器案例:Tomcat

反射的应用

由于方法太多,这里列举核心的几个玩法

  • Field
    功能
  1. 用于获取当前对象的成员变量的类型
  2. 用于对成员变量重新设值

获取Field对象的方法

  1. Class.getFields():获取类中public类型的属性,返回一个包含 Field 对象的数组;
    该数组包含此 Class 对象所表示的类或接口的所有可访问公共字段。
  2. getDeclaredFields():获取类中所有的属性(public、protected、default、private);
    但不包括继承的属性,返回Field对象的一个数组。
  3. getField(String name):获取类特定的方法,name参数指定了属性的名称;
    不能是方法名,而且必须public属性。
  4. getDeclaredField(String name): 获取类特定的方法,name参数指定了属性的名称。

常用方法

  1. 获取变量的类型
  • Field.getType():返回这个变量的类型
  • Field.getGenericType():如果当前属性有签名属性类型就返回
    否则就返回Field.getType()
  • isEnumConstant() : 判断这个属性是否是枚举类
  1. 获取成员变量的修饰符
  • Field.getModifiers():以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符
  1. 获取和修改成员变量的值
  • 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);

总结以下上面的应用,以后再对反射专门写一篇文章

  1. 获取类型的详细信息
  2. 创建任意引用类型的对象
  3. 操作任意类型的属性
  4. 调用任意类型的方法
  5. 获取泛型父类信息
  6. 读取注解信息

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.异常

在我的另外一篇文章有统一异常返回的处理类

java的核心内容 java核心知识点总结_java_02

4.日志

相信平时大家在调试bug的时候,无非就是两个玩法:Debug+Sout输出,如果采用输出的方式,回头还要把代码删掉,特别的麻烦,所以日志的API就是为了解决这个问题而存在。

常见的日志框架有如下

java的核心内容 java核心知识点总结_java的核心内容_03

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 接口们】{
		}
    }
}

实际案例先搁置。

三、集合

首先看看菜鸟教程的集合框架图


我觉得涉及到的知识点大概如下

  1. 有哪些集合
  2. 集合之间的差别
  3. 线程安全/效率
  4. 算法

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 的操作三个步骤
  1. 创建 Stream:通过一个数据源(如:集合、数组),获取一个流
  2. 中间操作:中间操作是个操作链,对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行
  3. 终止操作:一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream

注意

  • Stream 自己不会存储元素
  • Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。即所有的中间操作需要解释新的Stream,除非连写
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
创建Steam的四种方式
  1. 通过集合:Java8 中的 Collection 接口被扩展,提供了两个获取流的方法
  • public default Stream stream():返回一个顺序流
  • public default Stream parallelStream():返回一个并行流
  1. 通过数组
    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):返回一个浮点型数据流
  1. 通过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接口指定的操作,如果不存在就不做

你学废了吗