bilibili视频参考
public class Hello
{
public static void main(String[] args)
{
System.out.println("hello world!");
}
}
class Test
{
public static void main(String[] args)
{
System.out.println("I am a test!")
}
}
System.out.println(~4);
//-5
/*
原码:如果机器字长为n,那么一个数的原码就是用一个n位的二进制数,其中最高位为符号位:正数为0,负数为1。剩下的n-1位表示概数的绝对值。
反码:反码就是在原码的基础上,符号位不变其他位按位取反(就是0变1,1变0)就可以了。
补码:在反码的基础上按照正常的加法运算加1。
移码:不管正负数,只要将其补码的符号位取反即可。
*/
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区或者方法区内找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。
static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。
static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:
类名.静态方法名(参数列表…)
类名.静态变量名
用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块(用处非常大)。
static变量
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
静态方法
静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!
因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
作用
区分相同名字的类
当类很多时,可以有效果管理类
控制访问范围
同一个包中的类直接调用,无需import导入。包名全部小写
访问控制修饰符
公开级别:public 对外公开
受保护级别:protected 对子类和同一个包中的类公开
默认界别:用friendly或者缺省 向同一个包中的类公开
私有级别:private 只有类本身可以访问,不对外公开
修饰符 | 同一个类 | 同一个包 | 子类 | 其他包 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
继承
Java中所有的类都是Object类的子类
继承的规则:子类继承父类所有(可见)属性和所有(可见)方法,但是不继承构造方法
实例化过程:先父类,然后子类
成员方法的覆盖:名称、参数、返回值和父类方法的成员方法一样
覆盖规则:
super和this关键字
super:指向父类的引用
this:指向本类的引用
super()
作用:调用父类的构造器
只能出现在子类的构造器中,且必须是第一行
super()中的参数,决定了调用父类哪个构造器
如果子类构造器中没有出现super(),那么默认给增加super(),即调用父类的空构造器
this()
作用:调用本类的构造器
只能写在构造器的第一行
在同一个构造器中super()和this()不能同时出现
final可以修饰的元素】
变量(属性和局部变量):不能被重新赋值
方法:不能被覆盖,即不能被修改
类:不能被继承
抽象类
抽象方法:只有方法申明,没有方法实现的方法。一般用abstract申明,以“;”结尾,例如:public abstract void getArea();
抽象类:含有抽象方法的类必须声明为抽象类,用abstract声明class。
注意点:
接口
接口不是一个类,不能实例化;
接口是常量和抽象方法的集合
接口语法:访问权限控制符 interface 接口名
{
属性;
方法;
}
接口的特点:
接口和抽象类的区别
接口不能含有任何非抽象方法(jdk1.8之后就可以有一个default方法),而抽象类可以。
类可以实现许多接口,但只能有一个父类。
接口不是类分级结构的一部分,没有联系的类可以实现相同的接口
静态修饰符static
static可以修饰类的成员方法、类的成员变量
注意:static不能修饰局部变量
静态属性
序号 | 静态属性 | 非静态属性 |
---|---|---|
1 | 多个对象共享一个属性 | 每个对象共享一个属性 |
2 | 该属性属于类 | 该属性属于每个实例 |
3 | 类变量 | 实例变量 |
4 | 对象.属性/类.属性 | 对象.属性 |
静态属性在该类第一次被加载到虚拟机时,分配静态存储区,以后每次运行不在分配空间。
限制
包装类
Integer类构造方法有两种:
Integer类常用的方法:
Object.equals(Object IntegerObj)//比较此对象与指定的对象是否相等
Object.toString()//返回一个表示该Integer值的String对象
parseInt(String str)//返回包含在由str指定的字符串中的数字的等价整数值
Object类是所有类的父类,一个类都会直接或者间接继承自该类
toString()方法
作用:打印对象的信息(重写前:包名类名@地址值 重写后:对象中的属性值)
equals()方法
作用:用来比较两个对象(重写前:比较对象的地址值 重写后:比较对象中的属性值)
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));}
作用:比较两个对象是否相同,添加了一些健壮性判断
1.Date类
构造方法
Date():根据当前系统时间创建日期对象
Date(long time):根据传入的毫秒值时间创建日期对象
成员方法
long getTime():获取当前日期对象的毫秒值时间
String toLocaleString():根据本地格式转换日期对象
2.DateFormat类和SimpleDateFormat类
构造方法
SimpleDateFormat(String s):根据指定模板创建日期格式化对象
成员方法
String format(Date date):根据指定格式格式化日期对象
Date parse(String s):根据指定格式解析字符串
3.Calendar类
创建对象方式
Calendar c=Calendar.getInstance();//获取日历类对象
成员方法
int get(int field);//获取指定日历字段信息
void set(int field,int value);//将指定日历字段设置为指定的值
void add(int field,int amount);//将指定日历字段增加或者减少指定的值
java.lang.System类中提供了大量的静态方法,可以获取系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O1MlJ54F-1595949513967)(G:\图片\Java\Snipaste_2020-07-10_20-43-25.jpg)]
构造函数
public StringBuilder():构造一个空的StringBuilder容器
public StringBuilder(String str):构造一个StringBuilder容器,并将字符串添加进去
常用成员方法
public StringBuilder append(……):添加任意类型数据的字符串形式,并返回当前对象自身
public String toString():将当前StringBuilder对象转换成String对象
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
装箱与拆箱
装箱:从基本数据类型转换为对应的包装类对象
构造方法:
Integer(int value):构造一个新分配的Integer对象,表示指定的int值
Integer(String s):构造一个新分配的Integer对象,它表示String参数所指示的int值。传递的字符串,必须是基本类型的字符串,否则就会抛出异常,例如:“100”正确,而“hfh"会抛出异常
静态方法:
static Integer valueOf(int i):返回一个表示指定int值的Integer实例。
static Integer valueOf(String s):返回保存指定的String的值的Integer对象。
拆箱:从包装类对象转换为对应的基本数据类型
成员方法:
int intValue():以int类型返回该Integer的值。
//基本数据类型->包装对象
Integer i=new Integer(10);//使用构造函数
Integer j=Integer.valueOf(10);//使用包装类中的valueOf方法
//包装对象->基本数据类型
int num=i.intValue();
自动装箱与拆箱
自动装箱:直接把int类型的整数赋值给包装类
Integer in=1;等价于Integer in=new Integer(1);
自动拆箱:in是包装类,无法直接参与运算,可以自动转换为基本类型的数据,再参与运算
in=in+2;相当于in.intValue()+2=3;再将结果自动装箱
ArrayList<Integer> list=new ArrayList<>();
list.add(1);//自动装箱
int a=list.get(0);//自动拆箱
基本类型与字符串之间的转换
基本类型转换为String(假设a是一个integer类型的数据)
a.toString()
String.valueOf(a)
a+""
运行效率逐渐变低
toString()方法可以直接调用进行转换
String.valueOf(a)方法底层调用了Integer.toString()方法,但是会在调用前做空判
a+""底层使用了StringBuilder实现,先用append方法拼接,再利用toString()方法获取字符串
String转换成基本数据类型
除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串转换成为对应的基本类型:
- public static byte parseByte(String s)
- public static short parseShort(String s)
- public static int parseInt(String s)
- public static long parseLong(String s)
- public static float parseFloat(String s)
- public static double parseDouble(String s)
- public static boolean parseBoolean(String s)
**集合:**是Java中提供的一种容器,可以用来存储多个数据
集合和数组都是容器,它们的区别是什么?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-az4vgS9z-1595949513971)(G:\Snipaste_pic\Snipaste_2020-07-11_12-46-00.png)]
Collection集合的常用方法
boolean add(E e):向集合中添加元素
boolean remove(E e):删除集合中的某个元素
void clear():清空集合所有元素
boolean contains(E e):判断集合中是否包含某个元素
boolean isEmpty():判断集合是否为空
int size():获取集合的长度
Object[] toArray():将集合转换成一个数组
**迭代:**在取元素之前首先要判断集合中有没有元素,如果有就把这个元素取出来,继续判断
方法:
public E next():返回迭代的下一个元素
public boolean hasNext():如果仍然有元素可以迭代,则返回true
/*
* java.util.List接口 extends Collection接口
* List接口的特点:
* 1.有序集合,存储元素和取出元素的顺序是一致的
* 2.有索引,包含一些带索引的方法
* 3.允许存储重复的元素
* List接口中带索引的方法(特有):
* public void add(int index,E element):将指定的元素添加到该集合中指定的位置上
* public E get(int index):返回集合中指定位置的元素
* public E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素
* public E set(int index,E element):用指定的元素替换集合中指定位置的元素,返回值的更新前的元素
* Attention:操作索引要防止索引越界
*/
/*
* java.util.LinkedList集合 implements List接口
* 特点:
* 1.底层是一个链表结构,查询快,增删慢
* 2.里面包含了大量操作首尾元素的方法
* Attention:使用LinkedList集合特有的方法不能使用多态
*
* public void addFirst(E e):将指定元素插入此列表的开头
* public void addLast(E e):将指定元素添加到此类表的结尾
* public void push(E e):将元素推进此列表所表示的堆栈
*
* public E getFirst():返回列表的第一个元素
* public E getLast():返回列表的最后一个元素
*
* public E removeFirst():移除并返回此列表的第一个元素
* public E removeLast():移除并返回此列表的最后一个元素
* public E pop():从此列表所表示的堆栈处弹出一个元素
*
* public boolean isEmpty():如果列表不包含元素,则返回true
*/
/*
* java.util.Set接口 extends Collection接口
* Set接口的特点:
* 1.不允许存储重复的元素
* 2.没有索引,不能使用for循环遍历集合
* java.util.HashSet集合 implements Set接口
* HashSet特点:
* 1.不允许存储重复的值
* 2.没有索引,不能使用for循环遍历集合
* 3.是一个无序的集合,存储元素和取出元素的顺序可能不一样
* 4.底层是一个哈希表结构(查询速度非常快)
*/
* java.util.LinkedHashSet集合 extends HashSet集合
* LinkedHashSet集合特点:
* 底层是一个哈希表(数组+链表/红黑树)+链表:多了一个链表(记录元素的存储顺序,保证元素有序)
/*
java.util.Collections是集合工具类,用来对集合进行操作。部分方法如下:
public static boolean addAll(Collections c,T…… elements):往集合中添加一些元素
public static void shuffle(List> list):打乱集合顺序
public static void sort(List list):将集合中元素按照默认规则排序
Attention:sort(List list)使用的前提:
被排序的集合里面存储的元素,必须实现Comparable接口中的compareTo()
public static void sort(List list,Comparator super T>):将集合中的元素按指定规则排序
Comparator和Comparable的区别:
Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo()
Comparator:相当于找一个第三方的裁判,比较两个
*/
/*
java.util.Map集合
Map集合的特点:
1.Map集合是一个双列集合,一个元素包含两个值(key,value)
2.Map集合中的元素,key和value数据类型可以不同
3.Map集合中的元素,key不可以重复,value可以重复
4.Map集合中的元素,key和value是一一对应的
java.util.HashMap集合 implements Map接口
HashMap集合的特点:
1.HashMap底层是哈希表,查询速度快
JDK1.8之前:数组+单链表
JDK1.8之后:数组+单链表/红黑树
2.HashMap集合是一个无序的集合,存储元素和取出元素有可能不一致
java.util.LinkedHashMap集合 implements Map接口
LinkedHashMap特点:
1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2。LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一样的
Map接口中定义的常用方法:
public V put(K key,V value):把指定的键值对添加到Map集合中
public V remove(Object key):把指定的键所对应的键值对从Map集合中删除,返回删除元素的值
public V get(Object key):根据指定的键,在Map集合中获取对应的值
boolean containsKey(Object key):判断集合中是否包含指定的键
public Set keySet():获取Map集合中所有的键,存储到Set集合中去
public Set> entrySet():获取到Map集合中所有的键值对对象的Set集合
*/
遍历
/*
Map集合的第一种遍历方式:通过键找值的方式
Map集合中的方法:
Set keySet():返回此映射中包含的键的Set视图
实现步骤:
1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到Set集合
2.遍历Set集合,获取Map集合中的每一个key
3.通过Map集合中的方法get(key)获取对应的value
*/
/*
Map集合遍历的第二种方式:使用Entry对象遍历
Map集合中的方法:
Set> entrySet():返回此映射中包含的映射关系的Set视图
实现步骤:
1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey()和getValue()获取键与值
*/
Hashtable集合
/*
java.util.Hashtable集合 implements Map接口
Hashtable:底层也是一个哈希表,是一个线程安全的集合,单线程集合,速度慢
HashMap:底层是一个哈希表,线程不安全,多线程集合,速度快
HashMap集合(之前所有集合):可以存储null值,null键
Hashtable集合,不能存储null键,null值
Hashtable和Vector集合一样,在JDK1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
Hashtable的子类Properties依然在被使用
Properties集合是一个唯一的和IO流相结合的集合
*/
f8:逐行执行程序
f7:进入到方法中
shift+f8:跳出方法
f9:跳到下一个断点,如果没有就结束程序
ctrl+f2:退出debug模式
console:切换到控制台
异常产生的分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o1cxIVjg-1595949513975)(G:\Snipaste_pic\Snipaste_2020-07-15_22-42-23.png)]
Objects非空判断
该类由一些静态的实用方法组成,这些方法是空指针安全的,在源码中,对对象为null的值进行了抛出异常操作
public static T requireNonNull(T object):查看指定引用对象是不是null
public static <T> T requireNonNull(T object)
{
if(obj==null)
{
throw new NullPointException();
}
return obj;
}
声明异常throws
声明异常:将问题标识出来,报告给调用者。如果方法内通过了throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理
关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)。
声明异常的格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2……{}
Throwable类中定义了3个异常处理的方法:
String getMessage():返回此throwable的简短描述
String toString():返回此throwable的详细消息字符串
void printStackTrace():JVM打印异常对象,默认方法,打印的异常信息是最全面的
线程调度
创建线程类
java.lang.Thread 类代表线程,所有线程对象都必须是Thread类或者其子类的实例。
步骤:
1.定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
2.创建Thread子类的实例,即创建了线程的对象
3.调用线程对象的start()方法来启动线程
创建新线程有两种方式:
一种方法是将类声明为Thread的子类。该类应重写Thread类的run()方法。接下来可以分配并启动该子类的实例
class PrimeThraed extends Thread{
long minPrime;
PrimeThread(long minPrime)
{
this.minPrime=minPrime;
}
public void run()
{
……
}
}
PrimeThread p=new PrimeThread(111);
p.start();
多线程的内存图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e0YtiRrd-1595949513982)(G:\Snipaste_pic\Snipaste_2020-07-17_08-53-54.png)]
另一种方法是声明实现Runnable接口的类。该类然后实现run()方法。然后可以分配该类的实例,在创建Thread时作为一个参数来传递并启动。
class PrimeRun implements Runnable
{
long minPrime;
PrimeRum(long minPrime)
{
this.minPrime=minPrimr;
}
public void run()
{
……
}
}
//启动线程
PrimeRun p=new PrimeRun(111);
new Thread(p).start();
Thread类
构造函数:
常用方法:
多线程安全问题
public class RunnableImpl implements Runnable{
//定义一个多线程共享资源
private int ticket=100;
//设置线程任务:卖票
@Override
public void run() {
while(true)
{
if(ticket>0)
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
public class TicketDemo1 {
public static void main(String[] args) {
RunnableImpl r=new RunnableImpl();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
/*
……
Thread-2正在卖第6张票
Thread-0正在卖第5张票
Thread-1正在卖第4张票
Thread-2正在卖第3张票
Thread-0正在卖第2张票
Thread-1正在卖第1张票
Thread-2正在卖第0张票
Thread-0正在卖第-1张票
/*
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-syVOwinI-1595949513984)(G:\Snipaste_pic\Snipaste_2020-07-17_23-44-20.png)]
线程同步
有三种方式完成同步操作:
同步代码块:synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问
格式:
synchronized(同步锁)
{
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
1.锁对象可以是任意类型
2.多个线程对象要使用同一把锁
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能在外面等待(Blocked)
原理:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uzQdyFJ3-1595949513986)(G:\Snipaste_pic\Snipaste_2020-07-18_00-17-42.png)]
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行方法的时候,其他线程只能在方法外等着。
格式:
public synchronized void method()
{
可能会产生线程安全问题的代码
}
同步锁:
对于非static方法,同步锁就是this
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)
锁机制
/*
java.util.concurrent.locks.Lock接口
Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作
Lock接口中的方法:
void lock()获取锁
void unlock()释放锁
java.util.concurrent.locks.ReentrantLock implements Lock接口
使用步骤:
1.在成员位置创建一个ReentrantLock对象
2.在可能出现线程安全的代码前面调用Lock接口中的方法lock获得锁
3.在可能出现线程安全的代码后面调用Lock接口中的方法unlock释放锁
*/
线程的状态
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没有调用start() 方法 |
Runnable(可运行) | 线程可以在Java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,这取决于操作系统处理器 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态,当该线程持有锁时,该线程将变成Runnable状态 |
Waiting(无线等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep、Object.wait |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡 |
/*
等待唤醒案例:线程之间的通信
创建一个顾客线程(消费者):调用wait()方法,放弃cpu的执行权,进入无限等待状态(Waiting)
创建一个老板线程(生产者):调用notify()方法,唤醒顾客线程
注意:
顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个执行
同步使用锁对象必须保证唯一
只有锁对象才能调用wait()和notify()方法
Object类中的方法:
void wait()
在其他线程调用此对象的notify()方法或者notifyAll()方法前,导致当前线程等待
void notify()
唤醒在此对象监视器上等待的单个线程
会继续执行wait()方法之后的代码
*/
等待唤醒中的方法:
等待唤醒机制就是用于解决线程通信的问题的,使用到的3个方法的含义如下:
注意:
哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因此它当初中断的地方是在同步块内,而此刻它已经不再持有锁,所以它需要再次尝试去获取锁(很可能面临其他线程的竞争),成功后才能在当初调用wait方法之后的地方恢复执行。
总结:
调用wait和notify方法需要注意的细节
线程池
概念:就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗更多的资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yjrwyrr1-1595949513988)(G:\Snipaste_pic\Snipaste_2020-07-22_14-19-20.png)]
合理使用线程池带来的三个好处:
1.降低系统资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重新利用,可以执行多个任务
2.提高响应速度。当任务下达时,任务可以不需要等到线程创建就能立即执行
3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作的数量,防止因为消耗过多的内存
线程池的原理:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SP0rx8tu-1595949513990)(G:\Snipaste_pic\Snipaste_2020-07-22_14-16-51.png)]
线程池的使用:
/*
线程池:JDK1.5之后提供
java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数量的线程池
参数:int nThreads:创建线程池中包含的线程数量
返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorServices接口接收(面向接口编程)
java.util.concurrent.ExecutorService:线程池接口
用来从线程池中获取线程,调用start方法,执行线程任务
submit(Runnable task):提交一个Runnable任务用于执行
关闭/销毁线程池的方法:
void shutdown()
线程池的使用步骤:
1.使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdow销毁线程池(不建议执行)
*/
使用lambda必须具有接口,且要求接口中有且仅有一个抽象方法
无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用lambda
使用lambda必须具有上下文推断
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
**概念:**java.io.File类是文件和目录路径的抽象表示,主要用于文件和目录的创建、查找、删除等操作
构造方法
常用方法
判断功能的方法
创建删除功能
字节输出流(OutputStream)
java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
/*
追加写:使用两个参数的构造方法
FileOutputStream(String name, boolean append):创建一个向具有指定name的文件中写入数据的输出文件流
FileOutputStream(File file,boolean append):创建一个向指定File对象表示的文件中写入数据的文件输出流
*/
字节输入流(InputStream)
java.io.InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法:
字符输入流
/*
java.io.Reader:字符输入流最顶层的类,定义了一些共性成员方法,是一个抽象类
共性成员方法:
int read()读取单个字符并返回
int read(char[] buf)一次读取多个字符,将字符读入数组
void close()关闭该流,并释放与之相关的所有资源
java.io.FileReader extends InputStreamReader extends Reader
FileReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读取到内存中
FileReader(String filename)
FileReader(File file)
使用步骤:
1.创建一个FileReader对象
2.使用FileReader对象中的方法read读取文件
3.释放资源
*/
字符输出流
/*
java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类
共性成员的方法:
void write(int c)写入单个字符
void write(char[] cbuf)写入字符数组
abstract void write(char[] cbuf,int off,int len)写入字符数组的某一部分
void write(String str,int off,int len)写入字符串某一部分
void flush()刷新该流的缓冲
void close()关闭此流,但是要先刷新
java.io,FileWriter extends OutputStreamWriter extends Writer
FileWriter:文件字符输出流
作用:把内存中的数据写入到文件中
构造方法:
FileWriter(String filename)
FileWriter(File file)
使用步骤:
1.创建一个FileWriter对象,构造方法中绑定要写入数据的目的地
2.使用FileWriter中的方法write,把数据写入内存缓冲区中(字符转换成字节的过程)
3.使用FileWriter中的方法flush,把内存缓冲区中的数据刷新到文件中
4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
*/
/*
flush方法和close方法的区别
flush:刷新缓冲区,流对象可以继续使用
close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再使用了
*/
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
/*
java.util.Properties集合 extends Hashtable implements Map
Properties集合是一个唯一和IO流相结合的集合
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
属性列表中每个键及其对应值都是一个字符串
Properties集合是一个双列集合,key和value默认都是字符串
*/
public class PropertiesDemo1 {
public static void main(String[] args) throws IOException {
// show1();
// show2();
show3();
}
/*
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
void load(InputStream inStream)
void load(Reader reader)
参数:
InputStream inStream:字节输入流,不能读取含有中文的键值对
Reader reader:字符输入流,能读取含有中文的键值对
使用步骤:
1.创建Properties集合对象
2.使用Properties集合对象中的方法load读取保存键值对的文件
3.遍历Properties集合
注意:
1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
2.存储键值对的文件中,可以使用#进行注释,注释的键值对不会再被读取
3.存储键值对的文件中,键与值默认都是字符串,不用再加引号
*/
private static void show3() throws IOException {
Properties prop=new Properties();
prop.load(new FileReader("src\\com\\asus\\demo25\\c.txt"));
Set<String> set = prop.stringPropertyNames();
for (String s : set) {
System.out.println(s+"="+prop.getProperty(s));
}
}
/*
可以使用Properties集合中的方法store,把集合中的临时数据,持续化写入到硬盘中存储
void store(OutputStream out,String comments)
void store(Writer writer,String comments)
参数:
OutputStream out:字节输出流,不能写入中文
Writer writer:字符输出流,可以写中文
String comments:注释,用来解释说明保存的文件是做什么用的
不能使用中文,会产生乱码,默认是Unicode编码
一般使用”“(空字符串)
使用步骤:
1.创建Properties集合对象,添加数据
2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入硬盘中存储
4.释放资源
*/
private static void show2() throws IOException {
Properties prop=new Properties();
prop.setProperty("张三","29");
prop.setProperty("李四","19");
prop.setProperty("王五","39");
FileWriter fw=new FileWriter("src\\com\\asus\\demo25\\c.txt");
prop.store(fw,"save data");
fw.close();
}
/*
使用Properties集合存储数据,遍历取出Properties集合中的数据
Properties集合是一个双列集合,key和value都默认是字符串
Properties集合有一些操作字符串的特有方法
Object setProperties(String key,String value)调用Hashtable的方法put
String getProperties(String key)通过key找到value值,此方法相当于Map集合中的get(key)方法
set stringPropertyNames()返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
*/
private static void show1() {
//创建一个Properties集合对象
Properties prop=new Properties();
prop.setProperty("张三","29");
prop.setProperty("李四","19");
prop.setProperty("王五","39");
//使用stringPropertyNames把Properties集合中的键取出来,存储到一个Set集合中
Set<String> set=prop.stringPropertyNames();
//遍历Set集合
for (String s : set) {
System.out.println(s+"="+prop.getProperty(s));
}
}
}
字节缓冲输入流
package com.asus.demo27;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
/*
java.io.BufferedInputStream extends InputStream 字节缓冲输入流
继承自父类的成员方法:
int read()从输入流中读取数据的下一个字节
int read(bytr[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
void close()关闭此输入流并释放与该流关联的所有系统资源
构造方法:
创建一个BufferedInputStream并保存其参数,即输入流in
BufferedInputStream(InputStream in)
创建具有指定缓冲区大小的BufferedInputStream并保存其参数
BufferedInputStream(InputStream in,int size)
参数:
InputStream in:字节输入流
我们可以传递FileInputStream对象,缓冲流会给该对象增加一个缓冲区,提高读取效率
int size:指定缓冲流内部缓冲区大小,不指定默认
使用步骤:
1.创建FileInputStream对象,构造方法中要绑定要读取的数据源
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
3.使用BufferedInputStream对象中的方法read,读取文件
4.释放资源
*/
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("src\\com\\asus\\demo23\\a.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] bytes=new byte[1024];
int len=0;
while((len=bis.read(bytes))!=-1)
{
System.out.println(new String(bytes,0,len));
}
bis.close();
}
}
字节缓冲输出流
package com.asus.demo27;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
/*
java.io.BufferedOutputStream extends OutputStream
BufferedOutputStream:字符缓冲输出流
继承自父类的共性成员方法:
public void close()
public void flush()
public void write(byte[] b)
public void write(byte[] b,int offset,int length)
public abstract void write(int b)
构造方法:
BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,以将数据写入指定的底层输出流
BufferedOutputStream(OutputStream out,int size):创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入底层输出流
参数:
OutputStream out:字节输出流
我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
int size:指定缓冲流内部缓冲区的大小,如果不指定就默认
使用步骤:
1.创建一个FileOutputStream对象,构造方法中绑定要输出的目的地
2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象的效率
3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
4.使用BufferedOutputStraem对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
5.释放资源(会先调用flush方法刷新数据,第四步可以省略)
*/
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("src\\com\\asus\\demo25\\c.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
byte[] bytes={'j','a','v','a'};
bos.write(bytes);
bos.close();
}
}
字符缓冲输出流
package com.asus.demo27;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/*
BufferedWriter(Writer out)创建一个使用默认大小输出缓冲区的缓冲字符输出流
BufferedWriter(Writer out,int size)创建一个使用给定大小输出缓冲区的新缓冲字符输出流
参数:
Writer out:字符输出流
int size:指定缓冲区大小
特有成员方法:
void newLine()写入一个行分隔符。会根据不同的操作系统获取不同的行分隔符
使用步骤:
1.创建字符缓冲输出流对象,构造方法中传递字符输出流
2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区
3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源
*/
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter fw=new FileWriter("G:\\CodeFile\\demoPractice\\src\\com\\asus\\demo27\\a.txt",true);
BufferedWriter bw=new BufferedWriter(fw);
for (int i = 0; i < 10; i++) {
bw.write("hello world!");
// bw.write("\r\n");
bw.newLine();
}
bw.flush();
bw.close();
}
}
字符缓冲输入流
package com.asus.demo27;
/*
java.io.BufferedReader extends Reader
公共成员方法:
int read()读取单个字符并返回
int read(char[] cbuf)一次读取多个字符,将字符读入数组
void close()关闭该流并释放与之相关联的资源
特有成员方法:
String readLine()读取一个文本行。读取一行数据
行的终止符:换行符('\n')、回车('\r')或者回车后面直接跟着换行(\r\n)
返回值:包含该行内容的字符串,不包含任何终止符,如果已经到达流末尾,则返回null
使用步骤:
1.创建字符缓冲输入流对象,构造方法中传递字符输入流
2.使用字符缓冲输入流对象中的方法read/readLine读取文本
3.释放资源
*/
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("G:\\CodeFile\\demoPractice\\src\\com\\asus\\demo27\\b.txt");
BufferedReader br=new BufferedReader(fr);
/*char[] chars=new char[1024];
int len=0;
while((len=br.read(chars))!=-1)
{
System.out.println(new String(chars,0,len));
}*/
String line;
while((line=br.readLine())!=null)
{
System.out.println(line);
}
br.close();
}
}
写转换流OutputStreamWriter
package com.asus.demo28;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/*
java.io.OutputStreamWriter extends Writer
OutputStreamWriter:是字符流通向字节流的桥梁,可使用指定的charset将要写入流中的字符编码成字节
继承自父类的共性方法:
void write(int c)写入单个字符
void write(char[] cbuf)写入字符数组
abstract void write(char[] cbuf,int offset,int length)
void write(String str)
void write(String str,int offset,int length)
void flush()
void close()
构造方法:
OutputStreamWriter(OutputStream out)创建使用默认的字符编码的OutputStreamWriter
OutputStreamWriter(OutputStream out,String charsetName)创建使用指定字符集的OutputStreamWriter
参数:
Outputstream out:字节输出流,可以用来写转换之后的字节到文件中
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/gbk,默认是utf-8
使用步骤:
1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
2.使用OutputStreamWriter对象中的write,把字符转换为字节存储缓冲区中(编码)
3.使用OutputStreamWriter对象中的flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4.释放资源
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
// OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("G:\\CodeFile\\demoPractice\\src\\com\\asus\\demo28\\utf-8.txt"),"utf-8");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("G:\\CodeFile\\demoPractice\\src\\com\\asus\\demo28\\gbk.txt"),"gbk");
osw.write("这是一个UTF-8文件!");
osw.flush();
osw.close();
}
}
读转换流InputStreamReader
package com.asus.demo28;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/*
java.io.InputStreamReader
InputStreamReader:是字节流通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符
继承自父类的共性成员方法:
int read()读取单个字符并返回
int raed(char[] cbuf)一次读取多个字符,将字符读入数组
void close()
构造方法:
InputStreamReader(InputStream in)创建一个使用默认字符集的InputStreamReader
InputStreamReader(InputStream in,String charsetName)创建使用指定字符集的InputStreamReader
参数:
InputStream in:字节输入流,用来读取文件中保存的字节
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/gbk,默认是utf-8
使用步骤:
1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
2.使用InputStreamReader对象中的read方法读取文件
3.释放资源
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
InputStreamReader isr=new InputStreamReader(new FileInputStream("G:\\CodeFile\\demoPractice\\src\\com\\asus\\demo28\\gbk.txt"),"gbk");
int len=0;
while((len=isr.read())!=-1)
{
System.out.print((char)len);
}
isr.close();
}
}
原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QW3Z6hW7-1595949513992)(G:\Snipaste_pic\Snipaste_2020-07-26_00-24-44.png)]
序列化
package com.asus.demo29;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
java.io.ObjectOutputStream extends OutputStream对象的序列化流
构造方法:
ObjectOutputStream(OutputStream out)创建写入指定OutputStream的ObjectOutputStream
特有的成员方法:
void writeObject(Object obj)将指定的对象写入ObjectOutputStream
使用步骤:
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入文件中
3.释放资源
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src\\com\\asus\\demo29\\person.txt"));
oos.writeObject(new Person("张三",20));
oos.close();
}
}
反序列化
package com.asus.demo29;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
java.io.ObjectInputStream extends InputStream 对象的反序列化流
作用:把文件中保存的对象以流的方式读取出来使用
构造方法:
ObjectInputStream(InputStream in)创建从指定InputStream读取的ObjectInputStream
特有的成员方法:
Object readObject()从ObjectInputStream读取对象
使用步骤:
1.创建ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject读取保存的文件
3.释放资源
4.使用读取出来的对象(打印……)
readObject()方法声明抛出了ClassNotFoundException(class文件找不到异常)
当不存在对象的class文件时抛出此异常
反序列化的前提:
1.类必须实现Serializable
2.必须存在类对应的class文件
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("src\\com\\asus\\demo29\\person.txt"));
Object object = ois.readObject();
System.out.println(object);
ois.close();
}
}
InvalidClassException异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gIfZ9NIo-1595949513994)(G:\Snipaste_pic\Snipaste_2020-07-26_09-15-26.png)]
打印输出流
package com.asus.demo30;
import java.io.IOException;
import java.io.PrintStream;
/*
java.io.PrintStream:打印流
特点:
1.只负责数据的输出,不负责数据的读取
2.与其他输出流不同,PrintStream永远不会抛出IOException
3.特有成员方法print()、println()
构造方法:
PrintStream(File file):输出目的地是一个文件
PrintStream(OutputStream out):输出目的地是一个字节输出流
PrintStream(String fileName):输出目的地是一个文件路径
PrintStream extends OutputStream
继承自父类的成员方法:
public void close()
public void flush()
public void write(byte[] b)
public void write(byte[] b,int off,int len)
public abstract void write(int b)
注意事项:
如果使用继承的write方法写数据,那么查看数据的时候会查询编码表 97->a
如果使用自己特有的方法print()、println()写数据,写的数据原样输出 97->97
*/
public class PrintStreamDemo1 {
public static void main(String[] args) throws IOException {
PrintStream ps=new PrintStream("src\\com\\asus\\demo30\\print.txt");
ps.println("hello world!");
ps.write("This is a test!".getBytes());
ps.close();
}
}
在Java中,提供了两个类用于实现TCP通信程序:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HnIFNyEM-1595949513995)(G:\Snipaste_pic\Snipaste_2020-07-26_13-11-18.png)]
客户端
package com.asus.demo31;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回送的数据
表示客户端的类:
java.io.Socket:此类实现客户端套接字。套接字是两台机器通信的端点
构造方法:
Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号
参数:
String host:服务器主机的名称/服务器的IP地址
int port:服务器的端口号
成员方法:
OutputStream getOutputStream()返回此套接字的输出流
InputStream getInputStream()返回此套接字的输入流
void close()关闭此套接字
实现步骤:
1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
3.使用网络字节输出流Outputstream对象中的方法write,给服务器发送数据
4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
6.释放资源(Socket)
注意:
1.客户端和服务器进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
2.当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过三次握手建立连接通路
这时如果服务器没有启动,那么就会抛出异常
如果服务器已经启动,那么就可以进行交互了
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
os.write("你好,服务器,我是客户端!,我来给你发消息了。".getBytes());
InputStream is = socket.getInputStream();
byte[] bytes=new byte[1024];
int len=is.read(bytes);
System.out.println(new String(bytes,0,len));
os.close();
is.close();
}
}
服务器端
package com.asus.demo31;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
表示服务器的类:java.net.ServerSocket
构造方法:
ServerSocket(int port)创建绑定到特定端口号的服务器套接字
服务器端必须明确一件事:必须知道是哪一个客户端请求的服务器
所以可以使用accept()方法获取请求的客户端对象Socket
成员方法:
Socket accept()侦听并接受到此套接字的连接
服务器实现的步骤:
1.创建服务器ServerSocket对象和系统要指定的端口号
2.使用ServerSocket对象中的accept()方法获取到请求的客户端对象Socket
3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
4.使用网络字节输入流InputStream对象中的方法read()读取客户端发来的数据
5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
6.使用网络字节输出流OutputStream对象中的write(),给客户端回写数据
7.释放资源(Socket,ServerSocket)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket server=new ServerSocket(8888);
Socket socket = server.accept();
InputStream is = socket.getInputStream();
byte[] bytes=new byte[1024];
int len=is.read(bytes);
System.out.println(new String(bytes,0,len));
OutputStream os = socket.getOutputStream();
os.write("服务器已经收到信息,谢谢使用!".getBytes());
socket.close();
server.close();
}
}
案例:本地文件上传到服务器
//客户端程序
package com.asus.demo32;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",9999);
FileInputStream fis=new FileInputStream("src\\com\\asus\\demo32\\Client\\a.txt");
OutputStream outputStream = socket.getOutputStream();
byte[] bytes=new byte[1024];
int len=0;
while((len=fis.read(bytes))!=-1)
{
outputStream.write(bytes,0,len);
}
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
while((len=inputStream.read(bytes))!=-1)
{
System.out.println(new String(bytes,0,len));
}
fis.close();
socket.close();
}
}
//服务器端程序
package com.asus.demo32;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server=new ServerSocket(9999);
while(true)
{
Socket socket = server.accept();
new Thread(
new Runnable() {
@Override
public void run() {
try
{
File file=new File("src\\com\\asus\\demo32\\server");
if(!file.exists())
{
file.mkdirs();
}
String fileName="IT"+System.currentTimeMillis()+new Random().nextInt(99999)+".txt";
FileOutputStream fos=new FileOutputStream(file+"\\"+fileName);
InputStream inputStream = socket.getInputStream();
byte[] bytes=new byte[1024];
int len=0;
while((len=inputStream.read(bytes))!=-1)
{
fos.write(bytes,0,len);
}
socket.shutdownInput();
OutputStream outputStream = socket.getOutputStream();
outputStream.write("上传完成!".getBytes());
fos.close();
socket.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
).start();
}
}
}
BS案例
package com.asus.demo33;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
//创建BS版本的TCP服务器
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建一个服务器SerevrSocket,和系统要指定的端口号
ServerSocket server=new ServerSocket(8080);
/*
浏览器解析服务器回写html页面,页面中如果有图片,那么浏览器就会单独开启一个线程,读取服务器的图片
我们就得让服务器一直处于监听状态,客户端请求一次,服务器就回写一次
*/
while(true) {
//使用accept()获取到请求的客户端对象(浏览器)
Socket socket = server.accept();
new Thread(
new Runnable() {
@Override
public void run() {
try {
//使用Socket对象中的方法getInputStream(),获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//使用网络字节输入流InputStream对象中方法read()读取客户端的请求信息
/* byte[] bytes=new byte[1024];
int len=0;
while((len=is.read(bytes))!=-1)
{
System.out.println(new String(bytes,0,len));
}*/
//把is网络字节输入流对象转换成字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//把客户端请求信息的第一行读取出来 GET /G:/CodeFile/demoPractice/web/test.html HTTP/1.1
String line = br.readLine();
//字符串切割,截取中间部分
String[] arr = line.split(" ");
//把路径前面的/去掉
String htmlpath = arr[1].substring(1);
//创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
//必须写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
//一读一写复制文件,把服务器读取的html文件回写到客户端
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
//释放资源
fis.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
).start();
}
//server.close();
}
}
**概念:**有且只有一个抽象方法的接口
格式:
修饰符 interface 接口名称
{
public abstract 返回值类型 方法名称(可选参数信息);
//其他非抽象方法内容
}
//注意:接口中的抽象方法public abstract是可以省略的
Lambda的延迟执行
性能浪费的日志案例
注:日志可以帮助我们快速定位问题,记录程序运行过程中的情况,以便项目的监控和优化
一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出
public class LoggerDemo
{
private static void log(int level,String msg)
{
if(level==1)
{
System.out.println(msg);
}
}
public static void main(String[] args)
{
String msgA="Hello";
String msgB="World";
String msgC="Java";
log(1,msgA+msgB+msgC);
}
}
这段代码存在问题,无论级别是否满足要求,作为log方法的第二个参数,三个字符串一定会被自动拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费
优化
package com.asus.demo35;
@FunctionalInterface
public interface MessageBuilder {
//定义一个拼接消息的抽象方法,返回被拼接的消息
public abstract String buildMessage();
}
package com.asus.demo35;
/*
使用Lambda优化日志案例
Lambda特点:延迟加载
Lambda的使用前提,必须存在函数式接口
*/
public class LambdaDemo {
//定义一个显示日志的方法
public static void showLog(int level,MessageBuilder mb)
{
if(level==1)
{
System.out.println(mb.buildMessage());;
}
}
public static void main(String[] args) {
String msg1="hello";
String msg2="world";
String msg3="java";
showLog(2,()-> msg1+msg2+msg3);
/*
使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog()方法中
只要满足条件,日志的等级是1级
才会调用接口MessageBuilder中的方法buildMessage()
才会进行字符串的拼接
如果条件不满足,日志的等级不是1级
那么MessageBuilder接口中的方法buildMessage()也不会执行
所以拼接字符串的代码也不会执行
所以不会存在性能浪费问题
*/
}
}
使用Lambda作为参数和返回值
抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
例如java.lang.Runnable接口就是一个函数式接口,假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和Thread类的构造方法参数为Runnable没有本质区别。
package com.asus.demo36;
public class RunnableDemo {
//定义一个方法startThread(),方法的参数使用函数式接口Runnable
public static void startThread(Runnable run)
{
//开启多线程
new Thread(run).start();
}
public static void main(String[] args) {
//调用startThread()方法,方法的参数是一个接口,那么我们可以传递这个接口的匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"->"+"线程启动了");
}
});
//调用startThread()方法,方法的参数是一个函数式接口,所以可以传递Lambda表达式
startThread(()-> System.out.println(Thread.currentThread().getName()+"->"+"线程启动了"));
}
}
类似的,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法获取一个java.util.Compator接口类型的对象作为排序器时,就可以调该方法获取。
package com.asus.demo36;
import java.util.Arrays;
import java.util.Comparator;
public class ComparatorDemo {
//定义一个方法,方法的返回值类型使用函数式接口Comparator
public static Comparator<String> getComparator()
{
//方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
/* return new Comparator()
{
@Override
public int compare(String o1, String o2) {
//按照字符串的降序排序
return o1.length()-o2.length();
}
};*/
//方法的返回值类型是一个函数式接口,所以我们可以返回一个Lambda表达式
return (o1,o2)->o1.length()-o2.length();
}
public static void main(String[] args) {
//创建一个字符串数组
String[] arr={"aaa","b","fthrd","gg"};
Arrays.sort(arr,getComparator());
System.out.println(Arrays.toString(arr));
}
}
Supplier接口
java.util.function.Supplier
接口仅包含一个无参的方法:T get().用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。
package com.asus.demo37;
import java.util.function.Supplier;
/*
Supplier接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
*/
public class SupplierDemo {
//定义一个方法,方法的参数传递Supplier接口,泛型执行String,get方法就会返回一个String
public static String getString(Supplier<String> sup)
{
return sup.get();
}
public static void main(String[] args) {
//调用getString()方法,方法的参数Supplier是一个函数式接口,所以可以传递Lambda表达式
String s = getString(() -> "hello world!");
System.out.println(s);
}
}
Consumer接口
java.util.function.Consumer接口则正好与Supplier接口相反,它不是产生一个数据,而是消费一个数据,其数据类型由泛型决定。
抽象方法:accept
Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。基本使用如:
package com.asus.demo37;
import java.util.function.Consumer;
/*
Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据
*/
public class ConsumerDemo {
/*
定义一个方法
方法的参数传递一个字符串姓名
方法的参数传递Consumer接口,泛型使用String
可以使用Consumer接口消费字符串的姓名
*/
public static void consumerString(String name,Consumer<String> consumer)
{
consumer.accept(name);
}
public static void main(String[] args) {
//调用consumerString()方法,传递字符串姓名,方法的另一个参数是Consumer接口,是一个函数式接口,所以可以传递Lambda表达式
consumerString("Alice",(String name)->
{
//对传递的字符串进行消费
System.out.println(name);
//将字符串进行反转
String reName=new StringBuffer(name).reverse().toString();
System.out.println(reName);
});
}
}
默认方法:andThen
如果一个方法的参数和返回值全部都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen().JDK源码如下:
default Consumer<T> andThen(Consumer<? super T> after)
{
Objects.requireNonNull(after);
return (T t) -> {accept(t);after.accept(t);};
}
/*
java.util.Objects的requireNonNull静态方法将会在参数为null时主动抛出NullPointerException异常。这省去了重复编写if语句和抛出空指针异常的麻烦。
*/
package com.asus.demo37;
import java.util.function.Consumer;
/*
Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据
*/
public class ConsumerDemo {
/*
定义一个方法
方法的参数传递一个字符串姓名
方法的参数传递Consumer接口,泛型使用String
可以使用Consumer接口消费字符串的姓名
*/
public static void consumerString(String name,Consumer<String> consumer)
{
consumer.accept(name);
}
public static void main(String[] args) {
//调用consumerString()方法,传递字符串姓名,方法的另一个参数是Consumer接口,是一个函数式接口,所以可以传递Lambda表达式
consumerString("Alice",(String name)->
{
//对传递的字符串进行消费
System.out.println(name);
//将字符串进行反转
String reName=new StringBuffer(name).reverse().toString();
System.out.println(reName);
});
}
}
/*
HELLO,WORLD
hello,world
-------------------
hello,world
HELLO,WORLD
*/
例题
package com.asus.demo37;
import java.util.function.Consumer;
public class TestDemo2 {
public static void printOut(String[] arr, Consumer<String> con1,Consumer<String> con2)
{
for (String message : arr) {
con1.andThen(con2).accept(message);
}
}
public static void main(String[] args) {
String[] array={"Alice,20","Helen,25","Jackson,29"};
printOut(array,
(message)->{
String name = message.split(",")[0];
System.out.print("name:"+name);
},
(message)->{
String age = message.split(",")[1];
System.out.println(" age:"+age);
});
}
}
/*
name:Alice age:20
name:Helen age:25
name:Jackson age:29
*/
predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate接口。
抽象方法:test
predicate接口中包含一个抽象方法:boolean test(T t).用于条件判断的场景:
package com.asus.demo38;
import java.util.function.Predicate;
public class PredicateDemo {
/*
定义一个方法
参数传递一个String类型的字符串
传递一个Predicate接口,泛型使用String
使用Predicate中的方法test对字符串进行判断,并把判断的结果返回
*/
public static boolean checkString(String str, Predicate<String> pre)
{
return pre.test(str);
}
public static void main(String[] args) {
//定义一个字符串
String s="rthrth";
//调用checkString()对字符串进行校验,参数传递字符串和Lambda表达式
//对参数传递的字符串进行判断,判断字符串的长度是否大于5,并把判断的结果返回
boolean b = checkString(s, (str) -> (str.length() > 5));
System.out.println(b);
}
}
默认方法:and
将两个predicate条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法and。JDK源码如下:
default Predicate<T> and(Predicate<? super T>other)
{
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
默认方法:or
与and的“与”类似,默认方法or实现逻辑关系中的“或”。JDK源码如下:
default Predicate<T> or(Predicate<? super T> other)
{
Object.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
默认方法:negate
“与”、“或”,negate实现“非”。默认方法negate的JDK源码为:
default Predicate<T> negate()
{
return (t) -> !test(t);
}
案例:集合信息筛选
package com.asus.demo38;
import java.util.ArrayList;
import java.util.function.Predicate;
public class PredicateDemo5 {
public static boolean checkString(String str, Predicate<String> pre1,Predicate<String> pre2)
{
return pre1.and(pre2).test(str);
}
public static void main(String[] args) {
String arr[]={"迪丽热巴,女","古力娜扎,女","林俊杰,男","卫宫士郎,男"};
ArrayList<String> arrayList=new ArrayList<>();
for (String s : arr)
{
boolean b = checkString(s,
(str) -> {
String name = str.split(",")[0];
return name.length() == 4;
}, (str) -> {
String sex = str.split(",")[1];
return sex.equals("女");
});
if(b)
{
arrayList.add(s);
}
}
for (String string : arrayList) {
System.out.println(string);
}
}
}
Function接口
java.util.function.Function接口用来根据一个类型的数据得到另一个类型的数据,前者成为前置条件,后者称为后置条件。
抽象方法:apply
Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。
package com.asus.demo39;
import java.util.function.Function;
public class FunctionApplyDemo {
public static int change(String s,Function<String,Integer> function)
{
Integer in = function.apply(s);
return in;
}
public static void main(String[] args) {
String s="5438";
int i = change(s, (str) -> Integer.parseInt(str));
System.out.println(i);
}
}
默认方法:andThen
Function接口中有一个默认的andThen()方法,用来进行组合操作。JDK源代码如下:
default <V> Function<T,V> andThen(Function<? super R, ? extends V> after)
{
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
package com.asus.demo39;
import java.util.function.Function;
public class Function_andThenDemo {
public static String change(String str, Function<String,Integer> fun1,Function<Integer,String> fun2)
{
return fun1.andThen(fun2).apply(str);
}
public static void main(String[] args) {
String s="123";
String string = change(s,
(str) -> {
int i = Integer.parseInt(str);
return i + 10;
},
(str) -> {
return String.valueOf(str);
});
System.out.println(string);
}
}
案例
package com.asus.demo40;
import java.util.ArrayList;
import java.util.List;
/*
使用Stream流的方式,遍历集合,对集合中的数据进行过滤,Stream流是JDK1.8之后出现的
*/
public class StreamDemo1 {
public static void main(String[] args) {
//创建一个List集合,存储姓名
List<String> list=new ArrayList<>();
list.add("胡歌");
list.add("赵丽颖");
list.add("张无忌");
list.add("周芷若");
list.add("张三丰");
list.add("张三");
//对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
//再对新集合进行过滤,只要姓名长度为3的人,存储到一个新集合中,遍历该集合
list.stream()
.filter(name->name.startsWith("张"))
.filter(name->name.length()==3)
.forEach(name-> System.out.println(name));
}
}
获取流
java.util.stream.Stream是Java8新加入的最常用的流接口。(这并不是一个函数式接口)
获取一个流非常简单,有以下几种常用的方式:
根据Collection获取流
package com.asus.demo40;
import java.util.*;
import java.util.stream.Stream;
/*
java.util.stream.Stream是Java 8新加入的最常用的流接口
获取流接口的方法:
所有Collection集合都可以通过stream默认方法获取流;
default Stream stream()
Stream接口的静态方法of可以获取数组对应的流。
static Stream of(T …… values)
参数是一个可变参数,那么我们可以传递一个数组
*/
public class GetStreamDemo {
public static void main(String[] args) {
//把集合转换成为Stream流
List<String> list=new ArrayList<>();
Stream<String> stream1=list.stream();
Set<String> set=new HashSet<>();
Stream<String> stream2 = set.stream();
Map<String,String> map=new HashMap<>();
//获取键,存储到一个Set集合中去
Set<String> keyset = map.keySet();
Stream<String> stream3 = keyset.stream();
//获取值,存储到一个Collection集合中去
Collection<String> values=map.values();
Stream<String> stream4 = values.stream();
//获取键值对(键与值的映射关系 entrySet)
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream5 = entries.stream();
//把数组转换为stream流
Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);
//可变参数可以传递数组
Integer[] arr={1,2,3,4,5};
Stream<Integer> stream7 = Stream.of(arr);
String[] arr2={"a","bb","ccc"};
Stream<String> stream8 = Stream.of(arr2);
}
}
常用方法
逐一处理:forEach
package com.asus.demo40;
import java.util.stream.Stream;
/*
void forEach(Consumer super T> action);该方法接受一个Consumer接口函数,会将每一个流元素交给该函数进行处理
Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据
简言之:
forEach()方法,用来遍历流中的数据
是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
*/
public class Stream_forEach {
public static void main(String[] args) {
//获取一个stream流
Stream<String> stream=Stream.of("Helen","Alice","Saber","Tom","Jackson");
stream.forEach(name-> System.out.println(name));
}
}
过滤:filter
可以通过filter方法将一个流转换成另一个子集流。
package com.asus.demo40;
import java.util.stream.Stream;
/*
Stream流中常用的方法filter:用于对Stream流中的数据进行过滤
Stream filter(Predicate super T> predicate);
filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
*/
public class Stream_filter {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream1 = Stream.of("张三丰", "张无忌", "赵敏", "周芷若", "张翠山");
Stream<String> stream2 = stream1.filter(name -> name.startsWith("张"));
stream2.forEach(name-> System.out.println(name));
/*
stream流属于管道流,只能被消费一次
第一个stream流调用完毕方法,数据就会流转到下一个Stream上
而这时第一个Stream流已经使用完毕,就会关闭了
所以第一个Stream流就不能再调用方法了
*/
}
}
映射:map
如果需要将流中的元素映射到另一个流中,可以使用map方法。
<R> Stream<R> map(Function<? super T,? extends R> mapper);
案例
package com.asus.demo40;
import java.util.stream.Stream;
/*
如果需要将流中的元素映射到另一个流中,可以使用map方法。
Stream map(Function super T,? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换成另一种R类型的流
Function中的抽象方法:
R apply(T t);
*/
public class Stream_map {
public static void main(String[] args) {
//获取一个String类型的stream流
Stream<String> stream = Stream.of("1", "2", "3", "4");
//使用map方法,把字符串类型的整数转换成(映射)为Integer类型的整数
Stream<Integer> stream1 = stream.map(s -> Integer.parseInt(s));
//遍历
stream1.forEach(i-> System.out.println(i));
}
}
统计个数:count
正如旧集合Collection当中的size方法一样,流提供的count方法来数一数其中的元素个数,是一个终结方法:
long count();
取用前几个:limit
limit方法可以对流进行截取,只取用前n个。
是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法。
stream<T> limit(long maxSize);
跳过前几个:skip
如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:
Stream<T> skip(long n);
组合:concat
如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat:
static <T> Stream<T> concat(Stream<? extends T> a,Stream<? extends T>b)
通过对象名引用成员方法
package com.asus.demo41;
public interface Printable {
public abstract void print(String s);
}
package com.asus.demo41;
public class Method {
public void printUpperCase(String str)
{
System.out.println(str.toUpperCase());
}
}
package com.asus.demo41;
public class MethodDemo {
public static void printString(Printable p)
{
p.print("hello,world");
}
public static void main(String[] args) {
printString(s-> System.out.println(s.toUpperCase()));
printString(s->
new Method().printUpperCase(s)
);
//方法引用优化Lambda表达式
Method obj = new Method();
printString( obj::printUpperCase);
}
}
通过类名称引用静态方法
package com.asus.demo42;
@FunctionalInterface
public interface Calsable {
public abstract int calsAbs(int n);
}
package com.asus.demo42;
public class Calculate {
public static int method(int n,Calsable cal)
{
return cal.calsAbs(n);
}
public static void main(String[] args) {
int i = method(-10, number -> Math.abs(number));
System.out.println(i);
int j = method(-100, Math::abs);
System.out.println(j);
}
}
通过super引用成员方法
package com.asus.demo43;
@FunctionalInterface
public interface Greetable {
void greet();
}
package com.asus.demo43;
public class Father {
public void sayHello()
{
System.out.println("Hello,I am father!");
}
}
package com.asus.demo43;
public class Son extends Father{
@Override
public void sayHello() {
System.out.println("Hello,I am son!");
}
public void method(Greetable g)
{
g.greet();
}
public void show()
{
/* method(()->{
Father father = new Father();
father.sayHello();
});*/
//因为有子父类关系,所以存在的一个关键字super代表父类,我们可以直接使用super调用父类的成员方法
//method(()->{super.sayHello();});
/*
使用方法引用优化Lambda表达式
使用super引用类的成员方法
super是已经存在的
父类的成员方法sayHello()也是存在的
所以我们可以直接使用super引用父类的成员方法
*/
method(super::sayHello);
}
public static void main(String[] args) {
Son son = new Son();
son.show();
}
}
通过 this引用成员方法
this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法引用。
类的构造器引用
package com.asus.demo44;
@FunctionalInterface
public interface PersonBuilder {
//定义一个方法,根据传递的姓名,创建Person对象返回
Person buildPerson(String name);
}
package com.asus.demo44;
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
package com.asus.demo44;
/*
类的构造器(构造方法)引用
*/
public class Test {
//定义一个方法,参数传递姓名和PersonBuilder接口,方法中通过姓名创建Person对象
public static void printName(String name,PersonBuilder pb)
{
Person person = pb.buildPerson(name);
System.out.println(person);
}
public static void main(String[] args) {
//调用printName方法,方法的参数PersonBuilder接口是一个函数式接口,可以传递Lambda表达式
printName("赵丽颖",name->new Person(name));
/*
使用方法引用优化Lambda表达式
构造方法new Person(name)已知
创建对象已知 new
就可以使用Person引用new创建对象
*/
printName("杨幂",Person::new);
}
}
数组的构造器引用
package com.asus.demo45;
/*
一个创建数组的函数式接口
*/
@FunctionalInterface
public interface ArrayBuilder {
//定义一个创建int类型数组的方法,参数传递数组的长度,返回创建好的int类型数组
int[] buildArray(int length);
}
package com.asus.demo45;
/*
数组的构造器引用
*/
public class Test {
/*
定义一个方法,方法的参数传递创建数组的长度和ArrayBuilder接口
方法内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回
*/
public static int[] createArray(int length,ArrayBuilder ab)
{
return ab.buildArray(length);
}
public static void main(String[] args) {
//调用createArray()方法,传递数组的长度和Lambda表达式
int[] array = createArray(3, len -> new int[len]);
System.out.println(array.length);
/*
使用方法引用优化Lambda表达式
已知创建的就是int[]数组
数组的长度也是已知的
就可以使用方法引用
int[]引用new,根据参数传递的长度来创建数组
*/
int[] array1 = createArray(6, int[]::new);
System.out.println(array1.length);
}
}
ethod(()->{super.sayHello();});
/*
使用方法引用优化Lambda表达式
使用super引用类的成员方法
super是已经存在的
父类的成员方法sayHello()也是存在的
所以我们可以直接使用super引用父类的成员方法
*/
method(super::sayHello);
}
public static void main(String[] args) {
Son son = new Son();
son.show();
}
}
**通过 this引用成员方法**
this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法引用。
**类的构造器引用**
```java
package com.asus.demo44;
@FunctionalInterface
public interface PersonBuilder {
//定义一个方法,根据传递的姓名,创建Person对象返回
Person buildPerson(String name);
}
package com.asus.demo44;
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
package com.asus.demo44;
/*
类的构造器(构造方法)引用
*/
public class Test {
//定义一个方法,参数传递姓名和PersonBuilder接口,方法中通过姓名创建Person对象
public static void printName(String name,PersonBuilder pb)
{
Person person = pb.buildPerson(name);
System.out.println(person);
}
public static void main(String[] args) {
//调用printName方法,方法的参数PersonBuilder接口是一个函数式接口,可以传递Lambda表达式
printName("赵丽颖",name->new Person(name));
/*
使用方法引用优化Lambda表达式
构造方法new Person(name)已知
创建对象已知 new
就可以使用Person引用new创建对象
*/
printName("杨幂",Person::new);
}
}
数组的构造器引用
package com.asus.demo45;
/*
一个创建数组的函数式接口
*/
@FunctionalInterface
public interface ArrayBuilder {
//定义一个创建int类型数组的方法,参数传递数组的长度,返回创建好的int类型数组
int[] buildArray(int length);
}
package com.asus.demo45;
/*
数组的构造器引用
*/
public class Test {
/*
定义一个方法,方法的参数传递创建数组的长度和ArrayBuilder接口
方法内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回
*/
public static int[] createArray(int length,ArrayBuilder ab)
{
return ab.buildArray(length);
}
public static void main(String[] args) {
//调用createArray()方法,传递数组的长度和Lambda表达式
int[] array = createArray(3, len -> new int[len]);
System.out.println(array.length);
/*
使用方法引用优化Lambda表达式
已知创建的就是int[]数组
数组的长度也是已知的
就可以使用方法引用
int[]引用new,根据参数传递的长度来创建数组
*/
int[] array1 = createArray(6, int[]::new);
System.out.println(array1.length);
}
}