1.1多态的概述(记忆)
-
什么是多态
同一个对象,在不同时刻表现出来的不同形态
-
多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
1.2多态中的成员访问特点(记忆)
-
成员访问特点
-
成员变量
编译看父类,运行看父类
-
成员方法
编译看父类,运行看子类
-
1.3多态的好处和弊端(记忆)
-
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
-
弊端
不能使用子类的特有成员
2. 内部类
2.1 内部类的基本使用(理解)
-
内部类概念
- 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
······
1,Iterator遍历器
遍历过程中对集合添加元素会出现并发修改异常(modCount!=expectedModCount),而Listliterator遍历器在相同情况下添加元素,add操作会相应对expectedModCount + 1,所以Listliterator不会发生并发修改异常。
2,增强for循环
格式:
for(元素数据类型 变量名:数组或Collection集合 ){
//在此处使用变量,该变量就是元素
}
其内部是一个literator遍历器
3,数组特点:查询快,增删慢(ArrayList)
链表特点:查询慢,增删块(LinkedList)
LinkedList的特有功能:针对链表头和链表尾所进行的添加(addFirst()/addLast()),获取(getFirst())和删除(removeFirst())操作
4,Set集合特点:a,不包含重复元素的集合 b,没有带索引的方法,不能使用普通for循环遍历 c,对迭代顺序不做任何保证
5,哈希值
调用HashCode()返回对象的哈希值
默认情况下,对象的哈希值不相同。通过重写hashcode(),哈希值可能相同
6,LinkedHashSet集合特点
a,哈希表和链表实现的Set接口,具有可预测的迭代次序
b,链表保证元素有序
c,元素唯一
7,TreeSet
a,元素有序,有序是指按照构造方法中比较器的排序方法进行排序
b,不带索引不能进行for循环
c,不包含重复元素
比较器:
对于对象来说,既要写主要条件,更要写次要条件
public int compareTo(Student s){
int num = this.age - s.age;
return num;
}
假设有s1,s2两个对象,创建s2时构建比较器进行比较,此时this.age表示s2.age , s.age表示s1.age
返回(正数/0/负数)则表示s2的年龄(大于/等于/小于)s1的年龄。
比较姓名+年龄
public int compareTo(Student s){
int num1 = this.age - s.age;
int num2 = num1==0?this.name.compareTo(s.name):num1
return num2;
}
匿名内部类实现比较器
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>(){
@Override
public int compare(Student s1,Student s2){
//this.age s.age
//s1.age s2.age
int num1 = s1.getAge - s2.getAge;
int num2 = num1==0?s1.getName.compareTo(s2.getName):num1
return num2;
}
})
8,泛型
泛型的定义格式:
修饰符 class 类名<类型>{ }
public class Generic{ }
此处T可以为任意标识,如T,E,K,V
泛型接口
修饰符 interface 接口名 <类型>{ }
public interface Generic
类型通配符
类型通配符:<?>
List<?>:表示元素类型位置的List,它的元素可以匹配任何的类型
类型通配符上限:<? extends 类型>
类型通配符下限:<? super 类型>
9,可变参数
方法的参数可变
修饰符 返回值类型 方法名(数据类型… 变量名){ }
public static int sum(int… a){ }
其中a为集合形式。
如果一个方法要求多个参数,其中包含可变参数,可变参数放在最后
可变参数的使用
public static List asList<T… a>: 返回由指定数组支持的固定大小的列表
返回的集合不能做增删惭怍,可以做修改操作
public static List of<E… elements>: 返回由任意数量元素的不可变列表
返回的集合不能做增删改惭怍。
public static Set of<E… elements>: 返回一个包含任意数量元素的不可变集合
给元素时不能给重复元素
返回的集合不能做增删操作,没有修改操作
10,Map集合概述和使用
interface Map<K,V> K:键的类型 V:值的类型
将键映射到值的对象,不能包含重复的键,每个键可以映射到最多一个值
创建Map集合的对象
多态的方式
具体的实现类HashMap
Map集合的基本功能:
Map集合的获取功能
获取键值对对象的集合
Set<Map.Entry<K,V>> entrySet(): 获取所有键值对对象的集合
getKey()得到键
getValue()得到值
11,IO流
1,File
File: 它是文件和目录路径名的抽象表示
文件和目录是可以通过File封装成对象的
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。将来通过具体的操作把这个路径的内容转换为具体的存在
File类创建功能
createNewFile():
如果文件不存在就创建并返回True
如果文件存在就不创建并返回False
其他创建功能特点相同
a,创建文件用创建文件的功能,创建目录用创建目录的功能。
b,如果存在一个文件/文件夹与想要创建的文件夹/文件有相同的名字,则文件夹/文件创建不成功
File类判断和获取功能
File类删除功能
如果将要删除的目录下有内容,必须先删除内容再删除目录
2,字节流
IO流分类
按照数据的流向:
输入流:读数据
输出流:写数据
按照数据类型来分:
字节流
字符流(可以读懂的内容)
字节流抽象基类:
InputStream
OutputStream
子类名称以其父类名作为子类名的后缀
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
//做了三件事
A:调用系统功能创建了文件
B:创建了字节输出流对象
C:让字节输出流对象指向创建好的文件
fos.write(97);
//写入的是ascii码值为97的a
fos.close();
//关闭此文件输出流并释放与此流相关的任何系统资源
字节流写数据的3种方式
字符串.getBytes();
得到字符串对应的字符数组
字节流写数据的两个小问题:
1,字节流写数据如何实现换行?
windows:\r\n
linux:\n
mac:\r
2,字节流写数据如何实现追加写入?
public FileOutputStream(String name,
boolean append)
throws FileNotFoundException创建文件输出流以指定的名称写入文件。 如果第二个参数是true ,则字节将写入文件的末尾而不是开头。
字节流写入加异常处理
finally:在异常处理时提供finally块来执行所有清除操作。比如说IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
FileOutputStream fos = null;
try {
fos = new FileOutputStream("myByteStream\\fis.txt");
//fos = new FileOutputStream("Z:\\myByteStream\\fis.txt");
//会报IOException 与 空指针异常
} catch(IOException e){
e.printStackTrace();
} finally{
if(fos!=null){
try{
fos.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
字节流读数据(一次读一个字节数据)
FileInputStream fis = new FileInputStream(myByteStream\\fos.txt);
int by;
//(by=fis.read())!=-1
//1,fis.read() :读数据
//2,by=fis.read():把读取到的数据赋值给by read():每次只读一个字符
//3,by!=-1:判度是否读取到文章的末尾
while((by=fis.read())!=-1){
System.out.print((char)by)
}
字节流读数据(一次读一个字节数组)
FileInputStream fis = new FileInputStream(myByteStream\\fos.txt);
byte[] bys= new byte[1024];//1024及其整数倍
int len;
while((len=fis.read(bys))!=-1){
//fos.write(bys,0,len);//fos写入的代码
System.out.print(new String(bys,0,len));//0:初始坐标,len:读取长度
}//复制图片只需改写文件名部分
fis.close();
fos.close();
字节缓冲流
BufferOutputStream(OutputStream out)
BufferInputStream(InputStream in)
应用程序可以向底层输出流写入字节,而不必为输入的每个字节调用底层系统
为什么缓冲流的输入不是具体路径而是输入流?
字节缓冲流仅仅提供缓冲区,真正的读写操作还要依靠基本的字节流对象进行操作
//FileOutputStream fos = new FileOutputStream("myByteStream\\fis.txt");
//BufferedOutputStream bos = new BufferedOutputStream(fos);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\fis.txt"));
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
bos.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myByteStream\\fis.txt"));
读写速度测试:
3,字符流
一个汉字存储:
如果是GBK编码:占用2个字节
如果是UTF-8编码:占用3个字节
字符流=字节流+编码表
汉字无论采用那种编码方式存储,第一个字节都是负数
//编码
byte[] bys = s.getBytes("GBK");
//解码
String ss = new String(bys,"GBK")
字符流写数据的5种方式
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
osw.write(97);
osw.flush();//刷新流
//字符流相对于字节流来说,会先进去缓冲区,所以需要刷新缓冲区
osw.close();//关闭流,先刷新再关闭
字符流读数据的2种方式
当只涉及读写操作,不涉及编码转换问题时,可以使用InputStreamReader的直接字类FileReader和OutputStreamWriter的直接字类FileWriter。
字符缓冲流
BufferedWriter:将文本写入字符输入流,缓冲字符,可指定缓冲区大小但默认值足够大,可用于大多数用途。
BufferedReader:从字符输入流读取文本,缓冲字符,可指定缓冲区大小但默认值足够大,可用于大多数用途。
构造方法:
BufferedWriter(Writer out)
BufferedReader(Reader in)
字符缓冲流特有的功能
BufferedWriter:
void newLine():写一行行分隔符,行分隔符字符串由系统属性定义
BufferedReader:
public String readLine():读一行文字。不包括任何终止字符(换行符),如果流的结尾已经到达,则为null。
复制文件的异常处理
try…catch…finally的做法
try{
//可能出现异常的代码
}catch(异常类名 变量名){
//异常的处理代码
}finally{
//执行所有清除操作
}
JD7的改进方案(最优)
try(定义流对象){
//可能出现异常的代码
}catch(异常类名 变量名){
//异常的处理代码
}
//自动释放资源
JDK9改进方案kenen
//定义输入流对象
//定义输出流对象
try(输入流对象;输出流对象){
//可能出现异常的代码
}catch(异常类名 变量名){
//异常的处理代码
}
//自动释放资源
4,特殊操作流
标准输入输出流
System类中有两个静态的成员变量:
public static final InputStream in:标准输入流
public static final PrintStream out:标准输出流
输入:
自己实现键盘录入数据:
bufferedReader br = new BufferedReader(new InputStreamReader(System.in));
写起来太麻烦,java就提供了一个类显示键盘录入
Scanner sc = new Scanner(System.in);
输出:
输出语句的本质:是一个标准的输出流
PrintStream ps = System.out;
PrintStream类有的方法,System.out都可以使用
打印流
打印流分类:
字节打印流:PrintStream
字符打印流:PrintWriter
打印流特点:只负责输出数据,不负责读取数据
有自己的特有方法
字节打印流:
PrintStream(String fileName):使用指定的文件名创建新的打印流
使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出
字符打印流PrintWriter的构造方法
对象序列化流
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
字节序列写道文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
对象序列化流:ObjectOutputStream
对象反序列化流:ObjectInputStream
对象序列化流
对象序列化流:ObjectOutputStream
将java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
构造方法
ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
void writeObject(Object obj):将指定的对象写入ObjectOutputStream
注意:
一个对象要想被序列化,该对象所属的类必须实现Serializable接口
Serializable是一个标记接口,实现该接口,不需要重写任何方法
对象反序列化流
对象反序列化流:ObjectInputStream
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据与对象
构造方法:
ObjectInputStream(InputStream in): 创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:
Object readObject(): 从ObjectInputStream读取一个对象
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
会出问题,抛出InvalidClassException异常
如果出了问题,如何解决呢?
给对象所属的类加一个serialVersionUID
private static final long serialVersionUID =42L
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
Properties
Properties概述:
是一个Map体系的集合类
Properties可以保存到流中或从流中加载
注意:
1,Properties无泛型
properties<String,String> prop = new Properties<String,String>();错误
Properties prop = new Properties();正确
2,Properties中存储的对象都为Object类
Properties作为集合的特有方法:
Properties和IO流结合的方法:
12,多线程
1),实现多线程
①进程
进程:是正在运行的程序
是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
②线程
线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序(记事本程序)
多线程:一个进程如果有多条执行路径,则称为多线程程序(扫雷程序)
③多线程实现方式
方式1:继承Thread类
a,定义一个类MyThread继承Thread类
b,在MyThread类中重写run()方
c,创建MyThread类对象
d,启动线程
两个小问题:
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
④设置和获取线程名称
Thread类中设置和获取线程名称的方法
void setName(String name):将此线程的名称更改为等于参数name
String getName():返回此线程的名称
通过构造方法也可以设置线程名称
如何获取main()方法所在的线程名称?
public static Thread currentThread():返回对当前正在执行的线程对象的引用⑤线程调度
线程有两种调度模型
分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的CPU获取的CPU时间片相对多一些
Java使用的是抢占式调度模型
Thread类中设置和获取线程优先级的方法:
public final int getPriority():返回此线程的优先级
public final void setPriority():更改此线程的优先级
线程默认优先级是5;线程优先级的范围是:1-10(数字越大,优先级越高)
线程优先级高仅仅表示线程获取的CPU时间片的几率高
⑥线程控制
⑦线程的生命周期
⑧多线程的实现方式
方式2:实现runnable接口
a,定义一个类MyRunnable实现Runnable接口
b,在MyRunnable类中重写run()方法
c,创建MyRunnable类的对象
d,创建Thread类的对象,把MyRunnable对象作为构造方法的参数
e,启动线程
多线程的实现方案有两种
继承Thread类
实现Runnable接口
相比继承Thread类,实现Runnable接口的好处
避免了Java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
2),线程同步
①数据安全问题
为什么会出现数据安全问题?
a,是否是多线程环境
b,是否有共享数据
c,是否有多条语句操作共享数据
如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境(破坏abc中的任一条件)
怎么实现呢?
把多条语句操作共享数据的代码给锁起来,让任何时刻只能有一个线程执行即可
java提供了同步代码块来解决
②同步代码块
锁多条语句共享数据,可以使用同步代码块实现
格式:
synchronized(任意对象){
多条语句操作共享数据的的代码
}
synchronized(任意对象):就相当于给代码块加锁了,任意对象就可以看成一把锁 (把对象定义在run()方法外,这样多个程序就共用一把锁)
同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很消耗资源的,无形中会降低程序的运行效率
③同步方法
同步方法:就是把synchronized关键字加到方法上
格式:修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步方法的锁对象是什么呢?
this
同步静态方法:就是把synchronized关键字加到静态方法上
格式:修饰符 static synchronized 返回值类型 方法名(方法参数){ }
同步静态方法的锁对象是什么呢?
类名.class
④线程安全的类
StringBuffer
线程安全,可变的字符序列
从版本JDK5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步。
Vector
从Java 2平台v1.2,这个类被改造为实现List接口,使其成为成员Java Collections Framework 。 与新集合实现不同, Vector是同步的。 如果不需要线程安全的实现,建议使用ArrayList代替Vector 。
Hashtable
该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键值或值。
从Java 2平台v1.2,这个类被改造为实现Map接口,使其成为成员Java Collections Framework 。 与新的集合实现不同, Hashtable是同步的。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的并发实现,那么建议使用ConcurrentHashMap代替Hashtable 。
Vector与Hashtable在使用中一般被synchronizedList(List list) 、synchronizedMap(Map<K,V> m) 、synchronizedSet(Set s) 所替代
⑤Lock锁
Lock实现提供比使用Synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock():创建一个ReentrantLock的实例
private Lock lock = new ReentrantLock();
public void run(){
while(True){
try{
lock.lock();
执行代码
}finally{
lock.unlock();
}
}
}
3),生产者消费者
①生产者消费者模型概述
为了体现生产和消费过程中的等待和破解,Java就提供了几个方法供我们使用,这几个方法在Object类中
Object类的等待和唤醒方法
13,网络编程
1),网络编程入门
①网络编程概述
计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
网络编程
在网络通信协议下,实现网络互联的不同计算机上运行的程序间可以进行数据交换
②网络编程的三要素
IP地址、端口、协议
③IP地址
常用命令:
Ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
特殊IP地址
127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用
④InetAddress的使用
为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress供我们使用
InetAdress:此类表示Internet协议(IP)地址
2),UDP通信程序
①UDP通信原理
Java提供了DatagramSocket类作为基于UDP协议的Socket
②UDP发送数据
发送数据的步骤
a,创建发送端的Socket对象(DatagramSocket)
DatagramSocket()
b,创建数据,并把数据打包
DatagramPacket(byte[] buf,int length,InetAddress address,int port)
c,调用DatagramScoket对象的方法发送数据
void send(DatagramPacket p)
d,关闭发送端
void close()
③UDP接收数据
接收数据的步骤
a,创建接收端的Socket对象(DatagramSocket)
DatagramSocket()
b,创建数据包,用于接收数据
DatagramPacket(byte[] buf,int lengt)
c,调用DatagramScoket对象的方法接收数据
void receive(DatagramPacket p)
d,解析数据包,并把数据在控制台显示
byte[] getData()
int getLength()
e,关闭接收端
void close()
2),TCP通信程序
①TCP通信原理
java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
java为客户端提供了Socket类,为服务器端提供了ServerSocket类
②TCP发送数据
发送数据的步骤
a,创建客户端的Socket对象(Socket)
Socket(String host,int port)
b,获取输入流,写数据
OutputStream getOutputStream()
c,释放资源
void close()
③TCP接收数据
a,创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
b,监听客户端连接,返回一个Socket对象
Scoket accept()
b,获取输入流,读数据,并把数据显示在控制台上
InputStream getInputStream()
c,释放资源
void close()
自定义结束标记:shutdownOutput()方法
14,Lambda表达式
1)函数式编程思想概述
在数学中,函数就是有输入量、输出量的一套计算方法,也就是“拿数据的操作”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面想对象的复杂语法:“强调做什么,而不是以什么形式去做”
而Lambda表达式就是函数式思想的体现
new Thread( () -> {
System.out.println("多线程程序启动了");
} ).start();
2)Lambda表达式的标准格
Lambda表达式的格式
格式:(形式参数)->{ 代码块 }
形式参数:如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可
->:由英文中画线和大于符号组成,固定写法。代表指向动作
代码块:是我们具体要做的事情,也就是以前我们写的方法体的内容
Lambda表达式的使用前提
有一个接口
接口中有且仅有一个抽象方法
3)Lambda表达式的省略模式
省略规则:
参数类型可以省略。但是有多个参数的情况下,不能只省略一个
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
4)Lambda表达式的注意事项
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对应的接口:Runnable r = ()->System.out.println(“Lambda表达式”);
根据调用方法的参数得知Lambda对应的接口:new Thread = ()->System.out.println(“Lambda表达式”).start();
5)Lambda表达式与匿名内部类的区别
所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
15,接口组成更新
1)接口组成更新概述
接口的组成
常量:
public static final
抽象方法:
public abstract
默认方法(Java 8)
静态方法(Java 8)
私有方法(Java 9)
2)接口中默认方法
接口中默认方法的定义格式
格式:public default 返回值类型 方法名(参数列表){ }
范例:public default void show3(){ }
接口中默认方法的注意事项:
默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
public可以省略,default不能省略
3)接口中静态方法
接口中静态方法的定义格式
格式:public static 返回值类型 方法名(参数列表){ }
范例:public static void show3(){ }
接口中静态方法的注意事项:
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public interface Inter{
public static void test(){
System.out.println("Inter 中的静态方法被执行了")
}
}
class{
public static void main(){
Inter i = new InterImpl();
Inter.test();
}
}
public可以省略,static不能省略
4)接口中私有方法
Java 9 中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔:Java 8允许在接口中i当以带方法体的默认方法和静态方法。这样就可能引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
接口中私有方法的定义格式:
格式1:private返回值类型 方法名(参数列表){ }
范例1:private void show(){ }
格式2:private static 返回值类型 方法名(参数列表){ }
范例2:private static void method(){ }
接口中私有方法的注意事项:
默认方法可以调用私有的静态方法和非静态方法
静态方法只能调用私有的静态方法
16,方法引用
1)方法引用符
方法引用符:
该符号为引用运算符,而它所在的表达式被称为方法引用
回顾一下我们在体验方法中引用的代码
Lambda表达式:usePrintable(s->System.out.println(s));
分析:拿到参数s之后通过Lambda表达式,传递给System.out.println()方法处理
方法引用:usePrintable(System.out::println);
分析:直接使用System.out中的println方法来取代Lambda,代码更加简洁
推导与省略
如果使用Lambda,那么根据“可推到就是可省略”的原则,无需指定参数类型,也无需指定重载形式,他们都将被自动推导
如果使用方法引用,也是同样可以根据上下文进行推导的
方法引用是Lambda的孪生兄弟
2)Lambda表达式支持的方法引用
常见的引用方式:
引用类方法、引用对象的实例方法、引用类的实例方法、引用构造器
3)引用类方法
引用类方法,其实就是引用类方法的静态类方法
格式:类名::静态方法
范例:Integer::parseInt
Integer类的方法:public static int parseInt(String s)将此String 转换为int类型的数据
注意:Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
4)引用对象的实例方法
引用对象的实例方法,其实就是引用类中的成员方法
格式:对象::成员方法
范例:“HelloWorld”::toUpperCase
String类中的方法:public String toUpperCase()将此String所有字符转换为大写
5)引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
格式:类名::成员方法
范例:String::substring
String类中的方法:public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个字串,字串的长度为endIndex-begingIndex
注意:Lambda表达式被类的实例方法替代的时侯,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
6)引用构造器
引用构造器,其实就是引用构造方法
格式:类名::new
范例:Student::new
注意:Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
17,函数式接口
1)函数式接口概述
函数式接口概述:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导
如何检测一个接口是不是函数式接口?
@FunctionalInterface
放在接口定义的上方:如果接口是函数式接口,则编译通过;如果不是,编译失败
2)函数式接口作为方法的参数
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
startThread(()->System.out.println(Thread.currentThread().getName()+“线程启动”))
3)函数式接口作为方法的返回值
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
private static Comparator getComparator(){
return (s1,s2) -> s1.length() - s2.length();
}
4)常用的函数式接口
4.1 supplier接口
supplier :包含一个无参的方法
T get():获得结果
该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
4.2 Consumer接口
Consumer : 包含两个方法
void accept(T t):对给定的参数执行此操作
default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
4.3)predicate接口
Predicate:常用的四个方法
boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
default Predicate negate():返回一个逻辑的否定,对应逻辑非
default Predicate and(Predicate other):返回一个组合判断,对应短路与
default Predicate or(Predicate other):返回一个组合判断,对应短路或
Predicate 接口通常用于判断参数是否满足指定的条件
4.4)Function接口
Function<T,R> : 常用的两个方法
R apply(T t):将此函数应用于给定的参数
default Function andThen(Function after): 返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
Function<T,R> 接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
18,Stream流
1)Stream流的生成方式
Stream流的使用
生成流:
通过数据源(集合,数组等)生成流
list.stream()
中间操作:
一个流后面可以跟随零个或多个中间操作,其目的主要是打开流。做出某种程度的数过滤/映射,然后返回一个新的流,交给下一个操作使用
filter()
终结操作:
一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作
forEach()
2)Stream流的常见生成方式
Collection体系的集合可以使用默认方法stream()生成流
default Stream stream()
Map体系的集合间接的生成流
数组可以通过Stream接口的静态方法of(T…values)生成流
3)Stream流的常见中间操作方法
Stream< T > filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值
Stream< T > limit(long maxSize): 返回此流中的元素组成的流,截取前指定参数个数的数据
Stream< T > skip(long n): 跳过指定参数个数的数据,返回由该流的剩余元素组成的流
static < T > Stream< T > concat(Stream a,Stream b):合并a和b两个流为一个流
Stream< T > distinct():返回由该流的不同元素(根据Object equals(Object))组成的流
Stream< T > sorted():返回由此流的元素组成的流,根据自然顺序排序
Stream< T > sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
< R > Stream< R > map(Function mapper):返回由给定函数应用于此流的元素的结果组成的 Function接口中的方法 R apply(T t)
IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
IntStream:表示原始int流 ToIntFunction接口中的方法 int applyAsInt(T value)
4)Stream流的常见终结操作方法
Stream流的常见终结操作方法
void forEach(Consumer action):对此流的每个元素执行操 Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
long count():返回此流中的元素数
5)Stream流的收集操作
对数据使用Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办?
Stream流的收集方法
R collect(Collector collector)
但是这个收集方式的参数是一个Collector接口
工具类Collectors提供了具体的收集方式
public static < T > Collector toList():把元素收集到List集合中
public static < T > Collector toSet():把元素收集到Set集合中
public stati Collector toMap( Function keyMapper,Function valueMapper):把元素收集到Map集 合中
19,反射
1)类加载器
1.1类加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化,这三个步骤对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载
就是指将class文件读入内存,并为之创建一个java.lang.Class对象
任何类被使用时,系统都会为之建立一个java.lang.Class对象
类的连接
验证阶段:用于检验被加载的类是否由正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
在该阶段,主要就是对类变量进行初始化
类的初始化步骤
假如类还未被加载和连接,则程序先加载并连接该类
假如该类的直接父类还未被初始化,则先初始化其直接父类
假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机:
创建类的实例
调用类的类方法
访问类或者接口的类变量,或者为该类变量赋值
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
1.2 类加载器
类加载器的作用
负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
了解加载机制能更好的理解程序的运行
JVM的类加载机制
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他的Class也将由该类加载器负责载入,除非显示使用另一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图记载该Class,只有父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换程Class对象,存储到缓存区
ClassLoader:是负责加载类的对象
java运行时具有以下内置类加载器
Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其他祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
System class loader:它也被称为应用程序类加载器,与平台加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
ClassLoader中的两个方法
static ClassLoader getSystemClassLoader():返沪用于委派的系统类加载器
ClassLoader getParant():返回父类加载器进行委派
2)反射
2.1 反射概述
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以拓展。
2.2 获取Class类的对象
我们要想通过反射去使用一个类,首先我们要获取到该类的字节码对象,也就是类型为Class类型的对象,这里我们提供三种方式获取Class类型的对象
使用类的class属性来获取该类对应的Class对象。举例:Student.class将会返回Student类对应的Class对象
调用对象的getClass()方法,返回该对象所属类对应的Class对象,该方法是Object类中的方法,所有的Java对象都可以调用该方法
使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
2.3反射获取构造方法并使用
Class类中用于获取构造方法的方法
Constructor< ? >[] getConstructors():返回所有公共构造方法对象的数组
Constructor< ? >[] getDeclaredConstructors():返回所有构造方法对象的数组
Constructor< T > getConstructor( Class< ? >…parameterTypes):返回单个公共构造方法对象
Constructor< T > getDeclaredConstructor( Class< ? >…parameterTypes):返回单个构造方法对象
Constructor类中用于创建对象的方法
T newInstance( Object … initargs):根据指定的构造方法创建对象
基本数据类型也可以通过.class得到对应的Class类型
public void setAccessible(boolean flag):值为true,取消访问检查,可以通过调用私有方法创建对象
2.4反射获取成员变量并使用
Class类中用于获取成员变量的方法
- Field[] getFields():返回所有公共成员变量对象的数组
- Field[] getDeclaredFields():返回所有成员变量对象的数组
- Field getField(String name):返回单个公共成员变量对象
- Field getDeclaredField(String name):返回单个成员变量对象
Field类中用于给成员变量赋值的方法
- void set(Object obj,Object value):给obj对象的成员变量赋值为value
2.5反射获取成员方法并使用
同上
Method类中用于调用成员方法的方法
- Object invoke(Object obj,Object… args):调用obj对象的成员方法,参数是args,返回值是Object类型
通过反射,可以越过泛型检查
20,模块化
1)模块化
1.2 模块的基本使用
模块的基本使用步骤
- 创建模块
为了体现模块的使用,创建2个模块。一个是myOne,一个是myTwo - 在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名,访问权限,模块依赖等信息
描述性文件中使用模块导出和模块依赖进行配置并使用
*模块中所有未导出的包都是模块私有的,他们是不能在模块之外并访问的
在myOne这个模块下的描述性文件中配置模块导出
模块导出格式:exports 包名 - 一个模块要访问其他的模块,必须明确指定依赖哪些模块,未明确指定依赖的模块不能访问
在myTwo这个模块下的描述性文件中配置模块依赖
模块依赖格式:requires 模块名
注意:写模块名报错,需要按下Alt+Enter提示,然后选择模块依赖