IO流
1 JDK API的使用
2 io简介
输入流用来读取in
输出流用来写出Out
在Java中,根据处理的数据单位不同,分为字节流和字符流
继承结构
java.io包:
File
字节流:针对二进制文件
InputStream
--FileInputStream
--BufferedInputStream
--ObjectInputStream
OutputStream
--FileOutputStream
--BufferedOutputStream
--ObjectOutputStream
字符流:针对文本文件。读写容易发生乱码,在读写时最好指定编码集为utf-8
Writer
--BufferedWriter
--OutputStreamWriter
Reader
--BufferedReader
--InputStreamReader
--PrintWriter/PrintStream
3 File文件流(字符流)
封装一个磁盘路径字符串,对这个路径可以执行一次操作。
可以用来封装文件路径、文件夹路径、不存在的路径。
创建对象
File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
常用方法
length():文件的字节量
exists():是否存在,存在返回true
isFile():是否为文件,是文件返回true
isDirectory():是否为文件夹,是文件夹返回true
getName():获取文件/文件夹名
getParent():获取父文件夹的路径
getAbsolutePath():获取文件的完整路径
文件夹方法
createNewFile():新建文件,文件夹不存在会异常,文件已经存在返回false
mkdirs():新建多层不存在的文件夹\a\b\c
mkdir():新建单层不存在的文件夹\a
delete():删除文件,删除空文件夹
list():返回String[],包含文件名
listFiles():返回File[],包含文件对象
4 字节流的读取
字节流是由字节组成的,所有的InputStream和OutputStream的子类都是字节流,主要用在处理二进制数据
1 InputStream抽象类
此抽象类是表示字节输入流的所有类的父类。
常用方法
abstract int read():从输入流中读取数据的下一个字节。
int read(byte[] b):从输入流中读取一定数量的字节,将其存储在缓冲区数组b中。
int read(byte[] b, int off, int len):将输入流中最多len个数据字节读入 byte 数组。
void close():关闭此输入流并释放与该流关联的所有系统资源
2 FileInputStream子类
直接插在文件上,直接读取文件数据
创建对象
FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(String pathname)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
3 BufferedInputStream子类
BufferedInputStream,缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组(默认8M大小)。在读取字节时,可根据需要从输入流再次填充该内部缓冲区,一次填充多个字节。
创建对象
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
5 字节流的写出
1 OutputStream抽象类
表示输出字节流的所有类的超类。输出流接受输出字节并将字节发送到接收器。
常用方法:
void close():关闭此输出流并释放与此流有关的所有系统资源。
void flush():刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b):将指定的字节写入此输出流。
2 FileOutputStream子类
直接插在文件上,直接写出文件数据
创建对象:
FileOutputStream(String name)
--创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(File file)
--创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append) –追加
--创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
3 BufferedOutputStream子类
该类实现缓冲的输出流,效率高。
创建对象
BufferedOutputStream(OutputStream out)
--创建一个新的缓冲输出流,以将数据写入指定的底层输出流。、
6 close()和flush()的区别
close()关闭流对象,但先刷新一次缓冲区,关闭之后,流对象不可以继续再使用。
flush()仅仅是刷新缓冲区(一般写字符时要用,因为字符是先进入的缓冲区),流对象还可以继续使用。
7 BIO、NIO、AIO的区别
①阻塞IO,BIO 就是传统的 java.io 包,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。它的优点是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低。
②非阻塞IO,NIO是Java 1.4 引入的 java.nio 包,可以构建多路复用的、同步非阻塞IO程序,提供更接近操作系统底层高性能的数据操作方式。
③异步IO,AIO是Java 1.7之后引入的包,是NIO的升级版本,提供异步非堵塞的IO操作方式,是基于事件和回调机制实现的,应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
泛型
1 通过泛型语法定义,约束集合元素的类型,进行安全检查,把错误显示在编译期
2 代码通用性更强
3 泛型可以提升程序代码的可读性,但它只是一个语法糖(编译后这样的东西就被删除,不出现在最终源代码中),对于JVM运行时的性能是没有任何影响的。
1 泛型申明
泛型可以在接口、方法、返回值上使用:
java.util.List泛型接口/类:
public interface Collection<E> {}
泛型方法的声明:
public <E> void print(E e) {}
在方法返回值前声明一个<E>表示后面出现的E是泛型,而不是普通的java变量。
2 常用名称
E:Element (在集合中使用,因为集合中存放的是元素)
T:Type(Java 类)
K:Key(键)
V:Value(值)
N:Number(数值类型)
?:表示不确定的java类型
集合
1 Collection接口
用来存放对象的数据结构。长度可变,而且可以存放不同类型的对象。
数组的缺点:长度是固定不可变的,访问方式单一,插入、删除等操作繁琐。
1继承结构
Collection接口
--List接口:数据有序,可以重复。
--ArrayList子类
--LinkedList子类
--Set接口:数据无序,不可以存重复值
--HashSet子类
--Map接口:键值对存数据
--HashMap
2常用方法
boolean add(E e):添加元素。
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) :如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组
2 List接口
有序的 collection(也称为序列)
特点
1 数据有序
2 允许存放重复元素
3 元素都有索引
常用方法
ListIterator<E> listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
ListIterator<E> listIterator(int index)
返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
void add(int index, E element)
在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将所有元素都插入到列表中的指定位置(可选操作)。
List<E> subList(int fromIndex, int toIndex)
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
get(int index)
返回列表中指定位置的元素。
int indexOf(Object o)
返回列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
List接口提供的listIterator(),不仅顺序向后迭代还可以逆向迭代
3 ArrayList实现类
内部用数组存放数据,每个对象都有下标。
内部数组默认初始容量是10。如果不够会以1.5倍容量增长。
查询快,增删数据效率会降低。
创建对象
new ArrayList()
4 LinkedList实现类
1 双向链表,两端效率高。底层就是数组和链表实现的。
2 下标遍历效率低,迭代器遍历效率高
常用方法
add()
get()
size()
remove(i)
remove(数据)
iterator()
addFirst() addLast()
getFirst() getLast()
removeFirst() removeLast()
数组和链表区别
1 数组ArrayList遍历快,因为存储空间连续;链表LinkedList遍历慢,因为存储空间不连续,要去通过指针定位下一个元素,所以链表遍历慢。
2 数组插入元素和删除元素需要重新申请内存,然后将拼接结果保存进去,效率低。链表只要改变一下相邻两个元素的指针,所以链表的插入和修改操作性能高。
3 业务一次赋值,不会改变,顺序遍历,选择数组;业务频繁变化,有新增,有删除,选择链表。
5 Set接口
1 不包含重复元素的集合,常用于给数据去重
2 数据无序(因为set集合没有下标)。
特点
1 HashSet:底层是哈希表,包装了HashMap,向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中,K仍然不许重复。
2 TreeSet:底层就是TreeMap,是红黑树的形式,便于查找数据。
常用方法
boolean add(E e):添加元素
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组
6 HashSet实现类
1 实现Set接口,由哈希表(实际上是一个 HashMap 实例)支持。
2 不保证 Set 的迭代顺序;特别是不保证顺序恒久不变。允许使用 null 元素。
7 Map接口
也叫哈希表、散列表。常用于存 键值对 结构的数据。其中的键不能重复,值可以重复。
特点
可以根据键提取对应的值
键不允许重复,如果重复会被覆盖
存放的都是无序数据
初始容量是16,默认的加载因子是0.75
继承结构
常用方法
void clear():移除所有映射关系(可选操作)。
boolean containsKey(Object key):包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
此映射将一个或多个键映射到指定值,返回 true。
V get(Object key)
返回指定键所映射的值;如果不包含该键的映射关系,返回null
boolean isEmpty():如果未包含键-值映射关系,则返回 true。
V put(K key, V value):将指定的值与此映射中的指定键关联
void putAll(Map<? extends K,? extends V> m):
从指定映射中将所有映射关系复制到此映射中
V remove(Object key):如果存在一个键的映射关系,则将其移除
int size():返回此映射中的键-值映射关系数量
Set<Map.Entry<K,V>> entrySet():返回此映射所包含的映射关系的 Set 视图
8 hashMap实现类
1 hashMap特点
1 基于哈希表的 Map 接口的实现。允许使用 null 值和 null 键。
2 HashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置。
3 当计算的位置没有数据时,就直接存放
4 当计算的位置有数据时,就是发生hash冲突/hash碰撞时,采用链表来解决
5 1.7是采用表头插入法插入链表,1.8采用的是尾部插入法
6 表头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发下导致链表成环的问题;在1.8中采用尾部插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题
7 1.8中新增加红黑树,当数组长度大于64,同时链表长度大于8的情况下,链表将转化为红黑树。
2 红黑树
红黑树是一种自平衡的二叉查找树
优点:插入和查询效率高
特点:
1 节点是红色或黑色;
2 根节点是黑色;
3 每个叶子节点都是黑色的空节点(NIL节点);
4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点);
5 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点;
6 每次新插入的节点都必须是红色。
3 HashMap扩容
初始容量:16
成长(负载)因子:0.75f
1 发生hash冲突时,2个对象会形成链表(散列桶),这时需要参数成长因子来优化,成长因子默认值0.75f。
2 空间使用达到75%,HashMap就会扩容,不然的话会形成很长的散列桶结构,不利于查询和插入。
3 成长因子不能很小,频繁扩容会大大消耗内存,成长因子可以根据实际情况进行调整
1.8时hash算法:(n - 1) & hash
n是数组长度
&:异或运算,都为1时,运算结果才为1
hash:键的hashCode值
扩容算法:2*数组容量
多线程
1 进程
就是正在运行的程序。
特点
1 独立性:进程是系统中独立存在的实体,它可以拥有自己的独立的资源,每一个进程都拥有自己私有的地址空间。
2 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。进程具有自己的生命周期和各种不同的状态。
3 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
2 线程
1 线程是操作系统能够进行运算调度的最小单位。
2 被包含在进程之中,是进程中的实际运作单位。
3 一个进程可以开启多个线程。
4 一个进程只有一个线程,这种程序被称为单线程。
5 一个进程中有多个线程称为多线程程序。
3多线程
多线程执行具有随机性
线程的5种生命周期
1)新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
2)就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
3)运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。(注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中)
4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态
6)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
三种阻塞状态
1)等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3)其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到其他阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
4 线程创建-继承Thread类
1 Thread类是实现Runnable接口的一个实例,代表一个线程的实例。
2 实现方式:继承Thread,重写run()方法
3 启动线程的唯一方法就是通过Thread类的start()方法。
4 start()方法只是通知操作系统线程就绪,具体什么时间执行,操作系统决定
常用方法
String getName():返回该线程的名称。
static Thread currentThread():返回对当前正在执行的线程对象的引用。
void setName(String name):改变线程名称,使之与参数 name 相同。
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
void start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
Thread(String name):分配新的 Thread 对象。
5 线程创建-实现Runnable接口
如果自类已经extends另一个类,就无法多继承,可以实现一个Runnable接口。
实现方式
1 实现Runnable接口,重写run()
2 创建实现类,创建Thread对象,传入Runnable实现类
MyThread t = new MyThread ();
Thread target = new Thread(t);
3 调用start()方法
target.start();
常用方法
void run()
使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
6创建线程的其他方式比较
方式 | 优点 | 缺点 |
Thread | ①编写简单 ②如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this获得当前线程。 | 线程类已经继承了Thread类,所以不能再继承其他父类 |
Runnable | ①线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。 ②多个线程可以共享同一个target对象,非常适合多个相同线程来处理同一份资源,从而将CPU、代码和数据分开,形成清晰的模型,体现面向对象的思想。 | 编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。 |
Callable | ①Runnable重写的方法是run() Callable重写的方法是call() ②Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。 ③Call方法可抛出异常,run方法不可以。 ④运行Callable可拿到Future对象,表示异步计算的结果。 | 存取其他项慢 |
Pool | ①线程池可以创建固定大小,无需反复创建线程对象,线程是比较耗费资源的资源 ②同时线程不会一直创建下去,拖慢系统 | 编程繁琐,难以理解 |
7 JVM启动的线程
1 JVM启动是多线程,最少要启动main线程和GC线程。
2 JVM虚拟机退出条件,是所有前台线程结束,虚拟机会自动退出
不会等待后台线程结束。
3 垃圾回收器是一个后台线程
8 多线程问题
多个线程同时访问数据,数据不安全,出现多线程问题。使用同步锁解决。
9 同步锁-Synchronized
通过sychronized关键字实现同步,一次只让一个线程执行,当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。
使用
synchronized(对象){
需要同步的代码;}
特点
1 前提1,同步需要两个或者两个以上的线程。
2 前提2,多个线程间必须使用同一个锁。(一定要是一个不变的对象)
3 同步的缺点是会降低执行效率,为了保证线程安全,必须牺牲性能。
4 可以修饰方法称为同步方法,使用的锁对象是this。
5 可以修饰代码块称为同步代码块,锁对象可以任意,但一定要是一个不变的对象
10 互斥锁和读写锁
Synchronized 互斥锁(悲观锁,有罪假设)
1 采用synchronized实现的同步机制叫做互斥锁机制,它获得的锁叫做互斥锁。
2当线程拥有锁标记时才能访问资源,没有锁标记便进入锁池
ReentrantReadWriteLock 读写锁(乐观锁,无罪假设)
1 读操作之间不存在数据竞争问题,读操作能够以共享锁的方式进行,提升性能。
2读写锁内部又分为读锁和写锁,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。
3读写锁主要应用于读多写少的场景
注解
1 注解可以增强java代码,同时利用反射技术可以扩充实现很多功能
2 现在最主流的开发都是基于注解方式,代码量少,框架可以根据注解去自动生成很多代码,从而减少代码量,程序更易读。
分类
①JDK自带注解
②元注解
③自定义注解
1 JDK注解
JDK注解的注解,就5个:
@Override 重写
@Deprecated标记就表明这个方法已经过时,但还可以用
@SuppressWarnings(“deprecation”) 忽略警告
@SafeVarargs jdk1.7出现,堆污染,不常用
@FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
2 元注解
元注解
描述注解的注解,就5个:
@Target注解用在哪里:类上、方法上、属性上
@Retention注解的生命周期:源文件中、class文件中、运行中
@Inherited允许子注解继承
@Documented生成javadoc时会包含注解,不常用
@Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
2.1 @Target ElementType.class
描述注解的使用范围:
ElementType.ANNOTATION_TYPE:应用于注释类型
ElementType.CONSTRUCTOR:应用于构造函数
ElementType.FIELD:应用于字段或属性
ElementType.LOCAL_VARIABLE:应用于局部变量
ElementType.METHOD:应用于方法级
ElementType.PACKAGE:应用于包声明
ElementType.PARAMETER:应用于方法的参数
ElementType.TYPE:应用于类的元素
2.2 @Retention RetentionPolicy.class
定义该注解被保留的时间长短.,没有时,反射拿不到,从而就无法去识别处理。
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在运行时有效(即运行时保留)
3 自定义注解
1 设置注解的使用范围@Target
2 使用“@interface+注解名”
3 定义属性
反射
1 概率
1 反射可以在运行时获取一个类的所有信息,可以直接操作程序的私有属性。
2 spring提供的容器中beans就是基于反射技术
2 创建类对象
Class.forName(“类的全路径”);
类名.class
对象.getClass();
3 常用方法
获得包名、类名
类对象.getPackage().getName()//包名
类对象.getSimpleName()//类名
类对象.getName()//完整类名
成员变量定义信息
getFields()//获得所有公开的成员变量,包括继承的变量
getDeclaredFields()//获得本类定义的成员变量,包括私有,不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
构造方法定义信息
getConstructor(参数类型列表)//获得公开的构造方法
getConstructors()//获得所有公开的构造方法
getDeclaredConstructors()//获得所有构造方法,包括私有
getDeclaredConstructor(int.class, String.class)
方法定义信息
getMethods()//获得所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获得本类定义的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名, int.class, String.class)
反射新建实例
c.newInstance();//执行无参构造
c.newInstance(6, "abc");//执行有参构造
c.getConstructor(int.class, String.class); //执行含参构造,获取构造方法
反射调用成员变量
c.getDeclaredField(变量名); //获取变量
c.setAccessible(true); //使私有成员允许访问
f.set(实例, 值); //为指定实例的变量赋值,静态变量,第一参数给 null
f.get(实例); //访问指定实例的变量的值,静态变量,第一参数给 null
反射调用成员方法
获取方法
Method m = c.getDeclaredMethod(方法名, 参数类型列表);
m.setAccessible(true) ;//使私有方法允许被调用
m.invoke(实例, 参数数据) ;//让指定的实例来执行该方法
4 暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。
主要是 getDeclaredXXX()方法
Socket
1 内部类
A类中又定义了B类,B类就是内部类。
特点
1 内部类可以直接访问外部类中的成员,包括私有成员
2 外部类要访问内部类的成员,必须要建立内部类的对象
3 在成员位置的内部类是成员内部类
4 在局部位置的内部类是局部内部类
成员内部类被被private修饰
如果想要访问private的内部类,可以访问外部类提供的公开的方法
成员内部类被static修饰,不常用
通过外部类.内部类调用类中的静态资源
局部内部类,减少类的作用范围,提高效率节省内存,不常见
匿名内部类:属于局部内部类,并且是没有名字的内部类
2 socket
1 socket也叫套接字编程,应用程序可以通过它发送或接收数据,可对其像对文件一样的打开、读写和关闭等操作。
2 套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序通信
3 网络套接字是IP地址与端口与协议的组合。
4 Socket就是为网络编程提供的一种机制,通信的两端都有Socket
3 服务端ServerSocket
在服务器端,选择一个端口号,在指定端口上等待客户端发起连接。
启动服务:ServerSocket ss = new ServerSocket(端口);
等待客户端发起连接,并建立连接通道:Sokcet socket = ss.accept();
4 客户端Socket
新建Socket对象,连接指定ip的服务器的指定端口
Socket s = new Socket(ip, port);
从Socket获取双向的流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();