Java高级阶段学习

String,StringBuffer与StringBuilder的区别

1.首先在执行速度上 StringBuilder>StringBuffer>String

​ 原因如下:String为字符串常量,StringBuilder和StringBuffer均为字符串变量。String对象创建之后不可更改,后面两种对象是变量,可以更改。

​ 例如:String str="123"; str+="456";这句代码,首先JVM创建一个新的String对象 str,把“123”赋值给str,接下来,JVM会再创建一个新的对象str,把原来str的值和“456”加起来再赋值给新的str,原来的str就被垃圾回收了。

注意:利用String方法进行大量数据操作时会出现堆内存溢出错误。

​ StringBuilder和StringBuffer有append()方法添加任意类型的字符串。

2.StringBuilder和StringBuffer之间的区别

​ StringBuilder线程上不安全的,StringBffer线程上是安全的。

​ StringBuffer很多方法都带有synchronized关键字,则它的线程是安全的,但是StringBuilder则没有这些关键字,所以不能保证线程安全。

3.总结

​ 1)操作少量数据则使用String

​ 2)单线程下操作大量数据用StringBuilder。

​ 3)多线程下操作大量数据使用StringBuffer。

System类中的一个常用方法

​ System类中的静态方法Long currentTimeMillis(); 获取当前系统时间与1970年01月01日00:00点之间的毫秒差值。使用方式为: System.currentTimeMillis(); 通常用来计算程序所消耗的时间差值。

日期与日历

Date类的两个构造函数:

Date() 分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。

Date(long date) 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。

​ Date类当中很多方法都被DateFormatCalendar所取代,因此以下为DateFormatCalendar两个类的学习笔记。

DateFormat类是日期/时间格式化子类的抽象类,我们可以通过这个类帮我们完成Date对象和String对象之间的转换。分为格式化和解析两种转换方式。

​ 常用规则为:y —-> 年,M —-> 月,d —-> 日,H —-> 时,m —-> 分,s —-> 秒

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
/*
格式化:将Date对象按照指定格式格式化为String对象
*/
Date date = new Date();
//创建日期格式化对象,并指定格式
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String str = df.format(date);//将Date对象格式化为字符串
System.out.println(str);//2018-08-04 13-22-32
}
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws ParseException {
/*
解析:将String对象按照指定格式解析为Date对象
*/
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String str = "2018-08-04 13-28-52";
Date date = df.parse(str);//将字符串解析为Date对象
//由于parse抛出编译时期异常ParseException,所以我们在主方法中需要throws ParseException。
System.out.println(date);//Sat Aug 04 13:28:52 CST 2018
}

Calendar类是日历类,替换了Date类当中的很多方法,把时间信息封装为静态成员变量,方便获取各个时间的属性值。利用Calendar.getInstance()方法获取默认时区和语言环境的一个日历。

YEAR —-> 年,MONTH —-> 月(月份为0-11,可以+1使用),DAY_OF_MONTH —-> 月中的天(几号),HOUR —-> 时(12小时制),HOUR_OF_DAY —-> 时(24小时制),MINUTE —-> 分,SECOND —-> 秒,DAY_OF_WEEK —-> 周中的天(周几,周日为1,可以-1使用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
// 创建Calendar对象
Calendar cal = Calendar.getInstance();

//getTime()方法不是获取毫秒时刻,而是拿到对应的Date对象。
Date time = cal.getTime();
System.out.println(time);//Sat Aug 04 15:59:37 CST 2018

// 设置年
cal.set(Calendar.YEAR, 2018);
// 设置月 此处设置的月份为8月
cal.set(Calendar.MONTH, 7);
// 设置日
cal.set(Calendar.DAY_OF_MONTH, 4);

//使用add()可进行增减操作
//cal.add(Calendar.DAY_OF_MONTH,-2);//减两个月
//获取星期几

int i = cal.get(Calendar.DAY_OF_WEEK);
//星期是以星期日开头为1,因而得到的星期天数-1,即可得到星期值
System.out.println("今天是星期" + (i - 1));//今天是星期6
}

集合之Collection集合

​ 集合是用来存储多个数据的容器。它的长度是可变的,并不固定。集合存储的都是对象,当有多个对象时,使用集合存储。

1.Collection集合两大子接口之List接口

​ 有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。该接口有索引,并且可以存储重复元素。

List集合的两个常用实现类——–ArrayList,它具有元素增删慢,查找快的特点,所以日常开发中查找、遍历比较多的情况下使用ArrayList比较合适。

List集合的两个常用实现类——–LinkedList,它具有元素查找慢,增删快的特点,并且它提供了大量首尾操作的方法,因而在增删比较多的时候使用LinkedList。

2.Collection集合两大子接口之Set接口

​ 一个不包含重复元素的 collection。此接口无索引,并且不能存储重复元素。当存储自定义类的时候,为了去重则需要重写equals和hashCode方法。

Set接口的常用实现类———HashSet,此类实现 Set 接口,由哈希表支持。不能存储重复元素,并且存储无序(即存储的和取出的顺序有可能不一致)。并且查询速度比较快。若要实现存取顺序一致,可以使用LinkedHashSet.

