1.多线程的创建
方式一:继承于Thread类
①创建一个继承于Thread类的子类
②重写Thread类的run() - - >将此线程执行的操作声明在run()中
③创建Thread类的子类的对象
④通过此对象调用start() //启动当前线程 调用当前线程的run()
问题
①不能通过直接调用run()的方式启动线程
②不可以还让已经start()的线程再去执行start(),会报错
方式二:实现Runnable接口
①创建一个实现了Runnable接口的类
②实现类去实现Runnable中的抽象方法:run()
③创建实现类的对象
④将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
⑤通过Thread类的对象调用start()
比较方式一和方式二:开发中优先选择Runnable接口的方式
①实现的方式没有类的单继承性的局限性
②实现的方式更适合来处理多个线程有共享数据的情况
联系:Thread类本身也实现了Runnable接口,两种方式都需要重写run()
方式三:实现Callable接口 - - - JDK5.0新增
①创建一个实现Callable的实现类
②实现call方法,将此线程需要执行的操作声明在call()中
③创建Callable接口实现类的对象
④将此Callable接口实现类的对象传递到FutureTask构造器当中,创建FutureTask的对象
⑤将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
⑥获取Callable中call方法的返回值
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大
1)call()可以有返回值
2)call()可以抛出异常,被外面的操作捕获,获取异常的信息
3)Callable是支持泛型的
创建线程的方式四:使用线程池
①提供指定线程数量的线程池
②执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
③关闭线程池
好处:
1)提高响应速度(减少了创建新线程的时间)
2)降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
3)便于线程管理
2.Thread类中的常用方法
①start():启动当前线程;调用当前线程的run()
②run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
③currentThread():静态方法,返回执行当前代码的线程
④getName():获取当前线程的名字
⑤setName():设置当前线程的名字
⑥yield():释放当前CPU的执行权
⑦join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行以后,线程a才结束阻塞状态
⑧sleep(long millitime):让当前线程”睡眠“指定的millitime毫秒,在指定的millitime毫秒时间内,当前线程是阻塞状态
⑨isAlive():判断当前线程是否存活
⑩stop():已过时。当执行此方法时,强制结束当前线程
3.线程的优先级
①MAX_PRIORITY: 10 MIN_PRIORITY: 1 NORM_PRIORITY: 5
②getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程执行完以后,低优先级的进程才执行。
4.Java中通过同步机制来解决线程的安全问题
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:
①操作共享数据的代码,即为需要被同步的代码
②共享数据:多个线程共同操作的变量
③同步监视器:俗称锁。任何类的对象都可以充当锁
要求:多个线程必须要共用同一把锁
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
方式二:同步方法
①如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的
②同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
③非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
方式三:Lock锁 - - - JDK5.0新增
①实例化ReentrantLock
②调用锁定方法lock()
③调用解锁方法unlock()
5.线程通信涉及到的三个方法
①wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
②notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的
③notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
说明:
1)三个方法必须使用在同步代码块或同步方法中
2)三个方法的调用者必须是同步代码块或同步方法中的同步监视器
3)三个方法是定义在java.lang.Object类中
6.sleep()和wait()的异同
1)相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
2)不同点:
①两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
②调用的要求不同:sleep()可以在任何需要的场景下调用;wait()必须使用在同步代码块或同步方法中
③关于是否释放同步监视器:若两个方法都使用在同步代码块或同步方法中,sleep()不会释放同步监视器,wait()会释放同步监视器
7.synchronized与lock的异同
1)相同点:二者都可以解决线程安全问题
2)不同点:
①synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
②lock需要手动的启动同步,同时结束同步也需要手动的实现
8.String的使用
1)String声明为final的,不可被继承
2)String实现了Serializable接口:表示字符串是支持序列化的
实现了Comparable接口:表示String可比较大小
3)String内部定义了final char[] value用于存储字符串数据
4)String:代表不可变的字符序列。简称:不可变性
体现:
①当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
②当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
③当调用String的replace()方法修改指定字符或字符串时,不能使用原有的value进行赋值
5)通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
6)字符串常量池中是不会存储相同内容的字符串的
7)String的实例化方式
方式一:通过字面量定义的方式
方式二:通过new + 构造器的方式
8)String不同拼接操作的对比
①常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量
②只要其中有一个是变量,结果就在堆中
③如果拼接的结果调用intern()方法,返回值就在常量池中
9)String与char[]之间的转换
①String - - > char[] :调用String的toCharArray()
String str1 = "abc123";
char[] charArray = str1.toCharArray();
②char[] - - > String :调用String的构造器
char[] arr = new char[]{'h','e','l','l','o'};
String str2 = new String(arr);
10)String与byte[]之间的转换
①String - - > byte[] :调用String的getBytes()
String str1 = "abc123";
byte[] bytes = str1.getBytes();
②byte[] - - > String :调用String的构造器
String str2 = new String(bytes);
9.String常用方法
1)int length():返回字符串的长度
2)char charAt(int index):返回某索引处的字符
3)boolean isEmpty():判断是否是空字符串
4)String toLowerCase():使用默认语言环境,将String中的所有字符转换为小写
5)String toUpperCase():使用默认语言环境,将String中的所有字符转换为大写
6)String trim():返回字符串的副本,忽略前导空白和尾部空白
7)boolean equals(Object obj):比较字符串的内容是否相同
8)boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
9)String concat(String str):将指定字符串连接到此字符串的结尾,等价于用“+”
10)int CompareTo(String anotherString):比较两个字符串的大小
11)String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串
12)String substring(int beginIndex,int endIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到endIndex(不包含)的一个子字符串
13)boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
14)boolean startsWith(String suffix):测试此字符串是否以指定的前缀开始
15)boolean startsWith(String suffix,int toffset):测试此字符串从指定索引开始的子字符串是否以指定的前缀开始
16)boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true
17)int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
18)int indexOf(String str,int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定索引开始
19)int lastindexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
20)int lastindexOf(String str,int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定索引开始反向搜索
21)String replace(char oldChar,char newChar):返回一个新的字符串,它是通过newChar替换此字符串中出现的所有oldChar得到的
10.String、StringBuffer、StringBuilder 三者的异同
相同点:底层使用char[]存储
不同点:
1)String:不可变的字符序列;
2)StringBuffer:可变的字符序列;线程安全的,效率低;
3)StringBuilder:可变的字符序列;JDK5.0新增,线程不安全的,效率高;
效率排列: StringBuilder > StringBuffer > String
11.Java中两个Date类的使用
①java.util.Date ②java.sql.Date
1)两个构造器的使用
构造器一:Date():创建一个对应当前时间的Date对象
构造器二:创建指定毫秒数的Date对象
2)两个方法的使用
toString():显式当前的年、月、日、时、分、秒
getTime():获取当前Date对象对应的毫秒数。时间戳
12.SimpleDateFormat的使用
1)SimpleDateFormat的实例化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
2)两个操作:调用带参的构造器
①格式化:日期 - - - > 字符串
String format = sdf.format(date);
System.out.println(format);
②解析:格式化的逆过程,字符串 - - - > 日期
Date date = sdf.parse("2020-02-18 11:48:27");
System.out.println(date);
3)练习
@Test //字符串"2020-09-08"转化为java.sql.Date
public void SimpleDateFormat() throws ParseException {
String birth = "2020-09-03";
java.text.SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(birth);
System.out.println(date);//Thu Sep 03 00:00:00 CST 2020
java.sql.Date birthDate = new java.sql.Date(date.getTime());
System.out.println(birthDate); //2020-09-03
}
13.Calendar日历类(抽象类)的使用
1)实例化
方式一:创建其子类(GregorianCalendar)的对象
方式二:调用其静态方法getInstance()
Calendar calendar = Calerdar.getInstance();
2)常用方法
①get()
int days = calendar.get(Calendar.DAY_OF_MONTH);//是月的第几天
System.out.println(days);
②set()
calendar.set(Calendar.DAY_OF_MONTH,22);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); //22
③add()
calendar.add(Calendar.DAY_OF_MONTH,3);//加3天 可以加-3天
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); //25
④getTime():日历类 - - -> Date
Date date = calendar.getTime();
System.out.println(days);
⑤setTime():Date - - - > 日历类
Date date1 = new Date();//当前时间
calendar.setTime(date1);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
14.localDate、localTime、loaclDateTime
1)now():获取当前的日期、时间 + 时间
LocalDate localDate = localDate.now(); //2022-04-21
LocalTime localTime = localTime.now(); //16:37:43.535
LocalDateTime localDateTime = localDateTime.now();//2022-04-21T16:37:43.535
2)of():设置指定的年月日时分秒,没有偏移量
LocalDateTime localDateTime1 = localDateTime.of(2022,4,21);
3)getXxx() //不可变性
getDayOfMonth()
getDayOfWeek()
getMonth() //Thursday
getMonthValue() //4
getMinute()
15. Java比较器
1)说明:Java中的对象,正常情况下,只能进行比较: == 或 != 。不能使用 > 或 < 的,但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小,就需要用到两个接口中的一个:Comparable或Comparator
2)Comparable接口的使用 自然排序
①像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式
②像String、包装类重写compareTo()方法以后,进行了从小到大的排列
③重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数
如果当前对象this小于形参对象obj,则返回负整数
如果当前对象this等于形参对象obj,则返回零
④对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法,在compareTo(obj)方法中指明如何排序
3)Comparator接口的使用 定制排序
①背景:当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用Comparator的对象来排序
②重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2
如果返回0,表示相等
如果返回负整数,表示o1小于o2
4)两种接口使用的对比
Comparable接口方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小
Comparator接口属于临时性的比较
16.枚举类
1)枚举类的使用
①枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类
②当需要定义一组常量时,强烈建议使用枚举类
③如果枚举类中只有一个对象,则可以作为单例模式的实现方式
2)如何定义枚举类
方式一:jdk5.0之前,自定义枚举类
方式二:jdk5.0,可以使用enum关键字定义枚举类
3)Enum类中的常用方法
①value():返回枚举类型的对象数组,该方法可以很方便地遍历所有的枚举值
②valueOf(String objName):返回枚举类中对象名是objName的对象
③toString():返回当前枚举类对象常量的名称
4)使用enum关键字定义的枚举类实现接口的情况
情况一:实现接口,在enum类中实现抽象方法
情况二:让枚举类的对象分别实现接口中的抽象方法
17.注解(Annotation)
1)框架 = 注解 + 反射 + 设计模式
2)理解Annotation
①jdk5.0新增的功能
②Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并且程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息
3)Annotation的使用示例
示例一:生成文档相关的注解
示例二:在编译时进行格式检查(JDK内置的三个基本注解)
@Override:限定重写父类方法,该注解只能用于方法
@Deprecated:用于表示所修饰的元素(类,方法等)已过时
@SuppressWarnings:抑制编译器警告
示例三:跟踪代码依赖性,实现替代配置文件功能
4)如何自定义注解
①注解声明为@interface
②内部定义成员,通常用value表示
③可以指定成员的默认值,使用default定义
④如果自定义注解没有成员,表明是一个标识作用
如果注解有成员,在使用注释时,需要指明成员的值
自定义注解必须配上注解的信息处理流程(使用反射)才有意义
5)jdk提供的4种元注解
元注解:对现有的注解进行解释说明的注解
①Retention:指定所修饰的Annotation的生命周期:SOURCE\CLASS(默认行为)\RUNTIME
只有声明为RUNTIME生命周期的注解,才能通过反射获取
②Target:用于指定被修饰的Annotation,能用于修饰哪些程序元素
③Documented:表示所修饰的注解在被javadoc解析时,保留下来
④Inherited:被它修饰的Annotation将具有继承性
6)通过反射获取注解信息
7)jdk8 中注解的新特性
①可重复注解
②类型注解
18.集合框架的概述
1)集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)
2)数组在存储多个数据方面的缺点:
①一旦初始化以后,其长度就确定了 ,不可更改
②数组一旦定义好,其元素的类型也就确定了,我们也就只能操作指定类型的数据了
③数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便
④获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
⑤数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足
3)集合框架
①Collection接口:单列集合,用来存储一个一个的对象
List接口:存储有序的、可重复的数据 - - > “动态”数组
ArrayList、Linkedlist、Vector、
Set接口:存储无序的、不可重复的数据 - - >高中讲的“集合”
HashSet、LinkedHashSet、TreeSet
②Map接口:双列集合,用来存储一对(key - value)一对的数据 - - >函数:y = f(x)
HashMap、LinkedHashMap、TreeMap、Hashtable、properties
4)Collection接口的常用方法
public void Test1(){
Collection coll = new ArrayList();
//add(Object e):将元素e添加到集合coll中
coll.add("AA");
coll.add("BB");
coll.add(123); //自动装箱
coll.add(new Date());
System.out.println(coll.size()); //4
Collection coll1 = new ArrayList();
coll1.add("BB");
coll1.add("CC");
coll.addAll(coll1); //将coll1集合中的元素添加到当前的集合中
System.out.println(coll.size()); //6
System.out.println(coll);//[AA, BB, 123, Sat Apr 23 11:57:54 CST 2022, BB, CC]
//isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty());//false
coll1.clear(); //clear():清空集合元素
System.out.println(coll1.isEmpty());//true
//contains(Object obj):判断当前集合中是否包含obj
boolean contains = coll.contains("AA");
System.out.println(contains);
//containsAll(Collection coll1):判断coll1中的所有元素是否都存在于当前集合中
System.out.println(coll.containsAll(coll1)); //true
//remove(Object obj):从当前集合中移除obj元素
coll.remove("AA");
System.out.println(coll);
//rmoveAll(Collection coll1):从当前集合中移除coll1中所有的元素(差集)
coll.retainAll(coll1);//retainAll(Collection coll1):获取当前集合和coll集合的交集,并返回给当前集合
//equals(Object obj):要想返回true,需要当前集合和形参集合的元素相同
//hashCode():返回当前对象的哈希值
//toArray():集合 - - >数组:toArray
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//拓展:数组 - - >集合:调用Arrays类的静态方法asList()
List<String> list = Arrays.asList(new String[]{"AA","BB","CC"});
System.out.println(list);
//iterator():返回Iterator接口的实例,用于遍历集合元素,放在IteratorTest.java中测试
}
5)集合元素的遍历操作,使用迭代器Iterator接口
内部的方法:
①hasNext():判断是否还有下一个元素
②next():指针下移,将下移以后集合位置上的元素返回
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
内部定义了remove(),可以在遍历的时候,删除集合中的元素,此方法不用于集合直接调用remove()
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
19.ArrayList、LinkedList、Vector三者的异同?
相同点:
三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
不同点:
ArrayList:作为List接口的主要实现类:线程不安全的,效率低;底层使用Object[] elementData存储
LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
Vector:作为List接口的古老实现类:线程安全的,效率低;底层使用Object[] elementData存储
20.List中的常用方法
1)增:add(Object obj)
2)删:remove(int index) / remove(Object obj)
3)改:set(int index,Object ele)
4)查:get(int index)
5)插:add(int index,Object ele)
6)长度:size()
7)遍历:①Iterator迭代器方式
②增强for循环
③普通的循环
21.Set接口
1)实现类的对比
①HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
②LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
③TreeSet:可以按照添加对象的指定属性,进行排序
2)Set存储无序的、不可重复的数据(以HashSet为例说明)
①无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定
②不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个
3)添加元素的过程(以HashSet为例)
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。如果hash值相同,进而需要调用元素a所在类的equals()方法:equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功
HashSet底层:数组 + 链表的结构
4)向Set中添加的数据,其所在的类一定要重写hashCode()和equals
且重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
5)LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
对于频繁的遍历操作,LinkedHashSet效率高于HashSet
6)TreeSet
①向TreeSet中添加的数据,要求是相同类的对象
②两种排序方式:自然排序(Comparable)和定制排序(Comparator)
③自然排序中比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
④定制排序中比较两个对象是否相同的标准为:compare()返回0,不再是equals()
22.Map接口
1)实现类的对比
①HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
②LinkedHashMap:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,保证遍历map元素时,可以按照添加的顺序实现遍历;对于频繁的遍历操作,此类执行效率高于HashMap
③TreeMap:保证按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序;底层使用红黑树
④Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
⑤Properties:常用来处理配置文件。key和value都是String类型
2)Map结构的理解
Map中的key:无序的、不可重复的,使用Set存储所有的key
Map中的value:无序的、可重复的,使用Collection存储所有的value
一个键值对:key-value构成了一个Entry对象
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
3)HashMap的底层实现原理
以jdk7为例说明:
HashMap map = new HashMap();
在实例化以后,底层创建了长度是16的一维数组Entry[] table
. . . 可能已经执行过多次put . . .
map.put(key1,value1);
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
如果此位置上的数据为空,此时的key1-value1添加成功 - - - - 情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功 - - - - 情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
如果equals()返回false:此时key1-value1添加成功 - - - - 情况3
如果equals()返回true:使用value1替换相同key的value值
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储
在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来的2倍,并将原有的数据复制过来
jdk8相较于jdk7在底层实现方面的不同
①new HashMap():底层没有创建一个长度为16的数组
②jdk8底层的数组是:Node[],而非Entry[]
③首次调用put()方法时,底层创建长度为16的数组
④jdk7底层结构只有:数组+链表,jdk8中底层结构:数组+链表+红黑树
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8且当前数组的长度 >64时,此时索引位置上的所有数据元素改为使用红黑树存储
23.Map中的常用方法
1)Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
2)void putAll(Map m):将m中的所有key-value对存放到当前map中
3)Object remove(Object key):移除指定key的key-value对,并返回value
4)void clear():清空当前map中的所有数据
5)Object get(Object key):获取指定key对应的value
6)boolean containsKey(Object key):是否包含指定的key
7)boolean containsValue(Object value):是否包含指定的value
8)int size():返回map中key-value对的个数
9)boolean isEmpty():判断当前map是否为空
10)boolean equals(Object obj):判断当前map和参数对象obj是否相等
11)遍历所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
12)遍历所有的value集:values()
Collection values = map.value();
for(Object boj:values){
System.out.println(obj);
}
13)遍历所有的key-value
方式一:entrySet()
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while(iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)obj;
System‘.out.println(entry.getKey() + "----->" + entry.getValue());
}
方式二:
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "=====" + value);
}
24.泛型的使用
1)jdk5.0新增的特性
2)在集合中使用泛型:
①集合接口或集合类在jdk5.0时都修改为带泛型的结构
②在实例化集合类时,可以指明具体的泛型类型
③指明完以后,在集合类或接口中凡是定义类或接口时,内部结构使用到类的泛型的位置,都指定为实例化的泛型类型。比如:add(E e) - - - >实例化以后add(Integer e)
④注意点:泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,拿包装类替换
⑤如果实例化时,没有指明泛型的类型,默认类型为java.lang.Object类型
⑥Map<String,Integer> map = new HashMap<>(); jdk7新特性:类型推断
3)泛型在继承方面的体现
类A是类B的父类,G< A >和G< B >二者不具备子父类关系,二者是并列关系
补充:类A是类B的父类,A< G >是B< G >的父类
4)通配符的使用
通配符: ?
类A是类B的父类,G< A >和G< B >是没有关系的,二者共同的父类是:G< ? >
有限制条件的通配符的使用
? extends A:
G<? extends A> 可以作为G< A >和G< B >的父类的,其中B是A的子类
? super A:
G<? super A> 可以作为G< A >和G< B >的父类,其中B是A的子类
25.File类的使用
1)File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
2)File类声明在java.io包下
3)如何创建File类的实例
File file1 = new File(String filePath);
File file2 = new File(String parentPath,String childPath);
File file3 = new File(File parentFile,String childPath);
4)路径分隔符
windows: \ \
unix:/
5)File类的常用方法
①public String getAbsolute():获取绝对路径
②public String getPath():获取路径
③public String getName():获取名称
④public String getParent():获取上层文件目录路径。若无,返回null
⑤public long length():获取文件长度(即:字节数)。不能获取目录的长度
⑥public long lastModified():获取最后一次的修改时间,毫秒值
⑦public String[] list():获取指定目录下的所有文件或者文件目录的名称数组
⑧public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组
⑨public boolean renameTo(File dest):把文件重命名为指定的文件路径
例:file1.renameTo(file2) 要想保证返回true,需要file1在硬盘中存在且file2不存在
File类的判断功能
①public boolean isDirectory():判断是否是文件目录
②public boolean isFile():判断是否是文件
③public boolean exists():判断是否存在
④public boolean canRead():判断是否可读
⑤public boolean canWrite():判断是否可写
⑥public boolean isHidden():判断是否隐藏
创建硬盘中对应的文件或文件目录
①public boolean createNewFile():创建文件,若文件存在,则不创建,返回false
②public boolean mkdir():创建文件目录,如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建
③public boolean mkdirs():创建文件目录,如果上层文件目录不存在,一并创建
6)File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作,如果需要读取或写入文件内容,必须使用IO流来完成。
7)后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的“终点”
26.流
1)流的分类
①操作数据单位:字节流、字符流
②数据的流向:输入流、输出流
③流的角色:节点流、处理流
处理流就是“套接”在已有的流的基础上
2)流的体系结构
抽象基类 节点流(或文件流) 缓冲流(处理流的一种)
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputSream BufferedOutputStream
Reader FileReader BufferedReader
Writer FileWriter BufferedWriter
3)实现文本文件的复制
public void TestFileReaderWriter() {
//实现文本文件的复制
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//2.创建输入流和输出流的对象
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符的个数
while((len = fr.read(cbuf)) != -1){
fw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流资源
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4)结论
①对于文本文件( .txt, .java, .c, .cpp),使用字符流处理
②对于非文本文件( .jpg, .mp3, .mp4, .avi ,.doc .ppt),使用字节流处理
5)缓冲流的作用
提高流的读取、写入的速度
提高读写速度的原因:内部提供了一个缓冲区
6)转换流
①作用:提供了在字节流和字符流之间的转换
②转换流:属于字符流
InputStreamReader:将一个字节的输入流转换为字符的输入流
OutputStreamWriter:将一个字符的输出流转换为字节的输出流
③解码:字节、字节数组 - - - >字符数组、字符串
编码:字符数组、字符串 - - - >字节、字节数组
④转换流实现文件的读入和写出
public void test1() throws IOException { //要用try - catch
//1.造文件、造流
File file1 = new File("dbcp.txt");
File file2 = new File("dbcp_gbk.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
//2.读写过程
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
osw.write(cbuf,0,len);
}
//3.关闭资源
isr.close();
osw.close();
}
⑤字符集
ASCII:美国标准信息交换码。用一个字节的7位可以表示
ISO8859-1:拉丁码表。欧洲码表。用一个字节的8位表示
GB2312:中国的中文编码表,最多两个字节编码所有字符
GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多两个字节编码
Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码,所有的文字都用两个字节来表示
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符
7)其他流的使用
①标准的输入、输出流
System.in :标准的输入流,默认从键盘输入
System.out :标准的输出流,默认从控制台输出
System类的 setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流
②打印流:提供了一系列重载的print()和println()
PrintStream
PrintWriter
③数据流:用于读取或写出基本数据类型的变量或字符串
DataInputStream:读取
DataOutputStream:写出
④对象流:用于存储和读取基本数据类型数据或对象的处理流
序列化:用ObjectOutputStream类保存基本数据类型或对象的机制
反序列化:用ObjectInputStream类读取基本数据类型或对象的机制
对象序列化机制:允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的Java对象
自定义类需要满足如下的要求,方可序列化:
需要实现接口:Serializable
当前类提供一个全局常量:serialVersionUID
除了当前类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的
补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
⑤随机存取文件流
RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutPut接口
RandomAccessFile既可以作为一个输入流,又可以作为一个输出流
如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建,如果写出到的文件存在,则会对原有文件进行覆盖。(默认情况下从头覆盖)
有一个seek()方法实现数据的插入效果
27.反射
1)Class类的理解
①类的加载过程:程序经过java.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例
②换句话说,Class的实例就对应着一个运行时类
③加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类
2)获取Class的实例的方式()
方式一:调用运行时类的属性: .class
Class clazz1 = Person.class;
System.out.println(clazz1);
方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName(“com.atguigu.java.Person”);
System.out.println(clazz3);
方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classloader = ReflectionTest.class.getClassLoader();
Class clazz4 = classloader.loadClass(“com.atguigu.java.Person”);
System.out.println(clazz4);
3)使用ClassLoader加载配置文件
此时的文件默认在当前module下:
读取配置文件的方式一:
Properties pros = new Properties();
FileInputStream fis = new FileInputStream(“jdbc1.properties”);
pros.load(fis);
读取配置文件的方式二:使用ClassLoader
配置文件默认识别为:当前module的src下
Properties pros = new Properties();
ClassLoader classloader = ClassLoaderTest.class.getClassLoader();
InputStream is = classloader.getResourceAsStream(“jdbc2.properties”);
pros.load(is);
String user = pros.getProperty(“user”);
String password = pros.getProperty(“password”);
System.out.println("user = " + user + “,password = ” + password);
4)通过反射创建对应运行时类的对象
Class< Person > = Person.class;
// newInstance():调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参的构造器
Person obj = clazz.newInstance();
System.out.println(obj);
要求:
①运行时类必须提供空参的构造器
②空参的构造器的访问权限得够。通常,设置为public
在javabean中要求提供一个public的空参构造器,原因:
①便于通过反射,创建运行时类的对象
②便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
5)获取当前运行时类的属性结构及其内部结构
// getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Class clazz = Person.class;
Field[] fields = clazz.getFields();
for(Field f:fields){
System.out.println(f);
}
// getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f:declaredFields){
System.out.println(f);
}
// 权限修饰符 数据类型 变量名
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f:declaredFields){
//1.权限修饰符
int modifier = f.getModifiers();
System.out.println(Modifiers.toString(modifier)+ ''\ t');
//2.数据类型
Class type = f.getType();
System.out.println(type.getName()+ "\ t");
//3.变量名
String fName = f.getName();
System.out.println(fName);
}
6)获取当前运行时类的方法结构
// 获取当前运行时类及其所有父类中声明为public权限的方法
Class clazz = Person.class;
Method[] methods = clazz.getMethods();
for(Method m:methods){
System.out.println(m);
}
// 获取当前运行时类中声明的所有方法(不包含父类中声明的方法)
Class clazz = Person.class;
Method[] declaredMethods = clazz.declaredMethods();
for(Method m:declaredMethods){
System.out.println(m);
}
/*
@ Xxxx
权限修饰符 返回值类型 方法名(参数类型1 形参名, . . .)throws XxxException{}
*/
Class clazz = Person.class;
Method[] declaredMethods = clazz.declaredMethods();
for(Method m:declaredMethods){
//1.获取方法声明的注解
Annotation[] annos m.getAnnotations();
for(Annotation a:annos){
System.out.println(a);
}
//2.权限修饰符
System.out.print(Modifier.toString(m.getModifiers())+ "\ t");
//3.返回值类型
System.out.print(m.getReturnType().getName()+"\ t");
//4. 方法名
System.out.print(m.getName());
System.print("( ");
//5.形参列表
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length == 0)){
for(int i = 0;i < parameterTypes.length;i++){
if(i == parameterTypes.length - 1){
System.out.print(parameterTypes[i].getName()+ " args" + i );
break;
}
System.out.print(parameterTypes[i].getName()+ " args" + i + ",");
}
}else
System.print(" ) ");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(exceptionTypes.length > 0){
System.out.print("throws");
for(int i = 0;i < exceptionTypes.length;i++){
if(i == exceptionTypes.length - 1){
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName()+ ",");
}
}
System.out.println();
}
7)获取构造器结构
//getConstructors():获取当前运行时类中声明为public的构造器
Class clazz = Person.class;
Constructor[] constructors = clazz.getConstructors();
for(Constructor c:constructors){
System.out.println(c);
}
//getDeclaredConstructors():获取当前运行时类中声明的所有构造器
Class clazz = Person.class;
Constructor[] declaredConstructors = clazz.getdeclaredConstructors();
for(Constructor c:declaredConstructors){
System.out.println(c);
}
8)
①获取运行时类的父类
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
②获取运行时类的带泛型的父类
Class clazz = Person.class;
Class genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
③获取运行时类的带泛型的父类的泛型
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType)genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments[0].getTypeName());
9)
①获取运行时类实现的接口
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class c:interfaces){
System.out.println(c);
}
②获取运行时类的父类实现的接口
Class clazz = Person.class;
Class[] interfaces = clazz.getSuperclass().getInterfaces();
for(Class c:interfaces){
System.out.println(c);
}
③获取当前运行时类所在的包
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
④获取运行时类声明的注解
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annos:annotations){
System.out.println(annos);
}
10)调用运行时类的指定结构
①调用运行时类中的指定属性
Class clazz = Person.class;
Person p = (Person)clazz.newInstance();//创建运行时类的对象
Field id = clazz.getDeclaredField("id");//获取指定的属性
id.setAccessible(true);//保证当前属性是可访问的
id.set(p,1001);
id.get(p);
②调用运行时类中的指定方法
①调用非静态方法
Class clazz = Person.class;
Person p = (Person)clazz.newInstance();
Method show = clazz.getDeclaredMethod("show",String.class);
show.setAccessible(true);
Object returnVal = show.invoke(p,"CHN");
System.out.println(returnVal);
②调用静态方法
Class clazz = Person.class;
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);
③调用运行时类中的指定构造器(不常用)
Class clazz = Person.class;
Constructor constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
11)反射的应用:动态代理举例
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
Class SuperMan implements{
@Override
public String getBelief(){
return "I belief I can fly!";
}
@Override
public void est(String food){
System.out.println("我喜欢吃" + food);
}
}
/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
*/
class ProxyFactory{
//调用此方法,返回一个代理类的对象,解决问题一
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动的调和如下的方法:invoke()
//将被代理类要执行的方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnVal = method.invoke(obj,args);
//上述方法的返回值就作为当前类中的invoke()的返回值
return returnVal;
}
}
public class ProxyTest{
public static void main(String[] args){
SuperMan superMan = new SuperMan();
//proxyInstance:代理类的对象
Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("四川麻辣烫");
}
}
28.Java8新特性
1)Lambda表达式
举例: (o1,o2) - > Integer.compare(o1,o2);
格式:
- > :Lambda操作符 或箭头操作符
- > 左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
- > 右边:Lambda体(其实就是重写的抽象方法的方法体)
Lambda表达式的使用:(分为6种情况)
①无参,无返回值
Runnable r1 = new Runnable(){
@Override
public void run(){
System.out.println("我爱北京天安门");
}
}
r1.run();
Runnable r2 = () - > {
System.out.println("我爱北京故宫");
};
r2.run();
②Lambda需要一个参数,但是没有返回值
Consumer < String > con = new Consumer< String >{
@Override
public void accept(String s){
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
Consumer < String > con = (String s) - > {
System.out.println(s);
};
con.accept("一个是听的人当真了,一个是说的人当真了");
③数据类型可以省略,因为可由编译器推断得出,称为"类型推断"
Consumer < String > con1 = (String s) - > {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
Consumer < String > con2 = (s) - > {
System.out.println(s);
};
con2.accept("一个是听的人当真了,一个是说的人当真了");
④Lambda若只需要一个参数时,参数的小括号可以省略
Consumer < String > con1 = (s) - > {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
Consumer < String > con2 = s - > {
System.out.println(s);
};
con2.accept("一个是听的人当真了,一个是说的人当真了");
⑤Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator < Integer > com1 = new Comparator< Integer >(){
@Override
public int compare(Integer o1,Integer o2){
return o1.compareTo(o2);
}
}
Comparator < Integer > com2 = (o1,o2) - > { return o1.compareTo(o2);};
⑥当Lambda体只有一条语句时,return与大括号若有,都可以省略
Comparator < Integer > com1 = (o1,o2) - > { return o1.compareTo(o2);};
Comparator < Integer > com1 = (o1,o2) - > o1.compareTo(o2);
Lambda表示式的本质:作为函数式接口的实例
2)函数式(Functional)接口
①如果一个接口中,只声明了一个抽象方法,就称为函数式接口
②可以在一个接口上使用@FunctionalInterface 注解,可以检测它是否是一个函数式接口
③以前用匿名实现类表示的现在都可以用Lambda表达式来写
④java内置的4大核心函数式接口
消费型接口 Consumer< T > void accept(T t)
供给型接口 Supplier< T > T get()
函数型接口 Function< T,R > R apply(T t)
断定型接口 Predicate< T > boolean test(T t)
例:
@Test
public void test{
List< String > list = Arrays.asList("北京","南京","天津","东京","西京","普京");
List< String > filterStrs = filterString(list,new Predicate< String >(){
@Override
public boolean test(String s){
return s.contains("京");
}
});
System.out.println(filterStrs);
List< String > filterStrs1 = filterString(list,s - > s.contains("京"));
System.out.println(filterStrs1);
}
public List< String > filterString(List< String > list,Predicate< String > pre){
ArrayList< String > filterList = new ArrayList< >();
for(String s:list){
if(pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
3)方法引用
①使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
②方法引用,本质上就是Lambda表达式,而Lambda表达式就是函数式接口的实例,所以方法引用,也就是函数式接口的实例
③使用格式: 类(或对象) ::方法名
④具体分为如下的三种情况:
对象::非静态方法
//Consumer中的void accept(T t)
//printStream中的void println(T t)
@Test
public void test1(){
Consumer< String > con1 = str - > System.out.println(str);
con1.accept("北京");
PrintStream ps = System.out;
Consumer< String > con2 = ps::println;
con2.accept("beijing");
}
类::静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test2(){
Comparator< Integer > com1 = (t1,t2) - > Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
Comparator< Integer > com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
类::非静态方法 (有难度)
//Comparator中的int compare(T t1,T t2)
//String中的int t1.compareTo(t2)
@Test
public void test3(){
Comparator< String > com1 = (s1,s2) - > s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
Comparator< String > com2 = String::compareTo;
System.out.println(com2.compare("abc","abm"));
}
⑤方法引用的要求:接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同
4)强大的Stream API
①Stream关注的是对数据的运算,与CPU打交道,而集合关注的是数据的存储,与内存打交道
②Stream自己不会存储元素
③Stream不会改变源对象,相反,他们会返回一个持有结果的新Stream
④Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行
⑤Stream执行流程:
Stream的实例化
一系列的中间操作(过滤、映射、...)
终止操作
一:测试Stream的实例化(四种方式)
方式一:通过集合
//default Stream< E > stream():返回一个顺序流
Stream< Employee > stream = employees.stream();
//default Stream< E > parallelStream():返回一个并行流
Stream< Employee > parallelStream = employees.parallelStream();
方式二:通过数组
//调用Arrays类的static < T > Stream< T > stream(T[] array):返回一个流
IntStream stream = Arrays.stream(arr);
方式三:通过Stream的of()
Stream< Integer > stream = Stream.of(1,2,3,4,5,6);
方式四:创建无限流
//迭代 public static< T > Stream< T > iterator(final T seed,final UnaryOperator< T > f)
Stream.iterator(0,t - >t + 2).limit(10).forEach(System.out::println);
//生成 public static< T > Stream< T > generate(Supplier< T > s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
二:Stream的中间操作
1-筛选与切片
filter(Predicate p):接收Lambda,从流中排除某些元素
limit(n):截断流,使其元素不超过给定数量
skip(n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流
distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素
2-映射
map(Function f):接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素
flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3-排序
sorted():自然排序
sorted(Comparator com):定制排序
三:Stream的终止操作
1-匹配与查找
allMatch(Predicate p):检查是否匹配所有元素
anyMatch(Predicate p):检查是否至少匹配一个元素
noneMatch(Predicate p):检查是否没有匹配的元素
findFirst:返回第一个元素
FindAny:返回当前流中的任意元素
count:返回流中元素的总个数
max(Comparator c):返回流中的最大值
min(Comparator c):返回流中的最小值
forEach(Consumer c):内部迭代
2-归约
reduce(T identity,BinaryOperator):可以将流中元素反复结合起来,得到一个值,返回T
reduce(BinaryOperator):可以将流中元素反复结合起来,得到一个值,返回Optional< T >
3-收集
collect(Collector c):将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
5)Optional类
Optional提供很多有用的方法,这样我们就不用显式进行空值检测,避免空指针异常
①Optional.of(T t):创建一个Optional实例,t必须非空
②Optional.empty():创建一个空的Optional实例
③Optional.ofNullable(T t):t可以为空
④orElse(T t):若当前的Optional内部封装的t是非空的,则返回内部的t;若内部的t是空的,则返回orElse()方法中的参数t1