3.Collection集合的遍历

​ 1.迭代器遍历:当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
coll.add("科比1");
coll.add("麦迪1");
coll.add("科比2");
coll.add("麦迪2");
Iterator<String> it = coll.iterator();
while (it.hasNext()){
String s = it.next();
if(s.equals("麦迪1")){
it.remove();//解决并发异常
}

}
System.out.println(coll);
}

​ 注意:在遍历的过程中,如果想对集合进行数据的增删必须使用Iterator对象所有的增删方法,直接修改原集合会产生并发修改异常。

​ 2.增强for遍历:增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for (String s : list) {
System.out.println(s);
}
}

​ 3.如果是List接口的实现类,还可以通过普通for循环利用索引来遍历数据。

4.泛型

​ 泛型是可以在类或方法中预支地使用未知的类型。泛型的通配符为<?>,

​ 泛型的高级使用:类型名称 <? extends 类 > 对象名称 ——> 只能接收该类型及其子类

​ 类型名称 <? super 类 > 对象名称 ——> 只能接收该类型及其父类型

5.外比较器与内比较器

​ 内比较器实现Comparable接口泛型为实现类的本类类型或其父类类型,compareTo方法一个参数,this与这个参数之间进行数据比较。

1
2
3
4
5
6
7
8
9
10
public class Student implements Comparable<Student>{
···········
@Override
public int compareTo(Student o) {
if(this.score!=o.score){
return (int)(o.score - this.score);//score为学生成绩
}
return this.age-o.age;//age为学生年龄
}
}

​ 外比较器实现Comparator接口泛型为实现类的本类类型或其父类类型,实现compare方法两个参数之间进行数据比较。

1
2
3
4
5
6
7
8
9
10
public class MyComparator implements Comparator<Student> {

@Override
public int compare(Student o1, Student o2) {
if(o1.getScore()!=o2.getScore()){
return (int)(o2.getScore()-o1.getScore());//score为学生成绩
}
return o1.getAge()-o2.getAge();//age为学生年龄
}
}

集合之Map集合

​ public interface Map<K,V> 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射一个值。这个集合存储的是键值对,它的键不允许重复,值可以重复,键对值是一一对应关系。

1.Map常用的实现类有HashMap和TreeMap。

接下来主要说一下HashMap。HashMap是基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null值和 null键。它不能保证存取顺序一致(即存的数据和取的数据有可能不一致)。若想实现存储一致,可以使用LinkedHashMap。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {

Map<String, String> map = new HashMap<>();

String v1 = map.put("杨过", "小龙女");//给map中添加数据,添加成功则返回null
String v2 = map.put("杨过", "郭芙");//添加数据若键已经存在,则返回前一个被覆盖的value
System.out.println(v1);//null
System.out.println(v2);//小龙女
map.put("郭靖", "杨康");
map.put("sd", "康");
map.put("er", "gg");
map.remove("er");//移除数据,括号内放入键,移除了对应的value
System.out.println(map);//{sd=康, 杨过=郭芙, 郭靖=杨康}
}

2.Map集合的两种遍历方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1.使用keySet方法遍历
public static void main(String[] args) {
//通过KeySet方法获取键的Set集合,再用迭代器和get方法遍历Map
Map<String, Integer> map = new HashMap<>();
map.put("迪丽热巴", 170);
map.put("古力娜扎", 168);
map.put("郑爽", 169);

Set<String> key = map.keySet();

Iterator<String> it = key.iterator();
while (it.hasNext()) {
String k = it.next();
Integer h = map.get(k);
System.out.println(k + "=" + h);
}
}
2.使用entrySet方法遍历
public static void main(String[] args) {
//通过entrySet方法获得键值对entry对象的Set集合进行Map遍历
Map<String, Integer> map = new HashMap<>();
map.put("迪丽热巴", 170);
map.put("古力娜扎", 168);
map.put("郑爽", 169);

Set<Map.Entry<String, Integer>> set = map.entrySet();

for (Map.Entry<String, Integer> entry : set) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "=" + value);
}
}

异常的处理

1.异常处理的第一种方式throw

​ throw用在方法内,用来抛出一个异常,将这个异常传递到调用者处,并结束当前方法的执行。

使用格式:

1
throw new 异常类名(参数);

例如:

1
throw new  ArrayIndexOutOfBoundsException("哥们!你越界了");

2.异常处理的第二种方式throws

​ throws用在方法声明之上,表示不处理异常,交给方法的调用者来处理异常(抛出异常)。

使用格式:

1
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }

例如:

1
public static void readFile() throws FileNotFoundException,IOException {...}

3.异常处理的第三种方式try…catch…finally

​ try…catch…finally方法可以对Java中异常有针对性的语句进行捕获,可以对出现的指定方式进行处理。try代码块中编写可能出现异常的代码,catch用来进行某种异常的捕获,实现对捕获异常的处理。finally代码块当中无论异常是否发生,都需要执行。

使用格式:

1
2
3
4
5
6
7
8
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}finally{
finally代码块当中无论异常是否发生,都需要执行。
}

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void main(String[] args) {

int num = 1;
try {
System.out.println("0");
System.out.println(10/num);
readFile("a.txt");
System.out.println("1");
System.out.println("2");
} catch (IOException e) {
e.printStackTrace();
}catch (ArithmeticException e){
e.printStackTrace();
}finally {
System.out.println("资源释放");
}
//finally代码块当中无论异常是否发生,都需要执行。
System.out.println("后续代码");
}


public static void readFile(String fileName) throws IOException {

if(!fileName.endsWith(".txt")){
throw new IOException("后缀名不对");
}

}

注意:当catch语句中异常为父子关系时,父异常必须放在子异常的下面。

线程

​ 线程是分时调度原则:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。还有抢占式调度原则:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。Java在微观上使用的是并发执行多线程。

1.线程的两种创建方式——-继承Thread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyThread extends  Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() {
for (int i = 0; i < 10 ; i++) {
System.out.println(getName() + "正在执行!" + i);
}
}
}
public class Demo01 {
public static void main(String[] args) {

MyThread mt = new MyThread("新的线程");
mt.start();

for (int i = 0; i < 20 ; i++) {
System.out.println("main线程:" + i);
}
}
}

2.线程的两种创建方式——-实现Runnable接口,把实现类对象作为参数传入Thread构造方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
MyRunnable mb = new MyRunnable();
Thread t = new Thread(mb, "小强:");
t.start();

for (int i = 0; i < 20; i++) {
System.out.println("旺财:" + i);
}
}
}

3.继承Thread和实现Runnable之间的区别

实现Runnable接口比继承Thread类所具有的优势:

​ 1.适合多个相同的程序代码的线程去共享同一个资源。

​ 2.可以避免java中的单继承的局限性。

​ 3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

​ 4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

4.线程同步—–synchronized关键字和Lock锁

1.synchronized同步代码块

1
2
3
synchronized(同步锁){
需要同步操作的代码
}

​ 注意:同步锁对象可以是任意类型,多个线程对象要使用同一把锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//售票案例

public class SynchronizedRunnableImpl implements Runnable {

private int ticket = 100;

@Override
public void run() {

while (true) {

synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
}
}
}
}
}

2.synchronized同步方法

1
2
3
4
5
6
7
public synchronized void method(){
可能会产生线程安全问题的代码
}//对于非static方法,同步锁就是this。

public static synchronized void method(){
可能会产生线程安全问题的代码
}//对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//售票案例

public class SynchronizedRunnableImpl implements Runnable {

private static int ticket = 100;

@Override
public void run() {

while (true) {

payTicketStatic();
}

}

// public synchronized void payTicket() {//synchronized(this)
// if (ticket > 0) {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
// }
// }
//
public static synchronized void payTicketStatic() {

// synchronized (SynchronizedRunnableImpl.class){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
}
// }
}
}

3.Lock锁

​ 同步代码块/同步方法具有的功能Lock都有,Lock更加的面向对象。使用Lock lock = new ReentrantLock(); 获取Lock对象,加同步锁用lock()方法,释放同步锁用unlock()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//售票案例

public class RunnableImpl implements Runnable {

private int ticket = 100;

Lock l = new ReentrantLock();

@Override
public void run() {

while (true) {

l.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}

}

}
}
}

5.线程等待唤醒机制

​ 在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。wait方法与notify方法必须要由同一个锁对象调用。

经典案例——-生产者与消费者

包子类:

1
2
3
4
5
6
7
8
public class BaoZi {

public String pi;

public String xian;

public boolean flag = false;
}

包子铺类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class BaoZiPu extends Thread {

private BaoZi baozi;

public BaoZiPu(BaoZi baozi) {
this.baozi = baozi;
}

@Override
public void run() {
int count = 0;
while (true) {
synchronized (baozi) {

if (baozi.flag == true) {
try {
baozi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count % 2 == 0) {
baozi.pi = "薄皮";
baozi.xian = "三鲜馅";
} else {
baozi.pi = "冰皮";
baozi.xian = "牛肉馅";
}
count++;
System.out.println("正在做" + baozi.pi + baozi.xian + "包子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
baozi.flag = true;
baozi.notify();
System.out.println("快来吃" + baozi.pi + baozi.xian + "包子");

}

}

}
}

吃货类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ChiHuo extends Thread {

private BaoZi bz;

public ChiHuo(BaoZi bz){
this.bz = bz;
}

@Override
public void run() {

while (true){

synchronized (bz){
if (bz.flag==false){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

System.out.println("我要开吃了,包子真香");
bz.flag = false;
bz.notify();
System.out.println("老板,起来做包子了");
System.out.println("----------------------------");
}
}
}
}

测试类:

1
2
3
4
5
6
7
8
9
public class Demo {

public static void main(String[] args) {

BaoZi bz = new BaoZi();
new BaoZiPu(bz).start();
new ChiHuo(bz).start();
}
}

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
正在做薄皮三鲜馅包子
快来吃薄皮三鲜馅包子
我要开吃了,包子真香
老板,起来做包子了
----------------------------
正在做冰皮牛肉馅包子
快来吃冰皮牛肉馅包子
我要开吃了,包子真香
老板,起来做包子了
----------------------------
正在做薄皮三鲜馅包子
快来吃薄皮三鲜馅包子
我要开吃了,包子真香
老板,起来做包子了
----------------------------

6.线程池

线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。使用Executors工具类的newFixedThreadPool(nThreads)方法获取线程池对象,参数传入线程数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了一个新的线程");
}
}
public class Demo01ThreadPool {

public static void main(String[] args) {

ExecutorService es = Executors.newFixedThreadPool(2);

RunnableImpl rb = new RunnableImpl();

es.submit(rb);
es.submit(rb);
es.submit(rb);

//es.shutdown();//不建议关掉
}
}

代码的优化——Lambda表达式

在运用匿名内部类的时候可以使用Lambda表达式,前提是使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法(函数式接口)。

函数式接口(也可以包含静态,默认,私有的方法)接口之上可以加入注@FunctionalInterface,若接口不满足函数式接口时,该注解会有编译时异常。

使用Lambda必须具有上下文推断。Lambda表达式的格式如下:

1
(参数类型 参数名称) -> { 代码语句 }

在Lambda标准格式的基础上,使用省略写法的规则为:小括号内参数的类型可以省略;如果小括号内有且仅有一个参,则小括号可以省略;如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

1
2
3
4
5
6
7
public static void main(String[] args) {

new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "新的线程");
}).start();
new Thread(() -> System.out.println(Thread.currentThread().getName() + "新的线程")).start();
}

四个常用的函数式接口

1.生产接口Supplier接口

该接口下包含一个无参数的方法T get(),用来获取一个泛型参数指定类型的对象数据。

2.消费接口Consumer接口

包含抽象方法void accept(T t),意为消费一个指定泛型的数据。

默认方法andThen()参数和返回值都属Consumer类型,可以实现:首先消费一次,再消费一次,实现组合。

3.判断接口Predicate接口

包含抽象方法boolean test(T t).用于判断返回一个boolean类型的值。

默认方法and()起到了逻辑关系并且的作用。

默认方法or():起到了逻辑关系的作用。

默认方法negate():起到了逻辑关系的作用。

4.转换接口Function接口

Function:泛型有两个参数,把T类型转换为R类型。

R apply(T t),根据类型T的参数获取类型R的结果。

默认方法andThen():一个拼接的作用,需要注意的是:当第一次调用apply方法更改参数类型之后,andThen括号内的对象第二次调用时apply()传入的类型为前一次转换之后类型的结果。类似与流水线。

I/O流

1.File类及其递归遍历

File类是文件和目录路径的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

三个常用构造方法:

public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的 File实例。public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的 File实例。

File类的常用方法查询api可以得到。不再一一举例,下面为File的递归遍历。

遍历有两种方法,file.list()返回一个String数组,表示该File目录中的所有子文件或目录。file.listFiles(括号内可传入过滤器实现类对象)返回一个File数组,表示该File目录中的所有的子文件或目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public class Demo01Filter {

public static void main(String[] args) {
File f = new File("E:\\HTML\\index");
// long length = getLength(f);
// System.out.println(length);
// int count = getCount(f);
// System.out.println(count);
HashMap<String, Integer> map = new HashMap<>();
getKind(f, map);
System.out.println(map);

getAll(f);
}
//递归删除文件及文件夹
public static void delete(File f) {

if (f.isDirectory()) {

System.out.println(f);
File[] files = f.listFiles();
for (File file : files) {
delete(file);
}
f.delete();
} else {
f.delete();
}

}
//递归遍历打印所有文件夹以及文件名称
public static void getAll(File f) {
if (f.isDirectory()) {

System.out.println(f);
File[] files = f.listFiles();
for (File file : files) {
getAll(file);
}
} else {
System.out.println(f);
}
}
//递归获取文件夹下所有文件大小总和或者文件大小
public static long getLength(File f) {

if (f.isDirectory()) {
long length = 0L;
File[] files = f.listFiles();
for (File file : files) {
length += getLength(file);
}
return length;
} else {
return f.length();
}
}
//递归打印java总数
public static int getCount(File f) {
int sum = 0;
if (f.isDirectory()) {
File[] files = f.listFiles();
for (File file : files) {
sum += getCount(file);
}
return sum;
} else {
if (f.getName().endsWith(".java")) {
return 1;
}
return 0;
}
}
//递归将类型-->数量存入Map集合
public static void getKind(File f, HashMap<String, Integer> map) {

if (f.isDirectory()) {
File[] files = f.listFiles();
for (File file : files) {
getKind(file, map);
}

} else {
String name = f.getName();
String[] split = name.split("\\.");
String kind = split[split.length - 1];

Integer integer = map.get(kind);
if (integer == null) {
integer = 0;
}
integer++;
map.put(kind, integer);
}
}
}

FileFilter:文件过滤器。实现FileFilter中的accept方法可对文件进行过滤,根据pathname将满足条件的文件、文件夹返回ture,不满足的返回false即可。

1
2
3
4
5
6
7
8
9
10
public class FileFilterImpl implements FileFilter {
@Override
public boolean accept(File pathname) {

if(pathname.getName().endsWith(".html")||pathname.isDirectory()){
return true;
}
return false;
}
}

2.文件字节流输出流FileOutputStream(写入数据 由内存写出数据到其他设备)

1.构造方法

​ FileOutputStream(File file):构造方法传入File对象表示的文件创建文件输出流

​ FileOutputStream(String name):构造方法传入指定名称表示的文件创建文件输出流

2.写出字节数据(单个字节、多个字节)

​ write(int b):每次写出一个字节的数据

​ write(byte[] b):写出一个字节数组中的数据

​ write(byte[] b, int off, int len):从索引off开始,len个字节

3.数据追加、写出换行

​ 数据追加即在构造方法中的第二个参数设置为true即可,换行符号为 \r\n 写出到文件即可。

4.close()方法关闭文件

​ 为什么一定要调用close()方法关闭资源?因为如果不关闭资源,程序一旦无限循环时,资源文件就会一直打开无法关闭,我们就无法对文件进行删除等操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public static void main(String[] args) throws IOException {
//每次写出一个字节
FileOutputStream fos = new FileOutputStream("IOAndProperties\\a.txt");

fos.write((int)('a'));
fos.write((int)('9'));
fos.write((int)('7'));

fos.close();
}

public static void main(String[] args) throws IOException {
//每次写出一个字节数组或写出字节数组中索引位置起len个长度的数据
FileOutputStream fos = new FileOutputStream("IOAndProperties\\b.txt");

byte[] bytes = {97, 98, 99, 100, 101};
fos.write(bytes);
fos.write(bytes, 2, 2);

byte[] bytes1 = "你好".getBytes();
fos.write(bytes1);

fos.close();
}

public static void main(String[] args) throws IOException {
//数据追加以及换行
FileOutputStream fos = new FileOutputStream("IOAndProperties\\b.txt");

for (int i = 0; i < 1000; i++) {
fos.write("你好".getBytes());
fos.write("\r\n".getBytes());
}
fos.close();
}

3.文件字节流输入流FileInputStream(读取数据 由其他设备读取数据到内存)

1.构造方法

​ FileInputStream(File file):构造方法传入File对象表示的文件创建文件输入流

​ FileInputStream(String name):构造方法传入指定名称表示的文件创建文件输入流

2.读取字节

​ read():每次读取一个字节的数据,提升为int类型返回(例如读取文件中字符a的时候,返回97),读取到末尾时返回-1;

​ read(byte[] b):每次读取b的长度个字节到数组中,方法返回的是读取字节的个数,读取到末尾时返回-1;

3.close()方法关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws IOException {
//每次读取一个字节,循环读取
FileInputStream fis = new FileInputStream("IOAndProperties\\a.txt");

int len = 0;
while ((len = fis.read() )!=-1){
System.out.println((char)len);
}

fis.close();
}

public static void main(String[] args) throws IOException {
//一次读取一个字节数组的内容
FileInputStream fis = new FileInputStream("IOAndProperties\\b.txt");

int len = 0;
byte[] bys = new byte[1024];
while ((len = fis.read(bys)) != -1) {
System.out.println(new String(bys, 0, len));//打印有效的内容
}
}

4.字节流之间的图片复制

​ 首先用字节输入流从硬盘中读取一张图片,再利用字节输出流把读到的信息写出到另一个文件,达到了复制图片的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws IOException {

File f = new File("图片\\14-58-38-pnsexp271527.jpg");
FileInputStream fis = new FileInputStream(f);
FileOutputStream fos = new FileOutputStream(f.getName());
int len;
byte[] bytes = new byte[1024];

while ((len = fis.read(bytes)) != -1) {

fos.write(bytes, 0, len);
}
fos.close();
fis.close();
}

5.字符输出流FileWiter

​ 字符流的本质是字节流+编码集(IDEA为UTF-8)。

1.构造方法

​ FileWiter(File file):创建一个新的FileWriter,给定要读取的File对象。

​ FileWriter(String fileName):创建一个新的FileWriter,给定要读取的文件路径。

2.写出字符数据

​ write(int b):每次写出一个字符数据。

​ write(char[] cbuf):每次写出一个字符数组的数据。

​ write(char[] cbuf, int off , int len):从索引off开始写len个字符

​ write(String str)和write(String str, int off, int len):每次可以写出字符串当中的数据。

3.关闭和刷新

​ 由于每次调用write方法会先把数据写入内存缓冲区,不关闭输出流,无法写字符到文件中。但是关闭数据流无法继续写数据,此时需要用到flush()方法:刷新缓冲区,把字符写入文件。close()方法:先刷新缓冲区,再释放资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("IOAndProperties\\a.txt", true);

char[] c = {'a', 'b', 'c', 'd', 'e'};
fw.write(c);
fw.flush();
fw.write(c, 2, 3);

fw.write("\r\n");
fw.write("dashazi", 2, 5);

fw.flush();
fw.close();
}

6.字符输入流FileReader

1.构造方法

​ FileReader(File file):创建一个新的FileReader,给定要读取的File对象。

​ FileReader(String fileName):创建一个新的FileReader,给定要读取的文件路径。

2.读取字符数据

​ read():每次读取一个字符的数据,提升为int类型,返回回来,读取到文件的末尾返回-1。

​ read(char[] cbuf),每次读取cbuf的长度个字符到数组中,返回读取的有效字符个数,读取到末尾时返回-1。

3.关闭资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) throws IOException {

FileReader fr = new FileReader("IOAndProperties\\a.txt");

int len;
len = fr.read();
System.out.println((char)len);
while ((len = fr.read()) != -1) {

System.out.print(len);
}
fr.close();

// fr = new FileReader("09_IOAndProperties\\a.txt");
// char[] c = new char[1024];
// while ((len = fr.read(c)) != -1) {
// System.out.print(new String(c, 0, len));
// }
//
// fr.close();
}

7.IO异常处理JDK7/JDK9新特性

1.JDK7的处理

​ 把创建字符/字节的输出/输入流语句放入try()中,try-catch模块执行完自动释放资源。不用手动的close()。

1
2
3
4
5
try (创建流对象语句,如果多个,使用';'隔开) {
// 读写数据
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {


try (FileReader fr = new FileReader("IOAndProperties\\a.txt");

FileWriter fw = new FileWriter("IOAndProperties\\d.txt")) {
int len;
char[] c = new char[1024];

while ((len = fr.read(c)) != -1) {

fw.write(c, 0, len);
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}

2.JDK9的处理

​ 把字符/字节的输出/输入流对象放入try()中,try-catch模块执行完自动释放资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws IOException {

FileReader fr = new FileReader("IOAndProperties\\a.txt");

FileWriter fw = new FileWriter("IOAndProperties\\d.txt");

try(fr;fw) {
int len;
char[] c = new char[1024];

while ((len = fr.read(c)) != -1) {

fw.write(c, 0, len);
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}

8.Properties类

继承于Hashtable,是一个集合,存储的键值都是字符串类型。

setProperty(String key, String value):存储数据。

getProperty(String key):根据键找值。

Set StringPropertyNames():获取所有键的Set集合。

load(InputStream inStream):从字节输入流读取键值对(键与值之间可以是‘=’、‘ ’,注释用#)。

load(Reader reader):从字符输入流读取键值对(内容可以包含中文,字节流不可以)。

p.store(FileOutputStream outStream, String comments):从字节输出流写出键值对。

p.store(Writer witer, String comments):从字符输出流写出键值对(内容可以包含中文,字节流不可以)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static void main(String[] args) throws IOException {

show03();
}

private static void show03() throws IOException {
Properties prop = new Properties();
prop.load(new FileReader("IOAndProperties\\prop.txt"));

Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
}

private static void show02() throws IOException {
Properties p = new Properties();
p.setProperty("小西瓜", "顶呱呱");
p.setProperty("xia", "sdds");
p.setProperty("小西", "顶呱");

p.store(new FileWriter("IOAndProperties\\prop.txt"), "save data");
//p.store(new FileOutputStream("IOAndProperties\\prop.txt"),"save data");

}

private static void show01() {

Properties p = new Properties();
p.setProperty("小西瓜", "顶呱呱");
p.setProperty("xia", "sdds");
p.setProperty("小西", "顶呱");

Set<String> names = p.stringPropertyNames();
for (String name : names) {
String value = p.getProperty(name);
System.out.println(name + "=" + value);
}
}

9.缓冲流

​ 缓冲流,也叫做高效流、包装流,是对四个基本流的增强,创建流对象时会创建一个内置默认大小的缓冲区数组,通过缓冲区读写,减少系统的IO次数,从而提高读写的效率。

1.字节缓冲流

​ public BufferedInputStream(InputStream in):传入一个字节输入流,创建出一个新的缓冲输入流。

​ public BufferedOutputStream(OutputStream out):传入一个字节输出流,创建出一个新的缓冲输出流。缓冲字节输出流也需要flush()方法。

​ 缓冲字节流基本方法和普通字节流一致。

2.字符缓冲流

​ public BufferedReader(Reader in):传入一个字符输入流,创建出一个新的缓冲输入流。

​ public BufferedWriter(Writer out):传入一个字节输出流,创建出一个新的缓冲输出流。缓冲字符输出流也需要flush()方法。

​ 缓冲字符流基本方法和普通字符流一致。

​ BufferedReader里面包含一个特有方法:readLine():一次读取一行文字,返回一个字符串。但是读不到换行符。

​ BufferedWriter里面包含的特有方法:newLine():写一个换行符,此方法可以跨平台使用。

​ 缓冲流文件复制案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws IOException {

BufferedReader br = new BufferedReader(new FileReader("IO\\b.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("IO\\c.txt"));

String len;
while ((len = br.readLine()) != null) {
bw.write(len);
bw.newLine();
bw.flush();
}
bw.close();
br.close();

}

10.转换流

​ 转换流是用普通字节流加上字符集组成。

​ InputSreamReader(InputStream in,String charsetName):创建一个指定字符集的转换输入字符流,第二个参数不写会使用默认字符集(例如IDEA使用utf-8字符集)。

​ OutputStreamWriter(OutputStream out,String charsetName):创建一个指定字符集的转换输出字符流,第二个参数不写会使用默认字符集(例如IDEA使用utf-8字符集)。

案例:读取GBK文件,写出UTF-8文件

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws IOException {

InputStreamReader isr = new InputStreamReader(new FileInputStream("IO\\f.txt"), "GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("f.txt"), "UTF-8");
int len;
char[] c = new char[1024];
while ((len = isr.read(c)) != -1) {
osw.write(c, 0, len);
osw.flush();
}
osw.close();
isr.close();
}

11.序列化及反序列化

1.序列化:用一个字节序列表示一个对象,将该对象信息写出到文件。

​ ObjectOutputStream(OutputStream out)将Java对象的原始数据类型写出到文件,实现对象的持久存储。构造方法内传入一个字节输出流对象。

​ 一个对象(例如:Student类对象)要想序列化,必须实现Serializable接口作为可以被序列化的标记,另外此类要重写serialVersionUID这个参数,避免该类修改序列号改变引起异常。

​ 若是某个属性不想被序列化,可以加transient关键词修饰。可避免被序列化。

​ 调用writeObject(Object obj)方法写出指定对象。

2.反序列化:用该字节序列从文件中读取出来,重构对象。

​ ObjectInputStream(InputStream in):构造传入一个字节输入流对象。

​ 调用readObject()方法读取一个对象,如果想使用该对象的特有方法,我们则需要向下强转。

​ 由于读取对象没有结束标志,读完会产生一个异常,所以有多个对象时我们可以把它们写在一个集合当中,此时序列化时,直接将集合对象写出到文件当中,这样就可以写一次、读一次避免异常。

​ 序列化集合案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Person类:
public class Person implements Serializable {

private static final long serialVersionUID = 7551517898504602762L;
private String name;

private int age;

public Person() {
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

ObjectOutputStream类:
public class Demo01ObjectOutputStream {

public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("IO\\g.txt"));

ArrayList<Person> arr = new ArrayList<>();
arr.add(new Person("小李",18));
arr.add(new Person("sss",20));
arr.add(new Person("3s",18));
oos.writeObject(arr);

oos.flush();
oos.close();
}
}
ObjectInputStream类:
public class Demo02ObjectInputStream {

public static void main(String[] args) throws IOException, ClassNotFoundException {

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("IO\\g.txt"));

Object o = ois.readObject();
ArrayList<Person> arr = (ArrayList<Person>) o;
for (Person person : arr) {
System.out.println(person.getName() + ":" + person.getAge());
}

ois.close();
}
}

12.打印流PrintStream

​ 打印流的好处:可以使用print、println方法打印,后者自带换行。在构造方法可以指定编码集,自动刷新。打印的数据类型都保持原样。

​ PrintStream(String fileName):构造方法中传入文件路径。

​ System.setOut(PrintStream ps):可以改变打印流的流向,把输出到控制台的信息直接输出在文件里。

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws FileNotFoundException {

PrintStream ps = new PrintStream("10_IO\\print.txt");

ps.println(97);
ps.println(19.88);
ps.println(true);
ps.println("sss");

ps.close();
}

网络编程

Tcp协议的三次握手:

​ 第一次握手,客户端向服务器端发出连接请求,等待服务器请求。

​ 第二次握手,服务端向客户端回送一个回应,通知客户端收到了连接请求。

​ 第三次握手,客户端再次向服务器端发送确认信息,确认连接。

1.Socket类

​ public Socket(String host, int port):创建套接字对象并将其连接到主机上的指定端口号。如果制定的host是null,则相当于指定地址为回环地址(127.0.0.1).

​ host指定IP地址,port指定端口号。

​ getInputStream():返回此套接字的输入流。

​ getOutputStream():返回此套接字的输出流。

​ close():关闭套接字。

​ shutdownOutput():禁用此套接字的输出流。可以理解为写出了结束标志。

2.ServerSocket类

​ public ServerSocket(int port):使用该方法创建ServerSocket对象时,将其绑定到指定端口号上。

​ public Socket accept():侦听并接受连接,返回一个Socket对象,用于和客户端实现通信,该方法会一直阻塞直到建立连接。

客户端向服务端上传图片案例:服务器端使用无线循环和多线程,能多次接收上传减少耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Runnable的实现类:
public class RunnableImpl implements Runnable {

private Socket socket;

public RunnableImpl() {
}

public RunnableImpl(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try{
InputStream is = socket.getInputStream();
File file = new File("d:\\upload");
if (!file.exists()) {
file.mkdir();
}

String fileName = "wenc" + System.currentTimeMillis() + new Random().nextInt(999999) + ".png";
FileOutputStream fos = new FileOutputStream(file.getAbsoluteFile() + "\\" + fileName);
int len;
byte[] bytes = new byte[1024];
while ((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();

OutputStream os = socket.getOutputStream();
os.write("上传成功".getBytes());
socket.shutdownOutput();

socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
服务器端代码:
public class TCPServer {
public static void main(String[] args) throws IOException {


ServerSocket ssk = new ServerSocket(8888);
ExecutorService es = Executors.newFixedThreadPool(100);
while (true) {


Socket socket = ssk.accept();
es.submit(new RunnableImpl(socket));
}
}
}
客户端代码:
public class TCPClient {

public static void main(String[] args) throws IOException {

FileInputStream fis = new FileInputStream("Net\\dsj.png");

Socket sc = new Socket("127.0.0.1", 8888);
OutputStream os = sc.getOutputStream();

int len;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
sc.shutdownOutput();
fis.close();

InputStream is = sc.getInputStream();
while ((len = is.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
sc.close();
}
}

服务端和客户端实现实时聊天:客户端和服务端都采取了多线程,把读取的数据和写出的数据分别无线循环达到了实时发送、接收消息的目的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
服务器端代码:
public class TCPServer {

public static void main(String[] args) throws IOException {

ServerSocket ssk = new ServerSocket(9999);
Socket socket = ssk.accept();

new Thread(new SRunImpl(socket)).start();
new Thread(new SerRunImpl(socket)).start();
}
static class SRunImpl implements Runnable {

private Socket socket;

public SRunImpl(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try {
while (true) {
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
line = br.readLine();
System.out.println("客户端:" + line);
if (line.equals("886")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

static class SerRunImpl implements Runnable{


private Socket socket;

public SerRunImpl(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
Scanner sc = new Scanner(System.in);
while (true) {
OutputStream out = socket.getOutputStream();
BufferedWriter ps = new BufferedWriter(new OutputStreamWriter(out));
System.out.println("给客户端说什么:");
String str = sc.next();
ps.write(str);
ps.newLine();
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端代码:
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 9999);
new Thread(new SRunImpl(socket)).start();
new Thread(new SerRunImpl(socket)).start();
}
static class SRunImpl implements Runnable {

private Socket socket;

public SRunImpl(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try {
while (true) {
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
line = br.readLine();
System.out.println("服务端:" + line);
if (line.equals("886")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

static class SerRunImpl implements Runnable{


private Socket socket;

public SerRunImpl(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
Scanner sc = new Scanner(System.in);
while (true) {
OutputStream out = socket.getOutputStream();
BufferedWriter ps = new BufferedWriter(new OutputStreamWriter(out));
System.out.println("给服务端说什么:");
String str = sc.next();
ps.write(str);
ps.newLine();
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Stream流

Stream是Java 8新加入的最常用的接口。元素是特定类型的对象,形成一个队列。Stream并不会存储元素,而是按需计算,流的来源可以是数组,集合等。Stream流提供了内部迭代方式,流可以直接调用最终方法。

1.获取Stream流

Collection获取流:用.stream()获取流。

Map获取流:先转换为keySet()和entrySet(),然后再.stream()获取流。

数组获取流:用Sream.of()方法获取流。

2.常用方法

延迟方法:过滤方法filter,此方法参数传入一个判断接口(Predicate)的Lambda表达式,返回true保留,返回false则过滤。

​ 映射方法map,此方法传入一个转换接口(Function)的Lambda表达式,将stream流当中的格式转换。

​ 截取方法limit(n),传入一个长度,只取用前n个(包括n),如果集合当前长度大于参数则进行截取;否则不进行操作。

​ 跳过方法skip(n),跳过前n个元素(包括n)。

​ 组合方法concat(),将两个流合并为一个流。

终结方法:逐一处理方法foreach,方法内传入一个消费接口(Consumer)的Lambda表达式,一般用于集合数据打印。

​ 统计个数方法count(),计算流中的元素个数。

​ 存储到集合collect(Collectors.toList()),将流中的数据存储到集合里。

​ 存储到数组toArray(),将流中的数据存储到数组里。

反射

1.获取Class对象的方式

1
2
3
4
5
6
7
8
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
2. 类名.class:通过类名的属性class获取
多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

2.Class对象功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
获取功能:
1. 获取成员变量们
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
2. 获取构造方法们
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
3. 获取成员方法们:
Method[] getMethods()
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
获取全类名
String getName()

Field:成员变量
操作:
1. 设置值 void set(Object obj, Object value)
2. 获取值 get(Object obj)
3. 忽略访问权限修饰符的安全检查:setAccessible(true):暴力反射

Constructor:构造方法
创建对象:T newInstance(Object... initargs)
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
Method:方法对象
执行方法:Object invoke(Object obj, Object... args)
获取方法名称:String getName:获取方法名